18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * at76c503/at76c505 USB driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2002 - 2003 Oliver Kurth 68c2ecf20Sopenharmony_ci * Copyright (c) 2004 Joerg Albert <joerg.albert@gmx.de> 78c2ecf20Sopenharmony_ci * Copyright (c) 2004 Nick Jones 88c2ecf20Sopenharmony_ci * Copyright (c) 2004 Balint Seeber <n0_5p4m_p13453@hotmail.com> 98c2ecf20Sopenharmony_ci * Copyright (c) 2007 Guido Guenther <agx@sigxcpu.org> 108c2ecf20Sopenharmony_ci * Copyright (c) 2007 Kalle Valo <kalle.valo@iki.fi> 118c2ecf20Sopenharmony_ci * Copyright (c) 2010 Sebastian Smolorz <sesmo@gmx.net> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This file is part of the Berlios driver for WLAN USB devices based on the 148c2ecf20Sopenharmony_ci * Atmel AT76C503A/505/505A. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Some iw_handler code was taken from airo.c, (C) 1999 Benjamin Reed 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * TODO list is at the wiki: 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * https://wireless.wiki.kernel.org/en/users/Drivers/at76c50x-usb#TODO 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci#include <linux/kernel.h> 258c2ecf20Sopenharmony_ci#include <linux/sched.h> 268c2ecf20Sopenharmony_ci#include <linux/errno.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 308c2ecf20Sopenharmony_ci#include <linux/list.h> 318c2ecf20Sopenharmony_ci#include <linux/usb.h> 328c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 338c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 348c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 358c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 368c2ecf20Sopenharmony_ci#include <linux/wireless.h> 378c2ecf20Sopenharmony_ci#include <net/iw_handler.h> 388c2ecf20Sopenharmony_ci#include <net/ieee80211_radiotap.h> 398c2ecf20Sopenharmony_ci#include <linux/firmware.h> 408c2ecf20Sopenharmony_ci#include <linux/leds.h> 418c2ecf20Sopenharmony_ci#include <net/mac80211.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include "at76c50x-usb.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Version information */ 468c2ecf20Sopenharmony_ci#define DRIVER_NAME "at76c50x-usb" 478c2ecf20Sopenharmony_ci#define DRIVER_VERSION "0.17" 488c2ecf20Sopenharmony_ci#define DRIVER_DESC "Atmel at76x USB Wireless LAN Driver" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* at76_debug bits */ 518c2ecf20Sopenharmony_ci#define DBG_PROGRESS 0x00000001 /* authentication/accociation */ 528c2ecf20Sopenharmony_ci#define DBG_BSS_TABLE 0x00000002 /* show BSS table after scans */ 538c2ecf20Sopenharmony_ci#define DBG_IOCTL 0x00000004 /* ioctl calls / settings */ 548c2ecf20Sopenharmony_ci#define DBG_MAC_STATE 0x00000008 /* MAC state transitions */ 558c2ecf20Sopenharmony_ci#define DBG_TX_DATA 0x00000010 /* tx header */ 568c2ecf20Sopenharmony_ci#define DBG_TX_DATA_CONTENT 0x00000020 /* tx content */ 578c2ecf20Sopenharmony_ci#define DBG_TX_MGMT 0x00000040 /* tx management */ 588c2ecf20Sopenharmony_ci#define DBG_RX_DATA 0x00000080 /* rx data header */ 598c2ecf20Sopenharmony_ci#define DBG_RX_DATA_CONTENT 0x00000100 /* rx data content */ 608c2ecf20Sopenharmony_ci#define DBG_RX_MGMT 0x00000200 /* rx mgmt frame headers */ 618c2ecf20Sopenharmony_ci#define DBG_RX_BEACON 0x00000400 /* rx beacon */ 628c2ecf20Sopenharmony_ci#define DBG_RX_CTRL 0x00000800 /* rx control */ 638c2ecf20Sopenharmony_ci#define DBG_RX_MGMT_CONTENT 0x00001000 /* rx mgmt content */ 648c2ecf20Sopenharmony_ci#define DBG_RX_FRAGS 0x00002000 /* rx data fragment handling */ 658c2ecf20Sopenharmony_ci#define DBG_DEVSTART 0x00004000 /* fw download, device start */ 668c2ecf20Sopenharmony_ci#define DBG_URB 0x00008000 /* rx urb status, ... */ 678c2ecf20Sopenharmony_ci#define DBG_RX_ATMEL_HDR 0x00010000 /* Atmel-specific Rx headers */ 688c2ecf20Sopenharmony_ci#define DBG_PROC_ENTRY 0x00020000 /* procedure entries/exits */ 698c2ecf20Sopenharmony_ci#define DBG_PM 0x00040000 /* power management settings */ 708c2ecf20Sopenharmony_ci#define DBG_BSS_MATCH 0x00080000 /* BSS match failures */ 718c2ecf20Sopenharmony_ci#define DBG_PARAMS 0x00100000 /* show configured parameters */ 728c2ecf20Sopenharmony_ci#define DBG_WAIT_COMPLETE 0x00200000 /* command completion */ 738c2ecf20Sopenharmony_ci#define DBG_RX_FRAGS_SKB 0x00400000 /* skb header of Rx fragments */ 748c2ecf20Sopenharmony_ci#define DBG_BSS_TABLE_RM 0x00800000 /* purging bss table entries */ 758c2ecf20Sopenharmony_ci#define DBG_MONITOR_MODE 0x01000000 /* monitor mode */ 768c2ecf20Sopenharmony_ci#define DBG_MIB 0x02000000 /* dump all MIBs on startup */ 778c2ecf20Sopenharmony_ci#define DBG_MGMT_TIMER 0x04000000 /* dump mgmt_timer ops */ 788c2ecf20Sopenharmony_ci#define DBG_WE_EVENTS 0x08000000 /* dump wireless events */ 798c2ecf20Sopenharmony_ci#define DBG_FW 0x10000000 /* firmware download */ 808c2ecf20Sopenharmony_ci#define DBG_DFU 0x20000000 /* device firmware upgrade */ 818c2ecf20Sopenharmony_ci#define DBG_CMD 0x40000000 828c2ecf20Sopenharmony_ci#define DBG_MAC80211 0x80000000 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define DBG_DEFAULTS 0 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* Use our own dbg macro */ 878c2ecf20Sopenharmony_ci#define at76_dbg(bits, format, arg...) \ 888c2ecf20Sopenharmony_cido { \ 898c2ecf20Sopenharmony_ci if (at76_debug & (bits)) \ 908c2ecf20Sopenharmony_ci printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ 918c2ecf20Sopenharmony_ci} while (0) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define at76_dbg_dump(bits, buf, len, format, arg...) \ 948c2ecf20Sopenharmony_cido { \ 958c2ecf20Sopenharmony_ci if (at76_debug & (bits)) { \ 968c2ecf20Sopenharmony_ci printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ 978c2ecf20Sopenharmony_ci print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); \ 988c2ecf20Sopenharmony_ci } \ 998c2ecf20Sopenharmony_ci} while (0) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic uint at76_debug = DBG_DEFAULTS; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Protect against concurrent firmware loading and parsing */ 1048c2ecf20Sopenharmony_cistatic struct mutex fw_mutex; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic struct fwentry firmwares[] = { 1078c2ecf20Sopenharmony_ci [0] = { "" }, 1088c2ecf20Sopenharmony_ci [BOARD_503_ISL3861] = { "atmel_at76c503-i3861.bin" }, 1098c2ecf20Sopenharmony_ci [BOARD_503_ISL3863] = { "atmel_at76c503-i3863.bin" }, 1108c2ecf20Sopenharmony_ci [BOARD_503] = { "atmel_at76c503-rfmd.bin" }, 1118c2ecf20Sopenharmony_ci [BOARD_503_ACC] = { "atmel_at76c503-rfmd-acc.bin" }, 1128c2ecf20Sopenharmony_ci [BOARD_505] = { "atmel_at76c505-rfmd.bin" }, 1138c2ecf20Sopenharmony_ci [BOARD_505_2958] = { "atmel_at76c505-rfmd2958.bin" }, 1148c2ecf20Sopenharmony_ci [BOARD_505A] = { "atmel_at76c505a-rfmd2958.bin" }, 1158c2ecf20Sopenharmony_ci [BOARD_505AMX] = { "atmel_at76c505amx-rfmd.bin" }, 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c503-i3861.bin"); 1188c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c503-i3863.bin"); 1198c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c503-rfmd.bin"); 1208c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c503-rfmd-acc.bin"); 1218c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c505-rfmd.bin"); 1228c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c505-rfmd2958.bin"); 1238c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c505a-rfmd2958.bin"); 1248c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c505amx-rfmd.bin"); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci#define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const struct usb_device_id dev_table[] = { 1298c2ecf20Sopenharmony_ci /* 1308c2ecf20Sopenharmony_ci * at76c503-i3861 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci /* Generic AT76C503/3861 device */ 1338c2ecf20Sopenharmony_ci { USB_DEVICE(0x03eb, 0x7603), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1348c2ecf20Sopenharmony_ci /* Linksys WUSB11 v2.1/v2.6 */ 1358c2ecf20Sopenharmony_ci { USB_DEVICE(0x066b, 0x2211), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1368c2ecf20Sopenharmony_ci /* Netgear MA101 rev. A */ 1378c2ecf20Sopenharmony_ci { USB_DEVICE(0x0864, 0x4100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1388c2ecf20Sopenharmony_ci /* Tekram U300C / Allnet ALL0193 */ 1398c2ecf20Sopenharmony_ci { USB_DEVICE(0x0b3b, 0x1612), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1408c2ecf20Sopenharmony_ci /* HP HN210W J7801A */ 1418c2ecf20Sopenharmony_ci { USB_DEVICE(0x03f0, 0x011c), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1428c2ecf20Sopenharmony_ci /* Sitecom/Z-Com/Zyxel M4Y-750 */ 1438c2ecf20Sopenharmony_ci { USB_DEVICE(0x0cde, 0x0001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1448c2ecf20Sopenharmony_ci /* Dynalink/Askey WLL013 (intersil) */ 1458c2ecf20Sopenharmony_ci { USB_DEVICE(0x069a, 0x0320), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1468c2ecf20Sopenharmony_ci /* EZ connect 11Mpbs Wireless USB Adapter SMC2662W v1 */ 1478c2ecf20Sopenharmony_ci { USB_DEVICE(0x0d5c, 0xa001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1488c2ecf20Sopenharmony_ci /* BenQ AWL300 */ 1498c2ecf20Sopenharmony_ci { USB_DEVICE(0x04a5, 0x9000), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1508c2ecf20Sopenharmony_ci /* Addtron AWU-120, Compex WLU11 */ 1518c2ecf20Sopenharmony_ci { USB_DEVICE(0x05dd, 0xff31), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1528c2ecf20Sopenharmony_ci /* Intel AP310 AnyPoint II USB */ 1538c2ecf20Sopenharmony_ci { USB_DEVICE(0x8086, 0x0200), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1548c2ecf20Sopenharmony_ci /* Dynalink L11U */ 1558c2ecf20Sopenharmony_ci { USB_DEVICE(0x0d8e, 0x7100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1568c2ecf20Sopenharmony_ci /* Arescom WL-210, FCC id 07J-GL2411USB */ 1578c2ecf20Sopenharmony_ci { USB_DEVICE(0x0d8e, 0x7110), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1588c2ecf20Sopenharmony_ci /* I-O DATA WN-B11/USB */ 1598c2ecf20Sopenharmony_ci { USB_DEVICE(0x04bb, 0x0919), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1608c2ecf20Sopenharmony_ci /* BT Voyager 1010 */ 1618c2ecf20Sopenharmony_ci { USB_DEVICE(0x069a, 0x0821), USB_DEVICE_DATA(BOARD_503_ISL3861) }, 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * at76c503-i3863 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci /* Generic AT76C503/3863 device */ 1668c2ecf20Sopenharmony_ci { USB_DEVICE(0x03eb, 0x7604), USB_DEVICE_DATA(BOARD_503_ISL3863) }, 1678c2ecf20Sopenharmony_ci /* Samsung SWL-2100U */ 1688c2ecf20Sopenharmony_ci { USB_DEVICE(0x055d, 0xa000), USB_DEVICE_DATA(BOARD_503_ISL3863) }, 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * at76c503-rfmd 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci /* Generic AT76C503/RFMD device */ 1738c2ecf20Sopenharmony_ci { USB_DEVICE(0x03eb, 0x7605), USB_DEVICE_DATA(BOARD_503) }, 1748c2ecf20Sopenharmony_ci /* Dynalink/Askey WLL013 (rfmd) */ 1758c2ecf20Sopenharmony_ci { USB_DEVICE(0x069a, 0x0321), USB_DEVICE_DATA(BOARD_503) }, 1768c2ecf20Sopenharmony_ci /* Linksys WUSB11 v2.6 */ 1778c2ecf20Sopenharmony_ci { USB_DEVICE(0x077b, 0x2219), USB_DEVICE_DATA(BOARD_503) }, 1788c2ecf20Sopenharmony_ci /* Network Everywhere NWU11B */ 1798c2ecf20Sopenharmony_ci { USB_DEVICE(0x077b, 0x2227), USB_DEVICE_DATA(BOARD_503) }, 1808c2ecf20Sopenharmony_ci /* Netgear MA101 rev. B */ 1818c2ecf20Sopenharmony_ci { USB_DEVICE(0x0864, 0x4102), USB_DEVICE_DATA(BOARD_503) }, 1828c2ecf20Sopenharmony_ci /* D-Link DWL-120 rev. E */ 1838c2ecf20Sopenharmony_ci { USB_DEVICE(0x2001, 0x3200), USB_DEVICE_DATA(BOARD_503) }, 1848c2ecf20Sopenharmony_ci /* Actiontec 802UAT1, HWU01150-01UK */ 1858c2ecf20Sopenharmony_ci { USB_DEVICE(0x1668, 0x7605), USB_DEVICE_DATA(BOARD_503) }, 1868c2ecf20Sopenharmony_ci /* AirVast W-Buddie WN210 */ 1878c2ecf20Sopenharmony_ci { USB_DEVICE(0x03eb, 0x4102), USB_DEVICE_DATA(BOARD_503) }, 1888c2ecf20Sopenharmony_ci /* Dick Smith Electronics XH1153 802.11b USB adapter */ 1898c2ecf20Sopenharmony_ci { USB_DEVICE(0x1371, 0x5743), USB_DEVICE_DATA(BOARD_503) }, 1908c2ecf20Sopenharmony_ci /* CNet CNUSB611 */ 1918c2ecf20Sopenharmony_ci { USB_DEVICE(0x1371, 0x0001), USB_DEVICE_DATA(BOARD_503) }, 1928c2ecf20Sopenharmony_ci /* FiberLine FL-WL200U */ 1938c2ecf20Sopenharmony_ci { USB_DEVICE(0x1371, 0x0002), USB_DEVICE_DATA(BOARD_503) }, 1948c2ecf20Sopenharmony_ci /* BenQ AWL400 USB stick */ 1958c2ecf20Sopenharmony_ci { USB_DEVICE(0x04a5, 0x9001), USB_DEVICE_DATA(BOARD_503) }, 1968c2ecf20Sopenharmony_ci /* 3Com 3CRSHEW696 */ 1978c2ecf20Sopenharmony_ci { USB_DEVICE(0x0506, 0x0a01), USB_DEVICE_DATA(BOARD_503) }, 1988c2ecf20Sopenharmony_ci /* Siemens Santis ADSL WLAN USB adapter WLL 013 */ 1998c2ecf20Sopenharmony_ci { USB_DEVICE(0x0681, 0x001b), USB_DEVICE_DATA(BOARD_503) }, 2008c2ecf20Sopenharmony_ci /* Belkin F5D6050, version 2 */ 2018c2ecf20Sopenharmony_ci { USB_DEVICE(0x050d, 0x0050), USB_DEVICE_DATA(BOARD_503) }, 2028c2ecf20Sopenharmony_ci /* iBlitzz, BWU613 (not *B or *SB) */ 2038c2ecf20Sopenharmony_ci { USB_DEVICE(0x07b8, 0xb000), USB_DEVICE_DATA(BOARD_503) }, 2048c2ecf20Sopenharmony_ci /* Gigabyte GN-WLBM101 */ 2058c2ecf20Sopenharmony_ci { USB_DEVICE(0x1044, 0x8003), USB_DEVICE_DATA(BOARD_503) }, 2068c2ecf20Sopenharmony_ci /* Planex GW-US11S */ 2078c2ecf20Sopenharmony_ci { USB_DEVICE(0x2019, 0x3220), USB_DEVICE_DATA(BOARD_503) }, 2088c2ecf20Sopenharmony_ci /* Internal WLAN adapter in h5[4,5]xx series iPAQs */ 2098c2ecf20Sopenharmony_ci { USB_DEVICE(0x049f, 0x0032), USB_DEVICE_DATA(BOARD_503) }, 2108c2ecf20Sopenharmony_ci /* Corega Wireless LAN USB-11 mini */ 2118c2ecf20Sopenharmony_ci { USB_DEVICE(0x07aa, 0x0011), USB_DEVICE_DATA(BOARD_503) }, 2128c2ecf20Sopenharmony_ci /* Corega Wireless LAN USB-11 mini2 */ 2138c2ecf20Sopenharmony_ci { USB_DEVICE(0x07aa, 0x0018), USB_DEVICE_DATA(BOARD_503) }, 2148c2ecf20Sopenharmony_ci /* Uniden PCW100 */ 2158c2ecf20Sopenharmony_ci { USB_DEVICE(0x05dd, 0xff35), USB_DEVICE_DATA(BOARD_503) }, 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * at76c503-rfmd-acc 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci /* SMC2664W */ 2208c2ecf20Sopenharmony_ci { USB_DEVICE(0x083a, 0x3501), USB_DEVICE_DATA(BOARD_503_ACC) }, 2218c2ecf20Sopenharmony_ci /* Belkin F5D6050, SMC2662W v2, SMC2662W-AR */ 2228c2ecf20Sopenharmony_ci { USB_DEVICE(0x0d5c, 0xa002), USB_DEVICE_DATA(BOARD_503_ACC) }, 2238c2ecf20Sopenharmony_ci /* 2248c2ecf20Sopenharmony_ci * at76c505-rfmd 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci /* Generic AT76C505/RFMD */ 2278c2ecf20Sopenharmony_ci { USB_DEVICE(0x03eb, 0x7606), USB_DEVICE_DATA(BOARD_505) }, 2288c2ecf20Sopenharmony_ci /* 2298c2ecf20Sopenharmony_ci * at76c505-rfmd2958 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci /* Generic AT76C505/RFMD, OvisLink WL-1130USB */ 2328c2ecf20Sopenharmony_ci { USB_DEVICE(0x03eb, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, 2338c2ecf20Sopenharmony_ci /* Fiberline FL-WL240U */ 2348c2ecf20Sopenharmony_ci { USB_DEVICE(0x1371, 0x0014), USB_DEVICE_DATA(BOARD_505_2958) }, 2358c2ecf20Sopenharmony_ci /* CNet CNUSB-611G */ 2368c2ecf20Sopenharmony_ci { USB_DEVICE(0x1371, 0x0013), USB_DEVICE_DATA(BOARD_505_2958) }, 2378c2ecf20Sopenharmony_ci /* Linksys WUSB11 v2.8 */ 2388c2ecf20Sopenharmony_ci { USB_DEVICE(0x1915, 0x2233), USB_DEVICE_DATA(BOARD_505_2958) }, 2398c2ecf20Sopenharmony_ci /* Xterasys XN-2122B, IBlitzz BWU613B/BWU613SB */ 2408c2ecf20Sopenharmony_ci { USB_DEVICE(0x12fd, 0x1001), USB_DEVICE_DATA(BOARD_505_2958) }, 2418c2ecf20Sopenharmony_ci /* Corega WLAN USB Stick 11 */ 2428c2ecf20Sopenharmony_ci { USB_DEVICE(0x07aa, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, 2438c2ecf20Sopenharmony_ci /* Microstar MSI Box MS6978 */ 2448c2ecf20Sopenharmony_ci { USB_DEVICE(0x0db0, 0x1020), USB_DEVICE_DATA(BOARD_505_2958) }, 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * at76c505a-rfmd2958 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci /* Generic AT76C505A device */ 2498c2ecf20Sopenharmony_ci { USB_DEVICE(0x03eb, 0x7614), USB_DEVICE_DATA(BOARD_505A) }, 2508c2ecf20Sopenharmony_ci /* Generic AT76C505AS device */ 2518c2ecf20Sopenharmony_ci { USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A) }, 2528c2ecf20Sopenharmony_ci /* Siemens Gigaset USB WLAN Adapter 11 */ 2538c2ecf20Sopenharmony_ci { USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A) }, 2548c2ecf20Sopenharmony_ci /* OQO Model 01+ Internal Wi-Fi */ 2558c2ecf20Sopenharmony_ci { USB_DEVICE(0x1557, 0x0002), USB_DEVICE_DATA(BOARD_505A) }, 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * at76c505amx-rfmd 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci /* Generic AT76C505AMX device */ 2608c2ecf20Sopenharmony_ci { USB_DEVICE(0x03eb, 0x7615), USB_DEVICE_DATA(BOARD_505AMX) }, 2618c2ecf20Sopenharmony_ci { } 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, dev_table); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* Supported rates of this hardware, bit 7 marks basic rates */ 2678c2ecf20Sopenharmony_cistatic const u8 hw_rates[] = { 0x82, 0x84, 0x0b, 0x16 }; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic const char *const preambles[] = { "long", "short", "auto" }; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* Firmware download */ 2728c2ecf20Sopenharmony_ci/* DFU states */ 2738c2ecf20Sopenharmony_ci#define STATE_IDLE 0x00 2748c2ecf20Sopenharmony_ci#define STATE_DETACH 0x01 2758c2ecf20Sopenharmony_ci#define STATE_DFU_IDLE 0x02 2768c2ecf20Sopenharmony_ci#define STATE_DFU_DOWNLOAD_SYNC 0x03 2778c2ecf20Sopenharmony_ci#define STATE_DFU_DOWNLOAD_BUSY 0x04 2788c2ecf20Sopenharmony_ci#define STATE_DFU_DOWNLOAD_IDLE 0x05 2798c2ecf20Sopenharmony_ci#define STATE_DFU_MANIFEST_SYNC 0x06 2808c2ecf20Sopenharmony_ci#define STATE_DFU_MANIFEST 0x07 2818c2ecf20Sopenharmony_ci#define STATE_DFU_MANIFEST_WAIT_RESET 0x08 2828c2ecf20Sopenharmony_ci#define STATE_DFU_UPLOAD_IDLE 0x09 2838c2ecf20Sopenharmony_ci#define STATE_DFU_ERROR 0x0a 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* DFU commands */ 2868c2ecf20Sopenharmony_ci#define DFU_DETACH 0 2878c2ecf20Sopenharmony_ci#define DFU_DNLOAD 1 2888c2ecf20Sopenharmony_ci#define DFU_UPLOAD 2 2898c2ecf20Sopenharmony_ci#define DFU_GETSTATUS 3 2908c2ecf20Sopenharmony_ci#define DFU_CLRSTATUS 4 2918c2ecf20Sopenharmony_ci#define DFU_GETSTATE 5 2928c2ecf20Sopenharmony_ci#define DFU_ABORT 6 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci#define FW_BLOCK_SIZE 1024 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistruct dfu_status { 2978c2ecf20Sopenharmony_ci unsigned char status; 2988c2ecf20Sopenharmony_ci unsigned char poll_timeout[3]; 2998c2ecf20Sopenharmony_ci unsigned char state; 3008c2ecf20Sopenharmony_ci unsigned char string; 3018c2ecf20Sopenharmony_ci} __packed; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic inline int at76_is_intersil(enum board_type board) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci return (board == BOARD_503_ISL3861 || board == BOARD_503_ISL3863); 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic inline int at76_is_503rfmd(enum board_type board) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci return (board == BOARD_503 || board == BOARD_503_ACC); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic inline int at76_is_505a(enum board_type board) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci return (board == BOARD_505A || board == BOARD_505AMX); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* Load a block of the first (internal) part of the firmware */ 3198c2ecf20Sopenharmony_cistatic int at76_load_int_fw_block(struct usb_device *udev, int blockno, 3208c2ecf20Sopenharmony_ci void *block, int size) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), DFU_DNLOAD, 3238c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_DIR_OUT | 3248c2ecf20Sopenharmony_ci USB_RECIP_INTERFACE, blockno, 0, block, size, 3258c2ecf20Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int at76_dfu_get_status(struct usb_device *udev, 3298c2ecf20Sopenharmony_ci struct dfu_status *status) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci int ret; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATUS, 3348c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, 3358c2ecf20Sopenharmony_ci 0, 0, status, sizeof(struct dfu_status), 3368c2ecf20Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 3378c2ecf20Sopenharmony_ci return ret; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int at76_dfu_get_state(struct usb_device *udev, u8 *state) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci int ret; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATE, 3458c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, 3468c2ecf20Sopenharmony_ci 0, 0, state, 1, USB_CTRL_GET_TIMEOUT); 3478c2ecf20Sopenharmony_ci return ret; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/* Convert timeout from the DFU status to jiffies */ 3518c2ecf20Sopenharmony_cistatic inline unsigned long at76_get_timeout(struct dfu_status *s) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci return msecs_to_jiffies((s->poll_timeout[2] << 16) 3548c2ecf20Sopenharmony_ci | (s->poll_timeout[1] << 8) 3558c2ecf20Sopenharmony_ci | (s->poll_timeout[0])); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/* Load internal firmware from the buffer. If manifest_sync_timeout > 0, use 3598c2ecf20Sopenharmony_ci * its value in jiffies in the MANIFEST_SYNC state. */ 3608c2ecf20Sopenharmony_cistatic int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, 3618c2ecf20Sopenharmony_ci int manifest_sync_timeout) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci int ret = 0; 3648c2ecf20Sopenharmony_ci int need_dfu_state = 1; 3658c2ecf20Sopenharmony_ci int is_done = 0; 3668c2ecf20Sopenharmony_ci u32 dfu_timeout = 0; 3678c2ecf20Sopenharmony_ci int bsize = 0; 3688c2ecf20Sopenharmony_ci int blockno = 0; 3698c2ecf20Sopenharmony_ci struct dfu_status *dfu_stat_buf = NULL; 3708c2ecf20Sopenharmony_ci u8 *dfu_state = NULL; 3718c2ecf20Sopenharmony_ci u8 *block = NULL; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size, 3748c2ecf20Sopenharmony_ci manifest_sync_timeout); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (!size) { 3778c2ecf20Sopenharmony_ci dev_err(&udev->dev, "FW buffer length invalid!\n"); 3788c2ecf20Sopenharmony_ci return -EINVAL; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci dfu_stat_buf = kmalloc(sizeof(struct dfu_status), GFP_KERNEL); 3828c2ecf20Sopenharmony_ci if (!dfu_stat_buf) { 3838c2ecf20Sopenharmony_ci ret = -ENOMEM; 3848c2ecf20Sopenharmony_ci goto exit; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); 3888c2ecf20Sopenharmony_ci if (!block) { 3898c2ecf20Sopenharmony_ci ret = -ENOMEM; 3908c2ecf20Sopenharmony_ci goto exit; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci dfu_state = kmalloc(sizeof(u8), GFP_KERNEL); 3948c2ecf20Sopenharmony_ci if (!dfu_state) { 3958c2ecf20Sopenharmony_ci ret = -ENOMEM; 3968c2ecf20Sopenharmony_ci goto exit; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci *dfu_state = 0; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci do { 4018c2ecf20Sopenharmony_ci if (need_dfu_state) { 4028c2ecf20Sopenharmony_ci ret = at76_dfu_get_state(udev, dfu_state); 4038c2ecf20Sopenharmony_ci if (ret < 0) { 4048c2ecf20Sopenharmony_ci dev_err(&udev->dev, 4058c2ecf20Sopenharmony_ci "cannot get DFU state: %d\n", ret); 4068c2ecf20Sopenharmony_ci goto exit; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci need_dfu_state = 0; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci switch (*dfu_state) { 4128c2ecf20Sopenharmony_ci case STATE_DFU_DOWNLOAD_SYNC: 4138c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC"); 4148c2ecf20Sopenharmony_ci ret = at76_dfu_get_status(udev, dfu_stat_buf); 4158c2ecf20Sopenharmony_ci if (ret >= 0) { 4168c2ecf20Sopenharmony_ci *dfu_state = dfu_stat_buf->state; 4178c2ecf20Sopenharmony_ci dfu_timeout = at76_get_timeout(dfu_stat_buf); 4188c2ecf20Sopenharmony_ci need_dfu_state = 0; 4198c2ecf20Sopenharmony_ci } else 4208c2ecf20Sopenharmony_ci dev_err(&udev->dev, 4218c2ecf20Sopenharmony_ci "at76_dfu_get_status returned %d\n", 4228c2ecf20Sopenharmony_ci ret); 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci case STATE_DFU_DOWNLOAD_BUSY: 4268c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_BUSY"); 4278c2ecf20Sopenharmony_ci need_dfu_state = 1; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "DFU: Resetting device"); 4308c2ecf20Sopenharmony_ci schedule_timeout_interruptible(dfu_timeout); 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci case STATE_DFU_DOWNLOAD_IDLE: 4348c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "DOWNLOAD..."); 4358c2ecf20Sopenharmony_ci fallthrough; 4368c2ecf20Sopenharmony_ci case STATE_DFU_IDLE: 4378c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "DFU IDLE"); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci bsize = min_t(int, size, FW_BLOCK_SIZE); 4408c2ecf20Sopenharmony_ci memcpy(block, buf, bsize); 4418c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "int fw, size left = %5d, " 4428c2ecf20Sopenharmony_ci "bsize = %4d, blockno = %2d", size, bsize, 4438c2ecf20Sopenharmony_ci blockno); 4448c2ecf20Sopenharmony_ci ret = 4458c2ecf20Sopenharmony_ci at76_load_int_fw_block(udev, blockno, block, bsize); 4468c2ecf20Sopenharmony_ci buf += bsize; 4478c2ecf20Sopenharmony_ci size -= bsize; 4488c2ecf20Sopenharmony_ci blockno++; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (ret != bsize) 4518c2ecf20Sopenharmony_ci dev_err(&udev->dev, 4528c2ecf20Sopenharmony_ci "at76_load_int_fw_block returned %d\n", 4538c2ecf20Sopenharmony_ci ret); 4548c2ecf20Sopenharmony_ci need_dfu_state = 1; 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci case STATE_DFU_MANIFEST_SYNC: 4588c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC"); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci ret = at76_dfu_get_status(udev, dfu_stat_buf); 4618c2ecf20Sopenharmony_ci if (ret < 0) 4628c2ecf20Sopenharmony_ci break; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci *dfu_state = dfu_stat_buf->state; 4658c2ecf20Sopenharmony_ci dfu_timeout = at76_get_timeout(dfu_stat_buf); 4668c2ecf20Sopenharmony_ci need_dfu_state = 0; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* override the timeout from the status response, 4698c2ecf20Sopenharmony_ci needed for AT76C505A */ 4708c2ecf20Sopenharmony_ci if (manifest_sync_timeout > 0) 4718c2ecf20Sopenharmony_ci dfu_timeout = manifest_sync_timeout; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "DFU: Waiting for manifest phase"); 4748c2ecf20Sopenharmony_ci schedule_timeout_interruptible(dfu_timeout); 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci case STATE_DFU_MANIFEST: 4788c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST"); 4798c2ecf20Sopenharmony_ci is_done = 1; 4808c2ecf20Sopenharmony_ci break; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci case STATE_DFU_MANIFEST_WAIT_RESET: 4838c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_WAIT_RESET"); 4848c2ecf20Sopenharmony_ci is_done = 1; 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci case STATE_DFU_UPLOAD_IDLE: 4888c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "STATE_DFU_UPLOAD_IDLE"); 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci case STATE_DFU_ERROR: 4928c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "STATE_DFU_ERROR"); 4938c2ecf20Sopenharmony_ci ret = -EPIPE; 4948c2ecf20Sopenharmony_ci break; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci default: 4978c2ecf20Sopenharmony_ci at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", *dfu_state); 4988c2ecf20Sopenharmony_ci ret = -EINVAL; 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci } while (!is_done && (ret >= 0)); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ciexit: 5048c2ecf20Sopenharmony_ci kfree(dfu_state); 5058c2ecf20Sopenharmony_ci kfree(block); 5068c2ecf20Sopenharmony_ci kfree(dfu_stat_buf); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (ret >= 0) 5098c2ecf20Sopenharmony_ci ret = 0; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return ret; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci/* LED trigger */ 5158c2ecf20Sopenharmony_cistatic int tx_activity; 5168c2ecf20Sopenharmony_cistatic void at76_ledtrig_tx_timerfunc(struct timer_list *unused); 5178c2ecf20Sopenharmony_cistatic DEFINE_TIMER(ledtrig_tx_timer, at76_ledtrig_tx_timerfunc); 5188c2ecf20Sopenharmony_ciDEFINE_LED_TRIGGER(ledtrig_tx); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic void at76_ledtrig_tx_timerfunc(struct timer_list *unused) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci static int tx_lastactivity; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (tx_lastactivity != tx_activity) { 5258c2ecf20Sopenharmony_ci tx_lastactivity = tx_activity; 5268c2ecf20Sopenharmony_ci led_trigger_event(ledtrig_tx, LED_FULL); 5278c2ecf20Sopenharmony_ci mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); 5288c2ecf20Sopenharmony_ci } else 5298c2ecf20Sopenharmony_ci led_trigger_event(ledtrig_tx, LED_OFF); 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic void at76_ledtrig_tx_activity(void) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci tx_activity++; 5358c2ecf20Sopenharmony_ci if (!timer_pending(&ledtrig_tx_timer)) 5368c2ecf20Sopenharmony_ci mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic int at76_remap(struct usb_device *udev) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci int ret; 5428c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0a, 5438c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_OUT | 5448c2ecf20Sopenharmony_ci USB_RECIP_INTERFACE, 0, 0, NULL, 0, 5458c2ecf20Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 5468c2ecf20Sopenharmony_ci if (ret < 0) 5478c2ecf20Sopenharmony_ci return ret; 5488c2ecf20Sopenharmony_ci return 0; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic int at76_get_op_mode(struct usb_device *udev) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci int ret; 5548c2ecf20Sopenharmony_ci u8 saved; 5558c2ecf20Sopenharmony_ci u8 *op_mode; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci op_mode = kmalloc(1, GFP_NOIO); 5588c2ecf20Sopenharmony_ci if (!op_mode) 5598c2ecf20Sopenharmony_ci return -ENOMEM; 5608c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, 5618c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_IN | 5628c2ecf20Sopenharmony_ci USB_RECIP_INTERFACE, 0x01, 0, op_mode, 1, 5638c2ecf20Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 5648c2ecf20Sopenharmony_ci saved = *op_mode; 5658c2ecf20Sopenharmony_ci kfree(op_mode); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (ret < 0) 5688c2ecf20Sopenharmony_ci return ret; 5698c2ecf20Sopenharmony_ci else if (ret < 1) 5708c2ecf20Sopenharmony_ci return -EIO; 5718c2ecf20Sopenharmony_ci else 5728c2ecf20Sopenharmony_ci return saved; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci/* Load a block of the second ("external") part of the firmware */ 5768c2ecf20Sopenharmony_cistatic inline int at76_load_ext_fw_block(struct usb_device *udev, int blockno, 5778c2ecf20Sopenharmony_ci void *block, int size) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, 5808c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, 5818c2ecf20Sopenharmony_ci 0x0802, blockno, block, size, 5828c2ecf20Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic inline int at76_get_hw_cfg(struct usb_device *udev, 5868c2ecf20Sopenharmony_ci union at76_hwcfg *buf, int buf_size) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, 5898c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_IN | 5908c2ecf20Sopenharmony_ci USB_RECIP_INTERFACE, 0x0a02, 0, 5918c2ecf20Sopenharmony_ci buf, buf_size, USB_CTRL_GET_TIMEOUT); 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci/* Intersil boards use a different "value" for GetHWConfig requests */ 5958c2ecf20Sopenharmony_cistatic inline int at76_get_hw_cfg_intersil(struct usb_device *udev, 5968c2ecf20Sopenharmony_ci union at76_hwcfg *buf, int buf_size) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, 5998c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_IN | 6008c2ecf20Sopenharmony_ci USB_RECIP_INTERFACE, 0x0902, 0, 6018c2ecf20Sopenharmony_ci buf, buf_size, USB_CTRL_GET_TIMEOUT); 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci/* Get the hardware configuration for the adapter and put it to the appropriate 6058c2ecf20Sopenharmony_ci * fields of 'priv' (the GetHWConfig request and interpretation of the result 6068c2ecf20Sopenharmony_ci * depends on the board type) */ 6078c2ecf20Sopenharmony_cistatic int at76_get_hw_config(struct at76_priv *priv) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci int ret; 6108c2ecf20Sopenharmony_ci union at76_hwcfg *hwcfg = kmalloc(sizeof(*hwcfg), GFP_KERNEL); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (!hwcfg) 6138c2ecf20Sopenharmony_ci return -ENOMEM; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (at76_is_intersil(priv->board_type)) { 6168c2ecf20Sopenharmony_ci ret = at76_get_hw_cfg_intersil(priv->udev, hwcfg, 6178c2ecf20Sopenharmony_ci sizeof(hwcfg->i)); 6188c2ecf20Sopenharmony_ci if (ret < 0) 6198c2ecf20Sopenharmony_ci goto exit; 6208c2ecf20Sopenharmony_ci memcpy(priv->mac_addr, hwcfg->i.mac_addr, ETH_ALEN); 6218c2ecf20Sopenharmony_ci priv->regulatory_domain = hwcfg->i.regulatory_domain; 6228c2ecf20Sopenharmony_ci } else if (at76_is_503rfmd(priv->board_type)) { 6238c2ecf20Sopenharmony_ci ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r3)); 6248c2ecf20Sopenharmony_ci if (ret < 0) 6258c2ecf20Sopenharmony_ci goto exit; 6268c2ecf20Sopenharmony_ci memcpy(priv->mac_addr, hwcfg->r3.mac_addr, ETH_ALEN); 6278c2ecf20Sopenharmony_ci priv->regulatory_domain = hwcfg->r3.regulatory_domain; 6288c2ecf20Sopenharmony_ci } else { 6298c2ecf20Sopenharmony_ci ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r5)); 6308c2ecf20Sopenharmony_ci if (ret < 0) 6318c2ecf20Sopenharmony_ci goto exit; 6328c2ecf20Sopenharmony_ci memcpy(priv->mac_addr, hwcfg->r5.mac_addr, ETH_ALEN); 6338c2ecf20Sopenharmony_ci priv->regulatory_domain = hwcfg->r5.regulatory_domain; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ciexit: 6378c2ecf20Sopenharmony_ci kfree(hwcfg); 6388c2ecf20Sopenharmony_ci if (ret < 0) 6398c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "cannot get HW Config (error %d)\n", 6408c2ecf20Sopenharmony_ci ret); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return ret; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic struct reg_domain const *at76_get_reg_domain(u16 code) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci int i; 6488c2ecf20Sopenharmony_ci static struct reg_domain const fd_tab[] = { 6498c2ecf20Sopenharmony_ci { 0x10, "FCC (USA)", 0x7ff }, /* ch 1-11 */ 6508c2ecf20Sopenharmony_ci { 0x20, "IC (Canada)", 0x7ff }, /* ch 1-11 */ 6518c2ecf20Sopenharmony_ci { 0x30, "ETSI (most of Europe)", 0x1fff }, /* ch 1-13 */ 6528c2ecf20Sopenharmony_ci { 0x31, "Spain", 0x600 }, /* ch 10-11 */ 6538c2ecf20Sopenharmony_ci { 0x32, "France", 0x1e00 }, /* ch 10-13 */ 6548c2ecf20Sopenharmony_ci { 0x40, "MKK (Japan)", 0x2000 }, /* ch 14 */ 6558c2ecf20Sopenharmony_ci { 0x41, "MKK1 (Japan)", 0x3fff }, /* ch 1-14 */ 6568c2ecf20Sopenharmony_ci { 0x50, "Israel", 0x3fc }, /* ch 3-9 */ 6578c2ecf20Sopenharmony_ci { 0x00, "<unknown>", 0xffffffff } /* ch 1-32 */ 6588c2ecf20Sopenharmony_ci }; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* Last entry is fallback for unknown domain code */ 6618c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fd_tab) - 1; i++) 6628c2ecf20Sopenharmony_ci if (code == fd_tab[i].code) 6638c2ecf20Sopenharmony_ci break; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci return &fd_tab[i]; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic inline int at76_get_mib(struct usb_device *udev, u16 mib, void *buf, 6698c2ecf20Sopenharmony_ci int buf_size) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci int ret; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, 6748c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_IN | 6758c2ecf20Sopenharmony_ci USB_RECIP_INTERFACE, mib << 8, 0, buf, buf_size, 6768c2ecf20Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 6778c2ecf20Sopenharmony_ci if (ret >= 0 && ret != buf_size) 6788c2ecf20Sopenharmony_ci return -EIO; 6798c2ecf20Sopenharmony_ci return ret; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci/* Return positive number for status, negative for an error */ 6838c2ecf20Sopenharmony_cistatic inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci u8 *stat_buf; 6868c2ecf20Sopenharmony_ci int ret; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci stat_buf = kmalloc(40, GFP_NOIO); 6898c2ecf20Sopenharmony_ci if (!stat_buf) 6908c2ecf20Sopenharmony_ci return -ENOMEM; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x22, 6938c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_IN | 6948c2ecf20Sopenharmony_ci USB_RECIP_INTERFACE, cmd, 0, stat_buf, 6958c2ecf20Sopenharmony_ci 40, USB_CTRL_GET_TIMEOUT); 6968c2ecf20Sopenharmony_ci if (ret >= 0) 6978c2ecf20Sopenharmony_ci ret = stat_buf[5]; 6988c2ecf20Sopenharmony_ci kfree(stat_buf); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci return ret; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci#define MAKE_CMD_CASE(c) case (c): return #c 7048c2ecf20Sopenharmony_cistatic const char *at76_get_cmd_string(u8 cmd_status) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci switch (cmd_status) { 7078c2ecf20Sopenharmony_ci MAKE_CMD_CASE(CMD_SET_MIB); 7088c2ecf20Sopenharmony_ci MAKE_CMD_CASE(CMD_GET_MIB); 7098c2ecf20Sopenharmony_ci MAKE_CMD_CASE(CMD_SCAN); 7108c2ecf20Sopenharmony_ci MAKE_CMD_CASE(CMD_JOIN); 7118c2ecf20Sopenharmony_ci MAKE_CMD_CASE(CMD_START_IBSS); 7128c2ecf20Sopenharmony_ci MAKE_CMD_CASE(CMD_RADIO_ON); 7138c2ecf20Sopenharmony_ci MAKE_CMD_CASE(CMD_RADIO_OFF); 7148c2ecf20Sopenharmony_ci MAKE_CMD_CASE(CMD_STARTUP); 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci return "UNKNOWN"; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic int at76_set_card_command(struct usb_device *udev, u8 cmd, void *buf, 7218c2ecf20Sopenharmony_ci int buf_size) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci int ret; 7248c2ecf20Sopenharmony_ci struct at76_command *cmd_buf = kmalloc(sizeof(struct at76_command) + 7258c2ecf20Sopenharmony_ci buf_size, GFP_KERNEL); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (!cmd_buf) 7288c2ecf20Sopenharmony_ci return -ENOMEM; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci cmd_buf->cmd = cmd; 7318c2ecf20Sopenharmony_ci cmd_buf->reserved = 0; 7328c2ecf20Sopenharmony_ci cmd_buf->size = cpu_to_le16(buf_size); 7338c2ecf20Sopenharmony_ci memcpy(cmd_buf->data, buf, buf_size); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci at76_dbg_dump(DBG_CMD, cmd_buf, sizeof(struct at76_command) + buf_size, 7368c2ecf20Sopenharmony_ci "issuing command %s (0x%02x)", 7378c2ecf20Sopenharmony_ci at76_get_cmd_string(cmd), cmd); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, 7408c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, 7418c2ecf20Sopenharmony_ci 0, 0, cmd_buf, 7428c2ecf20Sopenharmony_ci sizeof(struct at76_command) + buf_size, 7438c2ecf20Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 7448c2ecf20Sopenharmony_ci kfree(cmd_buf); 7458c2ecf20Sopenharmony_ci return ret; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci#define MAKE_CMD_STATUS_CASE(c) case (c): return #c 7498c2ecf20Sopenharmony_cistatic const char *at76_get_cmd_status_string(u8 cmd_status) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci switch (cmd_status) { 7528c2ecf20Sopenharmony_ci MAKE_CMD_STATUS_CASE(CMD_STATUS_IDLE); 7538c2ecf20Sopenharmony_ci MAKE_CMD_STATUS_CASE(CMD_STATUS_COMPLETE); 7548c2ecf20Sopenharmony_ci MAKE_CMD_STATUS_CASE(CMD_STATUS_UNKNOWN); 7558c2ecf20Sopenharmony_ci MAKE_CMD_STATUS_CASE(CMD_STATUS_INVALID_PARAMETER); 7568c2ecf20Sopenharmony_ci MAKE_CMD_STATUS_CASE(CMD_STATUS_FUNCTION_NOT_SUPPORTED); 7578c2ecf20Sopenharmony_ci MAKE_CMD_STATUS_CASE(CMD_STATUS_TIME_OUT); 7588c2ecf20Sopenharmony_ci MAKE_CMD_STATUS_CASE(CMD_STATUS_IN_PROGRESS); 7598c2ecf20Sopenharmony_ci MAKE_CMD_STATUS_CASE(CMD_STATUS_HOST_FAILURE); 7608c2ecf20Sopenharmony_ci MAKE_CMD_STATUS_CASE(CMD_STATUS_SCAN_FAILED); 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return "UNKNOWN"; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci/* Wait until the command is completed */ 7678c2ecf20Sopenharmony_cistatic int at76_wait_completion(struct at76_priv *priv, int cmd) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci int status = 0; 7708c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + CMD_COMPLETION_TIMEOUT; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci do { 7738c2ecf20Sopenharmony_ci status = at76_get_cmd_status(priv->udev, cmd); 7748c2ecf20Sopenharmony_ci if (status < 0) { 7758c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 7768c2ecf20Sopenharmony_ci "at76_get_cmd_status failed: %d\n", 7778c2ecf20Sopenharmony_ci status); 7788c2ecf20Sopenharmony_ci break; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci at76_dbg(DBG_WAIT_COMPLETE, 7828c2ecf20Sopenharmony_ci "%s: Waiting on cmd %d, status = %d (%s)", 7838c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy), cmd, status, 7848c2ecf20Sopenharmony_ci at76_get_cmd_status_string(status)); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (status != CMD_STATUS_IN_PROGRESS 7878c2ecf20Sopenharmony_ci && status != CMD_STATUS_IDLE) 7888c2ecf20Sopenharmony_ci break; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci schedule_timeout_interruptible(HZ / 10); /* 100 ms */ 7918c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 7928c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 7938c2ecf20Sopenharmony_ci "completion timeout for command %d\n", cmd); 7948c2ecf20Sopenharmony_ci status = -ETIMEDOUT; 7958c2ecf20Sopenharmony_ci break; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci } while (1); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci return status; 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cistatic int at76_set_mib(struct at76_priv *priv, struct set_mib_buffer *buf) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci int ret; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci ret = at76_set_card_command(priv->udev, CMD_SET_MIB, buf, 8078c2ecf20Sopenharmony_ci offsetof(struct set_mib_buffer, 8088c2ecf20Sopenharmony_ci data) + buf->size); 8098c2ecf20Sopenharmony_ci if (ret < 0) 8108c2ecf20Sopenharmony_ci return ret; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci ret = at76_wait_completion(priv, CMD_SET_MIB); 8138c2ecf20Sopenharmony_ci if (ret != CMD_STATUS_COMPLETE) { 8148c2ecf20Sopenharmony_ci wiphy_info(priv->hw->wiphy, 8158c2ecf20Sopenharmony_ci "set_mib: at76_wait_completion failed with %d\n", 8168c2ecf20Sopenharmony_ci ret); 8178c2ecf20Sopenharmony_ci ret = -EIO; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci return ret; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci/* Return < 0 on error, == 0 if no command sent, == 1 if cmd sent */ 8248c2ecf20Sopenharmony_cistatic int at76_set_radio(struct at76_priv *priv, int enable) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci int ret; 8278c2ecf20Sopenharmony_ci int cmd; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (priv->radio_on == enable) 8308c2ecf20Sopenharmony_ci return 0; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci cmd = enable ? CMD_RADIO_ON : CMD_RADIO_OFF; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci ret = at76_set_card_command(priv->udev, cmd, NULL, 0); 8358c2ecf20Sopenharmony_ci if (ret < 0) 8368c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 8378c2ecf20Sopenharmony_ci "at76_set_card_command(%d) failed: %d\n", cmd, ret); 8388c2ecf20Sopenharmony_ci else 8398c2ecf20Sopenharmony_ci ret = 1; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci priv->radio_on = enable; 8428c2ecf20Sopenharmony_ci return ret; 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci/* Set current power save mode (AT76_PM_OFF/AT76_PM_ON/AT76_PM_SMART) */ 8468c2ecf20Sopenharmony_cistatic int at76_set_pm_mode(struct at76_priv *priv) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci int ret = 0; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci priv->mib_buf.type = MIB_MAC_MGMT; 8518c2ecf20Sopenharmony_ci priv->mib_buf.size = 1; 8528c2ecf20Sopenharmony_ci priv->mib_buf.index = offsetof(struct mib_mac_mgmt, power_mgmt_mode); 8538c2ecf20Sopenharmony_ci priv->mib_buf.data.byte = priv->pm_mode; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci ret = at76_set_mib(priv, &priv->mib_buf); 8568c2ecf20Sopenharmony_ci if (ret < 0) 8578c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "set_mib (pm_mode) failed: %d\n", 8588c2ecf20Sopenharmony_ci ret); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci return ret; 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic int at76_set_preamble(struct at76_priv *priv, u8 type) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci int ret = 0; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci priv->mib_buf.type = MIB_LOCAL; 8688c2ecf20Sopenharmony_ci priv->mib_buf.size = 1; 8698c2ecf20Sopenharmony_ci priv->mib_buf.index = offsetof(struct mib_local, preamble_type); 8708c2ecf20Sopenharmony_ci priv->mib_buf.data.byte = type; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci ret = at76_set_mib(priv, &priv->mib_buf); 8738c2ecf20Sopenharmony_ci if (ret < 0) 8748c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "set_mib (preamble) failed: %d\n", 8758c2ecf20Sopenharmony_ci ret); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci return ret; 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_cistatic int at76_set_frag(struct at76_priv *priv, u16 size) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci int ret = 0; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci priv->mib_buf.type = MIB_MAC; 8858c2ecf20Sopenharmony_ci priv->mib_buf.size = 2; 8868c2ecf20Sopenharmony_ci priv->mib_buf.index = offsetof(struct mib_mac, frag_threshold); 8878c2ecf20Sopenharmony_ci priv->mib_buf.data.word = cpu_to_le16(size); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci ret = at76_set_mib(priv, &priv->mib_buf); 8908c2ecf20Sopenharmony_ci if (ret < 0) 8918c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 8928c2ecf20Sopenharmony_ci "set_mib (frag threshold) failed: %d\n", ret); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci return ret; 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic int at76_set_rts(struct at76_priv *priv, u16 size) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci int ret = 0; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci priv->mib_buf.type = MIB_MAC; 9028c2ecf20Sopenharmony_ci priv->mib_buf.size = 2; 9038c2ecf20Sopenharmony_ci priv->mib_buf.index = offsetof(struct mib_mac, rts_threshold); 9048c2ecf20Sopenharmony_ci priv->mib_buf.data.word = cpu_to_le16(size); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci ret = at76_set_mib(priv, &priv->mib_buf); 9078c2ecf20Sopenharmony_ci if (ret < 0) 9088c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "set_mib (rts) failed: %d\n", ret); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci return ret; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic int at76_set_autorate_fallback(struct at76_priv *priv, int onoff) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci int ret = 0; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci priv->mib_buf.type = MIB_LOCAL; 9188c2ecf20Sopenharmony_ci priv->mib_buf.size = 1; 9198c2ecf20Sopenharmony_ci priv->mib_buf.index = offsetof(struct mib_local, txautorate_fallback); 9208c2ecf20Sopenharmony_ci priv->mib_buf.data.byte = onoff; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci ret = at76_set_mib(priv, &priv->mib_buf); 9238c2ecf20Sopenharmony_ci if (ret < 0) 9248c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 9258c2ecf20Sopenharmony_ci "set_mib (autorate fallback) failed: %d\n", ret); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci return ret; 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic void at76_dump_mib_mac_addr(struct at76_priv *priv) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci int i; 9338c2ecf20Sopenharmony_ci int ret; 9348c2ecf20Sopenharmony_ci struct mib_mac_addr *m = kmalloc(sizeof(struct mib_mac_addr), 9358c2ecf20Sopenharmony_ci GFP_KERNEL); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (!m) 9388c2ecf20Sopenharmony_ci return; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci ret = at76_get_mib(priv->udev, MIB_MAC_ADDR, m, 9418c2ecf20Sopenharmony_ci sizeof(struct mib_mac_addr)); 9428c2ecf20Sopenharmony_ci if (ret < 0) { 9438c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 9448c2ecf20Sopenharmony_ci "at76_get_mib (MAC_ADDR) failed: %d\n", ret); 9458c2ecf20Sopenharmony_ci goto exit; 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %pM res 0x%x 0x%x", 9498c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy), 9508c2ecf20Sopenharmony_ci m->mac_addr, m->res[0], m->res[1]); 9518c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(m->group_addr); i++) 9528c2ecf20Sopenharmony_ci at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %pM, " 9538c2ecf20Sopenharmony_ci "status %d", wiphy_name(priv->hw->wiphy), i, 9548c2ecf20Sopenharmony_ci m->group_addr[i], m->group_addr_status[i]); 9558c2ecf20Sopenharmony_ciexit: 9568c2ecf20Sopenharmony_ci kfree(m); 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic void at76_dump_mib_mac_wep(struct at76_priv *priv) 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci int i; 9628c2ecf20Sopenharmony_ci int ret; 9638c2ecf20Sopenharmony_ci int key_len; 9648c2ecf20Sopenharmony_ci struct mib_mac_wep *m = kmalloc(sizeof(struct mib_mac_wep), GFP_KERNEL); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci if (!m) 9678c2ecf20Sopenharmony_ci return; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci ret = at76_get_mib(priv->udev, MIB_MAC_WEP, m, 9708c2ecf20Sopenharmony_ci sizeof(struct mib_mac_wep)); 9718c2ecf20Sopenharmony_ci if (ret < 0) { 9728c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 9738c2ecf20Sopenharmony_ci "at76_get_mib (MAC_WEP) failed: %d\n", ret); 9748c2ecf20Sopenharmony_ci goto exit; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: priv_invoked %u def_key_id %u " 9788c2ecf20Sopenharmony_ci "key_len %u excl_unencr %u wep_icv_err %u wep_excluded %u " 9798c2ecf20Sopenharmony_ci "encr_level %u key %d", wiphy_name(priv->hw->wiphy), 9808c2ecf20Sopenharmony_ci m->privacy_invoked, m->wep_default_key_id, 9818c2ecf20Sopenharmony_ci m->wep_key_mapping_len, m->exclude_unencrypted, 9828c2ecf20Sopenharmony_ci le32_to_cpu(m->wep_icv_error_count), 9838c2ecf20Sopenharmony_ci le32_to_cpu(m->wep_excluded_count), m->encryption_level, 9848c2ecf20Sopenharmony_ci m->wep_default_key_id); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci key_len = (m->encryption_level == 1) ? 9878c2ecf20Sopenharmony_ci WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci for (i = 0; i < WEP_KEYS; i++) 9908c2ecf20Sopenharmony_ci at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: key %d: %*phD", 9918c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy), i, 9928c2ecf20Sopenharmony_ci key_len, m->wep_default_keyvalue[i]); 9938c2ecf20Sopenharmony_ciexit: 9948c2ecf20Sopenharmony_ci kfree(m); 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic void at76_dump_mib_mac_mgmt(struct at76_priv *priv) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci int ret; 10008c2ecf20Sopenharmony_ci struct mib_mac_mgmt *m = kmalloc(sizeof(struct mib_mac_mgmt), 10018c2ecf20Sopenharmony_ci GFP_KERNEL); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (!m) 10048c2ecf20Sopenharmony_ci return; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, m, 10078c2ecf20Sopenharmony_ci sizeof(struct mib_mac_mgmt)); 10088c2ecf20Sopenharmony_ci if (ret < 0) { 10098c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 10108c2ecf20Sopenharmony_ci "at76_get_mib (MAC_MGMT) failed: %d\n", ret); 10118c2ecf20Sopenharmony_ci goto exit; 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci at76_dbg(DBG_MIB, "%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration " 10158c2ecf20Sopenharmony_ci "%d medium_occupancy_limit %d station_id 0x%x ATIM_window %d " 10168c2ecf20Sopenharmony_ci "CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d " 10178c2ecf20Sopenharmony_ci "current_bssid %pM current_essid %*phD current_bss_type %d " 10188c2ecf20Sopenharmony_ci "pm_mode %d ibss_change %d res %d " 10198c2ecf20Sopenharmony_ci "multi_domain_capability_implemented %d " 10208c2ecf20Sopenharmony_ci "international_roaming %d country_string %.3s", 10218c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy), le16_to_cpu(m->beacon_period), 10228c2ecf20Sopenharmony_ci le16_to_cpu(m->CFP_max_duration), 10238c2ecf20Sopenharmony_ci le16_to_cpu(m->medium_occupancy_limit), 10248c2ecf20Sopenharmony_ci le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window), 10258c2ecf20Sopenharmony_ci m->CFP_mode, m->privacy_option_implemented, m->DTIM_period, 10268c2ecf20Sopenharmony_ci m->CFP_period, m->current_bssid, 10278c2ecf20Sopenharmony_ci IW_ESSID_MAX_SIZE, m->current_essid, 10288c2ecf20Sopenharmony_ci m->current_bss_type, m->power_mgmt_mode, m->ibss_change, 10298c2ecf20Sopenharmony_ci m->res, m->multi_domain_capability_implemented, 10308c2ecf20Sopenharmony_ci m->multi_domain_capability_enabled, m->country_string); 10318c2ecf20Sopenharmony_ciexit: 10328c2ecf20Sopenharmony_ci kfree(m); 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic void at76_dump_mib_mac(struct at76_priv *priv) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci int ret; 10388c2ecf20Sopenharmony_ci struct mib_mac *m = kmalloc(sizeof(struct mib_mac), GFP_KERNEL); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (!m) 10418c2ecf20Sopenharmony_ci return; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci ret = at76_get_mib(priv->udev, MIB_MAC, m, sizeof(struct mib_mac)); 10448c2ecf20Sopenharmony_ci if (ret < 0) { 10458c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 10468c2ecf20Sopenharmony_ci "at76_get_mib (MAC) failed: %d\n", ret); 10478c2ecf20Sopenharmony_ci goto exit; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci at76_dbg(DBG_MIB, "%s: MIB MAC: max_tx_msdu_lifetime %d " 10518c2ecf20Sopenharmony_ci "max_rx_lifetime %d frag_threshold %d rts_threshold %d " 10528c2ecf20Sopenharmony_ci "cwmin %d cwmax %d short_retry_time %d long_retry_time %d " 10538c2ecf20Sopenharmony_ci "scan_type %d scan_channel %d probe_delay %u " 10548c2ecf20Sopenharmony_ci "min_channel_time %d max_channel_time %d listen_int %d " 10558c2ecf20Sopenharmony_ci "desired_ssid %*phD desired_bssid %pM desired_bsstype %d", 10568c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy), 10578c2ecf20Sopenharmony_ci le32_to_cpu(m->max_tx_msdu_lifetime), 10588c2ecf20Sopenharmony_ci le32_to_cpu(m->max_rx_lifetime), 10598c2ecf20Sopenharmony_ci le16_to_cpu(m->frag_threshold), le16_to_cpu(m->rts_threshold), 10608c2ecf20Sopenharmony_ci le16_to_cpu(m->cwmin), le16_to_cpu(m->cwmax), 10618c2ecf20Sopenharmony_ci m->short_retry_time, m->long_retry_time, m->scan_type, 10628c2ecf20Sopenharmony_ci m->scan_channel, le16_to_cpu(m->probe_delay), 10638c2ecf20Sopenharmony_ci le16_to_cpu(m->min_channel_time), 10648c2ecf20Sopenharmony_ci le16_to_cpu(m->max_channel_time), 10658c2ecf20Sopenharmony_ci le16_to_cpu(m->listen_interval), 10668c2ecf20Sopenharmony_ci IW_ESSID_MAX_SIZE, m->desired_ssid, 10678c2ecf20Sopenharmony_ci m->desired_bssid, m->desired_bsstype); 10688c2ecf20Sopenharmony_ciexit: 10698c2ecf20Sopenharmony_ci kfree(m); 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic void at76_dump_mib_phy(struct at76_priv *priv) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci int ret; 10758c2ecf20Sopenharmony_ci struct mib_phy *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci if (!m) 10788c2ecf20Sopenharmony_ci return; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci ret = at76_get_mib(priv->udev, MIB_PHY, m, sizeof(struct mib_phy)); 10818c2ecf20Sopenharmony_ci if (ret < 0) { 10828c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 10838c2ecf20Sopenharmony_ci "at76_get_mib (PHY) failed: %d\n", ret); 10848c2ecf20Sopenharmony_ci goto exit; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci at76_dbg(DBG_MIB, "%s: MIB PHY: ed_threshold %d slot_time %d " 10888c2ecf20Sopenharmony_ci "sifs_time %d preamble_length %d plcp_header_length %d " 10898c2ecf20Sopenharmony_ci "mpdu_max_length %d cca_mode_supported %d operation_rate_set " 10908c2ecf20Sopenharmony_ci "0x%x 0x%x 0x%x 0x%x channel_id %d current_cca_mode %d " 10918c2ecf20Sopenharmony_ci "phy_type %d current_reg_domain %d", 10928c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy), le32_to_cpu(m->ed_threshold), 10938c2ecf20Sopenharmony_ci le16_to_cpu(m->slot_time), le16_to_cpu(m->sifs_time), 10948c2ecf20Sopenharmony_ci le16_to_cpu(m->preamble_length), 10958c2ecf20Sopenharmony_ci le16_to_cpu(m->plcp_header_length), 10968c2ecf20Sopenharmony_ci le16_to_cpu(m->mpdu_max_length), 10978c2ecf20Sopenharmony_ci le16_to_cpu(m->cca_mode_supported), m->operation_rate_set[0], 10988c2ecf20Sopenharmony_ci m->operation_rate_set[1], m->operation_rate_set[2], 10998c2ecf20Sopenharmony_ci m->operation_rate_set[3], m->channel_id, m->current_cca_mode, 11008c2ecf20Sopenharmony_ci m->phy_type, m->current_reg_domain); 11018c2ecf20Sopenharmony_ciexit: 11028c2ecf20Sopenharmony_ci kfree(m); 11038c2ecf20Sopenharmony_ci} 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_cistatic void at76_dump_mib_local(struct at76_priv *priv) 11068c2ecf20Sopenharmony_ci{ 11078c2ecf20Sopenharmony_ci int ret; 11088c2ecf20Sopenharmony_ci struct mib_local *m = kmalloc(sizeof(*m), GFP_KERNEL); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci if (!m) 11118c2ecf20Sopenharmony_ci return; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(*m)); 11148c2ecf20Sopenharmony_ci if (ret < 0) { 11158c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 11168c2ecf20Sopenharmony_ci "at76_get_mib (LOCAL) failed: %d\n", ret); 11178c2ecf20Sopenharmony_ci goto exit; 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci at76_dbg(DBG_MIB, "%s: MIB LOCAL: beacon_enable %d " 11218c2ecf20Sopenharmony_ci "txautorate_fallback %d ssid_size %d promiscuous_mode %d " 11228c2ecf20Sopenharmony_ci "preamble_type %d", wiphy_name(priv->hw->wiphy), 11238c2ecf20Sopenharmony_ci m->beacon_enable, 11248c2ecf20Sopenharmony_ci m->txautorate_fallback, m->ssid_size, m->promiscuous_mode, 11258c2ecf20Sopenharmony_ci m->preamble_type); 11268c2ecf20Sopenharmony_ciexit: 11278c2ecf20Sopenharmony_ci kfree(m); 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic void at76_dump_mib_mdomain(struct at76_priv *priv) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci int ret; 11338c2ecf20Sopenharmony_ci struct mib_mdomain *m = kmalloc(sizeof(struct mib_mdomain), GFP_KERNEL); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (!m) 11368c2ecf20Sopenharmony_ci return; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci ret = at76_get_mib(priv->udev, MIB_MDOMAIN, m, 11398c2ecf20Sopenharmony_ci sizeof(struct mib_mdomain)); 11408c2ecf20Sopenharmony_ci if (ret < 0) { 11418c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 11428c2ecf20Sopenharmony_ci "at76_get_mib (MDOMAIN) failed: %d\n", ret); 11438c2ecf20Sopenharmony_ci goto exit; 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %*phD", 11478c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy), 11488c2ecf20Sopenharmony_ci (int)sizeof(m->channel_list), m->channel_list); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: tx_powerlevel %*phD", 11518c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy), 11528c2ecf20Sopenharmony_ci (int)sizeof(m->tx_powerlevel), m->tx_powerlevel); 11538c2ecf20Sopenharmony_ciexit: 11548c2ecf20Sopenharmony_ci kfree(m); 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci/* Enable monitor mode */ 11588c2ecf20Sopenharmony_cistatic int at76_start_monitor(struct at76_priv *priv) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci struct at76_req_scan scan; 11618c2ecf20Sopenharmony_ci int ret; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci memset(&scan, 0, sizeof(struct at76_req_scan)); 11648c2ecf20Sopenharmony_ci eth_broadcast_addr(scan.bssid); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci scan.channel = priv->channel; 11678c2ecf20Sopenharmony_ci scan.scan_type = SCAN_TYPE_PASSIVE; 11688c2ecf20Sopenharmony_ci scan.international_scan = 0; 11698c2ecf20Sopenharmony_ci scan.min_channel_time = cpu_to_le16(priv->scan_min_time); 11708c2ecf20Sopenharmony_ci scan.max_channel_time = cpu_to_le16(priv->scan_max_time); 11718c2ecf20Sopenharmony_ci scan.probe_delay = cpu_to_le16(0); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); 11748c2ecf20Sopenharmony_ci if (ret >= 0) 11758c2ecf20Sopenharmony_ci ret = at76_get_cmd_status(priv->udev, CMD_SCAN); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci return ret; 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci/* Calculate padding from txbuf->wlength (which excludes the USB TX header), 11818c2ecf20Sopenharmony_ci likely to compensate a flaw in the AT76C503A USB part ... */ 11828c2ecf20Sopenharmony_cistatic inline int at76_calc_padding(int wlen) 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci /* add the USB TX header */ 11858c2ecf20Sopenharmony_ci wlen += AT76_TX_HDRLEN; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci wlen = wlen % 64; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (wlen < 50) 11908c2ecf20Sopenharmony_ci return 50 - wlen; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci if (wlen >= 61) 11938c2ecf20Sopenharmony_ci return 64 + 50 - wlen; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci return 0; 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic void at76_rx_callback(struct urb *urb) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci struct at76_priv *priv = urb->context; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci tasklet_schedule(&priv->rx_tasklet); 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_cistatic int at76_submit_rx_urb(struct at76_priv *priv) 12068c2ecf20Sopenharmony_ci{ 12078c2ecf20Sopenharmony_ci int ret; 12088c2ecf20Sopenharmony_ci int size; 12098c2ecf20Sopenharmony_ci struct sk_buff *skb = priv->rx_skb; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (!priv->rx_urb) { 12128c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "%s: priv->rx_urb is NULL\n", 12138c2ecf20Sopenharmony_ci __func__); 12148c2ecf20Sopenharmony_ci return -EFAULT; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (!skb) { 12188c2ecf20Sopenharmony_ci skb = dev_alloc_skb(sizeof(struct at76_rx_buffer)); 12198c2ecf20Sopenharmony_ci if (!skb) { 12208c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 12218c2ecf20Sopenharmony_ci "cannot allocate rx skbuff\n"); 12228c2ecf20Sopenharmony_ci ret = -ENOMEM; 12238c2ecf20Sopenharmony_ci goto exit; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci priv->rx_skb = skb; 12268c2ecf20Sopenharmony_ci } else { 12278c2ecf20Sopenharmony_ci skb_push(skb, skb_headroom(skb)); 12288c2ecf20Sopenharmony_ci skb_trim(skb, 0); 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci size = skb_tailroom(skb); 12328c2ecf20Sopenharmony_ci usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe, 12338c2ecf20Sopenharmony_ci skb_put(skb, size), size, at76_rx_callback, priv); 12348c2ecf20Sopenharmony_ci ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC); 12358c2ecf20Sopenharmony_ci if (ret < 0) { 12368c2ecf20Sopenharmony_ci if (ret == -ENODEV) 12378c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, 12388c2ecf20Sopenharmony_ci "usb_submit_urb returned -ENODEV"); 12398c2ecf20Sopenharmony_ci else 12408c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 12418c2ecf20Sopenharmony_ci "rx, usb_submit_urb failed: %d\n", ret); 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ciexit: 12458c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENODEV) 12468c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 12478c2ecf20Sopenharmony_ci "cannot submit rx urb - please unload the driver and/or power cycle the device\n"); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci return ret; 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci/* Download external firmware */ 12538c2ecf20Sopenharmony_cistatic int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci int ret; 12568c2ecf20Sopenharmony_ci int op_mode; 12578c2ecf20Sopenharmony_ci int blockno = 0; 12588c2ecf20Sopenharmony_ci int bsize; 12598c2ecf20Sopenharmony_ci u8 *block; 12608c2ecf20Sopenharmony_ci u8 *buf = fwe->extfw; 12618c2ecf20Sopenharmony_ci int size = fwe->extfw_size; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci if (!buf || !size) 12648c2ecf20Sopenharmony_ci return -ENOENT; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci op_mode = at76_get_op_mode(udev); 12678c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { 12708c2ecf20Sopenharmony_ci dev_err(&udev->dev, "unexpected opmode %d\n", op_mode); 12718c2ecf20Sopenharmony_ci return -EINVAL; 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); 12758c2ecf20Sopenharmony_ci if (!block) 12768c2ecf20Sopenharmony_ci return -ENOMEM; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, "downloading external firmware"); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci /* for fw >= 0.100, the device needs an extra empty block */ 12818c2ecf20Sopenharmony_ci do { 12828c2ecf20Sopenharmony_ci bsize = min_t(int, size, FW_BLOCK_SIZE); 12838c2ecf20Sopenharmony_ci memcpy(block, buf, bsize); 12848c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, 12858c2ecf20Sopenharmony_ci "ext fw, size left = %5d, bsize = %4d, blockno = %2d", 12868c2ecf20Sopenharmony_ci size, bsize, blockno); 12878c2ecf20Sopenharmony_ci ret = at76_load_ext_fw_block(udev, blockno, block, bsize); 12888c2ecf20Sopenharmony_ci if (ret != bsize) { 12898c2ecf20Sopenharmony_ci dev_err(&udev->dev, 12908c2ecf20Sopenharmony_ci "loading %dth firmware block failed: %d\n", 12918c2ecf20Sopenharmony_ci blockno, ret); 12928c2ecf20Sopenharmony_ci ret = -EIO; 12938c2ecf20Sopenharmony_ci goto exit; 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci buf += bsize; 12968c2ecf20Sopenharmony_ci size -= bsize; 12978c2ecf20Sopenharmony_ci blockno++; 12988c2ecf20Sopenharmony_ci } while (bsize > 0); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci if (at76_is_505a(fwe->board_type)) { 13018c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, "200 ms delay for 505a"); 13028c2ecf20Sopenharmony_ci schedule_timeout_interruptible(HZ / 5 + 1); 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ciexit: 13068c2ecf20Sopenharmony_ci kfree(block); 13078c2ecf20Sopenharmony_ci if (ret < 0) 13088c2ecf20Sopenharmony_ci dev_err(&udev->dev, 13098c2ecf20Sopenharmony_ci "downloading external firmware failed: %d\n", ret); 13108c2ecf20Sopenharmony_ci return ret; 13118c2ecf20Sopenharmony_ci} 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci/* Download internal firmware */ 13148c2ecf20Sopenharmony_cistatic int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe) 13158c2ecf20Sopenharmony_ci{ 13168c2ecf20Sopenharmony_ci int ret; 13178c2ecf20Sopenharmony_ci int need_remap = !at76_is_505a(fwe->board_type); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size, 13208c2ecf20Sopenharmony_ci need_remap ? 0 : 2 * HZ); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (ret < 0) { 13238c2ecf20Sopenharmony_ci dev_err(&udev->dev, 13248c2ecf20Sopenharmony_ci "downloading internal fw failed with %d\n", ret); 13258c2ecf20Sopenharmony_ci goto exit; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, "sending REMAP"); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci /* no REMAP for 505A (see SF driver) */ 13318c2ecf20Sopenharmony_ci if (need_remap) { 13328c2ecf20Sopenharmony_ci ret = at76_remap(udev); 13338c2ecf20Sopenharmony_ci if (ret < 0) { 13348c2ecf20Sopenharmony_ci dev_err(&udev->dev, 13358c2ecf20Sopenharmony_ci "sending REMAP failed with %d\n", ret); 13368c2ecf20Sopenharmony_ci goto exit; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds"); 13418c2ecf20Sopenharmony_ci schedule_timeout_interruptible(2 * HZ + 1); 13428c2ecf20Sopenharmony_ci usb_reset_device(udev); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ciexit: 13458c2ecf20Sopenharmony_ci return ret; 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic int at76_startup_device(struct at76_priv *priv) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci struct at76_card_config *ccfg = &priv->card_config; 13518c2ecf20Sopenharmony_ci int ret; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci at76_dbg(DBG_PARAMS, 13548c2ecf20Sopenharmony_ci "%s param: ssid %.*s (%*phD) mode %s ch %d wep %s key %d " 13558c2ecf20Sopenharmony_ci "keylen %d", wiphy_name(priv->hw->wiphy), priv->essid_size, 13568c2ecf20Sopenharmony_ci priv->essid, IW_ESSID_MAX_SIZE, priv->essid, 13578c2ecf20Sopenharmony_ci priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra", 13588c2ecf20Sopenharmony_ci priv->channel, priv->wep_enabled ? "enabled" : "disabled", 13598c2ecf20Sopenharmony_ci priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]); 13608c2ecf20Sopenharmony_ci at76_dbg(DBG_PARAMS, 13618c2ecf20Sopenharmony_ci "%s param: preamble %s rts %d retry %d frag %d " 13628c2ecf20Sopenharmony_ci "txrate %s auth_mode %d", wiphy_name(priv->hw->wiphy), 13638c2ecf20Sopenharmony_ci preambles[priv->preamble_type], priv->rts_threshold, 13648c2ecf20Sopenharmony_ci priv->short_retry_limit, priv->frag_threshold, 13658c2ecf20Sopenharmony_ci priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate == 13668c2ecf20Sopenharmony_ci TX_RATE_2MBIT ? "2MBit" : priv->txrate == 13678c2ecf20Sopenharmony_ci TX_RATE_5_5MBIT ? "5.5MBit" : priv->txrate == 13688c2ecf20Sopenharmony_ci TX_RATE_11MBIT ? "11MBit" : priv->txrate == 13698c2ecf20Sopenharmony_ci TX_RATE_AUTO ? "auto" : "<invalid>", priv->auth_mode); 13708c2ecf20Sopenharmony_ci at76_dbg(DBG_PARAMS, 13718c2ecf20Sopenharmony_ci "%s param: pm_mode %d pm_period %d auth_mode %s " 13728c2ecf20Sopenharmony_ci "scan_times %d %d scan_mode %s", 13738c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy), priv->pm_mode, priv->pm_period, 13748c2ecf20Sopenharmony_ci priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret", 13758c2ecf20Sopenharmony_ci priv->scan_min_time, priv->scan_max_time, 13768c2ecf20Sopenharmony_ci priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive"); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci memset(ccfg, 0, sizeof(struct at76_card_config)); 13798c2ecf20Sopenharmony_ci ccfg->promiscuous_mode = 0; 13808c2ecf20Sopenharmony_ci ccfg->short_retry_limit = priv->short_retry_limit; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci if (priv->wep_enabled) { 13838c2ecf20Sopenharmony_ci if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) 13848c2ecf20Sopenharmony_ci ccfg->encryption_type = 2; 13858c2ecf20Sopenharmony_ci else 13868c2ecf20Sopenharmony_ci ccfg->encryption_type = 1; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci /* jal: always exclude unencrypted if WEP is active */ 13898c2ecf20Sopenharmony_ci ccfg->exclude_unencrypted = 1; 13908c2ecf20Sopenharmony_ci } else { 13918c2ecf20Sopenharmony_ci ccfg->exclude_unencrypted = 0; 13928c2ecf20Sopenharmony_ci ccfg->encryption_type = 0; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci ccfg->rts_threshold = cpu_to_le16(priv->rts_threshold); 13968c2ecf20Sopenharmony_ci ccfg->fragmentation_threshold = cpu_to_le16(priv->frag_threshold); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci memcpy(ccfg->basic_rate_set, hw_rates, 4); 13998c2ecf20Sopenharmony_ci /* jal: really needed, we do a set_mib for autorate later ??? */ 14008c2ecf20Sopenharmony_ci ccfg->auto_rate_fallback = (priv->txrate == TX_RATE_AUTO ? 1 : 0); 14018c2ecf20Sopenharmony_ci ccfg->channel = priv->channel; 14028c2ecf20Sopenharmony_ci ccfg->privacy_invoked = priv->wep_enabled; 14038c2ecf20Sopenharmony_ci memcpy(ccfg->current_ssid, priv->essid, IW_ESSID_MAX_SIZE); 14048c2ecf20Sopenharmony_ci ccfg->ssid_len = priv->essid_size; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci ccfg->wep_default_key_id = priv->wep_key_id; 14078c2ecf20Sopenharmony_ci memcpy(ccfg->wep_default_key_value, priv->wep_keys, 14088c2ecf20Sopenharmony_ci sizeof(priv->wep_keys)); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci ccfg->short_preamble = priv->preamble_type; 14118c2ecf20Sopenharmony_ci ccfg->beacon_period = cpu_to_le16(priv->beacon_period); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci ret = at76_set_card_command(priv->udev, CMD_STARTUP, &priv->card_config, 14148c2ecf20Sopenharmony_ci sizeof(struct at76_card_config)); 14158c2ecf20Sopenharmony_ci if (ret < 0) { 14168c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", 14178c2ecf20Sopenharmony_ci ret); 14188c2ecf20Sopenharmony_ci return ret; 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci at76_wait_completion(priv, CMD_STARTUP); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci /* remove BSSID from previous run */ 14248c2ecf20Sopenharmony_ci eth_zero_addr(priv->bssid); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci priv->scanning = false; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci if (at76_set_radio(priv, 1) == 1) 14298c2ecf20Sopenharmony_ci at76_wait_completion(priv, CMD_RADIO_ON); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci ret = at76_set_preamble(priv, priv->preamble_type); 14328c2ecf20Sopenharmony_ci if (ret < 0) 14338c2ecf20Sopenharmony_ci return ret; 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci ret = at76_set_frag(priv, priv->frag_threshold); 14368c2ecf20Sopenharmony_ci if (ret < 0) 14378c2ecf20Sopenharmony_ci return ret; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci ret = at76_set_rts(priv, priv->rts_threshold); 14408c2ecf20Sopenharmony_ci if (ret < 0) 14418c2ecf20Sopenharmony_ci return ret; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci ret = at76_set_autorate_fallback(priv, 14448c2ecf20Sopenharmony_ci priv->txrate == TX_RATE_AUTO ? 1 : 0); 14458c2ecf20Sopenharmony_ci if (ret < 0) 14468c2ecf20Sopenharmony_ci return ret; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci ret = at76_set_pm_mode(priv); 14498c2ecf20Sopenharmony_ci if (ret < 0) 14508c2ecf20Sopenharmony_ci return ret; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci if (at76_debug & DBG_MIB) { 14538c2ecf20Sopenharmony_ci at76_dump_mib_mac(priv); 14548c2ecf20Sopenharmony_ci at76_dump_mib_mac_addr(priv); 14558c2ecf20Sopenharmony_ci at76_dump_mib_mac_mgmt(priv); 14568c2ecf20Sopenharmony_ci at76_dump_mib_mac_wep(priv); 14578c2ecf20Sopenharmony_ci at76_dump_mib_mdomain(priv); 14588c2ecf20Sopenharmony_ci at76_dump_mib_phy(priv); 14598c2ecf20Sopenharmony_ci at76_dump_mib_local(priv); 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci return 0; 14638c2ecf20Sopenharmony_ci} 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci/* Enable or disable promiscuous mode */ 14668c2ecf20Sopenharmony_cistatic void at76_work_set_promisc(struct work_struct *work) 14678c2ecf20Sopenharmony_ci{ 14688c2ecf20Sopenharmony_ci struct at76_priv *priv = container_of(work, struct at76_priv, 14698c2ecf20Sopenharmony_ci work_set_promisc); 14708c2ecf20Sopenharmony_ci int ret = 0; 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci if (priv->device_unplugged) 14738c2ecf20Sopenharmony_ci return; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci mutex_lock(&priv->mtx); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci priv->mib_buf.type = MIB_LOCAL; 14788c2ecf20Sopenharmony_ci priv->mib_buf.size = 1; 14798c2ecf20Sopenharmony_ci priv->mib_buf.index = offsetof(struct mib_local, promiscuous_mode); 14808c2ecf20Sopenharmony_ci priv->mib_buf.data.byte = priv->promisc ? 1 : 0; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci ret = at76_set_mib(priv, &priv->mib_buf); 14838c2ecf20Sopenharmony_ci if (ret < 0) 14848c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 14858c2ecf20Sopenharmony_ci "set_mib (promiscuous_mode) failed: %d\n", ret); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci/* Submit Rx urb back to the device */ 14918c2ecf20Sopenharmony_cistatic void at76_work_submit_rx(struct work_struct *work) 14928c2ecf20Sopenharmony_ci{ 14938c2ecf20Sopenharmony_ci struct at76_priv *priv = container_of(work, struct at76_priv, 14948c2ecf20Sopenharmony_ci work_submit_rx); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci mutex_lock(&priv->mtx); 14978c2ecf20Sopenharmony_ci at76_submit_rx_urb(priv); 14988c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 14998c2ecf20Sopenharmony_ci} 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci/* This is a workaround to make scan working: 15028c2ecf20Sopenharmony_ci * currently mac80211 does not process frames with no frequency 15038c2ecf20Sopenharmony_ci * information. 15048c2ecf20Sopenharmony_ci * However during scan the HW performs a sweep by itself, and we 15058c2ecf20Sopenharmony_ci * are unable to know where the radio is actually tuned. 15068c2ecf20Sopenharmony_ci * This function tries to do its best to guess this information.. 15078c2ecf20Sopenharmony_ci * During scan, If the current frame is a beacon or a probe response, 15088c2ecf20Sopenharmony_ci * the channel information is extracted from it. 15098c2ecf20Sopenharmony_ci * When not scanning, for other frames, or if it happens that for 15108c2ecf20Sopenharmony_ci * whatever reason we fail to parse beacons and probe responses, this 15118c2ecf20Sopenharmony_ci * function returns the priv->channel information, that should be correct 15128c2ecf20Sopenharmony_ci * at least when we are not scanning. 15138c2ecf20Sopenharmony_ci */ 15148c2ecf20Sopenharmony_cistatic inline int at76_guess_freq(struct at76_priv *priv) 15158c2ecf20Sopenharmony_ci{ 15168c2ecf20Sopenharmony_ci size_t el_off; 15178c2ecf20Sopenharmony_ci const u8 *el; 15188c2ecf20Sopenharmony_ci int channel = priv->channel; 15198c2ecf20Sopenharmony_ci int len = priv->rx_skb->len; 15208c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (void *)priv->rx_skb->data; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci if (!priv->scanning) 15238c2ecf20Sopenharmony_ci goto exit; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci if (len < 24) 15268c2ecf20Sopenharmony_ci goto exit; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci if (ieee80211_is_probe_resp(hdr->frame_control)) { 15298c2ecf20Sopenharmony_ci el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); 15308c2ecf20Sopenharmony_ci el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable; 15318c2ecf20Sopenharmony_ci } else if (ieee80211_is_beacon(hdr->frame_control)) { 15328c2ecf20Sopenharmony_ci el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable); 15338c2ecf20Sopenharmony_ci el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable; 15348c2ecf20Sopenharmony_ci } else { 15358c2ecf20Sopenharmony_ci goto exit; 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci len -= el_off; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len); 15408c2ecf20Sopenharmony_ci if (el && el[1] > 0) 15418c2ecf20Sopenharmony_ci channel = el[2]; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ciexit: 15448c2ecf20Sopenharmony_ci return ieee80211_channel_to_frequency(channel, NL80211_BAND_2GHZ); 15458c2ecf20Sopenharmony_ci} 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_cistatic void at76_rx_tasklet(struct tasklet_struct *t) 15488c2ecf20Sopenharmony_ci{ 15498c2ecf20Sopenharmony_ci struct at76_priv *priv = from_tasklet(priv, t, rx_tasklet); 15508c2ecf20Sopenharmony_ci struct urb *urb = priv->rx_urb; 15518c2ecf20Sopenharmony_ci struct at76_rx_buffer *buf; 15528c2ecf20Sopenharmony_ci struct ieee80211_rx_status rx_status = { 0 }; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci if (priv->device_unplugged) { 15558c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, "device unplugged"); 15568c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, "urb status %d", urb->status); 15578c2ecf20Sopenharmony_ci return; 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci if (!priv->rx_skb || !priv->rx_skb->data) 15618c2ecf20Sopenharmony_ci return; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci buf = (struct at76_rx_buffer *)priv->rx_skb->data; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci if (urb->status != 0) { 15668c2ecf20Sopenharmony_ci if (urb->status != -ENOENT && urb->status != -ECONNRESET) 15678c2ecf20Sopenharmony_ci at76_dbg(DBG_URB, 15688c2ecf20Sopenharmony_ci "%s %s: - nonzero Rx bulk status received: %d", 15698c2ecf20Sopenharmony_ci __func__, wiphy_name(priv->hw->wiphy), 15708c2ecf20Sopenharmony_ci urb->status); 15718c2ecf20Sopenharmony_ci return; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci at76_dbg(DBG_RX_ATMEL_HDR, 15758c2ecf20Sopenharmony_ci "%s: rx frame: rate %d rssi %d noise %d link %d", 15768c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy), buf->rx_rate, buf->rssi, 15778c2ecf20Sopenharmony_ci buf->noise_level, buf->link_quality); 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci skb_pull(priv->rx_skb, AT76_RX_HDRLEN); 15808c2ecf20Sopenharmony_ci skb_trim(priv->rx_skb, le16_to_cpu(buf->wlength)); 15818c2ecf20Sopenharmony_ci at76_dbg_dump(DBG_RX_DATA, priv->rx_skb->data, 15828c2ecf20Sopenharmony_ci priv->rx_skb->len, "RX: len=%d", priv->rx_skb->len); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci rx_status.signal = buf->rssi; 15858c2ecf20Sopenharmony_ci rx_status.flag |= RX_FLAG_DECRYPTED; 15868c2ecf20Sopenharmony_ci rx_status.flag |= RX_FLAG_IV_STRIPPED; 15878c2ecf20Sopenharmony_ci rx_status.band = NL80211_BAND_2GHZ; 15888c2ecf20Sopenharmony_ci rx_status.freq = at76_guess_freq(priv); 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d", 15918c2ecf20Sopenharmony_ci priv->rx_skb->len, priv->rx_skb->data_len); 15928c2ecf20Sopenharmony_ci memcpy(IEEE80211_SKB_RXCB(priv->rx_skb), &rx_status, sizeof(rx_status)); 15938c2ecf20Sopenharmony_ci ieee80211_rx_irqsafe(priv->hw, priv->rx_skb); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci /* Use a new skb for the next receive */ 15968c2ecf20Sopenharmony_ci priv->rx_skb = NULL; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci at76_submit_rx_urb(priv); 15998c2ecf20Sopenharmony_ci} 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci/* Load firmware into kernel memory and parse it */ 16028c2ecf20Sopenharmony_cistatic struct fwentry *at76_load_firmware(struct usb_device *udev, 16038c2ecf20Sopenharmony_ci enum board_type board_type) 16048c2ecf20Sopenharmony_ci{ 16058c2ecf20Sopenharmony_ci int ret; 16068c2ecf20Sopenharmony_ci char *str; 16078c2ecf20Sopenharmony_ci struct at76_fw_header *fwh; 16088c2ecf20Sopenharmony_ci struct fwentry *fwe = &firmwares[board_type]; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci mutex_lock(&fw_mutex); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (fwe->loaded) { 16138c2ecf20Sopenharmony_ci at76_dbg(DBG_FW, "re-using previously loaded fw"); 16148c2ecf20Sopenharmony_ci goto exit; 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname); 16188c2ecf20Sopenharmony_ci ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev); 16198c2ecf20Sopenharmony_ci if (ret < 0) { 16208c2ecf20Sopenharmony_ci dev_err(&udev->dev, "firmware %s not found!\n", 16218c2ecf20Sopenharmony_ci fwe->fwname); 16228c2ecf20Sopenharmony_ci dev_err(&udev->dev, 16238c2ecf20Sopenharmony_ci "you may need to download the firmware from http://developer.berlios.de/projects/at76c503a/\n"); 16248c2ecf20Sopenharmony_ci goto exit; 16258c2ecf20Sopenharmony_ci } 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci at76_dbg(DBG_FW, "got it."); 16288c2ecf20Sopenharmony_ci fwh = (struct at76_fw_header *)(fwe->fw->data); 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci if (fwe->fw->size <= sizeof(*fwh)) { 16318c2ecf20Sopenharmony_ci dev_err(&udev->dev, 16328c2ecf20Sopenharmony_ci "firmware is too short (0x%zx)\n", fwe->fw->size); 16338c2ecf20Sopenharmony_ci goto exit; 16348c2ecf20Sopenharmony_ci } 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci /* CRC currently not checked */ 16378c2ecf20Sopenharmony_ci fwe->board_type = le32_to_cpu(fwh->board_type); 16388c2ecf20Sopenharmony_ci if (fwe->board_type != board_type) { 16398c2ecf20Sopenharmony_ci dev_err(&udev->dev, 16408c2ecf20Sopenharmony_ci "board type mismatch, requested %u, got %u\n", 16418c2ecf20Sopenharmony_ci board_type, fwe->board_type); 16428c2ecf20Sopenharmony_ci goto exit; 16438c2ecf20Sopenharmony_ci } 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci fwe->fw_version.major = fwh->major; 16468c2ecf20Sopenharmony_ci fwe->fw_version.minor = fwh->minor; 16478c2ecf20Sopenharmony_ci fwe->fw_version.patch = fwh->patch; 16488c2ecf20Sopenharmony_ci fwe->fw_version.build = fwh->build; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci str = (char *)fwh + le32_to_cpu(fwh->str_offset); 16518c2ecf20Sopenharmony_ci fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset); 16528c2ecf20Sopenharmony_ci fwe->intfw_size = le32_to_cpu(fwh->int_fw_len); 16538c2ecf20Sopenharmony_ci fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset); 16548c2ecf20Sopenharmony_ci fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len); 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci fwe->loaded = 1; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &udev->dev, 16598c2ecf20Sopenharmony_ci "using firmware %s (version %d.%d.%d-%d)\n", 16608c2ecf20Sopenharmony_ci fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build); 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type, 16638c2ecf20Sopenharmony_ci le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len), 16648c2ecf20Sopenharmony_ci le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len)); 16658c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, "firmware id %s", str); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ciexit: 16688c2ecf20Sopenharmony_ci mutex_unlock(&fw_mutex); 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci if (fwe->loaded) 16718c2ecf20Sopenharmony_ci return fwe; 16728c2ecf20Sopenharmony_ci else 16738c2ecf20Sopenharmony_ci return NULL; 16748c2ecf20Sopenharmony_ci} 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_cistatic int at76_join(struct at76_priv *priv) 16778c2ecf20Sopenharmony_ci{ 16788c2ecf20Sopenharmony_ci struct at76_req_join join; 16798c2ecf20Sopenharmony_ci int ret; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci memset(&join, 0, sizeof(struct at76_req_join)); 16828c2ecf20Sopenharmony_ci memcpy(join.essid, priv->essid, priv->essid_size); 16838c2ecf20Sopenharmony_ci join.essid_size = priv->essid_size; 16848c2ecf20Sopenharmony_ci memcpy(join.bssid, priv->bssid, ETH_ALEN); 16858c2ecf20Sopenharmony_ci join.bss_type = INFRASTRUCTURE_MODE; 16868c2ecf20Sopenharmony_ci join.channel = priv->channel; 16878c2ecf20Sopenharmony_ci join.timeout = cpu_to_le16(2000); 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s: sending CMD_JOIN", __func__); 16908c2ecf20Sopenharmony_ci ret = at76_set_card_command(priv->udev, CMD_JOIN, &join, 16918c2ecf20Sopenharmony_ci sizeof(struct at76_req_join)); 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci if (ret < 0) { 16948c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", 16958c2ecf20Sopenharmony_ci ret); 16968c2ecf20Sopenharmony_ci return 0; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci ret = at76_wait_completion(priv, CMD_JOIN); 17008c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s: CMD_JOIN returned: 0x%02x", __func__, ret); 17018c2ecf20Sopenharmony_ci if (ret != CMD_STATUS_COMPLETE) { 17028c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "at76_wait_completion failed: %d\n", 17038c2ecf20Sopenharmony_ci ret); 17048c2ecf20Sopenharmony_ci return 0; 17058c2ecf20Sopenharmony_ci } 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci at76_set_pm_mode(priv); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci return 0; 17108c2ecf20Sopenharmony_ci} 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_cistatic void at76_work_join_bssid(struct work_struct *work) 17138c2ecf20Sopenharmony_ci{ 17148c2ecf20Sopenharmony_ci struct at76_priv *priv = container_of(work, struct at76_priv, 17158c2ecf20Sopenharmony_ci work_join_bssid); 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci if (priv->device_unplugged) 17188c2ecf20Sopenharmony_ci return; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci mutex_lock(&priv->mtx); 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci if (is_valid_ether_addr(priv->bssid)) 17238c2ecf20Sopenharmony_ci at76_join(priv); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 17268c2ecf20Sopenharmony_ci} 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_cistatic void at76_mac80211_tx_callback(struct urb *urb) 17298c2ecf20Sopenharmony_ci{ 17308c2ecf20Sopenharmony_ci struct at76_priv *priv = urb->context; 17318c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s()", __func__); 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci switch (urb->status) { 17368c2ecf20Sopenharmony_ci case 0: 17378c2ecf20Sopenharmony_ci /* success */ 17388c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 17398c2ecf20Sopenharmony_ci break; 17408c2ecf20Sopenharmony_ci case -ENOENT: 17418c2ecf20Sopenharmony_ci case -ECONNRESET: 17428c2ecf20Sopenharmony_ci /* fail, urb has been unlinked */ 17438c2ecf20Sopenharmony_ci /* FIXME: add error message */ 17448c2ecf20Sopenharmony_ci break; 17458c2ecf20Sopenharmony_ci default: 17468c2ecf20Sopenharmony_ci at76_dbg(DBG_URB, "%s - nonzero tx status received: %d", 17478c2ecf20Sopenharmony_ci __func__, urb->status); 17488c2ecf20Sopenharmony_ci break; 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci memset(&info->status, 0, sizeof(info->status)); 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci priv->tx_skb = NULL; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci ieee80211_wake_queues(priv->hw); 17588c2ecf20Sopenharmony_ci} 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_cistatic void at76_mac80211_tx(struct ieee80211_hw *hw, 17618c2ecf20Sopenharmony_ci struct ieee80211_tx_control *control, 17628c2ecf20Sopenharmony_ci struct sk_buff *skb) 17638c2ecf20Sopenharmony_ci{ 17648c2ecf20Sopenharmony_ci struct at76_priv *priv = hw->priv; 17658c2ecf20Sopenharmony_ci struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; 17668c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 17678c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; 17688c2ecf20Sopenharmony_ci int padding, submit_len, ret; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s()", __func__); 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci if (priv->tx_urb->status == -EINPROGRESS) { 17738c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 17748c2ecf20Sopenharmony_ci "%s called while tx urb is pending\n", __func__); 17758c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 17768c2ecf20Sopenharmony_ci return; 17778c2ecf20Sopenharmony_ci } 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci /* The following code lines are important when the device is going to 17808c2ecf20Sopenharmony_ci * authenticate with a new bssid. The driver must send CMD_JOIN before 17818c2ecf20Sopenharmony_ci * an authentication frame is transmitted. For this to succeed, the 17828c2ecf20Sopenharmony_ci * correct bssid of the AP must be known. As mac80211 does not inform 17838c2ecf20Sopenharmony_ci * drivers about the bssid prior to the authentication process the 17848c2ecf20Sopenharmony_ci * following workaround is necessary. If the TX frame is an 17858c2ecf20Sopenharmony_ci * authentication frame extract the bssid and send the CMD_JOIN. */ 17868c2ecf20Sopenharmony_ci if (mgmt->frame_control & cpu_to_le16(IEEE80211_STYPE_AUTH)) { 17878c2ecf20Sopenharmony_ci if (!ether_addr_equal_64bits(priv->bssid, mgmt->bssid)) { 17888c2ecf20Sopenharmony_ci memcpy(priv->bssid, mgmt->bssid, ETH_ALEN); 17898c2ecf20Sopenharmony_ci ieee80211_queue_work(hw, &priv->work_join_bssid); 17908c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 17918c2ecf20Sopenharmony_ci return; 17928c2ecf20Sopenharmony_ci } 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci ieee80211_stop_queues(hw); 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci at76_ledtrig_tx_activity(); /* tell ledtrigger we send a packet */ 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci WARN_ON(priv->tx_skb != NULL); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci priv->tx_skb = skb; 18028c2ecf20Sopenharmony_ci padding = at76_calc_padding(skb->len); 18038c2ecf20Sopenharmony_ci submit_len = AT76_TX_HDRLEN + skb->len + padding; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci /* setup 'Atmel' header */ 18068c2ecf20Sopenharmony_ci memset(tx_buffer, 0, sizeof(*tx_buffer)); 18078c2ecf20Sopenharmony_ci tx_buffer->padding = padding; 18088c2ecf20Sopenharmony_ci tx_buffer->wlength = cpu_to_le16(skb->len); 18098c2ecf20Sopenharmony_ci tx_buffer->tx_rate = ieee80211_get_tx_rate(hw, info)->hw_value; 18108c2ecf20Sopenharmony_ci memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved)); 18118c2ecf20Sopenharmony_ci memcpy(tx_buffer->packet, skb->data, skb->len); 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr", 18148c2ecf20Sopenharmony_ci wiphy_name(priv->hw->wiphy), le16_to_cpu(tx_buffer->wlength), 18158c2ecf20Sopenharmony_ci tx_buffer->padding, tx_buffer->tx_rate); 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci /* send stuff */ 18188c2ecf20Sopenharmony_ci at76_dbg_dump(DBG_TX_DATA_CONTENT, tx_buffer, submit_len, 18198c2ecf20Sopenharmony_ci "%s(): tx_buffer %d bytes:", __func__, submit_len); 18208c2ecf20Sopenharmony_ci usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer, 18218c2ecf20Sopenharmony_ci submit_len, at76_mac80211_tx_callback, priv); 18228c2ecf20Sopenharmony_ci ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); 18238c2ecf20Sopenharmony_ci if (ret) { 18248c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "error in tx submit urb: %d\n", ret); 18258c2ecf20Sopenharmony_ci if (ret == -EINVAL) 18268c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 18278c2ecf20Sopenharmony_ci "-EINVAL: tx urb %p hcpriv %p complete %p\n", 18288c2ecf20Sopenharmony_ci priv->tx_urb, 18298c2ecf20Sopenharmony_ci priv->tx_urb->hcpriv, priv->tx_urb->complete); 18308c2ecf20Sopenharmony_ci } 18318c2ecf20Sopenharmony_ci} 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_cistatic int at76_mac80211_start(struct ieee80211_hw *hw) 18348c2ecf20Sopenharmony_ci{ 18358c2ecf20Sopenharmony_ci struct at76_priv *priv = hw->priv; 18368c2ecf20Sopenharmony_ci int ret; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s()", __func__); 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci mutex_lock(&priv->mtx); 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci ret = at76_submit_rx_urb(priv); 18438c2ecf20Sopenharmony_ci if (ret < 0) { 18448c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "open: submit_rx_urb failed: %d\n", 18458c2ecf20Sopenharmony_ci ret); 18468c2ecf20Sopenharmony_ci goto error; 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci at76_startup_device(priv); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci at76_start_monitor(priv); 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_cierror: 18548c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci return 0; 18578c2ecf20Sopenharmony_ci} 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_cistatic void at76_mac80211_stop(struct ieee80211_hw *hw) 18608c2ecf20Sopenharmony_ci{ 18618c2ecf20Sopenharmony_ci struct at76_priv *priv = hw->priv; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s()", __func__); 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci cancel_delayed_work(&priv->dwork_hw_scan); 18668c2ecf20Sopenharmony_ci cancel_work_sync(&priv->work_join_bssid); 18678c2ecf20Sopenharmony_ci cancel_work_sync(&priv->work_set_promisc); 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci mutex_lock(&priv->mtx); 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci if (!priv->device_unplugged) { 18728c2ecf20Sopenharmony_ci /* We are called by "ifconfig ethX down", not because the 18738c2ecf20Sopenharmony_ci * device is not available anymore. */ 18748c2ecf20Sopenharmony_ci at76_set_radio(priv, 0); 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci /* We unlink rx_urb because at76_open() re-submits it. 18778c2ecf20Sopenharmony_ci * If unplugged, at76_delete_device() takes care of it. */ 18788c2ecf20Sopenharmony_ci usb_kill_urb(priv->rx_urb); 18798c2ecf20Sopenharmony_ci } 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 18828c2ecf20Sopenharmony_ci} 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_cistatic int at76_add_interface(struct ieee80211_hw *hw, 18858c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 18868c2ecf20Sopenharmony_ci{ 18878c2ecf20Sopenharmony_ci struct at76_priv *priv = hw->priv; 18888c2ecf20Sopenharmony_ci int ret = 0; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s()", __func__); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci mutex_lock(&priv->mtx); 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci switch (vif->type) { 18958c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 18968c2ecf20Sopenharmony_ci priv->iw_mode = IW_MODE_INFRA; 18978c2ecf20Sopenharmony_ci break; 18988c2ecf20Sopenharmony_ci default: 18998c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 19008c2ecf20Sopenharmony_ci goto exit; 19018c2ecf20Sopenharmony_ci } 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ciexit: 19048c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci return ret; 19078c2ecf20Sopenharmony_ci} 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_cistatic void at76_remove_interface(struct ieee80211_hw *hw, 19108c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 19118c2ecf20Sopenharmony_ci{ 19128c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s()", __func__); 19138c2ecf20Sopenharmony_ci} 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_cistatic void at76_dwork_hw_scan(struct work_struct *work) 19168c2ecf20Sopenharmony_ci{ 19178c2ecf20Sopenharmony_ci struct at76_priv *priv = container_of(work, struct at76_priv, 19188c2ecf20Sopenharmony_ci dwork_hw_scan.work); 19198c2ecf20Sopenharmony_ci struct cfg80211_scan_info info = { 19208c2ecf20Sopenharmony_ci .aborted = false, 19218c2ecf20Sopenharmony_ci }; 19228c2ecf20Sopenharmony_ci int ret; 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci if (priv->device_unplugged) 19258c2ecf20Sopenharmony_ci return; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci mutex_lock(&priv->mtx); 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci ret = at76_get_cmd_status(priv->udev, CMD_SCAN); 19308c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s: CMD_SCAN status 0x%02x", __func__, ret); 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci /* FIXME: add maximum time for scan to complete */ 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci if (ret != CMD_STATUS_COMPLETE) { 19358c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, 19368c2ecf20Sopenharmony_ci SCAN_POLL_INTERVAL); 19378c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 19388c2ecf20Sopenharmony_ci return; 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci if (is_valid_ether_addr(priv->bssid)) 19428c2ecf20Sopenharmony_ci at76_join(priv); 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci priv->scanning = false; 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci ieee80211_scan_completed(priv->hw, &info); 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci ieee80211_wake_queues(priv->hw); 19518c2ecf20Sopenharmony_ci} 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_cistatic int at76_hw_scan(struct ieee80211_hw *hw, 19548c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 19558c2ecf20Sopenharmony_ci struct ieee80211_scan_request *hw_req) 19568c2ecf20Sopenharmony_ci{ 19578c2ecf20Sopenharmony_ci struct cfg80211_scan_request *req = &hw_req->req; 19588c2ecf20Sopenharmony_ci struct at76_priv *priv = hw->priv; 19598c2ecf20Sopenharmony_ci struct at76_req_scan scan; 19608c2ecf20Sopenharmony_ci u8 *ssid = NULL; 19618c2ecf20Sopenharmony_ci int ret, len = 0; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s():", __func__); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci if (priv->device_unplugged) 19668c2ecf20Sopenharmony_ci return 0; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci mutex_lock(&priv->mtx); 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci ieee80211_stop_queues(hw); 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci memset(&scan, 0, sizeof(struct at76_req_scan)); 19738c2ecf20Sopenharmony_ci eth_broadcast_addr(scan.bssid); 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci if (req->n_ssids) { 19768c2ecf20Sopenharmony_ci scan.scan_type = SCAN_TYPE_ACTIVE; 19778c2ecf20Sopenharmony_ci ssid = req->ssids[0].ssid; 19788c2ecf20Sopenharmony_ci len = req->ssids[0].ssid_len; 19798c2ecf20Sopenharmony_ci } else { 19808c2ecf20Sopenharmony_ci scan.scan_type = SCAN_TYPE_PASSIVE; 19818c2ecf20Sopenharmony_ci } 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci if (len) { 19848c2ecf20Sopenharmony_ci memcpy(scan.essid, ssid, len); 19858c2ecf20Sopenharmony_ci scan.essid_size = len; 19868c2ecf20Sopenharmony_ci } 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci scan.min_channel_time = cpu_to_le16(priv->scan_min_time); 19898c2ecf20Sopenharmony_ci scan.max_channel_time = cpu_to_le16(priv->scan_max_time); 19908c2ecf20Sopenharmony_ci scan.probe_delay = cpu_to_le16(priv->scan_min_time * 1000); 19918c2ecf20Sopenharmony_ci scan.international_scan = 0; 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s: sending CMD_SCAN", __func__); 19948c2ecf20Sopenharmony_ci ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci if (ret < 0) { 19978c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "CMD_SCAN failed: %d\n", ret); 19988c2ecf20Sopenharmony_ci goto exit; 19998c2ecf20Sopenharmony_ci } 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci priv->scanning = true; 20028c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, 20038c2ecf20Sopenharmony_ci SCAN_POLL_INTERVAL); 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ciexit: 20068c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci return 0; 20098c2ecf20Sopenharmony_ci} 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_cistatic int at76_config(struct ieee80211_hw *hw, u32 changed) 20128c2ecf20Sopenharmony_ci{ 20138c2ecf20Sopenharmony_ci struct at76_priv *priv = hw->priv; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s(): channel %d", 20168c2ecf20Sopenharmony_ci __func__, hw->conf.chandef.chan->hw_value); 20178c2ecf20Sopenharmony_ci at76_dbg_dump(DBG_MAC80211, priv->bssid, ETH_ALEN, "bssid:"); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci mutex_lock(&priv->mtx); 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci priv->channel = hw->conf.chandef.chan->hw_value; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci if (is_valid_ether_addr(priv->bssid)) 20248c2ecf20Sopenharmony_ci at76_join(priv); 20258c2ecf20Sopenharmony_ci else 20268c2ecf20Sopenharmony_ci at76_start_monitor(priv); 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci return 0; 20318c2ecf20Sopenharmony_ci} 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_cistatic void at76_bss_info_changed(struct ieee80211_hw *hw, 20348c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 20358c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *conf, 20368c2ecf20Sopenharmony_ci u32 changed) 20378c2ecf20Sopenharmony_ci{ 20388c2ecf20Sopenharmony_ci struct at76_priv *priv = hw->priv; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s():", __func__); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci if (!(changed & BSS_CHANGED_BSSID)) 20438c2ecf20Sopenharmony_ci return; 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci at76_dbg_dump(DBG_MAC80211, conf->bssid, ETH_ALEN, "bssid:"); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci mutex_lock(&priv->mtx); 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci memcpy(priv->bssid, conf->bssid, ETH_ALEN); 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci if (is_valid_ether_addr(priv->bssid)) 20528c2ecf20Sopenharmony_ci /* mac80211 is joining a bss */ 20538c2ecf20Sopenharmony_ci at76_join(priv); 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 20568c2ecf20Sopenharmony_ci} 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci/* must be atomic */ 20598c2ecf20Sopenharmony_cistatic void at76_configure_filter(struct ieee80211_hw *hw, 20608c2ecf20Sopenharmony_ci unsigned int changed_flags, 20618c2ecf20Sopenharmony_ci unsigned int *total_flags, u64 multicast) 20628c2ecf20Sopenharmony_ci{ 20638c2ecf20Sopenharmony_ci struct at76_priv *priv = hw->priv; 20648c2ecf20Sopenharmony_ci int flags; 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x " 20678c2ecf20Sopenharmony_ci "total_flags=0x%08x", 20688c2ecf20Sopenharmony_ci __func__, changed_flags, *total_flags); 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci flags = changed_flags & AT76_SUPPORTED_FILTERS; 20718c2ecf20Sopenharmony_ci *total_flags = AT76_SUPPORTED_FILTERS; 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci /* Bail out after updating flags to prevent a WARN_ON in mac80211. */ 20748c2ecf20Sopenharmony_ci if (priv->device_unplugged) 20758c2ecf20Sopenharmony_ci return; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci /* FIXME: access to priv->promisc should be protected with 20788c2ecf20Sopenharmony_ci * priv->mtx, but it's impossible because this function needs to be 20798c2ecf20Sopenharmony_ci * atomic */ 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci if (flags && !priv->promisc) { 20828c2ecf20Sopenharmony_ci /* mac80211 wants us to enable promiscuous mode */ 20838c2ecf20Sopenharmony_ci priv->promisc = 1; 20848c2ecf20Sopenharmony_ci } else if (!flags && priv->promisc) { 20858c2ecf20Sopenharmony_ci /* we need to disable promiscuous mode */ 20868c2ecf20Sopenharmony_ci priv->promisc = 0; 20878c2ecf20Sopenharmony_ci } else 20888c2ecf20Sopenharmony_ci return; 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci ieee80211_queue_work(hw, &priv->work_set_promisc); 20918c2ecf20Sopenharmony_ci} 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_cistatic int at76_set_wep(struct at76_priv *priv) 20948c2ecf20Sopenharmony_ci{ 20958c2ecf20Sopenharmony_ci int ret = 0; 20968c2ecf20Sopenharmony_ci struct mib_mac_wep *mib_data = &priv->mib_buf.data.wep_mib; 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci priv->mib_buf.type = MIB_MAC_WEP; 20998c2ecf20Sopenharmony_ci priv->mib_buf.size = sizeof(struct mib_mac_wep); 21008c2ecf20Sopenharmony_ci priv->mib_buf.index = 0; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci memset(mib_data, 0, sizeof(*mib_data)); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci if (priv->wep_enabled) { 21058c2ecf20Sopenharmony_ci if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) 21068c2ecf20Sopenharmony_ci mib_data->encryption_level = 2; 21078c2ecf20Sopenharmony_ci else 21088c2ecf20Sopenharmony_ci mib_data->encryption_level = 1; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci /* always exclude unencrypted if WEP is active */ 21118c2ecf20Sopenharmony_ci mib_data->exclude_unencrypted = 1; 21128c2ecf20Sopenharmony_ci } else { 21138c2ecf20Sopenharmony_ci mib_data->exclude_unencrypted = 0; 21148c2ecf20Sopenharmony_ci mib_data->encryption_level = 0; 21158c2ecf20Sopenharmony_ci } 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci mib_data->privacy_invoked = priv->wep_enabled; 21188c2ecf20Sopenharmony_ci mib_data->wep_default_key_id = priv->wep_key_id; 21198c2ecf20Sopenharmony_ci memcpy(mib_data->wep_default_keyvalue, priv->wep_keys, 21208c2ecf20Sopenharmony_ci sizeof(priv->wep_keys)); 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci ret = at76_set_mib(priv, &priv->mib_buf); 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci if (ret < 0) 21258c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 21268c2ecf20Sopenharmony_ci "set_mib (wep) failed: %d\n", ret); 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci return ret; 21298c2ecf20Sopenharmony_ci} 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_cistatic int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 21328c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, struct ieee80211_sta *sta, 21338c2ecf20Sopenharmony_ci struct ieee80211_key_conf *key) 21348c2ecf20Sopenharmony_ci{ 21358c2ecf20Sopenharmony_ci struct at76_priv *priv = hw->priv; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci int i; 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci at76_dbg(DBG_MAC80211, "%s(): cmd %d key->cipher %d key->keyidx %d " 21408c2ecf20Sopenharmony_ci "key->keylen %d", 21418c2ecf20Sopenharmony_ci __func__, cmd, key->cipher, key->keyidx, key->keylen); 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci if ((key->cipher != WLAN_CIPHER_SUITE_WEP40) && 21448c2ecf20Sopenharmony_ci (key->cipher != WLAN_CIPHER_SUITE_WEP104)) 21458c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci key->hw_key_idx = key->keyidx; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci mutex_lock(&priv->mtx); 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci switch (cmd) { 21528c2ecf20Sopenharmony_ci case SET_KEY: 21538c2ecf20Sopenharmony_ci memcpy(priv->wep_keys[key->keyidx], key->key, key->keylen); 21548c2ecf20Sopenharmony_ci priv->wep_keys_len[key->keyidx] = key->keylen; 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci /* FIXME: find out how to do this properly */ 21578c2ecf20Sopenharmony_ci priv->wep_key_id = key->keyidx; 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci break; 21608c2ecf20Sopenharmony_ci case DISABLE_KEY: 21618c2ecf20Sopenharmony_ci default: 21628c2ecf20Sopenharmony_ci priv->wep_keys_len[key->keyidx] = 0; 21638c2ecf20Sopenharmony_ci break; 21648c2ecf20Sopenharmony_ci } 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci priv->wep_enabled = 0; 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci for (i = 0; i < WEP_KEYS; i++) { 21698c2ecf20Sopenharmony_ci if (priv->wep_keys_len[i] != 0) 21708c2ecf20Sopenharmony_ci priv->wep_enabled = 1; 21718c2ecf20Sopenharmony_ci } 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci at76_set_wep(priv); 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci mutex_unlock(&priv->mtx); 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci return 0; 21788c2ecf20Sopenharmony_ci} 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_cistatic const struct ieee80211_ops at76_ops = { 21818c2ecf20Sopenharmony_ci .tx = at76_mac80211_tx, 21828c2ecf20Sopenharmony_ci .add_interface = at76_add_interface, 21838c2ecf20Sopenharmony_ci .remove_interface = at76_remove_interface, 21848c2ecf20Sopenharmony_ci .config = at76_config, 21858c2ecf20Sopenharmony_ci .bss_info_changed = at76_bss_info_changed, 21868c2ecf20Sopenharmony_ci .configure_filter = at76_configure_filter, 21878c2ecf20Sopenharmony_ci .start = at76_mac80211_start, 21888c2ecf20Sopenharmony_ci .stop = at76_mac80211_stop, 21898c2ecf20Sopenharmony_ci .hw_scan = at76_hw_scan, 21908c2ecf20Sopenharmony_ci .set_key = at76_set_key, 21918c2ecf20Sopenharmony_ci}; 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci/* Allocate network device and initialize private data */ 21948c2ecf20Sopenharmony_cistatic struct at76_priv *at76_alloc_new_device(struct usb_device *udev) 21958c2ecf20Sopenharmony_ci{ 21968c2ecf20Sopenharmony_ci struct ieee80211_hw *hw; 21978c2ecf20Sopenharmony_ci struct at76_priv *priv; 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci hw = ieee80211_alloc_hw(sizeof(struct at76_priv), &at76_ops); 22008c2ecf20Sopenharmony_ci if (!hw) { 22018c2ecf20Sopenharmony_ci printk(KERN_ERR DRIVER_NAME ": could not register" 22028c2ecf20Sopenharmony_ci " ieee80211_hw\n"); 22038c2ecf20Sopenharmony_ci return NULL; 22048c2ecf20Sopenharmony_ci } 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci priv = hw->priv; 22078c2ecf20Sopenharmony_ci priv->hw = hw; 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci priv->udev = udev; 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci mutex_init(&priv->mtx); 22128c2ecf20Sopenharmony_ci INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc); 22138c2ecf20Sopenharmony_ci INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx); 22148c2ecf20Sopenharmony_ci INIT_WORK(&priv->work_join_bssid, at76_work_join_bssid); 22158c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&priv->dwork_hw_scan, at76_dwork_hw_scan); 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci tasklet_setup(&priv->rx_tasklet, at76_rx_tasklet); 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci priv->pm_mode = AT76_PM_OFF; 22208c2ecf20Sopenharmony_ci priv->pm_period = 0; 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci /* unit us */ 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci return priv; 22258c2ecf20Sopenharmony_ci} 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_cistatic int at76_alloc_urbs(struct at76_priv *priv, 22288c2ecf20Sopenharmony_ci struct usb_interface *interface) 22298c2ecf20Sopenharmony_ci{ 22308c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out; 22318c2ecf20Sopenharmony_ci int i; 22328c2ecf20Sopenharmony_ci int buffer_size; 22338c2ecf20Sopenharmony_ci struct usb_host_interface *iface_desc; 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__, 22388c2ecf20Sopenharmony_ci interface->cur_altsetting->desc.bNumEndpoints); 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci ep_in = NULL; 22418c2ecf20Sopenharmony_ci ep_out = NULL; 22428c2ecf20Sopenharmony_ci iface_desc = interface->cur_altsetting; 22438c2ecf20Sopenharmony_ci for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { 22448c2ecf20Sopenharmony_ci endpoint = &iface_desc->endpoint[i].desc; 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x", 22478c2ecf20Sopenharmony_ci __func__, i, endpoint->bEndpointAddress, 22488c2ecf20Sopenharmony_ci endpoint->bmAttributes); 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci if (!ep_in && usb_endpoint_is_bulk_in(endpoint)) 22518c2ecf20Sopenharmony_ci ep_in = endpoint; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci if (!ep_out && usb_endpoint_is_bulk_out(endpoint)) 22548c2ecf20Sopenharmony_ci ep_out = endpoint; 22558c2ecf20Sopenharmony_ci } 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci if (!ep_in || !ep_out) { 22588c2ecf20Sopenharmony_ci dev_err(&interface->dev, "bulk endpoints missing\n"); 22598c2ecf20Sopenharmony_ci return -ENXIO; 22608c2ecf20Sopenharmony_ci } 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci priv->rx_pipe = usb_rcvbulkpipe(priv->udev, ep_in->bEndpointAddress); 22638c2ecf20Sopenharmony_ci priv->tx_pipe = usb_sndbulkpipe(priv->udev, ep_out->bEndpointAddress); 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL); 22668c2ecf20Sopenharmony_ci priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL); 22678c2ecf20Sopenharmony_ci if (!priv->rx_urb || !priv->tx_urb) { 22688c2ecf20Sopenharmony_ci dev_err(&interface->dev, "cannot allocate URB\n"); 22698c2ecf20Sopenharmony_ci return -ENOMEM; 22708c2ecf20Sopenharmony_ci } 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE; 22738c2ecf20Sopenharmony_ci priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); 22748c2ecf20Sopenharmony_ci if (!priv->bulk_out_buffer) 22758c2ecf20Sopenharmony_ci return -ENOMEM; 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci return 0; 22808c2ecf20Sopenharmony_ci} 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_cistatic struct ieee80211_rate at76_rates[] = { 22838c2ecf20Sopenharmony_ci { .bitrate = 10, .hw_value = TX_RATE_1MBIT, }, 22848c2ecf20Sopenharmony_ci { .bitrate = 20, .hw_value = TX_RATE_2MBIT, }, 22858c2ecf20Sopenharmony_ci { .bitrate = 55, .hw_value = TX_RATE_5_5MBIT, }, 22868c2ecf20Sopenharmony_ci { .bitrate = 110, .hw_value = TX_RATE_11MBIT, }, 22878c2ecf20Sopenharmony_ci}; 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_cistatic struct ieee80211_channel at76_channels[] = { 22908c2ecf20Sopenharmony_ci { .center_freq = 2412, .hw_value = 1 }, 22918c2ecf20Sopenharmony_ci { .center_freq = 2417, .hw_value = 2 }, 22928c2ecf20Sopenharmony_ci { .center_freq = 2422, .hw_value = 3 }, 22938c2ecf20Sopenharmony_ci { .center_freq = 2427, .hw_value = 4 }, 22948c2ecf20Sopenharmony_ci { .center_freq = 2432, .hw_value = 5 }, 22958c2ecf20Sopenharmony_ci { .center_freq = 2437, .hw_value = 6 }, 22968c2ecf20Sopenharmony_ci { .center_freq = 2442, .hw_value = 7 }, 22978c2ecf20Sopenharmony_ci { .center_freq = 2447, .hw_value = 8 }, 22988c2ecf20Sopenharmony_ci { .center_freq = 2452, .hw_value = 9 }, 22998c2ecf20Sopenharmony_ci { .center_freq = 2457, .hw_value = 10 }, 23008c2ecf20Sopenharmony_ci { .center_freq = 2462, .hw_value = 11 }, 23018c2ecf20Sopenharmony_ci { .center_freq = 2467, .hw_value = 12 }, 23028c2ecf20Sopenharmony_ci { .center_freq = 2472, .hw_value = 13 }, 23038c2ecf20Sopenharmony_ci { .center_freq = 2484, .hw_value = 14 } 23048c2ecf20Sopenharmony_ci}; 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_cistatic struct ieee80211_supported_band at76_supported_band = { 23078c2ecf20Sopenharmony_ci .channels = at76_channels, 23088c2ecf20Sopenharmony_ci .n_channels = ARRAY_SIZE(at76_channels), 23098c2ecf20Sopenharmony_ci .bitrates = at76_rates, 23108c2ecf20Sopenharmony_ci .n_bitrates = ARRAY_SIZE(at76_rates), 23118c2ecf20Sopenharmony_ci}; 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci/* Register network device and initialize the hardware */ 23148c2ecf20Sopenharmony_cistatic int at76_init_new_device(struct at76_priv *priv, 23158c2ecf20Sopenharmony_ci struct usb_interface *interface) 23168c2ecf20Sopenharmony_ci{ 23178c2ecf20Sopenharmony_ci struct wiphy *wiphy; 23188c2ecf20Sopenharmony_ci size_t len; 23198c2ecf20Sopenharmony_ci int ret; 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci /* set up the endpoint information */ 23228c2ecf20Sopenharmony_ci /* check out the endpoints */ 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints", 23258c2ecf20Sopenharmony_ci interface->cur_altsetting->desc.bNumEndpoints); 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci ret = at76_alloc_urbs(priv, interface); 23288c2ecf20Sopenharmony_ci if (ret < 0) 23298c2ecf20Sopenharmony_ci goto exit; 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci /* MAC address */ 23328c2ecf20Sopenharmony_ci ret = at76_get_hw_config(priv); 23338c2ecf20Sopenharmony_ci if (ret < 0) { 23348c2ecf20Sopenharmony_ci dev_err(&interface->dev, "cannot get MAC address\n"); 23358c2ecf20Sopenharmony_ci goto exit; 23368c2ecf20Sopenharmony_ci } 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci priv->domain = at76_get_reg_domain(priv->regulatory_domain); 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci priv->channel = DEF_CHANNEL; 23418c2ecf20Sopenharmony_ci priv->iw_mode = IW_MODE_INFRA; 23428c2ecf20Sopenharmony_ci priv->rts_threshold = DEF_RTS_THRESHOLD; 23438c2ecf20Sopenharmony_ci priv->frag_threshold = DEF_FRAG_THRESHOLD; 23448c2ecf20Sopenharmony_ci priv->short_retry_limit = DEF_SHORT_RETRY_LIMIT; 23458c2ecf20Sopenharmony_ci priv->txrate = TX_RATE_AUTO; 23468c2ecf20Sopenharmony_ci priv->preamble_type = PREAMBLE_TYPE_LONG; 23478c2ecf20Sopenharmony_ci priv->beacon_period = 100; 23488c2ecf20Sopenharmony_ci priv->auth_mode = WLAN_AUTH_OPEN; 23498c2ecf20Sopenharmony_ci priv->scan_min_time = DEF_SCAN_MIN_TIME; 23508c2ecf20Sopenharmony_ci priv->scan_max_time = DEF_SCAN_MAX_TIME; 23518c2ecf20Sopenharmony_ci priv->scan_mode = SCAN_TYPE_ACTIVE; 23528c2ecf20Sopenharmony_ci priv->device_unplugged = 0; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci /* mac80211 initialisation */ 23558c2ecf20Sopenharmony_ci wiphy = priv->hw->wiphy; 23568c2ecf20Sopenharmony_ci priv->hw->wiphy->max_scan_ssids = 1; 23578c2ecf20Sopenharmony_ci priv->hw->wiphy->max_scan_ie_len = 0; 23588c2ecf20Sopenharmony_ci priv->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); 23598c2ecf20Sopenharmony_ci priv->hw->wiphy->bands[NL80211_BAND_2GHZ] = &at76_supported_band; 23608c2ecf20Sopenharmony_ci ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); 23618c2ecf20Sopenharmony_ci ieee80211_hw_set(priv->hw, SIGNAL_UNSPEC); 23628c2ecf20Sopenharmony_ci priv->hw->max_signal = 100; 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci SET_IEEE80211_DEV(priv->hw, &interface->dev); 23658c2ecf20Sopenharmony_ci SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci len = sizeof(wiphy->fw_version); 23688c2ecf20Sopenharmony_ci snprintf(wiphy->fw_version, len, "%d.%d.%d-%d", 23698c2ecf20Sopenharmony_ci priv->fw_version.major, priv->fw_version.minor, 23708c2ecf20Sopenharmony_ci priv->fw_version.patch, priv->fw_version.build); 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci wiphy->hw_version = priv->board_type; 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci ret = ieee80211_register_hw(priv->hw); 23778c2ecf20Sopenharmony_ci if (ret) { 23788c2ecf20Sopenharmony_ci printk(KERN_ERR "cannot register mac80211 hw (status %d)!\n", 23798c2ecf20Sopenharmony_ci ret); 23808c2ecf20Sopenharmony_ci goto exit; 23818c2ecf20Sopenharmony_ci } 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci priv->mac80211_registered = 1; 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci wiphy_info(priv->hw->wiphy, "USB %s, MAC %pM, firmware %d.%d.%d-%d\n", 23868c2ecf20Sopenharmony_ci dev_name(&interface->dev), priv->mac_addr, 23878c2ecf20Sopenharmony_ci priv->fw_version.major, priv->fw_version.minor, 23888c2ecf20Sopenharmony_ci priv->fw_version.patch, priv->fw_version.build); 23898c2ecf20Sopenharmony_ci wiphy_info(priv->hw->wiphy, "regulatory domain 0x%02x: %s\n", 23908c2ecf20Sopenharmony_ci priv->regulatory_domain, priv->domain->name); 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ciexit: 23938c2ecf20Sopenharmony_ci return ret; 23948c2ecf20Sopenharmony_ci} 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_cistatic void at76_delete_device(struct at76_priv *priv) 23978c2ecf20Sopenharmony_ci{ 23988c2ecf20Sopenharmony_ci at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci /* The device is gone, don't bother turning it off */ 24018c2ecf20Sopenharmony_ci priv->device_unplugged = 1; 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci tasklet_kill(&priv->rx_tasklet); 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci if (priv->mac80211_registered) 24068c2ecf20Sopenharmony_ci ieee80211_unregister_hw(priv->hw); 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci if (priv->tx_urb) { 24098c2ecf20Sopenharmony_ci usb_kill_urb(priv->tx_urb); 24108c2ecf20Sopenharmony_ci usb_free_urb(priv->tx_urb); 24118c2ecf20Sopenharmony_ci } 24128c2ecf20Sopenharmony_ci if (priv->rx_urb) { 24138c2ecf20Sopenharmony_ci usb_kill_urb(priv->rx_urb); 24148c2ecf20Sopenharmony_ci usb_free_urb(priv->rx_urb); 24158c2ecf20Sopenharmony_ci } 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __func__); 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci kfree(priv->bulk_out_buffer); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci del_timer_sync(&ledtrig_tx_timer); 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci kfree_skb(priv->rx_skb); 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/ieee80211_hw", 24268c2ecf20Sopenharmony_ci __func__); 24278c2ecf20Sopenharmony_ci ieee80211_free_hw(priv->hw); 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); 24308c2ecf20Sopenharmony_ci} 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_cistatic int at76_probe(struct usb_interface *interface, 24338c2ecf20Sopenharmony_ci const struct usb_device_id *id) 24348c2ecf20Sopenharmony_ci{ 24358c2ecf20Sopenharmony_ci int ret; 24368c2ecf20Sopenharmony_ci struct at76_priv *priv; 24378c2ecf20Sopenharmony_ci struct fwentry *fwe; 24388c2ecf20Sopenharmony_ci struct usb_device *udev; 24398c2ecf20Sopenharmony_ci int op_mode; 24408c2ecf20Sopenharmony_ci int need_ext_fw = 0; 24418c2ecf20Sopenharmony_ci struct mib_fw_version *fwv = NULL; 24428c2ecf20Sopenharmony_ci int board_type = (int)id->driver_info; 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci udev = usb_get_dev(interface_to_usbdev(interface)); 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci fwv = kmalloc(sizeof(*fwv), GFP_KERNEL); 24478c2ecf20Sopenharmony_ci if (!fwv) { 24488c2ecf20Sopenharmony_ci ret = -ENOMEM; 24498c2ecf20Sopenharmony_ci goto exit; 24508c2ecf20Sopenharmony_ci } 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci /* Load firmware into kernel memory */ 24538c2ecf20Sopenharmony_ci fwe = at76_load_firmware(udev, board_type); 24548c2ecf20Sopenharmony_ci if (!fwe) { 24558c2ecf20Sopenharmony_ci ret = -ENOENT; 24568c2ecf20Sopenharmony_ci goto exit; 24578c2ecf20Sopenharmony_ci } 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci op_mode = at76_get_op_mode(udev); 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci /* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ??? 24648c2ecf20Sopenharmony_ci we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */ 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_ci if (op_mode == OPMODE_HW_CONFIG_MODE) { 24678c2ecf20Sopenharmony_ci dev_err(&interface->dev, 24688c2ecf20Sopenharmony_ci "cannot handle a device in HW_CONFIG_MODE\n"); 24698c2ecf20Sopenharmony_ci ret = -EBUSY; 24708c2ecf20Sopenharmony_ci goto exit; 24718c2ecf20Sopenharmony_ci } 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH 24748c2ecf20Sopenharmony_ci && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { 24758c2ecf20Sopenharmony_ci /* download internal firmware part */ 24768c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &interface->dev, 24778c2ecf20Sopenharmony_ci "downloading internal firmware\n"); 24788c2ecf20Sopenharmony_ci ret = at76_load_internal_fw(udev, fwe); 24798c2ecf20Sopenharmony_ci if (ret < 0) { 24808c2ecf20Sopenharmony_ci dev_err(&interface->dev, 24818c2ecf20Sopenharmony_ci "error %d downloading internal firmware\n", 24828c2ecf20Sopenharmony_ci ret); 24838c2ecf20Sopenharmony_ci } 24848c2ecf20Sopenharmony_ci goto exit; 24858c2ecf20Sopenharmony_ci } 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci /* Internal firmware already inside the device. Get firmware 24888c2ecf20Sopenharmony_ci * version to test if external firmware is loaded. 24898c2ecf20Sopenharmony_ci * This works only for newer firmware, e.g. the Intersil 0.90.x 24908c2ecf20Sopenharmony_ci * says "control timeout on ep0in" and subsequent 24918c2ecf20Sopenharmony_ci * at76_get_op_mode() fail too :-( */ 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci /* if version >= 0.100.x.y or device with built-in flash we can 24948c2ecf20Sopenharmony_ci * query the device for the fw version */ 24958c2ecf20Sopenharmony_ci if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100) 24968c2ecf20Sopenharmony_ci || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { 24978c2ecf20Sopenharmony_ci ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); 24988c2ecf20Sopenharmony_ci if (ret < 0 || (fwv->major | fwv->minor) == 0) 24998c2ecf20Sopenharmony_ci need_ext_fw = 1; 25008c2ecf20Sopenharmony_ci } else 25018c2ecf20Sopenharmony_ci /* No way to check firmware version, reload to be sure */ 25028c2ecf20Sopenharmony_ci need_ext_fw = 1; 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci if (need_ext_fw) { 25058c2ecf20Sopenharmony_ci dev_printk(KERN_DEBUG, &interface->dev, 25068c2ecf20Sopenharmony_ci "downloading external firmware\n"); 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci ret = at76_load_external_fw(udev, fwe); 25098c2ecf20Sopenharmony_ci if (ret < 0) 25108c2ecf20Sopenharmony_ci goto exit; 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci /* Re-check firmware version */ 25138c2ecf20Sopenharmony_ci ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); 25148c2ecf20Sopenharmony_ci if (ret < 0) { 25158c2ecf20Sopenharmony_ci dev_err(&interface->dev, 25168c2ecf20Sopenharmony_ci "error %d getting firmware version\n", ret); 25178c2ecf20Sopenharmony_ci goto exit; 25188c2ecf20Sopenharmony_ci } 25198c2ecf20Sopenharmony_ci } 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci priv = at76_alloc_new_device(udev); 25228c2ecf20Sopenharmony_ci if (!priv) { 25238c2ecf20Sopenharmony_ci ret = -ENOMEM; 25248c2ecf20Sopenharmony_ci goto exit; 25258c2ecf20Sopenharmony_ci } 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci usb_set_intfdata(interface, priv); 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci memcpy(&priv->fw_version, fwv, sizeof(struct mib_fw_version)); 25308c2ecf20Sopenharmony_ci priv->board_type = board_type; 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci ret = at76_init_new_device(priv, interface); 25338c2ecf20Sopenharmony_ci if (ret < 0) 25348c2ecf20Sopenharmony_ci at76_delete_device(priv); 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ciexit: 25378c2ecf20Sopenharmony_ci kfree(fwv); 25388c2ecf20Sopenharmony_ci if (ret < 0) 25398c2ecf20Sopenharmony_ci usb_put_dev(udev); 25408c2ecf20Sopenharmony_ci return ret; 25418c2ecf20Sopenharmony_ci} 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_cistatic void at76_disconnect(struct usb_interface *interface) 25448c2ecf20Sopenharmony_ci{ 25458c2ecf20Sopenharmony_ci struct at76_priv *priv; 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci priv = usb_get_intfdata(interface); 25488c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci /* Disconnect after loading internal firmware */ 25518c2ecf20Sopenharmony_ci if (!priv) 25528c2ecf20Sopenharmony_ci return; 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci wiphy_info(priv->hw->wiphy, "disconnecting\n"); 25558c2ecf20Sopenharmony_ci at76_delete_device(priv); 25568c2ecf20Sopenharmony_ci usb_put_dev(priv->udev); 25578c2ecf20Sopenharmony_ci dev_info(&interface->dev, "disconnected\n"); 25588c2ecf20Sopenharmony_ci} 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci/* Structure for registering this driver with the USB subsystem */ 25618c2ecf20Sopenharmony_cistatic struct usb_driver at76_driver = { 25628c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 25638c2ecf20Sopenharmony_ci .probe = at76_probe, 25648c2ecf20Sopenharmony_ci .disconnect = at76_disconnect, 25658c2ecf20Sopenharmony_ci .id_table = dev_table, 25668c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 25678c2ecf20Sopenharmony_ci}; 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_cistatic int __init at76_mod_init(void) 25708c2ecf20Sopenharmony_ci{ 25718c2ecf20Sopenharmony_ci int result; 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " loading\n"); 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci mutex_init(&fw_mutex); 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci /* register this driver with the USB subsystem */ 25788c2ecf20Sopenharmony_ci result = usb_register(&at76_driver); 25798c2ecf20Sopenharmony_ci if (result < 0) 25808c2ecf20Sopenharmony_ci printk(KERN_ERR DRIVER_NAME 25818c2ecf20Sopenharmony_ci ": usb_register failed (status %d)\n", result); 25828c2ecf20Sopenharmony_ci else 25838c2ecf20Sopenharmony_ci led_trigger_register_simple("at76_usb-tx", &ledtrig_tx); 25848c2ecf20Sopenharmony_ci return result; 25858c2ecf20Sopenharmony_ci} 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_cistatic void __exit at76_mod_exit(void) 25888c2ecf20Sopenharmony_ci{ 25898c2ecf20Sopenharmony_ci int i; 25908c2ecf20Sopenharmony_ci 25918c2ecf20Sopenharmony_ci printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n"); 25928c2ecf20Sopenharmony_ci usb_deregister(&at76_driver); 25938c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(firmwares); i++) 25948c2ecf20Sopenharmony_ci release_firmware(firmwares[i].fw); 25958c2ecf20Sopenharmony_ci led_trigger_unregister_simple(ledtrig_tx); 25968c2ecf20Sopenharmony_ci} 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_cimodule_param_named(debug, at76_debug, uint, 0600); 25998c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debugging level"); 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_cimodule_init(at76_mod_init); 26028c2ecf20Sopenharmony_cimodule_exit(at76_mod_exit); 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ciMODULE_AUTHOR("Oliver Kurth <oku@masqmail.cx>"); 26058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joerg Albert <joerg.albert@gmx.de>"); 26068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alex <alex@foogod.com>"); 26078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nick Jones"); 26088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Balint Seeber <n0_5p4m_p13453@hotmail.com>"); 26098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pavel Roskin <proski@gnu.org>"); 26108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>"); 26118c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kalle Valo <kalle.valo@iki.fi>"); 26128c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sebastian Smolorz <sesmo@gmx.net>"); 26138c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 26148c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2615