18c2ecf20Sopenharmony_ci/*** -*- linux-c -*- ********************************************************** 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci Copyright 2000-2001 ATMEL Corporation. 68c2ecf20Sopenharmony_ci Copyright 2003-2004 Simon Kelley. 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci This code was developed from version 2.1.1 of the Atmel drivers, 98c2ecf20Sopenharmony_ci released by Atmel corp. under the GPL in December 2002. It also 108c2ecf20Sopenharmony_ci includes code from the Linux aironet drivers (C) Benjamin Reed, 118c2ecf20Sopenharmony_ci and the Linux PCMCIA package, (C) David Hinds and the Linux wireless 128c2ecf20Sopenharmony_ci extensions, (C) Jean Tourrilhes. 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci The firmware module for reading the MAC address of the card comes from 158c2ecf20Sopenharmony_ci net.russotto.AtmelMACFW, written by Matthew T. Russotto and copyright 168c2ecf20Sopenharmony_ci by him. net.russotto.AtmelMACFW is used under the GPL license version 2. 178c2ecf20Sopenharmony_ci This file contains the module in binary form and, under the terms 188c2ecf20Sopenharmony_ci of the GPL, in source form. The source is located at the end of the file. 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci This program is free software; you can redistribute it and/or modify 218c2ecf20Sopenharmony_ci it under the terms of the GNU General Public License as published by 228c2ecf20Sopenharmony_ci the Free Software Foundation; either version 2 of the License, or 238c2ecf20Sopenharmony_ci (at your option) any later version. 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci This software is distributed in the hope that it will be useful, 268c2ecf20Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 278c2ecf20Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 288c2ecf20Sopenharmony_ci GNU General Public License for more details. 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci You should have received a copy of the GNU General Public License 318c2ecf20Sopenharmony_ci along with Atmel wireless lan drivers; if not, see 328c2ecf20Sopenharmony_ci <http://www.gnu.org/licenses/>. 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci For all queries about this code, please contact the current author, 358c2ecf20Sopenharmony_ci Simon Kelley <simon@thekelleys.org.uk> and not Atmel Corporation. 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci Credit is due to HP UK and Cambridge Online Systems Ltd for supplying 388c2ecf20Sopenharmony_ci hardware used during development of this driver. 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci******************************************************************************/ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include <linux/kernel.h> 458c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 468c2ecf20Sopenharmony_ci#include <linux/slab.h> 478c2ecf20Sopenharmony_ci#include <linux/string.h> 488c2ecf20Sopenharmony_ci#include <linux/timer.h> 498c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 508c2ecf20Sopenharmony_ci#include <asm/io.h> 518c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 528c2ecf20Sopenharmony_ci#include <linux/module.h> 538c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 548c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 558c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 568c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 578c2ecf20Sopenharmony_ci#include <linux/ioport.h> 588c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 598c2ecf20Sopenharmony_ci#include <linux/delay.h> 608c2ecf20Sopenharmony_ci#include <linux/wireless.h> 618c2ecf20Sopenharmony_ci#include <net/iw_handler.h> 628c2ecf20Sopenharmony_ci#include <linux/crc32.h> 638c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 648c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 658c2ecf20Sopenharmony_ci#include <linux/device.h> 668c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 678c2ecf20Sopenharmony_ci#include <linux/firmware.h> 688c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 698c2ecf20Sopenharmony_ci#include <net/cfg80211.h> 708c2ecf20Sopenharmony_ci#include "atmel.h" 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define DRIVER_MAJOR 0 738c2ecf20Sopenharmony_ci#define DRIVER_MINOR 98 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ciMODULE_AUTHOR("Simon Kelley"); 768c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); 778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 788c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("Atmel at76c50x wireless cards"); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* The name of the firmware file to be loaded 818c2ecf20Sopenharmony_ci over-rides any automatic selection */ 828c2ecf20Sopenharmony_cistatic char *firmware = NULL; 838c2ecf20Sopenharmony_cimodule_param(firmware, charp, 0); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* table of firmware file names */ 868c2ecf20Sopenharmony_cistatic struct { 878c2ecf20Sopenharmony_ci AtmelFWType fw_type; 888c2ecf20Sopenharmony_ci const char *fw_file; 898c2ecf20Sopenharmony_ci const char *fw_file_ext; 908c2ecf20Sopenharmony_ci} fw_table[] = { 918c2ecf20Sopenharmony_ci { ATMEL_FW_TYPE_502, "atmel_at76c502", "bin" }, 928c2ecf20Sopenharmony_ci { ATMEL_FW_TYPE_502D, "atmel_at76c502d", "bin" }, 938c2ecf20Sopenharmony_ci { ATMEL_FW_TYPE_502E, "atmel_at76c502e", "bin" }, 948c2ecf20Sopenharmony_ci { ATMEL_FW_TYPE_502_3COM, "atmel_at76c502_3com", "bin" }, 958c2ecf20Sopenharmony_ci { ATMEL_FW_TYPE_504, "atmel_at76c504", "bin" }, 968c2ecf20Sopenharmony_ci { ATMEL_FW_TYPE_504_2958, "atmel_at76c504_2958", "bin" }, 978c2ecf20Sopenharmony_ci { ATMEL_FW_TYPE_504A_2958, "atmel_at76c504a_2958", "bin" }, 988c2ecf20Sopenharmony_ci { ATMEL_FW_TYPE_506, "atmel_at76c506", "bin" }, 998c2ecf20Sopenharmony_ci { ATMEL_FW_TYPE_NONE, NULL, NULL } 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502-wpa.bin"); 1028c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502.bin"); 1038c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502d-wpa.bin"); 1048c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502d.bin"); 1058c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502e-wpa.bin"); 1068c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502e.bin"); 1078c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin"); 1088c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502_3com.bin"); 1098c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504-wpa.bin"); 1108c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504.bin"); 1118c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin"); 1128c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504_2958.bin"); 1138c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin"); 1148c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504a_2958.bin"); 1158c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c506-wpa.bin"); 1168c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c506.bin"); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define MAX_SSID_LENGTH 32 1198c2ecf20Sopenharmony_ci#define MGMT_JIFFIES (256 * HZ / 100) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define MAX_BSS_ENTRIES 64 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* registers */ 1248c2ecf20Sopenharmony_ci#define GCR 0x00 /* (SIR0) General Configuration Register */ 1258c2ecf20Sopenharmony_ci#define BSR 0x02 /* (SIR1) Bank Switching Select Register */ 1268c2ecf20Sopenharmony_ci#define AR 0x04 1278c2ecf20Sopenharmony_ci#define DR 0x08 1288c2ecf20Sopenharmony_ci#define MR1 0x12 /* Mirror Register 1 */ 1298c2ecf20Sopenharmony_ci#define MR2 0x14 /* Mirror Register 2 */ 1308c2ecf20Sopenharmony_ci#define MR3 0x16 /* Mirror Register 3 */ 1318c2ecf20Sopenharmony_ci#define MR4 0x18 /* Mirror Register 4 */ 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#define GPR1 0x0c 1348c2ecf20Sopenharmony_ci#define GPR2 0x0e 1358c2ecf20Sopenharmony_ci#define GPR3 0x10 1368c2ecf20Sopenharmony_ci/* 1378c2ecf20Sopenharmony_ci * Constants for the GCR register. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ci#define GCR_REMAP 0x0400 /* Remap internal SRAM to 0 */ 1408c2ecf20Sopenharmony_ci#define GCR_SWRES 0x0080 /* BIU reset (ARM and PAI are NOT reset) */ 1418c2ecf20Sopenharmony_ci#define GCR_CORES 0x0060 /* Core Reset (ARM and PAI are reset) */ 1428c2ecf20Sopenharmony_ci#define GCR_ENINT 0x0002 /* Enable Interrupts */ 1438c2ecf20Sopenharmony_ci#define GCR_ACKINT 0x0008 /* Acknowledge Interrupts */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#define BSS_SRAM 0x0200 /* AMBA module selection --> SRAM */ 1468c2ecf20Sopenharmony_ci#define BSS_IRAM 0x0100 /* AMBA module selection --> IRAM */ 1478c2ecf20Sopenharmony_ci/* 1488c2ecf20Sopenharmony_ci *Constants for the MR registers. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci#define MAC_INIT_COMPLETE 0x0001 /* MAC init has been completed */ 1518c2ecf20Sopenharmony_ci#define MAC_BOOT_COMPLETE 0x0010 /* MAC boot has been completed */ 1528c2ecf20Sopenharmony_ci#define MAC_INIT_OK 0x0002 /* MAC boot has been completed */ 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#define MIB_MAX_DATA_BYTES 212 1558c2ecf20Sopenharmony_ci#define MIB_HEADER_SIZE 4 /* first four fields */ 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistruct get_set_mib { 1588c2ecf20Sopenharmony_ci u8 type; 1598c2ecf20Sopenharmony_ci u8 size; 1608c2ecf20Sopenharmony_ci u8 index; 1618c2ecf20Sopenharmony_ci u8 reserved; 1628c2ecf20Sopenharmony_ci u8 data[MIB_MAX_DATA_BYTES]; 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistruct rx_desc { 1668c2ecf20Sopenharmony_ci u32 Next; 1678c2ecf20Sopenharmony_ci u16 MsduPos; 1688c2ecf20Sopenharmony_ci u16 MsduSize; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci u8 State; 1718c2ecf20Sopenharmony_ci u8 Status; 1728c2ecf20Sopenharmony_ci u8 Rate; 1738c2ecf20Sopenharmony_ci u8 Rssi; 1748c2ecf20Sopenharmony_ci u8 LinkQuality; 1758c2ecf20Sopenharmony_ci u8 PreambleType; 1768c2ecf20Sopenharmony_ci u16 Duration; 1778c2ecf20Sopenharmony_ci u32 RxTime; 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#define RX_DESC_FLAG_VALID 0x80 1818c2ecf20Sopenharmony_ci#define RX_DESC_FLAG_CONSUMED 0x40 1828c2ecf20Sopenharmony_ci#define RX_DESC_FLAG_IDLE 0x00 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci#define RX_STATUS_SUCCESS 0x00 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci#define RX_DESC_MSDU_POS_OFFSET 4 1878c2ecf20Sopenharmony_ci#define RX_DESC_MSDU_SIZE_OFFSET 6 1888c2ecf20Sopenharmony_ci#define RX_DESC_FLAGS_OFFSET 8 1898c2ecf20Sopenharmony_ci#define RX_DESC_STATUS_OFFSET 9 1908c2ecf20Sopenharmony_ci#define RX_DESC_RSSI_OFFSET 11 1918c2ecf20Sopenharmony_ci#define RX_DESC_LINK_QUALITY_OFFSET 12 1928c2ecf20Sopenharmony_ci#define RX_DESC_PREAMBLE_TYPE_OFFSET 13 1938c2ecf20Sopenharmony_ci#define RX_DESC_DURATION_OFFSET 14 1948c2ecf20Sopenharmony_ci#define RX_DESC_RX_TIME_OFFSET 16 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistruct tx_desc { 1978c2ecf20Sopenharmony_ci u32 NextDescriptor; 1988c2ecf20Sopenharmony_ci u16 TxStartOfFrame; 1998c2ecf20Sopenharmony_ci u16 TxLength; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci u8 TxState; 2028c2ecf20Sopenharmony_ci u8 TxStatus; 2038c2ecf20Sopenharmony_ci u8 RetryCount; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci u8 TxRate; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci u8 KeyIndex; 2088c2ecf20Sopenharmony_ci u8 ChiperType; 2098c2ecf20Sopenharmony_ci u8 ChipreLength; 2108c2ecf20Sopenharmony_ci u8 Reserved1; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci u8 Reserved; 2138c2ecf20Sopenharmony_ci u8 PacketType; 2148c2ecf20Sopenharmony_ci u16 HostTxLength; 2158c2ecf20Sopenharmony_ci}; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci#define TX_DESC_NEXT_OFFSET 0 2188c2ecf20Sopenharmony_ci#define TX_DESC_POS_OFFSET 4 2198c2ecf20Sopenharmony_ci#define TX_DESC_SIZE_OFFSET 6 2208c2ecf20Sopenharmony_ci#define TX_DESC_FLAGS_OFFSET 8 2218c2ecf20Sopenharmony_ci#define TX_DESC_STATUS_OFFSET 9 2228c2ecf20Sopenharmony_ci#define TX_DESC_RETRY_OFFSET 10 2238c2ecf20Sopenharmony_ci#define TX_DESC_RATE_OFFSET 11 2248c2ecf20Sopenharmony_ci#define TX_DESC_KEY_INDEX_OFFSET 12 2258c2ecf20Sopenharmony_ci#define TX_DESC_CIPHER_TYPE_OFFSET 13 2268c2ecf20Sopenharmony_ci#define TX_DESC_CIPHER_LENGTH_OFFSET 14 2278c2ecf20Sopenharmony_ci#define TX_DESC_PACKET_TYPE_OFFSET 17 2288c2ecf20Sopenharmony_ci#define TX_DESC_HOST_LENGTH_OFFSET 18 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* 2318c2ecf20Sopenharmony_ci * Host-MAC interface 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci#define TX_STATUS_SUCCESS 0x00 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci#define TX_FIRM_OWN 0x80 2378c2ecf20Sopenharmony_ci#define TX_DONE 0x40 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci#define TX_ERROR 0x01 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci#define TX_PACKET_TYPE_DATA 0x01 2428c2ecf20Sopenharmony_ci#define TX_PACKET_TYPE_MGMT 0x02 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci#define ISR_EMPTY 0x00 /* no bits set in ISR */ 2458c2ecf20Sopenharmony_ci#define ISR_TxCOMPLETE 0x01 /* packet transmitted */ 2468c2ecf20Sopenharmony_ci#define ISR_RxCOMPLETE 0x02 /* packet received */ 2478c2ecf20Sopenharmony_ci#define ISR_RxFRAMELOST 0x04 /* Rx Frame lost */ 2488c2ecf20Sopenharmony_ci#define ISR_FATAL_ERROR 0x08 /* Fatal error */ 2498c2ecf20Sopenharmony_ci#define ISR_COMMAND_COMPLETE 0x10 /* command completed */ 2508c2ecf20Sopenharmony_ci#define ISR_OUT_OF_RANGE 0x20 /* command completed */ 2518c2ecf20Sopenharmony_ci#define ISR_IBSS_MERGE 0x40 /* (4.1.2.30): IBSS merge */ 2528c2ecf20Sopenharmony_ci#define ISR_GENERIC_IRQ 0x80 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci#define Local_Mib_Type 0x01 2558c2ecf20Sopenharmony_ci#define Mac_Address_Mib_Type 0x02 2568c2ecf20Sopenharmony_ci#define Mac_Mib_Type 0x03 2578c2ecf20Sopenharmony_ci#define Statistics_Mib_Type 0x04 2588c2ecf20Sopenharmony_ci#define Mac_Mgmt_Mib_Type 0x05 2598c2ecf20Sopenharmony_ci#define Mac_Wep_Mib_Type 0x06 2608c2ecf20Sopenharmony_ci#define Phy_Mib_Type 0x07 2618c2ecf20Sopenharmony_ci#define Multi_Domain_MIB 0x08 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci#define MAC_MGMT_MIB_CUR_BSSID_POS 14 2648c2ecf20Sopenharmony_ci#define MAC_MIB_FRAG_THRESHOLD_POS 8 2658c2ecf20Sopenharmony_ci#define MAC_MIB_RTS_THRESHOLD_POS 10 2668c2ecf20Sopenharmony_ci#define MAC_MIB_SHORT_RETRY_POS 16 2678c2ecf20Sopenharmony_ci#define MAC_MIB_LONG_RETRY_POS 17 2688c2ecf20Sopenharmony_ci#define MAC_MIB_SHORT_RETRY_LIMIT_POS 16 2698c2ecf20Sopenharmony_ci#define MAC_MGMT_MIB_BEACON_PER_POS 0 2708c2ecf20Sopenharmony_ci#define MAC_MGMT_MIB_STATION_ID_POS 6 2718c2ecf20Sopenharmony_ci#define MAC_MGMT_MIB_CUR_PRIVACY_POS 11 2728c2ecf20Sopenharmony_ci#define MAC_MGMT_MIB_CUR_BSSID_POS 14 2738c2ecf20Sopenharmony_ci#define MAC_MGMT_MIB_PS_MODE_POS 53 2748c2ecf20Sopenharmony_ci#define MAC_MGMT_MIB_LISTEN_INTERVAL_POS 54 2758c2ecf20Sopenharmony_ci#define MAC_MGMT_MIB_MULTI_DOMAIN_IMPLEMENTED 56 2768c2ecf20Sopenharmony_ci#define MAC_MGMT_MIB_MULTI_DOMAIN_ENABLED 57 2778c2ecf20Sopenharmony_ci#define PHY_MIB_CHANNEL_POS 14 2788c2ecf20Sopenharmony_ci#define PHY_MIB_RATE_SET_POS 20 2798c2ecf20Sopenharmony_ci#define PHY_MIB_REG_DOMAIN_POS 26 2808c2ecf20Sopenharmony_ci#define LOCAL_MIB_AUTO_TX_RATE_POS 3 2818c2ecf20Sopenharmony_ci#define LOCAL_MIB_SSID_SIZE 5 2828c2ecf20Sopenharmony_ci#define LOCAL_MIB_TX_PROMISCUOUS_POS 6 2838c2ecf20Sopenharmony_ci#define LOCAL_MIB_TX_MGMT_RATE_POS 7 2848c2ecf20Sopenharmony_ci#define LOCAL_MIB_TX_CONTROL_RATE_POS 8 2858c2ecf20Sopenharmony_ci#define LOCAL_MIB_PREAMBLE_TYPE 9 2868c2ecf20Sopenharmony_ci#define MAC_ADDR_MIB_MAC_ADDR_POS 0 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci#define CMD_Set_MIB_Vars 0x01 2898c2ecf20Sopenharmony_ci#define CMD_Get_MIB_Vars 0x02 2908c2ecf20Sopenharmony_ci#define CMD_Scan 0x03 2918c2ecf20Sopenharmony_ci#define CMD_Join 0x04 2928c2ecf20Sopenharmony_ci#define CMD_Start 0x05 2938c2ecf20Sopenharmony_ci#define CMD_EnableRadio 0x06 2948c2ecf20Sopenharmony_ci#define CMD_DisableRadio 0x07 2958c2ecf20Sopenharmony_ci#define CMD_SiteSurvey 0x0B 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci#define CMD_STATUS_IDLE 0x00 2988c2ecf20Sopenharmony_ci#define CMD_STATUS_COMPLETE 0x01 2998c2ecf20Sopenharmony_ci#define CMD_STATUS_UNKNOWN 0x02 3008c2ecf20Sopenharmony_ci#define CMD_STATUS_INVALID_PARAMETER 0x03 3018c2ecf20Sopenharmony_ci#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 3028c2ecf20Sopenharmony_ci#define CMD_STATUS_TIME_OUT 0x07 3038c2ecf20Sopenharmony_ci#define CMD_STATUS_IN_PROGRESS 0x08 3048c2ecf20Sopenharmony_ci#define CMD_STATUS_REJECTED_RADIO_OFF 0x09 3058c2ecf20Sopenharmony_ci#define CMD_STATUS_HOST_ERROR 0xFF 3068c2ecf20Sopenharmony_ci#define CMD_STATUS_BUSY 0xFE 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci#define CMD_BLOCK_COMMAND_OFFSET 0 3098c2ecf20Sopenharmony_ci#define CMD_BLOCK_STATUS_OFFSET 1 3108c2ecf20Sopenharmony_ci#define CMD_BLOCK_PARAMETERS_OFFSET 4 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci#define SCAN_OPTIONS_SITE_SURVEY 0x80 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci#define MGMT_FRAME_BODY_OFFSET 24 3158c2ecf20Sopenharmony_ci#define MAX_AUTHENTICATION_RETRIES 3 3168c2ecf20Sopenharmony_ci#define MAX_ASSOCIATION_RETRIES 3 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci#define AUTHENTICATION_RESPONSE_TIME_OUT 1000 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci#define MAX_WIRELESS_BODY 2316 /* mtu is 2312, CRC is 4 */ 3218c2ecf20Sopenharmony_ci#define LOOP_RETRY_LIMIT 500000 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci#define ACTIVE_MODE 1 3248c2ecf20Sopenharmony_ci#define PS_MODE 2 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci#define MAX_ENCRYPTION_KEYS 4 3278c2ecf20Sopenharmony_ci#define MAX_ENCRYPTION_KEY_SIZE 40 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/* 3308c2ecf20Sopenharmony_ci * 802.11 related definitions 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* 3348c2ecf20Sopenharmony_ci * Regulatory Domains 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci#define REG_DOMAIN_FCC 0x10 /* Channels 1-11 USA */ 3388c2ecf20Sopenharmony_ci#define REG_DOMAIN_DOC 0x20 /* Channel 1-11 Canada */ 3398c2ecf20Sopenharmony_ci#define REG_DOMAIN_ETSI 0x30 /* Channel 1-13 Europe (ex Spain/France) */ 3408c2ecf20Sopenharmony_ci#define REG_DOMAIN_SPAIN 0x31 /* Channel 10-11 Spain */ 3418c2ecf20Sopenharmony_ci#define REG_DOMAIN_FRANCE 0x32 /* Channel 10-13 France */ 3428c2ecf20Sopenharmony_ci#define REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */ 3438c2ecf20Sopenharmony_ci#define REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan(MKK1) */ 3448c2ecf20Sopenharmony_ci#define REG_DOMAIN_ISRAEL 0x50 /* Channel 3-9 ISRAEL */ 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci#define BSS_TYPE_AD_HOC 1 3478c2ecf20Sopenharmony_ci#define BSS_TYPE_INFRASTRUCTURE 2 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci#define SCAN_TYPE_ACTIVE 0 3508c2ecf20Sopenharmony_ci#define SCAN_TYPE_PASSIVE 1 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci#define LONG_PREAMBLE 0 3538c2ecf20Sopenharmony_ci#define SHORT_PREAMBLE 1 3548c2ecf20Sopenharmony_ci#define AUTO_PREAMBLE 2 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci#define DATA_FRAME_WS_HEADER_SIZE 30 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/* promiscuous mode control */ 3598c2ecf20Sopenharmony_ci#define PROM_MODE_OFF 0x0 3608c2ecf20Sopenharmony_ci#define PROM_MODE_UNKNOWN 0x1 3618c2ecf20Sopenharmony_ci#define PROM_MODE_CRC_FAILED 0x2 3628c2ecf20Sopenharmony_ci#define PROM_MODE_DUPLICATED 0x4 3638c2ecf20Sopenharmony_ci#define PROM_MODE_MGMT 0x8 3648c2ecf20Sopenharmony_ci#define PROM_MODE_CTRL 0x10 3658c2ecf20Sopenharmony_ci#define PROM_MODE_BAD_PROTOCOL 0x20 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci#define IFACE_INT_STATUS_OFFSET 0 3688c2ecf20Sopenharmony_ci#define IFACE_INT_MASK_OFFSET 1 3698c2ecf20Sopenharmony_ci#define IFACE_LOCKOUT_HOST_OFFSET 2 3708c2ecf20Sopenharmony_ci#define IFACE_LOCKOUT_MAC_OFFSET 3 3718c2ecf20Sopenharmony_ci#define IFACE_FUNC_CTRL_OFFSET 28 3728c2ecf20Sopenharmony_ci#define IFACE_MAC_STAT_OFFSET 30 3738c2ecf20Sopenharmony_ci#define IFACE_GENERIC_INT_TYPE_OFFSET 32 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci#define CIPHER_SUITE_NONE 0 3768c2ecf20Sopenharmony_ci#define CIPHER_SUITE_WEP_64 1 3778c2ecf20Sopenharmony_ci#define CIPHER_SUITE_TKIP 2 3788c2ecf20Sopenharmony_ci#define CIPHER_SUITE_AES 3 3798c2ecf20Sopenharmony_ci#define CIPHER_SUITE_CCX 4 3808c2ecf20Sopenharmony_ci#define CIPHER_SUITE_WEP_128 5 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/* 3838c2ecf20Sopenharmony_ci * IFACE MACROS & definitions 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* 3878c2ecf20Sopenharmony_ci * FuncCtrl field: 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci#define FUNC_CTRL_TxENABLE 0x10 3908c2ecf20Sopenharmony_ci#define FUNC_CTRL_RxENABLE 0x20 3918c2ecf20Sopenharmony_ci#define FUNC_CTRL_INIT_COMPLETE 0x01 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* A stub firmware image which reads the MAC address from NVRAM on the card. 3948c2ecf20Sopenharmony_ci For copyright information and source see the end of this file. */ 3958c2ecf20Sopenharmony_cistatic u8 mac_reader[] = { 3968c2ecf20Sopenharmony_ci 0x06, 0x00, 0x00, 0xea, 0x04, 0x00, 0x00, 0xea, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xea, 3978c2ecf20Sopenharmony_ci 0x01, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 3988c2ecf20Sopenharmony_ci 0xd3, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x21, 0xe1, 0x0e, 0x04, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 3998c2ecf20Sopenharmony_ci 0x81, 0x11, 0xa0, 0xe1, 0x00, 0x10, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x1c, 0x10, 0x90, 0xe5, 4008c2ecf20Sopenharmony_ci 0x10, 0x10, 0xc1, 0xe3, 0x1c, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 0x08, 0x10, 0x80, 0xe5, 4018c2ecf20Sopenharmony_ci 0x02, 0x03, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0xb0, 0x10, 0xc0, 0xe1, 0xb4, 0x10, 0xc0, 0xe1, 4028c2ecf20Sopenharmony_ci 0xb8, 0x10, 0xc0, 0xe1, 0xbc, 0x10, 0xc0, 0xe1, 0x56, 0xdc, 0xa0, 0xe3, 0x21, 0x00, 0x00, 0xeb, 4038c2ecf20Sopenharmony_ci 0x0a, 0x00, 0xa0, 0xe3, 0x1a, 0x00, 0x00, 0xeb, 0x10, 0x00, 0x00, 0xeb, 0x07, 0x00, 0x00, 0xeb, 4048c2ecf20Sopenharmony_ci 0x02, 0x03, 0xa0, 0xe3, 0x02, 0x14, 0xa0, 0xe3, 0xb4, 0x10, 0xc0, 0xe1, 0x4c, 0x10, 0x9f, 0xe5, 4058c2ecf20Sopenharmony_ci 0xbc, 0x10, 0xc0, 0xe1, 0x10, 0x10, 0xa0, 0xe3, 0xb8, 0x10, 0xc0, 0xe1, 0xfe, 0xff, 0xff, 0xea, 4068c2ecf20Sopenharmony_ci 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe3, 0x02, 0x3c, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 4078c2ecf20Sopenharmony_ci 0x28, 0x00, 0x9f, 0xe5, 0x37, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 4088c2ecf20Sopenharmony_ci 0x00, 0x40, 0x2d, 0xe9, 0x12, 0x2e, 0xa0, 0xe3, 0x06, 0x30, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 4098c2ecf20Sopenharmony_ci 0x02, 0x04, 0xa0, 0xe3, 0x2f, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 4108c2ecf20Sopenharmony_ci 0x00, 0x02, 0x00, 0x02, 0x80, 0x01, 0x90, 0xe0, 0x01, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x50, 0xe2, 4118c2ecf20Sopenharmony_ci 0xfc, 0xff, 0xff, 0xea, 0x1e, 0xff, 0x2f, 0xe1, 0x80, 0x10, 0xa0, 0xe3, 0xf3, 0x06, 0xa0, 0xe3, 4128c2ecf20Sopenharmony_ci 0x00, 0x10, 0x80, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 4138c2ecf20Sopenharmony_ci 0x04, 0x10, 0x80, 0xe5, 0x00, 0x10, 0x80, 0xe5, 0x0e, 0x34, 0xa0, 0xe3, 0x1c, 0x10, 0x93, 0xe5, 4148c2ecf20Sopenharmony_ci 0x02, 0x1a, 0x81, 0xe3, 0x1c, 0x10, 0x83, 0xe5, 0x58, 0x11, 0x9f, 0xe5, 0x30, 0x10, 0x80, 0xe5, 4158c2ecf20Sopenharmony_ci 0x54, 0x11, 0x9f, 0xe5, 0x34, 0x10, 0x80, 0xe5, 0x38, 0x10, 0x80, 0xe5, 0x3c, 0x10, 0x80, 0xe5, 4168c2ecf20Sopenharmony_ci 0x10, 0x10, 0x90, 0xe5, 0x08, 0x00, 0x90, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0xf3, 0x16, 0xa0, 0xe3, 4178c2ecf20Sopenharmony_ci 0x08, 0x00, 0x91, 0xe5, 0x05, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 0x10, 0x00, 0x91, 0xe5, 4188c2ecf20Sopenharmony_ci 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0xff, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 4198c2ecf20Sopenharmony_ci 0x10, 0x00, 0x91, 0xe5, 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, 4208c2ecf20Sopenharmony_ci 0x10, 0x00, 0x91, 0xe5, 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, 4218c2ecf20Sopenharmony_ci 0xff, 0x00, 0x00, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x30, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe1, 4228c2ecf20Sopenharmony_ci 0x03, 0x40, 0xa0, 0xe1, 0xa2, 0x02, 0xa0, 0xe1, 0x08, 0x00, 0x00, 0xe2, 0x03, 0x00, 0x80, 0xe2, 4238c2ecf20Sopenharmony_ci 0xd8, 0x10, 0x9f, 0xe5, 0x00, 0x00, 0xc1, 0xe5, 0x01, 0x20, 0xc1, 0xe5, 0xe2, 0xff, 0xff, 0xeb, 4248c2ecf20Sopenharmony_ci 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x14, 0x00, 0xa0, 0xe3, 0xc4, 0xff, 0xff, 0xeb, 4258c2ecf20Sopenharmony_ci 0x04, 0x20, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1, 0x02, 0x00, 0xa0, 0xe3, 0x01, 0x00, 0x00, 0xeb, 4268c2ecf20Sopenharmony_ci 0x30, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x70, 0x40, 0x2d, 0xe9, 0xf3, 0x46, 0xa0, 0xe3, 4278c2ecf20Sopenharmony_ci 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x08, 0x00, 0x00, 0x9a, 0x8c, 0x50, 0x9f, 0xe5, 4288c2ecf20Sopenharmony_ci 0x03, 0x60, 0xd5, 0xe7, 0x0c, 0x60, 0x84, 0xe5, 0x10, 0x60, 0x94, 0xe5, 0x02, 0x00, 0x16, 0xe3, 4298c2ecf20Sopenharmony_ci 0xfc, 0xff, 0xff, 0x0a, 0x01, 0x30, 0x83, 0xe2, 0x00, 0x00, 0x53, 0xe1, 0xf7, 0xff, 0xff, 0x3a, 4308c2ecf20Sopenharmony_ci 0xff, 0x30, 0xa0, 0xe3, 0x0c, 0x30, 0x84, 0xe5, 0x08, 0x00, 0x94, 0xe5, 0x10, 0x00, 0x94, 0xe5, 4318c2ecf20Sopenharmony_ci 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x94, 0xe5, 0x00, 0x00, 0xa0, 0xe3, 4328c2ecf20Sopenharmony_ci 0x00, 0x00, 0x52, 0xe3, 0x0b, 0x00, 0x00, 0x9a, 0x10, 0x50, 0x94, 0xe5, 0x02, 0x00, 0x15, 0xe3, 4338c2ecf20Sopenharmony_ci 0xfc, 0xff, 0xff, 0x0a, 0x0c, 0x30, 0x84, 0xe5, 0x10, 0x50, 0x94, 0xe5, 0x01, 0x00, 0x15, 0xe3, 4348c2ecf20Sopenharmony_ci 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x50, 0x94, 0xe5, 0x01, 0x50, 0xc1, 0xe4, 0x01, 0x00, 0x80, 0xe2, 4358c2ecf20Sopenharmony_ci 0x02, 0x00, 0x50, 0xe1, 0xf3, 0xff, 0xff, 0x3a, 0xc8, 0x00, 0xa0, 0xe3, 0x98, 0xff, 0xff, 0xeb, 4368c2ecf20Sopenharmony_ci 0x70, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x0c, 0x00, 0x02, 0x01, 0x02, 0x00, 0x02, 4378c2ecf20Sopenharmony_ci 0x00, 0x01, 0x00, 0x02 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistruct atmel_private { 4418c2ecf20Sopenharmony_ci void *card; /* Bus dependent structure varies for PCcard */ 4428c2ecf20Sopenharmony_ci int (*present_callback)(void *); /* And callback which uses it */ 4438c2ecf20Sopenharmony_ci char firmware_id[32]; 4448c2ecf20Sopenharmony_ci AtmelFWType firmware_type; 4458c2ecf20Sopenharmony_ci u8 *firmware; 4468c2ecf20Sopenharmony_ci int firmware_length; 4478c2ecf20Sopenharmony_ci struct timer_list management_timer; 4488c2ecf20Sopenharmony_ci struct net_device *dev; 4498c2ecf20Sopenharmony_ci struct device *sys_dev; 4508c2ecf20Sopenharmony_ci struct iw_statistics wstats; 4518c2ecf20Sopenharmony_ci spinlock_t irqlock, timerlock; /* spinlocks */ 4528c2ecf20Sopenharmony_ci enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type; 4538c2ecf20Sopenharmony_ci enum { 4548c2ecf20Sopenharmony_ci CARD_TYPE_PARALLEL_FLASH, 4558c2ecf20Sopenharmony_ci CARD_TYPE_SPI_FLASH, 4568c2ecf20Sopenharmony_ci CARD_TYPE_EEPROM 4578c2ecf20Sopenharmony_ci } card_type; 4588c2ecf20Sopenharmony_ci int do_rx_crc; /* If we need to CRC incoming packets */ 4598c2ecf20Sopenharmony_ci int probe_crc; /* set if we don't yet know */ 4608c2ecf20Sopenharmony_ci int crc_ok_cnt, crc_ko_cnt; /* counters for probing */ 4618c2ecf20Sopenharmony_ci u16 rx_desc_head; 4628c2ecf20Sopenharmony_ci u16 tx_desc_free, tx_desc_head, tx_desc_tail, tx_desc_previous; 4638c2ecf20Sopenharmony_ci u16 tx_free_mem, tx_buff_head, tx_buff_tail; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci u16 frag_seq, frag_len, frag_no; 4668c2ecf20Sopenharmony_ci u8 frag_source[6]; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci u8 wep_is_on, default_key, exclude_unencrypted, encryption_level; 4698c2ecf20Sopenharmony_ci u8 group_cipher_suite, pairwise_cipher_suite; 4708c2ecf20Sopenharmony_ci u8 wep_keys[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; 4718c2ecf20Sopenharmony_ci int wep_key_len[MAX_ENCRYPTION_KEYS]; 4728c2ecf20Sopenharmony_ci int use_wpa, radio_on_broken; /* firmware dependent stuff. */ 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci u16 host_info_base; 4758c2ecf20Sopenharmony_ci struct host_info_struct { 4768c2ecf20Sopenharmony_ci /* NB this is matched to the hardware, don't change. */ 4778c2ecf20Sopenharmony_ci u8 volatile int_status; 4788c2ecf20Sopenharmony_ci u8 volatile int_mask; 4798c2ecf20Sopenharmony_ci u8 volatile lockout_host; 4808c2ecf20Sopenharmony_ci u8 volatile lockout_mac; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci u16 tx_buff_pos; 4838c2ecf20Sopenharmony_ci u16 tx_buff_size; 4848c2ecf20Sopenharmony_ci u16 tx_desc_pos; 4858c2ecf20Sopenharmony_ci u16 tx_desc_count; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci u16 rx_buff_pos; 4888c2ecf20Sopenharmony_ci u16 rx_buff_size; 4898c2ecf20Sopenharmony_ci u16 rx_desc_pos; 4908c2ecf20Sopenharmony_ci u16 rx_desc_count; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci u16 build_version; 4938c2ecf20Sopenharmony_ci u16 command_pos; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci u16 major_version; 4968c2ecf20Sopenharmony_ci u16 minor_version; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci u16 func_ctrl; 4998c2ecf20Sopenharmony_ci u16 mac_status; 5008c2ecf20Sopenharmony_ci u16 generic_IRQ_type; 5018c2ecf20Sopenharmony_ci u8 reserved[2]; 5028c2ecf20Sopenharmony_ci } host_info; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci enum { 5058c2ecf20Sopenharmony_ci STATION_STATE_SCANNING, 5068c2ecf20Sopenharmony_ci STATION_STATE_JOINNING, 5078c2ecf20Sopenharmony_ci STATION_STATE_AUTHENTICATING, 5088c2ecf20Sopenharmony_ci STATION_STATE_ASSOCIATING, 5098c2ecf20Sopenharmony_ci STATION_STATE_READY, 5108c2ecf20Sopenharmony_ci STATION_STATE_REASSOCIATING, 5118c2ecf20Sopenharmony_ci STATION_STATE_DOWN, 5128c2ecf20Sopenharmony_ci STATION_STATE_MGMT_ERROR 5138c2ecf20Sopenharmony_ci } station_state; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci int operating_mode, power_mode; 5168c2ecf20Sopenharmony_ci unsigned long last_qual; 5178c2ecf20Sopenharmony_ci int beacons_this_sec; 5188c2ecf20Sopenharmony_ci int channel; 5198c2ecf20Sopenharmony_ci int reg_domain, config_reg_domain; 5208c2ecf20Sopenharmony_ci int tx_rate; 5218c2ecf20Sopenharmony_ci int auto_tx_rate; 5228c2ecf20Sopenharmony_ci int rts_threshold; 5238c2ecf20Sopenharmony_ci int frag_threshold; 5248c2ecf20Sopenharmony_ci int long_retry, short_retry; 5258c2ecf20Sopenharmony_ci int preamble; 5268c2ecf20Sopenharmony_ci int default_beacon_period, beacon_period, listen_interval; 5278c2ecf20Sopenharmony_ci int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum; 5288c2ecf20Sopenharmony_ci int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt; 5298c2ecf20Sopenharmony_ci enum { 5308c2ecf20Sopenharmony_ci SITE_SURVEY_IDLE, 5318c2ecf20Sopenharmony_ci SITE_SURVEY_IN_PROGRESS, 5328c2ecf20Sopenharmony_ci SITE_SURVEY_COMPLETED 5338c2ecf20Sopenharmony_ci } site_survey_state; 5348c2ecf20Sopenharmony_ci unsigned long last_survey; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci int station_was_associated, station_is_associated; 5378c2ecf20Sopenharmony_ci int fast_scan; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci struct bss_info { 5408c2ecf20Sopenharmony_ci int channel; 5418c2ecf20Sopenharmony_ci int SSIDsize; 5428c2ecf20Sopenharmony_ci int RSSI; 5438c2ecf20Sopenharmony_ci int UsingWEP; 5448c2ecf20Sopenharmony_ci int preamble; 5458c2ecf20Sopenharmony_ci int beacon_period; 5468c2ecf20Sopenharmony_ci int BSStype; 5478c2ecf20Sopenharmony_ci u8 BSSID[6]; 5488c2ecf20Sopenharmony_ci u8 SSID[MAX_SSID_LENGTH]; 5498c2ecf20Sopenharmony_ci } BSSinfo[MAX_BSS_ENTRIES]; 5508c2ecf20Sopenharmony_ci int BSS_list_entries, current_BSS; 5518c2ecf20Sopenharmony_ci int connect_to_any_BSS; 5528c2ecf20Sopenharmony_ci int SSID_size, new_SSID_size; 5538c2ecf20Sopenharmony_ci u8 CurrentBSSID[6], BSSID[6]; 5548c2ecf20Sopenharmony_ci u8 SSID[MAX_SSID_LENGTH], new_SSID[MAX_SSID_LENGTH]; 5558c2ecf20Sopenharmony_ci u64 last_beacon_timestamp; 5568c2ecf20Sopenharmony_ci u8 rx_buf[MAX_WIRELESS_BODY]; 5578c2ecf20Sopenharmony_ci}; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic u8 atmel_basic_rates[4] = {0x82, 0x84, 0x0b, 0x16}; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic const struct { 5628c2ecf20Sopenharmony_ci int reg_domain; 5638c2ecf20Sopenharmony_ci int min, max; 5648c2ecf20Sopenharmony_ci char *name; 5658c2ecf20Sopenharmony_ci} channel_table[] = { { REG_DOMAIN_FCC, 1, 11, "USA" }, 5668c2ecf20Sopenharmony_ci { REG_DOMAIN_DOC, 1, 11, "Canada" }, 5678c2ecf20Sopenharmony_ci { REG_DOMAIN_ETSI, 1, 13, "Europe" }, 5688c2ecf20Sopenharmony_ci { REG_DOMAIN_SPAIN, 10, 11, "Spain" }, 5698c2ecf20Sopenharmony_ci { REG_DOMAIN_FRANCE, 10, 13, "France" }, 5708c2ecf20Sopenharmony_ci { REG_DOMAIN_MKK, 14, 14, "MKK" }, 5718c2ecf20Sopenharmony_ci { REG_DOMAIN_MKK1, 1, 14, "MKK1" }, 5728c2ecf20Sopenharmony_ci { REG_DOMAIN_ISRAEL, 3, 9, "Israel"} }; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic void build_wpa_mib(struct atmel_private *priv); 5758c2ecf20Sopenharmony_cistatic int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); 5768c2ecf20Sopenharmony_cistatic void atmel_copy_to_card(struct net_device *dev, u16 dest, 5778c2ecf20Sopenharmony_ci const unsigned char *src, u16 len); 5788c2ecf20Sopenharmony_cistatic void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, 5798c2ecf20Sopenharmony_ci u16 src, u16 len); 5808c2ecf20Sopenharmony_cistatic void atmel_set_gcr(struct net_device *dev, u16 mask); 5818c2ecf20Sopenharmony_cistatic void atmel_clear_gcr(struct net_device *dev, u16 mask); 5828c2ecf20Sopenharmony_cistatic int atmel_lock_mac(struct atmel_private *priv); 5838c2ecf20Sopenharmony_cistatic void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data); 5848c2ecf20Sopenharmony_cistatic void atmel_command_irq(struct atmel_private *priv); 5858c2ecf20Sopenharmony_cistatic int atmel_validate_channel(struct atmel_private *priv, int channel); 5868c2ecf20Sopenharmony_cistatic void atmel_management_frame(struct atmel_private *priv, 5878c2ecf20Sopenharmony_ci struct ieee80211_hdr *header, 5888c2ecf20Sopenharmony_ci u16 frame_len, u8 rssi); 5898c2ecf20Sopenharmony_cistatic void atmel_management_timer(struct timer_list *t); 5908c2ecf20Sopenharmony_cistatic void atmel_send_command(struct atmel_private *priv, int command, 5918c2ecf20Sopenharmony_ci void *cmd, int cmd_size); 5928c2ecf20Sopenharmony_cistatic int atmel_send_command_wait(struct atmel_private *priv, int command, 5938c2ecf20Sopenharmony_ci void *cmd, int cmd_size); 5948c2ecf20Sopenharmony_cistatic void atmel_transmit_management_frame(struct atmel_private *priv, 5958c2ecf20Sopenharmony_ci struct ieee80211_hdr *header, 5968c2ecf20Sopenharmony_ci u8 *body, int body_len); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index); 5998c2ecf20Sopenharmony_cistatic void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, 6008c2ecf20Sopenharmony_ci u8 data); 6018c2ecf20Sopenharmony_cistatic void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, 6028c2ecf20Sopenharmony_ci u16 data); 6038c2ecf20Sopenharmony_cistatic void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, 6048c2ecf20Sopenharmony_ci u8 *data, int data_len); 6058c2ecf20Sopenharmony_cistatic void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, 6068c2ecf20Sopenharmony_ci u8 *data, int data_len); 6078c2ecf20Sopenharmony_cistatic void atmel_scan(struct atmel_private *priv, int specific_ssid); 6088c2ecf20Sopenharmony_cistatic void atmel_join_bss(struct atmel_private *priv, int bss_index); 6098c2ecf20Sopenharmony_cistatic void atmel_smooth_qual(struct atmel_private *priv); 6108c2ecf20Sopenharmony_cistatic void atmel_writeAR(struct net_device *dev, u16 data); 6118c2ecf20Sopenharmony_cistatic int probe_atmel_card(struct net_device *dev); 6128c2ecf20Sopenharmony_cistatic int reset_atmel_card(struct net_device *dev); 6138c2ecf20Sopenharmony_cistatic void atmel_enter_state(struct atmel_private *priv, int new_state); 6148c2ecf20Sopenharmony_ciint atmel_open (struct net_device *dev); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic inline u16 atmel_hi(struct atmel_private *priv, u16 offset) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci return priv->host_info_base + offset; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic inline u16 atmel_co(struct atmel_private *priv, u16 offset) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci return priv->host_info.command_pos + offset; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci return priv->host_info.rx_desc_pos + (sizeof(struct rx_desc) * desc) + offset; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci return priv->host_info.tx_desc_pos + (sizeof(struct tx_desc) * desc) + offset; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic inline u8 atmel_read8(struct net_device *dev, u16 offset) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci return inb(dev->base_addr + offset); 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic inline void atmel_write8(struct net_device *dev, u16 offset, u8 data) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci outb(data, dev->base_addr + offset); 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic inline u16 atmel_read16(struct net_device *dev, u16 offset) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci return inw(dev->base_addr + offset); 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic inline void atmel_write16(struct net_device *dev, u16 offset, u16 data) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci outw(data, dev->base_addr + offset); 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic inline u8 atmel_rmem8(struct atmel_private *priv, u16 pos) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci atmel_writeAR(priv->dev, pos); 6598c2ecf20Sopenharmony_ci return atmel_read8(priv->dev, DR); 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci atmel_writeAR(priv->dev, pos); 6658c2ecf20Sopenharmony_ci atmel_write8(priv->dev, DR, data); 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci atmel_writeAR(priv->dev, pos); 6718c2ecf20Sopenharmony_ci return atmel_read16(priv->dev, DR); 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci atmel_writeAR(priv->dev, pos); 6778c2ecf20Sopenharmony_ci atmel_write16(priv->dev, DR, data); 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic const struct iw_handler_def atmel_handler_def; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic void tx_done_irq(struct atmel_private *priv) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci int i; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci for (i = 0; 6878c2ecf20Sopenharmony_ci atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE && 6888c2ecf20Sopenharmony_ci i < priv->host_info.tx_desc_count; 6898c2ecf20Sopenharmony_ci i++) { 6908c2ecf20Sopenharmony_ci u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head)); 6918c2ecf20Sopenharmony_ci u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head)); 6928c2ecf20Sopenharmony_ci u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head)); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head), 0); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci priv->tx_free_mem += msdu_size; 6978c2ecf20Sopenharmony_ci priv->tx_desc_free++; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (priv->tx_buff_head + msdu_size > (priv->host_info.tx_buff_pos + priv->host_info.tx_buff_size)) 7008c2ecf20Sopenharmony_ci priv->tx_buff_head = 0; 7018c2ecf20Sopenharmony_ci else 7028c2ecf20Sopenharmony_ci priv->tx_buff_head += msdu_size; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1)) 7058c2ecf20Sopenharmony_ci priv->tx_desc_head++ ; 7068c2ecf20Sopenharmony_ci else 7078c2ecf20Sopenharmony_ci priv->tx_desc_head = 0; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (type == TX_PACKET_TYPE_DATA) { 7108c2ecf20Sopenharmony_ci if (status == TX_STATUS_SUCCESS) 7118c2ecf20Sopenharmony_ci priv->dev->stats.tx_packets++; 7128c2ecf20Sopenharmony_ci else 7138c2ecf20Sopenharmony_ci priv->dev->stats.tx_errors++; 7148c2ecf20Sopenharmony_ci netif_wake_queue(priv->dev); 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic u16 find_tx_buff(struct atmel_private *priv, u16 len) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci u16 bottom_free = priv->host_info.tx_buff_size - priv->tx_buff_tail; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (priv->tx_desc_free == 3 || priv->tx_free_mem < len) 7248c2ecf20Sopenharmony_ci return 0; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (bottom_free >= len) 7278c2ecf20Sopenharmony_ci return priv->host_info.tx_buff_pos + priv->tx_buff_tail; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (priv->tx_free_mem - bottom_free >= len) { 7308c2ecf20Sopenharmony_ci priv->tx_buff_tail = 0; 7318c2ecf20Sopenharmony_ci return priv->host_info.tx_buff_pos; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci return 0; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic void tx_update_descriptor(struct atmel_private *priv, int is_bcast, 7388c2ecf20Sopenharmony_ci u16 len, u16 buff, u8 type) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, priv->tx_desc_tail), buff); 7418c2ecf20Sopenharmony_ci atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_tail), len); 7428c2ecf20Sopenharmony_ci if (!priv->use_wpa) 7438c2ecf20Sopenharmony_ci atmel_wmem16(priv, atmel_tx(priv, TX_DESC_HOST_LENGTH_OFFSET, priv->tx_desc_tail), len); 7448c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_tail), type); 7458c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RATE_OFFSET, priv->tx_desc_tail), priv->tx_rate); 7468c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RETRY_OFFSET, priv->tx_desc_tail), 0); 7478c2ecf20Sopenharmony_ci if (priv->use_wpa) { 7488c2ecf20Sopenharmony_ci int cipher_type, cipher_length; 7498c2ecf20Sopenharmony_ci if (is_bcast) { 7508c2ecf20Sopenharmony_ci cipher_type = priv->group_cipher_suite; 7518c2ecf20Sopenharmony_ci if (cipher_type == CIPHER_SUITE_WEP_64 || 7528c2ecf20Sopenharmony_ci cipher_type == CIPHER_SUITE_WEP_128) 7538c2ecf20Sopenharmony_ci cipher_length = 8; 7548c2ecf20Sopenharmony_ci else if (cipher_type == CIPHER_SUITE_TKIP) 7558c2ecf20Sopenharmony_ci cipher_length = 12; 7568c2ecf20Sopenharmony_ci else if (priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_64 || 7578c2ecf20Sopenharmony_ci priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_128) { 7588c2ecf20Sopenharmony_ci cipher_type = priv->pairwise_cipher_suite; 7598c2ecf20Sopenharmony_ci cipher_length = 8; 7608c2ecf20Sopenharmony_ci } else { 7618c2ecf20Sopenharmony_ci cipher_type = CIPHER_SUITE_NONE; 7628c2ecf20Sopenharmony_ci cipher_length = 0; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci } else { 7658c2ecf20Sopenharmony_ci cipher_type = priv->pairwise_cipher_suite; 7668c2ecf20Sopenharmony_ci if (cipher_type == CIPHER_SUITE_WEP_64 || 7678c2ecf20Sopenharmony_ci cipher_type == CIPHER_SUITE_WEP_128) 7688c2ecf20Sopenharmony_ci cipher_length = 8; 7698c2ecf20Sopenharmony_ci else if (cipher_type == CIPHER_SUITE_TKIP) 7708c2ecf20Sopenharmony_ci cipher_length = 12; 7718c2ecf20Sopenharmony_ci else if (priv->group_cipher_suite == CIPHER_SUITE_WEP_64 || 7728c2ecf20Sopenharmony_ci priv->group_cipher_suite == CIPHER_SUITE_WEP_128) { 7738c2ecf20Sopenharmony_ci cipher_type = priv->group_cipher_suite; 7748c2ecf20Sopenharmony_ci cipher_length = 8; 7758c2ecf20Sopenharmony_ci } else { 7768c2ecf20Sopenharmony_ci cipher_type = CIPHER_SUITE_NONE; 7778c2ecf20Sopenharmony_ci cipher_length = 0; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail), 7828c2ecf20Sopenharmony_ci cipher_type); 7838c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_LENGTH_OFFSET, priv->tx_desc_tail), 7848c2ecf20Sopenharmony_ci cipher_length); 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_tail), 0x80000000L); 7878c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_tail), TX_FIRM_OWN); 7888c2ecf20Sopenharmony_ci if (priv->tx_desc_previous != priv->tx_desc_tail) 7898c2ecf20Sopenharmony_ci atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_previous), 0); 7908c2ecf20Sopenharmony_ci priv->tx_desc_previous = priv->tx_desc_tail; 7918c2ecf20Sopenharmony_ci if (priv->tx_desc_tail < (priv->host_info.tx_desc_count - 1)) 7928c2ecf20Sopenharmony_ci priv->tx_desc_tail++; 7938c2ecf20Sopenharmony_ci else 7948c2ecf20Sopenharmony_ci priv->tx_desc_tail = 0; 7958c2ecf20Sopenharmony_ci priv->tx_desc_free--; 7968c2ecf20Sopenharmony_ci priv->tx_free_mem -= len; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 8028c2ecf20Sopenharmony_ci struct ieee80211_hdr header; 8038c2ecf20Sopenharmony_ci unsigned long flags; 8048c2ecf20Sopenharmony_ci u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (priv->card && priv->present_callback && 8078c2ecf20Sopenharmony_ci !(*priv->present_callback)(priv->card)) { 8088c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 8098c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 8108c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (priv->station_state != STATION_STATE_READY) { 8148c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 8158c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 8168c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci /* first ensure the timer func cannot run */ 8208c2ecf20Sopenharmony_ci spin_lock_bh(&priv->timerlock); 8218c2ecf20Sopenharmony_ci /* then stop the hardware ISR */ 8228c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->irqlock, flags); 8238c2ecf20Sopenharmony_ci /* nb doing the above in the opposite order will deadlock */ 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the 8268c2ecf20Sopenharmony_ci 12 first bytes (containing DA/SA) and put them in the appropriate 8278c2ecf20Sopenharmony_ci fields of the Wireless Header. Thus the packet length is then the 8288c2ecf20Sopenharmony_ci initial + 18 (+30-12) */ 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (!(buff = find_tx_buff(priv, len + 18))) { 8318c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 8328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 8338c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->timerlock); 8348c2ecf20Sopenharmony_ci netif_stop_queue(dev); 8358c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci frame_ctl = IEEE80211_FTYPE_DATA; 8398c2ecf20Sopenharmony_ci header.duration_id = 0; 8408c2ecf20Sopenharmony_ci header.seq_ctrl = 0; 8418c2ecf20Sopenharmony_ci if (priv->wep_is_on) 8428c2ecf20Sopenharmony_ci frame_ctl |= IEEE80211_FCTL_PROTECTED; 8438c2ecf20Sopenharmony_ci if (priv->operating_mode == IW_MODE_ADHOC) { 8448c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN); 8458c2ecf20Sopenharmony_ci memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); 8468c2ecf20Sopenharmony_ci memcpy(&header.addr3, priv->BSSID, ETH_ALEN); 8478c2ecf20Sopenharmony_ci } else { 8488c2ecf20Sopenharmony_ci frame_ctl |= IEEE80211_FCTL_TODS; 8498c2ecf20Sopenharmony_ci memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN); 8508c2ecf20Sopenharmony_ci memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); 8518c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN); 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (priv->use_wpa) 8558c2ecf20Sopenharmony_ci memcpy(&header.addr4, rfc1042_header, ETH_ALEN); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci header.frame_control = cpu_to_le16(frame_ctl); 8588c2ecf20Sopenharmony_ci /* Copy the wireless header into the card */ 8598c2ecf20Sopenharmony_ci atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE); 8608c2ecf20Sopenharmony_ci /* Copy the packet sans its 802.3 header addresses which have been replaced */ 8618c2ecf20Sopenharmony_ci atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12); 8628c2ecf20Sopenharmony_ci priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci /* low bit of first byte of destination tells us if broadcast */ 8658c2ecf20Sopenharmony_ci tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA); 8668c2ecf20Sopenharmony_ci dev->stats.tx_bytes += len; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 8698c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->timerlock); 8708c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic void atmel_transmit_management_frame(struct atmel_private *priv, 8768c2ecf20Sopenharmony_ci struct ieee80211_hdr *header, 8778c2ecf20Sopenharmony_ci u8 *body, int body_len) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci u16 buff; 8808c2ecf20Sopenharmony_ci int len = MGMT_FRAME_BODY_OFFSET + body_len; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (!(buff = find_tx_buff(priv, len))) 8838c2ecf20Sopenharmony_ci return; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci atmel_copy_to_card(priv->dev, buff, (u8 *)header, MGMT_FRAME_BODY_OFFSET); 8868c2ecf20Sopenharmony_ci atmel_copy_to_card(priv->dev, buff + MGMT_FRAME_BODY_OFFSET, body, body_len); 8878c2ecf20Sopenharmony_ci priv->tx_buff_tail += len; 8888c2ecf20Sopenharmony_ci tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT); 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic void fast_rx_path(struct atmel_private *priv, 8928c2ecf20Sopenharmony_ci struct ieee80211_hdr *header, 8938c2ecf20Sopenharmony_ci u16 msdu_size, u16 rx_packet_loc, u32 crc) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci /* fast path: unfragmented packet copy directly into skbuf */ 8968c2ecf20Sopenharmony_ci u8 mac4[6]; 8978c2ecf20Sopenharmony_ci struct sk_buff *skb; 8988c2ecf20Sopenharmony_ci unsigned char *skbp; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci /* get the final, mac 4 header field, this tells us encapsulation */ 9018c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6); 9028c2ecf20Sopenharmony_ci msdu_size -= 6; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (priv->do_rx_crc) { 9058c2ecf20Sopenharmony_ci crc = crc32_le(crc, mac4, 6); 9068c2ecf20Sopenharmony_ci msdu_size -= 4; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (!(skb = dev_alloc_skb(msdu_size + 14))) { 9108c2ecf20Sopenharmony_ci priv->dev->stats.rx_dropped++; 9118c2ecf20Sopenharmony_ci return; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci skb_reserve(skb, 2); 9158c2ecf20Sopenharmony_ci skbp = skb_put(skb, msdu_size + 12); 9168c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (priv->do_rx_crc) { 9198c2ecf20Sopenharmony_ci u32 netcrc; 9208c2ecf20Sopenharmony_ci crc = crc32_le(crc, skbp + 12, msdu_size); 9218c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + 30 + msdu_size, 4); 9228c2ecf20Sopenharmony_ci if ((crc ^ 0xffffffff) != netcrc) { 9238c2ecf20Sopenharmony_ci priv->dev->stats.rx_crc_errors++; 9248c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 9258c2ecf20Sopenharmony_ci return; 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */ 9308c2ecf20Sopenharmony_ci if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) 9318c2ecf20Sopenharmony_ci memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN); 9328c2ecf20Sopenharmony_ci else 9338c2ecf20Sopenharmony_ci memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */ 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, priv->dev); 9368c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 9378c2ecf20Sopenharmony_ci netif_rx(skb); 9388c2ecf20Sopenharmony_ci priv->dev->stats.rx_bytes += 12 + msdu_size; 9398c2ecf20Sopenharmony_ci priv->dev->stats.rx_packets++; 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci/* Test to see if the packet in card memory at packet_loc has a valid CRC 9438c2ecf20Sopenharmony_ci It doesn't matter that this is slow: it is only used to proble the first few 9448c2ecf20Sopenharmony_ci packets. */ 9458c2ecf20Sopenharmony_cistatic int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci int i = msdu_size - 4; 9488c2ecf20Sopenharmony_ci u32 netcrc, crc = 0xffffffff; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (msdu_size < 4) 9518c2ecf20Sopenharmony_ci return 0; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, (void *)&netcrc, packet_loc + i, 4); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci atmel_writeAR(priv->dev, packet_loc); 9568c2ecf20Sopenharmony_ci while (i--) { 9578c2ecf20Sopenharmony_ci u8 octet = atmel_read8(priv->dev, DR); 9588c2ecf20Sopenharmony_ci crc = crc32_le(crc, &octet, 1); 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci return (crc ^ 0xffffffff) == netcrc; 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic void frag_rx_path(struct atmel_private *priv, 9658c2ecf20Sopenharmony_ci struct ieee80211_hdr *header, 9668c2ecf20Sopenharmony_ci u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, 9678c2ecf20Sopenharmony_ci u8 frag_no, int more_frags) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci u8 mac4[ETH_ALEN]; 9708c2ecf20Sopenharmony_ci u8 source[ETH_ALEN]; 9718c2ecf20Sopenharmony_ci struct sk_buff *skb; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) 9748c2ecf20Sopenharmony_ci memcpy(source, header->addr3, ETH_ALEN); 9758c2ecf20Sopenharmony_ci else 9768c2ecf20Sopenharmony_ci memcpy(source, header->addr2, ETH_ALEN); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci rx_packet_loc += 24; /* skip header */ 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (priv->do_rx_crc) 9818c2ecf20Sopenharmony_ci msdu_size -= 4; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (frag_no == 0) { /* first fragment */ 9848c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN); 9858c2ecf20Sopenharmony_ci msdu_size -= ETH_ALEN; 9868c2ecf20Sopenharmony_ci rx_packet_loc += ETH_ALEN; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (priv->do_rx_crc) 9898c2ecf20Sopenharmony_ci crc = crc32_le(crc, mac4, 6); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci priv->frag_seq = seq_no; 9928c2ecf20Sopenharmony_ci priv->frag_no = 1; 9938c2ecf20Sopenharmony_ci priv->frag_len = msdu_size; 9948c2ecf20Sopenharmony_ci memcpy(priv->frag_source, source, ETH_ALEN); 9958c2ecf20Sopenharmony_ci memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN); 9968c2ecf20Sopenharmony_ci memcpy(priv->rx_buf, header->addr1, ETH_ALEN); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci if (priv->do_rx_crc) { 10018c2ecf20Sopenharmony_ci u32 netcrc; 10028c2ecf20Sopenharmony_ci crc = crc32_le(crc, &priv->rx_buf[12], msdu_size); 10038c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); 10048c2ecf20Sopenharmony_ci if ((crc ^ 0xffffffff) != netcrc) { 10058c2ecf20Sopenharmony_ci priv->dev->stats.rx_crc_errors++; 10068c2ecf20Sopenharmony_ci eth_broadcast_addr(priv->frag_source); 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci } else if (priv->frag_no == frag_no && 10118c2ecf20Sopenharmony_ci priv->frag_seq == seq_no && 10128c2ecf20Sopenharmony_ci memcmp(priv->frag_source, source, ETH_ALEN) == 0) { 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len], 10158c2ecf20Sopenharmony_ci rx_packet_loc, msdu_size); 10168c2ecf20Sopenharmony_ci if (priv->do_rx_crc) { 10178c2ecf20Sopenharmony_ci u32 netcrc; 10188c2ecf20Sopenharmony_ci crc = crc32_le(crc, 10198c2ecf20Sopenharmony_ci &priv->rx_buf[12 + priv->frag_len], 10208c2ecf20Sopenharmony_ci msdu_size); 10218c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); 10228c2ecf20Sopenharmony_ci if ((crc ^ 0xffffffff) != netcrc) { 10238c2ecf20Sopenharmony_ci priv->dev->stats.rx_crc_errors++; 10248c2ecf20Sopenharmony_ci eth_broadcast_addr(priv->frag_source); 10258c2ecf20Sopenharmony_ci more_frags = 1; /* don't send broken assembly */ 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci priv->frag_len += msdu_size; 10308c2ecf20Sopenharmony_ci priv->frag_no++; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci if (!more_frags) { /* last one */ 10338c2ecf20Sopenharmony_ci eth_broadcast_addr(priv->frag_source); 10348c2ecf20Sopenharmony_ci if (!(skb = dev_alloc_skb(priv->frag_len + 14))) { 10358c2ecf20Sopenharmony_ci priv->dev->stats.rx_dropped++; 10368c2ecf20Sopenharmony_ci } else { 10378c2ecf20Sopenharmony_ci skb_reserve(skb, 2); 10388c2ecf20Sopenharmony_ci skb_put_data(skb, priv->rx_buf, 10398c2ecf20Sopenharmony_ci priv->frag_len + 12); 10408c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, priv->dev); 10418c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 10428c2ecf20Sopenharmony_ci netif_rx(skb); 10438c2ecf20Sopenharmony_ci priv->dev->stats.rx_bytes += priv->frag_len + 12; 10448c2ecf20Sopenharmony_ci priv->dev->stats.rx_packets++; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci } else 10488c2ecf20Sopenharmony_ci priv->wstats.discard.fragment++; 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_cistatic void rx_done_irq(struct atmel_private *priv) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci int i; 10548c2ecf20Sopenharmony_ci struct ieee80211_hdr header; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci for (i = 0; 10578c2ecf20Sopenharmony_ci atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID && 10588c2ecf20Sopenharmony_ci i < priv->host_info.rx_desc_count; 10598c2ecf20Sopenharmony_ci i++) { 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci u16 msdu_size, rx_packet_loc, frame_ctl, seq_control; 10628c2ecf20Sopenharmony_ci u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head)); 10638c2ecf20Sopenharmony_ci u32 crc = 0xffffffff; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci if (status != RX_STATUS_SUCCESS) { 10668c2ecf20Sopenharmony_ci if (status == 0xc1) /* determined by experiment */ 10678c2ecf20Sopenharmony_ci priv->wstats.discard.nwid++; 10688c2ecf20Sopenharmony_ci else 10698c2ecf20Sopenharmony_ci priv->dev->stats.rx_errors++; 10708c2ecf20Sopenharmony_ci goto next; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head)); 10748c2ecf20Sopenharmony_ci rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head)); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (msdu_size < 30) { 10778c2ecf20Sopenharmony_ci priv->dev->stats.rx_errors++; 10788c2ecf20Sopenharmony_ci goto next; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci /* Get header as far as end of seq_ctrl */ 10828c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24); 10838c2ecf20Sopenharmony_ci frame_ctl = le16_to_cpu(header.frame_control); 10848c2ecf20Sopenharmony_ci seq_control = le16_to_cpu(header.seq_ctrl); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci /* probe for CRC use here if needed once five packets have 10878c2ecf20Sopenharmony_ci arrived with the same crc status, we assume we know what's 10888c2ecf20Sopenharmony_ci happening and stop probing */ 10898c2ecf20Sopenharmony_ci if (priv->probe_crc) { 10908c2ecf20Sopenharmony_ci if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) { 10918c2ecf20Sopenharmony_ci priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size); 10928c2ecf20Sopenharmony_ci } else { 10938c2ecf20Sopenharmony_ci priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24); 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci if (priv->do_rx_crc) { 10968c2ecf20Sopenharmony_ci if (priv->crc_ok_cnt++ > 5) 10978c2ecf20Sopenharmony_ci priv->probe_crc = 0; 10988c2ecf20Sopenharmony_ci } else { 10998c2ecf20Sopenharmony_ci if (priv->crc_ko_cnt++ > 5) 11008c2ecf20Sopenharmony_ci priv->probe_crc = 0; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci /* don't CRC header when WEP in use */ 11058c2ecf20Sopenharmony_ci if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) { 11068c2ecf20Sopenharmony_ci crc = crc32_le(0xffffffff, (unsigned char *)&header, 24); 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci msdu_size -= 24; /* header */ 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { 11118c2ecf20Sopenharmony_ci int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS; 11128c2ecf20Sopenharmony_ci u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG; 11138c2ecf20Sopenharmony_ci u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (!more_fragments && packet_fragment_no == 0) { 11168c2ecf20Sopenharmony_ci fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc); 11178c2ecf20Sopenharmony_ci } else { 11188c2ecf20Sopenharmony_ci frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc, 11198c2ecf20Sopenharmony_ci packet_sequence_no, packet_fragment_no, more_fragments); 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { 11248c2ecf20Sopenharmony_ci /* copy rest of packet into buffer */ 11258c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* we use the same buffer for frag reassembly and control packets */ 11288c2ecf20Sopenharmony_ci eth_broadcast_addr(priv->frag_source); 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if (priv->do_rx_crc) { 11318c2ecf20Sopenharmony_ci /* last 4 octets is crc */ 11328c2ecf20Sopenharmony_ci msdu_size -= 4; 11338c2ecf20Sopenharmony_ci crc = crc32_le(crc, (unsigned char *)&priv->rx_buf, msdu_size); 11348c2ecf20Sopenharmony_ci if ((crc ^ 0xffffffff) != (*((u32 *)&priv->rx_buf[msdu_size]))) { 11358c2ecf20Sopenharmony_ci priv->dev->stats.rx_crc_errors++; 11368c2ecf20Sopenharmony_ci goto next; 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci atmel_management_frame(priv, &header, msdu_size, 11418c2ecf20Sopenharmony_ci atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head))); 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cinext: 11458c2ecf20Sopenharmony_ci /* release descriptor */ 11468c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1)) 11498c2ecf20Sopenharmony_ci priv->rx_desc_head++; 11508c2ecf20Sopenharmony_ci else 11518c2ecf20Sopenharmony_ci priv->rx_desc_head = 0; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cistatic irqreturn_t service_interrupt(int irq, void *dev_id) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci struct net_device *dev = (struct net_device *) dev_id; 11588c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 11598c2ecf20Sopenharmony_ci u8 isr; 11608c2ecf20Sopenharmony_ci int i = -1; 11618c2ecf20Sopenharmony_ci static const u8 irq_order[] = { 11628c2ecf20Sopenharmony_ci ISR_OUT_OF_RANGE, 11638c2ecf20Sopenharmony_ci ISR_RxCOMPLETE, 11648c2ecf20Sopenharmony_ci ISR_TxCOMPLETE, 11658c2ecf20Sopenharmony_ci ISR_RxFRAMELOST, 11668c2ecf20Sopenharmony_ci ISR_FATAL_ERROR, 11678c2ecf20Sopenharmony_ci ISR_COMMAND_COMPLETE, 11688c2ecf20Sopenharmony_ci ISR_IBSS_MERGE, 11698c2ecf20Sopenharmony_ci ISR_GENERIC_IRQ 11708c2ecf20Sopenharmony_ci }; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci if (priv->card && priv->present_callback && 11738c2ecf20Sopenharmony_ci !(*priv->present_callback)(priv->card)) 11748c2ecf20Sopenharmony_ci return IRQ_HANDLED; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci /* In this state upper-level code assumes it can mess with 11778c2ecf20Sopenharmony_ci the card unhampered by interrupts which may change register state. 11788c2ecf20Sopenharmony_ci Note that even though the card shouldn't generate interrupts 11798c2ecf20Sopenharmony_ci the inturrupt line may be shared. This allows card setup 11808c2ecf20Sopenharmony_ci to go on without disabling interrupts for a long time. */ 11818c2ecf20Sopenharmony_ci if (priv->station_state == STATION_STATE_DOWN) 11828c2ecf20Sopenharmony_ci return IRQ_NONE; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */ 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci while (1) { 11878c2ecf20Sopenharmony_ci if (!atmel_lock_mac(priv)) { 11888c2ecf20Sopenharmony_ci /* failed to contact card */ 11898c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); 11908c2ecf20Sopenharmony_ci return IRQ_HANDLED; 11918c2ecf20Sopenharmony_ci } 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); 11948c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci if (!isr) { 11978c2ecf20Sopenharmony_ci atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */ 11988c2ecf20Sopenharmony_ci return i == -1 ? IRQ_NONE : IRQ_HANDLED; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */ 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(irq_order); i++) 12048c2ecf20Sopenharmony_ci if (isr & irq_order[i]) 12058c2ecf20Sopenharmony_ci break; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci if (!atmel_lock_mac(priv)) { 12088c2ecf20Sopenharmony_ci /* failed to contact card */ 12098c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); 12108c2ecf20Sopenharmony_ci return IRQ_HANDLED; 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); 12148c2ecf20Sopenharmony_ci isr ^= irq_order[i]; 12158c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr); 12168c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci switch (irq_order[i]) { 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci case ISR_OUT_OF_RANGE: 12218c2ecf20Sopenharmony_ci if (priv->operating_mode == IW_MODE_INFRA && 12228c2ecf20Sopenharmony_ci priv->station_state == STATION_STATE_READY) { 12238c2ecf20Sopenharmony_ci priv->station_is_associated = 0; 12248c2ecf20Sopenharmony_ci atmel_scan(priv, 1); 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci break; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci case ISR_RxFRAMELOST: 12298c2ecf20Sopenharmony_ci priv->wstats.discard.misc++; 12308c2ecf20Sopenharmony_ci fallthrough; 12318c2ecf20Sopenharmony_ci case ISR_RxCOMPLETE: 12328c2ecf20Sopenharmony_ci rx_done_irq(priv); 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci case ISR_TxCOMPLETE: 12368c2ecf20Sopenharmony_ci tx_done_irq(priv); 12378c2ecf20Sopenharmony_ci break; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci case ISR_FATAL_ERROR: 12408c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name); 12418c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 12428c2ecf20Sopenharmony_ci break; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci case ISR_COMMAND_COMPLETE: 12458c2ecf20Sopenharmony_ci atmel_command_irq(priv); 12468c2ecf20Sopenharmony_ci break; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci case ISR_IBSS_MERGE: 12498c2ecf20Sopenharmony_ci atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, 12508c2ecf20Sopenharmony_ci priv->CurrentBSSID, 6); 12518c2ecf20Sopenharmony_ci /* The WPA stuff cares about the current AP address */ 12528c2ecf20Sopenharmony_ci if (priv->use_wpa) 12538c2ecf20Sopenharmony_ci build_wpa_mib(priv); 12548c2ecf20Sopenharmony_ci break; 12558c2ecf20Sopenharmony_ci case ISR_GENERIC_IRQ: 12568c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Generic_irq received.\n", dev->name); 12578c2ecf20Sopenharmony_ci break; 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cistatic struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* update the link quality here in case we are seeing no beacons 12678c2ecf20Sopenharmony_ci at all to drive the process */ 12688c2ecf20Sopenharmony_ci atmel_smooth_qual(priv); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci priv->wstats.status = priv->station_state; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (priv->operating_mode == IW_MODE_INFRA) { 12738c2ecf20Sopenharmony_ci if (priv->station_state != STATION_STATE_READY) { 12748c2ecf20Sopenharmony_ci priv->wstats.qual.qual = 0; 12758c2ecf20Sopenharmony_ci priv->wstats.qual.level = 0; 12768c2ecf20Sopenharmony_ci priv->wstats.qual.updated = (IW_QUAL_QUAL_INVALID 12778c2ecf20Sopenharmony_ci | IW_QUAL_LEVEL_INVALID); 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci priv->wstats.qual.noise = 0; 12808c2ecf20Sopenharmony_ci priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID; 12818c2ecf20Sopenharmony_ci } else { 12828c2ecf20Sopenharmony_ci /* Quality levels cannot be determined in ad-hoc mode, 12838c2ecf20Sopenharmony_ci because we can 'hear' more that one remote station. */ 12848c2ecf20Sopenharmony_ci priv->wstats.qual.qual = 0; 12858c2ecf20Sopenharmony_ci priv->wstats.qual.level = 0; 12868c2ecf20Sopenharmony_ci priv->wstats.qual.noise = 0; 12878c2ecf20Sopenharmony_ci priv->wstats.qual.updated = IW_QUAL_QUAL_INVALID 12888c2ecf20Sopenharmony_ci | IW_QUAL_LEVEL_INVALID 12898c2ecf20Sopenharmony_ci | IW_QUAL_NOISE_INVALID; 12908c2ecf20Sopenharmony_ci priv->wstats.miss.beacon = 0; 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci return &priv->wstats; 12948c2ecf20Sopenharmony_ci} 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_cistatic int atmel_set_mac_address(struct net_device *dev, void *p) 12978c2ecf20Sopenharmony_ci{ 12988c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci memcpy (dev->dev_addr, addr->sa_data, dev->addr_len); 13018c2ecf20Sopenharmony_ci return atmel_open(dev); 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atmel_open); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ciint atmel_open(struct net_device *dev) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 13098c2ecf20Sopenharmony_ci int i, channel, err; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci /* any scheduled timer is no longer needed and might screw things up.. */ 13128c2ecf20Sopenharmony_ci del_timer_sync(&priv->management_timer); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* Interrupts will not touch the card once in this state... */ 13158c2ecf20Sopenharmony_ci priv->station_state = STATION_STATE_DOWN; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci if (priv->new_SSID_size) { 13188c2ecf20Sopenharmony_ci memcpy(priv->SSID, priv->new_SSID, priv->new_SSID_size); 13198c2ecf20Sopenharmony_ci priv->SSID_size = priv->new_SSID_size; 13208c2ecf20Sopenharmony_ci priv->new_SSID_size = 0; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci priv->BSS_list_entries = 0; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci priv->AuthenticationRequestRetryCnt = 0; 13258c2ecf20Sopenharmony_ci priv->AssociationRequestRetryCnt = 0; 13268c2ecf20Sopenharmony_ci priv->ReAssociationRequestRetryCnt = 0; 13278c2ecf20Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum = 0x0001; 13288c2ecf20Sopenharmony_ci priv->ExpectedAuthentTransactionSeqNum = 0x0002; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci priv->site_survey_state = SITE_SURVEY_IDLE; 13318c2ecf20Sopenharmony_ci priv->station_is_associated = 0; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci err = reset_atmel_card(dev); 13348c2ecf20Sopenharmony_ci if (err) 13358c2ecf20Sopenharmony_ci return err; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci if (priv->config_reg_domain) { 13388c2ecf20Sopenharmony_ci priv->reg_domain = priv->config_reg_domain; 13398c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS, priv->reg_domain); 13408c2ecf20Sopenharmony_ci } else { 13418c2ecf20Sopenharmony_ci priv->reg_domain = atmel_get_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS); 13428c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_table); i++) 13438c2ecf20Sopenharmony_ci if (priv->reg_domain == channel_table[i].reg_domain) 13448c2ecf20Sopenharmony_ci break; 13458c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(channel_table)) { 13468c2ecf20Sopenharmony_ci priv->reg_domain = REG_DOMAIN_MKK1; 13478c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: failed to get regulatory domain: assuming MKK1.\n", dev->name); 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci if ((channel = atmel_validate_channel(priv, priv->channel))) 13528c2ecf20Sopenharmony_ci priv->channel = channel; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci /* this moves station_state on.... */ 13558c2ecf20Sopenharmony_ci atmel_scan(priv, 1); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci atmel_set_gcr(priv->dev, GCR_ENINT); /* enable interrupts */ 13588c2ecf20Sopenharmony_ci return 0; 13598c2ecf20Sopenharmony_ci} 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic int atmel_close(struct net_device *dev) 13628c2ecf20Sopenharmony_ci{ 13638c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci /* Send event to userspace that we are disassociating */ 13668c2ecf20Sopenharmony_ci if (priv->station_state == STATION_STATE_READY) { 13678c2ecf20Sopenharmony_ci union iwreq_data wrqu; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci wrqu.data.length = 0; 13708c2ecf20Sopenharmony_ci wrqu.data.flags = 0; 13718c2ecf20Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 13728c2ecf20Sopenharmony_ci eth_zero_addr(wrqu.ap_addr.sa_data); 13738c2ecf20Sopenharmony_ci wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_DOWN); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 13798c2ecf20Sopenharmony_ci atmel_write16(dev, GCR, 0x0060); 13808c2ecf20Sopenharmony_ci atmel_write16(dev, GCR, 0x0040); 13818c2ecf20Sopenharmony_ci return 0; 13828c2ecf20Sopenharmony_ci} 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_cistatic int atmel_validate_channel(struct atmel_private *priv, int channel) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci /* check that channel is OK, if so return zero, 13878c2ecf20Sopenharmony_ci else return suitable default channel */ 13888c2ecf20Sopenharmony_ci int i; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_table); i++) 13918c2ecf20Sopenharmony_ci if (priv->reg_domain == channel_table[i].reg_domain) { 13928c2ecf20Sopenharmony_ci if (channel >= channel_table[i].min && 13938c2ecf20Sopenharmony_ci channel <= channel_table[i].max) 13948c2ecf20Sopenharmony_ci return 0; 13958c2ecf20Sopenharmony_ci else 13968c2ecf20Sopenharmony_ci return channel_table[i].min; 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci return 0; 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 14028c2ecf20Sopenharmony_cistatic int atmel_proc_show(struct seq_file *m, void *v) 14038c2ecf20Sopenharmony_ci{ 14048c2ecf20Sopenharmony_ci struct atmel_private *priv = m->private; 14058c2ecf20Sopenharmony_ci int i; 14068c2ecf20Sopenharmony_ci char *s, *r, *c; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci seq_printf(m, "Driver version:\t\t%d.%d\n", DRIVER_MAJOR, DRIVER_MINOR); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci if (priv->station_state != STATION_STATE_DOWN) { 14118c2ecf20Sopenharmony_ci seq_printf(m, 14128c2ecf20Sopenharmony_ci "Firmware version:\t%d.%d build %d\n" 14138c2ecf20Sopenharmony_ci "Firmware location:\t", 14148c2ecf20Sopenharmony_ci priv->host_info.major_version, 14158c2ecf20Sopenharmony_ci priv->host_info.minor_version, 14168c2ecf20Sopenharmony_ci priv->host_info.build_version); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci if (priv->card_type != CARD_TYPE_EEPROM) 14198c2ecf20Sopenharmony_ci seq_puts(m, "on card\n"); 14208c2ecf20Sopenharmony_ci else if (priv->firmware) 14218c2ecf20Sopenharmony_ci seq_printf(m, "%s loaded by host\n", priv->firmware_id); 14228c2ecf20Sopenharmony_ci else 14238c2ecf20Sopenharmony_ci seq_printf(m, "%s loaded by hotplug\n", priv->firmware_id); 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci switch (priv->card_type) { 14268c2ecf20Sopenharmony_ci case CARD_TYPE_PARALLEL_FLASH: 14278c2ecf20Sopenharmony_ci c = "Parallel flash"; 14288c2ecf20Sopenharmony_ci break; 14298c2ecf20Sopenharmony_ci case CARD_TYPE_SPI_FLASH: 14308c2ecf20Sopenharmony_ci c = "SPI flash\n"; 14318c2ecf20Sopenharmony_ci break; 14328c2ecf20Sopenharmony_ci case CARD_TYPE_EEPROM: 14338c2ecf20Sopenharmony_ci c = "EEPROM"; 14348c2ecf20Sopenharmony_ci break; 14358c2ecf20Sopenharmony_ci default: 14368c2ecf20Sopenharmony_ci c = "<unknown>"; 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci r = "<unknown>"; 14408c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_table); i++) 14418c2ecf20Sopenharmony_ci if (priv->reg_domain == channel_table[i].reg_domain) 14428c2ecf20Sopenharmony_ci r = channel_table[i].name; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci seq_printf(m, "MAC memory type:\t%s\n", c); 14458c2ecf20Sopenharmony_ci seq_printf(m, "Regulatory domain:\t%s\n", r); 14468c2ecf20Sopenharmony_ci seq_printf(m, "Host CRC checking:\t%s\n", 14478c2ecf20Sopenharmony_ci priv->do_rx_crc ? "On" : "Off"); 14488c2ecf20Sopenharmony_ci seq_printf(m, "WPA-capable firmware:\t%s\n", 14498c2ecf20Sopenharmony_ci priv->use_wpa ? "Yes" : "No"); 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci switch (priv->station_state) { 14538c2ecf20Sopenharmony_ci case STATION_STATE_SCANNING: 14548c2ecf20Sopenharmony_ci s = "Scanning"; 14558c2ecf20Sopenharmony_ci break; 14568c2ecf20Sopenharmony_ci case STATION_STATE_JOINNING: 14578c2ecf20Sopenharmony_ci s = "Joining"; 14588c2ecf20Sopenharmony_ci break; 14598c2ecf20Sopenharmony_ci case STATION_STATE_AUTHENTICATING: 14608c2ecf20Sopenharmony_ci s = "Authenticating"; 14618c2ecf20Sopenharmony_ci break; 14628c2ecf20Sopenharmony_ci case STATION_STATE_ASSOCIATING: 14638c2ecf20Sopenharmony_ci s = "Associating"; 14648c2ecf20Sopenharmony_ci break; 14658c2ecf20Sopenharmony_ci case STATION_STATE_READY: 14668c2ecf20Sopenharmony_ci s = "Ready"; 14678c2ecf20Sopenharmony_ci break; 14688c2ecf20Sopenharmony_ci case STATION_STATE_REASSOCIATING: 14698c2ecf20Sopenharmony_ci s = "Reassociating"; 14708c2ecf20Sopenharmony_ci break; 14718c2ecf20Sopenharmony_ci case STATION_STATE_MGMT_ERROR: 14728c2ecf20Sopenharmony_ci s = "Management error"; 14738c2ecf20Sopenharmony_ci break; 14748c2ecf20Sopenharmony_ci case STATION_STATE_DOWN: 14758c2ecf20Sopenharmony_ci s = "Down"; 14768c2ecf20Sopenharmony_ci break; 14778c2ecf20Sopenharmony_ci default: 14788c2ecf20Sopenharmony_ci s = "<unknown>"; 14798c2ecf20Sopenharmony_ci } 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci seq_printf(m, "Current state:\t\t%s\n", s); 14828c2ecf20Sopenharmony_ci return 0; 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci#endif 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_cistatic const struct net_device_ops atmel_netdev_ops = { 14878c2ecf20Sopenharmony_ci .ndo_open = atmel_open, 14888c2ecf20Sopenharmony_ci .ndo_stop = atmel_close, 14898c2ecf20Sopenharmony_ci .ndo_set_mac_address = atmel_set_mac_address, 14908c2ecf20Sopenharmony_ci .ndo_start_xmit = start_tx, 14918c2ecf20Sopenharmony_ci .ndo_do_ioctl = atmel_ioctl, 14928c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 14938c2ecf20Sopenharmony_ci}; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistruct net_device *init_atmel_card(unsigned short irq, unsigned long port, 14968c2ecf20Sopenharmony_ci const AtmelFWType fw_type, 14978c2ecf20Sopenharmony_ci struct device *sys_dev, 14988c2ecf20Sopenharmony_ci int (*card_present)(void *), void *card) 14998c2ecf20Sopenharmony_ci{ 15008c2ecf20Sopenharmony_ci struct net_device *dev; 15018c2ecf20Sopenharmony_ci struct atmel_private *priv; 15028c2ecf20Sopenharmony_ci int rc; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci /* Create the network device object. */ 15058c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(*priv)); 15068c2ecf20Sopenharmony_ci if (!dev) 15078c2ecf20Sopenharmony_ci return NULL; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (dev_alloc_name(dev, dev->name) < 0) { 15108c2ecf20Sopenharmony_ci printk(KERN_ERR "atmel: Couldn't get name!\n"); 15118c2ecf20Sopenharmony_ci goto err_out_free; 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 15158c2ecf20Sopenharmony_ci priv->dev = dev; 15168c2ecf20Sopenharmony_ci priv->sys_dev = sys_dev; 15178c2ecf20Sopenharmony_ci priv->present_callback = card_present; 15188c2ecf20Sopenharmony_ci priv->card = card; 15198c2ecf20Sopenharmony_ci priv->firmware = NULL; 15208c2ecf20Sopenharmony_ci priv->firmware_type = fw_type; 15218c2ecf20Sopenharmony_ci if (firmware) /* module parameter */ 15228c2ecf20Sopenharmony_ci strlcpy(priv->firmware_id, firmware, sizeof(priv->firmware_id)); 15238c2ecf20Sopenharmony_ci priv->bus_type = card_present ? BUS_TYPE_PCCARD : BUS_TYPE_PCI; 15248c2ecf20Sopenharmony_ci priv->station_state = STATION_STATE_DOWN; 15258c2ecf20Sopenharmony_ci priv->do_rx_crc = 0; 15268c2ecf20Sopenharmony_ci /* For PCMCIA cards, some chips need CRC, some don't 15278c2ecf20Sopenharmony_ci so we have to probe. */ 15288c2ecf20Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) { 15298c2ecf20Sopenharmony_ci priv->probe_crc = 1; 15308c2ecf20Sopenharmony_ci priv->crc_ok_cnt = priv->crc_ko_cnt = 0; 15318c2ecf20Sopenharmony_ci } else 15328c2ecf20Sopenharmony_ci priv->probe_crc = 0; 15338c2ecf20Sopenharmony_ci priv->last_qual = jiffies; 15348c2ecf20Sopenharmony_ci priv->last_beacon_timestamp = 0; 15358c2ecf20Sopenharmony_ci memset(priv->frag_source, 0xff, sizeof(priv->frag_source)); 15368c2ecf20Sopenharmony_ci eth_zero_addr(priv->BSSID); 15378c2ecf20Sopenharmony_ci priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */ 15388c2ecf20Sopenharmony_ci priv->station_was_associated = 0; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci priv->last_survey = jiffies; 15418c2ecf20Sopenharmony_ci priv->preamble = LONG_PREAMBLE; 15428c2ecf20Sopenharmony_ci priv->operating_mode = IW_MODE_INFRA; 15438c2ecf20Sopenharmony_ci priv->connect_to_any_BSS = 0; 15448c2ecf20Sopenharmony_ci priv->config_reg_domain = 0; 15458c2ecf20Sopenharmony_ci priv->reg_domain = 0; 15468c2ecf20Sopenharmony_ci priv->tx_rate = 3; 15478c2ecf20Sopenharmony_ci priv->auto_tx_rate = 1; 15488c2ecf20Sopenharmony_ci priv->channel = 4; 15498c2ecf20Sopenharmony_ci priv->power_mode = 0; 15508c2ecf20Sopenharmony_ci priv->SSID[0] = '\0'; 15518c2ecf20Sopenharmony_ci priv->SSID_size = 0; 15528c2ecf20Sopenharmony_ci priv->new_SSID_size = 0; 15538c2ecf20Sopenharmony_ci priv->frag_threshold = 2346; 15548c2ecf20Sopenharmony_ci priv->rts_threshold = 2347; 15558c2ecf20Sopenharmony_ci priv->short_retry = 7; 15568c2ecf20Sopenharmony_ci priv->long_retry = 4; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci priv->wep_is_on = 0; 15598c2ecf20Sopenharmony_ci priv->default_key = 0; 15608c2ecf20Sopenharmony_ci priv->encryption_level = 0; 15618c2ecf20Sopenharmony_ci priv->exclude_unencrypted = 0; 15628c2ecf20Sopenharmony_ci priv->group_cipher_suite = priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; 15638c2ecf20Sopenharmony_ci priv->use_wpa = 0; 15648c2ecf20Sopenharmony_ci memset(priv->wep_keys, 0, sizeof(priv->wep_keys)); 15658c2ecf20Sopenharmony_ci memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci priv->default_beacon_period = priv->beacon_period = 100; 15688c2ecf20Sopenharmony_ci priv->listen_interval = 1; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci timer_setup(&priv->management_timer, atmel_management_timer, 0); 15718c2ecf20Sopenharmony_ci spin_lock_init(&priv->irqlock); 15728c2ecf20Sopenharmony_ci spin_lock_init(&priv->timerlock); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci dev->netdev_ops = &atmel_netdev_ops; 15758c2ecf20Sopenharmony_ci dev->wireless_handlers = &atmel_handler_def; 15768c2ecf20Sopenharmony_ci dev->irq = irq; 15778c2ecf20Sopenharmony_ci dev->base_addr = port; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci /* MTU range: 68 - 2312 */ 15808c2ecf20Sopenharmony_ci dev->min_mtu = 68; 15818c2ecf20Sopenharmony_ci dev->max_mtu = MAX_WIRELESS_BODY - ETH_FCS_LEN; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, sys_dev); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) { 15868c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc); 15878c2ecf20Sopenharmony_ci goto err_out_free; 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci if (!request_region(dev->base_addr, 32, 15918c2ecf20Sopenharmony_ci priv->bus_type == BUS_TYPE_PCCARD ? "atmel_cs" : "atmel_pci")) { 15928c2ecf20Sopenharmony_ci goto err_out_irq; 15938c2ecf20Sopenharmony_ci } 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci if (register_netdev(dev)) 15968c2ecf20Sopenharmony_ci goto err_out_res; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci if (!probe_atmel_card(dev)) { 15998c2ecf20Sopenharmony_ci unregister_netdev(dev); 16008c2ecf20Sopenharmony_ci goto err_out_res; 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci netif_carrier_off(dev); 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci if (!proc_create_single_data("driver/atmel", 0, NULL, atmel_proc_show, 16068c2ecf20Sopenharmony_ci priv)) 16078c2ecf20Sopenharmony_ci printk(KERN_WARNING "atmel: unable to create /proc entry.\n"); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %pM\n", 16108c2ecf20Sopenharmony_ci dev->name, DRIVER_MAJOR, DRIVER_MINOR, dev->dev_addr); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci return dev; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_cierr_out_res: 16158c2ecf20Sopenharmony_ci release_region(dev->base_addr, 32); 16168c2ecf20Sopenharmony_cierr_out_irq: 16178c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 16188c2ecf20Sopenharmony_cierr_out_free: 16198c2ecf20Sopenharmony_ci free_netdev(dev); 16208c2ecf20Sopenharmony_ci return NULL; 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(init_atmel_card); 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_civoid stop_atmel_card(struct net_device *dev) 16268c2ecf20Sopenharmony_ci{ 16278c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* put a brick on it... */ 16308c2ecf20Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 16318c2ecf20Sopenharmony_ci atmel_write16(dev, GCR, 0x0060); 16328c2ecf20Sopenharmony_ci atmel_write16(dev, GCR, 0x0040); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci del_timer_sync(&priv->management_timer); 16358c2ecf20Sopenharmony_ci unregister_netdev(dev); 16368c2ecf20Sopenharmony_ci remove_proc_entry("driver/atmel", NULL); 16378c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 16388c2ecf20Sopenharmony_ci kfree(priv->firmware); 16398c2ecf20Sopenharmony_ci release_region(dev->base_addr, 32); 16408c2ecf20Sopenharmony_ci free_netdev(dev); 16418c2ecf20Sopenharmony_ci} 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(stop_atmel_card); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_cistatic int atmel_set_essid(struct net_device *dev, 16468c2ecf20Sopenharmony_ci struct iw_request_info *info, 16478c2ecf20Sopenharmony_ci struct iw_point *dwrq, 16488c2ecf20Sopenharmony_ci char *extra) 16498c2ecf20Sopenharmony_ci{ 16508c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci /* Check if we asked for `any' */ 16538c2ecf20Sopenharmony_ci if (dwrq->flags == 0) { 16548c2ecf20Sopenharmony_ci priv->connect_to_any_BSS = 1; 16558c2ecf20Sopenharmony_ci } else { 16568c2ecf20Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci priv->connect_to_any_BSS = 0; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci /* Check the size of the string */ 16618c2ecf20Sopenharmony_ci if (dwrq->length > MAX_SSID_LENGTH) 16628c2ecf20Sopenharmony_ci return -E2BIG; 16638c2ecf20Sopenharmony_ci if (index != 0) 16648c2ecf20Sopenharmony_ci return -EINVAL; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci memcpy(priv->new_SSID, extra, dwrq->length); 16678c2ecf20Sopenharmony_ci priv->new_SSID_size = dwrq->length; 16688c2ecf20Sopenharmony_ci } 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci return -EINPROGRESS; 16718c2ecf20Sopenharmony_ci} 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_cistatic int atmel_get_essid(struct net_device *dev, 16748c2ecf20Sopenharmony_ci struct iw_request_info *info, 16758c2ecf20Sopenharmony_ci struct iw_point *dwrq, 16768c2ecf20Sopenharmony_ci char *extra) 16778c2ecf20Sopenharmony_ci{ 16788c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci /* Get the current SSID */ 16818c2ecf20Sopenharmony_ci if (priv->new_SSID_size != 0) { 16828c2ecf20Sopenharmony_ci memcpy(extra, priv->new_SSID, priv->new_SSID_size); 16838c2ecf20Sopenharmony_ci dwrq->length = priv->new_SSID_size; 16848c2ecf20Sopenharmony_ci } else { 16858c2ecf20Sopenharmony_ci memcpy(extra, priv->SSID, priv->SSID_size); 16868c2ecf20Sopenharmony_ci dwrq->length = priv->SSID_size; 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci dwrq->flags = !priv->connect_to_any_BSS; /* active */ 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci return 0; 16928c2ecf20Sopenharmony_ci} 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic int atmel_get_wap(struct net_device *dev, 16958c2ecf20Sopenharmony_ci struct iw_request_info *info, 16968c2ecf20Sopenharmony_ci struct sockaddr *awrq, 16978c2ecf20Sopenharmony_ci char *extra) 16988c2ecf20Sopenharmony_ci{ 16998c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 17008c2ecf20Sopenharmony_ci memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN); 17018c2ecf20Sopenharmony_ci awrq->sa_family = ARPHRD_ETHER; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci return 0; 17048c2ecf20Sopenharmony_ci} 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_cistatic int atmel_set_encode(struct net_device *dev, 17078c2ecf20Sopenharmony_ci struct iw_request_info *info, 17088c2ecf20Sopenharmony_ci struct iw_point *dwrq, 17098c2ecf20Sopenharmony_ci char *extra) 17108c2ecf20Sopenharmony_ci{ 17118c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci /* Basic checking: do we have a key to set ? 17148c2ecf20Sopenharmony_ci * Note : with the new API, it's impossible to get a NULL pointer. 17158c2ecf20Sopenharmony_ci * Therefore, we need to check a key size == 0 instead. 17168c2ecf20Sopenharmony_ci * New version of iwconfig properly set the IW_ENCODE_NOKEY flag 17178c2ecf20Sopenharmony_ci * when no key is present (only change flags), but older versions 17188c2ecf20Sopenharmony_ci * don't do it. - Jean II */ 17198c2ecf20Sopenharmony_ci if (dwrq->length > 0) { 17208c2ecf20Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 17218c2ecf20Sopenharmony_ci int current_index = priv->default_key; 17228c2ecf20Sopenharmony_ci /* Check the size of the key */ 17238c2ecf20Sopenharmony_ci if (dwrq->length > 13) { 17248c2ecf20Sopenharmony_ci return -EINVAL; 17258c2ecf20Sopenharmony_ci } 17268c2ecf20Sopenharmony_ci /* Check the index (none -> use current) */ 17278c2ecf20Sopenharmony_ci if (index < 0 || index >= 4) 17288c2ecf20Sopenharmony_ci index = current_index; 17298c2ecf20Sopenharmony_ci else 17308c2ecf20Sopenharmony_ci priv->default_key = index; 17318c2ecf20Sopenharmony_ci /* Set the length */ 17328c2ecf20Sopenharmony_ci if (dwrq->length > 5) 17338c2ecf20Sopenharmony_ci priv->wep_key_len[index] = 13; 17348c2ecf20Sopenharmony_ci else 17358c2ecf20Sopenharmony_ci if (dwrq->length > 0) 17368c2ecf20Sopenharmony_ci priv->wep_key_len[index] = 5; 17378c2ecf20Sopenharmony_ci else 17388c2ecf20Sopenharmony_ci /* Disable the key */ 17398c2ecf20Sopenharmony_ci priv->wep_key_len[index] = 0; 17408c2ecf20Sopenharmony_ci /* Check if the key is not marked as invalid */ 17418c2ecf20Sopenharmony_ci if (!(dwrq->flags & IW_ENCODE_NOKEY)) { 17428c2ecf20Sopenharmony_ci /* Cleanup */ 17438c2ecf20Sopenharmony_ci memset(priv->wep_keys[index], 0, 13); 17448c2ecf20Sopenharmony_ci /* Copy the key in the driver */ 17458c2ecf20Sopenharmony_ci memcpy(priv->wep_keys[index], extra, dwrq->length); 17468c2ecf20Sopenharmony_ci } 17478c2ecf20Sopenharmony_ci /* WE specify that if a valid key is set, encryption 17488c2ecf20Sopenharmony_ci * should be enabled (user may turn it off later) 17498c2ecf20Sopenharmony_ci * This is also how "iwconfig ethX key on" works */ 17508c2ecf20Sopenharmony_ci if (index == current_index && 17518c2ecf20Sopenharmony_ci priv->wep_key_len[index] > 0) { 17528c2ecf20Sopenharmony_ci priv->wep_is_on = 1; 17538c2ecf20Sopenharmony_ci priv->exclude_unencrypted = 1; 17548c2ecf20Sopenharmony_ci if (priv->wep_key_len[index] > 5) { 17558c2ecf20Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; 17568c2ecf20Sopenharmony_ci priv->encryption_level = 2; 17578c2ecf20Sopenharmony_ci } else { 17588c2ecf20Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; 17598c2ecf20Sopenharmony_ci priv->encryption_level = 1; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci } 17628c2ecf20Sopenharmony_ci } else { 17638c2ecf20Sopenharmony_ci /* Do we want to just set the transmit key index ? */ 17648c2ecf20Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 17658c2ecf20Sopenharmony_ci if (index >= 0 && index < 4) { 17668c2ecf20Sopenharmony_ci priv->default_key = index; 17678c2ecf20Sopenharmony_ci } else 17688c2ecf20Sopenharmony_ci /* Don't complain if only change the mode */ 17698c2ecf20Sopenharmony_ci if (!(dwrq->flags & IW_ENCODE_MODE)) 17708c2ecf20Sopenharmony_ci return -EINVAL; 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci /* Read the flags */ 17738c2ecf20Sopenharmony_ci if (dwrq->flags & IW_ENCODE_DISABLED) { 17748c2ecf20Sopenharmony_ci priv->wep_is_on = 0; 17758c2ecf20Sopenharmony_ci priv->encryption_level = 0; 17768c2ecf20Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; 17778c2ecf20Sopenharmony_ci } else { 17788c2ecf20Sopenharmony_ci priv->wep_is_on = 1; 17798c2ecf20Sopenharmony_ci if (priv->wep_key_len[priv->default_key] > 5) { 17808c2ecf20Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; 17818c2ecf20Sopenharmony_ci priv->encryption_level = 2; 17828c2ecf20Sopenharmony_ci } else { 17838c2ecf20Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; 17848c2ecf20Sopenharmony_ci priv->encryption_level = 1; 17858c2ecf20Sopenharmony_ci } 17868c2ecf20Sopenharmony_ci } 17878c2ecf20Sopenharmony_ci if (dwrq->flags & IW_ENCODE_RESTRICTED) 17888c2ecf20Sopenharmony_ci priv->exclude_unencrypted = 1; 17898c2ecf20Sopenharmony_ci if (dwrq->flags & IW_ENCODE_OPEN) 17908c2ecf20Sopenharmony_ci priv->exclude_unencrypted = 0; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 17938c2ecf20Sopenharmony_ci} 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_cistatic int atmel_get_encode(struct net_device *dev, 17968c2ecf20Sopenharmony_ci struct iw_request_info *info, 17978c2ecf20Sopenharmony_ci struct iw_point *dwrq, 17988c2ecf20Sopenharmony_ci char *extra) 17998c2ecf20Sopenharmony_ci{ 18008c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 18018c2ecf20Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci if (!priv->wep_is_on) 18048c2ecf20Sopenharmony_ci dwrq->flags = IW_ENCODE_DISABLED; 18058c2ecf20Sopenharmony_ci else { 18068c2ecf20Sopenharmony_ci if (priv->exclude_unencrypted) 18078c2ecf20Sopenharmony_ci dwrq->flags = IW_ENCODE_RESTRICTED; 18088c2ecf20Sopenharmony_ci else 18098c2ecf20Sopenharmony_ci dwrq->flags = IW_ENCODE_OPEN; 18108c2ecf20Sopenharmony_ci } 18118c2ecf20Sopenharmony_ci /* Which key do we want ? -1 -> tx index */ 18128c2ecf20Sopenharmony_ci if (index < 0 || index >= 4) 18138c2ecf20Sopenharmony_ci index = priv->default_key; 18148c2ecf20Sopenharmony_ci dwrq->flags |= index + 1; 18158c2ecf20Sopenharmony_ci /* Copy the key to the user buffer */ 18168c2ecf20Sopenharmony_ci dwrq->length = priv->wep_key_len[index]; 18178c2ecf20Sopenharmony_ci if (dwrq->length > 16) { 18188c2ecf20Sopenharmony_ci dwrq->length = 0; 18198c2ecf20Sopenharmony_ci } else { 18208c2ecf20Sopenharmony_ci memset(extra, 0, 16); 18218c2ecf20Sopenharmony_ci memcpy(extra, priv->wep_keys[index], dwrq->length); 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci return 0; 18258c2ecf20Sopenharmony_ci} 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_cistatic int atmel_set_encodeext(struct net_device *dev, 18288c2ecf20Sopenharmony_ci struct iw_request_info *info, 18298c2ecf20Sopenharmony_ci union iwreq_data *wrqu, 18308c2ecf20Sopenharmony_ci char *extra) 18318c2ecf20Sopenharmony_ci{ 18328c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 18338c2ecf20Sopenharmony_ci struct iw_point *encoding = &wrqu->encoding; 18348c2ecf20Sopenharmony_ci struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 18358c2ecf20Sopenharmony_ci int idx, key_len, alg = ext->alg, set_key = 1; 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci /* Determine and validate the key index */ 18388c2ecf20Sopenharmony_ci idx = encoding->flags & IW_ENCODE_INDEX; 18398c2ecf20Sopenharmony_ci if (idx) { 18408c2ecf20Sopenharmony_ci if (idx < 1 || idx > 4) 18418c2ecf20Sopenharmony_ci return -EINVAL; 18428c2ecf20Sopenharmony_ci idx--; 18438c2ecf20Sopenharmony_ci } else 18448c2ecf20Sopenharmony_ci idx = priv->default_key; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci if (encoding->flags & IW_ENCODE_DISABLED) 18478c2ecf20Sopenharmony_ci alg = IW_ENCODE_ALG_NONE; 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { 18508c2ecf20Sopenharmony_ci priv->default_key = idx; 18518c2ecf20Sopenharmony_ci set_key = ext->key_len > 0 ? 1 : 0; 18528c2ecf20Sopenharmony_ci } 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci if (set_key) { 18558c2ecf20Sopenharmony_ci /* Set the requested key first */ 18568c2ecf20Sopenharmony_ci switch (alg) { 18578c2ecf20Sopenharmony_ci case IW_ENCODE_ALG_NONE: 18588c2ecf20Sopenharmony_ci priv->wep_is_on = 0; 18598c2ecf20Sopenharmony_ci priv->encryption_level = 0; 18608c2ecf20Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; 18618c2ecf20Sopenharmony_ci break; 18628c2ecf20Sopenharmony_ci case IW_ENCODE_ALG_WEP: 18638c2ecf20Sopenharmony_ci if (ext->key_len > 5) { 18648c2ecf20Sopenharmony_ci priv->wep_key_len[idx] = 13; 18658c2ecf20Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; 18668c2ecf20Sopenharmony_ci priv->encryption_level = 2; 18678c2ecf20Sopenharmony_ci } else if (ext->key_len > 0) { 18688c2ecf20Sopenharmony_ci priv->wep_key_len[idx] = 5; 18698c2ecf20Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; 18708c2ecf20Sopenharmony_ci priv->encryption_level = 1; 18718c2ecf20Sopenharmony_ci } else { 18728c2ecf20Sopenharmony_ci return -EINVAL; 18738c2ecf20Sopenharmony_ci } 18748c2ecf20Sopenharmony_ci priv->wep_is_on = 1; 18758c2ecf20Sopenharmony_ci memset(priv->wep_keys[idx], 0, 13); 18768c2ecf20Sopenharmony_ci key_len = min ((int)ext->key_len, priv->wep_key_len[idx]); 18778c2ecf20Sopenharmony_ci memcpy(priv->wep_keys[idx], ext->key, key_len); 18788c2ecf20Sopenharmony_ci break; 18798c2ecf20Sopenharmony_ci default: 18808c2ecf20Sopenharmony_ci return -EINVAL; 18818c2ecf20Sopenharmony_ci } 18828c2ecf20Sopenharmony_ci } 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci return -EINPROGRESS; 18858c2ecf20Sopenharmony_ci} 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_cistatic int atmel_get_encodeext(struct net_device *dev, 18888c2ecf20Sopenharmony_ci struct iw_request_info *info, 18898c2ecf20Sopenharmony_ci union iwreq_data *wrqu, 18908c2ecf20Sopenharmony_ci char *extra) 18918c2ecf20Sopenharmony_ci{ 18928c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 18938c2ecf20Sopenharmony_ci struct iw_point *encoding = &wrqu->encoding; 18948c2ecf20Sopenharmony_ci struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 18958c2ecf20Sopenharmony_ci int idx, max_key_len; 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci max_key_len = encoding->length - sizeof(*ext); 18988c2ecf20Sopenharmony_ci if (max_key_len < 0) 18998c2ecf20Sopenharmony_ci return -EINVAL; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci idx = encoding->flags & IW_ENCODE_INDEX; 19028c2ecf20Sopenharmony_ci if (idx) { 19038c2ecf20Sopenharmony_ci if (idx < 1 || idx > 4) 19048c2ecf20Sopenharmony_ci return -EINVAL; 19058c2ecf20Sopenharmony_ci idx--; 19068c2ecf20Sopenharmony_ci } else 19078c2ecf20Sopenharmony_ci idx = priv->default_key; 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci encoding->flags = idx + 1; 19108c2ecf20Sopenharmony_ci memset(ext, 0, sizeof(*ext)); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci if (!priv->wep_is_on) { 19138c2ecf20Sopenharmony_ci ext->alg = IW_ENCODE_ALG_NONE; 19148c2ecf20Sopenharmony_ci ext->key_len = 0; 19158c2ecf20Sopenharmony_ci encoding->flags |= IW_ENCODE_DISABLED; 19168c2ecf20Sopenharmony_ci } else { 19178c2ecf20Sopenharmony_ci if (priv->encryption_level > 0) 19188c2ecf20Sopenharmony_ci ext->alg = IW_ENCODE_ALG_WEP; 19198c2ecf20Sopenharmony_ci else 19208c2ecf20Sopenharmony_ci return -EINVAL; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci ext->key_len = priv->wep_key_len[idx]; 19238c2ecf20Sopenharmony_ci memcpy(ext->key, priv->wep_keys[idx], ext->key_len); 19248c2ecf20Sopenharmony_ci encoding->flags |= IW_ENCODE_ENABLED; 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci return 0; 19288c2ecf20Sopenharmony_ci} 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_cistatic int atmel_set_auth(struct net_device *dev, 19318c2ecf20Sopenharmony_ci struct iw_request_info *info, 19328c2ecf20Sopenharmony_ci union iwreq_data *wrqu, char *extra) 19338c2ecf20Sopenharmony_ci{ 19348c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 19358c2ecf20Sopenharmony_ci struct iw_param *param = &wrqu->param; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci switch (param->flags & IW_AUTH_INDEX) { 19388c2ecf20Sopenharmony_ci case IW_AUTH_WPA_VERSION: 19398c2ecf20Sopenharmony_ci case IW_AUTH_CIPHER_PAIRWISE: 19408c2ecf20Sopenharmony_ci case IW_AUTH_CIPHER_GROUP: 19418c2ecf20Sopenharmony_ci case IW_AUTH_KEY_MGMT: 19428c2ecf20Sopenharmony_ci case IW_AUTH_RX_UNENCRYPTED_EAPOL: 19438c2ecf20Sopenharmony_ci case IW_AUTH_PRIVACY_INVOKED: 19448c2ecf20Sopenharmony_ci /* 19458c2ecf20Sopenharmony_ci * atmel does not use these parameters 19468c2ecf20Sopenharmony_ci */ 19478c2ecf20Sopenharmony_ci break; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci case IW_AUTH_DROP_UNENCRYPTED: 19508c2ecf20Sopenharmony_ci priv->exclude_unencrypted = param->value ? 1 : 0; 19518c2ecf20Sopenharmony_ci break; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci case IW_AUTH_80211_AUTH_ALG: { 19548c2ecf20Sopenharmony_ci if (param->value & IW_AUTH_ALG_SHARED_KEY) { 19558c2ecf20Sopenharmony_ci priv->exclude_unencrypted = 1; 19568c2ecf20Sopenharmony_ci } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { 19578c2ecf20Sopenharmony_ci priv->exclude_unencrypted = 0; 19588c2ecf20Sopenharmony_ci } else 19598c2ecf20Sopenharmony_ci return -EINVAL; 19608c2ecf20Sopenharmony_ci break; 19618c2ecf20Sopenharmony_ci } 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci case IW_AUTH_WPA_ENABLED: 19648c2ecf20Sopenharmony_ci /* Silently accept disable of WPA */ 19658c2ecf20Sopenharmony_ci if (param->value > 0) 19668c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 19678c2ecf20Sopenharmony_ci break; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci default: 19708c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 19718c2ecf20Sopenharmony_ci } 19728c2ecf20Sopenharmony_ci return -EINPROGRESS; 19738c2ecf20Sopenharmony_ci} 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_cistatic int atmel_get_auth(struct net_device *dev, 19768c2ecf20Sopenharmony_ci struct iw_request_info *info, 19778c2ecf20Sopenharmony_ci union iwreq_data *wrqu, char *extra) 19788c2ecf20Sopenharmony_ci{ 19798c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 19808c2ecf20Sopenharmony_ci struct iw_param *param = &wrqu->param; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci switch (param->flags & IW_AUTH_INDEX) { 19838c2ecf20Sopenharmony_ci case IW_AUTH_DROP_UNENCRYPTED: 19848c2ecf20Sopenharmony_ci param->value = priv->exclude_unencrypted; 19858c2ecf20Sopenharmony_ci break; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci case IW_AUTH_80211_AUTH_ALG: 19888c2ecf20Sopenharmony_ci if (priv->exclude_unencrypted == 1) 19898c2ecf20Sopenharmony_ci param->value = IW_AUTH_ALG_SHARED_KEY; 19908c2ecf20Sopenharmony_ci else 19918c2ecf20Sopenharmony_ci param->value = IW_AUTH_ALG_OPEN_SYSTEM; 19928c2ecf20Sopenharmony_ci break; 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci case IW_AUTH_WPA_ENABLED: 19958c2ecf20Sopenharmony_ci param->value = 0; 19968c2ecf20Sopenharmony_ci break; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci default: 19998c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 20008c2ecf20Sopenharmony_ci } 20018c2ecf20Sopenharmony_ci return 0; 20028c2ecf20Sopenharmony_ci} 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_cistatic int atmel_get_name(struct net_device *dev, 20068c2ecf20Sopenharmony_ci struct iw_request_info *info, 20078c2ecf20Sopenharmony_ci char *cwrq, 20088c2ecf20Sopenharmony_ci char *extra) 20098c2ecf20Sopenharmony_ci{ 20108c2ecf20Sopenharmony_ci strcpy(cwrq, "IEEE 802.11-DS"); 20118c2ecf20Sopenharmony_ci return 0; 20128c2ecf20Sopenharmony_ci} 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_cistatic int atmel_set_rate(struct net_device *dev, 20158c2ecf20Sopenharmony_ci struct iw_request_info *info, 20168c2ecf20Sopenharmony_ci struct iw_param *vwrq, 20178c2ecf20Sopenharmony_ci char *extra) 20188c2ecf20Sopenharmony_ci{ 20198c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci if (vwrq->fixed == 0) { 20228c2ecf20Sopenharmony_ci priv->tx_rate = 3; 20238c2ecf20Sopenharmony_ci priv->auto_tx_rate = 1; 20248c2ecf20Sopenharmony_ci } else { 20258c2ecf20Sopenharmony_ci priv->auto_tx_rate = 0; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci /* Which type of value ? */ 20288c2ecf20Sopenharmony_ci if ((vwrq->value < 4) && (vwrq->value >= 0)) { 20298c2ecf20Sopenharmony_ci /* Setting by rate index */ 20308c2ecf20Sopenharmony_ci priv->tx_rate = vwrq->value; 20318c2ecf20Sopenharmony_ci } else { 20328c2ecf20Sopenharmony_ci /* Setting by frequency value */ 20338c2ecf20Sopenharmony_ci switch (vwrq->value) { 20348c2ecf20Sopenharmony_ci case 1000000: 20358c2ecf20Sopenharmony_ci priv->tx_rate = 0; 20368c2ecf20Sopenharmony_ci break; 20378c2ecf20Sopenharmony_ci case 2000000: 20388c2ecf20Sopenharmony_ci priv->tx_rate = 1; 20398c2ecf20Sopenharmony_ci break; 20408c2ecf20Sopenharmony_ci case 5500000: 20418c2ecf20Sopenharmony_ci priv->tx_rate = 2; 20428c2ecf20Sopenharmony_ci break; 20438c2ecf20Sopenharmony_ci case 11000000: 20448c2ecf20Sopenharmony_ci priv->tx_rate = 3; 20458c2ecf20Sopenharmony_ci break; 20468c2ecf20Sopenharmony_ci default: 20478c2ecf20Sopenharmony_ci return -EINVAL; 20488c2ecf20Sopenharmony_ci } 20498c2ecf20Sopenharmony_ci } 20508c2ecf20Sopenharmony_ci } 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci return -EINPROGRESS; 20538c2ecf20Sopenharmony_ci} 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_cistatic int atmel_set_mode(struct net_device *dev, 20568c2ecf20Sopenharmony_ci struct iw_request_info *info, 20578c2ecf20Sopenharmony_ci __u32 *uwrq, 20588c2ecf20Sopenharmony_ci char *extra) 20598c2ecf20Sopenharmony_ci{ 20608c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci if (*uwrq != IW_MODE_ADHOC && *uwrq != IW_MODE_INFRA) 20638c2ecf20Sopenharmony_ci return -EINVAL; 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci priv->operating_mode = *uwrq; 20668c2ecf20Sopenharmony_ci return -EINPROGRESS; 20678c2ecf20Sopenharmony_ci} 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_cistatic int atmel_get_mode(struct net_device *dev, 20708c2ecf20Sopenharmony_ci struct iw_request_info *info, 20718c2ecf20Sopenharmony_ci __u32 *uwrq, 20728c2ecf20Sopenharmony_ci char *extra) 20738c2ecf20Sopenharmony_ci{ 20748c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci *uwrq = priv->operating_mode; 20778c2ecf20Sopenharmony_ci return 0; 20788c2ecf20Sopenharmony_ci} 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_cistatic int atmel_get_rate(struct net_device *dev, 20818c2ecf20Sopenharmony_ci struct iw_request_info *info, 20828c2ecf20Sopenharmony_ci struct iw_param *vwrq, 20838c2ecf20Sopenharmony_ci char *extra) 20848c2ecf20Sopenharmony_ci{ 20858c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci if (priv->auto_tx_rate) { 20888c2ecf20Sopenharmony_ci vwrq->fixed = 0; 20898c2ecf20Sopenharmony_ci vwrq->value = 11000000; 20908c2ecf20Sopenharmony_ci } else { 20918c2ecf20Sopenharmony_ci vwrq->fixed = 1; 20928c2ecf20Sopenharmony_ci switch (priv->tx_rate) { 20938c2ecf20Sopenharmony_ci case 0: 20948c2ecf20Sopenharmony_ci vwrq->value = 1000000; 20958c2ecf20Sopenharmony_ci break; 20968c2ecf20Sopenharmony_ci case 1: 20978c2ecf20Sopenharmony_ci vwrq->value = 2000000; 20988c2ecf20Sopenharmony_ci break; 20998c2ecf20Sopenharmony_ci case 2: 21008c2ecf20Sopenharmony_ci vwrq->value = 5500000; 21018c2ecf20Sopenharmony_ci break; 21028c2ecf20Sopenharmony_ci case 3: 21038c2ecf20Sopenharmony_ci vwrq->value = 11000000; 21048c2ecf20Sopenharmony_ci break; 21058c2ecf20Sopenharmony_ci } 21068c2ecf20Sopenharmony_ci } 21078c2ecf20Sopenharmony_ci return 0; 21088c2ecf20Sopenharmony_ci} 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_cistatic int atmel_set_power(struct net_device *dev, 21118c2ecf20Sopenharmony_ci struct iw_request_info *info, 21128c2ecf20Sopenharmony_ci struct iw_param *vwrq, 21138c2ecf20Sopenharmony_ci char *extra) 21148c2ecf20Sopenharmony_ci{ 21158c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 21168c2ecf20Sopenharmony_ci priv->power_mode = vwrq->disabled ? 0 : 1; 21178c2ecf20Sopenharmony_ci return -EINPROGRESS; 21188c2ecf20Sopenharmony_ci} 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_cistatic int atmel_get_power(struct net_device *dev, 21218c2ecf20Sopenharmony_ci struct iw_request_info *info, 21228c2ecf20Sopenharmony_ci struct iw_param *vwrq, 21238c2ecf20Sopenharmony_ci char *extra) 21248c2ecf20Sopenharmony_ci{ 21258c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 21268c2ecf20Sopenharmony_ci vwrq->disabled = priv->power_mode ? 0 : 1; 21278c2ecf20Sopenharmony_ci vwrq->flags = IW_POWER_ON; 21288c2ecf20Sopenharmony_ci return 0; 21298c2ecf20Sopenharmony_ci} 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_cistatic int atmel_set_retry(struct net_device *dev, 21328c2ecf20Sopenharmony_ci struct iw_request_info *info, 21338c2ecf20Sopenharmony_ci struct iw_param *vwrq, 21348c2ecf20Sopenharmony_ci char *extra) 21358c2ecf20Sopenharmony_ci{ 21368c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) { 21398c2ecf20Sopenharmony_ci if (vwrq->flags & IW_RETRY_LONG) 21408c2ecf20Sopenharmony_ci priv->long_retry = vwrq->value; 21418c2ecf20Sopenharmony_ci else if (vwrq->flags & IW_RETRY_SHORT) 21428c2ecf20Sopenharmony_ci priv->short_retry = vwrq->value; 21438c2ecf20Sopenharmony_ci else { 21448c2ecf20Sopenharmony_ci /* No modifier : set both */ 21458c2ecf20Sopenharmony_ci priv->long_retry = vwrq->value; 21468c2ecf20Sopenharmony_ci priv->short_retry = vwrq->value; 21478c2ecf20Sopenharmony_ci } 21488c2ecf20Sopenharmony_ci return -EINPROGRESS; 21498c2ecf20Sopenharmony_ci } 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci return -EINVAL; 21528c2ecf20Sopenharmony_ci} 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_cistatic int atmel_get_retry(struct net_device *dev, 21558c2ecf20Sopenharmony_ci struct iw_request_info *info, 21568c2ecf20Sopenharmony_ci struct iw_param *vwrq, 21578c2ecf20Sopenharmony_ci char *extra) 21588c2ecf20Sopenharmony_ci{ 21598c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci vwrq->disabled = 0; /* Can't be disabled */ 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci /* Note : by default, display the short retry number */ 21648c2ecf20Sopenharmony_ci if (vwrq->flags & IW_RETRY_LONG) { 21658c2ecf20Sopenharmony_ci vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; 21668c2ecf20Sopenharmony_ci vwrq->value = priv->long_retry; 21678c2ecf20Sopenharmony_ci } else { 21688c2ecf20Sopenharmony_ci vwrq->flags = IW_RETRY_LIMIT; 21698c2ecf20Sopenharmony_ci vwrq->value = priv->short_retry; 21708c2ecf20Sopenharmony_ci if (priv->long_retry != priv->short_retry) 21718c2ecf20Sopenharmony_ci vwrq->flags |= IW_RETRY_SHORT; 21728c2ecf20Sopenharmony_ci } 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci return 0; 21758c2ecf20Sopenharmony_ci} 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_cistatic int atmel_set_rts(struct net_device *dev, 21788c2ecf20Sopenharmony_ci struct iw_request_info *info, 21798c2ecf20Sopenharmony_ci struct iw_param *vwrq, 21808c2ecf20Sopenharmony_ci char *extra) 21818c2ecf20Sopenharmony_ci{ 21828c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 21838c2ecf20Sopenharmony_ci int rthr = vwrq->value; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci if (vwrq->disabled) 21868c2ecf20Sopenharmony_ci rthr = 2347; 21878c2ecf20Sopenharmony_ci if ((rthr < 0) || (rthr > 2347)) { 21888c2ecf20Sopenharmony_ci return -EINVAL; 21898c2ecf20Sopenharmony_ci } 21908c2ecf20Sopenharmony_ci priv->rts_threshold = rthr; 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 21938c2ecf20Sopenharmony_ci} 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_cistatic int atmel_get_rts(struct net_device *dev, 21968c2ecf20Sopenharmony_ci struct iw_request_info *info, 21978c2ecf20Sopenharmony_ci struct iw_param *vwrq, 21988c2ecf20Sopenharmony_ci char *extra) 21998c2ecf20Sopenharmony_ci{ 22008c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci vwrq->value = priv->rts_threshold; 22038c2ecf20Sopenharmony_ci vwrq->disabled = (vwrq->value >= 2347); 22048c2ecf20Sopenharmony_ci vwrq->fixed = 1; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci return 0; 22078c2ecf20Sopenharmony_ci} 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_cistatic int atmel_set_frag(struct net_device *dev, 22108c2ecf20Sopenharmony_ci struct iw_request_info *info, 22118c2ecf20Sopenharmony_ci struct iw_param *vwrq, 22128c2ecf20Sopenharmony_ci char *extra) 22138c2ecf20Sopenharmony_ci{ 22148c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 22158c2ecf20Sopenharmony_ci int fthr = vwrq->value; 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci if (vwrq->disabled) 22188c2ecf20Sopenharmony_ci fthr = 2346; 22198c2ecf20Sopenharmony_ci if ((fthr < 256) || (fthr > 2346)) { 22208c2ecf20Sopenharmony_ci return -EINVAL; 22218c2ecf20Sopenharmony_ci } 22228c2ecf20Sopenharmony_ci fthr &= ~0x1; /* Get an even value - is it really needed ??? */ 22238c2ecf20Sopenharmony_ci priv->frag_threshold = fthr; 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 22268c2ecf20Sopenharmony_ci} 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_cistatic int atmel_get_frag(struct net_device *dev, 22298c2ecf20Sopenharmony_ci struct iw_request_info *info, 22308c2ecf20Sopenharmony_ci struct iw_param *vwrq, 22318c2ecf20Sopenharmony_ci char *extra) 22328c2ecf20Sopenharmony_ci{ 22338c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci vwrq->value = priv->frag_threshold; 22368c2ecf20Sopenharmony_ci vwrq->disabled = (vwrq->value >= 2346); 22378c2ecf20Sopenharmony_ci vwrq->fixed = 1; 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci return 0; 22408c2ecf20Sopenharmony_ci} 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_cistatic int atmel_set_freq(struct net_device *dev, 22438c2ecf20Sopenharmony_ci struct iw_request_info *info, 22448c2ecf20Sopenharmony_ci struct iw_freq *fwrq, 22458c2ecf20Sopenharmony_ci char *extra) 22468c2ecf20Sopenharmony_ci{ 22478c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 22488c2ecf20Sopenharmony_ci int rc = -EINPROGRESS; /* Call commit handler */ 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci /* If setting by frequency, convert to a channel */ 22518c2ecf20Sopenharmony_ci if (fwrq->e == 1) { 22528c2ecf20Sopenharmony_ci int f = fwrq->m / 100000; 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci /* Hack to fall through... */ 22558c2ecf20Sopenharmony_ci fwrq->e = 0; 22568c2ecf20Sopenharmony_ci fwrq->m = ieee80211_frequency_to_channel(f); 22578c2ecf20Sopenharmony_ci } 22588c2ecf20Sopenharmony_ci /* Setting by channel number */ 22598c2ecf20Sopenharmony_ci if (fwrq->m < 0 || fwrq->m > 1000 || fwrq->e > 0) 22608c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 22618c2ecf20Sopenharmony_ci else { 22628c2ecf20Sopenharmony_ci int channel = fwrq->m; 22638c2ecf20Sopenharmony_ci if (atmel_validate_channel(priv, channel) == 0) { 22648c2ecf20Sopenharmony_ci priv->channel = channel; 22658c2ecf20Sopenharmony_ci } else { 22668c2ecf20Sopenharmony_ci rc = -EINVAL; 22678c2ecf20Sopenharmony_ci } 22688c2ecf20Sopenharmony_ci } 22698c2ecf20Sopenharmony_ci return rc; 22708c2ecf20Sopenharmony_ci} 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_cistatic int atmel_get_freq(struct net_device *dev, 22738c2ecf20Sopenharmony_ci struct iw_request_info *info, 22748c2ecf20Sopenharmony_ci struct iw_freq *fwrq, 22758c2ecf20Sopenharmony_ci char *extra) 22768c2ecf20Sopenharmony_ci{ 22778c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci fwrq->m = priv->channel; 22808c2ecf20Sopenharmony_ci fwrq->e = 0; 22818c2ecf20Sopenharmony_ci return 0; 22828c2ecf20Sopenharmony_ci} 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_cistatic int atmel_set_scan(struct net_device *dev, 22858c2ecf20Sopenharmony_ci struct iw_request_info *info, 22868c2ecf20Sopenharmony_ci struct iw_point *dwrq, 22878c2ecf20Sopenharmony_ci char *extra) 22888c2ecf20Sopenharmony_ci{ 22898c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 22908c2ecf20Sopenharmony_ci unsigned long flags; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci /* Note : you may have realised that, as this is a SET operation, 22938c2ecf20Sopenharmony_ci * this is privileged and therefore a normal user can't 22948c2ecf20Sopenharmony_ci * perform scanning. 22958c2ecf20Sopenharmony_ci * This is not an error, while the device perform scanning, 22968c2ecf20Sopenharmony_ci * traffic doesn't flow, so it's a perfect DoS... 22978c2ecf20Sopenharmony_ci * Jean II */ 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci if (priv->station_state == STATION_STATE_DOWN) 23008c2ecf20Sopenharmony_ci return -EAGAIN; 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci /* Timeout old surveys. */ 23038c2ecf20Sopenharmony_ci if (time_after(jiffies, priv->last_survey + 20 * HZ)) 23048c2ecf20Sopenharmony_ci priv->site_survey_state = SITE_SURVEY_IDLE; 23058c2ecf20Sopenharmony_ci priv->last_survey = jiffies; 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci /* Initiate a scan command */ 23088c2ecf20Sopenharmony_ci if (priv->site_survey_state == SITE_SURVEY_IN_PROGRESS) 23098c2ecf20Sopenharmony_ci return -EBUSY; 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci del_timer_sync(&priv->management_timer); 23128c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->irqlock, flags); 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci priv->site_survey_state = SITE_SURVEY_IN_PROGRESS; 23158c2ecf20Sopenharmony_ci priv->fast_scan = 0; 23168c2ecf20Sopenharmony_ci atmel_scan(priv, 0); 23178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci return 0; 23208c2ecf20Sopenharmony_ci} 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_cistatic int atmel_get_scan(struct net_device *dev, 23238c2ecf20Sopenharmony_ci struct iw_request_info *info, 23248c2ecf20Sopenharmony_ci struct iw_point *dwrq, 23258c2ecf20Sopenharmony_ci char *extra) 23268c2ecf20Sopenharmony_ci{ 23278c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 23288c2ecf20Sopenharmony_ci int i; 23298c2ecf20Sopenharmony_ci char *current_ev = extra; 23308c2ecf20Sopenharmony_ci struct iw_event iwe; 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci if (priv->site_survey_state != SITE_SURVEY_COMPLETED) 23338c2ecf20Sopenharmony_ci return -EAGAIN; 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci for (i = 0; i < priv->BSS_list_entries; i++) { 23368c2ecf20Sopenharmony_ci iwe.cmd = SIOCGIWAP; 23378c2ecf20Sopenharmony_ci iwe.u.ap_addr.sa_family = ARPHRD_ETHER; 23388c2ecf20Sopenharmony_ci memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN); 23398c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, 23408c2ecf20Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 23418c2ecf20Sopenharmony_ci &iwe, IW_EV_ADDR_LEN); 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_ci iwe.u.data.length = priv->BSSinfo[i].SSIDsize; 23448c2ecf20Sopenharmony_ci if (iwe.u.data.length > 32) 23458c2ecf20Sopenharmony_ci iwe.u.data.length = 32; 23468c2ecf20Sopenharmony_ci iwe.cmd = SIOCGIWESSID; 23478c2ecf20Sopenharmony_ci iwe.u.data.flags = 1; 23488c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_point(info, current_ev, 23498c2ecf20Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 23508c2ecf20Sopenharmony_ci &iwe, priv->BSSinfo[i].SSID); 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci iwe.cmd = SIOCGIWMODE; 23538c2ecf20Sopenharmony_ci iwe.u.mode = priv->BSSinfo[i].BSStype; 23548c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, 23558c2ecf20Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 23568c2ecf20Sopenharmony_ci &iwe, IW_EV_UINT_LEN); 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci iwe.cmd = SIOCGIWFREQ; 23598c2ecf20Sopenharmony_ci iwe.u.freq.m = priv->BSSinfo[i].channel; 23608c2ecf20Sopenharmony_ci iwe.u.freq.e = 0; 23618c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, 23628c2ecf20Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 23638c2ecf20Sopenharmony_ci &iwe, IW_EV_FREQ_LEN); 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci /* Add quality statistics */ 23668c2ecf20Sopenharmony_ci iwe.cmd = IWEVQUAL; 23678c2ecf20Sopenharmony_ci iwe.u.qual.level = priv->BSSinfo[i].RSSI; 23688c2ecf20Sopenharmony_ci iwe.u.qual.qual = iwe.u.qual.level; 23698c2ecf20Sopenharmony_ci /* iwe.u.qual.noise = SOMETHING */ 23708c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, 23718c2ecf20Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 23728c2ecf20Sopenharmony_ci &iwe, IW_EV_QUAL_LEN); 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci iwe.cmd = SIOCGIWENCODE; 23768c2ecf20Sopenharmony_ci if (priv->BSSinfo[i].UsingWEP) 23778c2ecf20Sopenharmony_ci iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; 23788c2ecf20Sopenharmony_ci else 23798c2ecf20Sopenharmony_ci iwe.u.data.flags = IW_ENCODE_DISABLED; 23808c2ecf20Sopenharmony_ci iwe.u.data.length = 0; 23818c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_point(info, current_ev, 23828c2ecf20Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 23838c2ecf20Sopenharmony_ci &iwe, NULL); 23848c2ecf20Sopenharmony_ci } 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci /* Length of data */ 23878c2ecf20Sopenharmony_ci dwrq->length = (current_ev - extra); 23888c2ecf20Sopenharmony_ci dwrq->flags = 0; 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci return 0; 23918c2ecf20Sopenharmony_ci} 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_cistatic int atmel_get_range(struct net_device *dev, 23948c2ecf20Sopenharmony_ci struct iw_request_info *info, 23958c2ecf20Sopenharmony_ci struct iw_point *dwrq, 23968c2ecf20Sopenharmony_ci char *extra) 23978c2ecf20Sopenharmony_ci{ 23988c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 23998c2ecf20Sopenharmony_ci struct iw_range *range = (struct iw_range *) extra; 24008c2ecf20Sopenharmony_ci int k, i, j; 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci dwrq->length = sizeof(struct iw_range); 24038c2ecf20Sopenharmony_ci memset(range, 0, sizeof(struct iw_range)); 24048c2ecf20Sopenharmony_ci range->min_nwid = 0x0000; 24058c2ecf20Sopenharmony_ci range->max_nwid = 0x0000; 24068c2ecf20Sopenharmony_ci range->num_channels = 0; 24078c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(channel_table); j++) 24088c2ecf20Sopenharmony_ci if (priv->reg_domain == channel_table[j].reg_domain) { 24098c2ecf20Sopenharmony_ci range->num_channels = channel_table[j].max - channel_table[j].min + 1; 24108c2ecf20Sopenharmony_ci break; 24118c2ecf20Sopenharmony_ci } 24128c2ecf20Sopenharmony_ci if (range->num_channels != 0) { 24138c2ecf20Sopenharmony_ci for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) { 24148c2ecf20Sopenharmony_ci range->freq[k].i = i; /* List index */ 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci /* Values in MHz -> * 10^5 * 10 */ 24178c2ecf20Sopenharmony_ci range->freq[k].m = 100000 * 24188c2ecf20Sopenharmony_ci ieee80211_channel_to_frequency(i, NL80211_BAND_2GHZ); 24198c2ecf20Sopenharmony_ci range->freq[k++].e = 1; 24208c2ecf20Sopenharmony_ci } 24218c2ecf20Sopenharmony_ci range->num_frequency = k; 24228c2ecf20Sopenharmony_ci } 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci range->max_qual.qual = 100; 24258c2ecf20Sopenharmony_ci range->max_qual.level = 100; 24268c2ecf20Sopenharmony_ci range->max_qual.noise = 0; 24278c2ecf20Sopenharmony_ci range->max_qual.updated = IW_QUAL_NOISE_INVALID; 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci range->avg_qual.qual = 50; 24308c2ecf20Sopenharmony_ci range->avg_qual.level = 50; 24318c2ecf20Sopenharmony_ci range->avg_qual.noise = 0; 24328c2ecf20Sopenharmony_ci range->avg_qual.updated = IW_QUAL_NOISE_INVALID; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci range->sensitivity = 0; 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci range->bitrate[0] = 1000000; 24378c2ecf20Sopenharmony_ci range->bitrate[1] = 2000000; 24388c2ecf20Sopenharmony_ci range->bitrate[2] = 5500000; 24398c2ecf20Sopenharmony_ci range->bitrate[3] = 11000000; 24408c2ecf20Sopenharmony_ci range->num_bitrates = 4; 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci range->min_rts = 0; 24438c2ecf20Sopenharmony_ci range->max_rts = 2347; 24448c2ecf20Sopenharmony_ci range->min_frag = 256; 24458c2ecf20Sopenharmony_ci range->max_frag = 2346; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci range->encoding_size[0] = 5; 24488c2ecf20Sopenharmony_ci range->encoding_size[1] = 13; 24498c2ecf20Sopenharmony_ci range->num_encoding_sizes = 2; 24508c2ecf20Sopenharmony_ci range->max_encoding_tokens = 4; 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci range->pmp_flags = IW_POWER_ON; 24538c2ecf20Sopenharmony_ci range->pmt_flags = IW_POWER_ON; 24548c2ecf20Sopenharmony_ci range->pm_capa = 0; 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci range->we_version_source = WIRELESS_EXT; 24578c2ecf20Sopenharmony_ci range->we_version_compiled = WIRELESS_EXT; 24588c2ecf20Sopenharmony_ci range->retry_capa = IW_RETRY_LIMIT ; 24598c2ecf20Sopenharmony_ci range->retry_flags = IW_RETRY_LIMIT; 24608c2ecf20Sopenharmony_ci range->r_time_flags = 0; 24618c2ecf20Sopenharmony_ci range->min_retry = 1; 24628c2ecf20Sopenharmony_ci range->max_retry = 65535; 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci return 0; 24658c2ecf20Sopenharmony_ci} 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_cistatic int atmel_set_wap(struct net_device *dev, 24688c2ecf20Sopenharmony_ci struct iw_request_info *info, 24698c2ecf20Sopenharmony_ci struct sockaddr *awrq, 24708c2ecf20Sopenharmony_ci char *extra) 24718c2ecf20Sopenharmony_ci{ 24728c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 24738c2ecf20Sopenharmony_ci int i; 24748c2ecf20Sopenharmony_ci static const u8 any[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 24758c2ecf20Sopenharmony_ci static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 24768c2ecf20Sopenharmony_ci unsigned long flags; 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci if (awrq->sa_family != ARPHRD_ETHER) 24798c2ecf20Sopenharmony_ci return -EINVAL; 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci if (!memcmp(any, awrq->sa_data, 6) || 24828c2ecf20Sopenharmony_ci !memcmp(off, awrq->sa_data, 6)) { 24838c2ecf20Sopenharmony_ci del_timer_sync(&priv->management_timer); 24848c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->irqlock, flags); 24858c2ecf20Sopenharmony_ci atmel_scan(priv, 1); 24868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 24878c2ecf20Sopenharmony_ci return 0; 24888c2ecf20Sopenharmony_ci } 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci for (i = 0; i < priv->BSS_list_entries; i++) { 24918c2ecf20Sopenharmony_ci if (memcmp(priv->BSSinfo[i].BSSID, awrq->sa_data, 6) == 0) { 24928c2ecf20Sopenharmony_ci if (!priv->wep_is_on && priv->BSSinfo[i].UsingWEP) { 24938c2ecf20Sopenharmony_ci return -EINVAL; 24948c2ecf20Sopenharmony_ci } else if (priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) { 24958c2ecf20Sopenharmony_ci return -EINVAL; 24968c2ecf20Sopenharmony_ci } else { 24978c2ecf20Sopenharmony_ci del_timer_sync(&priv->management_timer); 24988c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->irqlock, flags); 24998c2ecf20Sopenharmony_ci atmel_join_bss(priv, i); 25008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 25018c2ecf20Sopenharmony_ci return 0; 25028c2ecf20Sopenharmony_ci } 25038c2ecf20Sopenharmony_ci } 25048c2ecf20Sopenharmony_ci } 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci return -EINVAL; 25078c2ecf20Sopenharmony_ci} 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_cistatic int atmel_config_commit(struct net_device *dev, 25108c2ecf20Sopenharmony_ci struct iw_request_info *info, /* NULL */ 25118c2ecf20Sopenharmony_ci void *zwrq, /* NULL */ 25128c2ecf20Sopenharmony_ci char *extra) /* NULL */ 25138c2ecf20Sopenharmony_ci{ 25148c2ecf20Sopenharmony_ci return atmel_open(dev); 25158c2ecf20Sopenharmony_ci} 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_cistatic const iw_handler atmel_handler[] = 25188c2ecf20Sopenharmony_ci{ 25198c2ecf20Sopenharmony_ci (iw_handler) atmel_config_commit, /* SIOCSIWCOMMIT */ 25208c2ecf20Sopenharmony_ci (iw_handler) atmel_get_name, /* SIOCGIWNAME */ 25218c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWNWID */ 25228c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWNWID */ 25238c2ecf20Sopenharmony_ci (iw_handler) atmel_set_freq, /* SIOCSIWFREQ */ 25248c2ecf20Sopenharmony_ci (iw_handler) atmel_get_freq, /* SIOCGIWFREQ */ 25258c2ecf20Sopenharmony_ci (iw_handler) atmel_set_mode, /* SIOCSIWMODE */ 25268c2ecf20Sopenharmony_ci (iw_handler) atmel_get_mode, /* SIOCGIWMODE */ 25278c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWSENS */ 25288c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWSENS */ 25298c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWRANGE */ 25308c2ecf20Sopenharmony_ci (iw_handler) atmel_get_range, /* SIOCGIWRANGE */ 25318c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWPRIV */ 25328c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWPRIV */ 25338c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWSTATS */ 25348c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWSTATS */ 25358c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWSPY */ 25368c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWSPY */ 25378c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 25388c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 25398c2ecf20Sopenharmony_ci (iw_handler) atmel_set_wap, /* SIOCSIWAP */ 25408c2ecf20Sopenharmony_ci (iw_handler) atmel_get_wap, /* SIOCGIWAP */ 25418c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 25428c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWAPLIST */ 25438c2ecf20Sopenharmony_ci (iw_handler) atmel_set_scan, /* SIOCSIWSCAN */ 25448c2ecf20Sopenharmony_ci (iw_handler) atmel_get_scan, /* SIOCGIWSCAN */ 25458c2ecf20Sopenharmony_ci (iw_handler) atmel_set_essid, /* SIOCSIWESSID */ 25468c2ecf20Sopenharmony_ci (iw_handler) atmel_get_essid, /* SIOCGIWESSID */ 25478c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWNICKN */ 25488c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWNICKN */ 25498c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 25508c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 25518c2ecf20Sopenharmony_ci (iw_handler) atmel_set_rate, /* SIOCSIWRATE */ 25528c2ecf20Sopenharmony_ci (iw_handler) atmel_get_rate, /* SIOCGIWRATE */ 25538c2ecf20Sopenharmony_ci (iw_handler) atmel_set_rts, /* SIOCSIWRTS */ 25548c2ecf20Sopenharmony_ci (iw_handler) atmel_get_rts, /* SIOCGIWRTS */ 25558c2ecf20Sopenharmony_ci (iw_handler) atmel_set_frag, /* SIOCSIWFRAG */ 25568c2ecf20Sopenharmony_ci (iw_handler) atmel_get_frag, /* SIOCGIWFRAG */ 25578c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWTXPOW */ 25588c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWTXPOW */ 25598c2ecf20Sopenharmony_ci (iw_handler) atmel_set_retry, /* SIOCSIWRETRY */ 25608c2ecf20Sopenharmony_ci (iw_handler) atmel_get_retry, /* SIOCGIWRETRY */ 25618c2ecf20Sopenharmony_ci (iw_handler) atmel_set_encode, /* SIOCSIWENCODE */ 25628c2ecf20Sopenharmony_ci (iw_handler) atmel_get_encode, /* SIOCGIWENCODE */ 25638c2ecf20Sopenharmony_ci (iw_handler) atmel_set_power, /* SIOCSIWPOWER */ 25648c2ecf20Sopenharmony_ci (iw_handler) atmel_get_power, /* SIOCGIWPOWER */ 25658c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 25668c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 25678c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWGENIE */ 25688c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWGENIE */ 25698c2ecf20Sopenharmony_ci (iw_handler) atmel_set_auth, /* SIOCSIWAUTH */ 25708c2ecf20Sopenharmony_ci (iw_handler) atmel_get_auth, /* SIOCGIWAUTH */ 25718c2ecf20Sopenharmony_ci (iw_handler) atmel_set_encodeext, /* SIOCSIWENCODEEXT */ 25728c2ecf20Sopenharmony_ci (iw_handler) atmel_get_encodeext, /* SIOCGIWENCODEEXT */ 25738c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWPMKSA */ 25748c2ecf20Sopenharmony_ci}; 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_cistatic const iw_handler atmel_private_handler[] = 25778c2ecf20Sopenharmony_ci{ 25788c2ecf20Sopenharmony_ci NULL, /* SIOCIWFIRSTPRIV */ 25798c2ecf20Sopenharmony_ci}; 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_cistruct atmel_priv_ioctl { 25828c2ecf20Sopenharmony_ci char id[32]; 25838c2ecf20Sopenharmony_ci unsigned char __user *data; 25848c2ecf20Sopenharmony_ci unsigned short len; 25858c2ecf20Sopenharmony_ci}; 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci#define ATMELFWL SIOCIWFIRSTPRIV 25888c2ecf20Sopenharmony_ci#define ATMELIDIFC ATMELFWL + 1 25898c2ecf20Sopenharmony_ci#define ATMELRD ATMELFWL + 2 25908c2ecf20Sopenharmony_ci#define ATMELMAGIC 0x51807 25918c2ecf20Sopenharmony_ci#define REGDOMAINSZ 20 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_cistatic const struct iw_priv_args atmel_private_args[] = { 25948c2ecf20Sopenharmony_ci { 25958c2ecf20Sopenharmony_ci .cmd = ATMELFWL, 25968c2ecf20Sopenharmony_ci .set_args = IW_PRIV_TYPE_BYTE 25978c2ecf20Sopenharmony_ci | IW_PRIV_SIZE_FIXED 25988c2ecf20Sopenharmony_ci | sizeof(struct atmel_priv_ioctl), 25998c2ecf20Sopenharmony_ci .get_args = IW_PRIV_TYPE_NONE, 26008c2ecf20Sopenharmony_ci .name = "atmelfwl" 26018c2ecf20Sopenharmony_ci }, { 26028c2ecf20Sopenharmony_ci .cmd = ATMELIDIFC, 26038c2ecf20Sopenharmony_ci .set_args = IW_PRIV_TYPE_NONE, 26048c2ecf20Sopenharmony_ci .get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 26058c2ecf20Sopenharmony_ci .name = "atmelidifc" 26068c2ecf20Sopenharmony_ci }, { 26078c2ecf20Sopenharmony_ci .cmd = ATMELRD, 26088c2ecf20Sopenharmony_ci .set_args = IW_PRIV_TYPE_CHAR | REGDOMAINSZ, 26098c2ecf20Sopenharmony_ci .get_args = IW_PRIV_TYPE_NONE, 26108c2ecf20Sopenharmony_ci .name = "regdomain" 26118c2ecf20Sopenharmony_ci }, 26128c2ecf20Sopenharmony_ci}; 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_cistatic const struct iw_handler_def atmel_handler_def = { 26158c2ecf20Sopenharmony_ci .num_standard = ARRAY_SIZE(atmel_handler), 26168c2ecf20Sopenharmony_ci .num_private = ARRAY_SIZE(atmel_private_handler), 26178c2ecf20Sopenharmony_ci .num_private_args = ARRAY_SIZE(atmel_private_args), 26188c2ecf20Sopenharmony_ci .standard = (iw_handler *) atmel_handler, 26198c2ecf20Sopenharmony_ci .private = (iw_handler *) atmel_private_handler, 26208c2ecf20Sopenharmony_ci .private_args = (struct iw_priv_args *) atmel_private_args, 26218c2ecf20Sopenharmony_ci .get_wireless_stats = atmel_get_wireless_stats 26228c2ecf20Sopenharmony_ci}; 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_cistatic int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 26258c2ecf20Sopenharmony_ci{ 26268c2ecf20Sopenharmony_ci int i, rc = 0; 26278c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 26288c2ecf20Sopenharmony_ci struct atmel_priv_ioctl com; 26298c2ecf20Sopenharmony_ci struct iwreq *wrq = (struct iwreq *) rq; 26308c2ecf20Sopenharmony_ci unsigned char *new_firmware; 26318c2ecf20Sopenharmony_ci char domain[REGDOMAINSZ + 1]; 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_ci switch (cmd) { 26348c2ecf20Sopenharmony_ci case ATMELIDIFC: 26358c2ecf20Sopenharmony_ci wrq->u.param.value = ATMELMAGIC; 26368c2ecf20Sopenharmony_ci break; 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci case ATMELFWL: 26398c2ecf20Sopenharmony_ci if (copy_from_user(&com, rq->ifr_data, sizeof(com))) { 26408c2ecf20Sopenharmony_ci rc = -EFAULT; 26418c2ecf20Sopenharmony_ci break; 26428c2ecf20Sopenharmony_ci } 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 26458c2ecf20Sopenharmony_ci rc = -EPERM; 26468c2ecf20Sopenharmony_ci break; 26478c2ecf20Sopenharmony_ci } 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci new_firmware = memdup_user(com.data, com.len); 26508c2ecf20Sopenharmony_ci if (IS_ERR(new_firmware)) { 26518c2ecf20Sopenharmony_ci rc = PTR_ERR(new_firmware); 26528c2ecf20Sopenharmony_ci break; 26538c2ecf20Sopenharmony_ci } 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ci kfree(priv->firmware); 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_ci priv->firmware = new_firmware; 26588c2ecf20Sopenharmony_ci priv->firmware_length = com.len; 26598c2ecf20Sopenharmony_ci strncpy(priv->firmware_id, com.id, 31); 26608c2ecf20Sopenharmony_ci priv->firmware_id[31] = '\0'; 26618c2ecf20Sopenharmony_ci break; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci case ATMELRD: 26648c2ecf20Sopenharmony_ci if (copy_from_user(domain, rq->ifr_data, REGDOMAINSZ)) { 26658c2ecf20Sopenharmony_ci rc = -EFAULT; 26668c2ecf20Sopenharmony_ci break; 26678c2ecf20Sopenharmony_ci } 26688c2ecf20Sopenharmony_ci 26698c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 26708c2ecf20Sopenharmony_ci rc = -EPERM; 26718c2ecf20Sopenharmony_ci break; 26728c2ecf20Sopenharmony_ci } 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci domain[REGDOMAINSZ] = 0; 26758c2ecf20Sopenharmony_ci rc = -EINVAL; 26768c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_table); i++) { 26778c2ecf20Sopenharmony_ci if (!strcasecmp(channel_table[i].name, domain)) { 26788c2ecf20Sopenharmony_ci priv->config_reg_domain = channel_table[i].reg_domain; 26798c2ecf20Sopenharmony_ci rc = 0; 26808c2ecf20Sopenharmony_ci } 26818c2ecf20Sopenharmony_ci } 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_ci if (rc == 0 && priv->station_state != STATION_STATE_DOWN) 26848c2ecf20Sopenharmony_ci rc = atmel_open(dev); 26858c2ecf20Sopenharmony_ci break; 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci default: 26888c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 26898c2ecf20Sopenharmony_ci } 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci return rc; 26928c2ecf20Sopenharmony_ci} 26938c2ecf20Sopenharmony_ci 26948c2ecf20Sopenharmony_cistruct auth_body { 26958c2ecf20Sopenharmony_ci __le16 alg; 26968c2ecf20Sopenharmony_ci __le16 trans_seq; 26978c2ecf20Sopenharmony_ci __le16 status; 26988c2ecf20Sopenharmony_ci u8 el_id; 26998c2ecf20Sopenharmony_ci u8 chall_text_len; 27008c2ecf20Sopenharmony_ci u8 chall_text[253]; 27018c2ecf20Sopenharmony_ci}; 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_cistatic void atmel_enter_state(struct atmel_private *priv, int new_state) 27048c2ecf20Sopenharmony_ci{ 27058c2ecf20Sopenharmony_ci int old_state = priv->station_state; 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_ci if (new_state == old_state) 27088c2ecf20Sopenharmony_ci return; 27098c2ecf20Sopenharmony_ci 27108c2ecf20Sopenharmony_ci priv->station_state = new_state; 27118c2ecf20Sopenharmony_ci 27128c2ecf20Sopenharmony_ci if (new_state == STATION_STATE_READY) { 27138c2ecf20Sopenharmony_ci netif_start_queue(priv->dev); 27148c2ecf20Sopenharmony_ci netif_carrier_on(priv->dev); 27158c2ecf20Sopenharmony_ci } 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci if (old_state == STATION_STATE_READY) { 27188c2ecf20Sopenharmony_ci netif_carrier_off(priv->dev); 27198c2ecf20Sopenharmony_ci if (netif_running(priv->dev)) 27208c2ecf20Sopenharmony_ci netif_stop_queue(priv->dev); 27218c2ecf20Sopenharmony_ci priv->last_beacon_timestamp = 0; 27228c2ecf20Sopenharmony_ci } 27238c2ecf20Sopenharmony_ci} 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_cistatic void atmel_scan(struct atmel_private *priv, int specific_ssid) 27268c2ecf20Sopenharmony_ci{ 27278c2ecf20Sopenharmony_ci struct { 27288c2ecf20Sopenharmony_ci u8 BSSID[ETH_ALEN]; 27298c2ecf20Sopenharmony_ci u8 SSID[MAX_SSID_LENGTH]; 27308c2ecf20Sopenharmony_ci u8 scan_type; 27318c2ecf20Sopenharmony_ci u8 channel; 27328c2ecf20Sopenharmony_ci __le16 BSS_type; 27338c2ecf20Sopenharmony_ci __le16 min_channel_time; 27348c2ecf20Sopenharmony_ci __le16 max_channel_time; 27358c2ecf20Sopenharmony_ci u8 options; 27368c2ecf20Sopenharmony_ci u8 SSID_size; 27378c2ecf20Sopenharmony_ci } cmd; 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci eth_broadcast_addr(cmd.BSSID); 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_ci if (priv->fast_scan) { 27428c2ecf20Sopenharmony_ci cmd.SSID_size = priv->SSID_size; 27438c2ecf20Sopenharmony_ci memcpy(cmd.SSID, priv->SSID, priv->SSID_size); 27448c2ecf20Sopenharmony_ci cmd.min_channel_time = cpu_to_le16(10); 27458c2ecf20Sopenharmony_ci cmd.max_channel_time = cpu_to_le16(50); 27468c2ecf20Sopenharmony_ci } else { 27478c2ecf20Sopenharmony_ci priv->BSS_list_entries = 0; 27488c2ecf20Sopenharmony_ci cmd.SSID_size = 0; 27498c2ecf20Sopenharmony_ci cmd.min_channel_time = cpu_to_le16(10); 27508c2ecf20Sopenharmony_ci cmd.max_channel_time = cpu_to_le16(120); 27518c2ecf20Sopenharmony_ci } 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci cmd.options = 0; 27548c2ecf20Sopenharmony_ci 27558c2ecf20Sopenharmony_ci if (!specific_ssid) 27568c2ecf20Sopenharmony_ci cmd.options |= SCAN_OPTIONS_SITE_SURVEY; 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci cmd.channel = (priv->channel & 0x7f); 27598c2ecf20Sopenharmony_ci cmd.scan_type = SCAN_TYPE_ACTIVE; 27608c2ecf20Sopenharmony_ci cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ? 27618c2ecf20Sopenharmony_ci BSS_TYPE_AD_HOC : BSS_TYPE_INFRASTRUCTURE); 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_ci atmel_send_command(priv, CMD_Scan, &cmd, sizeof(cmd)); 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ci /* This must come after all hardware access to avoid being messed up 27668c2ecf20Sopenharmony_ci by stuff happening in interrupt context after we leave STATE_DOWN */ 27678c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_SCANNING); 27688c2ecf20Sopenharmony_ci} 27698c2ecf20Sopenharmony_ci 27708c2ecf20Sopenharmony_cistatic void join(struct atmel_private *priv, int type) 27718c2ecf20Sopenharmony_ci{ 27728c2ecf20Sopenharmony_ci struct { 27738c2ecf20Sopenharmony_ci u8 BSSID[6]; 27748c2ecf20Sopenharmony_ci u8 SSID[MAX_SSID_LENGTH]; 27758c2ecf20Sopenharmony_ci u8 BSS_type; /* this is a short in a scan command - weird */ 27768c2ecf20Sopenharmony_ci u8 channel; 27778c2ecf20Sopenharmony_ci __le16 timeout; 27788c2ecf20Sopenharmony_ci u8 SSID_size; 27798c2ecf20Sopenharmony_ci u8 reserved; 27808c2ecf20Sopenharmony_ci } cmd; 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci cmd.SSID_size = priv->SSID_size; 27838c2ecf20Sopenharmony_ci memcpy(cmd.SSID, priv->SSID, priv->SSID_size); 27848c2ecf20Sopenharmony_ci memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN); 27858c2ecf20Sopenharmony_ci cmd.channel = (priv->channel & 0x7f); 27868c2ecf20Sopenharmony_ci cmd.BSS_type = type; 27878c2ecf20Sopenharmony_ci cmd.timeout = cpu_to_le16(2000); 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd)); 27908c2ecf20Sopenharmony_ci} 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_cistatic void start(struct atmel_private *priv, int type) 27938c2ecf20Sopenharmony_ci{ 27948c2ecf20Sopenharmony_ci struct { 27958c2ecf20Sopenharmony_ci u8 BSSID[6]; 27968c2ecf20Sopenharmony_ci u8 SSID[MAX_SSID_LENGTH]; 27978c2ecf20Sopenharmony_ci u8 BSS_type; 27988c2ecf20Sopenharmony_ci u8 channel; 27998c2ecf20Sopenharmony_ci u8 SSID_size; 28008c2ecf20Sopenharmony_ci u8 reserved[3]; 28018c2ecf20Sopenharmony_ci } cmd; 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci cmd.SSID_size = priv->SSID_size; 28048c2ecf20Sopenharmony_ci memcpy(cmd.SSID, priv->SSID, priv->SSID_size); 28058c2ecf20Sopenharmony_ci memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN); 28068c2ecf20Sopenharmony_ci cmd.BSS_type = type; 28078c2ecf20Sopenharmony_ci cmd.channel = (priv->channel & 0x7f); 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_ci atmel_send_command(priv, CMD_Start, &cmd, sizeof(cmd)); 28108c2ecf20Sopenharmony_ci} 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_cistatic void handle_beacon_probe(struct atmel_private *priv, u16 capability, 28138c2ecf20Sopenharmony_ci u8 channel) 28148c2ecf20Sopenharmony_ci{ 28158c2ecf20Sopenharmony_ci int rejoin = 0; 28168c2ecf20Sopenharmony_ci int new = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? 28178c2ecf20Sopenharmony_ci SHORT_PREAMBLE : LONG_PREAMBLE; 28188c2ecf20Sopenharmony_ci 28198c2ecf20Sopenharmony_ci if (priv->preamble != new) { 28208c2ecf20Sopenharmony_ci priv->preamble = new; 28218c2ecf20Sopenharmony_ci rejoin = 1; 28228c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, new); 28238c2ecf20Sopenharmony_ci } 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci if (priv->channel != channel) { 28268c2ecf20Sopenharmony_ci priv->channel = channel; 28278c2ecf20Sopenharmony_ci rejoin = 1; 28288c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_CHANNEL_POS, channel); 28298c2ecf20Sopenharmony_ci } 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci if (rejoin) { 28328c2ecf20Sopenharmony_ci priv->station_is_associated = 0; 28338c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_JOINNING); 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci if (priv->operating_mode == IW_MODE_INFRA) 28368c2ecf20Sopenharmony_ci join(priv, BSS_TYPE_INFRASTRUCTURE); 28378c2ecf20Sopenharmony_ci else 28388c2ecf20Sopenharmony_ci join(priv, BSS_TYPE_AD_HOC); 28398c2ecf20Sopenharmony_ci } 28408c2ecf20Sopenharmony_ci} 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_cistatic void send_authentication_request(struct atmel_private *priv, u16 system, 28438c2ecf20Sopenharmony_ci u8 *challenge, int challenge_len) 28448c2ecf20Sopenharmony_ci{ 28458c2ecf20Sopenharmony_ci struct ieee80211_hdr header; 28468c2ecf20Sopenharmony_ci struct auth_body auth; 28478c2ecf20Sopenharmony_ci 28488c2ecf20Sopenharmony_ci header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); 28498c2ecf20Sopenharmony_ci header.duration_id = cpu_to_le16(0x8000); 28508c2ecf20Sopenharmony_ci header.seq_ctrl = 0; 28518c2ecf20Sopenharmony_ci memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); 28528c2ecf20Sopenharmony_ci memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); 28538c2ecf20Sopenharmony_ci memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1) 28568c2ecf20Sopenharmony_ci /* no WEP for authentication frames with TrSeqNo 1 */ 28578c2ecf20Sopenharmony_ci header.frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci auth.alg = cpu_to_le16(system); 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci auth.status = 0; 28628c2ecf20Sopenharmony_ci auth.trans_seq = cpu_to_le16(priv->CurrentAuthentTransactionSeqNum); 28638c2ecf20Sopenharmony_ci priv->ExpectedAuthentTransactionSeqNum = priv->CurrentAuthentTransactionSeqNum+1; 28648c2ecf20Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum += 2; 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_ci if (challenge_len != 0) { 28678c2ecf20Sopenharmony_ci auth.el_id = 16; /* challenge_text */ 28688c2ecf20Sopenharmony_ci auth.chall_text_len = challenge_len; 28698c2ecf20Sopenharmony_ci memcpy(auth.chall_text, challenge, challenge_len); 28708c2ecf20Sopenharmony_ci atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 8 + challenge_len); 28718c2ecf20Sopenharmony_ci } else { 28728c2ecf20Sopenharmony_ci atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 6); 28738c2ecf20Sopenharmony_ci } 28748c2ecf20Sopenharmony_ci} 28758c2ecf20Sopenharmony_ci 28768c2ecf20Sopenharmony_cistatic void send_association_request(struct atmel_private *priv, int is_reassoc) 28778c2ecf20Sopenharmony_ci{ 28788c2ecf20Sopenharmony_ci u8 *ssid_el_p; 28798c2ecf20Sopenharmony_ci int bodysize; 28808c2ecf20Sopenharmony_ci struct ieee80211_hdr header; 28818c2ecf20Sopenharmony_ci struct ass_req_format { 28828c2ecf20Sopenharmony_ci __le16 capability; 28838c2ecf20Sopenharmony_ci __le16 listen_interval; 28848c2ecf20Sopenharmony_ci u8 ap[ETH_ALEN]; /* nothing after here directly accessible */ 28858c2ecf20Sopenharmony_ci u8 ssid_el_id; 28868c2ecf20Sopenharmony_ci u8 ssid_len; 28878c2ecf20Sopenharmony_ci u8 ssid[MAX_SSID_LENGTH]; 28888c2ecf20Sopenharmony_ci u8 sup_rates_el_id; 28898c2ecf20Sopenharmony_ci u8 sup_rates_len; 28908c2ecf20Sopenharmony_ci u8 rates[4]; 28918c2ecf20Sopenharmony_ci } body; 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 28948c2ecf20Sopenharmony_ci (is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ)); 28958c2ecf20Sopenharmony_ci header.duration_id = cpu_to_le16(0x8000); 28968c2ecf20Sopenharmony_ci header.seq_ctrl = 0; 28978c2ecf20Sopenharmony_ci 28988c2ecf20Sopenharmony_ci memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); 28998c2ecf20Sopenharmony_ci memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); 29008c2ecf20Sopenharmony_ci memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); 29018c2ecf20Sopenharmony_ci 29028c2ecf20Sopenharmony_ci body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS); 29038c2ecf20Sopenharmony_ci if (priv->wep_is_on) 29048c2ecf20Sopenharmony_ci body.capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); 29058c2ecf20Sopenharmony_ci if (priv->preamble == SHORT_PREAMBLE) 29068c2ecf20Sopenharmony_ci body.capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci body.listen_interval = cpu_to_le16(priv->listen_interval * priv->beacon_period); 29098c2ecf20Sopenharmony_ci 29108c2ecf20Sopenharmony_ci /* current AP address - only in reassoc frame */ 29118c2ecf20Sopenharmony_ci if (is_reassoc) { 29128c2ecf20Sopenharmony_ci memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN); 29138c2ecf20Sopenharmony_ci ssid_el_p = &body.ssid_el_id; 29148c2ecf20Sopenharmony_ci bodysize = 18 + priv->SSID_size; 29158c2ecf20Sopenharmony_ci } else { 29168c2ecf20Sopenharmony_ci ssid_el_p = &body.ap[0]; 29178c2ecf20Sopenharmony_ci bodysize = 12 + priv->SSID_size; 29188c2ecf20Sopenharmony_ci } 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci ssid_el_p[0] = WLAN_EID_SSID; 29218c2ecf20Sopenharmony_ci ssid_el_p[1] = priv->SSID_size; 29228c2ecf20Sopenharmony_ci memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size); 29238c2ecf20Sopenharmony_ci ssid_el_p[2 + priv->SSID_size] = WLAN_EID_SUPP_RATES; 29248c2ecf20Sopenharmony_ci ssid_el_p[3 + priv->SSID_size] = 4; /* len of supported rates */ 29258c2ecf20Sopenharmony_ci memcpy(ssid_el_p + 4 + priv->SSID_size, atmel_basic_rates, 4); 29268c2ecf20Sopenharmony_ci 29278c2ecf20Sopenharmony_ci atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize); 29288c2ecf20Sopenharmony_ci} 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_cistatic int is_frame_from_current_bss(struct atmel_private *priv, 29318c2ecf20Sopenharmony_ci struct ieee80211_hdr *header) 29328c2ecf20Sopenharmony_ci{ 29338c2ecf20Sopenharmony_ci if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) 29348c2ecf20Sopenharmony_ci return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0; 29358c2ecf20Sopenharmony_ci else 29368c2ecf20Sopenharmony_ci return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0; 29378c2ecf20Sopenharmony_ci} 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_cistatic int retrieve_bss(struct atmel_private *priv) 29408c2ecf20Sopenharmony_ci{ 29418c2ecf20Sopenharmony_ci int i; 29428c2ecf20Sopenharmony_ci int max_rssi = -128; 29438c2ecf20Sopenharmony_ci int max_index = -1; 29448c2ecf20Sopenharmony_ci 29458c2ecf20Sopenharmony_ci if (priv->BSS_list_entries == 0) 29468c2ecf20Sopenharmony_ci return -1; 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci if (priv->connect_to_any_BSS) { 29498c2ecf20Sopenharmony_ci /* Select a BSS with the max-RSSI but of the same type and of 29508c2ecf20Sopenharmony_ci the same WEP mode and that it is not marked as 'bad' (i.e. 29518c2ecf20Sopenharmony_ci we had previously failed to connect to this BSS with the 29528c2ecf20Sopenharmony_ci settings that we currently use) */ 29538c2ecf20Sopenharmony_ci priv->current_BSS = 0; 29548c2ecf20Sopenharmony_ci for (i = 0; i < priv->BSS_list_entries; i++) { 29558c2ecf20Sopenharmony_ci if (priv->operating_mode == priv->BSSinfo[i].BSStype && 29568c2ecf20Sopenharmony_ci ((!priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) || 29578c2ecf20Sopenharmony_ci (priv->wep_is_on && priv->BSSinfo[i].UsingWEP)) && 29588c2ecf20Sopenharmony_ci !(priv->BSSinfo[i].channel & 0x80)) { 29598c2ecf20Sopenharmony_ci max_rssi = priv->BSSinfo[i].RSSI; 29608c2ecf20Sopenharmony_ci priv->current_BSS = max_index = i; 29618c2ecf20Sopenharmony_ci } 29628c2ecf20Sopenharmony_ci } 29638c2ecf20Sopenharmony_ci return max_index; 29648c2ecf20Sopenharmony_ci } 29658c2ecf20Sopenharmony_ci 29668c2ecf20Sopenharmony_ci for (i = 0; i < priv->BSS_list_entries; i++) { 29678c2ecf20Sopenharmony_ci if (priv->SSID_size == priv->BSSinfo[i].SSIDsize && 29688c2ecf20Sopenharmony_ci memcmp(priv->SSID, priv->BSSinfo[i].SSID, priv->SSID_size) == 0 && 29698c2ecf20Sopenharmony_ci priv->operating_mode == priv->BSSinfo[i].BSStype && 29708c2ecf20Sopenharmony_ci atmel_validate_channel(priv, priv->BSSinfo[i].channel) == 0) { 29718c2ecf20Sopenharmony_ci if (priv->BSSinfo[i].RSSI >= max_rssi) { 29728c2ecf20Sopenharmony_ci max_rssi = priv->BSSinfo[i].RSSI; 29738c2ecf20Sopenharmony_ci max_index = i; 29748c2ecf20Sopenharmony_ci } 29758c2ecf20Sopenharmony_ci } 29768c2ecf20Sopenharmony_ci } 29778c2ecf20Sopenharmony_ci return max_index; 29788c2ecf20Sopenharmony_ci} 29798c2ecf20Sopenharmony_ci 29808c2ecf20Sopenharmony_cistatic void store_bss_info(struct atmel_private *priv, 29818c2ecf20Sopenharmony_ci struct ieee80211_hdr *header, u16 capability, 29828c2ecf20Sopenharmony_ci u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len, 29838c2ecf20Sopenharmony_ci u8 *ssid, int is_beacon) 29848c2ecf20Sopenharmony_ci{ 29858c2ecf20Sopenharmony_ci u8 *bss = capability & WLAN_CAPABILITY_ESS ? header->addr2 : header->addr3; 29868c2ecf20Sopenharmony_ci int i, index; 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_ci for (index = -1, i = 0; i < priv->BSS_list_entries; i++) 29898c2ecf20Sopenharmony_ci if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0) 29908c2ecf20Sopenharmony_ci index = i; 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_ci /* If we process a probe and an entry from this BSS exists 29938c2ecf20Sopenharmony_ci we will update the BSS entry with the info from this BSS. 29948c2ecf20Sopenharmony_ci If we process a beacon we will only update RSSI */ 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_ci if (index == -1) { 29978c2ecf20Sopenharmony_ci if (priv->BSS_list_entries == MAX_BSS_ENTRIES) 29988c2ecf20Sopenharmony_ci return; 29998c2ecf20Sopenharmony_ci index = priv->BSS_list_entries++; 30008c2ecf20Sopenharmony_ci memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN); 30018c2ecf20Sopenharmony_ci priv->BSSinfo[index].RSSI = rssi; 30028c2ecf20Sopenharmony_ci } else { 30038c2ecf20Sopenharmony_ci if (rssi > priv->BSSinfo[index].RSSI) 30048c2ecf20Sopenharmony_ci priv->BSSinfo[index].RSSI = rssi; 30058c2ecf20Sopenharmony_ci if (is_beacon) 30068c2ecf20Sopenharmony_ci return; 30078c2ecf20Sopenharmony_ci } 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ci priv->BSSinfo[index].channel = channel; 30108c2ecf20Sopenharmony_ci priv->BSSinfo[index].beacon_period = beacon_period; 30118c2ecf20Sopenharmony_ci priv->BSSinfo[index].UsingWEP = capability & WLAN_CAPABILITY_PRIVACY; 30128c2ecf20Sopenharmony_ci memcpy(priv->BSSinfo[index].SSID, ssid, ssid_len); 30138c2ecf20Sopenharmony_ci priv->BSSinfo[index].SSIDsize = ssid_len; 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_ci if (capability & WLAN_CAPABILITY_IBSS) 30168c2ecf20Sopenharmony_ci priv->BSSinfo[index].BSStype = IW_MODE_ADHOC; 30178c2ecf20Sopenharmony_ci else if (capability & WLAN_CAPABILITY_ESS) 30188c2ecf20Sopenharmony_ci priv->BSSinfo[index].BSStype = IW_MODE_INFRA; 30198c2ecf20Sopenharmony_ci 30208c2ecf20Sopenharmony_ci priv->BSSinfo[index].preamble = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? 30218c2ecf20Sopenharmony_ci SHORT_PREAMBLE : LONG_PREAMBLE; 30228c2ecf20Sopenharmony_ci} 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_cistatic void authenticate(struct atmel_private *priv, u16 frame_len) 30258c2ecf20Sopenharmony_ci{ 30268c2ecf20Sopenharmony_ci struct auth_body *auth = (struct auth_body *)priv->rx_buf; 30278c2ecf20Sopenharmony_ci u16 status = le16_to_cpu(auth->status); 30288c2ecf20Sopenharmony_ci u16 trans_seq_no = le16_to_cpu(auth->trans_seq); 30298c2ecf20Sopenharmony_ci u16 system = le16_to_cpu(auth->alg); 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci if (status == WLAN_STATUS_SUCCESS && !priv->wep_is_on) { 30328c2ecf20Sopenharmony_ci /* no WEP */ 30338c2ecf20Sopenharmony_ci if (priv->station_was_associated) { 30348c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_REASSOCIATING); 30358c2ecf20Sopenharmony_ci send_association_request(priv, 1); 30368c2ecf20Sopenharmony_ci return; 30378c2ecf20Sopenharmony_ci } else { 30388c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_ASSOCIATING); 30398c2ecf20Sopenharmony_ci send_association_request(priv, 0); 30408c2ecf20Sopenharmony_ci return; 30418c2ecf20Sopenharmony_ci } 30428c2ecf20Sopenharmony_ci } 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci if (status == WLAN_STATUS_SUCCESS && priv->wep_is_on) { 30458c2ecf20Sopenharmony_ci int should_associate = 0; 30468c2ecf20Sopenharmony_ci /* WEP */ 30478c2ecf20Sopenharmony_ci if (trans_seq_no != priv->ExpectedAuthentTransactionSeqNum) 30488c2ecf20Sopenharmony_ci return; 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci if (system == WLAN_AUTH_OPEN) { 30518c2ecf20Sopenharmony_ci if (trans_seq_no == 0x0002) { 30528c2ecf20Sopenharmony_ci should_associate = 1; 30538c2ecf20Sopenharmony_ci } 30548c2ecf20Sopenharmony_ci } else if (system == WLAN_AUTH_SHARED_KEY) { 30558c2ecf20Sopenharmony_ci if (trans_seq_no == 0x0002 && 30568c2ecf20Sopenharmony_ci auth->el_id == WLAN_EID_CHALLENGE) { 30578c2ecf20Sopenharmony_ci send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len); 30588c2ecf20Sopenharmony_ci return; 30598c2ecf20Sopenharmony_ci } else if (trans_seq_no == 0x0004) { 30608c2ecf20Sopenharmony_ci should_associate = 1; 30618c2ecf20Sopenharmony_ci } 30628c2ecf20Sopenharmony_ci } 30638c2ecf20Sopenharmony_ci 30648c2ecf20Sopenharmony_ci if (should_associate) { 30658c2ecf20Sopenharmony_ci if (priv->station_was_associated) { 30668c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_REASSOCIATING); 30678c2ecf20Sopenharmony_ci send_association_request(priv, 1); 30688c2ecf20Sopenharmony_ci return; 30698c2ecf20Sopenharmony_ci } else { 30708c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_ASSOCIATING); 30718c2ecf20Sopenharmony_ci send_association_request(priv, 0); 30728c2ecf20Sopenharmony_ci return; 30738c2ecf20Sopenharmony_ci } 30748c2ecf20Sopenharmony_ci } 30758c2ecf20Sopenharmony_ci } 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci if (status == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { 30788c2ecf20Sopenharmony_ci /* Flip back and forth between WEP auth modes until the max 30798c2ecf20Sopenharmony_ci * authentication tries has been exceeded. 30808c2ecf20Sopenharmony_ci */ 30818c2ecf20Sopenharmony_ci if (system == WLAN_AUTH_OPEN) { 30828c2ecf20Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum = 0x001; 30838c2ecf20Sopenharmony_ci priv->exclude_unencrypted = 1; 30848c2ecf20Sopenharmony_ci send_authentication_request(priv, WLAN_AUTH_SHARED_KEY, NULL, 0); 30858c2ecf20Sopenharmony_ci return; 30868c2ecf20Sopenharmony_ci } else if (system == WLAN_AUTH_SHARED_KEY 30878c2ecf20Sopenharmony_ci && priv->wep_is_on) { 30888c2ecf20Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum = 0x001; 30898c2ecf20Sopenharmony_ci priv->exclude_unencrypted = 0; 30908c2ecf20Sopenharmony_ci send_authentication_request(priv, WLAN_AUTH_OPEN, NULL, 0); 30918c2ecf20Sopenharmony_ci return; 30928c2ecf20Sopenharmony_ci } else if (priv->connect_to_any_BSS) { 30938c2ecf20Sopenharmony_ci int bss_index; 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; 30968c2ecf20Sopenharmony_ci 30978c2ecf20Sopenharmony_ci if ((bss_index = retrieve_bss(priv)) != -1) { 30988c2ecf20Sopenharmony_ci atmel_join_bss(priv, bss_index); 30998c2ecf20Sopenharmony_ci return; 31008c2ecf20Sopenharmony_ci } 31018c2ecf20Sopenharmony_ci } 31028c2ecf20Sopenharmony_ci } 31038c2ecf20Sopenharmony_ci 31048c2ecf20Sopenharmony_ci priv->AuthenticationRequestRetryCnt = 0; 31058c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 31068c2ecf20Sopenharmony_ci priv->station_is_associated = 0; 31078c2ecf20Sopenharmony_ci} 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_cistatic void associate(struct atmel_private *priv, u16 frame_len, u16 subtype) 31108c2ecf20Sopenharmony_ci{ 31118c2ecf20Sopenharmony_ci struct ass_resp_format { 31128c2ecf20Sopenharmony_ci __le16 capability; 31138c2ecf20Sopenharmony_ci __le16 status; 31148c2ecf20Sopenharmony_ci __le16 ass_id; 31158c2ecf20Sopenharmony_ci u8 el_id; 31168c2ecf20Sopenharmony_ci u8 length; 31178c2ecf20Sopenharmony_ci u8 rates[4]; 31188c2ecf20Sopenharmony_ci } *ass_resp = (struct ass_resp_format *)priv->rx_buf; 31198c2ecf20Sopenharmony_ci 31208c2ecf20Sopenharmony_ci u16 status = le16_to_cpu(ass_resp->status); 31218c2ecf20Sopenharmony_ci u16 ass_id = le16_to_cpu(ass_resp->ass_id); 31228c2ecf20Sopenharmony_ci u16 rates_len = ass_resp->length > 4 ? 4 : ass_resp->length; 31238c2ecf20Sopenharmony_ci 31248c2ecf20Sopenharmony_ci union iwreq_data wrqu; 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ci if (frame_len < 8 + rates_len) 31278c2ecf20Sopenharmony_ci return; 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_ci if (status == WLAN_STATUS_SUCCESS) { 31308c2ecf20Sopenharmony_ci if (subtype == IEEE80211_STYPE_ASSOC_RESP) 31318c2ecf20Sopenharmony_ci priv->AssociationRequestRetryCnt = 0; 31328c2ecf20Sopenharmony_ci else 31338c2ecf20Sopenharmony_ci priv->ReAssociationRequestRetryCnt = 0; 31348c2ecf20Sopenharmony_ci 31358c2ecf20Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, 31368c2ecf20Sopenharmony_ci MAC_MGMT_MIB_STATION_ID_POS, ass_id & 0x3fff); 31378c2ecf20Sopenharmony_ci atmel_set_mib(priv, Phy_Mib_Type, 31388c2ecf20Sopenharmony_ci PHY_MIB_RATE_SET_POS, ass_resp->rates, rates_len); 31398c2ecf20Sopenharmony_ci if (priv->power_mode == 0) { 31408c2ecf20Sopenharmony_ci priv->listen_interval = 1; 31418c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, 31428c2ecf20Sopenharmony_ci MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); 31438c2ecf20Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, 31448c2ecf20Sopenharmony_ci MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); 31458c2ecf20Sopenharmony_ci } else { 31468c2ecf20Sopenharmony_ci priv->listen_interval = 2; 31478c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, 31488c2ecf20Sopenharmony_ci MAC_MGMT_MIB_PS_MODE_POS, PS_MODE); 31498c2ecf20Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, 31508c2ecf20Sopenharmony_ci MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 2); 31518c2ecf20Sopenharmony_ci } 31528c2ecf20Sopenharmony_ci 31538c2ecf20Sopenharmony_ci priv->station_is_associated = 1; 31548c2ecf20Sopenharmony_ci priv->station_was_associated = 1; 31558c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_READY); 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci /* Send association event to userspace */ 31588c2ecf20Sopenharmony_ci wrqu.data.length = 0; 31598c2ecf20Sopenharmony_ci wrqu.data.flags = 0; 31608c2ecf20Sopenharmony_ci memcpy(wrqu.ap_addr.sa_data, priv->CurrentBSSID, ETH_ALEN); 31618c2ecf20Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 31628c2ecf20Sopenharmony_ci wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_ci return; 31658c2ecf20Sopenharmony_ci } 31668c2ecf20Sopenharmony_ci 31678c2ecf20Sopenharmony_ci if (subtype == IEEE80211_STYPE_ASSOC_RESP && 31688c2ecf20Sopenharmony_ci status != WLAN_STATUS_ASSOC_DENIED_RATES && 31698c2ecf20Sopenharmony_ci status != WLAN_STATUS_CAPS_UNSUPPORTED && 31708c2ecf20Sopenharmony_ci priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { 31718c2ecf20Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 31728c2ecf20Sopenharmony_ci priv->AssociationRequestRetryCnt++; 31738c2ecf20Sopenharmony_ci send_association_request(priv, 0); 31748c2ecf20Sopenharmony_ci return; 31758c2ecf20Sopenharmony_ci } 31768c2ecf20Sopenharmony_ci 31778c2ecf20Sopenharmony_ci if (subtype == IEEE80211_STYPE_REASSOC_RESP && 31788c2ecf20Sopenharmony_ci status != WLAN_STATUS_ASSOC_DENIED_RATES && 31798c2ecf20Sopenharmony_ci status != WLAN_STATUS_CAPS_UNSUPPORTED && 31808c2ecf20Sopenharmony_ci priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { 31818c2ecf20Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 31828c2ecf20Sopenharmony_ci priv->ReAssociationRequestRetryCnt++; 31838c2ecf20Sopenharmony_ci send_association_request(priv, 1); 31848c2ecf20Sopenharmony_ci return; 31858c2ecf20Sopenharmony_ci } 31868c2ecf20Sopenharmony_ci 31878c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 31888c2ecf20Sopenharmony_ci priv->station_is_associated = 0; 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci if (priv->connect_to_any_BSS) { 31918c2ecf20Sopenharmony_ci int bss_index; 31928c2ecf20Sopenharmony_ci priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; 31938c2ecf20Sopenharmony_ci 31948c2ecf20Sopenharmony_ci if ((bss_index = retrieve_bss(priv)) != -1) 31958c2ecf20Sopenharmony_ci atmel_join_bss(priv, bss_index); 31968c2ecf20Sopenharmony_ci } 31978c2ecf20Sopenharmony_ci} 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_cistatic void atmel_join_bss(struct atmel_private *priv, int bss_index) 32008c2ecf20Sopenharmony_ci{ 32018c2ecf20Sopenharmony_ci struct bss_info *bss = &priv->BSSinfo[bss_index]; 32028c2ecf20Sopenharmony_ci 32038c2ecf20Sopenharmony_ci memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN); 32048c2ecf20Sopenharmony_ci memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize); 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci /* The WPA stuff cares about the current AP address */ 32078c2ecf20Sopenharmony_ci if (priv->use_wpa) 32088c2ecf20Sopenharmony_ci build_wpa_mib(priv); 32098c2ecf20Sopenharmony_ci 32108c2ecf20Sopenharmony_ci /* When switching to AdHoc turn OFF Power Save if needed */ 32118c2ecf20Sopenharmony_ci 32128c2ecf20Sopenharmony_ci if (bss->BSStype == IW_MODE_ADHOC && 32138c2ecf20Sopenharmony_ci priv->operating_mode != IW_MODE_ADHOC && 32148c2ecf20Sopenharmony_ci priv->power_mode) { 32158c2ecf20Sopenharmony_ci priv->power_mode = 0; 32168c2ecf20Sopenharmony_ci priv->listen_interval = 1; 32178c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, 32188c2ecf20Sopenharmony_ci MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); 32198c2ecf20Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, 32208c2ecf20Sopenharmony_ci MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); 32218c2ecf20Sopenharmony_ci } 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_ci priv->operating_mode = bss->BSStype; 32248c2ecf20Sopenharmony_ci priv->channel = bss->channel & 0x7f; 32258c2ecf20Sopenharmony_ci priv->beacon_period = bss->beacon_period; 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci if (priv->preamble != bss->preamble) { 32288c2ecf20Sopenharmony_ci priv->preamble = bss->preamble; 32298c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Local_Mib_Type, 32308c2ecf20Sopenharmony_ci LOCAL_MIB_PREAMBLE_TYPE, bss->preamble); 32318c2ecf20Sopenharmony_ci } 32328c2ecf20Sopenharmony_ci 32338c2ecf20Sopenharmony_ci if (!priv->wep_is_on && bss->UsingWEP) { 32348c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 32358c2ecf20Sopenharmony_ci priv->station_is_associated = 0; 32368c2ecf20Sopenharmony_ci return; 32378c2ecf20Sopenharmony_ci } 32388c2ecf20Sopenharmony_ci 32398c2ecf20Sopenharmony_ci if (priv->wep_is_on && !bss->UsingWEP) { 32408c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 32418c2ecf20Sopenharmony_ci priv->station_is_associated = 0; 32428c2ecf20Sopenharmony_ci return; 32438c2ecf20Sopenharmony_ci } 32448c2ecf20Sopenharmony_ci 32458c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_JOINNING); 32468c2ecf20Sopenharmony_ci 32478c2ecf20Sopenharmony_ci if (priv->operating_mode == IW_MODE_INFRA) 32488c2ecf20Sopenharmony_ci join(priv, BSS_TYPE_INFRASTRUCTURE); 32498c2ecf20Sopenharmony_ci else 32508c2ecf20Sopenharmony_ci join(priv, BSS_TYPE_AD_HOC); 32518c2ecf20Sopenharmony_ci} 32528c2ecf20Sopenharmony_ci 32538c2ecf20Sopenharmony_cistatic void restart_search(struct atmel_private *priv) 32548c2ecf20Sopenharmony_ci{ 32558c2ecf20Sopenharmony_ci int bss_index; 32568c2ecf20Sopenharmony_ci 32578c2ecf20Sopenharmony_ci if (!priv->connect_to_any_BSS) { 32588c2ecf20Sopenharmony_ci atmel_scan(priv, 1); 32598c2ecf20Sopenharmony_ci } else { 32608c2ecf20Sopenharmony_ci priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; 32618c2ecf20Sopenharmony_ci 32628c2ecf20Sopenharmony_ci if ((bss_index = retrieve_bss(priv)) != -1) 32638c2ecf20Sopenharmony_ci atmel_join_bss(priv, bss_index); 32648c2ecf20Sopenharmony_ci else 32658c2ecf20Sopenharmony_ci atmel_scan(priv, 0); 32668c2ecf20Sopenharmony_ci } 32678c2ecf20Sopenharmony_ci} 32688c2ecf20Sopenharmony_ci 32698c2ecf20Sopenharmony_cistatic void smooth_rssi(struct atmel_private *priv, u8 rssi) 32708c2ecf20Sopenharmony_ci{ 32718c2ecf20Sopenharmony_ci u8 old = priv->wstats.qual.level; 32728c2ecf20Sopenharmony_ci u8 max_rssi = 42; /* 502-rmfd-revd max by experiment, default for now */ 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_ci switch (priv->firmware_type) { 32758c2ecf20Sopenharmony_ci case ATMEL_FW_TYPE_502E: 32768c2ecf20Sopenharmony_ci max_rssi = 63; /* 502-rmfd-reve max by experiment */ 32778c2ecf20Sopenharmony_ci break; 32788c2ecf20Sopenharmony_ci default: 32798c2ecf20Sopenharmony_ci break; 32808c2ecf20Sopenharmony_ci } 32818c2ecf20Sopenharmony_ci 32828c2ecf20Sopenharmony_ci rssi = rssi * 100 / max_rssi; 32838c2ecf20Sopenharmony_ci if ((rssi + old) % 2) 32848c2ecf20Sopenharmony_ci priv->wstats.qual.level = (rssi + old) / 2 + 1; 32858c2ecf20Sopenharmony_ci else 32868c2ecf20Sopenharmony_ci priv->wstats.qual.level = (rssi + old) / 2; 32878c2ecf20Sopenharmony_ci priv->wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; 32888c2ecf20Sopenharmony_ci priv->wstats.qual.updated &= ~IW_QUAL_LEVEL_INVALID; 32898c2ecf20Sopenharmony_ci} 32908c2ecf20Sopenharmony_ci 32918c2ecf20Sopenharmony_cistatic void atmel_smooth_qual(struct atmel_private *priv) 32928c2ecf20Sopenharmony_ci{ 32938c2ecf20Sopenharmony_ci unsigned long time_diff = (jiffies - priv->last_qual) / HZ; 32948c2ecf20Sopenharmony_ci while (time_diff--) { 32958c2ecf20Sopenharmony_ci priv->last_qual += HZ; 32968c2ecf20Sopenharmony_ci priv->wstats.qual.qual = priv->wstats.qual.qual / 2; 32978c2ecf20Sopenharmony_ci priv->wstats.qual.qual += 32988c2ecf20Sopenharmony_ci priv->beacons_this_sec * priv->beacon_period * (priv->wstats.qual.level + 100) / 4000; 32998c2ecf20Sopenharmony_ci priv->beacons_this_sec = 0; 33008c2ecf20Sopenharmony_ci } 33018c2ecf20Sopenharmony_ci priv->wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; 33028c2ecf20Sopenharmony_ci priv->wstats.qual.updated &= ~IW_QUAL_QUAL_INVALID; 33038c2ecf20Sopenharmony_ci} 33048c2ecf20Sopenharmony_ci 33058c2ecf20Sopenharmony_ci/* deals with incoming management frames. */ 33068c2ecf20Sopenharmony_cistatic void atmel_management_frame(struct atmel_private *priv, 33078c2ecf20Sopenharmony_ci struct ieee80211_hdr *header, 33088c2ecf20Sopenharmony_ci u16 frame_len, u8 rssi) 33098c2ecf20Sopenharmony_ci{ 33108c2ecf20Sopenharmony_ci u16 subtype; 33118c2ecf20Sopenharmony_ci 33128c2ecf20Sopenharmony_ci subtype = le16_to_cpu(header->frame_control) & IEEE80211_FCTL_STYPE; 33138c2ecf20Sopenharmony_ci switch (subtype) { 33148c2ecf20Sopenharmony_ci case IEEE80211_STYPE_BEACON: 33158c2ecf20Sopenharmony_ci case IEEE80211_STYPE_PROBE_RESP: 33168c2ecf20Sopenharmony_ci 33178c2ecf20Sopenharmony_ci /* beacon frame has multiple variable-length fields - 33188c2ecf20Sopenharmony_ci never let an engineer loose with a data structure design. */ 33198c2ecf20Sopenharmony_ci { 33208c2ecf20Sopenharmony_ci struct beacon_format { 33218c2ecf20Sopenharmony_ci __le64 timestamp; 33228c2ecf20Sopenharmony_ci __le16 interval; 33238c2ecf20Sopenharmony_ci __le16 capability; 33248c2ecf20Sopenharmony_ci u8 ssid_el_id; 33258c2ecf20Sopenharmony_ci u8 ssid_length; 33268c2ecf20Sopenharmony_ci /* ssid here */ 33278c2ecf20Sopenharmony_ci u8 rates_el_id; 33288c2ecf20Sopenharmony_ci u8 rates_length; 33298c2ecf20Sopenharmony_ci /* rates here */ 33308c2ecf20Sopenharmony_ci u8 ds_el_id; 33318c2ecf20Sopenharmony_ci u8 ds_length; 33328c2ecf20Sopenharmony_ci /* ds here */ 33338c2ecf20Sopenharmony_ci } *beacon = (struct beacon_format *)priv->rx_buf; 33348c2ecf20Sopenharmony_ci 33358c2ecf20Sopenharmony_ci u8 channel, rates_length, ssid_length; 33368c2ecf20Sopenharmony_ci u64 timestamp = le64_to_cpu(beacon->timestamp); 33378c2ecf20Sopenharmony_ci u16 beacon_interval = le16_to_cpu(beacon->interval); 33388c2ecf20Sopenharmony_ci u16 capability = le16_to_cpu(beacon->capability); 33398c2ecf20Sopenharmony_ci u8 *beaconp = priv->rx_buf; 33408c2ecf20Sopenharmony_ci ssid_length = beacon->ssid_length; 33418c2ecf20Sopenharmony_ci /* this blows chunks. */ 33428c2ecf20Sopenharmony_ci if (frame_len < 14 || frame_len < ssid_length + 15) 33438c2ecf20Sopenharmony_ci return; 33448c2ecf20Sopenharmony_ci rates_length = beaconp[beacon->ssid_length + 15]; 33458c2ecf20Sopenharmony_ci if (frame_len < ssid_length + rates_length + 18) 33468c2ecf20Sopenharmony_ci return; 33478c2ecf20Sopenharmony_ci if (ssid_length > MAX_SSID_LENGTH) 33488c2ecf20Sopenharmony_ci return; 33498c2ecf20Sopenharmony_ci channel = beaconp[ssid_length + rates_length + 18]; 33508c2ecf20Sopenharmony_ci 33518c2ecf20Sopenharmony_ci if (priv->station_state == STATION_STATE_READY) { 33528c2ecf20Sopenharmony_ci smooth_rssi(priv, rssi); 33538c2ecf20Sopenharmony_ci if (is_frame_from_current_bss(priv, header)) { 33548c2ecf20Sopenharmony_ci priv->beacons_this_sec++; 33558c2ecf20Sopenharmony_ci atmel_smooth_qual(priv); 33568c2ecf20Sopenharmony_ci if (priv->last_beacon_timestamp) { 33578c2ecf20Sopenharmony_ci /* Note truncate this to 32 bits - kernel can't divide a long long */ 33588c2ecf20Sopenharmony_ci u32 beacon_delay = timestamp - priv->last_beacon_timestamp; 33598c2ecf20Sopenharmony_ci int beacons = beacon_delay / (beacon_interval * 1000); 33608c2ecf20Sopenharmony_ci if (beacons > 1) 33618c2ecf20Sopenharmony_ci priv->wstats.miss.beacon += beacons - 1; 33628c2ecf20Sopenharmony_ci } 33638c2ecf20Sopenharmony_ci priv->last_beacon_timestamp = timestamp; 33648c2ecf20Sopenharmony_ci handle_beacon_probe(priv, capability, channel); 33658c2ecf20Sopenharmony_ci } 33668c2ecf20Sopenharmony_ci } 33678c2ecf20Sopenharmony_ci 33688c2ecf20Sopenharmony_ci if (priv->station_state == STATION_STATE_SCANNING) 33698c2ecf20Sopenharmony_ci store_bss_info(priv, header, capability, 33708c2ecf20Sopenharmony_ci beacon_interval, channel, rssi, 33718c2ecf20Sopenharmony_ci ssid_length, 33728c2ecf20Sopenharmony_ci &beacon->rates_el_id, 33738c2ecf20Sopenharmony_ci subtype == IEEE80211_STYPE_BEACON); 33748c2ecf20Sopenharmony_ci } 33758c2ecf20Sopenharmony_ci break; 33768c2ecf20Sopenharmony_ci 33778c2ecf20Sopenharmony_ci case IEEE80211_STYPE_AUTH: 33788c2ecf20Sopenharmony_ci 33798c2ecf20Sopenharmony_ci if (priv->station_state == STATION_STATE_AUTHENTICATING) 33808c2ecf20Sopenharmony_ci authenticate(priv, frame_len); 33818c2ecf20Sopenharmony_ci 33828c2ecf20Sopenharmony_ci break; 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci case IEEE80211_STYPE_ASSOC_RESP: 33858c2ecf20Sopenharmony_ci case IEEE80211_STYPE_REASSOC_RESP: 33868c2ecf20Sopenharmony_ci 33878c2ecf20Sopenharmony_ci if (priv->station_state == STATION_STATE_ASSOCIATING || 33888c2ecf20Sopenharmony_ci priv->station_state == STATION_STATE_REASSOCIATING) 33898c2ecf20Sopenharmony_ci associate(priv, frame_len, subtype); 33908c2ecf20Sopenharmony_ci 33918c2ecf20Sopenharmony_ci break; 33928c2ecf20Sopenharmony_ci 33938c2ecf20Sopenharmony_ci case IEEE80211_STYPE_DISASSOC: 33948c2ecf20Sopenharmony_ci if (priv->station_is_associated && 33958c2ecf20Sopenharmony_ci priv->operating_mode == IW_MODE_INFRA && 33968c2ecf20Sopenharmony_ci is_frame_from_current_bss(priv, header)) { 33978c2ecf20Sopenharmony_ci priv->station_was_associated = 0; 33988c2ecf20Sopenharmony_ci priv->station_is_associated = 0; 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_JOINNING); 34018c2ecf20Sopenharmony_ci join(priv, BSS_TYPE_INFRASTRUCTURE); 34028c2ecf20Sopenharmony_ci } 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci break; 34058c2ecf20Sopenharmony_ci 34068c2ecf20Sopenharmony_ci case IEEE80211_STYPE_DEAUTH: 34078c2ecf20Sopenharmony_ci if (priv->operating_mode == IW_MODE_INFRA && 34088c2ecf20Sopenharmony_ci is_frame_from_current_bss(priv, header)) { 34098c2ecf20Sopenharmony_ci priv->station_was_associated = 0; 34108c2ecf20Sopenharmony_ci 34118c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_JOINNING); 34128c2ecf20Sopenharmony_ci join(priv, BSS_TYPE_INFRASTRUCTURE); 34138c2ecf20Sopenharmony_ci } 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci break; 34168c2ecf20Sopenharmony_ci } 34178c2ecf20Sopenharmony_ci} 34188c2ecf20Sopenharmony_ci 34198c2ecf20Sopenharmony_ci/* run when timer expires */ 34208c2ecf20Sopenharmony_cistatic void atmel_management_timer(struct timer_list *t) 34218c2ecf20Sopenharmony_ci{ 34228c2ecf20Sopenharmony_ci struct atmel_private *priv = from_timer(priv, t, management_timer); 34238c2ecf20Sopenharmony_ci unsigned long flags; 34248c2ecf20Sopenharmony_ci 34258c2ecf20Sopenharmony_ci /* Check if the card has been yanked. */ 34268c2ecf20Sopenharmony_ci if (priv->card && priv->present_callback && 34278c2ecf20Sopenharmony_ci !(*priv->present_callback)(priv->card)) 34288c2ecf20Sopenharmony_ci return; 34298c2ecf20Sopenharmony_ci 34308c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->irqlock, flags); 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci switch (priv->station_state) { 34338c2ecf20Sopenharmony_ci 34348c2ecf20Sopenharmony_ci case STATION_STATE_AUTHENTICATING: 34358c2ecf20Sopenharmony_ci if (priv->AuthenticationRequestRetryCnt >= MAX_AUTHENTICATION_RETRIES) { 34368c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 34378c2ecf20Sopenharmony_ci priv->station_is_associated = 0; 34388c2ecf20Sopenharmony_ci priv->AuthenticationRequestRetryCnt = 0; 34398c2ecf20Sopenharmony_ci restart_search(priv); 34408c2ecf20Sopenharmony_ci } else { 34418c2ecf20Sopenharmony_ci int auth = WLAN_AUTH_OPEN; 34428c2ecf20Sopenharmony_ci priv->AuthenticationRequestRetryCnt++; 34438c2ecf20Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum = 0x0001; 34448c2ecf20Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 34458c2ecf20Sopenharmony_ci if (priv->wep_is_on && priv->exclude_unencrypted) 34468c2ecf20Sopenharmony_ci auth = WLAN_AUTH_SHARED_KEY; 34478c2ecf20Sopenharmony_ci send_authentication_request(priv, auth, NULL, 0); 34488c2ecf20Sopenharmony_ci } 34498c2ecf20Sopenharmony_ci break; 34508c2ecf20Sopenharmony_ci 34518c2ecf20Sopenharmony_ci case STATION_STATE_ASSOCIATING: 34528c2ecf20Sopenharmony_ci if (priv->AssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { 34538c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 34548c2ecf20Sopenharmony_ci priv->station_is_associated = 0; 34558c2ecf20Sopenharmony_ci priv->AssociationRequestRetryCnt = 0; 34568c2ecf20Sopenharmony_ci restart_search(priv); 34578c2ecf20Sopenharmony_ci } else { 34588c2ecf20Sopenharmony_ci priv->AssociationRequestRetryCnt++; 34598c2ecf20Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 34608c2ecf20Sopenharmony_ci send_association_request(priv, 0); 34618c2ecf20Sopenharmony_ci } 34628c2ecf20Sopenharmony_ci break; 34638c2ecf20Sopenharmony_ci 34648c2ecf20Sopenharmony_ci case STATION_STATE_REASSOCIATING: 34658c2ecf20Sopenharmony_ci if (priv->ReAssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { 34668c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 34678c2ecf20Sopenharmony_ci priv->station_is_associated = 0; 34688c2ecf20Sopenharmony_ci priv->ReAssociationRequestRetryCnt = 0; 34698c2ecf20Sopenharmony_ci restart_search(priv); 34708c2ecf20Sopenharmony_ci } else { 34718c2ecf20Sopenharmony_ci priv->ReAssociationRequestRetryCnt++; 34728c2ecf20Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 34738c2ecf20Sopenharmony_ci send_association_request(priv, 1); 34748c2ecf20Sopenharmony_ci } 34758c2ecf20Sopenharmony_ci break; 34768c2ecf20Sopenharmony_ci 34778c2ecf20Sopenharmony_ci default: 34788c2ecf20Sopenharmony_ci break; 34798c2ecf20Sopenharmony_ci } 34808c2ecf20Sopenharmony_ci 34818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 34828c2ecf20Sopenharmony_ci} 34838c2ecf20Sopenharmony_ci 34848c2ecf20Sopenharmony_cistatic void atmel_command_irq(struct atmel_private *priv) 34858c2ecf20Sopenharmony_ci{ 34868c2ecf20Sopenharmony_ci u8 status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); 34878c2ecf20Sopenharmony_ci u8 command = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET)); 34888c2ecf20Sopenharmony_ci int fast_scan; 34898c2ecf20Sopenharmony_ci union iwreq_data wrqu; 34908c2ecf20Sopenharmony_ci 34918c2ecf20Sopenharmony_ci if (status == CMD_STATUS_IDLE || 34928c2ecf20Sopenharmony_ci status == CMD_STATUS_IN_PROGRESS) 34938c2ecf20Sopenharmony_ci return; 34948c2ecf20Sopenharmony_ci 34958c2ecf20Sopenharmony_ci switch (command) { 34968c2ecf20Sopenharmony_ci case CMD_Start: 34978c2ecf20Sopenharmony_ci if (status == CMD_STATUS_COMPLETE) { 34988c2ecf20Sopenharmony_ci priv->station_was_associated = priv->station_is_associated; 34998c2ecf20Sopenharmony_ci atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, 35008c2ecf20Sopenharmony_ci (u8 *)priv->CurrentBSSID, 6); 35018c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_READY); 35028c2ecf20Sopenharmony_ci } 35038c2ecf20Sopenharmony_ci break; 35048c2ecf20Sopenharmony_ci 35058c2ecf20Sopenharmony_ci case CMD_Scan: 35068c2ecf20Sopenharmony_ci fast_scan = priv->fast_scan; 35078c2ecf20Sopenharmony_ci priv->fast_scan = 0; 35088c2ecf20Sopenharmony_ci 35098c2ecf20Sopenharmony_ci if (status != CMD_STATUS_COMPLETE) { 35108c2ecf20Sopenharmony_ci atmel_scan(priv, 1); 35118c2ecf20Sopenharmony_ci } else { 35128c2ecf20Sopenharmony_ci int bss_index = retrieve_bss(priv); 35138c2ecf20Sopenharmony_ci int notify_scan_complete = 1; 35148c2ecf20Sopenharmony_ci if (bss_index != -1) { 35158c2ecf20Sopenharmony_ci atmel_join_bss(priv, bss_index); 35168c2ecf20Sopenharmony_ci } else if (priv->operating_mode == IW_MODE_ADHOC && 35178c2ecf20Sopenharmony_ci priv->SSID_size != 0) { 35188c2ecf20Sopenharmony_ci start(priv, BSS_TYPE_AD_HOC); 35198c2ecf20Sopenharmony_ci } else { 35208c2ecf20Sopenharmony_ci priv->fast_scan = !fast_scan; 35218c2ecf20Sopenharmony_ci atmel_scan(priv, 1); 35228c2ecf20Sopenharmony_ci notify_scan_complete = 0; 35238c2ecf20Sopenharmony_ci } 35248c2ecf20Sopenharmony_ci priv->site_survey_state = SITE_SURVEY_COMPLETED; 35258c2ecf20Sopenharmony_ci if (notify_scan_complete) { 35268c2ecf20Sopenharmony_ci wrqu.data.length = 0; 35278c2ecf20Sopenharmony_ci wrqu.data.flags = 0; 35288c2ecf20Sopenharmony_ci wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); 35298c2ecf20Sopenharmony_ci } 35308c2ecf20Sopenharmony_ci } 35318c2ecf20Sopenharmony_ci break; 35328c2ecf20Sopenharmony_ci 35338c2ecf20Sopenharmony_ci case CMD_SiteSurvey: 35348c2ecf20Sopenharmony_ci priv->fast_scan = 0; 35358c2ecf20Sopenharmony_ci 35368c2ecf20Sopenharmony_ci if (status != CMD_STATUS_COMPLETE) 35378c2ecf20Sopenharmony_ci return; 35388c2ecf20Sopenharmony_ci 35398c2ecf20Sopenharmony_ci priv->site_survey_state = SITE_SURVEY_COMPLETED; 35408c2ecf20Sopenharmony_ci if (priv->station_is_associated) { 35418c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_READY); 35428c2ecf20Sopenharmony_ci wrqu.data.length = 0; 35438c2ecf20Sopenharmony_ci wrqu.data.flags = 0; 35448c2ecf20Sopenharmony_ci wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); 35458c2ecf20Sopenharmony_ci } else { 35468c2ecf20Sopenharmony_ci atmel_scan(priv, 1); 35478c2ecf20Sopenharmony_ci } 35488c2ecf20Sopenharmony_ci break; 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci case CMD_Join: 35518c2ecf20Sopenharmony_ci if (status == CMD_STATUS_COMPLETE) { 35528c2ecf20Sopenharmony_ci if (priv->operating_mode == IW_MODE_ADHOC) { 35538c2ecf20Sopenharmony_ci priv->station_was_associated = priv->station_is_associated; 35548c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_READY); 35558c2ecf20Sopenharmony_ci } else { 35568c2ecf20Sopenharmony_ci int auth = WLAN_AUTH_OPEN; 35578c2ecf20Sopenharmony_ci priv->AuthenticationRequestRetryCnt = 0; 35588c2ecf20Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_AUTHENTICATING); 35598c2ecf20Sopenharmony_ci 35608c2ecf20Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 35618c2ecf20Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum = 0x0001; 35628c2ecf20Sopenharmony_ci if (priv->wep_is_on && priv->exclude_unencrypted) 35638c2ecf20Sopenharmony_ci auth = WLAN_AUTH_SHARED_KEY; 35648c2ecf20Sopenharmony_ci send_authentication_request(priv, auth, NULL, 0); 35658c2ecf20Sopenharmony_ci } 35668c2ecf20Sopenharmony_ci return; 35678c2ecf20Sopenharmony_ci } 35688c2ecf20Sopenharmony_ci 35698c2ecf20Sopenharmony_ci atmel_scan(priv, 1); 35708c2ecf20Sopenharmony_ci } 35718c2ecf20Sopenharmony_ci} 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_cistatic int atmel_wakeup_firmware(struct atmel_private *priv) 35748c2ecf20Sopenharmony_ci{ 35758c2ecf20Sopenharmony_ci struct host_info_struct *iface = &priv->host_info; 35768c2ecf20Sopenharmony_ci u16 mr1, mr3; 35778c2ecf20Sopenharmony_ci int i; 35788c2ecf20Sopenharmony_ci 35798c2ecf20Sopenharmony_ci if (priv->card_type == CARD_TYPE_SPI_FLASH) 35808c2ecf20Sopenharmony_ci atmel_set_gcr(priv->dev, GCR_REMAP); 35818c2ecf20Sopenharmony_ci 35828c2ecf20Sopenharmony_ci /* wake up on-board processor */ 35838c2ecf20Sopenharmony_ci atmel_clear_gcr(priv->dev, 0x0040); 35848c2ecf20Sopenharmony_ci atmel_write16(priv->dev, BSR, BSS_SRAM); 35858c2ecf20Sopenharmony_ci 35868c2ecf20Sopenharmony_ci if (priv->card_type == CARD_TYPE_SPI_FLASH) 35878c2ecf20Sopenharmony_ci mdelay(100); 35888c2ecf20Sopenharmony_ci 35898c2ecf20Sopenharmony_ci /* and wait for it */ 35908c2ecf20Sopenharmony_ci for (i = LOOP_RETRY_LIMIT; i; i--) { 35918c2ecf20Sopenharmony_ci mr1 = atmel_read16(priv->dev, MR1); 35928c2ecf20Sopenharmony_ci mr3 = atmel_read16(priv->dev, MR3); 35938c2ecf20Sopenharmony_ci 35948c2ecf20Sopenharmony_ci if (mr3 & MAC_BOOT_COMPLETE) 35958c2ecf20Sopenharmony_ci break; 35968c2ecf20Sopenharmony_ci if (mr1 & MAC_BOOT_COMPLETE && 35978c2ecf20Sopenharmony_ci priv->bus_type == BUS_TYPE_PCCARD) 35988c2ecf20Sopenharmony_ci break; 35998c2ecf20Sopenharmony_ci } 36008c2ecf20Sopenharmony_ci 36018c2ecf20Sopenharmony_ci if (i == 0) { 36028c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: MAC failed to boot.\n", priv->dev->name); 36038c2ecf20Sopenharmony_ci return -EIO; 36048c2ecf20Sopenharmony_ci } 36058c2ecf20Sopenharmony_ci 36068c2ecf20Sopenharmony_ci if ((priv->host_info_base = atmel_read16(priv->dev, MR2)) == 0xffff) { 36078c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: card missing.\n", priv->dev->name); 36088c2ecf20Sopenharmony_ci return -ENODEV; 36098c2ecf20Sopenharmony_ci } 36108c2ecf20Sopenharmony_ci 36118c2ecf20Sopenharmony_ci /* now check for completion of MAC initialization through 36128c2ecf20Sopenharmony_ci the FunCtrl field of the IFACE, poll MR1 to detect completion of 36138c2ecf20Sopenharmony_ci MAC initialization, check completion status, set interrupt mask, 36148c2ecf20Sopenharmony_ci enables interrupts and calls Tx and Rx initialization functions */ 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), FUNC_CTRL_INIT_COMPLETE); 36178c2ecf20Sopenharmony_ci 36188c2ecf20Sopenharmony_ci for (i = LOOP_RETRY_LIMIT; i; i--) { 36198c2ecf20Sopenharmony_ci mr1 = atmel_read16(priv->dev, MR1); 36208c2ecf20Sopenharmony_ci mr3 = atmel_read16(priv->dev, MR3); 36218c2ecf20Sopenharmony_ci 36228c2ecf20Sopenharmony_ci if (mr3 & MAC_INIT_COMPLETE) 36238c2ecf20Sopenharmony_ci break; 36248c2ecf20Sopenharmony_ci if (mr1 & MAC_INIT_COMPLETE && 36258c2ecf20Sopenharmony_ci priv->bus_type == BUS_TYPE_PCCARD) 36268c2ecf20Sopenharmony_ci break; 36278c2ecf20Sopenharmony_ci } 36288c2ecf20Sopenharmony_ci 36298c2ecf20Sopenharmony_ci if (i == 0) { 36308c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: MAC failed to initialise.\n", 36318c2ecf20Sopenharmony_ci priv->dev->name); 36328c2ecf20Sopenharmony_ci return -EIO; 36338c2ecf20Sopenharmony_ci } 36348c2ecf20Sopenharmony_ci 36358c2ecf20Sopenharmony_ci /* Check for MAC_INIT_OK only on the register that the MAC_INIT_OK was set */ 36368c2ecf20Sopenharmony_ci if ((mr3 & MAC_INIT_COMPLETE) && 36378c2ecf20Sopenharmony_ci !(atmel_read16(priv->dev, MR3) & MAC_INIT_OK)) { 36388c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: MAC failed MR3 self-test.\n", priv->dev->name); 36398c2ecf20Sopenharmony_ci return -EIO; 36408c2ecf20Sopenharmony_ci } 36418c2ecf20Sopenharmony_ci if ((mr1 & MAC_INIT_COMPLETE) && 36428c2ecf20Sopenharmony_ci !(atmel_read16(priv->dev, MR1) & MAC_INIT_OK)) { 36438c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: MAC failed MR1 self-test.\n", priv->dev->name); 36448c2ecf20Sopenharmony_ci return -EIO; 36458c2ecf20Sopenharmony_ci } 36468c2ecf20Sopenharmony_ci 36478c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, (unsigned char *)iface, 36488c2ecf20Sopenharmony_ci priv->host_info_base, sizeof(*iface)); 36498c2ecf20Sopenharmony_ci 36508c2ecf20Sopenharmony_ci iface->tx_buff_pos = le16_to_cpu(iface->tx_buff_pos); 36518c2ecf20Sopenharmony_ci iface->tx_buff_size = le16_to_cpu(iface->tx_buff_size); 36528c2ecf20Sopenharmony_ci iface->tx_desc_pos = le16_to_cpu(iface->tx_desc_pos); 36538c2ecf20Sopenharmony_ci iface->tx_desc_count = le16_to_cpu(iface->tx_desc_count); 36548c2ecf20Sopenharmony_ci iface->rx_buff_pos = le16_to_cpu(iface->rx_buff_pos); 36558c2ecf20Sopenharmony_ci iface->rx_buff_size = le16_to_cpu(iface->rx_buff_size); 36568c2ecf20Sopenharmony_ci iface->rx_desc_pos = le16_to_cpu(iface->rx_desc_pos); 36578c2ecf20Sopenharmony_ci iface->rx_desc_count = le16_to_cpu(iface->rx_desc_count); 36588c2ecf20Sopenharmony_ci iface->build_version = le16_to_cpu(iface->build_version); 36598c2ecf20Sopenharmony_ci iface->command_pos = le16_to_cpu(iface->command_pos); 36608c2ecf20Sopenharmony_ci iface->major_version = le16_to_cpu(iface->major_version); 36618c2ecf20Sopenharmony_ci iface->minor_version = le16_to_cpu(iface->minor_version); 36628c2ecf20Sopenharmony_ci iface->func_ctrl = le16_to_cpu(iface->func_ctrl); 36638c2ecf20Sopenharmony_ci iface->mac_status = le16_to_cpu(iface->mac_status); 36648c2ecf20Sopenharmony_ci 36658c2ecf20Sopenharmony_ci return 0; 36668c2ecf20Sopenharmony_ci} 36678c2ecf20Sopenharmony_ci 36688c2ecf20Sopenharmony_ci/* determine type of memory and MAC address */ 36698c2ecf20Sopenharmony_cistatic int probe_atmel_card(struct net_device *dev) 36708c2ecf20Sopenharmony_ci{ 36718c2ecf20Sopenharmony_ci int rc = 0; 36728c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 36738c2ecf20Sopenharmony_ci 36748c2ecf20Sopenharmony_ci /* reset pccard */ 36758c2ecf20Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 36768c2ecf20Sopenharmony_ci atmel_write16(dev, GCR, 0x0060); 36778c2ecf20Sopenharmony_ci 36788c2ecf20Sopenharmony_ci atmel_write16(dev, GCR, 0x0040); 36798c2ecf20Sopenharmony_ci msleep(500); 36808c2ecf20Sopenharmony_ci 36818c2ecf20Sopenharmony_ci if (atmel_read16(dev, MR2) == 0) { 36828c2ecf20Sopenharmony_ci /* No stored firmware so load a small stub which just 36838c2ecf20Sopenharmony_ci tells us the MAC address */ 36848c2ecf20Sopenharmony_ci int i; 36858c2ecf20Sopenharmony_ci priv->card_type = CARD_TYPE_EEPROM; 36868c2ecf20Sopenharmony_ci atmel_write16(dev, BSR, BSS_IRAM); 36878c2ecf20Sopenharmony_ci atmel_copy_to_card(dev, 0, mac_reader, sizeof(mac_reader)); 36888c2ecf20Sopenharmony_ci atmel_set_gcr(dev, GCR_REMAP); 36898c2ecf20Sopenharmony_ci atmel_clear_gcr(priv->dev, 0x0040); 36908c2ecf20Sopenharmony_ci atmel_write16(dev, BSR, BSS_SRAM); 36918c2ecf20Sopenharmony_ci for (i = LOOP_RETRY_LIMIT; i; i--) 36928c2ecf20Sopenharmony_ci if (atmel_read16(dev, MR3) & MAC_BOOT_COMPLETE) 36938c2ecf20Sopenharmony_ci break; 36948c2ecf20Sopenharmony_ci if (i == 0) { 36958c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: MAC failed to boot MAC address reader.\n", dev->name); 36968c2ecf20Sopenharmony_ci } else { 36978c2ecf20Sopenharmony_ci atmel_copy_to_host(dev, dev->dev_addr, atmel_read16(dev, MR2), 6); 36988c2ecf20Sopenharmony_ci /* got address, now squash it again until the network 36998c2ecf20Sopenharmony_ci interface is opened */ 37008c2ecf20Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 37018c2ecf20Sopenharmony_ci atmel_write16(dev, GCR, 0x0060); 37028c2ecf20Sopenharmony_ci atmel_write16(dev, GCR, 0x0040); 37038c2ecf20Sopenharmony_ci rc = 1; 37048c2ecf20Sopenharmony_ci } 37058c2ecf20Sopenharmony_ci } else if (atmel_read16(dev, MR4) == 0) { 37068c2ecf20Sopenharmony_ci /* Mac address easy in this case. */ 37078c2ecf20Sopenharmony_ci priv->card_type = CARD_TYPE_PARALLEL_FLASH; 37088c2ecf20Sopenharmony_ci atmel_write16(dev, BSR, 1); 37098c2ecf20Sopenharmony_ci atmel_copy_to_host(dev, dev->dev_addr, 0xc000, 6); 37108c2ecf20Sopenharmony_ci atmel_write16(dev, BSR, 0x200); 37118c2ecf20Sopenharmony_ci rc = 1; 37128c2ecf20Sopenharmony_ci } else { 37138c2ecf20Sopenharmony_ci /* Standard firmware in flash, boot it up and ask 37148c2ecf20Sopenharmony_ci for the Mac Address */ 37158c2ecf20Sopenharmony_ci priv->card_type = CARD_TYPE_SPI_FLASH; 37168c2ecf20Sopenharmony_ci if (atmel_wakeup_firmware(priv) == 0) { 37178c2ecf20Sopenharmony_ci atmel_get_mib(priv, Mac_Address_Mib_Type, 0, dev->dev_addr, 6); 37188c2ecf20Sopenharmony_ci 37198c2ecf20Sopenharmony_ci /* got address, now squash it again until the network 37208c2ecf20Sopenharmony_ci interface is opened */ 37218c2ecf20Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 37228c2ecf20Sopenharmony_ci atmel_write16(dev, GCR, 0x0060); 37238c2ecf20Sopenharmony_ci atmel_write16(dev, GCR, 0x0040); 37248c2ecf20Sopenharmony_ci rc = 1; 37258c2ecf20Sopenharmony_ci } 37268c2ecf20Sopenharmony_ci } 37278c2ecf20Sopenharmony_ci 37288c2ecf20Sopenharmony_ci if (rc) { 37298c2ecf20Sopenharmony_ci if (dev->dev_addr[0] == 0xFF) { 37308c2ecf20Sopenharmony_ci static const u8 default_mac[] = { 37318c2ecf20Sopenharmony_ci 0x00, 0x04, 0x25, 0x00, 0x00, 0x00 37328c2ecf20Sopenharmony_ci }; 37338c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name); 37348c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, default_mac, ETH_ALEN); 37358c2ecf20Sopenharmony_ci } 37368c2ecf20Sopenharmony_ci } 37378c2ecf20Sopenharmony_ci 37388c2ecf20Sopenharmony_ci return rc; 37398c2ecf20Sopenharmony_ci} 37408c2ecf20Sopenharmony_ci 37418c2ecf20Sopenharmony_ci/* Move the encyption information on the MIB structure. 37428c2ecf20Sopenharmony_ci This routine is for the pre-WPA firmware: later firmware has 37438c2ecf20Sopenharmony_ci a different format MIB and a different routine. */ 37448c2ecf20Sopenharmony_cistatic void build_wep_mib(struct atmel_private *priv) 37458c2ecf20Sopenharmony_ci{ 37468c2ecf20Sopenharmony_ci struct { /* NB this is matched to the hardware, don't change. */ 37478c2ecf20Sopenharmony_ci u8 wep_is_on; 37488c2ecf20Sopenharmony_ci u8 default_key; /* 0..3 */ 37498c2ecf20Sopenharmony_ci u8 reserved; 37508c2ecf20Sopenharmony_ci u8 exclude_unencrypted; 37518c2ecf20Sopenharmony_ci 37528c2ecf20Sopenharmony_ci u32 WEPICV_error_count; 37538c2ecf20Sopenharmony_ci u32 WEP_excluded_count; 37548c2ecf20Sopenharmony_ci 37558c2ecf20Sopenharmony_ci u8 wep_keys[MAX_ENCRYPTION_KEYS][13]; 37568c2ecf20Sopenharmony_ci u8 encryption_level; /* 0, 1, 2 */ 37578c2ecf20Sopenharmony_ci u8 reserved2[3]; 37588c2ecf20Sopenharmony_ci } mib; 37598c2ecf20Sopenharmony_ci int i; 37608c2ecf20Sopenharmony_ci 37618c2ecf20Sopenharmony_ci mib.wep_is_on = priv->wep_is_on; 37628c2ecf20Sopenharmony_ci if (priv->wep_is_on) { 37638c2ecf20Sopenharmony_ci if (priv->wep_key_len[priv->default_key] > 5) 37648c2ecf20Sopenharmony_ci mib.encryption_level = 2; 37658c2ecf20Sopenharmony_ci else 37668c2ecf20Sopenharmony_ci mib.encryption_level = 1; 37678c2ecf20Sopenharmony_ci } else { 37688c2ecf20Sopenharmony_ci mib.encryption_level = 0; 37698c2ecf20Sopenharmony_ci } 37708c2ecf20Sopenharmony_ci 37718c2ecf20Sopenharmony_ci mib.default_key = priv->default_key; 37728c2ecf20Sopenharmony_ci mib.exclude_unencrypted = priv->exclude_unencrypted; 37738c2ecf20Sopenharmony_ci 37748c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) 37758c2ecf20Sopenharmony_ci memcpy(mib.wep_keys[i], priv->wep_keys[i], 13); 37768c2ecf20Sopenharmony_ci 37778c2ecf20Sopenharmony_ci atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); 37788c2ecf20Sopenharmony_ci} 37798c2ecf20Sopenharmony_ci 37808c2ecf20Sopenharmony_cistatic void build_wpa_mib(struct atmel_private *priv) 37818c2ecf20Sopenharmony_ci{ 37828c2ecf20Sopenharmony_ci /* This is for the later (WPA enabled) firmware. */ 37838c2ecf20Sopenharmony_ci 37848c2ecf20Sopenharmony_ci struct { /* NB this is matched to the hardware, don't change. */ 37858c2ecf20Sopenharmony_ci u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; 37868c2ecf20Sopenharmony_ci u8 receiver_address[ETH_ALEN]; 37878c2ecf20Sopenharmony_ci u8 wep_is_on; 37888c2ecf20Sopenharmony_ci u8 default_key; /* 0..3 */ 37898c2ecf20Sopenharmony_ci u8 group_key; 37908c2ecf20Sopenharmony_ci u8 exclude_unencrypted; 37918c2ecf20Sopenharmony_ci u8 encryption_type; 37928c2ecf20Sopenharmony_ci u8 reserved; 37938c2ecf20Sopenharmony_ci 37948c2ecf20Sopenharmony_ci u32 WEPICV_error_count; 37958c2ecf20Sopenharmony_ci u32 WEP_excluded_count; 37968c2ecf20Sopenharmony_ci 37978c2ecf20Sopenharmony_ci u8 key_RSC[4][8]; 37988c2ecf20Sopenharmony_ci } mib; 37998c2ecf20Sopenharmony_ci 38008c2ecf20Sopenharmony_ci int i; 38018c2ecf20Sopenharmony_ci 38028c2ecf20Sopenharmony_ci mib.wep_is_on = priv->wep_is_on; 38038c2ecf20Sopenharmony_ci mib.exclude_unencrypted = priv->exclude_unencrypted; 38048c2ecf20Sopenharmony_ci memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN); 38058c2ecf20Sopenharmony_ci 38068c2ecf20Sopenharmony_ci /* zero all the keys before adding in valid ones. */ 38078c2ecf20Sopenharmony_ci memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value)); 38088c2ecf20Sopenharmony_ci 38098c2ecf20Sopenharmony_ci if (priv->wep_is_on) { 38108c2ecf20Sopenharmony_ci /* There's a comment in the Atmel code to the effect that this 38118c2ecf20Sopenharmony_ci is only valid when still using WEP, it may need to be set to 38128c2ecf20Sopenharmony_ci something to use WPA */ 38138c2ecf20Sopenharmony_ci memset(mib.key_RSC, 0, sizeof(mib.key_RSC)); 38148c2ecf20Sopenharmony_ci 38158c2ecf20Sopenharmony_ci mib.default_key = mib.group_key = 255; 38168c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) { 38178c2ecf20Sopenharmony_ci if (priv->wep_key_len[i] > 0) { 38188c2ecf20Sopenharmony_ci memcpy(mib.cipher_default_key_value[i], priv->wep_keys[i], MAX_ENCRYPTION_KEY_SIZE); 38198c2ecf20Sopenharmony_ci if (i == priv->default_key) { 38208c2ecf20Sopenharmony_ci mib.default_key = i; 38218c2ecf20Sopenharmony_ci mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 7; 38228c2ecf20Sopenharmony_ci mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->pairwise_cipher_suite; 38238c2ecf20Sopenharmony_ci } else { 38248c2ecf20Sopenharmony_ci mib.group_key = i; 38258c2ecf20Sopenharmony_ci priv->group_cipher_suite = priv->pairwise_cipher_suite; 38268c2ecf20Sopenharmony_ci mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 1; 38278c2ecf20Sopenharmony_ci mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->group_cipher_suite; 38288c2ecf20Sopenharmony_ci } 38298c2ecf20Sopenharmony_ci } 38308c2ecf20Sopenharmony_ci } 38318c2ecf20Sopenharmony_ci if (mib.default_key == 255) 38328c2ecf20Sopenharmony_ci mib.default_key = mib.group_key != 255 ? mib.group_key : 0; 38338c2ecf20Sopenharmony_ci if (mib.group_key == 255) 38348c2ecf20Sopenharmony_ci mib.group_key = mib.default_key; 38358c2ecf20Sopenharmony_ci 38368c2ecf20Sopenharmony_ci } 38378c2ecf20Sopenharmony_ci 38388c2ecf20Sopenharmony_ci atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); 38398c2ecf20Sopenharmony_ci} 38408c2ecf20Sopenharmony_ci 38418c2ecf20Sopenharmony_cistatic int reset_atmel_card(struct net_device *dev) 38428c2ecf20Sopenharmony_ci{ 38438c2ecf20Sopenharmony_ci /* do everything necessary to wake up the hardware, including 38448c2ecf20Sopenharmony_ci waiting for the lightning strike and throwing the knife switch.... 38458c2ecf20Sopenharmony_ci 38468c2ecf20Sopenharmony_ci set all the Mib values which matter in the card to match 38478c2ecf20Sopenharmony_ci their settings in the atmel_private structure. Some of these 38488c2ecf20Sopenharmony_ci can be altered on the fly, but many (WEP, infrastructure or ad-hoc) 38498c2ecf20Sopenharmony_ci can only be changed by tearing down the world and coming back through 38508c2ecf20Sopenharmony_ci here. 38518c2ecf20Sopenharmony_ci 38528c2ecf20Sopenharmony_ci This routine is also responsible for initialising some 38538c2ecf20Sopenharmony_ci hardware-specific fields in the atmel_private structure, 38548c2ecf20Sopenharmony_ci including a copy of the firmware's hostinfo structure 38558c2ecf20Sopenharmony_ci which is the route into the rest of the firmware datastructures. */ 38568c2ecf20Sopenharmony_ci 38578c2ecf20Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 38588c2ecf20Sopenharmony_ci u8 configuration; 38598c2ecf20Sopenharmony_ci int old_state = priv->station_state; 38608c2ecf20Sopenharmony_ci int err = 0; 38618c2ecf20Sopenharmony_ci 38628c2ecf20Sopenharmony_ci /* data to add to the firmware names, in priority order 38638c2ecf20Sopenharmony_ci this implemenents firmware versioning */ 38648c2ecf20Sopenharmony_ci 38658c2ecf20Sopenharmony_ci static char *firmware_modifier[] = { 38668c2ecf20Sopenharmony_ci "-wpa", 38678c2ecf20Sopenharmony_ci "", 38688c2ecf20Sopenharmony_ci NULL 38698c2ecf20Sopenharmony_ci }; 38708c2ecf20Sopenharmony_ci 38718c2ecf20Sopenharmony_ci /* reset pccard */ 38728c2ecf20Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 38738c2ecf20Sopenharmony_ci atmel_write16(priv->dev, GCR, 0x0060); 38748c2ecf20Sopenharmony_ci 38758c2ecf20Sopenharmony_ci /* stop card , disable interrupts */ 38768c2ecf20Sopenharmony_ci atmel_write16(priv->dev, GCR, 0x0040); 38778c2ecf20Sopenharmony_ci 38788c2ecf20Sopenharmony_ci if (priv->card_type == CARD_TYPE_EEPROM) { 38798c2ecf20Sopenharmony_ci /* copy in firmware if needed */ 38808c2ecf20Sopenharmony_ci const struct firmware *fw_entry = NULL; 38818c2ecf20Sopenharmony_ci const unsigned char *fw; 38828c2ecf20Sopenharmony_ci int len = priv->firmware_length; 38838c2ecf20Sopenharmony_ci if (!(fw = priv->firmware)) { 38848c2ecf20Sopenharmony_ci if (priv->firmware_type == ATMEL_FW_TYPE_NONE) { 38858c2ecf20Sopenharmony_ci if (strlen(priv->firmware_id) == 0) { 38868c2ecf20Sopenharmony_ci printk(KERN_INFO 38878c2ecf20Sopenharmony_ci "%s: card type is unknown: assuming at76c502 firmware is OK.\n", 38888c2ecf20Sopenharmony_ci dev->name); 38898c2ecf20Sopenharmony_ci printk(KERN_INFO 38908c2ecf20Sopenharmony_ci "%s: if not, use the firmware= module parameter.\n", 38918c2ecf20Sopenharmony_ci dev->name); 38928c2ecf20Sopenharmony_ci strcpy(priv->firmware_id, "atmel_at76c502.bin"); 38938c2ecf20Sopenharmony_ci } 38948c2ecf20Sopenharmony_ci err = request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev); 38958c2ecf20Sopenharmony_ci if (err != 0) { 38968c2ecf20Sopenharmony_ci printk(KERN_ALERT 38978c2ecf20Sopenharmony_ci "%s: firmware %s is missing, cannot continue.\n", 38988c2ecf20Sopenharmony_ci dev->name, priv->firmware_id); 38998c2ecf20Sopenharmony_ci return err; 39008c2ecf20Sopenharmony_ci } 39018c2ecf20Sopenharmony_ci } else { 39028c2ecf20Sopenharmony_ci int fw_index = 0; 39038c2ecf20Sopenharmony_ci int success = 0; 39048c2ecf20Sopenharmony_ci 39058c2ecf20Sopenharmony_ci /* get firmware filename entry based on firmware type ID */ 39068c2ecf20Sopenharmony_ci while (fw_table[fw_index].fw_type != priv->firmware_type 39078c2ecf20Sopenharmony_ci && fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) 39088c2ecf20Sopenharmony_ci fw_index++; 39098c2ecf20Sopenharmony_ci 39108c2ecf20Sopenharmony_ci /* construct the actual firmware file name */ 39118c2ecf20Sopenharmony_ci if (fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) { 39128c2ecf20Sopenharmony_ci int i; 39138c2ecf20Sopenharmony_ci for (i = 0; firmware_modifier[i]; i++) { 39148c2ecf20Sopenharmony_ci snprintf(priv->firmware_id, 32, "%s%s.%s", fw_table[fw_index].fw_file, 39158c2ecf20Sopenharmony_ci firmware_modifier[i], fw_table[fw_index].fw_file_ext); 39168c2ecf20Sopenharmony_ci priv->firmware_id[31] = '\0'; 39178c2ecf20Sopenharmony_ci if (request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev) == 0) { 39188c2ecf20Sopenharmony_ci success = 1; 39198c2ecf20Sopenharmony_ci break; 39208c2ecf20Sopenharmony_ci } 39218c2ecf20Sopenharmony_ci } 39228c2ecf20Sopenharmony_ci } 39238c2ecf20Sopenharmony_ci if (!success) { 39248c2ecf20Sopenharmony_ci printk(KERN_ALERT 39258c2ecf20Sopenharmony_ci "%s: firmware %s is missing, cannot start.\n", 39268c2ecf20Sopenharmony_ci dev->name, priv->firmware_id); 39278c2ecf20Sopenharmony_ci priv->firmware_id[0] = '\0'; 39288c2ecf20Sopenharmony_ci return -ENOENT; 39298c2ecf20Sopenharmony_ci } 39308c2ecf20Sopenharmony_ci } 39318c2ecf20Sopenharmony_ci 39328c2ecf20Sopenharmony_ci fw = fw_entry->data; 39338c2ecf20Sopenharmony_ci len = fw_entry->size; 39348c2ecf20Sopenharmony_ci } 39358c2ecf20Sopenharmony_ci 39368c2ecf20Sopenharmony_ci if (len <= 0x6000) { 39378c2ecf20Sopenharmony_ci atmel_write16(priv->dev, BSR, BSS_IRAM); 39388c2ecf20Sopenharmony_ci atmel_copy_to_card(priv->dev, 0, fw, len); 39398c2ecf20Sopenharmony_ci atmel_set_gcr(priv->dev, GCR_REMAP); 39408c2ecf20Sopenharmony_ci } else { 39418c2ecf20Sopenharmony_ci /* Remap */ 39428c2ecf20Sopenharmony_ci atmel_set_gcr(priv->dev, GCR_REMAP); 39438c2ecf20Sopenharmony_ci atmel_write16(priv->dev, BSR, BSS_IRAM); 39448c2ecf20Sopenharmony_ci atmel_copy_to_card(priv->dev, 0, fw, 0x6000); 39458c2ecf20Sopenharmony_ci atmel_write16(priv->dev, BSR, 0x2ff); 39468c2ecf20Sopenharmony_ci atmel_copy_to_card(priv->dev, 0x8000, &fw[0x6000], len - 0x6000); 39478c2ecf20Sopenharmony_ci } 39488c2ecf20Sopenharmony_ci 39498c2ecf20Sopenharmony_ci release_firmware(fw_entry); 39508c2ecf20Sopenharmony_ci } 39518c2ecf20Sopenharmony_ci 39528c2ecf20Sopenharmony_ci err = atmel_wakeup_firmware(priv); 39538c2ecf20Sopenharmony_ci if (err != 0) 39548c2ecf20Sopenharmony_ci return err; 39558c2ecf20Sopenharmony_ci 39568c2ecf20Sopenharmony_ci /* Check the version and set the correct flag for wpa stuff, 39578c2ecf20Sopenharmony_ci old and new firmware is incompatible. 39588c2ecf20Sopenharmony_ci The pre-wpa 3com firmware reports major version 5, 39598c2ecf20Sopenharmony_ci the wpa 3com firmware is major version 4 and doesn't need 39608c2ecf20Sopenharmony_ci the 3com broken-ness filter. */ 39618c2ecf20Sopenharmony_ci priv->use_wpa = (priv->host_info.major_version == 4); 39628c2ecf20Sopenharmony_ci priv->radio_on_broken = (priv->host_info.major_version == 5); 39638c2ecf20Sopenharmony_ci 39648c2ecf20Sopenharmony_ci /* unmask all irq sources */ 39658c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_MASK_OFFSET), 0xff); 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci /* int Tx system and enable Tx */ 39688c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, 0), 0); 39698c2ecf20Sopenharmony_ci atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, 0), 0x80000000L); 39708c2ecf20Sopenharmony_ci atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, 0), 0); 39718c2ecf20Sopenharmony_ci atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, 0), 0); 39728c2ecf20Sopenharmony_ci 39738c2ecf20Sopenharmony_ci priv->tx_desc_free = priv->host_info.tx_desc_count; 39748c2ecf20Sopenharmony_ci priv->tx_desc_head = 0; 39758c2ecf20Sopenharmony_ci priv->tx_desc_tail = 0; 39768c2ecf20Sopenharmony_ci priv->tx_desc_previous = 0; 39778c2ecf20Sopenharmony_ci priv->tx_free_mem = priv->host_info.tx_buff_size; 39788c2ecf20Sopenharmony_ci priv->tx_buff_head = 0; 39798c2ecf20Sopenharmony_ci priv->tx_buff_tail = 0; 39808c2ecf20Sopenharmony_ci 39818c2ecf20Sopenharmony_ci configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); 39828c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), 39838c2ecf20Sopenharmony_ci configuration | FUNC_CTRL_TxENABLE); 39848c2ecf20Sopenharmony_ci 39858c2ecf20Sopenharmony_ci /* init Rx system and enable */ 39868c2ecf20Sopenharmony_ci priv->rx_desc_head = 0; 39878c2ecf20Sopenharmony_ci 39888c2ecf20Sopenharmony_ci configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); 39898c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), 39908c2ecf20Sopenharmony_ci configuration | FUNC_CTRL_RxENABLE); 39918c2ecf20Sopenharmony_ci 39928c2ecf20Sopenharmony_ci if (!priv->radio_on_broken) { 39938c2ecf20Sopenharmony_ci if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) == 39948c2ecf20Sopenharmony_ci CMD_STATUS_REJECTED_RADIO_OFF) { 39958c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: cannot turn the radio on.\n", 39968c2ecf20Sopenharmony_ci dev->name); 39978c2ecf20Sopenharmony_ci return -EIO; 39988c2ecf20Sopenharmony_ci } 39998c2ecf20Sopenharmony_ci } 40008c2ecf20Sopenharmony_ci 40018c2ecf20Sopenharmony_ci /* set up enough MIB values to run. */ 40028c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_AUTO_TX_RATE_POS, priv->auto_tx_rate); 40038c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_TX_PROMISCUOUS_POS, PROM_MODE_OFF); 40048c2ecf20Sopenharmony_ci atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_RTS_THRESHOLD_POS, priv->rts_threshold); 40058c2ecf20Sopenharmony_ci atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_FRAG_THRESHOLD_POS, priv->frag_threshold); 40068c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_SHORT_RETRY_POS, priv->short_retry); 40078c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_LONG_RETRY_POS, priv->long_retry); 40088c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, priv->preamble); 40098c2ecf20Sopenharmony_ci atmel_set_mib(priv, Mac_Address_Mib_Type, MAC_ADDR_MIB_MAC_ADDR_POS, 40108c2ecf20Sopenharmony_ci priv->dev->dev_addr, 6); 40118c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); 40128c2ecf20Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); 40138c2ecf20Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_BEACON_PER_POS, priv->default_beacon_period); 40148c2ecf20Sopenharmony_ci atmel_set_mib(priv, Phy_Mib_Type, PHY_MIB_RATE_SET_POS, atmel_basic_rates, 4); 40158c2ecf20Sopenharmony_ci atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_PRIVACY_POS, priv->wep_is_on); 40168c2ecf20Sopenharmony_ci if (priv->use_wpa) 40178c2ecf20Sopenharmony_ci build_wpa_mib(priv); 40188c2ecf20Sopenharmony_ci else 40198c2ecf20Sopenharmony_ci build_wep_mib(priv); 40208c2ecf20Sopenharmony_ci 40218c2ecf20Sopenharmony_ci if (old_state == STATION_STATE_READY) { 40228c2ecf20Sopenharmony_ci union iwreq_data wrqu; 40238c2ecf20Sopenharmony_ci 40248c2ecf20Sopenharmony_ci wrqu.data.length = 0; 40258c2ecf20Sopenharmony_ci wrqu.data.flags = 0; 40268c2ecf20Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 40278c2ecf20Sopenharmony_ci eth_zero_addr(wrqu.ap_addr.sa_data); 40288c2ecf20Sopenharmony_ci wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); 40298c2ecf20Sopenharmony_ci } 40308c2ecf20Sopenharmony_ci 40318c2ecf20Sopenharmony_ci return 0; 40328c2ecf20Sopenharmony_ci} 40338c2ecf20Sopenharmony_ci 40348c2ecf20Sopenharmony_cistatic void atmel_send_command(struct atmel_private *priv, int command, 40358c2ecf20Sopenharmony_ci void *cmd, int cmd_size) 40368c2ecf20Sopenharmony_ci{ 40378c2ecf20Sopenharmony_ci if (cmd) 40388c2ecf20Sopenharmony_ci atmel_copy_to_card(priv->dev, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET), 40398c2ecf20Sopenharmony_ci cmd, cmd_size); 40408c2ecf20Sopenharmony_ci 40418c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET), command); 40428c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET), 0); 40438c2ecf20Sopenharmony_ci} 40448c2ecf20Sopenharmony_ci 40458c2ecf20Sopenharmony_cistatic int atmel_send_command_wait(struct atmel_private *priv, int command, 40468c2ecf20Sopenharmony_ci void *cmd, int cmd_size) 40478c2ecf20Sopenharmony_ci{ 40488c2ecf20Sopenharmony_ci int i, status; 40498c2ecf20Sopenharmony_ci 40508c2ecf20Sopenharmony_ci atmel_send_command(priv, command, cmd, cmd_size); 40518c2ecf20Sopenharmony_ci 40528c2ecf20Sopenharmony_ci for (i = 5000; i; i--) { 40538c2ecf20Sopenharmony_ci status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); 40548c2ecf20Sopenharmony_ci if (status != CMD_STATUS_IDLE && 40558c2ecf20Sopenharmony_ci status != CMD_STATUS_IN_PROGRESS) 40568c2ecf20Sopenharmony_ci break; 40578c2ecf20Sopenharmony_ci udelay(20); 40588c2ecf20Sopenharmony_ci } 40598c2ecf20Sopenharmony_ci 40608c2ecf20Sopenharmony_ci if (i == 0) { 40618c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: failed to contact MAC.\n", priv->dev->name); 40628c2ecf20Sopenharmony_ci status = CMD_STATUS_HOST_ERROR; 40638c2ecf20Sopenharmony_ci } else { 40648c2ecf20Sopenharmony_ci if (command != CMD_EnableRadio) 40658c2ecf20Sopenharmony_ci status = CMD_STATUS_COMPLETE; 40668c2ecf20Sopenharmony_ci } 40678c2ecf20Sopenharmony_ci 40688c2ecf20Sopenharmony_ci return status; 40698c2ecf20Sopenharmony_ci} 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_cistatic u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index) 40728c2ecf20Sopenharmony_ci{ 40738c2ecf20Sopenharmony_ci struct get_set_mib m; 40748c2ecf20Sopenharmony_ci m.type = type; 40758c2ecf20Sopenharmony_ci m.size = 1; 40768c2ecf20Sopenharmony_ci m.index = index; 40778c2ecf20Sopenharmony_ci 40788c2ecf20Sopenharmony_ci atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + 1); 40798c2ecf20Sopenharmony_ci return atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE)); 40808c2ecf20Sopenharmony_ci} 40818c2ecf20Sopenharmony_ci 40828c2ecf20Sopenharmony_cistatic void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, u8 data) 40838c2ecf20Sopenharmony_ci{ 40848c2ecf20Sopenharmony_ci struct get_set_mib m; 40858c2ecf20Sopenharmony_ci m.type = type; 40868c2ecf20Sopenharmony_ci m.size = 1; 40878c2ecf20Sopenharmony_ci m.index = index; 40888c2ecf20Sopenharmony_ci m.data[0] = data; 40898c2ecf20Sopenharmony_ci 40908c2ecf20Sopenharmony_ci atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 1); 40918c2ecf20Sopenharmony_ci} 40928c2ecf20Sopenharmony_ci 40938c2ecf20Sopenharmony_cistatic void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, 40948c2ecf20Sopenharmony_ci u16 data) 40958c2ecf20Sopenharmony_ci{ 40968c2ecf20Sopenharmony_ci struct get_set_mib m; 40978c2ecf20Sopenharmony_ci m.type = type; 40988c2ecf20Sopenharmony_ci m.size = 2; 40998c2ecf20Sopenharmony_ci m.index = index; 41008c2ecf20Sopenharmony_ci m.data[0] = data; 41018c2ecf20Sopenharmony_ci m.data[1] = data >> 8; 41028c2ecf20Sopenharmony_ci 41038c2ecf20Sopenharmony_ci atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 2); 41048c2ecf20Sopenharmony_ci} 41058c2ecf20Sopenharmony_ci 41068c2ecf20Sopenharmony_cistatic void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, 41078c2ecf20Sopenharmony_ci u8 *data, int data_len) 41088c2ecf20Sopenharmony_ci{ 41098c2ecf20Sopenharmony_ci struct get_set_mib m; 41108c2ecf20Sopenharmony_ci m.type = type; 41118c2ecf20Sopenharmony_ci m.size = data_len; 41128c2ecf20Sopenharmony_ci m.index = index; 41138c2ecf20Sopenharmony_ci 41148c2ecf20Sopenharmony_ci if (data_len > MIB_MAX_DATA_BYTES) 41158c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); 41168c2ecf20Sopenharmony_ci 41178c2ecf20Sopenharmony_ci memcpy(m.data, data, data_len); 41188c2ecf20Sopenharmony_ci atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); 41198c2ecf20Sopenharmony_ci} 41208c2ecf20Sopenharmony_ci 41218c2ecf20Sopenharmony_cistatic void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, 41228c2ecf20Sopenharmony_ci u8 *data, int data_len) 41238c2ecf20Sopenharmony_ci{ 41248c2ecf20Sopenharmony_ci struct get_set_mib m; 41258c2ecf20Sopenharmony_ci m.type = type; 41268c2ecf20Sopenharmony_ci m.size = data_len; 41278c2ecf20Sopenharmony_ci m.index = index; 41288c2ecf20Sopenharmony_ci 41298c2ecf20Sopenharmony_ci if (data_len > MIB_MAX_DATA_BYTES) 41308c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); 41318c2ecf20Sopenharmony_ci 41328c2ecf20Sopenharmony_ci atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); 41338c2ecf20Sopenharmony_ci atmel_copy_to_host(priv->dev, data, 41348c2ecf20Sopenharmony_ci atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE), data_len); 41358c2ecf20Sopenharmony_ci} 41368c2ecf20Sopenharmony_ci 41378c2ecf20Sopenharmony_cistatic void atmel_writeAR(struct net_device *dev, u16 data) 41388c2ecf20Sopenharmony_ci{ 41398c2ecf20Sopenharmony_ci int i; 41408c2ecf20Sopenharmony_ci outw(data, dev->base_addr + AR); 41418c2ecf20Sopenharmony_ci /* Address register appears to need some convincing..... */ 41428c2ecf20Sopenharmony_ci for (i = 0; data != inw(dev->base_addr + AR) && i < 10; i++) 41438c2ecf20Sopenharmony_ci outw(data, dev->base_addr + AR); 41448c2ecf20Sopenharmony_ci} 41458c2ecf20Sopenharmony_ci 41468c2ecf20Sopenharmony_cistatic void atmel_copy_to_card(struct net_device *dev, u16 dest, 41478c2ecf20Sopenharmony_ci const unsigned char *src, u16 len) 41488c2ecf20Sopenharmony_ci{ 41498c2ecf20Sopenharmony_ci int i; 41508c2ecf20Sopenharmony_ci atmel_writeAR(dev, dest); 41518c2ecf20Sopenharmony_ci if (dest % 2) { 41528c2ecf20Sopenharmony_ci atmel_write8(dev, DR, *src); 41538c2ecf20Sopenharmony_ci src++; len--; 41548c2ecf20Sopenharmony_ci } 41558c2ecf20Sopenharmony_ci for (i = len; i > 1 ; i -= 2) { 41568c2ecf20Sopenharmony_ci u8 lb = *src++; 41578c2ecf20Sopenharmony_ci u8 hb = *src++; 41588c2ecf20Sopenharmony_ci atmel_write16(dev, DR, lb | (hb << 8)); 41598c2ecf20Sopenharmony_ci } 41608c2ecf20Sopenharmony_ci if (i) 41618c2ecf20Sopenharmony_ci atmel_write8(dev, DR, *src); 41628c2ecf20Sopenharmony_ci} 41638c2ecf20Sopenharmony_ci 41648c2ecf20Sopenharmony_cistatic void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, 41658c2ecf20Sopenharmony_ci u16 src, u16 len) 41668c2ecf20Sopenharmony_ci{ 41678c2ecf20Sopenharmony_ci int i; 41688c2ecf20Sopenharmony_ci atmel_writeAR(dev, src); 41698c2ecf20Sopenharmony_ci if (src % 2) { 41708c2ecf20Sopenharmony_ci *dest = atmel_read8(dev, DR); 41718c2ecf20Sopenharmony_ci dest++; len--; 41728c2ecf20Sopenharmony_ci } 41738c2ecf20Sopenharmony_ci for (i = len; i > 1 ; i -= 2) { 41748c2ecf20Sopenharmony_ci u16 hw = atmel_read16(dev, DR); 41758c2ecf20Sopenharmony_ci *dest++ = hw; 41768c2ecf20Sopenharmony_ci *dest++ = hw >> 8; 41778c2ecf20Sopenharmony_ci } 41788c2ecf20Sopenharmony_ci if (i) 41798c2ecf20Sopenharmony_ci *dest = atmel_read8(dev, DR); 41808c2ecf20Sopenharmony_ci} 41818c2ecf20Sopenharmony_ci 41828c2ecf20Sopenharmony_cistatic void atmel_set_gcr(struct net_device *dev, u16 mask) 41838c2ecf20Sopenharmony_ci{ 41848c2ecf20Sopenharmony_ci outw(inw(dev->base_addr + GCR) | mask, dev->base_addr + GCR); 41858c2ecf20Sopenharmony_ci} 41868c2ecf20Sopenharmony_ci 41878c2ecf20Sopenharmony_cistatic void atmel_clear_gcr(struct net_device *dev, u16 mask) 41888c2ecf20Sopenharmony_ci{ 41898c2ecf20Sopenharmony_ci outw(inw(dev->base_addr + GCR) & ~mask, dev->base_addr + GCR); 41908c2ecf20Sopenharmony_ci} 41918c2ecf20Sopenharmony_ci 41928c2ecf20Sopenharmony_cistatic int atmel_lock_mac(struct atmel_private *priv) 41938c2ecf20Sopenharmony_ci{ 41948c2ecf20Sopenharmony_ci int i, j = 20; 41958c2ecf20Sopenharmony_ci retry: 41968c2ecf20Sopenharmony_ci for (i = 5000; i; i--) { 41978c2ecf20Sopenharmony_ci if (!atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) 41988c2ecf20Sopenharmony_ci break; 41998c2ecf20Sopenharmony_ci udelay(20); 42008c2ecf20Sopenharmony_ci } 42018c2ecf20Sopenharmony_ci 42028c2ecf20Sopenharmony_ci if (!i) 42038c2ecf20Sopenharmony_ci return 0; /* timed out */ 42048c2ecf20Sopenharmony_ci 42058c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 1); 42068c2ecf20Sopenharmony_ci if (atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) { 42078c2ecf20Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); 42088c2ecf20Sopenharmony_ci if (!j--) 42098c2ecf20Sopenharmony_ci return 0; /* timed out */ 42108c2ecf20Sopenharmony_ci goto retry; 42118c2ecf20Sopenharmony_ci } 42128c2ecf20Sopenharmony_ci 42138c2ecf20Sopenharmony_ci return 1; 42148c2ecf20Sopenharmony_ci} 42158c2ecf20Sopenharmony_ci 42168c2ecf20Sopenharmony_cistatic void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data) 42178c2ecf20Sopenharmony_ci{ 42188c2ecf20Sopenharmony_ci atmel_writeAR(priv->dev, pos); 42198c2ecf20Sopenharmony_ci atmel_write16(priv->dev, DR, data); /* card is little-endian */ 42208c2ecf20Sopenharmony_ci atmel_write16(priv->dev, DR, data >> 16); 42218c2ecf20Sopenharmony_ci} 42228c2ecf20Sopenharmony_ci 42238c2ecf20Sopenharmony_ci/***************************************************************************/ 42248c2ecf20Sopenharmony_ci/* There follows the source form of the MAC address reading firmware */ 42258c2ecf20Sopenharmony_ci/***************************************************************************/ 42268c2ecf20Sopenharmony_ci#if 0 42278c2ecf20Sopenharmony_ci 42288c2ecf20Sopenharmony_ci/* Copyright 2003 Matthew T. Russotto */ 42298c2ecf20Sopenharmony_ci/* But derived from the Atmel 76C502 firmware written by Atmel and */ 42308c2ecf20Sopenharmony_ci/* included in "atmel wireless lan drivers" package */ 42318c2ecf20Sopenharmony_ci/* 42328c2ecf20Sopenharmony_ci This file is part of net.russotto.AtmelMACFW, hereto referred to 42338c2ecf20Sopenharmony_ci as AtmelMACFW 42348c2ecf20Sopenharmony_ci 42358c2ecf20Sopenharmony_ci AtmelMACFW is free software; you can redistribute it and/or modify 42368c2ecf20Sopenharmony_ci it under the terms of the GNU General Public License version 2 42378c2ecf20Sopenharmony_ci as published by the Free Software Foundation. 42388c2ecf20Sopenharmony_ci 42398c2ecf20Sopenharmony_ci AtmelMACFW is distributed in the hope that it will be useful, 42408c2ecf20Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 42418c2ecf20Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 42428c2ecf20Sopenharmony_ci GNU General Public License for more details. 42438c2ecf20Sopenharmony_ci 42448c2ecf20Sopenharmony_ci You should have received a copy of the GNU General Public License 42458c2ecf20Sopenharmony_ci along with AtmelMACFW; if not, see <http://www.gnu.org/licenses/>. 42468c2ecf20Sopenharmony_ci 42478c2ecf20Sopenharmony_ci****************************************************************************/ 42488c2ecf20Sopenharmony_ci/* This firmware should work on the 76C502 RFMD, RFMD_D, and RFMD_E */ 42498c2ecf20Sopenharmony_ci/* It will probably work on the 76C504 and 76C502 RFMD_3COM */ 42508c2ecf20Sopenharmony_ci/* It only works on SPI EEPROM versions of the card. */ 42518c2ecf20Sopenharmony_ci 42528c2ecf20Sopenharmony_ci/* This firmware initializes the SPI controller and clock, reads the MAC */ 42538c2ecf20Sopenharmony_ci/* address from the EEPROM into SRAM, and puts the SRAM offset of the MAC */ 42548c2ecf20Sopenharmony_ci/* address in MR2, and sets MR3 to 0x10 to indicate it is done */ 42558c2ecf20Sopenharmony_ci/* It also puts a complete copy of the EEPROM in SRAM with the offset in */ 42568c2ecf20Sopenharmony_ci/* MR4, for investigational purposes (maybe we can determine chip type */ 42578c2ecf20Sopenharmony_ci/* from that?) */ 42588c2ecf20Sopenharmony_ci 42598c2ecf20Sopenharmony_ci .org 0 42608c2ecf20Sopenharmony_ci .set MRBASE, 0x8000000 42618c2ecf20Sopenharmony_ci .set CPSR_INITIAL, 0xD3 /* IRQ/FIQ disabled, ARM mode, Supervisor state */ 42628c2ecf20Sopenharmony_ci .set CPSR_USER, 0xD1 /* IRQ/FIQ disabled, ARM mode, USER state */ 42638c2ecf20Sopenharmony_ci .set SRAM_BASE, 0x02000000 42648c2ecf20Sopenharmony_ci .set SP_BASE, 0x0F300000 42658c2ecf20Sopenharmony_ci .set UNK_BASE, 0x0F000000 /* Some internal device, but which one? */ 42668c2ecf20Sopenharmony_ci .set SPI_CGEN_BASE, 0x0E000000 /* Some internal device, but which one? */ 42678c2ecf20Sopenharmony_ci .set UNK3_BASE, 0x02014000 /* Some internal device, but which one? */ 42688c2ecf20Sopenharmony_ci .set STACK_BASE, 0x5600 42698c2ecf20Sopenharmony_ci .set SP_SR, 0x10 42708c2ecf20Sopenharmony_ci .set SP_TDRE, 2 /* status register bit -- TDR empty */ 42718c2ecf20Sopenharmony_ci .set SP_RDRF, 1 /* status register bit -- RDR full */ 42728c2ecf20Sopenharmony_ci .set SP_SWRST, 0x80 42738c2ecf20Sopenharmony_ci .set SP_SPIEN, 0x1 42748c2ecf20Sopenharmony_ci .set SP_CR, 0 /* control register */ 42758c2ecf20Sopenharmony_ci .set SP_MR, 4 /* mode register */ 42768c2ecf20Sopenharmony_ci .set SP_RDR, 0x08 /* Read Data Register */ 42778c2ecf20Sopenharmony_ci .set SP_TDR, 0x0C /* Transmit Data Register */ 42788c2ecf20Sopenharmony_ci .set SP_CSR0, 0x30 /* chip select registers */ 42798c2ecf20Sopenharmony_ci .set SP_CSR1, 0x34 42808c2ecf20Sopenharmony_ci .set SP_CSR2, 0x38 42818c2ecf20Sopenharmony_ci .set SP_CSR3, 0x3C 42828c2ecf20Sopenharmony_ci .set NVRAM_CMD_RDSR, 5 /* read status register */ 42838c2ecf20Sopenharmony_ci .set NVRAM_CMD_READ, 3 /* read data */ 42848c2ecf20Sopenharmony_ci .set NVRAM_SR_RDY, 1 /* RDY bit. This bit is inverted */ 42858c2ecf20Sopenharmony_ci .set SPI_8CLOCKS, 0xFF /* Writing this to the TDR doesn't do anything to the 42868c2ecf20Sopenharmony_ci serial output, since SO is normally high. But it 42878c2ecf20Sopenharmony_ci does cause 8 clock cycles and thus 8 bits to be 42888c2ecf20Sopenharmony_ci clocked in to the chip. See Atmel's SPI 42898c2ecf20Sopenharmony_ci controller (e.g. AT91M55800) timing and 4K 42908c2ecf20Sopenharmony_ci SPI EEPROM manuals */ 42918c2ecf20Sopenharmony_ci 42928c2ecf20Sopenharmony_ci .set NVRAM_SCRATCH, 0x02000100 /* arbitrary area for scratchpad memory */ 42938c2ecf20Sopenharmony_ci .set NVRAM_IMAGE, 0x02000200 42948c2ecf20Sopenharmony_ci .set NVRAM_LENGTH, 0x0200 42958c2ecf20Sopenharmony_ci .set MAC_ADDRESS_MIB, SRAM_BASE 42968c2ecf20Sopenharmony_ci .set MAC_ADDRESS_LENGTH, 6 42978c2ecf20Sopenharmony_ci .set MAC_BOOT_FLAG, 0x10 42988c2ecf20Sopenharmony_ci .set MR1, 0 42998c2ecf20Sopenharmony_ci .set MR2, 4 43008c2ecf20Sopenharmony_ci .set MR3, 8 43018c2ecf20Sopenharmony_ci .set MR4, 0xC 43028c2ecf20Sopenharmony_ciRESET_VECTOR: 43038c2ecf20Sopenharmony_ci b RESET_HANDLER 43048c2ecf20Sopenharmony_ciUNDEF_VECTOR: 43058c2ecf20Sopenharmony_ci b HALT1 43068c2ecf20Sopenharmony_ciSWI_VECTOR: 43078c2ecf20Sopenharmony_ci b HALT1 43088c2ecf20Sopenharmony_ciIABORT_VECTOR: 43098c2ecf20Sopenharmony_ci b HALT1 43108c2ecf20Sopenharmony_ciDABORT_VECTOR: 43118c2ecf20Sopenharmony_ciRESERVED_VECTOR: 43128c2ecf20Sopenharmony_ci b HALT1 43138c2ecf20Sopenharmony_ciIRQ_VECTOR: 43148c2ecf20Sopenharmony_ci b HALT1 43158c2ecf20Sopenharmony_ciFIQ_VECTOR: 43168c2ecf20Sopenharmony_ci b HALT1 43178c2ecf20Sopenharmony_ciHALT1: b HALT1 43188c2ecf20Sopenharmony_ciRESET_HANDLER: 43198c2ecf20Sopenharmony_ci mov r0, #CPSR_INITIAL 43208c2ecf20Sopenharmony_ci msr CPSR_c, r0 /* This is probably unnecessary */ 43218c2ecf20Sopenharmony_ci 43228c2ecf20Sopenharmony_ci/* I'm guessing this is initializing clock generator electronics for SPI */ 43238c2ecf20Sopenharmony_ci ldr r0, =SPI_CGEN_BASE 43248c2ecf20Sopenharmony_ci mov r1, #0 43258c2ecf20Sopenharmony_ci mov r1, r1, lsl #3 43268c2ecf20Sopenharmony_ci orr r1, r1, #0 43278c2ecf20Sopenharmony_ci str r1, [r0] 43288c2ecf20Sopenharmony_ci ldr r1, [r0, #28] 43298c2ecf20Sopenharmony_ci bic r1, r1, #16 43308c2ecf20Sopenharmony_ci str r1, [r0, #28] 43318c2ecf20Sopenharmony_ci mov r1, #1 43328c2ecf20Sopenharmony_ci str r1, [r0, #8] 43338c2ecf20Sopenharmony_ci 43348c2ecf20Sopenharmony_ci ldr r0, =MRBASE 43358c2ecf20Sopenharmony_ci mov r1, #0 43368c2ecf20Sopenharmony_ci strh r1, [r0, #MR1] 43378c2ecf20Sopenharmony_ci strh r1, [r0, #MR2] 43388c2ecf20Sopenharmony_ci strh r1, [r0, #MR3] 43398c2ecf20Sopenharmony_ci strh r1, [r0, #MR4] 43408c2ecf20Sopenharmony_ci 43418c2ecf20Sopenharmony_ci mov sp, #STACK_BASE 43428c2ecf20Sopenharmony_ci bl SP_INIT 43438c2ecf20Sopenharmony_ci mov r0, #10 43448c2ecf20Sopenharmony_ci bl DELAY9 43458c2ecf20Sopenharmony_ci bl GET_MAC_ADDR 43468c2ecf20Sopenharmony_ci bl GET_WHOLE_NVRAM 43478c2ecf20Sopenharmony_ci ldr r0, =MRBASE 43488c2ecf20Sopenharmony_ci ldr r1, =MAC_ADDRESS_MIB 43498c2ecf20Sopenharmony_ci strh r1, [r0, #MR2] 43508c2ecf20Sopenharmony_ci ldr r1, =NVRAM_IMAGE 43518c2ecf20Sopenharmony_ci strh r1, [r0, #MR4] 43528c2ecf20Sopenharmony_ci mov r1, #MAC_BOOT_FLAG 43538c2ecf20Sopenharmony_ci strh r1, [r0, #MR3] 43548c2ecf20Sopenharmony_ciHALT2: b HALT2 43558c2ecf20Sopenharmony_ci.func Get_Whole_NVRAM, GET_WHOLE_NVRAM 43568c2ecf20Sopenharmony_ciGET_WHOLE_NVRAM: 43578c2ecf20Sopenharmony_ci stmdb sp!, {lr} 43588c2ecf20Sopenharmony_ci mov r2, #0 /* 0th bytes of NVRAM */ 43598c2ecf20Sopenharmony_ci mov r3, #NVRAM_LENGTH 43608c2ecf20Sopenharmony_ci mov r1, #0 /* not used in routine */ 43618c2ecf20Sopenharmony_ci ldr r0, =NVRAM_IMAGE 43628c2ecf20Sopenharmony_ci bl NVRAM_XFER 43638c2ecf20Sopenharmony_ci ldmia sp!, {lr} 43648c2ecf20Sopenharmony_ci bx lr 43658c2ecf20Sopenharmony_ci.endfunc 43668c2ecf20Sopenharmony_ci 43678c2ecf20Sopenharmony_ci.func Get_MAC_Addr, GET_MAC_ADDR 43688c2ecf20Sopenharmony_ciGET_MAC_ADDR: 43698c2ecf20Sopenharmony_ci stmdb sp!, {lr} 43708c2ecf20Sopenharmony_ci mov r2, #0x120 /* address of MAC Address within NVRAM */ 43718c2ecf20Sopenharmony_ci mov r3, #MAC_ADDRESS_LENGTH 43728c2ecf20Sopenharmony_ci mov r1, #0 /* not used in routine */ 43738c2ecf20Sopenharmony_ci ldr r0, =MAC_ADDRESS_MIB 43748c2ecf20Sopenharmony_ci bl NVRAM_XFER 43758c2ecf20Sopenharmony_ci ldmia sp!, {lr} 43768c2ecf20Sopenharmony_ci bx lr 43778c2ecf20Sopenharmony_ci.endfunc 43788c2ecf20Sopenharmony_ci.ltorg 43798c2ecf20Sopenharmony_ci.func Delay9, DELAY9 43808c2ecf20Sopenharmony_ciDELAY9: 43818c2ecf20Sopenharmony_ci adds r0, r0, r0, LSL #3 /* r0 = r0 * 9 */ 43828c2ecf20Sopenharmony_ciDELAYLOOP: 43838c2ecf20Sopenharmony_ci beq DELAY9_done 43848c2ecf20Sopenharmony_ci subs r0, r0, #1 43858c2ecf20Sopenharmony_ci b DELAYLOOP 43868c2ecf20Sopenharmony_ciDELAY9_done: 43878c2ecf20Sopenharmony_ci bx lr 43888c2ecf20Sopenharmony_ci.endfunc 43898c2ecf20Sopenharmony_ci 43908c2ecf20Sopenharmony_ci.func SP_Init, SP_INIT 43918c2ecf20Sopenharmony_ciSP_INIT: 43928c2ecf20Sopenharmony_ci mov r1, #SP_SWRST 43938c2ecf20Sopenharmony_ci ldr r0, =SP_BASE 43948c2ecf20Sopenharmony_ci str r1, [r0, #SP_CR] /* reset the SPI */ 43958c2ecf20Sopenharmony_ci mov r1, #0 43968c2ecf20Sopenharmony_ci str r1, [r0, #SP_CR] /* release SPI from reset state */ 43978c2ecf20Sopenharmony_ci mov r1, #SP_SPIEN 43988c2ecf20Sopenharmony_ci str r1, [r0, #SP_MR] /* set the SPI to MASTER mode*/ 43998c2ecf20Sopenharmony_ci str r1, [r0, #SP_CR] /* enable the SPI */ 44008c2ecf20Sopenharmony_ci 44018c2ecf20Sopenharmony_ci/* My guess would be this turns on the SPI clock */ 44028c2ecf20Sopenharmony_ci ldr r3, =SPI_CGEN_BASE 44038c2ecf20Sopenharmony_ci ldr r1, [r3, #28] 44048c2ecf20Sopenharmony_ci orr r1, r1, #0x2000 44058c2ecf20Sopenharmony_ci str r1, [r3, #28] 44068c2ecf20Sopenharmony_ci 44078c2ecf20Sopenharmony_ci ldr r1, =0x2000c01 44088c2ecf20Sopenharmony_ci str r1, [r0, #SP_CSR0] 44098c2ecf20Sopenharmony_ci ldr r1, =0x2000201 44108c2ecf20Sopenharmony_ci str r1, [r0, #SP_CSR1] 44118c2ecf20Sopenharmony_ci str r1, [r0, #SP_CSR2] 44128c2ecf20Sopenharmony_ci str r1, [r0, #SP_CSR3] 44138c2ecf20Sopenharmony_ci ldr r1, [r0, #SP_SR] 44148c2ecf20Sopenharmony_ci ldr r0, [r0, #SP_RDR] 44158c2ecf20Sopenharmony_ci bx lr 44168c2ecf20Sopenharmony_ci.endfunc 44178c2ecf20Sopenharmony_ci.func NVRAM_Init, NVRAM_INIT 44188c2ecf20Sopenharmony_ciNVRAM_INIT: 44198c2ecf20Sopenharmony_ci ldr r1, =SP_BASE 44208c2ecf20Sopenharmony_ci ldr r0, [r1, #SP_RDR] 44218c2ecf20Sopenharmony_ci mov r0, #NVRAM_CMD_RDSR 44228c2ecf20Sopenharmony_ci str r0, [r1, #SP_TDR] 44238c2ecf20Sopenharmony_ciSP_loop1: 44248c2ecf20Sopenharmony_ci ldr r0, [r1, #SP_SR] 44258c2ecf20Sopenharmony_ci tst r0, #SP_TDRE 44268c2ecf20Sopenharmony_ci beq SP_loop1 44278c2ecf20Sopenharmony_ci 44288c2ecf20Sopenharmony_ci mov r0, #SPI_8CLOCKS 44298c2ecf20Sopenharmony_ci str r0, [r1, #SP_TDR] 44308c2ecf20Sopenharmony_ciSP_loop2: 44318c2ecf20Sopenharmony_ci ldr r0, [r1, #SP_SR] 44328c2ecf20Sopenharmony_ci tst r0, #SP_TDRE 44338c2ecf20Sopenharmony_ci beq SP_loop2 44348c2ecf20Sopenharmony_ci 44358c2ecf20Sopenharmony_ci ldr r0, [r1, #SP_RDR] 44368c2ecf20Sopenharmony_ciSP_loop3: 44378c2ecf20Sopenharmony_ci ldr r0, [r1, #SP_SR] 44388c2ecf20Sopenharmony_ci tst r0, #SP_RDRF 44398c2ecf20Sopenharmony_ci beq SP_loop3 44408c2ecf20Sopenharmony_ci 44418c2ecf20Sopenharmony_ci ldr r0, [r1, #SP_RDR] 44428c2ecf20Sopenharmony_ci and r0, r0, #255 44438c2ecf20Sopenharmony_ci bx lr 44448c2ecf20Sopenharmony_ci.endfunc 44458c2ecf20Sopenharmony_ci 44468c2ecf20Sopenharmony_ci.func NVRAM_Xfer, NVRAM_XFER 44478c2ecf20Sopenharmony_ci /* r0 = dest address */ 44488c2ecf20Sopenharmony_ci /* r1 = not used */ 44498c2ecf20Sopenharmony_ci /* r2 = src address within NVRAM */ 44508c2ecf20Sopenharmony_ci /* r3 = length */ 44518c2ecf20Sopenharmony_ciNVRAM_XFER: 44528c2ecf20Sopenharmony_ci stmdb sp!, {r4, r5, lr} 44538c2ecf20Sopenharmony_ci mov r5, r0 /* save r0 (dest address) */ 44548c2ecf20Sopenharmony_ci mov r4, r3 /* save r3 (length) */ 44558c2ecf20Sopenharmony_ci mov r0, r2, LSR #5 /* SPI memories put A8 in the command field */ 44568c2ecf20Sopenharmony_ci and r0, r0, #8 44578c2ecf20Sopenharmony_ci add r0, r0, #NVRAM_CMD_READ 44588c2ecf20Sopenharmony_ci ldr r1, =NVRAM_SCRATCH 44598c2ecf20Sopenharmony_ci strb r0, [r1, #0] /* save command in NVRAM_SCRATCH[0] */ 44608c2ecf20Sopenharmony_ci strb r2, [r1, #1] /* save low byte of source address in NVRAM_SCRATCH[1] */ 44618c2ecf20Sopenharmony_ci_local1: 44628c2ecf20Sopenharmony_ci bl NVRAM_INIT 44638c2ecf20Sopenharmony_ci tst r0, #NVRAM_SR_RDY 44648c2ecf20Sopenharmony_ci bne _local1 44658c2ecf20Sopenharmony_ci mov r0, #20 44668c2ecf20Sopenharmony_ci bl DELAY9 44678c2ecf20Sopenharmony_ci mov r2, r4 /* length */ 44688c2ecf20Sopenharmony_ci mov r1, r5 /* dest address */ 44698c2ecf20Sopenharmony_ci mov r0, #2 /* bytes to transfer in command */ 44708c2ecf20Sopenharmony_ci bl NVRAM_XFER2 44718c2ecf20Sopenharmony_ci ldmia sp!, {r4, r5, lr} 44728c2ecf20Sopenharmony_ci bx lr 44738c2ecf20Sopenharmony_ci.endfunc 44748c2ecf20Sopenharmony_ci 44758c2ecf20Sopenharmony_ci.func NVRAM_Xfer2, NVRAM_XFER2 44768c2ecf20Sopenharmony_ciNVRAM_XFER2: 44778c2ecf20Sopenharmony_ci stmdb sp!, {r4, r5, r6, lr} 44788c2ecf20Sopenharmony_ci ldr r4, =SP_BASE 44798c2ecf20Sopenharmony_ci mov r3, #0 44808c2ecf20Sopenharmony_ci cmp r0, #0 44818c2ecf20Sopenharmony_ci bls _local2 44828c2ecf20Sopenharmony_ci ldr r5, =NVRAM_SCRATCH 44838c2ecf20Sopenharmony_ci_local4: 44848c2ecf20Sopenharmony_ci ldrb r6, [r5, r3] 44858c2ecf20Sopenharmony_ci str r6, [r4, #SP_TDR] 44868c2ecf20Sopenharmony_ci_local3: 44878c2ecf20Sopenharmony_ci ldr r6, [r4, #SP_SR] 44888c2ecf20Sopenharmony_ci tst r6, #SP_TDRE 44898c2ecf20Sopenharmony_ci beq _local3 44908c2ecf20Sopenharmony_ci add r3, r3, #1 44918c2ecf20Sopenharmony_ci cmp r3, r0 /* r0 is # of bytes to send out (command+addr) */ 44928c2ecf20Sopenharmony_ci blo _local4 44938c2ecf20Sopenharmony_ci_local2: 44948c2ecf20Sopenharmony_ci mov r3, #SPI_8CLOCKS 44958c2ecf20Sopenharmony_ci str r3, [r4, #SP_TDR] 44968c2ecf20Sopenharmony_ci ldr r0, [r4, #SP_RDR] 44978c2ecf20Sopenharmony_ci_local5: 44988c2ecf20Sopenharmony_ci ldr r0, [r4, #SP_SR] 44998c2ecf20Sopenharmony_ci tst r0, #SP_RDRF 45008c2ecf20Sopenharmony_ci beq _local5 45018c2ecf20Sopenharmony_ci ldr r0, [r4, #SP_RDR] /* what's this byte? It's the byte read while writing the TDR -- nonsense, because the NVRAM doesn't read and write at the same time */ 45028c2ecf20Sopenharmony_ci mov r0, #0 45038c2ecf20Sopenharmony_ci cmp r2, #0 /* r2 is # of bytes to copy in */ 45048c2ecf20Sopenharmony_ci bls _local6 45058c2ecf20Sopenharmony_ci_local7: 45068c2ecf20Sopenharmony_ci ldr r5, [r4, #SP_SR] 45078c2ecf20Sopenharmony_ci tst r5, #SP_TDRE 45088c2ecf20Sopenharmony_ci beq _local7 45098c2ecf20Sopenharmony_ci str r3, [r4, #SP_TDR] /* r3 has SPI_8CLOCKS */ 45108c2ecf20Sopenharmony_ci_local8: 45118c2ecf20Sopenharmony_ci ldr r5, [r4, #SP_SR] 45128c2ecf20Sopenharmony_ci tst r5, #SP_RDRF 45138c2ecf20Sopenharmony_ci beq _local8 45148c2ecf20Sopenharmony_ci ldr r5, [r4, #SP_RDR] /* but didn't we read this byte above? */ 45158c2ecf20Sopenharmony_ci strb r5, [r1], #1 /* postindexed */ 45168c2ecf20Sopenharmony_ci add r0, r0, #1 45178c2ecf20Sopenharmony_ci cmp r0, r2 45188c2ecf20Sopenharmony_ci blo _local7 /* since we don't send another address, the NVRAM must be capable of sequential reads */ 45198c2ecf20Sopenharmony_ci_local6: 45208c2ecf20Sopenharmony_ci mov r0, #200 45218c2ecf20Sopenharmony_ci bl DELAY9 45228c2ecf20Sopenharmony_ci ldmia sp!, {r4, r5, r6, lr} 45238c2ecf20Sopenharmony_ci bx lr 45248c2ecf20Sopenharmony_ci#endif 4525