blob: a08aadfa09acbd4a7a456094e8e091f1d9140ad3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*======================================================================
2
3 Aironet driver for 4500 and 4800 series cards
4
5 This code is released under both the GPL version 2 and BSD licenses.
6 Either license may be used. The respective licenses are found at
7 the end of this file.
8
9 This code was developed by Benjamin Reed <breed@users.sourceforge.net>
10 including portions of which come from the Aironet PC4500
11 Developer's Reference Manual and used with permission. Copyright
12 (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use
13 code in the Developer's manual was granted for this driver by
14 Aironet. Major code contributions were received from Javier Achirica
15 <achirica@users.sourceforge.net> and Jean Tourrilhes <jt@hpl.hp.com>.
16 Code was also integrated from the Cisco Aironet driver for Linux.
17 Support for MPI350 cards was added by Fabrice Bellet
18 <fabrice@bellet.info>.
19
20======================================================================*/
21
Herbert Xuf12cc202006-08-22 20:36:13 +100022#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/init.h>
24
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/proc_fs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#include <linux/sched.h>
30#include <linux/ptrace.h>
31#include <linux/slab.h>
32#include <linux/string.h>
33#include <linux/timer.h>
34#include <linux/interrupt.h>
35#include <linux/in.h>
36#include <linux/bitops.h>
David Hardeman378f0582005-09-17 17:55:31 +100037#include <linux/scatterlist.h>
Adrian Bunka39d3e72006-01-21 01:35:15 +010038#include <linux/crypto.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <asm/io.h>
40#include <asm/system.h>
Al Viro593c2b92007-12-17 15:09:34 -050041#include <asm/unaligned.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
43#include <linux/netdevice.h>
44#include <linux/etherdevice.h>
45#include <linux/skbuff.h>
46#include <linux/if_arp.h>
47#include <linux/ioport.h>
48#include <linux/pci.h>
49#include <asm/uaccess.h>
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -070050#include <linux/kthread.h>
Nigel Cunningham7dfb7102006-12-06 20:34:23 -080051#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
Johannes Berg2c7060022008-10-30 22:09:54 +010053#include <linux/ieee80211.h>
54
Adrian Bunkd3808762005-11-05 17:42:27 +010055#include "airo.h"
56
Michal Schmidt1138c372007-06-29 15:33:41 +020057#define DRV_NAME "airo"
58
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#ifdef CONFIG_PCI
60static struct pci_device_id card_ids[] = {
61 { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, },
62 { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID },
63 { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, },
64 { 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, },
65 { 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, },
66 { 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, },
67 { 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, },
68 { 0, }
69};
70MODULE_DEVICE_TABLE(pci, card_ids);
71
72static int airo_pci_probe(struct pci_dev *, const struct pci_device_id *);
73static void airo_pci_remove(struct pci_dev *);
Pavel Machek05adc3b2005-04-16 15:25:25 -070074static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state);
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static int airo_pci_resume(struct pci_dev *pdev);
76
77static struct pci_driver airo_driver = {
Michal Schmidt1138c372007-06-29 15:33:41 +020078 .name = DRV_NAME,
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 .id_table = card_ids,
80 .probe = airo_pci_probe,
81 .remove = __devexit_p(airo_pci_remove),
82 .suspend = airo_pci_suspend,
83 .resume = airo_pci_resume,
84};
85#endif /* CONFIG_PCI */
86
87/* Include Wireless Extension definition and check version - Jean II */
88#include <linux/wireless.h>
Pavel Macheke292c732008-06-25 12:25:53 +020089#define WIRELESS_SPY /* enable iwspy support */
90#include <net/iw_handler.h> /* New driver API */
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Pavel Macheke292c732008-06-25 12:25:53 +020092#define CISCO_EXT /* enable Cisco extensions */
Linus Torvalds1da177e2005-04-16 15:20:36 -070093#ifdef CISCO_EXT
94#include <linux/delay.h>
95#endif
96
Linus Torvalds1da177e2005-04-16 15:20:36 -070097/* Hack to do some power saving */
98#define POWER_ON_DOWN
99
100/* As you can see this list is HUGH!
101 I really don't know what a lot of these counts are about, but they
102 are all here for completeness. If the IGNLABEL macro is put in
103 infront of the label, that statistic will not be included in the list
104 of statistics in the /proc filesystem */
105
106#define IGNLABEL(comment) NULL
107static char *statsLabels[] = {
108 "RxOverrun",
109 IGNLABEL("RxPlcpCrcErr"),
110 IGNLABEL("RxPlcpFormatErr"),
111 IGNLABEL("RxPlcpLengthErr"),
112 "RxMacCrcErr",
113 "RxMacCrcOk",
114 "RxWepErr",
115 "RxWepOk",
116 "RetryLong",
117 "RetryShort",
118 "MaxRetries",
119 "NoAck",
120 "NoCts",
121 "RxAck",
122 "RxCts",
123 "TxAck",
124 "TxRts",
125 "TxCts",
126 "TxMc",
127 "TxBc",
128 "TxUcFrags",
129 "TxUcPackets",
130 "TxBeacon",
131 "RxBeacon",
132 "TxSinColl",
133 "TxMulColl",
134 "DefersNo",
135 "DefersProt",
136 "DefersEngy",
137 "DupFram",
138 "RxFragDisc",
139 "TxAged",
140 "RxAged",
141 "LostSync-MaxRetry",
142 "LostSync-MissedBeacons",
143 "LostSync-ArlExceeded",
144 "LostSync-Deauth",
145 "LostSync-Disassoced",
146 "LostSync-TsfTiming",
147 "HostTxMc",
148 "HostTxBc",
149 "HostTxUc",
150 "HostTxFail",
151 "HostRxMc",
152 "HostRxBc",
153 "HostRxUc",
154 "HostRxDiscard",
155 IGNLABEL("HmacTxMc"),
156 IGNLABEL("HmacTxBc"),
157 IGNLABEL("HmacTxUc"),
158 IGNLABEL("HmacTxFail"),
159 IGNLABEL("HmacRxMc"),
160 IGNLABEL("HmacRxBc"),
161 IGNLABEL("HmacRxUc"),
162 IGNLABEL("HmacRxDiscard"),
163 IGNLABEL("HmacRxAccepted"),
164 "SsidMismatch",
165 "ApMismatch",
166 "RatesMismatch",
167 "AuthReject",
168 "AuthTimeout",
169 "AssocReject",
170 "AssocTimeout",
171 IGNLABEL("ReasonOutsideTable"),
172 IGNLABEL("ReasonStatus1"),
173 IGNLABEL("ReasonStatus2"),
174 IGNLABEL("ReasonStatus3"),
175 IGNLABEL("ReasonStatus4"),
176 IGNLABEL("ReasonStatus5"),
177 IGNLABEL("ReasonStatus6"),
178 IGNLABEL("ReasonStatus7"),
179 IGNLABEL("ReasonStatus8"),
180 IGNLABEL("ReasonStatus9"),
181 IGNLABEL("ReasonStatus10"),
182 IGNLABEL("ReasonStatus11"),
183 IGNLABEL("ReasonStatus12"),
184 IGNLABEL("ReasonStatus13"),
185 IGNLABEL("ReasonStatus14"),
186 IGNLABEL("ReasonStatus15"),
187 IGNLABEL("ReasonStatus16"),
188 IGNLABEL("ReasonStatus17"),
189 IGNLABEL("ReasonStatus18"),
190 IGNLABEL("ReasonStatus19"),
191 "RxMan",
192 "TxMan",
193 "RxRefresh",
194 "TxRefresh",
195 "RxPoll",
196 "TxPoll",
197 "HostRetries",
198 "LostSync-HostReq",
199 "HostTxBytes",
200 "HostRxBytes",
201 "ElapsedUsec",
202 "ElapsedSec",
203 "LostSyncBetterAP",
204 "PrivacyMismatch",
205 "Jammed",
206 "DiscRxNotWepped",
207 "PhyEleMismatch",
208 (char*)-1 };
209#ifndef RUN_AT
210#define RUN_AT(x) (jiffies+(x))
211#endif
212
213
214/* These variables are for insmod, since it seems that the rates
215 can only be set in setup_card. Rates should be a comma separated
216 (no spaces) list of rates (up to 8). */
217
218static int rates[8];
219static int basic_rate;
220static char *ssids[3];
221
222static int io[4];
223static int irq[4];
224
225static
226int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at.
227 0 means no limit. For old cards this was 4 */
228
229static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */
230static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read
231 the bap, needed on some older cards and buses. */
232static int adhoc;
233
234static int probe = 1;
235
236static int proc_uid /* = 0 */;
237
238static int proc_gid /* = 0 */;
239
240static int airo_perm = 0555;
241
242static int proc_perm = 0644;
243
244MODULE_AUTHOR("Benjamin Reed");
245MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet \
Bill Nottinghamd73ae552007-07-27 19:43:17 -0400246cards. Direct support for ISA/PCI/MPI cards and support \
247for PCMCIA when used with airo_cs.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248MODULE_LICENSE("Dual BSD/GPL");
249MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350");
250module_param_array(io, int, NULL, 0);
251module_param_array(irq, int, NULL, 0);
252module_param(basic_rate, int, 0);
253module_param_array(rates, int, NULL, 0);
254module_param_array(ssids, charp, NULL, 0);
255module_param(auto_wep, int, 0);
256MODULE_PARM_DESC(auto_wep, "If non-zero, the driver will keep looping through \
257the authentication options until an association is made. The value of \
258auto_wep is number of the wep keys to check. A value of 2 will try using \
259the key at index 0 and index 1.");
260module_param(aux_bap, int, 0);
261MODULE_PARM_DESC(aux_bap, "If non-zero, the driver will switch into a mode \
262than seems to work better for older cards with some older buses. Before \
263switching it checks that the switch is needed.");
264module_param(maxencrypt, int, 0);
265MODULE_PARM_DESC(maxencrypt, "The maximum speed that the card can do \
266encryption. Units are in 512kbs. Zero (default) means there is no limit. \
267Older cards used to be limited to 2mbs (4).");
268module_param(adhoc, int, 0);
269MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode.");
270module_param(probe, int, 0);
271MODULE_PARM_DESC(probe, "If zero, the driver won't start the card.");
272
273module_param(proc_uid, int, 0);
274MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to.");
275module_param(proc_gid, int, 0);
276MODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to.");
277module_param(airo_perm, int, 0);
278MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet.");
279module_param(proc_perm, int, 0);
280MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc");
281
282/* This is a kind of sloppy hack to get this information to OUT4500 and
283 IN4500. I would be extremely interested in the situation where this
284 doesn't work though!!! */
Pavel Macheke292c732008-06-25 12:25:53 +0200285static int do8bitIO /* = 0 */;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
287/* Return codes */
288#define SUCCESS 0
289#define ERROR -1
290#define NO_PACKET -2
291
292/* Commands */
293#define NOP2 0x0000
294#define MAC_ENABLE 0x0001
295#define MAC_DISABLE 0x0002
296#define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */
297#define CMD_SOFTRESET 0x0004
298#define HOSTSLEEP 0x0005
299#define CMD_MAGIC_PKT 0x0006
300#define CMD_SETWAKEMASK 0x0007
301#define CMD_READCFG 0x0008
302#define CMD_SETMODE 0x0009
303#define CMD_ALLOCATETX 0x000a
304#define CMD_TRANSMIT 0x000b
305#define CMD_DEALLOCATETX 0x000c
306#define NOP 0x0010
307#define CMD_WORKAROUND 0x0011
308#define CMD_ALLOCATEAUX 0x0020
309#define CMD_ACCESS 0x0021
310#define CMD_PCIBAP 0x0022
311#define CMD_PCIAUX 0x0023
312#define CMD_ALLOCBUF 0x0028
313#define CMD_GETTLV 0x0029
314#define CMD_PUTTLV 0x002a
315#define CMD_DELTLV 0x002b
316#define CMD_FINDNEXTTLV 0x002c
317#define CMD_PSPNODES 0x0030
318#define CMD_SETCW 0x0031
319#define CMD_SETPCF 0x0032
320#define CMD_SETPHYREG 0x003e
321#define CMD_TXTEST 0x003f
322#define MAC_ENABLETX 0x0101
323#define CMD_LISTBSS 0x0103
324#define CMD_SAVECFG 0x0108
325#define CMD_ENABLEAUX 0x0111
326#define CMD_WRITERID 0x0121
327#define CMD_USEPSPNODES 0x0130
328#define MAC_ENABLERX 0x0201
329
330/* Command errors */
331#define ERROR_QUALIF 0x00
332#define ERROR_ILLCMD 0x01
333#define ERROR_ILLFMT 0x02
334#define ERROR_INVFID 0x03
335#define ERROR_INVRID 0x04
336#define ERROR_LARGE 0x05
337#define ERROR_NDISABL 0x06
338#define ERROR_ALLOCBSY 0x07
339#define ERROR_NORD 0x0B
340#define ERROR_NOWR 0x0C
341#define ERROR_INVFIDTX 0x0D
342#define ERROR_TESTACT 0x0E
343#define ERROR_TAGNFND 0x12
344#define ERROR_DECODE 0x20
345#define ERROR_DESCUNAV 0x21
346#define ERROR_BADLEN 0x22
347#define ERROR_MODE 0x80
348#define ERROR_HOP 0x81
349#define ERROR_BINTER 0x82
350#define ERROR_RXMODE 0x83
351#define ERROR_MACADDR 0x84
352#define ERROR_RATES 0x85
353#define ERROR_ORDER 0x86
354#define ERROR_SCAN 0x87
355#define ERROR_AUTH 0x88
356#define ERROR_PSMODE 0x89
357#define ERROR_RTYPE 0x8A
358#define ERROR_DIVER 0x8B
359#define ERROR_SSID 0x8C
360#define ERROR_APLIST 0x8D
361#define ERROR_AUTOWAKE 0x8E
362#define ERROR_LEAP 0x8F
363
364/* Registers */
365#define COMMAND 0x00
366#define PARAM0 0x02
367#define PARAM1 0x04
368#define PARAM2 0x06
369#define STATUS 0x08
370#define RESP0 0x0a
371#define RESP1 0x0c
372#define RESP2 0x0e
373#define LINKSTAT 0x10
374#define SELECT0 0x18
375#define OFFSET0 0x1c
376#define RXFID 0x20
377#define TXALLOCFID 0x22
378#define TXCOMPLFID 0x24
379#define DATA0 0x36
380#define EVSTAT 0x30
381#define EVINTEN 0x32
382#define EVACK 0x34
383#define SWS0 0x28
384#define SWS1 0x2a
385#define SWS2 0x2c
386#define SWS3 0x2e
387#define AUXPAGE 0x3A
388#define AUXOFF 0x3C
389#define AUXDATA 0x3E
390
391#define FID_TX 1
392#define FID_RX 2
393/* Offset into aux memory for descriptors */
394#define AUX_OFFSET 0x800
395/* Size of allocated packets */
396#define PKTSIZE 1840
397#define RIDSIZE 2048
398/* Size of the transmit queue */
399#define MAXTXQ 64
400
401/* BAP selectors */
Pavel Macheke292c732008-06-25 12:25:53 +0200402#define BAP0 0 /* Used for receiving packets */
403#define BAP1 2 /* Used for xmiting packets and working with RIDS */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
405/* Flags */
406#define COMMAND_BUSY 0x8000
407
408#define BAP_BUSY 0x8000
409#define BAP_ERR 0x4000
410#define BAP_DONE 0x2000
411
412#define PROMISC 0xffff
413#define NOPROMISC 0x0000
414
415#define EV_CMD 0x10
416#define EV_CLEARCOMMANDBUSY 0x4000
417#define EV_RX 0x01
418#define EV_TX 0x02
419#define EV_TXEXC 0x04
420#define EV_ALLOC 0x08
421#define EV_LINK 0x80
422#define EV_AWAKE 0x100
423#define EV_TXCPY 0x400
424#define EV_UNKNOWN 0x800
425#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */
426#define EV_AWAKEN 0x2000
427#define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC)
428
429#ifdef CHECK_UNKNOWN_INTS
430#define IGNORE_INTS ( EV_CMD | EV_UNKNOWN)
431#else
432#define IGNORE_INTS (~STATUS_INTS)
433#endif
434
435/* RID TYPES */
436#define RID_RW 0x20
437
438/* The RIDs */
439#define RID_CAPABILITIES 0xFF00
440#define RID_APINFO 0xFF01
441#define RID_RADIOINFO 0xFF02
442#define RID_UNKNOWN3 0xFF03
443#define RID_RSSI 0xFF04
444#define RID_CONFIG 0xFF10
445#define RID_SSID 0xFF11
446#define RID_APLIST 0xFF12
447#define RID_DRVNAME 0xFF13
448#define RID_ETHERENCAP 0xFF14
449#define RID_WEP_TEMP 0xFF15
450#define RID_WEP_PERM 0xFF16
451#define RID_MODULATION 0xFF17
452#define RID_OPTIONS 0xFF18
453#define RID_ACTUALCONFIG 0xFF20 /*readonly*/
454#define RID_FACTORYCONFIG 0xFF21
455#define RID_UNKNOWN22 0xFF22
456#define RID_LEAPUSERNAME 0xFF23
457#define RID_LEAPPASSWORD 0xFF24
458#define RID_STATUS 0xFF50
459#define RID_BEACON_HST 0xFF51
460#define RID_BUSY_HST 0xFF52
461#define RID_RETRIES_HST 0xFF53
462#define RID_UNKNOWN54 0xFF54
463#define RID_UNKNOWN55 0xFF55
464#define RID_UNKNOWN56 0xFF56
465#define RID_MIC 0xFF57
466#define RID_STATS16 0xFF60
467#define RID_STATS16DELTA 0xFF61
468#define RID_STATS16DELTACLEAR 0xFF62
469#define RID_STATS 0xFF68
470#define RID_STATSDELTA 0xFF69
471#define RID_STATSDELTACLEAR 0xFF6A
472#define RID_ECHOTEST_RID 0xFF70
473#define RID_ECHOTEST_RESULTS 0xFF71
474#define RID_BSSLISTFIRST 0xFF72
475#define RID_BSSLISTNEXT 0xFF73
Dan Williams3c304952006-04-15 12:26:18 -0400476#define RID_WPA_BSSLISTFIRST 0xFF74
477#define RID_WPA_BSSLISTNEXT 0xFF75
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478
479typedef struct {
480 u16 cmd;
481 u16 parm0;
482 u16 parm1;
483 u16 parm2;
484} Cmd;
485
486typedef struct {
487 u16 status;
488 u16 rsp0;
489 u16 rsp1;
490 u16 rsp2;
491} Resp;
492
493/*
494 * Rids and endian-ness: The Rids will always be in cpu endian, since
495 * this all the patches from the big-endian guys end up doing that.
496 * so all rid access should use the read/writeXXXRid routines.
497 */
498
499/* This is redundant for x86 archs, but it seems necessary for ARM */
500#pragma pack(1)
501
502/* This structure came from an email sent to me from an engineer at
503 aironet for inclusion into this driver */
504typedef struct {
Al Viro4293ea32007-12-19 19:21:51 -0500505 __le16 len;
506 __le16 kindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 u8 mac[ETH_ALEN];
Al Viro4293ea32007-12-19 19:21:51 -0500508 __le16 klen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 u8 key[16];
510} WepKeyRid;
511
512/* These structures are from the Aironet's PC4500 Developers Manual */
513typedef struct {
Al Viro0dd22122007-12-17 16:11:57 -0500514 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 u8 ssid[32];
516} Ssid;
517
518typedef struct {
Al Viro0dd22122007-12-17 16:11:57 -0500519 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 Ssid ssids[3];
521} SsidRid;
522
523typedef struct {
Al Viro3eb9b412007-12-21 00:00:35 -0500524 __le16 len;
525 __le16 modulation;
526#define MOD_DEFAULT cpu_to_le16(0)
527#define MOD_CCK cpu_to_le16(1)
528#define MOD_MOK cpu_to_le16(2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529} ModulationRid;
530
531typedef struct {
Al Viro3eb9b412007-12-21 00:00:35 -0500532 __le16 len; /* sizeof(ConfigRid) */
533 __le16 opmode; /* operating mode */
534#define MODE_STA_IBSS cpu_to_le16(0)
535#define MODE_STA_ESS cpu_to_le16(1)
536#define MODE_AP cpu_to_le16(2)
537#define MODE_AP_RPTR cpu_to_le16(3)
538#define MODE_CFG_MASK cpu_to_le16(0xff)
539#define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */
540#define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */
541#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extenstions */
542#define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */
543#define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */
544#define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */
545#define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */
546#define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */
547#define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */
548 __le16 rmode; /* receive mode */
549#define RXMODE_BC_MC_ADDR cpu_to_le16(0)
550#define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */
551#define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */
552#define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */
553#define RXMODE_RFMON_ANYBSS cpu_to_le16(4)
554#define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */
555#define RXMODE_MASK cpu_to_le16(255)
556#define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */
557#define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER)
558#define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */
559 __le16 fragThresh;
560 __le16 rtsThres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 u8 macAddr[ETH_ALEN];
562 u8 rates[8];
Al Viro3eb9b412007-12-21 00:00:35 -0500563 __le16 shortRetryLimit;
564 __le16 longRetryLimit;
565 __le16 txLifetime; /* in kusec */
566 __le16 rxLifetime; /* in kusec */
567 __le16 stationary;
568 __le16 ordering;
569 __le16 u16deviceType; /* for overriding device type */
570 __le16 cfpRate;
571 __le16 cfpDuration;
572 __le16 _reserved1[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 /*---------- Scanning/Associating ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500574 __le16 scanMode;
575#define SCANMODE_ACTIVE cpu_to_le16(0)
576#define SCANMODE_PASSIVE cpu_to_le16(1)
577#define SCANMODE_AIROSCAN cpu_to_le16(2)
578 __le16 probeDelay; /* in kusec */
579 __le16 probeEnergyTimeout; /* in kusec */
580 __le16 probeResponseTimeout;
581 __le16 beaconListenTimeout;
582 __le16 joinNetTimeout;
583 __le16 authTimeout;
584 __le16 authType;
585#define AUTH_OPEN cpu_to_le16(0x1)
586#define AUTH_ENCRYPT cpu_to_le16(0x101)
587#define AUTH_SHAREDKEY cpu_to_le16(0x102)
588#define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200)
589 __le16 associationTimeout;
590 __le16 specifiedApTimeout;
591 __le16 offlineScanInterval;
592 __le16 offlineScanDuration;
593 __le16 linkLossDelay;
594 __le16 maxBeaconLostTime;
595 __le16 refreshInterval;
596#define DISABLE_REFRESH cpu_to_le16(0xFFFF)
597 __le16 _reserved1a[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 /*---------- Power save operation ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500599 __le16 powerSaveMode;
600#define POWERSAVE_CAM cpu_to_le16(0)
601#define POWERSAVE_PSP cpu_to_le16(1)
602#define POWERSAVE_PSPCAM cpu_to_le16(2)
603 __le16 sleepForDtims;
604 __le16 listenInterval;
605 __le16 fastListenInterval;
606 __le16 listenDecay;
607 __le16 fastListenDelay;
608 __le16 _reserved2[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 /*---------- Ap/Ibss config items ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500610 __le16 beaconPeriod;
611 __le16 atimDuration;
612 __le16 hopPeriod;
613 __le16 channelSet;
614 __le16 channel;
615 __le16 dtimPeriod;
616 __le16 bridgeDistance;
617 __le16 radioID;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 /*---------- Radio configuration ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500619 __le16 radioType;
620#define RADIOTYPE_DEFAULT cpu_to_le16(0)
621#define RADIOTYPE_802_11 cpu_to_le16(1)
622#define RADIOTYPE_LEGACY cpu_to_le16(2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 u8 rxDiversity;
624 u8 txDiversity;
Al Viro3eb9b412007-12-21 00:00:35 -0500625 __le16 txPower;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626#define TXPOWER_DEFAULT 0
Al Viro3eb9b412007-12-21 00:00:35 -0500627 __le16 rssiThreshold;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628#define RSSI_DEFAULT 0
Al Viro3eb9b412007-12-21 00:00:35 -0500629 __le16 modulation;
630#define PREAMBLE_AUTO cpu_to_le16(0)
631#define PREAMBLE_LONG cpu_to_le16(1)
632#define PREAMBLE_SHORT cpu_to_le16(2)
633 __le16 preamble;
634 __le16 homeProduct;
635 __le16 radioSpecific;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 /*---------- Aironet Extensions ----------*/
637 u8 nodeName[16];
Al Viro3eb9b412007-12-21 00:00:35 -0500638 __le16 arlThreshold;
639 __le16 arlDecay;
640 __le16 arlDelay;
641 __le16 _reserved4[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 /*---------- Aironet Extensions ----------*/
643 u8 magicAction;
644#define MAGIC_ACTION_STSCHG 1
645#define MAGIC_ACTION_RESUME 2
646#define MAGIC_IGNORE_MCAST (1<<8)
647#define MAGIC_IGNORE_BCAST (1<<9)
648#define MAGIC_SWITCH_TO_PSP (0<<10)
649#define MAGIC_STAY_IN_CAM (1<<10)
650 u8 magicControl;
Al Viro3eb9b412007-12-21 00:00:35 -0500651 __le16 autoWake;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652} ConfigRid;
653
654typedef struct {
Al Viro329e2c02007-12-20 22:58:57 -0500655 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 u8 mac[ETH_ALEN];
Al Viro329e2c02007-12-20 22:58:57 -0500657 __le16 mode;
658 __le16 errorCode;
659 __le16 sigQuality;
660 __le16 SSIDlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 char SSID[32];
662 char apName[16];
663 u8 bssid[4][ETH_ALEN];
Al Viro329e2c02007-12-20 22:58:57 -0500664 __le16 beaconPeriod;
665 __le16 dimPeriod;
666 __le16 atimDuration;
667 __le16 hopPeriod;
668 __le16 channelSet;
669 __le16 channel;
670 __le16 hopsToBackbone;
671 __le16 apTotalLoad;
672 __le16 generatedLoad;
673 __le16 accumulatedArl;
674 __le16 signalQuality;
675 __le16 currentXmitRate;
676 __le16 apDevExtensions;
677 __le16 normalizedSignalStrength;
678 __le16 shortPreamble;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 u8 apIP[4];
680 u8 noisePercent; /* Noise percent in last second */
681 u8 noisedBm; /* Noise dBm in last second */
682 u8 noiseAvePercent; /* Noise percent in last minute */
683 u8 noiseAvedBm; /* Noise dBm in last minute */
684 u8 noiseMaxPercent; /* Highest noise percent in last minute */
685 u8 noiseMaxdBm; /* Highest noise dbm in last minute */
Al Viro329e2c02007-12-20 22:58:57 -0500686 __le16 load;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 u8 carrier[4];
Al Viro329e2c02007-12-20 22:58:57 -0500688 __le16 assocStatus;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689#define STAT_NOPACKETS 0
690#define STAT_NOCARRIERSET 10
691#define STAT_GOTCARRIERSET 11
692#define STAT_WRONGSSID 20
693#define STAT_BADCHANNEL 25
694#define STAT_BADBITRATES 30
695#define STAT_BADPRIVACY 35
696#define STAT_APFOUND 40
697#define STAT_APREJECTED 50
698#define STAT_AUTHENTICATING 60
699#define STAT_DEAUTHENTICATED 61
700#define STAT_AUTHTIMEOUT 62
701#define STAT_ASSOCIATING 70
702#define STAT_DEASSOCIATED 71
703#define STAT_ASSOCTIMEOUT 72
704#define STAT_NOTAIROAP 73
705#define STAT_ASSOCIATED 80
706#define STAT_LEAPING 90
707#define STAT_LEAPFAILED 91
708#define STAT_LEAPTIMEDOUT 92
709#define STAT_LEAPCOMPLETE 93
710} StatusRid;
711
712typedef struct {
Al Viroa23ace52007-12-19 22:24:16 -0500713 __le16 len;
714 __le16 spacer;
715 __le32 vals[100];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716} StatsRid;
717
718
719typedef struct {
Al Viroa7497162007-12-20 17:49:41 -0500720 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 u8 ap[4][ETH_ALEN];
722} APListRid;
723
724typedef struct {
Al Viro56d81bd2007-12-20 17:18:35 -0500725 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 char oui[3];
727 char zero;
Al Viro56d81bd2007-12-20 17:18:35 -0500728 __le16 prodNum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 char manName[32];
730 char prodName[16];
731 char prodVer[8];
732 char factoryAddr[ETH_ALEN];
733 char aironetAddr[ETH_ALEN];
Al Viro56d81bd2007-12-20 17:18:35 -0500734 __le16 radioType;
735 __le16 country;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 char callid[ETH_ALEN];
737 char supportedRates[8];
738 char rxDiversity;
739 char txDiversity;
Al Viro56d81bd2007-12-20 17:18:35 -0500740 __le16 txPowerLevels[8];
741 __le16 hardVer;
742 __le16 hardCap;
743 __le16 tempRange;
744 __le16 softVer;
745 __le16 softSubVer;
746 __le16 interfaceVer;
747 __le16 softCap;
748 __le16 bootBlockVer;
749 __le16 requiredHard;
750 __le16 extSoftCap;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751} CapabilityRid;
752
Dan Williams3c304952006-04-15 12:26:18 -0400753
754/* Only present on firmware >= 5.30.17 */
755typedef struct {
Al Viro17e70492007-12-19 18:56:37 -0500756 __le16 unknown[4];
Dan Williams3c304952006-04-15 12:26:18 -0400757 u8 fixed[12]; /* WLAN management frame */
758 u8 iep[624];
759} BSSListRidExtra;
760
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761typedef struct {
Al Viro17e70492007-12-19 18:56:37 -0500762 __le16 len;
763 __le16 index; /* First is 0 and 0xffff means end of list */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764#define RADIO_FH 1 /* Frequency hopping radio type */
765#define RADIO_DS 2 /* Direct sequence radio type */
766#define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */
Al Viro17e70492007-12-19 18:56:37 -0500767 __le16 radioType;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 u8 bssid[ETH_ALEN]; /* Mac address of the BSS */
769 u8 zero;
770 u8 ssidLen;
771 u8 ssid[32];
Al Viro17e70492007-12-19 18:56:37 -0500772 __le16 dBm;
773#define CAP_ESS cpu_to_le16(1<<0)
774#define CAP_IBSS cpu_to_le16(1<<1)
775#define CAP_PRIVACY cpu_to_le16(1<<4)
776#define CAP_SHORTHDR cpu_to_le16(1<<5)
777 __le16 cap;
778 __le16 beaconInterval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 u8 rates[8]; /* Same as rates for config rid */
780 struct { /* For frequency hopping only */
Al Viro17e70492007-12-19 18:56:37 -0500781 __le16 dwell;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 u8 hopSet;
783 u8 hopPattern;
784 u8 hopIndex;
785 u8 fill;
786 } fh;
Al Viro17e70492007-12-19 18:56:37 -0500787 __le16 dsChannel;
788 __le16 atimWindow;
Dan Williams3c304952006-04-15 12:26:18 -0400789
790 /* Only present on firmware >= 5.30.17 */
791 BSSListRidExtra extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792} BSSListRid;
793
794typedef struct {
Dan Williams9e75af32006-03-16 13:46:29 -0500795 BSSListRid bss;
796 struct list_head list;
797} BSSListElement;
798
799typedef struct {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 u8 rssipct;
801 u8 rssidBm;
802} tdsRssiEntry;
803
804typedef struct {
805 u16 len;
806 tdsRssiEntry x[256];
807} tdsRssiRid;
808
809typedef struct {
810 u16 len;
811 u16 state;
812 u16 multicastValid;
813 u8 multicast[16];
814 u16 unicastValid;
815 u8 unicast[16];
816} MICRid;
817
818typedef struct {
Al Viro593c2b92007-12-17 15:09:34 -0500819 __be16 typelen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
821 union {
822 u8 snap[8];
823 struct {
824 u8 dsap;
825 u8 ssap;
826 u8 control;
827 u8 orgcode[3];
828 u8 fieldtype[2];
829 } llc;
830 } u;
Al Viro593c2b92007-12-17 15:09:34 -0500831 __be32 mic;
832 __be32 seq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833} MICBuffer;
834
835typedef struct {
836 u8 da[ETH_ALEN];
837 u8 sa[ETH_ALEN];
838} etherHead;
839
840#pragma pack()
841
842#define TXCTL_TXOK (1<<1) /* report if tx is ok */
843#define TXCTL_TXEX (1<<2) /* report if tx fails */
844#define TXCTL_802_3 (0<<3) /* 802.3 packet */
845#define TXCTL_802_11 (1<<3) /* 802.11 mac packet */
846#define TXCTL_ETHERNET (0<<4) /* payload has ethertype */
847#define TXCTL_LLC (1<<4) /* payload is llc */
848#define TXCTL_RELEASE (0<<5) /* release after completion */
849#define TXCTL_NORELEASE (1<<5) /* on completion returns to host */
850
851#define BUSY_FID 0x10000
852
853#ifdef CISCO_EXT
854#define AIROMAGIC 0xa55a
855/* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */
856#ifdef SIOCIWFIRSTPRIV
857#ifdef SIOCDEVPRIVATE
858#define AIROOLDIOCTL SIOCDEVPRIVATE
859#define AIROOLDIDIFC AIROOLDIOCTL + 1
860#endif /* SIOCDEVPRIVATE */
861#else /* SIOCIWFIRSTPRIV */
862#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
863#endif /* SIOCIWFIRSTPRIV */
864/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably
865 * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root
866 * only and don't return the modified struct ifreq to the application which
867 * is usually a problem. - Jean II */
868#define AIROIOCTL SIOCIWFIRSTPRIV
869#define AIROIDIFC AIROIOCTL + 1
870
871/* Ioctl constants to be used in airo_ioctl.command */
872
873#define AIROGCAP 0 // Capability rid
874#define AIROGCFG 1 // USED A LOT
875#define AIROGSLIST 2 // System ID list
876#define AIROGVLIST 3 // List of specified AP's
877#define AIROGDRVNAM 4 // NOTUSED
878#define AIROGEHTENC 5 // NOTUSED
879#define AIROGWEPKTMP 6
880#define AIROGWEPKNV 7
881#define AIROGSTAT 8
882#define AIROGSTATSC32 9
883#define AIROGSTATSD32 10
884#define AIROGMICRID 11
885#define AIROGMICSTATS 12
886#define AIROGFLAGS 13
887#define AIROGID 14
888#define AIRORRID 15
889#define AIRORSWVERSION 17
890
891/* Leave gap of 40 commands after AIROGSTATSD32 for future */
892
893#define AIROPCAP AIROGSTATSD32 + 40
894#define AIROPVLIST AIROPCAP + 1
895#define AIROPSLIST AIROPVLIST + 1
896#define AIROPCFG AIROPSLIST + 1
897#define AIROPSIDS AIROPCFG + 1
898#define AIROPAPLIST AIROPSIDS + 1
899#define AIROPMACON AIROPAPLIST + 1 /* Enable mac */
900#define AIROPMACOFF AIROPMACON + 1 /* Disable mac */
901#define AIROPSTCLR AIROPMACOFF + 1
902#define AIROPWEPKEY AIROPSTCLR + 1
903#define AIROPWEPKEYNV AIROPWEPKEY + 1
904#define AIROPLEAPPWD AIROPWEPKEYNV + 1
905#define AIROPLEAPUSR AIROPLEAPPWD + 1
906
907/* Flash codes */
908
909#define AIROFLSHRST AIROPWEPKEYNV + 40
910#define AIROFLSHGCHR AIROFLSHRST + 1
911#define AIROFLSHSTFL AIROFLSHGCHR + 1
912#define AIROFLSHPCHR AIROFLSHSTFL + 1
913#define AIROFLPUTBUF AIROFLSHPCHR + 1
914#define AIRORESTART AIROFLPUTBUF + 1
915
916#define FLASHSIZE 32768
917#define AUXMEMSIZE (256 * 1024)
918
919typedef struct aironet_ioctl {
920 unsigned short command; // What to do
921 unsigned short len; // Len of data
922 unsigned short ridnum; // rid number
923 unsigned char __user *data; // d-data
924} aironet_ioctl;
925
Domen Puncer62595eb2005-06-20 23:54:37 +0200926static char swversion[] = "2.1";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927#endif /* CISCO_EXT */
928
929#define NUM_MODULES 2
930#define MIC_MSGLEN_MAX 2400
931#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX
Dan Williams15db2762006-03-16 13:46:27 -0500932#define AIRO_DEF_MTU 2312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933
934typedef struct {
935 u32 size; // size
936 u8 enabled; // MIC enabled or not
937 u32 rxSuccess; // successful packets received
938 u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison
939 u32 rxNotMICed; // pkts dropped due to not being MIC'd
940 u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed
941 u32 rxWrongSequence; // pkts dropped due to sequence number violation
942 u32 reserve[32];
943} mic_statistics;
944
945typedef struct {
946 u32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2];
947 u64 accum; // accumulated mic, reduced to u32 in final()
948 int position; // current position (byte offset) in message
949 union {
950 u8 d8[4];
Al Viro593c2b92007-12-17 15:09:34 -0500951 __be32 d32;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 } part; // saves partial message word across update() calls
953} emmh32_context;
954
955typedef struct {
956 emmh32_context seed; // Context - the seed
957 u32 rx; // Received sequence number
958 u32 tx; // Tx sequence number
959 u32 window; // Start of window
960 u8 valid; // Flag to say if context is valid or not
961 u8 key[16];
962} miccntx;
963
964typedef struct {
965 miccntx mCtx; // Multicast context
966 miccntx uCtx; // Unicast context
967} mic_module;
968
969typedef struct {
970 unsigned int rid: 16;
971 unsigned int len: 15;
972 unsigned int valid: 1;
973 dma_addr_t host_addr;
974} Rid;
975
976typedef struct {
977 unsigned int offset: 15;
978 unsigned int eoc: 1;
979 unsigned int len: 15;
980 unsigned int valid: 1;
981 dma_addr_t host_addr;
982} TxFid;
983
Dan Williamsf55d4512009-01-24 09:04:12 -0500984struct rx_hdr {
985 __le16 status, len;
986 u8 rssi[2];
987 u8 rate;
988 u8 freq;
989 __le16 tmp[4];
990} __attribute__ ((packed));
991
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992typedef struct {
993 unsigned int ctl: 15;
994 unsigned int rdy: 1;
995 unsigned int len: 15;
996 unsigned int valid: 1;
997 dma_addr_t host_addr;
998} RxFid;
999
1000/*
1001 * Host receive descriptor
1002 */
1003typedef struct {
1004 unsigned char __iomem *card_ram_off; /* offset into card memory of the
1005 desc */
1006 RxFid rx_desc; /* card receive descriptor */
1007 char *virtual_host_addr; /* virtual address of host receive
1008 buffer */
1009 int pending;
1010} HostRxDesc;
1011
1012/*
1013 * Host transmit descriptor
1014 */
1015typedef struct {
1016 unsigned char __iomem *card_ram_off; /* offset into card memory of the
1017 desc */
1018 TxFid tx_desc; /* card transmit descriptor */
1019 char *virtual_host_addr; /* virtual address of host receive
1020 buffer */
1021 int pending;
1022} HostTxDesc;
1023
1024/*
1025 * Host RID descriptor
1026 */
1027typedef struct {
1028 unsigned char __iomem *card_ram_off; /* offset into card memory of the
1029 descriptor */
1030 Rid rid_desc; /* card RID descriptor */
1031 char *virtual_host_addr; /* virtual address of host receive
1032 buffer */
1033} HostRidDesc;
1034
1035typedef struct {
1036 u16 sw0;
1037 u16 sw1;
1038 u16 status;
1039 u16 len;
1040#define HOST_SET (1 << 0)
1041#define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */
1042#define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */
1043#define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */
1044#define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */
1045#define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */
1046#define HOST_CLR_AID (1 << 7) /* clear AID failure */
1047#define HOST_RTS (1 << 9) /* Force RTS use */
1048#define HOST_SHORT (1 << 10) /* Do short preamble */
1049 u16 ctl;
1050 u16 aid;
1051 u16 retries;
1052 u16 fill;
1053} TxCtlHdr;
1054
1055typedef struct {
1056 u16 ctl;
1057 u16 duration;
1058 char addr1[6];
1059 char addr2[6];
1060 char addr3[6];
1061 u16 seq;
1062 char addr4[6];
1063} WifiHdr;
1064
1065
1066typedef struct {
1067 TxCtlHdr ctlhdr;
1068 u16 fill1;
1069 u16 fill2;
1070 WifiHdr wifihdr;
1071 u16 gaplen;
1072 u16 status;
1073} WifiCtlHdr;
1074
Jouni Malinenff1d2762005-05-12 22:54:16 -04001075static WifiCtlHdr wifictlhdr8023 = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 .ctlhdr = {
1077 .ctl = HOST_DONT_RLSE,
1078 }
1079};
1080
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081// A few details needed for WEP (Wireless Equivalent Privacy)
1082#define MAX_KEY_SIZE 13 // 128 (?) bits
1083#define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP
1084typedef struct wep_key_t {
1085 u16 len;
1086 u8 key[16]; /* 40-bit and 104-bit keys */
1087} wep_key_t;
1088
1089/* Backward compatibility */
1090#ifndef IW_ENCODE_NOKEY
1091#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */
1092#define IW_ENCODE_MODE (IW_ENCODE_DISABLED | IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN)
1093#endif /* IW_ENCODE_NOKEY */
1094
1095/* List of Wireless Handlers (new API) */
1096static const struct iw_handler_def airo_handler_def;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097
1098static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)";
1099
1100struct airo_info;
1101
1102static int get_dec_u16( char *buffer, int *start, int limit );
1103static void OUT4500( struct airo_info *, u16 register, u16 value );
1104static unsigned short IN4500( struct airo_info *, u16 register );
1105static u16 setup_card(struct airo_info*, u8 *mac, int lock);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02001106static int enable_MAC(struct airo_info *ai, int lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107static void disable_MAC(struct airo_info *ai, int lock);
1108static void enable_interrupts(struct airo_info*);
1109static void disable_interrupts(struct airo_info*);
1110static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp);
1111static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001112static int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001114static int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001116static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 int whichbap);
1118static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd);
1119static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock);
1120static int PC4500_writerid(struct airo_info*, u16 rid, const void
1121 *pBuf, int len, int lock);
1122static int do_writerid( struct airo_info*, u16 rid, const void *rid_data,
1123 int len, int dummy );
1124static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw);
1125static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket);
1126static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket);
1127
1128static int mpi_send_packet (struct net_device *dev);
1129static void mpi_unmap_card(struct pci_dev *pci);
1130static void mpi_receive_802_3(struct airo_info *ai);
1131static void mpi_receive_802_11(struct airo_info *ai);
1132static int waitbusy (struct airo_info *ai);
1133
David Howells7d12e782006-10-05 14:55:46 +01001134static irqreturn_t airo_interrupt( int irq, void* dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135static int airo_thread(void *data);
1136static void timer_func( struct net_device *dev );
1137static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001138static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139static void airo_read_wireless_stats (struct airo_info *local);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140#ifdef CISCO_EXT
1141static int readrids(struct net_device *dev, aironet_ioctl *comp);
1142static int writerids(struct net_device *dev, aironet_ioctl *comp);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001143static int flashcard(struct net_device *dev, aironet_ioctl *comp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144#endif /* CISCO_EXT */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145static void micinit(struct airo_info *ai);
1146static int micsetup(struct airo_info *ai);
1147static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len);
1148static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen);
1149
Dan Williams41480af2005-05-10 09:45:51 -04001150static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi);
1151static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm);
1152
Dan Williams9e75af32006-03-16 13:46:29 -05001153static void airo_networks_free(struct airo_info *ai);
1154
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155struct airo_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 struct net_device *dev;
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01001157 struct list_head dev_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we
1159 use the high bit to mark whether it is in use. */
1160#define MAX_FIDS 6
1161#define MPI_MAX_FIDS 1
1162 int fids[MAX_FIDS];
1163 ConfigRid config;
1164 char keyindex; // Used with auto wep
1165 char defindex; // Used with auto wep
1166 struct proc_dir_entry *proc_entry;
1167 spinlock_t aux_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168#define FLAG_RADIO_OFF 0 /* User disabling of MAC */
1169#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */
1170#define FLAG_RADIO_MASK 0x03
1171#define FLAG_ENABLED 2
1172#define FLAG_ADHOC 3 /* Needed by MIC */
1173#define FLAG_MIC_CAPABLE 4
1174#define FLAG_UPDATE_MULTI 5
1175#define FLAG_UPDATE_UNI 6
1176#define FLAG_802_11 7
Dan Williams3c304952006-04-15 12:26:18 -04001177#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178#define FLAG_PENDING_XMIT 9
1179#define FLAG_PENDING_XMIT11 10
1180#define FLAG_MPI 11
1181#define FLAG_REGISTERED 12
1182#define FLAG_COMMIT 13
1183#define FLAG_RESET 14
1184#define FLAG_FLASHING 15
Dan Williams3c304952006-04-15 12:26:18 -04001185#define FLAG_WPA_CAPABLE 16
1186 unsigned long flags;
1187#define JOB_DIE 0
1188#define JOB_XMIT 1
1189#define JOB_XMIT11 2
1190#define JOB_STATS 3
1191#define JOB_PROMISC 4
1192#define JOB_MIC 5
1193#define JOB_EVENT 6
1194#define JOB_AUTOWEP 7
1195#define JOB_WSTATS 8
1196#define JOB_SCAN_RESULTS 9
1197 unsigned long jobs;
Al Virob8c06bc2007-12-19 17:55:43 -05001198 int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 int whichbap);
1200 unsigned short *flash;
1201 tdsRssiEntry *rssi;
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001202 struct task_struct *list_bss_task;
1203 struct task_struct *airo_thread_task;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 struct semaphore sem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 wait_queue_head_t thr_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 unsigned long expires;
1207 struct {
1208 struct sk_buff *skb;
1209 int fid;
1210 } xmit, xmit11;
1211 struct net_device *wifidev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 struct iw_statistics wstats; // wireless stats
Dan Williams9e75af32006-03-16 13:46:29 -05001213 unsigned long scan_timeout; /* Time scan should be read */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 struct iw_spy_data spy_data;
1215 struct iw_public_data wireless_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 /* MIC stuff */
Herbert Xuf12cc202006-08-22 20:36:13 +10001217 struct crypto_cipher *tfm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 mic_module mod[2];
1219 mic_statistics micstats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors
1221 HostTxDesc txfids[MPI_MAX_FIDS];
1222 HostRidDesc config_desc;
1223 unsigned long ridbus; // phys addr of config_desc
1224 struct sk_buff_head txq;// tx queue used by mpi350 code
1225 struct pci_dev *pci;
1226 unsigned char __iomem *pcimem;
1227 unsigned char __iomem *pciaux;
1228 unsigned char *shared;
1229 dma_addr_t shared_dma;
Pavel Machek1cc68ae2005-06-20 15:33:04 -07001230 pm_message_t power;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 SsidRid *SSID;
1232 APListRid *APList;
1233#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
1234 char proc_name[IFNAMSIZ];
Dan Williams9e75af32006-03-16 13:46:29 -05001235
Dan Williams3c304952006-04-15 12:26:18 -04001236 /* WPA-related stuff */
1237 unsigned int bssListFirst;
1238 unsigned int bssListNext;
1239 unsigned int bssListRidLen;
1240
Dan Williams9e75af32006-03-16 13:46:29 -05001241 struct list_head network_list;
1242 struct list_head network_free_list;
1243 BSSListElement *networks;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244};
1245
Al Virob8c06bc2007-12-19 17:55:43 -05001246static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen,
1247 int whichbap)
1248{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 return ai->bap_read(ai, pu16Dst, bytelen, whichbap);
1250}
1251
1252static int setup_proc_entry( struct net_device *dev,
1253 struct airo_info *apriv );
1254static int takedown_proc_entry( struct net_device *dev,
1255 struct airo_info *apriv );
1256
Jouni Malinenff1d2762005-05-12 22:54:16 -04001257static int cmdreset(struct airo_info *ai);
1258static int setflashmode (struct airo_info *ai);
1259static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
1260static int flashputbuf(struct airo_info *ai);
1261static int flashrestart(struct airo_info *ai,struct net_device *dev);
1262
Dan Williams934d8bf2006-03-16 13:46:23 -05001263#define airo_print(type, name, fmt, args...) \
Michal Schmidt1138c372007-06-29 15:33:41 +02001264 printk(type DRV_NAME "(%s): " fmt "\n", name, ##args)
Dan Williams934d8bf2006-03-16 13:46:23 -05001265
1266#define airo_print_info(name, fmt, args...) \
1267 airo_print(KERN_INFO, name, fmt, ##args)
1268
1269#define airo_print_dbg(name, fmt, args...) \
1270 airo_print(KERN_DEBUG, name, fmt, ##args)
1271
1272#define airo_print_warn(name, fmt, args...) \
1273 airo_print(KERN_WARNING, name, fmt, ##args)
1274
1275#define airo_print_err(name, fmt, args...) \
1276 airo_print(KERN_ERR, name, fmt, ##args)
1277
Wang Chenfaf39942008-10-14 13:30:33 +08001278#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash)
Dan Williams934d8bf2006-03-16 13:46:23 -05001279
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280/***********************************************************************
1281 * MIC ROUTINES *
1282 ***********************************************************************
1283 */
1284
1285static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq);
1286static void MoveWindow(miccntx *context, u32 micSeq);
Herbert Xuf12cc202006-08-22 20:36:13 +10001287static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
1288 struct crypto_cipher *tfm);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001289static void emmh32_init(emmh32_context *context);
1290static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
1291static void emmh32_final(emmh32_context *context, u8 digest[4]);
1292static int flashpchar(struct airo_info *ai,int byte,int dwelltime);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293
1294/* micinit - Initialize mic seed */
1295
1296static void micinit(struct airo_info *ai)
1297{
1298 MICRid mic_rid;
1299
Dan Williams3c304952006-04-15 12:26:18 -04001300 clear_bit(JOB_MIC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
1302 up(&ai->sem);
1303
1304 ai->micstats.enabled = (mic_rid.state & 0x00FF) ? 1 : 0;
1305
1306 if (ai->micstats.enabled) {
1307 /* Key must be valid and different */
1308 if (mic_rid.multicastValid && (!ai->mod[0].mCtx.valid ||
1309 (memcmp (ai->mod[0].mCtx.key, mic_rid.multicast,
1310 sizeof(ai->mod[0].mCtx.key)) != 0))) {
1311 /* Age current mic Context */
1312 memcpy(&ai->mod[1].mCtx,&ai->mod[0].mCtx,sizeof(miccntx));
1313 /* Initialize new context */
1314 memcpy(&ai->mod[0].mCtx.key,mic_rid.multicast,sizeof(mic_rid.multicast));
1315 ai->mod[0].mCtx.window = 33; //Window always points to the middle
1316 ai->mod[0].mCtx.rx = 0; //Rx Sequence numbers
1317 ai->mod[0].mCtx.tx = 0; //Tx sequence numbers
1318 ai->mod[0].mCtx.valid = 1; //Key is now valid
1319
1320 /* Give key to mic seed */
1321 emmh32_setseed(&ai->mod[0].mCtx.seed,mic_rid.multicast,sizeof(mic_rid.multicast), ai->tfm);
1322 }
1323
1324 /* Key must be valid and different */
1325 if (mic_rid.unicastValid && (!ai->mod[0].uCtx.valid ||
1326 (memcmp(ai->mod[0].uCtx.key, mic_rid.unicast,
1327 sizeof(ai->mod[0].uCtx.key)) != 0))) {
1328 /* Age current mic Context */
1329 memcpy(&ai->mod[1].uCtx,&ai->mod[0].uCtx,sizeof(miccntx));
1330 /* Initialize new context */
1331 memcpy(&ai->mod[0].uCtx.key,mic_rid.unicast,sizeof(mic_rid.unicast));
1332
1333 ai->mod[0].uCtx.window = 33; //Window always points to the middle
1334 ai->mod[0].uCtx.rx = 0; //Rx Sequence numbers
1335 ai->mod[0].uCtx.tx = 0; //Tx sequence numbers
1336 ai->mod[0].uCtx.valid = 1; //Key is now valid
1337
1338 //Give key to mic seed
1339 emmh32_setseed(&ai->mod[0].uCtx.seed, mic_rid.unicast, sizeof(mic_rid.unicast), ai->tfm);
1340 }
1341 } else {
1342 /* So next time we have a valid key and mic is enabled, we will update
1343 * the sequence number if the key is the same as before.
1344 */
1345 ai->mod[0].uCtx.valid = 0;
1346 ai->mod[0].mCtx.valid = 0;
1347 }
1348}
1349
1350/* micsetup - Get ready for business */
1351
1352static int micsetup(struct airo_info *ai) {
1353 int i;
1354
1355 if (ai->tfm == NULL)
Herbert Xuf12cc202006-08-22 20:36:13 +10001356 ai->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
Herbert Xuf12cc202006-08-22 20:36:13 +10001358 if (IS_ERR(ai->tfm)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05001359 airo_print_err(ai->dev->name, "failed to load transform for AES");
Herbert Xuf12cc202006-08-22 20:36:13 +10001360 ai->tfm = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 return ERROR;
1362 }
1363
1364 for (i=0; i < NUM_MODULES; i++) {
1365 memset(&ai->mod[i].mCtx,0,sizeof(miccntx));
1366 memset(&ai->mod[i].uCtx,0,sizeof(miccntx));
1367 }
1368 return SUCCESS;
1369}
1370
Jouni Malinenff1d2762005-05-12 22:54:16 -04001371static char micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
1373/*===========================================================================
1374 * Description: Mic a packet
1375 *
1376 * Inputs: etherHead * pointer to an 802.3 frame
1377 *
1378 * Returns: BOOLEAN if successful, otherwise false.
1379 * PacketTxLen will be updated with the mic'd packets size.
1380 *
1381 * Caveats: It is assumed that the frame buffer will already
1382 * be big enough to hold the largets mic message possible.
1383 * (No memory allocation is done here).
1384 *
1385 * Author: sbraneky (10/15/01)
1386 * Merciless hacks by rwilcher (1/14/02)
1387 */
1388
1389static int encapsulate(struct airo_info *ai ,etherHead *frame, MICBuffer *mic, int payLen)
1390{
1391 miccntx *context;
1392
1393 // Determine correct context
1394 // If not adhoc, always use unicast key
1395
1396 if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1))
1397 context = &ai->mod[0].mCtx;
1398 else
1399 context = &ai->mod[0].uCtx;
1400
1401 if (!context->valid)
1402 return ERROR;
1403
1404 mic->typelen = htons(payLen + 16); //Length of Mic'd packet
1405
1406 memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap
1407
1408 // Add Tx sequence
1409 mic->seq = htonl(context->tx);
1410 context->tx += 2;
1411
1412 emmh32_init(&context->seed); // Mic the packet
1413 emmh32_update(&context->seed,frame->da,ETH_ALEN * 2); // DA,SA
1414 emmh32_update(&context->seed,(u8*)&mic->typelen,10); // Type/Length and Snap
1415 emmh32_update(&context->seed,(u8*)&mic->seq,sizeof(mic->seq)); //SEQ
1416 emmh32_update(&context->seed,frame->da + ETH_ALEN * 2,payLen); //payload
1417 emmh32_final(&context->seed, (u8*)&mic->mic);
1418
1419 /* New Type/length ?????????? */
1420 mic->typelen = 0; //Let NIC know it could be an oversized packet
1421 return SUCCESS;
1422}
1423
1424typedef enum {
1425 NONE,
1426 NOMIC,
1427 NOMICPLUMMED,
1428 SEQUENCE,
1429 INCORRECTMIC,
1430} mic_error;
1431
1432/*===========================================================================
1433 * Description: Decapsulates a MIC'd packet and returns the 802.3 packet
1434 * (removes the MIC stuff) if packet is a valid packet.
1435 *
1436 * Inputs: etherHead pointer to the 802.3 packet
1437 *
1438 * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE
1439 *
1440 * Author: sbraneky (10/15/01)
1441 * Merciless hacks by rwilcher (1/14/02)
1442 *---------------------------------------------------------------------------
1443 */
1444
1445static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen)
1446{
1447 int i;
1448 u32 micSEQ;
1449 miccntx *context;
1450 u8 digest[4];
1451 mic_error micError = NONE;
1452
1453 // Check if the packet is a Mic'd packet
1454
1455 if (!ai->micstats.enabled) {
1456 //No Mic set or Mic OFF but we received a MIC'd packet.
1457 if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) {
1458 ai->micstats.rxMICPlummed++;
1459 return ERROR;
1460 }
1461 return SUCCESS;
1462 }
1463
1464 if (ntohs(mic->typelen) == 0x888E)
1465 return SUCCESS;
1466
1467 if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) {
1468 // Mic enabled but packet isn't Mic'd
1469 ai->micstats.rxMICPlummed++;
1470 return ERROR;
1471 }
1472
1473 micSEQ = ntohl(mic->seq); //store SEQ as CPU order
1474
1475 //At this point we a have a mic'd packet and mic is enabled
1476 //Now do the mic error checking.
1477
1478 //Receive seq must be odd
1479 if ( (micSEQ & 1) == 0 ) {
1480 ai->micstats.rxWrongSequence++;
1481 return ERROR;
1482 }
1483
1484 for (i = 0; i < NUM_MODULES; i++) {
1485 int mcast = eth->da[0] & 1;
1486 //Determine proper context
1487 context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx;
1488
1489 //Make sure context is valid
1490 if (!context->valid) {
1491 if (i == 0)
1492 micError = NOMICPLUMMED;
1493 continue;
1494 }
1495 //DeMic it
1496
1497 if (!mic->typelen)
1498 mic->typelen = htons(payLen + sizeof(MICBuffer) - 2);
1499
1500 emmh32_init(&context->seed);
1501 emmh32_update(&context->seed, eth->da, ETH_ALEN*2);
1502 emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap));
1503 emmh32_update(&context->seed, (u8 *)&mic->seq,sizeof(mic->seq));
1504 emmh32_update(&context->seed, eth->da + ETH_ALEN*2,payLen);
1505 //Calculate MIC
1506 emmh32_final(&context->seed, digest);
1507
1508 if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match
1509 //Invalid Mic
1510 if (i == 0)
1511 micError = INCORRECTMIC;
1512 continue;
1513 }
1514
1515 //Check Sequence number if mics pass
1516 if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) {
1517 ai->micstats.rxSuccess++;
1518 return SUCCESS;
1519 }
1520 if (i == 0)
1521 micError = SEQUENCE;
1522 }
1523
1524 // Update statistics
1525 switch (micError) {
1526 case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break;
1527 case SEQUENCE: ai->micstats.rxWrongSequence++; break;
1528 case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break;
1529 case NONE: break;
1530 case NOMIC: break;
1531 }
1532 return ERROR;
1533}
1534
1535/*===========================================================================
1536 * Description: Checks the Rx Seq number to make sure it is valid
1537 * and hasn't already been received
1538 *
1539 * Inputs: miccntx - mic context to check seq against
1540 * micSeq - the Mic seq number
1541 *
1542 * Returns: TRUE if valid otherwise FALSE.
1543 *
1544 * Author: sbraneky (10/15/01)
1545 * Merciless hacks by rwilcher (1/14/02)
1546 *---------------------------------------------------------------------------
1547 */
1548
1549static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq)
1550{
1551 u32 seq,index;
1552
1553 //Allow for the ap being rebooted - if it is then use the next
1554 //sequence number of the current sequence number - might go backwards
1555
1556 if (mcast) {
1557 if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) {
1558 clear_bit (FLAG_UPDATE_MULTI, &ai->flags);
1559 context->window = (micSeq > 33) ? micSeq : 33;
1560 context->rx = 0; // Reset rx
1561 }
1562 } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) {
1563 clear_bit (FLAG_UPDATE_UNI, &ai->flags);
1564 context->window = (micSeq > 33) ? micSeq : 33; // Move window
1565 context->rx = 0; // Reset rx
1566 }
1567
1568 //Make sequence number relative to START of window
1569 seq = micSeq - (context->window - 33);
1570
1571 //Too old of a SEQ number to check.
1572 if ((s32)seq < 0)
1573 return ERROR;
1574
1575 if ( seq > 64 ) {
1576 //Window is infinite forward
1577 MoveWindow(context,micSeq);
1578 return SUCCESS;
1579 }
1580
1581 // We are in the window. Now check the context rx bit to see if it was already sent
1582 seq >>= 1; //divide by 2 because we only have odd numbers
1583 index = 1 << seq; //Get an index number
1584
1585 if (!(context->rx & index)) {
1586 //micSEQ falls inside the window.
1587 //Add seqence number to the list of received numbers.
1588 context->rx |= index;
1589
1590 MoveWindow(context,micSeq);
1591
1592 return SUCCESS;
1593 }
1594 return ERROR;
1595}
1596
1597static void MoveWindow(miccntx *context, u32 micSeq)
1598{
1599 u32 shift;
1600
1601 //Move window if seq greater than the middle of the window
1602 if (micSeq > context->window) {
1603 shift = (micSeq - context->window) >> 1;
1604
1605 //Shift out old
1606 if (shift < 32)
1607 context->rx >>= shift;
1608 else
1609 context->rx = 0;
1610
1611 context->window = micSeq; //Move window
1612 }
1613}
1614
1615/*==============================================*/
1616/*========== EMMH ROUTINES ====================*/
1617/*==============================================*/
1618
1619/* mic accumulate */
1620#define MIC_ACCUM(val) \
1621 context->accum += (u64)(val) * context->coeff[coeff_position++];
1622
1623static unsigned char aes_counter[16];
1624
1625/* expand the key to fill the MMH coefficient array */
Herbert Xuf12cc202006-08-22 20:36:13 +10001626static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
1627 struct crypto_cipher *tfm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628{
1629 /* take the keying material, expand if necessary, truncate at 16-bytes */
1630 /* run through AES counter mode to generate context->coeff[] */
1631
1632 int i,j;
1633 u32 counter;
1634 u8 *cipher, plain[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635
1636 crypto_cipher_setkey(tfm, pkey, 16);
1637 counter = 0;
Ahmed S. Darwishe7c04fd2007-02-05 18:58:29 +02001638 for (i = 0; i < ARRAY_SIZE(context->coeff); ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 aes_counter[15] = (u8)(counter >> 0);
1640 aes_counter[14] = (u8)(counter >> 8);
1641 aes_counter[13] = (u8)(counter >> 16);
1642 aes_counter[12] = (u8)(counter >> 24);
1643 counter++;
1644 memcpy (plain, aes_counter, 16);
Herbert Xuf12cc202006-08-22 20:36:13 +10001645 crypto_cipher_encrypt_one(tfm, plain, plain);
1646 cipher = plain;
Ahmed S. Darwishe7c04fd2007-02-05 18:58:29 +02001647 for (j = 0; (j < 16) && (i < ARRAY_SIZE(context->coeff)); ) {
Al Viro593c2b92007-12-17 15:09:34 -05001648 context->coeff[i++] = ntohl(*(__be32 *)&cipher[j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 j += 4;
1650 }
1651 }
1652}
1653
1654/* prepare for calculation of a new mic */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001655static void emmh32_init(emmh32_context *context)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656{
1657 /* prepare for new mic calculation */
1658 context->accum = 0;
1659 context->position = 0;
1660}
1661
1662/* add some bytes to the mic calculation */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001663static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664{
1665 int coeff_position, byte_position;
1666
1667 if (len == 0) return;
1668
1669 coeff_position = context->position >> 2;
1670
1671 /* deal with partial 32-bit word left over from last update */
1672 byte_position = context->position & 3;
1673 if (byte_position) {
1674 /* have a partial word in part to deal with */
1675 do {
1676 if (len == 0) return;
1677 context->part.d8[byte_position++] = *pOctets++;
1678 context->position++;
1679 len--;
1680 } while (byte_position < 4);
Al Viro593c2b92007-12-17 15:09:34 -05001681 MIC_ACCUM(ntohl(context->part.d32));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 }
1683
1684 /* deal with full 32-bit words */
1685 while (len >= 4) {
Al Viro593c2b92007-12-17 15:09:34 -05001686 MIC_ACCUM(ntohl(*(__be32 *)pOctets));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 context->position += 4;
1688 pOctets += 4;
1689 len -= 4;
1690 }
1691
1692 /* deal with partial 32-bit word that will be left over from this update */
1693 byte_position = 0;
1694 while (len > 0) {
1695 context->part.d8[byte_position++] = *pOctets++;
1696 context->position++;
1697 len--;
1698 }
1699}
1700
1701/* mask used to zero empty bytes for final partial word */
1702static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
1703
1704/* calculate the mic */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001705static void emmh32_final(emmh32_context *context, u8 digest[4])
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706{
1707 int coeff_position, byte_position;
1708 u32 val;
1709
1710 u64 sum, utmp;
1711 s64 stmp;
1712
1713 coeff_position = context->position >> 2;
1714
1715 /* deal with partial 32-bit word left over from last update */
1716 byte_position = context->position & 3;
1717 if (byte_position) {
1718 /* have a partial word in part to deal with */
Al Viro593c2b92007-12-17 15:09:34 -05001719 val = ntohl(context->part.d32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */
1721 }
1722
1723 /* reduce the accumulated u64 to a 32-bit MIC */
1724 sum = context->accum;
1725 stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15);
1726 utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15);
1727 sum = utmp & 0xffffffffLL;
1728 if (utmp > 0x10000000fLL)
1729 sum -= 15;
1730
1731 val = (u32)sum;
1732 digest[0] = (val>>24) & 0xFF;
1733 digest[1] = (val>>16) & 0xFF;
1734 digest[2] = (val>>8) & 0xFF;
1735 digest[3] = val & 0xFF;
1736}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737
1738static int readBSSListRid(struct airo_info *ai, int first,
Al Viro17e70492007-12-19 18:56:37 -05001739 BSSListRid *list)
1740{
Dan Williams3c304952006-04-15 12:26:18 -04001741 Cmd cmd;
1742 Resp rsp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743
1744 if (first == 1) {
Dan Williams3c304952006-04-15 12:26:18 -04001745 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
1746 memset(&cmd, 0, sizeof(cmd));
1747 cmd.cmd=CMD_LISTBSS;
1748 if (down_interruptible(&ai->sem))
1749 return -ERESTARTSYS;
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001750 ai->list_bss_task = current;
Dan Williams3c304952006-04-15 12:26:18 -04001751 issuecommand(ai, &cmd, &rsp);
1752 up(&ai->sem);
1753 /* Let the command take effect */
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001754 schedule_timeout_uninterruptible(3 * HZ);
1755 ai->list_bss_task = NULL;
Dan Williams3c304952006-04-15 12:26:18 -04001756 }
Al Viro17e70492007-12-19 18:56:37 -05001757 return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext,
Dan Williams3c304952006-04-15 12:26:18 -04001758 list, ai->bssListRidLen, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759}
1760
Al Viro4293ea32007-12-19 19:21:51 -05001761static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock)
1762{
1763 return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764 wkr, sizeof(*wkr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766
Al Viro4293ea32007-12-19 19:21:51 -05001767static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock)
1768{
1769 int rc;
1770 rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock);
1771 if (rc!=SUCCESS)
1772 airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 if (perm) {
Al Viro4293ea32007-12-19 19:21:51 -05001774 rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock);
1775 if (rc!=SUCCESS)
Dan Williams934d8bf2006-03-16 13:46:23 -05001776 airo_print_err(ai->dev->name, "WEP_PERM set %x", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777 }
1778 return rc;
1779}
1780
Al Viro0dd22122007-12-17 16:11:57 -05001781static int readSsidRid(struct airo_info*ai, SsidRid *ssidr)
1782{
1783 return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785
Al Viro0dd22122007-12-17 16:11:57 -05001786static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock)
1787{
1788 return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789}
Al Viro0dd22122007-12-17 16:11:57 -05001790
Al Viro3eb9b412007-12-21 00:00:35 -05001791static int readConfigRid(struct airo_info *ai, int lock)
1792{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794 ConfigRid cfg;
1795
1796 if (ai->config.len)
1797 return SUCCESS;
1798
1799 rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock);
1800 if (rc != SUCCESS)
1801 return rc;
1802
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803 ai->config = cfg;
1804 return SUCCESS;
1805}
Al Viro3eb9b412007-12-21 00:00:35 -05001806
1807static inline void checkThrottle(struct airo_info *ai)
1808{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 int i;
1810/* Old hardware had a limit on encryption speed */
1811 if (ai->config.authType != AUTH_OPEN && maxencrypt) {
1812 for(i=0; i<8; i++) {
1813 if (ai->config.rates[i] > maxencrypt) {
1814 ai->config.rates[i] = 0;
1815 }
1816 }
1817 }
1818}
Al Viro3eb9b412007-12-21 00:00:35 -05001819
1820static int writeConfigRid(struct airo_info *ai, int lock)
1821{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822 ConfigRid cfgr;
1823
1824 if (!test_bit (FLAG_COMMIT, &ai->flags))
1825 return SUCCESS;
1826
1827 clear_bit (FLAG_COMMIT, &ai->flags);
1828 clear_bit (FLAG_RESET, &ai->flags);
1829 checkThrottle(ai);
1830 cfgr = ai->config;
1831
Al Viro3eb9b412007-12-21 00:00:35 -05001832 if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 set_bit(FLAG_ADHOC, &ai->flags);
1834 else
1835 clear_bit(FLAG_ADHOC, &ai->flags);
1836
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock);
1838}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839
Al Viro329e2c02007-12-20 22:58:57 -05001840static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock)
1841{
1842 return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843}
Al Viroa7497162007-12-20 17:49:41 -05001844
1845static int readAPListRid(struct airo_info *ai, APListRid *aplr)
1846{
1847 return PC4500_readrid(ai, RID_APLIST, aplr, sizeof(*aplr), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848}
Al Viroa7497162007-12-20 17:49:41 -05001849
1850static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock)
1851{
1852 return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854
Al Viro56d81bd2007-12-20 17:18:35 -05001855static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock)
1856{
1857 return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859
Al Viroa23ace52007-12-19 22:24:16 -05001860static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock)
1861{
1862 return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863}
1864
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001865static void try_auto_wep(struct airo_info *ai)
1866{
1867 if (auto_wep && !(ai->flags & FLAG_RADIO_DOWN)) {
1868 ai->expires = RUN_AT(3*HZ);
1869 wake_up_interruptible(&ai->thr_wait);
1870 }
1871}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001873static int airo_open(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08001874 struct airo_info *ai = dev->ml_priv;
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001875 int rc = 0;
1876
1877 if (test_bit(FLAG_FLASHING, &ai->flags))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878 return -EIO;
1879
1880 /* Make sure the card is configured.
1881 * Wireless Extensions may postpone config changes until the card
1882 * is open (to pipeline changes and speed-up card setup). If
1883 * those changes are not yet commited, do it now - Jean II */
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001884 if (test_bit(FLAG_COMMIT, &ai->flags)) {
1885 disable_MAC(ai, 1);
1886 writeConfigRid(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 }
1888
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001889 if (ai->wifidev != dev) {
1890 clear_bit(JOB_DIE, &ai->jobs);
1891 ai->airo_thread_task = kthread_run(airo_thread, dev, dev->name);
1892 if (IS_ERR(ai->airo_thread_task))
1893 return (int)PTR_ERR(ai->airo_thread_task);
1894
1895 rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED,
1896 dev->name, dev);
1897 if (rc) {
1898 airo_print_err(dev->name,
1899 "register interrupt %d failed, rc %d",
1900 dev->irq, rc);
1901 set_bit(JOB_DIE, &ai->jobs);
1902 kthread_stop(ai->airo_thread_task);
1903 return rc;
1904 }
1905
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906 /* Power on the MAC controller (which may have been disabled) */
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001907 clear_bit(FLAG_RADIO_DOWN, &ai->flags);
1908 enable_interrupts(ai);
1909
1910 try_auto_wep(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 }
Michal Schmidt175ec1a2007-06-29 15:33:47 +02001912 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913
1914 netif_start_queue(dev);
1915 return 0;
1916}
1917
1918static int mpi_start_xmit(struct sk_buff *skb, struct net_device *dev) {
1919 int npacks, pending;
1920 unsigned long flags;
Wang Chenfaf39942008-10-14 13:30:33 +08001921 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922
1923 if (!skb) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07001924 airo_print_err(dev->name, "%s: skb == NULL!",__func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925 return 0;
1926 }
1927 npacks = skb_queue_len (&ai->txq);
1928
1929 if (npacks >= MAXTXQ - 1) {
1930 netif_stop_queue (dev);
1931 if (npacks > MAXTXQ) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03001932 dev->stats.tx_fifo_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 return 1;
1934 }
1935 skb_queue_tail (&ai->txq, skb);
1936 return 0;
1937 }
1938
1939 spin_lock_irqsave(&ai->aux_lock, flags);
1940 skb_queue_tail (&ai->txq, skb);
1941 pending = test_bit(FLAG_PENDING_XMIT, &ai->flags);
1942 spin_unlock_irqrestore(&ai->aux_lock,flags);
1943 netif_wake_queue (dev);
1944
1945 if (pending == 0) {
1946 set_bit(FLAG_PENDING_XMIT, &ai->flags);
1947 mpi_send_packet (dev);
1948 }
1949 return 0;
1950}
1951
1952/*
1953 * @mpi_send_packet
1954 *
1955 * Attempt to transmit a packet. Can be called from interrupt
1956 * or transmit . return number of packets we tried to send
1957 */
1958
1959static int mpi_send_packet (struct net_device *dev)
1960{
1961 struct sk_buff *skb;
1962 unsigned char *buffer;
Al Viro593c2b92007-12-17 15:09:34 -05001963 s16 len;
1964 __le16 *payloadLen;
Wang Chenfaf39942008-10-14 13:30:33 +08001965 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966 u8 *sendbuf;
1967
1968 /* get a packet to send */
1969
Al Viro79ea13c2008-01-24 02:06:46 -08001970 if ((skb = skb_dequeue(&ai->txq)) == NULL) {
Dan Williams934d8bf2006-03-16 13:46:23 -05001971 airo_print_err(dev->name,
1972 "%s: Dequeue'd zero in send_packet()",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07001973 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 return 0;
1975 }
1976
1977 /* check min length*/
1978 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
1979 buffer = skb->data;
1980
1981 ai->txfids[0].tx_desc.offset = 0;
1982 ai->txfids[0].tx_desc.valid = 1;
1983 ai->txfids[0].tx_desc.eoc = 1;
1984 ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr);
1985
1986/*
1987 * Magic, the cards firmware needs a length count (2 bytes) in the host buffer
1988 * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen
1989 * is immediatly after it. ------------------------------------------------
1990 * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA|
1991 * ------------------------------------------------
1992 */
1993
1994 memcpy((char *)ai->txfids[0].virtual_host_addr,
1995 (char *)&wifictlhdr8023, sizeof(wifictlhdr8023));
1996
Al Viro593c2b92007-12-17 15:09:34 -05001997 payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr +
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 sizeof(wifictlhdr8023));
1999 sendbuf = ai->txfids[0].virtual_host_addr +
2000 sizeof(wifictlhdr8023) + 2 ;
2001
2002 /*
2003 * Firmware automaticly puts 802 header on so
2004 * we don't need to account for it in the length
2005 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
Al Viro593c2b92007-12-17 15:09:34 -05002007 (ntohs(((__be16 *)buffer)[6]) != 0x888E)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 MICBuffer pMic;
2009
2010 if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS)
2011 return ERROR;
2012
2013 *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic));
2014 ai->txfids[0].tx_desc.len += sizeof(pMic);
2015 /* copy data into airo dma buffer */
2016 memcpy (sendbuf, buffer, sizeof(etherHead));
2017 buffer += sizeof(etherHead);
2018 sendbuf += sizeof(etherHead);
2019 memcpy (sendbuf, &pMic, sizeof(pMic));
2020 sendbuf += sizeof(pMic);
2021 memcpy (sendbuf, buffer, len - sizeof(etherHead));
Adrian Bunka39d3e72006-01-21 01:35:15 +01002022 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 *payloadLen = cpu_to_le16(len - sizeof(etherHead));
2024
2025 dev->trans_start = jiffies;
2026
2027 /* copy data into airo dma buffer */
2028 memcpy(sendbuf, buffer, len);
2029 }
2030
2031 memcpy_toio(ai->txfids[0].card_ram_off,
2032 &ai->txfids[0].tx_desc, sizeof(TxFid));
2033
2034 OUT4500(ai, EVACK, 8);
2035
2036 dev_kfree_skb_any(skb);
2037 return 1;
2038}
2039
Gabriel A. Devenyi29b09fc2005-11-03 19:30:47 -05002040static void get_tx_error(struct airo_info *ai, s32 fid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041{
Al Viro593c2b92007-12-17 15:09:34 -05002042 __le16 status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043
2044 if (fid < 0)
2045 status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status;
2046 else {
2047 if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS)
2048 return;
2049 bap_read(ai, &status, 2, BAP0);
2050 }
2051 if (le16_to_cpu(status) & 2) /* Too many retries */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002052 ai->dev->stats.tx_aborted_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002054 ai->dev->stats.tx_heartbeat_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 if (le16_to_cpu(status) & 8) /* Aid fail */
2056 { }
2057 if (le16_to_cpu(status) & 0x10) /* MAC disabled */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002058 ai->dev->stats.tx_carrier_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 if (le16_to_cpu(status) & 0x20) /* Association lost */
2060 { }
2061 /* We produce a TXDROP event only for retry or lifetime
2062 * exceeded, because that's the only status that really mean
2063 * that this particular node went away.
2064 * Other errors means that *we* screwed up. - Jean II */
2065 if ((le16_to_cpu(status) & 2) ||
2066 (le16_to_cpu(status) & 4)) {
2067 union iwreq_data wrqu;
2068 char junk[0x18];
2069
2070 /* Faster to skip over useless data than to do
2071 * another bap_setup(). We are at offset 0x6 and
2072 * need to go to 0x18 and read 6 bytes - Jean II */
Al Virob8c06bc2007-12-19 17:55:43 -05002073 bap_read(ai, (__le16 *) junk, 0x18, BAP0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074
2075 /* Copy 802.11 dest address.
2076 * We use the 802.11 header because the frame may
2077 * not be 802.3 or may be mangled...
2078 * In Ad-Hoc mode, it will be the node address.
2079 * In managed mode, it will be most likely the AP addr
2080 * User space will figure out how to convert it to
2081 * whatever it needs (IP address or else).
2082 * - Jean II */
2083 memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN);
2084 wrqu.addr.sa_family = ARPHRD_ETHER;
2085
2086 /* Send event to user space */
2087 wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL);
2088 }
2089}
2090
2091static void airo_end_xmit(struct net_device *dev) {
2092 u16 status;
2093 int i;
Wang Chenfaf39942008-10-14 13:30:33 +08002094 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 struct sk_buff *skb = priv->xmit.skb;
2096 int fid = priv->xmit.fid;
2097 u32 *fids = priv->fids;
2098
Dan Williams3c304952006-04-15 12:26:18 -04002099 clear_bit(JOB_XMIT, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100 clear_bit(FLAG_PENDING_XMIT, &priv->flags);
2101 status = transmit_802_3_packet (priv, fids[fid], skb->data);
2102 up(&priv->sem);
2103
2104 i = 0;
2105 if ( status == SUCCESS ) {
2106 dev->trans_start = jiffies;
2107 for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++);
2108 } else {
2109 priv->fids[fid] &= 0xffff;
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002110 dev->stats.tx_window_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 }
2112 if (i < MAX_FIDS / 2)
2113 netif_wake_queue(dev);
2114 dev_kfree_skb(skb);
2115}
2116
2117static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) {
2118 s16 len;
2119 int i, j;
Wang Chenfaf39942008-10-14 13:30:33 +08002120 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 u32 *fids = priv->fids;
2122
2123 if ( skb == NULL ) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07002124 airo_print_err(dev->name, "%s: skb == NULL!", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 return 0;
2126 }
2127
2128 /* Find a vacant FID */
2129 for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ );
2130 for( j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++ );
2131
2132 if ( j >= MAX_FIDS / 2 ) {
2133 netif_stop_queue(dev);
2134
2135 if (i == MAX_FIDS / 2) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002136 dev->stats.tx_fifo_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 return 1;
2138 }
2139 }
2140 /* check min length*/
2141 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
2142 /* Mark fid as used & save length for later */
2143 fids[i] |= (len << 16);
2144 priv->xmit.skb = skb;
2145 priv->xmit.fid = i;
2146 if (down_trylock(&priv->sem) != 0) {
2147 set_bit(FLAG_PENDING_XMIT, &priv->flags);
2148 netif_stop_queue(dev);
Dan Williams3c304952006-04-15 12:26:18 -04002149 set_bit(JOB_XMIT, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150 wake_up_interruptible(&priv->thr_wait);
2151 } else
2152 airo_end_xmit(dev);
2153 return 0;
2154}
2155
2156static void airo_end_xmit11(struct net_device *dev) {
2157 u16 status;
2158 int i;
Wang Chenfaf39942008-10-14 13:30:33 +08002159 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160 struct sk_buff *skb = priv->xmit11.skb;
2161 int fid = priv->xmit11.fid;
2162 u32 *fids = priv->fids;
2163
Dan Williams3c304952006-04-15 12:26:18 -04002164 clear_bit(JOB_XMIT11, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165 clear_bit(FLAG_PENDING_XMIT11, &priv->flags);
2166 status = transmit_802_11_packet (priv, fids[fid], skb->data);
2167 up(&priv->sem);
2168
2169 i = MAX_FIDS / 2;
2170 if ( status == SUCCESS ) {
2171 dev->trans_start = jiffies;
2172 for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++);
2173 } else {
2174 priv->fids[fid] &= 0xffff;
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002175 dev->stats.tx_window_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 }
2177 if (i < MAX_FIDS)
2178 netif_wake_queue(dev);
2179 dev_kfree_skb(skb);
2180}
2181
2182static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) {
2183 s16 len;
2184 int i, j;
Wang Chenfaf39942008-10-14 13:30:33 +08002185 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186 u32 *fids = priv->fids;
2187
2188 if (test_bit(FLAG_MPI, &priv->flags)) {
2189 /* Not implemented yet for MPI350 */
2190 netif_stop_queue(dev);
2191 return -ENETDOWN;
2192 }
2193
2194 if ( skb == NULL ) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07002195 airo_print_err(dev->name, "%s: skb == NULL!", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196 return 0;
2197 }
2198
2199 /* Find a vacant FID */
2200 for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ );
2201 for( j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++ );
2202
2203 if ( j >= MAX_FIDS ) {
2204 netif_stop_queue(dev);
2205
2206 if (i == MAX_FIDS) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002207 dev->stats.tx_fifo_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208 return 1;
2209 }
2210 }
2211 /* check min length*/
2212 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
2213 /* Mark fid as used & save length for later */
2214 fids[i] |= (len << 16);
2215 priv->xmit11.skb = skb;
2216 priv->xmit11.fid = i;
2217 if (down_trylock(&priv->sem) != 0) {
2218 set_bit(FLAG_PENDING_XMIT11, &priv->flags);
2219 netif_stop_queue(dev);
Dan Williams3c304952006-04-15 12:26:18 -04002220 set_bit(JOB_XMIT11, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 wake_up_interruptible(&priv->thr_wait);
2222 } else
2223 airo_end_xmit11(dev);
2224 return 0;
2225}
2226
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002227static void airo_read_stats(struct net_device *dev)
Al Viroa23ace52007-12-19 22:24:16 -05002228{
Wang Chenfaf39942008-10-14 13:30:33 +08002229 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 StatsRid stats_rid;
Al Viroa23ace52007-12-19 22:24:16 -05002231 __le32 *vals = stats_rid.vals;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232
Dan Williams3c304952006-04-15 12:26:18 -04002233 clear_bit(JOB_STATS, &ai->jobs);
Pavel Machekca078ba2005-09-03 15:56:57 -07002234 if (ai->power.event) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235 up(&ai->sem);
2236 return;
2237 }
2238 readStatsRid(ai, &stats_rid, RID_STATS, 0);
2239 up(&ai->sem);
2240
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002241 dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) +
Al Viroa23ace52007-12-19 22:24:16 -05002242 le32_to_cpu(vals[45]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002243 dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) +
Al Viroa23ace52007-12-19 22:24:16 -05002244 le32_to_cpu(vals[41]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002245 dev->stats.rx_bytes = le32_to_cpu(vals[92]);
2246 dev->stats.tx_bytes = le32_to_cpu(vals[91]);
2247 dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) +
Al Viroa23ace52007-12-19 22:24:16 -05002248 le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002249 dev->stats.tx_errors = le32_to_cpu(vals[42]) +
2250 dev->stats.tx_fifo_errors;
2251 dev->stats.multicast = le32_to_cpu(vals[43]);
2252 dev->stats.collisions = le32_to_cpu(vals[89]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253
2254 /* detailed rx_errors: */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002255 dev->stats.rx_length_errors = le32_to_cpu(vals[3]);
2256 dev->stats.rx_crc_errors = le32_to_cpu(vals[4]);
2257 dev->stats.rx_frame_errors = le32_to_cpu(vals[2]);
2258 dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259}
2260
Jouni Malinenff1d2762005-05-12 22:54:16 -04002261static struct net_device_stats *airo_get_stats(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262{
Wang Chenfaf39942008-10-14 13:30:33 +08002263 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264
Dan Williams3c304952006-04-15 12:26:18 -04002265 if (!test_bit(JOB_STATS, &local->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 /* Get stats out of the card if available */
2267 if (down_trylock(&local->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04002268 set_bit(JOB_STATS, &local->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 wake_up_interruptible(&local->thr_wait);
2270 } else
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002271 airo_read_stats(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 }
2273
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002274 return &dev->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275}
2276
2277static void airo_set_promisc(struct airo_info *ai) {
2278 Cmd cmd;
2279 Resp rsp;
2280
2281 memset(&cmd, 0, sizeof(cmd));
2282 cmd.cmd=CMD_SETMODE;
Dan Williams3c304952006-04-15 12:26:18 -04002283 clear_bit(JOB_PROMISC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284 cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC;
2285 issuecommand(ai, &cmd, &rsp);
2286 up(&ai->sem);
2287}
2288
2289static void airo_set_multicast_list(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08002290 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291
2292 if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
2293 change_bit(FLAG_PROMISC, &ai->flags);
2294 if (down_trylock(&ai->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04002295 set_bit(JOB_PROMISC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 wake_up_interruptible(&ai->thr_wait);
2297 } else
2298 airo_set_promisc(ai);
2299 }
2300
2301 if ((dev->flags&IFF_ALLMULTI)||dev->mc_count>0) {
2302 /* Turn on multicast. (Should be already setup...) */
2303 }
2304}
2305
2306static int airo_set_mac_address(struct net_device *dev, void *p)
2307{
Wang Chenfaf39942008-10-14 13:30:33 +08002308 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309 struct sockaddr *addr = p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310
2311 readConfigRid(ai, 1);
2312 memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len);
2313 set_bit (FLAG_COMMIT, &ai->flags);
2314 disable_MAC(ai, 1);
2315 writeConfigRid (ai, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02002316 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317 memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len);
2318 if (ai->wifidev)
2319 memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len);
2320 return 0;
2321}
2322
2323static int airo_change_mtu(struct net_device *dev, int new_mtu)
2324{
2325 if ((new_mtu < 68) || (new_mtu > 2400))
2326 return -EINVAL;
2327 dev->mtu = new_mtu;
2328 return 0;
2329}
2330
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002331static LIST_HEAD(airo_devices);
2332
2333static void add_airo_dev(struct airo_info *ai)
2334{
2335 /* Upper layers already keep track of PCI devices,
2336 * so we only need to remember our non-PCI cards. */
2337 if (!ai->pci)
2338 list_add_tail(&ai->dev_list, &airo_devices);
2339}
2340
2341static void del_airo_dev(struct airo_info *ai)
2342{
2343 if (!ai->pci)
2344 list_del(&ai->dev_list);
2345}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346
2347static int airo_close(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08002348 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349
2350 netif_stop_queue(dev);
2351
2352 if (ai->wifidev != dev) {
2353#ifdef POWER_ON_DOWN
2354 /* Shut power to the card. The idea is that the user can save
2355 * power when he doesn't need the card with "ifconfig down".
2356 * That's the method that is most friendly towards the network
2357 * stack (i.e. the network stack won't try to broadcast
2358 * anything on the interface and routes are gone. Jean II */
2359 set_bit(FLAG_RADIO_DOWN, &ai->flags);
2360 disable_MAC(ai, 1);
2361#endif
2362 disable_interrupts( ai );
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002363
2364 free_irq(dev->irq, dev);
2365
2366 set_bit(JOB_DIE, &ai->jobs);
2367 kthread_stop(ai->airo_thread_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002368 }
2369 return 0;
2370}
2371
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372void stop_airo_card( struct net_device *dev, int freeres )
2373{
Wang Chenfaf39942008-10-14 13:30:33 +08002374 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375
2376 set_bit(FLAG_RADIO_DOWN, &ai->flags);
2377 disable_MAC(ai, 1);
2378 disable_interrupts(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379 takedown_proc_entry( dev, ai );
2380 if (test_bit(FLAG_REGISTERED, &ai->flags)) {
2381 unregister_netdev( dev );
2382 if (ai->wifidev) {
2383 unregister_netdev(ai->wifidev);
2384 free_netdev(ai->wifidev);
2385 ai->wifidev = NULL;
2386 }
2387 clear_bit(FLAG_REGISTERED, &ai->flags);
2388 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389 /*
2390 * Clean out tx queue
2391 */
David S. Millerb03efcf2005-07-08 14:57:23 -07002392 if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 struct sk_buff *skb = NULL;
2394 for (;(skb = skb_dequeue(&ai->txq));)
2395 dev_kfree_skb(skb);
2396 }
2397
Dan Williams9e75af32006-03-16 13:46:29 -05002398 airo_networks_free (ai);
2399
Jesper Juhlb4558ea2005-10-28 16:53:13 -04002400 kfree(ai->flash);
2401 kfree(ai->rssi);
2402 kfree(ai->APList);
2403 kfree(ai->SSID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404 if (freeres) {
2405 /* PCMCIA frees this stuff, so only for PCI and ISA */
2406 release_region( dev->base_addr, 64 );
2407 if (test_bit(FLAG_MPI, &ai->flags)) {
2408 if (ai->pci)
2409 mpi_unmap_card(ai->pci);
2410 if (ai->pcimem)
2411 iounmap(ai->pcimem);
2412 if (ai->pciaux)
2413 iounmap(ai->pciaux);
2414 pci_free_consistent(ai->pci, PCI_SHARED_LEN,
2415 ai->shared, ai->shared_dma);
2416 }
2417 }
Herbert Xuf12cc202006-08-22 20:36:13 +10002418 crypto_free_cipher(ai->tfm);
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002419 del_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420 free_netdev( dev );
2421}
2422
2423EXPORT_SYMBOL(stop_airo_card);
2424
Stephen Hemmingerb95cce32007-09-26 22:13:38 -07002425static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426{
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -07002427 memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 return ETH_ALEN;
2429}
2430
2431static void mpi_unmap_card(struct pci_dev *pci)
2432{
2433 unsigned long mem_start = pci_resource_start(pci, 1);
2434 unsigned long mem_len = pci_resource_len(pci, 1);
2435 unsigned long aux_start = pci_resource_start(pci, 2);
2436 unsigned long aux_len = AUXMEMSIZE;
2437
2438 release_mem_region(aux_start, aux_len);
2439 release_mem_region(mem_start, mem_len);
2440}
2441
2442/*************************************************************
2443 * This routine assumes that descriptors have been setup .
2444 * Run at insmod time or after reset when the decriptors
2445 * have been initialized . Returns 0 if all is well nz
2446 * otherwise . Does not allocate memory but sets up card
2447 * using previously allocated descriptors.
2448 */
2449static int mpi_init_descriptors (struct airo_info *ai)
2450{
2451 Cmd cmd;
2452 Resp rsp;
2453 int i;
2454 int rc = SUCCESS;
2455
2456 /* Alloc card RX descriptors */
2457 netif_stop_queue(ai->dev);
2458
2459 memset(&rsp,0,sizeof(rsp));
2460 memset(&cmd,0,sizeof(cmd));
2461
2462 cmd.cmd = CMD_ALLOCATEAUX;
2463 cmd.parm0 = FID_RX;
2464 cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux);
2465 cmd.parm2 = MPI_MAX_FIDS;
2466 rc=issuecommand(ai, &cmd, &rsp);
2467 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002468 airo_print_err(ai->dev->name, "Couldn't allocate RX FID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469 return rc;
2470 }
2471
2472 for (i=0; i<MPI_MAX_FIDS; i++) {
2473 memcpy_toio(ai->rxfids[i].card_ram_off,
2474 &ai->rxfids[i].rx_desc, sizeof(RxFid));
2475 }
2476
2477 /* Alloc card TX descriptors */
2478
2479 memset(&rsp,0,sizeof(rsp));
2480 memset(&cmd,0,sizeof(cmd));
2481
2482 cmd.cmd = CMD_ALLOCATEAUX;
2483 cmd.parm0 = FID_TX;
2484 cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux);
2485 cmd.parm2 = MPI_MAX_FIDS;
2486
2487 for (i=0; i<MPI_MAX_FIDS; i++) {
2488 ai->txfids[i].tx_desc.valid = 1;
2489 memcpy_toio(ai->txfids[i].card_ram_off,
2490 &ai->txfids[i].tx_desc, sizeof(TxFid));
2491 }
2492 ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
2493
2494 rc=issuecommand(ai, &cmd, &rsp);
2495 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002496 airo_print_err(ai->dev->name, "Couldn't allocate TX FID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497 return rc;
2498 }
2499
2500 /* Alloc card Rid descriptor */
2501 memset(&rsp,0,sizeof(rsp));
2502 memset(&cmd,0,sizeof(cmd));
2503
2504 cmd.cmd = CMD_ALLOCATEAUX;
2505 cmd.parm0 = RID_RW;
2506 cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux);
2507 cmd.parm2 = 1; /* Magic number... */
2508 rc=issuecommand(ai, &cmd, &rsp);
2509 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002510 airo_print_err(ai->dev->name, "Couldn't allocate RID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511 return rc;
2512 }
2513
2514 memcpy_toio(ai->config_desc.card_ram_off,
2515 &ai->config_desc.rid_desc, sizeof(Rid));
2516
2517 return rc;
2518}
2519
2520/*
2521 * We are setting up three things here:
2522 * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid.
2523 * 2) Map PCI memory for issueing commands.
2524 * 3) Allocate memory (shared) to send and receive ethernet frames.
2525 */
Michal Schmidt1138c372007-06-29 15:33:41 +02002526static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527{
2528 unsigned long mem_start, mem_len, aux_start, aux_len;
2529 int rc = -1;
2530 int i;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002531 dma_addr_t busaddroff;
2532 unsigned char *vpackoff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533 unsigned char __iomem *pciaddroff;
2534
2535 mem_start = pci_resource_start(pci, 1);
2536 mem_len = pci_resource_len(pci, 1);
2537 aux_start = pci_resource_start(pci, 2);
2538 aux_len = AUXMEMSIZE;
2539
Michal Schmidt1138c372007-06-29 15:33:41 +02002540 if (!request_mem_region(mem_start, mem_len, DRV_NAME)) {
2541 airo_print_err("", "Couldn't get region %x[%x]",
2542 (int)mem_start, (int)mem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543 goto out;
2544 }
Michal Schmidt1138c372007-06-29 15:33:41 +02002545 if (!request_mem_region(aux_start, aux_len, DRV_NAME)) {
2546 airo_print_err("", "Couldn't get region %x[%x]",
2547 (int)aux_start, (int)aux_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548 goto free_region1;
2549 }
2550
2551 ai->pcimem = ioremap(mem_start, mem_len);
2552 if (!ai->pcimem) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002553 airo_print_err("", "Couldn't map region %x[%x]",
2554 (int)mem_start, (int)mem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555 goto free_region2;
2556 }
2557 ai->pciaux = ioremap(aux_start, aux_len);
2558 if (!ai->pciaux) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002559 airo_print_err("", "Couldn't map region %x[%x]",
2560 (int)aux_start, (int)aux_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561 goto free_memmap;
2562 }
2563
2564 /* Reserve PKTSIZE for each fid and 2K for the Rids */
2565 ai->shared = pci_alloc_consistent(pci, PCI_SHARED_LEN, &ai->shared_dma);
2566 if (!ai->shared) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002567 airo_print_err("", "Couldn't alloc_consistent %d",
2568 PCI_SHARED_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569 goto free_auxmap;
2570 }
2571
2572 /*
2573 * Setup descriptor RX, TX, CONFIG
2574 */
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002575 busaddroff = ai->shared_dma;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576 pciaddroff = ai->pciaux + AUX_OFFSET;
2577 vpackoff = ai->shared;
2578
2579 /* RX descriptor setup */
2580 for(i = 0; i < MPI_MAX_FIDS; i++) {
2581 ai->rxfids[i].pending = 0;
2582 ai->rxfids[i].card_ram_off = pciaddroff;
2583 ai->rxfids[i].virtual_host_addr = vpackoff;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002584 ai->rxfids[i].rx_desc.host_addr = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 ai->rxfids[i].rx_desc.valid = 1;
2586 ai->rxfids[i].rx_desc.len = PKTSIZE;
2587 ai->rxfids[i].rx_desc.rdy = 0;
2588
2589 pciaddroff += sizeof(RxFid);
2590 busaddroff += PKTSIZE;
2591 vpackoff += PKTSIZE;
2592 }
2593
2594 /* TX descriptor setup */
2595 for(i = 0; i < MPI_MAX_FIDS; i++) {
2596 ai->txfids[i].card_ram_off = pciaddroff;
2597 ai->txfids[i].virtual_host_addr = vpackoff;
2598 ai->txfids[i].tx_desc.valid = 1;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002599 ai->txfids[i].tx_desc.host_addr = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600 memcpy(ai->txfids[i].virtual_host_addr,
2601 &wifictlhdr8023, sizeof(wifictlhdr8023));
2602
2603 pciaddroff += sizeof(TxFid);
2604 busaddroff += PKTSIZE;
2605 vpackoff += PKTSIZE;
2606 }
2607 ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
2608
2609 /* Rid descriptor setup */
2610 ai->config_desc.card_ram_off = pciaddroff;
2611 ai->config_desc.virtual_host_addr = vpackoff;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002612 ai->config_desc.rid_desc.host_addr = busaddroff;
2613 ai->ridbus = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614 ai->config_desc.rid_desc.rid = 0;
2615 ai->config_desc.rid_desc.len = RIDSIZE;
2616 ai->config_desc.rid_desc.valid = 1;
2617 pciaddroff += sizeof(Rid);
2618 busaddroff += RIDSIZE;
2619 vpackoff += RIDSIZE;
2620
2621 /* Tell card about descriptors */
2622 if (mpi_init_descriptors (ai) != SUCCESS)
2623 goto free_shared;
2624
2625 return 0;
2626 free_shared:
2627 pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
2628 free_auxmap:
2629 iounmap(ai->pciaux);
2630 free_memmap:
2631 iounmap(ai->pcimem);
2632 free_region2:
2633 release_mem_region(aux_start, aux_len);
2634 free_region1:
2635 release_mem_region(mem_start, mem_len);
2636 out:
2637 return rc;
2638}
2639
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -07002640static const struct header_ops airo_header_ops = {
2641 .parse = wll_header_parse,
2642};
2643
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644static void wifi_setup(struct net_device *dev)
2645{
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -07002646 dev->header_ops = &airo_header_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647 dev->hard_start_xmit = &airo_start_xmit11;
2648 dev->get_stats = &airo_get_stats;
2649 dev->set_mac_address = &airo_set_mac_address;
2650 dev->do_ioctl = &airo_ioctl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 dev->wireless_handlers = &airo_handler_def;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002652 dev->change_mtu = &airo_change_mtu;
2653 dev->open = &airo_open;
2654 dev->stop = &airo_close;
2655
2656 dev->type = ARPHRD_IEEE80211;
2657 dev->hard_header_len = ETH_HLEN;
Dan Williams15db2762006-03-16 13:46:27 -05002658 dev->mtu = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659 dev->addr_len = ETH_ALEN;
2660 dev->tx_queue_len = 100;
2661
2662 memset(dev->broadcast,0xFF, ETH_ALEN);
2663
2664 dev->flags = IFF_BROADCAST|IFF_MULTICAST;
2665}
2666
2667static struct net_device *init_wifidev(struct airo_info *ai,
2668 struct net_device *ethdev)
2669{
2670 int err;
2671 struct net_device *dev = alloc_netdev(0, "wifi%d", wifi_setup);
2672 if (!dev)
2673 return NULL;
Wang Chenfaf39942008-10-14 13:30:33 +08002674 dev->ml_priv = ethdev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675 dev->irq = ethdev->irq;
2676 dev->base_addr = ethdev->base_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677 dev->wireless_data = ethdev->wireless_data;
Masakazu Mokuno229ce3a2008-05-14 14:16:50 +09002678 SET_NETDEV_DEV(dev, ethdev->dev.parent);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679 memcpy(dev->dev_addr, ethdev->dev_addr, dev->addr_len);
2680 err = register_netdev(dev);
2681 if (err<0) {
2682 free_netdev(dev);
2683 return NULL;
2684 }
2685 return dev;
2686}
2687
Jouni Malinenff1d2762005-05-12 22:54:16 -04002688static int reset_card( struct net_device *dev , int lock) {
Wang Chenfaf39942008-10-14 13:30:33 +08002689 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690
2691 if (lock && down_interruptible(&ai->sem))
2692 return -1;
2693 waitbusy (ai);
2694 OUT4500(ai,COMMAND,CMD_SOFTRESET);
2695 msleep(200);
2696 waitbusy (ai);
2697 msleep(200);
2698 if (lock)
2699 up(&ai->sem);
2700 return 0;
2701}
2702
Dan Williams3c304952006-04-15 12:26:18 -04002703#define AIRO_MAX_NETWORK_COUNT 64
Dan Williams9e75af32006-03-16 13:46:29 -05002704static int airo_networks_allocate(struct airo_info *ai)
2705{
2706 if (ai->networks)
2707 return 0;
2708
2709 ai->networks =
Dan Williams3c304952006-04-15 12:26:18 -04002710 kzalloc(AIRO_MAX_NETWORK_COUNT * sizeof(BSSListElement),
Dan Williams9e75af32006-03-16 13:46:29 -05002711 GFP_KERNEL);
2712 if (!ai->networks) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002713 airo_print_warn("", "Out of memory allocating beacons");
Dan Williams9e75af32006-03-16 13:46:29 -05002714 return -ENOMEM;
2715 }
2716
2717 return 0;
2718}
2719
2720static void airo_networks_free(struct airo_info *ai)
2721{
Dan Williams9e75af32006-03-16 13:46:29 -05002722 kfree(ai->networks);
2723 ai->networks = NULL;
2724}
2725
2726static void airo_networks_initialize(struct airo_info *ai)
2727{
2728 int i;
2729
2730 INIT_LIST_HEAD(&ai->network_free_list);
2731 INIT_LIST_HEAD(&ai->network_list);
Dan Williams3c304952006-04-15 12:26:18 -04002732 for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++)
Dan Williams9e75af32006-03-16 13:46:29 -05002733 list_add_tail(&ai->networks[i].list,
2734 &ai->network_free_list);
2735}
2736
Jouni Malinenff1d2762005-05-12 22:54:16 -04002737static struct net_device *_init_airo_card( unsigned short irq, int port,
2738 int is_pcmcia, struct pci_dev *pci,
2739 struct device *dmdev )
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740{
2741 struct net_device *dev;
2742 struct airo_info *ai;
2743 int i, rc;
Dan Williamsf65b56d2009-01-24 09:10:42 -05002744 CapabilityRid cap_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745
2746 /* Create the network device object. */
Michal Schmidt1138c372007-06-29 15:33:41 +02002747 dev = alloc_netdev(sizeof(*ai), "", ether_setup);
2748 if (!dev) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002749 airo_print_err("", "Couldn't alloc_etherdev");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751 }
2752
Wang Chenfaf39942008-10-14 13:30:33 +08002753 ai = dev->ml_priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 ai->wifidev = NULL;
Michal Schmidtfb038c22007-06-29 15:33:52 +02002755 ai->flags = 1 << FLAG_RADIO_DOWN;
Dan Williams3c304952006-04-15 12:26:18 -04002756 ai->jobs = 0;
Dan Williams934d8bf2006-03-16 13:46:23 -05002757 ai->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758 if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002759 airo_print_dbg("", "Found an MPI350 card");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760 set_bit(FLAG_MPI, &ai->flags);
2761 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762 spin_lock_init(&ai->aux_lock);
2763 sema_init(&ai->sem, 1);
2764 ai->config.len = 0;
2765 ai->pci = pci;
2766 init_waitqueue_head (&ai->thr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 ai->tfm = NULL;
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002768 add_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769
Dan Williams9e75af32006-03-16 13:46:29 -05002770 if (airo_networks_allocate (ai))
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002771 goto err_out_free;
Dan Williams9e75af32006-03-16 13:46:29 -05002772 airo_networks_initialize (ai);
2773
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 /* The Airo-specific entries in the device structure. */
2775 if (test_bit(FLAG_MPI,&ai->flags)) {
2776 skb_queue_head_init (&ai->txq);
2777 dev->hard_start_xmit = &mpi_start_xmit;
2778 } else
2779 dev->hard_start_xmit = &airo_start_xmit;
2780 dev->get_stats = &airo_get_stats;
2781 dev->set_multicast_list = &airo_set_multicast_list;
2782 dev->set_mac_address = &airo_set_mac_address;
2783 dev->do_ioctl = &airo_ioctl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 dev->wireless_handlers = &airo_handler_def;
2785 ai->wireless_data.spy_data = &ai->spy_data;
2786 dev->wireless_data = &ai->wireless_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 dev->change_mtu = &airo_change_mtu;
2788 dev->open = &airo_open;
2789 dev->stop = &airo_close;
2790 dev->irq = irq;
2791 dev->base_addr = port;
2792
2793 SET_NETDEV_DEV(dev, dmdev);
2794
Matthieu CASTET1d97f382005-12-01 02:35:26 -05002795 reset_card (dev, 1);
2796 msleep(400);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 if (!is_pcmcia) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002799 if (!request_region(dev->base_addr, 64, DRV_NAME)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 rc = -EBUSY;
Dan Williams934d8bf2006-03-16 13:46:23 -05002801 airo_print_err(dev->name, "Couldn't request region");
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002802 goto err_out_nets;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 }
2804 }
2805
2806 if (test_bit(FLAG_MPI,&ai->flags)) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002807 if (mpi_map_card(ai, pci)) {
2808 airo_print_err("", "Could not map memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 goto err_out_res;
2810 }
2811 }
2812
2813 if (probe) {
Dan Williamsf65b56d2009-01-24 09:10:42 -05002814 if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002815 airo_print_err(dev->name, "MAC could not be enabled" );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 rc = -EIO;
2817 goto err_out_map;
2818 }
2819 } else if (!test_bit(FLAG_MPI,&ai->flags)) {
2820 ai->bap_read = fast_bap_read;
2821 set_bit(FLAG_FLASHING, &ai->flags);
2822 }
2823
Michal Schmidt1138c372007-06-29 15:33:41 +02002824 strcpy(dev->name, "eth%d");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 rc = register_netdev(dev);
2826 if (rc) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002827 airo_print_err(dev->name, "Couldn't register_netdev");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 goto err_out_map;
2829 }
2830 ai->wifidev = init_wifidev(ai, dev);
Florin Malita431aca52006-10-10 16:46:30 -04002831 if (!ai->wifidev)
2832 goto err_out_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833
Dan Williamsf65b56d2009-01-24 09:10:42 -05002834 rc = readCapabilityRid(ai, &cap_rid, 1);
2835 if (rc != SUCCESS) {
2836 rc = -EIO;
2837 goto err_out_wifi;
2838 }
2839
2840 airo_print_info(dev->name, "Firmware version %x.%x.%02x",
2841 ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF),
2842 (le16_to_cpu(cap_rid.softVer) & 0xFF),
2843 le16_to_cpu(cap_rid.softSubVer));
2844
2845 /* Test for WPA support */
2846 /* Only firmware versions 5.30.17 or better can do WPA */
2847 if (le16_to_cpu(cap_rid.softVer) > 0x530
2848 || (le16_to_cpu(cap_rid.softVer) == 0x530
2849 && le16_to_cpu(cap_rid.softSubVer) >= 17)) {
2850 airo_print_info(ai->dev->name, "WPA supported.");
2851
2852 set_bit(FLAG_WPA_CAPABLE, &ai->flags);
2853 ai->bssListFirst = RID_WPA_BSSLISTFIRST;
2854 ai->bssListNext = RID_WPA_BSSLISTNEXT;
2855 ai->bssListRidLen = sizeof(BSSListRid);
2856 } else {
2857 airo_print_info(ai->dev->name, "WPA unsupported with firmware "
2858 "versions older than 5.30.17.");
2859
2860 ai->bssListFirst = RID_BSSLISTFIRST;
2861 ai->bssListNext = RID_BSSLISTNEXT;
2862 ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra);
2863 }
2864
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865 set_bit(FLAG_REGISTERED,&ai->flags);
Johannes Berge1749612008-10-27 15:59:26 -07002866 airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002867
2868 /* Allocate the transmit buffers */
2869 if (probe && !test_bit(FLAG_MPI,&ai->flags))
2870 for( i = 0; i < MAX_FIDS; i++ )
Dan Williams15db2762006-03-16 13:46:27 -05002871 ai->fids[i] = transmit_allocate(ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872
Wang Chenfaf39942008-10-14 13:30:33 +08002873 if (setup_proc_entry(dev, dev->ml_priv) < 0)
Florin Malita431aca52006-10-10 16:46:30 -04002874 goto err_out_wifi;
2875
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876 return dev;
2877
Florin Malita431aca52006-10-10 16:46:30 -04002878err_out_wifi:
2879 unregister_netdev(ai->wifidev);
2880 free_netdev(ai->wifidev);
2881err_out_reg:
2882 unregister_netdev(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002883err_out_map:
2884 if (test_bit(FLAG_MPI,&ai->flags) && pci) {
2885 pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
2886 iounmap(ai->pciaux);
2887 iounmap(ai->pcimem);
2888 mpi_unmap_card(ai->pci);
2889 }
2890err_out_res:
2891 if (!is_pcmcia)
2892 release_region( dev->base_addr, 64 );
Michal Schmidt4d881902007-03-16 12:42:59 +01002893err_out_nets:
2894 airo_networks_free(ai);
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002895 del_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896err_out_free:
2897 free_netdev(dev);
2898 return NULL;
2899}
2900
2901struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia,
2902 struct device *dmdev)
2903{
2904 return _init_airo_card ( irq, port, is_pcmcia, NULL, dmdev);
2905}
2906
2907EXPORT_SYMBOL(init_airo_card);
2908
2909static int waitbusy (struct airo_info *ai) {
2910 int delay = 0;
Andrew Mortonb212f332008-05-28 12:40:39 -07002911 while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912 udelay (10);
2913 if ((++delay % 20) == 0)
2914 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
2915 }
2916 return delay < 10000;
2917}
2918
2919int reset_airo_card( struct net_device *dev )
2920{
2921 int i;
Wang Chenfaf39942008-10-14 13:30:33 +08002922 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002923
2924 if (reset_card (dev, 1))
2925 return -1;
2926
2927 if ( setup_card(ai, dev->dev_addr, 1 ) != SUCCESS ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002928 airo_print_err(dev->name, "MAC could not be enabled");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002929 return -1;
2930 }
Johannes Berge1749612008-10-27 15:59:26 -07002931 airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002932 /* Allocate the transmit buffers if needed */
2933 if (!test_bit(FLAG_MPI,&ai->flags))
2934 for( i = 0; i < MAX_FIDS; i++ )
Dan Williams15db2762006-03-16 13:46:27 -05002935 ai->fids[i] = transmit_allocate (ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002936
2937 enable_interrupts( ai );
2938 netif_wake_queue(dev);
2939 return 0;
2940}
2941
2942EXPORT_SYMBOL(reset_airo_card);
2943
2944static void airo_send_event(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08002945 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946 union iwreq_data wrqu;
2947 StatusRid status_rid;
2948
Dan Williams3c304952006-04-15 12:26:18 -04002949 clear_bit(JOB_EVENT, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002950 PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
2951 up(&ai->sem);
2952 wrqu.data.length = 0;
2953 wrqu.data.flags = 0;
2954 memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN);
2955 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
2956
2957 /* Send event to user space */
2958 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
2959}
2960
Dan Williams9e75af32006-03-16 13:46:29 -05002961static void airo_process_scan_results (struct airo_info *ai) {
2962 union iwreq_data wrqu;
Dan Williams3c304952006-04-15 12:26:18 -04002963 BSSListRid bss;
Dan Williams9e75af32006-03-16 13:46:29 -05002964 int rc;
2965 BSSListElement * loop_net;
2966 BSSListElement * tmp_net;
2967
2968 /* Blow away current list of scan results */
2969 list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) {
2970 list_move_tail (&loop_net->list, &ai->network_free_list);
2971 /* Don't blow away ->list, just BSS data */
2972 memset (loop_net, 0, sizeof (loop_net->bss));
2973 }
2974
2975 /* Try to read the first entry of the scan result */
Dan Williams3c304952006-04-15 12:26:18 -04002976 rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0);
Al Viro17e70492007-12-19 18:56:37 -05002977 if((rc) || (bss.index == cpu_to_le16(0xffff))) {
Dan Williams9e75af32006-03-16 13:46:29 -05002978 /* No scan results */
2979 goto out;
2980 }
2981
2982 /* Read and parse all entries */
2983 tmp_net = NULL;
Al Viro17e70492007-12-19 18:56:37 -05002984 while((!rc) && (bss.index != cpu_to_le16(0xffff))) {
Dan Williams9e75af32006-03-16 13:46:29 -05002985 /* Grab a network off the free list */
2986 if (!list_empty(&ai->network_free_list)) {
2987 tmp_net = list_entry(ai->network_free_list.next,
2988 BSSListElement, list);
2989 list_del(ai->network_free_list.next);
2990 }
2991
2992 if (tmp_net != NULL) {
Dan Williams3c304952006-04-15 12:26:18 -04002993 memcpy(tmp_net, &bss, sizeof(tmp_net->bss));
Dan Williams9e75af32006-03-16 13:46:29 -05002994 list_add_tail(&tmp_net->list, &ai->network_list);
2995 tmp_net = NULL;
2996 }
2997
2998 /* Read next entry */
Dan Williams3c304952006-04-15 12:26:18 -04002999 rc = PC4500_readrid(ai, ai->bssListNext,
3000 &bss, ai->bssListRidLen, 0);
Dan Williams9e75af32006-03-16 13:46:29 -05003001 }
3002
3003out:
3004 ai->scan_timeout = 0;
Dan Williams3c304952006-04-15 12:26:18 -04003005 clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
Dan Williams9e75af32006-03-16 13:46:29 -05003006 up(&ai->sem);
3007
3008 /* Send an empty event to user space.
3009 * We don't send the received data on
3010 * the event because it would require
3011 * us to do complex transcoding, and
3012 * we want to minimise the work done in
3013 * the irq handler. Use a request to
3014 * extract the data - Jean II */
3015 wrqu.data.length = 0;
3016 wrqu.data.flags = 0;
3017 wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL);
3018}
3019
Linus Torvalds1da177e2005-04-16 15:20:36 -07003020static int airo_thread(void *data) {
3021 struct net_device *dev = data;
Wang Chenfaf39942008-10-14 13:30:33 +08003022 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003023 int locked;
Rafael J. Wysocki83144182007-07-17 04:03:35 -07003024
3025 set_freezable();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003026 while(1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003027 /* make swsusp happy with our thread */
Christoph Lameter3e1d1d22005-06-24 23:13:50 -07003028 try_to_freeze();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003029
Dan Williams3c304952006-04-15 12:26:18 -04003030 if (test_bit(JOB_DIE, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003031 break;
3032
Dan Williams3c304952006-04-15 12:26:18 -04003033 if (ai->jobs) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034 locked = down_interruptible(&ai->sem);
3035 } else {
3036 wait_queue_t wait;
3037
3038 init_waitqueue_entry(&wait, current);
3039 add_wait_queue(&ai->thr_wait, &wait);
3040 for (;;) {
3041 set_current_state(TASK_INTERRUPTIBLE);
Dan Williams3c304952006-04-15 12:26:18 -04003042 if (ai->jobs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003043 break;
Dan Williams9e75af32006-03-16 13:46:29 -05003044 if (ai->expires || ai->scan_timeout) {
3045 if (ai->scan_timeout &&
3046 time_after_eq(jiffies,ai->scan_timeout)){
Dan Williams3c304952006-04-15 12:26:18 -04003047 set_bit(JOB_SCAN_RESULTS, &ai->jobs);
Dan Williams9e75af32006-03-16 13:46:29 -05003048 break;
3049 } else if (ai->expires &&
3050 time_after_eq(jiffies,ai->expires)){
Dan Williams3c304952006-04-15 12:26:18 -04003051 set_bit(JOB_AUTOWEP, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003052 break;
3053 }
Dave Kleikamp5bb85f12006-10-10 14:45:46 -07003054 if (!kthread_should_stop() &&
3055 !freezing(current)) {
Dan Williams9e75af32006-03-16 13:46:29 -05003056 unsigned long wake_at;
3057 if (!ai->expires || !ai->scan_timeout) {
3058 wake_at = max(ai->expires,
3059 ai->scan_timeout);
3060 } else {
3061 wake_at = min(ai->expires,
3062 ai->scan_timeout);
3063 }
3064 schedule_timeout(wake_at - jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003065 continue;
3066 }
Dave Kleikamp5bb85f12006-10-10 14:45:46 -07003067 } else if (!kthread_should_stop() &&
3068 !freezing(current)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003069 schedule();
3070 continue;
3071 }
3072 break;
3073 }
3074 current->state = TASK_RUNNING;
3075 remove_wait_queue(&ai->thr_wait, &wait);
3076 locked = 1;
3077 }
3078
3079 if (locked)
3080 continue;
3081
Dan Williams3c304952006-04-15 12:26:18 -04003082 if (test_bit(JOB_DIE, &ai->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003083 up(&ai->sem);
3084 break;
3085 }
3086
Pavel Machekca078ba2005-09-03 15:56:57 -07003087 if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003088 up(&ai->sem);
3089 continue;
3090 }
3091
Dan Williams3c304952006-04-15 12:26:18 -04003092 if (test_bit(JOB_XMIT, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093 airo_end_xmit(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003094 else if (test_bit(JOB_XMIT11, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003095 airo_end_xmit11(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003096 else if (test_bit(JOB_STATS, &ai->jobs))
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003097 airo_read_stats(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003098 else if (test_bit(JOB_WSTATS, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003099 airo_read_wireless_stats(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003100 else if (test_bit(JOB_PROMISC, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003101 airo_set_promisc(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003102 else if (test_bit(JOB_MIC, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003103 micinit(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003104 else if (test_bit(JOB_EVENT, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003105 airo_send_event(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003106 else if (test_bit(JOB_AUTOWEP, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003107 timer_func(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003108 else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs))
Dan Williams9e75af32006-03-16 13:46:29 -05003109 airo_process_scan_results(ai);
3110 else /* Shouldn't get here, but we make sure to unlock */
3111 up(&ai->sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003112 }
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07003113
3114 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003115}
3116
Al Viro0300b332007-12-19 22:38:33 -05003117static int header_len(__le16 ctl)
3118{
3119 u16 fc = le16_to_cpu(ctl);
3120 switch (fc & 0xc) {
3121 case 4:
3122 if ((fc & 0xe0) == 0xc0)
3123 return 10; /* one-address control packet */
3124 return 16; /* two-address control packet */
3125 case 8:
3126 if ((fc & 0x300) == 0x300)
3127 return 30; /* WDS packet */
3128 }
3129 return 24;
3130}
3131
Dan Williamsf55d4512009-01-24 09:04:12 -05003132static void airo_handle_cisco_mic(struct airo_info *ai)
3133{
3134 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) {
3135 set_bit(JOB_MIC, &ai->jobs);
3136 wake_up_interruptible(&ai->thr_wait);
3137 }
3138}
3139
3140/* Airo Status codes */
3141#define STAT_NOBEACON 0x8000 /* Loss of sync - missed beacons */
3142#define STAT_MAXRETRIES 0x8001 /* Loss of sync - max retries */
3143#define STAT_MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/
3144#define STAT_FORCELOSS 0x8003 /* Loss of sync - host request */
3145#define STAT_TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */
3146#define STAT_DEAUTH 0x8100 /* low byte is 802.11 reason code */
3147#define STAT_DISASSOC 0x8200 /* low byte is 802.11 reason code */
3148#define STAT_ASSOC_FAIL 0x8400 /* low byte is 802.11 reason code */
3149#define STAT_AUTH_FAIL 0x0300 /* low byte is 802.11 reason code */
3150#define STAT_ASSOC 0x0400 /* Associated */
3151#define STAT_REASSOC 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */
3152
3153static void airo_print_status(const char *devname, u16 status)
3154{
3155 u8 reason = status & 0xFF;
3156
3157 switch (status) {
3158 case STAT_NOBEACON:
3159 airo_print_dbg(devname, "link lost (missed beacons)");
3160 break;
3161 case STAT_MAXRETRIES:
3162 case STAT_MAXARL:
3163 airo_print_dbg(devname, "link lost (max retries)");
3164 break;
3165 case STAT_FORCELOSS:
3166 airo_print_dbg(devname, "link lost (local choice)");
3167 break;
3168 case STAT_TSFSYNC:
3169 airo_print_dbg(devname, "link lost (TSF sync lost)");
3170 break;
3171 case STAT_DEAUTH:
3172 airo_print_dbg(devname, "deauthenticated (reason: %d)", reason);
3173 break;
3174 case STAT_DISASSOC:
3175 airo_print_dbg(devname, "disassociated (reason: %d)", reason);
3176 break;
3177 case STAT_ASSOC_FAIL:
3178 airo_print_dbg(devname, "association failed (reason: %d)",
3179 reason);
3180 break;
3181 case STAT_AUTH_FAIL:
3182 airo_print_dbg(devname, "authentication failed (reason: %d)",
3183 reason);
3184 break;
3185 default:
3186 break;
3187 }
3188}
3189
3190static void airo_handle_link(struct airo_info *ai)
3191{
3192 union iwreq_data wrqu;
3193 int scan_forceloss = 0;
3194 u16 status;
3195
3196 /* Get new status and acknowledge the link change */
3197 status = le16_to_cpu(IN4500(ai, LINKSTAT));
3198 OUT4500(ai, EVACK, EV_LINK);
3199
3200 if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0))
3201 scan_forceloss = 1;
3202
3203 airo_print_status(ai->dev->name, status);
3204
3205 if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) {
3206 if (auto_wep)
3207 ai->expires = 0;
3208 if (ai->list_bss_task)
3209 wake_up_process(ai->list_bss_task);
3210 set_bit(FLAG_UPDATE_UNI, &ai->flags);
3211 set_bit(FLAG_UPDATE_MULTI, &ai->flags);
3212
3213 if (down_trylock(&ai->sem) != 0) {
3214 set_bit(JOB_EVENT, &ai->jobs);
3215 wake_up_interruptible(&ai->thr_wait);
3216 } else
3217 airo_send_event(ai->dev);
3218 } else if (!scan_forceloss) {
3219 if (auto_wep && !ai->expires) {
3220 ai->expires = RUN_AT(3*HZ);
3221 wake_up_interruptible(&ai->thr_wait);
3222 }
3223
3224 /* Send event to user space */
3225 memset(wrqu.ap_addr.sa_data, '\0', ETH_ALEN);
3226 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
3227 wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL);
3228 }
3229}
3230
3231static void airo_handle_rx(struct airo_info *ai)
3232{
3233 struct sk_buff *skb = NULL;
3234 __le16 fc, v, *buffer, tmpbuf[4];
3235 u16 len, hdrlen = 0, gap, fid;
3236 struct rx_hdr hdr;
3237 int success = 0;
3238
3239 if (test_bit(FLAG_MPI, &ai->flags)) {
3240 if (test_bit(FLAG_802_11, &ai->flags))
3241 mpi_receive_802_11(ai);
3242 else
3243 mpi_receive_802_3(ai);
3244 OUT4500(ai, EVACK, EV_RX);
3245 return;
3246 }
3247
3248 fid = IN4500(ai, RXFID);
3249
3250 /* Get the packet length */
3251 if (test_bit(FLAG_802_11, &ai->flags)) {
3252 bap_setup (ai, fid, 4, BAP0);
3253 bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0);
3254 /* Bad CRC. Ignore packet */
3255 if (le16_to_cpu(hdr.status) & 2)
3256 hdr.len = 0;
3257 if (ai->wifidev == NULL)
3258 hdr.len = 0;
3259 } else {
3260 bap_setup(ai, fid, 0x36, BAP0);
3261 bap_read(ai, &hdr.len, 2, BAP0);
3262 }
3263 len = le16_to_cpu(hdr.len);
3264
3265 if (len > AIRO_DEF_MTU) {
3266 airo_print_err(ai->dev->name, "Bad size %d", len);
3267 goto done;
3268 }
3269 if (len == 0)
3270 goto done;
3271
3272 if (test_bit(FLAG_802_11, &ai->flags)) {
3273 bap_read(ai, &fc, sizeof (fc), BAP0);
3274 hdrlen = header_len(fc);
3275 } else
3276 hdrlen = ETH_ALEN * 2;
3277
3278 skb = dev_alloc_skb(len + hdrlen + 2 + 2);
3279 if (!skb) {
3280 ai->dev->stats.rx_dropped++;
3281 goto done;
3282 }
3283
3284 skb_reserve(skb, 2); /* This way the IP header is aligned */
3285 buffer = (__le16 *) skb_put(skb, len + hdrlen);
3286 if (test_bit(FLAG_802_11, &ai->flags)) {
3287 buffer[0] = fc;
3288 bap_read(ai, buffer + 1, hdrlen - 2, BAP0);
3289 if (hdrlen == 24)
3290 bap_read(ai, tmpbuf, 6, BAP0);
3291
3292 bap_read(ai, &v, sizeof(v), BAP0);
3293 gap = le16_to_cpu(v);
3294 if (gap) {
3295 if (gap <= 8) {
3296 bap_read(ai, tmpbuf, gap, BAP0);
3297 } else {
3298 airo_print_err(ai->dev->name, "gaplen too "
3299 "big. Problems will follow...");
3300 }
3301 }
3302 bap_read(ai, buffer + hdrlen/2, len, BAP0);
3303 } else {
3304 MICBuffer micbuf;
3305
3306 bap_read(ai, buffer, ETH_ALEN * 2, BAP0);
3307 if (ai->micstats.enabled) {
3308 bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0);
3309 if (ntohs(micbuf.typelen) > 0x05DC)
3310 bap_setup(ai, fid, 0x44, BAP0);
3311 else {
3312 if (len <= sizeof (micbuf)) {
3313 dev_kfree_skb_irq(skb);
3314 goto done;
3315 }
3316
3317 len -= sizeof(micbuf);
3318 skb_trim(skb, len + hdrlen);
3319 }
3320 }
3321
3322 bap_read(ai, buffer + ETH_ALEN, len, BAP0);
3323 if (decapsulate(ai, &micbuf, (etherHead*) buffer, len))
3324 dev_kfree_skb_irq (skb);
3325 else
3326 success = 1;
3327 }
3328
3329#ifdef WIRELESS_SPY
3330 if (success && (ai->spy_data.spy_number > 0)) {
3331 char *sa;
3332 struct iw_quality wstats;
3333
3334 /* Prepare spy data : addr + qual */
3335 if (!test_bit(FLAG_802_11, &ai->flags)) {
3336 sa = (char *) buffer + 6;
3337 bap_setup(ai, fid, 8, BAP0);
3338 bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0);
3339 } else
3340 sa = (char *) buffer + 10;
3341 wstats.qual = hdr.rssi[0];
3342 if (ai->rssi)
3343 wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
3344 else
3345 wstats.level = (hdr.rssi[1] + 321) / 2;
3346 wstats.noise = ai->wstats.qual.noise;
3347 wstats.updated = IW_QUAL_LEVEL_UPDATED
3348 | IW_QUAL_QUAL_UPDATED
3349 | IW_QUAL_DBM;
3350 /* Update spy records */
3351 wireless_spy_update(ai->dev, sa, &wstats);
3352 }
3353#endif /* WIRELESS_SPY */
3354
3355done:
3356 OUT4500(ai, EVACK, EV_RX);
3357
3358 if (success) {
3359 if (test_bit(FLAG_802_11, &ai->flags)) {
3360 skb_reset_mac_header(skb);
3361 skb->pkt_type = PACKET_OTHERHOST;
3362 skb->dev = ai->wifidev;
3363 skb->protocol = htons(ETH_P_802_2);
3364 } else
3365 skb->protocol = eth_type_trans(skb, ai->dev);
3366 skb->ip_summed = CHECKSUM_NONE;
3367
3368 netif_rx(skb);
3369 }
3370}
3371
3372static void airo_handle_tx(struct airo_info *ai, u16 status)
3373{
3374 int i, len = 0, index = -1;
3375 u16 fid;
3376
3377 if (test_bit(FLAG_MPI, &ai->flags)) {
3378 unsigned long flags;
3379
3380 if (status & EV_TXEXC)
3381 get_tx_error(ai, -1);
3382
3383 spin_lock_irqsave(&ai->aux_lock, flags);
3384 if (!skb_queue_empty(&ai->txq)) {
3385 spin_unlock_irqrestore(&ai->aux_lock,flags);
3386 mpi_send_packet(ai->dev);
3387 } else {
3388 clear_bit(FLAG_PENDING_XMIT, &ai->flags);
3389 spin_unlock_irqrestore(&ai->aux_lock,flags);
3390 netif_wake_queue(ai->dev);
3391 }
3392 OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
3393 return;
3394 }
3395
3396 fid = IN4500(ai, TXCOMPLFID);
3397
3398 for(i = 0; i < MAX_FIDS; i++) {
3399 if ((ai->fids[i] & 0xffff) == fid) {
3400 len = ai->fids[i] >> 16;
3401 index = i;
3402 }
3403 }
3404
3405 if (index != -1) {
3406 if (status & EV_TXEXC)
3407 get_tx_error(ai, index);
3408
3409 OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC));
3410
3411 /* Set up to be used again */
3412 ai->fids[index] &= 0xffff;
3413 if (index < MAX_FIDS / 2) {
3414 if (!test_bit(FLAG_PENDING_XMIT, &ai->flags))
3415 netif_wake_queue(ai->dev);
3416 } else {
3417 if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags))
3418 netif_wake_queue(ai->wifidev);
3419 }
3420 } else {
3421 OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
3422 airo_print_err(ai->dev->name, "Unallocated FID was used to xmit");
3423 }
3424}
3425
Jeff Garzik28fc1f52007-10-29 05:46:16 -04003426static irqreturn_t airo_interrupt(int irq, void *dev_id)
3427{
3428 struct net_device *dev = dev_id;
Dan Williamsf55d4512009-01-24 09:04:12 -05003429 u16 status, savedInterrupts = 0;
3430 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003431 int handled = 0;
3432
3433 if (!netif_device_present(dev))
3434 return IRQ_NONE;
3435
3436 for (;;) {
Dan Williamsf55d4512009-01-24 09:04:12 -05003437 status = IN4500(ai, EVSTAT);
3438 if (!(status & STATUS_INTS) || (status == 0xffff))
3439 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003440
3441 handled = 1;
3442
Dan Williamsf55d4512009-01-24 09:04:12 -05003443 if (status & EV_AWAKE) {
3444 OUT4500(ai, EVACK, EV_AWAKE);
3445 OUT4500(ai, EVACK, EV_AWAKE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003446 }
3447
3448 if (!savedInterrupts) {
Dan Williamsf55d4512009-01-24 09:04:12 -05003449 savedInterrupts = IN4500(ai, EVINTEN);
3450 OUT4500(ai, EVINTEN, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003451 }
3452
Dan Williamsf55d4512009-01-24 09:04:12 -05003453 if (status & EV_MIC) {
3454 OUT4500(ai, EVACK, EV_MIC);
3455 airo_handle_cisco_mic(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003456 }
Dan Williams6fcdf562006-03-31 15:08:46 -05003457
Dan Williamsf55d4512009-01-24 09:04:12 -05003458 if (status & EV_LINK) {
3459 /* Link status changed */
3460 airo_handle_link(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003461 }
3462
3463 /* Check to see if there is something to receive */
Dan Williamsf55d4512009-01-24 09:04:12 -05003464 if (status & EV_RX)
3465 airo_handle_rx(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003466
3467 /* Check to see if a packet has been transmitted */
Dan Williamsf55d4512009-01-24 09:04:12 -05003468 if (status & (EV_TX | EV_TXCPY | EV_TXEXC))
3469 airo_handle_tx(ai, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003470
Dan Williamsf55d4512009-01-24 09:04:12 -05003471 if ( status & ~STATUS_INTS & ~IGNORE_INTS ) {
3472 airo_print_warn(ai->dev->name, "Got weird status %x",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003473 status & ~STATUS_INTS & ~IGNORE_INTS );
Dan Williamsf55d4512009-01-24 09:04:12 -05003474 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003475 }
3476
3477 if (savedInterrupts)
Dan Williamsf55d4512009-01-24 09:04:12 -05003478 OUT4500(ai, EVINTEN, savedInterrupts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003479
Linus Torvalds1da177e2005-04-16 15:20:36 -07003480 return IRQ_RETVAL(handled);
3481}
3482
3483/*
3484 * Routines to talk to the card
3485 */
3486
3487/*
3488 * This was originally written for the 4500, hence the name
3489 * NOTE: If use with 8bit mode and SMP bad things will happen!
3490 * Why would some one do 8 bit IO in an SMP machine?!?
3491 */
3492static void OUT4500( struct airo_info *ai, u16 reg, u16 val ) {
3493 if (test_bit(FLAG_MPI,&ai->flags))
3494 reg <<= 1;
3495 if ( !do8bitIO )
3496 outw( val, ai->dev->base_addr + reg );
3497 else {
3498 outb( val & 0xff, ai->dev->base_addr + reg );
3499 outb( val >> 8, ai->dev->base_addr + reg + 1 );
3500 }
3501}
3502
3503static u16 IN4500( struct airo_info *ai, u16 reg ) {
3504 unsigned short rc;
3505
3506 if (test_bit(FLAG_MPI,&ai->flags))
3507 reg <<= 1;
3508 if ( !do8bitIO )
3509 rc = inw( ai->dev->base_addr + reg );
3510 else {
3511 rc = inb( ai->dev->base_addr + reg );
3512 rc += ((int)inb( ai->dev->base_addr + reg + 1 )) << 8;
3513 }
3514 return rc;
3515}
3516
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003517static int enable_MAC(struct airo_info *ai, int lock)
3518{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003519 int rc;
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003520 Cmd cmd;
3521 Resp rsp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003522
3523 /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions
3524 * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down"
3525 * Note : we could try to use !netif_running(dev) in enable_MAC()
3526 * instead of this flag, but I don't trust it *within* the
3527 * open/close functions, and testing both flags together is
3528 * "cheaper" - Jean II */
3529 if (ai->flags & FLAG_RADIO_MASK) return SUCCESS;
3530
3531 if (lock && down_interruptible(&ai->sem))
3532 return -ERESTARTSYS;
3533
3534 if (!test_bit(FLAG_ENABLED, &ai->flags)) {
3535 memset(&cmd, 0, sizeof(cmd));
3536 cmd.cmd = MAC_ENABLE;
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003537 rc = issuecommand(ai, &cmd, &rsp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003538 if (rc == SUCCESS)
3539 set_bit(FLAG_ENABLED, &ai->flags);
3540 } else
3541 rc = SUCCESS;
3542
3543 if (lock)
3544 up(&ai->sem);
3545
3546 if (rc)
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003547 airo_print_err(ai->dev->name, "Cannot enable MAC");
3548 else if ((rsp.status & 0xFF00) != 0) {
3549 airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, "
3550 "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2);
3551 rc = ERROR;
3552 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003553 return rc;
3554}
3555
3556static void disable_MAC( struct airo_info *ai, int lock ) {
3557 Cmd cmd;
3558 Resp rsp;
3559
3560 if (lock && down_interruptible(&ai->sem))
3561 return;
3562
3563 if (test_bit(FLAG_ENABLED, &ai->flags)) {
3564 memset(&cmd, 0, sizeof(cmd));
3565 cmd.cmd = MAC_DISABLE; // disable in case already enabled
3566 issuecommand(ai, &cmd, &rsp);
3567 clear_bit(FLAG_ENABLED, &ai->flags);
3568 }
3569 if (lock)
3570 up(&ai->sem);
3571}
3572
3573static void enable_interrupts( struct airo_info *ai ) {
3574 /* Enable the interrupts */
3575 OUT4500( ai, EVINTEN, STATUS_INTS );
3576}
3577
3578static void disable_interrupts( struct airo_info *ai ) {
3579 OUT4500( ai, EVINTEN, 0 );
3580}
3581
3582static void mpi_receive_802_3(struct airo_info *ai)
3583{
3584 RxFid rxd;
3585 int len = 0;
3586 struct sk_buff *skb;
3587 char *buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003588 int off = 0;
3589 MICBuffer micbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003590
3591 memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
3592 /* Make sure we got something */
3593 if (rxd.rdy && rxd.valid == 0) {
3594 len = rxd.len + 12;
3595 if (len < 12 || len > 2048)
3596 goto badrx;
3597
3598 skb = dev_alloc_skb(len);
3599 if (!skb) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003600 ai->dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003601 goto badrx;
3602 }
3603 buffer = skb_put(skb,len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003604 memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2);
3605 if (ai->micstats.enabled) {
3606 memcpy(&micbuf,
3607 ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2,
3608 sizeof(micbuf));
3609 if (ntohs(micbuf.typelen) <= 0x05DC) {
3610 if (len <= sizeof(micbuf) + ETH_ALEN * 2)
3611 goto badmic;
3612
3613 off = sizeof(micbuf);
3614 skb_trim (skb, len - off);
3615 }
3616 }
3617 memcpy(buffer + ETH_ALEN * 2,
3618 ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off,
3619 len - ETH_ALEN * 2 - off);
3620 if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) {
3621badmic:
3622 dev_kfree_skb_irq (skb);
3623 goto badrx;
3624 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003625#ifdef WIRELESS_SPY
3626 if (ai->spy_data.spy_number > 0) {
3627 char *sa;
3628 struct iw_quality wstats;
3629 /* Prepare spy data : addr + qual */
3630 sa = buffer + ETH_ALEN;
3631 wstats.qual = 0; /* XXX Where do I get that info from ??? */
3632 wstats.level = 0;
3633 wstats.updated = 0;
3634 /* Update spy records */
3635 wireless_spy_update(ai->dev, sa, &wstats);
3636 }
3637#endif /* WIRELESS_SPY */
3638
Linus Torvalds1da177e2005-04-16 15:20:36 -07003639 skb->ip_summed = CHECKSUM_NONE;
3640 skb->protocol = eth_type_trans(skb, ai->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003641 netif_rx(skb);
3642 }
3643badrx:
3644 if (rxd.valid == 0) {
3645 rxd.valid = 1;
3646 rxd.rdy = 0;
3647 rxd.len = PKTSIZE;
3648 memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
3649 }
3650}
3651
Hannes Eder2ed5ba82008-12-26 00:12:59 -08003652static void mpi_receive_802_11(struct airo_info *ai)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003653{
3654 RxFid rxd;
3655 struct sk_buff *skb = NULL;
Al Viro0300b332007-12-19 22:38:33 -05003656 u16 len, hdrlen = 0;
3657 __le16 fc;
Dan Williamsf55d4512009-01-24 09:04:12 -05003658 struct rx_hdr hdr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003659 u16 gap;
3660 u16 *buffer;
Dan Williamsf55d4512009-01-24 09:04:12 -05003661 char *ptr = ai->rxfids[0].virtual_host_addr + 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003662
3663 memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
3664 memcpy ((char *)&hdr, ptr, sizeof(hdr));
3665 ptr += sizeof(hdr);
3666 /* Bad CRC. Ignore packet */
3667 if (le16_to_cpu(hdr.status) & 2)
3668 hdr.len = 0;
3669 if (ai->wifidev == NULL)
3670 hdr.len = 0;
3671 len = le16_to_cpu(hdr.len);
Dan Williams15db2762006-03-16 13:46:27 -05003672 if (len > AIRO_DEF_MTU) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003673 airo_print_err(ai->dev->name, "Bad size %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003674 goto badrx;
3675 }
3676 if (len == 0)
3677 goto badrx;
3678
Al Viro0300b332007-12-19 22:38:33 -05003679 fc = get_unaligned((__le16 *)ptr);
3680 hdrlen = header_len(fc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003681
3682 skb = dev_alloc_skb( len + hdrlen + 2 );
3683 if ( !skb ) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003684 ai->dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003685 goto badrx;
3686 }
3687 buffer = (u16*)skb_put (skb, len + hdrlen);
3688 memcpy ((char *)buffer, ptr, hdrlen);
3689 ptr += hdrlen;
3690 if (hdrlen == 24)
3691 ptr += 6;
Harvey Harrison533dd1b2008-04-29 01:03:36 -07003692 gap = get_unaligned_le16(ptr);
Al Viro593c2b92007-12-17 15:09:34 -05003693 ptr += sizeof(__le16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003694 if (gap) {
3695 if (gap <= 8)
3696 ptr += gap;
3697 else
Dan Williams934d8bf2006-03-16 13:46:23 -05003698 airo_print_err(ai->dev->name,
3699 "gaplen too big. Problems will follow...");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003700 }
3701 memcpy ((char *)buffer + hdrlen, ptr, len);
3702 ptr += len;
3703#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
3704 if (ai->spy_data.spy_number > 0) {
3705 char *sa;
3706 struct iw_quality wstats;
3707 /* Prepare spy data : addr + qual */
3708 sa = (char*)buffer + 10;
3709 wstats.qual = hdr.rssi[0];
3710 if (ai->rssi)
3711 wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
3712 else
3713 wstats.level = (hdr.rssi[1] + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04003714 wstats.noise = ai->wstats.qual.noise;
3715 wstats.updated = IW_QUAL_QUAL_UPDATED
3716 | IW_QUAL_LEVEL_UPDATED
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07003717 | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003718 /* Update spy records */
3719 wireless_spy_update(ai->dev, sa, &wstats);
3720 }
3721#endif /* IW_WIRELESS_SPY */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07003722 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003723 skb->pkt_type = PACKET_OTHERHOST;
3724 skb->dev = ai->wifidev;
3725 skb->protocol = htons(ETH_P_802_2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003726 skb->ip_summed = CHECKSUM_NONE;
3727 netif_rx( skb );
Dan Williamsf55d4512009-01-24 09:04:12 -05003728
Linus Torvalds1da177e2005-04-16 15:20:36 -07003729badrx:
3730 if (rxd.valid == 0) {
3731 rxd.valid = 1;
3732 rxd.rdy = 0;
3733 rxd.len = PKTSIZE;
3734 memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
3735 }
3736}
3737
3738static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
3739{
3740 Cmd cmd;
3741 Resp rsp;
3742 int status;
3743 int i;
3744 SsidRid mySsid;
Al Viro4293ea32007-12-19 19:21:51 -05003745 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003746 WepKeyRid wkr;
3747 int rc;
3748
3749 memset( &mySsid, 0, sizeof( mySsid ) );
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003750 kfree (ai->flash);
3751 ai->flash = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752
3753 /* The NOP is the first step in getting the card going */
3754 cmd.cmd = NOP;
3755 cmd.parm0 = cmd.parm1 = cmd.parm2 = 0;
3756 if (lock && down_interruptible(&ai->sem))
3757 return ERROR;
3758 if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) {
3759 if (lock)
3760 up(&ai->sem);
3761 return ERROR;
3762 }
3763 disable_MAC( ai, 0);
3764
3765 // Let's figure out if we need to use the AUX port
3766 if (!test_bit(FLAG_MPI,&ai->flags)) {
3767 cmd.cmd = CMD_ENABLEAUX;
3768 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
3769 if (lock)
3770 up(&ai->sem);
Dan Williams934d8bf2006-03-16 13:46:23 -05003771 airo_print_err(ai->dev->name, "Error checking for AUX port");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003772 return ERROR;
3773 }
3774 if (!aux_bap || rsp.status & 0xff00) {
3775 ai->bap_read = fast_bap_read;
Dan Williams934d8bf2006-03-16 13:46:23 -05003776 airo_print_dbg(ai->dev->name, "Doing fast bap_reads");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003777 } else {
3778 ai->bap_read = aux_bap_read;
Dan Williams934d8bf2006-03-16 13:46:23 -05003779 airo_print_dbg(ai->dev->name, "Doing AUX bap_reads");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003780 }
3781 }
3782 if (lock)
3783 up(&ai->sem);
3784 if (ai->config.len == 0) {
3785 tdsRssiRid rssi_rid;
3786 CapabilityRid cap_rid;
3787
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003788 kfree(ai->APList);
3789 ai->APList = NULL;
3790 kfree(ai->SSID);
3791 ai->SSID = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003792 // general configuration (read/modify/write)
3793 status = readConfigRid(ai, lock);
3794 if ( status != SUCCESS ) return ERROR;
3795
3796 status = readCapabilityRid(ai, &cap_rid, lock);
3797 if ( status != SUCCESS ) return ERROR;
3798
3799 status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),lock);
3800 if ( status == SUCCESS ) {
3801 if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
Dan Williams41480af2005-05-10 09:45:51 -04003802 memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003803 }
3804 else {
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003805 kfree(ai->rssi);
3806 ai->rssi = NULL;
Al Viro56d81bd2007-12-20 17:18:35 -05003807 if (cap_rid.softCap & cpu_to_le16(8))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003808 ai->config.rmode |= RXMODE_NORMALIZED_RSSI;
3809 else
Dan Williams934d8bf2006-03-16 13:46:23 -05003810 airo_print_warn(ai->dev->name, "unknown received signal "
3811 "level scale");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003812 }
3813 ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
3814 ai->config.authType = AUTH_OPEN;
3815 ai->config.modulation = MOD_CCK;
3816
Al Viro56d81bd2007-12-20 17:18:35 -05003817 if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) &&
Al Viro156178582007-12-20 17:21:36 -05003818 (cap_rid.extSoftCap & cpu_to_le16(1)) &&
Al Viro56d81bd2007-12-20 17:18:35 -05003819 micsetup(ai) == SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003820 ai->config.opmode |= MODE_MIC;
3821 set_bit(FLAG_MIC_CAPABLE, &ai->flags);
3822 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003823
3824 /* Save off the MAC */
3825 for( i = 0; i < ETH_ALEN; i++ ) {
3826 mac[i] = ai->config.macAddr[i];
3827 }
3828
3829 /* Check to see if there are any insmod configured
3830 rates to add */
3831 if ( rates[0] ) {
3832 int i = 0;
3833 memset(ai->config.rates,0,sizeof(ai->config.rates));
3834 for( i = 0; i < 8 && rates[i]; i++ ) {
3835 ai->config.rates[i] = rates[i];
3836 }
3837 }
3838 if ( basic_rate > 0 ) {
3839 int i;
3840 for( i = 0; i < 8; i++ ) {
3841 if ( ai->config.rates[i] == basic_rate ||
3842 !ai->config.rates ) {
3843 ai->config.rates[i] = basic_rate | 0x80;
3844 break;
3845 }
3846 }
3847 }
3848 set_bit (FLAG_COMMIT, &ai->flags);
3849 }
3850
3851 /* Setup the SSIDs if present */
3852 if ( ssids[0] ) {
3853 int i;
3854 for( i = 0; i < 3 && ssids[i]; i++ ) {
Al Viro0dd22122007-12-17 16:11:57 -05003855 size_t len = strlen(ssids[i]);
3856 if (len > 32)
3857 len = 32;
3858 mySsid.ssids[i].len = cpu_to_le16(len);
3859 memcpy(mySsid.ssids[i].ssid, ssids[i], len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003860 }
Al Viro0dd22122007-12-17 16:11:57 -05003861 mySsid.len = cpu_to_le16(sizeof(mySsid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003862 }
3863
3864 status = writeConfigRid(ai, lock);
3865 if ( status != SUCCESS ) return ERROR;
3866
3867 /* Set up the SSID list */
3868 if ( ssids[0] ) {
3869 status = writeSsidRid(ai, &mySsid, lock);
3870 if ( status != SUCCESS ) return ERROR;
3871 }
3872
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003873 status = enable_MAC(ai, lock);
3874 if (status != SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003875 return ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003876
3877 /* Grab the initial wep key, we gotta save it for auto_wep */
3878 rc = readWepKeyRid(ai, &wkr, 1, lock);
3879 if (rc == SUCCESS) do {
3880 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05003881 if (wkr.kindex == cpu_to_le16(0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003882 ai->defindex = wkr.mac[0];
3883 }
3884 rc = readWepKeyRid(ai, &wkr, 0, lock);
3885 } while(lastindex != wkr.kindex);
3886
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02003887 try_auto_wep(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003888
3889 return SUCCESS;
3890}
3891
3892static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) {
3893 // Im really paranoid about letting it run forever!
3894 int max_tries = 600000;
3895
3896 if (IN4500(ai, EVSTAT) & EV_CMD)
3897 OUT4500(ai, EVACK, EV_CMD);
3898
3899 OUT4500(ai, PARAM0, pCmd->parm0);
3900 OUT4500(ai, PARAM1, pCmd->parm1);
3901 OUT4500(ai, PARAM2, pCmd->parm2);
3902 OUT4500(ai, COMMAND, pCmd->cmd);
3903
3904 while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) {
3905 if ((IN4500(ai, COMMAND)) == pCmd->cmd)
3906 // PC4500 didn't notice command, try again
3907 OUT4500(ai, COMMAND, pCmd->cmd);
3908 if (!in_atomic() && (max_tries & 255) == 0)
3909 schedule();
3910 }
3911
3912 if ( max_tries == -1 ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003913 airo_print_err(ai->dev->name,
3914 "Max tries exceeded when issueing command");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003915 if (IN4500(ai, COMMAND) & COMMAND_BUSY)
3916 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
3917 return ERROR;
3918 }
3919
3920 // command completed
3921 pRsp->status = IN4500(ai, STATUS);
3922 pRsp->rsp0 = IN4500(ai, RESP0);
3923 pRsp->rsp1 = IN4500(ai, RESP1);
3924 pRsp->rsp2 = IN4500(ai, RESP2);
Robert Schulze13dca9b2006-07-10 18:37:44 +02003925 if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET)
3926 airo_print_err(ai->dev->name,
3927 "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x",
3928 pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1,
3929 pRsp->rsp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003930
3931 // clear stuck command busy if necessary
3932 if (IN4500(ai, COMMAND) & COMMAND_BUSY) {
3933 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
3934 }
3935 // acknowledge processing the status/response
3936 OUT4500(ai, EVACK, EV_CMD);
3937
3938 return SUCCESS;
3939}
3940
3941/* Sets up the bap to start exchange data. whichbap should
3942 * be one of the BAP0 or BAP1 defines. Locks should be held before
3943 * calling! */
3944static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap )
3945{
3946 int timeout = 50;
3947 int max_tries = 3;
3948
3949 OUT4500(ai, SELECT0+whichbap, rid);
3950 OUT4500(ai, OFFSET0+whichbap, offset);
3951 while (1) {
3952 int status = IN4500(ai, OFFSET0+whichbap);
3953 if (status & BAP_BUSY) {
3954 /* This isn't really a timeout, but its kinda
3955 close */
3956 if (timeout--) {
3957 continue;
3958 }
3959 } else if ( status & BAP_ERR ) {
3960 /* invalid rid or offset */
Dan Williams934d8bf2006-03-16 13:46:23 -05003961 airo_print_err(ai->dev->name, "BAP error %x %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003962 status, whichbap );
3963 return ERROR;
3964 } else if (status & BAP_DONE) { // success
3965 return SUCCESS;
3966 }
3967 if ( !(max_tries--) ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003968 airo_print_err(ai->dev->name,
Michal Schmidt1138c372007-06-29 15:33:41 +02003969 "BAP setup error too many retries\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003970 return ERROR;
3971 }
3972 // -- PC4500 missed it, try again
3973 OUT4500(ai, SELECT0+whichbap, rid);
3974 OUT4500(ai, OFFSET0+whichbap, offset);
3975 timeout = 50;
3976 }
3977}
3978
3979/* should only be called by aux_bap_read. This aux function and the
3980 following use concepts not documented in the developers guide. I
3981 got them from a patch given to my by Aironet */
3982static u16 aux_setup(struct airo_info *ai, u16 page,
3983 u16 offset, u16 *len)
3984{
3985 u16 next;
3986
3987 OUT4500(ai, AUXPAGE, page);
3988 OUT4500(ai, AUXOFF, 0);
3989 next = IN4500(ai, AUXDATA);
3990 *len = IN4500(ai, AUXDATA)&0xff;
3991 if (offset != 4) OUT4500(ai, AUXOFF, offset);
3992 return next;
3993}
3994
3995/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05003996static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003997 int bytelen, int whichbap)
3998{
3999 u16 len;
4000 u16 page;
4001 u16 offset;
4002 u16 next;
4003 int words;
4004 int i;
4005 unsigned long flags;
4006
4007 spin_lock_irqsave(&ai->aux_lock, flags);
4008 page = IN4500(ai, SWS0+whichbap);
4009 offset = IN4500(ai, SWS2+whichbap);
4010 next = aux_setup(ai, page, offset, &len);
4011 words = (bytelen+1)>>1;
4012
4013 for (i=0; i<words;) {
4014 int count;
4015 count = (len>>1) < (words-i) ? (len>>1) : (words-i);
4016 if ( !do8bitIO )
4017 insw( ai->dev->base_addr+DATA0+whichbap,
4018 pu16Dst+i,count );
4019 else
4020 insb( ai->dev->base_addr+DATA0+whichbap,
4021 pu16Dst+i, count << 1 );
4022 i += count;
4023 if (i<words) {
4024 next = aux_setup(ai, next, 4, &len);
4025 }
4026 }
4027 spin_unlock_irqrestore(&ai->aux_lock, flags);
4028 return SUCCESS;
4029}
4030
4031
4032/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004033static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004034 int bytelen, int whichbap)
4035{
4036 bytelen = (bytelen + 1) & (~1); // round up to even value
4037 if ( !do8bitIO )
4038 insw( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1 );
4039 else
4040 insb( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen );
4041 return SUCCESS;
4042}
4043
4044/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004045static int bap_write(struct airo_info *ai, const __le16 *pu16Src,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004046 int bytelen, int whichbap)
4047{
4048 bytelen = (bytelen + 1) & (~1); // round up to even value
4049 if ( !do8bitIO )
4050 outsw( ai->dev->base_addr+DATA0+whichbap,
4051 pu16Src, bytelen>>1 );
4052 else
4053 outsb( ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen );
4054 return SUCCESS;
4055}
4056
4057static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd)
4058{
4059 Cmd cmd; /* for issuing commands */
4060 Resp rsp; /* response from commands */
4061 u16 status;
4062
4063 memset(&cmd, 0, sizeof(cmd));
4064 cmd.cmd = accmd;
4065 cmd.parm0 = rid;
4066 status = issuecommand(ai, &cmd, &rsp);
4067 if (status != 0) return status;
4068 if ( (rsp.status & 0x7F00) != 0) {
4069 return (accmd << 8) + (rsp.rsp0 & 0xFF);
4070 }
4071 return 0;
4072}
4073
4074/* Note, that we are using BAP1 which is also used by transmit, so
4075 * we must get a lock. */
4076static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock)
4077{
4078 u16 status;
4079 int rc = SUCCESS;
4080
4081 if (lock) {
4082 if (down_interruptible(&ai->sem))
4083 return ERROR;
4084 }
4085 if (test_bit(FLAG_MPI,&ai->flags)) {
4086 Cmd cmd;
4087 Resp rsp;
4088
4089 memset(&cmd, 0, sizeof(cmd));
4090 memset(&rsp, 0, sizeof(rsp));
4091 ai->config_desc.rid_desc.valid = 1;
4092 ai->config_desc.rid_desc.len = RIDSIZE;
4093 ai->config_desc.rid_desc.rid = 0;
4094 ai->config_desc.rid_desc.host_addr = ai->ridbus;
4095
4096 cmd.cmd = CMD_ACCESS;
4097 cmd.parm0 = rid;
4098
4099 memcpy_toio(ai->config_desc.card_ram_off,
4100 &ai->config_desc.rid_desc, sizeof(Rid));
4101
4102 rc = issuecommand(ai, &cmd, &rsp);
4103
4104 if (rsp.status & 0x7f00)
4105 rc = rsp.rsp0;
4106 if (!rc)
4107 memcpy(pBuf, ai->config_desc.virtual_host_addr, len);
4108 goto done;
4109 } else {
4110 if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) {
4111 rc = status;
4112 goto done;
4113 }
4114 if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
4115 rc = ERROR;
4116 goto done;
4117 }
4118 // read the rid length field
4119 bap_read(ai, pBuf, 2, BAP1);
4120 // length for remaining part of rid
Al Viro593c2b92007-12-17 15:09:34 -05004121 len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004122
4123 if ( len <= 2 ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004124 airo_print_err(ai->dev->name,
4125 "Rid %x has a length of %d which is too short",
Linus Torvalds1da177e2005-04-16 15:20:36 -07004126 (int)rid, (int)len );
4127 rc = ERROR;
4128 goto done;
4129 }
4130 // read remainder of the rid
Al Virob8c06bc2007-12-19 17:55:43 -05004131 rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004132 }
4133done:
4134 if (lock)
4135 up(&ai->sem);
4136 return rc;
4137}
4138
4139/* Note, that we are using BAP1 which is also used by transmit, so
4140 * make sure this isnt called when a transmit is happening */
4141static int PC4500_writerid(struct airo_info *ai, u16 rid,
4142 const void *pBuf, int len, int lock)
4143{
4144 u16 status;
4145 int rc = SUCCESS;
4146
Al Viro593c2b92007-12-17 15:09:34 -05004147 *(__le16*)pBuf = cpu_to_le16((u16)len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004148
4149 if (lock) {
4150 if (down_interruptible(&ai->sem))
4151 return ERROR;
4152 }
4153 if (test_bit(FLAG_MPI,&ai->flags)) {
4154 Cmd cmd;
4155 Resp rsp;
4156
Dan Streetmanf89b2322005-11-11 11:41:42 -05004157 if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid))
Dan Williams934d8bf2006-03-16 13:46:23 -05004158 airo_print_err(ai->dev->name,
4159 "%s: MAC should be disabled (rid=%04x)",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004160 __func__, rid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004161 memset(&cmd, 0, sizeof(cmd));
4162 memset(&rsp, 0, sizeof(rsp));
4163
4164 ai->config_desc.rid_desc.valid = 1;
4165 ai->config_desc.rid_desc.len = *((u16 *)pBuf);
4166 ai->config_desc.rid_desc.rid = 0;
4167
4168 cmd.cmd = CMD_WRITERID;
4169 cmd.parm0 = rid;
4170
4171 memcpy_toio(ai->config_desc.card_ram_off,
4172 &ai->config_desc.rid_desc, sizeof(Rid));
4173
4174 if (len < 4 || len > 2047) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004175 airo_print_err(ai->dev->name, "%s: len=%d", __func__, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004176 rc = -1;
4177 } else {
4178 memcpy((char *)ai->config_desc.virtual_host_addr,
4179 pBuf, len);
4180
4181 rc = issuecommand(ai, &cmd, &rsp);
4182 if ((rc & 0xff00) != 0) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004183 airo_print_err(ai->dev->name, "%s: Write rid Error %d",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004184 __func__, rc);
Dan Williams934d8bf2006-03-16 13:46:23 -05004185 airo_print_err(ai->dev->name, "%s: Cmd=%04x",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004186 __func__, cmd.cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004187 }
4188
4189 if ((rsp.status & 0x7f00))
4190 rc = rsp.rsp0;
4191 }
4192 } else {
4193 // --- first access so that we can write the rid data
4194 if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) {
4195 rc = status;
4196 goto done;
4197 }
4198 // --- now write the rid data
4199 if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
4200 rc = ERROR;
4201 goto done;
4202 }
4203 bap_write(ai, pBuf, len, BAP1);
4204 // ---now commit the rid data
4205 rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS);
4206 }
4207done:
4208 if (lock)
4209 up(&ai->sem);
4210 return rc;
4211}
4212
4213/* Allocates a FID to be used for transmitting packets. We only use
4214 one for now. */
4215static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw)
4216{
4217 unsigned int loop = 3000;
4218 Cmd cmd;
4219 Resp rsp;
4220 u16 txFid;
Al Viro593c2b92007-12-17 15:09:34 -05004221 __le16 txControl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004222
4223 cmd.cmd = CMD_ALLOCATETX;
4224 cmd.parm0 = lenPayload;
4225 if (down_interruptible(&ai->sem))
4226 return ERROR;
4227 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
4228 txFid = ERROR;
4229 goto done;
4230 }
4231 if ( (rsp.status & 0xFF00) != 0) {
4232 txFid = ERROR;
4233 goto done;
4234 }
4235 /* wait for the allocate event/indication
4236 * It makes me kind of nervous that this can just sit here and spin,
4237 * but in practice it only loops like four times. */
4238 while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop);
4239 if (!loop) {
4240 txFid = ERROR;
4241 goto done;
4242 }
4243
4244 // get the allocated fid and acknowledge
4245 txFid = IN4500(ai, TXALLOCFID);
4246 OUT4500(ai, EVACK, EV_ALLOC);
4247
4248 /* The CARD is pretty cool since it converts the ethernet packet
4249 * into 802.11. Also note that we don't release the FID since we
4250 * will be using the same one over and over again. */
4251 /* We only have to setup the control once since we are not
4252 * releasing the fid. */
4253 if (raw)
4254 txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11
4255 | TXCTL_ETHERNET | TXCTL_NORELEASE);
4256 else
4257 txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3
4258 | TXCTL_ETHERNET | TXCTL_NORELEASE);
4259 if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS)
4260 txFid = ERROR;
4261 else
4262 bap_write(ai, &txControl, sizeof(txControl), BAP1);
4263
4264done:
4265 up(&ai->sem);
4266
4267 return txFid;
4268}
4269
4270/* In general BAP1 is dedicated to transmiting packets. However,
4271 since we need a BAP when accessing RIDs, we also use BAP1 for that.
4272 Make sure the BAP1 spinlock is held when this is called. */
4273static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket)
4274{
Al Viro593c2b92007-12-17 15:09:34 -05004275 __le16 payloadLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004276 Cmd cmd;
4277 Resp rsp;
4278 int miclen = 0;
4279 u16 txFid = len;
4280 MICBuffer pMic;
4281
4282 len >>= 16;
4283
4284 if (len <= ETH_ALEN * 2) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004285 airo_print_warn(ai->dev->name, "Short packet %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004286 return ERROR;
4287 }
4288 len -= ETH_ALEN * 2;
4289
Linus Torvalds1da177e2005-04-16 15:20:36 -07004290 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
Al Viro593c2b92007-12-17 15:09:34 -05004291 (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004292 if (encapsulate(ai,(etherHead *)pPacket,&pMic,len) != SUCCESS)
4293 return ERROR;
4294 miclen = sizeof(pMic);
4295 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004296 // packet is destination[6], source[6], payload[len-12]
4297 // write the payload length and dst/src/payload
4298 if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR;
4299 /* The hardware addresses aren't counted as part of the payload, so
4300 * we have to subtract the 12 bytes for the addresses off */
4301 payloadLen = cpu_to_le16(len + miclen);
4302 bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
Al Virob8c06bc2007-12-19 17:55:43 -05004303 bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004304 if (miclen)
Al Virob8c06bc2007-12-19 17:55:43 -05004305 bap_write(ai, (__le16*)&pMic, miclen, BAP1);
4306 bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004307 // issue the transmit command
4308 memset( &cmd, 0, sizeof( cmd ) );
4309 cmd.cmd = CMD_TRANSMIT;
4310 cmd.parm0 = txFid;
4311 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
4312 if ( (rsp.status & 0xFF00) != 0) return ERROR;
4313 return SUCCESS;
4314}
4315
4316static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket)
4317{
Al Viro593c2b92007-12-17 15:09:34 -05004318 __le16 fc, payloadLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004319 Cmd cmd;
4320 Resp rsp;
4321 int hdrlen;
Al Viro977b1432007-12-19 16:45:29 -05004322 static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6};
4323 /* padding of header to full size + le16 gaplen (6) + gaplen bytes */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004324 u16 txFid = len;
4325 len >>= 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004326
Al Viro0300b332007-12-19 22:38:33 -05004327 fc = *(__le16*)pPacket;
4328 hdrlen = header_len(fc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004329
4330 if (len < hdrlen) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004331 airo_print_warn(ai->dev->name, "Short packet %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004332 return ERROR;
4333 }
4334
4335 /* packet is 802.11 header + payload
4336 * write the payload length and dst/src/payload */
4337 if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR;
4338 /* The 802.11 header aren't counted as part of the payload, so
4339 * we have to subtract the header bytes off */
4340 payloadLen = cpu_to_le16(len-hdrlen);
4341 bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
4342 if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR;
Al Virob8c06bc2007-12-19 17:55:43 -05004343 bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1);
4344 bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004345
Al Virob8c06bc2007-12-19 17:55:43 -05004346 bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004347 // issue the transmit command
4348 memset( &cmd, 0, sizeof( cmd ) );
4349 cmd.cmd = CMD_TRANSMIT;
4350 cmd.parm0 = txFid;
4351 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
4352 if ( (rsp.status & 0xFF00) != 0) return ERROR;
4353 return SUCCESS;
4354}
4355
4356/*
4357 * This is the proc_fs routines. It is a bit messier than I would
4358 * like! Feel free to clean it up!
4359 */
4360
4361static ssize_t proc_read( struct file *file,
4362 char __user *buffer,
4363 size_t len,
4364 loff_t *offset);
4365
4366static ssize_t proc_write( struct file *file,
4367 const char __user *buffer,
4368 size_t len,
4369 loff_t *offset );
4370static int proc_close( struct inode *inode, struct file *file );
4371
4372static int proc_stats_open( struct inode *inode, struct file *file );
4373static int proc_statsdelta_open( struct inode *inode, struct file *file );
4374static int proc_status_open( struct inode *inode, struct file *file );
4375static int proc_SSID_open( struct inode *inode, struct file *file );
4376static int proc_APList_open( struct inode *inode, struct file *file );
4377static int proc_BSSList_open( struct inode *inode, struct file *file );
4378static int proc_config_open( struct inode *inode, struct file *file );
4379static int proc_wepkey_open( struct inode *inode, struct file *file );
4380
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004381static const struct file_operations proc_statsdelta_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004382 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004383 .read = proc_read,
4384 .open = proc_statsdelta_open,
4385 .release = proc_close
4386};
4387
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004388static const struct file_operations proc_stats_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004389 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004390 .read = proc_read,
4391 .open = proc_stats_open,
4392 .release = proc_close
4393};
4394
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004395static const struct file_operations proc_status_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004396 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004397 .read = proc_read,
4398 .open = proc_status_open,
4399 .release = proc_close
4400};
4401
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004402static const struct file_operations proc_SSID_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004403 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004404 .read = proc_read,
4405 .write = proc_write,
4406 .open = proc_SSID_open,
4407 .release = proc_close
4408};
4409
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004410static const struct file_operations proc_BSSList_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004411 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004412 .read = proc_read,
4413 .write = proc_write,
4414 .open = proc_BSSList_open,
4415 .release = proc_close
4416};
4417
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004418static const struct file_operations proc_APList_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004419 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004420 .read = proc_read,
4421 .write = proc_write,
4422 .open = proc_APList_open,
4423 .release = proc_close
4424};
4425
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004426static const struct file_operations proc_config_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004427 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004428 .read = proc_read,
4429 .write = proc_write,
4430 .open = proc_config_open,
4431 .release = proc_close
4432};
4433
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004434static const struct file_operations proc_wepkey_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004435 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004436 .read = proc_read,
4437 .write = proc_write,
4438 .open = proc_wepkey_open,
4439 .release = proc_close
4440};
4441
4442static struct proc_dir_entry *airo_entry;
4443
4444struct proc_data {
4445 int release_buffer;
4446 int readlen;
4447 char *rbuffer;
4448 int writelen;
4449 int maxwritelen;
4450 char *wbuffer;
4451 void (*on_close) (struct inode *, struct file *);
4452};
4453
Linus Torvalds1da177e2005-04-16 15:20:36 -07004454static int setup_proc_entry( struct net_device *dev,
4455 struct airo_info *apriv ) {
4456 struct proc_dir_entry *entry;
4457 /* First setup the device directory */
4458 strcpy(apriv->proc_name,dev->name);
4459 apriv->proc_entry = create_proc_entry(apriv->proc_name,
4460 S_IFDIR|airo_perm,
4461 airo_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004462 if (!apriv->proc_entry)
4463 goto fail;
4464 apriv->proc_entry->uid = proc_uid;
4465 apriv->proc_entry->gid = proc_gid;
4466 apriv->proc_entry->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004467
4468 /* Setup the StatsDelta */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004469 entry = proc_create_data("StatsDelta",
4470 S_IFREG | (S_IRUGO&proc_perm),
4471 apriv->proc_entry, &proc_statsdelta_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004472 if (!entry)
4473 goto fail_stats_delta;
4474 entry->uid = proc_uid;
4475 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004476
4477 /* Setup the Stats */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004478 entry = proc_create_data("Stats",
4479 S_IFREG | (S_IRUGO&proc_perm),
4480 apriv->proc_entry, &proc_stats_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004481 if (!entry)
4482 goto fail_stats;
4483 entry->uid = proc_uid;
4484 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004485
4486 /* Setup the Status */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004487 entry = proc_create_data("Status",
4488 S_IFREG | (S_IRUGO&proc_perm),
4489 apriv->proc_entry, &proc_status_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004490 if (!entry)
4491 goto fail_status;
4492 entry->uid = proc_uid;
4493 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004494
4495 /* Setup the Config */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004496 entry = proc_create_data("Config",
4497 S_IFREG | proc_perm,
4498 apriv->proc_entry, &proc_config_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004499 if (!entry)
4500 goto fail_config;
4501 entry->uid = proc_uid;
4502 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004503
4504 /* Setup the SSID */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004505 entry = proc_create_data("SSID",
4506 S_IFREG | proc_perm,
4507 apriv->proc_entry, &proc_SSID_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004508 if (!entry)
4509 goto fail_ssid;
4510 entry->uid = proc_uid;
4511 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004512
4513 /* Setup the APList */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004514 entry = proc_create_data("APList",
4515 S_IFREG | proc_perm,
4516 apriv->proc_entry, &proc_APList_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004517 if (!entry)
4518 goto fail_aplist;
4519 entry->uid = proc_uid;
4520 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004521
4522 /* Setup the BSSList */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004523 entry = proc_create_data("BSSList",
4524 S_IFREG | proc_perm,
4525 apriv->proc_entry, &proc_BSSList_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004526 if (!entry)
4527 goto fail_bsslist;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004528 entry->uid = proc_uid;
4529 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004530
4531 /* Setup the WepKey */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004532 entry = proc_create_data("WepKey",
4533 S_IFREG | proc_perm,
4534 apriv->proc_entry, &proc_wepkey_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004535 if (!entry)
4536 goto fail_wepkey;
4537 entry->uid = proc_uid;
4538 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004539
4540 return 0;
Florin Malita431aca52006-10-10 16:46:30 -04004541
4542fail_wepkey:
4543 remove_proc_entry("BSSList", apriv->proc_entry);
4544fail_bsslist:
4545 remove_proc_entry("APList", apriv->proc_entry);
4546fail_aplist:
4547 remove_proc_entry("SSID", apriv->proc_entry);
4548fail_ssid:
4549 remove_proc_entry("Config", apriv->proc_entry);
4550fail_config:
4551 remove_proc_entry("Status", apriv->proc_entry);
4552fail_status:
4553 remove_proc_entry("Stats", apriv->proc_entry);
4554fail_stats:
4555 remove_proc_entry("StatsDelta", apriv->proc_entry);
4556fail_stats_delta:
4557 remove_proc_entry(apriv->proc_name, airo_entry);
4558fail:
4559 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004560}
4561
4562static int takedown_proc_entry( struct net_device *dev,
4563 struct airo_info *apriv ) {
4564 if ( !apriv->proc_entry->namelen ) return 0;
4565 remove_proc_entry("Stats",apriv->proc_entry);
4566 remove_proc_entry("StatsDelta",apriv->proc_entry);
4567 remove_proc_entry("Status",apriv->proc_entry);
4568 remove_proc_entry("Config",apriv->proc_entry);
4569 remove_proc_entry("SSID",apriv->proc_entry);
4570 remove_proc_entry("APList",apriv->proc_entry);
4571 remove_proc_entry("BSSList",apriv->proc_entry);
4572 remove_proc_entry("WepKey",apriv->proc_entry);
4573 remove_proc_entry(apriv->proc_name,airo_entry);
4574 return 0;
4575}
4576
4577/*
4578 * What we want from the proc_fs is to be able to efficiently read
4579 * and write the configuration. To do this, we want to read the
4580 * configuration when the file is opened and write it when the file is
4581 * closed. So basically we allocate a read buffer at open and fill it
4582 * with data, and allocate a write buffer and read it at close.
4583 */
4584
4585/*
4586 * The read routine is generic, it relies on the preallocated rbuffer
4587 * to supply the data.
4588 */
4589static ssize_t proc_read( struct file *file,
4590 char __user *buffer,
4591 size_t len,
4592 loff_t *offset )
4593{
Akinobu Mitacc0d9ff2008-06-09 16:44:30 -07004594 struct proc_data *priv = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004595
4596 if (!priv->rbuffer)
4597 return -EINVAL;
4598
Akinobu Mitacc0d9ff2008-06-09 16:44:30 -07004599 return simple_read_from_buffer(buffer, len, offset, priv->rbuffer,
4600 priv->readlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004601}
4602
4603/*
4604 * The write routine is generic, it fills in a preallocated rbuffer
4605 * to supply the data.
4606 */
4607static ssize_t proc_write( struct file *file,
4608 const char __user *buffer,
4609 size_t len,
4610 loff_t *offset )
4611{
4612 loff_t pos = *offset;
4613 struct proc_data *priv = (struct proc_data*)file->private_data;
4614
4615 if (!priv->wbuffer)
4616 return -EINVAL;
4617
4618 if (pos < 0)
4619 return -EINVAL;
4620 if (pos >= priv->maxwritelen)
4621 return 0;
4622 if (len > priv->maxwritelen - pos)
4623 len = priv->maxwritelen - pos;
4624 if (copy_from_user(priv->wbuffer + pos, buffer, len))
4625 return -EFAULT;
4626 if ( pos + len > priv->writelen )
4627 priv->writelen = len + file->f_pos;
4628 *offset = pos + len;
4629 return len;
4630}
4631
Al Viro329e2c02007-12-20 22:58:57 -05004632static int proc_status_open(struct inode *inode, struct file *file)
4633{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004634 struct proc_data *data;
4635 struct proc_dir_entry *dp = PDE(inode);
4636 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08004637 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004638 CapabilityRid cap_rid;
4639 StatusRid status_rid;
Al Viro329e2c02007-12-20 22:58:57 -05004640 u16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004641 int i;
4642
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01004643 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004644 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004645 data = (struct proc_data *)file->private_data;
4646 if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
4647 kfree (file->private_data);
4648 return -ENOMEM;
4649 }
4650
4651 readStatusRid(apriv, &status_rid, 1);
4652 readCapabilityRid(apriv, &cap_rid, 1);
4653
Al Viro329e2c02007-12-20 22:58:57 -05004654 mode = le16_to_cpu(status_rid.mode);
4655
Linus Torvalds1da177e2005-04-16 15:20:36 -07004656 i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n",
Al Viro329e2c02007-12-20 22:58:57 -05004657 mode & 1 ? "CFG ": "",
4658 mode & 2 ? "ACT ": "",
4659 mode & 0x10 ? "SYN ": "",
4660 mode & 0x20 ? "LNK ": "",
4661 mode & 0x40 ? "LEAP ": "",
4662 mode & 0x80 ? "PRIV ": "",
4663 mode & 0x100 ? "KEY ": "",
4664 mode & 0x200 ? "WEP ": "",
4665 mode & 0x8000 ? "ERR ": "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004666 sprintf( data->rbuffer+i, "Mode: %x\n"
4667 "Signal Strength: %d\n"
4668 "Signal Quality: %d\n"
4669 "SSID: %-.*s\n"
4670 "AP: %-.16s\n"
4671 "Freq: %d\n"
4672 "BitRate: %dmbs\n"
4673 "Driver Version: %s\n"
4674 "Device: %s\nManufacturer: %s\nFirmware Version: %s\n"
4675 "Radio type: %x\nCountry: %x\nHardware Version: %x\n"
4676 "Software Version: %x\nSoftware Subversion: %x\n"
4677 "Boot block version: %x\n",
Al Viro329e2c02007-12-20 22:58:57 -05004678 le16_to_cpu(status_rid.mode),
4679 le16_to_cpu(status_rid.normalizedSignalStrength),
4680 le16_to_cpu(status_rid.signalQuality),
4681 le16_to_cpu(status_rid.SSIDlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07004682 status_rid.SSID,
4683 status_rid.apName,
Al Viro329e2c02007-12-20 22:58:57 -05004684 le16_to_cpu(status_rid.channel),
4685 le16_to_cpu(status_rid.currentXmitRate) / 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004686 version,
4687 cap_rid.prodName,
4688 cap_rid.manName,
4689 cap_rid.prodVer,
Al Viro56d81bd2007-12-20 17:18:35 -05004690 le16_to_cpu(cap_rid.radioType),
4691 le16_to_cpu(cap_rid.country),
4692 le16_to_cpu(cap_rid.hardVer),
4693 le16_to_cpu(cap_rid.softVer),
4694 le16_to_cpu(cap_rid.softSubVer),
4695 le16_to_cpu(cap_rid.bootBlockVer));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004696 data->readlen = strlen( data->rbuffer );
4697 return 0;
4698}
4699
4700static int proc_stats_rid_open(struct inode*, struct file*, u16);
4701static int proc_statsdelta_open( struct inode *inode,
4702 struct file *file ) {
4703 if (file->f_mode&FMODE_WRITE) {
4704 return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR);
4705 }
4706 return proc_stats_rid_open(inode, file, RID_STATSDELTA);
4707}
4708
4709static int proc_stats_open( struct inode *inode, struct file *file ) {
4710 return proc_stats_rid_open(inode, file, RID_STATS);
4711}
4712
4713static int proc_stats_rid_open( struct inode *inode,
4714 struct file *file,
Al Viroa23ace52007-12-19 22:24:16 -05004715 u16 rid )
4716{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004717 struct proc_data *data;
4718 struct proc_dir_entry *dp = PDE(inode);
4719 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08004720 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004721 StatsRid stats;
4722 int i, j;
Al Viroa23ace52007-12-19 22:24:16 -05004723 __le32 *vals = stats.vals;
4724 int len = le16_to_cpu(stats.len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004725
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01004726 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004727 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004728 data = (struct proc_data *)file->private_data;
4729 if ((data->rbuffer = kmalloc( 4096, GFP_KERNEL )) == NULL) {
4730 kfree (file->private_data);
4731 return -ENOMEM;
4732 }
4733
4734 readStatsRid(apriv, &stats, rid, 1);
4735
4736 j = 0;
Al Viroa23ace52007-12-19 22:24:16 -05004737 for(i=0; statsLabels[i]!=(char *)-1 && i*4<len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004738 if (!statsLabels[i]) continue;
4739 if (j+strlen(statsLabels[i])+16>4096) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004740 airo_print_warn(apriv->dev->name,
4741 "Potentially disasterous buffer overflow averted!");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004742 break;
4743 }
Al Viroa23ace52007-12-19 22:24:16 -05004744 j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i],
4745 le32_to_cpu(vals[i]));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004746 }
Al Viroa23ace52007-12-19 22:24:16 -05004747 if (i*4 >= len) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004748 airo_print_warn(apriv->dev->name, "Got a short rid");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004749 }
4750 data->readlen = j;
4751 return 0;
4752}
4753
4754static int get_dec_u16( char *buffer, int *start, int limit ) {
4755 u16 value;
4756 int valid = 0;
4757 for( value = 0; buffer[*start] >= '0' &&
4758 buffer[*start] <= '9' &&
4759 *start < limit; (*start)++ ) {
4760 valid = 1;
4761 value *= 10;
4762 value += buffer[*start] - '0';
4763 }
4764 if ( !valid ) return -1;
4765 return value;
4766}
4767
4768static int airo_config_commit(struct net_device *dev,
4769 struct iw_request_info *info, void *zwrq,
4770 char *extra);
4771
Al Viro3eb9b412007-12-21 00:00:35 -05004772static inline int sniffing_mode(struct airo_info *ai)
4773{
4774 return le16_to_cpu(ai->config.rmode & RXMODE_MASK) >=
4775 le16_to_cpu(RXMODE_RFMON);
4776}
4777
4778static void proc_config_on_close(struct inode *inode, struct file *file)
4779{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004780 struct proc_data *data = file->private_data;
4781 struct proc_dir_entry *dp = PDE(inode);
4782 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08004783 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004784 char *line;
4785
4786 if ( !data->writelen ) return;
4787
4788 readConfigRid(ai, 1);
4789 set_bit (FLAG_COMMIT, &ai->flags);
4790
4791 line = data->wbuffer;
4792 while( line[0] ) {
4793/*** Mode processing */
4794 if ( !strncmp( line, "Mode: ", 6 ) ) {
4795 line += 6;
Al Viro3eb9b412007-12-21 00:00:35 -05004796 if (sniffing_mode(ai))
4797 set_bit (FLAG_RESET, &ai->flags);
4798 ai->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004799 clear_bit (FLAG_802_11, &ai->flags);
Al Viro3eb9b412007-12-21 00:00:35 -05004800 ai->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004801 ai->config.scanMode = SCANMODE_ACTIVE;
4802 if ( line[0] == 'a' ) {
Al Viro3eb9b412007-12-21 00:00:35 -05004803 ai->config.opmode |= MODE_STA_IBSS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004804 } else {
Al Viro3eb9b412007-12-21 00:00:35 -05004805 ai->config.opmode |= MODE_STA_ESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004806 if ( line[0] == 'r' ) {
4807 ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
4808 ai->config.scanMode = SCANMODE_PASSIVE;
4809 set_bit (FLAG_802_11, &ai->flags);
4810 } else if ( line[0] == 'y' ) {
4811 ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER;
4812 ai->config.scanMode = SCANMODE_PASSIVE;
4813 set_bit (FLAG_802_11, &ai->flags);
4814 } else if ( line[0] == 'l' )
4815 ai->config.rmode |= RXMODE_LANMON;
4816 }
4817 set_bit (FLAG_COMMIT, &ai->flags);
4818 }
4819
4820/*** Radio status */
4821 else if (!strncmp(line,"Radio: ", 7)) {
4822 line += 7;
4823 if (!strncmp(line,"off",3)) {
4824 set_bit (FLAG_RADIO_OFF, &ai->flags);
4825 } else {
4826 clear_bit (FLAG_RADIO_OFF, &ai->flags);
4827 }
4828 }
4829/*** NodeName processing */
4830 else if ( !strncmp( line, "NodeName: ", 10 ) ) {
4831 int j;
4832
4833 line += 10;
4834 memset( ai->config.nodeName, 0, 16 );
4835/* Do the name, assume a space between the mode and node name */
4836 for( j = 0; j < 16 && line[j] != '\n'; j++ ) {
4837 ai->config.nodeName[j] = line[j];
4838 }
4839 set_bit (FLAG_COMMIT, &ai->flags);
4840 }
4841
4842/*** PowerMode processing */
4843 else if ( !strncmp( line, "PowerMode: ", 11 ) ) {
4844 line += 11;
4845 if ( !strncmp( line, "PSPCAM", 6 ) ) {
4846 ai->config.powerSaveMode = POWERSAVE_PSPCAM;
4847 set_bit (FLAG_COMMIT, &ai->flags);
4848 } else if ( !strncmp( line, "PSP", 3 ) ) {
4849 ai->config.powerSaveMode = POWERSAVE_PSP;
4850 set_bit (FLAG_COMMIT, &ai->flags);
4851 } else {
4852 ai->config.powerSaveMode = POWERSAVE_CAM;
4853 set_bit (FLAG_COMMIT, &ai->flags);
4854 }
4855 } else if ( !strncmp( line, "DataRates: ", 11 ) ) {
4856 int v, i = 0, k = 0; /* i is index into line,
4857 k is index to rates */
4858
4859 line += 11;
4860 while((v = get_dec_u16(line, &i, 3))!=-1) {
4861 ai->config.rates[k++] = (u8)v;
4862 line += i + 1;
4863 i = 0;
4864 }
4865 set_bit (FLAG_COMMIT, &ai->flags);
4866 } else if ( !strncmp( line, "Channel: ", 9 ) ) {
4867 int v, i = 0;
4868 line += 9;
4869 v = get_dec_u16(line, &i, i+3);
4870 if ( v != -1 ) {
Al Viro3eb9b412007-12-21 00:00:35 -05004871 ai->config.channelSet = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004872 set_bit (FLAG_COMMIT, &ai->flags);
4873 }
4874 } else if ( !strncmp( line, "XmitPower: ", 11 ) ) {
4875 int v, i = 0;
4876 line += 11;
4877 v = get_dec_u16(line, &i, i+3);
4878 if ( v != -1 ) {
Al Viro3eb9b412007-12-21 00:00:35 -05004879 ai->config.txPower = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004880 set_bit (FLAG_COMMIT, &ai->flags);
4881 }
4882 } else if ( !strncmp( line, "WEP: ", 5 ) ) {
4883 line += 5;
4884 switch( line[0] ) {
4885 case 's':
Al Viro3eb9b412007-12-21 00:00:35 -05004886 ai->config.authType = AUTH_SHAREDKEY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004887 break;
4888 case 'e':
Al Viro3eb9b412007-12-21 00:00:35 -05004889 ai->config.authType = AUTH_ENCRYPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004890 break;
4891 default:
Al Viro3eb9b412007-12-21 00:00:35 -05004892 ai->config.authType = AUTH_OPEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004893 break;
4894 }
4895 set_bit (FLAG_COMMIT, &ai->flags);
4896 } else if ( !strncmp( line, "LongRetryLimit: ", 16 ) ) {
4897 int v, i = 0;
4898
4899 line += 16;
4900 v = get_dec_u16(line, &i, 3);
4901 v = (v<0) ? 0 : ((v>255) ? 255 : v);
Al Viro3eb9b412007-12-21 00:00:35 -05004902 ai->config.longRetryLimit = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004903 set_bit (FLAG_COMMIT, &ai->flags);
4904 } else if ( !strncmp( line, "ShortRetryLimit: ", 17 ) ) {
4905 int v, i = 0;
4906
4907 line += 17;
4908 v = get_dec_u16(line, &i, 3);
4909 v = (v<0) ? 0 : ((v>255) ? 255 : v);
Al Viro3eb9b412007-12-21 00:00:35 -05004910 ai->config.shortRetryLimit = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004911 set_bit (FLAG_COMMIT, &ai->flags);
4912 } else if ( !strncmp( line, "RTSThreshold: ", 14 ) ) {
4913 int v, i = 0;
4914
4915 line += 14;
4916 v = get_dec_u16(line, &i, 4);
Dan Williams15db2762006-03-16 13:46:27 -05004917 v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
Al Viro3eb9b412007-12-21 00:00:35 -05004918 ai->config.rtsThres = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004919 set_bit (FLAG_COMMIT, &ai->flags);
4920 } else if ( !strncmp( line, "TXMSDULifetime: ", 16 ) ) {
4921 int v, i = 0;
4922
4923 line += 16;
4924 v = get_dec_u16(line, &i, 5);
4925 v = (v<0) ? 0 : v;
Al Viro3eb9b412007-12-21 00:00:35 -05004926 ai->config.txLifetime = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004927 set_bit (FLAG_COMMIT, &ai->flags);
4928 } else if ( !strncmp( line, "RXMSDULifetime: ", 16 ) ) {
4929 int v, i = 0;
4930
4931 line += 16;
4932 v = get_dec_u16(line, &i, 5);
4933 v = (v<0) ? 0 : v;
Al Viro3eb9b412007-12-21 00:00:35 -05004934 ai->config.rxLifetime = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004935 set_bit (FLAG_COMMIT, &ai->flags);
4936 } else if ( !strncmp( line, "TXDiversity: ", 13 ) ) {
4937 ai->config.txDiversity =
4938 (line[13]=='l') ? 1 :
4939 ((line[13]=='r')? 2: 3);
4940 set_bit (FLAG_COMMIT, &ai->flags);
4941 } else if ( !strncmp( line, "RXDiversity: ", 13 ) ) {
4942 ai->config.rxDiversity =
4943 (line[13]=='l') ? 1 :
4944 ((line[13]=='r')? 2: 3);
4945 set_bit (FLAG_COMMIT, &ai->flags);
4946 } else if ( !strncmp( line, "FragThreshold: ", 15 ) ) {
4947 int v, i = 0;
4948
4949 line += 15;
4950 v = get_dec_u16(line, &i, 4);
Dan Williams15db2762006-03-16 13:46:27 -05004951 v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004952 v = v & 0xfffe; /* Make sure its even */
Al Viro3eb9b412007-12-21 00:00:35 -05004953 ai->config.fragThresh = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004954 set_bit (FLAG_COMMIT, &ai->flags);
4955 } else if (!strncmp(line, "Modulation: ", 12)) {
4956 line += 12;
4957 switch(*line) {
4958 case 'd': ai->config.modulation=MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break;
4959 case 'c': ai->config.modulation=MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break;
4960 case 'm': ai->config.modulation=MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break;
Dan Williams934d8bf2006-03-16 13:46:23 -05004961 default: airo_print_warn(ai->dev->name, "Unknown modulation");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004962 }
4963 } else if (!strncmp(line, "Preamble: ", 10)) {
4964 line += 10;
4965 switch(*line) {
4966 case 'a': ai->config.preamble=PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break;
4967 case 'l': ai->config.preamble=PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break;
4968 case 's': ai->config.preamble=PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break;
Dan Williams934d8bf2006-03-16 13:46:23 -05004969 default: airo_print_warn(ai->dev->name, "Unknown preamble");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004970 }
4971 } else {
Dan Williams934d8bf2006-03-16 13:46:23 -05004972 airo_print_warn(ai->dev->name, "Couldn't figure out %s", line);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004973 }
4974 while( line[0] && line[0] != '\n' ) line++;
4975 if ( line[0] ) line++;
4976 }
4977 airo_config_commit(dev, NULL, NULL, NULL);
4978}
4979
Al Viro3eb9b412007-12-21 00:00:35 -05004980static char *get_rmode(__le16 mode)
4981{
4982 switch(mode & RXMODE_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004983 case RXMODE_RFMON: return "rfmon";
4984 case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon";
4985 case RXMODE_LANMON: return "lanmon";
4986 }
4987 return "ESS";
4988}
4989
Al Viro3eb9b412007-12-21 00:00:35 -05004990static int proc_config_open(struct inode *inode, struct file *file)
4991{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004992 struct proc_data *data;
4993 struct proc_dir_entry *dp = PDE(inode);
4994 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08004995 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004996 int i;
Al Viro3eb9b412007-12-21 00:00:35 -05004997 __le16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004998
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01004999 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005000 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005001 data = (struct proc_data *)file->private_data;
5002 if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
5003 kfree (file->private_data);
5004 return -ENOMEM;
5005 }
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005006 if ((data->wbuffer = kzalloc( 2048, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005007 kfree (data->rbuffer);
5008 kfree (file->private_data);
5009 return -ENOMEM;
5010 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005011 data->maxwritelen = 2048;
5012 data->on_close = proc_config_on_close;
5013
5014 readConfigRid(ai, 1);
5015
Al Viro3eb9b412007-12-21 00:00:35 -05005016 mode = ai->config.opmode & MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005017 i = sprintf( data->rbuffer,
5018 "Mode: %s\n"
5019 "Radio: %s\n"
5020 "NodeName: %-16s\n"
5021 "PowerMode: %s\n"
5022 "DataRates: %d %d %d %d %d %d %d %d\n"
5023 "Channel: %d\n"
5024 "XmitPower: %d\n",
Al Viro3eb9b412007-12-21 00:00:35 -05005025 mode == MODE_STA_IBSS ? "adhoc" :
5026 mode == MODE_STA_ESS ? get_rmode(ai->config.rmode):
5027 mode == MODE_AP ? "AP" :
5028 mode == MODE_AP_RPTR ? "AP RPTR" : "Error",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005029 test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on",
5030 ai->config.nodeName,
Al Viro3eb9b412007-12-21 00:00:35 -05005031 ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" :
5032 ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" :
5033 ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" :
5034 "Error",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005035 (int)ai->config.rates[0],
5036 (int)ai->config.rates[1],
5037 (int)ai->config.rates[2],
5038 (int)ai->config.rates[3],
5039 (int)ai->config.rates[4],
5040 (int)ai->config.rates[5],
5041 (int)ai->config.rates[6],
5042 (int)ai->config.rates[7],
Al Viro3eb9b412007-12-21 00:00:35 -05005043 le16_to_cpu(ai->config.channelSet),
5044 le16_to_cpu(ai->config.txPower)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005045 );
5046 sprintf( data->rbuffer + i,
5047 "LongRetryLimit: %d\n"
5048 "ShortRetryLimit: %d\n"
5049 "RTSThreshold: %d\n"
5050 "TXMSDULifetime: %d\n"
5051 "RXMSDULifetime: %d\n"
5052 "TXDiversity: %s\n"
5053 "RXDiversity: %s\n"
5054 "FragThreshold: %d\n"
5055 "WEP: %s\n"
5056 "Modulation: %s\n"
5057 "Preamble: %s\n",
Al Viro3eb9b412007-12-21 00:00:35 -05005058 le16_to_cpu(ai->config.longRetryLimit),
5059 le16_to_cpu(ai->config.shortRetryLimit),
5060 le16_to_cpu(ai->config.rtsThres),
5061 le16_to_cpu(ai->config.txLifetime),
5062 le16_to_cpu(ai->config.rxLifetime),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005063 ai->config.txDiversity == 1 ? "left" :
5064 ai->config.txDiversity == 2 ? "right" : "both",
5065 ai->config.rxDiversity == 1 ? "left" :
5066 ai->config.rxDiversity == 2 ? "right" : "both",
Al Viro3eb9b412007-12-21 00:00:35 -05005067 le16_to_cpu(ai->config.fragThresh),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005068 ai->config.authType == AUTH_ENCRYPT ? "encrypt" :
5069 ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open",
Al Viro3eb9b412007-12-21 00:00:35 -05005070 ai->config.modulation == MOD_DEFAULT ? "default" :
Linus Torvalds1da177e2005-04-16 15:20:36 -07005071 ai->config.modulation == MOD_CCK ? "cck" :
5072 ai->config.modulation == MOD_MOK ? "mok" : "error",
5073 ai->config.preamble == PREAMBLE_AUTO ? "auto" :
5074 ai->config.preamble == PREAMBLE_LONG ? "long" :
5075 ai->config.preamble == PREAMBLE_SHORT ? "short" : "error"
5076 );
5077 data->readlen = strlen( data->rbuffer );
5078 return 0;
5079}
5080
Al Viro0dd22122007-12-17 16:11:57 -05005081static void proc_SSID_on_close(struct inode *inode, struct file *file)
5082{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005083 struct proc_data *data = (struct proc_data *)file->private_data;
5084 struct proc_dir_entry *dp = PDE(inode);
5085 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005086 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005087 SsidRid SSID_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005088 int i;
Al Viro0dd22122007-12-17 16:11:57 -05005089 char *p = data->wbuffer;
5090 char *end = p + data->writelen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005091
Al Viro0dd22122007-12-17 16:11:57 -05005092 if (!data->writelen)
5093 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005094
Al Viro0dd22122007-12-17 16:11:57 -05005095 *end = '\n'; /* sentinel; we have space for it */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005096
Al Viro0dd22122007-12-17 16:11:57 -05005097 memset(&SSID_rid, 0, sizeof(SSID_rid));
5098
5099 for (i = 0; i < 3 && p < end; i++) {
5100 int j = 0;
5101 /* copy up to 32 characters from this line */
5102 while (*p != '\n' && j < 32)
5103 SSID_rid.ssids[i].ssid[j++] = *p++;
5104 if (j == 0)
5105 break;
5106 SSID_rid.ssids[i].len = cpu_to_le16(j);
5107 /* skip to the beginning of the next line */
5108 while (*p++ != '\n')
5109 ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005110 }
5111 if (i)
Al Viro0dd22122007-12-17 16:11:57 -05005112 SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005113 disable_MAC(ai, 1);
5114 writeSsidRid(ai, &SSID_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005115 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005116}
5117
Jesper Juhl77933d72005-07-27 11:46:09 -07005118static inline u8 hexVal(char c) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005119 if (c>='0' && c<='9') return c -= '0';
5120 if (c>='a' && c<='f') return c -= 'a'-10;
5121 if (c>='A' && c<='F') return c -= 'A'-10;
5122 return 0;
5123}
5124
5125static void proc_APList_on_close( struct inode *inode, struct file *file ) {
5126 struct proc_data *data = (struct proc_data *)file->private_data;
5127 struct proc_dir_entry *dp = PDE(inode);
5128 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005129 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005130 APListRid APList_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005131 int i;
5132
5133 if ( !data->writelen ) return;
5134
5135 memset( &APList_rid, 0, sizeof(APList_rid) );
Al Viroa7497162007-12-20 17:49:41 -05005136 APList_rid.len = cpu_to_le16(sizeof(APList_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005137
5138 for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) {
5139 int j;
5140 for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) {
5141 switch(j%3) {
5142 case 0:
5143 APList_rid.ap[i][j/3]=
5144 hexVal(data->wbuffer[j+i*6*3])<<4;
5145 break;
5146 case 1:
5147 APList_rid.ap[i][j/3]|=
5148 hexVal(data->wbuffer[j+i*6*3]);
5149 break;
5150 }
5151 }
5152 }
5153 disable_MAC(ai, 1);
5154 writeAPListRid(ai, &APList_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005155 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005156}
5157
5158/* This function wraps PC4500_writerid with a MAC disable */
5159static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data,
5160 int len, int dummy ) {
5161 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005162
5163 disable_MAC(ai, 1);
5164 rc = PC4500_writerid(ai, rid, rid_data, len, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005165 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005166 return rc;
5167}
5168
5169/* Returns the length of the key at the index. If index == 0xffff
5170 * the index of the transmit key is returned. If the key doesn't exist,
5171 * -1 will be returned.
5172 */
5173static int get_wep_key(struct airo_info *ai, u16 index) {
5174 WepKeyRid wkr;
5175 int rc;
Al Viro4293ea32007-12-19 19:21:51 -05005176 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005177
5178 rc = readWepKeyRid(ai, &wkr, 1, 1);
5179 if (rc == SUCCESS) do {
5180 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05005181 if (wkr.kindex == cpu_to_le16(index)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005182 if (index == 0xffff) {
5183 return wkr.mac[0];
5184 }
Al Viro4293ea32007-12-19 19:21:51 -05005185 return le16_to_cpu(wkr.klen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005186 }
5187 readWepKeyRid(ai, &wkr, 0, 1);
Al Viro4293ea32007-12-19 19:21:51 -05005188 } while (lastindex != wkr.kindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005189 return -1;
5190}
5191
5192static int set_wep_key(struct airo_info *ai, u16 index,
Al Viro4293ea32007-12-19 19:21:51 -05005193 const char *key, u16 keylen, int perm, int lock )
5194{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005195 static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
5196 WepKeyRid wkr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005197
5198 memset(&wkr, 0, sizeof(wkr));
5199 if (keylen == 0) {
5200// We are selecting which key to use
Al Viro4293ea32007-12-19 19:21:51 -05005201 wkr.len = cpu_to_le16(sizeof(wkr));
5202 wkr.kindex = cpu_to_le16(0xffff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005203 wkr.mac[0] = (char)index;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005204 if (perm) ai->defindex = (char)index;
5205 } else {
5206// We are actually setting the key
Al Viro4293ea32007-12-19 19:21:51 -05005207 wkr.len = cpu_to_le16(sizeof(wkr));
5208 wkr.kindex = cpu_to_le16(index);
5209 wkr.klen = cpu_to_le16(keylen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005210 memcpy( wkr.key, key, keylen );
5211 memcpy( wkr.mac, macaddr, ETH_ALEN );
Linus Torvalds1da177e2005-04-16 15:20:36 -07005212 }
5213
Dan Streetmanf89b2322005-11-11 11:41:42 -05005214 if (perm) disable_MAC(ai, lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005215 writeWepKeyRid(ai, &wkr, perm, lock);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005216 if (perm) enable_MAC(ai, lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005217 return 0;
5218}
5219
5220static void proc_wepkey_on_close( struct inode *inode, struct file *file ) {
5221 struct proc_data *data;
5222 struct proc_dir_entry *dp = PDE(inode);
5223 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005224 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005225 int i;
5226 char key[16];
5227 u16 index = 0;
5228 int j = 0;
5229
5230 memset(key, 0, sizeof(key));
5231
5232 data = (struct proc_data *)file->private_data;
5233 if ( !data->writelen ) return;
5234
5235 if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' &&
5236 (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) {
5237 index = data->wbuffer[0] - '0';
5238 if (data->wbuffer[1] == '\n') {
5239 set_wep_key(ai, index, NULL, 0, 1, 1);
5240 return;
5241 }
5242 j = 2;
5243 } else {
Dan Williams934d8bf2006-03-16 13:46:23 -05005244 airo_print_err(ai->dev->name, "WepKey passed invalid key index");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005245 return;
5246 }
5247
5248 for( i = 0; i < 16*3 && data->wbuffer[i+j]; i++ ) {
5249 switch(i%3) {
5250 case 0:
5251 key[i/3] = hexVal(data->wbuffer[i+j])<<4;
5252 break;
5253 case 1:
5254 key[i/3] |= hexVal(data->wbuffer[i+j]);
5255 break;
5256 }
5257 }
5258 set_wep_key(ai, index, key, i/3, 1, 1);
5259}
5260
Al Viro4293ea32007-12-19 19:21:51 -05005261static int proc_wepkey_open( struct inode *inode, struct file *file )
5262{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005263 struct proc_data *data;
5264 struct proc_dir_entry *dp = PDE(inode);
5265 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005266 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005267 char *ptr;
5268 WepKeyRid wkr;
Al Viro4293ea32007-12-19 19:21:51 -05005269 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005270 int j=0;
5271 int rc;
5272
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005273 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005274 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005275 memset(&wkr, 0, sizeof(wkr));
5276 data = (struct proc_data *)file->private_data;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005277 if ((data->rbuffer = kzalloc( 180, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005278 kfree (file->private_data);
5279 return -ENOMEM;
5280 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005281 data->writelen = 0;
5282 data->maxwritelen = 80;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005283 if ((data->wbuffer = kzalloc( 80, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005284 kfree (data->rbuffer);
5285 kfree (file->private_data);
5286 return -ENOMEM;
5287 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005288 data->on_close = proc_wepkey_on_close;
5289
5290 ptr = data->rbuffer;
5291 strcpy(ptr, "No wep keys\n");
5292 rc = readWepKeyRid(ai, &wkr, 1, 1);
5293 if (rc == SUCCESS) do {
5294 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05005295 if (wkr.kindex == cpu_to_le16(0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005296 j += sprintf(ptr+j, "Tx key = %d\n",
5297 (int)wkr.mac[0]);
5298 } else {
5299 j += sprintf(ptr+j, "Key %d set with length = %d\n",
Al Viro4293ea32007-12-19 19:21:51 -05005300 le16_to_cpu(wkr.kindex),
5301 le16_to_cpu(wkr.klen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005302 }
5303 readWepKeyRid(ai, &wkr, 0, 1);
5304 } while((lastindex != wkr.kindex) && (j < 180-30));
5305
5306 data->readlen = strlen( data->rbuffer );
5307 return 0;
5308}
5309
Al Viro0dd22122007-12-17 16:11:57 -05005310static int proc_SSID_open(struct inode *inode, struct file *file)
5311{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005312 struct proc_data *data;
5313 struct proc_dir_entry *dp = PDE(inode);
5314 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005315 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005316 int i;
5317 char *ptr;
5318 SsidRid SSID_rid;
5319
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005320 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005321 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005322 data = (struct proc_data *)file->private_data;
5323 if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
5324 kfree (file->private_data);
5325 return -ENOMEM;
5326 }
5327 data->writelen = 0;
5328 data->maxwritelen = 33*3;
Al Viro0dd22122007-12-17 16:11:57 -05005329 /* allocate maxwritelen + 1; we'll want a sentinel */
5330 if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005331 kfree (data->rbuffer);
5332 kfree (file->private_data);
5333 return -ENOMEM;
5334 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005335 data->on_close = proc_SSID_on_close;
5336
5337 readSsidRid(ai, &SSID_rid);
5338 ptr = data->rbuffer;
Al Viro0dd22122007-12-17 16:11:57 -05005339 for (i = 0; i < 3; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005340 int j;
Al Viro0dd22122007-12-17 16:11:57 -05005341 size_t len = le16_to_cpu(SSID_rid.ssids[i].len);
5342 if (!len)
5343 break;
5344 if (len > 32)
5345 len = 32;
5346 for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005347 *ptr++ = SSID_rid.ssids[i].ssid[j];
Linus Torvalds1da177e2005-04-16 15:20:36 -07005348 *ptr++ = '\n';
5349 }
5350 *ptr = '\0';
5351 data->readlen = strlen( data->rbuffer );
5352 return 0;
5353}
5354
5355static int proc_APList_open( struct inode *inode, struct file *file ) {
5356 struct proc_data *data;
5357 struct proc_dir_entry *dp = PDE(inode);
5358 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005359 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005360 int i;
5361 char *ptr;
5362 APListRid APList_rid;
5363
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005364 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005365 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005366 data = (struct proc_data *)file->private_data;
5367 if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
5368 kfree (file->private_data);
5369 return -ENOMEM;
5370 }
5371 data->writelen = 0;
5372 data->maxwritelen = 4*6*3;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005373 if ((data->wbuffer = kzalloc( data->maxwritelen, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005374 kfree (data->rbuffer);
5375 kfree (file->private_data);
5376 return -ENOMEM;
5377 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005378 data->on_close = proc_APList_on_close;
5379
5380 readAPListRid(ai, &APList_rid);
5381 ptr = data->rbuffer;
5382 for( i = 0; i < 4; i++ ) {
5383// We end when we find a zero MAC
5384 if ( !*(int*)APList_rid.ap[i] &&
5385 !*(int*)&APList_rid.ap[i][2]) break;
Johannes Berge1749612008-10-27 15:59:26 -07005386 ptr += sprintf(ptr, "%pM\n", APList_rid.ap[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005387 }
5388 if (i==0) ptr += sprintf(ptr, "Not using specific APs\n");
5389
5390 *ptr = '\0';
5391 data->readlen = strlen( data->rbuffer );
5392 return 0;
5393}
5394
5395static int proc_BSSList_open( struct inode *inode, struct file *file ) {
5396 struct proc_data *data;
5397 struct proc_dir_entry *dp = PDE(inode);
5398 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005399 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005400 char *ptr;
5401 BSSListRid BSSList_rid;
5402 int rc;
5403 /* If doLoseSync is not 1, we won't do a Lose Sync */
5404 int doLoseSync = -1;
5405
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005406 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005407 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005408 data = (struct proc_data *)file->private_data;
5409 if ((data->rbuffer = kmalloc( 1024, GFP_KERNEL )) == NULL) {
5410 kfree (file->private_data);
5411 return -ENOMEM;
5412 }
5413 data->writelen = 0;
5414 data->maxwritelen = 0;
5415 data->wbuffer = NULL;
5416 data->on_close = NULL;
5417
5418 if (file->f_mode & FMODE_WRITE) {
5419 if (!(file->f_mode & FMODE_READ)) {
5420 Cmd cmd;
5421 Resp rsp;
5422
5423 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
5424 memset(&cmd, 0, sizeof(cmd));
5425 cmd.cmd=CMD_LISTBSS;
5426 if (down_interruptible(&ai->sem))
5427 return -ERESTARTSYS;
5428 issuecommand(ai, &cmd, &rsp);
5429 up(&ai->sem);
5430 data->readlen = 0;
5431 return 0;
5432 }
5433 doLoseSync = 1;
5434 }
5435 ptr = data->rbuffer;
5436 /* There is a race condition here if there are concurrent opens.
5437 Since it is a rare condition, we'll just live with it, otherwise
5438 we have to add a spin lock... */
5439 rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
Al Viro17e70492007-12-19 18:56:37 -05005440 while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
Johannes Berge1749612008-10-27 15:59:26 -07005441 ptr += sprintf(ptr, "%pM %*s rssi = %d",
5442 BSSList_rid.bssid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005443 (int)BSSList_rid.ssidLen,
5444 BSSList_rid.ssid,
Al Viro17e70492007-12-19 18:56:37 -05005445 le16_to_cpu(BSSList_rid.dBm));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005446 ptr += sprintf(ptr, " channel = %d %s %s %s %s\n",
Al Viro17e70492007-12-19 18:56:37 -05005447 le16_to_cpu(BSSList_rid.dsChannel),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005448 BSSList_rid.cap & CAP_ESS ? "ESS" : "",
5449 BSSList_rid.cap & CAP_IBSS ? "adhoc" : "",
5450 BSSList_rid.cap & CAP_PRIVACY ? "wep" : "",
5451 BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : "");
5452 rc = readBSSListRid(ai, 0, &BSSList_rid);
5453 }
5454 *ptr = '\0';
5455 data->readlen = strlen( data->rbuffer );
5456 return 0;
5457}
5458
5459static int proc_close( struct inode *inode, struct file *file )
5460{
Jesper Juhlb4558ea2005-10-28 16:53:13 -04005461 struct proc_data *data = file->private_data;
5462
5463 if (data->on_close != NULL)
5464 data->on_close(inode, file);
5465 kfree(data->rbuffer);
5466 kfree(data->wbuffer);
5467 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005468 return 0;
5469}
5470
Linus Torvalds1da177e2005-04-16 15:20:36 -07005471/* Since the card doesn't automatically switch to the right WEP mode,
5472 we will make it do it. If the card isn't associated, every secs we
5473 will switch WEP modes to see if that will help. If the card is
5474 associated we will check every minute to see if anything has
5475 changed. */
5476static void timer_func( struct net_device *dev ) {
Wang Chenfaf39942008-10-14 13:30:33 +08005477 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005478
5479/* We don't have a link so try changing the authtype */
5480 readConfigRid(apriv, 0);
5481 disable_MAC(apriv, 0);
5482 switch(apriv->config.authType) {
5483 case AUTH_ENCRYPT:
5484/* So drop to OPEN */
5485 apriv->config.authType = AUTH_OPEN;
5486 break;
5487 case AUTH_SHAREDKEY:
5488 if (apriv->keyindex < auto_wep) {
5489 set_wep_key(apriv, apriv->keyindex, NULL, 0, 0, 0);
5490 apriv->config.authType = AUTH_SHAREDKEY;
5491 apriv->keyindex++;
5492 } else {
5493 /* Drop to ENCRYPT */
5494 apriv->keyindex = 0;
5495 set_wep_key(apriv, apriv->defindex, NULL, 0, 0, 0);
5496 apriv->config.authType = AUTH_ENCRYPT;
5497 }
5498 break;
5499 default: /* We'll escalate to SHAREDKEY */
5500 apriv->config.authType = AUTH_SHAREDKEY;
5501 }
5502 set_bit (FLAG_COMMIT, &apriv->flags);
5503 writeConfigRid(apriv, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005504 enable_MAC(apriv, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005505 up(&apriv->sem);
5506
5507/* Schedule check to see if the change worked */
Dan Williams3c304952006-04-15 12:26:18 -04005508 clear_bit(JOB_AUTOWEP, &apriv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005509 apriv->expires = RUN_AT(HZ*3);
5510}
5511
Linus Torvalds1da177e2005-04-16 15:20:36 -07005512#ifdef CONFIG_PCI
5513static int __devinit airo_pci_probe(struct pci_dev *pdev,
5514 const struct pci_device_id *pent)
5515{
5516 struct net_device *dev;
5517
5518 if (pci_enable_device(pdev))
5519 return -ENODEV;
5520 pci_set_master(pdev);
5521
5522 if (pdev->device == 0x5000 || pdev->device == 0xa504)
5523 dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev);
5524 else
5525 dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev);
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005526 if (!dev) {
5527 pci_disable_device(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005528 return -ENODEV;
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005529 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005530
5531 pci_set_drvdata(pdev, dev);
5532 return 0;
5533}
5534
5535static void __devexit airo_pci_remove(struct pci_dev *pdev)
5536{
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01005537 struct net_device *dev = pci_get_drvdata(pdev);
5538
5539 airo_print_info(dev->name, "Unregistering...");
5540 stop_airo_card(dev, 1);
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005541 pci_disable_device(pdev);
5542 pci_set_drvdata(pdev, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005543}
5544
Pavel Machek05adc3b2005-04-16 15:25:25 -07005545static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005546{
5547 struct net_device *dev = pci_get_drvdata(pdev);
Wang Chenfaf39942008-10-14 13:30:33 +08005548 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005549 Cmd cmd;
5550 Resp rsp;
5551
Pavel Macheke292c732008-06-25 12:25:53 +02005552 if (!ai->APList)
5553 ai->APList = kmalloc(sizeof(APListRid), GFP_KERNEL);
5554 if (!ai->APList)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005555 return -ENOMEM;
Pavel Macheke292c732008-06-25 12:25:53 +02005556 if (!ai->SSID)
5557 ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL);
5558 if (!ai->SSID)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005559 return -ENOMEM;
5560 readAPListRid(ai, ai->APList);
5561 readSsidRid(ai, ai->SSID);
5562 memset(&cmd, 0, sizeof(cmd));
5563 /* the lock will be released at the end of the resume callback */
5564 if (down_interruptible(&ai->sem))
5565 return -EAGAIN;
5566 disable_MAC(ai, 0);
5567 netif_device_detach(dev);
5568 ai->power = state;
Pavel Macheke292c732008-06-25 12:25:53 +02005569 cmd.cmd = HOSTSLEEP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005570 issuecommand(ai, &cmd, &rsp);
5571
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005572 pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005573 pci_save_state(pdev);
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005574 return pci_set_power_state(pdev, pci_choose_state(pdev, state));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005575}
5576
5577static int airo_pci_resume(struct pci_dev *pdev)
5578{
5579 struct net_device *dev = pci_get_drvdata(pdev);
Wang Chenfaf39942008-10-14 13:30:33 +08005580 struct airo_info *ai = dev->ml_priv;
Michal Schmidt53232802005-10-04 07:46:21 -04005581 pci_power_t prev_state = pdev->current_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005582
Michal Schmidt53232802005-10-04 07:46:21 -04005583 pci_set_power_state(pdev, PCI_D0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005584 pci_restore_state(pdev);
Michal Schmidt53232802005-10-04 07:46:21 -04005585 pci_enable_wake(pdev, PCI_D0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005586
Michal Schmidt53232802005-10-04 07:46:21 -04005587 if (prev_state != PCI_D1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005588 reset_card(dev, 0);
5589 mpi_init_descriptors(ai);
5590 setup_card(ai, dev->dev_addr, 0);
5591 clear_bit(FLAG_RADIO_OFF, &ai->flags);
5592 clear_bit(FLAG_PENDING_XMIT, &ai->flags);
5593 } else {
5594 OUT4500(ai, EVACK, EV_AWAKEN);
5595 OUT4500(ai, EVACK, EV_AWAKEN);
5596 msleep(100);
5597 }
5598
Pavel Macheke292c732008-06-25 12:25:53 +02005599 set_bit(FLAG_COMMIT, &ai->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005600 disable_MAC(ai, 0);
5601 msleep(200);
5602 if (ai->SSID) {
5603 writeSsidRid(ai, ai->SSID, 0);
5604 kfree(ai->SSID);
5605 ai->SSID = NULL;
5606 }
5607 if (ai->APList) {
5608 writeAPListRid(ai, ai->APList, 0);
5609 kfree(ai->APList);
5610 ai->APList = NULL;
5611 }
5612 writeConfigRid(ai, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005613 enable_MAC(ai, 0);
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005614 ai->power = PMSG_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005615 netif_device_attach(dev);
5616 netif_wake_queue(dev);
5617 enable_interrupts(ai);
5618 up(&ai->sem);
5619 return 0;
5620}
5621#endif
5622
5623static int __init airo_init_module( void )
5624{
Jeff Garzikde897882006-10-01 07:31:09 -04005625 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005626
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005627 airo_entry = create_proc_entry("driver/aironet",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005628 S_IFDIR | airo_perm,
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005629 NULL);
Jeff Garzikde897882006-10-01 07:31:09 -04005630
5631 if (airo_entry) {
5632 airo_entry->uid = proc_uid;
5633 airo_entry->gid = proc_gid;
5634 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005635
Pavel Macheke292c732008-06-25 12:25:53 +02005636 for (i = 0; i < 4 && io[i] && irq[i]; i++) {
Dan Williams934d8bf2006-03-16 13:46:23 -05005637 airo_print_info("", "Trying to configure ISA adapter at irq=%d "
5638 "io=0x%x", irq[i], io[i] );
Linus Torvalds1da177e2005-04-16 15:20:36 -07005639 if (init_airo_card( irq[i], io[i], 0, NULL ))
Jeff Garzikde897882006-10-01 07:31:09 -04005640 /* do nothing */ ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005641 }
5642
5643#ifdef CONFIG_PCI
Dan Williams934d8bf2006-03-16 13:46:23 -05005644 airo_print_info("", "Probing for PCI adapters");
Jeff Garzikde897882006-10-01 07:31:09 -04005645 i = pci_register_driver(&airo_driver);
Dan Williams934d8bf2006-03-16 13:46:23 -05005646 airo_print_info("", "Finished probing for PCI adapters");
Jeff Garzikde897882006-10-01 07:31:09 -04005647
5648 if (i) {
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005649 remove_proc_entry("driver/aironet", NULL);
Jeff Garzikde897882006-10-01 07:31:09 -04005650 return i;
5651 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005652#endif
5653
5654 /* Always exit with success, as we are a library module
5655 * as well as a driver module
5656 */
5657 return 0;
5658}
5659
5660static void __exit airo_cleanup_module( void )
5661{
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01005662 struct airo_info *ai;
5663 while(!list_empty(&airo_devices)) {
5664 ai = list_entry(airo_devices.next, struct airo_info, dev_list);
5665 airo_print_info(ai->dev->name, "Unregistering...");
5666 stop_airo_card(ai->dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005667 }
5668#ifdef CONFIG_PCI
5669 pci_unregister_driver(&airo_driver);
5670#endif
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005671 remove_proc_entry("driver/aironet", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005672}
5673
Linus Torvalds1da177e2005-04-16 15:20:36 -07005674/*
5675 * Initial Wireless Extension code for Aironet driver by :
5676 * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00
5677 * Conversion to new driver API by :
5678 * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02
5679 * Javier also did a good amount of work here, adding some new extensions
5680 * and fixing my code. Let's just say that without him this code just
5681 * would not work at all... - Jean II
5682 */
5683
Dan Williams41480af2005-05-10 09:45:51 -04005684static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi)
5685{
Pavel Macheke292c732008-06-25 12:25:53 +02005686 if (!rssi_rid)
Dan Williams41480af2005-05-10 09:45:51 -04005687 return 0;
5688
5689 return (0x100 - rssi_rid[rssi].rssidBm);
5690}
5691
5692static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm)
5693{
5694 int i;
5695
Pavel Macheke292c732008-06-25 12:25:53 +02005696 if (!rssi_rid)
Dan Williams41480af2005-05-10 09:45:51 -04005697 return 0;
5698
Pavel Macheke292c732008-06-25 12:25:53 +02005699 for (i = 0; i < 256; i++)
Dan Williams41480af2005-05-10 09:45:51 -04005700 if (rssi_rid[i].rssidBm == dbm)
5701 return rssi_rid[i].rssipct;
5702
5703 return 0;
5704}
5705
5706
Linus Torvalds1da177e2005-04-16 15:20:36 -07005707static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid)
5708{
5709 int quality = 0;
Al Viro329e2c02007-12-20 22:58:57 -05005710 u16 sq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005711
Al Viro329e2c02007-12-20 22:58:57 -05005712 if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f))
Al Viro56d81bd2007-12-20 17:18:35 -05005713 return 0;
5714
5715 if (!(cap_rid->hardCap & cpu_to_le16(8)))
5716 return 0;
5717
Al Viro329e2c02007-12-20 22:58:57 -05005718 sq = le16_to_cpu(status_rid->signalQuality);
Al Viro56d81bd2007-12-20 17:18:35 -05005719 if (memcmp(cap_rid->prodName, "350", 3))
Al Viro329e2c02007-12-20 22:58:57 -05005720 if (sq > 0x20)
Al Viro56d81bd2007-12-20 17:18:35 -05005721 quality = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005722 else
Al Viro329e2c02007-12-20 22:58:57 -05005723 quality = 0x20 - sq;
Al Viro56d81bd2007-12-20 17:18:35 -05005724 else
Al Viro329e2c02007-12-20 22:58:57 -05005725 if (sq > 0xb0)
Al Viro56d81bd2007-12-20 17:18:35 -05005726 quality = 0;
Al Viro329e2c02007-12-20 22:58:57 -05005727 else if (sq < 0x10)
Al Viro56d81bd2007-12-20 17:18:35 -05005728 quality = 0xa0;
5729 else
Al Viro329e2c02007-12-20 22:58:57 -05005730 quality = 0xb0 - sq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005731 return quality;
5732}
5733
5734#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0)
5735#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50);
5736
5737/*------------------------------------------------------------------*/
5738/*
5739 * Wireless Handler : get protocol name
5740 */
5741static int airo_get_name(struct net_device *dev,
5742 struct iw_request_info *info,
5743 char *cwrq,
5744 char *extra)
5745{
5746 strcpy(cwrq, "IEEE 802.11-DS");
5747 return 0;
5748}
5749
5750/*------------------------------------------------------------------*/
5751/*
5752 * Wireless Handler : set frequency
5753 */
5754static int airo_set_freq(struct net_device *dev,
5755 struct iw_request_info *info,
5756 struct iw_freq *fwrq,
5757 char *extra)
5758{
Wang Chenfaf39942008-10-14 13:30:33 +08005759 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005760 int rc = -EINPROGRESS; /* Call commit handler */
5761
5762 /* If setting by frequency, convert to a channel */
David Kilroy9ee677c2008-12-23 14:03:38 +00005763 if(fwrq->e == 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005764 int f = fwrq->m / 100000;
David Kilroy9ee677c2008-12-23 14:03:38 +00005765
Linus Torvalds1da177e2005-04-16 15:20:36 -07005766 /* Hack to fall through... */
5767 fwrq->e = 0;
David Kilroy9ee677c2008-12-23 14:03:38 +00005768 fwrq->m = ieee80211_freq_to_dsss_chan(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005769 }
5770 /* Setting by channel number */
5771 if((fwrq->m > 1000) || (fwrq->e > 0))
5772 rc = -EOPNOTSUPP;
5773 else {
5774 int channel = fwrq->m;
5775 /* We should do a better check than that,
5776 * based on the card capability !!! */
Javier Achirica2610c732006-01-17 08:01:01 -05005777 if((channel < 1) || (channel > 14)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05005778 airo_print_dbg(dev->name, "New channel value of %d is invalid!",
5779 fwrq->m);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005780 rc = -EINVAL;
5781 } else {
5782 readConfigRid(local, 1);
5783 /* Yes ! We can set it !!! */
Al Viro3eb9b412007-12-21 00:00:35 -05005784 local->config.channelSet = cpu_to_le16(channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005785 set_bit (FLAG_COMMIT, &local->flags);
5786 }
5787 }
5788 return rc;
5789}
5790
5791/*------------------------------------------------------------------*/
5792/*
5793 * Wireless Handler : get frequency
5794 */
5795static int airo_get_freq(struct net_device *dev,
5796 struct iw_request_info *info,
5797 struct iw_freq *fwrq,
5798 char *extra)
5799{
Wang Chenfaf39942008-10-14 13:30:33 +08005800 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005801 StatusRid status_rid; /* Card status info */
Javier Achirica2610c732006-01-17 08:01:01 -05005802 int ch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005803
5804 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05005805 if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS)
5806 status_rid.channel = local->config.channelSet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005807 else
5808 readStatusRid(local, &status_rid, 1);
5809
Al Viro329e2c02007-12-20 22:58:57 -05005810 ch = le16_to_cpu(status_rid.channel);
Javier Achirica2610c732006-01-17 08:01:01 -05005811 if((ch > 0) && (ch < 15)) {
David Kilroy9ee677c2008-12-23 14:03:38 +00005812 fwrq->m = ieee80211_dsss_chan_to_freq(ch) * 100000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005813 fwrq->e = 1;
Javier Achirica2610c732006-01-17 08:01:01 -05005814 } else {
5815 fwrq->m = ch;
5816 fwrq->e = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005817 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005818
5819 return 0;
5820}
5821
5822/*------------------------------------------------------------------*/
5823/*
5824 * Wireless Handler : set ESSID
5825 */
5826static int airo_set_essid(struct net_device *dev,
5827 struct iw_request_info *info,
5828 struct iw_point *dwrq,
5829 char *extra)
5830{
Wang Chenfaf39942008-10-14 13:30:33 +08005831 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005832 SsidRid SSID_rid; /* SSIDs */
5833
5834 /* Reload the list of current SSID */
5835 readSsidRid(local, &SSID_rid);
5836
5837 /* Check if we asked for `any' */
5838 if(dwrq->flags == 0) {
5839 /* Just send an empty SSID list */
5840 memset(&SSID_rid, 0, sizeof(SSID_rid));
5841 } else {
5842 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
5843
5844 /* Check the size of the string */
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07005845 if(dwrq->length > IW_ESSID_MAX_SIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005846 return -E2BIG ;
5847 }
5848 /* Check if index is valid */
5849 if((index < 0) || (index >= 4)) {
5850 return -EINVAL;
5851 }
5852
5853 /* Set the SSID */
5854 memset(SSID_rid.ssids[index].ssid, 0,
5855 sizeof(SSID_rid.ssids[index].ssid));
5856 memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length);
Al Viro0dd22122007-12-17 16:11:57 -05005857 SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005858 }
Al Viro0dd22122007-12-17 16:11:57 -05005859 SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005860 /* Write it to the card */
5861 disable_MAC(local, 1);
5862 writeSsidRid(local, &SSID_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005863 enable_MAC(local, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005864
5865 return 0;
5866}
5867
5868/*------------------------------------------------------------------*/
5869/*
5870 * Wireless Handler : get ESSID
5871 */
5872static int airo_get_essid(struct net_device *dev,
5873 struct iw_request_info *info,
5874 struct iw_point *dwrq,
5875 char *extra)
5876{
Wang Chenfaf39942008-10-14 13:30:33 +08005877 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005878 StatusRid status_rid; /* Card status info */
5879
5880 readStatusRid(local, &status_rid, 1);
5881
5882 /* Note : if dwrq->flags != 0, we should
5883 * get the relevant SSID from the SSID list... */
5884
5885 /* Get the current SSID */
Al Viro329e2c02007-12-20 22:58:57 -05005886 memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005887 /* If none, we may want to get the one that was set */
5888
5889 /* Push it out ! */
Al Viro329e2c02007-12-20 22:58:57 -05005890 dwrq->length = le16_to_cpu(status_rid.SSIDlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005891 dwrq->flags = 1; /* active */
5892
5893 return 0;
5894}
5895
5896/*------------------------------------------------------------------*/
5897/*
5898 * Wireless Handler : set AP address
5899 */
5900static int airo_set_wap(struct net_device *dev,
5901 struct iw_request_info *info,
5902 struct sockaddr *awrq,
5903 char *extra)
5904{
Wang Chenfaf39942008-10-14 13:30:33 +08005905 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005906 Cmd cmd;
5907 Resp rsp;
5908 APListRid APList_rid;
Dan Williams4be757d2006-01-30 11:58:00 -05005909 static const u8 any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
5910 static const u8 off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07005911
5912 if (awrq->sa_family != ARPHRD_ETHER)
5913 return -EINVAL;
Dan Williams4be757d2006-01-30 11:58:00 -05005914 else if (!memcmp(any, awrq->sa_data, ETH_ALEN) ||
5915 !memcmp(off, awrq->sa_data, ETH_ALEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005916 memset(&cmd, 0, sizeof(cmd));
5917 cmd.cmd=CMD_LOSE_SYNC;
5918 if (down_interruptible(&local->sem))
5919 return -ERESTARTSYS;
5920 issuecommand(local, &cmd, &rsp);
5921 up(&local->sem);
5922 } else {
5923 memset(&APList_rid, 0, sizeof(APList_rid));
Al Viroa7497162007-12-20 17:49:41 -05005924 APList_rid.len = cpu_to_le16(sizeof(APList_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005925 memcpy(APList_rid.ap[0], awrq->sa_data, ETH_ALEN);
5926 disable_MAC(local, 1);
5927 writeAPListRid(local, &APList_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005928 enable_MAC(local, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005929 }
5930 return 0;
5931}
5932
5933/*------------------------------------------------------------------*/
5934/*
5935 * Wireless Handler : get AP address
5936 */
5937static int airo_get_wap(struct net_device *dev,
5938 struct iw_request_info *info,
5939 struct sockaddr *awrq,
5940 char *extra)
5941{
Wang Chenfaf39942008-10-14 13:30:33 +08005942 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005943 StatusRid status_rid; /* Card status info */
5944
5945 readStatusRid(local, &status_rid, 1);
5946
5947 /* Tentative. This seems to work, wow, I'm lucky !!! */
5948 memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN);
5949 awrq->sa_family = ARPHRD_ETHER;
5950
5951 return 0;
5952}
5953
5954/*------------------------------------------------------------------*/
5955/*
5956 * Wireless Handler : set Nickname
5957 */
5958static int airo_set_nick(struct net_device *dev,
5959 struct iw_request_info *info,
5960 struct iw_point *dwrq,
5961 char *extra)
5962{
Wang Chenfaf39942008-10-14 13:30:33 +08005963 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005964
5965 /* Check the size of the string */
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07005966 if(dwrq->length > 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005967 return -E2BIG;
5968 }
5969 readConfigRid(local, 1);
5970 memset(local->config.nodeName, 0, sizeof(local->config.nodeName));
5971 memcpy(local->config.nodeName, extra, dwrq->length);
5972 set_bit (FLAG_COMMIT, &local->flags);
5973
5974 return -EINPROGRESS; /* Call commit handler */
5975}
5976
5977/*------------------------------------------------------------------*/
5978/*
5979 * Wireless Handler : get Nickname
5980 */
5981static int airo_get_nick(struct net_device *dev,
5982 struct iw_request_info *info,
5983 struct iw_point *dwrq,
5984 char *extra)
5985{
Wang Chenfaf39942008-10-14 13:30:33 +08005986 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005987
5988 readConfigRid(local, 1);
5989 strncpy(extra, local->config.nodeName, 16);
5990 extra[16] = '\0';
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07005991 dwrq->length = strlen(extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005992
5993 return 0;
5994}
5995
5996/*------------------------------------------------------------------*/
5997/*
5998 * Wireless Handler : set Bit-Rate
5999 */
6000static int airo_set_rate(struct net_device *dev,
6001 struct iw_request_info *info,
6002 struct iw_param *vwrq,
6003 char *extra)
6004{
Wang Chenfaf39942008-10-14 13:30:33 +08006005 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006006 CapabilityRid cap_rid; /* Card capability info */
6007 u8 brate = 0;
6008 int i;
6009
6010 /* First : get a valid bit rate value */
6011 readCapabilityRid(local, &cap_rid, 1);
6012
6013 /* Which type of value ? */
6014 if((vwrq->value < 8) && (vwrq->value >= 0)) {
6015 /* Setting by rate index */
6016 /* Find value in the magic rate table */
6017 brate = cap_rid.supportedRates[vwrq->value];
6018 } else {
6019 /* Setting by frequency value */
6020 u8 normvalue = (u8) (vwrq->value/500000);
6021
6022 /* Check if rate is valid */
6023 for(i = 0 ; i < 8 ; i++) {
6024 if(normvalue == cap_rid.supportedRates[i]) {
6025 brate = normvalue;
6026 break;
6027 }
6028 }
6029 }
6030 /* -1 designed the max rate (mostly auto mode) */
6031 if(vwrq->value == -1) {
6032 /* Get the highest available rate */
6033 for(i = 0 ; i < 8 ; i++) {
6034 if(cap_rid.supportedRates[i] == 0)
6035 break;
6036 }
6037 if(i != 0)
6038 brate = cap_rid.supportedRates[i - 1];
6039 }
6040 /* Check that it is valid */
6041 if(brate == 0) {
6042 return -EINVAL;
6043 }
6044
6045 readConfigRid(local, 1);
6046 /* Now, check if we want a fixed or auto value */
6047 if(vwrq->fixed == 0) {
6048 /* Fill all the rates up to this max rate */
6049 memset(local->config.rates, 0, 8);
6050 for(i = 0 ; i < 8 ; i++) {
6051 local->config.rates[i] = cap_rid.supportedRates[i];
6052 if(local->config.rates[i] == brate)
6053 break;
6054 }
6055 } else {
6056 /* Fixed mode */
6057 /* One rate, fixed */
6058 memset(local->config.rates, 0, 8);
6059 local->config.rates[0] = brate;
6060 }
6061 set_bit (FLAG_COMMIT, &local->flags);
6062
6063 return -EINPROGRESS; /* Call commit handler */
6064}
6065
6066/*------------------------------------------------------------------*/
6067/*
6068 * Wireless Handler : get Bit-Rate
6069 */
6070static int airo_get_rate(struct net_device *dev,
6071 struct iw_request_info *info,
6072 struct iw_param *vwrq,
6073 char *extra)
6074{
Wang Chenfaf39942008-10-14 13:30:33 +08006075 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006076 StatusRid status_rid; /* Card status info */
6077
6078 readStatusRid(local, &status_rid, 1);
6079
Al Viro329e2c02007-12-20 22:58:57 -05006080 vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006081 /* If more than one rate, set auto */
6082 readConfigRid(local, 1);
6083 vwrq->fixed = (local->config.rates[1] == 0);
6084
6085 return 0;
6086}
6087
6088/*------------------------------------------------------------------*/
6089/*
6090 * Wireless Handler : set RTS threshold
6091 */
6092static int airo_set_rts(struct net_device *dev,
6093 struct iw_request_info *info,
6094 struct iw_param *vwrq,
6095 char *extra)
6096{
Wang Chenfaf39942008-10-14 13:30:33 +08006097 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006098 int rthr = vwrq->value;
6099
6100 if(vwrq->disabled)
Dan Williams15db2762006-03-16 13:46:27 -05006101 rthr = AIRO_DEF_MTU;
6102 if((rthr < 0) || (rthr > AIRO_DEF_MTU)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006103 return -EINVAL;
6104 }
6105 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006106 local->config.rtsThres = cpu_to_le16(rthr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006107 set_bit (FLAG_COMMIT, &local->flags);
6108
6109 return -EINPROGRESS; /* Call commit handler */
6110}
6111
6112/*------------------------------------------------------------------*/
6113/*
6114 * Wireless Handler : get RTS threshold
6115 */
6116static int airo_get_rts(struct net_device *dev,
6117 struct iw_request_info *info,
6118 struct iw_param *vwrq,
6119 char *extra)
6120{
Wang Chenfaf39942008-10-14 13:30:33 +08006121 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006122
6123 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006124 vwrq->value = le16_to_cpu(local->config.rtsThres);
Dan Williams15db2762006-03-16 13:46:27 -05006125 vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006126 vwrq->fixed = 1;
6127
6128 return 0;
6129}
6130
6131/*------------------------------------------------------------------*/
6132/*
6133 * Wireless Handler : set Fragmentation threshold
6134 */
6135static int airo_set_frag(struct net_device *dev,
6136 struct iw_request_info *info,
6137 struct iw_param *vwrq,
6138 char *extra)
6139{
Wang Chenfaf39942008-10-14 13:30:33 +08006140 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006141 int fthr = vwrq->value;
6142
6143 if(vwrq->disabled)
Dan Williams15db2762006-03-16 13:46:27 -05006144 fthr = AIRO_DEF_MTU;
6145 if((fthr < 256) || (fthr > AIRO_DEF_MTU)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006146 return -EINVAL;
6147 }
6148 fthr &= ~0x1; /* Get an even value - is it really needed ??? */
6149 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006150 local->config.fragThresh = cpu_to_le16(fthr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006151 set_bit (FLAG_COMMIT, &local->flags);
6152
6153 return -EINPROGRESS; /* Call commit handler */
6154}
6155
6156/*------------------------------------------------------------------*/
6157/*
6158 * Wireless Handler : get Fragmentation threshold
6159 */
6160static int airo_get_frag(struct net_device *dev,
6161 struct iw_request_info *info,
6162 struct iw_param *vwrq,
6163 char *extra)
6164{
Wang Chenfaf39942008-10-14 13:30:33 +08006165 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006166
6167 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006168 vwrq->value = le16_to_cpu(local->config.fragThresh);
Dan Williams15db2762006-03-16 13:46:27 -05006169 vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006170 vwrq->fixed = 1;
6171
6172 return 0;
6173}
6174
6175/*------------------------------------------------------------------*/
6176/*
6177 * Wireless Handler : set Mode of Operation
6178 */
6179static int airo_set_mode(struct net_device *dev,
6180 struct iw_request_info *info,
6181 __u32 *uwrq,
6182 char *extra)
6183{
Wang Chenfaf39942008-10-14 13:30:33 +08006184 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006185 int reset = 0;
6186
6187 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006188 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006189 reset = 1;
6190
6191 switch(*uwrq) {
6192 case IW_MODE_ADHOC:
Al Viro3eb9b412007-12-21 00:00:35 -05006193 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006194 local->config.opmode |= MODE_STA_IBSS;
Al Viro3eb9b412007-12-21 00:00:35 -05006195 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006196 local->config.scanMode = SCANMODE_ACTIVE;
6197 clear_bit (FLAG_802_11, &local->flags);
6198 break;
6199 case IW_MODE_INFRA:
Al Viro3eb9b412007-12-21 00:00:35 -05006200 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006201 local->config.opmode |= MODE_STA_ESS;
Al Viro3eb9b412007-12-21 00:00:35 -05006202 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006203 local->config.scanMode = SCANMODE_ACTIVE;
6204 clear_bit (FLAG_802_11, &local->flags);
6205 break;
6206 case IW_MODE_MASTER:
Al Viro3eb9b412007-12-21 00:00:35 -05006207 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006208 local->config.opmode |= MODE_AP;
Al Viro3eb9b412007-12-21 00:00:35 -05006209 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006210 local->config.scanMode = SCANMODE_ACTIVE;
6211 clear_bit (FLAG_802_11, &local->flags);
6212 break;
6213 case IW_MODE_REPEAT:
Al Viro3eb9b412007-12-21 00:00:35 -05006214 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006215 local->config.opmode |= MODE_AP_RPTR;
Al Viro3eb9b412007-12-21 00:00:35 -05006216 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006217 local->config.scanMode = SCANMODE_ACTIVE;
6218 clear_bit (FLAG_802_11, &local->flags);
6219 break;
6220 case IW_MODE_MONITOR:
Al Viro3eb9b412007-12-21 00:00:35 -05006221 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006222 local->config.opmode |= MODE_STA_ESS;
Al Viro3eb9b412007-12-21 00:00:35 -05006223 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006224 local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
6225 local->config.scanMode = SCANMODE_PASSIVE;
6226 set_bit (FLAG_802_11, &local->flags);
6227 break;
6228 default:
6229 return -EINVAL;
6230 }
6231 if (reset)
6232 set_bit (FLAG_RESET, &local->flags);
6233 set_bit (FLAG_COMMIT, &local->flags);
6234
6235 return -EINPROGRESS; /* Call commit handler */
6236}
6237
6238/*------------------------------------------------------------------*/
6239/*
6240 * Wireless Handler : get Mode of Operation
6241 */
6242static int airo_get_mode(struct net_device *dev,
6243 struct iw_request_info *info,
6244 __u32 *uwrq,
6245 char *extra)
6246{
Wang Chenfaf39942008-10-14 13:30:33 +08006247 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006248
6249 readConfigRid(local, 1);
6250 /* If not managed, assume it's ad-hoc */
Al Viro3eb9b412007-12-21 00:00:35 -05006251 switch (local->config.opmode & MODE_CFG_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006252 case MODE_STA_ESS:
6253 *uwrq = IW_MODE_INFRA;
6254 break;
6255 case MODE_AP:
6256 *uwrq = IW_MODE_MASTER;
6257 break;
6258 case MODE_AP_RPTR:
6259 *uwrq = IW_MODE_REPEAT;
6260 break;
6261 default:
6262 *uwrq = IW_MODE_ADHOC;
6263 }
6264
6265 return 0;
6266}
6267
Al Viro56d81bd2007-12-20 17:18:35 -05006268static inline int valid_index(CapabilityRid *p, int index)
6269{
6270 if (index < 0)
6271 return 0;
6272 return index < (p->softCap & cpu_to_le16(0x80) ? 4 : 1);
6273}
6274
Linus Torvalds1da177e2005-04-16 15:20:36 -07006275/*------------------------------------------------------------------*/
6276/*
6277 * Wireless Handler : set Encryption Key
6278 */
6279static int airo_set_encode(struct net_device *dev,
6280 struct iw_request_info *info,
6281 struct iw_point *dwrq,
6282 char *extra)
6283{
Wang Chenfaf39942008-10-14 13:30:33 +08006284 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006285 CapabilityRid cap_rid; /* Card capability info */
Dan Streetmanf89b2322005-11-11 11:41:42 -05006286 int perm = ( dwrq->flags & IW_ENCODE_TEMP ? 0 : 1 );
Al Viro3eb9b412007-12-21 00:00:35 -05006287 __le16 currentAuthType = local->config.authType;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006288
6289 /* Is WEP supported ? */
6290 readCapabilityRid(local, &cap_rid, 1);
6291 /* Older firmware doesn't support this...
Al Viro56d81bd2007-12-20 17:18:35 -05006292 if(!(cap_rid.softCap & cpu_to_le16(2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006293 return -EOPNOTSUPP;
6294 } */
6295 readConfigRid(local, 1);
6296
6297 /* Basic checking: do we have a key to set ?
6298 * Note : with the new API, it's impossible to get a NULL pointer.
6299 * Therefore, we need to check a key size == 0 instead.
6300 * New version of iwconfig properly set the IW_ENCODE_NOKEY flag
6301 * when no key is present (only change flags), but older versions
6302 * don't do it. - Jean II */
6303 if (dwrq->length > 0) {
6304 wep_key_t key;
6305 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
6306 int current_index = get_wep_key(local, 0xffff);
6307 /* Check the size of the key */
6308 if (dwrq->length > MAX_KEY_SIZE) {
6309 return -EINVAL;
6310 }
6311 /* Check the index (none -> use current) */
Al Viro56d81bd2007-12-20 17:18:35 -05006312 if (!valid_index(&cap_rid, index))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006313 index = current_index;
6314 /* Set the length */
6315 if (dwrq->length > MIN_KEY_SIZE)
6316 key.len = MAX_KEY_SIZE;
6317 else
6318 if (dwrq->length > 0)
6319 key.len = MIN_KEY_SIZE;
6320 else
6321 /* Disable the key */
6322 key.len = 0;
6323 /* Check if the key is not marked as invalid */
6324 if(!(dwrq->flags & IW_ENCODE_NOKEY)) {
6325 /* Cleanup */
6326 memset(key.key, 0, MAX_KEY_SIZE);
6327 /* Copy the key in the driver */
6328 memcpy(key.key, extra, dwrq->length);
6329 /* Send the key to the card */
Dan Streetmanf89b2322005-11-11 11:41:42 -05006330 set_wep_key(local, index, key.key, key.len, perm, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006331 }
6332 /* WE specify that if a valid key is set, encryption
6333 * should be enabled (user may turn it off later)
6334 * This is also how "iwconfig ethX key on" works */
6335 if((index == current_index) && (key.len > 0) &&
6336 (local->config.authType == AUTH_OPEN)) {
6337 local->config.authType = AUTH_ENCRYPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006338 }
6339 } else {
6340 /* Do we want to just set the transmit key index ? */
6341 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Al Viro56d81bd2007-12-20 17:18:35 -05006342 if (valid_index(&cap_rid, index)) {
Dan Streetmanf89b2322005-11-11 11:41:42 -05006343 set_wep_key(local, index, NULL, 0, perm, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006344 } else
6345 /* Don't complain if only change the mode */
Jeff Garzik93a3b602007-11-23 21:50:20 -05006346 if (!(dwrq->flags & IW_ENCODE_MODE))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006347 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006348 }
6349 /* Read the flags */
6350 if(dwrq->flags & IW_ENCODE_DISABLED)
6351 local->config.authType = AUTH_OPEN; // disable encryption
6352 if(dwrq->flags & IW_ENCODE_RESTRICTED)
6353 local->config.authType = AUTH_SHAREDKEY; // Only Both
6354 if(dwrq->flags & IW_ENCODE_OPEN)
6355 local->config.authType = AUTH_ENCRYPT; // Only Wep
6356 /* Commit the changes to flags if needed */
Dan Streetmanf89b2322005-11-11 11:41:42 -05006357 if (local->config.authType != currentAuthType)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006358 set_bit (FLAG_COMMIT, &local->flags);
6359 return -EINPROGRESS; /* Call commit handler */
6360}
6361
6362/*------------------------------------------------------------------*/
6363/*
6364 * Wireless Handler : get Encryption Key
6365 */
6366static int airo_get_encode(struct net_device *dev,
6367 struct iw_request_info *info,
6368 struct iw_point *dwrq,
6369 char *extra)
6370{
Wang Chenfaf39942008-10-14 13:30:33 +08006371 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006372 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
6373 CapabilityRid cap_rid; /* Card capability info */
6374
6375 /* Is it supported ? */
6376 readCapabilityRid(local, &cap_rid, 1);
Al Viro56d81bd2007-12-20 17:18:35 -05006377 if(!(cap_rid.softCap & cpu_to_le16(2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006378 return -EOPNOTSUPP;
6379 }
6380 readConfigRid(local, 1);
6381 /* Check encryption mode */
6382 switch(local->config.authType) {
6383 case AUTH_ENCRYPT:
6384 dwrq->flags = IW_ENCODE_OPEN;
6385 break;
6386 case AUTH_SHAREDKEY:
6387 dwrq->flags = IW_ENCODE_RESTRICTED;
6388 break;
6389 default:
6390 case AUTH_OPEN:
6391 dwrq->flags = IW_ENCODE_DISABLED;
6392 break;
6393 }
6394 /* We can't return the key, so set the proper flag and return zero */
6395 dwrq->flags |= IW_ENCODE_NOKEY;
6396 memset(extra, 0, 16);
6397
6398 /* Which key do we want ? -1 -> tx index */
Al Viro56d81bd2007-12-20 17:18:35 -05006399 if (!valid_index(&cap_rid, index))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006400 index = get_wep_key(local, 0xffff);
6401 dwrq->flags |= index + 1;
6402 /* Copy the key to the user buffer */
6403 dwrq->length = get_wep_key(local, index);
6404 if (dwrq->length > 16) {
6405 dwrq->length=0;
6406 }
6407 return 0;
6408}
6409
6410/*------------------------------------------------------------------*/
6411/*
Dan Williams4be757d2006-01-30 11:58:00 -05006412 * Wireless Handler : set extended Encryption parameters
6413 */
6414static int airo_set_encodeext(struct net_device *dev,
6415 struct iw_request_info *info,
6416 union iwreq_data *wrqu,
6417 char *extra)
6418{
Wang Chenfaf39942008-10-14 13:30:33 +08006419 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006420 struct iw_point *encoding = &wrqu->encoding;
6421 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
6422 CapabilityRid cap_rid; /* Card capability info */
6423 int perm = ( encoding->flags & IW_ENCODE_TEMP ? 0 : 1 );
Al Viro3eb9b412007-12-21 00:00:35 -05006424 __le16 currentAuthType = local->config.authType;
Dan Williams22d88462006-02-05 18:00:30 -05006425 int idx, key_len, alg = ext->alg, set_key = 1;
Dan Williams4be757d2006-01-30 11:58:00 -05006426 wep_key_t key;
6427
6428 /* Is WEP supported ? */
6429 readCapabilityRid(local, &cap_rid, 1);
6430 /* Older firmware doesn't support this...
Al Viro56d81bd2007-12-20 17:18:35 -05006431 if(!(cap_rid.softCap & cpu_to_le16(2))) {
Dan Williams4be757d2006-01-30 11:58:00 -05006432 return -EOPNOTSUPP;
6433 } */
6434 readConfigRid(local, 1);
6435
6436 /* Determine and validate the key index */
6437 idx = encoding->flags & IW_ENCODE_INDEX;
6438 if (idx) {
Al Viro56d81bd2007-12-20 17:18:35 -05006439 if (!valid_index(&cap_rid, idx - 1))
Dan Williams4be757d2006-01-30 11:58:00 -05006440 return -EINVAL;
6441 idx--;
6442 } else
6443 idx = get_wep_key(local, 0xffff);
6444
6445 if (encoding->flags & IW_ENCODE_DISABLED)
6446 alg = IW_ENCODE_ALG_NONE;
6447
Dan Williams4be757d2006-01-30 11:58:00 -05006448 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
Dan Williams22d88462006-02-05 18:00:30 -05006449 /* Only set transmit key index here, actual
6450 * key is set below if needed.
6451 */
Dan Williams4be757d2006-01-30 11:58:00 -05006452 set_wep_key(local, idx, NULL, 0, perm, 1);
Dan Williams22d88462006-02-05 18:00:30 -05006453 set_key = ext->key_len > 0 ? 1 : 0;
6454 }
6455
6456 if (set_key) {
Dan Williams4be757d2006-01-30 11:58:00 -05006457 /* Set the requested key first */
6458 memset(key.key, 0, MAX_KEY_SIZE);
6459 switch (alg) {
6460 case IW_ENCODE_ALG_NONE:
6461 key.len = 0;
6462 break;
6463 case IW_ENCODE_ALG_WEP:
6464 if (ext->key_len > MIN_KEY_SIZE) {
6465 key.len = MAX_KEY_SIZE;
6466 } else if (ext->key_len > 0) {
6467 key.len = MIN_KEY_SIZE;
6468 } else {
6469 return -EINVAL;
6470 }
6471 key_len = min (ext->key_len, key.len);
6472 memcpy(key.key, ext->key, key_len);
6473 break;
6474 default:
6475 return -EINVAL;
6476 }
6477 /* Send the key to the card */
6478 set_wep_key(local, idx, key.key, key.len, perm, 1);
6479 }
6480
6481 /* Read the flags */
6482 if(encoding->flags & IW_ENCODE_DISABLED)
6483 local->config.authType = AUTH_OPEN; // disable encryption
6484 if(encoding->flags & IW_ENCODE_RESTRICTED)
6485 local->config.authType = AUTH_SHAREDKEY; // Only Both
6486 if(encoding->flags & IW_ENCODE_OPEN)
6487 local->config.authType = AUTH_ENCRYPT; // Only Wep
6488 /* Commit the changes to flags if needed */
6489 if (local->config.authType != currentAuthType)
6490 set_bit (FLAG_COMMIT, &local->flags);
6491
6492 return -EINPROGRESS;
6493}
6494
6495
6496/*------------------------------------------------------------------*/
6497/*
6498 * Wireless Handler : get extended Encryption parameters
6499 */
6500static int airo_get_encodeext(struct net_device *dev,
6501 struct iw_request_info *info,
6502 union iwreq_data *wrqu,
6503 char *extra)
6504{
Wang Chenfaf39942008-10-14 13:30:33 +08006505 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006506 struct iw_point *encoding = &wrqu->encoding;
6507 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
6508 CapabilityRid cap_rid; /* Card capability info */
6509 int idx, max_key_len;
6510
6511 /* Is it supported ? */
6512 readCapabilityRid(local, &cap_rid, 1);
Al Viro56d81bd2007-12-20 17:18:35 -05006513 if(!(cap_rid.softCap & cpu_to_le16(2))) {
Dan Williams4be757d2006-01-30 11:58:00 -05006514 return -EOPNOTSUPP;
6515 }
6516 readConfigRid(local, 1);
6517
6518 max_key_len = encoding->length - sizeof(*ext);
6519 if (max_key_len < 0)
6520 return -EINVAL;
6521
6522 idx = encoding->flags & IW_ENCODE_INDEX;
6523 if (idx) {
Al Viro56d81bd2007-12-20 17:18:35 -05006524 if (!valid_index(&cap_rid, idx - 1))
Dan Williams4be757d2006-01-30 11:58:00 -05006525 return -EINVAL;
6526 idx--;
6527 } else
6528 idx = get_wep_key(local, 0xffff);
6529
6530 encoding->flags = idx + 1;
6531 memset(ext, 0, sizeof(*ext));
6532
6533 /* Check encryption mode */
6534 switch(local->config.authType) {
6535 case AUTH_ENCRYPT:
6536 encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
6537 break;
6538 case AUTH_SHAREDKEY:
6539 encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
6540 break;
6541 default:
6542 case AUTH_OPEN:
6543 encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED;
6544 break;
6545 }
6546 /* We can't return the key, so set the proper flag and return zero */
6547 encoding->flags |= IW_ENCODE_NOKEY;
6548 memset(extra, 0, 16);
6549
6550 /* Copy the key to the user buffer */
6551 ext->key_len = get_wep_key(local, idx);
6552 if (ext->key_len > 16) {
6553 ext->key_len=0;
6554 }
6555
6556 return 0;
6557}
6558
6559
6560/*------------------------------------------------------------------*/
6561/*
6562 * Wireless Handler : set extended authentication parameters
6563 */
6564static int airo_set_auth(struct net_device *dev,
6565 struct iw_request_info *info,
6566 union iwreq_data *wrqu, char *extra)
6567{
Wang Chenfaf39942008-10-14 13:30:33 +08006568 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006569 struct iw_param *param = &wrqu->param;
Al Viro3eb9b412007-12-21 00:00:35 -05006570 __le16 currentAuthType = local->config.authType;
Dan Williams4be757d2006-01-30 11:58:00 -05006571
6572 switch (param->flags & IW_AUTH_INDEX) {
6573 case IW_AUTH_WPA_VERSION:
6574 case IW_AUTH_CIPHER_PAIRWISE:
6575 case IW_AUTH_CIPHER_GROUP:
6576 case IW_AUTH_KEY_MGMT:
6577 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6578 case IW_AUTH_PRIVACY_INVOKED:
6579 /*
6580 * airo does not use these parameters
6581 */
6582 break;
6583
6584 case IW_AUTH_DROP_UNENCRYPTED:
6585 if (param->value) {
6586 /* Only change auth type if unencrypted */
6587 if (currentAuthType == AUTH_OPEN)
6588 local->config.authType = AUTH_ENCRYPT;
6589 } else {
6590 local->config.authType = AUTH_OPEN;
6591 }
6592
6593 /* Commit the changes to flags if needed */
6594 if (local->config.authType != currentAuthType)
6595 set_bit (FLAG_COMMIT, &local->flags);
6596 break;
6597
6598 case IW_AUTH_80211_AUTH_ALG: {
6599 /* FIXME: What about AUTH_OPEN? This API seems to
6600 * disallow setting our auth to AUTH_OPEN.
6601 */
6602 if (param->value & IW_AUTH_ALG_SHARED_KEY) {
6603 local->config.authType = AUTH_SHAREDKEY;
6604 } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
6605 local->config.authType = AUTH_ENCRYPT;
6606 } else
6607 return -EINVAL;
6608 break;
6609
6610 /* Commit the changes to flags if needed */
6611 if (local->config.authType != currentAuthType)
6612 set_bit (FLAG_COMMIT, &local->flags);
6613 }
6614
6615 case IW_AUTH_WPA_ENABLED:
6616 /* Silently accept disable of WPA */
6617 if (param->value > 0)
6618 return -EOPNOTSUPP;
6619 break;
6620
6621 default:
6622 return -EOPNOTSUPP;
6623 }
6624 return -EINPROGRESS;
6625}
6626
6627
6628/*------------------------------------------------------------------*/
6629/*
6630 * Wireless Handler : get extended authentication parameters
6631 */
6632static int airo_get_auth(struct net_device *dev,
6633 struct iw_request_info *info,
6634 union iwreq_data *wrqu, char *extra)
6635{
Wang Chenfaf39942008-10-14 13:30:33 +08006636 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006637 struct iw_param *param = &wrqu->param;
Al Viro3eb9b412007-12-21 00:00:35 -05006638 __le16 currentAuthType = local->config.authType;
Dan Williams4be757d2006-01-30 11:58:00 -05006639
6640 switch (param->flags & IW_AUTH_INDEX) {
6641 case IW_AUTH_DROP_UNENCRYPTED:
6642 switch (currentAuthType) {
6643 case AUTH_SHAREDKEY:
6644 case AUTH_ENCRYPT:
6645 param->value = 1;
6646 break;
6647 default:
6648 param->value = 0;
6649 break;
6650 }
6651 break;
6652
6653 case IW_AUTH_80211_AUTH_ALG:
6654 switch (currentAuthType) {
6655 case AUTH_SHAREDKEY:
6656 param->value = IW_AUTH_ALG_SHARED_KEY;
6657 break;
6658 case AUTH_ENCRYPT:
6659 default:
6660 param->value = IW_AUTH_ALG_OPEN_SYSTEM;
6661 break;
6662 }
6663 break;
6664
6665 case IW_AUTH_WPA_ENABLED:
6666 param->value = 0;
6667 break;
6668
6669 default:
6670 return -EOPNOTSUPP;
6671 }
6672 return 0;
6673}
6674
6675
6676/*------------------------------------------------------------------*/
6677/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07006678 * Wireless Handler : set Tx-Power
6679 */
6680static int airo_set_txpow(struct net_device *dev,
6681 struct iw_request_info *info,
6682 struct iw_param *vwrq,
6683 char *extra)
6684{
Wang Chenfaf39942008-10-14 13:30:33 +08006685 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006686 CapabilityRid cap_rid; /* Card capability info */
6687 int i;
6688 int rc = -EINVAL;
Al Viro3eb9b412007-12-21 00:00:35 -05006689 __le16 v = cpu_to_le16(vwrq->value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006690
6691 readCapabilityRid(local, &cap_rid, 1);
6692
6693 if (vwrq->disabled) {
6694 set_bit (FLAG_RADIO_OFF, &local->flags);
6695 set_bit (FLAG_COMMIT, &local->flags);
6696 return -EINPROGRESS; /* Call commit handler */
6697 }
6698 if (vwrq->flags != IW_TXPOW_MWATT) {
6699 return -EINVAL;
6700 }
6701 clear_bit (FLAG_RADIO_OFF, &local->flags);
6702 for (i = 0; cap_rid.txPowerLevels[i] && (i < 8); i++)
Al Viro3eb9b412007-12-21 00:00:35 -05006703 if (v == cap_rid.txPowerLevels[i]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006704 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006705 local->config.txPower = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006706 set_bit (FLAG_COMMIT, &local->flags);
6707 rc = -EINPROGRESS; /* Call commit handler */
6708 break;
6709 }
6710 return rc;
6711}
6712
6713/*------------------------------------------------------------------*/
6714/*
6715 * Wireless Handler : get Tx-Power
6716 */
6717static int airo_get_txpow(struct net_device *dev,
6718 struct iw_request_info *info,
6719 struct iw_param *vwrq,
6720 char *extra)
6721{
Wang Chenfaf39942008-10-14 13:30:33 +08006722 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006723
6724 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006725 vwrq->value = le16_to_cpu(local->config.txPower);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006726 vwrq->fixed = 1; /* No power control */
6727 vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags);
6728 vwrq->flags = IW_TXPOW_MWATT;
6729
6730 return 0;
6731}
6732
6733/*------------------------------------------------------------------*/
6734/*
6735 * Wireless Handler : set Retry limits
6736 */
6737static int airo_set_retry(struct net_device *dev,
6738 struct iw_request_info *info,
6739 struct iw_param *vwrq,
6740 char *extra)
6741{
Wang Chenfaf39942008-10-14 13:30:33 +08006742 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006743 int rc = -EINVAL;
6744
6745 if(vwrq->disabled) {
6746 return -EINVAL;
6747 }
6748 readConfigRid(local, 1);
6749 if(vwrq->flags & IW_RETRY_LIMIT) {
Al Viro3eb9b412007-12-21 00:00:35 -05006750 __le16 v = cpu_to_le16(vwrq->value);
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006751 if(vwrq->flags & IW_RETRY_LONG)
Al Viro3eb9b412007-12-21 00:00:35 -05006752 local->config.longRetryLimit = v;
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006753 else if (vwrq->flags & IW_RETRY_SHORT)
Al Viro3eb9b412007-12-21 00:00:35 -05006754 local->config.shortRetryLimit = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006755 else {
6756 /* No modifier : set both */
Al Viro3eb9b412007-12-21 00:00:35 -05006757 local->config.longRetryLimit = v;
6758 local->config.shortRetryLimit = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006759 }
6760 set_bit (FLAG_COMMIT, &local->flags);
6761 rc = -EINPROGRESS; /* Call commit handler */
6762 }
6763 if(vwrq->flags & IW_RETRY_LIFETIME) {
Al Viro3eb9b412007-12-21 00:00:35 -05006764 local->config.txLifetime = cpu_to_le16(vwrq->value / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006765 set_bit (FLAG_COMMIT, &local->flags);
6766 rc = -EINPROGRESS; /* Call commit handler */
6767 }
6768 return rc;
6769}
6770
6771/*------------------------------------------------------------------*/
6772/*
6773 * Wireless Handler : get Retry limits
6774 */
6775static int airo_get_retry(struct net_device *dev,
6776 struct iw_request_info *info,
6777 struct iw_param *vwrq,
6778 char *extra)
6779{
Wang Chenfaf39942008-10-14 13:30:33 +08006780 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006781
6782 vwrq->disabled = 0; /* Can't be disabled */
6783
6784 readConfigRid(local, 1);
6785 /* Note : by default, display the min retry number */
6786 if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
6787 vwrq->flags = IW_RETRY_LIFETIME;
Al Viro3eb9b412007-12-21 00:00:35 -05006788 vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024;
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006789 } else if((vwrq->flags & IW_RETRY_LONG)) {
6790 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
Al Viro3eb9b412007-12-21 00:00:35 -05006791 vwrq->value = le16_to_cpu(local->config.longRetryLimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006792 } else {
6793 vwrq->flags = IW_RETRY_LIMIT;
Al Viro3eb9b412007-12-21 00:00:35 -05006794 vwrq->value = le16_to_cpu(local->config.shortRetryLimit);
6795 if(local->config.shortRetryLimit != local->config.longRetryLimit)
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006796 vwrq->flags |= IW_RETRY_SHORT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006797 }
6798
6799 return 0;
6800}
6801
6802/*------------------------------------------------------------------*/
6803/*
6804 * Wireless Handler : get range info
6805 */
6806static int airo_get_range(struct net_device *dev,
6807 struct iw_request_info *info,
6808 struct iw_point *dwrq,
6809 char *extra)
6810{
Wang Chenfaf39942008-10-14 13:30:33 +08006811 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006812 struct iw_range *range = (struct iw_range *) extra;
6813 CapabilityRid cap_rid; /* Card capability info */
6814 int i;
6815 int k;
6816
6817 readCapabilityRid(local, &cap_rid, 1);
6818
6819 dwrq->length = sizeof(struct iw_range);
6820 memset(range, 0, sizeof(*range));
6821 range->min_nwid = 0x0000;
6822 range->max_nwid = 0x0000;
6823 range->num_channels = 14;
6824 /* Should be based on cap_rid.country to give only
6825 * what the current card support */
6826 k = 0;
6827 for(i = 0; i < 14; i++) {
6828 range->freq[k].i = i + 1; /* List index */
David Kilroy9ee677c2008-12-23 14:03:38 +00006829 range->freq[k].m = ieee80211_dsss_chan_to_freq(i + 1) * 100000;
6830 range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006831 }
6832 range->num_frequency = k;
6833
Linus Torvalds1da177e2005-04-16 15:20:36 -07006834 range->sensitivity = 65535;
6835
Dan Williams41480af2005-05-10 09:45:51 -04006836 /* Hum... Should put the right values there */
6837 if (local->rssi)
6838 range->max_qual.qual = 100; /* % */
6839 else
6840 range->max_qual.qual = airo_get_max_quality(&cap_rid);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006841 range->max_qual.level = 0x100 - 120; /* -120 dBm */
6842 range->max_qual.noise = 0x100 - 120; /* -120 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006843
6844 /* Experimental measurements - boundary 11/5.5 Mb/s */
6845 /* Note : with or without the (local->rssi), results
6846 * are somewhat different. - Jean II */
6847 if (local->rssi) {
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006848 range->avg_qual.qual = 50; /* % */
6849 range->avg_qual.level = 0x100 - 70; /* -70 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006850 } else {
6851 range->avg_qual.qual = airo_get_avg_quality(&cap_rid);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006852 range->avg_qual.level = 0x100 - 80; /* -80 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006853 }
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006854 range->avg_qual.noise = 0x100 - 85; /* -85 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006855
Linus Torvalds1da177e2005-04-16 15:20:36 -07006856 for(i = 0 ; i < 8 ; i++) {
6857 range->bitrate[i] = cap_rid.supportedRates[i] * 500000;
6858 if(range->bitrate[i] == 0)
6859 break;
6860 }
6861 range->num_bitrates = i;
6862
6863 /* Set an indication of the max TCP throughput
6864 * in bit/s that we can expect using this interface.
6865 * May be use for QoS stuff... Jean II */
6866 if(i > 2)
6867 range->throughput = 5000 * 1000;
6868 else
6869 range->throughput = 1500 * 1000;
6870
6871 range->min_rts = 0;
Dan Williams15db2762006-03-16 13:46:27 -05006872 range->max_rts = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006873 range->min_frag = 256;
Dan Williams15db2762006-03-16 13:46:27 -05006874 range->max_frag = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006875
Al Viro56d81bd2007-12-20 17:18:35 -05006876 if(cap_rid.softCap & cpu_to_le16(2)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006877 // WEP: RC4 40 bits
6878 range->encoding_size[0] = 5;
6879 // RC4 ~128 bits
Al Viro56d81bd2007-12-20 17:18:35 -05006880 if (cap_rid.softCap & cpu_to_le16(0x100)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006881 range->encoding_size[1] = 13;
6882 range->num_encoding_sizes = 2;
6883 } else
6884 range->num_encoding_sizes = 1;
Al Viro56d81bd2007-12-20 17:18:35 -05006885 range->max_encoding_tokens =
6886 cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006887 } else {
6888 range->num_encoding_sizes = 0;
6889 range->max_encoding_tokens = 0;
6890 }
6891 range->min_pmp = 0;
6892 range->max_pmp = 5000000; /* 5 secs */
6893 range->min_pmt = 0;
6894 range->max_pmt = 65535 * 1024; /* ??? */
6895 range->pmp_flags = IW_POWER_PERIOD;
6896 range->pmt_flags = IW_POWER_TIMEOUT;
6897 range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
6898
6899 /* Transmit Power - values are in mW */
6900 for(i = 0 ; i < 8 ; i++) {
Al Viro56d81bd2007-12-20 17:18:35 -05006901 range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006902 if(range->txpower[i] == 0)
6903 break;
6904 }
6905 range->num_txpower = i;
6906 range->txpower_capa = IW_TXPOW_MWATT;
Dan Williams3c304952006-04-15 12:26:18 -04006907 range->we_version_source = 19;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006908 range->we_version_compiled = WIRELESS_EXT;
6909 range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
6910 range->retry_flags = IW_RETRY_LIMIT;
6911 range->r_time_flags = IW_RETRY_LIFETIME;
6912 range->min_retry = 1;
6913 range->max_retry = 65535;
6914 range->min_r_time = 1024;
6915 range->max_r_time = 65535 * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006916
6917 /* Event capability (kernel + driver) */
6918 range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
6919 IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
6920 IW_EVENT_CAPA_MASK(SIOCGIWAP) |
6921 IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
6922 range->event_capa[1] = IW_EVENT_CAPA_K_1;
6923 range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP);
6924 return 0;
6925}
6926
6927/*------------------------------------------------------------------*/
6928/*
6929 * Wireless Handler : set Power Management
6930 */
6931static int airo_set_power(struct net_device *dev,
6932 struct iw_request_info *info,
6933 struct iw_param *vwrq,
6934 char *extra)
6935{
Wang Chenfaf39942008-10-14 13:30:33 +08006936 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006937
6938 readConfigRid(local, 1);
6939 if (vwrq->disabled) {
Al Viro3eb9b412007-12-21 00:00:35 -05006940 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006941 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006942 local->config.powerSaveMode = POWERSAVE_CAM;
Al Viro3eb9b412007-12-21 00:00:35 -05006943 local->config.rmode &= ~RXMODE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006944 local->config.rmode |= RXMODE_BC_MC_ADDR;
6945 set_bit (FLAG_COMMIT, &local->flags);
6946 return -EINPROGRESS; /* Call commit handler */
6947 }
6948 if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
Al Viro3eb9b412007-12-21 00:00:35 -05006949 local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006950 local->config.powerSaveMode = POWERSAVE_PSPCAM;
6951 set_bit (FLAG_COMMIT, &local->flags);
6952 } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
Al Viro3eb9b412007-12-21 00:00:35 -05006953 local->config.fastListenInterval =
6954 local->config.listenInterval =
6955 cpu_to_le16((vwrq->value + 500) / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006956 local->config.powerSaveMode = POWERSAVE_PSPCAM;
6957 set_bit (FLAG_COMMIT, &local->flags);
6958 }
6959 switch (vwrq->flags & IW_POWER_MODE) {
6960 case IW_POWER_UNICAST_R:
Al Viro3eb9b412007-12-21 00:00:35 -05006961 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006962 return -EINVAL;
Al Viro3eb9b412007-12-21 00:00:35 -05006963 local->config.rmode &= ~RXMODE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006964 local->config.rmode |= RXMODE_ADDR;
6965 set_bit (FLAG_COMMIT, &local->flags);
6966 break;
6967 case IW_POWER_ALL_R:
Al Viro3eb9b412007-12-21 00:00:35 -05006968 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006969 return -EINVAL;
Al Viro3eb9b412007-12-21 00:00:35 -05006970 local->config.rmode &= ~RXMODE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006971 local->config.rmode |= RXMODE_BC_MC_ADDR;
6972 set_bit (FLAG_COMMIT, &local->flags);
6973 case IW_POWER_ON:
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006974 /* This is broken, fixme ;-) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006975 break;
6976 default:
6977 return -EINVAL;
6978 }
6979 // Note : we may want to factor local->need_commit here
6980 // Note2 : may also want to factor RXMODE_RFMON test
6981 return -EINPROGRESS; /* Call commit handler */
6982}
6983
6984/*------------------------------------------------------------------*/
6985/*
6986 * Wireless Handler : get Power Management
6987 */
6988static int airo_get_power(struct net_device *dev,
6989 struct iw_request_info *info,
6990 struct iw_param *vwrq,
6991 char *extra)
6992{
Wang Chenfaf39942008-10-14 13:30:33 +08006993 struct airo_info *local = dev->ml_priv;
Al Viro3eb9b412007-12-21 00:00:35 -05006994 __le16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006995
6996 readConfigRid(local, 1);
6997 mode = local->config.powerSaveMode;
6998 if ((vwrq->disabled = (mode == POWERSAVE_CAM)))
6999 return 0;
7000 if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
Al Viro3eb9b412007-12-21 00:00:35 -05007001 vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007002 vwrq->flags = IW_POWER_TIMEOUT;
7003 } else {
Al Viro3eb9b412007-12-21 00:00:35 -05007004 vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007005 vwrq->flags = IW_POWER_PERIOD;
7006 }
Al Viro3eb9b412007-12-21 00:00:35 -05007007 if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007008 vwrq->flags |= IW_POWER_UNICAST_R;
7009 else
7010 vwrq->flags |= IW_POWER_ALL_R;
7011
7012 return 0;
7013}
7014
7015/*------------------------------------------------------------------*/
7016/*
7017 * Wireless Handler : set Sensitivity
7018 */
7019static int airo_set_sens(struct net_device *dev,
7020 struct iw_request_info *info,
7021 struct iw_param *vwrq,
7022 char *extra)
7023{
Wang Chenfaf39942008-10-14 13:30:33 +08007024 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007025
7026 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05007027 local->config.rssiThreshold =
7028 cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007029 set_bit (FLAG_COMMIT, &local->flags);
7030
7031 return -EINPROGRESS; /* Call commit handler */
7032}
7033
7034/*------------------------------------------------------------------*/
7035/*
7036 * Wireless Handler : get Sensitivity
7037 */
7038static int airo_get_sens(struct net_device *dev,
7039 struct iw_request_info *info,
7040 struct iw_param *vwrq,
7041 char *extra)
7042{
Wang Chenfaf39942008-10-14 13:30:33 +08007043 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007044
7045 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05007046 vwrq->value = le16_to_cpu(local->config.rssiThreshold);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007047 vwrq->disabled = (vwrq->value == 0);
7048 vwrq->fixed = 1;
7049
7050 return 0;
7051}
7052
7053/*------------------------------------------------------------------*/
7054/*
7055 * Wireless Handler : get AP List
7056 * Note : this is deprecated in favor of IWSCAN
7057 */
7058static int airo_get_aplist(struct net_device *dev,
7059 struct iw_request_info *info,
7060 struct iw_point *dwrq,
7061 char *extra)
7062{
Wang Chenfaf39942008-10-14 13:30:33 +08007063 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007064 struct sockaddr *address = (struct sockaddr *) extra;
7065 struct iw_quality qual[IW_MAX_AP];
7066 BSSListRid BSSList;
7067 int i;
7068 int loseSync = capable(CAP_NET_ADMIN) ? 1: -1;
7069
7070 for (i = 0; i < IW_MAX_AP; i++) {
Al Viro17e70492007-12-19 18:56:37 -05007071 u16 dBm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007072 if (readBSSListRid(local, loseSync, &BSSList))
7073 break;
7074 loseSync = 0;
7075 memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN);
7076 address[i].sa_family = ARPHRD_ETHER;
Al Viro17e70492007-12-19 18:56:37 -05007077 dBm = le16_to_cpu(BSSList.dBm);
Dan Williams41480af2005-05-10 09:45:51 -04007078 if (local->rssi) {
Al Viro17e70492007-12-19 18:56:37 -05007079 qual[i].level = 0x100 - dBm;
7080 qual[i].qual = airo_dbm_to_pct(local->rssi, dBm);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007081 qual[i].updated = IW_QUAL_QUAL_UPDATED
7082 | IW_QUAL_LEVEL_UPDATED
7083 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007084 } else {
Al Viro17e70492007-12-19 18:56:37 -05007085 qual[i].level = (dBm + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007086 qual[i].qual = 0;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007087 qual[i].updated = IW_QUAL_QUAL_INVALID
7088 | IW_QUAL_LEVEL_UPDATED
7089 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007090 }
7091 qual[i].noise = local->wstats.qual.noise;
Al Viro17e70492007-12-19 18:56:37 -05007092 if (BSSList.index == cpu_to_le16(0xffff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007093 break;
7094 }
7095 if (!i) {
7096 StatusRid status_rid; /* Card status info */
7097 readStatusRid(local, &status_rid, 1);
7098 for (i = 0;
7099 i < min(IW_MAX_AP, 4) &&
7100 (status_rid.bssid[i][0]
7101 & status_rid.bssid[i][1]
7102 & status_rid.bssid[i][2]
7103 & status_rid.bssid[i][3]
7104 & status_rid.bssid[i][4]
7105 & status_rid.bssid[i][5])!=0xff &&
7106 (status_rid.bssid[i][0]
7107 | status_rid.bssid[i][1]
7108 | status_rid.bssid[i][2]
7109 | status_rid.bssid[i][3]
7110 | status_rid.bssid[i][4]
7111 | status_rid.bssid[i][5]);
7112 i++) {
7113 memcpy(address[i].sa_data,
7114 status_rid.bssid[i], ETH_ALEN);
7115 address[i].sa_family = ARPHRD_ETHER;
7116 }
7117 } else {
7118 dwrq->flags = 1; /* Should be define'd */
7119 memcpy(extra + sizeof(struct sockaddr)*i,
7120 &qual, sizeof(struct iw_quality)*i);
7121 }
7122 dwrq->length = i;
7123
7124 return 0;
7125}
7126
7127/*------------------------------------------------------------------*/
7128/*
7129 * Wireless Handler : Initiate Scan
7130 */
7131static int airo_set_scan(struct net_device *dev,
7132 struct iw_request_info *info,
David Kilroy9930cce2008-09-13 12:22:05 +01007133 struct iw_point *dwrq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007134 char *extra)
7135{
Wang Chenfaf39942008-10-14 13:30:33 +08007136 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007137 Cmd cmd;
7138 Resp rsp;
Dan Williams9e75af32006-03-16 13:46:29 -05007139 int wake = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007140
7141 /* Note : you may have realised that, as this is a SET operation,
7142 * this is privileged and therefore a normal user can't
7143 * perform scanning.
7144 * This is not an error, while the device perform scanning,
7145 * traffic doesn't flow, so it's a perfect DoS...
7146 * Jean II */
7147 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
7148
Dan Williams9e75af32006-03-16 13:46:29 -05007149 if (down_interruptible(&ai->sem))
7150 return -ERESTARTSYS;
7151
7152 /* If there's already a scan in progress, don't
7153 * trigger another one. */
7154 if (ai->scan_timeout > 0)
7155 goto out;
7156
Linus Torvalds1da177e2005-04-16 15:20:36 -07007157 /* Initiate a scan command */
Dan Williams6fcdf562006-03-31 15:08:46 -05007158 ai->scan_timeout = RUN_AT(3*HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007159 memset(&cmd, 0, sizeof(cmd));
7160 cmd.cmd=CMD_LISTBSS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007161 issuecommand(ai, &cmd, &rsp);
Dan Williams9e75af32006-03-16 13:46:29 -05007162 wake = 1;
7163
7164out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07007165 up(&ai->sem);
Dan Williams9e75af32006-03-16 13:46:29 -05007166 if (wake)
7167 wake_up_interruptible(&ai->thr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007168 return 0;
7169}
7170
7171/*------------------------------------------------------------------*/
7172/*
7173 * Translate scan data returned from the card to a card independent
7174 * format that the Wireless Tools will understand - Jean II
7175 */
7176static inline char *airo_translate_scan(struct net_device *dev,
David S. Millerccc58052008-06-16 18:50:49 -07007177 struct iw_request_info *info,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007178 char *current_ev,
7179 char *end_buf,
Dan Williams41480af2005-05-10 09:45:51 -04007180 BSSListRid *bss)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007181{
Wang Chenfaf39942008-10-14 13:30:33 +08007182 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007183 struct iw_event iwe; /* Temporary buffer */
Al Viro17e70492007-12-19 18:56:37 -05007184 __le16 capabilities;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007185 char * current_val; /* For rates */
7186 int i;
Dan Williams3c304952006-04-15 12:26:18 -04007187 char * buf;
Al Viro851b3e52007-12-19 19:20:12 -05007188 u16 dBm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007189
7190 /* First entry *MUST* be the AP MAC address */
7191 iwe.cmd = SIOCGIWAP;
7192 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
Dan Williams41480af2005-05-10 09:45:51 -04007193 memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -07007194 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7195 &iwe, IW_EV_ADDR_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007196
7197 /* Other entries will be displayed in the order we give them */
7198
7199 /* Add the ESSID */
Dan Williams41480af2005-05-10 09:45:51 -04007200 iwe.u.data.length = bss->ssidLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007201 if(iwe.u.data.length > 32)
7202 iwe.u.data.length = 32;
7203 iwe.cmd = SIOCGIWESSID;
7204 iwe.u.data.flags = 1;
David S. Millerccc58052008-06-16 18:50:49 -07007205 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7206 &iwe, bss->ssid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007207
7208 /* Add mode */
7209 iwe.cmd = SIOCGIWMODE;
Al Viro17e70492007-12-19 18:56:37 -05007210 capabilities = bss->cap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007211 if(capabilities & (CAP_ESS | CAP_IBSS)) {
7212 if(capabilities & CAP_ESS)
7213 iwe.u.mode = IW_MODE_MASTER;
7214 else
7215 iwe.u.mode = IW_MODE_ADHOC;
David S. Millerccc58052008-06-16 18:50:49 -07007216 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7217 &iwe, IW_EV_UINT_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007218 }
7219
7220 /* Add frequency */
7221 iwe.cmd = SIOCGIWFREQ;
Dan Williams41480af2005-05-10 09:45:51 -04007222 iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
David Kilroy9ee677c2008-12-23 14:03:38 +00007223 iwe.u.freq.m = ieee80211_dsss_chan_to_freq(iwe.u.freq.m) * 100000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007224 iwe.u.freq.e = 1;
David S. Millerccc58052008-06-16 18:50:49 -07007225 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7226 &iwe, IW_EV_FREQ_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007227
Al Viro851b3e52007-12-19 19:20:12 -05007228 dBm = le16_to_cpu(bss->dBm);
7229
Linus Torvalds1da177e2005-04-16 15:20:36 -07007230 /* Add quality statistics */
7231 iwe.cmd = IWEVQUAL;
Dan Williams41480af2005-05-10 09:45:51 -04007232 if (ai->rssi) {
Al Viro851b3e52007-12-19 19:20:12 -05007233 iwe.u.qual.level = 0x100 - dBm;
7234 iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007235 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
7236 | IW_QUAL_LEVEL_UPDATED
7237 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007238 } else {
Al Viro851b3e52007-12-19 19:20:12 -05007239 iwe.u.qual.level = (dBm + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007240 iwe.u.qual.qual = 0;
Jeff Garzikbbeec902005-09-07 00:27:54 -04007241 iwe.u.qual.updated = IW_QUAL_QUAL_INVALID
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007242 | IW_QUAL_LEVEL_UPDATED
7243 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007244 }
7245 iwe.u.qual.noise = ai->wstats.qual.noise;
David S. Millerccc58052008-06-16 18:50:49 -07007246 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7247 &iwe, IW_EV_QUAL_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007248
7249 /* Add encryption capability */
7250 iwe.cmd = SIOCGIWENCODE;
7251 if(capabilities & CAP_PRIVACY)
7252 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
7253 else
7254 iwe.u.data.flags = IW_ENCODE_DISABLED;
7255 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -07007256 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7257 &iwe, bss->ssid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007258
7259 /* Rate : stuffing multiple values in a single event require a bit
7260 * more of magic - Jean II */
David S. Millerccc58052008-06-16 18:50:49 -07007261 current_val = current_ev + iwe_stream_lcp_len(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007262
7263 iwe.cmd = SIOCGIWRATE;
7264 /* Those two flags are ignored... */
7265 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
7266 /* Max 8 values */
7267 for(i = 0 ; i < 8 ; i++) {
7268 /* NULL terminated */
Dan Williams41480af2005-05-10 09:45:51 -04007269 if(bss->rates[i] == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007270 break;
7271 /* Bit rate given in 500 kb/s units (+ 0x80) */
Dan Williams41480af2005-05-10 09:45:51 -04007272 iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007273 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -07007274 current_val = iwe_stream_add_value(info, current_ev,
7275 current_val, end_buf,
7276 &iwe, IW_EV_PARAM_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007277 }
7278 /* Check if we added any event */
David S. Millerccc58052008-06-16 18:50:49 -07007279 if ((current_val - current_ev) > iwe_stream_lcp_len(info))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007280 current_ev = current_val;
7281
Dan Williams3c304952006-04-15 12:26:18 -04007282 /* Beacon interval */
7283 buf = kmalloc(30, GFP_KERNEL);
7284 if (buf) {
7285 iwe.cmd = IWEVCUSTOM;
7286 sprintf(buf, "bcn_int=%d", bss->beaconInterval);
7287 iwe.u.data.length = strlen(buf);
David S. Millerccc58052008-06-16 18:50:49 -07007288 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7289 &iwe, buf);
Dan Williams3c304952006-04-15 12:26:18 -04007290 kfree(buf);
7291 }
7292
7293 /* Put WPA/RSN Information Elements into the event stream */
7294 if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
7295 unsigned int num_null_ies = 0;
7296 u16 length = sizeof (bss->extra.iep);
Johannes Berg2c7060022008-10-30 22:09:54 +01007297 u8 *ie = (void *)&bss->extra.iep;
Dan Williams3c304952006-04-15 12:26:18 -04007298
Johannes Berg2c7060022008-10-30 22:09:54 +01007299 while ((length >= 2) && (num_null_ies < 2)) {
7300 if (2 + ie[1] > length) {
Dan Williams3c304952006-04-15 12:26:18 -04007301 /* Invalid element, don't continue parsing IE */
7302 break;
7303 }
7304
Johannes Berg2c7060022008-10-30 22:09:54 +01007305 switch (ie[0]) {
7306 case WLAN_EID_SSID:
Dan Williams3c304952006-04-15 12:26:18 -04007307 /* Two zero-length SSID elements
7308 * mean we're done parsing elements */
Johannes Berg2c7060022008-10-30 22:09:54 +01007309 if (!ie[1])
Dan Williams3c304952006-04-15 12:26:18 -04007310 num_null_ies++;
7311 break;
7312
Johannes Berg2c7060022008-10-30 22:09:54 +01007313 case WLAN_EID_GENERIC:
7314 if (ie[1] >= 4 &&
7315 ie[2] == 0x00 &&
7316 ie[3] == 0x50 &&
7317 ie[4] == 0xf2 &&
7318 ie[5] == 0x01) {
Dan Williams3c304952006-04-15 12:26:18 -04007319 iwe.cmd = IWEVGENIE;
Johannes Berg2c7060022008-10-30 22:09:54 +01007320 /* 64 is an arbitrary cut-off */
7321 iwe.u.data.length = min(ie[1] + 2,
7322 64);
David S. Millerccc58052008-06-16 18:50:49 -07007323 current_ev = iwe_stream_add_point(
7324 info, current_ev,
Johannes Berg2c7060022008-10-30 22:09:54 +01007325 end_buf, &iwe, ie);
Dan Williams3c304952006-04-15 12:26:18 -04007326 }
7327 break;
7328
Johannes Berg2c7060022008-10-30 22:09:54 +01007329 case WLAN_EID_RSN:
Dan Williams3c304952006-04-15 12:26:18 -04007330 iwe.cmd = IWEVGENIE;
Johannes Berg2c7060022008-10-30 22:09:54 +01007331 /* 64 is an arbitrary cut-off */
7332 iwe.u.data.length = min(ie[1] + 2, 64);
David S. Millerccc58052008-06-16 18:50:49 -07007333 current_ev = iwe_stream_add_point(
7334 info, current_ev, end_buf,
Johannes Berg2c7060022008-10-30 22:09:54 +01007335 &iwe, ie);
Dan Williams3c304952006-04-15 12:26:18 -04007336 break;
7337
7338 default:
7339 break;
7340 }
7341
Johannes Berg2c7060022008-10-30 22:09:54 +01007342 length -= 2 + ie[1];
7343 ie += 2 + ie[1];
Dan Williams3c304952006-04-15 12:26:18 -04007344 }
7345 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07007346 return current_ev;
7347}
7348
7349/*------------------------------------------------------------------*/
7350/*
7351 * Wireless Handler : Read Scan Results
7352 */
7353static int airo_get_scan(struct net_device *dev,
7354 struct iw_request_info *info,
7355 struct iw_point *dwrq,
7356 char *extra)
7357{
Wang Chenfaf39942008-10-14 13:30:33 +08007358 struct airo_info *ai = dev->ml_priv;
Dan Williams9e75af32006-03-16 13:46:29 -05007359 BSSListElement *net;
7360 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007361 char *current_ev = extra;
7362
Dan Williams9e75af32006-03-16 13:46:29 -05007363 /* If a scan is in-progress, return -EAGAIN */
7364 if (ai->scan_timeout > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007365 return -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007366
Dan Williams9e75af32006-03-16 13:46:29 -05007367 if (down_interruptible(&ai->sem))
7368 return -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007369
Dan Williams9e75af32006-03-16 13:46:29 -05007370 list_for_each_entry (net, &ai->network_list, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007371 /* Translate to WE format this entry */
David S. Millerccc58052008-06-16 18:50:49 -07007372 current_ev = airo_translate_scan(dev, info, current_ev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007373 extra + dwrq->length,
Dan Williams9e75af32006-03-16 13:46:29 -05007374 &net->bss);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007375
7376 /* Check if there is space for one more entry */
7377 if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
7378 /* Ask user space to try again with a bigger buffer */
Dan Williams9e75af32006-03-16 13:46:29 -05007379 err = -E2BIG;
7380 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007381 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07007382 }
Dan Williams9e75af32006-03-16 13:46:29 -05007383
Linus Torvalds1da177e2005-04-16 15:20:36 -07007384 /* Length of data */
7385 dwrq->length = (current_ev - extra);
7386 dwrq->flags = 0; /* todo */
7387
Dan Williams9e75af32006-03-16 13:46:29 -05007388out:
7389 up(&ai->sem);
7390 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007391}
7392
7393/*------------------------------------------------------------------*/
7394/*
7395 * Commit handler : called after a bunch of SET operations
7396 */
7397static int airo_config_commit(struct net_device *dev,
7398 struct iw_request_info *info, /* NULL */
7399 void *zwrq, /* NULL */
7400 char *extra) /* NULL */
7401{
Wang Chenfaf39942008-10-14 13:30:33 +08007402 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007403
7404 if (!test_bit (FLAG_COMMIT, &local->flags))
7405 return 0;
7406
7407 /* Some of the "SET" function may have modified some of the
7408 * parameters. It's now time to commit them in the card */
7409 disable_MAC(local, 1);
7410 if (test_bit (FLAG_RESET, &local->flags)) {
7411 APListRid APList_rid;
7412 SsidRid SSID_rid;
7413
7414 readAPListRid(local, &APList_rid);
7415 readSsidRid(local, &SSID_rid);
7416 if (test_bit(FLAG_MPI,&local->flags))
7417 setup_card(local, dev->dev_addr, 1 );
7418 else
7419 reset_airo_card(dev);
7420 disable_MAC(local, 1);
7421 writeSsidRid(local, &SSID_rid, 1);
7422 writeAPListRid(local, &APList_rid, 1);
7423 }
7424 if (down_interruptible(&local->sem))
7425 return -ERESTARTSYS;
7426 writeConfigRid(local, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007427 enable_MAC(local, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007428 if (test_bit (FLAG_RESET, &local->flags))
7429 airo_set_promisc(local);
7430 else
7431 up(&local->sem);
7432
7433 return 0;
7434}
7435
7436/*------------------------------------------------------------------*/
7437/*
7438 * Structures to export the Wireless Handlers
7439 */
7440
7441static const struct iw_priv_args airo_private_args[] = {
7442/*{ cmd, set_args, get_args, name } */
7443 { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
7444 IW_PRIV_TYPE_BYTE | 2047, "airoioctl" },
7445 { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
7446 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" },
7447};
7448
7449static const iw_handler airo_handler[] =
7450{
7451 (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */
7452 (iw_handler) airo_get_name, /* SIOCGIWNAME */
7453 (iw_handler) NULL, /* SIOCSIWNWID */
7454 (iw_handler) NULL, /* SIOCGIWNWID */
7455 (iw_handler) airo_set_freq, /* SIOCSIWFREQ */
7456 (iw_handler) airo_get_freq, /* SIOCGIWFREQ */
7457 (iw_handler) airo_set_mode, /* SIOCSIWMODE */
7458 (iw_handler) airo_get_mode, /* SIOCGIWMODE */
7459 (iw_handler) airo_set_sens, /* SIOCSIWSENS */
7460 (iw_handler) airo_get_sens, /* SIOCGIWSENS */
7461 (iw_handler) NULL, /* SIOCSIWRANGE */
7462 (iw_handler) airo_get_range, /* SIOCGIWRANGE */
7463 (iw_handler) NULL, /* SIOCSIWPRIV */
7464 (iw_handler) NULL, /* SIOCGIWPRIV */
7465 (iw_handler) NULL, /* SIOCSIWSTATS */
7466 (iw_handler) NULL, /* SIOCGIWSTATS */
7467 iw_handler_set_spy, /* SIOCSIWSPY */
7468 iw_handler_get_spy, /* SIOCGIWSPY */
7469 iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
7470 iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
7471 (iw_handler) airo_set_wap, /* SIOCSIWAP */
7472 (iw_handler) airo_get_wap, /* SIOCGIWAP */
7473 (iw_handler) NULL, /* -- hole -- */
7474 (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */
7475 (iw_handler) airo_set_scan, /* SIOCSIWSCAN */
7476 (iw_handler) airo_get_scan, /* SIOCGIWSCAN */
7477 (iw_handler) airo_set_essid, /* SIOCSIWESSID */
7478 (iw_handler) airo_get_essid, /* SIOCGIWESSID */
7479 (iw_handler) airo_set_nick, /* SIOCSIWNICKN */
7480 (iw_handler) airo_get_nick, /* SIOCGIWNICKN */
7481 (iw_handler) NULL, /* -- hole -- */
7482 (iw_handler) NULL, /* -- hole -- */
7483 (iw_handler) airo_set_rate, /* SIOCSIWRATE */
7484 (iw_handler) airo_get_rate, /* SIOCGIWRATE */
7485 (iw_handler) airo_set_rts, /* SIOCSIWRTS */
7486 (iw_handler) airo_get_rts, /* SIOCGIWRTS */
7487 (iw_handler) airo_set_frag, /* SIOCSIWFRAG */
7488 (iw_handler) airo_get_frag, /* SIOCGIWFRAG */
7489 (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */
7490 (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */
7491 (iw_handler) airo_set_retry, /* SIOCSIWRETRY */
7492 (iw_handler) airo_get_retry, /* SIOCGIWRETRY */
7493 (iw_handler) airo_set_encode, /* SIOCSIWENCODE */
7494 (iw_handler) airo_get_encode, /* SIOCGIWENCODE */
7495 (iw_handler) airo_set_power, /* SIOCSIWPOWER */
7496 (iw_handler) airo_get_power, /* SIOCGIWPOWER */
Dan Williams4be757d2006-01-30 11:58:00 -05007497 (iw_handler) NULL, /* -- hole -- */
7498 (iw_handler) NULL, /* -- hole -- */
7499 (iw_handler) NULL, /* SIOCSIWGENIE */
7500 (iw_handler) NULL, /* SIOCGIWGENIE */
7501 (iw_handler) airo_set_auth, /* SIOCSIWAUTH */
7502 (iw_handler) airo_get_auth, /* SIOCGIWAUTH */
7503 (iw_handler) airo_set_encodeext, /* SIOCSIWENCODEEXT */
7504 (iw_handler) airo_get_encodeext, /* SIOCGIWENCODEEXT */
7505 (iw_handler) NULL, /* SIOCSIWPMKSA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07007506};
7507
7508/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here.
7509 * We want to force the use of the ioctl code, because those can't be
7510 * won't work the iw_handler code (because they simultaneously read
7511 * and write data and iw_handler can't do that).
7512 * Note that it's perfectly legal to read/write on a single ioctl command,
7513 * you just can't use iwpriv and need to force it via the ioctl handler.
7514 * Jean II */
7515static const iw_handler airo_private_handler[] =
7516{
7517 NULL, /* SIOCIWFIRSTPRIV */
7518};
7519
7520static const struct iw_handler_def airo_handler_def =
7521{
Denis Chengff8ac602007-09-02 18:30:18 +08007522 .num_standard = ARRAY_SIZE(airo_handler),
7523 .num_private = ARRAY_SIZE(airo_private_handler),
7524 .num_private_args = ARRAY_SIZE(airo_private_args),
Linus Torvalds1da177e2005-04-16 15:20:36 -07007525 .standard = airo_handler,
7526 .private = airo_private_handler,
7527 .private_args = airo_private_args,
7528 .get_wireless_stats = airo_get_wireless_stats,
7529};
7530
Linus Torvalds1da177e2005-04-16 15:20:36 -07007531/*
7532 * This defines the configuration part of the Wireless Extensions
7533 * Note : irq and spinlock protection will occur in the subroutines
7534 *
7535 * TODO :
7536 * o Check input value more carefully and fill correct values in range
7537 * o Test and shakeout the bugs (if any)
7538 *
7539 * Jean II
7540 *
7541 * Javier Achirica did a great job of merging code from the unnamed CISCO
7542 * developer that added support for flashing the card.
7543 */
7544static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
7545{
7546 int rc = 0;
Wang Chenfaf39942008-10-14 13:30:33 +08007547 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007548
Pavel Machekca078ba2005-09-03 15:56:57 -07007549 if (ai->power.event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007550 return 0;
7551
7552 switch (cmd) {
7553#ifdef CISCO_EXT
7554 case AIROIDIFC:
7555#ifdef AIROOLDIDIFC
7556 case AIROOLDIDIFC:
7557#endif
7558 {
7559 int val = AIROMAGIC;
7560 aironet_ioctl com;
7561 if (copy_from_user(&com,rq->ifr_data,sizeof(com)))
7562 rc = -EFAULT;
7563 else if (copy_to_user(com.data,(char *)&val,sizeof(val)))
7564 rc = -EFAULT;
7565 }
7566 break;
7567
7568 case AIROIOCTL:
7569#ifdef AIROOLDIOCTL
7570 case AIROOLDIOCTL:
7571#endif
7572 /* Get the command struct and hand it off for evaluation by
7573 * the proper subfunction
7574 */
7575 {
7576 aironet_ioctl com;
7577 if (copy_from_user(&com,rq->ifr_data,sizeof(com))) {
7578 rc = -EFAULT;
7579 break;
7580 }
7581
7582 /* Separate R/W functions bracket legality here
7583 */
7584 if ( com.command == AIRORSWVERSION ) {
7585 if (copy_to_user(com.data, swversion, sizeof(swversion)))
7586 rc = -EFAULT;
7587 else
7588 rc = 0;
7589 }
7590 else if ( com.command <= AIRORRID)
7591 rc = readrids(dev,&com);
7592 else if ( com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2) )
7593 rc = writerids(dev,&com);
7594 else if ( com.command >= AIROFLSHRST && com.command <= AIRORESTART )
7595 rc = flashcard(dev,&com);
7596 else
7597 rc = -EINVAL; /* Bad command in ioctl */
7598 }
7599 break;
7600#endif /* CISCO_EXT */
7601
7602 // All other calls are currently unsupported
7603 default:
7604 rc = -EOPNOTSUPP;
7605 }
7606 return rc;
7607}
7608
Linus Torvalds1da177e2005-04-16 15:20:36 -07007609/*
7610 * Get the Wireless stats out of the driver
7611 * Note : irq and spinlock protection will occur in the subroutines
7612 *
7613 * TODO :
7614 * o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs)
7615 *
7616 * Jean
7617 */
7618static void airo_read_wireless_stats(struct airo_info *local)
7619{
7620 StatusRid status_rid;
7621 StatsRid stats_rid;
7622 CapabilityRid cap_rid;
Al Viroa23ace52007-12-19 22:24:16 -05007623 __le32 *vals = stats_rid.vals;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007624
7625 /* Get stats out of the card */
Dan Williams3c304952006-04-15 12:26:18 -04007626 clear_bit(JOB_WSTATS, &local->jobs);
Pavel Machekca078ba2005-09-03 15:56:57 -07007627 if (local->power.event) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007628 up(&local->sem);
7629 return;
7630 }
7631 readCapabilityRid(local, &cap_rid, 0);
7632 readStatusRid(local, &status_rid, 0);
7633 readStatsRid(local, &stats_rid, RID_STATS, 0);
7634 up(&local->sem);
7635
7636 /* The status */
Al Viro329e2c02007-12-20 22:58:57 -05007637 local->wstats.status = le16_to_cpu(status_rid.mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007638
Dan Williams41480af2005-05-10 09:45:51 -04007639 /* Signal quality and co */
7640 if (local->rssi) {
Al Viro329e2c02007-12-20 22:58:57 -05007641 local->wstats.qual.level =
7642 airo_rssi_to_dbm(local->rssi,
7643 le16_to_cpu(status_rid.sigQuality));
Dan Williams41480af2005-05-10 09:45:51 -04007644 /* normalizedSignalStrength appears to be a percentage */
Al Viro329e2c02007-12-20 22:58:57 -05007645 local->wstats.qual.qual =
7646 le16_to_cpu(status_rid.normalizedSignalStrength);
Dan Williams41480af2005-05-10 09:45:51 -04007647 } else {
Al Viro329e2c02007-12-20 22:58:57 -05007648 local->wstats.qual.level =
7649 (le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007650 local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid);
7651 }
Al Viro329e2c02007-12-20 22:58:57 -05007652 if (le16_to_cpu(status_rid.len) >= 124) {
Dan Williams41480af2005-05-10 09:45:51 -04007653 local->wstats.qual.noise = 0x100 - status_rid.noisedBm;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007654 local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007655 } else {
7656 local->wstats.qual.noise = 0;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007657 local->wstats.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007658 }
7659
7660 /* Packets discarded in the wireless adapter due to wireless
7661 * specific problems */
Al Viroa23ace52007-12-19 22:24:16 -05007662 local->wstats.discard.nwid = le32_to_cpu(vals[56]) +
7663 le32_to_cpu(vals[57]) +
7664 le32_to_cpu(vals[58]); /* SSID Mismatch */
7665 local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */
7666 local->wstats.discard.fragment = le32_to_cpu(vals[30]);
7667 local->wstats.discard.retries = le32_to_cpu(vals[10]);
7668 local->wstats.discard.misc = le32_to_cpu(vals[1]) +
7669 le32_to_cpu(vals[32]);
7670 local->wstats.miss.beacon = le32_to_cpu(vals[34]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007671}
7672
Jouni Malinenff1d2762005-05-12 22:54:16 -04007673static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007674{
Wang Chenfaf39942008-10-14 13:30:33 +08007675 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007676
Dan Williams3c304952006-04-15 12:26:18 -04007677 if (!test_bit(JOB_WSTATS, &local->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007678 /* Get stats out of the card if available */
7679 if (down_trylock(&local->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04007680 set_bit(JOB_WSTATS, &local->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007681 wake_up_interruptible(&local->thr_wait);
7682 } else
7683 airo_read_wireless_stats(local);
7684 }
7685
7686 return &local->wstats;
7687}
Linus Torvalds1da177e2005-04-16 15:20:36 -07007688
7689#ifdef CISCO_EXT
7690/*
7691 * This just translates from driver IOCTL codes to the command codes to
7692 * feed to the radio's host interface. Things can be added/deleted
7693 * as needed. This represents the READ side of control I/O to
7694 * the card
7695 */
7696static int readrids(struct net_device *dev, aironet_ioctl *comp) {
7697 unsigned short ridcode;
7698 unsigned char *iobuf;
7699 int len;
Wang Chenfaf39942008-10-14 13:30:33 +08007700 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007701
7702 if (test_bit(FLAG_FLASHING, &ai->flags))
7703 return -EIO;
7704
7705 switch(comp->command)
7706 {
7707 case AIROGCAP: ridcode = RID_CAPABILITIES; break;
7708 case AIROGCFG: ridcode = RID_CONFIG;
7709 if (test_bit(FLAG_COMMIT, &ai->flags)) {
7710 disable_MAC (ai, 1);
7711 writeConfigRid (ai, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007712 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007713 }
7714 break;
7715 case AIROGSLIST: ridcode = RID_SSID; break;
7716 case AIROGVLIST: ridcode = RID_APLIST; break;
7717 case AIROGDRVNAM: ridcode = RID_DRVNAME; break;
7718 case AIROGEHTENC: ridcode = RID_ETHERENCAP; break;
7719 case AIROGWEPKTMP: ridcode = RID_WEP_TEMP;
7720 /* Only super-user can read WEP keys */
7721 if (!capable(CAP_NET_ADMIN))
7722 return -EPERM;
7723 break;
7724 case AIROGWEPKNV: ridcode = RID_WEP_PERM;
7725 /* Only super-user can read WEP keys */
7726 if (!capable(CAP_NET_ADMIN))
7727 return -EPERM;
7728 break;
7729 case AIROGSTAT: ridcode = RID_STATUS; break;
7730 case AIROGSTATSD32: ridcode = RID_STATSDELTA; break;
7731 case AIROGSTATSC32: ridcode = RID_STATS; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007732 case AIROGMICSTATS:
7733 if (copy_to_user(comp->data, &ai->micstats,
7734 min((int)comp->len,(int)sizeof(ai->micstats))))
7735 return -EFAULT;
7736 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007737 case AIRORRID: ridcode = comp->ridnum; break;
7738 default:
7739 return -EINVAL;
7740 break;
7741 }
7742
7743 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7744 return -ENOMEM;
7745
7746 PC4500_readrid(ai,ridcode,iobuf,RIDSIZE, 1);
7747 /* get the count of bytes in the rid docs say 1st 2 bytes is it.
7748 * then return it to the user
7749 * 9/22/2000 Honor user given length
7750 */
7751 len = comp->len;
7752
7753 if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) {
7754 kfree (iobuf);
7755 return -EFAULT;
7756 }
7757 kfree (iobuf);
7758 return 0;
7759}
7760
7761/*
7762 * Danger Will Robinson write the rids here
7763 */
7764
7765static int writerids(struct net_device *dev, aironet_ioctl *comp) {
Wang Chenfaf39942008-10-14 13:30:33 +08007766 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007767 int ridcode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007768 int enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007769 static int (* writer)(struct airo_info *, u16 rid, const void *, int, int);
7770 unsigned char *iobuf;
7771
7772 /* Only super-user can write RIDs */
7773 if (!capable(CAP_NET_ADMIN))
7774 return -EPERM;
7775
7776 if (test_bit(FLAG_FLASHING, &ai->flags))
7777 return -EIO;
7778
7779 ridcode = 0;
7780 writer = do_writerid;
7781
7782 switch(comp->command)
7783 {
7784 case AIROPSIDS: ridcode = RID_SSID; break;
7785 case AIROPCAP: ridcode = RID_CAPABILITIES; break;
7786 case AIROPAPLIST: ridcode = RID_APLIST; break;
7787 case AIROPCFG: ai->config.len = 0;
7788 clear_bit(FLAG_COMMIT, &ai->flags);
7789 ridcode = RID_CONFIG; break;
7790 case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break;
7791 case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break;
7792 case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break;
7793 case AIROPWEPKEY: ridcode = RID_WEP_TEMP; writer = PC4500_writerid;
7794 break;
7795 case AIROPLEAPUSR+1: ridcode = 0xFF2A; break;
7796 case AIROPLEAPUSR+2: ridcode = 0xFF2B; break;
7797
7798 /* this is not really a rid but a command given to the card
7799 * same with MAC off
7800 */
7801 case AIROPMACON:
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007802 if (enable_MAC(ai, 1) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007803 return -EIO;
7804 return 0;
7805
7806 /*
7807 * Evidently this code in the airo driver does not get a symbol
7808 * as disable_MAC. it's probably so short the compiler does not gen one.
7809 */
7810 case AIROPMACOFF:
7811 disable_MAC(ai, 1);
7812 return 0;
7813
7814 /* This command merely clears the counts does not actually store any data
7815 * only reads rid. But as it changes the cards state, I put it in the
7816 * writerid routines.
7817 */
7818 case AIROPSTCLR:
7819 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7820 return -ENOMEM;
7821
7822 PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDSIZE, 1);
7823
Linus Torvalds1da177e2005-04-16 15:20:36 -07007824 enabled = ai->micstats.enabled;
7825 memset(&ai->micstats,0,sizeof(ai->micstats));
7826 ai->micstats.enabled = enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007827
7828 if (copy_to_user(comp->data, iobuf,
7829 min((int)comp->len, (int)RIDSIZE))) {
7830 kfree (iobuf);
7831 return -EFAULT;
7832 }
7833 kfree (iobuf);
7834 return 0;
7835
7836 default:
7837 return -EOPNOTSUPP; /* Blarg! */
7838 }
7839 if(comp->len > RIDSIZE)
7840 return -EINVAL;
7841
7842 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7843 return -ENOMEM;
7844
7845 if (copy_from_user(iobuf,comp->data,comp->len)) {
7846 kfree (iobuf);
7847 return -EFAULT;
7848 }
7849
7850 if (comp->command == AIROPCFG) {
7851 ConfigRid *cfg = (ConfigRid *)iobuf;
7852
7853 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags))
Al Viro3eb9b412007-12-21 00:00:35 -05007854 cfg->opmode |= MODE_MIC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007855
Al Viro3eb9b412007-12-21 00:00:35 -05007856 if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007857 set_bit (FLAG_ADHOC, &ai->flags);
7858 else
7859 clear_bit (FLAG_ADHOC, &ai->flags);
7860 }
7861
7862 if((*writer)(ai, ridcode, iobuf,comp->len,1)) {
7863 kfree (iobuf);
7864 return -EIO;
7865 }
7866 kfree (iobuf);
7867 return 0;
7868}
7869
7870/*****************************************************************************
7871 * Ancillary flash / mod functions much black magic lurkes here *
7872 *****************************************************************************
7873 */
7874
7875/*
7876 * Flash command switch table
7877 */
7878
Jouni Malinenff1d2762005-05-12 22:54:16 -04007879static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007880 int z;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007881
7882 /* Only super-user can modify flash */
7883 if (!capable(CAP_NET_ADMIN))
7884 return -EPERM;
7885
7886 switch(comp->command)
7887 {
7888 case AIROFLSHRST:
Wang Chenfaf39942008-10-14 13:30:33 +08007889 return cmdreset((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007890
7891 case AIROFLSHSTFL:
Wang Chenfaf39942008-10-14 13:30:33 +08007892 if (!AIRO_FLASH(dev) &&
7893 (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007894 return -ENOMEM;
Wang Chenfaf39942008-10-14 13:30:33 +08007895 return setflashmode((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007896
7897 case AIROFLSHGCHR: /* Get char from aux */
7898 if(comp->len != sizeof(int))
7899 return -EINVAL;
7900 if (copy_from_user(&z,comp->data,comp->len))
7901 return -EFAULT;
Wang Chenfaf39942008-10-14 13:30:33 +08007902 return flashgchar((struct airo_info *)dev->ml_priv, z, 8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007903
7904 case AIROFLSHPCHR: /* Send char to card. */
7905 if(comp->len != sizeof(int))
7906 return -EINVAL;
7907 if (copy_from_user(&z,comp->data,comp->len))
7908 return -EFAULT;
Wang Chenfaf39942008-10-14 13:30:33 +08007909 return flashpchar((struct airo_info *)dev->ml_priv, z, 8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007910
7911 case AIROFLPUTBUF: /* Send 32k to card */
Wang Chenfaf39942008-10-14 13:30:33 +08007912 if (!AIRO_FLASH(dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007913 return -ENOMEM;
7914 if(comp->len > FLASHSIZE)
7915 return -EINVAL;
Wang Chenfaf39942008-10-14 13:30:33 +08007916 if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007917 return -EFAULT;
7918
Wang Chenfaf39942008-10-14 13:30:33 +08007919 flashputbuf((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007920 return 0;
7921
7922 case AIRORESTART:
Wang Chenfaf39942008-10-14 13:30:33 +08007923 if (flashrestart((struct airo_info *)dev->ml_priv, dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007924 return -EIO;
7925 return 0;
7926 }
7927 return -EINVAL;
7928}
7929
7930#define FLASH_COMMAND 0x7e7e
7931
7932/*
7933 * STEP 1)
7934 * Disable MAC and do soft reset on
7935 * card.
7936 */
7937
Jouni Malinenff1d2762005-05-12 22:54:16 -04007938static int cmdreset(struct airo_info *ai) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007939 disable_MAC(ai, 1);
7940
7941 if(!waitbusy (ai)){
Dan Williams934d8bf2006-03-16 13:46:23 -05007942 airo_print_info(ai->dev->name, "Waitbusy hang before RESET");
Linus Torvalds1da177e2005-04-16 15:20:36 -07007943 return -EBUSY;
7944 }
7945
7946 OUT4500(ai,COMMAND,CMD_SOFTRESET);
7947
7948 ssleep(1); /* WAS 600 12/7/00 */
7949
7950 if(!waitbusy (ai)){
Dan Williams934d8bf2006-03-16 13:46:23 -05007951 airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET");
Linus Torvalds1da177e2005-04-16 15:20:36 -07007952 return -EBUSY;
7953 }
7954 return 0;
7955}
7956
7957/* STEP 2)
7958 * Put the card in legendary flash
7959 * mode
7960 */
7961
Jouni Malinenff1d2762005-05-12 22:54:16 -04007962static int setflashmode (struct airo_info *ai) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007963 set_bit (FLAG_FLASHING, &ai->flags);
7964
7965 OUT4500(ai, SWS0, FLASH_COMMAND);
7966 OUT4500(ai, SWS1, FLASH_COMMAND);
7967 if (probe) {
7968 OUT4500(ai, SWS0, FLASH_COMMAND);
7969 OUT4500(ai, COMMAND,0x10);
7970 } else {
7971 OUT4500(ai, SWS2, FLASH_COMMAND);
7972 OUT4500(ai, SWS3, FLASH_COMMAND);
7973 OUT4500(ai, COMMAND,0);
7974 }
7975 msleep(500); /* 500ms delay */
7976
7977 if(!waitbusy(ai)) {
7978 clear_bit (FLAG_FLASHING, &ai->flags);
Dan Williams934d8bf2006-03-16 13:46:23 -05007979 airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode");
Linus Torvalds1da177e2005-04-16 15:20:36 -07007980 return -EIO;
7981 }
7982 return 0;
7983}
7984
7985/* Put character to SWS0 wait for dwelltime
7986 * x 50us for echo .
7987 */
7988
Jouni Malinenff1d2762005-05-12 22:54:16 -04007989static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007990 int echo;
7991 int waittime;
7992
7993 byte |= 0x8000;
7994
7995 if(dwelltime == 0 )
7996 dwelltime = 200;
7997
7998 waittime=dwelltime;
7999
8000 /* Wait for busy bit d15 to go false indicating buffer empty */
8001 while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) {
8002 udelay (50);
8003 waittime -= 50;
8004 }
8005
8006 /* timeout for busy clear wait */
8007 if(waittime <= 0 ){
Dan Williams934d8bf2006-03-16 13:46:23 -05008008 airo_print_info(ai->dev->name, "flash putchar busywait timeout!");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008009 return -EBUSY;
8010 }
8011
8012 /* Port is clear now write byte and wait for it to echo back */
8013 do {
8014 OUT4500(ai,SWS0,byte);
8015 udelay(50);
8016 dwelltime -= 50;
8017 echo = IN4500(ai,SWS1);
8018 } while (dwelltime >= 0 && echo != byte);
8019
8020 OUT4500(ai,SWS1,0);
8021
8022 return (echo == byte) ? 0 : -EIO;
8023}
8024
8025/*
8026 * Get a character from the card matching matchbyte
8027 * Step 3)
8028 */
Jouni Malinenff1d2762005-05-12 22:54:16 -04008029static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008030 int rchar;
8031 unsigned char rbyte=0;
8032
8033 do {
8034 rchar = IN4500(ai,SWS1);
8035
8036 if(dwelltime && !(0x8000 & rchar)){
8037 dwelltime -= 10;
8038 mdelay(10);
8039 continue;
8040 }
8041 rbyte = 0xff & rchar;
8042
8043 if( (rbyte == matchbyte) && (0x8000 & rchar) ){
8044 OUT4500(ai,SWS1,0);
8045 return 0;
8046 }
8047 if( rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar)
8048 break;
8049 OUT4500(ai,SWS1,0);
8050
8051 }while(dwelltime > 0);
8052 return -EIO;
8053}
8054
8055/*
8056 * Transfer 32k of firmware data from user buffer to our buffer and
8057 * send to the card
8058 */
8059
Jouni Malinenff1d2762005-05-12 22:54:16 -04008060static int flashputbuf(struct airo_info *ai){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008061 int nwords;
8062
8063 /* Write stuff */
8064 if (test_bit(FLAG_MPI,&ai->flags))
8065 memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE);
8066 else {
8067 OUT4500(ai,AUXPAGE,0x100);
8068 OUT4500(ai,AUXOFF,0);
8069
8070 for(nwords=0;nwords != FLASHSIZE / 2;nwords++){
8071 OUT4500(ai,AUXDATA,ai->flash[nwords] & 0xffff);
8072 }
8073 }
8074 OUT4500(ai,SWS0,0x8000);
8075
8076 return 0;
8077}
8078
8079/*
8080 *
8081 */
Jouni Malinenff1d2762005-05-12 22:54:16 -04008082static int flashrestart(struct airo_info *ai,struct net_device *dev){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008083 int i,status;
8084
8085 ssleep(1); /* Added 12/7/00 */
8086 clear_bit (FLAG_FLASHING, &ai->flags);
8087 if (test_bit(FLAG_MPI, &ai->flags)) {
8088 status = mpi_init_descriptors(ai);
8089 if (status != SUCCESS)
8090 return status;
8091 }
8092 status = setup_card(ai, dev->dev_addr, 1);
8093
8094 if (!test_bit(FLAG_MPI,&ai->flags))
8095 for( i = 0; i < MAX_FIDS; i++ ) {
8096 ai->fids[i] = transmit_allocate
Dan Williams15db2762006-03-16 13:46:27 -05008097 ( ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07008098 }
8099
8100 ssleep(1); /* Added 12/7/00 */
8101 return status;
8102}
8103#endif /* CISCO_EXT */
8104
8105/*
8106 This program is free software; you can redistribute it and/or
8107 modify it under the terms of the GNU General Public License
8108 as published by the Free Software Foundation; either version 2
8109 of the License, or (at your option) any later version.
8110
8111 This program is distributed in the hope that it will be useful,
8112 but WITHOUT ANY WARRANTY; without even the implied warranty of
8113 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8114 GNU General Public License for more details.
8115
8116 In addition:
8117
8118 Redistribution and use in source and binary forms, with or without
8119 modification, are permitted provided that the following conditions
8120 are met:
8121
8122 1. Redistributions of source code must retain the above copyright
8123 notice, this list of conditions and the following disclaimer.
8124 2. Redistributions in binary form must reproduce the above copyright
8125 notice, this list of conditions and the following disclaimer in the
8126 documentation and/or other materials provided with the distribution.
8127 3. The name of the author may not be used to endorse or promote
8128 products derived from this software without specific prior written
8129 permission.
8130
8131 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
8132 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8133 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
8134 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
8135 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
8136 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
8137 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
8138 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
8139 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
8140 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
8141 POSSIBILITY OF SUCH DAMAGE.
8142*/
8143
8144module_init(airo_init_module);
8145module_exit(airo_cleanup_module);