blob: 6a1f03c271c1c04074ec76fcad031f5bce954382 [file] [log] [blame]
/*** -*- linux-c -*- **********************************************************
Driver for Atmel at76c502 at76c504 and at76c506 wireless cards.
Copyright 2000-2001 ATMEL Corporation.
Copyright 2003-2004 Simon Kelley.
This code was developed from version 2.1.1 of the Atmel drivers,
released by Atmel corp. under the GPL in December 2002. It also
includes code from the Linux aironet drivers (C) Benjamin Reed,
and the Linux PCMCIA package, (C) David Hinds and the Linux wireless
extensions, (C) Jean Tourrilhes.
The firmware module for reading the MAC address of the card comes from
net.russotto.AtmelMACFW, written by Matthew T. Russotto and copyright
by him. net.russotto.AtmelMACFW is used under the GPL license version 2.
This file contains the module in binary form and, under the terms
of the GPL, in source form. The source is located at the end of the file.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Atmel wireless lan drivers; if not, see
<http://www.gnu.org/licenses/>.
For all queries about this code, please contact the current author,
Simon Kelley <simon@thekelleys.org.uk> and not Atmel Corporation.
Credit is due to HP UK and Cambridge Online Systems Ltd for supplying
hardware used during development of this driver.
******************************************************************************/
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/wireless.h>
#include <net/iw_handler.h>
#include <linux/crc32.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#include <linux/jiffies.h>
#include <net/cfg80211.h>
#include "atmel.h"
#define DRIVER_MAJOR 0
#define DRIVER_MINOR 98
MODULE_AUTHOR("Simon Kelley");
MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards.");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("Atmel at76c50x wireless cards");
/* The name of the firmware file to be loaded
over-rides any automatic selection */
static char *firmware = NULL;
module_param(firmware, charp, 0);
/* table of firmware file names */
static struct {
AtmelFWType fw_type;
const char *fw_file;
const char *fw_file_ext;
} fw_table[] = {
{ ATMEL_FW_TYPE_502, "atmel_at76c502", "bin" },
{ ATMEL_FW_TYPE_502D, "atmel_at76c502d", "bin" },
{ ATMEL_FW_TYPE_502E, "atmel_at76c502e", "bin" },
{ ATMEL_FW_TYPE_502_3COM, "atmel_at76c502_3com", "bin" },
{ ATMEL_FW_TYPE_504, "atmel_at76c504", "bin" },
{ ATMEL_FW_TYPE_504_2958, "atmel_at76c504_2958", "bin" },
{ ATMEL_FW_TYPE_504A_2958, "atmel_at76c504a_2958", "bin" },
{ ATMEL_FW_TYPE_506, "atmel_at76c506", "bin" },
{ ATMEL_FW_TYPE_NONE, NULL, NULL }
};
MODULE_FIRMWARE("atmel_at76c502-wpa.bin");
MODULE_FIRMWARE("atmel_at76c502.bin");
MODULE_FIRMWARE("atmel_at76c502d-wpa.bin");
MODULE_FIRMWARE("atmel_at76c502d.bin");
MODULE_FIRMWARE("atmel_at76c502e-wpa.bin");
MODULE_FIRMWARE("atmel_at76c502e.bin");
MODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin");
MODULE_FIRMWARE("atmel_at76c502_3com.bin");
MODULE_FIRMWARE("atmel_at76c504-wpa.bin");
MODULE_FIRMWARE("atmel_at76c504.bin");
MODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin");
MODULE_FIRMWARE("atmel_at76c504_2958.bin");
MODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin");
MODULE_FIRMWARE("atmel_at76c504a_2958.bin");
MODULE_FIRMWARE("atmel_at76c506-wpa.bin");
MODULE_FIRMWARE("atmel_at76c506.bin");
#define MAX_SSID_LENGTH 32
#define MGMT_JIFFIES (256 * HZ / 100)
#define MAX_BSS_ENTRIES 64
/* registers */
#define GCR 0x00 /* (SIR0) General Configuration Register */
#define BSR 0x02 /* (SIR1) Bank Switching Select Register */
#define AR 0x04
#define DR 0x08
#define MR1 0x12 /* Mirror Register 1 */
#define MR2 0x14 /* Mirror Register 2 */
#define MR3 0x16 /* Mirror Register 3 */
#define MR4 0x18 /* Mirror Register 4 */
#define GPR1 0x0c
#define GPR2 0x0e
#define GPR3 0x10
/*
* Constants for the GCR register.
*/
#define GCR_REMAP 0x0400 /* Remap internal SRAM to 0 */
#define GCR_SWRES 0x0080 /* BIU reset (ARM and PAI are NOT reset) */
#define GCR_CORES 0x0060 /* Core Reset (ARM and PAI are reset) */
#define GCR_ENINT 0x0002 /* Enable Interrupts */
#define GCR_ACKINT 0x0008 /* Acknowledge Interrupts */
#define BSS_SRAM 0x0200 /* AMBA module selection --> SRAM */
#define BSS_IRAM 0x0100 /* AMBA module selection --> IRAM */
/*
*Constants for the MR registers.
*/
#define MAC_INIT_COMPLETE 0x0001 /* MAC init has been completed */
#define MAC_BOOT_COMPLETE 0x0010 /* MAC boot has been completed */
#define MAC_INIT_OK 0x0002 /* MAC boot has been completed */
#define MIB_MAX_DATA_BYTES 212
#define MIB_HEADER_SIZE 4 /* first four fields */
struct get_set_mib {
u8 type;
u8 size;
u8 index;
u8 reserved;
u8 data[MIB_MAX_DATA_BYTES];
};
struct rx_desc {
u32 Next;
u16 MsduPos;
u16 MsduSize;
u8 State;
u8 Status;
u8 Rate;
u8 Rssi;
u8 LinkQuality;
u8 PreambleType;
u16 Duration;
u32 RxTime;
};
#define RX_DESC_FLAG_VALID 0x80
#define RX_DESC_FLAG_CONSUMED 0x40
#define RX_DESC_FLAG_IDLE 0x00
#define RX_STATUS_SUCCESS 0x00
#define RX_DESC_MSDU_POS_OFFSET 4
#define RX_DESC_MSDU_SIZE_OFFSET 6
#define RX_DESC_FLAGS_OFFSET 8
#define RX_DESC_STATUS_OFFSET 9
#define RX_DESC_RSSI_OFFSET 11
#define RX_DESC_LINK_QUALITY_OFFSET 12
#define RX_DESC_PREAMBLE_TYPE_OFFSET 13
#define RX_DESC_DURATION_OFFSET 14
#define RX_DESC_RX_TIME_OFFSET 16
struct tx_desc {
u32 NextDescriptor;
u16 TxStartOfFrame;
u16 TxLength;
u8 TxState;
u8 TxStatus;
u8 RetryCount;
u8 TxRate;
u8 KeyIndex;
u8 ChiperType;
u8 ChipreLength;
u8 Reserved1;
u8 Reserved;
u8 PacketType;
u16 HostTxLength;
};
#define TX_DESC_NEXT_OFFSET 0
#define TX_DESC_POS_OFFSET 4
#define TX_DESC_SIZE_OFFSET 6
#define TX_DESC_FLAGS_OFFSET 8
#define TX_DESC_STATUS_OFFSET 9
#define TX_DESC_RETRY_OFFSET 10
#define TX_DESC_RATE_OFFSET 11
#define TX_DESC_KEY_INDEX_OFFSET 12
#define TX_DESC_CIPHER_TYPE_OFFSET 13
#define TX_DESC_CIPHER_LENGTH_OFFSET 14
#define TX_DESC_PACKET_TYPE_OFFSET 17
#define TX_DESC_HOST_LENGTH_OFFSET 18
/*
* Host-MAC interface
*/
#define TX_STATUS_SUCCESS 0x00
#define TX_FIRM_OWN 0x80
#define TX_DONE 0x40
#define TX_ERROR 0x01
#define TX_PACKET_TYPE_DATA 0x01
#define TX_PACKET_TYPE_MGMT 0x02
#define ISR_EMPTY 0x00 /* no bits set in ISR */
#define ISR_TxCOMPLETE 0x01 /* packet transmitted */
#define ISR_RxCOMPLETE 0x02 /* packet received */
#define ISR_RxFRAMELOST 0x04 /* Rx Frame lost */
#define ISR_FATAL_ERROR 0x08 /* Fatal error */
#define ISR_COMMAND_COMPLETE 0x10 /* command completed */
#define ISR_OUT_OF_RANGE 0x20 /* command completed */
#define ISR_IBSS_MERGE 0x40 /* (4.1.2.30): IBSS merge */
#define ISR_GENERIC_IRQ 0x80
#define Local_Mib_Type 0x01
#define Mac_Address_Mib_Type 0x02
#define Mac_Mib_Type 0x03
#define Statistics_Mib_Type 0x04
#define Mac_Mgmt_Mib_Type 0x05
#define Mac_Wep_Mib_Type 0x06
#define Phy_Mib_Type 0x07
#define Multi_Domain_MIB 0x08
#define MAC_MGMT_MIB_CUR_BSSID_POS 14
#define MAC_MIB_FRAG_THRESHOLD_POS 8
#define MAC_MIB_RTS_THRESHOLD_POS 10
#define MAC_MIB_SHORT_RETRY_POS 16
#define MAC_MIB_LONG_RETRY_POS 17
#define MAC_MIB_SHORT_RETRY_LIMIT_POS 16
#define MAC_MGMT_MIB_BEACON_PER_POS 0
#define MAC_MGMT_MIB_STATION_ID_POS 6
#define MAC_MGMT_MIB_CUR_PRIVACY_POS 11
#define MAC_MGMT_MIB_CUR_BSSID_POS 14
#define MAC_MGMT_MIB_PS_MODE_POS 53
#define MAC_MGMT_MIB_LISTEN_INTERVAL_POS 54
#define MAC_MGMT_MIB_MULTI_DOMAIN_IMPLEMENTED 56
#define MAC_MGMT_MIB_MULTI_DOMAIN_ENABLED 57
#define PHY_MIB_CHANNEL_POS 14
#define PHY_MIB_RATE_SET_POS 20
#define PHY_MIB_REG_DOMAIN_POS 26
#define LOCAL_MIB_AUTO_TX_RATE_POS 3
#define LOCAL_MIB_SSID_SIZE 5
#define LOCAL_MIB_TX_PROMISCUOUS_POS 6
#define LOCAL_MIB_TX_MGMT_RATE_POS 7
#define LOCAL_MIB_TX_CONTROL_RATE_POS 8
#define LOCAL_MIB_PREAMBLE_TYPE 9
#define MAC_ADDR_MIB_MAC_ADDR_POS 0
#define CMD_Set_MIB_Vars 0x01
#define CMD_Get_MIB_Vars 0x02
#define CMD_Scan 0x03
#define CMD_Join 0x04
#define CMD_Start 0x05
#define CMD_EnableRadio 0x06
#define CMD_DisableRadio 0x07
#define CMD_SiteSurvey 0x0B
#define CMD_STATUS_IDLE 0x00
#define CMD_STATUS_COMPLETE 0x01
#define CMD_STATUS_UNKNOWN 0x02
#define CMD_STATUS_INVALID_PARAMETER 0x03
#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04
#define CMD_STATUS_TIME_OUT 0x07
#define CMD_STATUS_IN_PROGRESS 0x08
#define CMD_STATUS_REJECTED_RADIO_OFF 0x09
#define CMD_STATUS_HOST_ERROR 0xFF
#define CMD_STATUS_BUSY 0xFE
#define CMD_BLOCK_COMMAND_OFFSET 0
#define CMD_BLOCK_STATUS_OFFSET 1
#define CMD_BLOCK_PARAMETERS_OFFSET 4
#define SCAN_OPTIONS_SITE_SURVEY 0x80
#define MGMT_FRAME_BODY_OFFSET 24
#define MAX_AUTHENTICATION_RETRIES 3
#define MAX_ASSOCIATION_RETRIES 3
#define AUTHENTICATION_RESPONSE_TIME_OUT 1000
#define MAX_WIRELESS_BODY 2316 /* mtu is 2312, CRC is 4 */
#define LOOP_RETRY_LIMIT 500000
#define ACTIVE_MODE 1
#define PS_MODE 2
#define MAX_ENCRYPTION_KEYS 4
#define MAX_ENCRYPTION_KEY_SIZE 40
/*
* 802.11 related definitions
*/
/*
* Regulatory Domains
*/
#define REG_DOMAIN_FCC 0x10 /* Channels 1-11 USA */
#define REG_DOMAIN_DOC 0x20 /* Channel 1-11 Canada */
#define REG_DOMAIN_ETSI 0x30 /* Channel 1-13 Europe (ex Spain/France) */
#define REG_DOMAIN_SPAIN 0x31 /* Channel 10-11 Spain */
#define REG_DOMAIN_FRANCE 0x32 /* Channel 10-13 France */
#define REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */
#define REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan(MKK1) */
#define REG_DOMAIN_ISRAEL 0x50 /* Channel 3-9 ISRAEL */
#define BSS_TYPE_AD_HOC 1
#define BSS_TYPE_INFRASTRUCTURE 2
#define SCAN_TYPE_ACTIVE 0
#define SCAN_TYPE_PASSIVE 1
#define LONG_PREAMBLE 0
#define SHORT_PREAMBLE 1
#define AUTO_PREAMBLE 2
#define DATA_FRAME_WS_HEADER_SIZE 30
/* promiscuous mode control */
#define PROM_MODE_OFF 0x0
#define PROM_MODE_UNKNOWN 0x1
#define PROM_MODE_CRC_FAILED 0x2
#define PROM_MODE_DUPLICATED 0x4
#define PROM_MODE_MGMT 0x8
#define PROM_MODE_CTRL 0x10
#define PROM_MODE_BAD_PROTOCOL 0x20
#define IFACE_INT_STATUS_OFFSET 0
#define IFACE_INT_MASK_OFFSET 1
#define IFACE_LOCKOUT_HOST_OFFSET 2
#define IFACE_LOCKOUT_MAC_OFFSET 3
#define IFACE_FUNC_CTRL_OFFSET 28
#define IFACE_MAC_STAT_OFFSET 30
#define IFACE_GENERIC_INT_TYPE_OFFSET 32
#define CIPHER_SUITE_NONE 0
#define CIPHER_SUITE_WEP_64 1
#define CIPHER_SUITE_TKIP 2
#define CIPHER_SUITE_AES 3
#define CIPHER_SUITE_CCX 4
#define CIPHER_SUITE_WEP_128 5
/*
* IFACE MACROS & definitions
*/
/*
* FuncCtrl field:
*/
#define FUNC_CTRL_TxENABLE 0x10
#define FUNC_CTRL_RxENABLE 0x20
#define FUNC_CTRL_INIT_COMPLETE 0x01
/* A stub firmware image which reads the MAC address from NVRAM on the card.
For copyright information and source see the end of this file. */
static u8 mac_reader[] = {
0x06, 0x00, 0x00, 0xea, 0x04, 0x00, 0x00, 0xea, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xea,
0x01, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea,
0xd3, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x21, 0xe1, 0x0e, 0x04, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
0x81, 0x11, 0xa0, 0xe1, 0x00, 0x10, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x1c, 0x10, 0x90, 0xe5,
0x10, 0x10, 0xc1, 0xe3, 0x1c, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 0x08, 0x10, 0x80, 0xe5,
0x02, 0x03, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0xb0, 0x10, 0xc0, 0xe1, 0xb4, 0x10, 0xc0, 0xe1,
0xb8, 0x10, 0xc0, 0xe1, 0xbc, 0x10, 0xc0, 0xe1, 0x56, 0xdc, 0xa0, 0xe3, 0x21, 0x00, 0x00, 0xeb,
0x0a, 0x00, 0xa0, 0xe3, 0x1a, 0x00, 0x00, 0xeb, 0x10, 0x00, 0x00, 0xeb, 0x07, 0x00, 0x00, 0xeb,
0x02, 0x03, 0xa0, 0xe3, 0x02, 0x14, 0xa0, 0xe3, 0xb4, 0x10, 0xc0, 0xe1, 0x4c, 0x10, 0x9f, 0xe5,
0xbc, 0x10, 0xc0, 0xe1, 0x10, 0x10, 0xa0, 0xe3, 0xb8, 0x10, 0xc0, 0xe1, 0xfe, 0xff, 0xff, 0xea,
0x00, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe3, 0x02, 0x3c, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
0x28, 0x00, 0x9f, 0xe5, 0x37, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
0x00, 0x40, 0x2d, 0xe9, 0x12, 0x2e, 0xa0, 0xe3, 0x06, 0x30, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
0x02, 0x04, 0xa0, 0xe3, 0x2f, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
0x00, 0x02, 0x00, 0x02, 0x80, 0x01, 0x90, 0xe0, 0x01, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x50, 0xe2,
0xfc, 0xff, 0xff, 0xea, 0x1e, 0xff, 0x2f, 0xe1, 0x80, 0x10, 0xa0, 0xe3, 0xf3, 0x06, 0xa0, 0xe3,
0x00, 0x10, 0x80, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3,
0x04, 0x10, 0x80, 0xe5, 0x00, 0x10, 0x80, 0xe5, 0x0e, 0x34, 0xa0, 0xe3, 0x1c, 0x10, 0x93, 0xe5,
0x02, 0x1a, 0x81, 0xe3, 0x1c, 0x10, 0x83, 0xe5, 0x58, 0x11, 0x9f, 0xe5, 0x30, 0x10, 0x80, 0xe5,
0x54, 0x11, 0x9f, 0xe5, 0x34, 0x10, 0x80, 0xe5, 0x38, 0x10, 0x80, 0xe5, 0x3c, 0x10, 0x80, 0xe5,
0x10, 0x10, 0x90, 0xe5, 0x08, 0x00, 0x90, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0xf3, 0x16, 0xa0, 0xe3,
0x08, 0x00, 0x91, 0xe5, 0x05, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 0x10, 0x00, 0x91, 0xe5,
0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0xff, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5,
0x10, 0x00, 0x91, 0xe5, 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5,
0x10, 0x00, 0x91, 0xe5, 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5,
0xff, 0x00, 0x00, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x30, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe1,
0x03, 0x40, 0xa0, 0xe1, 0xa2, 0x02, 0xa0, 0xe1, 0x08, 0x00, 0x00, 0xe2, 0x03, 0x00, 0x80, 0xe2,
0xd8, 0x10, 0x9f, 0xe5, 0x00, 0x00, 0xc1, 0xe5, 0x01, 0x20, 0xc1, 0xe5, 0xe2, 0xff, 0xff, 0xeb,
0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x14, 0x00, 0xa0, 0xe3, 0xc4, 0xff, 0xff, 0xeb,
0x04, 0x20, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1, 0x02, 0x00, 0xa0, 0xe3, 0x01, 0x00, 0x00, 0xeb,
0x30, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x70, 0x40, 0x2d, 0xe9, 0xf3, 0x46, 0xa0, 0xe3,
0x00, 0x30, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x08, 0x00, 0x00, 0x9a, 0x8c, 0x50, 0x9f, 0xe5,
0x03, 0x60, 0xd5, 0xe7, 0x0c, 0x60, 0x84, 0xe5, 0x10, 0x60, 0x94, 0xe5, 0x02, 0x00, 0x16, 0xe3,
0xfc, 0xff, 0xff, 0x0a, 0x01, 0x30, 0x83, 0xe2, 0x00, 0x00, 0x53, 0xe1, 0xf7, 0xff, 0xff, 0x3a,
0xff, 0x30, 0xa0, 0xe3, 0x0c, 0x30, 0x84, 0xe5, 0x08, 0x00, 0x94, 0xe5, 0x10, 0x00, 0x94, 0xe5,
0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x94, 0xe5, 0x00, 0x00, 0xa0, 0xe3,
0x00, 0x00, 0x52, 0xe3, 0x0b, 0x00, 0x00, 0x9a, 0x10, 0x50, 0x94, 0xe5, 0x02, 0x00, 0x15, 0xe3,
0xfc, 0xff, 0xff, 0x0a, 0x0c, 0x30, 0x84, 0xe5, 0x10, 0x50, 0x94, 0xe5, 0x01, 0x00, 0x15, 0xe3,
0xfc, 0xff, 0xff, 0x0a, 0x08, 0x50, 0x94, 0xe5, 0x01, 0x50, 0xc1, 0xe4, 0x01, 0x00, 0x80, 0xe2,
0x02, 0x00, 0x50, 0xe1, 0xf3, 0xff, 0xff, 0x3a, 0xc8, 0x00, 0xa0, 0xe3, 0x98, 0xff, 0xff, 0xeb,
0x70, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x0c, 0x00, 0x02, 0x01, 0x02, 0x00, 0x02,
0x00, 0x01, 0x00, 0x02
};
struct atmel_private {
void *card; /* Bus dependent structure varies for PCcard */
int (*present_callback)(void *); /* And callback which uses it */
char firmware_id[32];
AtmelFWType firmware_type;
u8 *firmware;
int firmware_length;
struct timer_list management_timer;
struct net_device *dev;
struct device *sys_dev;
struct iw_statistics wstats;
spinlock_t irqlock, timerlock; /* spinlocks */
enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type;
enum {
CARD_TYPE_PARALLEL_FLASH,
CARD_TYPE_SPI_FLASH,
CARD_TYPE_EEPROM
} card_type;
int do_rx_crc; /* If we need to CRC incoming packets */
int probe_crc; /* set if we don't yet know */
int crc_ok_cnt, crc_ko_cnt; /* counters for probing */
u16 rx_desc_head;
u16 tx_desc_free, tx_desc_head, tx_desc_tail, tx_desc_previous;
u16 tx_free_mem, tx_buff_head, tx_buff_tail;
u16 frag_seq, frag_len, frag_no;
u8 frag_source[6];
u8 wep_is_on, default_key, exclude_unencrypted, encryption_level;
u8 group_cipher_suite, pairwise_cipher_suite;
u8 wep_keys[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE];
int wep_key_len[MAX_ENCRYPTION_KEYS];
int use_wpa, radio_on_broken; /* firmware dependent stuff. */
u16 host_info_base;
struct host_info_struct {
/* NB this is matched to the hardware, don't change. */
u8 volatile int_status;
u8 volatile int_mask;
u8 volatile lockout_host;
u8 volatile lockout_mac;
u16 tx_buff_pos;
u16 tx_buff_size;
u16 tx_desc_pos;
u16 tx_desc_count;
u16 rx_buff_pos;
u16 rx_buff_size;
u16 rx_desc_pos;
u16 rx_desc_count;
u16 build_version;
u16 command_pos;
u16 major_version;
u16 minor_version;
u16 func_ctrl;
u16 mac_status;
u16 generic_IRQ_type;
u8 reserved[2];
} host_info;
enum {
STATION_STATE_SCANNING,
STATION_STATE_JOINNING,
STATION_STATE_AUTHENTICATING,
STATION_STATE_ASSOCIATING,
STATION_STATE_READY,
STATION_STATE_REASSOCIATING,
STATION_STATE_DOWN,
STATION_STATE_MGMT_ERROR
} station_state;
int operating_mode, power_mode;
time_t last_qual;
int beacons_this_sec;
int channel;
int reg_domain, config_reg_domain;
int tx_rate;
int auto_tx_rate;
int rts_threshold;
int frag_threshold;
int long_retry, short_retry;
int preamble;
int default_beacon_period, beacon_period, listen_interval;
int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum;
int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt;
enum {
SITE_SURVEY_IDLE,
SITE_SURVEY_IN_PROGRESS,
SITE_SURVEY_COMPLETED
} site_survey_state;
unsigned long last_survey;
int station_was_associated, station_is_associated;
int fast_scan;
struct bss_info {
int channel;
int SSIDsize;
int RSSI;
int UsingWEP;
int preamble;
int beacon_period;
int BSStype;
u8 BSSID[6];
u8 SSID[MAX_SSID_LENGTH];
} BSSinfo[MAX_BSS_ENTRIES];
int BSS_list_entries, current_BSS;
int connect_to_any_BSS;
int SSID_size, new_SSID_size;
u8 CurrentBSSID[6], BSSID[6];
u8 SSID[MAX_SSID_LENGTH], new_SSID[MAX_SSID_LENGTH];
u64 last_beacon_timestamp;
u8 rx_buf[MAX_WIRELESS_BODY];
};
static u8 atmel_basic_rates[4] = {0x82, 0x84, 0x0b, 0x16};
static const struct {
int reg_domain;
int min, max;
char *name;
} channel_table[] = { { REG_DOMAIN_FCC, 1, 11, "USA" },
{ REG_DOMAIN_DOC, 1, 11, "Canada" },
{ REG_DOMAIN_ETSI, 1, 13, "Europe" },
{ REG_DOMAIN_SPAIN, 10, 11, "Spain" },
{ REG_DOMAIN_FRANCE, 10, 13, "France" },
{ REG_DOMAIN_MKK, 14, 14, "MKK" },
{ REG_DOMAIN_MKK1, 1, 14, "MKK1" },
{ REG_DOMAIN_ISRAEL, 3, 9, "Israel"} };
static void build_wpa_mib(struct atmel_private *priv);
static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static void atmel_copy_to_card(struct net_device *dev, u16 dest,
const unsigned char *src, u16 len);
static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest,
u16 src, u16 len);
static void atmel_set_gcr(struct net_device *dev, u16 mask);
static void atmel_clear_gcr(struct net_device *dev, u16 mask);
static int atmel_lock_mac(struct atmel_private *priv);
static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data);
static void atmel_command_irq(struct atmel_private *priv);
static int atmel_validate_channel(struct atmel_private *priv, int channel);
static void atmel_management_frame(struct atmel_private *priv,
struct ieee80211_hdr *header,
u16 frame_len, u8 rssi);
static void atmel_management_timer(u_long a);
static void atmel_send_command(struct atmel_private *priv, int command,
void *cmd, int cmd_size);
static int atmel_send_command_wait(struct atmel_private *priv, int command,
void *cmd, int cmd_size);
static void atmel_transmit_management_frame(struct atmel_private *priv,
struct ieee80211_hdr *header,
u8 *body, int body_len);
static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index);
static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index,
u8 data);
static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index,
u16 data);
static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index,
u8 *data, int data_len);
static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index,
u8 *data, int data_len);
static void atmel_scan(struct atmel_private *priv, int specific_ssid);
static void atmel_join_bss(struct atmel_private *priv, int bss_index);
static void atmel_smooth_qual(struct atmel_private *priv);
static void atmel_writeAR(struct net_device *dev, u16 data);
static int probe_atmel_card(struct net_device *dev);
static int reset_atmel_card(struct net_device *dev);
static void atmel_enter_state(struct atmel_private *priv, int new_state);
int atmel_open (struct net_device *dev);
static inline u16 atmel_hi(struct atmel_private *priv, u16 offset)
{
return priv->host_info_base + offset;
}
static inline u16 atmel_co(struct atmel_private *priv, u16 offset)
{
return priv->host_info.command_pos + offset;
}
static inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc)
{
return priv->host_info.rx_desc_pos + (sizeof(struct rx_desc) * desc) + offset;
}
static inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc)
{
return priv->host_info.tx_desc_pos + (sizeof(struct tx_desc) * desc) + offset;
}
static inline u8 atmel_read8(struct net_device *dev, u16 offset)
{
return inb(dev->base_addr + offset);
}
static inline void atmel_write8(struct net_device *dev, u16 offset, u8 data)
{
outb(data, dev->base_addr + offset);
}
static inline u16 atmel_read16(struct net_device *dev, u16 offset)
{
return inw(dev->base_addr + offset);
}
static inline void atmel_write16(struct net_device *dev, u16 offset, u16 data)
{
outw(data, dev->base_addr + offset);
}
static inline u8 atmel_rmem8(struct atmel_private *priv, u16 pos)
{
atmel_writeAR(priv->dev, pos);
return atmel_read8(priv->dev, DR);
}
static inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data)
{
atmel_writeAR(priv->dev, pos);
atmel_write8(priv->dev, DR, data);
}
static inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos)
{
atmel_writeAR(priv->dev, pos);
return atmel_read16(priv->dev, DR);
}
static inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data)
{
atmel_writeAR(priv->dev, pos);
atmel_write16(priv->dev, DR, data);
}
static const struct iw_handler_def atmel_handler_def;
static void tx_done_irq(struct atmel_private *priv)
{
int i;
for (i = 0;
atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE &&
i < priv->host_info.tx_desc_count;
i++) {
u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head));
u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head));
u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head));
atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head), 0);
priv->tx_free_mem += msdu_size;
priv->tx_desc_free++;
if (priv->tx_buff_head + msdu_size > (priv->host_info.tx_buff_pos + priv->host_info.tx_buff_size))
priv->tx_buff_head = 0;
else
priv->tx_buff_head += msdu_size;
if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1))
priv->tx_desc_head++ ;
else
priv->tx_desc_head = 0;
if (type == TX_PACKET_TYPE_DATA) {
if (status == TX_STATUS_SUCCESS)
priv->dev->stats.tx_packets++;
else
priv->dev->stats.tx_errors++;
netif_wake_queue(priv->dev);
}
}
}
static u16 find_tx_buff(struct atmel_private *priv, u16 len)
{
u16 bottom_free = priv->host_info.tx_buff_size - priv->tx_buff_tail;
if (priv->tx_desc_free == 3 || priv->tx_free_mem < len)
return 0;
if (bottom_free >= len)
return priv->host_info.tx_buff_pos + priv->tx_buff_tail;
if (priv->tx_free_mem - bottom_free >= len) {
priv->tx_buff_tail = 0;
return priv->host_info.tx_buff_pos;
}
return 0;
}
static void tx_update_descriptor(struct atmel_private *priv, int is_bcast,
u16 len, u16 buff, u8 type)
{
atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, priv->tx_desc_tail), buff);
atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_tail), len);
if (!priv->use_wpa)
atmel_wmem16(priv, atmel_tx(priv, TX_DESC_HOST_LENGTH_OFFSET, priv->tx_desc_tail), len);
atmel_wmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_tail), type);
atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RATE_OFFSET, priv->tx_desc_tail), priv->tx_rate);
atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RETRY_OFFSET, priv->tx_desc_tail), 0);
if (priv->use_wpa) {
int cipher_type, cipher_length;
if (is_bcast) {
cipher_type = priv->group_cipher_suite;
if (cipher_type == CIPHER_SUITE_WEP_64 ||
cipher_type == CIPHER_SUITE_WEP_128)
cipher_length = 8;
else if (cipher_type == CIPHER_SUITE_TKIP)
cipher_length = 12;
else if (priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_64 ||
priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_128) {
cipher_type = priv->pairwise_cipher_suite;
cipher_length = 8;
} else {
cipher_type = CIPHER_SUITE_NONE;
cipher_length = 0;
}
} else {
cipher_type = priv->pairwise_cipher_suite;
if (cipher_type == CIPHER_SUITE_WEP_64 ||
cipher_type == CIPHER_SUITE_WEP_128)
cipher_length = 8;
else if (cipher_type == CIPHER_SUITE_TKIP)
cipher_length = 12;
else if (priv->group_cipher_suite == CIPHER_SUITE_WEP_64 ||
priv->group_cipher_suite == CIPHER_SUITE_WEP_128) {
cipher_type = priv->group_cipher_suite;
cipher_length = 8;
} else {
cipher_type = CIPHER_SUITE_NONE;
cipher_length = 0;
}
}
atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail),
cipher_type);
atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_LENGTH_OFFSET, priv->tx_desc_tail),
cipher_length);
}
atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_tail), 0x80000000L);
atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_tail), TX_FIRM_OWN);
if (priv->tx_desc_previous != priv->tx_desc_tail)
atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_previous), 0);
priv->tx_desc_previous = priv->tx_desc_tail;
if (priv->tx_desc_tail < (priv->host_info.tx_desc_count - 1))
priv->tx_desc_tail++;
else
priv->tx_desc_tail = 0;
priv->tx_desc_free--;
priv->tx_free_mem -= len;
}
static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
{
static const u8 SNAP_RFC1024[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
struct atmel_private *priv = netdev_priv(dev);
struct ieee80211_hdr header;
unsigned long flags;
u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
if (priv->card && priv->present_callback &&
!(*priv->present_callback)(priv->card)) {
dev->stats.tx_errors++;
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
if (priv->station_state != STATION_STATE_READY) {
dev->stats.tx_errors++;
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
/* first ensure the timer func cannot run */
spin_lock_bh(&priv->timerlock);
/* then stop the hardware ISR */
spin_lock_irqsave(&priv->irqlock, flags);
/* nb doing the above in the opposite order will deadlock */
/* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the
12 first bytes (containing DA/SA) and put them in the appropriate
fields of the Wireless Header. Thus the packet length is then the
initial + 18 (+30-12) */
if (!(buff = find_tx_buff(priv, len + 18))) {
dev->stats.tx_dropped++;
spin_unlock_irqrestore(&priv->irqlock, flags);
spin_unlock_bh(&priv->timerlock);
netif_stop_queue(dev);
return NETDEV_TX_BUSY;
}
frame_ctl = IEEE80211_FTYPE_DATA;
header.duration_id = 0;
header.seq_ctrl = 0;
if (priv->wep_is_on)
frame_ctl |= IEEE80211_FCTL_PROTECTED;
if (priv->operating_mode == IW_MODE_ADHOC) {
skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN);
memcpy(&header.addr2, dev->dev_addr, ETH_ALEN);
memcpy(&header.addr3, priv->BSSID, ETH_ALEN);
} else {
frame_ctl |= IEEE80211_FCTL_TODS;
memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN);
memcpy(&header.addr2, dev->dev_addr, ETH_ALEN);
skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN);
}
if (priv->use_wpa)
memcpy(&header.addr4, SNAP_RFC1024, ETH_ALEN);
header.frame_control = cpu_to_le16(frame_ctl);
/* Copy the wireless header into the card */
atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE);
/* Copy the packet sans its 802.3 header addresses which have been replaced */
atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12);
priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE;
/* low bit of first byte of destination tells us if broadcast */
tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA);
dev->stats.tx_bytes += len;
spin_unlock_irqrestore(&priv->irqlock, flags);
spin_unlock_bh(&priv->timerlock);
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
static void atmel_transmit_management_frame(struct atmel_private *priv,
struct ieee80211_hdr *header,
u8 *body, int body_len)
{
u16 buff;
int len = MGMT_FRAME_BODY_OFFSET + body_len;
if (!(buff = find_tx_buff(priv, len)))
return;
atmel_copy_to_card(priv->dev, buff, (u8 *)header, MGMT_FRAME_BODY_OFFSET);
atmel_copy_to_card(priv->dev, buff + MGMT_FRAME_BODY_OFFSET, body, body_len);
priv->tx_buff_tail += len;
tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT);
}
static void fast_rx_path(struct atmel_private *priv,
struct ieee80211_hdr *header,
u16 msdu_size, u16 rx_packet_loc, u32 crc)
{
/* fast path: unfragmented packet copy directly into skbuf */
u8 mac4[6];
struct sk_buff *skb;
unsigned char *skbp;
/* get the final, mac 4 header field, this tells us encapsulation */
atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6);
msdu_size -= 6;
if (priv->do_rx_crc) {
crc = crc32_le(crc, mac4, 6);
msdu_size -= 4;
}
if (!(skb = dev_alloc_skb(msdu_size + 14))) {
priv->dev->stats.rx_dropped++;
return;
}
skb_reserve(skb, 2);
skbp = skb_put(skb, msdu_size + 12);
atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size);
if (priv->do_rx_crc) {
u32 netcrc;
crc = crc32_le(crc, skbp + 12, msdu_size);
atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + 30 + msdu_size, 4);
if ((crc ^ 0xffffffff) != netcrc) {
priv->dev->stats.rx_crc_errors++;
dev_kfree_skb(skb);
return;
}
}
memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */
if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN);
else
memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */
skb->protocol = eth_type_trans(skb, priv->dev);
skb->ip_summed = CHECKSUM_NONE;
netif_rx(skb);
priv->dev->stats.rx_bytes += 12 + msdu_size;
priv->dev->stats.rx_packets++;
}
/* Test to see if the packet in card memory at packet_loc has a valid CRC
It doesn't matter that this is slow: it is only used to proble the first few
packets. */
static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size)
{
int i = msdu_size - 4;
u32 netcrc, crc = 0xffffffff;
if (msdu_size < 4)
return 0;
atmel_copy_to_host(priv->dev, (void *)&netcrc, packet_loc + i, 4);
atmel_writeAR(priv->dev, packet_loc);
while (i--) {
u8 octet = atmel_read8(priv->dev, DR);
crc = crc32_le(crc, &octet, 1);
}
return (crc ^ 0xffffffff) == netcrc;
}
static void frag_rx_path(struct atmel_private *priv,
struct ieee80211_hdr *header,
u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no,
u8 frag_no, int more_frags)
{
u8 mac4[ETH_ALEN];
u8 source[ETH_ALEN];
struct sk_buff *skb;
if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
memcpy(source, header->addr3, ETH_ALEN);
else
memcpy(source, header->addr2, ETH_ALEN);
rx_packet_loc += 24; /* skip header */
if (priv->do_rx_crc)
msdu_size -= 4;
if (frag_no == 0) { /* first fragment */
atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN);
msdu_size -= ETH_ALEN;
rx_packet_loc += ETH_ALEN;
if (priv->do_rx_crc)
crc = crc32_le(crc, mac4, 6);
priv->frag_seq = seq_no;
priv->frag_no = 1;
priv->frag_len = msdu_size;
memcpy(priv->frag_source, source, ETH_ALEN);
memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN);
memcpy(priv->rx_buf, header->addr1, ETH_ALEN);
atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size);
if (priv->do_rx_crc) {
u32 netcrc;
crc = crc32_le(crc, &priv->rx_buf[12], msdu_size);
atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4);
if ((crc ^ 0xffffffff) != netcrc) {
priv->dev->stats.rx_crc_errors++;
eth_broadcast_addr(priv->frag_source);
}
}
} else if (priv->frag_no == frag_no &&
priv->frag_seq == seq_no &&
memcmp(priv->frag_source, source, ETH_ALEN) == 0) {
atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len],
rx_packet_loc, msdu_size);
if (priv->do_rx_crc) {
u32 netcrc;
crc = crc32_le(crc,
&priv->rx_buf[12 + priv->frag_len],
msdu_size);
atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4);
if ((crc ^ 0xffffffff) != netcrc) {
priv->dev->stats.rx_crc_errors++;
eth_broadcast_addr(priv->frag_source);
more_frags = 1; /* don't send broken assembly */
}
}
priv->frag_len += msdu_size;
priv->frag_no++;
if (!more_frags) { /* last one */
eth_broadcast_addr(priv->frag_source);
if (!(skb = dev_alloc_skb(priv->frag_len + 14))) {
priv->dev->stats.rx_dropped++;
} else {
skb_reserve(skb, 2);
memcpy(skb_put(skb, priv->frag_len + 12),
priv->rx_buf,
priv->frag_len + 12);
skb->protocol = eth_type_trans(skb, priv->dev);
skb->ip_summed = CHECKSUM_NONE;
netif_rx(skb);
priv->dev->stats.rx_bytes += priv->frag_len + 12;
priv->dev->stats.rx_packets++;
}
}
} else
priv->wstats.discard.fragment++;
}
static void rx_done_irq(struct atmel_private *priv)
{
int i;
struct ieee80211_hdr header;
for (i = 0;
atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID &&
i < priv->host_info.rx_desc_count;
i++) {
u16 msdu_size, rx_packet_loc, frame_ctl, seq_control;
u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head));
u32 crc = 0xffffffff;
if (status != RX_STATUS_SUCCESS) {
if (status == 0xc1) /* determined by experiment */
priv->wstats.discard.nwid++;
else
priv->dev->stats.rx_errors++;
goto next;
}
msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head));
rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head));
if (msdu_size < 30) {
priv->dev->stats.rx_errors++;
goto next;
}
/* Get header as far as end of seq_ctrl */
atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24);
frame_ctl = le16_to_cpu(header.frame_control);
seq_control = le16_to_cpu(header.seq_ctrl);
/* probe for CRC use here if needed once five packets have
arrived with the same crc status, we assume we know what's
happening and stop probing */
if (priv->probe_crc) {
if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) {
priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size);
} else {
priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24);
}
if (priv->do_rx_crc) {
if (priv->crc_ok_cnt++ > 5)
priv->probe_crc = 0;
} else {
if (priv->crc_ko_cnt++ > 5)
priv->probe_crc = 0;
}
}
/* don't CRC header when WEP in use */
if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) {
crc = crc32_le(0xffffffff, (unsigned char *)&header, 24);
}
msdu_size -= 24; /* header */
if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS;
u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG;
u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4;
if (!more_fragments && packet_fragment_no == 0) {
fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc);
} else {
frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc,
packet_sequence_no, packet_fragment_no, more_fragments);
}
}
if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
/* copy rest of packet into buffer */
atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size);
/* we use the same buffer for frag reassembly and control packets */
eth_broadcast_addr(priv->frag_source);
if (priv->do_rx_crc) {
/* last 4 octets is crc */
msdu_size -= 4;
crc = crc32_le(crc, (unsigned char *)&priv->rx_buf, msdu_size);
if ((crc ^ 0xffffffff) != (*((u32 *)&priv->rx_buf[msdu_size]))) {
priv->dev->stats.rx_crc_errors++;
goto next;
}
}
atmel_management_frame(priv, &header, msdu_size,
atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head)));
}
next:
/* release descriptor */
atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED);
if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1))
priv->rx_desc_head++;
else
priv->rx_desc_head = 0;
}
}
static irqreturn_t service_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *) dev_id;
struct atmel_private *priv = netdev_priv(dev);
u8 isr;
int i = -1;
static const u8 irq_order[] = {
ISR_OUT_OF_RANGE,
ISR_RxCOMPLETE,
ISR_TxCOMPLETE,
ISR_RxFRAMELOST,
ISR_FATAL_ERROR,
ISR_COMMAND_COMPLETE,
ISR_IBSS_MERGE,
ISR_GENERIC_IRQ
};
if (priv->card && priv->present_callback &&
!(*priv->present_callback)(priv->card))
return IRQ_HANDLED;
/* In this state upper-level code assumes it can mess with
the card unhampered by interrupts which may change register state.
Note that even though the card shouldn't generate interrupts
the inturrupt line may be shared. This allows card setup
to go on without disabling interrupts for a long time. */
if (priv->station_state == STATION_STATE_DOWN)
return IRQ_NONE;
atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */
while (1) {
if (!atmel_lock_mac(priv)) {
/* failed to contact card */
printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name);
return IRQ_HANDLED;
}
isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET));
atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);
if (!isr) {
atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */
return i == -1 ? IRQ_NONE : IRQ_HANDLED;
}
atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */
for (i = 0; i < ARRAY_SIZE(irq_order); i++)
if (isr & irq_order[i])
break;
if (!atmel_lock_mac(priv)) {
/* failed to contact card */
printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name);
return IRQ_HANDLED;
}
isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET));
isr ^= irq_order[i];
atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr);
atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);
switch (irq_order[i]) {
case ISR_OUT_OF_RANGE:
if (priv->operating_mode == IW_MODE_INFRA &&
priv->station_state == STATION_STATE_READY) {
priv->station_is_associated = 0;
atmel_scan(priv, 1);
}
break;
case ISR_RxFRAMELOST:
priv->wstats.discard.misc++;
/* fall through */
case ISR_RxCOMPLETE:
rx_done_irq(priv);
break;
case ISR_TxCOMPLETE:
tx_done_irq(priv);
break;
case ISR_FATAL_ERROR:
printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name);
atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
break;
case ISR_COMMAND_COMPLETE:
atmel_command_irq(priv);
break;
case ISR_IBSS_MERGE:
atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS,
priv->CurrentBSSID, 6);
/* The WPA stuff cares about the current AP address */
if (priv->use_wpa)
build_wpa_mib(priv);
break;
case ISR_GENERIC_IRQ:
printk(KERN_INFO "%s: Generic_irq received.\n", dev->name);
break;
}
}
}
static struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev)
{
struct atmel_private *priv = netdev_priv(dev);
/* update the link quality here in case we are seeing no beacons
at all to drive the process */
atmel_smooth_qual(priv);
priv->wstats.status = priv->station_state;
if (priv->operating_mode == IW_MODE_INFRA) {
if (priv->station_state != STATION_STATE_READY) {
priv->wstats.qual.qual = 0;
priv->wstats.qual.level = 0;
priv->wstats.qual.updated = (IW_QUAL_QUAL_INVALID
| IW_QUAL_LEVEL_INVALID);
}
priv->wstats.qual.noise = 0;
priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID;
} else {
/* Quality levels cannot be determined in ad-hoc mode,
because we can 'hear' more that one remote station. */
priv->wstats.qual.qual = 0;
priv->wstats.qual.level = 0;
priv->wstats.qual.noise = 0;
priv->wstats.qual.updated = IW_QUAL_QUAL_INVALID
| IW_QUAL_LEVEL_INVALID
| IW_QUAL_NOISE_INVALID;
priv->wstats.miss.beacon = 0;
}
return &priv->wstats;
}
static int atmel_change_mtu(struct net_device *dev, int new_mtu)
{
if ((new_mtu < 68) || (new_mtu > 2312))
return -EINVAL;
dev->mtu = new_mtu;
return 0;
}
static int atmel_set_mac_address(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
memcpy (dev->dev_addr, addr->sa_data, dev->addr_len);
return atmel_open(dev);
}
EXPORT_SYMBOL(atmel_open);
int atmel_open(struct net_device *dev)
{
struct atmel_private *priv = netdev_priv(dev);
int i, channel, err;
/* any scheduled timer is no longer needed and might screw things up.. */
del_timer_sync(&priv->management_timer);
/* Interrupts will not touch the card once in this state... */
priv->station_state = STATION_STATE_DOWN;
if (priv->new_SSID_size) {
memcpy(priv->SSID, priv->new_SSID, priv->new_SSID_size);
priv->SSID_size = priv->new_SSID_size;
priv->new_SSID_size = 0;
}
priv->BSS_list_entries = 0;
priv->AuthenticationRequestRetryCnt = 0;
priv->AssociationRequestRetryCnt = 0;
priv->ReAssociationRequestRetryCnt = 0;
priv->CurrentAuthentTransactionSeqNum = 0x0001;
priv->ExpectedAuthentTransactionSeqNum = 0x0002;
priv->site_survey_state = SITE_SURVEY_IDLE;
priv->station_is_associated = 0;
err = reset_atmel_card(dev);
if (err)
return err;
if (priv->config_reg_domain) {
priv->reg_domain = priv->config_reg_domain;
atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS, priv->reg_domain);
} else {
priv->reg_domain = atmel_get_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS);
for (i = 0; i < ARRAY_SIZE(channel_table); i++)
if (priv->reg_domain == channel_table[i].reg_domain)
break;
if (i == ARRAY_SIZE(channel_table)) {
priv->reg_domain = REG_DOMAIN_MKK1;
printk(KERN_ALERT "%s: failed to get regulatory domain: assuming MKK1.\n", dev->name);
}
}
if ((channel = atmel_validate_channel(priv, priv->channel)))
priv->channel = channel;
/* this moves station_state on.... */
atmel_scan(priv, 1);
atmel_set_gcr(priv->dev, GCR_ENINT); /* enable interrupts */
return 0;
}
static int atmel_close(struct net_device *dev)
{
struct atmel_private *priv = netdev_priv(dev);
/* Send event to userspace that we are disassociating */
if (priv->station_state == STATION_STATE_READY) {
union iwreq_data wrqu;
wrqu.data.length = 0;
wrqu.data.flags = 0;
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
eth_zero_addr(wrqu.ap_addr.sa_data);
wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
}
atmel_enter_state(priv, STATION_STATE_DOWN);
if (priv->bus_type == BUS_TYPE_PCCARD)
atmel_write16(dev, GCR, 0x0060);
atmel_write16(dev, GCR, 0x0040);
return 0;
}
static int atmel_validate_channel(struct atmel_private *priv, int channel)
{
/* check that channel is OK, if so return zero,
else return suitable default channel */
int i;
for (i = 0; i < ARRAY_SIZE(channel_table); i++)
if (priv->reg_domain == channel_table[i].reg_domain) {
if (channel >= channel_table[i].min &&
channel <= channel_table[i].max)
return 0;
else
return channel_table[i].min;
}
return 0;
}
static int atmel_proc_show(struct seq_file *m, void *v)
{
struct atmel_private *priv = m->private;
int i;
char *s, *r, *c;
seq_printf(m, "Driver version:\t\t%d.%d\n", DRIVER_MAJOR, DRIVER_MINOR);
if (priv->station_state != STATION_STATE_DOWN) {
seq_printf(m,
"Firmware version:\t%d.%d build %d\n"
"Firmware location:\t",
priv->host_info.major_version,
priv->host_info.minor_version,
priv->host_info.build_version);
if (priv->card_type != CARD_TYPE_EEPROM)
seq_puts(m, "on card\n");
else if (priv->firmware)
seq_printf(m, "%s loaded by host\n", priv->firmware_id);
else
seq_printf(m, "%s loaded by hotplug\n", priv->firmware_id);
switch (priv->card_type) {
case CARD_TYPE_PARALLEL_FLASH:
c = "Parallel flash";
break;
case CARD_TYPE_SPI_FLASH:
c = "SPI flash\n";
break;
case CARD_TYPE_EEPROM:
c = "EEPROM";
break;
default:
c = "<unknown>";
}
r = "<unknown>";
for (i = 0; i < ARRAY_SIZE(channel_table); i++)
if (priv->reg_domain == channel_table[i].reg_domain)
r = channel_table[i].name;
seq_printf(m, "MAC memory type:\t%s\n", c);
seq_printf(m, "Regulatory domain:\t%s\n", r);
seq_printf(m, "Host CRC checking:\t%s\n",
priv->do_rx_crc ? "On" : "Off");
seq_printf(m, "WPA-capable firmware:\t%s\n",
priv->use_wpa ? "Yes" : "No");
}
switch (priv->station_state) {
case STATION_STATE_SCANNING:
s = "Scanning";
break;
case STATION_STATE_JOINNING:
s = "Joining";
break;
case STATION_STATE_AUTHENTICATING:
s = "Authenticating";
break;
case STATION_STATE_ASSOCIATING:
s = "Associating";
break;
case STATION_STATE_READY:
s = "Ready";
break;
case STATION_STATE_REASSOCIATING:
s = "Reassociating";
break;
case STATION_STATE_MGMT_ERROR:
s = "Management error";
break;
case STATION_STATE_DOWN:
s = "Down";
break;
default:
s = "<unknown>";
}
seq_printf(m, "Current state:\t\t%s\n", s);
return 0;
}
static int atmel_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, atmel_proc_show, PDE_DATA(inode));
}
static const struct file_operations atmel_proc_fops = {
.open = atmel_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct net_device_ops atmel_netdev_ops = {
.ndo_open = atmel_open,
.ndo_stop = atmel_close,
.ndo_change_mtu = atmel_change_mtu,
.ndo_set_mac_address = atmel_set_mac_address,
.ndo_start_xmit = start_tx,
.ndo_do_ioctl = atmel_ioctl,
.ndo_validate_addr = eth_validate_addr,
};
struct net_device *init_atmel_card(unsigned short irq, unsigned long port,
const AtmelFWType fw_type,
struct device *sys_dev,
int (*card_present)(void *), void *card)
{
struct net_device *dev;
struct atmel_private *priv;
int rc;
/* Create the network device object. */
dev = alloc_etherdev(sizeof(*priv));
if (!dev)
return NULL;
if (dev_alloc_name(dev, dev->name) < 0) {
printk(KERN_ERR "atmel: Couldn't get name!\n");
goto err_out_free;
}
priv = netdev_priv(dev);
priv->dev = dev;
priv->sys_dev = sys_dev;
priv->present_callback = card_present;
priv->card = card;
priv->firmware = NULL;
priv->firmware_id[0] = '\0';
priv->firmware_type = fw_type;
if (firmware) /* module parameter */
strcpy(priv->firmware_id, firmware);
priv->bus_type = card_present ? BUS_TYPE_PCCARD : BUS_TYPE_PCI;
priv->station_state = STATION_STATE_DOWN;
priv->do_rx_crc = 0;
/* For PCMCIA cards, some chips need CRC, some don't
so we have to probe. */
if (priv->bus_type == BUS_TYPE_PCCARD) {
priv->probe_crc = 1;
priv->crc_ok_cnt = priv->crc_ko_cnt = 0;
} else
priv->probe_crc = 0;
priv->last_qual = jiffies;
priv->last_beacon_timestamp = 0;
memset(priv->frag_source, 0xff, sizeof(priv->frag_source));
eth_zero_addr(priv->BSSID);
priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */
priv->station_was_associated = 0;
priv->last_survey = jiffies;
priv->preamble = LONG_PREAMBLE;
priv->operating_mode = IW_MODE_INFRA;
priv->connect_to_any_BSS = 0;
priv->config_reg_domain = 0;
priv->reg_domain = 0;
priv->tx_rate = 3;
priv->auto_tx_rate = 1;
priv->channel = 4;
priv->power_mode = 0;
priv->SSID[0] = '\0';
priv->SSID_size = 0;
priv->new_SSID_size = 0;
priv->frag_threshold = 2346;
priv->rts_threshold = 2347;
priv->short_retry = 7;
priv->long_retry = 4;
priv->wep_is_on = 0;
priv->default_key = 0;
priv->encryption_level = 0;
priv->exclude_unencrypted = 0;
priv->group_cipher_suite = priv->pairwise_cipher_suite = CIPHER_SUITE_NONE;
priv->use_wpa = 0;
memset(priv->wep_keys, 0, sizeof(priv->wep_keys));
memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len));
priv->default_beacon_period = priv->beacon_period = 100;
priv->listen_interval = 1;
init_timer(&priv->management_timer);
spin_lock_init(&priv->irqlock);
spin_lock_init(&priv->timerlock);
priv->management_timer.function = atmel_management_timer;
priv->management_timer.data = (unsigned long) dev;
dev->netdev_ops = &atmel_netdev_ops;
dev->wireless_handlers = &atmel_handler_def;
dev->irq = irq;
dev->base_addr = port;
SET_NETDEV_DEV(dev, sys_dev);
if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) {
printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc);
goto err_out_free;
}
if (!request_region(dev->base_addr, 32,
priv->bus_type == BUS_TYPE_PCCARD ? "atmel_cs" : "atmel_pci")) {
goto err_out_irq;
}
if (register_netdev(dev))
goto err_out_res;
if (!probe_atmel_card(dev)) {
unregister_netdev(dev);
goto err_out_res;
}
netif_carrier_off(dev);
if (!proc_create_data("driver/atmel", 0, NULL, &atmel_proc_fops, priv))
printk(KERN_WARNING "atmel: unable to create /proc entry.\n");
printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %pM\n",
dev->name, DRIVER_MAJOR, DRIVER_MINOR, dev->dev_addr);
return dev;
err_out_res:
release_region(dev->base_addr, 32);
err_out_irq:
free_irq(dev->irq, dev);
err_out_free:
free_netdev(dev);
return NULL;
}
EXPORT_SYMBOL(init_atmel_card);
void stop_atmel_card(struct net_device *dev)
{
struct atmel_private *priv = netdev_priv(dev);
/* put a brick on it... */
if (priv->bus_type == BUS_TYPE_PCCARD)
atmel_write16(dev, GCR, 0x0060);
atmel_write16(dev, GCR, 0x0040);
del_timer_sync(&priv->management_timer);
unregister_netdev(dev);
remove_proc_entry("driver/atmel", NULL);
free_irq(dev->irq, dev);
kfree(priv->firmware);
release_region(dev->base_addr, 32);
free_netdev(dev);
}
EXPORT_SYMBOL(stop_atmel_card);
static int atmel_set_essid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
/* Check if we asked for `any' */
if (dwrq->flags == 0) {
priv->connect_to_any_BSS = 1;
} else {
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
priv->connect_to_any_BSS = 0;
/* Check the size of the string */
if (dwrq->length > MAX_SSID_LENGTH)
return -E2BIG;
if (index != 0)
return -EINVAL;
memcpy(priv->new_SSID, extra, dwrq->length);
priv->new_SSID_size = dwrq->length;
}
return -EINPROGRESS;
}
static int atmel_get_essid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
/* Get the current SSID */
if (priv->new_SSID_size != 0) {
memcpy(extra, priv->new_SSID, priv->new_SSID_size);
dwrq->length = priv->new_SSID_size;
} else {
memcpy(extra, priv->SSID, priv->SSID_size);
dwrq->length = priv->SSID_size;
}
dwrq->flags = !priv->connect_to_any_BSS; /* active */
return 0;
}
static int atmel_get_wap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *awrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN);
awrq->sa_family = ARPHRD_ETHER;
return 0;
}
static int atmel_set_encode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
/* Basic checking: do we have a key to set ?
* Note : with the new API, it's impossible to get a NULL pointer.
* Therefore, we need to check a key size == 0 instead.
* New version of iwconfig properly set the IW_ENCODE_NOKEY flag
* when no key is present (only change flags), but older versions
* don't do it. - Jean II */
if (dwrq->length > 0) {
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
int current_index = priv->default_key;
/* Check the size of the key */
if (dwrq->length > 13) {
return -EINVAL;
}
/* Check the index (none -> use current) */
if (index < 0 || index >= 4)
index = current_index;
else
priv->default_key = index;
/* Set the length */
if (dwrq->length > 5)
priv->wep_key_len[index] = 13;
else
if (dwrq->length > 0)
priv->wep_key_len[index] = 5;
else
/* Disable the key */
priv->wep_key_len[index] = 0;
/* Check if the key is not marked as invalid */
if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
/* Cleanup */
memset(priv->wep_keys[index], 0, 13);
/* Copy the key in the driver */
memcpy(priv->wep_keys[index], extra, dwrq->length);
}
/* WE specify that if a valid key is set, encryption
* should be enabled (user may turn it off later)
* This is also how "iwconfig ethX key on" works */
if (index == current_index &&
priv->wep_key_len[index] > 0) {
priv->wep_is_on = 1;
priv->exclude_unencrypted = 1;
if (priv->wep_key_len[index] > 5) {
priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128;
priv->encryption_level = 2;
} else {
priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64;
priv->encryption_level = 1;
}
}
} else {
/* Do we want to just set the transmit key index ? */
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
if (index >= 0 && index < 4) {
priv->default_key = index;
} else
/* Don't complain if only change the mode */
if (!(dwrq->flags & IW_ENCODE_MODE))
return -EINVAL;
}
/* Read the flags */
if (dwrq->flags & IW_ENCODE_DISABLED) {
priv->wep_is_on = 0;
priv->encryption_level = 0;
priv->pairwise_cipher_suite = CIPHER_SUITE_NONE;
} else {
priv->wep_is_on = 1;
if (priv->wep_key_len[priv->default_key] > 5) {
priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128;
priv->encryption_level = 2;
} else {
priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64;
priv->encryption_level = 1;
}
}
if (dwrq->flags & IW_ENCODE_RESTRICTED)
priv->exclude_unencrypted = 1;
if (dwrq->flags & IW_ENCODE_OPEN)
priv->exclude_unencrypted = 0;
return -EINPROGRESS; /* Call commit handler */
}
static int atmel_get_encode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
if (!priv->wep_is_on)
dwrq->flags = IW_ENCODE_DISABLED;
else {
if (priv->exclude_unencrypted)
dwrq->flags = IW_ENCODE_RESTRICTED;
else
dwrq->flags = IW_ENCODE_OPEN;
}
/* Which key do we want ? -1 -> tx index */
if (index < 0 || index >= 4)
index = priv->default_key;
dwrq->flags |= index + 1;
/* Copy the key to the user buffer */
dwrq->length = priv->wep_key_len[index];
if (dwrq->length > 16) {
dwrq->length = 0;
} else {
memset(extra, 0, 16);
memcpy(extra, priv->wep_keys[index], dwrq->length);
}
return 0;
}
static int atmel_set_encodeext(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
struct iw_point *encoding = &wrqu->encoding;
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
int idx, key_len, alg = ext->alg, set_key = 1;
/* Determine and validate the key index */
idx = encoding->flags & IW_ENCODE_INDEX;
if (idx) {
if (idx < 1 || idx > 4)
return -EINVAL;
idx--;
} else
idx = priv->default_key;
if (encoding->flags & IW_ENCODE_DISABLED)
alg = IW_ENCODE_ALG_NONE;
if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
priv->default_key = idx;
set_key = ext->key_len > 0 ? 1 : 0;
}
if (set_key) {
/* Set the requested key first */
switch (alg) {
case IW_ENCODE_ALG_NONE:
priv->wep_is_on = 0;
priv->encryption_level = 0;
priv->pairwise_cipher_suite = CIPHER_SUITE_NONE;
break;
case IW_ENCODE_ALG_WEP:
if (ext->key_len > 5) {
priv->wep_key_len[idx] = 13;
priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128;
priv->encryption_level = 2;
} else if (ext->key_len > 0) {
priv->wep_key_len[idx] = 5;
priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64;
priv->encryption_level = 1;
} else {
return -EINVAL;
}
priv->wep_is_on = 1;
memset(priv->wep_keys[idx], 0, 13);
key_len = min ((int)ext->key_len, priv->wep_key_len[idx]);
memcpy(priv->wep_keys[idx], ext->key, key_len);
break;
default:
return -EINVAL;
}
}
return -EINPROGRESS;
}
static int atmel_get_encodeext(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
struct iw_point *encoding = &wrqu->encoding;
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
int idx, max_key_len;
max_key_len = encoding->length - sizeof(*ext);
if (max_key_len < 0)
return -EINVAL;
idx = encoding->flags & IW_ENCODE_INDEX;
if (idx) {
if (idx < 1 || idx > 4)
return -EINVAL;
idx--;
} else
idx = priv->default_key;
encoding->flags = idx + 1;
memset(ext, 0, sizeof(*ext));
if (!priv->wep_is_on) {
ext->alg = IW_ENCODE_ALG_NONE;
ext->key_len = 0;
encoding->flags |= IW_ENCODE_DISABLED;
} else {
if (priv->encryption_level > 0)
ext->alg = IW_ENCODE_ALG_WEP;
else
return -EINVAL;
ext->key_len = priv->wep_key_len[idx];
memcpy(ext->key, priv->wep_keys[idx], ext->key_len);
encoding->flags |= IW_ENCODE_ENABLED;
}
return 0;
}
static int atmel_set_auth(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
struct iw_param *param = &wrqu->param;
switch (param->flags & IW_AUTH_INDEX) {
case IW_AUTH_WPA_VERSION:
case IW_AUTH_CIPHER_PAIRWISE:
case IW_AUTH_CIPHER_GROUP:
case IW_AUTH_KEY_MGMT:
case IW_AUTH_RX_UNENCRYPTED_EAPOL:
case IW_AUTH_PRIVACY_INVOKED:
/*
* atmel does not use these parameters
*/
break;
case IW_AUTH_DROP_UNENCRYPTED:
priv->exclude_unencrypted = param->value ? 1 : 0;
break;
case IW_AUTH_80211_AUTH_ALG: {
if (param->value & IW_AUTH_ALG_SHARED_KEY) {
priv->exclude_unencrypted = 1;
} else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
priv->exclude_unencrypted = 0;
} else
return -EINVAL;
break;
}
case IW_AUTH_WPA_ENABLED:
/* Silently accept disable of WPA */
if (param->value > 0)
return -EOPNOTSUPP;
break;
default:
return -EOPNOTSUPP;
}
return -EINPROGRESS;
}
static int atmel_get_auth(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
struct iw_param *param = &wrqu->param;
switch (param->flags & IW_AUTH_INDEX) {
case IW_AUTH_DROP_UNENCRYPTED:
param->value = priv->exclude_unencrypted;
break;
case IW_AUTH_80211_AUTH_ALG:
if (priv->exclude_unencrypted == 1)
param->value = IW_AUTH_ALG_SHARED_KEY;
else
param->value = IW_AUTH_ALG_OPEN_SYSTEM;
break;
case IW_AUTH_WPA_ENABLED:
param->value = 0;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int atmel_get_name(struct net_device *dev,
struct iw_request_info *info,
char *cwrq,
char *extra)
{
strcpy(cwrq, "IEEE 802.11-DS");
return 0;
}
static int atmel_set_rate(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *vwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
if (vwrq->fixed == 0) {
priv->tx_rate = 3;
priv->auto_tx_rate = 1;
} else {
priv->auto_tx_rate = 0;
/* Which type of value ? */
if ((vwrq->value < 4) && (vwrq->value >= 0)) {
/* Setting by rate index */
priv->tx_rate = vwrq->value;
} else {
/* Setting by frequency value */
switch (vwrq->value) {
case 1000000:
priv->tx_rate = 0;
break;
case 2000000:
priv->tx_rate = 1;
break;
case 5500000:
priv->tx_rate = 2;
break;
case 11000000:
priv->tx_rate = 3;
break;
default:
return -EINVAL;
}
}
}
return -EINPROGRESS;
}
static int atmel_set_mode(struct net_device *dev,
struct iw_request_info *info,
__u32 *uwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
if (*uwrq != IW_MODE_ADHOC && *uwrq != IW_MODE_INFRA)
return -EINVAL;
priv->operating_mode = *uwrq;
return -EINPROGRESS;
}
static int atmel_get_mode(struct net_device *dev,
struct iw_request_info *info,
__u32 *uwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
*uwrq = priv->operating_mode;
return 0;
}
static int atmel_get_rate(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *vwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
if (priv->auto_tx_rate) {
vwrq->fixed = 0;
vwrq->value = 11000000;
} else {
vwrq->fixed = 1;
switch (priv->tx_rate) {
case 0:
vwrq->value = 1000000;
break;
case 1:
vwrq->value = 2000000;
break;
case 2:
vwrq->value = 5500000;
break;
case 3:
vwrq->value = 11000000;
break;
}
}
return 0;
}
static int atmel_set_power(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *vwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
priv->power_mode = vwrq->disabled ? 0 : 1;
return -EINPROGRESS;
}
static int atmel_get_power(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *vwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
vwrq->disabled = priv->power_mode ? 0 : 1;
vwrq->flags = IW_POWER_ON;
return 0;
}
static int atmel_set_retry(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *vwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) {
if (vwrq->flags & IW_RETRY_LONG)
priv->long_retry = vwrq->value;
else if (vwrq->flags & IW_RETRY_SHORT)
priv->short_retry = vwrq->value;
else {
/* No modifier : set both */
priv->long_retry = vwrq->value;
priv->short_retry = vwrq->value;
}
return -EINPROGRESS;
}
return -EINVAL;
}
static int atmel_get_retry(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *vwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
vwrq->disabled = 0; /* Can't be disabled */
/* Note : by default, display the short retry number */
if (vwrq->flags & IW_RETRY_LONG) {
vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
vwrq->value = priv->long_retry;
} else {
vwrq->flags = IW_RETRY_LIMIT;
vwrq->value = priv->short_retry;
if (priv->long_retry != priv->short_retry)
vwrq->flags |= IW_RETRY_SHORT;
}
return 0;
}
static int atmel_set_rts(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *vwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
int rthr = vwrq->value;
if (vwrq->disabled)
rthr = 2347;
if ((rthr < 0) || (rthr > 2347)) {
return -EINVAL;
}
priv->rts_threshold = rthr;
return -EINPROGRESS; /* Call commit handler */
}
static int atmel_get_rts(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *vwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
vwrq->value = priv->rts_threshold;
vwrq->disabled = (vwrq->value >= 2347);
vwrq->fixed = 1;
return 0;
}
static int atmel_set_frag(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *vwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
int fthr = vwrq->value;
if (vwrq->disabled)
fthr = 2346;
if ((fthr < 256) || (fthr > 2346)) {
return -EINVAL;
}
fthr &= ~0x1; /* Get an even value - is it really needed ??? */
priv->frag_threshold = fthr;
return -EINPROGRESS; /* Call commit handler */
}
static int atmel_get_frag(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *vwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
vwrq->value = priv->frag_threshold;
vwrq->disabled = (vwrq->value >= 2346);
vwrq->fixed = 1;
return 0;
}
static int atmel_set_freq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *fwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
int rc = -EINPROGRESS; /* Call commit handler */
/* If setting by frequency, convert to a channel */
if (fwrq->e == 1) {
int f = fwrq->m / 100000;
/* Hack to fall through... */
fwrq->e = 0;
fwrq->m = ieee80211_frequency_to_channel(f);
}
/* Setting by channel number */
if ((fwrq->m > 1000) || (fwrq->e > 0))
rc = -EOPNOTSUPP;
else {
int channel = fwrq->m;
if (atmel_validate_channel(priv, channel) == 0) {
priv->channel = channel;
} else {
rc = -EINVAL;
}
}
return rc;
}
static int atmel_get_freq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *fwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
fwrq->m = priv->channel;
fwrq->e = 0;
return 0;
}
static int atmel_set_scan(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
unsigned long flags;
/* Note : you may have realised that, as this is a SET operation,
* this is privileged and therefore a normal user can't
* perform scanning.
* This is not an error, while the device perform scanning,
* traffic doesn't flow, so it's a perfect DoS...
* Jean II */
if (priv->station_state == STATION_STATE_DOWN)
return -EAGAIN;
/* Timeout old surveys. */
if (time_after(jiffies, priv->last_survey + 20 * HZ))
priv->site_survey_state = SITE_SURVEY_IDLE;
priv->last_survey = jiffies;
/* Initiate a scan command */
if (priv->site_survey_state == SITE_SURVEY_IN_PROGRESS)
return -EBUSY;
del_timer_sync(&priv->management_timer);
spin_lock_irqsave(&priv->irqlock, flags);
priv->site_survey_state = SITE_SURVEY_IN_PROGRESS;
priv->fast_scan = 0;
atmel_scan(priv, 0);
spin_unlock_irqrestore(&priv->irqlock, flags);
return 0;
}
static int atmel_get_scan(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
int i;
char *current_ev = extra;
struct iw_event iwe;
if (priv->site_survey_state != SITE_SURVEY_COMPLETED)
return -EAGAIN;
for (i = 0; i < priv->BSS_list_entries; i++) {
iwe.cmd = SIOCGIWAP;
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN);
current_ev = iwe_stream_add_event(info, current_ev,
extra + IW_SCAN_MAX_DATA,
&iwe, IW_EV_ADDR_LEN);
iwe.u.data.length = priv->BSSinfo[i].SSIDsize;
if (iwe.u.data.length > 32)
iwe.u.data.length = 32;
iwe.cmd = SIOCGIWESSID;
iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(info, current_ev,
extra + IW_SCAN_MAX_DATA,
&iwe, priv->BSSinfo[i].SSID);
iwe.cmd = SIOCGIWMODE;
iwe.u.mode = priv->BSSinfo[i].BSStype;
current_ev = iwe_stream_add_event(info, current_ev,
extra + IW_SCAN_MAX_DATA,
&iwe, IW_EV_UINT_LEN);
iwe.cmd = SIOCGIWFREQ;
iwe.u.freq.m = priv->BSSinfo[i].channel;
iwe.u.freq.e = 0;
current_ev = iwe_stream_add_event(info, current_ev,
extra + IW_SCAN_MAX_DATA,
&iwe, IW_EV_FREQ_LEN);
/* Add quality statistics */
iwe.cmd = IWEVQUAL;
iwe.u.qual.level = priv->BSSinfo[i].RSSI;
iwe.u.qual.qual = iwe.u.qual.level;
/* iwe.u.qual.noise = SOMETHING */
current_ev = iwe_stream_add_event(info, current_ev,
extra + IW_SCAN_MAX_DATA,
&iwe, IW_EV_QUAL_LEN);
iwe.cmd = SIOCGIWENCODE;
if (priv->BSSinfo[i].UsingWEP)
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
else
iwe.u.data.flags = IW_ENCODE_DISABLED;
iwe.u.data.length = 0;
current_ev = iwe_stream_add_point(info, current_ev,
extra + IW_SCAN_MAX_DATA,
&iwe, NULL);
}
/* Length of data */
dwrq->length = (current_ev - extra);
dwrq->flags = 0;
return 0;
}
static int atmel_get_range(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
struct iw_range *range = (struct iw_range *) extra;
int k, i, j;
dwrq->length = sizeof(struct iw_range);
memset(range, 0, sizeof(struct iw_range));
range->min_nwid = 0x0000;
range->max_nwid = 0x0000;
range->num_channels = 0;
for (j = 0; j < ARRAY_SIZE(channel_table); j++)
if (priv->reg_domain == channel_table[j].reg_domain) {
range->num_channels = channel_table[j].max - channel_table[j].min + 1;
break;
}
if (range->num_channels != 0) {
for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) {
range->freq[k].i = i; /* List index */
/* Values in MHz -> * 10^5 * 10 */
range->freq[k].m = 100000 *
ieee80211_channel_to_frequency(i, IEEE80211_BAND_2GHZ);
range->freq[k++].e = 1;
}
range->num_frequency = k;
}
range->max_qual.qual = 100;
range->max_qual.level = 100;
range->max_qual.noise = 0;
range->max_qual.updated = IW_QUAL_NOISE_INVALID;
range->avg_qual.qual = 50;
range->avg_qual.level = 50;
range->avg_qual.noise = 0;
range->avg_qual.updated = IW_QUAL_NOISE_INVALID;
range->sensitivity = 0;
range->bitrate[0] = 1000000;
range->bitrate[1] = 2000000;
range->bitrate[2] = 5500000;
range->bitrate[3] = 11000000;
range->num_bitrates = 4;
range->min_rts = 0;
range->max_rts = 2347;
range->min_frag = 256;
range->max_frag = 2346;
range->encoding_size[0] = 5;
range->encoding_size[1] = 13;
range->num_encoding_sizes = 2;
range->max_encoding_tokens = 4;
range->pmp_flags = IW_POWER_ON;
range->pmt_flags = IW_POWER_ON;
range->pm_capa = 0;
range->we_version_source = WIRELESS_EXT;
range->we_version_compiled = WIRELESS_EXT;
range->retry_capa = IW_RETRY_LIMIT ;
range->retry_flags = IW_RETRY_LIMIT;
range->r_time_flags = 0;
range->min_retry = 1;
range->max_retry = 65535;
return 0;
}
static int atmel_set_wap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *awrq,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
int i;
static const u8 any[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
unsigned long flags;
if (awrq->sa_family != ARPHRD_ETHER)
return -EINVAL;
if (!memcmp(any, awrq->sa_data, 6) ||
!memcmp(off, awrq->sa_data, 6)) {
del_timer_sync(&priv->management_timer);
spin_lock_irqsave(&priv->irqlock, flags);
atmel_scan(priv, 1);
spin_unlock_irqrestore(&priv->irqlock, flags);
return 0;
}
for (i = 0; i < priv->BSS_list_entries; i++) {
if (memcmp(priv->BSSinfo[i].BSSID, awrq->sa_data, 6) == 0) {
if (!priv->wep_is_on && priv->BSSinfo[i].UsingWEP) {
return -EINVAL;
} else if (priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) {
return -EINVAL;
} else {
del_timer_sync(&priv->management_timer);
spin_lock_irqsave(&priv->irqlock, flags);
atmel_join_bss(priv, i);
spin_unlock_irqrestore(&priv->irqlock, flags);
return 0;
}
}
}
return -EINVAL;
}
static int atmel_config_commit(struct net_device *dev,
struct iw_request_info *info, /* NULL */
void *zwrq, /* NULL */
char *extra) /* NULL */
{
return atmel_open(dev);
}
static const iw_handler atmel_handler[] =
{
(iw_handler) atmel_config_commit, /* SIOCSIWCOMMIT */
(iw_handler) atmel_get_name, /* SIOCGIWNAME */
(iw_handler) NULL, /* SIOCSIWNWID */
(iw_handler) NULL, /* SIOCGIWNWID */
(iw_handler) atmel_set_freq, /* SIOCSIWFREQ */
(iw_handler) atmel_get_freq, /* SIOCGIWFREQ */
(iw_handler) atmel_set_mode, /* SIOCSIWMODE */
(iw_handler) atmel_get_mode, /* SIOCGIWMODE */
(iw_handler) NULL, /* SIOCSIWSENS */
(iw_handler) NULL, /* SIOCGIWSENS */
(iw_handler) NULL, /* SIOCSIWRANGE */
(iw_handler) atmel_get_range, /* SIOCGIWRANGE */
(iw_handler) NULL, /* SIOCSIWPRIV */
(iw_handler) NULL, /* SIOCGIWPRIV */
(iw_handler) NULL, /* SIOCSIWSTATS */
(iw_handler) NULL, /* SIOCGIWSTATS */
(iw_handler) NULL, /* SIOCSIWSPY */
(iw_handler) NULL, /* SIOCGIWSPY */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) atmel_set_wap, /* SIOCSIWAP */
(iw_handler) atmel_get_wap, /* SIOCGIWAP */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* SIOCGIWAPLIST */
(iw_handler) atmel_set_scan, /* SIOCSIWSCAN */
(iw_handler) atmel_get_scan, /* SIOCGIWSCAN */
(iw_handler) atmel_set_essid, /* SIOCSIWESSID */
(iw_handler) atmel_get_essid, /* SIOCGIWESSID */
(iw_handler) NULL, /* SIOCSIWNICKN */
(iw_handler) NULL, /* SIOCGIWNICKN */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) atmel_set_rate, /* SIOCSIWRATE */
(iw_handler) atmel_get_rate, /* SIOCGIWRATE */
(iw_handler) atmel_set_rts, /* SIOCSIWRTS */
(iw_handler) atmel_get_rts, /* SIOCGIWRTS */
(iw_handler) atmel_set_frag, /* SIOCSIWFRAG */
(iw_handler) atmel_get_frag, /* SIOCGIWFRAG */
(iw_handler) NULL, /* SIOCSIWTXPOW */
(iw_handler) NULL, /* SIOCGIWTXPOW */
(iw_handler) atmel_set_retry, /* SIOCSIWRETRY */
(iw_handler) atmel_get_retry, /* SIOCGIWRETRY */
(iw_handler) atmel_set_encode, /* SIOCSIWENCODE */
(iw_handler) atmel_get_encode, /* SIOCGIWENCODE */
(iw_handler) atmel_set_power, /* SIOCSIWPOWER */
(iw_handler) atmel_get_power, /* SIOCGIWPOWER */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* SIOCSIWGENIE */
(iw_handler) NULL, /* SIOCGIWGENIE */
(iw_handler) atmel_set_auth, /* SIOCSIWAUTH */
(iw_handler) atmel_get_auth, /* SIOCGIWAUTH */
(iw_handler) atmel_set_encodeext, /* SIOCSIWENCODEEXT */
(iw_handler) atmel_get_encodeext, /* SIOCGIWENCODEEXT */
(iw_handler) NULL, /* SIOCSIWPMKSA */
};
static const iw_handler atmel_private_handler[] =
{
NULL, /* SIOCIWFIRSTPRIV */
};
struct atmel_priv_ioctl {
char id[32];
unsigned char __user *data;
unsigned short len;
};
#define ATMELFWL SIOCIWFIRSTPRIV
#define ATMELIDIFC ATMELFWL + 1
#define ATMELRD ATMELFWL + 2
#define ATMELMAGIC 0x51807
#define REGDOMAINSZ 20
static const struct iw_priv_args atmel_private_args[] = {
{
.cmd = ATMELFWL,
.set_args = IW_PRIV_TYPE_BYTE
| IW_PRIV_SIZE_FIXED
| sizeof(struct atmel_priv_ioctl),
.get_args = IW_PRIV_TYPE_NONE,
.name = "atmelfwl"
}, {
.cmd = ATMELIDIFC,
.set_args = IW_PRIV_TYPE_NONE,
.get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
.name = "atmelidifc"
}, {
.cmd = ATMELRD,
.set_args = IW_PRIV_TYPE_CHAR | REGDOMAINSZ,
.get_args = IW_PRIV_TYPE_NONE,
.name = "regdomain"
},
};
static const struct iw_handler_def atmel_handler_def = {
.num_standard = ARRAY_SIZE(atmel_handler),
.num_private = ARRAY_SIZE(atmel_private_handler),
.num_private_args = ARRAY_SIZE(atmel_private_args),
.standard = (iw_handler *) atmel_handler,
.private = (iw_handler *) atmel_private_handler,
.private_args = (struct iw_priv_args *) atmel_private_args,
.get_wireless_stats = atmel_get_wireless_stats
};
static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
int i, rc = 0;
struct atmel_private *priv = netdev_priv(dev);
struct atmel_priv_ioctl com;
struct iwreq *wrq = (struct iwreq *) rq;
unsigned char *new_firmware;
char domain[REGDOMAINSZ + 1];
switch (cmd) {
case ATMELIDIFC:
wrq->u.param.value = ATMELMAGIC;
break;
case ATMELFWL:
if (copy_from_user(&com, rq->ifr_data, sizeof(com))) {
rc = -EFAULT;
break;
}
if (!capable(CAP_NET_ADMIN)) {
rc = -EPERM;
break;
}
if (!(new_firmware = kmalloc(com.len, GFP_KERNEL))) {
rc = -ENOMEM;
break;
}
if (copy_from_user(new_firmware, com.data, com.len)) {
kfree(new_firmware);
rc = -EFAULT;
break;
}
kfree(priv->firmware);
priv->firmware = new_firmware;
priv->firmware_length = com.len;
strncpy(priv->firmware_id, com.id, 31);
priv->firmware_id[31] = '\0';
break;
case ATMELRD:
if (copy_from_user(domain, rq->ifr_data, REGDOMAINSZ)) {
rc = -EFAULT;
break;
}
if (!capable(CAP_NET_ADMIN)) {
rc = -EPERM;
break;
}
domain[REGDOMAINSZ] = 0;
rc = -EINVAL;
for (i = 0; i < ARRAY_SIZE(channel_table); i++) {
if (!strcasecmp(channel_table[i].name, domain)) {
priv->config_reg_domain = channel_table[i].reg_domain;
rc = 0;
}
}
if (rc == 0 && priv->station_state != STATION_STATE_DOWN)
rc = atmel_open(dev);
break;
default:
rc = -EOPNOTSUPP;
}
return rc;
}
struct auth_body {
__le16 alg;
__le16 trans_seq;
__le16 status;
u8 el_id;
u8 chall_text_len;
u8 chall_text[253];
};
static void atmel_enter_state(struct atmel_private *priv, int new_state)
{
int old_state = priv->station_state;
if (new_state == old_state)
return;
priv->station_state = new_state;
if (new_state == STATION_STATE_READY) {
netif_start_queue(priv->dev);
netif_carrier_on(priv->dev);
}
if (old_state == STATION_STATE_READY) {
netif_carrier_off(priv->dev);
if (netif_running(priv->dev))
netif_stop_queue(priv->dev);
priv->last_beacon_timestamp = 0;
}
}
static void atmel_scan(struct atmel_private *priv, int specific_ssid)
{
struct {
u8 BSSID[ETH_ALEN];
u8 SSID[MAX_SSID_LENGTH];
u8 scan_type;
u8 channel;
__le16 BSS_type;
__le16 min_channel_time;
__le16 max_channel_time;
u8 options;
u8 SSID_size;
} cmd;
eth_broadcast_addr(cmd.BSSID);
if (priv->fast_scan) {
cmd.SSID_size = priv->SSID_size;
memcpy(cmd.SSID, priv->SSID, priv->SSID_size);
cmd.min_channel_time = cpu_to_le16(10);
cmd.max_channel_time = cpu_to_le16(50);
} else {
priv->BSS_list_entries = 0;
cmd.SSID_size = 0;
cmd.min_channel_time = cpu_to_le16(10);
cmd.max_channel_time = cpu_to_le16(120);
}
cmd.options = 0;
if (!specific_ssid)
cmd.options |= SCAN_OPTIONS_SITE_SURVEY;
cmd.channel = (priv->channel & 0x7f);
cmd.scan_type = SCAN_TYPE_ACTIVE;
cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ?
BSS_TYPE_AD_HOC : BSS_TYPE_INFRASTRUCTURE);
atmel_send_command(priv, CMD_Scan, &cmd, sizeof(cmd));
/* This must come after all hardware access to avoid being messed up
by stuff happening in interrupt context after we leave STATE_DOWN */
atmel_enter_state(priv, STATION_STATE_SCANNING);
}
static void join(struct atmel_private *priv, int type)
{
struct {
u8 BSSID[6];
u8 SSID[MAX_SSID_LENGTH];
u8 BSS_type; /* this is a short in a scan command - weird */
u8 channel;
__le16 timeout;
u8 SSID_size;
u8 reserved;
} cmd;
cmd.SSID_size = priv->SSID_size;
memcpy(cmd.SSID, priv->SSID, priv->SSID_size);
memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN);
cmd.channel = (priv->channel & 0x7f);
cmd.BSS_type = type;
cmd.timeout = cpu_to_le16(2000);
atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd));
}
static void start(struct atmel_private *priv, int type)
{
struct {
u8 BSSID