Skip to content
Snippets Groups Projects
atmel.c 130 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
/*** -*- linux-c -*- **********************************************************

     Driver for Atmel at76c502 at76c504 and at76c506 wireless cards.

	Copyright 2000-2001 ATMEL Corporation.
	Copyright 2003-2004 Simon Kelley.
Linus Torvalds's avatar
Linus Torvalds committed

    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,
Linus Torvalds's avatar
Linus Torvalds committed
    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, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    For all queries about this code, please contact the current author,
Linus Torvalds's avatar
Linus Torvalds committed
    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/init.h>

#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/timer.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <asm/io.h>
#include <asm/system.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/device.h>
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#include <linux/jiffies.h>
Johannes Berg's avatar
Johannes Berg committed
#include <linux/ieee80211.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include "atmel.h"

#define DRIVER_MAJOR 0
#define DRIVER_MINOR 98
Linus Torvalds's avatar
Linus Torvalds committed

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
Linus Torvalds's avatar
Linus Torvalds committed
   over-rides any automatic selection */
static char *firmware = NULL;
module_param(firmware, charp, 0);

/* table of firmware file names */
static struct {
Linus Torvalds's avatar
Linus Torvalds committed
	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 }
Linus Torvalds's avatar
Linus Torvalds committed
};
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");
Linus Torvalds's avatar
Linus Torvalds committed

#define MAX_SSID_LENGTH 32
#define MGMT_JIFFIES (256 * HZ / 100)

#define MAX_BSS_ENTRIES	64
Linus Torvalds's avatar
Linus Torvalds committed

/* registers */
#define GCR  0x00    /* (SIR0)  General Configuration Register */
#define BSR  0x02    /* (SIR1)  Bank Switching Select Register */
Linus Torvalds's avatar
Linus Torvalds committed
#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 */
Linus Torvalds's avatar
Linus Torvalds committed

#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 */
Linus Torvalds's avatar
Linus Torvalds committed

#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];
Linus Torvalds's avatar
Linus Torvalds committed
};

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;
Linus Torvalds's avatar
Linus Torvalds committed
};

#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;
Linus Torvalds's avatar
Linus Torvalds committed
	u8        TxState;
	u8        TxStatus;
	u8        RetryCount;
Linus Torvalds's avatar
Linus Torvalds committed
	u8        TxRate;

	u8        KeyIndex;
	u8        ChiperType;
	u8        ChipreLength;
	u8        Reserved1;
Linus Torvalds's avatar
Linus Torvalds committed

	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
 */
Linus Torvalds's avatar
Linus Torvalds committed

#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
Linus Torvalds's avatar
Linus Torvalds committed

#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
Linus Torvalds's avatar
Linus Torvalds committed

#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
Linus Torvalds's avatar
Linus Torvalds committed

#define MAX_ENCRYPTION_KEYS 4
#define MAX_ENCRYPTION_KEY_SIZE 40

/*
 * 802.11 related definitions
 */
Linus Torvalds's avatar
Linus Torvalds committed

/*
 * Regulatory Domains
 */
Linus Torvalds's avatar
Linus Torvalds committed

#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				*/
Linus Torvalds's avatar
Linus Torvalds committed

#define BSS_TYPE_AD_HOC		1
Linus Torvalds's avatar
Linus Torvalds committed
#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 */
Linus Torvalds's avatar
Linus Torvalds committed
#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
Linus Torvalds's avatar
Linus Torvalds committed
#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
Linus Torvalds's avatar
Linus Torvalds committed
#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
 */
Linus Torvalds's avatar
Linus Torvalds committed

/*
 * FuncCtrl field:
 */
Linus Torvalds's avatar
Linus Torvalds committed
#define FUNC_CTRL_TxENABLE		0x10
#define FUNC_CTRL_RxENABLE		0x20
#define FUNC_CTRL_INIT_COMPLETE		0x01
Linus Torvalds's avatar
Linus Torvalds committed

/* 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
Linus Torvalds's avatar
Linus Torvalds committed
};

struct atmel_private {
	void *card; /* Bus dependent stucture 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 */
Linus Torvalds's avatar
Linus Torvalds committed
	enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type;
	enum {
		CARD_TYPE_PARALLEL_FLASH,
Linus Torvalds's avatar
Linus Torvalds committed
		CARD_TYPE_SPI_FLASH,
		CARD_TYPE_EEPROM
Linus Torvalds's avatar
Linus Torvalds committed
	} 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;
Linus Torvalds's avatar
Linus Torvalds committed
	u16 frag_seq, frag_len, frag_no;
	u8 frag_source[6];

Linus Torvalds's avatar
Linus Torvalds committed
	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];
Linus Torvalds's avatar
Linus Torvalds committed
	int use_wpa, radio_on_broken; /* firmware dependent stuff. */

	u16 host_info_base;
	struct host_info_struct {
Linus Torvalds's avatar
Linus Torvalds committed
		/* 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;
Linus Torvalds's avatar
Linus Torvalds committed
		u16 build_version;
		u16 command_pos;

Linus Torvalds's avatar
Linus Torvalds committed
		u16 major_version;
		u16 minor_version;
Linus Torvalds's avatar
Linus Torvalds committed
		u16 func_ctrl;
		u16 mac_status;
		u16 generic_IRQ_type;
		u8  reserved[2];
	} host_info;

Linus Torvalds's avatar
Linus Torvalds committed
		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;
Linus Torvalds's avatar
Linus Torvalds committed
	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;
Linus Torvalds's avatar
Linus Torvalds committed
	int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt;
	enum {
		SITE_SURVEY_IDLE,
		SITE_SURVEY_IN_PROGRESS,
		SITE_SURVEY_COMPLETED
Linus Torvalds's avatar
Linus Torvalds committed
	} site_survey_state;
	unsigned long last_survey;
Linus Torvalds's avatar
Linus Torvalds committed

	int station_was_associated, station_is_associated;
	int fast_scan;
Linus Torvalds's avatar
Linus Torvalds committed
	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;
Linus Torvalds's avatar
Linus Torvalds committed
	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};
Linus Torvalds's avatar
Linus Torvalds committed

static const struct {
	int reg_domain;
	int min, max;
	char *name;
Linus Torvalds's avatar
Linus Torvalds committed
} 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" },
Linus Torvalds's avatar
Linus Torvalds committed
		      { 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);
Linus Torvalds's avatar
Linus Torvalds committed
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,
Johannes Berg's avatar
Johannes Berg committed
				   struct ieee80211_hdr *header,
Linus Torvalds's avatar
Linus Torvalds committed
				   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,
Johannes Berg's avatar
Johannes Berg committed
					    struct ieee80211_hdr *header,
Linus Torvalds's avatar
Linus Torvalds committed
					    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);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
{
	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)
Linus Torvalds's avatar
Linus Torvalds committed
{
	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);
Linus Torvalds's avatar
Linus Torvalds committed
	return atmel_read8(priv->dev, DR);
}

static inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data)
{
	atmel_writeAR(priv->dev, pos);
Linus Torvalds's avatar
Linus Torvalds committed
	atmel_write8(priv->dev, DR, data);
}

static inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos)
{
	atmel_writeAR(priv->dev, pos);
Linus Torvalds's avatar
Linus Torvalds committed
	return atmel_read16(priv->dev, DR);
}

static inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data)
{
	atmel_writeAR(priv->dev, pos);
Linus Torvalds's avatar
Linus Torvalds committed
	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;
Linus Torvalds's avatar
Linus Torvalds committed
	     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;
Linus Torvalds's avatar
Linus Torvalds committed
		if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1))
			priv->tx_desc_head++ ;
Linus Torvalds's avatar
Linus Torvalds committed
		else
			priv->tx_desc_head = 0;
Linus Torvalds's avatar
Linus Torvalds committed
		if (type == TX_PACKET_TYPE_DATA) {
			if (status == TX_STATUS_SUCCESS)
				priv->dev->stats.tx_packets++;
				priv->dev->stats.tx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
			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)
Linus Torvalds's avatar
Linus Torvalds committed
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
	if (bottom_free >= len)
		return priv->host_info.tx_buff_pos + priv->tx_buff_tail;
Linus Torvalds's avatar
Linus Torvalds committed
	if (priv->tx_free_mem - bottom_free >= len) {
		priv->tx_buff_tail = 0;
		return priv->host_info.tx_buff_pos;
	}
Linus Torvalds's avatar
Linus Torvalds committed
	return 0;
}

static void tx_update_descriptor(struct atmel_private *priv, int is_bcast,
				 u16 len, u16 buff, u8 type)
Linus Torvalds's avatar
Linus Torvalds committed
{
	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)
Linus Torvalds's avatar
Linus Torvalds committed
				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)
Linus Torvalds's avatar
Linus Torvalds committed
				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;
			}
		}
Linus Torvalds's avatar
Linus Torvalds committed
		atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail),
			    cipher_type);
Linus Torvalds's avatar
Linus Torvalds committed
		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))
Linus Torvalds's avatar
Linus Torvalds committed
		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)
Linus Torvalds's avatar
Linus Torvalds committed
{
	static const u8 SNAP_RFC1024[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
Linus Torvalds's avatar
Linus Torvalds committed
	struct atmel_private *priv = netdev_priv(dev);
Johannes Berg's avatar
Johannes Berg committed
	struct ieee80211_hdr header;
Linus Torvalds's avatar
Linus Torvalds committed
	unsigned long flags;
	u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;

	if (priv->card && priv->present_callback &&
Linus Torvalds's avatar
Linus Torvalds committed
	    !(*priv->present_callback)(priv->card)) {
		dev->stats.tx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
		dev_kfree_skb(skb);
Linus Torvalds's avatar
Linus Torvalds committed
	}
Linus Torvalds's avatar
Linus Torvalds committed
	if (priv->station_state != STATION_STATE_READY) {
		dev->stats.tx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
		dev_kfree_skb(skb);
Linus Torvalds's avatar
Linus Torvalds committed
	}
Linus Torvalds's avatar
Linus Torvalds committed
	/* first ensure the timer func cannot run */
	spin_lock_bh(&priv->timerlock);
Linus Torvalds's avatar
Linus Torvalds committed
	/* then stop the hardware ISR */
	spin_lock_irqsave(&priv->irqlock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
	/* nb doing the above in the opposite order will deadlock */
Linus Torvalds's avatar
Linus Torvalds committed
	/* 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) */

Linus Torvalds's avatar
Linus Torvalds committed
	if (!(buff = find_tx_buff(priv, len + 18))) {
		dev->stats.tx_dropped++;
Linus Torvalds's avatar
Linus Torvalds committed
		spin_unlock_irqrestore(&priv->irqlock, flags);
		spin_unlock_bh(&priv->timerlock);
		netif_stop_queue(dev);
Linus Torvalds's avatar
Linus Torvalds committed
	}
Jeff Garzik's avatar
Jeff Garzik committed
	frame_ctl = IEEE80211_FTYPE_DATA;
Linus Torvalds's avatar
Linus Torvalds committed
	header.duration_id = 0;
Johannes Berg's avatar
Johannes Berg committed
	header.seq_ctrl = 0;
Linus Torvalds's avatar
Linus Torvalds committed
	if (priv->wep_is_on)
		frame_ctl |= IEEE80211_FCTL_PROTECTED;
Linus Torvalds's avatar
Linus Torvalds committed
	if (priv->operating_mode == IW_MODE_ADHOC) {
		skb_copy_from_linear_data(skb, &header.addr1, 6);
Linus Torvalds's avatar
Linus Torvalds committed
		memcpy(&header.addr2, dev->dev_addr, 6);
		memcpy(&header.addr3, priv->BSSID, 6);
	} else {
Jeff Garzik's avatar
Jeff Garzik committed
		frame_ctl |= IEEE80211_FCTL_TODS;
Linus Torvalds's avatar
Linus Torvalds committed
		memcpy(&header.addr1, priv->CurrentBSSID, 6);
		memcpy(&header.addr2, dev->dev_addr, 6);
		skb_copy_from_linear_data(skb, &header.addr3, 6);
Linus Torvalds's avatar
Linus Torvalds committed
	}
Linus Torvalds's avatar
Linus Torvalds committed
	if (priv->use_wpa)
		memcpy(&header.addr4, SNAP_RFC1024, 6);

Johannes Berg's avatar
Johannes Berg committed
	header.frame_control = cpu_to_le16(frame_ctl);
Linus Torvalds's avatar
Linus Torvalds committed
	/* 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;
Linus Torvalds's avatar
Linus Torvalds committed
	/* 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->trans_start = jiffies;
	dev->stats.tx_bytes += len;
Linus Torvalds's avatar
Linus Torvalds committed
	spin_unlock_irqrestore(&priv->irqlock, flags);
	spin_unlock_bh(&priv->timerlock);
	dev_kfree_skb(skb);
static void atmel_transmit_management_frame(struct atmel_private *priv,
Johannes Berg's avatar
Johannes Berg committed
					    struct ieee80211_hdr *header,
Linus Torvalds's avatar
Linus Torvalds committed
					    u8 *body, int body_len)
{
	u16 buff;
	int len = MGMT_FRAME_BODY_OFFSET + body_len;

	if (!(buff = find_tx_buff(priv, len)))
Linus Torvalds's avatar
Linus Torvalds committed
		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,
Johannes Berg's avatar
Johannes Berg committed
			 struct ieee80211_hdr *header,
Linus Torvalds's avatar
Linus Torvalds committed
			 u16 msdu_size, u16 rx_packet_loc, u32 crc)
{
	/* fast path: unfragmented packet copy directly into skbuf */
	u8 mac4[6];
	struct sk_buff	*skb;
Linus Torvalds's avatar
Linus Torvalds committed
	unsigned char *skbp;
Linus Torvalds's avatar
Linus Torvalds committed
	/* 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;
Linus Torvalds's avatar
Linus Torvalds committed
	if (priv->do_rx_crc) {
		crc = crc32_le(crc, mac4, 6);
		msdu_size -= 4;
	}
Linus Torvalds's avatar
Linus Torvalds committed
	if (!(skb = dev_alloc_skb(msdu_size + 14))) {
		priv->dev->stats.rx_dropped++;
Linus Torvalds's avatar
Linus Torvalds committed
		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);
Linus Torvalds's avatar
Linus Torvalds committed
	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++;
Linus Torvalds's avatar
Linus Torvalds committed
			dev_kfree_skb(skb);
			return;
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
	memcpy(skbp, header->addr1, 6); /* destination address */
Johannes Berg's avatar
Johannes Berg committed
	if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
Linus Torvalds's avatar
Linus Torvalds committed
		memcpy(&skbp[6], header->addr3, 6);
	else
		memcpy(&skbp[6], header->addr2, 6); /* source address */
Linus Torvalds's avatar
Linus Torvalds committed
	skb->protocol = eth_type_trans(skb, priv->dev);
	skb->ip_summed = CHECKSUM_NONE;
Linus Torvalds's avatar
Linus Torvalds committed
	netif_rx(skb);
	priv->dev->stats.rx_bytes += 12 + msdu_size;
	priv->dev->stats.rx_packets++;
Linus Torvalds's avatar
Linus Torvalds committed
}

/* 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. */
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
	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,
Johannes Berg's avatar
Johannes Berg committed
			 struct ieee80211_hdr *header,
			 u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no,
			 u8 frag_no, int more_frags)
Linus Torvalds's avatar
Linus Torvalds committed
{
	u8 mac4[6];
Linus Torvalds's avatar
Linus Torvalds committed
	u8 source[6];
	struct sk_buff *skb;

Johannes Berg's avatar
Johannes Berg committed
	if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
Linus Torvalds's avatar
Linus Torvalds committed
		memcpy(source, header->addr3, 6);
	else
		memcpy(source, header->addr2, 6);

Linus Torvalds's avatar
Linus Torvalds committed
	rx_packet_loc += 24; /* skip header */
Linus Torvalds's avatar
Linus Torvalds committed
	if (priv->do_rx_crc)
		msdu_size -= 4;

	if (frag_no == 0) { /* first fragment */
		atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, 6);
		msdu_size -= 6;
		rx_packet_loc += 6;

		if (priv->do_rx_crc)
Linus Torvalds's avatar
Linus Torvalds committed
			crc = crc32_le(crc, mac4, 6);
Linus Torvalds's avatar
Linus Torvalds committed
		priv->frag_seq = seq_no;
		priv->frag_no = 1;
		priv->frag_len = msdu_size;
		memcpy(priv->frag_source, source, 6);
Linus Torvalds's avatar
Linus Torvalds committed
		memcpy(&priv->rx_buf[6], source, 6);
		memcpy(priv->rx_buf, header->addr1, 6);