162306a36Sopenharmony_ci/*** -*- linux-c -*- ********************************************************** 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci Copyright 2000-2001 ATMEL Corporation. 662306a36Sopenharmony_ci Copyright 2003-2004 Simon Kelley. 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci This code was developed from version 2.1.1 of the Atmel drivers, 962306a36Sopenharmony_ci released by Atmel corp. under the GPL in December 2002. It also 1062306a36Sopenharmony_ci includes code from the Linux aironet drivers (C) Benjamin Reed, 1162306a36Sopenharmony_ci and the Linux PCMCIA package, (C) David Hinds and the Linux wireless 1262306a36Sopenharmony_ci extensions, (C) Jean Tourrilhes. 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci The firmware module for reading the MAC address of the card comes from 1562306a36Sopenharmony_ci net.russotto.AtmelMACFW, written by Matthew T. Russotto and copyright 1662306a36Sopenharmony_ci by him. net.russotto.AtmelMACFW is used under the GPL license version 2. 1762306a36Sopenharmony_ci This file contains the module in binary form and, under the terms 1862306a36Sopenharmony_ci of the GPL, in source form. The source is located at the end of the file. 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci This program is free software; you can redistribute it and/or modify 2162306a36Sopenharmony_ci it under the terms of the GNU General Public License as published by 2262306a36Sopenharmony_ci the Free Software Foundation; either version 2 of the License, or 2362306a36Sopenharmony_ci (at your option) any later version. 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci This software is distributed in the hope that it will be useful, 2662306a36Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 2762306a36Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 2862306a36Sopenharmony_ci GNU General Public License for more details. 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci You should have received a copy of the GNU General Public License 3162306a36Sopenharmony_ci along with Atmel wireless lan drivers; if not, see 3262306a36Sopenharmony_ci <http://www.gnu.org/licenses/>. 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci For all queries about this code, please contact the current author, 3562306a36Sopenharmony_ci Simon Kelley <simon@thekelleys.org.uk> and not Atmel Corporation. 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci Credit is due to HP UK and Cambridge Online Systems Ltd for supplying 3862306a36Sopenharmony_ci hardware used during development of this driver. 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci******************************************************************************/ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include <linux/interrupt.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include <linux/kernel.h> 4562306a36Sopenharmony_ci#include <linux/ptrace.h> 4662306a36Sopenharmony_ci#include <linux/slab.h> 4762306a36Sopenharmony_ci#include <linux/string.h> 4862306a36Sopenharmony_ci#include <linux/timer.h> 4962306a36Sopenharmony_ci#include <asm/byteorder.h> 5062306a36Sopenharmony_ci#include <asm/io.h> 5162306a36Sopenharmony_ci#include <linux/uaccess.h> 5262306a36Sopenharmony_ci#include <linux/module.h> 5362306a36Sopenharmony_ci#include <linux/netdevice.h> 5462306a36Sopenharmony_ci#include <linux/etherdevice.h> 5562306a36Sopenharmony_ci#include <linux/skbuff.h> 5662306a36Sopenharmony_ci#include <linux/if_arp.h> 5762306a36Sopenharmony_ci#include <linux/ioport.h> 5862306a36Sopenharmony_ci#include <linux/fcntl.h> 5962306a36Sopenharmony_ci#include <linux/delay.h> 6062306a36Sopenharmony_ci#include <linux/wireless.h> 6162306a36Sopenharmony_ci#include <net/iw_handler.h> 6262306a36Sopenharmony_ci#include <linux/crc32.h> 6362306a36Sopenharmony_ci#include <linux/proc_fs.h> 6462306a36Sopenharmony_ci#include <linux/seq_file.h> 6562306a36Sopenharmony_ci#include <linux/device.h> 6662306a36Sopenharmony_ci#include <linux/moduleparam.h> 6762306a36Sopenharmony_ci#include <linux/firmware.h> 6862306a36Sopenharmony_ci#include <linux/jiffies.h> 6962306a36Sopenharmony_ci#include <net/cfg80211.h> 7062306a36Sopenharmony_ci#include "atmel.h" 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define DRIVER_MAJOR 0 7362306a36Sopenharmony_ci#define DRIVER_MINOR 98 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciMODULE_AUTHOR("Simon Kelley"); 7662306a36Sopenharmony_ciMODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); 7762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* The name of the firmware file to be loaded 8062306a36Sopenharmony_ci over-rides any automatic selection */ 8162306a36Sopenharmony_cistatic char *firmware = NULL; 8262306a36Sopenharmony_cimodule_param(firmware, charp, 0); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* table of firmware file names */ 8562306a36Sopenharmony_cistatic struct { 8662306a36Sopenharmony_ci AtmelFWType fw_type; 8762306a36Sopenharmony_ci const char *fw_file; 8862306a36Sopenharmony_ci const char *fw_file_ext; 8962306a36Sopenharmony_ci} fw_table[] = { 9062306a36Sopenharmony_ci { ATMEL_FW_TYPE_502, "atmel_at76c502", "bin" }, 9162306a36Sopenharmony_ci { ATMEL_FW_TYPE_502D, "atmel_at76c502d", "bin" }, 9262306a36Sopenharmony_ci { ATMEL_FW_TYPE_502E, "atmel_at76c502e", "bin" }, 9362306a36Sopenharmony_ci { ATMEL_FW_TYPE_502_3COM, "atmel_at76c502_3com", "bin" }, 9462306a36Sopenharmony_ci { ATMEL_FW_TYPE_504, "atmel_at76c504", "bin" }, 9562306a36Sopenharmony_ci { ATMEL_FW_TYPE_504_2958, "atmel_at76c504_2958", "bin" }, 9662306a36Sopenharmony_ci { ATMEL_FW_TYPE_504A_2958, "atmel_at76c504a_2958", "bin" }, 9762306a36Sopenharmony_ci { ATMEL_FW_TYPE_506, "atmel_at76c506", "bin" }, 9862306a36Sopenharmony_ci { ATMEL_FW_TYPE_NONE, NULL, NULL } 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502-wpa.bin"); 10162306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502.bin"); 10262306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502d-wpa.bin"); 10362306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502d.bin"); 10462306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502e-wpa.bin"); 10562306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502e.bin"); 10662306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin"); 10762306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c502_3com.bin"); 10862306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504-wpa.bin"); 10962306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504.bin"); 11062306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin"); 11162306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504_2958.bin"); 11262306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin"); 11362306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c504a_2958.bin"); 11462306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c506-wpa.bin"); 11562306a36Sopenharmony_ciMODULE_FIRMWARE("atmel_at76c506.bin"); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define MAX_SSID_LENGTH 32 11862306a36Sopenharmony_ci#define MGMT_JIFFIES (256 * HZ / 100) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define MAX_BSS_ENTRIES 64 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* registers */ 12362306a36Sopenharmony_ci#define GCR 0x00 /* (SIR0) General Configuration Register */ 12462306a36Sopenharmony_ci#define BSR 0x02 /* (SIR1) Bank Switching Select Register */ 12562306a36Sopenharmony_ci#define AR 0x04 12662306a36Sopenharmony_ci#define DR 0x08 12762306a36Sopenharmony_ci#define MR1 0x12 /* Mirror Register 1 */ 12862306a36Sopenharmony_ci#define MR2 0x14 /* Mirror Register 2 */ 12962306a36Sopenharmony_ci#define MR3 0x16 /* Mirror Register 3 */ 13062306a36Sopenharmony_ci#define MR4 0x18 /* Mirror Register 4 */ 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define GPR1 0x0c 13362306a36Sopenharmony_ci#define GPR2 0x0e 13462306a36Sopenharmony_ci#define GPR3 0x10 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * Constants for the GCR register. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci#define GCR_REMAP 0x0400 /* Remap internal SRAM to 0 */ 13962306a36Sopenharmony_ci#define GCR_SWRES 0x0080 /* BIU reset (ARM and PAI are NOT reset) */ 14062306a36Sopenharmony_ci#define GCR_CORES 0x0060 /* Core Reset (ARM and PAI are reset) */ 14162306a36Sopenharmony_ci#define GCR_ENINT 0x0002 /* Enable Interrupts */ 14262306a36Sopenharmony_ci#define GCR_ACKINT 0x0008 /* Acknowledge Interrupts */ 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#define BSS_SRAM 0x0200 /* AMBA module selection --> SRAM */ 14562306a36Sopenharmony_ci#define BSS_IRAM 0x0100 /* AMBA module selection --> IRAM */ 14662306a36Sopenharmony_ci/* 14762306a36Sopenharmony_ci *Constants for the MR registers. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci#define MAC_INIT_COMPLETE 0x0001 /* MAC init has been completed */ 15062306a36Sopenharmony_ci#define MAC_BOOT_COMPLETE 0x0010 /* MAC boot has been completed */ 15162306a36Sopenharmony_ci#define MAC_INIT_OK 0x0002 /* MAC boot has been completed */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#define MIB_MAX_DATA_BYTES 212 15462306a36Sopenharmony_ci#define MIB_HEADER_SIZE 4 /* first four fields */ 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistruct get_set_mib { 15762306a36Sopenharmony_ci u8 type; 15862306a36Sopenharmony_ci u8 size; 15962306a36Sopenharmony_ci u8 index; 16062306a36Sopenharmony_ci u8 reserved; 16162306a36Sopenharmony_ci u8 data[MIB_MAX_DATA_BYTES]; 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistruct rx_desc { 16562306a36Sopenharmony_ci u32 Next; 16662306a36Sopenharmony_ci u16 MsduPos; 16762306a36Sopenharmony_ci u16 MsduSize; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci u8 State; 17062306a36Sopenharmony_ci u8 Status; 17162306a36Sopenharmony_ci u8 Rate; 17262306a36Sopenharmony_ci u8 Rssi; 17362306a36Sopenharmony_ci u8 LinkQuality; 17462306a36Sopenharmony_ci u8 PreambleType; 17562306a36Sopenharmony_ci u16 Duration; 17662306a36Sopenharmony_ci u32 RxTime; 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#define RX_DESC_FLAG_VALID 0x80 18062306a36Sopenharmony_ci#define RX_DESC_FLAG_CONSUMED 0x40 18162306a36Sopenharmony_ci#define RX_DESC_FLAG_IDLE 0x00 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci#define RX_STATUS_SUCCESS 0x00 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci#define RX_DESC_MSDU_POS_OFFSET 4 18662306a36Sopenharmony_ci#define RX_DESC_MSDU_SIZE_OFFSET 6 18762306a36Sopenharmony_ci#define RX_DESC_FLAGS_OFFSET 8 18862306a36Sopenharmony_ci#define RX_DESC_STATUS_OFFSET 9 18962306a36Sopenharmony_ci#define RX_DESC_RSSI_OFFSET 11 19062306a36Sopenharmony_ci#define RX_DESC_LINK_QUALITY_OFFSET 12 19162306a36Sopenharmony_ci#define RX_DESC_PREAMBLE_TYPE_OFFSET 13 19262306a36Sopenharmony_ci#define RX_DESC_DURATION_OFFSET 14 19362306a36Sopenharmony_ci#define RX_DESC_RX_TIME_OFFSET 16 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistruct tx_desc { 19662306a36Sopenharmony_ci u32 NextDescriptor; 19762306a36Sopenharmony_ci u16 TxStartOfFrame; 19862306a36Sopenharmony_ci u16 TxLength; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci u8 TxState; 20162306a36Sopenharmony_ci u8 TxStatus; 20262306a36Sopenharmony_ci u8 RetryCount; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci u8 TxRate; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci u8 KeyIndex; 20762306a36Sopenharmony_ci u8 ChiperType; 20862306a36Sopenharmony_ci u8 ChipreLength; 20962306a36Sopenharmony_ci u8 Reserved1; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci u8 Reserved; 21262306a36Sopenharmony_ci u8 PacketType; 21362306a36Sopenharmony_ci u16 HostTxLength; 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci#define TX_DESC_NEXT_OFFSET 0 21762306a36Sopenharmony_ci#define TX_DESC_POS_OFFSET 4 21862306a36Sopenharmony_ci#define TX_DESC_SIZE_OFFSET 6 21962306a36Sopenharmony_ci#define TX_DESC_FLAGS_OFFSET 8 22062306a36Sopenharmony_ci#define TX_DESC_STATUS_OFFSET 9 22162306a36Sopenharmony_ci#define TX_DESC_RETRY_OFFSET 10 22262306a36Sopenharmony_ci#define TX_DESC_RATE_OFFSET 11 22362306a36Sopenharmony_ci#define TX_DESC_KEY_INDEX_OFFSET 12 22462306a36Sopenharmony_ci#define TX_DESC_CIPHER_TYPE_OFFSET 13 22562306a36Sopenharmony_ci#define TX_DESC_CIPHER_LENGTH_OFFSET 14 22662306a36Sopenharmony_ci#define TX_DESC_PACKET_TYPE_OFFSET 17 22762306a36Sopenharmony_ci#define TX_DESC_HOST_LENGTH_OFFSET 18 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* 23062306a36Sopenharmony_ci * Host-MAC interface 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci#define TX_STATUS_SUCCESS 0x00 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci#define TX_FIRM_OWN 0x80 23662306a36Sopenharmony_ci#define TX_DONE 0x40 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci#define TX_ERROR 0x01 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci#define TX_PACKET_TYPE_DATA 0x01 24162306a36Sopenharmony_ci#define TX_PACKET_TYPE_MGMT 0x02 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci#define ISR_EMPTY 0x00 /* no bits set in ISR */ 24462306a36Sopenharmony_ci#define ISR_TxCOMPLETE 0x01 /* packet transmitted */ 24562306a36Sopenharmony_ci#define ISR_RxCOMPLETE 0x02 /* packet received */ 24662306a36Sopenharmony_ci#define ISR_RxFRAMELOST 0x04 /* Rx Frame lost */ 24762306a36Sopenharmony_ci#define ISR_FATAL_ERROR 0x08 /* Fatal error */ 24862306a36Sopenharmony_ci#define ISR_COMMAND_COMPLETE 0x10 /* command completed */ 24962306a36Sopenharmony_ci#define ISR_OUT_OF_RANGE 0x20 /* command completed */ 25062306a36Sopenharmony_ci#define ISR_IBSS_MERGE 0x40 /* (4.1.2.30): IBSS merge */ 25162306a36Sopenharmony_ci#define ISR_GENERIC_IRQ 0x80 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci#define Local_Mib_Type 0x01 25462306a36Sopenharmony_ci#define Mac_Address_Mib_Type 0x02 25562306a36Sopenharmony_ci#define Mac_Mib_Type 0x03 25662306a36Sopenharmony_ci#define Statistics_Mib_Type 0x04 25762306a36Sopenharmony_ci#define Mac_Mgmt_Mib_Type 0x05 25862306a36Sopenharmony_ci#define Mac_Wep_Mib_Type 0x06 25962306a36Sopenharmony_ci#define Phy_Mib_Type 0x07 26062306a36Sopenharmony_ci#define Multi_Domain_MIB 0x08 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci#define MAC_MGMT_MIB_CUR_BSSID_POS 14 26362306a36Sopenharmony_ci#define MAC_MIB_FRAG_THRESHOLD_POS 8 26462306a36Sopenharmony_ci#define MAC_MIB_RTS_THRESHOLD_POS 10 26562306a36Sopenharmony_ci#define MAC_MIB_SHORT_RETRY_POS 16 26662306a36Sopenharmony_ci#define MAC_MIB_LONG_RETRY_POS 17 26762306a36Sopenharmony_ci#define MAC_MIB_SHORT_RETRY_LIMIT_POS 16 26862306a36Sopenharmony_ci#define MAC_MGMT_MIB_BEACON_PER_POS 0 26962306a36Sopenharmony_ci#define MAC_MGMT_MIB_STATION_ID_POS 6 27062306a36Sopenharmony_ci#define MAC_MGMT_MIB_CUR_PRIVACY_POS 11 27162306a36Sopenharmony_ci#define MAC_MGMT_MIB_CUR_BSSID_POS 14 27262306a36Sopenharmony_ci#define MAC_MGMT_MIB_PS_MODE_POS 53 27362306a36Sopenharmony_ci#define MAC_MGMT_MIB_LISTEN_INTERVAL_POS 54 27462306a36Sopenharmony_ci#define MAC_MGMT_MIB_MULTI_DOMAIN_IMPLEMENTED 56 27562306a36Sopenharmony_ci#define MAC_MGMT_MIB_MULTI_DOMAIN_ENABLED 57 27662306a36Sopenharmony_ci#define PHY_MIB_CHANNEL_POS 14 27762306a36Sopenharmony_ci#define PHY_MIB_RATE_SET_POS 20 27862306a36Sopenharmony_ci#define PHY_MIB_REG_DOMAIN_POS 26 27962306a36Sopenharmony_ci#define LOCAL_MIB_AUTO_TX_RATE_POS 3 28062306a36Sopenharmony_ci#define LOCAL_MIB_SSID_SIZE 5 28162306a36Sopenharmony_ci#define LOCAL_MIB_TX_PROMISCUOUS_POS 6 28262306a36Sopenharmony_ci#define LOCAL_MIB_TX_MGMT_RATE_POS 7 28362306a36Sopenharmony_ci#define LOCAL_MIB_TX_CONTROL_RATE_POS 8 28462306a36Sopenharmony_ci#define LOCAL_MIB_PREAMBLE_TYPE 9 28562306a36Sopenharmony_ci#define MAC_ADDR_MIB_MAC_ADDR_POS 0 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci#define CMD_Set_MIB_Vars 0x01 28862306a36Sopenharmony_ci#define CMD_Get_MIB_Vars 0x02 28962306a36Sopenharmony_ci#define CMD_Scan 0x03 29062306a36Sopenharmony_ci#define CMD_Join 0x04 29162306a36Sopenharmony_ci#define CMD_Start 0x05 29262306a36Sopenharmony_ci#define CMD_EnableRadio 0x06 29362306a36Sopenharmony_ci#define CMD_DisableRadio 0x07 29462306a36Sopenharmony_ci#define CMD_SiteSurvey 0x0B 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci#define CMD_STATUS_IDLE 0x00 29762306a36Sopenharmony_ci#define CMD_STATUS_COMPLETE 0x01 29862306a36Sopenharmony_ci#define CMD_STATUS_UNKNOWN 0x02 29962306a36Sopenharmony_ci#define CMD_STATUS_INVALID_PARAMETER 0x03 30062306a36Sopenharmony_ci#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 30162306a36Sopenharmony_ci#define CMD_STATUS_TIME_OUT 0x07 30262306a36Sopenharmony_ci#define CMD_STATUS_IN_PROGRESS 0x08 30362306a36Sopenharmony_ci#define CMD_STATUS_REJECTED_RADIO_OFF 0x09 30462306a36Sopenharmony_ci#define CMD_STATUS_HOST_ERROR 0xFF 30562306a36Sopenharmony_ci#define CMD_STATUS_BUSY 0xFE 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci#define CMD_BLOCK_COMMAND_OFFSET 0 30862306a36Sopenharmony_ci#define CMD_BLOCK_STATUS_OFFSET 1 30962306a36Sopenharmony_ci#define CMD_BLOCK_PARAMETERS_OFFSET 4 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci#define SCAN_OPTIONS_SITE_SURVEY 0x80 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci#define MGMT_FRAME_BODY_OFFSET 24 31462306a36Sopenharmony_ci#define MAX_AUTHENTICATION_RETRIES 3 31562306a36Sopenharmony_ci#define MAX_ASSOCIATION_RETRIES 3 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci#define AUTHENTICATION_RESPONSE_TIME_OUT 1000 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci#define MAX_WIRELESS_BODY 2316 /* mtu is 2312, CRC is 4 */ 32062306a36Sopenharmony_ci#define LOOP_RETRY_LIMIT 500000 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci#define ACTIVE_MODE 1 32362306a36Sopenharmony_ci#define PS_MODE 2 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci#define MAX_ENCRYPTION_KEYS 4 32662306a36Sopenharmony_ci#define MAX_ENCRYPTION_KEY_SIZE 40 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/* 32962306a36Sopenharmony_ci * 802.11 related definitions 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* 33362306a36Sopenharmony_ci * Regulatory Domains 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci#define REG_DOMAIN_FCC 0x10 /* Channels 1-11 USA */ 33762306a36Sopenharmony_ci#define REG_DOMAIN_DOC 0x20 /* Channel 1-11 Canada */ 33862306a36Sopenharmony_ci#define REG_DOMAIN_ETSI 0x30 /* Channel 1-13 Europe (ex Spain/France) */ 33962306a36Sopenharmony_ci#define REG_DOMAIN_SPAIN 0x31 /* Channel 10-11 Spain */ 34062306a36Sopenharmony_ci#define REG_DOMAIN_FRANCE 0x32 /* Channel 10-13 France */ 34162306a36Sopenharmony_ci#define REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */ 34262306a36Sopenharmony_ci#define REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan(MKK1) */ 34362306a36Sopenharmony_ci#define REG_DOMAIN_ISRAEL 0x50 /* Channel 3-9 ISRAEL */ 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci#define BSS_TYPE_AD_HOC 1 34662306a36Sopenharmony_ci#define BSS_TYPE_INFRASTRUCTURE 2 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci#define SCAN_TYPE_ACTIVE 0 34962306a36Sopenharmony_ci#define SCAN_TYPE_PASSIVE 1 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci#define LONG_PREAMBLE 0 35262306a36Sopenharmony_ci#define SHORT_PREAMBLE 1 35362306a36Sopenharmony_ci#define AUTO_PREAMBLE 2 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci#define DATA_FRAME_WS_HEADER_SIZE 30 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/* promiscuous mode control */ 35862306a36Sopenharmony_ci#define PROM_MODE_OFF 0x0 35962306a36Sopenharmony_ci#define PROM_MODE_UNKNOWN 0x1 36062306a36Sopenharmony_ci#define PROM_MODE_CRC_FAILED 0x2 36162306a36Sopenharmony_ci#define PROM_MODE_DUPLICATED 0x4 36262306a36Sopenharmony_ci#define PROM_MODE_MGMT 0x8 36362306a36Sopenharmony_ci#define PROM_MODE_CTRL 0x10 36462306a36Sopenharmony_ci#define PROM_MODE_BAD_PROTOCOL 0x20 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci#define IFACE_INT_STATUS_OFFSET 0 36762306a36Sopenharmony_ci#define IFACE_INT_MASK_OFFSET 1 36862306a36Sopenharmony_ci#define IFACE_LOCKOUT_HOST_OFFSET 2 36962306a36Sopenharmony_ci#define IFACE_LOCKOUT_MAC_OFFSET 3 37062306a36Sopenharmony_ci#define IFACE_FUNC_CTRL_OFFSET 28 37162306a36Sopenharmony_ci#define IFACE_MAC_STAT_OFFSET 30 37262306a36Sopenharmony_ci#define IFACE_GENERIC_INT_TYPE_OFFSET 32 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci#define CIPHER_SUITE_NONE 0 37562306a36Sopenharmony_ci#define CIPHER_SUITE_WEP_64 1 37662306a36Sopenharmony_ci#define CIPHER_SUITE_TKIP 2 37762306a36Sopenharmony_ci#define CIPHER_SUITE_AES 3 37862306a36Sopenharmony_ci#define CIPHER_SUITE_CCX 4 37962306a36Sopenharmony_ci#define CIPHER_SUITE_WEP_128 5 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/* 38262306a36Sopenharmony_ci * IFACE MACROS & definitions 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/* 38662306a36Sopenharmony_ci * FuncCtrl field: 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci#define FUNC_CTRL_TxENABLE 0x10 38962306a36Sopenharmony_ci#define FUNC_CTRL_RxENABLE 0x20 39062306a36Sopenharmony_ci#define FUNC_CTRL_INIT_COMPLETE 0x01 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* A stub firmware image which reads the MAC address from NVRAM on the card. 39362306a36Sopenharmony_ci For copyright information and source see the end of this file. */ 39462306a36Sopenharmony_cistatic u8 mac_reader[] = { 39562306a36Sopenharmony_ci 0x06, 0x00, 0x00, 0xea, 0x04, 0x00, 0x00, 0xea, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xea, 39662306a36Sopenharmony_ci 0x01, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 39762306a36Sopenharmony_ci 0xd3, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x21, 0xe1, 0x0e, 0x04, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 39862306a36Sopenharmony_ci 0x81, 0x11, 0xa0, 0xe1, 0x00, 0x10, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x1c, 0x10, 0x90, 0xe5, 39962306a36Sopenharmony_ci 0x10, 0x10, 0xc1, 0xe3, 0x1c, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 0x08, 0x10, 0x80, 0xe5, 40062306a36Sopenharmony_ci 0x02, 0x03, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0xb0, 0x10, 0xc0, 0xe1, 0xb4, 0x10, 0xc0, 0xe1, 40162306a36Sopenharmony_ci 0xb8, 0x10, 0xc0, 0xe1, 0xbc, 0x10, 0xc0, 0xe1, 0x56, 0xdc, 0xa0, 0xe3, 0x21, 0x00, 0x00, 0xeb, 40262306a36Sopenharmony_ci 0x0a, 0x00, 0xa0, 0xe3, 0x1a, 0x00, 0x00, 0xeb, 0x10, 0x00, 0x00, 0xeb, 0x07, 0x00, 0x00, 0xeb, 40362306a36Sopenharmony_ci 0x02, 0x03, 0xa0, 0xe3, 0x02, 0x14, 0xa0, 0xe3, 0xb4, 0x10, 0xc0, 0xe1, 0x4c, 0x10, 0x9f, 0xe5, 40462306a36Sopenharmony_ci 0xbc, 0x10, 0xc0, 0xe1, 0x10, 0x10, 0xa0, 0xe3, 0xb8, 0x10, 0xc0, 0xe1, 0xfe, 0xff, 0xff, 0xea, 40562306a36Sopenharmony_ci 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe3, 0x02, 0x3c, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 40662306a36Sopenharmony_ci 0x28, 0x00, 0x9f, 0xe5, 0x37, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 40762306a36Sopenharmony_ci 0x00, 0x40, 0x2d, 0xe9, 0x12, 0x2e, 0xa0, 0xe3, 0x06, 0x30, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 40862306a36Sopenharmony_ci 0x02, 0x04, 0xa0, 0xe3, 0x2f, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 40962306a36Sopenharmony_ci 0x00, 0x02, 0x00, 0x02, 0x80, 0x01, 0x90, 0xe0, 0x01, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x50, 0xe2, 41062306a36Sopenharmony_ci 0xfc, 0xff, 0xff, 0xea, 0x1e, 0xff, 0x2f, 0xe1, 0x80, 0x10, 0xa0, 0xe3, 0xf3, 0x06, 0xa0, 0xe3, 41162306a36Sopenharmony_ci 0x00, 0x10, 0x80, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 41262306a36Sopenharmony_ci 0x04, 0x10, 0x80, 0xe5, 0x00, 0x10, 0x80, 0xe5, 0x0e, 0x34, 0xa0, 0xe3, 0x1c, 0x10, 0x93, 0xe5, 41362306a36Sopenharmony_ci 0x02, 0x1a, 0x81, 0xe3, 0x1c, 0x10, 0x83, 0xe5, 0x58, 0x11, 0x9f, 0xe5, 0x30, 0x10, 0x80, 0xe5, 41462306a36Sopenharmony_ci 0x54, 0x11, 0x9f, 0xe5, 0x34, 0x10, 0x80, 0xe5, 0x38, 0x10, 0x80, 0xe5, 0x3c, 0x10, 0x80, 0xe5, 41562306a36Sopenharmony_ci 0x10, 0x10, 0x90, 0xe5, 0x08, 0x00, 0x90, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0xf3, 0x16, 0xa0, 0xe3, 41662306a36Sopenharmony_ci 0x08, 0x00, 0x91, 0xe5, 0x05, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 0x10, 0x00, 0x91, 0xe5, 41762306a36Sopenharmony_ci 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0xff, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 41862306a36Sopenharmony_ci 0x10, 0x00, 0x91, 0xe5, 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, 41962306a36Sopenharmony_ci 0x10, 0x00, 0x91, 0xe5, 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, 42062306a36Sopenharmony_ci 0xff, 0x00, 0x00, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x30, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe1, 42162306a36Sopenharmony_ci 0x03, 0x40, 0xa0, 0xe1, 0xa2, 0x02, 0xa0, 0xe1, 0x08, 0x00, 0x00, 0xe2, 0x03, 0x00, 0x80, 0xe2, 42262306a36Sopenharmony_ci 0xd8, 0x10, 0x9f, 0xe5, 0x00, 0x00, 0xc1, 0xe5, 0x01, 0x20, 0xc1, 0xe5, 0xe2, 0xff, 0xff, 0xeb, 42362306a36Sopenharmony_ci 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x14, 0x00, 0xa0, 0xe3, 0xc4, 0xff, 0xff, 0xeb, 42462306a36Sopenharmony_ci 0x04, 0x20, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1, 0x02, 0x00, 0xa0, 0xe3, 0x01, 0x00, 0x00, 0xeb, 42562306a36Sopenharmony_ci 0x30, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x70, 0x40, 0x2d, 0xe9, 0xf3, 0x46, 0xa0, 0xe3, 42662306a36Sopenharmony_ci 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x08, 0x00, 0x00, 0x9a, 0x8c, 0x50, 0x9f, 0xe5, 42762306a36Sopenharmony_ci 0x03, 0x60, 0xd5, 0xe7, 0x0c, 0x60, 0x84, 0xe5, 0x10, 0x60, 0x94, 0xe5, 0x02, 0x00, 0x16, 0xe3, 42862306a36Sopenharmony_ci 0xfc, 0xff, 0xff, 0x0a, 0x01, 0x30, 0x83, 0xe2, 0x00, 0x00, 0x53, 0xe1, 0xf7, 0xff, 0xff, 0x3a, 42962306a36Sopenharmony_ci 0xff, 0x30, 0xa0, 0xe3, 0x0c, 0x30, 0x84, 0xe5, 0x08, 0x00, 0x94, 0xe5, 0x10, 0x00, 0x94, 0xe5, 43062306a36Sopenharmony_ci 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x94, 0xe5, 0x00, 0x00, 0xa0, 0xe3, 43162306a36Sopenharmony_ci 0x00, 0x00, 0x52, 0xe3, 0x0b, 0x00, 0x00, 0x9a, 0x10, 0x50, 0x94, 0xe5, 0x02, 0x00, 0x15, 0xe3, 43262306a36Sopenharmony_ci 0xfc, 0xff, 0xff, 0x0a, 0x0c, 0x30, 0x84, 0xe5, 0x10, 0x50, 0x94, 0xe5, 0x01, 0x00, 0x15, 0xe3, 43362306a36Sopenharmony_ci 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x50, 0x94, 0xe5, 0x01, 0x50, 0xc1, 0xe4, 0x01, 0x00, 0x80, 0xe2, 43462306a36Sopenharmony_ci 0x02, 0x00, 0x50, 0xe1, 0xf3, 0xff, 0xff, 0x3a, 0xc8, 0x00, 0xa0, 0xe3, 0x98, 0xff, 0xff, 0xeb, 43562306a36Sopenharmony_ci 0x70, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x0c, 0x00, 0x02, 0x01, 0x02, 0x00, 0x02, 43662306a36Sopenharmony_ci 0x00, 0x01, 0x00, 0x02 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistruct atmel_private { 44062306a36Sopenharmony_ci void *card; /* Bus dependent structure varies for PCcard */ 44162306a36Sopenharmony_ci int (*present_callback)(void *); /* And callback which uses it */ 44262306a36Sopenharmony_ci char firmware_id[32]; 44362306a36Sopenharmony_ci AtmelFWType firmware_type; 44462306a36Sopenharmony_ci u8 *firmware; 44562306a36Sopenharmony_ci int firmware_length; 44662306a36Sopenharmony_ci struct timer_list management_timer; 44762306a36Sopenharmony_ci struct net_device *dev; 44862306a36Sopenharmony_ci struct device *sys_dev; 44962306a36Sopenharmony_ci struct iw_statistics wstats; 45062306a36Sopenharmony_ci spinlock_t irqlock, timerlock; /* spinlocks */ 45162306a36Sopenharmony_ci enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type; 45262306a36Sopenharmony_ci enum { 45362306a36Sopenharmony_ci CARD_TYPE_PARALLEL_FLASH, 45462306a36Sopenharmony_ci CARD_TYPE_SPI_FLASH, 45562306a36Sopenharmony_ci CARD_TYPE_EEPROM 45662306a36Sopenharmony_ci } card_type; 45762306a36Sopenharmony_ci int do_rx_crc; /* If we need to CRC incoming packets */ 45862306a36Sopenharmony_ci int probe_crc; /* set if we don't yet know */ 45962306a36Sopenharmony_ci int crc_ok_cnt, crc_ko_cnt; /* counters for probing */ 46062306a36Sopenharmony_ci u16 rx_desc_head; 46162306a36Sopenharmony_ci u16 tx_desc_free, tx_desc_head, tx_desc_tail, tx_desc_previous; 46262306a36Sopenharmony_ci u16 tx_free_mem, tx_buff_head, tx_buff_tail; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci u16 frag_seq, frag_len, frag_no; 46562306a36Sopenharmony_ci u8 frag_source[6]; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci u8 wep_is_on, default_key, exclude_unencrypted, encryption_level; 46862306a36Sopenharmony_ci u8 group_cipher_suite, pairwise_cipher_suite; 46962306a36Sopenharmony_ci u8 wep_keys[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; 47062306a36Sopenharmony_ci int wep_key_len[MAX_ENCRYPTION_KEYS]; 47162306a36Sopenharmony_ci int use_wpa, radio_on_broken; /* firmware dependent stuff. */ 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci u16 host_info_base; 47462306a36Sopenharmony_ci struct host_info_struct { 47562306a36Sopenharmony_ci /* NB this is matched to the hardware, don't change. */ 47662306a36Sopenharmony_ci u8 volatile int_status; 47762306a36Sopenharmony_ci u8 volatile int_mask; 47862306a36Sopenharmony_ci u8 volatile lockout_host; 47962306a36Sopenharmony_ci u8 volatile lockout_mac; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci u16 tx_buff_pos; 48262306a36Sopenharmony_ci u16 tx_buff_size; 48362306a36Sopenharmony_ci u16 tx_desc_pos; 48462306a36Sopenharmony_ci u16 tx_desc_count; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci u16 rx_buff_pos; 48762306a36Sopenharmony_ci u16 rx_buff_size; 48862306a36Sopenharmony_ci u16 rx_desc_pos; 48962306a36Sopenharmony_ci u16 rx_desc_count; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci u16 build_version; 49262306a36Sopenharmony_ci u16 command_pos; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci u16 major_version; 49562306a36Sopenharmony_ci u16 minor_version; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci u16 func_ctrl; 49862306a36Sopenharmony_ci u16 mac_status; 49962306a36Sopenharmony_ci u16 generic_IRQ_type; 50062306a36Sopenharmony_ci u8 reserved[2]; 50162306a36Sopenharmony_ci } host_info; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci enum { 50462306a36Sopenharmony_ci STATION_STATE_SCANNING, 50562306a36Sopenharmony_ci STATION_STATE_JOINNING, 50662306a36Sopenharmony_ci STATION_STATE_AUTHENTICATING, 50762306a36Sopenharmony_ci STATION_STATE_ASSOCIATING, 50862306a36Sopenharmony_ci STATION_STATE_READY, 50962306a36Sopenharmony_ci STATION_STATE_REASSOCIATING, 51062306a36Sopenharmony_ci STATION_STATE_DOWN, 51162306a36Sopenharmony_ci STATION_STATE_MGMT_ERROR 51262306a36Sopenharmony_ci } station_state; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci int operating_mode, power_mode; 51562306a36Sopenharmony_ci unsigned long last_qual; 51662306a36Sopenharmony_ci int beacons_this_sec; 51762306a36Sopenharmony_ci int channel; 51862306a36Sopenharmony_ci int reg_domain, config_reg_domain; 51962306a36Sopenharmony_ci int tx_rate; 52062306a36Sopenharmony_ci int auto_tx_rate; 52162306a36Sopenharmony_ci int rts_threshold; 52262306a36Sopenharmony_ci int frag_threshold; 52362306a36Sopenharmony_ci int long_retry, short_retry; 52462306a36Sopenharmony_ci int preamble; 52562306a36Sopenharmony_ci int default_beacon_period, beacon_period, listen_interval; 52662306a36Sopenharmony_ci int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum; 52762306a36Sopenharmony_ci int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt; 52862306a36Sopenharmony_ci enum { 52962306a36Sopenharmony_ci SITE_SURVEY_IDLE, 53062306a36Sopenharmony_ci SITE_SURVEY_IN_PROGRESS, 53162306a36Sopenharmony_ci SITE_SURVEY_COMPLETED 53262306a36Sopenharmony_ci } site_survey_state; 53362306a36Sopenharmony_ci unsigned long last_survey; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci int station_was_associated, station_is_associated; 53662306a36Sopenharmony_ci int fast_scan; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci struct bss_info { 53962306a36Sopenharmony_ci int channel; 54062306a36Sopenharmony_ci int SSIDsize; 54162306a36Sopenharmony_ci int RSSI; 54262306a36Sopenharmony_ci int UsingWEP; 54362306a36Sopenharmony_ci int preamble; 54462306a36Sopenharmony_ci int beacon_period; 54562306a36Sopenharmony_ci int BSStype; 54662306a36Sopenharmony_ci u8 BSSID[6]; 54762306a36Sopenharmony_ci u8 SSID[MAX_SSID_LENGTH]; 54862306a36Sopenharmony_ci } BSSinfo[MAX_BSS_ENTRIES]; 54962306a36Sopenharmony_ci int BSS_list_entries, current_BSS; 55062306a36Sopenharmony_ci int connect_to_any_BSS; 55162306a36Sopenharmony_ci int SSID_size, new_SSID_size; 55262306a36Sopenharmony_ci u8 CurrentBSSID[6], BSSID[6]; 55362306a36Sopenharmony_ci u8 SSID[MAX_SSID_LENGTH], new_SSID[MAX_SSID_LENGTH]; 55462306a36Sopenharmony_ci u64 last_beacon_timestamp; 55562306a36Sopenharmony_ci u8 rx_buf[MAX_WIRELESS_BODY]; 55662306a36Sopenharmony_ci}; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic u8 atmel_basic_rates[4] = {0x82, 0x84, 0x0b, 0x16}; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic const struct { 56162306a36Sopenharmony_ci int reg_domain; 56262306a36Sopenharmony_ci int min, max; 56362306a36Sopenharmony_ci char *name; 56462306a36Sopenharmony_ci} channel_table[] = { { REG_DOMAIN_FCC, 1, 11, "USA" }, 56562306a36Sopenharmony_ci { REG_DOMAIN_DOC, 1, 11, "Canada" }, 56662306a36Sopenharmony_ci { REG_DOMAIN_ETSI, 1, 13, "Europe" }, 56762306a36Sopenharmony_ci { REG_DOMAIN_SPAIN, 10, 11, "Spain" }, 56862306a36Sopenharmony_ci { REG_DOMAIN_FRANCE, 10, 13, "France" }, 56962306a36Sopenharmony_ci { REG_DOMAIN_MKK, 14, 14, "MKK" }, 57062306a36Sopenharmony_ci { REG_DOMAIN_MKK1, 1, 14, "MKK1" }, 57162306a36Sopenharmony_ci { REG_DOMAIN_ISRAEL, 3, 9, "Israel"} }; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic void build_wpa_mib(struct atmel_private *priv); 57462306a36Sopenharmony_cistatic int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); 57562306a36Sopenharmony_cistatic void atmel_copy_to_card(struct net_device *dev, u16 dest, 57662306a36Sopenharmony_ci const unsigned char *src, u16 len); 57762306a36Sopenharmony_cistatic void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, 57862306a36Sopenharmony_ci u16 src, u16 len); 57962306a36Sopenharmony_cistatic void atmel_set_gcr(struct net_device *dev, u16 mask); 58062306a36Sopenharmony_cistatic void atmel_clear_gcr(struct net_device *dev, u16 mask); 58162306a36Sopenharmony_cistatic int atmel_lock_mac(struct atmel_private *priv); 58262306a36Sopenharmony_cistatic void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data); 58362306a36Sopenharmony_cistatic void atmel_command_irq(struct atmel_private *priv); 58462306a36Sopenharmony_cistatic int atmel_validate_channel(struct atmel_private *priv, int channel); 58562306a36Sopenharmony_cistatic void atmel_management_frame(struct atmel_private *priv, 58662306a36Sopenharmony_ci struct ieee80211_hdr *header, 58762306a36Sopenharmony_ci u16 frame_len, u8 rssi); 58862306a36Sopenharmony_cistatic void atmel_management_timer(struct timer_list *t); 58962306a36Sopenharmony_cistatic void atmel_send_command(struct atmel_private *priv, int command, 59062306a36Sopenharmony_ci void *cmd, int cmd_size); 59162306a36Sopenharmony_cistatic int atmel_send_command_wait(struct atmel_private *priv, int command, 59262306a36Sopenharmony_ci void *cmd, int cmd_size); 59362306a36Sopenharmony_cistatic void atmel_transmit_management_frame(struct atmel_private *priv, 59462306a36Sopenharmony_ci struct ieee80211_hdr *header, 59562306a36Sopenharmony_ci u8 *body, int body_len); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index); 59862306a36Sopenharmony_cistatic void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, 59962306a36Sopenharmony_ci u8 data); 60062306a36Sopenharmony_cistatic void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, 60162306a36Sopenharmony_ci u16 data); 60262306a36Sopenharmony_cistatic void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, 60362306a36Sopenharmony_ci const u8 *data, int data_len); 60462306a36Sopenharmony_cistatic void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, 60562306a36Sopenharmony_ci u8 *data, int data_len); 60662306a36Sopenharmony_cistatic void atmel_scan(struct atmel_private *priv, int specific_ssid); 60762306a36Sopenharmony_cistatic void atmel_join_bss(struct atmel_private *priv, int bss_index); 60862306a36Sopenharmony_cistatic void atmel_smooth_qual(struct atmel_private *priv); 60962306a36Sopenharmony_cistatic void atmel_writeAR(struct net_device *dev, u16 data); 61062306a36Sopenharmony_cistatic int probe_atmel_card(struct net_device *dev); 61162306a36Sopenharmony_cistatic int reset_atmel_card(struct net_device *dev); 61262306a36Sopenharmony_cistatic void atmel_enter_state(struct atmel_private *priv, int new_state); 61362306a36Sopenharmony_ciint atmel_open (struct net_device *dev); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic inline u16 atmel_hi(struct atmel_private *priv, u16 offset) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci return priv->host_info_base + offset; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic inline u16 atmel_co(struct atmel_private *priv, u16 offset) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci return priv->host_info.command_pos + offset; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci return priv->host_info.rx_desc_pos + (sizeof(struct rx_desc) * desc) + offset; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci return priv->host_info.tx_desc_pos + (sizeof(struct tx_desc) * desc) + offset; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic inline u8 atmel_read8(struct net_device *dev, u16 offset) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci return inb(dev->base_addr + offset); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic inline void atmel_write8(struct net_device *dev, u16 offset, u8 data) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci outb(data, dev->base_addr + offset); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic inline u16 atmel_read16(struct net_device *dev, u16 offset) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci return inw(dev->base_addr + offset); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic inline void atmel_write16(struct net_device *dev, u16 offset, u16 data) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci outw(data, dev->base_addr + offset); 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic inline u8 atmel_rmem8(struct atmel_private *priv, u16 pos) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci atmel_writeAR(priv->dev, pos); 65862306a36Sopenharmony_ci return atmel_read8(priv->dev, DR); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci atmel_writeAR(priv->dev, pos); 66462306a36Sopenharmony_ci atmel_write8(priv->dev, DR, data); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci atmel_writeAR(priv->dev, pos); 67062306a36Sopenharmony_ci return atmel_read16(priv->dev, DR); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci atmel_writeAR(priv->dev, pos); 67662306a36Sopenharmony_ci atmel_write16(priv->dev, DR, data); 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic const struct iw_handler_def atmel_handler_def; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void tx_done_irq(struct atmel_private *priv) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci int i; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci for (i = 0; 68662306a36Sopenharmony_ci atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE && 68762306a36Sopenharmony_ci i < priv->host_info.tx_desc_count; 68862306a36Sopenharmony_ci i++) { 68962306a36Sopenharmony_ci u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head)); 69062306a36Sopenharmony_ci u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head)); 69162306a36Sopenharmony_ci u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head)); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head), 0); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci priv->tx_free_mem += msdu_size; 69662306a36Sopenharmony_ci priv->tx_desc_free++; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (priv->tx_buff_head + msdu_size > (priv->host_info.tx_buff_pos + priv->host_info.tx_buff_size)) 69962306a36Sopenharmony_ci priv->tx_buff_head = 0; 70062306a36Sopenharmony_ci else 70162306a36Sopenharmony_ci priv->tx_buff_head += msdu_size; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1)) 70462306a36Sopenharmony_ci priv->tx_desc_head++ ; 70562306a36Sopenharmony_ci else 70662306a36Sopenharmony_ci priv->tx_desc_head = 0; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (type == TX_PACKET_TYPE_DATA) { 70962306a36Sopenharmony_ci if (status == TX_STATUS_SUCCESS) 71062306a36Sopenharmony_ci priv->dev->stats.tx_packets++; 71162306a36Sopenharmony_ci else 71262306a36Sopenharmony_ci priv->dev->stats.tx_errors++; 71362306a36Sopenharmony_ci netif_wake_queue(priv->dev); 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic u16 find_tx_buff(struct atmel_private *priv, u16 len) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci u16 bottom_free = priv->host_info.tx_buff_size - priv->tx_buff_tail; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (priv->tx_desc_free == 3 || priv->tx_free_mem < len) 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (bottom_free >= len) 72662306a36Sopenharmony_ci return priv->host_info.tx_buff_pos + priv->tx_buff_tail; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (priv->tx_free_mem - bottom_free >= len) { 72962306a36Sopenharmony_ci priv->tx_buff_tail = 0; 73062306a36Sopenharmony_ci return priv->host_info.tx_buff_pos; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic void tx_update_descriptor(struct atmel_private *priv, int is_bcast, 73762306a36Sopenharmony_ci u16 len, u16 buff, u8 type) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, priv->tx_desc_tail), buff); 74062306a36Sopenharmony_ci atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_tail), len); 74162306a36Sopenharmony_ci if (!priv->use_wpa) 74262306a36Sopenharmony_ci atmel_wmem16(priv, atmel_tx(priv, TX_DESC_HOST_LENGTH_OFFSET, priv->tx_desc_tail), len); 74362306a36Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_tail), type); 74462306a36Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RATE_OFFSET, priv->tx_desc_tail), priv->tx_rate); 74562306a36Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RETRY_OFFSET, priv->tx_desc_tail), 0); 74662306a36Sopenharmony_ci if (priv->use_wpa) { 74762306a36Sopenharmony_ci int cipher_type, cipher_length; 74862306a36Sopenharmony_ci if (is_bcast) { 74962306a36Sopenharmony_ci cipher_type = priv->group_cipher_suite; 75062306a36Sopenharmony_ci if (cipher_type == CIPHER_SUITE_WEP_64 || 75162306a36Sopenharmony_ci cipher_type == CIPHER_SUITE_WEP_128) 75262306a36Sopenharmony_ci cipher_length = 8; 75362306a36Sopenharmony_ci else if (cipher_type == CIPHER_SUITE_TKIP) 75462306a36Sopenharmony_ci cipher_length = 12; 75562306a36Sopenharmony_ci else if (priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_64 || 75662306a36Sopenharmony_ci priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_128) { 75762306a36Sopenharmony_ci cipher_type = priv->pairwise_cipher_suite; 75862306a36Sopenharmony_ci cipher_length = 8; 75962306a36Sopenharmony_ci } else { 76062306a36Sopenharmony_ci cipher_type = CIPHER_SUITE_NONE; 76162306a36Sopenharmony_ci cipher_length = 0; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci } else { 76462306a36Sopenharmony_ci cipher_type = priv->pairwise_cipher_suite; 76562306a36Sopenharmony_ci if (cipher_type == CIPHER_SUITE_WEP_64 || 76662306a36Sopenharmony_ci cipher_type == CIPHER_SUITE_WEP_128) 76762306a36Sopenharmony_ci cipher_length = 8; 76862306a36Sopenharmony_ci else if (cipher_type == CIPHER_SUITE_TKIP) 76962306a36Sopenharmony_ci cipher_length = 12; 77062306a36Sopenharmony_ci else if (priv->group_cipher_suite == CIPHER_SUITE_WEP_64 || 77162306a36Sopenharmony_ci priv->group_cipher_suite == CIPHER_SUITE_WEP_128) { 77262306a36Sopenharmony_ci cipher_type = priv->group_cipher_suite; 77362306a36Sopenharmony_ci cipher_length = 8; 77462306a36Sopenharmony_ci } else { 77562306a36Sopenharmony_ci cipher_type = CIPHER_SUITE_NONE; 77662306a36Sopenharmony_ci cipher_length = 0; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail), 78162306a36Sopenharmony_ci cipher_type); 78262306a36Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_LENGTH_OFFSET, priv->tx_desc_tail), 78362306a36Sopenharmony_ci cipher_length); 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_tail), 0x80000000L); 78662306a36Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_tail), TX_FIRM_OWN); 78762306a36Sopenharmony_ci if (priv->tx_desc_previous != priv->tx_desc_tail) 78862306a36Sopenharmony_ci atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_previous), 0); 78962306a36Sopenharmony_ci priv->tx_desc_previous = priv->tx_desc_tail; 79062306a36Sopenharmony_ci if (priv->tx_desc_tail < (priv->host_info.tx_desc_count - 1)) 79162306a36Sopenharmony_ci priv->tx_desc_tail++; 79262306a36Sopenharmony_ci else 79362306a36Sopenharmony_ci priv->tx_desc_tail = 0; 79462306a36Sopenharmony_ci priv->tx_desc_free--; 79562306a36Sopenharmony_ci priv->tx_free_mem -= len; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 80162306a36Sopenharmony_ci struct ieee80211_hdr header; 80262306a36Sopenharmony_ci unsigned long flags; 80362306a36Sopenharmony_ci u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (priv->card && priv->present_callback && 80662306a36Sopenharmony_ci !(*priv->present_callback)(priv->card)) { 80762306a36Sopenharmony_ci dev->stats.tx_errors++; 80862306a36Sopenharmony_ci dev_kfree_skb(skb); 80962306a36Sopenharmony_ci return NETDEV_TX_OK; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (priv->station_state != STATION_STATE_READY) { 81362306a36Sopenharmony_ci dev->stats.tx_errors++; 81462306a36Sopenharmony_ci dev_kfree_skb(skb); 81562306a36Sopenharmony_ci return NETDEV_TX_OK; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* first ensure the timer func cannot run */ 81962306a36Sopenharmony_ci spin_lock_bh(&priv->timerlock); 82062306a36Sopenharmony_ci /* then stop the hardware ISR */ 82162306a36Sopenharmony_ci spin_lock_irqsave(&priv->irqlock, flags); 82262306a36Sopenharmony_ci /* nb doing the above in the opposite order will deadlock */ 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the 82562306a36Sopenharmony_ci 12 first bytes (containing DA/SA) and put them in the appropriate 82662306a36Sopenharmony_ci fields of the Wireless Header. Thus the packet length is then the 82762306a36Sopenharmony_ci initial + 18 (+30-12) */ 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if (!(buff = find_tx_buff(priv, len + 18))) { 83062306a36Sopenharmony_ci dev->stats.tx_dropped++; 83162306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 83262306a36Sopenharmony_ci spin_unlock_bh(&priv->timerlock); 83362306a36Sopenharmony_ci netif_stop_queue(dev); 83462306a36Sopenharmony_ci return NETDEV_TX_BUSY; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci frame_ctl = IEEE80211_FTYPE_DATA; 83862306a36Sopenharmony_ci header.duration_id = 0; 83962306a36Sopenharmony_ci header.seq_ctrl = 0; 84062306a36Sopenharmony_ci if (priv->wep_is_on) 84162306a36Sopenharmony_ci frame_ctl |= IEEE80211_FCTL_PROTECTED; 84262306a36Sopenharmony_ci if (priv->operating_mode == IW_MODE_ADHOC) { 84362306a36Sopenharmony_ci skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN); 84462306a36Sopenharmony_ci memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); 84562306a36Sopenharmony_ci memcpy(&header.addr3, priv->BSSID, ETH_ALEN); 84662306a36Sopenharmony_ci } else { 84762306a36Sopenharmony_ci frame_ctl |= IEEE80211_FCTL_TODS; 84862306a36Sopenharmony_ci memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN); 84962306a36Sopenharmony_ci memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); 85062306a36Sopenharmony_ci skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN); 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (priv->use_wpa) 85462306a36Sopenharmony_ci memcpy(&header.addr4, rfc1042_header, ETH_ALEN); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci header.frame_control = cpu_to_le16(frame_ctl); 85762306a36Sopenharmony_ci /* Copy the wireless header into the card */ 85862306a36Sopenharmony_ci atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE); 85962306a36Sopenharmony_ci /* Copy the packet sans its 802.3 header addresses which have been replaced */ 86062306a36Sopenharmony_ci atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12); 86162306a36Sopenharmony_ci priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* low bit of first byte of destination tells us if broadcast */ 86462306a36Sopenharmony_ci tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA); 86562306a36Sopenharmony_ci dev->stats.tx_bytes += len; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 86862306a36Sopenharmony_ci spin_unlock_bh(&priv->timerlock); 86962306a36Sopenharmony_ci dev_kfree_skb(skb); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci return NETDEV_TX_OK; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic void atmel_transmit_management_frame(struct atmel_private *priv, 87562306a36Sopenharmony_ci struct ieee80211_hdr *header, 87662306a36Sopenharmony_ci u8 *body, int body_len) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci u16 buff; 87962306a36Sopenharmony_ci int len = MGMT_FRAME_BODY_OFFSET + body_len; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (!(buff = find_tx_buff(priv, len))) 88262306a36Sopenharmony_ci return; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci atmel_copy_to_card(priv->dev, buff, (u8 *)header, MGMT_FRAME_BODY_OFFSET); 88562306a36Sopenharmony_ci atmel_copy_to_card(priv->dev, buff + MGMT_FRAME_BODY_OFFSET, body, body_len); 88662306a36Sopenharmony_ci priv->tx_buff_tail += len; 88762306a36Sopenharmony_ci tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT); 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic void fast_rx_path(struct atmel_private *priv, 89162306a36Sopenharmony_ci struct ieee80211_hdr *header, 89262306a36Sopenharmony_ci u16 msdu_size, u16 rx_packet_loc, u32 crc) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci /* fast path: unfragmented packet copy directly into skbuf */ 89562306a36Sopenharmony_ci u8 mac4[6]; 89662306a36Sopenharmony_ci struct sk_buff *skb; 89762306a36Sopenharmony_ci unsigned char *skbp; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* get the final, mac 4 header field, this tells us encapsulation */ 90062306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6); 90162306a36Sopenharmony_ci msdu_size -= 6; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (priv->do_rx_crc) { 90462306a36Sopenharmony_ci crc = crc32_le(crc, mac4, 6); 90562306a36Sopenharmony_ci msdu_size -= 4; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (!(skb = dev_alloc_skb(msdu_size + 14))) { 90962306a36Sopenharmony_ci priv->dev->stats.rx_dropped++; 91062306a36Sopenharmony_ci return; 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci skb_reserve(skb, 2); 91462306a36Sopenharmony_ci skbp = skb_put(skb, msdu_size + 12); 91562306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (priv->do_rx_crc) { 91862306a36Sopenharmony_ci u32 netcrc; 91962306a36Sopenharmony_ci crc = crc32_le(crc, skbp + 12, msdu_size); 92062306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + 30 + msdu_size, 4); 92162306a36Sopenharmony_ci if ((crc ^ 0xffffffff) != netcrc) { 92262306a36Sopenharmony_ci priv->dev->stats.rx_crc_errors++; 92362306a36Sopenharmony_ci dev_kfree_skb(skb); 92462306a36Sopenharmony_ci return; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */ 92962306a36Sopenharmony_ci if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) 93062306a36Sopenharmony_ci memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN); 93162306a36Sopenharmony_ci else 93262306a36Sopenharmony_ci memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */ 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, priv->dev); 93562306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 93662306a36Sopenharmony_ci netif_rx(skb); 93762306a36Sopenharmony_ci priv->dev->stats.rx_bytes += 12 + msdu_size; 93862306a36Sopenharmony_ci priv->dev->stats.rx_packets++; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci/* Test to see if the packet in card memory at packet_loc has a valid CRC 94262306a36Sopenharmony_ci It doesn't matter that this is slow: it is only used to proble the first few 94362306a36Sopenharmony_ci packets. */ 94462306a36Sopenharmony_cistatic int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci int i = msdu_size - 4; 94762306a36Sopenharmony_ci u32 netcrc, crc = 0xffffffff; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (msdu_size < 4) 95062306a36Sopenharmony_ci return 0; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, (void *)&netcrc, packet_loc + i, 4); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci atmel_writeAR(priv->dev, packet_loc); 95562306a36Sopenharmony_ci while (i--) { 95662306a36Sopenharmony_ci u8 octet = atmel_read8(priv->dev, DR); 95762306a36Sopenharmony_ci crc = crc32_le(crc, &octet, 1); 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci return (crc ^ 0xffffffff) == netcrc; 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic void frag_rx_path(struct atmel_private *priv, 96462306a36Sopenharmony_ci struct ieee80211_hdr *header, 96562306a36Sopenharmony_ci u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, 96662306a36Sopenharmony_ci u8 frag_no, int more_frags) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci u8 mac4[ETH_ALEN]; 96962306a36Sopenharmony_ci u8 source[ETH_ALEN]; 97062306a36Sopenharmony_ci struct sk_buff *skb; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) 97362306a36Sopenharmony_ci memcpy(source, header->addr3, ETH_ALEN); 97462306a36Sopenharmony_ci else 97562306a36Sopenharmony_ci memcpy(source, header->addr2, ETH_ALEN); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci rx_packet_loc += 24; /* skip header */ 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (priv->do_rx_crc) 98062306a36Sopenharmony_ci msdu_size -= 4; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (frag_no == 0) { /* first fragment */ 98362306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN); 98462306a36Sopenharmony_ci msdu_size -= ETH_ALEN; 98562306a36Sopenharmony_ci rx_packet_loc += ETH_ALEN; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (priv->do_rx_crc) 98862306a36Sopenharmony_ci crc = crc32_le(crc, mac4, 6); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci priv->frag_seq = seq_no; 99162306a36Sopenharmony_ci priv->frag_no = 1; 99262306a36Sopenharmony_ci priv->frag_len = msdu_size; 99362306a36Sopenharmony_ci memcpy(priv->frag_source, source, ETH_ALEN); 99462306a36Sopenharmony_ci memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN); 99562306a36Sopenharmony_ci memcpy(priv->rx_buf, header->addr1, ETH_ALEN); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (priv->do_rx_crc) { 100062306a36Sopenharmony_ci u32 netcrc; 100162306a36Sopenharmony_ci crc = crc32_le(crc, &priv->rx_buf[12], msdu_size); 100262306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); 100362306a36Sopenharmony_ci if ((crc ^ 0xffffffff) != netcrc) { 100462306a36Sopenharmony_ci priv->dev->stats.rx_crc_errors++; 100562306a36Sopenharmony_ci eth_broadcast_addr(priv->frag_source); 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci } else if (priv->frag_no == frag_no && 101062306a36Sopenharmony_ci priv->frag_seq == seq_no && 101162306a36Sopenharmony_ci memcmp(priv->frag_source, source, ETH_ALEN) == 0) { 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len], 101462306a36Sopenharmony_ci rx_packet_loc, msdu_size); 101562306a36Sopenharmony_ci if (priv->do_rx_crc) { 101662306a36Sopenharmony_ci u32 netcrc; 101762306a36Sopenharmony_ci crc = crc32_le(crc, 101862306a36Sopenharmony_ci &priv->rx_buf[12 + priv->frag_len], 101962306a36Sopenharmony_ci msdu_size); 102062306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); 102162306a36Sopenharmony_ci if ((crc ^ 0xffffffff) != netcrc) { 102262306a36Sopenharmony_ci priv->dev->stats.rx_crc_errors++; 102362306a36Sopenharmony_ci eth_broadcast_addr(priv->frag_source); 102462306a36Sopenharmony_ci more_frags = 1; /* don't send broken assembly */ 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci priv->frag_len += msdu_size; 102962306a36Sopenharmony_ci priv->frag_no++; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (!more_frags) { /* last one */ 103262306a36Sopenharmony_ci eth_broadcast_addr(priv->frag_source); 103362306a36Sopenharmony_ci if (!(skb = dev_alloc_skb(priv->frag_len + 14))) { 103462306a36Sopenharmony_ci priv->dev->stats.rx_dropped++; 103562306a36Sopenharmony_ci } else { 103662306a36Sopenharmony_ci skb_reserve(skb, 2); 103762306a36Sopenharmony_ci skb_put_data(skb, priv->rx_buf, 103862306a36Sopenharmony_ci priv->frag_len + 12); 103962306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, priv->dev); 104062306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 104162306a36Sopenharmony_ci netif_rx(skb); 104262306a36Sopenharmony_ci priv->dev->stats.rx_bytes += priv->frag_len + 12; 104362306a36Sopenharmony_ci priv->dev->stats.rx_packets++; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci } else 104762306a36Sopenharmony_ci priv->wstats.discard.fragment++; 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic void rx_done_irq(struct atmel_private *priv) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci int i; 105362306a36Sopenharmony_ci struct ieee80211_hdr header; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci for (i = 0; 105662306a36Sopenharmony_ci atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID && 105762306a36Sopenharmony_ci i < priv->host_info.rx_desc_count; 105862306a36Sopenharmony_ci i++) { 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci u16 msdu_size, rx_packet_loc, frame_ctl, seq_control; 106162306a36Sopenharmony_ci u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head)); 106262306a36Sopenharmony_ci u32 crc = 0xffffffff; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (status != RX_STATUS_SUCCESS) { 106562306a36Sopenharmony_ci if (status == 0xc1) /* determined by experiment */ 106662306a36Sopenharmony_ci priv->wstats.discard.nwid++; 106762306a36Sopenharmony_ci else 106862306a36Sopenharmony_ci priv->dev->stats.rx_errors++; 106962306a36Sopenharmony_ci goto next; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head)); 107362306a36Sopenharmony_ci rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head)); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (msdu_size < 30) { 107662306a36Sopenharmony_ci priv->dev->stats.rx_errors++; 107762306a36Sopenharmony_ci goto next; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci /* Get header as far as end of seq_ctrl */ 108162306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24); 108262306a36Sopenharmony_ci frame_ctl = le16_to_cpu(header.frame_control); 108362306a36Sopenharmony_ci seq_control = le16_to_cpu(header.seq_ctrl); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci /* probe for CRC use here if needed once five packets have 108662306a36Sopenharmony_ci arrived with the same crc status, we assume we know what's 108762306a36Sopenharmony_ci happening and stop probing */ 108862306a36Sopenharmony_ci if (priv->probe_crc) { 108962306a36Sopenharmony_ci if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) { 109062306a36Sopenharmony_ci priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size); 109162306a36Sopenharmony_ci } else { 109262306a36Sopenharmony_ci priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24); 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci if (priv->do_rx_crc) { 109562306a36Sopenharmony_ci if (priv->crc_ok_cnt++ > 5) 109662306a36Sopenharmony_ci priv->probe_crc = 0; 109762306a36Sopenharmony_ci } else { 109862306a36Sopenharmony_ci if (priv->crc_ko_cnt++ > 5) 109962306a36Sopenharmony_ci priv->probe_crc = 0; 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* don't CRC header when WEP in use */ 110462306a36Sopenharmony_ci if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) { 110562306a36Sopenharmony_ci crc = crc32_le(0xffffffff, (unsigned char *)&header, 24); 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci msdu_size -= 24; /* header */ 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { 111062306a36Sopenharmony_ci int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS; 111162306a36Sopenharmony_ci u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG; 111262306a36Sopenharmony_ci u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci if (!more_fragments && packet_fragment_no == 0) { 111562306a36Sopenharmony_ci fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc); 111662306a36Sopenharmony_ci } else { 111762306a36Sopenharmony_ci frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc, 111862306a36Sopenharmony_ci packet_sequence_no, packet_fragment_no, more_fragments); 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { 112362306a36Sopenharmony_ci /* copy rest of packet into buffer */ 112462306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci /* we use the same buffer for frag reassembly and control packets */ 112762306a36Sopenharmony_ci eth_broadcast_addr(priv->frag_source); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (priv->do_rx_crc) { 113062306a36Sopenharmony_ci /* last 4 octets is crc */ 113162306a36Sopenharmony_ci msdu_size -= 4; 113262306a36Sopenharmony_ci crc = crc32_le(crc, (unsigned char *)&priv->rx_buf, msdu_size); 113362306a36Sopenharmony_ci if ((crc ^ 0xffffffff) != (*((u32 *)&priv->rx_buf[msdu_size]))) { 113462306a36Sopenharmony_ci priv->dev->stats.rx_crc_errors++; 113562306a36Sopenharmony_ci goto next; 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci atmel_management_frame(priv, &header, msdu_size, 114062306a36Sopenharmony_ci atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head))); 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cinext: 114462306a36Sopenharmony_ci /* release descriptor */ 114562306a36Sopenharmony_ci atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1)) 114862306a36Sopenharmony_ci priv->rx_desc_head++; 114962306a36Sopenharmony_ci else 115062306a36Sopenharmony_ci priv->rx_desc_head = 0; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci} 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic irqreturn_t service_interrupt(int irq, void *dev_id) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci struct net_device *dev = (struct net_device *) dev_id; 115762306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 115862306a36Sopenharmony_ci u8 isr; 115962306a36Sopenharmony_ci int i = -1; 116062306a36Sopenharmony_ci static const u8 irq_order[] = { 116162306a36Sopenharmony_ci ISR_OUT_OF_RANGE, 116262306a36Sopenharmony_ci ISR_RxCOMPLETE, 116362306a36Sopenharmony_ci ISR_TxCOMPLETE, 116462306a36Sopenharmony_ci ISR_RxFRAMELOST, 116562306a36Sopenharmony_ci ISR_FATAL_ERROR, 116662306a36Sopenharmony_ci ISR_COMMAND_COMPLETE, 116762306a36Sopenharmony_ci ISR_IBSS_MERGE, 116862306a36Sopenharmony_ci ISR_GENERIC_IRQ 116962306a36Sopenharmony_ci }; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (priv->card && priv->present_callback && 117262306a36Sopenharmony_ci !(*priv->present_callback)(priv->card)) 117362306a36Sopenharmony_ci return IRQ_HANDLED; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci /* In this state upper-level code assumes it can mess with 117662306a36Sopenharmony_ci the card unhampered by interrupts which may change register state. 117762306a36Sopenharmony_ci Note that even though the card shouldn't generate interrupts 117862306a36Sopenharmony_ci the inturrupt line may be shared. This allows card setup 117962306a36Sopenharmony_ci to go on without disabling interrupts for a long time. */ 118062306a36Sopenharmony_ci if (priv->station_state == STATION_STATE_DOWN) 118162306a36Sopenharmony_ci return IRQ_NONE; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */ 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci while (1) { 118662306a36Sopenharmony_ci if (!atmel_lock_mac(priv)) { 118762306a36Sopenharmony_ci /* failed to contact card */ 118862306a36Sopenharmony_ci printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); 118962306a36Sopenharmony_ci return IRQ_HANDLED; 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); 119362306a36Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (!isr) { 119662306a36Sopenharmony_ci atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */ 119762306a36Sopenharmony_ci return i == -1 ? IRQ_NONE : IRQ_HANDLED; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */ 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(irq_order); i++) 120362306a36Sopenharmony_ci if (isr & irq_order[i]) 120462306a36Sopenharmony_ci break; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (!atmel_lock_mac(priv)) { 120762306a36Sopenharmony_ci /* failed to contact card */ 120862306a36Sopenharmony_ci printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); 120962306a36Sopenharmony_ci return IRQ_HANDLED; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); 121362306a36Sopenharmony_ci isr ^= irq_order[i]; 121462306a36Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr); 121562306a36Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci switch (irq_order[i]) { 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci case ISR_OUT_OF_RANGE: 122062306a36Sopenharmony_ci if (priv->operating_mode == IW_MODE_INFRA && 122162306a36Sopenharmony_ci priv->station_state == STATION_STATE_READY) { 122262306a36Sopenharmony_ci priv->station_is_associated = 0; 122362306a36Sopenharmony_ci atmel_scan(priv, 1); 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci break; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci case ISR_RxFRAMELOST: 122862306a36Sopenharmony_ci priv->wstats.discard.misc++; 122962306a36Sopenharmony_ci fallthrough; 123062306a36Sopenharmony_ci case ISR_RxCOMPLETE: 123162306a36Sopenharmony_ci rx_done_irq(priv); 123262306a36Sopenharmony_ci break; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci case ISR_TxCOMPLETE: 123562306a36Sopenharmony_ci tx_done_irq(priv); 123662306a36Sopenharmony_ci break; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci case ISR_FATAL_ERROR: 123962306a36Sopenharmony_ci printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name); 124062306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 124162306a36Sopenharmony_ci break; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci case ISR_COMMAND_COMPLETE: 124462306a36Sopenharmony_ci atmel_command_irq(priv); 124562306a36Sopenharmony_ci break; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci case ISR_IBSS_MERGE: 124862306a36Sopenharmony_ci atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, 124962306a36Sopenharmony_ci priv->CurrentBSSID, 6); 125062306a36Sopenharmony_ci /* The WPA stuff cares about the current AP address */ 125162306a36Sopenharmony_ci if (priv->use_wpa) 125262306a36Sopenharmony_ci build_wpa_mib(priv); 125362306a36Sopenharmony_ci break; 125462306a36Sopenharmony_ci case ISR_GENERIC_IRQ: 125562306a36Sopenharmony_ci printk(KERN_INFO "%s: Generic_irq received.\n", dev->name); 125662306a36Sopenharmony_ci break; 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_cistatic struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci /* update the link quality here in case we are seeing no beacons 126662306a36Sopenharmony_ci at all to drive the process */ 126762306a36Sopenharmony_ci atmel_smooth_qual(priv); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci priv->wstats.status = priv->station_state; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci if (priv->operating_mode == IW_MODE_INFRA) { 127262306a36Sopenharmony_ci if (priv->station_state != STATION_STATE_READY) { 127362306a36Sopenharmony_ci priv->wstats.qual.qual = 0; 127462306a36Sopenharmony_ci priv->wstats.qual.level = 0; 127562306a36Sopenharmony_ci priv->wstats.qual.updated = (IW_QUAL_QUAL_INVALID 127662306a36Sopenharmony_ci | IW_QUAL_LEVEL_INVALID); 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci priv->wstats.qual.noise = 0; 127962306a36Sopenharmony_ci priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID; 128062306a36Sopenharmony_ci } else { 128162306a36Sopenharmony_ci /* Quality levels cannot be determined in ad-hoc mode, 128262306a36Sopenharmony_ci because we can 'hear' more that one remote station. */ 128362306a36Sopenharmony_ci priv->wstats.qual.qual = 0; 128462306a36Sopenharmony_ci priv->wstats.qual.level = 0; 128562306a36Sopenharmony_ci priv->wstats.qual.noise = 0; 128662306a36Sopenharmony_ci priv->wstats.qual.updated = IW_QUAL_QUAL_INVALID 128762306a36Sopenharmony_ci | IW_QUAL_LEVEL_INVALID 128862306a36Sopenharmony_ci | IW_QUAL_NOISE_INVALID; 128962306a36Sopenharmony_ci priv->wstats.miss.beacon = 0; 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci return &priv->wstats; 129362306a36Sopenharmony_ci} 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_cistatic int atmel_set_mac_address(struct net_device *dev, void *p) 129662306a36Sopenharmony_ci{ 129762306a36Sopenharmony_ci struct sockaddr *addr = p; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci eth_hw_addr_set(dev, addr->sa_data); 130062306a36Sopenharmony_ci return atmel_open(dev); 130162306a36Sopenharmony_ci} 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ciEXPORT_SYMBOL(atmel_open); 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ciint atmel_open(struct net_device *dev) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 130862306a36Sopenharmony_ci int i, channel, err; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci /* any scheduled timer is no longer needed and might screw things up.. */ 131162306a36Sopenharmony_ci del_timer_sync(&priv->management_timer); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* Interrupts will not touch the card once in this state... */ 131462306a36Sopenharmony_ci priv->station_state = STATION_STATE_DOWN; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (priv->new_SSID_size) { 131762306a36Sopenharmony_ci memcpy(priv->SSID, priv->new_SSID, priv->new_SSID_size); 131862306a36Sopenharmony_ci priv->SSID_size = priv->new_SSID_size; 131962306a36Sopenharmony_ci priv->new_SSID_size = 0; 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci priv->BSS_list_entries = 0; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci priv->AuthenticationRequestRetryCnt = 0; 132462306a36Sopenharmony_ci priv->AssociationRequestRetryCnt = 0; 132562306a36Sopenharmony_ci priv->ReAssociationRequestRetryCnt = 0; 132662306a36Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum = 0x0001; 132762306a36Sopenharmony_ci priv->ExpectedAuthentTransactionSeqNum = 0x0002; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci priv->site_survey_state = SITE_SURVEY_IDLE; 133062306a36Sopenharmony_ci priv->station_is_associated = 0; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci err = reset_atmel_card(dev); 133362306a36Sopenharmony_ci if (err) 133462306a36Sopenharmony_ci return err; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci if (priv->config_reg_domain) { 133762306a36Sopenharmony_ci priv->reg_domain = priv->config_reg_domain; 133862306a36Sopenharmony_ci atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS, priv->reg_domain); 133962306a36Sopenharmony_ci } else { 134062306a36Sopenharmony_ci priv->reg_domain = atmel_get_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS); 134162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_table); i++) 134262306a36Sopenharmony_ci if (priv->reg_domain == channel_table[i].reg_domain) 134362306a36Sopenharmony_ci break; 134462306a36Sopenharmony_ci if (i == ARRAY_SIZE(channel_table)) { 134562306a36Sopenharmony_ci priv->reg_domain = REG_DOMAIN_MKK1; 134662306a36Sopenharmony_ci printk(KERN_ALERT "%s: failed to get regulatory domain: assuming MKK1.\n", dev->name); 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci } 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if ((channel = atmel_validate_channel(priv, priv->channel))) 135162306a36Sopenharmony_ci priv->channel = channel; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci /* this moves station_state on.... */ 135462306a36Sopenharmony_ci atmel_scan(priv, 1); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci atmel_set_gcr(priv->dev, GCR_ENINT); /* enable interrupts */ 135762306a36Sopenharmony_ci return 0; 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic int atmel_close(struct net_device *dev) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* Send event to userspace that we are disassociating */ 136562306a36Sopenharmony_ci if (priv->station_state == STATION_STATE_READY) { 136662306a36Sopenharmony_ci union iwreq_data wrqu; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci wrqu.data.length = 0; 136962306a36Sopenharmony_ci wrqu.data.flags = 0; 137062306a36Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 137162306a36Sopenharmony_ci eth_zero_addr(wrqu.ap_addr.sa_data); 137262306a36Sopenharmony_ci wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); 137362306a36Sopenharmony_ci } 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_DOWN); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 137862306a36Sopenharmony_ci atmel_write16(dev, GCR, 0x0060); 137962306a36Sopenharmony_ci atmel_write16(dev, GCR, 0x0040); 138062306a36Sopenharmony_ci return 0; 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_cistatic int atmel_validate_channel(struct atmel_private *priv, int channel) 138462306a36Sopenharmony_ci{ 138562306a36Sopenharmony_ci /* check that channel is OK, if so return zero, 138662306a36Sopenharmony_ci else return suitable default channel */ 138762306a36Sopenharmony_ci int i; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_table); i++) 139062306a36Sopenharmony_ci if (priv->reg_domain == channel_table[i].reg_domain) { 139162306a36Sopenharmony_ci if (channel >= channel_table[i].min && 139262306a36Sopenharmony_ci channel <= channel_table[i].max) 139362306a36Sopenharmony_ci return 0; 139462306a36Sopenharmony_ci else 139562306a36Sopenharmony_ci return channel_table[i].min; 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci return 0; 139862306a36Sopenharmony_ci} 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 140162306a36Sopenharmony_cistatic int atmel_proc_show(struct seq_file *m, void *v) 140262306a36Sopenharmony_ci{ 140362306a36Sopenharmony_ci struct atmel_private *priv = m->private; 140462306a36Sopenharmony_ci int i; 140562306a36Sopenharmony_ci char *s, *r, *c; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci seq_printf(m, "Driver version:\t\t%d.%d\n", DRIVER_MAJOR, DRIVER_MINOR); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (priv->station_state != STATION_STATE_DOWN) { 141062306a36Sopenharmony_ci seq_printf(m, 141162306a36Sopenharmony_ci "Firmware version:\t%d.%d build %d\n" 141262306a36Sopenharmony_ci "Firmware location:\t", 141362306a36Sopenharmony_ci priv->host_info.major_version, 141462306a36Sopenharmony_ci priv->host_info.minor_version, 141562306a36Sopenharmony_ci priv->host_info.build_version); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci if (priv->card_type != CARD_TYPE_EEPROM) 141862306a36Sopenharmony_ci seq_puts(m, "on card\n"); 141962306a36Sopenharmony_ci else if (priv->firmware) 142062306a36Sopenharmony_ci seq_printf(m, "%s loaded by host\n", priv->firmware_id); 142162306a36Sopenharmony_ci else 142262306a36Sopenharmony_ci seq_printf(m, "%s loaded by hotplug\n", priv->firmware_id); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci switch (priv->card_type) { 142562306a36Sopenharmony_ci case CARD_TYPE_PARALLEL_FLASH: 142662306a36Sopenharmony_ci c = "Parallel flash"; 142762306a36Sopenharmony_ci break; 142862306a36Sopenharmony_ci case CARD_TYPE_SPI_FLASH: 142962306a36Sopenharmony_ci c = "SPI flash\n"; 143062306a36Sopenharmony_ci break; 143162306a36Sopenharmony_ci case CARD_TYPE_EEPROM: 143262306a36Sopenharmony_ci c = "EEPROM"; 143362306a36Sopenharmony_ci break; 143462306a36Sopenharmony_ci default: 143562306a36Sopenharmony_ci c = "<unknown>"; 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci r = "<unknown>"; 143962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_table); i++) 144062306a36Sopenharmony_ci if (priv->reg_domain == channel_table[i].reg_domain) 144162306a36Sopenharmony_ci r = channel_table[i].name; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci seq_printf(m, "MAC memory type:\t%s\n", c); 144462306a36Sopenharmony_ci seq_printf(m, "Regulatory domain:\t%s\n", r); 144562306a36Sopenharmony_ci seq_printf(m, "Host CRC checking:\t%s\n", 144662306a36Sopenharmony_ci priv->do_rx_crc ? "On" : "Off"); 144762306a36Sopenharmony_ci seq_printf(m, "WPA-capable firmware:\t%s\n", 144862306a36Sopenharmony_ci priv->use_wpa ? "Yes" : "No"); 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci switch (priv->station_state) { 145262306a36Sopenharmony_ci case STATION_STATE_SCANNING: 145362306a36Sopenharmony_ci s = "Scanning"; 145462306a36Sopenharmony_ci break; 145562306a36Sopenharmony_ci case STATION_STATE_JOINNING: 145662306a36Sopenharmony_ci s = "Joining"; 145762306a36Sopenharmony_ci break; 145862306a36Sopenharmony_ci case STATION_STATE_AUTHENTICATING: 145962306a36Sopenharmony_ci s = "Authenticating"; 146062306a36Sopenharmony_ci break; 146162306a36Sopenharmony_ci case STATION_STATE_ASSOCIATING: 146262306a36Sopenharmony_ci s = "Associating"; 146362306a36Sopenharmony_ci break; 146462306a36Sopenharmony_ci case STATION_STATE_READY: 146562306a36Sopenharmony_ci s = "Ready"; 146662306a36Sopenharmony_ci break; 146762306a36Sopenharmony_ci case STATION_STATE_REASSOCIATING: 146862306a36Sopenharmony_ci s = "Reassociating"; 146962306a36Sopenharmony_ci break; 147062306a36Sopenharmony_ci case STATION_STATE_MGMT_ERROR: 147162306a36Sopenharmony_ci s = "Management error"; 147262306a36Sopenharmony_ci break; 147362306a36Sopenharmony_ci case STATION_STATE_DOWN: 147462306a36Sopenharmony_ci s = "Down"; 147562306a36Sopenharmony_ci break; 147662306a36Sopenharmony_ci default: 147762306a36Sopenharmony_ci s = "<unknown>"; 147862306a36Sopenharmony_ci } 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci seq_printf(m, "Current state:\t\t%s\n", s); 148162306a36Sopenharmony_ci return 0; 148262306a36Sopenharmony_ci} 148362306a36Sopenharmony_ci#endif 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_cistatic const struct net_device_ops atmel_netdev_ops = { 148662306a36Sopenharmony_ci .ndo_open = atmel_open, 148762306a36Sopenharmony_ci .ndo_stop = atmel_close, 148862306a36Sopenharmony_ci .ndo_set_mac_address = atmel_set_mac_address, 148962306a36Sopenharmony_ci .ndo_start_xmit = start_tx, 149062306a36Sopenharmony_ci .ndo_do_ioctl = atmel_ioctl, 149162306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 149262306a36Sopenharmony_ci}; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistruct net_device *init_atmel_card(unsigned short irq, unsigned long port, 149562306a36Sopenharmony_ci const AtmelFWType fw_type, 149662306a36Sopenharmony_ci struct device *sys_dev, 149762306a36Sopenharmony_ci int (*card_present)(void *), void *card) 149862306a36Sopenharmony_ci{ 149962306a36Sopenharmony_ci struct net_device *dev; 150062306a36Sopenharmony_ci struct atmel_private *priv; 150162306a36Sopenharmony_ci int rc; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci /* Create the network device object. */ 150462306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(*priv)); 150562306a36Sopenharmony_ci if (!dev) 150662306a36Sopenharmony_ci return NULL; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci if (dev_alloc_name(dev, dev->name) < 0) { 150962306a36Sopenharmony_ci printk(KERN_ERR "atmel: Couldn't get name!\n"); 151062306a36Sopenharmony_ci goto err_out_free; 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci priv = netdev_priv(dev); 151462306a36Sopenharmony_ci priv->dev = dev; 151562306a36Sopenharmony_ci priv->sys_dev = sys_dev; 151662306a36Sopenharmony_ci priv->present_callback = card_present; 151762306a36Sopenharmony_ci priv->card = card; 151862306a36Sopenharmony_ci priv->firmware = NULL; 151962306a36Sopenharmony_ci priv->firmware_type = fw_type; 152062306a36Sopenharmony_ci if (firmware) /* module parameter */ 152162306a36Sopenharmony_ci strscpy(priv->firmware_id, firmware, sizeof(priv->firmware_id)); 152262306a36Sopenharmony_ci priv->bus_type = card_present ? BUS_TYPE_PCCARD : BUS_TYPE_PCI; 152362306a36Sopenharmony_ci priv->station_state = STATION_STATE_DOWN; 152462306a36Sopenharmony_ci priv->do_rx_crc = 0; 152562306a36Sopenharmony_ci /* For PCMCIA cards, some chips need CRC, some don't 152662306a36Sopenharmony_ci so we have to probe. */ 152762306a36Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) { 152862306a36Sopenharmony_ci priv->probe_crc = 1; 152962306a36Sopenharmony_ci priv->crc_ok_cnt = priv->crc_ko_cnt = 0; 153062306a36Sopenharmony_ci } else 153162306a36Sopenharmony_ci priv->probe_crc = 0; 153262306a36Sopenharmony_ci priv->last_qual = jiffies; 153362306a36Sopenharmony_ci priv->last_beacon_timestamp = 0; 153462306a36Sopenharmony_ci memset(priv->frag_source, 0xff, sizeof(priv->frag_source)); 153562306a36Sopenharmony_ci eth_zero_addr(priv->BSSID); 153662306a36Sopenharmony_ci priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */ 153762306a36Sopenharmony_ci priv->station_was_associated = 0; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci priv->last_survey = jiffies; 154062306a36Sopenharmony_ci priv->preamble = LONG_PREAMBLE; 154162306a36Sopenharmony_ci priv->operating_mode = IW_MODE_INFRA; 154262306a36Sopenharmony_ci priv->connect_to_any_BSS = 0; 154362306a36Sopenharmony_ci priv->config_reg_domain = 0; 154462306a36Sopenharmony_ci priv->reg_domain = 0; 154562306a36Sopenharmony_ci priv->tx_rate = 3; 154662306a36Sopenharmony_ci priv->auto_tx_rate = 1; 154762306a36Sopenharmony_ci priv->channel = 4; 154862306a36Sopenharmony_ci priv->power_mode = 0; 154962306a36Sopenharmony_ci priv->SSID[0] = '\0'; 155062306a36Sopenharmony_ci priv->SSID_size = 0; 155162306a36Sopenharmony_ci priv->new_SSID_size = 0; 155262306a36Sopenharmony_ci priv->frag_threshold = 2346; 155362306a36Sopenharmony_ci priv->rts_threshold = 2347; 155462306a36Sopenharmony_ci priv->short_retry = 7; 155562306a36Sopenharmony_ci priv->long_retry = 4; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci priv->wep_is_on = 0; 155862306a36Sopenharmony_ci priv->default_key = 0; 155962306a36Sopenharmony_ci priv->encryption_level = 0; 156062306a36Sopenharmony_ci priv->exclude_unencrypted = 0; 156162306a36Sopenharmony_ci priv->group_cipher_suite = priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; 156262306a36Sopenharmony_ci priv->use_wpa = 0; 156362306a36Sopenharmony_ci memset(priv->wep_keys, 0, sizeof(priv->wep_keys)); 156462306a36Sopenharmony_ci memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci priv->default_beacon_period = priv->beacon_period = 100; 156762306a36Sopenharmony_ci priv->listen_interval = 1; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci timer_setup(&priv->management_timer, atmel_management_timer, 0); 157062306a36Sopenharmony_ci spin_lock_init(&priv->irqlock); 157162306a36Sopenharmony_ci spin_lock_init(&priv->timerlock); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci dev->netdev_ops = &atmel_netdev_ops; 157462306a36Sopenharmony_ci dev->wireless_handlers = &atmel_handler_def; 157562306a36Sopenharmony_ci dev->irq = irq; 157662306a36Sopenharmony_ci dev->base_addr = port; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci /* MTU range: 68 - 2312 */ 157962306a36Sopenharmony_ci dev->min_mtu = 68; 158062306a36Sopenharmony_ci dev->max_mtu = MAX_WIRELESS_BODY - ETH_FCS_LEN; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci SET_NETDEV_DEV(dev, sys_dev); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) { 158562306a36Sopenharmony_ci printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc); 158662306a36Sopenharmony_ci goto err_out_free; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci if (!request_region(dev->base_addr, 32, 159062306a36Sopenharmony_ci priv->bus_type == BUS_TYPE_PCCARD ? "atmel_cs" : "atmel_pci")) { 159162306a36Sopenharmony_ci goto err_out_irq; 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (register_netdev(dev)) 159562306a36Sopenharmony_ci goto err_out_res; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (!probe_atmel_card(dev)) { 159862306a36Sopenharmony_ci unregister_netdev(dev); 159962306a36Sopenharmony_ci goto err_out_res; 160062306a36Sopenharmony_ci } 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci netif_carrier_off(dev); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (!proc_create_single_data("driver/atmel", 0, NULL, atmel_proc_show, 160562306a36Sopenharmony_ci priv)) 160662306a36Sopenharmony_ci printk(KERN_WARNING "atmel: unable to create /proc entry.\n"); 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %pM\n", 160962306a36Sopenharmony_ci dev->name, DRIVER_MAJOR, DRIVER_MINOR, dev->dev_addr); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci return dev; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_cierr_out_res: 161462306a36Sopenharmony_ci release_region(dev->base_addr, 32); 161562306a36Sopenharmony_cierr_out_irq: 161662306a36Sopenharmony_ci free_irq(dev->irq, dev); 161762306a36Sopenharmony_cierr_out_free: 161862306a36Sopenharmony_ci free_netdev(dev); 161962306a36Sopenharmony_ci return NULL; 162062306a36Sopenharmony_ci} 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ciEXPORT_SYMBOL(init_atmel_card); 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_civoid stop_atmel_card(struct net_device *dev) 162562306a36Sopenharmony_ci{ 162662306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci /* put a brick on it... */ 162962306a36Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 163062306a36Sopenharmony_ci atmel_write16(dev, GCR, 0x0060); 163162306a36Sopenharmony_ci atmel_write16(dev, GCR, 0x0040); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci del_timer_sync(&priv->management_timer); 163462306a36Sopenharmony_ci unregister_netdev(dev); 163562306a36Sopenharmony_ci remove_proc_entry("driver/atmel", NULL); 163662306a36Sopenharmony_ci free_irq(dev->irq, dev); 163762306a36Sopenharmony_ci kfree(priv->firmware); 163862306a36Sopenharmony_ci release_region(dev->base_addr, 32); 163962306a36Sopenharmony_ci free_netdev(dev); 164062306a36Sopenharmony_ci} 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ciEXPORT_SYMBOL(stop_atmel_card); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_cistatic int atmel_set_essid(struct net_device *dev, 164562306a36Sopenharmony_ci struct iw_request_info *info, 164662306a36Sopenharmony_ci union iwreq_data *wrqu, 164762306a36Sopenharmony_ci char *extra) 164862306a36Sopenharmony_ci{ 164962306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->essid; 165062306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci /* Check if we asked for `any' */ 165362306a36Sopenharmony_ci if (dwrq->flags == 0) { 165462306a36Sopenharmony_ci priv->connect_to_any_BSS = 1; 165562306a36Sopenharmony_ci } else { 165662306a36Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci priv->connect_to_any_BSS = 0; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci /* Check the size of the string */ 166162306a36Sopenharmony_ci if (dwrq->length > MAX_SSID_LENGTH) 166262306a36Sopenharmony_ci return -E2BIG; 166362306a36Sopenharmony_ci if (index != 0) 166462306a36Sopenharmony_ci return -EINVAL; 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci memcpy(priv->new_SSID, extra, dwrq->length); 166762306a36Sopenharmony_ci priv->new_SSID_size = dwrq->length; 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci return -EINPROGRESS; 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_cistatic int atmel_get_essid(struct net_device *dev, 167462306a36Sopenharmony_ci struct iw_request_info *info, 167562306a36Sopenharmony_ci union iwreq_data *wrqu, 167662306a36Sopenharmony_ci char *extra) 167762306a36Sopenharmony_ci{ 167862306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->essid; 167962306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci /* Get the current SSID */ 168262306a36Sopenharmony_ci if (priv->new_SSID_size != 0) { 168362306a36Sopenharmony_ci memcpy(extra, priv->new_SSID, priv->new_SSID_size); 168462306a36Sopenharmony_ci dwrq->length = priv->new_SSID_size; 168562306a36Sopenharmony_ci } else { 168662306a36Sopenharmony_ci memcpy(extra, priv->SSID, priv->SSID_size); 168762306a36Sopenharmony_ci dwrq->length = priv->SSID_size; 168862306a36Sopenharmony_ci } 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci dwrq->flags = !priv->connect_to_any_BSS; /* active */ 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci return 0; 169362306a36Sopenharmony_ci} 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cistatic int atmel_get_wap(struct net_device *dev, 169662306a36Sopenharmony_ci struct iw_request_info *info, 169762306a36Sopenharmony_ci union iwreq_data *wrqu, 169862306a36Sopenharmony_ci char *extra) 169962306a36Sopenharmony_ci{ 170062306a36Sopenharmony_ci struct sockaddr *awrq = &wrqu->ap_addr; 170162306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 170262306a36Sopenharmony_ci memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN); 170362306a36Sopenharmony_ci awrq->sa_family = ARPHRD_ETHER; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci return 0; 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_cistatic int atmel_set_encode(struct net_device *dev, 170962306a36Sopenharmony_ci struct iw_request_info *info, 171062306a36Sopenharmony_ci union iwreq_data *wrqu, 171162306a36Sopenharmony_ci char *extra) 171262306a36Sopenharmony_ci{ 171362306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->encoding; 171462306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci /* Basic checking: do we have a key to set ? 171762306a36Sopenharmony_ci * Note : with the new API, it's impossible to get a NULL pointer. 171862306a36Sopenharmony_ci * Therefore, we need to check a key size == 0 instead. 171962306a36Sopenharmony_ci * New version of iwconfig properly set the IW_ENCODE_NOKEY flag 172062306a36Sopenharmony_ci * when no key is present (only change flags), but older versions 172162306a36Sopenharmony_ci * don't do it. - Jean II */ 172262306a36Sopenharmony_ci if (dwrq->length > 0) { 172362306a36Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 172462306a36Sopenharmony_ci int current_index = priv->default_key; 172562306a36Sopenharmony_ci /* Check the size of the key */ 172662306a36Sopenharmony_ci if (dwrq->length > 13) { 172762306a36Sopenharmony_ci return -EINVAL; 172862306a36Sopenharmony_ci } 172962306a36Sopenharmony_ci /* Check the index (none -> use current) */ 173062306a36Sopenharmony_ci if (index < 0 || index >= 4) 173162306a36Sopenharmony_ci index = current_index; 173262306a36Sopenharmony_ci else 173362306a36Sopenharmony_ci priv->default_key = index; 173462306a36Sopenharmony_ci /* Set the length */ 173562306a36Sopenharmony_ci if (dwrq->length > 5) 173662306a36Sopenharmony_ci priv->wep_key_len[index] = 13; 173762306a36Sopenharmony_ci else 173862306a36Sopenharmony_ci if (dwrq->length > 0) 173962306a36Sopenharmony_ci priv->wep_key_len[index] = 5; 174062306a36Sopenharmony_ci else 174162306a36Sopenharmony_ci /* Disable the key */ 174262306a36Sopenharmony_ci priv->wep_key_len[index] = 0; 174362306a36Sopenharmony_ci /* Check if the key is not marked as invalid */ 174462306a36Sopenharmony_ci if (!(dwrq->flags & IW_ENCODE_NOKEY)) { 174562306a36Sopenharmony_ci /* Cleanup */ 174662306a36Sopenharmony_ci memset(priv->wep_keys[index], 0, 13); 174762306a36Sopenharmony_ci /* Copy the key in the driver */ 174862306a36Sopenharmony_ci memcpy(priv->wep_keys[index], extra, dwrq->length); 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci /* WE specify that if a valid key is set, encryption 175162306a36Sopenharmony_ci * should be enabled (user may turn it off later) 175262306a36Sopenharmony_ci * This is also how "iwconfig ethX key on" works */ 175362306a36Sopenharmony_ci if (index == current_index && 175462306a36Sopenharmony_ci priv->wep_key_len[index] > 0) { 175562306a36Sopenharmony_ci priv->wep_is_on = 1; 175662306a36Sopenharmony_ci priv->exclude_unencrypted = 1; 175762306a36Sopenharmony_ci if (priv->wep_key_len[index] > 5) { 175862306a36Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; 175962306a36Sopenharmony_ci priv->encryption_level = 2; 176062306a36Sopenharmony_ci } else { 176162306a36Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; 176262306a36Sopenharmony_ci priv->encryption_level = 1; 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci } 176562306a36Sopenharmony_ci } else { 176662306a36Sopenharmony_ci /* Do we want to just set the transmit key index ? */ 176762306a36Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 176862306a36Sopenharmony_ci if (index >= 0 && index < 4) { 176962306a36Sopenharmony_ci priv->default_key = index; 177062306a36Sopenharmony_ci } else 177162306a36Sopenharmony_ci /* Don't complain if only change the mode */ 177262306a36Sopenharmony_ci if (!(dwrq->flags & IW_ENCODE_MODE)) 177362306a36Sopenharmony_ci return -EINVAL; 177462306a36Sopenharmony_ci } 177562306a36Sopenharmony_ci /* Read the flags */ 177662306a36Sopenharmony_ci if (dwrq->flags & IW_ENCODE_DISABLED) { 177762306a36Sopenharmony_ci priv->wep_is_on = 0; 177862306a36Sopenharmony_ci priv->encryption_level = 0; 177962306a36Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; 178062306a36Sopenharmony_ci } else { 178162306a36Sopenharmony_ci priv->wep_is_on = 1; 178262306a36Sopenharmony_ci if (priv->wep_key_len[priv->default_key] > 5) { 178362306a36Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; 178462306a36Sopenharmony_ci priv->encryption_level = 2; 178562306a36Sopenharmony_ci } else { 178662306a36Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; 178762306a36Sopenharmony_ci priv->encryption_level = 1; 178862306a36Sopenharmony_ci } 178962306a36Sopenharmony_ci } 179062306a36Sopenharmony_ci if (dwrq->flags & IW_ENCODE_RESTRICTED) 179162306a36Sopenharmony_ci priv->exclude_unencrypted = 1; 179262306a36Sopenharmony_ci if (dwrq->flags & IW_ENCODE_OPEN) 179362306a36Sopenharmony_ci priv->exclude_unencrypted = 0; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 179662306a36Sopenharmony_ci} 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_cistatic int atmel_get_encode(struct net_device *dev, 179962306a36Sopenharmony_ci struct iw_request_info *info, 180062306a36Sopenharmony_ci union iwreq_data *wrqu, 180162306a36Sopenharmony_ci char *extra) 180262306a36Sopenharmony_ci{ 180362306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->encoding; 180462306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 180562306a36Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci if (!priv->wep_is_on) 180862306a36Sopenharmony_ci dwrq->flags = IW_ENCODE_DISABLED; 180962306a36Sopenharmony_ci else { 181062306a36Sopenharmony_ci if (priv->exclude_unencrypted) 181162306a36Sopenharmony_ci dwrq->flags = IW_ENCODE_RESTRICTED; 181262306a36Sopenharmony_ci else 181362306a36Sopenharmony_ci dwrq->flags = IW_ENCODE_OPEN; 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci /* Which key do we want ? -1 -> tx index */ 181662306a36Sopenharmony_ci if (index < 0 || index >= 4) 181762306a36Sopenharmony_ci index = priv->default_key; 181862306a36Sopenharmony_ci dwrq->flags |= index + 1; 181962306a36Sopenharmony_ci /* Copy the key to the user buffer */ 182062306a36Sopenharmony_ci dwrq->length = priv->wep_key_len[index]; 182162306a36Sopenharmony_ci if (dwrq->length > 16) { 182262306a36Sopenharmony_ci dwrq->length = 0; 182362306a36Sopenharmony_ci } else { 182462306a36Sopenharmony_ci memset(extra, 0, 16); 182562306a36Sopenharmony_ci memcpy(extra, priv->wep_keys[index], dwrq->length); 182662306a36Sopenharmony_ci } 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci return 0; 182962306a36Sopenharmony_ci} 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_cistatic int atmel_set_encodeext(struct net_device *dev, 183262306a36Sopenharmony_ci struct iw_request_info *info, 183362306a36Sopenharmony_ci union iwreq_data *wrqu, 183462306a36Sopenharmony_ci char *extra) 183562306a36Sopenharmony_ci{ 183662306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 183762306a36Sopenharmony_ci struct iw_point *encoding = &wrqu->encoding; 183862306a36Sopenharmony_ci struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 183962306a36Sopenharmony_ci int idx, key_len, alg = ext->alg, set_key = 1; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci /* Determine and validate the key index */ 184262306a36Sopenharmony_ci idx = encoding->flags & IW_ENCODE_INDEX; 184362306a36Sopenharmony_ci if (idx) { 184462306a36Sopenharmony_ci if (idx < 1 || idx > 4) 184562306a36Sopenharmony_ci return -EINVAL; 184662306a36Sopenharmony_ci idx--; 184762306a36Sopenharmony_ci } else 184862306a36Sopenharmony_ci idx = priv->default_key; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci if (encoding->flags & IW_ENCODE_DISABLED) 185162306a36Sopenharmony_ci alg = IW_ENCODE_ALG_NONE; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { 185462306a36Sopenharmony_ci priv->default_key = idx; 185562306a36Sopenharmony_ci set_key = ext->key_len > 0 ? 1 : 0; 185662306a36Sopenharmony_ci } 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci if (set_key) { 185962306a36Sopenharmony_ci /* Set the requested key first */ 186062306a36Sopenharmony_ci switch (alg) { 186162306a36Sopenharmony_ci case IW_ENCODE_ALG_NONE: 186262306a36Sopenharmony_ci priv->wep_is_on = 0; 186362306a36Sopenharmony_ci priv->encryption_level = 0; 186462306a36Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; 186562306a36Sopenharmony_ci break; 186662306a36Sopenharmony_ci case IW_ENCODE_ALG_WEP: 186762306a36Sopenharmony_ci if (ext->key_len > 5) { 186862306a36Sopenharmony_ci priv->wep_key_len[idx] = 13; 186962306a36Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; 187062306a36Sopenharmony_ci priv->encryption_level = 2; 187162306a36Sopenharmony_ci } else if (ext->key_len > 0) { 187262306a36Sopenharmony_ci priv->wep_key_len[idx] = 5; 187362306a36Sopenharmony_ci priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; 187462306a36Sopenharmony_ci priv->encryption_level = 1; 187562306a36Sopenharmony_ci } else { 187662306a36Sopenharmony_ci return -EINVAL; 187762306a36Sopenharmony_ci } 187862306a36Sopenharmony_ci priv->wep_is_on = 1; 187962306a36Sopenharmony_ci memset(priv->wep_keys[idx], 0, 13); 188062306a36Sopenharmony_ci key_len = min ((int)ext->key_len, priv->wep_key_len[idx]); 188162306a36Sopenharmony_ci memcpy(priv->wep_keys[idx], ext->key, key_len); 188262306a36Sopenharmony_ci break; 188362306a36Sopenharmony_ci default: 188462306a36Sopenharmony_ci return -EINVAL; 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci } 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci return -EINPROGRESS; 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_cistatic int atmel_get_encodeext(struct net_device *dev, 189262306a36Sopenharmony_ci struct iw_request_info *info, 189362306a36Sopenharmony_ci union iwreq_data *wrqu, 189462306a36Sopenharmony_ci char *extra) 189562306a36Sopenharmony_ci{ 189662306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 189762306a36Sopenharmony_ci struct iw_point *encoding = &wrqu->encoding; 189862306a36Sopenharmony_ci struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 189962306a36Sopenharmony_ci int idx, max_key_len; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci max_key_len = encoding->length - sizeof(*ext); 190262306a36Sopenharmony_ci if (max_key_len < 0) 190362306a36Sopenharmony_ci return -EINVAL; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci idx = encoding->flags & IW_ENCODE_INDEX; 190662306a36Sopenharmony_ci if (idx) { 190762306a36Sopenharmony_ci if (idx < 1 || idx > 4) 190862306a36Sopenharmony_ci return -EINVAL; 190962306a36Sopenharmony_ci idx--; 191062306a36Sopenharmony_ci } else 191162306a36Sopenharmony_ci idx = priv->default_key; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci encoding->flags = idx + 1; 191462306a36Sopenharmony_ci memset(ext, 0, sizeof(*ext)); 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci if (!priv->wep_is_on) { 191762306a36Sopenharmony_ci ext->alg = IW_ENCODE_ALG_NONE; 191862306a36Sopenharmony_ci ext->key_len = 0; 191962306a36Sopenharmony_ci encoding->flags |= IW_ENCODE_DISABLED; 192062306a36Sopenharmony_ci } else { 192162306a36Sopenharmony_ci if (priv->encryption_level > 0) 192262306a36Sopenharmony_ci ext->alg = IW_ENCODE_ALG_WEP; 192362306a36Sopenharmony_ci else 192462306a36Sopenharmony_ci return -EINVAL; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci ext->key_len = priv->wep_key_len[idx]; 192762306a36Sopenharmony_ci memcpy(ext->key, priv->wep_keys[idx], ext->key_len); 192862306a36Sopenharmony_ci encoding->flags |= IW_ENCODE_ENABLED; 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci return 0; 193262306a36Sopenharmony_ci} 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_cistatic int atmel_set_auth(struct net_device *dev, 193562306a36Sopenharmony_ci struct iw_request_info *info, 193662306a36Sopenharmony_ci union iwreq_data *wrqu, char *extra) 193762306a36Sopenharmony_ci{ 193862306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 193962306a36Sopenharmony_ci struct iw_param *param = &wrqu->param; 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci switch (param->flags & IW_AUTH_INDEX) { 194262306a36Sopenharmony_ci case IW_AUTH_WPA_VERSION: 194362306a36Sopenharmony_ci case IW_AUTH_CIPHER_PAIRWISE: 194462306a36Sopenharmony_ci case IW_AUTH_CIPHER_GROUP: 194562306a36Sopenharmony_ci case IW_AUTH_KEY_MGMT: 194662306a36Sopenharmony_ci case IW_AUTH_RX_UNENCRYPTED_EAPOL: 194762306a36Sopenharmony_ci case IW_AUTH_PRIVACY_INVOKED: 194862306a36Sopenharmony_ci /* 194962306a36Sopenharmony_ci * atmel does not use these parameters 195062306a36Sopenharmony_ci */ 195162306a36Sopenharmony_ci break; 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci case IW_AUTH_DROP_UNENCRYPTED: 195462306a36Sopenharmony_ci priv->exclude_unencrypted = param->value ? 1 : 0; 195562306a36Sopenharmony_ci break; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci case IW_AUTH_80211_AUTH_ALG: { 195862306a36Sopenharmony_ci if (param->value & IW_AUTH_ALG_SHARED_KEY) { 195962306a36Sopenharmony_ci priv->exclude_unencrypted = 1; 196062306a36Sopenharmony_ci } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { 196162306a36Sopenharmony_ci priv->exclude_unencrypted = 0; 196262306a36Sopenharmony_ci } else 196362306a36Sopenharmony_ci return -EINVAL; 196462306a36Sopenharmony_ci break; 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci case IW_AUTH_WPA_ENABLED: 196862306a36Sopenharmony_ci /* Silently accept disable of WPA */ 196962306a36Sopenharmony_ci if (param->value > 0) 197062306a36Sopenharmony_ci return -EOPNOTSUPP; 197162306a36Sopenharmony_ci break; 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci default: 197462306a36Sopenharmony_ci return -EOPNOTSUPP; 197562306a36Sopenharmony_ci } 197662306a36Sopenharmony_ci return -EINPROGRESS; 197762306a36Sopenharmony_ci} 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_cistatic int atmel_get_auth(struct net_device *dev, 198062306a36Sopenharmony_ci struct iw_request_info *info, 198162306a36Sopenharmony_ci union iwreq_data *wrqu, char *extra) 198262306a36Sopenharmony_ci{ 198362306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 198462306a36Sopenharmony_ci struct iw_param *param = &wrqu->param; 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci switch (param->flags & IW_AUTH_INDEX) { 198762306a36Sopenharmony_ci case IW_AUTH_DROP_UNENCRYPTED: 198862306a36Sopenharmony_ci param->value = priv->exclude_unencrypted; 198962306a36Sopenharmony_ci break; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci case IW_AUTH_80211_AUTH_ALG: 199262306a36Sopenharmony_ci if (priv->exclude_unencrypted == 1) 199362306a36Sopenharmony_ci param->value = IW_AUTH_ALG_SHARED_KEY; 199462306a36Sopenharmony_ci else 199562306a36Sopenharmony_ci param->value = IW_AUTH_ALG_OPEN_SYSTEM; 199662306a36Sopenharmony_ci break; 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci case IW_AUTH_WPA_ENABLED: 199962306a36Sopenharmony_ci param->value = 0; 200062306a36Sopenharmony_ci break; 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci default: 200362306a36Sopenharmony_ci return -EOPNOTSUPP; 200462306a36Sopenharmony_ci } 200562306a36Sopenharmony_ci return 0; 200662306a36Sopenharmony_ci} 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_cistatic int atmel_get_name(struct net_device *dev, 201062306a36Sopenharmony_ci struct iw_request_info *info, 201162306a36Sopenharmony_ci union iwreq_data *wrqu, 201262306a36Sopenharmony_ci char *extra) 201362306a36Sopenharmony_ci{ 201462306a36Sopenharmony_ci strcpy(wrqu->name, "IEEE 802.11-DS"); 201562306a36Sopenharmony_ci return 0; 201662306a36Sopenharmony_ci} 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_cistatic int atmel_set_rate(struct net_device *dev, 201962306a36Sopenharmony_ci struct iw_request_info *info, 202062306a36Sopenharmony_ci union iwreq_data *wrqu, 202162306a36Sopenharmony_ci char *extra) 202262306a36Sopenharmony_ci{ 202362306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->bitrate; 202462306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci if (vwrq->fixed == 0) { 202762306a36Sopenharmony_ci priv->tx_rate = 3; 202862306a36Sopenharmony_ci priv->auto_tx_rate = 1; 202962306a36Sopenharmony_ci } else { 203062306a36Sopenharmony_ci priv->auto_tx_rate = 0; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci /* Which type of value ? */ 203362306a36Sopenharmony_ci if ((vwrq->value < 4) && (vwrq->value >= 0)) { 203462306a36Sopenharmony_ci /* Setting by rate index */ 203562306a36Sopenharmony_ci priv->tx_rate = vwrq->value; 203662306a36Sopenharmony_ci } else { 203762306a36Sopenharmony_ci /* Setting by frequency value */ 203862306a36Sopenharmony_ci switch (vwrq->value) { 203962306a36Sopenharmony_ci case 1000000: 204062306a36Sopenharmony_ci priv->tx_rate = 0; 204162306a36Sopenharmony_ci break; 204262306a36Sopenharmony_ci case 2000000: 204362306a36Sopenharmony_ci priv->tx_rate = 1; 204462306a36Sopenharmony_ci break; 204562306a36Sopenharmony_ci case 5500000: 204662306a36Sopenharmony_ci priv->tx_rate = 2; 204762306a36Sopenharmony_ci break; 204862306a36Sopenharmony_ci case 11000000: 204962306a36Sopenharmony_ci priv->tx_rate = 3; 205062306a36Sopenharmony_ci break; 205162306a36Sopenharmony_ci default: 205262306a36Sopenharmony_ci return -EINVAL; 205362306a36Sopenharmony_ci } 205462306a36Sopenharmony_ci } 205562306a36Sopenharmony_ci } 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci return -EINPROGRESS; 205862306a36Sopenharmony_ci} 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_cistatic int atmel_set_mode(struct net_device *dev, 206162306a36Sopenharmony_ci struct iw_request_info *info, 206262306a36Sopenharmony_ci union iwreq_data *wrqu, 206362306a36Sopenharmony_ci char *extra) 206462306a36Sopenharmony_ci{ 206562306a36Sopenharmony_ci __u32 *uwrq = &wrqu->mode; 206662306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci if (*uwrq != IW_MODE_ADHOC && *uwrq != IW_MODE_INFRA) 206962306a36Sopenharmony_ci return -EINVAL; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci priv->operating_mode = *uwrq; 207262306a36Sopenharmony_ci return -EINPROGRESS; 207362306a36Sopenharmony_ci} 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_cistatic int atmel_get_mode(struct net_device *dev, 207662306a36Sopenharmony_ci struct iw_request_info *info, 207762306a36Sopenharmony_ci union iwreq_data *wrqu, 207862306a36Sopenharmony_ci char *extra) 207962306a36Sopenharmony_ci{ 208062306a36Sopenharmony_ci __u32 *uwrq = &wrqu->mode; 208162306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci *uwrq = priv->operating_mode; 208462306a36Sopenharmony_ci return 0; 208562306a36Sopenharmony_ci} 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_cistatic int atmel_get_rate(struct net_device *dev, 208862306a36Sopenharmony_ci struct iw_request_info *info, 208962306a36Sopenharmony_ci union iwreq_data *wrqu, 209062306a36Sopenharmony_ci char *extra) 209162306a36Sopenharmony_ci{ 209262306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->bitrate; 209362306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci if (priv->auto_tx_rate) { 209662306a36Sopenharmony_ci vwrq->fixed = 0; 209762306a36Sopenharmony_ci vwrq->value = 11000000; 209862306a36Sopenharmony_ci } else { 209962306a36Sopenharmony_ci vwrq->fixed = 1; 210062306a36Sopenharmony_ci switch (priv->tx_rate) { 210162306a36Sopenharmony_ci case 0: 210262306a36Sopenharmony_ci vwrq->value = 1000000; 210362306a36Sopenharmony_ci break; 210462306a36Sopenharmony_ci case 1: 210562306a36Sopenharmony_ci vwrq->value = 2000000; 210662306a36Sopenharmony_ci break; 210762306a36Sopenharmony_ci case 2: 210862306a36Sopenharmony_ci vwrq->value = 5500000; 210962306a36Sopenharmony_ci break; 211062306a36Sopenharmony_ci case 3: 211162306a36Sopenharmony_ci vwrq->value = 11000000; 211262306a36Sopenharmony_ci break; 211362306a36Sopenharmony_ci } 211462306a36Sopenharmony_ci } 211562306a36Sopenharmony_ci return 0; 211662306a36Sopenharmony_ci} 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_cistatic int atmel_set_power(struct net_device *dev, 211962306a36Sopenharmony_ci struct iw_request_info *info, 212062306a36Sopenharmony_ci union iwreq_data *wrqu, 212162306a36Sopenharmony_ci char *extra) 212262306a36Sopenharmony_ci{ 212362306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->power; 212462306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 212562306a36Sopenharmony_ci priv->power_mode = vwrq->disabled ? 0 : 1; 212662306a36Sopenharmony_ci return -EINPROGRESS; 212762306a36Sopenharmony_ci} 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_cistatic int atmel_get_power(struct net_device *dev, 213062306a36Sopenharmony_ci struct iw_request_info *info, 213162306a36Sopenharmony_ci union iwreq_data *wrqu, 213262306a36Sopenharmony_ci char *extra) 213362306a36Sopenharmony_ci{ 213462306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->power; 213562306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 213662306a36Sopenharmony_ci vwrq->disabled = priv->power_mode ? 0 : 1; 213762306a36Sopenharmony_ci vwrq->flags = IW_POWER_ON; 213862306a36Sopenharmony_ci return 0; 213962306a36Sopenharmony_ci} 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_cistatic int atmel_set_retry(struct net_device *dev, 214262306a36Sopenharmony_ci struct iw_request_info *info, 214362306a36Sopenharmony_ci union iwreq_data *wrqu, 214462306a36Sopenharmony_ci char *extra) 214562306a36Sopenharmony_ci{ 214662306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->retry; 214762306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) { 215062306a36Sopenharmony_ci if (vwrq->flags & IW_RETRY_LONG) 215162306a36Sopenharmony_ci priv->long_retry = vwrq->value; 215262306a36Sopenharmony_ci else if (vwrq->flags & IW_RETRY_SHORT) 215362306a36Sopenharmony_ci priv->short_retry = vwrq->value; 215462306a36Sopenharmony_ci else { 215562306a36Sopenharmony_ci /* No modifier : set both */ 215662306a36Sopenharmony_ci priv->long_retry = vwrq->value; 215762306a36Sopenharmony_ci priv->short_retry = vwrq->value; 215862306a36Sopenharmony_ci } 215962306a36Sopenharmony_ci return -EINPROGRESS; 216062306a36Sopenharmony_ci } 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci return -EINVAL; 216362306a36Sopenharmony_ci} 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_cistatic int atmel_get_retry(struct net_device *dev, 216662306a36Sopenharmony_ci struct iw_request_info *info, 216762306a36Sopenharmony_ci union iwreq_data *wrqu, 216862306a36Sopenharmony_ci char *extra) 216962306a36Sopenharmony_ci{ 217062306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->retry; 217162306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci vwrq->disabled = 0; /* Can't be disabled */ 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci /* Note : by default, display the short retry number */ 217662306a36Sopenharmony_ci if (vwrq->flags & IW_RETRY_LONG) { 217762306a36Sopenharmony_ci vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; 217862306a36Sopenharmony_ci vwrq->value = priv->long_retry; 217962306a36Sopenharmony_ci } else { 218062306a36Sopenharmony_ci vwrq->flags = IW_RETRY_LIMIT; 218162306a36Sopenharmony_ci vwrq->value = priv->short_retry; 218262306a36Sopenharmony_ci if (priv->long_retry != priv->short_retry) 218362306a36Sopenharmony_ci vwrq->flags |= IW_RETRY_SHORT; 218462306a36Sopenharmony_ci } 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci return 0; 218762306a36Sopenharmony_ci} 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_cistatic int atmel_set_rts(struct net_device *dev, 219062306a36Sopenharmony_ci struct iw_request_info *info, 219162306a36Sopenharmony_ci union iwreq_data *wrqu, 219262306a36Sopenharmony_ci char *extra) 219362306a36Sopenharmony_ci{ 219462306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->rts; 219562306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 219662306a36Sopenharmony_ci int rthr = vwrq->value; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci if (vwrq->disabled) 219962306a36Sopenharmony_ci rthr = 2347; 220062306a36Sopenharmony_ci if ((rthr < 0) || (rthr > 2347)) { 220162306a36Sopenharmony_ci return -EINVAL; 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci priv->rts_threshold = rthr; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 220662306a36Sopenharmony_ci} 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_cistatic int atmel_get_rts(struct net_device *dev, 220962306a36Sopenharmony_ci struct iw_request_info *info, 221062306a36Sopenharmony_ci union iwreq_data *wrqu, 221162306a36Sopenharmony_ci char *extra) 221262306a36Sopenharmony_ci{ 221362306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->rts; 221462306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci vwrq->value = priv->rts_threshold; 221762306a36Sopenharmony_ci vwrq->disabled = (vwrq->value >= 2347); 221862306a36Sopenharmony_ci vwrq->fixed = 1; 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci return 0; 222162306a36Sopenharmony_ci} 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_cistatic int atmel_set_frag(struct net_device *dev, 222462306a36Sopenharmony_ci struct iw_request_info *info, 222562306a36Sopenharmony_ci union iwreq_data *wrqu, 222662306a36Sopenharmony_ci char *extra) 222762306a36Sopenharmony_ci{ 222862306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->frag; 222962306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 223062306a36Sopenharmony_ci int fthr = vwrq->value; 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci if (vwrq->disabled) 223362306a36Sopenharmony_ci fthr = 2346; 223462306a36Sopenharmony_ci if ((fthr < 256) || (fthr > 2346)) { 223562306a36Sopenharmony_ci return -EINVAL; 223662306a36Sopenharmony_ci } 223762306a36Sopenharmony_ci fthr &= ~0x1; /* Get an even value - is it really needed ??? */ 223862306a36Sopenharmony_ci priv->frag_threshold = fthr; 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 224162306a36Sopenharmony_ci} 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_cistatic int atmel_get_frag(struct net_device *dev, 224462306a36Sopenharmony_ci struct iw_request_info *info, 224562306a36Sopenharmony_ci union iwreq_data *wrqu, 224662306a36Sopenharmony_ci char *extra) 224762306a36Sopenharmony_ci{ 224862306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->frag; 224962306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci vwrq->value = priv->frag_threshold; 225262306a36Sopenharmony_ci vwrq->disabled = (vwrq->value >= 2346); 225362306a36Sopenharmony_ci vwrq->fixed = 1; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci return 0; 225662306a36Sopenharmony_ci} 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_cistatic int atmel_set_freq(struct net_device *dev, 225962306a36Sopenharmony_ci struct iw_request_info *info, 226062306a36Sopenharmony_ci union iwreq_data *wrqu, 226162306a36Sopenharmony_ci char *extra) 226262306a36Sopenharmony_ci{ 226362306a36Sopenharmony_ci struct iw_freq *fwrq = &wrqu->freq; 226462306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 226562306a36Sopenharmony_ci int rc = -EINPROGRESS; /* Call commit handler */ 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci /* If setting by frequency, convert to a channel */ 226862306a36Sopenharmony_ci if (fwrq->e == 1) { 226962306a36Sopenharmony_ci int f = fwrq->m / 100000; 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci /* Hack to fall through... */ 227262306a36Sopenharmony_ci fwrq->e = 0; 227362306a36Sopenharmony_ci fwrq->m = ieee80211_frequency_to_channel(f); 227462306a36Sopenharmony_ci } 227562306a36Sopenharmony_ci /* Setting by channel number */ 227662306a36Sopenharmony_ci if (fwrq->m < 0 || fwrq->m > 1000 || fwrq->e > 0) 227762306a36Sopenharmony_ci rc = -EOPNOTSUPP; 227862306a36Sopenharmony_ci else { 227962306a36Sopenharmony_ci int channel = fwrq->m; 228062306a36Sopenharmony_ci if (atmel_validate_channel(priv, channel) == 0) { 228162306a36Sopenharmony_ci priv->channel = channel; 228262306a36Sopenharmony_ci } else { 228362306a36Sopenharmony_ci rc = -EINVAL; 228462306a36Sopenharmony_ci } 228562306a36Sopenharmony_ci } 228662306a36Sopenharmony_ci return rc; 228762306a36Sopenharmony_ci} 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_cistatic int atmel_get_freq(struct net_device *dev, 229062306a36Sopenharmony_ci struct iw_request_info *info, 229162306a36Sopenharmony_ci union iwreq_data *wrqu, 229262306a36Sopenharmony_ci char *extra) 229362306a36Sopenharmony_ci{ 229462306a36Sopenharmony_ci struct iw_freq *fwrq = &wrqu->freq; 229562306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci fwrq->m = priv->channel; 229862306a36Sopenharmony_ci fwrq->e = 0; 229962306a36Sopenharmony_ci return 0; 230062306a36Sopenharmony_ci} 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_cistatic int atmel_set_scan(struct net_device *dev, 230362306a36Sopenharmony_ci struct iw_request_info *info, 230462306a36Sopenharmony_ci union iwreq_data *dwrq, 230562306a36Sopenharmony_ci char *extra) 230662306a36Sopenharmony_ci{ 230762306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 230862306a36Sopenharmony_ci unsigned long flags; 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci /* Note : you may have realised that, as this is a SET operation, 231162306a36Sopenharmony_ci * this is privileged and therefore a normal user can't 231262306a36Sopenharmony_ci * perform scanning. 231362306a36Sopenharmony_ci * This is not an error, while the device perform scanning, 231462306a36Sopenharmony_ci * traffic doesn't flow, so it's a perfect DoS... 231562306a36Sopenharmony_ci * Jean II */ 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci if (priv->station_state == STATION_STATE_DOWN) 231862306a36Sopenharmony_ci return -EAGAIN; 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ci /* Timeout old surveys. */ 232162306a36Sopenharmony_ci if (time_after(jiffies, priv->last_survey + 20 * HZ)) 232262306a36Sopenharmony_ci priv->site_survey_state = SITE_SURVEY_IDLE; 232362306a36Sopenharmony_ci priv->last_survey = jiffies; 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci /* Initiate a scan command */ 232662306a36Sopenharmony_ci if (priv->site_survey_state == SITE_SURVEY_IN_PROGRESS) 232762306a36Sopenharmony_ci return -EBUSY; 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci del_timer_sync(&priv->management_timer); 233062306a36Sopenharmony_ci spin_lock_irqsave(&priv->irqlock, flags); 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci priv->site_survey_state = SITE_SURVEY_IN_PROGRESS; 233362306a36Sopenharmony_ci priv->fast_scan = 0; 233462306a36Sopenharmony_ci atmel_scan(priv, 0); 233562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_ci return 0; 233862306a36Sopenharmony_ci} 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_cistatic int atmel_get_scan(struct net_device *dev, 234162306a36Sopenharmony_ci struct iw_request_info *info, 234262306a36Sopenharmony_ci union iwreq_data *wrqu, 234362306a36Sopenharmony_ci char *extra) 234462306a36Sopenharmony_ci{ 234562306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->data; 234662306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 234762306a36Sopenharmony_ci int i; 234862306a36Sopenharmony_ci char *current_ev = extra; 234962306a36Sopenharmony_ci struct iw_event iwe; 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci if (priv->site_survey_state != SITE_SURVEY_COMPLETED) 235262306a36Sopenharmony_ci return -EAGAIN; 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci for (i = 0; i < priv->BSS_list_entries; i++) { 235562306a36Sopenharmony_ci iwe.cmd = SIOCGIWAP; 235662306a36Sopenharmony_ci iwe.u.ap_addr.sa_family = ARPHRD_ETHER; 235762306a36Sopenharmony_ci memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN); 235862306a36Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, 235962306a36Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 236062306a36Sopenharmony_ci &iwe, IW_EV_ADDR_LEN); 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci iwe.u.data.length = priv->BSSinfo[i].SSIDsize; 236362306a36Sopenharmony_ci if (iwe.u.data.length > 32) 236462306a36Sopenharmony_ci iwe.u.data.length = 32; 236562306a36Sopenharmony_ci iwe.cmd = SIOCGIWESSID; 236662306a36Sopenharmony_ci iwe.u.data.flags = 1; 236762306a36Sopenharmony_ci current_ev = iwe_stream_add_point(info, current_ev, 236862306a36Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 236962306a36Sopenharmony_ci &iwe, priv->BSSinfo[i].SSID); 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci iwe.cmd = SIOCGIWMODE; 237262306a36Sopenharmony_ci iwe.u.mode = priv->BSSinfo[i].BSStype; 237362306a36Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, 237462306a36Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 237562306a36Sopenharmony_ci &iwe, IW_EV_UINT_LEN); 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci iwe.cmd = SIOCGIWFREQ; 237862306a36Sopenharmony_ci iwe.u.freq.m = priv->BSSinfo[i].channel; 237962306a36Sopenharmony_ci iwe.u.freq.e = 0; 238062306a36Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, 238162306a36Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 238262306a36Sopenharmony_ci &iwe, IW_EV_FREQ_LEN); 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci /* Add quality statistics */ 238562306a36Sopenharmony_ci iwe.cmd = IWEVQUAL; 238662306a36Sopenharmony_ci iwe.u.qual.level = priv->BSSinfo[i].RSSI; 238762306a36Sopenharmony_ci iwe.u.qual.qual = iwe.u.qual.level; 238862306a36Sopenharmony_ci /* iwe.u.qual.noise = SOMETHING */ 238962306a36Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, 239062306a36Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 239162306a36Sopenharmony_ci &iwe, IW_EV_QUAL_LEN); 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci iwe.cmd = SIOCGIWENCODE; 239562306a36Sopenharmony_ci if (priv->BSSinfo[i].UsingWEP) 239662306a36Sopenharmony_ci iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; 239762306a36Sopenharmony_ci else 239862306a36Sopenharmony_ci iwe.u.data.flags = IW_ENCODE_DISABLED; 239962306a36Sopenharmony_ci iwe.u.data.length = 0; 240062306a36Sopenharmony_ci current_ev = iwe_stream_add_point(info, current_ev, 240162306a36Sopenharmony_ci extra + IW_SCAN_MAX_DATA, 240262306a36Sopenharmony_ci &iwe, NULL); 240362306a36Sopenharmony_ci } 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci /* Length of data */ 240662306a36Sopenharmony_ci dwrq->length = (current_ev - extra); 240762306a36Sopenharmony_ci dwrq->flags = 0; 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ci return 0; 241062306a36Sopenharmony_ci} 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_cistatic int atmel_get_range(struct net_device *dev, 241362306a36Sopenharmony_ci struct iw_request_info *info, 241462306a36Sopenharmony_ci union iwreq_data *wrqu, 241562306a36Sopenharmony_ci char *extra) 241662306a36Sopenharmony_ci{ 241762306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->data; 241862306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 241962306a36Sopenharmony_ci struct iw_range *range = (struct iw_range *) extra; 242062306a36Sopenharmony_ci int k, i, j; 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci dwrq->length = sizeof(struct iw_range); 242362306a36Sopenharmony_ci memset(range, 0, sizeof(struct iw_range)); 242462306a36Sopenharmony_ci range->min_nwid = 0x0000; 242562306a36Sopenharmony_ci range->max_nwid = 0x0000; 242662306a36Sopenharmony_ci range->num_channels = 0; 242762306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(channel_table); j++) 242862306a36Sopenharmony_ci if (priv->reg_domain == channel_table[j].reg_domain) { 242962306a36Sopenharmony_ci range->num_channels = channel_table[j].max - channel_table[j].min + 1; 243062306a36Sopenharmony_ci break; 243162306a36Sopenharmony_ci } 243262306a36Sopenharmony_ci if (range->num_channels != 0) { 243362306a36Sopenharmony_ci for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) { 243462306a36Sopenharmony_ci range->freq[k].i = i; /* List index */ 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci /* Values in MHz -> * 10^5 * 10 */ 243762306a36Sopenharmony_ci range->freq[k].m = 100000 * 243862306a36Sopenharmony_ci ieee80211_channel_to_frequency(i, NL80211_BAND_2GHZ); 243962306a36Sopenharmony_ci range->freq[k++].e = 1; 244062306a36Sopenharmony_ci } 244162306a36Sopenharmony_ci range->num_frequency = k; 244262306a36Sopenharmony_ci } 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci range->max_qual.qual = 100; 244562306a36Sopenharmony_ci range->max_qual.level = 100; 244662306a36Sopenharmony_ci range->max_qual.noise = 0; 244762306a36Sopenharmony_ci range->max_qual.updated = IW_QUAL_NOISE_INVALID; 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci range->avg_qual.qual = 50; 245062306a36Sopenharmony_ci range->avg_qual.level = 50; 245162306a36Sopenharmony_ci range->avg_qual.noise = 0; 245262306a36Sopenharmony_ci range->avg_qual.updated = IW_QUAL_NOISE_INVALID; 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_ci range->sensitivity = 0; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci range->bitrate[0] = 1000000; 245762306a36Sopenharmony_ci range->bitrate[1] = 2000000; 245862306a36Sopenharmony_ci range->bitrate[2] = 5500000; 245962306a36Sopenharmony_ci range->bitrate[3] = 11000000; 246062306a36Sopenharmony_ci range->num_bitrates = 4; 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci range->min_rts = 0; 246362306a36Sopenharmony_ci range->max_rts = 2347; 246462306a36Sopenharmony_ci range->min_frag = 256; 246562306a36Sopenharmony_ci range->max_frag = 2346; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci range->encoding_size[0] = 5; 246862306a36Sopenharmony_ci range->encoding_size[1] = 13; 246962306a36Sopenharmony_ci range->num_encoding_sizes = 2; 247062306a36Sopenharmony_ci range->max_encoding_tokens = 4; 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci range->pmp_flags = IW_POWER_ON; 247362306a36Sopenharmony_ci range->pmt_flags = IW_POWER_ON; 247462306a36Sopenharmony_ci range->pm_capa = 0; 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci range->we_version_source = WIRELESS_EXT; 247762306a36Sopenharmony_ci range->we_version_compiled = WIRELESS_EXT; 247862306a36Sopenharmony_ci range->retry_capa = IW_RETRY_LIMIT ; 247962306a36Sopenharmony_ci range->retry_flags = IW_RETRY_LIMIT; 248062306a36Sopenharmony_ci range->r_time_flags = 0; 248162306a36Sopenharmony_ci range->min_retry = 1; 248262306a36Sopenharmony_ci range->max_retry = 65535; 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci return 0; 248562306a36Sopenharmony_ci} 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_cistatic int atmel_set_wap(struct net_device *dev, 248862306a36Sopenharmony_ci struct iw_request_info *info, 248962306a36Sopenharmony_ci union iwreq_data *wrqu, 249062306a36Sopenharmony_ci char *extra) 249162306a36Sopenharmony_ci{ 249262306a36Sopenharmony_ci struct sockaddr *awrq = &wrqu->ap_addr; 249362306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 249462306a36Sopenharmony_ci int i; 249562306a36Sopenharmony_ci static const u8 any[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 249662306a36Sopenharmony_ci static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 249762306a36Sopenharmony_ci unsigned long flags; 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci if (awrq->sa_family != ARPHRD_ETHER) 250062306a36Sopenharmony_ci return -EINVAL; 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci if (!memcmp(any, awrq->sa_data, 6) || 250362306a36Sopenharmony_ci !memcmp(off, awrq->sa_data, 6)) { 250462306a36Sopenharmony_ci del_timer_sync(&priv->management_timer); 250562306a36Sopenharmony_ci spin_lock_irqsave(&priv->irqlock, flags); 250662306a36Sopenharmony_ci atmel_scan(priv, 1); 250762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 250862306a36Sopenharmony_ci return 0; 250962306a36Sopenharmony_ci } 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci for (i = 0; i < priv->BSS_list_entries; i++) { 251262306a36Sopenharmony_ci if (memcmp(priv->BSSinfo[i].BSSID, awrq->sa_data, 6) == 0) { 251362306a36Sopenharmony_ci if (!priv->wep_is_on && priv->BSSinfo[i].UsingWEP) { 251462306a36Sopenharmony_ci return -EINVAL; 251562306a36Sopenharmony_ci } else if (priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) { 251662306a36Sopenharmony_ci return -EINVAL; 251762306a36Sopenharmony_ci } else { 251862306a36Sopenharmony_ci del_timer_sync(&priv->management_timer); 251962306a36Sopenharmony_ci spin_lock_irqsave(&priv->irqlock, flags); 252062306a36Sopenharmony_ci atmel_join_bss(priv, i); 252162306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 252262306a36Sopenharmony_ci return 0; 252362306a36Sopenharmony_ci } 252462306a36Sopenharmony_ci } 252562306a36Sopenharmony_ci } 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci return -EINVAL; 252862306a36Sopenharmony_ci} 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_cistatic int atmel_config_commit(struct net_device *dev, 253162306a36Sopenharmony_ci struct iw_request_info *info, /* NULL */ 253262306a36Sopenharmony_ci union iwreq_data *zwrq, /* NULL */ 253362306a36Sopenharmony_ci char *extra) /* NULL */ 253462306a36Sopenharmony_ci{ 253562306a36Sopenharmony_ci return atmel_open(dev); 253662306a36Sopenharmony_ci} 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_cistatic const iw_handler atmel_handler[] = 253962306a36Sopenharmony_ci{ 254062306a36Sopenharmony_ci IW_HANDLER(SIOCSIWCOMMIT, atmel_config_commit), 254162306a36Sopenharmony_ci IW_HANDLER(SIOCGIWNAME, atmel_get_name), 254262306a36Sopenharmony_ci IW_HANDLER(SIOCSIWFREQ, atmel_set_freq), 254362306a36Sopenharmony_ci IW_HANDLER(SIOCGIWFREQ, atmel_get_freq), 254462306a36Sopenharmony_ci IW_HANDLER(SIOCSIWMODE, atmel_set_mode), 254562306a36Sopenharmony_ci IW_HANDLER(SIOCGIWMODE, atmel_get_mode), 254662306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRANGE, atmel_get_range), 254762306a36Sopenharmony_ci IW_HANDLER(SIOCSIWAP, atmel_set_wap), 254862306a36Sopenharmony_ci IW_HANDLER(SIOCGIWAP, atmel_get_wap), 254962306a36Sopenharmony_ci IW_HANDLER(SIOCSIWSCAN, atmel_set_scan), 255062306a36Sopenharmony_ci IW_HANDLER(SIOCGIWSCAN, atmel_get_scan), 255162306a36Sopenharmony_ci IW_HANDLER(SIOCSIWESSID, atmel_set_essid), 255262306a36Sopenharmony_ci IW_HANDLER(SIOCGIWESSID, atmel_get_essid), 255362306a36Sopenharmony_ci IW_HANDLER(SIOCSIWRATE, atmel_set_rate), 255462306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRATE, atmel_get_rate), 255562306a36Sopenharmony_ci IW_HANDLER(SIOCSIWRTS, atmel_set_rts), 255662306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRTS, atmel_get_rts), 255762306a36Sopenharmony_ci IW_HANDLER(SIOCSIWFRAG, atmel_set_frag), 255862306a36Sopenharmony_ci IW_HANDLER(SIOCGIWFRAG, atmel_get_frag), 255962306a36Sopenharmony_ci IW_HANDLER(SIOCSIWRETRY, atmel_set_retry), 256062306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRETRY, atmel_get_retry), 256162306a36Sopenharmony_ci IW_HANDLER(SIOCSIWENCODE, atmel_set_encode), 256262306a36Sopenharmony_ci IW_HANDLER(SIOCGIWENCODE, atmel_get_encode), 256362306a36Sopenharmony_ci IW_HANDLER(SIOCSIWPOWER, atmel_set_power), 256462306a36Sopenharmony_ci IW_HANDLER(SIOCGIWPOWER, atmel_get_power), 256562306a36Sopenharmony_ci IW_HANDLER(SIOCSIWAUTH, atmel_set_auth), 256662306a36Sopenharmony_ci IW_HANDLER(SIOCGIWAUTH, atmel_get_auth), 256762306a36Sopenharmony_ci IW_HANDLER(SIOCSIWENCODEEXT, atmel_set_encodeext), 256862306a36Sopenharmony_ci IW_HANDLER(SIOCGIWENCODEEXT, atmel_get_encodeext), 256962306a36Sopenharmony_ci}; 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_cistatic const iw_handler atmel_private_handler[] = 257262306a36Sopenharmony_ci{ 257362306a36Sopenharmony_ci NULL, /* SIOCIWFIRSTPRIV */ 257462306a36Sopenharmony_ci}; 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_cistruct atmel_priv_ioctl { 257762306a36Sopenharmony_ci char id[32]; 257862306a36Sopenharmony_ci unsigned char __user *data; 257962306a36Sopenharmony_ci unsigned short len; 258062306a36Sopenharmony_ci}; 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci#define ATMELFWL SIOCIWFIRSTPRIV 258362306a36Sopenharmony_ci#define ATMELIDIFC ATMELFWL + 1 258462306a36Sopenharmony_ci#define ATMELRD ATMELFWL + 2 258562306a36Sopenharmony_ci#define ATMELMAGIC 0x51807 258662306a36Sopenharmony_ci#define REGDOMAINSZ 20 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_cistatic const struct iw_priv_args atmel_private_args[] = { 258962306a36Sopenharmony_ci { 259062306a36Sopenharmony_ci .cmd = ATMELFWL, 259162306a36Sopenharmony_ci .set_args = IW_PRIV_TYPE_BYTE 259262306a36Sopenharmony_ci | IW_PRIV_SIZE_FIXED 259362306a36Sopenharmony_ci | sizeof(struct atmel_priv_ioctl), 259462306a36Sopenharmony_ci .get_args = IW_PRIV_TYPE_NONE, 259562306a36Sopenharmony_ci .name = "atmelfwl" 259662306a36Sopenharmony_ci }, { 259762306a36Sopenharmony_ci .cmd = ATMELIDIFC, 259862306a36Sopenharmony_ci .set_args = IW_PRIV_TYPE_NONE, 259962306a36Sopenharmony_ci .get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 260062306a36Sopenharmony_ci .name = "atmelidifc" 260162306a36Sopenharmony_ci }, { 260262306a36Sopenharmony_ci .cmd = ATMELRD, 260362306a36Sopenharmony_ci .set_args = IW_PRIV_TYPE_CHAR | REGDOMAINSZ, 260462306a36Sopenharmony_ci .get_args = IW_PRIV_TYPE_NONE, 260562306a36Sopenharmony_ci .name = "regdomain" 260662306a36Sopenharmony_ci }, 260762306a36Sopenharmony_ci}; 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_cistatic const struct iw_handler_def atmel_handler_def = { 261062306a36Sopenharmony_ci .num_standard = ARRAY_SIZE(atmel_handler), 261162306a36Sopenharmony_ci .num_private = ARRAY_SIZE(atmel_private_handler), 261262306a36Sopenharmony_ci .num_private_args = ARRAY_SIZE(atmel_private_args), 261362306a36Sopenharmony_ci .standard = atmel_handler, 261462306a36Sopenharmony_ci .private = atmel_private_handler, 261562306a36Sopenharmony_ci .private_args = (struct iw_priv_args *) atmel_private_args, 261662306a36Sopenharmony_ci .get_wireless_stats = atmel_get_wireless_stats 261762306a36Sopenharmony_ci}; 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_cistatic int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 262062306a36Sopenharmony_ci{ 262162306a36Sopenharmony_ci int i, rc = 0; 262262306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 262362306a36Sopenharmony_ci struct atmel_priv_ioctl com; 262462306a36Sopenharmony_ci struct iwreq *wrq = (struct iwreq *) rq; 262562306a36Sopenharmony_ci unsigned char *new_firmware; 262662306a36Sopenharmony_ci char domain[REGDOMAINSZ + 1]; 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci switch (cmd) { 262962306a36Sopenharmony_ci case ATMELIDIFC: 263062306a36Sopenharmony_ci wrq->u.param.value = ATMELMAGIC; 263162306a36Sopenharmony_ci break; 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_ci case ATMELFWL: 263462306a36Sopenharmony_ci if (copy_from_user(&com, rq->ifr_data, sizeof(com))) { 263562306a36Sopenharmony_ci rc = -EFAULT; 263662306a36Sopenharmony_ci break; 263762306a36Sopenharmony_ci } 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 264062306a36Sopenharmony_ci rc = -EPERM; 264162306a36Sopenharmony_ci break; 264262306a36Sopenharmony_ci } 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci new_firmware = memdup_user(com.data, com.len); 264562306a36Sopenharmony_ci if (IS_ERR(new_firmware)) { 264662306a36Sopenharmony_ci rc = PTR_ERR(new_firmware); 264762306a36Sopenharmony_ci break; 264862306a36Sopenharmony_ci } 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_ci kfree(priv->firmware); 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci priv->firmware = new_firmware; 265362306a36Sopenharmony_ci priv->firmware_length = com.len; 265462306a36Sopenharmony_ci strncpy(priv->firmware_id, com.id, 31); 265562306a36Sopenharmony_ci priv->firmware_id[31] = '\0'; 265662306a36Sopenharmony_ci break; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci case ATMELRD: 265962306a36Sopenharmony_ci if (copy_from_user(domain, rq->ifr_data, REGDOMAINSZ)) { 266062306a36Sopenharmony_ci rc = -EFAULT; 266162306a36Sopenharmony_ci break; 266262306a36Sopenharmony_ci } 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 266562306a36Sopenharmony_ci rc = -EPERM; 266662306a36Sopenharmony_ci break; 266762306a36Sopenharmony_ci } 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci domain[REGDOMAINSZ] = 0; 267062306a36Sopenharmony_ci rc = -EINVAL; 267162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(channel_table); i++) { 267262306a36Sopenharmony_ci if (!strcasecmp(channel_table[i].name, domain)) { 267362306a36Sopenharmony_ci priv->config_reg_domain = channel_table[i].reg_domain; 267462306a36Sopenharmony_ci rc = 0; 267562306a36Sopenharmony_ci } 267662306a36Sopenharmony_ci } 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci if (rc == 0 && priv->station_state != STATION_STATE_DOWN) 267962306a36Sopenharmony_ci rc = atmel_open(dev); 268062306a36Sopenharmony_ci break; 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci default: 268362306a36Sopenharmony_ci rc = -EOPNOTSUPP; 268462306a36Sopenharmony_ci } 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_ci return rc; 268762306a36Sopenharmony_ci} 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_cistruct auth_body { 269062306a36Sopenharmony_ci __le16 alg; 269162306a36Sopenharmony_ci __le16 trans_seq; 269262306a36Sopenharmony_ci __le16 status; 269362306a36Sopenharmony_ci u8 el_id; 269462306a36Sopenharmony_ci u8 chall_text_len; 269562306a36Sopenharmony_ci u8 chall_text[253]; 269662306a36Sopenharmony_ci}; 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_cistatic void atmel_enter_state(struct atmel_private *priv, int new_state) 269962306a36Sopenharmony_ci{ 270062306a36Sopenharmony_ci int old_state = priv->station_state; 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_ci if (new_state == old_state) 270362306a36Sopenharmony_ci return; 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_ci priv->station_state = new_state; 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci if (new_state == STATION_STATE_READY) { 270862306a36Sopenharmony_ci netif_start_queue(priv->dev); 270962306a36Sopenharmony_ci netif_carrier_on(priv->dev); 271062306a36Sopenharmony_ci } 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_ci if (old_state == STATION_STATE_READY) { 271362306a36Sopenharmony_ci netif_carrier_off(priv->dev); 271462306a36Sopenharmony_ci if (netif_running(priv->dev)) 271562306a36Sopenharmony_ci netif_stop_queue(priv->dev); 271662306a36Sopenharmony_ci priv->last_beacon_timestamp = 0; 271762306a36Sopenharmony_ci } 271862306a36Sopenharmony_ci} 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_cistatic void atmel_scan(struct atmel_private *priv, int specific_ssid) 272162306a36Sopenharmony_ci{ 272262306a36Sopenharmony_ci struct { 272362306a36Sopenharmony_ci u8 BSSID[ETH_ALEN]; 272462306a36Sopenharmony_ci u8 SSID[MAX_SSID_LENGTH]; 272562306a36Sopenharmony_ci u8 scan_type; 272662306a36Sopenharmony_ci u8 channel; 272762306a36Sopenharmony_ci __le16 BSS_type; 272862306a36Sopenharmony_ci __le16 min_channel_time; 272962306a36Sopenharmony_ci __le16 max_channel_time; 273062306a36Sopenharmony_ci u8 options; 273162306a36Sopenharmony_ci u8 SSID_size; 273262306a36Sopenharmony_ci } cmd; 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci eth_broadcast_addr(cmd.BSSID); 273562306a36Sopenharmony_ci 273662306a36Sopenharmony_ci if (priv->fast_scan) { 273762306a36Sopenharmony_ci cmd.SSID_size = priv->SSID_size; 273862306a36Sopenharmony_ci memcpy(cmd.SSID, priv->SSID, priv->SSID_size); 273962306a36Sopenharmony_ci cmd.min_channel_time = cpu_to_le16(10); 274062306a36Sopenharmony_ci cmd.max_channel_time = cpu_to_le16(50); 274162306a36Sopenharmony_ci } else { 274262306a36Sopenharmony_ci priv->BSS_list_entries = 0; 274362306a36Sopenharmony_ci cmd.SSID_size = 0; 274462306a36Sopenharmony_ci cmd.min_channel_time = cpu_to_le16(10); 274562306a36Sopenharmony_ci cmd.max_channel_time = cpu_to_le16(120); 274662306a36Sopenharmony_ci } 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci cmd.options = 0; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci if (!specific_ssid) 275162306a36Sopenharmony_ci cmd.options |= SCAN_OPTIONS_SITE_SURVEY; 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci cmd.channel = (priv->channel & 0x7f); 275462306a36Sopenharmony_ci cmd.scan_type = SCAN_TYPE_ACTIVE; 275562306a36Sopenharmony_ci cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ? 275662306a36Sopenharmony_ci BSS_TYPE_AD_HOC : BSS_TYPE_INFRASTRUCTURE); 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci atmel_send_command(priv, CMD_Scan, &cmd, sizeof(cmd)); 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci /* This must come after all hardware access to avoid being messed up 276162306a36Sopenharmony_ci by stuff happening in interrupt context after we leave STATE_DOWN */ 276262306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_SCANNING); 276362306a36Sopenharmony_ci} 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_cistatic void join(struct atmel_private *priv, int type) 276662306a36Sopenharmony_ci{ 276762306a36Sopenharmony_ci struct { 276862306a36Sopenharmony_ci u8 BSSID[6]; 276962306a36Sopenharmony_ci u8 SSID[MAX_SSID_LENGTH]; 277062306a36Sopenharmony_ci u8 BSS_type; /* this is a short in a scan command - weird */ 277162306a36Sopenharmony_ci u8 channel; 277262306a36Sopenharmony_ci __le16 timeout; 277362306a36Sopenharmony_ci u8 SSID_size; 277462306a36Sopenharmony_ci u8 reserved; 277562306a36Sopenharmony_ci } cmd; 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci cmd.SSID_size = priv->SSID_size; 277862306a36Sopenharmony_ci memcpy(cmd.SSID, priv->SSID, priv->SSID_size); 277962306a36Sopenharmony_ci memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN); 278062306a36Sopenharmony_ci cmd.channel = (priv->channel & 0x7f); 278162306a36Sopenharmony_ci cmd.BSS_type = type; 278262306a36Sopenharmony_ci cmd.timeout = cpu_to_le16(2000); 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd)); 278562306a36Sopenharmony_ci} 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_cistatic void start(struct atmel_private *priv, int type) 278862306a36Sopenharmony_ci{ 278962306a36Sopenharmony_ci struct { 279062306a36Sopenharmony_ci u8 BSSID[6]; 279162306a36Sopenharmony_ci u8 SSID[MAX_SSID_LENGTH]; 279262306a36Sopenharmony_ci u8 BSS_type; 279362306a36Sopenharmony_ci u8 channel; 279462306a36Sopenharmony_ci u8 SSID_size; 279562306a36Sopenharmony_ci u8 reserved[3]; 279662306a36Sopenharmony_ci } cmd; 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ci cmd.SSID_size = priv->SSID_size; 279962306a36Sopenharmony_ci memcpy(cmd.SSID, priv->SSID, priv->SSID_size); 280062306a36Sopenharmony_ci memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN); 280162306a36Sopenharmony_ci cmd.BSS_type = type; 280262306a36Sopenharmony_ci cmd.channel = (priv->channel & 0x7f); 280362306a36Sopenharmony_ci 280462306a36Sopenharmony_ci atmel_send_command(priv, CMD_Start, &cmd, sizeof(cmd)); 280562306a36Sopenharmony_ci} 280662306a36Sopenharmony_ci 280762306a36Sopenharmony_cistatic void handle_beacon_probe(struct atmel_private *priv, u16 capability, 280862306a36Sopenharmony_ci u8 channel) 280962306a36Sopenharmony_ci{ 281062306a36Sopenharmony_ci int rejoin = 0; 281162306a36Sopenharmony_ci int new = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? 281262306a36Sopenharmony_ci SHORT_PREAMBLE : LONG_PREAMBLE; 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci if (priv->preamble != new) { 281562306a36Sopenharmony_ci priv->preamble = new; 281662306a36Sopenharmony_ci rejoin = 1; 281762306a36Sopenharmony_ci atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, new); 281862306a36Sopenharmony_ci } 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci if (priv->channel != channel) { 282162306a36Sopenharmony_ci priv->channel = channel; 282262306a36Sopenharmony_ci rejoin = 1; 282362306a36Sopenharmony_ci atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_CHANNEL_POS, channel); 282462306a36Sopenharmony_ci } 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci if (rejoin) { 282762306a36Sopenharmony_ci priv->station_is_associated = 0; 282862306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_JOINNING); 282962306a36Sopenharmony_ci 283062306a36Sopenharmony_ci if (priv->operating_mode == IW_MODE_INFRA) 283162306a36Sopenharmony_ci join(priv, BSS_TYPE_INFRASTRUCTURE); 283262306a36Sopenharmony_ci else 283362306a36Sopenharmony_ci join(priv, BSS_TYPE_AD_HOC); 283462306a36Sopenharmony_ci } 283562306a36Sopenharmony_ci} 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_cistatic void send_authentication_request(struct atmel_private *priv, u16 system, 283862306a36Sopenharmony_ci u8 *challenge, int challenge_len) 283962306a36Sopenharmony_ci{ 284062306a36Sopenharmony_ci struct ieee80211_hdr header; 284162306a36Sopenharmony_ci struct auth_body auth; 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_ci header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); 284462306a36Sopenharmony_ci header.duration_id = cpu_to_le16(0x8000); 284562306a36Sopenharmony_ci header.seq_ctrl = 0; 284662306a36Sopenharmony_ci memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); 284762306a36Sopenharmony_ci memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); 284862306a36Sopenharmony_ci memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1) 285162306a36Sopenharmony_ci /* no WEP for authentication frames with TrSeqNo 1 */ 285262306a36Sopenharmony_ci header.frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci auth.alg = cpu_to_le16(system); 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci auth.status = 0; 285762306a36Sopenharmony_ci auth.trans_seq = cpu_to_le16(priv->CurrentAuthentTransactionSeqNum); 285862306a36Sopenharmony_ci priv->ExpectedAuthentTransactionSeqNum = priv->CurrentAuthentTransactionSeqNum+1; 285962306a36Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum += 2; 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_ci if (challenge_len != 0) { 286262306a36Sopenharmony_ci auth.el_id = 16; /* challenge_text */ 286362306a36Sopenharmony_ci auth.chall_text_len = challenge_len; 286462306a36Sopenharmony_ci memcpy(auth.chall_text, challenge, challenge_len); 286562306a36Sopenharmony_ci atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 8 + challenge_len); 286662306a36Sopenharmony_ci } else { 286762306a36Sopenharmony_ci atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 6); 286862306a36Sopenharmony_ci } 286962306a36Sopenharmony_ci} 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_cistatic void send_association_request(struct atmel_private *priv, int is_reassoc) 287262306a36Sopenharmony_ci{ 287362306a36Sopenharmony_ci u8 *ssid_el_p; 287462306a36Sopenharmony_ci int bodysize; 287562306a36Sopenharmony_ci struct ieee80211_hdr header; 287662306a36Sopenharmony_ci struct ass_req_format { 287762306a36Sopenharmony_ci __le16 capability; 287862306a36Sopenharmony_ci __le16 listen_interval; 287962306a36Sopenharmony_ci u8 ap[ETH_ALEN]; /* nothing after here directly accessible */ 288062306a36Sopenharmony_ci u8 ssid_el_id; 288162306a36Sopenharmony_ci u8 ssid_len; 288262306a36Sopenharmony_ci u8 ssid[MAX_SSID_LENGTH]; 288362306a36Sopenharmony_ci u8 sup_rates_el_id; 288462306a36Sopenharmony_ci u8 sup_rates_len; 288562306a36Sopenharmony_ci u8 rates[4]; 288662306a36Sopenharmony_ci } body; 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 288962306a36Sopenharmony_ci (is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ)); 289062306a36Sopenharmony_ci header.duration_id = cpu_to_le16(0x8000); 289162306a36Sopenharmony_ci header.seq_ctrl = 0; 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); 289462306a36Sopenharmony_ci memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); 289562306a36Sopenharmony_ci memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_ci body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS); 289862306a36Sopenharmony_ci if (priv->wep_is_on) 289962306a36Sopenharmony_ci body.capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); 290062306a36Sopenharmony_ci if (priv->preamble == SHORT_PREAMBLE) 290162306a36Sopenharmony_ci body.capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci body.listen_interval = cpu_to_le16(priv->listen_interval * priv->beacon_period); 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_ci /* current AP address - only in reassoc frame */ 290662306a36Sopenharmony_ci if (is_reassoc) { 290762306a36Sopenharmony_ci memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN); 290862306a36Sopenharmony_ci ssid_el_p = &body.ssid_el_id; 290962306a36Sopenharmony_ci bodysize = 18 + priv->SSID_size; 291062306a36Sopenharmony_ci } else { 291162306a36Sopenharmony_ci ssid_el_p = &body.ap[0]; 291262306a36Sopenharmony_ci bodysize = 12 + priv->SSID_size; 291362306a36Sopenharmony_ci } 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci ssid_el_p[0] = WLAN_EID_SSID; 291662306a36Sopenharmony_ci ssid_el_p[1] = priv->SSID_size; 291762306a36Sopenharmony_ci memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size); 291862306a36Sopenharmony_ci ssid_el_p[2 + priv->SSID_size] = WLAN_EID_SUPP_RATES; 291962306a36Sopenharmony_ci ssid_el_p[3 + priv->SSID_size] = 4; /* len of supported rates */ 292062306a36Sopenharmony_ci memcpy(ssid_el_p + 4 + priv->SSID_size, atmel_basic_rates, 4); 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize); 292362306a36Sopenharmony_ci} 292462306a36Sopenharmony_ci 292562306a36Sopenharmony_cistatic int is_frame_from_current_bss(struct atmel_private *priv, 292662306a36Sopenharmony_ci struct ieee80211_hdr *header) 292762306a36Sopenharmony_ci{ 292862306a36Sopenharmony_ci if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) 292962306a36Sopenharmony_ci return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0; 293062306a36Sopenharmony_ci else 293162306a36Sopenharmony_ci return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0; 293262306a36Sopenharmony_ci} 293362306a36Sopenharmony_ci 293462306a36Sopenharmony_cistatic int retrieve_bss(struct atmel_private *priv) 293562306a36Sopenharmony_ci{ 293662306a36Sopenharmony_ci int i; 293762306a36Sopenharmony_ci int max_rssi = -128; 293862306a36Sopenharmony_ci int max_index = -1; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci if (priv->BSS_list_entries == 0) 294162306a36Sopenharmony_ci return -1; 294262306a36Sopenharmony_ci 294362306a36Sopenharmony_ci if (priv->connect_to_any_BSS) { 294462306a36Sopenharmony_ci /* Select a BSS with the max-RSSI but of the same type and of 294562306a36Sopenharmony_ci the same WEP mode and that it is not marked as 'bad' (i.e. 294662306a36Sopenharmony_ci we had previously failed to connect to this BSS with the 294762306a36Sopenharmony_ci settings that we currently use) */ 294862306a36Sopenharmony_ci priv->current_BSS = 0; 294962306a36Sopenharmony_ci for (i = 0; i < priv->BSS_list_entries; i++) { 295062306a36Sopenharmony_ci if (priv->operating_mode == priv->BSSinfo[i].BSStype && 295162306a36Sopenharmony_ci ((!priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) || 295262306a36Sopenharmony_ci (priv->wep_is_on && priv->BSSinfo[i].UsingWEP)) && 295362306a36Sopenharmony_ci !(priv->BSSinfo[i].channel & 0x80)) { 295462306a36Sopenharmony_ci max_rssi = priv->BSSinfo[i].RSSI; 295562306a36Sopenharmony_ci priv->current_BSS = max_index = i; 295662306a36Sopenharmony_ci } 295762306a36Sopenharmony_ci } 295862306a36Sopenharmony_ci return max_index; 295962306a36Sopenharmony_ci } 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci for (i = 0; i < priv->BSS_list_entries; i++) { 296262306a36Sopenharmony_ci if (priv->SSID_size == priv->BSSinfo[i].SSIDsize && 296362306a36Sopenharmony_ci memcmp(priv->SSID, priv->BSSinfo[i].SSID, priv->SSID_size) == 0 && 296462306a36Sopenharmony_ci priv->operating_mode == priv->BSSinfo[i].BSStype && 296562306a36Sopenharmony_ci atmel_validate_channel(priv, priv->BSSinfo[i].channel) == 0) { 296662306a36Sopenharmony_ci if (priv->BSSinfo[i].RSSI >= max_rssi) { 296762306a36Sopenharmony_ci max_rssi = priv->BSSinfo[i].RSSI; 296862306a36Sopenharmony_ci max_index = i; 296962306a36Sopenharmony_ci } 297062306a36Sopenharmony_ci } 297162306a36Sopenharmony_ci } 297262306a36Sopenharmony_ci return max_index; 297362306a36Sopenharmony_ci} 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_cistatic void store_bss_info(struct atmel_private *priv, 297662306a36Sopenharmony_ci struct ieee80211_hdr *header, u16 capability, 297762306a36Sopenharmony_ci u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len, 297862306a36Sopenharmony_ci u8 *ssid, int is_beacon) 297962306a36Sopenharmony_ci{ 298062306a36Sopenharmony_ci u8 *bss = capability & WLAN_CAPABILITY_ESS ? header->addr2 : header->addr3; 298162306a36Sopenharmony_ci int i, index; 298262306a36Sopenharmony_ci 298362306a36Sopenharmony_ci for (index = -1, i = 0; i < priv->BSS_list_entries; i++) 298462306a36Sopenharmony_ci if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0) 298562306a36Sopenharmony_ci index = i; 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci /* If we process a probe and an entry from this BSS exists 298862306a36Sopenharmony_ci we will update the BSS entry with the info from this BSS. 298962306a36Sopenharmony_ci If we process a beacon we will only update RSSI */ 299062306a36Sopenharmony_ci 299162306a36Sopenharmony_ci if (index == -1) { 299262306a36Sopenharmony_ci if (priv->BSS_list_entries == MAX_BSS_ENTRIES) 299362306a36Sopenharmony_ci return; 299462306a36Sopenharmony_ci index = priv->BSS_list_entries++; 299562306a36Sopenharmony_ci memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN); 299662306a36Sopenharmony_ci priv->BSSinfo[index].RSSI = rssi; 299762306a36Sopenharmony_ci } else { 299862306a36Sopenharmony_ci if (rssi > priv->BSSinfo[index].RSSI) 299962306a36Sopenharmony_ci priv->BSSinfo[index].RSSI = rssi; 300062306a36Sopenharmony_ci if (is_beacon) 300162306a36Sopenharmony_ci return; 300262306a36Sopenharmony_ci } 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci priv->BSSinfo[index].channel = channel; 300562306a36Sopenharmony_ci priv->BSSinfo[index].beacon_period = beacon_period; 300662306a36Sopenharmony_ci priv->BSSinfo[index].UsingWEP = capability & WLAN_CAPABILITY_PRIVACY; 300762306a36Sopenharmony_ci memcpy(priv->BSSinfo[index].SSID, ssid, ssid_len); 300862306a36Sopenharmony_ci priv->BSSinfo[index].SSIDsize = ssid_len; 300962306a36Sopenharmony_ci 301062306a36Sopenharmony_ci if (capability & WLAN_CAPABILITY_IBSS) 301162306a36Sopenharmony_ci priv->BSSinfo[index].BSStype = IW_MODE_ADHOC; 301262306a36Sopenharmony_ci else if (capability & WLAN_CAPABILITY_ESS) 301362306a36Sopenharmony_ci priv->BSSinfo[index].BSStype = IW_MODE_INFRA; 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_ci priv->BSSinfo[index].preamble = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? 301662306a36Sopenharmony_ci SHORT_PREAMBLE : LONG_PREAMBLE; 301762306a36Sopenharmony_ci} 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_cistatic void authenticate(struct atmel_private *priv, u16 frame_len) 302062306a36Sopenharmony_ci{ 302162306a36Sopenharmony_ci struct auth_body *auth = (struct auth_body *)priv->rx_buf; 302262306a36Sopenharmony_ci u16 status = le16_to_cpu(auth->status); 302362306a36Sopenharmony_ci u16 trans_seq_no = le16_to_cpu(auth->trans_seq); 302462306a36Sopenharmony_ci u16 system = le16_to_cpu(auth->alg); 302562306a36Sopenharmony_ci 302662306a36Sopenharmony_ci if (status == WLAN_STATUS_SUCCESS && !priv->wep_is_on) { 302762306a36Sopenharmony_ci /* no WEP */ 302862306a36Sopenharmony_ci if (priv->station_was_associated) { 302962306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_REASSOCIATING); 303062306a36Sopenharmony_ci send_association_request(priv, 1); 303162306a36Sopenharmony_ci return; 303262306a36Sopenharmony_ci } else { 303362306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_ASSOCIATING); 303462306a36Sopenharmony_ci send_association_request(priv, 0); 303562306a36Sopenharmony_ci return; 303662306a36Sopenharmony_ci } 303762306a36Sopenharmony_ci } 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_ci if (status == WLAN_STATUS_SUCCESS && priv->wep_is_on) { 304062306a36Sopenharmony_ci int should_associate = 0; 304162306a36Sopenharmony_ci /* WEP */ 304262306a36Sopenharmony_ci if (trans_seq_no != priv->ExpectedAuthentTransactionSeqNum) 304362306a36Sopenharmony_ci return; 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_ci if (system == WLAN_AUTH_OPEN) { 304662306a36Sopenharmony_ci if (trans_seq_no == 0x0002) { 304762306a36Sopenharmony_ci should_associate = 1; 304862306a36Sopenharmony_ci } 304962306a36Sopenharmony_ci } else if (system == WLAN_AUTH_SHARED_KEY) { 305062306a36Sopenharmony_ci if (trans_seq_no == 0x0002 && 305162306a36Sopenharmony_ci auth->el_id == WLAN_EID_CHALLENGE) { 305262306a36Sopenharmony_ci send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len); 305362306a36Sopenharmony_ci return; 305462306a36Sopenharmony_ci } else if (trans_seq_no == 0x0004) { 305562306a36Sopenharmony_ci should_associate = 1; 305662306a36Sopenharmony_ci } 305762306a36Sopenharmony_ci } 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci if (should_associate) { 306062306a36Sopenharmony_ci if (priv->station_was_associated) { 306162306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_REASSOCIATING); 306262306a36Sopenharmony_ci send_association_request(priv, 1); 306362306a36Sopenharmony_ci return; 306462306a36Sopenharmony_ci } else { 306562306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_ASSOCIATING); 306662306a36Sopenharmony_ci send_association_request(priv, 0); 306762306a36Sopenharmony_ci return; 306862306a36Sopenharmony_ci } 306962306a36Sopenharmony_ci } 307062306a36Sopenharmony_ci } 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci if (status == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { 307362306a36Sopenharmony_ci /* Flip back and forth between WEP auth modes until the max 307462306a36Sopenharmony_ci * authentication tries has been exceeded. 307562306a36Sopenharmony_ci */ 307662306a36Sopenharmony_ci if (system == WLAN_AUTH_OPEN) { 307762306a36Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum = 0x001; 307862306a36Sopenharmony_ci priv->exclude_unencrypted = 1; 307962306a36Sopenharmony_ci send_authentication_request(priv, WLAN_AUTH_SHARED_KEY, NULL, 0); 308062306a36Sopenharmony_ci return; 308162306a36Sopenharmony_ci } else if (system == WLAN_AUTH_SHARED_KEY 308262306a36Sopenharmony_ci && priv->wep_is_on) { 308362306a36Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum = 0x001; 308462306a36Sopenharmony_ci priv->exclude_unencrypted = 0; 308562306a36Sopenharmony_ci send_authentication_request(priv, WLAN_AUTH_OPEN, NULL, 0); 308662306a36Sopenharmony_ci return; 308762306a36Sopenharmony_ci } else if (priv->connect_to_any_BSS) { 308862306a36Sopenharmony_ci int bss_index; 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ci priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ci if ((bss_index = retrieve_bss(priv)) != -1) { 309362306a36Sopenharmony_ci atmel_join_bss(priv, bss_index); 309462306a36Sopenharmony_ci return; 309562306a36Sopenharmony_ci } 309662306a36Sopenharmony_ci } 309762306a36Sopenharmony_ci } 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci priv->AuthenticationRequestRetryCnt = 0; 310062306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 310162306a36Sopenharmony_ci priv->station_is_associated = 0; 310262306a36Sopenharmony_ci} 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_cistatic void associate(struct atmel_private *priv, u16 frame_len, u16 subtype) 310562306a36Sopenharmony_ci{ 310662306a36Sopenharmony_ci struct ass_resp_format { 310762306a36Sopenharmony_ci __le16 capability; 310862306a36Sopenharmony_ci __le16 status; 310962306a36Sopenharmony_ci __le16 ass_id; 311062306a36Sopenharmony_ci u8 el_id; 311162306a36Sopenharmony_ci u8 length; 311262306a36Sopenharmony_ci u8 rates[4]; 311362306a36Sopenharmony_ci } *ass_resp = (struct ass_resp_format *)priv->rx_buf; 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci u16 status = le16_to_cpu(ass_resp->status); 311662306a36Sopenharmony_ci u16 ass_id = le16_to_cpu(ass_resp->ass_id); 311762306a36Sopenharmony_ci u16 rates_len = ass_resp->length > 4 ? 4 : ass_resp->length; 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_ci union iwreq_data wrqu; 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_ci if (frame_len < 8 + rates_len) 312262306a36Sopenharmony_ci return; 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_ci if (status == WLAN_STATUS_SUCCESS) { 312562306a36Sopenharmony_ci if (subtype == IEEE80211_STYPE_ASSOC_RESP) 312662306a36Sopenharmony_ci priv->AssociationRequestRetryCnt = 0; 312762306a36Sopenharmony_ci else 312862306a36Sopenharmony_ci priv->ReAssociationRequestRetryCnt = 0; 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, 313162306a36Sopenharmony_ci MAC_MGMT_MIB_STATION_ID_POS, ass_id & 0x3fff); 313262306a36Sopenharmony_ci atmel_set_mib(priv, Phy_Mib_Type, 313362306a36Sopenharmony_ci PHY_MIB_RATE_SET_POS, ass_resp->rates, rates_len); 313462306a36Sopenharmony_ci if (priv->power_mode == 0) { 313562306a36Sopenharmony_ci priv->listen_interval = 1; 313662306a36Sopenharmony_ci atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, 313762306a36Sopenharmony_ci MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); 313862306a36Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, 313962306a36Sopenharmony_ci MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); 314062306a36Sopenharmony_ci } else { 314162306a36Sopenharmony_ci priv->listen_interval = 2; 314262306a36Sopenharmony_ci atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, 314362306a36Sopenharmony_ci MAC_MGMT_MIB_PS_MODE_POS, PS_MODE); 314462306a36Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, 314562306a36Sopenharmony_ci MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 2); 314662306a36Sopenharmony_ci } 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci priv->station_is_associated = 1; 314962306a36Sopenharmony_ci priv->station_was_associated = 1; 315062306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_READY); 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci /* Send association event to userspace */ 315362306a36Sopenharmony_ci wrqu.data.length = 0; 315462306a36Sopenharmony_ci wrqu.data.flags = 0; 315562306a36Sopenharmony_ci memcpy(wrqu.ap_addr.sa_data, priv->CurrentBSSID, ETH_ALEN); 315662306a36Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 315762306a36Sopenharmony_ci wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); 315862306a36Sopenharmony_ci 315962306a36Sopenharmony_ci return; 316062306a36Sopenharmony_ci } 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_ci if (subtype == IEEE80211_STYPE_ASSOC_RESP && 316362306a36Sopenharmony_ci status != WLAN_STATUS_ASSOC_DENIED_RATES && 316462306a36Sopenharmony_ci status != WLAN_STATUS_CAPS_UNSUPPORTED && 316562306a36Sopenharmony_ci priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { 316662306a36Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 316762306a36Sopenharmony_ci priv->AssociationRequestRetryCnt++; 316862306a36Sopenharmony_ci send_association_request(priv, 0); 316962306a36Sopenharmony_ci return; 317062306a36Sopenharmony_ci } 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci if (subtype == IEEE80211_STYPE_REASSOC_RESP && 317362306a36Sopenharmony_ci status != WLAN_STATUS_ASSOC_DENIED_RATES && 317462306a36Sopenharmony_ci status != WLAN_STATUS_CAPS_UNSUPPORTED && 317562306a36Sopenharmony_ci priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { 317662306a36Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 317762306a36Sopenharmony_ci priv->ReAssociationRequestRetryCnt++; 317862306a36Sopenharmony_ci send_association_request(priv, 1); 317962306a36Sopenharmony_ci return; 318062306a36Sopenharmony_ci } 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 318362306a36Sopenharmony_ci priv->station_is_associated = 0; 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci if (priv->connect_to_any_BSS) { 318662306a36Sopenharmony_ci int bss_index; 318762306a36Sopenharmony_ci priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci if ((bss_index = retrieve_bss(priv)) != -1) 319062306a36Sopenharmony_ci atmel_join_bss(priv, bss_index); 319162306a36Sopenharmony_ci } 319262306a36Sopenharmony_ci} 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_cistatic void atmel_join_bss(struct atmel_private *priv, int bss_index) 319562306a36Sopenharmony_ci{ 319662306a36Sopenharmony_ci struct bss_info *bss = &priv->BSSinfo[bss_index]; 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ci memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN); 319962306a36Sopenharmony_ci memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize); 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci /* The WPA stuff cares about the current AP address */ 320262306a36Sopenharmony_ci if (priv->use_wpa) 320362306a36Sopenharmony_ci build_wpa_mib(priv); 320462306a36Sopenharmony_ci 320562306a36Sopenharmony_ci /* When switching to AdHoc turn OFF Power Save if needed */ 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ci if (bss->BSStype == IW_MODE_ADHOC && 320862306a36Sopenharmony_ci priv->operating_mode != IW_MODE_ADHOC && 320962306a36Sopenharmony_ci priv->power_mode) { 321062306a36Sopenharmony_ci priv->power_mode = 0; 321162306a36Sopenharmony_ci priv->listen_interval = 1; 321262306a36Sopenharmony_ci atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, 321362306a36Sopenharmony_ci MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); 321462306a36Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, 321562306a36Sopenharmony_ci MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); 321662306a36Sopenharmony_ci } 321762306a36Sopenharmony_ci 321862306a36Sopenharmony_ci priv->operating_mode = bss->BSStype; 321962306a36Sopenharmony_ci priv->channel = bss->channel & 0x7f; 322062306a36Sopenharmony_ci priv->beacon_period = bss->beacon_period; 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ci if (priv->preamble != bss->preamble) { 322362306a36Sopenharmony_ci priv->preamble = bss->preamble; 322462306a36Sopenharmony_ci atmel_set_mib8(priv, Local_Mib_Type, 322562306a36Sopenharmony_ci LOCAL_MIB_PREAMBLE_TYPE, bss->preamble); 322662306a36Sopenharmony_ci } 322762306a36Sopenharmony_ci 322862306a36Sopenharmony_ci if (!priv->wep_is_on && bss->UsingWEP) { 322962306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 323062306a36Sopenharmony_ci priv->station_is_associated = 0; 323162306a36Sopenharmony_ci return; 323262306a36Sopenharmony_ci } 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci if (priv->wep_is_on && !bss->UsingWEP) { 323562306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 323662306a36Sopenharmony_ci priv->station_is_associated = 0; 323762306a36Sopenharmony_ci return; 323862306a36Sopenharmony_ci } 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_JOINNING); 324162306a36Sopenharmony_ci 324262306a36Sopenharmony_ci if (priv->operating_mode == IW_MODE_INFRA) 324362306a36Sopenharmony_ci join(priv, BSS_TYPE_INFRASTRUCTURE); 324462306a36Sopenharmony_ci else 324562306a36Sopenharmony_ci join(priv, BSS_TYPE_AD_HOC); 324662306a36Sopenharmony_ci} 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_cistatic void restart_search(struct atmel_private *priv) 324962306a36Sopenharmony_ci{ 325062306a36Sopenharmony_ci int bss_index; 325162306a36Sopenharmony_ci 325262306a36Sopenharmony_ci if (!priv->connect_to_any_BSS) { 325362306a36Sopenharmony_ci atmel_scan(priv, 1); 325462306a36Sopenharmony_ci } else { 325562306a36Sopenharmony_ci priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; 325662306a36Sopenharmony_ci 325762306a36Sopenharmony_ci if ((bss_index = retrieve_bss(priv)) != -1) 325862306a36Sopenharmony_ci atmel_join_bss(priv, bss_index); 325962306a36Sopenharmony_ci else 326062306a36Sopenharmony_ci atmel_scan(priv, 0); 326162306a36Sopenharmony_ci } 326262306a36Sopenharmony_ci} 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_cistatic void smooth_rssi(struct atmel_private *priv, u8 rssi) 326562306a36Sopenharmony_ci{ 326662306a36Sopenharmony_ci u8 old = priv->wstats.qual.level; 326762306a36Sopenharmony_ci u8 max_rssi = 42; /* 502-rmfd-revd max by experiment, default for now */ 326862306a36Sopenharmony_ci 326962306a36Sopenharmony_ci switch (priv->firmware_type) { 327062306a36Sopenharmony_ci case ATMEL_FW_TYPE_502E: 327162306a36Sopenharmony_ci max_rssi = 63; /* 502-rmfd-reve max by experiment */ 327262306a36Sopenharmony_ci break; 327362306a36Sopenharmony_ci default: 327462306a36Sopenharmony_ci break; 327562306a36Sopenharmony_ci } 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_ci rssi = rssi * 100 / max_rssi; 327862306a36Sopenharmony_ci if ((rssi + old) % 2) 327962306a36Sopenharmony_ci priv->wstats.qual.level = (rssi + old) / 2 + 1; 328062306a36Sopenharmony_ci else 328162306a36Sopenharmony_ci priv->wstats.qual.level = (rssi + old) / 2; 328262306a36Sopenharmony_ci priv->wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; 328362306a36Sopenharmony_ci priv->wstats.qual.updated &= ~IW_QUAL_LEVEL_INVALID; 328462306a36Sopenharmony_ci} 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_cistatic void atmel_smooth_qual(struct atmel_private *priv) 328762306a36Sopenharmony_ci{ 328862306a36Sopenharmony_ci unsigned long time_diff = (jiffies - priv->last_qual) / HZ; 328962306a36Sopenharmony_ci while (time_diff--) { 329062306a36Sopenharmony_ci priv->last_qual += HZ; 329162306a36Sopenharmony_ci priv->wstats.qual.qual = priv->wstats.qual.qual / 2; 329262306a36Sopenharmony_ci priv->wstats.qual.qual += 329362306a36Sopenharmony_ci priv->beacons_this_sec * priv->beacon_period * (priv->wstats.qual.level + 100) / 4000; 329462306a36Sopenharmony_ci priv->beacons_this_sec = 0; 329562306a36Sopenharmony_ci } 329662306a36Sopenharmony_ci priv->wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; 329762306a36Sopenharmony_ci priv->wstats.qual.updated &= ~IW_QUAL_QUAL_INVALID; 329862306a36Sopenharmony_ci} 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci/* deals with incoming management frames. */ 330162306a36Sopenharmony_cistatic void atmel_management_frame(struct atmel_private *priv, 330262306a36Sopenharmony_ci struct ieee80211_hdr *header, 330362306a36Sopenharmony_ci u16 frame_len, u8 rssi) 330462306a36Sopenharmony_ci{ 330562306a36Sopenharmony_ci u16 subtype; 330662306a36Sopenharmony_ci 330762306a36Sopenharmony_ci subtype = le16_to_cpu(header->frame_control) & IEEE80211_FCTL_STYPE; 330862306a36Sopenharmony_ci switch (subtype) { 330962306a36Sopenharmony_ci case IEEE80211_STYPE_BEACON: 331062306a36Sopenharmony_ci case IEEE80211_STYPE_PROBE_RESP: 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_ci /* beacon frame has multiple variable-length fields - 331362306a36Sopenharmony_ci never let an engineer loose with a data structure design. */ 331462306a36Sopenharmony_ci { 331562306a36Sopenharmony_ci struct beacon_format { 331662306a36Sopenharmony_ci __le64 timestamp; 331762306a36Sopenharmony_ci __le16 interval; 331862306a36Sopenharmony_ci __le16 capability; 331962306a36Sopenharmony_ci u8 ssid_el_id; 332062306a36Sopenharmony_ci u8 ssid_length; 332162306a36Sopenharmony_ci /* ssid here */ 332262306a36Sopenharmony_ci u8 rates_el_id; 332362306a36Sopenharmony_ci u8 rates_length; 332462306a36Sopenharmony_ci /* rates here */ 332562306a36Sopenharmony_ci u8 ds_el_id; 332662306a36Sopenharmony_ci u8 ds_length; 332762306a36Sopenharmony_ci /* ds here */ 332862306a36Sopenharmony_ci } *beacon = (struct beacon_format *)priv->rx_buf; 332962306a36Sopenharmony_ci 333062306a36Sopenharmony_ci u8 channel, rates_length, ssid_length; 333162306a36Sopenharmony_ci u64 timestamp = le64_to_cpu(beacon->timestamp); 333262306a36Sopenharmony_ci u16 beacon_interval = le16_to_cpu(beacon->interval); 333362306a36Sopenharmony_ci u16 capability = le16_to_cpu(beacon->capability); 333462306a36Sopenharmony_ci u8 *beaconp = priv->rx_buf; 333562306a36Sopenharmony_ci ssid_length = beacon->ssid_length; 333662306a36Sopenharmony_ci /* this blows chunks. */ 333762306a36Sopenharmony_ci if (frame_len < 14 || frame_len < ssid_length + 15) 333862306a36Sopenharmony_ci return; 333962306a36Sopenharmony_ci rates_length = beaconp[beacon->ssid_length + 15]; 334062306a36Sopenharmony_ci if (frame_len < ssid_length + rates_length + 18) 334162306a36Sopenharmony_ci return; 334262306a36Sopenharmony_ci if (ssid_length > MAX_SSID_LENGTH) 334362306a36Sopenharmony_ci return; 334462306a36Sopenharmony_ci channel = beaconp[ssid_length + rates_length + 18]; 334562306a36Sopenharmony_ci 334662306a36Sopenharmony_ci if (priv->station_state == STATION_STATE_READY) { 334762306a36Sopenharmony_ci smooth_rssi(priv, rssi); 334862306a36Sopenharmony_ci if (is_frame_from_current_bss(priv, header)) { 334962306a36Sopenharmony_ci priv->beacons_this_sec++; 335062306a36Sopenharmony_ci atmel_smooth_qual(priv); 335162306a36Sopenharmony_ci if (priv->last_beacon_timestamp) { 335262306a36Sopenharmony_ci /* Note truncate this to 32 bits - kernel can't divide a long */ 335362306a36Sopenharmony_ci u32 beacon_delay = timestamp - priv->last_beacon_timestamp; 335462306a36Sopenharmony_ci int beacons = beacon_delay / (beacon_interval * 1000); 335562306a36Sopenharmony_ci if (beacons > 1) 335662306a36Sopenharmony_ci priv->wstats.miss.beacon += beacons - 1; 335762306a36Sopenharmony_ci } 335862306a36Sopenharmony_ci priv->last_beacon_timestamp = timestamp; 335962306a36Sopenharmony_ci handle_beacon_probe(priv, capability, channel); 336062306a36Sopenharmony_ci } 336162306a36Sopenharmony_ci } 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_ci if (priv->station_state == STATION_STATE_SCANNING) 336462306a36Sopenharmony_ci store_bss_info(priv, header, capability, 336562306a36Sopenharmony_ci beacon_interval, channel, rssi, 336662306a36Sopenharmony_ci ssid_length, 336762306a36Sopenharmony_ci &beacon->rates_el_id, 336862306a36Sopenharmony_ci subtype == IEEE80211_STYPE_BEACON); 336962306a36Sopenharmony_ci } 337062306a36Sopenharmony_ci break; 337162306a36Sopenharmony_ci 337262306a36Sopenharmony_ci case IEEE80211_STYPE_AUTH: 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_ci if (priv->station_state == STATION_STATE_AUTHENTICATING) 337562306a36Sopenharmony_ci authenticate(priv, frame_len); 337662306a36Sopenharmony_ci 337762306a36Sopenharmony_ci break; 337862306a36Sopenharmony_ci 337962306a36Sopenharmony_ci case IEEE80211_STYPE_ASSOC_RESP: 338062306a36Sopenharmony_ci case IEEE80211_STYPE_REASSOC_RESP: 338162306a36Sopenharmony_ci 338262306a36Sopenharmony_ci if (priv->station_state == STATION_STATE_ASSOCIATING || 338362306a36Sopenharmony_ci priv->station_state == STATION_STATE_REASSOCIATING) 338462306a36Sopenharmony_ci associate(priv, frame_len, subtype); 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_ci break; 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ci case IEEE80211_STYPE_DISASSOC: 338962306a36Sopenharmony_ci if (priv->station_is_associated && 339062306a36Sopenharmony_ci priv->operating_mode == IW_MODE_INFRA && 339162306a36Sopenharmony_ci is_frame_from_current_bss(priv, header)) { 339262306a36Sopenharmony_ci priv->station_was_associated = 0; 339362306a36Sopenharmony_ci priv->station_is_associated = 0; 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_JOINNING); 339662306a36Sopenharmony_ci join(priv, BSS_TYPE_INFRASTRUCTURE); 339762306a36Sopenharmony_ci } 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_ci break; 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci case IEEE80211_STYPE_DEAUTH: 340262306a36Sopenharmony_ci if (priv->operating_mode == IW_MODE_INFRA && 340362306a36Sopenharmony_ci is_frame_from_current_bss(priv, header)) { 340462306a36Sopenharmony_ci priv->station_was_associated = 0; 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_JOINNING); 340762306a36Sopenharmony_ci join(priv, BSS_TYPE_INFRASTRUCTURE); 340862306a36Sopenharmony_ci } 340962306a36Sopenharmony_ci 341062306a36Sopenharmony_ci break; 341162306a36Sopenharmony_ci } 341262306a36Sopenharmony_ci} 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci/* run when timer expires */ 341562306a36Sopenharmony_cistatic void atmel_management_timer(struct timer_list *t) 341662306a36Sopenharmony_ci{ 341762306a36Sopenharmony_ci struct atmel_private *priv = from_timer(priv, t, management_timer); 341862306a36Sopenharmony_ci unsigned long flags; 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci /* Check if the card has been yanked. */ 342162306a36Sopenharmony_ci if (priv->card && priv->present_callback && 342262306a36Sopenharmony_ci !(*priv->present_callback)(priv->card)) 342362306a36Sopenharmony_ci return; 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci spin_lock_irqsave(&priv->irqlock, flags); 342662306a36Sopenharmony_ci 342762306a36Sopenharmony_ci switch (priv->station_state) { 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci case STATION_STATE_AUTHENTICATING: 343062306a36Sopenharmony_ci if (priv->AuthenticationRequestRetryCnt >= MAX_AUTHENTICATION_RETRIES) { 343162306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 343262306a36Sopenharmony_ci priv->station_is_associated = 0; 343362306a36Sopenharmony_ci priv->AuthenticationRequestRetryCnt = 0; 343462306a36Sopenharmony_ci restart_search(priv); 343562306a36Sopenharmony_ci } else { 343662306a36Sopenharmony_ci int auth = WLAN_AUTH_OPEN; 343762306a36Sopenharmony_ci priv->AuthenticationRequestRetryCnt++; 343862306a36Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum = 0x0001; 343962306a36Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 344062306a36Sopenharmony_ci if (priv->wep_is_on && priv->exclude_unencrypted) 344162306a36Sopenharmony_ci auth = WLAN_AUTH_SHARED_KEY; 344262306a36Sopenharmony_ci send_authentication_request(priv, auth, NULL, 0); 344362306a36Sopenharmony_ci } 344462306a36Sopenharmony_ci break; 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci case STATION_STATE_ASSOCIATING: 344762306a36Sopenharmony_ci if (priv->AssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { 344862306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 344962306a36Sopenharmony_ci priv->station_is_associated = 0; 345062306a36Sopenharmony_ci priv->AssociationRequestRetryCnt = 0; 345162306a36Sopenharmony_ci restart_search(priv); 345262306a36Sopenharmony_ci } else { 345362306a36Sopenharmony_ci priv->AssociationRequestRetryCnt++; 345462306a36Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 345562306a36Sopenharmony_ci send_association_request(priv, 0); 345662306a36Sopenharmony_ci } 345762306a36Sopenharmony_ci break; 345862306a36Sopenharmony_ci 345962306a36Sopenharmony_ci case STATION_STATE_REASSOCIATING: 346062306a36Sopenharmony_ci if (priv->ReAssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { 346162306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); 346262306a36Sopenharmony_ci priv->station_is_associated = 0; 346362306a36Sopenharmony_ci priv->ReAssociationRequestRetryCnt = 0; 346462306a36Sopenharmony_ci restart_search(priv); 346562306a36Sopenharmony_ci } else { 346662306a36Sopenharmony_ci priv->ReAssociationRequestRetryCnt++; 346762306a36Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 346862306a36Sopenharmony_ci send_association_request(priv, 1); 346962306a36Sopenharmony_ci } 347062306a36Sopenharmony_ci break; 347162306a36Sopenharmony_ci 347262306a36Sopenharmony_ci default: 347362306a36Sopenharmony_ci break; 347462306a36Sopenharmony_ci } 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->irqlock, flags); 347762306a36Sopenharmony_ci} 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_cistatic void atmel_command_irq(struct atmel_private *priv) 348062306a36Sopenharmony_ci{ 348162306a36Sopenharmony_ci u8 status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); 348262306a36Sopenharmony_ci u8 command = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET)); 348362306a36Sopenharmony_ci int fast_scan; 348462306a36Sopenharmony_ci union iwreq_data wrqu; 348562306a36Sopenharmony_ci 348662306a36Sopenharmony_ci if (status == CMD_STATUS_IDLE || 348762306a36Sopenharmony_ci status == CMD_STATUS_IN_PROGRESS) 348862306a36Sopenharmony_ci return; 348962306a36Sopenharmony_ci 349062306a36Sopenharmony_ci switch (command) { 349162306a36Sopenharmony_ci case CMD_Start: 349262306a36Sopenharmony_ci if (status == CMD_STATUS_COMPLETE) { 349362306a36Sopenharmony_ci priv->station_was_associated = priv->station_is_associated; 349462306a36Sopenharmony_ci atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, 349562306a36Sopenharmony_ci (u8 *)priv->CurrentBSSID, 6); 349662306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_READY); 349762306a36Sopenharmony_ci } 349862306a36Sopenharmony_ci break; 349962306a36Sopenharmony_ci 350062306a36Sopenharmony_ci case CMD_Scan: 350162306a36Sopenharmony_ci fast_scan = priv->fast_scan; 350262306a36Sopenharmony_ci priv->fast_scan = 0; 350362306a36Sopenharmony_ci 350462306a36Sopenharmony_ci if (status != CMD_STATUS_COMPLETE) { 350562306a36Sopenharmony_ci atmel_scan(priv, 1); 350662306a36Sopenharmony_ci } else { 350762306a36Sopenharmony_ci int bss_index = retrieve_bss(priv); 350862306a36Sopenharmony_ci int notify_scan_complete = 1; 350962306a36Sopenharmony_ci if (bss_index != -1) { 351062306a36Sopenharmony_ci atmel_join_bss(priv, bss_index); 351162306a36Sopenharmony_ci } else if (priv->operating_mode == IW_MODE_ADHOC && 351262306a36Sopenharmony_ci priv->SSID_size != 0) { 351362306a36Sopenharmony_ci start(priv, BSS_TYPE_AD_HOC); 351462306a36Sopenharmony_ci } else { 351562306a36Sopenharmony_ci priv->fast_scan = !fast_scan; 351662306a36Sopenharmony_ci atmel_scan(priv, 1); 351762306a36Sopenharmony_ci notify_scan_complete = 0; 351862306a36Sopenharmony_ci } 351962306a36Sopenharmony_ci priv->site_survey_state = SITE_SURVEY_COMPLETED; 352062306a36Sopenharmony_ci if (notify_scan_complete) { 352162306a36Sopenharmony_ci wrqu.data.length = 0; 352262306a36Sopenharmony_ci wrqu.data.flags = 0; 352362306a36Sopenharmony_ci wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); 352462306a36Sopenharmony_ci } 352562306a36Sopenharmony_ci } 352662306a36Sopenharmony_ci break; 352762306a36Sopenharmony_ci 352862306a36Sopenharmony_ci case CMD_SiteSurvey: 352962306a36Sopenharmony_ci priv->fast_scan = 0; 353062306a36Sopenharmony_ci 353162306a36Sopenharmony_ci if (status != CMD_STATUS_COMPLETE) 353262306a36Sopenharmony_ci return; 353362306a36Sopenharmony_ci 353462306a36Sopenharmony_ci priv->site_survey_state = SITE_SURVEY_COMPLETED; 353562306a36Sopenharmony_ci if (priv->station_is_associated) { 353662306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_READY); 353762306a36Sopenharmony_ci wrqu.data.length = 0; 353862306a36Sopenharmony_ci wrqu.data.flags = 0; 353962306a36Sopenharmony_ci wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); 354062306a36Sopenharmony_ci } else { 354162306a36Sopenharmony_ci atmel_scan(priv, 1); 354262306a36Sopenharmony_ci } 354362306a36Sopenharmony_ci break; 354462306a36Sopenharmony_ci 354562306a36Sopenharmony_ci case CMD_Join: 354662306a36Sopenharmony_ci if (status == CMD_STATUS_COMPLETE) { 354762306a36Sopenharmony_ci if (priv->operating_mode == IW_MODE_ADHOC) { 354862306a36Sopenharmony_ci priv->station_was_associated = priv->station_is_associated; 354962306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_READY); 355062306a36Sopenharmony_ci } else { 355162306a36Sopenharmony_ci int auth = WLAN_AUTH_OPEN; 355262306a36Sopenharmony_ci priv->AuthenticationRequestRetryCnt = 0; 355362306a36Sopenharmony_ci atmel_enter_state(priv, STATION_STATE_AUTHENTICATING); 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_ci mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); 355662306a36Sopenharmony_ci priv->CurrentAuthentTransactionSeqNum = 0x0001; 355762306a36Sopenharmony_ci if (priv->wep_is_on && priv->exclude_unencrypted) 355862306a36Sopenharmony_ci auth = WLAN_AUTH_SHARED_KEY; 355962306a36Sopenharmony_ci send_authentication_request(priv, auth, NULL, 0); 356062306a36Sopenharmony_ci } 356162306a36Sopenharmony_ci return; 356262306a36Sopenharmony_ci } 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_ci atmel_scan(priv, 1); 356562306a36Sopenharmony_ci } 356662306a36Sopenharmony_ci} 356762306a36Sopenharmony_ci 356862306a36Sopenharmony_cistatic int atmel_wakeup_firmware(struct atmel_private *priv) 356962306a36Sopenharmony_ci{ 357062306a36Sopenharmony_ci struct host_info_struct *iface = &priv->host_info; 357162306a36Sopenharmony_ci u16 mr1, mr3; 357262306a36Sopenharmony_ci int i; 357362306a36Sopenharmony_ci 357462306a36Sopenharmony_ci if (priv->card_type == CARD_TYPE_SPI_FLASH) 357562306a36Sopenharmony_ci atmel_set_gcr(priv->dev, GCR_REMAP); 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_ci /* wake up on-board processor */ 357862306a36Sopenharmony_ci atmel_clear_gcr(priv->dev, 0x0040); 357962306a36Sopenharmony_ci atmel_write16(priv->dev, BSR, BSS_SRAM); 358062306a36Sopenharmony_ci 358162306a36Sopenharmony_ci if (priv->card_type == CARD_TYPE_SPI_FLASH) 358262306a36Sopenharmony_ci mdelay(100); 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_ci /* and wait for it */ 358562306a36Sopenharmony_ci for (i = LOOP_RETRY_LIMIT; i; i--) { 358662306a36Sopenharmony_ci mr1 = atmel_read16(priv->dev, MR1); 358762306a36Sopenharmony_ci mr3 = atmel_read16(priv->dev, MR3); 358862306a36Sopenharmony_ci 358962306a36Sopenharmony_ci if (mr3 & MAC_BOOT_COMPLETE) 359062306a36Sopenharmony_ci break; 359162306a36Sopenharmony_ci if (mr1 & MAC_BOOT_COMPLETE && 359262306a36Sopenharmony_ci priv->bus_type == BUS_TYPE_PCCARD) 359362306a36Sopenharmony_ci break; 359462306a36Sopenharmony_ci } 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_ci if (i == 0) { 359762306a36Sopenharmony_ci printk(KERN_ALERT "%s: MAC failed to boot.\n", priv->dev->name); 359862306a36Sopenharmony_ci return -EIO; 359962306a36Sopenharmony_ci } 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ci if ((priv->host_info_base = atmel_read16(priv->dev, MR2)) == 0xffff) { 360262306a36Sopenharmony_ci printk(KERN_ALERT "%s: card missing.\n", priv->dev->name); 360362306a36Sopenharmony_ci return -ENODEV; 360462306a36Sopenharmony_ci } 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci /* now check for completion of MAC initialization through 360762306a36Sopenharmony_ci the FunCtrl field of the IFACE, poll MR1 to detect completion of 360862306a36Sopenharmony_ci MAC initialization, check completion status, set interrupt mask, 360962306a36Sopenharmony_ci enables interrupts and calls Tx and Rx initialization functions */ 361062306a36Sopenharmony_ci 361162306a36Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), FUNC_CTRL_INIT_COMPLETE); 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci for (i = LOOP_RETRY_LIMIT; i; i--) { 361462306a36Sopenharmony_ci mr1 = atmel_read16(priv->dev, MR1); 361562306a36Sopenharmony_ci mr3 = atmel_read16(priv->dev, MR3); 361662306a36Sopenharmony_ci 361762306a36Sopenharmony_ci if (mr3 & MAC_INIT_COMPLETE) 361862306a36Sopenharmony_ci break; 361962306a36Sopenharmony_ci if (mr1 & MAC_INIT_COMPLETE && 362062306a36Sopenharmony_ci priv->bus_type == BUS_TYPE_PCCARD) 362162306a36Sopenharmony_ci break; 362262306a36Sopenharmony_ci } 362362306a36Sopenharmony_ci 362462306a36Sopenharmony_ci if (i == 0) { 362562306a36Sopenharmony_ci printk(KERN_ALERT "%s: MAC failed to initialise.\n", 362662306a36Sopenharmony_ci priv->dev->name); 362762306a36Sopenharmony_ci return -EIO; 362862306a36Sopenharmony_ci } 362962306a36Sopenharmony_ci 363062306a36Sopenharmony_ci /* Check for MAC_INIT_OK only on the register that the MAC_INIT_OK was set */ 363162306a36Sopenharmony_ci if ((mr3 & MAC_INIT_COMPLETE) && 363262306a36Sopenharmony_ci !(atmel_read16(priv->dev, MR3) & MAC_INIT_OK)) { 363362306a36Sopenharmony_ci printk(KERN_ALERT "%s: MAC failed MR3 self-test.\n", priv->dev->name); 363462306a36Sopenharmony_ci return -EIO; 363562306a36Sopenharmony_ci } 363662306a36Sopenharmony_ci if ((mr1 & MAC_INIT_COMPLETE) && 363762306a36Sopenharmony_ci !(atmel_read16(priv->dev, MR1) & MAC_INIT_OK)) { 363862306a36Sopenharmony_ci printk(KERN_ALERT "%s: MAC failed MR1 self-test.\n", priv->dev->name); 363962306a36Sopenharmony_ci return -EIO; 364062306a36Sopenharmony_ci } 364162306a36Sopenharmony_ci 364262306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, (unsigned char *)iface, 364362306a36Sopenharmony_ci priv->host_info_base, sizeof(*iface)); 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci iface->tx_buff_pos = le16_to_cpu(iface->tx_buff_pos); 364662306a36Sopenharmony_ci iface->tx_buff_size = le16_to_cpu(iface->tx_buff_size); 364762306a36Sopenharmony_ci iface->tx_desc_pos = le16_to_cpu(iface->tx_desc_pos); 364862306a36Sopenharmony_ci iface->tx_desc_count = le16_to_cpu(iface->tx_desc_count); 364962306a36Sopenharmony_ci iface->rx_buff_pos = le16_to_cpu(iface->rx_buff_pos); 365062306a36Sopenharmony_ci iface->rx_buff_size = le16_to_cpu(iface->rx_buff_size); 365162306a36Sopenharmony_ci iface->rx_desc_pos = le16_to_cpu(iface->rx_desc_pos); 365262306a36Sopenharmony_ci iface->rx_desc_count = le16_to_cpu(iface->rx_desc_count); 365362306a36Sopenharmony_ci iface->build_version = le16_to_cpu(iface->build_version); 365462306a36Sopenharmony_ci iface->command_pos = le16_to_cpu(iface->command_pos); 365562306a36Sopenharmony_ci iface->major_version = le16_to_cpu(iface->major_version); 365662306a36Sopenharmony_ci iface->minor_version = le16_to_cpu(iface->minor_version); 365762306a36Sopenharmony_ci iface->func_ctrl = le16_to_cpu(iface->func_ctrl); 365862306a36Sopenharmony_ci iface->mac_status = le16_to_cpu(iface->mac_status); 365962306a36Sopenharmony_ci 366062306a36Sopenharmony_ci return 0; 366162306a36Sopenharmony_ci} 366262306a36Sopenharmony_ci 366362306a36Sopenharmony_ci/* determine type of memory and MAC address */ 366462306a36Sopenharmony_cistatic int probe_atmel_card(struct net_device *dev) 366562306a36Sopenharmony_ci{ 366662306a36Sopenharmony_ci int rc = 0; 366762306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 366862306a36Sopenharmony_ci u8 addr[ETH_ALEN] = {}; 366962306a36Sopenharmony_ci 367062306a36Sopenharmony_ci /* reset pccard */ 367162306a36Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 367262306a36Sopenharmony_ci atmel_write16(dev, GCR, 0x0060); 367362306a36Sopenharmony_ci 367462306a36Sopenharmony_ci atmel_write16(dev, GCR, 0x0040); 367562306a36Sopenharmony_ci msleep(500); 367662306a36Sopenharmony_ci 367762306a36Sopenharmony_ci if (atmel_read16(dev, MR2) == 0) { 367862306a36Sopenharmony_ci /* No stored firmware so load a small stub which just 367962306a36Sopenharmony_ci tells us the MAC address */ 368062306a36Sopenharmony_ci int i; 368162306a36Sopenharmony_ci priv->card_type = CARD_TYPE_EEPROM; 368262306a36Sopenharmony_ci atmel_write16(dev, BSR, BSS_IRAM); 368362306a36Sopenharmony_ci atmel_copy_to_card(dev, 0, mac_reader, sizeof(mac_reader)); 368462306a36Sopenharmony_ci atmel_set_gcr(dev, GCR_REMAP); 368562306a36Sopenharmony_ci atmel_clear_gcr(priv->dev, 0x0040); 368662306a36Sopenharmony_ci atmel_write16(dev, BSR, BSS_SRAM); 368762306a36Sopenharmony_ci for (i = LOOP_RETRY_LIMIT; i; i--) 368862306a36Sopenharmony_ci if (atmel_read16(dev, MR3) & MAC_BOOT_COMPLETE) 368962306a36Sopenharmony_ci break; 369062306a36Sopenharmony_ci if (i == 0) { 369162306a36Sopenharmony_ci printk(KERN_ALERT "%s: MAC failed to boot MAC address reader.\n", dev->name); 369262306a36Sopenharmony_ci } else { 369362306a36Sopenharmony_ci 369462306a36Sopenharmony_ci atmel_copy_to_host(dev, addr, atmel_read16(dev, MR2), 6); 369562306a36Sopenharmony_ci eth_hw_addr_set(dev, addr); 369662306a36Sopenharmony_ci /* got address, now squash it again until the network 369762306a36Sopenharmony_ci interface is opened */ 369862306a36Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 369962306a36Sopenharmony_ci atmel_write16(dev, GCR, 0x0060); 370062306a36Sopenharmony_ci atmel_write16(dev, GCR, 0x0040); 370162306a36Sopenharmony_ci rc = 1; 370262306a36Sopenharmony_ci } 370362306a36Sopenharmony_ci } else if (atmel_read16(dev, MR4) == 0) { 370462306a36Sopenharmony_ci /* Mac address easy in this case. */ 370562306a36Sopenharmony_ci priv->card_type = CARD_TYPE_PARALLEL_FLASH; 370662306a36Sopenharmony_ci atmel_write16(dev, BSR, 1); 370762306a36Sopenharmony_ci atmel_copy_to_host(dev, addr, 0xc000, 6); 370862306a36Sopenharmony_ci eth_hw_addr_set(dev, addr); 370962306a36Sopenharmony_ci atmel_write16(dev, BSR, 0x200); 371062306a36Sopenharmony_ci rc = 1; 371162306a36Sopenharmony_ci } else { 371262306a36Sopenharmony_ci /* Standard firmware in flash, boot it up and ask 371362306a36Sopenharmony_ci for the Mac Address */ 371462306a36Sopenharmony_ci priv->card_type = CARD_TYPE_SPI_FLASH; 371562306a36Sopenharmony_ci if (atmel_wakeup_firmware(priv) == 0) { 371662306a36Sopenharmony_ci atmel_get_mib(priv, Mac_Address_Mib_Type, 0, addr, 6); 371762306a36Sopenharmony_ci eth_hw_addr_set(dev, addr); 371862306a36Sopenharmony_ci 371962306a36Sopenharmony_ci /* got address, now squash it again until the network 372062306a36Sopenharmony_ci interface is opened */ 372162306a36Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 372262306a36Sopenharmony_ci atmel_write16(dev, GCR, 0x0060); 372362306a36Sopenharmony_ci atmel_write16(dev, GCR, 0x0040); 372462306a36Sopenharmony_ci rc = 1; 372562306a36Sopenharmony_ci } 372662306a36Sopenharmony_ci } 372762306a36Sopenharmony_ci 372862306a36Sopenharmony_ci if (rc) { 372962306a36Sopenharmony_ci if (dev->dev_addr[0] == 0xFF) { 373062306a36Sopenharmony_ci static const u8 default_mac[] = { 373162306a36Sopenharmony_ci 0x00, 0x04, 0x25, 0x00, 0x00, 0x00 373262306a36Sopenharmony_ci }; 373362306a36Sopenharmony_ci printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name); 373462306a36Sopenharmony_ci eth_hw_addr_set(dev, default_mac); 373562306a36Sopenharmony_ci } 373662306a36Sopenharmony_ci } 373762306a36Sopenharmony_ci 373862306a36Sopenharmony_ci return rc; 373962306a36Sopenharmony_ci} 374062306a36Sopenharmony_ci 374162306a36Sopenharmony_ci/* Move the encyption information on the MIB structure. 374262306a36Sopenharmony_ci This routine is for the pre-WPA firmware: later firmware has 374362306a36Sopenharmony_ci a different format MIB and a different routine. */ 374462306a36Sopenharmony_cistatic void build_wep_mib(struct atmel_private *priv) 374562306a36Sopenharmony_ci{ 374662306a36Sopenharmony_ci struct { /* NB this is matched to the hardware, don't change. */ 374762306a36Sopenharmony_ci u8 wep_is_on; 374862306a36Sopenharmony_ci u8 default_key; /* 0..3 */ 374962306a36Sopenharmony_ci u8 reserved; 375062306a36Sopenharmony_ci u8 exclude_unencrypted; 375162306a36Sopenharmony_ci 375262306a36Sopenharmony_ci u32 WEPICV_error_count; 375362306a36Sopenharmony_ci u32 WEP_excluded_count; 375462306a36Sopenharmony_ci 375562306a36Sopenharmony_ci u8 wep_keys[MAX_ENCRYPTION_KEYS][13]; 375662306a36Sopenharmony_ci u8 encryption_level; /* 0, 1, 2 */ 375762306a36Sopenharmony_ci u8 reserved2[3]; 375862306a36Sopenharmony_ci } mib; 375962306a36Sopenharmony_ci int i; 376062306a36Sopenharmony_ci 376162306a36Sopenharmony_ci mib.wep_is_on = priv->wep_is_on; 376262306a36Sopenharmony_ci if (priv->wep_is_on) { 376362306a36Sopenharmony_ci if (priv->wep_key_len[priv->default_key] > 5) 376462306a36Sopenharmony_ci mib.encryption_level = 2; 376562306a36Sopenharmony_ci else 376662306a36Sopenharmony_ci mib.encryption_level = 1; 376762306a36Sopenharmony_ci } else { 376862306a36Sopenharmony_ci mib.encryption_level = 0; 376962306a36Sopenharmony_ci } 377062306a36Sopenharmony_ci 377162306a36Sopenharmony_ci mib.default_key = priv->default_key; 377262306a36Sopenharmony_ci mib.exclude_unencrypted = priv->exclude_unencrypted; 377362306a36Sopenharmony_ci 377462306a36Sopenharmony_ci for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) 377562306a36Sopenharmony_ci memcpy(mib.wep_keys[i], priv->wep_keys[i], 13); 377662306a36Sopenharmony_ci 377762306a36Sopenharmony_ci atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); 377862306a36Sopenharmony_ci} 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_cistatic void build_wpa_mib(struct atmel_private *priv) 378162306a36Sopenharmony_ci{ 378262306a36Sopenharmony_ci /* This is for the later (WPA enabled) firmware. */ 378362306a36Sopenharmony_ci 378462306a36Sopenharmony_ci struct { /* NB this is matched to the hardware, don't change. */ 378562306a36Sopenharmony_ci u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; 378662306a36Sopenharmony_ci u8 receiver_address[ETH_ALEN]; 378762306a36Sopenharmony_ci u8 wep_is_on; 378862306a36Sopenharmony_ci u8 default_key; /* 0..3 */ 378962306a36Sopenharmony_ci u8 group_key; 379062306a36Sopenharmony_ci u8 exclude_unencrypted; 379162306a36Sopenharmony_ci u8 encryption_type; 379262306a36Sopenharmony_ci u8 reserved; 379362306a36Sopenharmony_ci 379462306a36Sopenharmony_ci u32 WEPICV_error_count; 379562306a36Sopenharmony_ci u32 WEP_excluded_count; 379662306a36Sopenharmony_ci 379762306a36Sopenharmony_ci u8 key_RSC[4][8]; 379862306a36Sopenharmony_ci } mib; 379962306a36Sopenharmony_ci 380062306a36Sopenharmony_ci int i; 380162306a36Sopenharmony_ci 380262306a36Sopenharmony_ci mib.wep_is_on = priv->wep_is_on; 380362306a36Sopenharmony_ci mib.exclude_unencrypted = priv->exclude_unencrypted; 380462306a36Sopenharmony_ci memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN); 380562306a36Sopenharmony_ci 380662306a36Sopenharmony_ci /* zero all the keys before adding in valid ones. */ 380762306a36Sopenharmony_ci memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value)); 380862306a36Sopenharmony_ci 380962306a36Sopenharmony_ci if (priv->wep_is_on) { 381062306a36Sopenharmony_ci /* There's a comment in the Atmel code to the effect that this 381162306a36Sopenharmony_ci is only valid when still using WEP, it may need to be set to 381262306a36Sopenharmony_ci something to use WPA */ 381362306a36Sopenharmony_ci memset(mib.key_RSC, 0, sizeof(mib.key_RSC)); 381462306a36Sopenharmony_ci 381562306a36Sopenharmony_ci mib.default_key = mib.group_key = 255; 381662306a36Sopenharmony_ci for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) { 381762306a36Sopenharmony_ci if (priv->wep_key_len[i] > 0) { 381862306a36Sopenharmony_ci memcpy(mib.cipher_default_key_value[i], priv->wep_keys[i], MAX_ENCRYPTION_KEY_SIZE); 381962306a36Sopenharmony_ci if (i == priv->default_key) { 382062306a36Sopenharmony_ci mib.default_key = i; 382162306a36Sopenharmony_ci mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 7; 382262306a36Sopenharmony_ci mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->pairwise_cipher_suite; 382362306a36Sopenharmony_ci } else { 382462306a36Sopenharmony_ci mib.group_key = i; 382562306a36Sopenharmony_ci priv->group_cipher_suite = priv->pairwise_cipher_suite; 382662306a36Sopenharmony_ci mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 1; 382762306a36Sopenharmony_ci mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->group_cipher_suite; 382862306a36Sopenharmony_ci } 382962306a36Sopenharmony_ci } 383062306a36Sopenharmony_ci } 383162306a36Sopenharmony_ci if (mib.default_key == 255) 383262306a36Sopenharmony_ci mib.default_key = mib.group_key != 255 ? mib.group_key : 0; 383362306a36Sopenharmony_ci if (mib.group_key == 255) 383462306a36Sopenharmony_ci mib.group_key = mib.default_key; 383562306a36Sopenharmony_ci 383662306a36Sopenharmony_ci } 383762306a36Sopenharmony_ci 383862306a36Sopenharmony_ci atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); 383962306a36Sopenharmony_ci} 384062306a36Sopenharmony_ci 384162306a36Sopenharmony_cistatic int reset_atmel_card(struct net_device *dev) 384262306a36Sopenharmony_ci{ 384362306a36Sopenharmony_ci /* do everything necessary to wake up the hardware, including 384462306a36Sopenharmony_ci waiting for the lightning strike and throwing the knife switch.... 384562306a36Sopenharmony_ci 384662306a36Sopenharmony_ci set all the Mib values which matter in the card to match 384762306a36Sopenharmony_ci their settings in the atmel_private structure. Some of these 384862306a36Sopenharmony_ci can be altered on the fly, but many (WEP, infrastructure or ad-hoc) 384962306a36Sopenharmony_ci can only be changed by tearing down the world and coming back through 385062306a36Sopenharmony_ci here. 385162306a36Sopenharmony_ci 385262306a36Sopenharmony_ci This routine is also responsible for initialising some 385362306a36Sopenharmony_ci hardware-specific fields in the atmel_private structure, 385462306a36Sopenharmony_ci including a copy of the firmware's hostinfo structure 385562306a36Sopenharmony_ci which is the route into the rest of the firmware datastructures. */ 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_ci struct atmel_private *priv = netdev_priv(dev); 385862306a36Sopenharmony_ci u8 configuration; 385962306a36Sopenharmony_ci int old_state = priv->station_state; 386062306a36Sopenharmony_ci int err = 0; 386162306a36Sopenharmony_ci 386262306a36Sopenharmony_ci /* data to add to the firmware names, in priority order 386362306a36Sopenharmony_ci this implemenents firmware versioning */ 386462306a36Sopenharmony_ci 386562306a36Sopenharmony_ci static char *firmware_modifier[] = { 386662306a36Sopenharmony_ci "-wpa", 386762306a36Sopenharmony_ci "", 386862306a36Sopenharmony_ci NULL 386962306a36Sopenharmony_ci }; 387062306a36Sopenharmony_ci 387162306a36Sopenharmony_ci /* reset pccard */ 387262306a36Sopenharmony_ci if (priv->bus_type == BUS_TYPE_PCCARD) 387362306a36Sopenharmony_ci atmel_write16(priv->dev, GCR, 0x0060); 387462306a36Sopenharmony_ci 387562306a36Sopenharmony_ci /* stop card , disable interrupts */ 387662306a36Sopenharmony_ci atmel_write16(priv->dev, GCR, 0x0040); 387762306a36Sopenharmony_ci 387862306a36Sopenharmony_ci if (priv->card_type == CARD_TYPE_EEPROM) { 387962306a36Sopenharmony_ci /* copy in firmware if needed */ 388062306a36Sopenharmony_ci const struct firmware *fw_entry = NULL; 388162306a36Sopenharmony_ci const unsigned char *fw; 388262306a36Sopenharmony_ci int len = priv->firmware_length; 388362306a36Sopenharmony_ci if (!(fw = priv->firmware)) { 388462306a36Sopenharmony_ci if (priv->firmware_type == ATMEL_FW_TYPE_NONE) { 388562306a36Sopenharmony_ci if (strlen(priv->firmware_id) == 0) { 388662306a36Sopenharmony_ci printk(KERN_INFO 388762306a36Sopenharmony_ci "%s: card type is unknown: assuming at76c502 firmware is OK.\n", 388862306a36Sopenharmony_ci dev->name); 388962306a36Sopenharmony_ci printk(KERN_INFO 389062306a36Sopenharmony_ci "%s: if not, use the firmware= module parameter.\n", 389162306a36Sopenharmony_ci dev->name); 389262306a36Sopenharmony_ci strcpy(priv->firmware_id, "atmel_at76c502.bin"); 389362306a36Sopenharmony_ci } 389462306a36Sopenharmony_ci err = request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev); 389562306a36Sopenharmony_ci if (err != 0) { 389662306a36Sopenharmony_ci printk(KERN_ALERT 389762306a36Sopenharmony_ci "%s: firmware %s is missing, cannot continue.\n", 389862306a36Sopenharmony_ci dev->name, priv->firmware_id); 389962306a36Sopenharmony_ci return err; 390062306a36Sopenharmony_ci } 390162306a36Sopenharmony_ci } else { 390262306a36Sopenharmony_ci int fw_index = 0; 390362306a36Sopenharmony_ci int success = 0; 390462306a36Sopenharmony_ci 390562306a36Sopenharmony_ci /* get firmware filename entry based on firmware type ID */ 390662306a36Sopenharmony_ci while (fw_table[fw_index].fw_type != priv->firmware_type 390762306a36Sopenharmony_ci && fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) 390862306a36Sopenharmony_ci fw_index++; 390962306a36Sopenharmony_ci 391062306a36Sopenharmony_ci /* construct the actual firmware file name */ 391162306a36Sopenharmony_ci if (fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) { 391262306a36Sopenharmony_ci int i; 391362306a36Sopenharmony_ci for (i = 0; firmware_modifier[i]; i++) { 391462306a36Sopenharmony_ci snprintf(priv->firmware_id, 32, "%s%s.%s", fw_table[fw_index].fw_file, 391562306a36Sopenharmony_ci firmware_modifier[i], fw_table[fw_index].fw_file_ext); 391662306a36Sopenharmony_ci priv->firmware_id[31] = '\0'; 391762306a36Sopenharmony_ci if (request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev) == 0) { 391862306a36Sopenharmony_ci success = 1; 391962306a36Sopenharmony_ci break; 392062306a36Sopenharmony_ci } 392162306a36Sopenharmony_ci } 392262306a36Sopenharmony_ci } 392362306a36Sopenharmony_ci if (!success) { 392462306a36Sopenharmony_ci printk(KERN_ALERT 392562306a36Sopenharmony_ci "%s: firmware %s is missing, cannot start.\n", 392662306a36Sopenharmony_ci dev->name, priv->firmware_id); 392762306a36Sopenharmony_ci priv->firmware_id[0] = '\0'; 392862306a36Sopenharmony_ci return -ENOENT; 392962306a36Sopenharmony_ci } 393062306a36Sopenharmony_ci } 393162306a36Sopenharmony_ci 393262306a36Sopenharmony_ci fw = fw_entry->data; 393362306a36Sopenharmony_ci len = fw_entry->size; 393462306a36Sopenharmony_ci } 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_ci if (len <= 0x6000) { 393762306a36Sopenharmony_ci atmel_write16(priv->dev, BSR, BSS_IRAM); 393862306a36Sopenharmony_ci atmel_copy_to_card(priv->dev, 0, fw, len); 393962306a36Sopenharmony_ci atmel_set_gcr(priv->dev, GCR_REMAP); 394062306a36Sopenharmony_ci } else { 394162306a36Sopenharmony_ci /* Remap */ 394262306a36Sopenharmony_ci atmel_set_gcr(priv->dev, GCR_REMAP); 394362306a36Sopenharmony_ci atmel_write16(priv->dev, BSR, BSS_IRAM); 394462306a36Sopenharmony_ci atmel_copy_to_card(priv->dev, 0, fw, 0x6000); 394562306a36Sopenharmony_ci atmel_write16(priv->dev, BSR, 0x2ff); 394662306a36Sopenharmony_ci atmel_copy_to_card(priv->dev, 0x8000, &fw[0x6000], len - 0x6000); 394762306a36Sopenharmony_ci } 394862306a36Sopenharmony_ci 394962306a36Sopenharmony_ci release_firmware(fw_entry); 395062306a36Sopenharmony_ci } 395162306a36Sopenharmony_ci 395262306a36Sopenharmony_ci err = atmel_wakeup_firmware(priv); 395362306a36Sopenharmony_ci if (err != 0) 395462306a36Sopenharmony_ci return err; 395562306a36Sopenharmony_ci 395662306a36Sopenharmony_ci /* Check the version and set the correct flag for wpa stuff, 395762306a36Sopenharmony_ci old and new firmware is incompatible. 395862306a36Sopenharmony_ci The pre-wpa 3com firmware reports major version 5, 395962306a36Sopenharmony_ci the wpa 3com firmware is major version 4 and doesn't need 396062306a36Sopenharmony_ci the 3com broken-ness filter. */ 396162306a36Sopenharmony_ci priv->use_wpa = (priv->host_info.major_version == 4); 396262306a36Sopenharmony_ci priv->radio_on_broken = (priv->host_info.major_version == 5); 396362306a36Sopenharmony_ci 396462306a36Sopenharmony_ci /* unmask all irq sources */ 396562306a36Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_MASK_OFFSET), 0xff); 396662306a36Sopenharmony_ci 396762306a36Sopenharmony_ci /* int Tx system and enable Tx */ 396862306a36Sopenharmony_ci atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, 0), 0); 396962306a36Sopenharmony_ci atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, 0), 0x80000000L); 397062306a36Sopenharmony_ci atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, 0), 0); 397162306a36Sopenharmony_ci atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, 0), 0); 397262306a36Sopenharmony_ci 397362306a36Sopenharmony_ci priv->tx_desc_free = priv->host_info.tx_desc_count; 397462306a36Sopenharmony_ci priv->tx_desc_head = 0; 397562306a36Sopenharmony_ci priv->tx_desc_tail = 0; 397662306a36Sopenharmony_ci priv->tx_desc_previous = 0; 397762306a36Sopenharmony_ci priv->tx_free_mem = priv->host_info.tx_buff_size; 397862306a36Sopenharmony_ci priv->tx_buff_head = 0; 397962306a36Sopenharmony_ci priv->tx_buff_tail = 0; 398062306a36Sopenharmony_ci 398162306a36Sopenharmony_ci configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); 398262306a36Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), 398362306a36Sopenharmony_ci configuration | FUNC_CTRL_TxENABLE); 398462306a36Sopenharmony_ci 398562306a36Sopenharmony_ci /* init Rx system and enable */ 398662306a36Sopenharmony_ci priv->rx_desc_head = 0; 398762306a36Sopenharmony_ci 398862306a36Sopenharmony_ci configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); 398962306a36Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), 399062306a36Sopenharmony_ci configuration | FUNC_CTRL_RxENABLE); 399162306a36Sopenharmony_ci 399262306a36Sopenharmony_ci if (!priv->radio_on_broken) { 399362306a36Sopenharmony_ci if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) == 399462306a36Sopenharmony_ci CMD_STATUS_REJECTED_RADIO_OFF) { 399562306a36Sopenharmony_ci printk(KERN_INFO "%s: cannot turn the radio on.\n", 399662306a36Sopenharmony_ci dev->name); 399762306a36Sopenharmony_ci return -EIO; 399862306a36Sopenharmony_ci } 399962306a36Sopenharmony_ci } 400062306a36Sopenharmony_ci 400162306a36Sopenharmony_ci /* set up enough MIB values to run. */ 400262306a36Sopenharmony_ci atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_AUTO_TX_RATE_POS, priv->auto_tx_rate); 400362306a36Sopenharmony_ci atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_TX_PROMISCUOUS_POS, PROM_MODE_OFF); 400462306a36Sopenharmony_ci atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_RTS_THRESHOLD_POS, priv->rts_threshold); 400562306a36Sopenharmony_ci atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_FRAG_THRESHOLD_POS, priv->frag_threshold); 400662306a36Sopenharmony_ci atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_SHORT_RETRY_POS, priv->short_retry); 400762306a36Sopenharmony_ci atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_LONG_RETRY_POS, priv->long_retry); 400862306a36Sopenharmony_ci atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, priv->preamble); 400962306a36Sopenharmony_ci atmel_set_mib(priv, Mac_Address_Mib_Type, MAC_ADDR_MIB_MAC_ADDR_POS, 401062306a36Sopenharmony_ci priv->dev->dev_addr, 6); 401162306a36Sopenharmony_ci atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); 401262306a36Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); 401362306a36Sopenharmony_ci atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_BEACON_PER_POS, priv->default_beacon_period); 401462306a36Sopenharmony_ci atmel_set_mib(priv, Phy_Mib_Type, PHY_MIB_RATE_SET_POS, atmel_basic_rates, 4); 401562306a36Sopenharmony_ci atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_PRIVACY_POS, priv->wep_is_on); 401662306a36Sopenharmony_ci if (priv->use_wpa) 401762306a36Sopenharmony_ci build_wpa_mib(priv); 401862306a36Sopenharmony_ci else 401962306a36Sopenharmony_ci build_wep_mib(priv); 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_ci if (old_state == STATION_STATE_READY) { 402262306a36Sopenharmony_ci union iwreq_data wrqu; 402362306a36Sopenharmony_ci 402462306a36Sopenharmony_ci wrqu.data.length = 0; 402562306a36Sopenharmony_ci wrqu.data.flags = 0; 402662306a36Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 402762306a36Sopenharmony_ci eth_zero_addr(wrqu.ap_addr.sa_data); 402862306a36Sopenharmony_ci wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); 402962306a36Sopenharmony_ci } 403062306a36Sopenharmony_ci 403162306a36Sopenharmony_ci return 0; 403262306a36Sopenharmony_ci} 403362306a36Sopenharmony_ci 403462306a36Sopenharmony_cistatic void atmel_send_command(struct atmel_private *priv, int command, 403562306a36Sopenharmony_ci void *cmd, int cmd_size) 403662306a36Sopenharmony_ci{ 403762306a36Sopenharmony_ci if (cmd) 403862306a36Sopenharmony_ci atmel_copy_to_card(priv->dev, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET), 403962306a36Sopenharmony_ci cmd, cmd_size); 404062306a36Sopenharmony_ci 404162306a36Sopenharmony_ci atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET), command); 404262306a36Sopenharmony_ci atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET), 0); 404362306a36Sopenharmony_ci} 404462306a36Sopenharmony_ci 404562306a36Sopenharmony_cistatic int atmel_send_command_wait(struct atmel_private *priv, int command, 404662306a36Sopenharmony_ci void *cmd, int cmd_size) 404762306a36Sopenharmony_ci{ 404862306a36Sopenharmony_ci int i, status; 404962306a36Sopenharmony_ci 405062306a36Sopenharmony_ci atmel_send_command(priv, command, cmd, cmd_size); 405162306a36Sopenharmony_ci 405262306a36Sopenharmony_ci for (i = 5000; i; i--) { 405362306a36Sopenharmony_ci status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); 405462306a36Sopenharmony_ci if (status != CMD_STATUS_IDLE && 405562306a36Sopenharmony_ci status != CMD_STATUS_IN_PROGRESS) 405662306a36Sopenharmony_ci break; 405762306a36Sopenharmony_ci udelay(20); 405862306a36Sopenharmony_ci } 405962306a36Sopenharmony_ci 406062306a36Sopenharmony_ci if (i == 0) { 406162306a36Sopenharmony_ci printk(KERN_ALERT "%s: failed to contact MAC.\n", priv->dev->name); 406262306a36Sopenharmony_ci status = CMD_STATUS_HOST_ERROR; 406362306a36Sopenharmony_ci } else { 406462306a36Sopenharmony_ci if (command != CMD_EnableRadio) 406562306a36Sopenharmony_ci status = CMD_STATUS_COMPLETE; 406662306a36Sopenharmony_ci } 406762306a36Sopenharmony_ci 406862306a36Sopenharmony_ci return status; 406962306a36Sopenharmony_ci} 407062306a36Sopenharmony_ci 407162306a36Sopenharmony_cistatic u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index) 407262306a36Sopenharmony_ci{ 407362306a36Sopenharmony_ci struct get_set_mib m; 407462306a36Sopenharmony_ci m.type = type; 407562306a36Sopenharmony_ci m.size = 1; 407662306a36Sopenharmony_ci m.index = index; 407762306a36Sopenharmony_ci 407862306a36Sopenharmony_ci atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + 1); 407962306a36Sopenharmony_ci return atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE)); 408062306a36Sopenharmony_ci} 408162306a36Sopenharmony_ci 408262306a36Sopenharmony_cistatic void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, u8 data) 408362306a36Sopenharmony_ci{ 408462306a36Sopenharmony_ci struct get_set_mib m; 408562306a36Sopenharmony_ci m.type = type; 408662306a36Sopenharmony_ci m.size = 1; 408762306a36Sopenharmony_ci m.index = index; 408862306a36Sopenharmony_ci m.data[0] = data; 408962306a36Sopenharmony_ci 409062306a36Sopenharmony_ci atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 1); 409162306a36Sopenharmony_ci} 409262306a36Sopenharmony_ci 409362306a36Sopenharmony_cistatic void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, 409462306a36Sopenharmony_ci u16 data) 409562306a36Sopenharmony_ci{ 409662306a36Sopenharmony_ci struct get_set_mib m; 409762306a36Sopenharmony_ci m.type = type; 409862306a36Sopenharmony_ci m.size = 2; 409962306a36Sopenharmony_ci m.index = index; 410062306a36Sopenharmony_ci m.data[0] = data; 410162306a36Sopenharmony_ci m.data[1] = data >> 8; 410262306a36Sopenharmony_ci 410362306a36Sopenharmony_ci atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 2); 410462306a36Sopenharmony_ci} 410562306a36Sopenharmony_ci 410662306a36Sopenharmony_cistatic void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, 410762306a36Sopenharmony_ci const u8 *data, int data_len) 410862306a36Sopenharmony_ci{ 410962306a36Sopenharmony_ci struct get_set_mib m; 411062306a36Sopenharmony_ci m.type = type; 411162306a36Sopenharmony_ci m.size = data_len; 411262306a36Sopenharmony_ci m.index = index; 411362306a36Sopenharmony_ci 411462306a36Sopenharmony_ci if (data_len > MIB_MAX_DATA_BYTES) 411562306a36Sopenharmony_ci printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); 411662306a36Sopenharmony_ci 411762306a36Sopenharmony_ci memcpy(m.data, data, data_len); 411862306a36Sopenharmony_ci atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); 411962306a36Sopenharmony_ci} 412062306a36Sopenharmony_ci 412162306a36Sopenharmony_cistatic void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, 412262306a36Sopenharmony_ci u8 *data, int data_len) 412362306a36Sopenharmony_ci{ 412462306a36Sopenharmony_ci struct get_set_mib m; 412562306a36Sopenharmony_ci m.type = type; 412662306a36Sopenharmony_ci m.size = data_len; 412762306a36Sopenharmony_ci m.index = index; 412862306a36Sopenharmony_ci 412962306a36Sopenharmony_ci if (data_len > MIB_MAX_DATA_BYTES) 413062306a36Sopenharmony_ci printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); 413162306a36Sopenharmony_ci 413262306a36Sopenharmony_ci atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); 413362306a36Sopenharmony_ci atmel_copy_to_host(priv->dev, data, 413462306a36Sopenharmony_ci atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE), data_len); 413562306a36Sopenharmony_ci} 413662306a36Sopenharmony_ci 413762306a36Sopenharmony_cistatic void atmel_writeAR(struct net_device *dev, u16 data) 413862306a36Sopenharmony_ci{ 413962306a36Sopenharmony_ci int i; 414062306a36Sopenharmony_ci outw(data, dev->base_addr + AR); 414162306a36Sopenharmony_ci /* Address register appears to need some convincing..... */ 414262306a36Sopenharmony_ci for (i = 0; data != inw(dev->base_addr + AR) && i < 10; i++) 414362306a36Sopenharmony_ci outw(data, dev->base_addr + AR); 414462306a36Sopenharmony_ci} 414562306a36Sopenharmony_ci 414662306a36Sopenharmony_cistatic void atmel_copy_to_card(struct net_device *dev, u16 dest, 414762306a36Sopenharmony_ci const unsigned char *src, u16 len) 414862306a36Sopenharmony_ci{ 414962306a36Sopenharmony_ci int i; 415062306a36Sopenharmony_ci atmel_writeAR(dev, dest); 415162306a36Sopenharmony_ci if (dest % 2) { 415262306a36Sopenharmony_ci atmel_write8(dev, DR, *src); 415362306a36Sopenharmony_ci src++; len--; 415462306a36Sopenharmony_ci } 415562306a36Sopenharmony_ci for (i = len; i > 1 ; i -= 2) { 415662306a36Sopenharmony_ci u8 lb = *src++; 415762306a36Sopenharmony_ci u8 hb = *src++; 415862306a36Sopenharmony_ci atmel_write16(dev, DR, lb | (hb << 8)); 415962306a36Sopenharmony_ci } 416062306a36Sopenharmony_ci if (i) 416162306a36Sopenharmony_ci atmel_write8(dev, DR, *src); 416262306a36Sopenharmony_ci} 416362306a36Sopenharmony_ci 416462306a36Sopenharmony_cistatic void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, 416562306a36Sopenharmony_ci u16 src, u16 len) 416662306a36Sopenharmony_ci{ 416762306a36Sopenharmony_ci int i; 416862306a36Sopenharmony_ci atmel_writeAR(dev, src); 416962306a36Sopenharmony_ci if (src % 2) { 417062306a36Sopenharmony_ci *dest = atmel_read8(dev, DR); 417162306a36Sopenharmony_ci dest++; len--; 417262306a36Sopenharmony_ci } 417362306a36Sopenharmony_ci for (i = len; i > 1 ; i -= 2) { 417462306a36Sopenharmony_ci u16 hw = atmel_read16(dev, DR); 417562306a36Sopenharmony_ci *dest++ = hw; 417662306a36Sopenharmony_ci *dest++ = hw >> 8; 417762306a36Sopenharmony_ci } 417862306a36Sopenharmony_ci if (i) 417962306a36Sopenharmony_ci *dest = atmel_read8(dev, DR); 418062306a36Sopenharmony_ci} 418162306a36Sopenharmony_ci 418262306a36Sopenharmony_cistatic void atmel_set_gcr(struct net_device *dev, u16 mask) 418362306a36Sopenharmony_ci{ 418462306a36Sopenharmony_ci outw(inw(dev->base_addr + GCR) | mask, dev->base_addr + GCR); 418562306a36Sopenharmony_ci} 418662306a36Sopenharmony_ci 418762306a36Sopenharmony_cistatic void atmel_clear_gcr(struct net_device *dev, u16 mask) 418862306a36Sopenharmony_ci{ 418962306a36Sopenharmony_ci outw(inw(dev->base_addr + GCR) & ~mask, dev->base_addr + GCR); 419062306a36Sopenharmony_ci} 419162306a36Sopenharmony_ci 419262306a36Sopenharmony_cistatic int atmel_lock_mac(struct atmel_private *priv) 419362306a36Sopenharmony_ci{ 419462306a36Sopenharmony_ci int i, j = 20; 419562306a36Sopenharmony_ci retry: 419662306a36Sopenharmony_ci for (i = 5000; i; i--) { 419762306a36Sopenharmony_ci if (!atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) 419862306a36Sopenharmony_ci break; 419962306a36Sopenharmony_ci udelay(20); 420062306a36Sopenharmony_ci } 420162306a36Sopenharmony_ci 420262306a36Sopenharmony_ci if (!i) 420362306a36Sopenharmony_ci return 0; /* timed out */ 420462306a36Sopenharmony_ci 420562306a36Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 1); 420662306a36Sopenharmony_ci if (atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) { 420762306a36Sopenharmony_ci atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); 420862306a36Sopenharmony_ci if (!j--) 420962306a36Sopenharmony_ci return 0; /* timed out */ 421062306a36Sopenharmony_ci goto retry; 421162306a36Sopenharmony_ci } 421262306a36Sopenharmony_ci 421362306a36Sopenharmony_ci return 1; 421462306a36Sopenharmony_ci} 421562306a36Sopenharmony_ci 421662306a36Sopenharmony_cistatic void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data) 421762306a36Sopenharmony_ci{ 421862306a36Sopenharmony_ci atmel_writeAR(priv->dev, pos); 421962306a36Sopenharmony_ci atmel_write16(priv->dev, DR, data); /* card is little-endian */ 422062306a36Sopenharmony_ci atmel_write16(priv->dev, DR, data >> 16); 422162306a36Sopenharmony_ci} 422262306a36Sopenharmony_ci 422362306a36Sopenharmony_ci/***************************************************************************/ 422462306a36Sopenharmony_ci/* There follows the source form of the MAC address reading firmware */ 422562306a36Sopenharmony_ci/***************************************************************************/ 422662306a36Sopenharmony_ci#if 0 422762306a36Sopenharmony_ci 422862306a36Sopenharmony_ci/* Copyright 2003 Matthew T. Russotto */ 422962306a36Sopenharmony_ci/* But derived from the Atmel 76C502 firmware written by Atmel and */ 423062306a36Sopenharmony_ci/* included in "atmel wireless lan drivers" package */ 423162306a36Sopenharmony_ci/* 423262306a36Sopenharmony_ci This file is part of net.russotto.AtmelMACFW, hereto referred to 423362306a36Sopenharmony_ci as AtmelMACFW 423462306a36Sopenharmony_ci 423562306a36Sopenharmony_ci AtmelMACFW is free software; you can redistribute it and/or modify 423662306a36Sopenharmony_ci it under the terms of the GNU General Public License version 2 423762306a36Sopenharmony_ci as published by the Free Software Foundation. 423862306a36Sopenharmony_ci 423962306a36Sopenharmony_ci AtmelMACFW is distributed in the hope that it will be useful, 424062306a36Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 424162306a36Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 424262306a36Sopenharmony_ci GNU General Public License for more details. 424362306a36Sopenharmony_ci 424462306a36Sopenharmony_ci You should have received a copy of the GNU General Public License 424562306a36Sopenharmony_ci along with AtmelMACFW; if not, see <http://www.gnu.org/licenses/>. 424662306a36Sopenharmony_ci 424762306a36Sopenharmony_ci****************************************************************************/ 424862306a36Sopenharmony_ci/* This firmware should work on the 76C502 RFMD, RFMD_D, and RFMD_E */ 424962306a36Sopenharmony_ci/* It will probably work on the 76C504 and 76C502 RFMD_3COM */ 425062306a36Sopenharmony_ci/* It only works on SPI EEPROM versions of the card. */ 425162306a36Sopenharmony_ci 425262306a36Sopenharmony_ci/* This firmware initializes the SPI controller and clock, reads the MAC */ 425362306a36Sopenharmony_ci/* address from the EEPROM into SRAM, and puts the SRAM offset of the MAC */ 425462306a36Sopenharmony_ci/* address in MR2, and sets MR3 to 0x10 to indicate it is done */ 425562306a36Sopenharmony_ci/* It also puts a complete copy of the EEPROM in SRAM with the offset in */ 425662306a36Sopenharmony_ci/* MR4, for investigational purposes (maybe we can determine chip type */ 425762306a36Sopenharmony_ci/* from that?) */ 425862306a36Sopenharmony_ci 425962306a36Sopenharmony_ci .org 0 426062306a36Sopenharmony_ci .set MRBASE, 0x8000000 426162306a36Sopenharmony_ci .set CPSR_INITIAL, 0xD3 /* IRQ/FIQ disabled, ARM mode, Supervisor state */ 426262306a36Sopenharmony_ci .set CPSR_USER, 0xD1 /* IRQ/FIQ disabled, ARM mode, USER state */ 426362306a36Sopenharmony_ci .set SRAM_BASE, 0x02000000 426462306a36Sopenharmony_ci .set SP_BASE, 0x0F300000 426562306a36Sopenharmony_ci .set UNK_BASE, 0x0F000000 /* Some internal device, but which one? */ 426662306a36Sopenharmony_ci .set SPI_CGEN_BASE, 0x0E000000 /* Some internal device, but which one? */ 426762306a36Sopenharmony_ci .set UNK3_BASE, 0x02014000 /* Some internal device, but which one? */ 426862306a36Sopenharmony_ci .set STACK_BASE, 0x5600 426962306a36Sopenharmony_ci .set SP_SR, 0x10 427062306a36Sopenharmony_ci .set SP_TDRE, 2 /* status register bit -- TDR empty */ 427162306a36Sopenharmony_ci .set SP_RDRF, 1 /* status register bit -- RDR full */ 427262306a36Sopenharmony_ci .set SP_SWRST, 0x80 427362306a36Sopenharmony_ci .set SP_SPIEN, 0x1 427462306a36Sopenharmony_ci .set SP_CR, 0 /* control register */ 427562306a36Sopenharmony_ci .set SP_MR, 4 /* mode register */ 427662306a36Sopenharmony_ci .set SP_RDR, 0x08 /* Read Data Register */ 427762306a36Sopenharmony_ci .set SP_TDR, 0x0C /* Transmit Data Register */ 427862306a36Sopenharmony_ci .set SP_CSR0, 0x30 /* chip select registers */ 427962306a36Sopenharmony_ci .set SP_CSR1, 0x34 428062306a36Sopenharmony_ci .set SP_CSR2, 0x38 428162306a36Sopenharmony_ci .set SP_CSR3, 0x3C 428262306a36Sopenharmony_ci .set NVRAM_CMD_RDSR, 5 /* read status register */ 428362306a36Sopenharmony_ci .set NVRAM_CMD_READ, 3 /* read data */ 428462306a36Sopenharmony_ci .set NVRAM_SR_RDY, 1 /* RDY bit. This bit is inverted */ 428562306a36Sopenharmony_ci .set SPI_8CLOCKS, 0xFF /* Writing this to the TDR doesn't do anything to the 428662306a36Sopenharmony_ci serial output, since SO is normally high. But it 428762306a36Sopenharmony_ci does cause 8 clock cycles and thus 8 bits to be 428862306a36Sopenharmony_ci clocked in to the chip. See Atmel's SPI 428962306a36Sopenharmony_ci controller (e.g. AT91M55800) timing and 4K 429062306a36Sopenharmony_ci SPI EEPROM manuals */ 429162306a36Sopenharmony_ci 429262306a36Sopenharmony_ci .set NVRAM_SCRATCH, 0x02000100 /* arbitrary area for scratchpad memory */ 429362306a36Sopenharmony_ci .set NVRAM_IMAGE, 0x02000200 429462306a36Sopenharmony_ci .set NVRAM_LENGTH, 0x0200 429562306a36Sopenharmony_ci .set MAC_ADDRESS_MIB, SRAM_BASE 429662306a36Sopenharmony_ci .set MAC_ADDRESS_LENGTH, 6 429762306a36Sopenharmony_ci .set MAC_BOOT_FLAG, 0x10 429862306a36Sopenharmony_ci .set MR1, 0 429962306a36Sopenharmony_ci .set MR2, 4 430062306a36Sopenharmony_ci .set MR3, 8 430162306a36Sopenharmony_ci .set MR4, 0xC 430262306a36Sopenharmony_ciRESET_VECTOR: 430362306a36Sopenharmony_ci b RESET_HANDLER 430462306a36Sopenharmony_ciUNDEF_VECTOR: 430562306a36Sopenharmony_ci b HALT1 430662306a36Sopenharmony_ciSWI_VECTOR: 430762306a36Sopenharmony_ci b HALT1 430862306a36Sopenharmony_ciIABORT_VECTOR: 430962306a36Sopenharmony_ci b HALT1 431062306a36Sopenharmony_ciDABORT_VECTOR: 431162306a36Sopenharmony_ciRESERVED_VECTOR: 431262306a36Sopenharmony_ci b HALT1 431362306a36Sopenharmony_ciIRQ_VECTOR: 431462306a36Sopenharmony_ci b HALT1 431562306a36Sopenharmony_ciFIQ_VECTOR: 431662306a36Sopenharmony_ci b HALT1 431762306a36Sopenharmony_ciHALT1: b HALT1 431862306a36Sopenharmony_ciRESET_HANDLER: 431962306a36Sopenharmony_ci mov r0, #CPSR_INITIAL 432062306a36Sopenharmony_ci msr CPSR_c, r0 /* This is probably unnecessary */ 432162306a36Sopenharmony_ci 432262306a36Sopenharmony_ci/* I'm guessing this is initializing clock generator electronics for SPI */ 432362306a36Sopenharmony_ci ldr r0, =SPI_CGEN_BASE 432462306a36Sopenharmony_ci mov r1, #0 432562306a36Sopenharmony_ci mov r1, r1, lsl #3 432662306a36Sopenharmony_ci orr r1, r1, #0 432762306a36Sopenharmony_ci str r1, [r0] 432862306a36Sopenharmony_ci ldr r1, [r0, #28] 432962306a36Sopenharmony_ci bic r1, r1, #16 433062306a36Sopenharmony_ci str r1, [r0, #28] 433162306a36Sopenharmony_ci mov r1, #1 433262306a36Sopenharmony_ci str r1, [r0, #8] 433362306a36Sopenharmony_ci 433462306a36Sopenharmony_ci ldr r0, =MRBASE 433562306a36Sopenharmony_ci mov r1, #0 433662306a36Sopenharmony_ci strh r1, [r0, #MR1] 433762306a36Sopenharmony_ci strh r1, [r0, #MR2] 433862306a36Sopenharmony_ci strh r1, [r0, #MR3] 433962306a36Sopenharmony_ci strh r1, [r0, #MR4] 434062306a36Sopenharmony_ci 434162306a36Sopenharmony_ci mov sp, #STACK_BASE 434262306a36Sopenharmony_ci bl SP_INIT 434362306a36Sopenharmony_ci mov r0, #10 434462306a36Sopenharmony_ci bl DELAY9 434562306a36Sopenharmony_ci bl GET_MAC_ADDR 434662306a36Sopenharmony_ci bl GET_WHOLE_NVRAM 434762306a36Sopenharmony_ci ldr r0, =MRBASE 434862306a36Sopenharmony_ci ldr r1, =MAC_ADDRESS_MIB 434962306a36Sopenharmony_ci strh r1, [r0, #MR2] 435062306a36Sopenharmony_ci ldr r1, =NVRAM_IMAGE 435162306a36Sopenharmony_ci strh r1, [r0, #MR4] 435262306a36Sopenharmony_ci mov r1, #MAC_BOOT_FLAG 435362306a36Sopenharmony_ci strh r1, [r0, #MR3] 435462306a36Sopenharmony_ciHALT2: b HALT2 435562306a36Sopenharmony_ci.func Get_Whole_NVRAM, GET_WHOLE_NVRAM 435662306a36Sopenharmony_ciGET_WHOLE_NVRAM: 435762306a36Sopenharmony_ci stmdb sp!, {lr} 435862306a36Sopenharmony_ci mov r2, #0 /* 0th bytes of NVRAM */ 435962306a36Sopenharmony_ci mov r3, #NVRAM_LENGTH 436062306a36Sopenharmony_ci mov r1, #0 /* not used in routine */ 436162306a36Sopenharmony_ci ldr r0, =NVRAM_IMAGE 436262306a36Sopenharmony_ci bl NVRAM_XFER 436362306a36Sopenharmony_ci ldmia sp!, {lr} 436462306a36Sopenharmony_ci bx lr 436562306a36Sopenharmony_ci.endfunc 436662306a36Sopenharmony_ci 436762306a36Sopenharmony_ci.func Get_MAC_Addr, GET_MAC_ADDR 436862306a36Sopenharmony_ciGET_MAC_ADDR: 436962306a36Sopenharmony_ci stmdb sp!, {lr} 437062306a36Sopenharmony_ci mov r2, #0x120 /* address of MAC Address within NVRAM */ 437162306a36Sopenharmony_ci mov r3, #MAC_ADDRESS_LENGTH 437262306a36Sopenharmony_ci mov r1, #0 /* not used in routine */ 437362306a36Sopenharmony_ci ldr r0, =MAC_ADDRESS_MIB 437462306a36Sopenharmony_ci bl NVRAM_XFER 437562306a36Sopenharmony_ci ldmia sp!, {lr} 437662306a36Sopenharmony_ci bx lr 437762306a36Sopenharmony_ci.endfunc 437862306a36Sopenharmony_ci.ltorg 437962306a36Sopenharmony_ci.func Delay9, DELAY9 438062306a36Sopenharmony_ciDELAY9: 438162306a36Sopenharmony_ci adds r0, r0, r0, LSL #3 /* r0 = r0 * 9 */ 438262306a36Sopenharmony_ciDELAYLOOP: 438362306a36Sopenharmony_ci beq DELAY9_done 438462306a36Sopenharmony_ci subs r0, r0, #1 438562306a36Sopenharmony_ci b DELAYLOOP 438662306a36Sopenharmony_ciDELAY9_done: 438762306a36Sopenharmony_ci bx lr 438862306a36Sopenharmony_ci.endfunc 438962306a36Sopenharmony_ci 439062306a36Sopenharmony_ci.func SP_Init, SP_INIT 439162306a36Sopenharmony_ciSP_INIT: 439262306a36Sopenharmony_ci mov r1, #SP_SWRST 439362306a36Sopenharmony_ci ldr r0, =SP_BASE 439462306a36Sopenharmony_ci str r1, [r0, #SP_CR] /* reset the SPI */ 439562306a36Sopenharmony_ci mov r1, #0 439662306a36Sopenharmony_ci str r1, [r0, #SP_CR] /* release SPI from reset state */ 439762306a36Sopenharmony_ci mov r1, #SP_SPIEN 439862306a36Sopenharmony_ci str r1, [r0, #SP_MR] /* set the SPI to MASTER mode*/ 439962306a36Sopenharmony_ci str r1, [r0, #SP_CR] /* enable the SPI */ 440062306a36Sopenharmony_ci 440162306a36Sopenharmony_ci/* My guess would be this turns on the SPI clock */ 440262306a36Sopenharmony_ci ldr r3, =SPI_CGEN_BASE 440362306a36Sopenharmony_ci ldr r1, [r3, #28] 440462306a36Sopenharmony_ci orr r1, r1, #0x2000 440562306a36Sopenharmony_ci str r1, [r3, #28] 440662306a36Sopenharmony_ci 440762306a36Sopenharmony_ci ldr r1, =0x2000c01 440862306a36Sopenharmony_ci str r1, [r0, #SP_CSR0] 440962306a36Sopenharmony_ci ldr r1, =0x2000201 441062306a36Sopenharmony_ci str r1, [r0, #SP_CSR1] 441162306a36Sopenharmony_ci str r1, [r0, #SP_CSR2] 441262306a36Sopenharmony_ci str r1, [r0, #SP_CSR3] 441362306a36Sopenharmony_ci ldr r1, [r0, #SP_SR] 441462306a36Sopenharmony_ci ldr r0, [r0, #SP_RDR] 441562306a36Sopenharmony_ci bx lr 441662306a36Sopenharmony_ci.endfunc 441762306a36Sopenharmony_ci.func NVRAM_Init, NVRAM_INIT 441862306a36Sopenharmony_ciNVRAM_INIT: 441962306a36Sopenharmony_ci ldr r1, =SP_BASE 442062306a36Sopenharmony_ci ldr r0, [r1, #SP_RDR] 442162306a36Sopenharmony_ci mov r0, #NVRAM_CMD_RDSR 442262306a36Sopenharmony_ci str r0, [r1, #SP_TDR] 442362306a36Sopenharmony_ciSP_loop1: 442462306a36Sopenharmony_ci ldr r0, [r1, #SP_SR] 442562306a36Sopenharmony_ci tst r0, #SP_TDRE 442662306a36Sopenharmony_ci beq SP_loop1 442762306a36Sopenharmony_ci 442862306a36Sopenharmony_ci mov r0, #SPI_8CLOCKS 442962306a36Sopenharmony_ci str r0, [r1, #SP_TDR] 443062306a36Sopenharmony_ciSP_loop2: 443162306a36Sopenharmony_ci ldr r0, [r1, #SP_SR] 443262306a36Sopenharmony_ci tst r0, #SP_TDRE 443362306a36Sopenharmony_ci beq SP_loop2 443462306a36Sopenharmony_ci 443562306a36Sopenharmony_ci ldr r0, [r1, #SP_RDR] 443662306a36Sopenharmony_ciSP_loop3: 443762306a36Sopenharmony_ci ldr r0, [r1, #SP_SR] 443862306a36Sopenharmony_ci tst r0, #SP_RDRF 443962306a36Sopenharmony_ci beq SP_loop3 444062306a36Sopenharmony_ci 444162306a36Sopenharmony_ci ldr r0, [r1, #SP_RDR] 444262306a36Sopenharmony_ci and r0, r0, #255 444362306a36Sopenharmony_ci bx lr 444462306a36Sopenharmony_ci.endfunc 444562306a36Sopenharmony_ci 444662306a36Sopenharmony_ci.func NVRAM_Xfer, NVRAM_XFER 444762306a36Sopenharmony_ci /* r0 = dest address */ 444862306a36Sopenharmony_ci /* r1 = not used */ 444962306a36Sopenharmony_ci /* r2 = src address within NVRAM */ 445062306a36Sopenharmony_ci /* r3 = length */ 445162306a36Sopenharmony_ciNVRAM_XFER: 445262306a36Sopenharmony_ci stmdb sp!, {r4, r5, lr} 445362306a36Sopenharmony_ci mov r5, r0 /* save r0 (dest address) */ 445462306a36Sopenharmony_ci mov r4, r3 /* save r3 (length) */ 445562306a36Sopenharmony_ci mov r0, r2, LSR #5 /* SPI memories put A8 in the command field */ 445662306a36Sopenharmony_ci and r0, r0, #8 445762306a36Sopenharmony_ci add r0, r0, #NVRAM_CMD_READ 445862306a36Sopenharmony_ci ldr r1, =NVRAM_SCRATCH 445962306a36Sopenharmony_ci strb r0, [r1, #0] /* save command in NVRAM_SCRATCH[0] */ 446062306a36Sopenharmony_ci strb r2, [r1, #1] /* save low byte of source address in NVRAM_SCRATCH[1] */ 446162306a36Sopenharmony_ci_local1: 446262306a36Sopenharmony_ci bl NVRAM_INIT 446362306a36Sopenharmony_ci tst r0, #NVRAM_SR_RDY 446462306a36Sopenharmony_ci bne _local1 446562306a36Sopenharmony_ci mov r0, #20 446662306a36Sopenharmony_ci bl DELAY9 446762306a36Sopenharmony_ci mov r2, r4 /* length */ 446862306a36Sopenharmony_ci mov r1, r5 /* dest address */ 446962306a36Sopenharmony_ci mov r0, #2 /* bytes to transfer in command */ 447062306a36Sopenharmony_ci bl NVRAM_XFER2 447162306a36Sopenharmony_ci ldmia sp!, {r4, r5, lr} 447262306a36Sopenharmony_ci bx lr 447362306a36Sopenharmony_ci.endfunc 447462306a36Sopenharmony_ci 447562306a36Sopenharmony_ci.func NVRAM_Xfer2, NVRAM_XFER2 447662306a36Sopenharmony_ciNVRAM_XFER2: 447762306a36Sopenharmony_ci stmdb sp!, {r4, r5, r6, lr} 447862306a36Sopenharmony_ci ldr r4, =SP_BASE 447962306a36Sopenharmony_ci mov r3, #0 448062306a36Sopenharmony_ci cmp r0, #0 448162306a36Sopenharmony_ci bls _local2 448262306a36Sopenharmony_ci ldr r5, =NVRAM_SCRATCH 448362306a36Sopenharmony_ci_local4: 448462306a36Sopenharmony_ci ldrb r6, [r5, r3] 448562306a36Sopenharmony_ci str r6, [r4, #SP_TDR] 448662306a36Sopenharmony_ci_local3: 448762306a36Sopenharmony_ci ldr r6, [r4, #SP_SR] 448862306a36Sopenharmony_ci tst r6, #SP_TDRE 448962306a36Sopenharmony_ci beq _local3 449062306a36Sopenharmony_ci add r3, r3, #1 449162306a36Sopenharmony_ci cmp r3, r0 /* r0 is # of bytes to send out (command+addr) */ 449262306a36Sopenharmony_ci blo _local4 449362306a36Sopenharmony_ci_local2: 449462306a36Sopenharmony_ci mov r3, #SPI_8CLOCKS 449562306a36Sopenharmony_ci str r3, [r4, #SP_TDR] 449662306a36Sopenharmony_ci ldr r0, [r4, #SP_RDR] 449762306a36Sopenharmony_ci_local5: 449862306a36Sopenharmony_ci ldr r0, [r4, #SP_SR] 449962306a36Sopenharmony_ci tst r0, #SP_RDRF 450062306a36Sopenharmony_ci beq _local5 450162306a36Sopenharmony_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 */ 450262306a36Sopenharmony_ci mov r0, #0 450362306a36Sopenharmony_ci cmp r2, #0 /* r2 is # of bytes to copy in */ 450462306a36Sopenharmony_ci bls _local6 450562306a36Sopenharmony_ci_local7: 450662306a36Sopenharmony_ci ldr r5, [r4, #SP_SR] 450762306a36Sopenharmony_ci tst r5, #SP_TDRE 450862306a36Sopenharmony_ci beq _local7 450962306a36Sopenharmony_ci str r3, [r4, #SP_TDR] /* r3 has SPI_8CLOCKS */ 451062306a36Sopenharmony_ci_local8: 451162306a36Sopenharmony_ci ldr r5, [r4, #SP_SR] 451262306a36Sopenharmony_ci tst r5, #SP_RDRF 451362306a36Sopenharmony_ci beq _local8 451462306a36Sopenharmony_ci ldr r5, [r4, #SP_RDR] /* but didn't we read this byte above? */ 451562306a36Sopenharmony_ci strb r5, [r1], #1 /* postindexed */ 451662306a36Sopenharmony_ci add r0, r0, #1 451762306a36Sopenharmony_ci cmp r0, r2 451862306a36Sopenharmony_ci blo _local7 /* since we don't send another address, the NVRAM must be capable of sequential reads */ 451962306a36Sopenharmony_ci_local6: 452062306a36Sopenharmony_ci mov r0, #200 452162306a36Sopenharmony_ci bl DELAY9 452262306a36Sopenharmony_ci ldmia sp!, {r4, r5, r6, lr} 452362306a36Sopenharmony_ci bx lr 452462306a36Sopenharmony_ci#endif 4525