blob: b68436b23a6362ba1d6fef6acca39a0be9d746f4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*** -*- linux-c -*- **********************************************************
2
3 Driver for Atmel at76c502 at76c504 and at76c506 wireless cards.
4
John Daiker8830cb62009-03-08 22:18:35 -07005 Copyright 2000-2001 ATMEL Corporation.
6 Copyright 2003-2004 Simon Kelley.
Linus Torvalds1da177e2005-04-16 15:20:36 -07007
Carlo Perassi4d791aa2005-11-13 15:02:15 +03008 This code was developed from version 2.1.1 of the Atmel drivers,
9 released by Atmel corp. under the GPL in December 2002. It also
10 includes code from the Linux aironet drivers (C) Benjamin Reed,
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 and the Linux PCMCIA package, (C) David Hinds and the Linux wireless
12 extensions, (C) Jean Tourrilhes.
13
14 The firmware module for reading the MAC address of the card comes from
15 net.russotto.AtmelMACFW, written by Matthew T. Russotto and copyright
16 by him. net.russotto.AtmelMACFW is used under the GPL license version 2.
17 This file contains the module in binary form and, under the terms
18 of the GPL, in source form. The source is located at the end of the file.
19
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation; either version 2 of the License, or
23 (at your option) any later version.
24
25 This software is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
29
30 You should have received a copy of the GNU General Public License
Jeff Kirsher36769152013-12-06 03:32:13 -080031 along with Atmel wireless lan drivers; if not, see
32 <http://www.gnu.org/licenses/>.
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Carlo Perassi4d791aa2005-11-13 15:02:15 +030034 For all queries about this code, please contact the current author,
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 Simon Kelley <simon@thekelleys.org.uk> and not Atmel Corporation.
36
37 Credit is due to HP UK and Cambridge Online Systems Ltd for supplying
38 hardware used during development of this driver.
39
40******************************************************************************/
41
Alexey Dobriyana6b7a402011-06-06 10:43:46 +000042#include <linux/interrupt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#include <linux/ptrace.h>
46#include <linux/slab.h>
47#include <linux/string.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <linux/timer.h>
Harvey Harrison9a6ab762008-05-16 11:20:25 -070049#include <asm/byteorder.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070050#include <asm/io.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080051#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#include <linux/module.h>
53#include <linux/netdevice.h>
54#include <linux/etherdevice.h>
55#include <linux/skbuff.h>
56#include <linux/if_arp.h>
57#include <linux/ioport.h>
58#include <linux/fcntl.h>
59#include <linux/delay.h>
60#include <linux/wireless.h>
61#include <net/iw_handler.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#include <linux/crc32.h>
63#include <linux/proc_fs.h>
David Howells4c4df9b2013-04-10 16:50:58 +010064#include <linux/seq_file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070065#include <linux/device.h>
66#include <linux/moduleparam.h>
67#include <linux/firmware.h>
S.Çağlar Onur6e33e302008-02-14 17:36:49 +020068#include <linux/jiffies.h>
Zhao, Gang61e54872014-02-18 21:35:58 +080069#include <net/cfg80211.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#include "atmel.h"
71
72#define DRIVER_MAJOR 0
simon@thekelleys.org.ukb16a2282005-10-30 15:50:15 +000073#define DRIVER_MINOR 98
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
75MODULE_AUTHOR("Simon Kelley");
76MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards.");
77MODULE_LICENSE("GPL");
78MODULE_SUPPORTED_DEVICE("Atmel at76c50x wireless cards");
79
Carlo Perassi4d791aa2005-11-13 15:02:15 +030080/* The name of the firmware file to be loaded
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 over-rides any automatic selection */
82static char *firmware = NULL;
83module_param(firmware, charp, 0);
84
85/* table of firmware file names */
Carlo Perassi4d791aa2005-11-13 15:02:15 +030086static struct {
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 AtmelFWType fw_type;
88 const char *fw_file;
89 const char *fw_file_ext;
90} fw_table[] = {
John Daiker8830cb62009-03-08 22:18:35 -070091 { ATMEL_FW_TYPE_502, "atmel_at76c502", "bin" },
92 { ATMEL_FW_TYPE_502D, "atmel_at76c502d", "bin" },
93 { ATMEL_FW_TYPE_502E, "atmel_at76c502e", "bin" },
94 { ATMEL_FW_TYPE_502_3COM, "atmel_at76c502_3com", "bin" },
95 { ATMEL_FW_TYPE_504, "atmel_at76c504", "bin" },
96 { ATMEL_FW_TYPE_504_2958, "atmel_at76c504_2958", "bin" },
97 { ATMEL_FW_TYPE_504A_2958, "atmel_at76c504a_2958", "bin" },
98 { ATMEL_FW_TYPE_506, "atmel_at76c506", "bin" },
99 { ATMEL_FW_TYPE_NONE, NULL, NULL }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100};
Ben Hutchingsb98a0322009-11-07 21:58:05 +0000101MODULE_FIRMWARE("atmel_at76c502-wpa.bin");
102MODULE_FIRMWARE("atmel_at76c502.bin");
103MODULE_FIRMWARE("atmel_at76c502d-wpa.bin");
104MODULE_FIRMWARE("atmel_at76c502d.bin");
105MODULE_FIRMWARE("atmel_at76c502e-wpa.bin");
106MODULE_FIRMWARE("atmel_at76c502e.bin");
107MODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin");
108MODULE_FIRMWARE("atmel_at76c502_3com.bin");
109MODULE_FIRMWARE("atmel_at76c504-wpa.bin");
110MODULE_FIRMWARE("atmel_at76c504.bin");
111MODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin");
112MODULE_FIRMWARE("atmel_at76c504_2958.bin");
113MODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin");
114MODULE_FIRMWARE("atmel_at76c504a_2958.bin");
115MODULE_FIRMWARE("atmel_at76c506-wpa.bin");
116MODULE_FIRMWARE("atmel_at76c506.bin");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
118#define MAX_SSID_LENGTH 32
119#define MGMT_JIFFIES (256 * HZ / 100)
120
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300121#define MAX_BSS_ENTRIES 64
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122
123/* registers */
John Daiker8830cb62009-03-08 22:18:35 -0700124#define GCR 0x00 /* (SIR0) General Configuration Register */
125#define BSR 0x02 /* (SIR1) Bank Switching Select Register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126#define AR 0x04
127#define DR 0x08
John Daiker8830cb62009-03-08 22:18:35 -0700128#define MR1 0x12 /* Mirror Register 1 */
129#define MR2 0x14 /* Mirror Register 2 */
130#define MR3 0x16 /* Mirror Register 3 */
131#define MR4 0x18 /* Mirror Register 4 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
133#define GPR1 0x0c
134#define GPR2 0x0e
135#define GPR3 0x10
John Daiker8830cb62009-03-08 22:18:35 -0700136/*
137 * Constants for the GCR register.
138 */
139#define GCR_REMAP 0x0400 /* Remap internal SRAM to 0 */
140#define GCR_SWRES 0x0080 /* BIU reset (ARM and PAI are NOT reset) */
141#define GCR_CORES 0x0060 /* Core Reset (ARM and PAI are reset) */
142#define GCR_ENINT 0x0002 /* Enable Interrupts */
143#define GCR_ACKINT 0x0008 /* Acknowledge Interrupts */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
John Daiker8830cb62009-03-08 22:18:35 -0700145#define BSS_SRAM 0x0200 /* AMBA module selection --> SRAM */
146#define BSS_IRAM 0x0100 /* AMBA module selection --> IRAM */
147/*
148 *Constants for the MR registers.
149 */
150#define MAC_INIT_COMPLETE 0x0001 /* MAC init has been completed */
151#define MAC_BOOT_COMPLETE 0x0010 /* MAC boot has been completed */
152#define MAC_INIT_OK 0x0002 /* MAC boot has been completed */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154#define MIB_MAX_DATA_BYTES 212
155#define MIB_HEADER_SIZE 4 /* first four fields */
156
157struct get_set_mib {
John Daiker8830cb62009-03-08 22:18:35 -0700158 u8 type;
159 u8 size;
160 u8 index;
161 u8 reserved;
162 u8 data[MIB_MAX_DATA_BYTES];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163};
164
165struct rx_desc {
John Daiker8830cb62009-03-08 22:18:35 -0700166 u32 Next;
167 u16 MsduPos;
168 u16 MsduSize;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300169
John Daiker8830cb62009-03-08 22:18:35 -0700170 u8 State;
171 u8 Status;
172 u8 Rate;
173 u8 Rssi;
174 u8 LinkQuality;
175 u8 PreambleType;
176 u16 Duration;
177 u32 RxTime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178};
179
180#define RX_DESC_FLAG_VALID 0x80
181#define RX_DESC_FLAG_CONSUMED 0x40
182#define RX_DESC_FLAG_IDLE 0x00
183
184#define RX_STATUS_SUCCESS 0x00
185
186#define RX_DESC_MSDU_POS_OFFSET 4
187#define RX_DESC_MSDU_SIZE_OFFSET 6
188#define RX_DESC_FLAGS_OFFSET 8
189#define RX_DESC_STATUS_OFFSET 9
190#define RX_DESC_RSSI_OFFSET 11
191#define RX_DESC_LINK_QUALITY_OFFSET 12
192#define RX_DESC_PREAMBLE_TYPE_OFFSET 13
193#define RX_DESC_DURATION_OFFSET 14
194#define RX_DESC_RX_TIME_OFFSET 16
195
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196struct tx_desc {
197 u32 NextDescriptor;
198 u16 TxStartOfFrame;
199 u16 TxLength;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300200
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 u8 TxState;
202 u8 TxStatus;
203 u8 RetryCount;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300204
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 u8 TxRate;
206
207 u8 KeyIndex;
208 u8 ChiperType;
209 u8 ChipreLength;
John Daiker8830cb62009-03-08 22:18:35 -0700210 u8 Reserved1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211
212 u8 Reserved;
213 u8 PacketType;
214 u16 HostTxLength;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215};
216
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217#define TX_DESC_NEXT_OFFSET 0
218#define TX_DESC_POS_OFFSET 4
219#define TX_DESC_SIZE_OFFSET 6
220#define TX_DESC_FLAGS_OFFSET 8
221#define TX_DESC_STATUS_OFFSET 9
222#define TX_DESC_RETRY_OFFSET 10
223#define TX_DESC_RATE_OFFSET 11
224#define TX_DESC_KEY_INDEX_OFFSET 12
225#define TX_DESC_CIPHER_TYPE_OFFSET 13
226#define TX_DESC_CIPHER_LENGTH_OFFSET 14
227#define TX_DESC_PACKET_TYPE_OFFSET 17
228#define TX_DESC_HOST_LENGTH_OFFSET 18
229
John Daiker8830cb62009-03-08 22:18:35 -0700230/*
231 * Host-MAC interface
232 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
234#define TX_STATUS_SUCCESS 0x00
235
236#define TX_FIRM_OWN 0x80
237#define TX_DONE 0x40
238
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239#define TX_ERROR 0x01
240
241#define TX_PACKET_TYPE_DATA 0x01
242#define TX_PACKET_TYPE_MGMT 0x02
243
John Daiker8830cb62009-03-08 22:18:35 -0700244#define ISR_EMPTY 0x00 /* no bits set in ISR */
245#define ISR_TxCOMPLETE 0x01 /* packet transmitted */
246#define ISR_RxCOMPLETE 0x02 /* packet received */
247#define ISR_RxFRAMELOST 0x04 /* Rx Frame lost */
248#define ISR_FATAL_ERROR 0x08 /* Fatal error */
249#define ISR_COMMAND_COMPLETE 0x10 /* command completed */
250#define ISR_OUT_OF_RANGE 0x20 /* command completed */
251#define ISR_IBSS_MERGE 0x40 /* (4.1.2.30): IBSS merge */
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300252#define ISR_GENERIC_IRQ 0x80
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
254#define Local_Mib_Type 0x01
255#define Mac_Address_Mib_Type 0x02
256#define Mac_Mib_Type 0x03
257#define Statistics_Mib_Type 0x04
258#define Mac_Mgmt_Mib_Type 0x05
259#define Mac_Wep_Mib_Type 0x06
260#define Phy_Mib_Type 0x07
261#define Multi_Domain_MIB 0x08
262
263#define MAC_MGMT_MIB_CUR_BSSID_POS 14
264#define MAC_MIB_FRAG_THRESHOLD_POS 8
265#define MAC_MIB_RTS_THRESHOLD_POS 10
266#define MAC_MIB_SHORT_RETRY_POS 16
267#define MAC_MIB_LONG_RETRY_POS 17
268#define MAC_MIB_SHORT_RETRY_LIMIT_POS 16
269#define MAC_MGMT_MIB_BEACON_PER_POS 0
270#define MAC_MGMT_MIB_STATION_ID_POS 6
271#define MAC_MGMT_MIB_CUR_PRIVACY_POS 11
272#define MAC_MGMT_MIB_CUR_BSSID_POS 14
273#define MAC_MGMT_MIB_PS_MODE_POS 53
274#define MAC_MGMT_MIB_LISTEN_INTERVAL_POS 54
275#define MAC_MGMT_MIB_MULTI_DOMAIN_IMPLEMENTED 56
276#define MAC_MGMT_MIB_MULTI_DOMAIN_ENABLED 57
277#define PHY_MIB_CHANNEL_POS 14
278#define PHY_MIB_RATE_SET_POS 20
279#define PHY_MIB_REG_DOMAIN_POS 26
280#define LOCAL_MIB_AUTO_TX_RATE_POS 3
281#define LOCAL_MIB_SSID_SIZE 5
282#define LOCAL_MIB_TX_PROMISCUOUS_POS 6
283#define LOCAL_MIB_TX_MGMT_RATE_POS 7
284#define LOCAL_MIB_TX_CONTROL_RATE_POS 8
285#define LOCAL_MIB_PREAMBLE_TYPE 9
286#define MAC_ADDR_MIB_MAC_ADDR_POS 0
287
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288#define CMD_Set_MIB_Vars 0x01
289#define CMD_Get_MIB_Vars 0x02
290#define CMD_Scan 0x03
291#define CMD_Join 0x04
292#define CMD_Start 0x05
293#define CMD_EnableRadio 0x06
294#define CMD_DisableRadio 0x07
295#define CMD_SiteSurvey 0x0B
296
297#define CMD_STATUS_IDLE 0x00
298#define CMD_STATUS_COMPLETE 0x01
299#define CMD_STATUS_UNKNOWN 0x02
300#define CMD_STATUS_INVALID_PARAMETER 0x03
301#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04
302#define CMD_STATUS_TIME_OUT 0x07
303#define CMD_STATUS_IN_PROGRESS 0x08
304#define CMD_STATUS_REJECTED_RADIO_OFF 0x09
305#define CMD_STATUS_HOST_ERROR 0xFF
306#define CMD_STATUS_BUSY 0xFE
307
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308#define CMD_BLOCK_COMMAND_OFFSET 0
309#define CMD_BLOCK_STATUS_OFFSET 1
310#define CMD_BLOCK_PARAMETERS_OFFSET 4
311
312#define SCAN_OPTIONS_SITE_SURVEY 0x80
313
314#define MGMT_FRAME_BODY_OFFSET 24
315#define MAX_AUTHENTICATION_RETRIES 3
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300316#define MAX_ASSOCIATION_RETRIES 3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
318#define AUTHENTICATION_RESPONSE_TIME_OUT 1000
319
320#define MAX_WIRELESS_BODY 2316 /* mtu is 2312, CRC is 4 */
321#define LOOP_RETRY_LIMIT 500000
322
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300323#define ACTIVE_MODE 1
324#define PS_MODE 2
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
326#define MAX_ENCRYPTION_KEYS 4
327#define MAX_ENCRYPTION_KEY_SIZE 40
328
John Daiker8830cb62009-03-08 22:18:35 -0700329/*
330 * 802.11 related definitions
331 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
John Daiker8830cb62009-03-08 22:18:35 -0700333/*
334 * Regulatory Domains
335 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
John Daiker8830cb62009-03-08 22:18:35 -0700337#define REG_DOMAIN_FCC 0x10 /* Channels 1-11 USA */
338#define REG_DOMAIN_DOC 0x20 /* Channel 1-11 Canada */
339#define REG_DOMAIN_ETSI 0x30 /* Channel 1-13 Europe (ex Spain/France) */
340#define REG_DOMAIN_SPAIN 0x31 /* Channel 10-11 Spain */
341#define REG_DOMAIN_FRANCE 0x32 /* Channel 10-13 France */
342#define REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */
343#define REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan(MKK1) */
344#define REG_DOMAIN_ISRAEL 0x50 /* Channel 3-9 ISRAEL */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300346#define BSS_TYPE_AD_HOC 1
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347#define BSS_TYPE_INFRASTRUCTURE 2
348
349#define SCAN_TYPE_ACTIVE 0
350#define SCAN_TYPE_PASSIVE 1
351
352#define LONG_PREAMBLE 0
353#define SHORT_PREAMBLE 1
354#define AUTO_PREAMBLE 2
355
356#define DATA_FRAME_WS_HEADER_SIZE 30
357
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300358/* promiscuous mode control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359#define PROM_MODE_OFF 0x0
360#define PROM_MODE_UNKNOWN 0x1
361#define PROM_MODE_CRC_FAILED 0x2
362#define PROM_MODE_DUPLICATED 0x4
363#define PROM_MODE_MGMT 0x8
364#define PROM_MODE_CTRL 0x10
365#define PROM_MODE_BAD_PROTOCOL 0x20
366
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300367#define IFACE_INT_STATUS_OFFSET 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368#define IFACE_INT_MASK_OFFSET 1
369#define IFACE_LOCKOUT_HOST_OFFSET 2
370#define IFACE_LOCKOUT_MAC_OFFSET 3
371#define IFACE_FUNC_CTRL_OFFSET 28
372#define IFACE_MAC_STAT_OFFSET 30
373#define IFACE_GENERIC_INT_TYPE_OFFSET 32
374
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300375#define CIPHER_SUITE_NONE 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376#define CIPHER_SUITE_WEP_64 1
377#define CIPHER_SUITE_TKIP 2
378#define CIPHER_SUITE_AES 3
379#define CIPHER_SUITE_CCX 4
380#define CIPHER_SUITE_WEP_128 5
381
John Daiker8830cb62009-03-08 22:18:35 -0700382/*
383 * IFACE MACROS & definitions
384 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
John Daiker8830cb62009-03-08 22:18:35 -0700386/*
387 * FuncCtrl field:
388 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389#define FUNC_CTRL_TxENABLE 0x10
390#define FUNC_CTRL_RxENABLE 0x20
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300391#define FUNC_CTRL_INIT_COMPLETE 0x01
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
393/* A stub firmware image which reads the MAC address from NVRAM on the card.
394 For copyright information and source see the end of this file. */
395static u8 mac_reader[] = {
John Daiker8830cb62009-03-08 22:18:35 -0700396 0x06, 0x00, 0x00, 0xea, 0x04, 0x00, 0x00, 0xea, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xea,
397 0x01, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea,
398 0xd3, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x21, 0xe1, 0x0e, 0x04, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
399 0x81, 0x11, 0xa0, 0xe1, 0x00, 0x10, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x1c, 0x10, 0x90, 0xe5,
400 0x10, 0x10, 0xc1, 0xe3, 0x1c, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 0x08, 0x10, 0x80, 0xe5,
401 0x02, 0x03, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0xb0, 0x10, 0xc0, 0xe1, 0xb4, 0x10, 0xc0, 0xe1,
402 0xb8, 0x10, 0xc0, 0xe1, 0xbc, 0x10, 0xc0, 0xe1, 0x56, 0xdc, 0xa0, 0xe3, 0x21, 0x00, 0x00, 0xeb,
403 0x0a, 0x00, 0xa0, 0xe3, 0x1a, 0x00, 0x00, 0xeb, 0x10, 0x00, 0x00, 0xeb, 0x07, 0x00, 0x00, 0xeb,
404 0x02, 0x03, 0xa0, 0xe3, 0x02, 0x14, 0xa0, 0xe3, 0xb4, 0x10, 0xc0, 0xe1, 0x4c, 0x10, 0x9f, 0xe5,
405 0xbc, 0x10, 0xc0, 0xe1, 0x10, 0x10, 0xa0, 0xe3, 0xb8, 0x10, 0xc0, 0xe1, 0xfe, 0xff, 0xff, 0xea,
406 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe3, 0x02, 0x3c, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
407 0x28, 0x00, 0x9f, 0xe5, 0x37, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
408 0x00, 0x40, 0x2d, 0xe9, 0x12, 0x2e, 0xa0, 0xe3, 0x06, 0x30, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
409 0x02, 0x04, 0xa0, 0xe3, 0x2f, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1,
410 0x00, 0x02, 0x00, 0x02, 0x80, 0x01, 0x90, 0xe0, 0x01, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x50, 0xe2,
411 0xfc, 0xff, 0xff, 0xea, 0x1e, 0xff, 0x2f, 0xe1, 0x80, 0x10, 0xa0, 0xe3, 0xf3, 0x06, 0xa0, 0xe3,
412 0x00, 0x10, 0x80, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3,
413 0x04, 0x10, 0x80, 0xe5, 0x00, 0x10, 0x80, 0xe5, 0x0e, 0x34, 0xa0, 0xe3, 0x1c, 0x10, 0x93, 0xe5,
414 0x02, 0x1a, 0x81, 0xe3, 0x1c, 0x10, 0x83, 0xe5, 0x58, 0x11, 0x9f, 0xe5, 0x30, 0x10, 0x80, 0xe5,
415 0x54, 0x11, 0x9f, 0xe5, 0x34, 0x10, 0x80, 0xe5, 0x38, 0x10, 0x80, 0xe5, 0x3c, 0x10, 0x80, 0xe5,
416 0x10, 0x10, 0x90, 0xe5, 0x08, 0x00, 0x90, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0xf3, 0x16, 0xa0, 0xe3,
417 0x08, 0x00, 0x91, 0xe5, 0x05, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 0x10, 0x00, 0x91, 0xe5,
418 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0xff, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5,
419 0x10, 0x00, 0x91, 0xe5, 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5,
420 0x10, 0x00, 0x91, 0xe5, 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5,
421 0xff, 0x00, 0x00, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x30, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe1,
422 0x03, 0x40, 0xa0, 0xe1, 0xa2, 0x02, 0xa0, 0xe1, 0x08, 0x00, 0x00, 0xe2, 0x03, 0x00, 0x80, 0xe2,
423 0xd8, 0x10, 0x9f, 0xe5, 0x00, 0x00, 0xc1, 0xe5, 0x01, 0x20, 0xc1, 0xe5, 0xe2, 0xff, 0xff, 0xeb,
424 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x14, 0x00, 0xa0, 0xe3, 0xc4, 0xff, 0xff, 0xeb,
425 0x04, 0x20, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1, 0x02, 0x00, 0xa0, 0xe3, 0x01, 0x00, 0x00, 0xeb,
426 0x30, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x70, 0x40, 0x2d, 0xe9, 0xf3, 0x46, 0xa0, 0xe3,
427 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x08, 0x00, 0x00, 0x9a, 0x8c, 0x50, 0x9f, 0xe5,
428 0x03, 0x60, 0xd5, 0xe7, 0x0c, 0x60, 0x84, 0xe5, 0x10, 0x60, 0x94, 0xe5, 0x02, 0x00, 0x16, 0xe3,
429 0xfc, 0xff, 0xff, 0x0a, 0x01, 0x30, 0x83, 0xe2, 0x00, 0x00, 0x53, 0xe1, 0xf7, 0xff, 0xff, 0x3a,
430 0xff, 0x30, 0xa0, 0xe3, 0x0c, 0x30, 0x84, 0xe5, 0x08, 0x00, 0x94, 0xe5, 0x10, 0x00, 0x94, 0xe5,
431 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x94, 0xe5, 0x00, 0x00, 0xa0, 0xe3,
432 0x00, 0x00, 0x52, 0xe3, 0x0b, 0x00, 0x00, 0x9a, 0x10, 0x50, 0x94, 0xe5, 0x02, 0x00, 0x15, 0xe3,
433 0xfc, 0xff, 0xff, 0x0a, 0x0c, 0x30, 0x84, 0xe5, 0x10, 0x50, 0x94, 0xe5, 0x01, 0x00, 0x15, 0xe3,
434 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x50, 0x94, 0xe5, 0x01, 0x50, 0xc1, 0xe4, 0x01, 0x00, 0x80, 0xe2,
435 0x02, 0x00, 0x50, 0xe1, 0xf3, 0xff, 0xff, 0x3a, 0xc8, 0x00, 0xa0, 0xe3, 0x98, 0xff, 0xff, 0xeb,
436 0x70, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x0c, 0x00, 0x02, 0x01, 0x02, 0x00, 0x02,
437 0x00, 0x01, 0x00, 0x02
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438};
439
440struct atmel_private {
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300441 void *card; /* Bus dependent structure varies for PCcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 int (*present_callback)(void *); /* And callback which uses it */
443 char firmware_id[32];
444 AtmelFWType firmware_type;
445 u8 *firmware;
446 int firmware_length;
447 struct timer_list management_timer;
448 struct net_device *dev;
449 struct device *sys_dev;
450 struct iw_statistics wstats;
John Daiker8830cb62009-03-08 22:18:35 -0700451 spinlock_t irqlock, timerlock; /* spinlocks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300453 enum {
454 CARD_TYPE_PARALLEL_FLASH,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 CARD_TYPE_SPI_FLASH,
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300456 CARD_TYPE_EEPROM
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 } card_type;
458 int do_rx_crc; /* If we need to CRC incoming packets */
459 int probe_crc; /* set if we don't yet know */
460 int crc_ok_cnt, crc_ko_cnt; /* counters for probing */
461 u16 rx_desc_head;
462 u16 tx_desc_free, tx_desc_head, tx_desc_tail, tx_desc_previous;
463 u16 tx_free_mem, tx_buff_head, tx_buff_tail;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300464
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 u16 frag_seq, frag_len, frag_no;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300466 u8 frag_source[6];
467
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 u8 wep_is_on, default_key, exclude_unencrypted, encryption_level;
469 u8 group_cipher_suite, pairwise_cipher_suite;
470 u8 wep_keys[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE];
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300471 int wep_key_len[MAX_ENCRYPTION_KEYS];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 int use_wpa, radio_on_broken; /* firmware dependent stuff. */
473
474 u16 host_info_base;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300475 struct host_info_struct {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 /* NB this is matched to the hardware, don't change. */
477 u8 volatile int_status;
478 u8 volatile int_mask;
479 u8 volatile lockout_host;
480 u8 volatile lockout_mac;
481
482 u16 tx_buff_pos;
483 u16 tx_buff_size;
484 u16 tx_desc_pos;
485 u16 tx_desc_count;
486
487 u16 rx_buff_pos;
488 u16 rx_buff_size;
489 u16 rx_desc_pos;
490 u16 rx_desc_count;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300491
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 u16 build_version;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300493 u16 command_pos;
494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 u16 major_version;
496 u16 minor_version;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300497
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 u16 func_ctrl;
499 u16 mac_status;
500 u16 generic_IRQ_type;
501 u8 reserved[2];
502 } host_info;
503
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300504 enum {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 STATION_STATE_SCANNING,
506 STATION_STATE_JOINNING,
507 STATION_STATE_AUTHENTICATING,
508 STATION_STATE_ASSOCIATING,
509 STATION_STATE_READY,
510 STATION_STATE_REASSOCIATING,
511 STATION_STATE_DOWN,
512 STATION_STATE_MGMT_ERROR
513 } station_state;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300514
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 int operating_mode, power_mode;
Alexandre Belloni5c510562017-02-23 17:14:45 +0100516 unsigned long last_qual;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 int beacons_this_sec;
518 int channel;
519 int reg_domain, config_reg_domain;
520 int tx_rate;
521 int auto_tx_rate;
522 int rts_threshold;
523 int frag_threshold;
524 int long_retry, short_retry;
525 int preamble;
526 int default_beacon_period, beacon_period, listen_interval;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300527 int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt;
529 enum {
530 SITE_SURVEY_IDLE,
531 SITE_SURVEY_IN_PROGRESS,
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300532 SITE_SURVEY_COMPLETED
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 } site_survey_state;
S.Çağlar Onur6e33e302008-02-14 17:36:49 +0200534 unsigned long last_survey;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
536 int station_was_associated, station_is_associated;
537 int fast_scan;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300538
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 struct bss_info {
540 int channel;
541 int SSIDsize;
542 int RSSI;
543 int UsingWEP;
544 int preamble;
545 int beacon_period;
546 int BSStype;
547 u8 BSSID[6];
548 u8 SSID[MAX_SSID_LENGTH];
549 } BSSinfo[MAX_BSS_ENTRIES];
550 int BSS_list_entries, current_BSS;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300551 int connect_to_any_BSS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 int SSID_size, new_SSID_size;
553 u8 CurrentBSSID[6], BSSID[6];
554 u8 SSID[MAX_SSID_LENGTH], new_SSID[MAX_SSID_LENGTH];
555 u64 last_beacon_timestamp;
556 u8 rx_buf[MAX_WIRELESS_BODY];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557};
558
John Daiker8830cb62009-03-08 22:18:35 -0700559static u8 atmel_basic_rates[4] = {0x82, 0x84, 0x0b, 0x16};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
561static const struct {
562 int reg_domain;
563 int min, max;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300564 char *name;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565} channel_table[] = { { REG_DOMAIN_FCC, 1, 11, "USA" },
566 { REG_DOMAIN_DOC, 1, 11, "Canada" },
567 { REG_DOMAIN_ETSI, 1, 13, "Europe" },
568 { REG_DOMAIN_SPAIN, 10, 11, "Spain" },
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300569 { REG_DOMAIN_FRANCE, 10, 13, "France" },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 { REG_DOMAIN_MKK, 14, 14, "MKK" },
571 { REG_DOMAIN_MKK1, 1, 14, "MKK1" },
572 { REG_DOMAIN_ISRAEL, 3, 9, "Israel"} };
573
574static void build_wpa_mib(struct atmel_private *priv);
575static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300576static void atmel_copy_to_card(struct net_device *dev, u16 dest,
David Woodhouse2f26e8a2008-05-24 00:09:29 +0100577 const unsigned char *src, u16 len);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300578static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest,
579 u16 src, u16 len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580static void atmel_set_gcr(struct net_device *dev, u16 mask);
581static void atmel_clear_gcr(struct net_device *dev, u16 mask);
582static int atmel_lock_mac(struct atmel_private *priv);
583static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data);
584static void atmel_command_irq(struct atmel_private *priv);
585static int atmel_validate_channel(struct atmel_private *priv, int channel);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300586static void atmel_management_frame(struct atmel_private *priv,
Johannes Berg2c7060022008-10-30 22:09:54 +0100587 struct ieee80211_hdr *header,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 u16 frame_len, u8 rssi);
589static void atmel_management_timer(u_long a);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300590static void atmel_send_command(struct atmel_private *priv, int command,
591 void *cmd, int cmd_size);
592static int atmel_send_command_wait(struct atmel_private *priv, int command,
593 void *cmd, int cmd_size);
594static void atmel_transmit_management_frame(struct atmel_private *priv,
Johannes Berg2c7060022008-10-30 22:09:54 +0100595 struct ieee80211_hdr *header,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 u8 *body, int body_len);
597
598static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300599static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index,
600 u8 data);
601static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index,
602 u16 data);
603static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index,
604 u8 *data, int data_len);
605static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index,
606 u8 *data, int data_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607static void atmel_scan(struct atmel_private *priv, int specific_ssid);
608static void atmel_join_bss(struct atmel_private *priv, int bss_index);
609static void atmel_smooth_qual(struct atmel_private *priv);
610static void atmel_writeAR(struct net_device *dev, u16 data);
611static int probe_atmel_card(struct net_device *dev);
Dmitry Torokhov5c877fe2006-10-08 00:14:30 -0400612static int reset_atmel_card(struct net_device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613static void atmel_enter_state(struct atmel_private *priv, int new_state);
614int atmel_open (struct net_device *dev);
615
616static inline u16 atmel_hi(struct atmel_private *priv, u16 offset)
617{
618 return priv->host_info_base + offset;
619}
620
621static inline u16 atmel_co(struct atmel_private *priv, u16 offset)
622{
623 return priv->host_info.command_pos + offset;
624}
625
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300626static inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627{
628 return priv->host_info.rx_desc_pos + (sizeof(struct rx_desc) * desc) + offset;
629}
630
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300631static inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632{
633 return priv->host_info.tx_desc_pos + (sizeof(struct tx_desc) * desc) + offset;
634}
635
636static inline u8 atmel_read8(struct net_device *dev, u16 offset)
637{
638 return inb(dev->base_addr + offset);
639}
640
641static inline void atmel_write8(struct net_device *dev, u16 offset, u8 data)
642{
643 outb(data, dev->base_addr + offset);
644}
645
646static inline u16 atmel_read16(struct net_device *dev, u16 offset)
647{
648 return inw(dev->base_addr + offset);
649}
650
651static inline void atmel_write16(struct net_device *dev, u16 offset, u16 data)
652{
653 outw(data, dev->base_addr + offset);
654}
655
656static inline u8 atmel_rmem8(struct atmel_private *priv, u16 pos)
657{
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300658 atmel_writeAR(priv->dev, pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 return atmel_read8(priv->dev, DR);
660}
661
662static inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data)
663{
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300664 atmel_writeAR(priv->dev, pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 atmel_write8(priv->dev, DR, data);
666}
667
668static inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos)
669{
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300670 atmel_writeAR(priv->dev, pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 return atmel_read16(priv->dev, DR);
672}
673
674static inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data)
675{
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300676 atmel_writeAR(priv->dev, pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 atmel_write16(priv->dev, DR, data);
678}
679
680static const struct iw_handler_def atmel_handler_def;
681
682static void tx_done_irq(struct atmel_private *priv)
683{
684 int i;
685
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300686 for (i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE &&
688 i < priv->host_info.tx_desc_count;
689 i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head));
691 u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head));
692 u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head));
693
694 atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head), 0);
695
696 priv->tx_free_mem += msdu_size;
697 priv->tx_desc_free++;
698
699 if (priv->tx_buff_head + msdu_size > (priv->host_info.tx_buff_pos + priv->host_info.tx_buff_size))
700 priv->tx_buff_head = 0;
701 else
702 priv->tx_buff_head += msdu_size;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300703
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1))
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300705 priv->tx_desc_head++ ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 else
707 priv->tx_desc_head = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300708
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 if (type == TX_PACKET_TYPE_DATA) {
710 if (status == TX_STATUS_SUCCESS)
Paulius Zaleckas736bc922008-05-06 23:50:30 +0300711 priv->dev->stats.tx_packets++;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300712 else
Paulius Zaleckas736bc922008-05-06 23:50:30 +0300713 priv->dev->stats.tx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 netif_wake_queue(priv->dev);
715 }
716 }
717}
718
719static u16 find_tx_buff(struct atmel_private *priv, u16 len)
720{
721 u16 bottom_free = priv->host_info.tx_buff_size - priv->tx_buff_tail;
722
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300723 if (priv->tx_desc_free == 3 || priv->tx_free_mem < len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 return 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300725
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 if (bottom_free >= len)
727 return priv->host_info.tx_buff_pos + priv->tx_buff_tail;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300728
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 if (priv->tx_free_mem - bottom_free >= len) {
730 priv->tx_buff_tail = 0;
731 return priv->host_info.tx_buff_pos;
732 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300733
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 return 0;
735}
736
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300737static void tx_update_descriptor(struct atmel_private *priv, int is_bcast,
738 u16 len, u16 buff, u8 type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739{
740 atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, priv->tx_desc_tail), buff);
741 atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_tail), len);
742 if (!priv->use_wpa)
743 atmel_wmem16(priv, atmel_tx(priv, TX_DESC_HOST_LENGTH_OFFSET, priv->tx_desc_tail), len);
744 atmel_wmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_tail), type);
745 atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RATE_OFFSET, priv->tx_desc_tail), priv->tx_rate);
746 atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RETRY_OFFSET, priv->tx_desc_tail), 0);
747 if (priv->use_wpa) {
748 int cipher_type, cipher_length;
749 if (is_bcast) {
750 cipher_type = priv->group_cipher_suite;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300751 if (cipher_type == CIPHER_SUITE_WEP_64 ||
752 cipher_type == CIPHER_SUITE_WEP_128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 cipher_length = 8;
754 else if (cipher_type == CIPHER_SUITE_TKIP)
755 cipher_length = 12;
756 else if (priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_64 ||
757 priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_128) {
758 cipher_type = priv->pairwise_cipher_suite;
759 cipher_length = 8;
760 } else {
761 cipher_type = CIPHER_SUITE_NONE;
762 cipher_length = 0;
763 }
764 } else {
765 cipher_type = priv->pairwise_cipher_suite;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300766 if (cipher_type == CIPHER_SUITE_WEP_64 ||
767 cipher_type == CIPHER_SUITE_WEP_128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 cipher_length = 8;
769 else if (cipher_type == CIPHER_SUITE_TKIP)
770 cipher_length = 12;
771 else if (priv->group_cipher_suite == CIPHER_SUITE_WEP_64 ||
772 priv->group_cipher_suite == CIPHER_SUITE_WEP_128) {
773 cipher_type = priv->group_cipher_suite;
774 cipher_length = 8;
775 } else {
776 cipher_type = CIPHER_SUITE_NONE;
777 cipher_length = 0;
778 }
779 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300780
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail),
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300782 cipher_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_LENGTH_OFFSET, priv->tx_desc_tail),
784 cipher_length);
785 }
786 atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_tail), 0x80000000L);
787 atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_tail), TX_FIRM_OWN);
788 if (priv->tx_desc_previous != priv->tx_desc_tail)
789 atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_previous), 0);
790 priv->tx_desc_previous = priv->tx_desc_tail;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300791 if (priv->tx_desc_tail < (priv->host_info.tx_desc_count - 1))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 priv->tx_desc_tail++;
793 else
794 priv->tx_desc_tail = 0;
795 priv->tx_desc_free--;
796 priv->tx_free_mem -= len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797}
798
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +0000799static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800{
Dmitry Torokhov00a5ebf2006-10-08 00:14:28 -0400801 static const u8 SNAP_RFC1024[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 struct atmel_private *priv = netdev_priv(dev);
Johannes Berg2c7060022008-10-30 22:09:54 +0100803 struct ieee80211_hdr header;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 unsigned long flags;
805 u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300806
807 if (priv->card && priv->present_callback &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 !(*priv->present_callback)(priv->card)) {
Paulius Zaleckas736bc922008-05-06 23:50:30 +0300809 dev->stats.tx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 dev_kfree_skb(skb);
Patrick McHardy6ed10652009-06-23 06:03:08 +0000811 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300813
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 if (priv->station_state != STATION_STATE_READY) {
Paulius Zaleckas736bc922008-05-06 23:50:30 +0300815 dev->stats.tx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 dev_kfree_skb(skb);
Patrick McHardy6ed10652009-06-23 06:03:08 +0000817 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300819
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 /* first ensure the timer func cannot run */
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300821 spin_lock_bh(&priv->timerlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 /* then stop the hardware ISR */
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300823 spin_lock_irqsave(&priv->irqlock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 /* nb doing the above in the opposite order will deadlock */
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300825
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 /* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300827 12 first bytes (containing DA/SA) and put them in the appropriate
828 fields of the Wireless Header. Thus the packet length is then the
829 initial + 18 (+30-12) */
830
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 if (!(buff = find_tx_buff(priv, len + 18))) {
Paulius Zaleckas736bc922008-05-06 23:50:30 +0300832 dev->stats.tx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 spin_unlock_irqrestore(&priv->irqlock, flags);
834 spin_unlock_bh(&priv->timerlock);
835 netif_stop_queue(dev);
Patrick McHardy5b548142009-06-12 06:22:29 +0000836 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300838
Jeff Garzikb4538722005-05-12 22:48:20 -0400839 frame_ctl = IEEE80211_FTYPE_DATA;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 header.duration_id = 0;
Johannes Berg2c7060022008-10-30 22:09:54 +0100841 header.seq_ctrl = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 if (priv->wep_is_on)
Jiri Bencf13baae2005-08-25 20:11:46 -0400843 frame_ctl |= IEEE80211_FCTL_PROTECTED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 if (priv->operating_mode == IW_MODE_ADHOC) {
Joe Perchesd458cdf2013-10-01 19:04:40 -0700845 skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN);
846 memcpy(&header.addr2, dev->dev_addr, ETH_ALEN);
847 memcpy(&header.addr3, priv->BSSID, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 } else {
Jeff Garzikb4538722005-05-12 22:48:20 -0400849 frame_ctl |= IEEE80211_FCTL_TODS;
Joe Perchesd458cdf2013-10-01 19:04:40 -0700850 memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN);
851 memcpy(&header.addr2, dev->dev_addr, ETH_ALEN);
852 skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300854
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 if (priv->use_wpa)
Joe Perchesd458cdf2013-10-01 19:04:40 -0700856 memcpy(&header.addr4, SNAP_RFC1024, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
Johannes Berg2c7060022008-10-30 22:09:54 +0100858 header.frame_control = cpu_to_le16(frame_ctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 /* Copy the wireless header into the card */
860 atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE);
861 /* Copy the packet sans its 802.3 header addresses which have been replaced */
862 atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12);
863 priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300864
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 /* low bit of first byte of destination tells us if broadcast */
866 tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA);
Paulius Zaleckas736bc922008-05-06 23:50:30 +0300867 dev->stats.tx_bytes += len;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300868
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 spin_unlock_irqrestore(&priv->irqlock, flags);
870 spin_unlock_bh(&priv->timerlock);
871 dev_kfree_skb(skb);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300872
Patrick McHardy6ed10652009-06-23 06:03:08 +0000873 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874}
875
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300876static void atmel_transmit_management_frame(struct atmel_private *priv,
Johannes Berg2c7060022008-10-30 22:09:54 +0100877 struct ieee80211_hdr *header,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 u8 *body, int body_len)
879{
880 u16 buff;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300881 int len = MGMT_FRAME_BODY_OFFSET + body_len;
882
883 if (!(buff = find_tx_buff(priv, len)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 return;
885
886 atmel_copy_to_card(priv->dev, buff, (u8 *)header, MGMT_FRAME_BODY_OFFSET);
887 atmel_copy_to_card(priv->dev, buff + MGMT_FRAME_BODY_OFFSET, body, body_len);
888 priv->tx_buff_tail += len;
889 tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT);
890}
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300891
892static void fast_rx_path(struct atmel_private *priv,
Johannes Berg2c7060022008-10-30 22:09:54 +0100893 struct ieee80211_hdr *header,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 u16 msdu_size, u16 rx_packet_loc, u32 crc)
895{
896 /* fast path: unfragmented packet copy directly into skbuf */
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300897 u8 mac4[6];
898 struct sk_buff *skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 unsigned char *skbp;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300900
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 /* get the final, mac 4 header field, this tells us encapsulation */
902 atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6);
903 msdu_size -= 6;
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300904
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 if (priv->do_rx_crc) {
906 crc = crc32_le(crc, mac4, 6);
907 msdu_size -= 4;
908 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300909
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 if (!(skb = dev_alloc_skb(msdu_size + 14))) {
Paulius Zaleckas736bc922008-05-06 23:50:30 +0300911 priv->dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 return;
913 }
914
915 skb_reserve(skb, 2);
916 skbp = skb_put(skb, msdu_size + 12);
917 atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300918
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 if (priv->do_rx_crc) {
920 u32 netcrc;
921 crc = crc32_le(crc, skbp + 12, msdu_size);
922 atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + 30 + msdu_size, 4);
923 if ((crc ^ 0xffffffff) != netcrc) {
Paulius Zaleckas736bc922008-05-06 23:50:30 +0300924 priv->dev->stats.rx_crc_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 dev_kfree_skb(skb);
926 return;
927 }
928 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300929
Joe Perchesd458cdf2013-10-01 19:04:40 -0700930 memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */
Johannes Berg2c7060022008-10-30 22:09:54 +0100931 if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
Joe Perchesd458cdf2013-10-01 19:04:40 -0700932 memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 else
Joe Perchesd458cdf2013-10-01 19:04:40 -0700934 memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300935
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 skb->protocol = eth_type_trans(skb, priv->dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300937 skb->ip_summed = CHECKSUM_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 netif_rx(skb);
Paulius Zaleckas736bc922008-05-06 23:50:30 +0300939 priv->dev->stats.rx_bytes += 12 + msdu_size;
940 priv->dev->stats.rx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941}
942
943/* Test to see if the packet in card memory at packet_loc has a valid CRC
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300944 It doesn't matter that this is slow: it is only used to proble the first few
945 packets. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size)
947{
948 int i = msdu_size - 4;
949 u32 netcrc, crc = 0xffffffff;
950
951 if (msdu_size < 4)
952 return 0;
953
954 atmel_copy_to_host(priv->dev, (void *)&netcrc, packet_loc + i, 4);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300955
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 atmel_writeAR(priv->dev, packet_loc);
957 while (i--) {
958 u8 octet = atmel_read8(priv->dev, DR);
959 crc = crc32_le(crc, &octet, 1);
960 }
961
962 return (crc ^ 0xffffffff) == netcrc;
963}
964
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300965static void frag_rx_path(struct atmel_private *priv,
Johannes Berg2c7060022008-10-30 22:09:54 +0100966 struct ieee80211_hdr *header,
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300967 u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no,
968 u8 frag_no, int more_frags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969{
Joe Perchesd458cdf2013-10-01 19:04:40 -0700970 u8 mac4[ETH_ALEN];
971 u8 source[ETH_ALEN];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 struct sk_buff *skb;
973
Johannes Berg2c7060022008-10-30 22:09:54 +0100974 if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
Joe Perchesd458cdf2013-10-01 19:04:40 -0700975 memcpy(source, header->addr3, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 else
Joe Perchesd458cdf2013-10-01 19:04:40 -0700977 memcpy(source, header->addr2, ETH_ALEN);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300978
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 rx_packet_loc += 24; /* skip header */
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300980
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 if (priv->do_rx_crc)
982 msdu_size -= 4;
983
984 if (frag_no == 0) { /* first fragment */
Joe Perchesd458cdf2013-10-01 19:04:40 -0700985 atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN);
986 msdu_size -= ETH_ALEN;
987 rx_packet_loc += ETH_ALEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300989 if (priv->do_rx_crc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 crc = crc32_le(crc, mac4, 6);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300991
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 priv->frag_seq = seq_no;
993 priv->frag_no = 1;
994 priv->frag_len = msdu_size;
Joe Perchesd458cdf2013-10-01 19:04:40 -0700995 memcpy(priv->frag_source, source, ETH_ALEN);
996 memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN);
997 memcpy(priv->rx_buf, header->addr1, ETH_ALEN);
Carlo Perassi4d791aa2005-11-13 15:02:15 +0300998
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size);
1000
1001 if (priv->do_rx_crc) {
1002 u32 netcrc;
1003 crc = crc32_le(crc, &priv->rx_buf[12], msdu_size);
1004 atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4);
1005 if ((crc ^ 0xffffffff) != netcrc) {
Paulius Zaleckas736bc922008-05-06 23:50:30 +03001006 priv->dev->stats.rx_crc_errors++;
Joe Perches93803b32015-03-02 19:54:49 -08001007 eth_broadcast_addr(priv->frag_source);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 }
1009 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001010
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 } else if (priv->frag_no == frag_no &&
1012 priv->frag_seq == seq_no &&
Joe Perchesd458cdf2013-10-01 19:04:40 -07001013 memcmp(priv->frag_source, source, ETH_ALEN) == 0) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001014
1015 atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len],
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 rx_packet_loc, msdu_size);
1017 if (priv->do_rx_crc) {
1018 u32 netcrc;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001019 crc = crc32_le(crc,
1020 &priv->rx_buf[12 + priv->frag_len],
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 msdu_size);
1022 atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4);
1023 if ((crc ^ 0xffffffff) != netcrc) {
Paulius Zaleckas736bc922008-05-06 23:50:30 +03001024 priv->dev->stats.rx_crc_errors++;
Joe Perches93803b32015-03-02 19:54:49 -08001025 eth_broadcast_addr(priv->frag_source);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 more_frags = 1; /* don't send broken assembly */
1027 }
1028 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001029
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 priv->frag_len += msdu_size;
1031 priv->frag_no++;
1032
1033 if (!more_frags) { /* last one */
Joe Perches93803b32015-03-02 19:54:49 -08001034 eth_broadcast_addr(priv->frag_source);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 if (!(skb = dev_alloc_skb(priv->frag_len + 14))) {
Paulius Zaleckas736bc922008-05-06 23:50:30 +03001036 priv->dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 } else {
1038 skb_reserve(skb, 2);
Johannes Berg59ae1d12017-06-16 14:29:20 +02001039 skb_put_data(skb, priv->rx_buf,
1040 priv->frag_len + 12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 skb->protocol = eth_type_trans(skb, priv->dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001042 skb->ip_summed = CHECKSUM_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 netif_rx(skb);
Paulius Zaleckas736bc922008-05-06 23:50:30 +03001044 priv->dev->stats.rx_bytes += priv->frag_len + 12;
1045 priv->dev->stats.rx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 }
1047 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 } else
1049 priv->wstats.discard.fragment++;
1050}
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001051
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052static void rx_done_irq(struct atmel_private *priv)
1053{
1054 int i;
Johannes Berg2c7060022008-10-30 22:09:54 +01001055 struct ieee80211_hdr header;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001056
1057 for (i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID &&
1059 i < priv->host_info.rx_desc_count;
1060 i++) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001061
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 u16 msdu_size, rx_packet_loc, frame_ctl, seq_control;
1063 u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head));
1064 u32 crc = 0xffffffff;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001065
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 if (status != RX_STATUS_SUCCESS) {
1067 if (status == 0xc1) /* determined by experiment */
1068 priv->wstats.discard.nwid++;
1069 else
Paulius Zaleckas736bc922008-05-06 23:50:30 +03001070 priv->dev->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 goto next;
1072 }
1073
1074 msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head));
1075 rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head));
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001076
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 if (msdu_size < 30) {
Paulius Zaleckas736bc922008-05-06 23:50:30 +03001078 priv->dev->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 goto next;
1080 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001081
Johannes Berg2c7060022008-10-30 22:09:54 +01001082 /* Get header as far as end of seq_ctrl */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24);
Johannes Berg2c7060022008-10-30 22:09:54 +01001084 frame_ctl = le16_to_cpu(header.frame_control);
1085 seq_control = le16_to_cpu(header.seq_ctrl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001087 /* probe for CRC use here if needed once five packets have
1088 arrived with the same crc status, we assume we know what's
1089 happening and stop probing */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 if (priv->probe_crc) {
Jiri Bencf13baae2005-08-25 20:11:46 -04001091 if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size);
1093 } else {
1094 priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24);
1095 }
1096 if (priv->do_rx_crc) {
1097 if (priv->crc_ok_cnt++ > 5)
1098 priv->probe_crc = 0;
1099 } else {
1100 if (priv->crc_ko_cnt++ > 5)
1101 priv->probe_crc = 0;
1102 }
1103 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001104
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 /* don't CRC header when WEP in use */
Jiri Bencf13baae2005-08-25 20:11:46 -04001106 if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 crc = crc32_le(0xffffffff, (unsigned char *)&header, 24);
1108 }
1109 msdu_size -= 24; /* header */
1110
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001111 if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
Jeff Garzikb4538722005-05-12 22:48:20 -04001112 int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS;
1113 u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG;
1114 u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001115
1116 if (!more_fragments && packet_fragment_no == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc);
1118 } else {
1119 frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc,
1120 packet_sequence_no, packet_fragment_no, more_fragments);
1121 }
1122 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001123
Jeff Garzikb4538722005-05-12 22:48:20 -04001124 if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 /* copy rest of packet into buffer */
1126 atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001127
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 /* we use the same buffer for frag reassembly and control packets */
Joe Perches93803b32015-03-02 19:54:49 -08001129 eth_broadcast_addr(priv->frag_source);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001130
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 if (priv->do_rx_crc) {
1132 /* last 4 octets is crc */
1133 msdu_size -= 4;
1134 crc = crc32_le(crc, (unsigned char *)&priv->rx_buf, msdu_size);
1135 if ((crc ^ 0xffffffff) != (*((u32 *)&priv->rx_buf[msdu_size]))) {
Paulius Zaleckas736bc922008-05-06 23:50:30 +03001136 priv->dev->stats.rx_crc_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 goto next;
1138 }
1139 }
1140
1141 atmel_management_frame(priv, &header, msdu_size,
1142 atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head)));
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001143 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001145next:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 /* release descriptor */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001147 atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED);
1148
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1))
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001150 priv->rx_desc_head++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 else
1152 priv->rx_desc_head = 0;
1153 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001154}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155
David Howells7d12e782006-10-05 14:55:46 +01001156static irqreturn_t service_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157{
1158 struct net_device *dev = (struct net_device *) dev_id;
1159 struct atmel_private *priv = netdev_priv(dev);
1160 u8 isr;
1161 int i = -1;
Joe Perches3370a892010-11-20 18:38:55 -08001162 static const u8 irq_order[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 ISR_OUT_OF_RANGE,
1164 ISR_RxCOMPLETE,
1165 ISR_TxCOMPLETE,
1166 ISR_RxFRAMELOST,
1167 ISR_FATAL_ERROR,
1168 ISR_COMMAND_COMPLETE,
1169 ISR_IBSS_MERGE,
1170 ISR_GENERIC_IRQ
1171 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001173 if (priv->card && priv->present_callback &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 !(*priv->present_callback)(priv->card))
1175 return IRQ_HANDLED;
1176
1177 /* In this state upper-level code assumes it can mess with
1178 the card unhampered by interrupts which may change register state.
1179 Note that even though the card shouldn't generate interrupts
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001180 the inturrupt line may be shared. This allows card setup
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 to go on without disabling interrupts for a long time. */
1182 if (priv->station_state == STATION_STATE_DOWN)
1183 return IRQ_NONE;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001184
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */
1186
1187 while (1) {
1188 if (!atmel_lock_mac(priv)) {
1189 /* failed to contact card */
1190 printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name);
1191 return IRQ_HANDLED;
1192 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001193
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET));
1195 atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001196
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 if (!isr) {
1198 atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */
1199 return i == -1 ? IRQ_NONE : IRQ_HANDLED;
1200 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001201
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001203
Dmitry Torokhovb4341132006-10-08 00:14:29 -04001204 for (i = 0; i < ARRAY_SIZE(irq_order); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 if (isr & irq_order[i])
1206 break;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001207
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 if (!atmel_lock_mac(priv)) {
1209 /* failed to contact card */
1210 printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name);
1211 return IRQ_HANDLED;
1212 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001213
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET));
1215 isr ^= irq_order[i];
1216 atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr);
1217 atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001218
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 switch (irq_order[i]) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001220
1221 case ISR_OUT_OF_RANGE:
1222 if (priv->operating_mode == IW_MODE_INFRA &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 priv->station_state == STATION_STATE_READY) {
1224 priv->station_is_associated = 0;
1225 atmel_scan(priv, 1);
1226 }
1227 break;
1228
1229 case ISR_RxFRAMELOST:
1230 priv->wstats.discard.misc++;
1231 /* fall through */
1232 case ISR_RxCOMPLETE:
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001233 rx_done_irq(priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 break;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001235
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 case ISR_TxCOMPLETE:
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001237 tx_done_irq(priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 break;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001239
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 case ISR_FATAL_ERROR:
1241 printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name);
1242 atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
1243 break;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001244
1245 case ISR_COMMAND_COMPLETE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 atmel_command_irq(priv);
1247 break;
1248
1249 case ISR_IBSS_MERGE:
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001250 atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 priv->CurrentBSSID, 6);
1252 /* The WPA stuff cares about the current AP address */
1253 if (priv->use_wpa)
1254 build_wpa_mib(priv);
1255 break;
1256 case ISR_GENERIC_IRQ:
1257 printk(KERN_INFO "%s: Generic_irq received.\n", dev->name);
1258 break;
1259 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001260 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261}
1262
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001263static struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264{
1265 struct atmel_private *priv = netdev_priv(dev);
1266
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001267 /* update the link quality here in case we are seeing no beacons
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 at all to drive the process */
1269 atmel_smooth_qual(priv);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001270
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 priv->wstats.status = priv->station_state;
1272
1273 if (priv->operating_mode == IW_MODE_INFRA) {
1274 if (priv->station_state != STATION_STATE_READY) {
1275 priv->wstats.qual.qual = 0;
1276 priv->wstats.qual.level = 0;
1277 priv->wstats.qual.updated = (IW_QUAL_QUAL_INVALID
1278 | IW_QUAL_LEVEL_INVALID);
1279 }
1280 priv->wstats.qual.noise = 0;
1281 priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID;
1282 } else {
1283 /* Quality levels cannot be determined in ad-hoc mode,
1284 because we can 'hear' more that one remote station. */
1285 priv->wstats.qual.qual = 0;
1286 priv->wstats.qual.level = 0;
1287 priv->wstats.qual.noise = 0;
1288 priv->wstats.qual.updated = IW_QUAL_QUAL_INVALID
1289 | IW_QUAL_LEVEL_INVALID
1290 | IW_QUAL_NOISE_INVALID;
1291 priv->wstats.miss.beacon = 0;
1292 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001293
1294 return &priv->wstats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295}
1296
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297static int atmel_set_mac_address(struct net_device *dev, void *p)
1298{
1299 struct sockaddr *addr = p;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001300
John Daiker8830cb62009-03-08 22:18:35 -07001301 memcpy (dev->dev_addr, addr->sa_data, dev->addr_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 return atmel_open(dev);
1303}
1304
1305EXPORT_SYMBOL(atmel_open);
1306
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001307int atmel_open(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308{
1309 struct atmel_private *priv = netdev_priv(dev);
Dan Williams3c34a5d2008-08-18 15:40:02 -04001310 int i, channel, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311
1312 /* any scheduled timer is no longer needed and might screw things up.. */
1313 del_timer_sync(&priv->management_timer);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001314
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 /* Interrupts will not touch the card once in this state... */
1316 priv->station_state = STATION_STATE_DOWN;
1317
1318 if (priv->new_SSID_size) {
1319 memcpy(priv->SSID, priv->new_SSID, priv->new_SSID_size);
1320 priv->SSID_size = priv->new_SSID_size;
1321 priv->new_SSID_size = 0;
1322 }
1323 priv->BSS_list_entries = 0;
1324
1325 priv->AuthenticationRequestRetryCnt = 0;
1326 priv->AssociationRequestRetryCnt = 0;
1327 priv->ReAssociationRequestRetryCnt = 0;
1328 priv->CurrentAuthentTransactionSeqNum = 0x0001;
1329 priv->ExpectedAuthentTransactionSeqNum = 0x0002;
1330
1331 priv->site_survey_state = SITE_SURVEY_IDLE;
1332 priv->station_is_associated = 0;
1333
Dan Williams3c34a5d2008-08-18 15:40:02 -04001334 err = reset_atmel_card(dev);
1335 if (err)
1336 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337
1338 if (priv->config_reg_domain) {
1339 priv->reg_domain = priv->config_reg_domain;
1340 atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS, priv->reg_domain);
1341 } else {
1342 priv->reg_domain = atmel_get_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS);
Dmitry Torokhovb4341132006-10-08 00:14:29 -04001343 for (i = 0; i < ARRAY_SIZE(channel_table); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 if (priv->reg_domain == channel_table[i].reg_domain)
1345 break;
Dmitry Torokhovb4341132006-10-08 00:14:29 -04001346 if (i == ARRAY_SIZE(channel_table)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 priv->reg_domain = REG_DOMAIN_MKK1;
1348 printk(KERN_ALERT "%s: failed to get regulatory domain: assuming MKK1.\n", dev->name);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001349 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001351
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 if ((channel = atmel_validate_channel(priv, priv->channel)))
1353 priv->channel = channel;
1354
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001355 /* this moves station_state on.... */
1356 atmel_scan(priv, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
1358 atmel_set_gcr(priv->dev, GCR_ENINT); /* enable interrupts */
1359 return 0;
1360}
1361
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001362static int atmel_close(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363{
1364 struct atmel_private *priv = netdev_priv(dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001365
Dan Williams9a6301c2006-01-10 00:56:11 -05001366 /* Send event to userspace that we are disassociating */
1367 if (priv->station_state == STATION_STATE_READY) {
1368 union iwreq_data wrqu;
1369
1370 wrqu.data.length = 0;
1371 wrqu.data.flags = 0;
1372 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
Joe Perches93803b32015-03-02 19:54:49 -08001373 eth_zero_addr(wrqu.ap_addr.sa_data);
Dan Williams9a6301c2006-01-10 00:56:11 -05001374 wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
1375 }
1376
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 atmel_enter_state(priv, STATION_STATE_DOWN);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001378
1379 if (priv->bus_type == BUS_TYPE_PCCARD)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 atmel_write16(dev, GCR, 0x0060);
1381 atmel_write16(dev, GCR, 0x0040);
1382 return 0;
1383}
1384
1385static int atmel_validate_channel(struct atmel_private *priv, int channel)
1386{
1387 /* check that channel is OK, if so return zero,
1388 else return suitable default channel */
1389 int i;
1390
Dmitry Torokhovb4341132006-10-08 00:14:29 -04001391 for (i = 0; i < ARRAY_SIZE(channel_table); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 if (priv->reg_domain == channel_table[i].reg_domain) {
1393 if (channel >= channel_table[i].min &&
1394 channel <= channel_table[i].max)
1395 return 0;
1396 else
1397 return channel_table[i].min;
1398 }
1399 return 0;
1400}
1401
David Howells4c4df9b2013-04-10 16:50:58 +01001402static int atmel_proc_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403{
David Howells4c4df9b2013-04-10 16:50:58 +01001404 struct atmel_private *priv = m->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 char *s, *r, *c;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001407
David Howells4c4df9b2013-04-10 16:50:58 +01001408 seq_printf(m, "Driver version:\t\t%d.%d\n", DRIVER_MAJOR, DRIVER_MINOR);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001409
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 if (priv->station_state != STATION_STATE_DOWN) {
David Howells4c4df9b2013-04-10 16:50:58 +01001411 seq_printf(m,
1412 "Firmware version:\t%d.%d build %d\n"
1413 "Firmware location:\t",
1414 priv->host_info.major_version,
1415 priv->host_info.minor_version,
1416 priv->host_info.build_version);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001417
1418 if (priv->card_type != CARD_TYPE_EEPROM)
David Howells4c4df9b2013-04-10 16:50:58 +01001419 seq_puts(m, "on card\n");
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001420 else if (priv->firmware)
David Howells4c4df9b2013-04-10 16:50:58 +01001421 seq_printf(m, "%s loaded by host\n", priv->firmware_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 else
David Howells4c4df9b2013-04-10 16:50:58 +01001423 seq_printf(m, "%s loaded by hotplug\n", priv->firmware_id);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001424
1425 switch (priv->card_type) {
John Daiker8830cb62009-03-08 22:18:35 -07001426 case CARD_TYPE_PARALLEL_FLASH:
1427 c = "Parallel flash";
1428 break;
1429 case CARD_TYPE_SPI_FLASH:
1430 c = "SPI flash\n";
1431 break;
1432 case CARD_TYPE_EEPROM:
1433 c = "EEPROM";
1434 break;
1435 default:
1436 c = "<unknown>";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 }
1438
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 r = "<unknown>";
Dmitry Torokhovb4341132006-10-08 00:14:29 -04001440 for (i = 0; i < ARRAY_SIZE(channel_table); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 if (priv->reg_domain == channel_table[i].reg_domain)
1442 r = channel_table[i].name;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001443
David Howells4c4df9b2013-04-10 16:50:58 +01001444 seq_printf(m, "MAC memory type:\t%s\n", c);
1445 seq_printf(m, "Regulatory domain:\t%s\n", r);
1446 seq_printf(m, "Host CRC checking:\t%s\n",
1447 priv->do_rx_crc ? "On" : "Off");
1448 seq_printf(m, "WPA-capable firmware:\t%s\n",
1449 priv->use_wpa ? "Yes" : "No");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001451
John Daiker8830cb62009-03-08 22:18:35 -07001452 switch (priv->station_state) {
1453 case STATION_STATE_SCANNING:
1454 s = "Scanning";
1455 break;
1456 case STATION_STATE_JOINNING:
1457 s = "Joining";
1458 break;
1459 case STATION_STATE_AUTHENTICATING:
1460 s = "Authenticating";
1461 break;
1462 case STATION_STATE_ASSOCIATING:
1463 s = "Associating";
1464 break;
1465 case STATION_STATE_READY:
1466 s = "Ready";
1467 break;
1468 case STATION_STATE_REASSOCIATING:
1469 s = "Reassociating";
1470 break;
1471 case STATION_STATE_MGMT_ERROR:
1472 s = "Management error";
1473 break;
1474 case STATION_STATE_DOWN:
1475 s = "Down";
1476 break;
1477 default:
1478 s = "<unknown>";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001480
David Howells4c4df9b2013-04-10 16:50:58 +01001481 seq_printf(m, "Current state:\t\t%s\n", s);
1482 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483}
1484
David Howells4c4df9b2013-04-10 16:50:58 +01001485static int atmel_proc_open(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486{
David Howells4c4df9b2013-04-10 16:50:58 +01001487 return single_open(file, atmel_proc_show, PDE_DATA(inode));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488}
1489
David Howells4c4df9b2013-04-10 16:50:58 +01001490static const struct file_operations atmel_proc_fops = {
1491 .open = atmel_proc_open,
1492 .read = seq_read,
1493 .llseek = seq_lseek,
Al Virobc3041f2013-05-05 00:13:20 -04001494 .release = single_release,
David Howells4c4df9b2013-04-10 16:50:58 +01001495};
1496
Stephen Hemminger824f1df2009-03-20 19:36:27 +00001497static const struct net_device_ops atmel_netdev_ops = {
1498 .ndo_open = atmel_open,
1499 .ndo_stop = atmel_close,
Stephen Hemminger824f1df2009-03-20 19:36:27 +00001500 .ndo_set_mac_address = atmel_set_mac_address,
1501 .ndo_start_xmit = start_tx,
1502 .ndo_do_ioctl = atmel_ioctl,
Stephen Hemminger824f1df2009-03-20 19:36:27 +00001503 .ndo_validate_addr = eth_validate_addr,
1504};
1505
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001506struct net_device *init_atmel_card(unsigned short irq, unsigned long port,
1507 const AtmelFWType fw_type,
1508 struct device *sys_dev,
1509 int (*card_present)(void *), void *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510{
1511 struct net_device *dev;
1512 struct atmel_private *priv;
1513 int rc;
1514
1515 /* Create the network device object. */
John Daiker8830cb62009-03-08 22:18:35 -07001516 dev = alloc_etherdev(sizeof(*priv));
Joe Perches41de8d42012-01-29 13:47:52 +00001517 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 return NULL;
Joe Perches41de8d42012-01-29 13:47:52 +00001519
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 if (dev_alloc_name(dev, dev->name) < 0) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001521 printk(KERN_ERR "atmel: Couldn't get name!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 goto err_out_free;
1523 }
1524
1525 priv = netdev_priv(dev);
1526 priv->dev = dev;
1527 priv->sys_dev = sys_dev;
1528 priv->present_callback = card_present;
1529 priv->card = card;
1530 priv->firmware = NULL;
1531 priv->firmware_id[0] = '\0';
1532 priv->firmware_type = fw_type;
1533 if (firmware) /* module parameter */
1534 strcpy(priv->firmware_id, firmware);
1535 priv->bus_type = card_present ? BUS_TYPE_PCCARD : BUS_TYPE_PCI;
1536 priv->station_state = STATION_STATE_DOWN;
1537 priv->do_rx_crc = 0;
1538 /* For PCMCIA cards, some chips need CRC, some don't
1539 so we have to probe. */
1540 if (priv->bus_type == BUS_TYPE_PCCARD) {
1541 priv->probe_crc = 1;
1542 priv->crc_ok_cnt = priv->crc_ko_cnt = 0;
1543 } else
1544 priv->probe_crc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 priv->last_qual = jiffies;
1546 priv->last_beacon_timestamp = 0;
1547 memset(priv->frag_source, 0xff, sizeof(priv->frag_source));
Joe Perches93803b32015-03-02 19:54:49 -08001548 eth_zero_addr(priv->BSSID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */
1550 priv->station_was_associated = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001551
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 priv->last_survey = jiffies;
1553 priv->preamble = LONG_PREAMBLE;
1554 priv->operating_mode = IW_MODE_INFRA;
1555 priv->connect_to_any_BSS = 0;
1556 priv->config_reg_domain = 0;
1557 priv->reg_domain = 0;
1558 priv->tx_rate = 3;
1559 priv->auto_tx_rate = 1;
1560 priv->channel = 4;
1561 priv->power_mode = 0;
1562 priv->SSID[0] = '\0';
1563 priv->SSID_size = 0;
1564 priv->new_SSID_size = 0;
1565 priv->frag_threshold = 2346;
1566 priv->rts_threshold = 2347;
1567 priv->short_retry = 7;
1568 priv->long_retry = 4;
1569
1570 priv->wep_is_on = 0;
1571 priv->default_key = 0;
1572 priv->encryption_level = 0;
1573 priv->exclude_unencrypted = 0;
1574 priv->group_cipher_suite = priv->pairwise_cipher_suite = CIPHER_SUITE_NONE;
1575 priv->use_wpa = 0;
1576 memset(priv->wep_keys, 0, sizeof(priv->wep_keys));
1577 memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len));
1578
1579 priv->default_beacon_period = priv->beacon_period = 100;
1580 priv->listen_interval = 1;
1581
1582 init_timer(&priv->management_timer);
1583 spin_lock_init(&priv->irqlock);
1584 spin_lock_init(&priv->timerlock);
1585 priv->management_timer.function = atmel_management_timer;
1586 priv->management_timer.data = (unsigned long) dev;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001587
Stephen Hemminger824f1df2009-03-20 19:36:27 +00001588 dev->netdev_ops = &atmel_netdev_ops;
1589 dev->wireless_handlers = &atmel_handler_def;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 dev->irq = irq;
1591 dev->base_addr = port;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001592
Jarod Wilson9c22b4a2016-10-20 13:55:18 -04001593 /* MTU range: 68 - 2312 */
1594 dev->min_mtu = 68;
1595 dev->max_mtu = MAX_WIRELESS_BODY - ETH_FCS_LEN;
1596
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597 SET_NETDEV_DEV(dev, sys_dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001598
Thomas Gleixner1fb9df52006-07-01 19:29:39 -07001599 if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001600 printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 goto err_out_free;
1602 }
1603
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001604 if (!request_region(dev->base_addr, 32,
simon@thekelleys.org.ukb16a2282005-10-30 15:50:15 +00001605 priv->bus_type == BUS_TYPE_PCCARD ? "atmel_cs" : "atmel_pci")) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606 goto err_out_irq;
1607 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001608
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 if (register_netdev(dev))
1610 goto err_out_res;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001611
John Daiker8830cb62009-03-08 22:18:35 -07001612 if (!probe_atmel_card(dev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613 unregister_netdev(dev);
1614 goto err_out_res;
1615 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001616
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617 netif_carrier_off(dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001618
Dan Carpenterbeaee9c2013-04-30 10:57:05 +03001619 if (!proc_create_data("driver/atmel", 0, NULL, &atmel_proc_fops, priv))
Christophe Lucas5bc4c362005-11-12 21:58:53 +03001620 printk(KERN_WARNING "atmel: unable to create /proc entry.\n");
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001621
Johannes Berge1749612008-10-27 15:59:26 -07001622 printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %pM\n",
1623 dev->name, DRIVER_MAJOR, DRIVER_MINOR, dev->dev_addr);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001624
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 return dev;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001626
1627err_out_res:
John Daiker8830cb62009-03-08 22:18:35 -07001628 release_region(dev->base_addr, 32);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001629err_out_irq:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630 free_irq(dev->irq, dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001631err_out_free:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 free_netdev(dev);
1633 return NULL;
1634}
1635
1636EXPORT_SYMBOL(init_atmel_card);
1637
simon@thekelleys.org.ukb16a2282005-10-30 15:50:15 +00001638void stop_atmel_card(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639{
1640 struct atmel_private *priv = netdev_priv(dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001641
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 /* put a brick on it... */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001643 if (priv->bus_type == BUS_TYPE_PCCARD)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 atmel_write16(dev, GCR, 0x0060);
1645 atmel_write16(dev, GCR, 0x0040);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001646
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647 del_timer_sync(&priv->management_timer);
1648 unregister_netdev(dev);
1649 remove_proc_entry("driver/atmel", NULL);
1650 free_irq(dev->irq, dev);
Jesper Juhlb4558ea2005-10-28 16:53:13 -04001651 kfree(priv->firmware);
simon@thekelleys.org.ukb16a2282005-10-30 15:50:15 +00001652 release_region(dev->base_addr, 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 free_netdev(dev);
1654}
1655
1656EXPORT_SYMBOL(stop_atmel_card);
1657
1658static int atmel_set_essid(struct net_device *dev,
1659 struct iw_request_info *info,
1660 struct iw_point *dwrq,
1661 char *extra)
1662{
1663 struct atmel_private *priv = netdev_priv(dev);
1664
1665 /* Check if we asked for `any' */
John Daiker8830cb62009-03-08 22:18:35 -07001666 if (dwrq->flags == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 priv->connect_to_any_BSS = 1;
1668 } else {
1669 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
1670
1671 priv->connect_to_any_BSS = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001672
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 /* Check the size of the string */
Jean Tourrilhes6a484db2006-08-29 17:59:03 -07001674 if (dwrq->length > MAX_SSID_LENGTH)
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001675 return -E2BIG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 if (index != 0)
1677 return -EINVAL;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001678
Jean Tourrilhes6a484db2006-08-29 17:59:03 -07001679 memcpy(priv->new_SSID, extra, dwrq->length);
1680 priv->new_SSID_size = dwrq->length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 }
1682
1683 return -EINPROGRESS;
1684}
1685
1686static int atmel_get_essid(struct net_device *dev,
1687 struct iw_request_info *info,
1688 struct iw_point *dwrq,
1689 char *extra)
1690{
1691 struct atmel_private *priv = netdev_priv(dev);
1692
1693 /* Get the current SSID */
1694 if (priv->new_SSID_size != 0) {
1695 memcpy(extra, priv->new_SSID, priv->new_SSID_size);
Dan Williamsd6a13a22006-01-12 15:00:58 -05001696 dwrq->length = priv->new_SSID_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 } else {
1698 memcpy(extra, priv->SSID, priv->SSID_size);
Dan Williamsd6a13a22006-01-12 15:00:58 -05001699 dwrq->length = priv->SSID_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001701
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 dwrq->flags = !priv->connect_to_any_BSS; /* active */
1703
1704 return 0;
1705}
1706
1707static int atmel_get_wap(struct net_device *dev,
1708 struct iw_request_info *info,
1709 struct sockaddr *awrq,
1710 char *extra)
1711{
1712 struct atmel_private *priv = netdev_priv(dev);
Joe Perchesd458cdf2013-10-01 19:04:40 -07001713 memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714 awrq->sa_family = ARPHRD_ETHER;
1715
1716 return 0;
1717}
1718
1719static int atmel_set_encode(struct net_device *dev,
1720 struct iw_request_info *info,
1721 struct iw_point *dwrq,
1722 char *extra)
1723{
1724 struct atmel_private *priv = netdev_priv(dev);
1725
1726 /* Basic checking: do we have a key to set ?
1727 * Note : with the new API, it's impossible to get a NULL pointer.
1728 * Therefore, we need to check a key size == 0 instead.
1729 * New version of iwconfig properly set the IW_ENCODE_NOKEY flag
1730 * when no key is present (only change flags), but older versions
1731 * don't do it. - Jean II */
1732 if (dwrq->length > 0) {
1733 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
1734 int current_index = priv->default_key;
1735 /* Check the size of the key */
1736 if (dwrq->length > 13) {
1737 return -EINVAL;
1738 }
1739 /* Check the index (none -> use current) */
1740 if (index < 0 || index >= 4)
1741 index = current_index;
1742 else
1743 priv->default_key = index;
1744 /* Set the length */
1745 if (dwrq->length > 5)
1746 priv->wep_key_len[index] = 13;
1747 else
1748 if (dwrq->length > 0)
1749 priv->wep_key_len[index] = 5;
1750 else
1751 /* Disable the key */
1752 priv->wep_key_len[index] = 0;
1753 /* Check if the key is not marked as invalid */
Dmitry Torokhov5c877fe2006-10-08 00:14:30 -04001754 if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 /* Cleanup */
1756 memset(priv->wep_keys[index], 0, 13);
1757 /* Copy the key in the driver */
1758 memcpy(priv->wep_keys[index], extra, dwrq->length);
1759 }
1760 /* WE specify that if a valid key is set, encryption
1761 * should be enabled (user may turn it off later)
1762 * This is also how "iwconfig ethX key on" works */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001763 if (index == current_index &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764 priv->wep_key_len[index] > 0) {
1765 priv->wep_is_on = 1;
1766 priv->exclude_unencrypted = 1;
1767 if (priv->wep_key_len[index] > 5) {
Dan Williams9a6301c2006-01-10 00:56:11 -05001768 priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769 priv->encryption_level = 2;
1770 } else {
Dan Williams9a6301c2006-01-10 00:56:11 -05001771 priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 priv->encryption_level = 1;
1773 }
1774 }
1775 } else {
1776 /* Do we want to just set the transmit key index ? */
1777 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001778 if (index >= 0 && index < 4) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779 priv->default_key = index;
1780 } else
1781 /* Don't complain if only change the mode */
Jeff Garzik93a3b602007-11-23 21:50:20 -05001782 if (!(dwrq->flags & IW_ENCODE_MODE))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 }
1785 /* Read the flags */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001786 if (dwrq->flags & IW_ENCODE_DISABLED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787 priv->wep_is_on = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001788 priv->encryption_level = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789 priv->pairwise_cipher_suite = CIPHER_SUITE_NONE;
1790 } else {
1791 priv->wep_is_on = 1;
1792 if (priv->wep_key_len[priv->default_key] > 5) {
1793 priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128;
1794 priv->encryption_level = 2;
1795 } else {
1796 priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64;
1797 priv->encryption_level = 1;
1798 }
1799 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001800 if (dwrq->flags & IW_ENCODE_RESTRICTED)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801 priv->exclude_unencrypted = 1;
John Daiker8830cb62009-03-08 22:18:35 -07001802 if (dwrq->flags & IW_ENCODE_OPEN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803 priv->exclude_unencrypted = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001804
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805 return -EINPROGRESS; /* Call commit handler */
1806}
1807
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808static int atmel_get_encode(struct net_device *dev,
1809 struct iw_request_info *info,
1810 struct iw_point *dwrq,
1811 char *extra)
1812{
1813 struct atmel_private *priv = netdev_priv(dev);
1814 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001815
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 if (!priv->wep_is_on)
1817 dwrq->flags = IW_ENCODE_DISABLED;
simon@thekelleys.org.ukb16a2282005-10-30 15:50:15 +00001818 else {
1819 if (priv->exclude_unencrypted)
1820 dwrq->flags = IW_ENCODE_RESTRICTED;
1821 else
1822 dwrq->flags = IW_ENCODE_OPEN;
1823 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824 /* Which key do we want ? -1 -> tx index */
1825 if (index < 0 || index >= 4)
1826 index = priv->default_key;
1827 dwrq->flags |= index + 1;
1828 /* Copy the key to the user buffer */
1829 dwrq->length = priv->wep_key_len[index];
1830 if (dwrq->length > 16) {
John Daiker8830cb62009-03-08 22:18:35 -07001831 dwrq->length = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832 } else {
1833 memset(extra, 0, 16);
1834 memcpy(extra, priv->wep_keys[index], dwrq->length);
1835 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03001836
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 return 0;
1838}
1839
Dan Williams9a6301c2006-01-10 00:56:11 -05001840static int atmel_set_encodeext(struct net_device *dev,
1841 struct iw_request_info *info,
1842 union iwreq_data *wrqu,
1843 char *extra)
1844{
1845 struct atmel_private *priv = netdev_priv(dev);
1846 struct iw_point *encoding = &wrqu->encoding;
1847 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
Dan Williams0d467502006-02-05 17:52:21 -05001848 int idx, key_len, alg = ext->alg, set_key = 1;
Dan Williams9a6301c2006-01-10 00:56:11 -05001849
1850 /* Determine and validate the key index */
1851 idx = encoding->flags & IW_ENCODE_INDEX;
1852 if (idx) {
Johannes Berg2c7060022008-10-30 22:09:54 +01001853 if (idx < 1 || idx > 4)
Dan Williams9a6301c2006-01-10 00:56:11 -05001854 return -EINVAL;
1855 idx--;
1856 } else
1857 idx = priv->default_key;
1858
Dan Williams0d467502006-02-05 17:52:21 -05001859 if (encoding->flags & IW_ENCODE_DISABLED)
1860 alg = IW_ENCODE_ALG_NONE;
1861
1862 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
1863 priv->default_key = idx;
1864 set_key = ext->key_len > 0 ? 1 : 0;
Dan Williams9a6301c2006-01-10 00:56:11 -05001865 }
1866
Dan Williams0d467502006-02-05 17:52:21 -05001867 if (set_key) {
1868 /* Set the requested key first */
1869 switch (alg) {
1870 case IW_ENCODE_ALG_NONE:
1871 priv->wep_is_on = 0;
1872 priv->encryption_level = 0;
1873 priv->pairwise_cipher_suite = CIPHER_SUITE_NONE;
1874 break;
1875 case IW_ENCODE_ALG_WEP:
1876 if (ext->key_len > 5) {
1877 priv->wep_key_len[idx] = 13;
1878 priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128;
1879 priv->encryption_level = 2;
1880 } else if (ext->key_len > 0) {
1881 priv->wep_key_len[idx] = 5;
1882 priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64;
1883 priv->encryption_level = 1;
1884 } else {
1885 return -EINVAL;
1886 }
1887 priv->wep_is_on = 1;
1888 memset(priv->wep_keys[idx], 0, 13);
1889 key_len = min ((int)ext->key_len, priv->wep_key_len[idx]);
1890 memcpy(priv->wep_keys[idx], ext->key, key_len);
1891 break;
1892 default:
Dan Williams9a6301c2006-01-10 00:56:11 -05001893 return -EINVAL;
1894 }
Dan Williams9a6301c2006-01-10 00:56:11 -05001895 }
1896
1897 return -EINPROGRESS;
1898}
1899
1900static int atmel_get_encodeext(struct net_device *dev,
1901 struct iw_request_info *info,
1902 union iwreq_data *wrqu,
1903 char *extra)
1904{
1905 struct atmel_private *priv = netdev_priv(dev);
1906 struct iw_point *encoding = &wrqu->encoding;
1907 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
1908 int idx, max_key_len;
1909
1910 max_key_len = encoding->length - sizeof(*ext);
1911 if (max_key_len < 0)
1912 return -EINVAL;
1913
1914 idx = encoding->flags & IW_ENCODE_INDEX;
1915 if (idx) {
Johannes Berg2c7060022008-10-30 22:09:54 +01001916 if (idx < 1 || idx > 4)
Dan Williams9a6301c2006-01-10 00:56:11 -05001917 return -EINVAL;
1918 idx--;
1919 } else
1920 idx = priv->default_key;
1921
1922 encoding->flags = idx + 1;
1923 memset(ext, 0, sizeof(*ext));
Dmitry Torokhov5c877fe2006-10-08 00:14:30 -04001924
Dan Williams9a6301c2006-01-10 00:56:11 -05001925 if (!priv->wep_is_on) {
1926 ext->alg = IW_ENCODE_ALG_NONE;
1927 ext->key_len = 0;
1928 encoding->flags |= IW_ENCODE_DISABLED;
1929 } else {
1930 if (priv->encryption_level > 0)
1931 ext->alg = IW_ENCODE_ALG_WEP;
1932 else
1933 return -EINVAL;
1934
1935 ext->key_len = priv->wep_key_len[idx];
1936 memcpy(ext->key, priv->wep_keys[idx], ext->key_len);
1937 encoding->flags |= IW_ENCODE_ENABLED;
1938 }
1939
1940 return 0;
1941}
1942
1943static int atmel_set_auth(struct net_device *dev,
1944 struct iw_request_info *info,
1945 union iwreq_data *wrqu, char *extra)
1946{
1947 struct atmel_private *priv = netdev_priv(dev);
1948 struct iw_param *param = &wrqu->param;
1949
1950 switch (param->flags & IW_AUTH_INDEX) {
1951 case IW_AUTH_WPA_VERSION:
1952 case IW_AUTH_CIPHER_PAIRWISE:
1953 case IW_AUTH_CIPHER_GROUP:
1954 case IW_AUTH_KEY_MGMT:
1955 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
1956 case IW_AUTH_PRIVACY_INVOKED:
1957 /*
1958 * atmel does not use these parameters
1959 */
1960 break;
1961
1962 case IW_AUTH_DROP_UNENCRYPTED:
1963 priv->exclude_unencrypted = param->value ? 1 : 0;
1964 break;
1965
1966 case IW_AUTH_80211_AUTH_ALG: {
1967 if (param->value & IW_AUTH_ALG_SHARED_KEY) {
1968 priv->exclude_unencrypted = 1;
1969 } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
1970 priv->exclude_unencrypted = 0;
1971 } else
1972 return -EINVAL;
1973 break;
1974 }
1975
1976 case IW_AUTH_WPA_ENABLED:
1977 /* Silently accept disable of WPA */
1978 if (param->value > 0)
1979 return -EOPNOTSUPP;
1980 break;
1981
1982 default:
1983 return -EOPNOTSUPP;
1984 }
1985 return -EINPROGRESS;
1986}
1987
1988static int atmel_get_auth(struct net_device *dev,
1989 struct iw_request_info *info,
1990 union iwreq_data *wrqu, char *extra)
1991{
1992 struct atmel_private *priv = netdev_priv(dev);
1993 struct iw_param *param = &wrqu->param;
1994
1995 switch (param->flags & IW_AUTH_INDEX) {
1996 case IW_AUTH_DROP_UNENCRYPTED:
1997 param->value = priv->exclude_unencrypted;
1998 break;
1999
2000 case IW_AUTH_80211_AUTH_ALG:
2001 if (priv->exclude_unencrypted == 1)
2002 param->value = IW_AUTH_ALG_SHARED_KEY;
2003 else
2004 param->value = IW_AUTH_ALG_OPEN_SYSTEM;
2005 break;
2006
2007 case IW_AUTH_WPA_ENABLED:
2008 param->value = 0;
2009 break;
2010
2011 default:
2012 return -EOPNOTSUPP;
2013 }
2014 return 0;
2015}
2016
2017
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018static int atmel_get_name(struct net_device *dev,
2019 struct iw_request_info *info,
2020 char *cwrq,
2021 char *extra)
2022{
2023 strcpy(cwrq, "IEEE 802.11-DS");
2024 return 0;
2025}
2026
2027static int atmel_set_rate(struct net_device *dev,
2028 struct iw_request_info *info,
2029 struct iw_param *vwrq,
2030 char *extra)
2031{
2032 struct atmel_private *priv = netdev_priv(dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002033
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034 if (vwrq->fixed == 0) {
2035 priv->tx_rate = 3;
2036 priv->auto_tx_rate = 1;
2037 } else {
2038 priv->auto_tx_rate = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002039
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040 /* Which type of value ? */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002041 if ((vwrq->value < 4) && (vwrq->value >= 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 /* Setting by rate index */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002043 priv->tx_rate = vwrq->value;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044 } else {
2045 /* Setting by frequency value */
2046 switch (vwrq->value) {
John Daiker8830cb62009-03-08 22:18:35 -07002047 case 1000000:
2048 priv->tx_rate = 0;
2049 break;
2050 case 2000000:
2051 priv->tx_rate = 1;
2052 break;
2053 case 5500000:
2054 priv->tx_rate = 2;
2055 break;
2056 case 11000000:
2057 priv->tx_rate = 3;
2058 break;
2059 default:
2060 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 }
2062 }
2063 }
2064
2065 return -EINPROGRESS;
2066}
2067
2068static int atmel_set_mode(struct net_device *dev,
2069 struct iw_request_info *info,
2070 __u32 *uwrq,
2071 char *extra)
2072{
2073 struct atmel_private *priv = netdev_priv(dev);
2074
2075 if (*uwrq != IW_MODE_ADHOC && *uwrq != IW_MODE_INFRA)
2076 return -EINVAL;
2077
2078 priv->operating_mode = *uwrq;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002079 return -EINPROGRESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080}
2081
2082static int atmel_get_mode(struct net_device *dev,
2083 struct iw_request_info *info,
2084 __u32 *uwrq,
2085 char *extra)
2086{
2087 struct atmel_private *priv = netdev_priv(dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002088
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 *uwrq = priv->operating_mode;
2090 return 0;
2091}
2092
2093static int atmel_get_rate(struct net_device *dev,
2094 struct iw_request_info *info,
2095 struct iw_param *vwrq,
2096 char *extra)
2097{
2098 struct atmel_private *priv = netdev_priv(dev);
2099
2100 if (priv->auto_tx_rate) {
2101 vwrq->fixed = 0;
2102 vwrq->value = 11000000;
2103 } else {
2104 vwrq->fixed = 1;
John Daiker8830cb62009-03-08 22:18:35 -07002105 switch (priv->tx_rate) {
2106 case 0:
2107 vwrq->value = 1000000;
2108 break;
2109 case 1:
2110 vwrq->value = 2000000;
2111 break;
2112 case 2:
2113 vwrq->value = 5500000;
2114 break;
2115 case 3:
2116 vwrq->value = 11000000;
2117 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118 }
2119 }
2120 return 0;
2121}
2122
2123static int atmel_set_power(struct net_device *dev,
2124 struct iw_request_info *info,
2125 struct iw_param *vwrq,
2126 char *extra)
2127{
2128 struct atmel_private *priv = netdev_priv(dev);
2129 priv->power_mode = vwrq->disabled ? 0 : 1;
2130 return -EINPROGRESS;
2131}
2132
2133static int atmel_get_power(struct net_device *dev,
2134 struct iw_request_info *info,
2135 struct iw_param *vwrq,
2136 char *extra)
2137{
2138 struct atmel_private *priv = netdev_priv(dev);
2139 vwrq->disabled = priv->power_mode ? 0 : 1;
2140 vwrq->flags = IW_POWER_ON;
2141 return 0;
2142}
2143
2144static int atmel_set_retry(struct net_device *dev,
2145 struct iw_request_info *info,
2146 struct iw_param *vwrq,
2147 char *extra)
2148{
2149 struct atmel_private *priv = netdev_priv(dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002150
2151 if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) {
Jean Tourrilhes6a484db2006-08-29 17:59:03 -07002152 if (vwrq->flags & IW_RETRY_LONG)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153 priv->long_retry = vwrq->value;
Jean Tourrilhes6a484db2006-08-29 17:59:03 -07002154 else if (vwrq->flags & IW_RETRY_SHORT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 priv->short_retry = vwrq->value;
2156 else {
2157 /* No modifier : set both */
2158 priv->long_retry = vwrq->value;
2159 priv->short_retry = vwrq->value;
2160 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002161 return -EINPROGRESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002163
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164 return -EINVAL;
2165}
2166
2167static int atmel_get_retry(struct net_device *dev,
2168 struct iw_request_info *info,
2169 struct iw_param *vwrq,
2170 char *extra)
2171{
2172 struct atmel_private *priv = netdev_priv(dev);
2173
2174 vwrq->disabled = 0; /* Can't be disabled */
2175
Jean Tourrilhes6a484db2006-08-29 17:59:03 -07002176 /* Note : by default, display the short retry number */
2177 if (vwrq->flags & IW_RETRY_LONG) {
2178 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 vwrq->value = priv->long_retry;
2180 } else {
2181 vwrq->flags = IW_RETRY_LIMIT;
2182 vwrq->value = priv->short_retry;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002183 if (priv->long_retry != priv->short_retry)
Jean Tourrilhes6a484db2006-08-29 17:59:03 -07002184 vwrq->flags |= IW_RETRY_SHORT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 }
2186
2187 return 0;
2188}
2189
2190static int atmel_set_rts(struct net_device *dev,
2191 struct iw_request_info *info,
2192 struct iw_param *vwrq,
2193 char *extra)
2194{
2195 struct atmel_private *priv = netdev_priv(dev);
2196 int rthr = vwrq->value;
2197
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002198 if (vwrq->disabled)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199 rthr = 2347;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002200 if ((rthr < 0) || (rthr > 2347)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201 return -EINVAL;
2202 }
2203 priv->rts_threshold = rthr;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002204
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 return -EINPROGRESS; /* Call commit handler */
2206}
2207
2208static int atmel_get_rts(struct net_device *dev,
2209 struct iw_request_info *info,
2210 struct iw_param *vwrq,
2211 char *extra)
2212{
2213 struct atmel_private *priv = netdev_priv(dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002214
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215 vwrq->value = priv->rts_threshold;
2216 vwrq->disabled = (vwrq->value >= 2347);
2217 vwrq->fixed = 1;
2218
2219 return 0;
2220}
2221
2222static int atmel_set_frag(struct net_device *dev,
2223 struct iw_request_info *info,
2224 struct iw_param *vwrq,
2225 char *extra)
2226{
2227 struct atmel_private *priv = netdev_priv(dev);
2228 int fthr = vwrq->value;
2229
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002230 if (vwrq->disabled)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231 fthr = 2346;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002232 if ((fthr < 256) || (fthr > 2346)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233 return -EINVAL;
2234 }
2235 fthr &= ~0x1; /* Get an even value - is it really needed ??? */
2236 priv->frag_threshold = fthr;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002237
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238 return -EINPROGRESS; /* Call commit handler */
2239}
2240
2241static int atmel_get_frag(struct net_device *dev,
2242 struct iw_request_info *info,
2243 struct iw_param *vwrq,
2244 char *extra)
2245{
2246 struct atmel_private *priv = netdev_priv(dev);
2247
2248 vwrq->value = priv->frag_threshold;
2249 vwrq->disabled = (vwrq->value >= 2346);
2250 vwrq->fixed = 1;
2251
2252 return 0;
2253}
2254
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255static int atmel_set_freq(struct net_device *dev,
2256 struct iw_request_info *info,
2257 struct iw_freq *fwrq,
2258 char *extra)
2259{
2260 struct atmel_private *priv = netdev_priv(dev);
2261 int rc = -EINPROGRESS; /* Call commit handler */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002262
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 /* If setting by frequency, convert to a channel */
David Kilroy9ee677c2008-12-23 14:03:38 +00002264 if (fwrq->e == 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265 int f = fwrq->m / 100000;
David Kilroy9ee677c2008-12-23 14:03:38 +00002266
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 /* Hack to fall through... */
2268 fwrq->e = 0;
Zhao, Gang61e54872014-02-18 21:35:58 +08002269 fwrq->m = ieee80211_frequency_to_channel(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 }
2271 /* Setting by channel number */
Dan Carpenterd9739a22016-05-10 22:21:17 +03002272 if (fwrq->m < 0 || fwrq->m > 1000 || fwrq->e > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273 rc = -EOPNOTSUPP;
2274 else {
2275 int channel = fwrq->m;
2276 if (atmel_validate_channel(priv, channel) == 0) {
2277 priv->channel = channel;
2278 } else {
2279 rc = -EINVAL;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002280 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 }
2282 return rc;
2283}
2284
2285static int atmel_get_freq(struct net_device *dev,
2286 struct iw_request_info *info,
2287 struct iw_freq *fwrq,
2288 char *extra)
2289{
2290 struct atmel_private *priv = netdev_priv(dev);
2291
2292 fwrq->m = priv->channel;
2293 fwrq->e = 0;
2294 return 0;
2295}
2296
2297static int atmel_set_scan(struct net_device *dev,
2298 struct iw_request_info *info,
David Kilroy9930cce2008-09-13 12:22:05 +01002299 struct iw_point *dwrq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300 char *extra)
2301{
2302 struct atmel_private *priv = netdev_priv(dev);
2303 unsigned long flags;
2304
2305 /* Note : you may have realised that, as this is a SET operation,
2306 * this is privileged and therefore a normal user can't
2307 * perform scanning.
2308 * This is not an error, while the device perform scanning,
2309 * traffic doesn't flow, so it's a perfect DoS...
2310 * Jean II */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002311
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 if (priv->station_state == STATION_STATE_DOWN)
2313 return -EAGAIN;
2314
2315 /* Timeout old surveys. */
S.Çağlar Onur6e33e302008-02-14 17:36:49 +02002316 if (time_after(jiffies, priv->last_survey + 20 * HZ))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317 priv->site_survey_state = SITE_SURVEY_IDLE;
2318 priv->last_survey = jiffies;
2319
2320 /* Initiate a scan command */
2321 if (priv->site_survey_state == SITE_SURVEY_IN_PROGRESS)
2322 return -EBUSY;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002323
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324 del_timer_sync(&priv->management_timer);
2325 spin_lock_irqsave(&priv->irqlock, flags);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002326
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327 priv->site_survey_state = SITE_SURVEY_IN_PROGRESS;
2328 priv->fast_scan = 0;
2329 atmel_scan(priv, 0);
2330 spin_unlock_irqrestore(&priv->irqlock, flags);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002331
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332 return 0;
2333}
2334
2335static int atmel_get_scan(struct net_device *dev,
2336 struct iw_request_info *info,
2337 struct iw_point *dwrq,
2338 char *extra)
2339{
2340 struct atmel_private *priv = netdev_priv(dev);
2341 int i;
2342 char *current_ev = extra;
2343 struct iw_event iwe;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002344
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345 if (priv->site_survey_state != SITE_SURVEY_COMPLETED)
2346 return -EAGAIN;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002347
2348 for (i = 0; i < priv->BSS_list_entries; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349 iwe.cmd = SIOCGIWAP;
2350 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
Joe Perchesd458cdf2013-10-01 19:04:40 -07002351 memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -07002352 current_ev = iwe_stream_add_event(info, current_ev,
2353 extra + IW_SCAN_MAX_DATA,
2354 &iwe, IW_EV_ADDR_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355
2356 iwe.u.data.length = priv->BSSinfo[i].SSIDsize;
2357 if (iwe.u.data.length > 32)
2358 iwe.u.data.length = 32;
2359 iwe.cmd = SIOCGIWESSID;
2360 iwe.u.data.flags = 1;
David S. Millerccc58052008-06-16 18:50:49 -07002361 current_ev = iwe_stream_add_point(info, current_ev,
2362 extra + IW_SCAN_MAX_DATA,
2363 &iwe, priv->BSSinfo[i].SSID);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002364
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 iwe.cmd = SIOCGIWMODE;
2366 iwe.u.mode = priv->BSSinfo[i].BSStype;
David S. Millerccc58052008-06-16 18:50:49 -07002367 current_ev = iwe_stream_add_event(info, current_ev,
2368 extra + IW_SCAN_MAX_DATA,
2369 &iwe, IW_EV_UINT_LEN);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002370
Linus Torvalds1da177e2005-04-16 15:20:36 -07002371 iwe.cmd = SIOCGIWFREQ;
2372 iwe.u.freq.m = priv->BSSinfo[i].channel;
2373 iwe.u.freq.e = 0;
David S. Millerccc58052008-06-16 18:50:49 -07002374 current_ev = iwe_stream_add_event(info, current_ev,
2375 extra + IW_SCAN_MAX_DATA,
2376 &iwe, IW_EV_FREQ_LEN);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002377
Holden Karau3b31dc32006-10-10 14:45:33 -07002378 /* Add quality statistics */
2379 iwe.cmd = IWEVQUAL;
2380 iwe.u.qual.level = priv->BSSinfo[i].RSSI;
2381 iwe.u.qual.qual = iwe.u.qual.level;
2382 /* iwe.u.qual.noise = SOMETHING */
David S. Millerccc58052008-06-16 18:50:49 -07002383 current_ev = iwe_stream_add_event(info, current_ev,
2384 extra + IW_SCAN_MAX_DATA,
2385 &iwe, IW_EV_QUAL_LEN);
Holden Karau3b31dc32006-10-10 14:45:33 -07002386
2387
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388 iwe.cmd = SIOCGIWENCODE;
2389 if (priv->BSSinfo[i].UsingWEP)
2390 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
2391 else
2392 iwe.u.data.flags = IW_ENCODE_DISABLED;
2393 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -07002394 current_ev = iwe_stream_add_point(info, current_ev,
2395 extra + IW_SCAN_MAX_DATA,
2396 &iwe, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 }
2398
2399 /* Length of data */
2400 dwrq->length = (current_ev - extra);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002401 dwrq->flags = 0;
2402
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403 return 0;
2404}
2405
2406static int atmel_get_range(struct net_device *dev,
2407 struct iw_request_info *info,
2408 struct iw_point *dwrq,
2409 char *extra)
2410{
2411 struct atmel_private *priv = netdev_priv(dev);
2412 struct iw_range *range = (struct iw_range *) extra;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002413 int k, i, j;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002414
2415 dwrq->length = sizeof(struct iw_range);
Alexey Dobriyanf36be622005-11-08 00:41:48 +03002416 memset(range, 0, sizeof(struct iw_range));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417 range->min_nwid = 0x0000;
2418 range->max_nwid = 0x0000;
2419 range->num_channels = 0;
Dmitry Torokhovb4341132006-10-08 00:14:29 -04002420 for (j = 0; j < ARRAY_SIZE(channel_table); j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421 if (priv->reg_domain == channel_table[j].reg_domain) {
2422 range->num_channels = channel_table[j].max - channel_table[j].min + 1;
2423 break;
2424 }
2425 if (range->num_channels != 0) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002426 for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427 range->freq[k].i = i; /* List index */
David Kilroy9ee677c2008-12-23 14:03:38 +00002428
2429 /* Values in MHz -> * 10^5 * 10 */
Zhao, Gang61e54872014-02-18 21:35:58 +08002430 range->freq[k].m = 100000 *
Johannes Berg57fbcce2016-04-12 15:56:15 +02002431 ieee80211_channel_to_frequency(i, NL80211_BAND_2GHZ);
David Kilroy9ee677c2008-12-23 14:03:38 +00002432 range->freq[k++].e = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433 }
2434 range->num_frequency = k;
2435 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002436
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437 range->max_qual.qual = 100;
2438 range->max_qual.level = 100;
2439 range->max_qual.noise = 0;
2440 range->max_qual.updated = IW_QUAL_NOISE_INVALID;
2441
2442 range->avg_qual.qual = 50;
2443 range->avg_qual.level = 50;
2444 range->avg_qual.noise = 0;
2445 range->avg_qual.updated = IW_QUAL_NOISE_INVALID;
2446
2447 range->sensitivity = 0;
2448
2449 range->bitrate[0] = 1000000;
2450 range->bitrate[1] = 2000000;
2451 range->bitrate[2] = 5500000;
2452 range->bitrate[3] = 11000000;
2453 range->num_bitrates = 4;
2454
2455 range->min_rts = 0;
2456 range->max_rts = 2347;
2457 range->min_frag = 256;
2458 range->max_frag = 2346;
2459
2460 range->encoding_size[0] = 5;
2461 range->encoding_size[1] = 13;
2462 range->num_encoding_sizes = 2;
2463 range->max_encoding_tokens = 4;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002464
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465 range->pmp_flags = IW_POWER_ON;
2466 range->pmt_flags = IW_POWER_ON;
2467 range->pm_capa = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002468
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469 range->we_version_source = WIRELESS_EXT;
2470 range->we_version_compiled = WIRELESS_EXT;
2471 range->retry_capa = IW_RETRY_LIMIT ;
2472 range->retry_flags = IW_RETRY_LIMIT;
2473 range->r_time_flags = 0;
2474 range->min_retry = 1;
2475 range->max_retry = 65535;
2476
2477 return 0;
2478}
2479
2480static int atmel_set_wap(struct net_device *dev,
2481 struct iw_request_info *info,
2482 struct sockaddr *awrq,
2483 char *extra)
2484{
2485 struct atmel_private *priv = netdev_priv(dev);
2486 int i;
Dan Williams9a6301c2006-01-10 00:56:11 -05002487 static const u8 any[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
2488 static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489 unsigned long flags;
2490
2491 if (awrq->sa_family != ARPHRD_ETHER)
2492 return -EINVAL;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002493
Dan Williams9a6301c2006-01-10 00:56:11 -05002494 if (!memcmp(any, awrq->sa_data, 6) ||
2495 !memcmp(off, awrq->sa_data, 6)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496 del_timer_sync(&priv->management_timer);
2497 spin_lock_irqsave(&priv->irqlock, flags);
2498 atmel_scan(priv, 1);
2499 spin_unlock_irqrestore(&priv->irqlock, flags);
2500 return 0;
2501 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002502
2503 for (i = 0; i < priv->BSS_list_entries; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504 if (memcmp(priv->BSSinfo[i].BSSID, awrq->sa_data, 6) == 0) {
2505 if (!priv->wep_is_on && priv->BSSinfo[i].UsingWEP) {
2506 return -EINVAL;
2507 } else if (priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) {
2508 return -EINVAL;
2509 } else {
2510 del_timer_sync(&priv->management_timer);
2511 spin_lock_irqsave(&priv->irqlock, flags);
2512 atmel_join_bss(priv, i);
2513 spin_unlock_irqrestore(&priv->irqlock, flags);
2514 return 0;
2515 }
2516 }
2517 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002518
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519 return -EINVAL;
2520}
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002521
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522static int atmel_config_commit(struct net_device *dev,
2523 struct iw_request_info *info, /* NULL */
2524 void *zwrq, /* NULL */
2525 char *extra) /* NULL */
2526{
2527 return atmel_open(dev);
2528}
2529
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002530static const iw_handler atmel_handler[] =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531{
2532 (iw_handler) atmel_config_commit, /* SIOCSIWCOMMIT */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002533 (iw_handler) atmel_get_name, /* SIOCGIWNAME */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 (iw_handler) NULL, /* SIOCSIWNWID */
2535 (iw_handler) NULL, /* SIOCGIWNWID */
2536 (iw_handler) atmel_set_freq, /* SIOCSIWFREQ */
2537 (iw_handler) atmel_get_freq, /* SIOCGIWFREQ */
2538 (iw_handler) atmel_set_mode, /* SIOCSIWMODE */
2539 (iw_handler) atmel_get_mode, /* SIOCGIWMODE */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002540 (iw_handler) NULL, /* SIOCSIWSENS */
2541 (iw_handler) NULL, /* SIOCGIWSENS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 (iw_handler) NULL, /* SIOCSIWRANGE */
2543 (iw_handler) atmel_get_range, /* SIOCGIWRANGE */
2544 (iw_handler) NULL, /* SIOCSIWPRIV */
2545 (iw_handler) NULL, /* SIOCGIWPRIV */
2546 (iw_handler) NULL, /* SIOCSIWSTATS */
2547 (iw_handler) NULL, /* SIOCGIWSTATS */
2548 (iw_handler) NULL, /* SIOCSIWSPY */
2549 (iw_handler) NULL, /* SIOCGIWSPY */
2550 (iw_handler) NULL, /* -- hole -- */
2551 (iw_handler) NULL, /* -- hole -- */
2552 (iw_handler) atmel_set_wap, /* SIOCSIWAP */
2553 (iw_handler) atmel_get_wap, /* SIOCGIWAP */
2554 (iw_handler) NULL, /* -- hole -- */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002555 (iw_handler) NULL, /* SIOCGIWAPLIST */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556 (iw_handler) atmel_set_scan, /* SIOCSIWSCAN */
2557 (iw_handler) atmel_get_scan, /* SIOCGIWSCAN */
2558 (iw_handler) atmel_set_essid, /* SIOCSIWESSID */
2559 (iw_handler) atmel_get_essid, /* SIOCGIWESSID */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002560 (iw_handler) NULL, /* SIOCSIWNICKN */
2561 (iw_handler) NULL, /* SIOCGIWNICKN */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 (iw_handler) NULL, /* -- hole -- */
2563 (iw_handler) NULL, /* -- hole -- */
2564 (iw_handler) atmel_set_rate, /* SIOCSIWRATE */
2565 (iw_handler) atmel_get_rate, /* SIOCGIWRATE */
2566 (iw_handler) atmel_set_rts, /* SIOCSIWRTS */
2567 (iw_handler) atmel_get_rts, /* SIOCGIWRTS */
2568 (iw_handler) atmel_set_frag, /* SIOCSIWFRAG */
2569 (iw_handler) atmel_get_frag, /* SIOCGIWFRAG */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002570 (iw_handler) NULL, /* SIOCSIWTXPOW */
2571 (iw_handler) NULL, /* SIOCGIWTXPOW */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 (iw_handler) atmel_set_retry, /* SIOCSIWRETRY */
2573 (iw_handler) atmel_get_retry, /* SIOCGIWRETRY */
2574 (iw_handler) atmel_set_encode, /* SIOCSIWENCODE */
2575 (iw_handler) atmel_get_encode, /* SIOCGIWENCODE */
2576 (iw_handler) atmel_set_power, /* SIOCSIWPOWER */
2577 (iw_handler) atmel_get_power, /* SIOCGIWPOWER */
Dan Williams9a6301c2006-01-10 00:56:11 -05002578 (iw_handler) NULL, /* -- hole -- */
2579 (iw_handler) NULL, /* -- hole -- */
2580 (iw_handler) NULL, /* SIOCSIWGENIE */
2581 (iw_handler) NULL, /* SIOCGIWGENIE */
2582 (iw_handler) atmel_set_auth, /* SIOCSIWAUTH */
2583 (iw_handler) atmel_get_auth, /* SIOCGIWAUTH */
2584 (iw_handler) atmel_set_encodeext, /* SIOCSIWENCODEEXT */
2585 (iw_handler) atmel_get_encodeext, /* SIOCGIWENCODEEXT */
2586 (iw_handler) NULL, /* SIOCSIWPMKSA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587};
2588
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002589static const iw_handler atmel_private_handler[] =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590{
2591 NULL, /* SIOCIWFIRSTPRIV */
2592};
2593
Himangi Saraogif0db82a2014-08-09 21:52:20 +05302594struct atmel_priv_ioctl {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002595 char id[32];
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002596 unsigned char __user *data;
2597 unsigned short len;
Himangi Saraogif0db82a2014-08-09 21:52:20 +05302598};
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002600#define ATMELFWL SIOCIWFIRSTPRIV
2601#define ATMELIDIFC ATMELFWL + 1
2602#define ATMELRD ATMELFWL + 2
2603#define ATMELMAGIC 0x51807
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604#define REGDOMAINSZ 20
2605
2606static const struct iw_priv_args atmel_private_args[] = {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002607 {
2608 .cmd = ATMELFWL,
2609 .set_args = IW_PRIV_TYPE_BYTE
2610 | IW_PRIV_SIZE_FIXED
Himangi Saraogif0db82a2014-08-09 21:52:20 +05302611 | sizeof(struct atmel_priv_ioctl),
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002612 .get_args = IW_PRIV_TYPE_NONE,
2613 .name = "atmelfwl"
2614 }, {
2615 .cmd = ATMELIDIFC,
2616 .set_args = IW_PRIV_TYPE_NONE,
2617 .get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
2618 .name = "atmelidifc"
2619 }, {
2620 .cmd = ATMELRD,
2621 .set_args = IW_PRIV_TYPE_CHAR | REGDOMAINSZ,
2622 .get_args = IW_PRIV_TYPE_NONE,
2623 .name = "regdomain"
2624 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625};
2626
John Daiker8830cb62009-03-08 22:18:35 -07002627static const struct iw_handler_def atmel_handler_def = {
Dmitry Torokhovb4341132006-10-08 00:14:29 -04002628 .num_standard = ARRAY_SIZE(atmel_handler),
2629 .num_private = ARRAY_SIZE(atmel_private_handler),
2630 .num_private_args = ARRAY_SIZE(atmel_private_args),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631 .standard = (iw_handler *) atmel_handler,
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002632 .private = (iw_handler *) atmel_private_handler,
Jean Tourrilhes72f98d32005-09-02 11:36:00 -07002633 .private_args = (struct iw_priv_args *) atmel_private_args,
2634 .get_wireless_stats = atmel_get_wireless_stats
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635};
2636
2637static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
2638{
2639 int i, rc = 0;
2640 struct atmel_private *priv = netdev_priv(dev);
Himangi Saraogif0db82a2014-08-09 21:52:20 +05302641 struct atmel_priv_ioctl com;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642 struct iwreq *wrq = (struct iwreq *) rq;
2643 unsigned char *new_firmware;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002644 char domain[REGDOMAINSZ + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645
2646 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647 case ATMELIDIFC:
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002648 wrq->u.param.value = ATMELMAGIC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002649 break;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002650
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 case ATMELFWL:
2652 if (copy_from_user(&com, rq->ifr_data, sizeof(com))) {
2653 rc = -EFAULT;
2654 break;
2655 }
2656
2657 if (!capable(CAP_NET_ADMIN)) {
2658 rc = -EPERM;
2659 break;
2660 }
2661
2662 if (!(new_firmware = kmalloc(com.len, GFP_KERNEL))) {
2663 rc = -ENOMEM;
2664 break;
2665 }
2666
2667 if (copy_from_user(new_firmware, com.data, com.len)) {
2668 kfree(new_firmware);
2669 rc = -EFAULT;
2670 break;
2671 }
2672
Jesper Juhlb4558ea2005-10-28 16:53:13 -04002673 kfree(priv->firmware);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002674
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675 priv->firmware = new_firmware;
2676 priv->firmware_length = com.len;
2677 strncpy(priv->firmware_id, com.id, 31);
2678 priv->firmware_id[31] = '\0';
2679 break;
2680
2681 case ATMELRD:
2682 if (copy_from_user(domain, rq->ifr_data, REGDOMAINSZ)) {
2683 rc = -EFAULT;
2684 break;
2685 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002686
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687 if (!capable(CAP_NET_ADMIN)) {
2688 rc = -EPERM;
2689 break;
2690 }
2691
2692 domain[REGDOMAINSZ] = 0;
2693 rc = -EINVAL;
Dmitry Torokhovb4341132006-10-08 00:14:29 -04002694 for (i = 0; i < ARRAY_SIZE(channel_table); i++) {
Rasmus Villemoes908414a2015-01-21 12:52:07 +01002695 if (!strcasecmp(channel_table[i].name, domain)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696 priv->config_reg_domain = channel_table[i].reg_domain;
2697 rc = 0;
2698 }
2699 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002700
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701 if (rc == 0 && priv->station_state != STATION_STATE_DOWN)
2702 rc = atmel_open(dev);
2703 break;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002704
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 default:
2706 rc = -EOPNOTSUPP;
2707 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002708
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709 return rc;
2710}
2711
2712struct auth_body {
Al Viro0e5ce1f2007-12-22 13:45:50 -05002713 __le16 alg;
2714 __le16 trans_seq;
2715 __le16 status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716 u8 el_id;
2717 u8 chall_text_len;
2718 u8 chall_text[253];
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002719};
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720
2721static void atmel_enter_state(struct atmel_private *priv, int new_state)
2722{
2723 int old_state = priv->station_state;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002724
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725 if (new_state == old_state)
2726 return;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002727
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728 priv->station_state = new_state;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002729
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730 if (new_state == STATION_STATE_READY) {
2731 netif_start_queue(priv->dev);
2732 netif_carrier_on(priv->dev);
2733 }
2734
2735 if (old_state == STATION_STATE_READY) {
2736 netif_carrier_off(priv->dev);
2737 if (netif_running(priv->dev))
2738 netif_stop_queue(priv->dev);
2739 priv->last_beacon_timestamp = 0;
2740 }
2741}
2742
2743static void atmel_scan(struct atmel_private *priv, int specific_ssid)
2744{
2745 struct {
Joe Perchesd458cdf2013-10-01 19:04:40 -07002746 u8 BSSID[ETH_ALEN];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747 u8 SSID[MAX_SSID_LENGTH];
2748 u8 scan_type;
2749 u8 channel;
Al Viro0e5ce1f2007-12-22 13:45:50 -05002750 __le16 BSS_type;
2751 __le16 min_channel_time;
2752 __le16 max_channel_time;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753 u8 options;
2754 u8 SSID_size;
2755 } cmd;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002756
Joe Perches93803b32015-03-02 19:54:49 -08002757 eth_broadcast_addr(cmd.BSSID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758
2759 if (priv->fast_scan) {
2760 cmd.SSID_size = priv->SSID_size;
2761 memcpy(cmd.SSID, priv->SSID, priv->SSID_size);
2762 cmd.min_channel_time = cpu_to_le16(10);
2763 cmd.max_channel_time = cpu_to_le16(50);
2764 } else {
2765 priv->BSS_list_entries = 0;
2766 cmd.SSID_size = 0;
2767 cmd.min_channel_time = cpu_to_le16(10);
2768 cmd.max_channel_time = cpu_to_le16(120);
2769 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002770
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 cmd.options = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002772
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773 if (!specific_ssid)
2774 cmd.options |= SCAN_OPTIONS_SITE_SURVEY;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002775
2776 cmd.channel = (priv->channel & 0x7f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777 cmd.scan_type = SCAN_TYPE_ACTIVE;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002778 cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779 BSS_TYPE_AD_HOC : BSS_TYPE_INFRASTRUCTURE);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002780
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 atmel_send_command(priv, CMD_Scan, &cmd, sizeof(cmd));
2782
2783 /* This must come after all hardware access to avoid being messed up
2784 by stuff happening in interrupt context after we leave STATE_DOWN */
2785 atmel_enter_state(priv, STATION_STATE_SCANNING);
2786}
2787
2788static void join(struct atmel_private *priv, int type)
2789{
2790 struct {
2791 u8 BSSID[6];
2792 u8 SSID[MAX_SSID_LENGTH];
2793 u8 BSS_type; /* this is a short in a scan command - weird */
2794 u8 channel;
Al Viro0e5ce1f2007-12-22 13:45:50 -05002795 __le16 timeout;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 u8 SSID_size;
2797 u8 reserved;
2798 } cmd;
2799
2800 cmd.SSID_size = priv->SSID_size;
2801 memcpy(cmd.SSID, priv->SSID, priv->SSID_size);
Joe Perchesd458cdf2013-10-01 19:04:40 -07002802 memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 cmd.channel = (priv->channel & 0x7f);
2804 cmd.BSS_type = type;
2805 cmd.timeout = cpu_to_le16(2000);
2806
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002807 atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808}
2809
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810static void start(struct atmel_private *priv, int type)
2811{
2812 struct {
2813 u8 BSSID[6];
2814 u8 SSID[MAX_SSID_LENGTH];
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002815 u8 BSS_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 u8 channel;
2817 u8 SSID_size;
2818 u8 reserved[3];
2819 } cmd;
2820
2821 cmd.SSID_size = priv->SSID_size;
2822 memcpy(cmd.SSID, priv->SSID, priv->SSID_size);
Joe Perchesd458cdf2013-10-01 19:04:40 -07002823 memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824 cmd.BSS_type = type;
2825 cmd.channel = (priv->channel & 0x7f);
2826
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002827 atmel_send_command(priv, CMD_Start, &cmd, sizeof(cmd));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828}
2829
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002830static void handle_beacon_probe(struct atmel_private *priv, u16 capability,
2831 u8 channel)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832{
2833 int rejoin = 0;
Johannes Berg2c7060022008-10-30 22:09:54 +01002834 int new = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835 SHORT_PREAMBLE : LONG_PREAMBLE;
2836
2837 if (priv->preamble != new) {
2838 priv->preamble = new;
2839 rejoin = 1;
2840 atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, new);
2841 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002842
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843 if (priv->channel != channel) {
2844 priv->channel = channel;
2845 rejoin = 1;
2846 atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_CHANNEL_POS, channel);
2847 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002848
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849 if (rejoin) {
2850 priv->station_is_associated = 0;
2851 atmel_enter_state(priv, STATION_STATE_JOINNING);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002852
Linus Torvalds1da177e2005-04-16 15:20:36 -07002853 if (priv->operating_mode == IW_MODE_INFRA)
2854 join(priv, BSS_TYPE_INFRASTRUCTURE);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002855 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856 join(priv, BSS_TYPE_AD_HOC);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002857 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858}
2859
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002860static void send_authentication_request(struct atmel_private *priv, u16 system,
2861 u8 *challenge, int challenge_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862{
Johannes Berg2c7060022008-10-30 22:09:54 +01002863 struct ieee80211_hdr header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864 struct auth_body auth;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002865
Johannes Berg2c7060022008-10-30 22:09:54 +01002866 header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002867 header.duration_id = cpu_to_le16(0x8000);
Johannes Berg2c7060022008-10-30 22:09:54 +01002868 header.seq_ctrl = 0;
Joe Perchesd458cdf2013-10-01 19:04:40 -07002869 memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN);
2870 memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN);
2871 memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002872
2873 if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874 /* no WEP for authentication frames with TrSeqNo 1 */
John Daiker8830cb62009-03-08 22:18:35 -07002875 header.frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002876
2877 auth.alg = cpu_to_le16(system);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878
2879 auth.status = 0;
2880 auth.trans_seq = cpu_to_le16(priv->CurrentAuthentTransactionSeqNum);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002881 priv->ExpectedAuthentTransactionSeqNum = priv->CurrentAuthentTransactionSeqNum+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882 priv->CurrentAuthentTransactionSeqNum += 2;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002883
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884 if (challenge_len != 0) {
2885 auth.el_id = 16; /* challenge_text */
2886 auth.chall_text_len = challenge_len;
2887 memcpy(auth.chall_text, challenge, challenge_len);
2888 atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 8 + challenge_len);
2889 } else {
2890 atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 6);
2891 }
2892}
2893
2894static void send_association_request(struct atmel_private *priv, int is_reassoc)
2895{
2896 u8 *ssid_el_p;
2897 int bodysize;
Johannes Berg2c7060022008-10-30 22:09:54 +01002898 struct ieee80211_hdr header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002899 struct ass_req_format {
Al Viro0e5ce1f2007-12-22 13:45:50 -05002900 __le16 capability;
2901 __le16 listen_interval;
Joe Perchesd458cdf2013-10-01 19:04:40 -07002902 u8 ap[ETH_ALEN]; /* nothing after here directly accessible */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002903 u8 ssid_el_id;
2904 u8 ssid_len;
2905 u8 ssid[MAX_SSID_LENGTH];
2906 u8 sup_rates_el_id;
2907 u8 sup_rates_len;
2908 u8 rates[4];
2909 } body;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002910
Johannes Berg2c7060022008-10-30 22:09:54 +01002911 header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
Jeff Garzikb4538722005-05-12 22:48:20 -04002912 (is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002913 header.duration_id = cpu_to_le16(0x8000);
Johannes Berg2c7060022008-10-30 22:09:54 +01002914 header.seq_ctrl = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002915
Joe Perchesd458cdf2013-10-01 19:04:40 -07002916 memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN);
2917 memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN);
2918 memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002919
Dan Williams4861dd72006-02-05 17:57:36 -05002920 body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002921 if (priv->wep_is_on)
Dan Williams4861dd72006-02-05 17:57:36 -05002922 body.capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002923 if (priv->preamble == SHORT_PREAMBLE)
Johannes Berg2c7060022008-10-30 22:09:54 +01002924 body.capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002925
2926 body.listen_interval = cpu_to_le16(priv->listen_interval * priv->beacon_period);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002927
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928 /* current AP address - only in reassoc frame */
2929 if (is_reassoc) {
Joe Perchesd458cdf2013-10-01 19:04:40 -07002930 memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN);
Joe Perches2c208892012-06-04 12:44:17 +00002931 ssid_el_p = &body.ssid_el_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002932 bodysize = 18 + priv->SSID_size;
2933 } else {
Joe Perches2c208892012-06-04 12:44:17 +00002934 ssid_el_p = &body.ap[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002935 bodysize = 12 + priv->SSID_size;
2936 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002937
Johannes Berg2c7060022008-10-30 22:09:54 +01002938 ssid_el_p[0] = WLAN_EID_SSID;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002939 ssid_el_p[1] = priv->SSID_size;
2940 memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size);
Johannes Berg2c7060022008-10-30 22:09:54 +01002941 ssid_el_p[2 + priv->SSID_size] = WLAN_EID_SUPP_RATES;
Masanari Iida02582e92012-08-22 19:11:26 +09002942 ssid_el_p[3 + priv->SSID_size] = 4; /* len of supported rates */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943 memcpy(ssid_el_p + 4 + priv->SSID_size, atmel_basic_rates, 4);
2944
2945 atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize);
2946}
2947
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002948static int is_frame_from_current_bss(struct atmel_private *priv,
Johannes Berg2c7060022008-10-30 22:09:54 +01002949 struct ieee80211_hdr *header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002950{
Johannes Berg2c7060022008-10-30 22:09:54 +01002951 if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002952 return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0;
2953 else
2954 return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0;
2955}
2956
2957static int retrieve_bss(struct atmel_private *priv)
2958{
2959 int i;
2960 int max_rssi = -128;
2961 int max_index = -1;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002962
Linus Torvalds1da177e2005-04-16 15:20:36 -07002963 if (priv->BSS_list_entries == 0)
2964 return -1;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002965
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966 if (priv->connect_to_any_BSS) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002967 /* Select a BSS with the max-RSSI but of the same type and of
2968 the same WEP mode and that it is not marked as 'bad' (i.e.
2969 we had previously failed to connect to this BSS with the
2970 settings that we currently use) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002971 priv->current_BSS = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002972 for (i = 0; i < priv->BSS_list_entries; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002973 if (priv->operating_mode == priv->BSSinfo[i].BSStype &&
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002974 ((!priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975 (priv->wep_is_on && priv->BSSinfo[i].UsingWEP)) &&
2976 !(priv->BSSinfo[i].channel & 0x80)) {
2977 max_rssi = priv->BSSinfo[i].RSSI;
2978 priv->current_BSS = max_index = i;
2979 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002980 }
2981 return max_index;
2982 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002983
2984 for (i = 0; i < priv->BSS_list_entries; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985 if (priv->SSID_size == priv->BSSinfo[i].SSIDsize &&
2986 memcmp(priv->SSID, priv->BSSinfo[i].SSID, priv->SSID_size) == 0 &&
2987 priv->operating_mode == priv->BSSinfo[i].BSStype &&
2988 atmel_validate_channel(priv, priv->BSSinfo[i].channel) == 0) {
2989 if (priv->BSSinfo[i].RSSI >= max_rssi) {
2990 max_rssi = priv->BSSinfo[i].RSSI;
2991 max_index = i;
2992 }
2993 }
2994 }
2995 return max_index;
2996}
2997
Carlo Perassi4d791aa2005-11-13 15:02:15 +03002998static void store_bss_info(struct atmel_private *priv,
Johannes Berg2c7060022008-10-30 22:09:54 +01002999 struct ieee80211_hdr *header, u16 capability,
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003000 u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len,
3001 u8 *ssid, int is_beacon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003002{
Dan Williams4861dd72006-02-05 17:57:36 -05003003 u8 *bss = capability & WLAN_CAPABILITY_ESS ? header->addr2 : header->addr3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003004 int i, index;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003005
3006 for (index = -1, i = 0; i < priv->BSS_list_entries; i++)
Joe Perchesd458cdf2013-10-01 19:04:40 -07003007 if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003008 index = i;
3009
John Daiker8830cb62009-03-08 22:18:35 -07003010 /* If we process a probe and an entry from this BSS exists
Linus Torvalds1da177e2005-04-16 15:20:36 -07003011 we will update the BSS entry with the info from this BSS.
3012 If we process a beacon we will only update RSSI */
3013
3014 if (index == -1) {
3015 if (priv->BSS_list_entries == MAX_BSS_ENTRIES)
3016 return;
3017 index = priv->BSS_list_entries++;
Joe Perchesd458cdf2013-10-01 19:04:40 -07003018 memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003019 priv->BSSinfo[index].RSSI = rssi;
3020 } else {
3021 if (rssi > priv->BSSinfo[index].RSSI)
3022 priv->BSSinfo[index].RSSI = rssi;
3023 if (is_beacon)
3024 return;
3025 }
3026
3027 priv->BSSinfo[index].channel = channel;
3028 priv->BSSinfo[index].beacon_period = beacon_period;
Dan Williams4861dd72006-02-05 17:57:36 -05003029 priv->BSSinfo[index].UsingWEP = capability & WLAN_CAPABILITY_PRIVACY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003030 memcpy(priv->BSSinfo[index].SSID, ssid, ssid_len);
3031 priv->BSSinfo[index].SSIDsize = ssid_len;
3032
Dan Williams4861dd72006-02-05 17:57:36 -05003033 if (capability & WLAN_CAPABILITY_IBSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034 priv->BSSinfo[index].BSStype = IW_MODE_ADHOC;
Dan Williams4861dd72006-02-05 17:57:36 -05003035 else if (capability & WLAN_CAPABILITY_ESS)
John Daiker8830cb62009-03-08 22:18:35 -07003036 priv->BSSinfo[index].BSStype = IW_MODE_INFRA;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003037
Johannes Berg2c7060022008-10-30 22:09:54 +01003038 priv->BSSinfo[index].preamble = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07003039 SHORT_PREAMBLE : LONG_PREAMBLE;
3040}
3041
3042static void authenticate(struct atmel_private *priv, u16 frame_len)
3043{
3044 struct auth_body *auth = (struct auth_body *)priv->rx_buf;
3045 u16 status = le16_to_cpu(auth->status);
3046 u16 trans_seq_no = le16_to_cpu(auth->trans_seq);
simon@thekelleys.org.ukb16a2282005-10-30 15:50:15 +00003047 u16 system = le16_to_cpu(auth->alg);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003048
Dan Williams4861dd72006-02-05 17:57:36 -05003049 if (status == WLAN_STATUS_SUCCESS && !priv->wep_is_on) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003050 /* no WEP */
3051 if (priv->station_was_associated) {
3052 atmel_enter_state(priv, STATION_STATE_REASSOCIATING);
3053 send_association_request(priv, 1);
3054 return;
3055 } else {
3056 atmel_enter_state(priv, STATION_STATE_ASSOCIATING);
3057 send_association_request(priv, 0);
3058 return;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003059 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003060 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003061
Dan Williams4861dd72006-02-05 17:57:36 -05003062 if (status == WLAN_STATUS_SUCCESS && priv->wep_is_on) {
Dan Williams73451372006-02-05 17:55:16 -05003063 int should_associate = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003064 /* WEP */
3065 if (trans_seq_no != priv->ExpectedAuthentTransactionSeqNum)
3066 return;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003067
Dan Williams4861dd72006-02-05 17:57:36 -05003068 if (system == WLAN_AUTH_OPEN) {
Dan Williams73451372006-02-05 17:55:16 -05003069 if (trans_seq_no == 0x0002) {
3070 should_associate = 1;
3071 }
Dan Williams4861dd72006-02-05 17:57:36 -05003072 } else if (system == WLAN_AUTH_SHARED_KEY) {
Dan Williams73451372006-02-05 17:55:16 -05003073 if (trans_seq_no == 0x0002 &&
Johannes Berg2c7060022008-10-30 22:09:54 +01003074 auth->el_id == WLAN_EID_CHALLENGE) {
Dan Williams73451372006-02-05 17:55:16 -05003075 send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len);
3076 return;
3077 } else if (trans_seq_no == 0x0004) {
3078 should_associate = 1;
3079 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003081
Dan Williams73451372006-02-05 17:55:16 -05003082 if (should_associate) {
John Daiker8830cb62009-03-08 22:18:35 -07003083 if (priv->station_was_associated) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084 atmel_enter_state(priv, STATION_STATE_REASSOCIATING);
3085 send_association_request(priv, 1);
3086 return;
3087 } else {
3088 atmel_enter_state(priv, STATION_STATE_ASSOCIATING);
3089 send_association_request(priv, 0);
3090 return;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003091 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003092 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003093 }
3094
Dan Williams73451372006-02-05 17:55:16 -05003095 if (status == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
Dan Williamsd0c29122008-08-18 15:32:41 -04003096 /* Flip back and forth between WEP auth modes until the max
3097 * authentication tries has been exceeded.
3098 */
Dan Williams73451372006-02-05 17:55:16 -05003099 if (system == WLAN_AUTH_OPEN) {
simon@thekelleys.org.ukb16a2282005-10-30 15:50:15 +00003100 priv->CurrentAuthentTransactionSeqNum = 0x001;
Dan Williams73451372006-02-05 17:55:16 -05003101 priv->exclude_unencrypted = 1;
3102 send_authentication_request(priv, WLAN_AUTH_SHARED_KEY, NULL, 0);
3103 return;
John Daiker8830cb62009-03-08 22:18:35 -07003104 } else if (system == WLAN_AUTH_SHARED_KEY
3105 && priv->wep_is_on) {
Dan Williamsd0c29122008-08-18 15:32:41 -04003106 priv->CurrentAuthentTransactionSeqNum = 0x001;
3107 priv->exclude_unencrypted = 0;
3108 send_authentication_request(priv, WLAN_AUTH_OPEN, NULL, 0);
3109 return;
simon@thekelleys.org.ukb16a2282005-10-30 15:50:15 +00003110 } else if (priv->connect_to_any_BSS) {
3111 int bss_index;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003112
simon@thekelleys.org.ukb16a2282005-10-30 15:50:15 +00003113 priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003114
simon@thekelleys.org.ukb16a2282005-10-30 15:50:15 +00003115 if ((bss_index = retrieve_bss(priv)) != -1) {
3116 atmel_join_bss(priv, bss_index);
3117 return;
3118 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003119 }
3120 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003121
Linus Torvalds1da177e2005-04-16 15:20:36 -07003122 priv->AuthenticationRequestRetryCnt = 0;
3123 atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
3124 priv->station_is_associated = 0;
3125}
3126
3127static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype)
3128{
3129 struct ass_resp_format {
Al Viro0e5ce1f2007-12-22 13:45:50 -05003130 __le16 capability;
3131 __le16 status;
3132 __le16 ass_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003133 u8 el_id;
3134 u8 length;
3135 u8 rates[4];
3136 } *ass_resp = (struct ass_resp_format *)priv->rx_buf;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003137
3138 u16 status = le16_to_cpu(ass_resp->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003139 u16 ass_id = le16_to_cpu(ass_resp->ass_id);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003140 u16 rates_len = ass_resp->length > 4 ? 4 : ass_resp->length;
3141
Dan Williams9a6301c2006-01-10 00:56:11 -05003142 union iwreq_data wrqu;
3143
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144 if (frame_len < 8 + rates_len)
3145 return;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003146
Dan Williams4861dd72006-02-05 17:57:36 -05003147 if (status == WLAN_STATUS_SUCCESS) {
3148 if (subtype == IEEE80211_STYPE_ASSOC_RESP)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003149 priv->AssociationRequestRetryCnt = 0;
3150 else
3151 priv->ReAssociationRequestRetryCnt = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003152
3153 atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
3154 MAC_MGMT_MIB_STATION_ID_POS, ass_id & 0x3fff);
3155 atmel_set_mib(priv, Phy_Mib_Type,
3156 PHY_MIB_RATE_SET_POS, ass_resp->rates, rates_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003157 if (priv->power_mode == 0) {
3158 priv->listen_interval = 1;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003159 atmel_set_mib8(priv, Mac_Mgmt_Mib_Type,
3160 MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE);
3161 atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
3162 MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003163 } else {
3164 priv->listen_interval = 2;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003165 atmel_set_mib8(priv, Mac_Mgmt_Mib_Type,
3166 MAC_MGMT_MIB_PS_MODE_POS, PS_MODE);
3167 atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
3168 MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003169 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003170
Linus Torvalds1da177e2005-04-16 15:20:36 -07003171 priv->station_is_associated = 1;
3172 priv->station_was_associated = 1;
3173 atmel_enter_state(priv, STATION_STATE_READY);
Dan Williams9a6301c2006-01-10 00:56:11 -05003174
3175 /* Send association event to userspace */
3176 wrqu.data.length = 0;
3177 wrqu.data.flags = 0;
3178 memcpy(wrqu.ap_addr.sa_data, priv->CurrentBSSID, ETH_ALEN);
3179 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
3180 wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
3181
Linus Torvalds1da177e2005-04-16 15:20:36 -07003182 return;
3183 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003184
Dan Williams4861dd72006-02-05 17:57:36 -05003185 if (subtype == IEEE80211_STYPE_ASSOC_RESP &&
3186 status != WLAN_STATUS_ASSOC_DENIED_RATES &&
3187 status != WLAN_STATUS_CAPS_UNSUPPORTED &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07003188 priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) {
3189 mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
3190 priv->AssociationRequestRetryCnt++;
3191 send_association_request(priv, 0);
3192 return;
3193 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003194
Dan Williams4861dd72006-02-05 17:57:36 -05003195 if (subtype == IEEE80211_STYPE_REASSOC_RESP &&
3196 status != WLAN_STATUS_ASSOC_DENIED_RATES &&
3197 status != WLAN_STATUS_CAPS_UNSUPPORTED &&
Felipe Pena13996ad2013-10-15 21:07:47 -03003198 priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003199 mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
3200 priv->ReAssociationRequestRetryCnt++;
3201 send_association_request(priv, 1);
3202 return;
3203 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003204
Linus Torvalds1da177e2005-04-16 15:20:36 -07003205 atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
3206 priv->station_is_associated = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003207
3208 if (priv->connect_to_any_BSS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209 int bss_index;
3210 priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003211
3212 if ((bss_index = retrieve_bss(priv)) != -1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003213 atmel_join_bss(priv, bss_index);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003214 }
3215}
3216
Hannes Eder2ed5ba82008-12-26 00:12:59 -08003217static void atmel_join_bss(struct atmel_private *priv, int bss_index)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003218{
3219 struct bss_info *bss = &priv->BSSinfo[bss_index];
3220
Joe Perchesd458cdf2013-10-01 19:04:40 -07003221 memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003222 memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize);
3223
3224 /* The WPA stuff cares about the current AP address */
3225 if (priv->use_wpa)
3226 build_wpa_mib(priv);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003227
Linus Torvalds1da177e2005-04-16 15:20:36 -07003228 /* When switching to AdHoc turn OFF Power Save if needed */
3229
3230 if (bss->BSStype == IW_MODE_ADHOC &&
3231 priv->operating_mode != IW_MODE_ADHOC &&
3232 priv->power_mode) {
3233 priv->power_mode = 0;
3234 priv->listen_interval = 1;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003235 atmel_set_mib8(priv, Mac_Mgmt_Mib_Type,
3236 MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE);
3237 atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
3238 MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003239 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003240
Linus Torvalds1da177e2005-04-16 15:20:36 -07003241 priv->operating_mode = bss->BSStype;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003242 priv->channel = bss->channel & 0x7f;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003243 priv->beacon_period = bss->beacon_period;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003244
Linus Torvalds1da177e2005-04-16 15:20:36 -07003245 if (priv->preamble != bss->preamble) {
3246 priv->preamble = bss->preamble;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003247 atmel_set_mib8(priv, Local_Mib_Type,
3248 LOCAL_MIB_PREAMBLE_TYPE, bss->preamble);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003249 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003250
Linus Torvalds1da177e2005-04-16 15:20:36 -07003251 if (!priv->wep_is_on && bss->UsingWEP) {
3252 atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
3253 priv->station_is_associated = 0;
3254 return;
3255 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003256
Linus Torvalds1da177e2005-04-16 15:20:36 -07003257 if (priv->wep_is_on && !bss->UsingWEP) {
3258 atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
3259 priv->station_is_associated = 0;
3260 return;
3261 }
3262
3263 atmel_enter_state(priv, STATION_STATE_JOINNING);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003264
Linus Torvalds1da177e2005-04-16 15:20:36 -07003265 if (priv->operating_mode == IW_MODE_INFRA)
3266 join(priv, BSS_TYPE_INFRASTRUCTURE);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003267 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07003268 join(priv, BSS_TYPE_AD_HOC);
3269}
3270
Linus Torvalds1da177e2005-04-16 15:20:36 -07003271static void restart_search(struct atmel_private *priv)
3272{
3273 int bss_index;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003274
Linus Torvalds1da177e2005-04-16 15:20:36 -07003275 if (!priv->connect_to_any_BSS) {
3276 atmel_scan(priv, 1);
3277 } else {
3278 priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003279
3280 if ((bss_index = retrieve_bss(priv)) != -1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003281 atmel_join_bss(priv, bss_index);
3282 else
3283 atmel_scan(priv, 0);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003284 }
3285}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003286
3287static void smooth_rssi(struct atmel_private *priv, u8 rssi)
3288{
3289 u8 old = priv->wstats.qual.level;
3290 u8 max_rssi = 42; /* 502-rmfd-revd max by experiment, default for now */
3291
3292 switch (priv->firmware_type) {
John Daiker8830cb62009-03-08 22:18:35 -07003293 case ATMEL_FW_TYPE_502E:
3294 max_rssi = 63; /* 502-rmfd-reve max by experiment */
3295 break;
3296 default:
3297 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003298 }
3299
3300 rssi = rssi * 100 / max_rssi;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003301 if ((rssi + old) % 2)
3302 priv->wstats.qual.level = (rssi + old) / 2 + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003303 else
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003304 priv->wstats.qual.level = (rssi + old) / 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003305 priv->wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;
3306 priv->wstats.qual.updated &= ~IW_QUAL_LEVEL_INVALID;
3307}
3308
3309static void atmel_smooth_qual(struct atmel_private *priv)
3310{
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003311 unsigned long time_diff = (jiffies - priv->last_qual) / HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003312 while (time_diff--) {
3313 priv->last_qual += HZ;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003314 priv->wstats.qual.qual = priv->wstats.qual.qual / 2;
3315 priv->wstats.qual.qual +=
Linus Torvalds1da177e2005-04-16 15:20:36 -07003316 priv->beacons_this_sec * priv->beacon_period * (priv->wstats.qual.level + 100) / 4000;
3317 priv->beacons_this_sec = 0;
3318 }
3319 priv->wstats.qual.updated |= IW_QUAL_QUAL_UPDATED;
3320 priv->wstats.qual.updated &= ~IW_QUAL_QUAL_INVALID;
3321}
3322
Uwe Kleine-Koenig3dbda772009-07-23 08:31:31 +02003323/* deals with incoming management frames. */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003324static void atmel_management_frame(struct atmel_private *priv,
Johannes Berg2c7060022008-10-30 22:09:54 +01003325 struct ieee80211_hdr *header,
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003326 u16 frame_len, u8 rssi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003327{
3328 u16 subtype;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003329
Johannes Berg2c7060022008-10-30 22:09:54 +01003330 subtype = le16_to_cpu(header->frame_control) & IEEE80211_FCTL_STYPE;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003331 switch (subtype) {
Dan Williams4861dd72006-02-05 17:57:36 -05003332 case IEEE80211_STYPE_BEACON:
3333 case IEEE80211_STYPE_PROBE_RESP:
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003334
Linus Torvalds1da177e2005-04-16 15:20:36 -07003335 /* beacon frame has multiple variable-length fields -
3336 never let an engineer loose with a data structure design. */
3337 {
3338 struct beacon_format {
Al Viro0e5ce1f2007-12-22 13:45:50 -05003339 __le64 timestamp;
3340 __le16 interval;
3341 __le16 capability;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003342 u8 ssid_el_id;
3343 u8 ssid_length;
3344 /* ssid here */
3345 u8 rates_el_id;
3346 u8 rates_length;
3347 /* rates here */
3348 u8 ds_el_id;
3349 u8 ds_length;
3350 /* ds here */
3351 } *beacon = (struct beacon_format *)priv->rx_buf;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003352
Linus Torvalds1da177e2005-04-16 15:20:36 -07003353 u8 channel, rates_length, ssid_length;
3354 u64 timestamp = le64_to_cpu(beacon->timestamp);
3355 u16 beacon_interval = le16_to_cpu(beacon->interval);
3356 u16 capability = le16_to_cpu(beacon->capability);
3357 u8 *beaconp = priv->rx_buf;
3358 ssid_length = beacon->ssid_length;
3359 /* this blows chunks. */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003360 if (frame_len < 14 || frame_len < ssid_length + 15)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003361 return;
3362 rates_length = beaconp[beacon->ssid_length + 15];
3363 if (frame_len < ssid_length + rates_length + 18)
3364 return;
3365 if (ssid_length > MAX_SSID_LENGTH)
3366 return;
3367 channel = beaconp[ssid_length + rates_length + 18];
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003368
Linus Torvalds1da177e2005-04-16 15:20:36 -07003369 if (priv->station_state == STATION_STATE_READY) {
3370 smooth_rssi(priv, rssi);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003371 if (is_frame_from_current_bss(priv, header)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003372 priv->beacons_this_sec++;
3373 atmel_smooth_qual(priv);
3374 if (priv->last_beacon_timestamp) {
3375 /* Note truncate this to 32 bits - kernel can't divide a long long */
3376 u32 beacon_delay = timestamp - priv->last_beacon_timestamp;
3377 int beacons = beacon_delay / (beacon_interval * 1000);
3378 if (beacons > 1)
3379 priv->wstats.miss.beacon += beacons - 1;
3380 }
3381 priv->last_beacon_timestamp = timestamp;
3382 handle_beacon_probe(priv, capability, channel);
3383 }
3384 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003385
3386 if (priv->station_state == STATION_STATE_SCANNING)
3387 store_bss_info(priv, header, capability,
3388 beacon_interval, channel, rssi,
3389 ssid_length,
3390 &beacon->rates_el_id,
Dan Williams4861dd72006-02-05 17:57:36 -05003391 subtype == IEEE80211_STYPE_BEACON);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003392 }
3393 break;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003394
Dan Williams4861dd72006-02-05 17:57:36 -05003395 case IEEE80211_STYPE_AUTH:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003396
3397 if (priv->station_state == STATION_STATE_AUTHENTICATING)
3398 authenticate(priv, frame_len);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003399
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 break;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003401
Dan Williams4861dd72006-02-05 17:57:36 -05003402 case IEEE80211_STYPE_ASSOC_RESP:
3403 case IEEE80211_STYPE_REASSOC_RESP:
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003404
3405 if (priv->station_state == STATION_STATE_ASSOCIATING ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07003406 priv->station_state == STATION_STATE_REASSOCIATING)
3407 associate(priv, frame_len, subtype);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003408
Linus Torvalds1da177e2005-04-16 15:20:36 -07003409 break;
3410
Dan Williams4861dd72006-02-05 17:57:36 -05003411 case IEEE80211_STYPE_DISASSOC:
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003412 if (priv->station_is_associated &&
3413 priv->operating_mode == IW_MODE_INFRA &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07003414 is_frame_from_current_bss(priv, header)) {
3415 priv->station_was_associated = 0;
3416 priv->station_is_associated = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003417
Linus Torvalds1da177e2005-04-16 15:20:36 -07003418 atmel_enter_state(priv, STATION_STATE_JOINNING);
3419 join(priv, BSS_TYPE_INFRASTRUCTURE);
3420 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003421
Linus Torvalds1da177e2005-04-16 15:20:36 -07003422 break;
3423
Dan Williams4861dd72006-02-05 17:57:36 -05003424 case IEEE80211_STYPE_DEAUTH:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003425 if (priv->operating_mode == IW_MODE_INFRA &&
3426 is_frame_from_current_bss(priv, header)) {
3427 priv->station_was_associated = 0;
3428
3429 atmel_enter_state(priv, STATION_STATE_JOINNING);
3430 join(priv, BSS_TYPE_INFRASTRUCTURE);
3431 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003432
Linus Torvalds1da177e2005-04-16 15:20:36 -07003433 break;
3434 }
3435}
3436
3437/* run when timer expires */
3438static void atmel_management_timer(u_long a)
3439{
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003440 struct net_device *dev = (struct net_device *) a;
3441 struct atmel_private *priv = netdev_priv(dev);
3442 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003443
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003444 /* Check if the card has been yanked. */
3445 if (priv->card && priv->present_callback &&
3446 !(*priv->present_callback)(priv->card))
3447 return;
3448
3449 spin_lock_irqsave(&priv->irqlock, flags);
3450
3451 switch (priv->station_state) {
3452
3453 case STATION_STATE_AUTHENTICATING:
3454 if (priv->AuthenticationRequestRetryCnt >= MAX_AUTHENTICATION_RETRIES) {
3455 atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
3456 priv->station_is_associated = 0;
3457 priv->AuthenticationRequestRetryCnt = 0;
3458 restart_search(priv);
3459 } else {
Dan Williams4861dd72006-02-05 17:57:36 -05003460 int auth = WLAN_AUTH_OPEN;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003461 priv->AuthenticationRequestRetryCnt++;
3462 priv->CurrentAuthentTransactionSeqNum = 0x0001;
3463 mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
Dan Williams73451372006-02-05 17:55:16 -05003464 if (priv->wep_is_on && priv->exclude_unencrypted)
Dan Williams4861dd72006-02-05 17:57:36 -05003465 auth = WLAN_AUTH_SHARED_KEY;
Dan Williams73451372006-02-05 17:55:16 -05003466 send_authentication_request(priv, auth, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003467 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003468 break;
3469
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003470 case STATION_STATE_ASSOCIATING:
3471 if (priv->AssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) {
3472 atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
3473 priv->station_is_associated = 0;
3474 priv->AssociationRequestRetryCnt = 0;
3475 restart_search(priv);
3476 } else {
3477 priv->AssociationRequestRetryCnt++;
3478 mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
3479 send_association_request(priv, 0);
3480 }
3481 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003482
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003483 case STATION_STATE_REASSOCIATING:
3484 if (priv->ReAssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) {
3485 atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
3486 priv->station_is_associated = 0;
3487 priv->ReAssociationRequestRetryCnt = 0;
3488 restart_search(priv);
3489 } else {
3490 priv->ReAssociationRequestRetryCnt++;
3491 mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
3492 send_association_request(priv, 1);
3493 }
3494 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003495
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003496 default:
3497 break;
3498 }
3499
3500 spin_unlock_irqrestore(&priv->irqlock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003501}
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003502
Linus Torvalds1da177e2005-04-16 15:20:36 -07003503static void atmel_command_irq(struct atmel_private *priv)
3504{
3505 u8 status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET));
3506 u8 command = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET));
3507 int fast_scan;
Dan Williams3a1af6f2006-03-31 15:13:31 -05003508 union iwreq_data wrqu;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003509
3510 if (status == CMD_STATUS_IDLE ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07003511 status == CMD_STATUS_IN_PROGRESS)
3512 return;
3513
John Daiker8830cb62009-03-08 22:18:35 -07003514 switch (command) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003515 case CMD_Start:
3516 if (status == CMD_STATUS_COMPLETE) {
3517 priv->station_was_associated = priv->station_is_associated;
3518 atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS,
3519 (u8 *)priv->CurrentBSSID, 6);
3520 atmel_enter_state(priv, STATION_STATE_READY);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003521 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003522 break;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003523
Linus Torvalds1da177e2005-04-16 15:20:36 -07003524 case CMD_Scan:
3525 fast_scan = priv->fast_scan;
3526 priv->fast_scan = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003527
Linus Torvalds1da177e2005-04-16 15:20:36 -07003528 if (status != CMD_STATUS_COMPLETE) {
3529 atmel_scan(priv, 1);
3530 } else {
3531 int bss_index = retrieve_bss(priv);
Dan Williams3a1af6f2006-03-31 15:13:31 -05003532 int notify_scan_complete = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003533 if (bss_index != -1) {
3534 atmel_join_bss(priv, bss_index);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003535 } else if (priv->operating_mode == IW_MODE_ADHOC &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07003536 priv->SSID_size != 0) {
3537 start(priv, BSS_TYPE_AD_HOC);
3538 } else {
3539 priv->fast_scan = !fast_scan;
3540 atmel_scan(priv, 1);
Dan Williams3a1af6f2006-03-31 15:13:31 -05003541 notify_scan_complete = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003542 }
3543 priv->site_survey_state = SITE_SURVEY_COMPLETED;
Dan Williams3a1af6f2006-03-31 15:13:31 -05003544 if (notify_scan_complete) {
3545 wrqu.data.length = 0;
3546 wrqu.data.flags = 0;
3547 wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL);
3548 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003549 }
3550 break;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003551
Linus Torvalds1da177e2005-04-16 15:20:36 -07003552 case CMD_SiteSurvey:
3553 priv->fast_scan = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003554
Linus Torvalds1da177e2005-04-16 15:20:36 -07003555 if (status != CMD_STATUS_COMPLETE)
3556 return;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003557
Linus Torvalds1da177e2005-04-16 15:20:36 -07003558 priv->site_survey_state = SITE_SURVEY_COMPLETED;
3559 if (priv->station_is_associated) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003560 atmel_enter_state(priv, STATION_STATE_READY);
Dan Williams3a1af6f2006-03-31 15:13:31 -05003561 wrqu.data.length = 0;
3562 wrqu.data.flags = 0;
3563 wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003564 } else {
3565 atmel_scan(priv, 1);
3566 }
3567 break;
3568
3569 case CMD_Join:
3570 if (status == CMD_STATUS_COMPLETE) {
3571 if (priv->operating_mode == IW_MODE_ADHOC) {
3572 priv->station_was_associated = priv->station_is_associated;
3573 atmel_enter_state(priv, STATION_STATE_READY);
3574 } else {
Dan Williams4861dd72006-02-05 17:57:36 -05003575 int auth = WLAN_AUTH_OPEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003576 priv->AuthenticationRequestRetryCnt = 0;
3577 atmel_enter_state(priv, STATION_STATE_AUTHENTICATING);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003578
Linus Torvalds1da177e2005-04-16 15:20:36 -07003579 mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
3580 priv->CurrentAuthentTransactionSeqNum = 0x0001;
Dan Williams73451372006-02-05 17:55:16 -05003581 if (priv->wep_is_on && priv->exclude_unencrypted)
Dan Williams4861dd72006-02-05 17:57:36 -05003582 auth = WLAN_AUTH_SHARED_KEY;
Dan Williams73451372006-02-05 17:55:16 -05003583 send_authentication_request(priv, auth, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003584 }
3585 return;
3586 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003587
Linus Torvalds1da177e2005-04-16 15:20:36 -07003588 atmel_scan(priv, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003589 }
3590}
3591
3592static int atmel_wakeup_firmware(struct atmel_private *priv)
3593{
3594 struct host_info_struct *iface = &priv->host_info;
3595 u16 mr1, mr3;
3596 int i;
3597
3598 if (priv->card_type == CARD_TYPE_SPI_FLASH)
3599 atmel_set_gcr(priv->dev, GCR_REMAP);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003600
Linus Torvalds1da177e2005-04-16 15:20:36 -07003601 /* wake up on-board processor */
3602 atmel_clear_gcr(priv->dev, 0x0040);
3603 atmel_write16(priv->dev, BSR, BSS_SRAM);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003604
Linus Torvalds1da177e2005-04-16 15:20:36 -07003605 if (priv->card_type == CARD_TYPE_SPI_FLASH)
3606 mdelay(100);
3607
3608 /* and wait for it */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003609 for (i = LOOP_RETRY_LIMIT; i; i--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003610 mr1 = atmel_read16(priv->dev, MR1);
3611 mr3 = atmel_read16(priv->dev, MR3);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003612
3613 if (mr3 & MAC_BOOT_COMPLETE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003614 break;
3615 if (mr1 & MAC_BOOT_COMPLETE &&
3616 priv->bus_type == BUS_TYPE_PCCARD)
3617 break;
3618 }
3619
3620 if (i == 0) {
3621 printk(KERN_ALERT "%s: MAC failed to boot.\n", priv->dev->name);
Dan Williams3c34a5d2008-08-18 15:40:02 -04003622 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003623 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003624
Linus Torvalds1da177e2005-04-16 15:20:36 -07003625 if ((priv->host_info_base = atmel_read16(priv->dev, MR2)) == 0xffff) {
3626 printk(KERN_ALERT "%s: card missing.\n", priv->dev->name);
Dan Williams3c34a5d2008-08-18 15:40:02 -04003627 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003628 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003629
3630 /* now check for completion of MAC initialization through
3631 the FunCtrl field of the IFACE, poll MR1 to detect completion of
3632 MAC initialization, check completion status, set interrupt mask,
3633 enables interrupts and calls Tx and Rx initialization functions */
3634
Linus Torvalds1da177e2005-04-16 15:20:36 -07003635 atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), FUNC_CTRL_INIT_COMPLETE);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003636
3637 for (i = LOOP_RETRY_LIMIT; i; i--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003638 mr1 = atmel_read16(priv->dev, MR1);
3639 mr3 = atmel_read16(priv->dev, MR3);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003640
3641 if (mr3 & MAC_INIT_COMPLETE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003642 break;
3643 if (mr1 & MAC_INIT_COMPLETE &&
3644 priv->bus_type == BUS_TYPE_PCCARD)
3645 break;
3646 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003647
Linus Torvalds1da177e2005-04-16 15:20:36 -07003648 if (i == 0) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003649 printk(KERN_ALERT "%s: MAC failed to initialise.\n",
3650 priv->dev->name);
Dan Williams3c34a5d2008-08-18 15:40:02 -04003651 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003652 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003653
Linus Torvalds1da177e2005-04-16 15:20:36 -07003654 /* Check for MAC_INIT_OK only on the register that the MAC_INIT_OK was set */
3655 if ((mr3 & MAC_INIT_COMPLETE) &&
3656 !(atmel_read16(priv->dev, MR3) & MAC_INIT_OK)) {
3657 printk(KERN_ALERT "%s: MAC failed MR3 self-test.\n", priv->dev->name);
Dan Williams3c34a5d2008-08-18 15:40:02 -04003658 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003659 }
3660 if ((mr1 & MAC_INIT_COMPLETE) &&
3661 !(atmel_read16(priv->dev, MR1) & MAC_INIT_OK)) {
3662 printk(KERN_ALERT "%s: MAC failed MR1 self-test.\n", priv->dev->name);
Dan Williams3c34a5d2008-08-18 15:40:02 -04003663 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003664 }
3665
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003666 atmel_copy_to_host(priv->dev, (unsigned char *)iface,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003667 priv->host_info_base, sizeof(*iface));
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003668
Linus Torvalds1da177e2005-04-16 15:20:36 -07003669 iface->tx_buff_pos = le16_to_cpu(iface->tx_buff_pos);
3670 iface->tx_buff_size = le16_to_cpu(iface->tx_buff_size);
3671 iface->tx_desc_pos = le16_to_cpu(iface->tx_desc_pos);
3672 iface->tx_desc_count = le16_to_cpu(iface->tx_desc_count);
3673 iface->rx_buff_pos = le16_to_cpu(iface->rx_buff_pos);
3674 iface->rx_buff_size = le16_to_cpu(iface->rx_buff_size);
3675 iface->rx_desc_pos = le16_to_cpu(iface->rx_desc_pos);
3676 iface->rx_desc_count = le16_to_cpu(iface->rx_desc_count);
3677 iface->build_version = le16_to_cpu(iface->build_version);
3678 iface->command_pos = le16_to_cpu(iface->command_pos);
3679 iface->major_version = le16_to_cpu(iface->major_version);
3680 iface->minor_version = le16_to_cpu(iface->minor_version);
3681 iface->func_ctrl = le16_to_cpu(iface->func_ctrl);
3682 iface->mac_status = le16_to_cpu(iface->mac_status);
3683
Dan Williams3c34a5d2008-08-18 15:40:02 -04003684 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003685}
3686
3687/* determine type of memory and MAC address */
3688static int probe_atmel_card(struct net_device *dev)
3689{
3690 int rc = 0;
3691 struct atmel_private *priv = netdev_priv(dev);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003692
Linus Torvalds1da177e2005-04-16 15:20:36 -07003693 /* reset pccard */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003694 if (priv->bus_type == BUS_TYPE_PCCARD)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003695 atmel_write16(dev, GCR, 0x0060);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003696
Linus Torvalds1da177e2005-04-16 15:20:36 -07003697 atmel_write16(dev, GCR, 0x0040);
3698 mdelay(500);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003699
Linus Torvalds1da177e2005-04-16 15:20:36 -07003700 if (atmel_read16(dev, MR2) == 0) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003701 /* No stored firmware so load a small stub which just
Linus Torvalds1da177e2005-04-16 15:20:36 -07003702 tells us the MAC address */
3703 int i;
3704 priv->card_type = CARD_TYPE_EEPROM;
3705 atmel_write16(dev, BSR, BSS_IRAM);
3706 atmel_copy_to_card(dev, 0, mac_reader, sizeof(mac_reader));
3707 atmel_set_gcr(dev, GCR_REMAP);
3708 atmel_clear_gcr(priv->dev, 0x0040);
3709 atmel_write16(dev, BSR, BSS_SRAM);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003710 for (i = LOOP_RETRY_LIMIT; i; i--)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003711 if (atmel_read16(dev, MR3) & MAC_BOOT_COMPLETE)
3712 break;
3713 if (i == 0) {
3714 printk(KERN_ALERT "%s: MAC failed to boot MAC address reader.\n", dev->name);
3715 } else {
3716 atmel_copy_to_host(dev, dev->dev_addr, atmel_read16(dev, MR2), 6);
3717 /* got address, now squash it again until the network
3718 interface is opened */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003719 if (priv->bus_type == BUS_TYPE_PCCARD)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003720 atmel_write16(dev, GCR, 0x0060);
3721 atmel_write16(dev, GCR, 0x0040);
3722 rc = 1;
3723 }
3724 } else if (atmel_read16(dev, MR4) == 0) {
3725 /* Mac address easy in this case. */
3726 priv->card_type = CARD_TYPE_PARALLEL_FLASH;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003727 atmel_write16(dev, BSR, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003728 atmel_copy_to_host(dev, dev->dev_addr, 0xc000, 6);
3729 atmel_write16(dev, BSR, 0x200);
3730 rc = 1;
3731 } else {
3732 /* Standard firmware in flash, boot it up and ask
3733 for the Mac Address */
3734 priv->card_type = CARD_TYPE_SPI_FLASH;
Dan Williams3c34a5d2008-08-18 15:40:02 -04003735 if (atmel_wakeup_firmware(priv) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003736 atmel_get_mib(priv, Mac_Address_Mib_Type, 0, dev->dev_addr, 6);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003737
Linus Torvalds1da177e2005-04-16 15:20:36 -07003738 /* got address, now squash it again until the network
3739 interface is opened */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003740 if (priv->bus_type == BUS_TYPE_PCCARD)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003741 atmel_write16(dev, GCR, 0x0060);
3742 atmel_write16(dev, GCR, 0x0040);
3743 rc = 1;
3744 }
3745 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003746
Linus Torvalds1da177e2005-04-16 15:20:36 -07003747 if (rc) {
3748 if (dev->dev_addr[0] == 0xFF) {
Joe Perches3370a892010-11-20 18:38:55 -08003749 static const u8 default_mac[] = {
3750 0x00, 0x04, 0x25, 0x00, 0x00, 0x00
3751 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752 printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name);
Joe Perchesd458cdf2013-10-01 19:04:40 -07003753 memcpy(dev->dev_addr, default_mac, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003754 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003755 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003756
Linus Torvalds1da177e2005-04-16 15:20:36 -07003757 return rc;
3758}
3759
Linus Torvalds1da177e2005-04-16 15:20:36 -07003760/* Move the encyption information on the MIB structure.
3761 This routine is for the pre-WPA firmware: later firmware has
3762 a different format MIB and a different routine. */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003763static void build_wep_mib(struct atmel_private *priv)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003764{
3765 struct { /* NB this is matched to the hardware, don't change. */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003766 u8 wep_is_on;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003767 u8 default_key; /* 0..3 */
3768 u8 reserved;
3769 u8 exclude_unencrypted;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003770
Linus Torvalds1da177e2005-04-16 15:20:36 -07003771 u32 WEPICV_error_count;
3772 u32 WEP_excluded_count;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003773
Linus Torvalds1da177e2005-04-16 15:20:36 -07003774 u8 wep_keys[MAX_ENCRYPTION_KEYS][13];
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003775 u8 encryption_level; /* 0, 1, 2 */
3776 u8 reserved2[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -07003777 } mib;
3778 int i;
3779
3780 mib.wep_is_on = priv->wep_is_on;
3781 if (priv->wep_is_on) {
3782 if (priv->wep_key_len[priv->default_key] > 5)
3783 mib.encryption_level = 2;
3784 else
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003785 mib.encryption_level = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003786 } else {
3787 mib.encryption_level = 0;
3788 }
3789
3790 mib.default_key = priv->default_key;
3791 mib.exclude_unencrypted = priv->exclude_unencrypted;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003792
3793 for (i = 0; i < MAX_ENCRYPTION_KEYS; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003794 memcpy(mib.wep_keys[i], priv->wep_keys[i], 13);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003795
Linus Torvalds1da177e2005-04-16 15:20:36 -07003796 atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib));
3797}
3798
3799static void build_wpa_mib(struct atmel_private *priv)
3800{
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003801 /* This is for the later (WPA enabled) firmware. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003802
3803 struct { /* NB this is matched to the hardware, don't change. */
3804 u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE];
Joe Perchesd458cdf2013-10-01 19:04:40 -07003805 u8 receiver_address[ETH_ALEN];
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003806 u8 wep_is_on;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003807 u8 default_key; /* 0..3 */
3808 u8 group_key;
3809 u8 exclude_unencrypted;
3810 u8 encryption_type;
3811 u8 reserved;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003812
Linus Torvalds1da177e2005-04-16 15:20:36 -07003813 u32 WEPICV_error_count;
3814 u32 WEP_excluded_count;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003815
Linus Torvalds1da177e2005-04-16 15:20:36 -07003816 u8 key_RSC[4][8];
3817 } mib;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003818
Linus Torvalds1da177e2005-04-16 15:20:36 -07003819 int i;
3820
3821 mib.wep_is_on = priv->wep_is_on;
3822 mib.exclude_unencrypted = priv->exclude_unencrypted;
Joe Perchesd458cdf2013-10-01 19:04:40 -07003823 memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003824
Linus Torvalds1da177e2005-04-16 15:20:36 -07003825 /* zero all the keys before adding in valid ones. */
3826 memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value));
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003827
Linus Torvalds1da177e2005-04-16 15:20:36 -07003828 if (priv->wep_is_on) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003829 /* There's a comment in the Atmel code to the effect that this
3830 is only valid when still using WEP, it may need to be set to
3831 something to use WPA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003832 memset(mib.key_RSC, 0, sizeof(mib.key_RSC));
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003833
Linus Torvalds1da177e2005-04-16 15:20:36 -07003834 mib.default_key = mib.group_key = 255;
3835 for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) {
3836 if (priv->wep_key_len[i] > 0) {
3837 memcpy(mib.cipher_default_key_value[i], priv->wep_keys[i], MAX_ENCRYPTION_KEY_SIZE);
3838 if (i == priv->default_key) {
3839 mib.default_key = i;
3840 mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 7;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003841 mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->pairwise_cipher_suite;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003842 } else {
3843 mib.group_key = i;
3844 priv->group_cipher_suite = priv->pairwise_cipher_suite;
John Daiker8830cb62009-03-08 22:18:35 -07003845 mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 1;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003846 mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->group_cipher_suite;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003847 }
3848 }
3849 }
3850 if (mib.default_key == 255)
3851 mib.default_key = mib.group_key != 255 ? mib.group_key : 0;
3852 if (mib.group_key == 255)
3853 mib.group_key = mib.default_key;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003854
Linus Torvalds1da177e2005-04-16 15:20:36 -07003855 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003856
Linus Torvalds1da177e2005-04-16 15:20:36 -07003857 atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib));
3858}
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003859
3860static int reset_atmel_card(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003861{
3862 /* do everything necessary to wake up the hardware, including
3863 waiting for the lightning strike and throwing the knife switch....
3864
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003865 set all the Mib values which matter in the card to match
Linus Torvalds1da177e2005-04-16 15:20:36 -07003866 their settings in the atmel_private structure. Some of these
3867 can be altered on the fly, but many (WEP, infrastucture or ad-hoc)
3868 can only be changed by tearing down the world and coming back through
3869 here.
3870
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003871 This routine is also responsible for initialising some
3872 hardware-specific fields in the atmel_private structure,
Lucas De Marchi25985ed2011-03-30 22:57:33 -03003873 including a copy of the firmware's hostinfo structure
Nick Andrewd5352952009-01-03 18:51:33 +11003874 which is the route into the rest of the firmware datastructures. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003875
3876 struct atmel_private *priv = netdev_priv(dev);
3877 u8 configuration;
Dan Williams9a6301c2006-01-10 00:56:11 -05003878 int old_state = priv->station_state;
Dan Williams3c34a5d2008-08-18 15:40:02 -04003879 int err = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003880
Linus Torvalds1da177e2005-04-16 15:20:36 -07003881 /* data to add to the firmware names, in priority order
3882 this implemenents firmware versioning */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003883
Linus Torvalds1da177e2005-04-16 15:20:36 -07003884 static char *firmware_modifier[] = {
3885 "-wpa",
3886 "",
3887 NULL
3888 };
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003889
Linus Torvalds1da177e2005-04-16 15:20:36 -07003890 /* reset pccard */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003891 if (priv->bus_type == BUS_TYPE_PCCARD)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003892 atmel_write16(priv->dev, GCR, 0x0060);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003893
Linus Torvalds1da177e2005-04-16 15:20:36 -07003894 /* stop card , disable interrupts */
3895 atmel_write16(priv->dev, GCR, 0x0040);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003896
Linus Torvalds1da177e2005-04-16 15:20:36 -07003897 if (priv->card_type == CARD_TYPE_EEPROM) {
3898 /* copy in firmware if needed */
3899 const struct firmware *fw_entry = NULL;
David Woodhouse2f26e8a2008-05-24 00:09:29 +01003900 const unsigned char *fw;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003901 int len = priv->firmware_length;
3902 if (!(fw = priv->firmware)) {
3903 if (priv->firmware_type == ATMEL_FW_TYPE_NONE) {
3904 if (strlen(priv->firmware_id) == 0) {
3905 printk(KERN_INFO
3906 "%s: card type is unknown: assuming at76c502 firmware is OK.\n",
3907 dev->name);
3908 printk(KERN_INFO
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003909 "%s: if not, use the firmware= module parameter.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003910 dev->name);
3911 strcpy(priv->firmware_id, "atmel_at76c502.bin");
3912 }
Dan Williams3c34a5d2008-08-18 15:40:02 -04003913 err = request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev);
3914 if (err != 0) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003915 printk(KERN_ALERT
3916 "%s: firmware %s is missing, cannot continue.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003917 dev->name, priv->firmware_id);
Dan Williams3c34a5d2008-08-18 15:40:02 -04003918 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003919 }
3920 } else {
3921 int fw_index = 0;
3922 int success = 0;
3923
3924 /* get firmware filename entry based on firmware type ID */
3925 while (fw_table[fw_index].fw_type != priv->firmware_type
3926 && fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE)
3927 fw_index++;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003928
Linus Torvalds1da177e2005-04-16 15:20:36 -07003929 /* construct the actual firmware file name */
3930 if (fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) {
3931 int i;
3932 for (i = 0; firmware_modifier[i]; i++) {
3933 snprintf(priv->firmware_id, 32, "%s%s.%s", fw_table[fw_index].fw_file,
3934 firmware_modifier[i], fw_table[fw_index].fw_file_ext);
3935 priv->firmware_id[31] = '\0';
3936 if (request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev) == 0) {
3937 success = 1;
3938 break;
3939 }
3940 }
3941 }
3942 if (!success) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003943 printk(KERN_ALERT
3944 "%s: firmware %s is missing, cannot start.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003945 dev->name, priv->firmware_id);
3946 priv->firmware_id[0] = '\0';
Dan Williams3c34a5d2008-08-18 15:40:02 -04003947 return -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003948 }
3949 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003950
Linus Torvalds1da177e2005-04-16 15:20:36 -07003951 fw = fw_entry->data;
3952 len = fw_entry->size;
3953 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003954
John Daiker8830cb62009-03-08 22:18:35 -07003955 if (len <= 0x6000) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003956 atmel_write16(priv->dev, BSR, BSS_IRAM);
3957 atmel_copy_to_card(priv->dev, 0, fw, len);
3958 atmel_set_gcr(priv->dev, GCR_REMAP);
3959 } else {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003960 /* Remap */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003961 atmel_set_gcr(priv->dev, GCR_REMAP);
3962 atmel_write16(priv->dev, BSR, BSS_IRAM);
3963 atmel_copy_to_card(priv->dev, 0, fw, 0x6000);
3964 atmel_write16(priv->dev, BSR, 0x2ff);
3965 atmel_copy_to_card(priv->dev, 0x8000, &fw[0x6000], len - 0x6000);
3966 }
3967
Jesper Juhl74033022012-04-09 22:50:53 +02003968 release_firmware(fw_entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003969 }
3970
Dan Williams3c34a5d2008-08-18 15:40:02 -04003971 err = atmel_wakeup_firmware(priv);
3972 if (err != 0)
3973 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003974
3975 /* Check the version and set the correct flag for wpa stuff,
3976 old and new firmware is incompatible.
3977 The pre-wpa 3com firmware reports major version 5,
3978 the wpa 3com firmware is major version 4 and doesn't need
3979 the 3com broken-ness filter. */
3980 priv->use_wpa = (priv->host_info.major_version == 4);
3981 priv->radio_on_broken = (priv->host_info.major_version == 5);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003982
John Daiker8830cb62009-03-08 22:18:35 -07003983 /* unmask all irq sources */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003984 atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_MASK_OFFSET), 0xff);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003985
Linus Torvalds1da177e2005-04-16 15:20:36 -07003986 /* int Tx system and enable Tx */
3987 atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, 0), 0);
3988 atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, 0), 0x80000000L);
3989 atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, 0), 0);
3990 atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, 0), 0);
3991
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003992 priv->tx_desc_free = priv->host_info.tx_desc_count;
3993 priv->tx_desc_head = 0;
3994 priv->tx_desc_tail = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003995 priv->tx_desc_previous = 0;
3996 priv->tx_free_mem = priv->host_info.tx_buff_size;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03003997 priv->tx_buff_head = 0;
3998 priv->tx_buff_tail = 0;
3999
4000 configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET));
4001 atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET),
Linus Torvalds1da177e2005-04-16 15:20:36 -07004002 configuration | FUNC_CTRL_TxENABLE);
4003
4004 /* init Rx system and enable */
4005 priv->rx_desc_head = 0;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004006
4007 configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET));
4008 atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET),
Linus Torvalds1da177e2005-04-16 15:20:36 -07004009 configuration | FUNC_CTRL_RxENABLE);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004010
Linus Torvalds1da177e2005-04-16 15:20:36 -07004011 if (!priv->radio_on_broken) {
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004012 if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) ==
Linus Torvalds1da177e2005-04-16 15:20:36 -07004013 CMD_STATUS_REJECTED_RADIO_OFF) {
Dan Williams3c34a5d2008-08-18 15:40:02 -04004014 printk(KERN_INFO "%s: cannot turn the radio on.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07004015 dev->name);
John Daiker8830cb62009-03-08 22:18:35 -07004016 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004017 }
4018 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004019
Linus Torvalds1da177e2005-04-16 15:20:36 -07004020 /* set up enough MIB values to run. */
4021 atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_AUTO_TX_RATE_POS, priv->auto_tx_rate);
4022 atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_TX_PROMISCUOUS_POS, PROM_MODE_OFF);
4023 atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_RTS_THRESHOLD_POS, priv->rts_threshold);
4024 atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_FRAG_THRESHOLD_POS, priv->frag_threshold);
4025 atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_SHORT_RETRY_POS, priv->short_retry);
4026 atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_LONG_RETRY_POS, priv->long_retry);
4027 atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, priv->preamble);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004028 atmel_set_mib(priv, Mac_Address_Mib_Type, MAC_ADDR_MIB_MAC_ADDR_POS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004029 priv->dev->dev_addr, 6);
4030 atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE);
4031 atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1);
4032 atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_BEACON_PER_POS, priv->default_beacon_period);
4033 atmel_set_mib(priv, Phy_Mib_Type, PHY_MIB_RATE_SET_POS, atmel_basic_rates, 4);
4034 atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_PRIVACY_POS, priv->wep_is_on);
4035 if (priv->use_wpa)
4036 build_wpa_mib(priv);
4037 else
4038 build_wep_mib(priv);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004039
John Daiker8830cb62009-03-08 22:18:35 -07004040 if (old_state == STATION_STATE_READY) {
Dan Williams9a6301c2006-01-10 00:56:11 -05004041 union iwreq_data wrqu;
4042
4043 wrqu.data.length = 0;
4044 wrqu.data.flags = 0;
4045 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
Joe Perches93803b32015-03-02 19:54:49 -08004046 eth_zero_addr(wrqu.ap_addr.sa_data);
Dan Williams9a6301c2006-01-10 00:56:11 -05004047 wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
4048 }
4049
Dan Williams3c34a5d2008-08-18 15:40:02 -04004050 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004051}
4052
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004053static void atmel_send_command(struct atmel_private *priv, int command,
4054 void *cmd, int cmd_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004055{
4056 if (cmd)
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004057 atmel_copy_to_card(priv->dev, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET),
Linus Torvalds1da177e2005-04-16 15:20:36 -07004058 cmd, cmd_size);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004059
Linus Torvalds1da177e2005-04-16 15:20:36 -07004060 atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET), command);
4061 atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET), 0);
4062}
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004063
4064static int atmel_send_command_wait(struct atmel_private *priv, int command,
4065 void *cmd, int cmd_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004066{
4067 int i, status;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004068
Linus Torvalds1da177e2005-04-16 15:20:36 -07004069 atmel_send_command(priv, command, cmd, cmd_size);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004070
Linus Torvalds1da177e2005-04-16 15:20:36 -07004071 for (i = 5000; i; i--) {
4072 status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET));
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004073 if (status != CMD_STATUS_IDLE &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07004074 status != CMD_STATUS_IN_PROGRESS)
4075 break;
4076 udelay(20);
4077 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004078
Linus Torvalds1da177e2005-04-16 15:20:36 -07004079 if (i == 0) {
4080 printk(KERN_ALERT "%s: failed to contact MAC.\n", priv->dev->name);
4081 status = CMD_STATUS_HOST_ERROR;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004082 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004083 if (command != CMD_EnableRadio)
4084 status = CMD_STATUS_COMPLETE;
4085 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004086
Linus Torvalds1da177e2005-04-16 15:20:36 -07004087 return status;
4088}
4089
4090static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index)
4091{
4092 struct get_set_mib m;
4093 m.type = type;
4094 m.size = 1;
4095 m.index = index;
4096
4097 atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + 1);
4098 return atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE));
4099}
4100
4101static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, u8 data)
4102{
4103 struct get_set_mib m;
4104 m.type = type;
4105 m.size = 1;
4106 m.index = index;
4107 m.data[0] = data;
4108
4109 atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 1);
4110}
4111
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004112static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index,
4113 u16 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004114{
4115 struct get_set_mib m;
4116 m.type = type;
4117 m.size = 2;
4118 m.index = index;
4119 m.data[0] = data;
4120 m.data[1] = data >> 8;
4121
4122 atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 2);
4123}
4124
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004125static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index,
4126 u8 *data, int data_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004127{
4128 struct get_set_mib m;
4129 m.type = type;
4130 m.size = data_len;
4131 m.index = index;
4132
4133 if (data_len > MIB_MAX_DATA_BYTES)
4134 printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004135
Linus Torvalds1da177e2005-04-16 15:20:36 -07004136 memcpy(m.data, data, data_len);
4137 atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + data_len);
4138}
4139
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004140static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index,
4141 u8 *data, int data_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004142{
4143 struct get_set_mib m;
4144 m.type = type;
4145 m.size = data_len;
4146 m.index = index;
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004147
Linus Torvalds1da177e2005-04-16 15:20:36 -07004148 if (data_len > MIB_MAX_DATA_BYTES)
4149 printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004150
Linus Torvalds1da177e2005-04-16 15:20:36 -07004151 atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + data_len);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004152 atmel_copy_to_host(priv->dev, data,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004153 atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE), data_len);
4154}
4155
4156static void atmel_writeAR(struct net_device *dev, u16 data)
4157{
4158 int i;
4159 outw(data, dev->base_addr + AR);
4160 /* Address register appears to need some convincing..... */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004161 for (i = 0; data != inw(dev->base_addr + AR) && i < 10; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004162 outw(data, dev->base_addr + AR);
4163}
4164
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004165static void atmel_copy_to_card(struct net_device *dev, u16 dest,
David Woodhouse2f26e8a2008-05-24 00:09:29 +01004166 const unsigned char *src, u16 len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004167{
4168 int i;
4169 atmel_writeAR(dev, dest);
4170 if (dest % 2) {
4171 atmel_write8(dev, DR, *src);
4172 src++; len--;
4173 }
4174 for (i = len; i > 1 ; i -= 2) {
4175 u8 lb = *src++;
4176 u8 hb = *src++;
4177 atmel_write16(dev, DR, lb | (hb << 8));
4178 }
4179 if (i)
4180 atmel_write8(dev, DR, *src);
4181}
4182
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004183static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest,
4184 u16 src, u16 len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004185{
4186 int i;
4187 atmel_writeAR(dev, src);
4188 if (src % 2) {
4189 *dest = atmel_read8(dev, DR);
4190 dest++; len--;
4191 }
4192 for (i = len; i > 1 ; i -= 2) {
4193 u16 hw = atmel_read16(dev, DR);
4194 *dest++ = hw;
4195 *dest++ = hw >> 8;
4196 }
4197 if (i)
4198 *dest = atmel_read8(dev, DR);
4199}
4200
4201static void atmel_set_gcr(struct net_device *dev, u16 mask)
4202{
4203 outw(inw(dev->base_addr + GCR) | mask, dev->base_addr + GCR);
4204}
4205
4206static void atmel_clear_gcr(struct net_device *dev, u16 mask)
4207{
4208 outw(inw(dev->base_addr + GCR) & ~mask, dev->base_addr + GCR);
4209}
4210
4211static int atmel_lock_mac(struct atmel_private *priv)
4212{
4213 int i, j = 20;
4214 retry:
4215 for (i = 5000; i; i--) {
4216 if (!atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET)))
4217 break;
4218 udelay(20);
4219 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004220
4221 if (!i)
4222 return 0; /* timed out */
4223
Linus Torvalds1da177e2005-04-16 15:20:36 -07004224 atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 1);
4225 if (atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) {
4226 atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004227 if (!j--)
4228 return 0; /* timed out */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004229 goto retry;
4230 }
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004231
Linus Torvalds1da177e2005-04-16 15:20:36 -07004232 return 1;
4233}
4234
4235static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data)
4236{
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004237 atmel_writeAR(priv->dev, pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004238 atmel_write16(priv->dev, DR, data); /* card is little-endian */
4239 atmel_write16(priv->dev, DR, data >> 16);
4240}
4241
4242/***************************************************************************/
4243/* There follows the source form of the MAC address reading firmware */
4244/***************************************************************************/
4245#if 0
4246
4247/* Copyright 2003 Matthew T. Russotto */
4248/* But derived from the Atmel 76C502 firmware written by Atmel and */
4249/* included in "atmel wireless lan drivers" package */
4250/**
4251 This file is part of net.russotto.AtmelMACFW, hereto referred to
4252 as AtmelMACFW
4253
4254 AtmelMACFW is free software; you can redistribute it and/or modify
4255 it under the terms of the GNU General Public License version 2
4256 as published by the Free Software Foundation.
4257
4258 AtmelMACFW is distributed in the hope that it will be useful,
4259 but WITHOUT ANY WARRANTY; without even the implied warranty of
4260 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4261 GNU General Public License for more details.
4262
4263 You should have received a copy of the GNU General Public License
Jeff Kirsher36769152013-12-06 03:32:13 -08004264 along with AtmelMACFW; if not, see <http://www.gnu.org/licenses/>.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004265
4266****************************************************************************/
4267/* This firmware should work on the 76C502 RFMD, RFMD_D, and RFMD_E */
4268/* It will probably work on the 76C504 and 76C502 RFMD_3COM */
4269/* It only works on SPI EEPROM versions of the card. */
4270
4271/* This firmware initializes the SPI controller and clock, reads the MAC */
4272/* address from the EEPROM into SRAM, and puts the SRAM offset of the MAC */
4273/* address in MR2, and sets MR3 to 0x10 to indicate it is done */
4274/* It also puts a complete copy of the EEPROM in SRAM with the offset in */
4275/* MR4, for investigational purposes (maybe we can determine chip type */
4276/* from that?) */
4277
4278 .org 0
4279 .set MRBASE, 0x8000000
4280 .set CPSR_INITIAL, 0xD3 /* IRQ/FIQ disabled, ARM mode, Supervisor state */
4281 .set CPSR_USER, 0xD1 /* IRQ/FIQ disabled, ARM mode, USER state */
4282 .set SRAM_BASE, 0x02000000
4283 .set SP_BASE, 0x0F300000
4284 .set UNK_BASE, 0x0F000000 /* Some internal device, but which one? */
4285 .set SPI_CGEN_BASE, 0x0E000000 /* Some internal device, but which one? */
4286 .set UNK3_BASE, 0x02014000 /* Some internal device, but which one? */
4287 .set STACK_BASE, 0x5600
4288 .set SP_SR, 0x10
4289 .set SP_TDRE, 2 /* status register bit -- TDR empty */
4290 .set SP_RDRF, 1 /* status register bit -- RDR full */
4291 .set SP_SWRST, 0x80
4292 .set SP_SPIEN, 0x1
4293 .set SP_CR, 0 /* control register */
4294 .set SP_MR, 4 /* mode register */
4295 .set SP_RDR, 0x08 /* Read Data Register */
4296 .set SP_TDR, 0x0C /* Transmit Data Register */
4297 .set SP_CSR0, 0x30 /* chip select registers */
4298 .set SP_CSR1, 0x34
4299 .set SP_CSR2, 0x38
4300 .set SP_CSR3, 0x3C
4301 .set NVRAM_CMD_RDSR, 5 /* read status register */
4302 .set NVRAM_CMD_READ, 3 /* read data */
4303 .set NVRAM_SR_RDY, 1 /* RDY bit. This bit is inverted */
4304 .set SPI_8CLOCKS, 0xFF /* Writing this to the TDR doesn't do anything to the
4305 serial output, since SO is normally high. But it
4306 does cause 8 clock cycles and thus 8 bits to be
4307 clocked in to the chip. See Atmel's SPI
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004308 controller (e.g. AT91M55800) timing and 4K
Linus Torvalds1da177e2005-04-16 15:20:36 -07004309 SPI EEPROM manuals */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004310
Linus Torvalds1da177e2005-04-16 15:20:36 -07004311 .set NVRAM_SCRATCH, 0x02000100 /* arbitrary area for scratchpad memory */
4312 .set NVRAM_IMAGE, 0x02000200
4313 .set NVRAM_LENGTH, 0x0200
4314 .set MAC_ADDRESS_MIB, SRAM_BASE
4315 .set MAC_ADDRESS_LENGTH, 6
John Daiker8830cb62009-03-08 22:18:35 -07004316 .set MAC_BOOT_FLAG, 0x10
Linus Torvalds1da177e2005-04-16 15:20:36 -07004317 .set MR1, 0
4318 .set MR2, 4
4319 .set MR3, 8
4320 .set MR4, 0xC
4321RESET_VECTOR:
John Daiker8830cb62009-03-08 22:18:35 -07004322 b RESET_HANDLER
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004323UNDEF_VECTOR:
John Daiker8830cb62009-03-08 22:18:35 -07004324 b HALT1
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004325SWI_VECTOR:
John Daiker8830cb62009-03-08 22:18:35 -07004326 b HALT1
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004327IABORT_VECTOR:
John Daiker8830cb62009-03-08 22:18:35 -07004328 b HALT1
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004329DABORT_VECTOR:
4330RESERVED_VECTOR:
John Daiker8830cb62009-03-08 22:18:35 -07004331 b HALT1
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004332IRQ_VECTOR:
John Daiker8830cb62009-03-08 22:18:35 -07004333 b HALT1
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004334FIQ_VECTOR:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004335 b HALT1
4336HALT1: b HALT1
4337RESET_HANDLER:
4338 mov r0, #CPSR_INITIAL
4339 msr CPSR_c, r0 /* This is probably unnecessary */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004340
Linus Torvalds1da177e2005-04-16 15:20:36 -07004341/* I'm guessing this is initializing clock generator electronics for SPI */
4342 ldr r0, =SPI_CGEN_BASE
4343 mov r1, #0
4344 mov r1, r1, lsl #3
John Daiker8830cb62009-03-08 22:18:35 -07004345 orr r1, r1, #0
Linus Torvalds1da177e2005-04-16 15:20:36 -07004346 str r1, [r0]
4347 ldr r1, [r0, #28]
4348 bic r1, r1, #16
4349 str r1, [r0, #28]
4350 mov r1, #1
4351 str r1, [r0, #8]
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004352
Linus Torvalds1da177e2005-04-16 15:20:36 -07004353 ldr r0, =MRBASE
4354 mov r1, #0
4355 strh r1, [r0, #MR1]
4356 strh r1, [r0, #MR2]
4357 strh r1, [r0, #MR3]
4358 strh r1, [r0, #MR4]
4359
4360 mov sp, #STACK_BASE
4361 bl SP_INIT
4362 mov r0, #10
4363 bl DELAY9
4364 bl GET_MAC_ADDR
4365 bl GET_WHOLE_NVRAM
4366 ldr r0, =MRBASE
4367 ldr r1, =MAC_ADDRESS_MIB
4368 strh r1, [r0, #MR2]
4369 ldr r1, =NVRAM_IMAGE
4370 strh r1, [r0, #MR4]
4371 mov r1, #MAC_BOOT_FLAG
4372 strh r1, [r0, #MR3]
4373HALT2: b HALT2
4374.func Get_Whole_NVRAM, GET_WHOLE_NVRAM
4375GET_WHOLE_NVRAM:
4376 stmdb sp!, {lr}
4377 mov r2, #0 /* 0th bytes of NVRAM */
4378 mov r3, #NVRAM_LENGTH
4379 mov r1, #0 /* not used in routine */
4380 ldr r0, =NVRAM_IMAGE
4381 bl NVRAM_XFER
4382 ldmia sp!, {lr}
4383 bx lr
4384.endfunc
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004385
Linus Torvalds1da177e2005-04-16 15:20:36 -07004386.func Get_MAC_Addr, GET_MAC_ADDR
4387GET_MAC_ADDR:
4388 stmdb sp!, {lr}
4389 mov r2, #0x120 /* address of MAC Address within NVRAM */
4390 mov r3, #MAC_ADDRESS_LENGTH
4391 mov r1, #0 /* not used in routine */
4392 ldr r0, =MAC_ADDRESS_MIB
4393 bl NVRAM_XFER
4394 ldmia sp!, {lr}
4395 bx lr
4396.endfunc
4397.ltorg
4398.func Delay9, DELAY9
4399DELAY9:
4400 adds r0, r0, r0, LSL #3 /* r0 = r0 * 9 */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004401DELAYLOOP:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004402 beq DELAY9_done
4403 subs r0, r0, #1
4404 b DELAYLOOP
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004405DELAY9_done:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004406 bx lr
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004407.endfunc
Linus Torvalds1da177e2005-04-16 15:20:36 -07004408
4409.func SP_Init, SP_INIT
4410SP_INIT:
4411 mov r1, #SP_SWRST
4412 ldr r0, =SP_BASE
4413 str r1, [r0, #SP_CR] /* reset the SPI */
4414 mov r1, #0
4415 str r1, [r0, #SP_CR] /* release SPI from reset state */
4416 mov r1, #SP_SPIEN
4417 str r1, [r0, #SP_MR] /* set the SPI to MASTER mode*/
4418 str r1, [r0, #SP_CR] /* enable the SPI */
4419
4420/* My guess would be this turns on the SPI clock */
4421 ldr r3, =SPI_CGEN_BASE
4422 ldr r1, [r3, #28]
4423 orr r1, r1, #0x2000
4424 str r1, [r3, #28]
4425
4426 ldr r1, =0x2000c01
4427 str r1, [r0, #SP_CSR0]
4428 ldr r1, =0x2000201
4429 str r1, [r0, #SP_CSR1]
4430 str r1, [r0, #SP_CSR2]
4431 str r1, [r0, #SP_CSR3]
4432 ldr r1, [r0, #SP_SR]
4433 ldr r0, [r0, #SP_RDR]
4434 bx lr
4435.endfunc
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004436.func NVRAM_Init, NVRAM_INIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07004437NVRAM_INIT:
4438 ldr r1, =SP_BASE
4439 ldr r0, [r1, #SP_RDR]
4440 mov r0, #NVRAM_CMD_RDSR
4441 str r0, [r1, #SP_TDR]
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004442SP_loop1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004443 ldr r0, [r1, #SP_SR]
4444 tst r0, #SP_TDRE
4445 beq SP_loop1
4446
4447 mov r0, #SPI_8CLOCKS
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004448 str r0, [r1, #SP_TDR]
4449SP_loop2:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004450 ldr r0, [r1, #SP_SR]
4451 tst r0, #SP_TDRE
4452 beq SP_loop2
4453
4454 ldr r0, [r1, #SP_RDR]
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004455SP_loop3:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004456 ldr r0, [r1, #SP_SR]
4457 tst r0, #SP_RDRF
4458 beq SP_loop3
4459
4460 ldr r0, [r1, #SP_RDR]
4461 and r0, r0, #255
4462 bx lr
4463.endfunc
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004464
Linus Torvalds1da177e2005-04-16 15:20:36 -07004465.func NVRAM_Xfer, NVRAM_XFER
4466 /* r0 = dest address */
4467 /* r1 = not used */
4468 /* r2 = src address within NVRAM */
4469 /* r3 = length */
4470NVRAM_XFER:
4471 stmdb sp!, {r4, r5, lr}
4472 mov r5, r0 /* save r0 (dest address) */
4473 mov r4, r3 /* save r3 (length) */
4474 mov r0, r2, LSR #5 /* SPI memories put A8 in the command field */
4475 and r0, r0, #8
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004476 add r0, r0, #NVRAM_CMD_READ
Linus Torvalds1da177e2005-04-16 15:20:36 -07004477 ldr r1, =NVRAM_SCRATCH
4478 strb r0, [r1, #0] /* save command in NVRAM_SCRATCH[0] */
4479 strb r2, [r1, #1] /* save low byte of source address in NVRAM_SCRATCH[1] */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004480_local1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004481 bl NVRAM_INIT
4482 tst r0, #NVRAM_SR_RDY
4483 bne _local1
4484 mov r0, #20
4485 bl DELAY9
4486 mov r2, r4 /* length */
4487 mov r1, r5 /* dest address */
4488 mov r0, #2 /* bytes to transfer in command */
4489 bl NVRAM_XFER2
4490 ldmia sp!, {r4, r5, lr}
4491 bx lr
4492.endfunc
4493
4494.func NVRAM_Xfer2, NVRAM_XFER2
4495NVRAM_XFER2:
4496 stmdb sp!, {r4, r5, r6, lr}
4497 ldr r4, =SP_BASE
4498 mov r3, #0
4499 cmp r0, #0
4500 bls _local2
4501 ldr r5, =NVRAM_SCRATCH
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004502_local4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004503 ldrb r6, [r5, r3]
4504 str r6, [r4, #SP_TDR]
4505_local3:
4506 ldr r6, [r4, #SP_SR]
4507 tst r6, #SP_TDRE
4508 beq _local3
4509 add r3, r3, #1
4510 cmp r3, r0 /* r0 is # of bytes to send out (command+addr) */
4511 blo _local4
4512_local2:
4513 mov r3, #SPI_8CLOCKS
4514 str r3, [r4, #SP_TDR]
4515 ldr r0, [r4, #SP_RDR]
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004516_local5:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004517 ldr r0, [r4, #SP_SR]
4518 tst r0, #SP_RDRF
4519 beq _local5
4520 ldr r0, [r4, #SP_RDR] /* what's this byte? It's the byte read while writing the TDR -- nonsense, because the NVRAM doesn't read and write at the same time */
4521 mov r0, #0
4522 cmp r2, #0 /* r2 is # of bytes to copy in */
4523 bls _local6
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004524_local7:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004525 ldr r5, [r4, #SP_SR]
4526 tst r5, #SP_TDRE
4527 beq _local7
4528 str r3, [r4, #SP_TDR] /* r3 has SPI_8CLOCKS */
Carlo Perassi4d791aa2005-11-13 15:02:15 +03004529_local8:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004530 ldr r5, [r4, #SP_SR]
4531 tst r5, #SP_RDRF
4532 beq _local8
4533 ldr r5, [r4, #SP_RDR] /* but didn't we read this byte above? */
4534 strb r5, [r1], #1 /* postindexed */
4535 add r0, r0, #1
4536 cmp r0, r2
4537 blo _local7 /* since we don't send another address, the NVRAM must be capable of sequential reads */
4538_local6:
4539 mov r0, #200
4540 bl DELAY9
4541 ldmia sp!, {r4, r5, r6, lr}
4542 bx lr
4543#endif