blob: 67001a86f24088a29cb2ae50b8d94e5661afbc2e [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>
Avinash kumar16847f42014-02-18 17:04:10 +053039#include <linux/io.h>
Al Viro593c2b92007-12-17 15:09:34 -050040#include <asm/unaligned.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42#include <linux/netdevice.h>
43#include <linux/etherdevice.h>
44#include <linux/skbuff.h>
45#include <linux/if_arp.h>
46#include <linux/ioport.h>
47#include <linux/pci.h>
Avinash kumar16847f42014-02-18 17:04:10 +053048#include <linux/uaccess.h>
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -070049#include <linux/kthread.h>
Nigel Cunningham7dfb7102006-12-06 20:34:23 -080050#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
Zhao, Gange0febf12014-02-18 21:35:57 +080052#include <net/cfg80211.h>
Randy Dunlap2bf9fa62010-02-12 13:02:23 -080053#include <net/iw_handler.h>
Johannes Berg2c7060022008-10-30 22:09:54 +010054
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
Benoit Taine9baa3c32014-08-08 15:56:03 +020060static const struct pci_device_id card_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 { 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,
Bill Pemberton04bfffb2012-12-03 09:56:27 -050081 .remove = airo_pci_remove,
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 .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 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
Pavel Macheke292c732008-06-25 12:25:53 +020091#define CISCO_EXT /* enable Cisco extensions */
Linus Torvalds1da177e2005-04-16 15:20:36 -070092#ifdef CISCO_EXT
93#include <linux/delay.h>
94#endif
95
Linus Torvalds1da177e2005-04-16 15:20:36 -070096/* Hack to do some power saving */
97#define POWER_ON_DOWN
98
99/* As you can see this list is HUGH!
100 I really don't know what a lot of these counts are about, but they
101 are all here for completeness. If the IGNLABEL macro is put in
102 infront of the label, that statistic will not be included in the list
103 of statistics in the /proc filesystem */
104
105#define IGNLABEL(comment) NULL
Stephen Hemminger7a374d82010-09-01 18:17:21 -0700106static const char *statsLabels[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 "RxOverrun",
108 IGNLABEL("RxPlcpCrcErr"),
109 IGNLABEL("RxPlcpFormatErr"),
110 IGNLABEL("RxPlcpLengthErr"),
111 "RxMacCrcErr",
112 "RxMacCrcOk",
113 "RxWepErr",
114 "RxWepOk",
115 "RetryLong",
116 "RetryShort",
117 "MaxRetries",
118 "NoAck",
119 "NoCts",
120 "RxAck",
121 "RxCts",
122 "TxAck",
123 "TxRts",
124 "TxCts",
125 "TxMc",
126 "TxBc",
127 "TxUcFrags",
128 "TxUcPackets",
129 "TxBeacon",
130 "RxBeacon",
131 "TxSinColl",
132 "TxMulColl",
133 "DefersNo",
134 "DefersProt",
135 "DefersEngy",
136 "DupFram",
137 "RxFragDisc",
138 "TxAged",
139 "RxAged",
140 "LostSync-MaxRetry",
141 "LostSync-MissedBeacons",
142 "LostSync-ArlExceeded",
143 "LostSync-Deauth",
144 "LostSync-Disassoced",
145 "LostSync-TsfTiming",
146 "HostTxMc",
147 "HostTxBc",
148 "HostTxUc",
149 "HostTxFail",
150 "HostRxMc",
151 "HostRxBc",
152 "HostRxUc",
153 "HostRxDiscard",
154 IGNLABEL("HmacTxMc"),
155 IGNLABEL("HmacTxBc"),
156 IGNLABEL("HmacTxUc"),
157 IGNLABEL("HmacTxFail"),
158 IGNLABEL("HmacRxMc"),
159 IGNLABEL("HmacRxBc"),
160 IGNLABEL("HmacRxUc"),
161 IGNLABEL("HmacRxDiscard"),
162 IGNLABEL("HmacRxAccepted"),
163 "SsidMismatch",
164 "ApMismatch",
165 "RatesMismatch",
166 "AuthReject",
167 "AuthTimeout",
168 "AssocReject",
169 "AssocTimeout",
170 IGNLABEL("ReasonOutsideTable"),
171 IGNLABEL("ReasonStatus1"),
172 IGNLABEL("ReasonStatus2"),
173 IGNLABEL("ReasonStatus3"),
174 IGNLABEL("ReasonStatus4"),
175 IGNLABEL("ReasonStatus5"),
176 IGNLABEL("ReasonStatus6"),
177 IGNLABEL("ReasonStatus7"),
178 IGNLABEL("ReasonStatus8"),
179 IGNLABEL("ReasonStatus9"),
180 IGNLABEL("ReasonStatus10"),
181 IGNLABEL("ReasonStatus11"),
182 IGNLABEL("ReasonStatus12"),
183 IGNLABEL("ReasonStatus13"),
184 IGNLABEL("ReasonStatus14"),
185 IGNLABEL("ReasonStatus15"),
186 IGNLABEL("ReasonStatus16"),
187 IGNLABEL("ReasonStatus17"),
188 IGNLABEL("ReasonStatus18"),
189 IGNLABEL("ReasonStatus19"),
190 "RxMan",
191 "TxMan",
192 "RxRefresh",
193 "TxRefresh",
194 "RxPoll",
195 "TxPoll",
196 "HostRetries",
197 "LostSync-HostReq",
198 "HostTxBytes",
199 "HostRxBytes",
200 "ElapsedUsec",
201 "ElapsedSec",
202 "LostSyncBetterAP",
203 "PrivacyMismatch",
204 "Jammed",
205 "DiscRxNotWepped",
206 "PhyEleMismatch",
207 (char*)-1 };
208#ifndef RUN_AT
209#define RUN_AT(x) (jiffies+(x))
210#endif
211
212
213/* These variables are for insmod, since it seems that the rates
214 can only be set in setup_card. Rates should be a comma separated
215 (no spaces) list of rates (up to 8). */
216
217static int rates[8];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218static char *ssids[3];
219
220static int io[4];
221static int irq[4];
222
223static
224int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at.
225 0 means no limit. For old cards this was 4 */
226
227static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */
228static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read
229 the bap, needed on some older cards and buses. */
230static int adhoc;
231
232static int probe = 1;
233
Eric W. Biederman1efa29c2012-02-10 14:01:03 -0800234static kuid_t proc_kuid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235static int proc_uid /* = 0 */;
236
Eric W. Biederman1efa29c2012-02-10 14:01:03 -0800237static kgid_t proc_kgid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238static int proc_gid /* = 0 */;
239
240static int airo_perm = 0555;
241
242static int proc_perm = 0644;
243
244MODULE_AUTHOR("Benjamin Reed");
Joe Perches85ee7a12011-04-23 20:38:19 -0700245MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet cards. "
246 "Direct support for ISA/PCI/MPI cards and support for PCMCIA when used with airo_cs.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247MODULE_LICENSE("Dual BSD/GPL");
248MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350");
249module_param_array(io, int, NULL, 0);
250module_param_array(irq, int, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251module_param_array(rates, int, NULL, 0);
252module_param_array(ssids, charp, NULL, 0);
253module_param(auto_wep, int, 0);
Joe Perches85ee7a12011-04-23 20:38:19 -0700254MODULE_PARM_DESC(auto_wep,
255 "If non-zero, the driver will keep looping through the authentication options until an association is made. "
256 "The value of auto_wep is number of the wep keys to check. "
257 "A value of 2 will try using the key at index 0 and index 1.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258module_param(aux_bap, int, 0);
Joe Perches85ee7a12011-04-23 20:38:19 -0700259MODULE_PARM_DESC(aux_bap,
260 "If non-zero, the driver will switch into a mode that seems to work better for older cards with some older buses. "
261 "Before switching it checks that the switch is needed.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262module_param(maxencrypt, int, 0);
Joe Perches85ee7a12011-04-23 20:38:19 -0700263MODULE_PARM_DESC(maxencrypt,
264 "The maximum speed that the card can do encryption. "
265 "Units are in 512kbs. "
266 "Zero (default) means there is no limit. "
267 "Older cards used to be limited to 2mbs (4).");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268module_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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499/* This structure came from an email sent to me from an engineer at
500 aironet for inclusion into this driver */
Dan Williams99590ff2009-01-24 09:12:58 -0500501typedef struct WepKeyRid WepKeyRid;
502struct WepKeyRid {
Al Viro4293ea32007-12-19 19:21:51 -0500503 __le16 len;
504 __le16 kindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 u8 mac[ETH_ALEN];
Al Viro4293ea32007-12-19 19:21:51 -0500506 __le16 klen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 u8 key[16];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000508} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
510/* These structures are from the Aironet's PC4500 Developers Manual */
Dan Williams99590ff2009-01-24 09:12:58 -0500511typedef struct Ssid Ssid;
512struct Ssid {
Al Viro0dd22122007-12-17 16:11:57 -0500513 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 u8 ssid[32];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000515} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
Dan Williams99590ff2009-01-24 09:12:58 -0500517typedef struct SsidRid SsidRid;
518struct SsidRid {
Al Viro0dd22122007-12-17 16:11:57 -0500519 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 Ssid ssids[3];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000521} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522
Dan Williams99590ff2009-01-24 09:12:58 -0500523typedef struct ModulationRid ModulationRid;
524struct ModulationRid {
Al Viro3eb9b412007-12-21 00:00:35 -0500525 __le16 len;
526 __le16 modulation;
527#define MOD_DEFAULT cpu_to_le16(0)
528#define MOD_CCK cpu_to_le16(1)
529#define MOD_MOK cpu_to_le16(2)
Eric Dumazetba2d3582010-06-02 18:10:09 +0000530} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531
Dan Williams99590ff2009-01-24 09:12:58 -0500532typedef struct ConfigRid ConfigRid;
533struct ConfigRid {
Al Viro3eb9b412007-12-21 00:00:35 -0500534 __le16 len; /* sizeof(ConfigRid) */
535 __le16 opmode; /* operating mode */
536#define MODE_STA_IBSS cpu_to_le16(0)
537#define MODE_STA_ESS cpu_to_le16(1)
538#define MODE_AP cpu_to_le16(2)
539#define MODE_AP_RPTR cpu_to_le16(3)
540#define MODE_CFG_MASK cpu_to_le16(0xff)
541#define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */
542#define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */
543#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extenstions */
544#define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */
545#define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */
546#define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */
547#define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */
548#define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */
549#define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */
550 __le16 rmode; /* receive mode */
551#define RXMODE_BC_MC_ADDR cpu_to_le16(0)
552#define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */
553#define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */
554#define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */
555#define RXMODE_RFMON_ANYBSS cpu_to_le16(4)
556#define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */
557#define RXMODE_MASK cpu_to_le16(255)
558#define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */
559#define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER)
560#define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */
561 __le16 fragThresh;
562 __le16 rtsThres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 u8 macAddr[ETH_ALEN];
564 u8 rates[8];
Al Viro3eb9b412007-12-21 00:00:35 -0500565 __le16 shortRetryLimit;
566 __le16 longRetryLimit;
567 __le16 txLifetime; /* in kusec */
568 __le16 rxLifetime; /* in kusec */
569 __le16 stationary;
570 __le16 ordering;
571 __le16 u16deviceType; /* for overriding device type */
572 __le16 cfpRate;
573 __le16 cfpDuration;
574 __le16 _reserved1[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 /*---------- Scanning/Associating ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500576 __le16 scanMode;
577#define SCANMODE_ACTIVE cpu_to_le16(0)
578#define SCANMODE_PASSIVE cpu_to_le16(1)
579#define SCANMODE_AIROSCAN cpu_to_le16(2)
580 __le16 probeDelay; /* in kusec */
581 __le16 probeEnergyTimeout; /* in kusec */
582 __le16 probeResponseTimeout;
583 __le16 beaconListenTimeout;
584 __le16 joinNetTimeout;
585 __le16 authTimeout;
586 __le16 authType;
587#define AUTH_OPEN cpu_to_le16(0x1)
588#define AUTH_ENCRYPT cpu_to_le16(0x101)
589#define AUTH_SHAREDKEY cpu_to_le16(0x102)
590#define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200)
591 __le16 associationTimeout;
592 __le16 specifiedApTimeout;
593 __le16 offlineScanInterval;
594 __le16 offlineScanDuration;
595 __le16 linkLossDelay;
596 __le16 maxBeaconLostTime;
597 __le16 refreshInterval;
598#define DISABLE_REFRESH cpu_to_le16(0xFFFF)
599 __le16 _reserved1a[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 /*---------- Power save operation ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500601 __le16 powerSaveMode;
602#define POWERSAVE_CAM cpu_to_le16(0)
603#define POWERSAVE_PSP cpu_to_le16(1)
604#define POWERSAVE_PSPCAM cpu_to_le16(2)
605 __le16 sleepForDtims;
606 __le16 listenInterval;
607 __le16 fastListenInterval;
608 __le16 listenDecay;
609 __le16 fastListenDelay;
610 __le16 _reserved2[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 /*---------- Ap/Ibss config items ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500612 __le16 beaconPeriod;
613 __le16 atimDuration;
614 __le16 hopPeriod;
615 __le16 channelSet;
616 __le16 channel;
617 __le16 dtimPeriod;
618 __le16 bridgeDistance;
619 __le16 radioID;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 /*---------- Radio configuration ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500621 __le16 radioType;
622#define RADIOTYPE_DEFAULT cpu_to_le16(0)
623#define RADIOTYPE_802_11 cpu_to_le16(1)
624#define RADIOTYPE_LEGACY cpu_to_le16(2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 u8 rxDiversity;
626 u8 txDiversity;
Al Viro3eb9b412007-12-21 00:00:35 -0500627 __le16 txPower;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628#define TXPOWER_DEFAULT 0
Al Viro3eb9b412007-12-21 00:00:35 -0500629 __le16 rssiThreshold;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630#define RSSI_DEFAULT 0
Al Viro3eb9b412007-12-21 00:00:35 -0500631 __le16 modulation;
632#define PREAMBLE_AUTO cpu_to_le16(0)
633#define PREAMBLE_LONG cpu_to_le16(1)
634#define PREAMBLE_SHORT cpu_to_le16(2)
635 __le16 preamble;
636 __le16 homeProduct;
637 __le16 radioSpecific;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 /*---------- Aironet Extensions ----------*/
639 u8 nodeName[16];
Al Viro3eb9b412007-12-21 00:00:35 -0500640 __le16 arlThreshold;
641 __le16 arlDecay;
642 __le16 arlDelay;
643 __le16 _reserved4[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 /*---------- Aironet Extensions ----------*/
645 u8 magicAction;
646#define MAGIC_ACTION_STSCHG 1
647#define MAGIC_ACTION_RESUME 2
648#define MAGIC_IGNORE_MCAST (1<<8)
649#define MAGIC_IGNORE_BCAST (1<<9)
650#define MAGIC_SWITCH_TO_PSP (0<<10)
651#define MAGIC_STAY_IN_CAM (1<<10)
652 u8 magicControl;
Al Viro3eb9b412007-12-21 00:00:35 -0500653 __le16 autoWake;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000654} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655
Dan Williams99590ff2009-01-24 09:12:58 -0500656typedef struct StatusRid StatusRid;
657struct StatusRid {
Al Viro329e2c02007-12-20 22:58:57 -0500658 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 u8 mac[ETH_ALEN];
Al Viro329e2c02007-12-20 22:58:57 -0500660 __le16 mode;
661 __le16 errorCode;
662 __le16 sigQuality;
663 __le16 SSIDlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 char SSID[32];
665 char apName[16];
666 u8 bssid[4][ETH_ALEN];
Al Viro329e2c02007-12-20 22:58:57 -0500667 __le16 beaconPeriod;
668 __le16 dimPeriod;
669 __le16 atimDuration;
670 __le16 hopPeriod;
671 __le16 channelSet;
672 __le16 channel;
673 __le16 hopsToBackbone;
674 __le16 apTotalLoad;
675 __le16 generatedLoad;
676 __le16 accumulatedArl;
677 __le16 signalQuality;
678 __le16 currentXmitRate;
679 __le16 apDevExtensions;
680 __le16 normalizedSignalStrength;
681 __le16 shortPreamble;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 u8 apIP[4];
683 u8 noisePercent; /* Noise percent in last second */
684 u8 noisedBm; /* Noise dBm in last second */
685 u8 noiseAvePercent; /* Noise percent in last minute */
686 u8 noiseAvedBm; /* Noise dBm in last minute */
687 u8 noiseMaxPercent; /* Highest noise percent in last minute */
688 u8 noiseMaxdBm; /* Highest noise dbm in last minute */
Al Viro329e2c02007-12-20 22:58:57 -0500689 __le16 load;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 u8 carrier[4];
Al Viro329e2c02007-12-20 22:58:57 -0500691 __le16 assocStatus;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692#define STAT_NOPACKETS 0
693#define STAT_NOCARRIERSET 10
694#define STAT_GOTCARRIERSET 11
695#define STAT_WRONGSSID 20
696#define STAT_BADCHANNEL 25
697#define STAT_BADBITRATES 30
698#define STAT_BADPRIVACY 35
699#define STAT_APFOUND 40
700#define STAT_APREJECTED 50
701#define STAT_AUTHENTICATING 60
702#define STAT_DEAUTHENTICATED 61
703#define STAT_AUTHTIMEOUT 62
704#define STAT_ASSOCIATING 70
705#define STAT_DEASSOCIATED 71
706#define STAT_ASSOCTIMEOUT 72
707#define STAT_NOTAIROAP 73
708#define STAT_ASSOCIATED 80
709#define STAT_LEAPING 90
710#define STAT_LEAPFAILED 91
711#define STAT_LEAPTIMEDOUT 92
712#define STAT_LEAPCOMPLETE 93
Eric Dumazetba2d3582010-06-02 18:10:09 +0000713} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Dan Williams99590ff2009-01-24 09:12:58 -0500715typedef struct StatsRid StatsRid;
716struct StatsRid {
Al Viroa23ace52007-12-19 22:24:16 -0500717 __le16 len;
718 __le16 spacer;
719 __le32 vals[100];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000720} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
Dan Williams99590ff2009-01-24 09:12:58 -0500722typedef struct APListRid APListRid;
723struct APListRid {
Al Viroa7497162007-12-20 17:49:41 -0500724 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 u8 ap[4][ETH_ALEN];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000726} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
Dan Williams99590ff2009-01-24 09:12:58 -0500728typedef struct CapabilityRid CapabilityRid;
729struct CapabilityRid {
Al Viro56d81bd2007-12-20 17:18:35 -0500730 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 char oui[3];
732 char zero;
Al Viro56d81bd2007-12-20 17:18:35 -0500733 __le16 prodNum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 char manName[32];
735 char prodName[16];
736 char prodVer[8];
737 char factoryAddr[ETH_ALEN];
738 char aironetAddr[ETH_ALEN];
Al Viro56d81bd2007-12-20 17:18:35 -0500739 __le16 radioType;
740 __le16 country;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 char callid[ETH_ALEN];
742 char supportedRates[8];
743 char rxDiversity;
744 char txDiversity;
Al Viro56d81bd2007-12-20 17:18:35 -0500745 __le16 txPowerLevels[8];
746 __le16 hardVer;
747 __le16 hardCap;
748 __le16 tempRange;
749 __le16 softVer;
750 __le16 softSubVer;
751 __le16 interfaceVer;
752 __le16 softCap;
753 __le16 bootBlockVer;
754 __le16 requiredHard;
755 __le16 extSoftCap;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000756} __packed;
Dan Williams3c304952006-04-15 12:26:18 -0400757
758/* Only present on firmware >= 5.30.17 */
Dan Williams99590ff2009-01-24 09:12:58 -0500759typedef struct BSSListRidExtra BSSListRidExtra;
760struct BSSListRidExtra {
Al Viro17e70492007-12-19 18:56:37 -0500761 __le16 unknown[4];
Dan Williams3c304952006-04-15 12:26:18 -0400762 u8 fixed[12]; /* WLAN management frame */
763 u8 iep[624];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000764} __packed;
Dan Williams3c304952006-04-15 12:26:18 -0400765
Dan Williams99590ff2009-01-24 09:12:58 -0500766typedef struct BSSListRid BSSListRid;
767struct BSSListRid {
Al Viro17e70492007-12-19 18:56:37 -0500768 __le16 len;
769 __le16 index; /* First is 0 and 0xffff means end of list */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770#define RADIO_FH 1 /* Frequency hopping radio type */
771#define RADIO_DS 2 /* Direct sequence radio type */
772#define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */
Al Viro17e70492007-12-19 18:56:37 -0500773 __le16 radioType;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 u8 bssid[ETH_ALEN]; /* Mac address of the BSS */
775 u8 zero;
776 u8 ssidLen;
777 u8 ssid[32];
Al Viro17e70492007-12-19 18:56:37 -0500778 __le16 dBm;
779#define CAP_ESS cpu_to_le16(1<<0)
780#define CAP_IBSS cpu_to_le16(1<<1)
781#define CAP_PRIVACY cpu_to_le16(1<<4)
782#define CAP_SHORTHDR cpu_to_le16(1<<5)
783 __le16 cap;
784 __le16 beaconInterval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 u8 rates[8]; /* Same as rates for config rid */
786 struct { /* For frequency hopping only */
Al Viro17e70492007-12-19 18:56:37 -0500787 __le16 dwell;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 u8 hopSet;
789 u8 hopPattern;
790 u8 hopIndex;
791 u8 fill;
792 } fh;
Al Viro17e70492007-12-19 18:56:37 -0500793 __le16 dsChannel;
794 __le16 atimWindow;
Dan Williams3c304952006-04-15 12:26:18 -0400795
796 /* Only present on firmware >= 5.30.17 */
797 BSSListRidExtra extra;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000798} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800typedef struct {
Dan Williams9e75af32006-03-16 13:46:29 -0500801 BSSListRid bss;
802 struct list_head list;
803} BSSListElement;
804
Dan Williams99590ff2009-01-24 09:12:58 -0500805typedef struct tdsRssiEntry tdsRssiEntry;
806struct tdsRssiEntry {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 u8 rssipct;
808 u8 rssidBm;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000809} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
Dan Williams99590ff2009-01-24 09:12:58 -0500811typedef struct tdsRssiRid tdsRssiRid;
812struct tdsRssiRid {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 u16 len;
814 tdsRssiEntry x[256];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000815} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
Dan Williams99590ff2009-01-24 09:12:58 -0500817typedef struct MICRid MICRid;
818struct MICRid {
819 __le16 len;
820 __le16 state;
821 __le16 multicastValid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 u8 multicast[16];
Dan Williams99590ff2009-01-24 09:12:58 -0500823 __le16 unicastValid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 u8 unicast[16];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000825} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
Dan Williams99590ff2009-01-24 09:12:58 -0500827typedef struct MICBuffer MICBuffer;
828struct MICBuffer {
Al Viro593c2b92007-12-17 15:09:34 -0500829 __be16 typelen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
831 union {
832 u8 snap[8];
833 struct {
834 u8 dsap;
835 u8 ssap;
836 u8 control;
837 u8 orgcode[3];
838 u8 fieldtype[2];
839 } llc;
840 } u;
Al Viro593c2b92007-12-17 15:09:34 -0500841 __be32 mic;
842 __be32 seq;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000843} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844
845typedef struct {
846 u8 da[ETH_ALEN];
847 u8 sa[ETH_ALEN];
848} etherHead;
849
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850#define TXCTL_TXOK (1<<1) /* report if tx is ok */
851#define TXCTL_TXEX (1<<2) /* report if tx fails */
852#define TXCTL_802_3 (0<<3) /* 802.3 packet */
853#define TXCTL_802_11 (1<<3) /* 802.11 mac packet */
854#define TXCTL_ETHERNET (0<<4) /* payload has ethertype */
855#define TXCTL_LLC (1<<4) /* payload is llc */
856#define TXCTL_RELEASE (0<<5) /* release after completion */
857#define TXCTL_NORELEASE (1<<5) /* on completion returns to host */
858
859#define BUSY_FID 0x10000
860
861#ifdef CISCO_EXT
862#define AIROMAGIC 0xa55a
863/* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */
864#ifdef SIOCIWFIRSTPRIV
865#ifdef SIOCDEVPRIVATE
866#define AIROOLDIOCTL SIOCDEVPRIVATE
867#define AIROOLDIDIFC AIROOLDIOCTL + 1
868#endif /* SIOCDEVPRIVATE */
869#else /* SIOCIWFIRSTPRIV */
870#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
871#endif /* SIOCIWFIRSTPRIV */
872/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably
873 * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root
874 * only and don't return the modified struct ifreq to the application which
875 * is usually a problem. - Jean II */
876#define AIROIOCTL SIOCIWFIRSTPRIV
877#define AIROIDIFC AIROIOCTL + 1
878
879/* Ioctl constants to be used in airo_ioctl.command */
880
881#define AIROGCAP 0 // Capability rid
882#define AIROGCFG 1 // USED A LOT
883#define AIROGSLIST 2 // System ID list
884#define AIROGVLIST 3 // List of specified AP's
885#define AIROGDRVNAM 4 // NOTUSED
886#define AIROGEHTENC 5 // NOTUSED
887#define AIROGWEPKTMP 6
888#define AIROGWEPKNV 7
889#define AIROGSTAT 8
890#define AIROGSTATSC32 9
891#define AIROGSTATSD32 10
892#define AIROGMICRID 11
893#define AIROGMICSTATS 12
894#define AIROGFLAGS 13
895#define AIROGID 14
896#define AIRORRID 15
897#define AIRORSWVERSION 17
898
899/* Leave gap of 40 commands after AIROGSTATSD32 for future */
900
901#define AIROPCAP AIROGSTATSD32 + 40
902#define AIROPVLIST AIROPCAP + 1
903#define AIROPSLIST AIROPVLIST + 1
904#define AIROPCFG AIROPSLIST + 1
905#define AIROPSIDS AIROPCFG + 1
906#define AIROPAPLIST AIROPSIDS + 1
907#define AIROPMACON AIROPAPLIST + 1 /* Enable mac */
908#define AIROPMACOFF AIROPMACON + 1 /* Disable mac */
909#define AIROPSTCLR AIROPMACOFF + 1
910#define AIROPWEPKEY AIROPSTCLR + 1
911#define AIROPWEPKEYNV AIROPWEPKEY + 1
912#define AIROPLEAPPWD AIROPWEPKEYNV + 1
913#define AIROPLEAPUSR AIROPLEAPPWD + 1
914
915/* Flash codes */
916
917#define AIROFLSHRST AIROPWEPKEYNV + 40
918#define AIROFLSHGCHR AIROFLSHRST + 1
919#define AIROFLSHSTFL AIROFLSHGCHR + 1
920#define AIROFLSHPCHR AIROFLSHSTFL + 1
921#define AIROFLPUTBUF AIROFLSHPCHR + 1
922#define AIRORESTART AIROFLPUTBUF + 1
923
924#define FLASHSIZE 32768
925#define AUXMEMSIZE (256 * 1024)
926
927typedef struct aironet_ioctl {
928 unsigned short command; // What to do
929 unsigned short len; // Len of data
930 unsigned short ridnum; // rid number
931 unsigned char __user *data; // d-data
932} aironet_ioctl;
933
Stephen Hemminger7a374d82010-09-01 18:17:21 -0700934static const char swversion[] = "2.1";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935#endif /* CISCO_EXT */
936
937#define NUM_MODULES 2
938#define MIC_MSGLEN_MAX 2400
939#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX
Dan Williams15db2762006-03-16 13:46:27 -0500940#define AIRO_DEF_MTU 2312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941
942typedef struct {
943 u32 size; // size
944 u8 enabled; // MIC enabled or not
945 u32 rxSuccess; // successful packets received
946 u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison
947 u32 rxNotMICed; // pkts dropped due to not being MIC'd
948 u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed
949 u32 rxWrongSequence; // pkts dropped due to sequence number violation
950 u32 reserve[32];
951} mic_statistics;
952
953typedef struct {
954 u32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2];
955 u64 accum; // accumulated mic, reduced to u32 in final()
956 int position; // current position (byte offset) in message
957 union {
958 u8 d8[4];
Al Viro593c2b92007-12-17 15:09:34 -0500959 __be32 d32;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 } part; // saves partial message word across update() calls
961} emmh32_context;
962
963typedef struct {
964 emmh32_context seed; // Context - the seed
965 u32 rx; // Received sequence number
966 u32 tx; // Tx sequence number
967 u32 window; // Start of window
968 u8 valid; // Flag to say if context is valid or not
969 u8 key[16];
970} miccntx;
971
972typedef struct {
973 miccntx mCtx; // Multicast context
974 miccntx uCtx; // Unicast context
975} mic_module;
976
977typedef struct {
978 unsigned int rid: 16;
979 unsigned int len: 15;
980 unsigned int valid: 1;
981 dma_addr_t host_addr;
982} Rid;
983
984typedef struct {
985 unsigned int offset: 15;
986 unsigned int eoc: 1;
987 unsigned int len: 15;
988 unsigned int valid: 1;
989 dma_addr_t host_addr;
990} TxFid;
991
Dan Williamsf55d4512009-01-24 09:04:12 -0500992struct rx_hdr {
993 __le16 status, len;
994 u8 rssi[2];
995 u8 rate;
996 u8 freq;
997 __le16 tmp[4];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000998} __packed;
Dan Williamsf55d4512009-01-24 09:04:12 -0500999
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000typedef struct {
1001 unsigned int ctl: 15;
1002 unsigned int rdy: 1;
1003 unsigned int len: 15;
1004 unsigned int valid: 1;
1005 dma_addr_t host_addr;
1006} RxFid;
1007
1008/*
1009 * Host receive descriptor
1010 */
1011typedef struct {
1012 unsigned char __iomem *card_ram_off; /* offset into card memory of the
1013 desc */
1014 RxFid rx_desc; /* card receive descriptor */
1015 char *virtual_host_addr; /* virtual address of host receive
1016 buffer */
1017 int pending;
1018} HostRxDesc;
1019
1020/*
1021 * Host transmit descriptor
1022 */
1023typedef struct {
1024 unsigned char __iomem *card_ram_off; /* offset into card memory of the
1025 desc */
1026 TxFid tx_desc; /* card transmit descriptor */
1027 char *virtual_host_addr; /* virtual address of host receive
1028 buffer */
1029 int pending;
1030} HostTxDesc;
1031
1032/*
1033 * Host RID descriptor
1034 */
1035typedef struct {
1036 unsigned char __iomem *card_ram_off; /* offset into card memory of the
1037 descriptor */
1038 Rid rid_desc; /* card RID descriptor */
1039 char *virtual_host_addr; /* virtual address of host receive
1040 buffer */
1041} HostRidDesc;
1042
1043typedef struct {
1044 u16 sw0;
1045 u16 sw1;
1046 u16 status;
1047 u16 len;
1048#define HOST_SET (1 << 0)
1049#define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */
1050#define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */
1051#define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */
1052#define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */
1053#define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */
1054#define HOST_CLR_AID (1 << 7) /* clear AID failure */
1055#define HOST_RTS (1 << 9) /* Force RTS use */
1056#define HOST_SHORT (1 << 10) /* Do short preamble */
1057 u16 ctl;
1058 u16 aid;
1059 u16 retries;
1060 u16 fill;
1061} TxCtlHdr;
1062
1063typedef struct {
1064 u16 ctl;
1065 u16 duration;
1066 char addr1[6];
1067 char addr2[6];
1068 char addr3[6];
1069 u16 seq;
1070 char addr4[6];
1071} WifiHdr;
1072
1073
1074typedef struct {
1075 TxCtlHdr ctlhdr;
1076 u16 fill1;
1077 u16 fill2;
1078 WifiHdr wifihdr;
1079 u16 gaplen;
1080 u16 status;
1081} WifiCtlHdr;
1082
Jouni Malinenff1d2762005-05-12 22:54:16 -04001083static WifiCtlHdr wifictlhdr8023 = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 .ctlhdr = {
1085 .ctl = HOST_DONT_RLSE,
1086 }
1087};
1088
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089// A few details needed for WEP (Wireless Equivalent Privacy)
1090#define MAX_KEY_SIZE 13 // 128 (?) bits
1091#define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP
1092typedef struct wep_key_t {
1093 u16 len;
1094 u8 key[16]; /* 40-bit and 104-bit keys */
1095} wep_key_t;
1096
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097/* List of Wireless Handlers (new API) */
1098static const struct iw_handler_def airo_handler_def;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099
1100static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)";
1101
1102struct airo_info;
1103
1104static int get_dec_u16( char *buffer, int *start, int limit );
1105static void OUT4500( struct airo_info *, u16 register, u16 value );
1106static unsigned short IN4500( struct airo_info *, u16 register );
1107static u16 setup_card(struct airo_info*, u8 *mac, int lock);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02001108static int enable_MAC(struct airo_info *ai, int lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109static void disable_MAC(struct airo_info *ai, int lock);
1110static void enable_interrupts(struct airo_info*);
1111static void disable_interrupts(struct airo_info*);
1112static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp);
1113static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001114static int aux_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 fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001118static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 int whichbap);
1120static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd);
1121static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock);
1122static int PC4500_writerid(struct airo_info*, u16 rid, const void
1123 *pBuf, int len, int lock);
1124static int do_writerid( struct airo_info*, u16 rid, const void *rid_data,
1125 int len, int dummy );
1126static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw);
1127static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket);
1128static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket);
1129
1130static int mpi_send_packet (struct net_device *dev);
1131static void mpi_unmap_card(struct pci_dev *pci);
1132static void mpi_receive_802_3(struct airo_info *ai);
1133static void mpi_receive_802_11(struct airo_info *ai);
1134static int waitbusy (struct airo_info *ai);
1135
David Howells7d12e782006-10-05 14:55:46 +01001136static irqreturn_t airo_interrupt( int irq, void* dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137static int airo_thread(void *data);
1138static void timer_func( struct net_device *dev );
1139static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001140static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141static void airo_read_wireless_stats (struct airo_info *local);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142#ifdef CISCO_EXT
1143static int readrids(struct net_device *dev, aironet_ioctl *comp);
1144static int writerids(struct net_device *dev, aironet_ioctl *comp);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001145static int flashcard(struct net_device *dev, aironet_ioctl *comp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146#endif /* CISCO_EXT */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147static void micinit(struct airo_info *ai);
1148static int micsetup(struct airo_info *ai);
1149static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len);
1150static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen);
1151
Dan Williams41480af2005-05-10 09:45:51 -04001152static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi);
1153static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm);
1154
Dan Williams9e75af32006-03-16 13:46:29 -05001155static void airo_networks_free(struct airo_info *ai);
1156
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157struct airo_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 struct net_device *dev;
Michal Schmidtaf5b5c9a2007-03-16 12:44:40 +01001159 struct list_head dev_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we
1161 use the high bit to mark whether it is in use. */
1162#define MAX_FIDS 6
1163#define MPI_MAX_FIDS 1
Hannes Eder9e05a2d2009-02-14 11:49:26 +00001164 u32 fids[MAX_FIDS];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 ConfigRid config;
1166 char keyindex; // Used with auto wep
1167 char defindex; // Used with auto wep
1168 struct proc_dir_entry *proc_entry;
1169 spinlock_t aux_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170#define FLAG_RADIO_OFF 0 /* User disabling of MAC */
1171#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */
1172#define FLAG_RADIO_MASK 0x03
1173#define FLAG_ENABLED 2
1174#define FLAG_ADHOC 3 /* Needed by MIC */
1175#define FLAG_MIC_CAPABLE 4
1176#define FLAG_UPDATE_MULTI 5
1177#define FLAG_UPDATE_UNI 6
1178#define FLAG_802_11 7
Dan Williams3c304952006-04-15 12:26:18 -04001179#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180#define FLAG_PENDING_XMIT 9
1181#define FLAG_PENDING_XMIT11 10
1182#define FLAG_MPI 11
1183#define FLAG_REGISTERED 12
1184#define FLAG_COMMIT 13
1185#define FLAG_RESET 14
1186#define FLAG_FLASHING 15
Dan Williams3c304952006-04-15 12:26:18 -04001187#define FLAG_WPA_CAPABLE 16
1188 unsigned long flags;
1189#define JOB_DIE 0
1190#define JOB_XMIT 1
1191#define JOB_XMIT11 2
1192#define JOB_STATS 3
1193#define JOB_PROMISC 4
1194#define JOB_MIC 5
1195#define JOB_EVENT 6
1196#define JOB_AUTOWEP 7
1197#define JOB_WSTATS 8
1198#define JOB_SCAN_RESULTS 9
1199 unsigned long jobs;
Al Virob8c06bc2007-12-19 17:55:43 -05001200 int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 int whichbap);
1202 unsigned short *flash;
1203 tdsRssiEntry *rssi;
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001204 struct task_struct *list_bss_task;
1205 struct task_struct *airo_thread_task;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 struct semaphore sem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 wait_queue_head_t thr_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 unsigned long expires;
1209 struct {
1210 struct sk_buff *skb;
1211 int fid;
1212 } xmit, xmit11;
1213 struct net_device *wifidev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 struct iw_statistics wstats; // wireless stats
Dan Williams9e75af32006-03-16 13:46:29 -05001215 unsigned long scan_timeout; /* Time scan should be read */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 struct iw_spy_data spy_data;
1217 struct iw_public_data wireless_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 /* MIC stuff */
Herbert Xuf12cc202006-08-22 20:36:13 +10001219 struct crypto_cipher *tfm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 mic_module mod[2];
1221 mic_statistics micstats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors
1223 HostTxDesc txfids[MPI_MAX_FIDS];
1224 HostRidDesc config_desc;
1225 unsigned long ridbus; // phys addr of config_desc
1226 struct sk_buff_head txq;// tx queue used by mpi350 code
1227 struct pci_dev *pci;
1228 unsigned char __iomem *pcimem;
1229 unsigned char __iomem *pciaux;
1230 unsigned char *shared;
1231 dma_addr_t shared_dma;
Pavel Machek1cc68ae2005-06-20 15:33:04 -07001232 pm_message_t power;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 SsidRid *SSID;
1234 APListRid *APList;
1235#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
1236 char proc_name[IFNAMSIZ];
Dan Williams9e75af32006-03-16 13:46:29 -05001237
Dan Williams138c0c62009-01-24 09:11:35 -05001238 int wep_capable;
1239 int max_wep_idx;
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02001240 int last_auth;
Dan Williams138c0c62009-01-24 09:11:35 -05001241
Dan Williams3c304952006-04-15 12:26:18 -04001242 /* WPA-related stuff */
1243 unsigned int bssListFirst;
1244 unsigned int bssListNext;
1245 unsigned int bssListRidLen;
1246
Dan Williams9e75af32006-03-16 13:46:29 -05001247 struct list_head network_list;
1248 struct list_head network_free_list;
1249 BSSListElement *networks;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250};
1251
Al Virob8c06bc2007-12-19 17:55:43 -05001252static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen,
1253 int whichbap)
1254{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 return ai->bap_read(ai, pu16Dst, bytelen, whichbap);
1256}
1257
1258static int setup_proc_entry( struct net_device *dev,
1259 struct airo_info *apriv );
1260static int takedown_proc_entry( struct net_device *dev,
1261 struct airo_info *apriv );
1262
Jouni Malinenff1d2762005-05-12 22:54:16 -04001263static int cmdreset(struct airo_info *ai);
1264static int setflashmode (struct airo_info *ai);
1265static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
1266static int flashputbuf(struct airo_info *ai);
1267static int flashrestart(struct airo_info *ai,struct net_device *dev);
1268
Dan Williams934d8bf2006-03-16 13:46:23 -05001269#define airo_print(type, name, fmt, args...) \
Michal Schmidt1138c372007-06-29 15:33:41 +02001270 printk(type DRV_NAME "(%s): " fmt "\n", name, ##args)
Dan Williams934d8bf2006-03-16 13:46:23 -05001271
1272#define airo_print_info(name, fmt, args...) \
1273 airo_print(KERN_INFO, name, fmt, ##args)
1274
1275#define airo_print_dbg(name, fmt, args...) \
1276 airo_print(KERN_DEBUG, name, fmt, ##args)
1277
1278#define airo_print_warn(name, fmt, args...) \
1279 airo_print(KERN_WARNING, name, fmt, ##args)
1280
1281#define airo_print_err(name, fmt, args...) \
1282 airo_print(KERN_ERR, name, fmt, ##args)
1283
Wang Chenfaf39942008-10-14 13:30:33 +08001284#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash)
Dan Williams934d8bf2006-03-16 13:46:23 -05001285
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286/***********************************************************************
1287 * MIC ROUTINES *
1288 ***********************************************************************
1289 */
1290
1291static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq);
1292static void MoveWindow(miccntx *context, u32 micSeq);
Herbert Xuf12cc202006-08-22 20:36:13 +10001293static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
1294 struct crypto_cipher *tfm);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001295static void emmh32_init(emmh32_context *context);
1296static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
1297static void emmh32_final(emmh32_context *context, u8 digest[4]);
1298static int flashpchar(struct airo_info *ai,int byte,int dwelltime);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299
Dan Williams018697d2009-01-24 09:13:32 -05001300static void age_mic_context(miccntx *cur, miccntx *old, u8 *key, int key_len,
1301 struct crypto_cipher *tfm)
1302{
1303 /* If the current MIC context is valid and its key is the same as
1304 * the MIC register, there's nothing to do.
1305 */
1306 if (cur->valid && (memcmp(cur->key, key, key_len) == 0))
1307 return;
1308
1309 /* Age current mic Context */
1310 memcpy(old, cur, sizeof(*cur));
1311
1312 /* Initialize new context */
1313 memcpy(cur->key, key, key_len);
1314 cur->window = 33; /* Window always points to the middle */
1315 cur->rx = 0; /* Rx Sequence numbers */
1316 cur->tx = 0; /* Tx sequence numbers */
1317 cur->valid = 1; /* Key is now valid */
1318
1319 /* Give key to mic seed */
1320 emmh32_setseed(&cur->seed, key, key_len, tfm);
1321}
1322
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323/* micinit - Initialize mic seed */
1324
1325static void micinit(struct airo_info *ai)
1326{
1327 MICRid mic_rid;
1328
Dan Williams3c304952006-04-15 12:26:18 -04001329 clear_bit(JOB_MIC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
1331 up(&ai->sem);
1332
Dan Williams018697d2009-01-24 09:13:32 -05001333 ai->micstats.enabled = (le16_to_cpu(mic_rid.state) & 0x00FF) ? 1 : 0;
1334 if (!ai->micstats.enabled) {
1335 /* So next time we have a valid key and mic is enabled, we will
1336 * update the sequence number if the key is the same as before.
1337 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 ai->mod[0].uCtx.valid = 0;
1339 ai->mod[0].mCtx.valid = 0;
Dan Williams018697d2009-01-24 09:13:32 -05001340 return;
1341 }
1342
1343 if (mic_rid.multicastValid) {
1344 age_mic_context(&ai->mod[0].mCtx, &ai->mod[1].mCtx,
1345 mic_rid.multicast, sizeof(mic_rid.multicast),
1346 ai->tfm);
1347 }
1348
1349 if (mic_rid.unicastValid) {
1350 age_mic_context(&ai->mod[0].uCtx, &ai->mod[1].uCtx,
1351 mic_rid.unicast, sizeof(mic_rid.unicast),
1352 ai->tfm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 }
1354}
1355
1356/* micsetup - Get ready for business */
1357
1358static int micsetup(struct airo_info *ai) {
1359 int i;
1360
1361 if (ai->tfm == NULL)
Herbert Xuf12cc202006-08-22 20:36:13 +10001362 ai->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
Herbert Xuf12cc202006-08-22 20:36:13 +10001364 if (IS_ERR(ai->tfm)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05001365 airo_print_err(ai->dev->name, "failed to load transform for AES");
Herbert Xuf12cc202006-08-22 20:36:13 +10001366 ai->tfm = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 return ERROR;
1368 }
1369
1370 for (i=0; i < NUM_MODULES; i++) {
1371 memset(&ai->mod[i].mCtx,0,sizeof(miccntx));
1372 memset(&ai->mod[i].uCtx,0,sizeof(miccntx));
1373 }
1374 return SUCCESS;
1375}
1376
Stephen Hemminger7a374d82010-09-01 18:17:21 -07001377static const u8 micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378
1379/*===========================================================================
1380 * Description: Mic a packet
1381 *
1382 * Inputs: etherHead * pointer to an 802.3 frame
1383 *
1384 * Returns: BOOLEAN if successful, otherwise false.
1385 * PacketTxLen will be updated with the mic'd packets size.
1386 *
1387 * Caveats: It is assumed that the frame buffer will already
1388 * be big enough to hold the largets mic message possible.
1389 * (No memory allocation is done here).
1390 *
1391 * Author: sbraneky (10/15/01)
1392 * Merciless hacks by rwilcher (1/14/02)
1393 */
1394
1395static int encapsulate(struct airo_info *ai ,etherHead *frame, MICBuffer *mic, int payLen)
1396{
1397 miccntx *context;
1398
1399 // Determine correct context
1400 // If not adhoc, always use unicast key
1401
1402 if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1))
1403 context = &ai->mod[0].mCtx;
1404 else
1405 context = &ai->mod[0].uCtx;
1406
1407 if (!context->valid)
1408 return ERROR;
1409
1410 mic->typelen = htons(payLen + 16); //Length of Mic'd packet
1411
1412 memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap
1413
1414 // Add Tx sequence
1415 mic->seq = htonl(context->tx);
1416 context->tx += 2;
1417
1418 emmh32_init(&context->seed); // Mic the packet
1419 emmh32_update(&context->seed,frame->da,ETH_ALEN * 2); // DA,SA
1420 emmh32_update(&context->seed,(u8*)&mic->typelen,10); // Type/Length and Snap
1421 emmh32_update(&context->seed,(u8*)&mic->seq,sizeof(mic->seq)); //SEQ
David S. Millerf4739842011-11-26 15:35:10 -05001422 emmh32_update(&context->seed,(u8*)(frame + 1),payLen); //payload
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 emmh32_final(&context->seed, (u8*)&mic->mic);
1424
1425 /* New Type/length ?????????? */
1426 mic->typelen = 0; //Let NIC know it could be an oversized packet
1427 return SUCCESS;
1428}
1429
1430typedef enum {
1431 NONE,
1432 NOMIC,
1433 NOMICPLUMMED,
1434 SEQUENCE,
1435 INCORRECTMIC,
1436} mic_error;
1437
1438/*===========================================================================
1439 * Description: Decapsulates a MIC'd packet and returns the 802.3 packet
1440 * (removes the MIC stuff) if packet is a valid packet.
1441 *
1442 * Inputs: etherHead pointer to the 802.3 packet
1443 *
1444 * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE
1445 *
1446 * Author: sbraneky (10/15/01)
1447 * Merciless hacks by rwilcher (1/14/02)
1448 *---------------------------------------------------------------------------
1449 */
1450
1451static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen)
1452{
1453 int i;
1454 u32 micSEQ;
1455 miccntx *context;
1456 u8 digest[4];
1457 mic_error micError = NONE;
1458
1459 // Check if the packet is a Mic'd packet
1460
1461 if (!ai->micstats.enabled) {
1462 //No Mic set or Mic OFF but we received a MIC'd packet.
1463 if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) {
1464 ai->micstats.rxMICPlummed++;
1465 return ERROR;
1466 }
1467 return SUCCESS;
1468 }
1469
1470 if (ntohs(mic->typelen) == 0x888E)
1471 return SUCCESS;
1472
1473 if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) {
1474 // Mic enabled but packet isn't Mic'd
1475 ai->micstats.rxMICPlummed++;
1476 return ERROR;
1477 }
1478
1479 micSEQ = ntohl(mic->seq); //store SEQ as CPU order
1480
1481 //At this point we a have a mic'd packet and mic is enabled
1482 //Now do the mic error checking.
1483
1484 //Receive seq must be odd
1485 if ( (micSEQ & 1) == 0 ) {
1486 ai->micstats.rxWrongSequence++;
1487 return ERROR;
1488 }
1489
1490 for (i = 0; i < NUM_MODULES; i++) {
1491 int mcast = eth->da[0] & 1;
1492 //Determine proper context
1493 context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx;
1494
1495 //Make sure context is valid
1496 if (!context->valid) {
1497 if (i == 0)
1498 micError = NOMICPLUMMED;
1499 continue;
1500 }
1501 //DeMic it
1502
1503 if (!mic->typelen)
1504 mic->typelen = htons(payLen + sizeof(MICBuffer) - 2);
1505
1506 emmh32_init(&context->seed);
1507 emmh32_update(&context->seed, eth->da, ETH_ALEN*2);
1508 emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap));
1509 emmh32_update(&context->seed, (u8 *)&mic->seq,sizeof(mic->seq));
David S. Millerf4739842011-11-26 15:35:10 -05001510 emmh32_update(&context->seed, (u8 *)(eth + 1),payLen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 //Calculate MIC
1512 emmh32_final(&context->seed, digest);
1513
1514 if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match
1515 //Invalid Mic
1516 if (i == 0)
1517 micError = INCORRECTMIC;
1518 continue;
1519 }
1520
1521 //Check Sequence number if mics pass
1522 if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) {
1523 ai->micstats.rxSuccess++;
1524 return SUCCESS;
1525 }
1526 if (i == 0)
1527 micError = SEQUENCE;
1528 }
1529
1530 // Update statistics
1531 switch (micError) {
1532 case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break;
1533 case SEQUENCE: ai->micstats.rxWrongSequence++; break;
1534 case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break;
1535 case NONE: break;
1536 case NOMIC: break;
1537 }
1538 return ERROR;
1539}
1540
1541/*===========================================================================
1542 * Description: Checks the Rx Seq number to make sure it is valid
1543 * and hasn't already been received
1544 *
1545 * Inputs: miccntx - mic context to check seq against
1546 * micSeq - the Mic seq number
1547 *
1548 * Returns: TRUE if valid otherwise FALSE.
1549 *
1550 * Author: sbraneky (10/15/01)
1551 * Merciless hacks by rwilcher (1/14/02)
1552 *---------------------------------------------------------------------------
1553 */
1554
1555static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq)
1556{
1557 u32 seq,index;
1558
1559 //Allow for the ap being rebooted - if it is then use the next
1560 //sequence number of the current sequence number - might go backwards
1561
1562 if (mcast) {
1563 if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) {
1564 clear_bit (FLAG_UPDATE_MULTI, &ai->flags);
1565 context->window = (micSeq > 33) ? micSeq : 33;
1566 context->rx = 0; // Reset rx
1567 }
1568 } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) {
1569 clear_bit (FLAG_UPDATE_UNI, &ai->flags);
1570 context->window = (micSeq > 33) ? micSeq : 33; // Move window
1571 context->rx = 0; // Reset rx
1572 }
1573
1574 //Make sequence number relative to START of window
1575 seq = micSeq - (context->window - 33);
1576
1577 //Too old of a SEQ number to check.
1578 if ((s32)seq < 0)
1579 return ERROR;
1580
1581 if ( seq > 64 ) {
1582 //Window is infinite forward
1583 MoveWindow(context,micSeq);
1584 return SUCCESS;
1585 }
1586
1587 // We are in the window. Now check the context rx bit to see if it was already sent
1588 seq >>= 1; //divide by 2 because we only have odd numbers
1589 index = 1 << seq; //Get an index number
1590
1591 if (!(context->rx & index)) {
1592 //micSEQ falls inside the window.
1593 //Add seqence number to the list of received numbers.
1594 context->rx |= index;
1595
1596 MoveWindow(context,micSeq);
1597
1598 return SUCCESS;
1599 }
1600 return ERROR;
1601}
1602
1603static void MoveWindow(miccntx *context, u32 micSeq)
1604{
1605 u32 shift;
1606
1607 //Move window if seq greater than the middle of the window
1608 if (micSeq > context->window) {
1609 shift = (micSeq - context->window) >> 1;
1610
1611 //Shift out old
1612 if (shift < 32)
1613 context->rx >>= shift;
1614 else
1615 context->rx = 0;
1616
1617 context->window = micSeq; //Move window
1618 }
1619}
1620
1621/*==============================================*/
1622/*========== EMMH ROUTINES ====================*/
1623/*==============================================*/
1624
1625/* mic accumulate */
1626#define MIC_ACCUM(val) \
1627 context->accum += (u64)(val) * context->coeff[coeff_position++];
1628
1629static unsigned char aes_counter[16];
1630
1631/* expand the key to fill the MMH coefficient array */
Herbert Xuf12cc202006-08-22 20:36:13 +10001632static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
1633 struct crypto_cipher *tfm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634{
1635 /* take the keying material, expand if necessary, truncate at 16-bytes */
1636 /* run through AES counter mode to generate context->coeff[] */
1637
1638 int i,j;
1639 u32 counter;
1640 u8 *cipher, plain[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
1642 crypto_cipher_setkey(tfm, pkey, 16);
1643 counter = 0;
Ahmed S. Darwishe7c04fd2007-02-05 18:58:29 +02001644 for (i = 0; i < ARRAY_SIZE(context->coeff); ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 aes_counter[15] = (u8)(counter >> 0);
1646 aes_counter[14] = (u8)(counter >> 8);
1647 aes_counter[13] = (u8)(counter >> 16);
1648 aes_counter[12] = (u8)(counter >> 24);
1649 counter++;
1650 memcpy (plain, aes_counter, 16);
Herbert Xuf12cc202006-08-22 20:36:13 +10001651 crypto_cipher_encrypt_one(tfm, plain, plain);
1652 cipher = plain;
Ahmed S. Darwishe7c04fd2007-02-05 18:58:29 +02001653 for (j = 0; (j < 16) && (i < ARRAY_SIZE(context->coeff)); ) {
Al Viro593c2b92007-12-17 15:09:34 -05001654 context->coeff[i++] = ntohl(*(__be32 *)&cipher[j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 j += 4;
1656 }
1657 }
1658}
1659
1660/* prepare for calculation of a new mic */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001661static void emmh32_init(emmh32_context *context)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662{
1663 /* prepare for new mic calculation */
1664 context->accum = 0;
1665 context->position = 0;
1666}
1667
1668/* add some bytes to the mic calculation */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001669static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670{
1671 int coeff_position, byte_position;
1672
1673 if (len == 0) return;
1674
1675 coeff_position = context->position >> 2;
1676
1677 /* deal with partial 32-bit word left over from last update */
1678 byte_position = context->position & 3;
1679 if (byte_position) {
1680 /* have a partial word in part to deal with */
1681 do {
1682 if (len == 0) return;
1683 context->part.d8[byte_position++] = *pOctets++;
1684 context->position++;
1685 len--;
1686 } while (byte_position < 4);
Al Viro593c2b92007-12-17 15:09:34 -05001687 MIC_ACCUM(ntohl(context->part.d32));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 }
1689
1690 /* deal with full 32-bit words */
1691 while (len >= 4) {
Al Viro593c2b92007-12-17 15:09:34 -05001692 MIC_ACCUM(ntohl(*(__be32 *)pOctets));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 context->position += 4;
1694 pOctets += 4;
1695 len -= 4;
1696 }
1697
1698 /* deal with partial 32-bit word that will be left over from this update */
1699 byte_position = 0;
1700 while (len > 0) {
1701 context->part.d8[byte_position++] = *pOctets++;
1702 context->position++;
1703 len--;
1704 }
1705}
1706
1707/* mask used to zero empty bytes for final partial word */
1708static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
1709
1710/* calculate the mic */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001711static void emmh32_final(emmh32_context *context, u8 digest[4])
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712{
1713 int coeff_position, byte_position;
1714 u32 val;
1715
1716 u64 sum, utmp;
1717 s64 stmp;
1718
1719 coeff_position = context->position >> 2;
1720
1721 /* deal with partial 32-bit word left over from last update */
1722 byte_position = context->position & 3;
1723 if (byte_position) {
1724 /* have a partial word in part to deal with */
Al Viro593c2b92007-12-17 15:09:34 -05001725 val = ntohl(context->part.d32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726 MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */
1727 }
1728
1729 /* reduce the accumulated u64 to a 32-bit MIC */
1730 sum = context->accum;
1731 stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15);
1732 utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15);
1733 sum = utmp & 0xffffffffLL;
1734 if (utmp > 0x10000000fLL)
1735 sum -= 15;
1736
1737 val = (u32)sum;
1738 digest[0] = (val>>24) & 0xFF;
1739 digest[1] = (val>>16) & 0xFF;
1740 digest[2] = (val>>8) & 0xFF;
1741 digest[3] = val & 0xFF;
1742}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743
1744static int readBSSListRid(struct airo_info *ai, int first,
Al Viro17e70492007-12-19 18:56:37 -05001745 BSSListRid *list)
1746{
Dan Williams3c304952006-04-15 12:26:18 -04001747 Cmd cmd;
1748 Resp rsp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749
1750 if (first == 1) {
Dan Williams3c304952006-04-15 12:26:18 -04001751 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
1752 memset(&cmd, 0, sizeof(cmd));
1753 cmd.cmd=CMD_LISTBSS;
1754 if (down_interruptible(&ai->sem))
1755 return -ERESTARTSYS;
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001756 ai->list_bss_task = current;
Dan Williams3c304952006-04-15 12:26:18 -04001757 issuecommand(ai, &cmd, &rsp);
1758 up(&ai->sem);
1759 /* Let the command take effect */
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001760 schedule_timeout_uninterruptible(3 * HZ);
1761 ai->list_bss_task = NULL;
Dan Williams3c304952006-04-15 12:26:18 -04001762 }
Al Viro17e70492007-12-19 18:56:37 -05001763 return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext,
Dan Williams3c304952006-04-15 12:26:18 -04001764 list, ai->bssListRidLen, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765}
1766
Al Viro4293ea32007-12-19 19:21:51 -05001767static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock)
1768{
1769 return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 wkr, sizeof(*wkr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772
Al Viro4293ea32007-12-19 19:21:51 -05001773static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock)
1774{
1775 int rc;
1776 rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock);
1777 if (rc!=SUCCESS)
1778 airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779 if (perm) {
Al Viro4293ea32007-12-19 19:21:51 -05001780 rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock);
1781 if (rc!=SUCCESS)
Dan Williams934d8bf2006-03-16 13:46:23 -05001782 airo_print_err(ai->dev->name, "WEP_PERM set %x", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 }
1784 return rc;
1785}
1786
Al Viro0dd22122007-12-17 16:11:57 -05001787static int readSsidRid(struct airo_info*ai, SsidRid *ssidr)
1788{
1789 return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791
Al Viro0dd22122007-12-17 16:11:57 -05001792static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock)
1793{
1794 return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795}
Al Viro0dd22122007-12-17 16:11:57 -05001796
Al Viro3eb9b412007-12-21 00:00:35 -05001797static int readConfigRid(struct airo_info *ai, int lock)
1798{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 ConfigRid cfg;
1801
1802 if (ai->config.len)
1803 return SUCCESS;
1804
1805 rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock);
1806 if (rc != SUCCESS)
1807 return rc;
1808
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 ai->config = cfg;
1810 return SUCCESS;
1811}
Al Viro3eb9b412007-12-21 00:00:35 -05001812
1813static inline void checkThrottle(struct airo_info *ai)
1814{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815 int i;
1816/* Old hardware had a limit on encryption speed */
1817 if (ai->config.authType != AUTH_OPEN && maxencrypt) {
1818 for(i=0; i<8; i++) {
1819 if (ai->config.rates[i] > maxencrypt) {
1820 ai->config.rates[i] = 0;
1821 }
1822 }
1823 }
1824}
Al Viro3eb9b412007-12-21 00:00:35 -05001825
1826static int writeConfigRid(struct airo_info *ai, int lock)
1827{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828 ConfigRid cfgr;
1829
1830 if (!test_bit (FLAG_COMMIT, &ai->flags))
1831 return SUCCESS;
1832
1833 clear_bit (FLAG_COMMIT, &ai->flags);
1834 clear_bit (FLAG_RESET, &ai->flags);
1835 checkThrottle(ai);
1836 cfgr = ai->config;
1837
Al Viro3eb9b412007-12-21 00:00:35 -05001838 if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839 set_bit(FLAG_ADHOC, &ai->flags);
1840 else
1841 clear_bit(FLAG_ADHOC, &ai->flags);
1842
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843 return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock);
1844}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845
Al Viro329e2c02007-12-20 22:58:57 -05001846static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock)
1847{
1848 return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849}
Al Viroa7497162007-12-20 17:49:41 -05001850
1851static int readAPListRid(struct airo_info *ai, APListRid *aplr)
1852{
1853 return PC4500_readrid(ai, RID_APLIST, aplr, sizeof(*aplr), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854}
Al Viroa7497162007-12-20 17:49:41 -05001855
1856static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock)
1857{
1858 return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860
Al Viro56d81bd2007-12-20 17:18:35 -05001861static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock)
1862{
1863 return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865
Al Viroa23ace52007-12-19 22:24:16 -05001866static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock)
1867{
1868 return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869}
1870
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001871static void try_auto_wep(struct airo_info *ai)
1872{
Dan Carpentere9a8e8e2012-03-05 21:10:07 +03001873 if (auto_wep && !test_bit(FLAG_RADIO_DOWN, &ai->flags)) {
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001874 ai->expires = RUN_AT(3*HZ);
1875 wake_up_interruptible(&ai->thr_wait);
1876 }
1877}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001879static int airo_open(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08001880 struct airo_info *ai = dev->ml_priv;
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001881 int rc = 0;
1882
1883 if (test_bit(FLAG_FLASHING, &ai->flags))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884 return -EIO;
1885
1886 /* Make sure the card is configured.
1887 * Wireless Extensions may postpone config changes until the card
1888 * is open (to pipeline changes and speed-up card setup). If
Lucas De Marchi25985ed2011-03-30 22:57:33 -03001889 * those changes are not yet committed, do it now - Jean II */
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001890 if (test_bit(FLAG_COMMIT, &ai->flags)) {
1891 disable_MAC(ai, 1);
1892 writeConfigRid(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 }
1894
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001895 if (ai->wifidev != dev) {
1896 clear_bit(JOB_DIE, &ai->jobs);
Kees Cookf1701682013-07-03 15:04:58 -07001897 ai->airo_thread_task = kthread_run(airo_thread, dev, "%s",
1898 dev->name);
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001899 if (IS_ERR(ai->airo_thread_task))
1900 return (int)PTR_ERR(ai->airo_thread_task);
1901
1902 rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED,
1903 dev->name, dev);
1904 if (rc) {
1905 airo_print_err(dev->name,
1906 "register interrupt %d failed, rc %d",
1907 dev->irq, rc);
1908 set_bit(JOB_DIE, &ai->jobs);
1909 kthread_stop(ai->airo_thread_task);
1910 return rc;
1911 }
1912
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 /* Power on the MAC controller (which may have been disabled) */
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001914 clear_bit(FLAG_RADIO_DOWN, &ai->flags);
1915 enable_interrupts(ai);
1916
1917 try_auto_wep(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 }
Michal Schmidt175ec1a2007-06-29 15:33:47 +02001919 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920
1921 netif_start_queue(dev);
1922 return 0;
1923}
1924
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +00001925static netdev_tx_t mpi_start_xmit(struct sk_buff *skb,
1926 struct net_device *dev)
1927{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 int npacks, pending;
1929 unsigned long flags;
Wang Chenfaf39942008-10-14 13:30:33 +08001930 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931
1932 if (!skb) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07001933 airo_print_err(dev->name, "%s: skb == NULL!",__func__);
Patrick McHardy6ed10652009-06-23 06:03:08 +00001934 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 }
1936 npacks = skb_queue_len (&ai->txq);
1937
1938 if (npacks >= MAXTXQ - 1) {
1939 netif_stop_queue (dev);
1940 if (npacks > MAXTXQ) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03001941 dev->stats.tx_fifo_errors++;
Patrick McHardy5b548142009-06-12 06:22:29 +00001942 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 }
1944 skb_queue_tail (&ai->txq, skb);
Patrick McHardy6ed10652009-06-23 06:03:08 +00001945 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 }
1947
1948 spin_lock_irqsave(&ai->aux_lock, flags);
1949 skb_queue_tail (&ai->txq, skb);
1950 pending = test_bit(FLAG_PENDING_XMIT, &ai->flags);
1951 spin_unlock_irqrestore(&ai->aux_lock,flags);
1952 netif_wake_queue (dev);
1953
1954 if (pending == 0) {
1955 set_bit(FLAG_PENDING_XMIT, &ai->flags);
1956 mpi_send_packet (dev);
1957 }
Patrick McHardy6ed10652009-06-23 06:03:08 +00001958 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959}
1960
1961/*
1962 * @mpi_send_packet
1963 *
1964 * Attempt to transmit a packet. Can be called from interrupt
1965 * or transmit . return number of packets we tried to send
1966 */
1967
1968static int mpi_send_packet (struct net_device *dev)
1969{
1970 struct sk_buff *skb;
1971 unsigned char *buffer;
Al Viro593c2b92007-12-17 15:09:34 -05001972 s16 len;
1973 __le16 *payloadLen;
Wang Chenfaf39942008-10-14 13:30:33 +08001974 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 u8 *sendbuf;
1976
1977 /* get a packet to send */
1978
Al Viro79ea13c2008-01-24 02:06:46 -08001979 if ((skb = skb_dequeue(&ai->txq)) == NULL) {
Dan Williams934d8bf2006-03-16 13:46:23 -05001980 airo_print_err(dev->name,
1981 "%s: Dequeue'd zero in send_packet()",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07001982 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983 return 0;
1984 }
1985
1986 /* check min length*/
1987 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
1988 buffer = skb->data;
1989
1990 ai->txfids[0].tx_desc.offset = 0;
1991 ai->txfids[0].tx_desc.valid = 1;
1992 ai->txfids[0].tx_desc.eoc = 1;
1993 ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr);
1994
1995/*
1996 * Magic, the cards firmware needs a length count (2 bytes) in the host buffer
1997 * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen
Lucas De Marchi25985ed2011-03-30 22:57:33 -03001998 * is immediately after it. ------------------------------------------------
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA|
2000 * ------------------------------------------------
2001 */
2002
Joe Perches2c208892012-06-04 12:44:17 +00002003 memcpy(ai->txfids[0].virtual_host_addr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 (char *)&wifictlhdr8023, sizeof(wifictlhdr8023));
2005
Al Viro593c2b92007-12-17 15:09:34 -05002006 payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 sizeof(wifictlhdr8023));
2008 sendbuf = ai->txfids[0].virtual_host_addr +
2009 sizeof(wifictlhdr8023) + 2 ;
2010
2011 /*
Lucas De Marchi25985ed2011-03-30 22:57:33 -03002012 * Firmware automatically puts 802 header on so
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 * we don't need to account for it in the length
2014 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
Al Viro593c2b92007-12-17 15:09:34 -05002016 (ntohs(((__be16 *)buffer)[6]) != 0x888E)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 MICBuffer pMic;
2018
2019 if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS)
2020 return ERROR;
2021
2022 *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic));
2023 ai->txfids[0].tx_desc.len += sizeof(pMic);
2024 /* copy data into airo dma buffer */
2025 memcpy (sendbuf, buffer, sizeof(etherHead));
2026 buffer += sizeof(etherHead);
2027 sendbuf += sizeof(etherHead);
2028 memcpy (sendbuf, &pMic, sizeof(pMic));
2029 sendbuf += sizeof(pMic);
2030 memcpy (sendbuf, buffer, len - sizeof(etherHead));
Adrian Bunka39d3e72006-01-21 01:35:15 +01002031 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 *payloadLen = cpu_to_le16(len - sizeof(etherHead));
2033
2034 dev->trans_start = jiffies;
2035
2036 /* copy data into airo dma buffer */
2037 memcpy(sendbuf, buffer, len);
2038 }
2039
2040 memcpy_toio(ai->txfids[0].card_ram_off,
2041 &ai->txfids[0].tx_desc, sizeof(TxFid));
2042
2043 OUT4500(ai, EVACK, 8);
2044
2045 dev_kfree_skb_any(skb);
2046 return 1;
2047}
2048
Gabriel A. Devenyi29b09fc2005-11-03 19:30:47 -05002049static void get_tx_error(struct airo_info *ai, s32 fid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050{
Al Viro593c2b92007-12-17 15:09:34 -05002051 __le16 status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052
2053 if (fid < 0)
2054 status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status;
2055 else {
2056 if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS)
2057 return;
2058 bap_read(ai, &status, 2, BAP0);
2059 }
2060 if (le16_to_cpu(status) & 2) /* Too many retries */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002061 ai->dev->stats.tx_aborted_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062 if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002063 ai->dev->stats.tx_heartbeat_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 if (le16_to_cpu(status) & 8) /* Aid fail */
2065 { }
2066 if (le16_to_cpu(status) & 0x10) /* MAC disabled */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002067 ai->dev->stats.tx_carrier_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 if (le16_to_cpu(status) & 0x20) /* Association lost */
2069 { }
2070 /* We produce a TXDROP event only for retry or lifetime
2071 * exceeded, because that's the only status that really mean
2072 * that this particular node went away.
2073 * Other errors means that *we* screwed up. - Jean II */
2074 if ((le16_to_cpu(status) & 2) ||
2075 (le16_to_cpu(status) & 4)) {
2076 union iwreq_data wrqu;
2077 char junk[0x18];
2078
2079 /* Faster to skip over useless data than to do
2080 * another bap_setup(). We are at offset 0x6 and
2081 * need to go to 0x18 and read 6 bytes - Jean II */
Al Virob8c06bc2007-12-19 17:55:43 -05002082 bap_read(ai, (__le16 *) junk, 0x18, BAP0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083
2084 /* Copy 802.11 dest address.
2085 * We use the 802.11 header because the frame may
2086 * not be 802.3 or may be mangled...
2087 * In Ad-Hoc mode, it will be the node address.
2088 * In managed mode, it will be most likely the AP addr
2089 * User space will figure out how to convert it to
2090 * whatever it needs (IP address or else).
2091 * - Jean II */
2092 memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN);
2093 wrqu.addr.sa_family = ARPHRD_ETHER;
2094
2095 /* Send event to user space */
2096 wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL);
2097 }
2098}
2099
2100static void airo_end_xmit(struct net_device *dev) {
2101 u16 status;
2102 int i;
Wang Chenfaf39942008-10-14 13:30:33 +08002103 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 struct sk_buff *skb = priv->xmit.skb;
2105 int fid = priv->xmit.fid;
2106 u32 *fids = priv->fids;
2107
Dan Williams3c304952006-04-15 12:26:18 -04002108 clear_bit(JOB_XMIT, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 clear_bit(FLAG_PENDING_XMIT, &priv->flags);
2110 status = transmit_802_3_packet (priv, fids[fid], skb->data);
2111 up(&priv->sem);
2112
2113 i = 0;
2114 if ( status == SUCCESS ) {
2115 dev->trans_start = jiffies;
2116 for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++);
2117 } else {
2118 priv->fids[fid] &= 0xffff;
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002119 dev->stats.tx_window_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120 }
2121 if (i < MAX_FIDS / 2)
2122 netif_wake_queue(dev);
2123 dev_kfree_skb(skb);
2124}
2125
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +00002126static netdev_tx_t airo_start_xmit(struct sk_buff *skb,
2127 struct net_device *dev)
2128{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129 s16 len;
2130 int i, j;
Wang Chenfaf39942008-10-14 13:30:33 +08002131 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132 u32 *fids = priv->fids;
2133
2134 if ( skb == NULL ) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07002135 airo_print_err(dev->name, "%s: skb == NULL!", __func__);
Patrick McHardy6ed10652009-06-23 06:03:08 +00002136 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 }
2138
2139 /* Find a vacant FID */
2140 for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ );
2141 for( j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++ );
2142
2143 if ( j >= MAX_FIDS / 2 ) {
2144 netif_stop_queue(dev);
2145
2146 if (i == MAX_FIDS / 2) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002147 dev->stats.tx_fifo_errors++;
Patrick McHardy5b548142009-06-12 06:22:29 +00002148 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 }
2150 }
2151 /* check min length*/
2152 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
2153 /* Mark fid as used & save length for later */
2154 fids[i] |= (len << 16);
2155 priv->xmit.skb = skb;
2156 priv->xmit.fid = i;
2157 if (down_trylock(&priv->sem) != 0) {
2158 set_bit(FLAG_PENDING_XMIT, &priv->flags);
2159 netif_stop_queue(dev);
Dan Williams3c304952006-04-15 12:26:18 -04002160 set_bit(JOB_XMIT, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 wake_up_interruptible(&priv->thr_wait);
2162 } else
2163 airo_end_xmit(dev);
Patrick McHardy6ed10652009-06-23 06:03:08 +00002164 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165}
2166
2167static void airo_end_xmit11(struct net_device *dev) {
2168 u16 status;
2169 int i;
Wang Chenfaf39942008-10-14 13:30:33 +08002170 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 struct sk_buff *skb = priv->xmit11.skb;
2172 int fid = priv->xmit11.fid;
2173 u32 *fids = priv->fids;
2174
Dan Williams3c304952006-04-15 12:26:18 -04002175 clear_bit(JOB_XMIT11, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 clear_bit(FLAG_PENDING_XMIT11, &priv->flags);
2177 status = transmit_802_11_packet (priv, fids[fid], skb->data);
2178 up(&priv->sem);
2179
2180 i = MAX_FIDS / 2;
2181 if ( status == SUCCESS ) {
2182 dev->trans_start = jiffies;
2183 for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++);
2184 } else {
2185 priv->fids[fid] &= 0xffff;
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002186 dev->stats.tx_window_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187 }
2188 if (i < MAX_FIDS)
2189 netif_wake_queue(dev);
2190 dev_kfree_skb(skb);
2191}
2192
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +00002193static netdev_tx_t airo_start_xmit11(struct sk_buff *skb,
2194 struct net_device *dev)
2195{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196 s16 len;
2197 int i, j;
Wang Chenfaf39942008-10-14 13:30:33 +08002198 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199 u32 *fids = priv->fids;
2200
2201 if (test_bit(FLAG_MPI, &priv->flags)) {
2202 /* Not implemented yet for MPI350 */
2203 netif_stop_queue(dev);
Patrick McHardy4153e772009-06-12 04:08:02 +00002204 dev_kfree_skb_any(skb);
2205 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206 }
2207
2208 if ( skb == NULL ) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07002209 airo_print_err(dev->name, "%s: skb == NULL!", __func__);
Patrick McHardy6ed10652009-06-23 06:03:08 +00002210 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211 }
2212
2213 /* Find a vacant FID */
2214 for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ );
2215 for( j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++ );
2216
2217 if ( j >= MAX_FIDS ) {
2218 netif_stop_queue(dev);
2219
2220 if (i == MAX_FIDS) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002221 dev->stats.tx_fifo_errors++;
Patrick McHardy5b548142009-06-12 06:22:29 +00002222 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223 }
2224 }
2225 /* check min length*/
2226 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
2227 /* Mark fid as used & save length for later */
2228 fids[i] |= (len << 16);
2229 priv->xmit11.skb = skb;
2230 priv->xmit11.fid = i;
2231 if (down_trylock(&priv->sem) != 0) {
2232 set_bit(FLAG_PENDING_XMIT11, &priv->flags);
2233 netif_stop_queue(dev);
Dan Williams3c304952006-04-15 12:26:18 -04002234 set_bit(JOB_XMIT11, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235 wake_up_interruptible(&priv->thr_wait);
2236 } else
2237 airo_end_xmit11(dev);
Patrick McHardy6ed10652009-06-23 06:03:08 +00002238 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239}
2240
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002241static void airo_read_stats(struct net_device *dev)
Al Viroa23ace52007-12-19 22:24:16 -05002242{
Wang Chenfaf39942008-10-14 13:30:33 +08002243 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244 StatsRid stats_rid;
Al Viroa23ace52007-12-19 22:24:16 -05002245 __le32 *vals = stats_rid.vals;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246
Dan Williams3c304952006-04-15 12:26:18 -04002247 clear_bit(JOB_STATS, &ai->jobs);
Pavel Machekca078ba2005-09-03 15:56:57 -07002248 if (ai->power.event) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249 up(&ai->sem);
2250 return;
2251 }
2252 readStatsRid(ai, &stats_rid, RID_STATS, 0);
2253 up(&ai->sem);
2254
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002255 dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) +
Al Viroa23ace52007-12-19 22:24:16 -05002256 le32_to_cpu(vals[45]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002257 dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) +
Al Viroa23ace52007-12-19 22:24:16 -05002258 le32_to_cpu(vals[41]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002259 dev->stats.rx_bytes = le32_to_cpu(vals[92]);
2260 dev->stats.tx_bytes = le32_to_cpu(vals[91]);
2261 dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) +
Al Viroa23ace52007-12-19 22:24:16 -05002262 le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002263 dev->stats.tx_errors = le32_to_cpu(vals[42]) +
2264 dev->stats.tx_fifo_errors;
2265 dev->stats.multicast = le32_to_cpu(vals[43]);
2266 dev->stats.collisions = le32_to_cpu(vals[89]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267
2268 /* detailed rx_errors: */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002269 dev->stats.rx_length_errors = le32_to_cpu(vals[3]);
2270 dev->stats.rx_crc_errors = le32_to_cpu(vals[4]);
2271 dev->stats.rx_frame_errors = le32_to_cpu(vals[2]);
2272 dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273}
2274
Jouni Malinenff1d2762005-05-12 22:54:16 -04002275static struct net_device_stats *airo_get_stats(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276{
Wang Chenfaf39942008-10-14 13:30:33 +08002277 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278
Dan Williams3c304952006-04-15 12:26:18 -04002279 if (!test_bit(JOB_STATS, &local->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 /* Get stats out of the card if available */
2281 if (down_trylock(&local->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04002282 set_bit(JOB_STATS, &local->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 wake_up_interruptible(&local->thr_wait);
2284 } else
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002285 airo_read_stats(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 }
2287
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002288 return &dev->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289}
2290
2291static void airo_set_promisc(struct airo_info *ai) {
2292 Cmd cmd;
2293 Resp rsp;
2294
2295 memset(&cmd, 0, sizeof(cmd));
2296 cmd.cmd=CMD_SETMODE;
Dan Williams3c304952006-04-15 12:26:18 -04002297 clear_bit(JOB_PROMISC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298 cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC;
2299 issuecommand(ai, &cmd, &rsp);
2300 up(&ai->sem);
2301}
2302
2303static void airo_set_multicast_list(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08002304 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305
2306 if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
2307 change_bit(FLAG_PROMISC, &ai->flags);
2308 if (down_trylock(&ai->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04002309 set_bit(JOB_PROMISC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 wake_up_interruptible(&ai->thr_wait);
2311 } else
2312 airo_set_promisc(ai);
2313 }
2314
Jiri Pirko4cd24ea2010-02-08 04:30:35 +00002315 if ((dev->flags&IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316 /* Turn on multicast. (Should be already setup...) */
2317 }
2318}
2319
2320static int airo_set_mac_address(struct net_device *dev, void *p)
2321{
Wang Chenfaf39942008-10-14 13:30:33 +08002322 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323 struct sockaddr *addr = p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324
2325 readConfigRid(ai, 1);
2326 memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len);
2327 set_bit (FLAG_COMMIT, &ai->flags);
2328 disable_MAC(ai, 1);
2329 writeConfigRid (ai, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02002330 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331 memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len);
2332 if (ai->wifidev)
2333 memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len);
2334 return 0;
2335}
2336
2337static int airo_change_mtu(struct net_device *dev, int new_mtu)
2338{
2339 if ((new_mtu < 68) || (new_mtu > 2400))
2340 return -EINVAL;
2341 dev->mtu = new_mtu;
2342 return 0;
2343}
2344
Michal Schmidtaf5b5c9a2007-03-16 12:44:40 +01002345static LIST_HEAD(airo_devices);
2346
2347static void add_airo_dev(struct airo_info *ai)
2348{
2349 /* Upper layers already keep track of PCI devices,
2350 * so we only need to remember our non-PCI cards. */
2351 if (!ai->pci)
2352 list_add_tail(&ai->dev_list, &airo_devices);
2353}
2354
2355static void del_airo_dev(struct airo_info *ai)
2356{
2357 if (!ai->pci)
2358 list_del(&ai->dev_list);
2359}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360
2361static int airo_close(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08002362 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363
2364 netif_stop_queue(dev);
2365
2366 if (ai->wifidev != dev) {
2367#ifdef POWER_ON_DOWN
2368 /* Shut power to the card. The idea is that the user can save
2369 * power when he doesn't need the card with "ifconfig down".
2370 * That's the method that is most friendly towards the network
2371 * stack (i.e. the network stack won't try to broadcast
2372 * anything on the interface and routes are gone. Jean II */
2373 set_bit(FLAG_RADIO_DOWN, &ai->flags);
2374 disable_MAC(ai, 1);
2375#endif
2376 disable_interrupts( ai );
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002377
2378 free_irq(dev->irq, dev);
2379
2380 set_bit(JOB_DIE, &ai->jobs);
2381 kthread_stop(ai->airo_thread_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382 }
2383 return 0;
2384}
2385
Linus Torvalds1da177e2005-04-16 15:20:36 -07002386void stop_airo_card( struct net_device *dev, int freeres )
2387{
Wang Chenfaf39942008-10-14 13:30:33 +08002388 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389
2390 set_bit(FLAG_RADIO_DOWN, &ai->flags);
2391 disable_MAC(ai, 1);
2392 disable_interrupts(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 takedown_proc_entry( dev, ai );
2394 if (test_bit(FLAG_REGISTERED, &ai->flags)) {
2395 unregister_netdev( dev );
2396 if (ai->wifidev) {
2397 unregister_netdev(ai->wifidev);
2398 free_netdev(ai->wifidev);
2399 ai->wifidev = NULL;
2400 }
2401 clear_bit(FLAG_REGISTERED, &ai->flags);
2402 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403 /*
2404 * Clean out tx queue
2405 */
David S. Millerb03efcf2005-07-08 14:57:23 -07002406 if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407 struct sk_buff *skb = NULL;
2408 for (;(skb = skb_dequeue(&ai->txq));)
2409 dev_kfree_skb(skb);
2410 }
2411
Dan Williams9e75af32006-03-16 13:46:29 -05002412 airo_networks_free (ai);
2413
Jesper Juhlb4558ea2005-10-28 16:53:13 -04002414 kfree(ai->flash);
2415 kfree(ai->rssi);
2416 kfree(ai->APList);
2417 kfree(ai->SSID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418 if (freeres) {
2419 /* PCMCIA frees this stuff, so only for PCI and ISA */
2420 release_region( dev->base_addr, 64 );
2421 if (test_bit(FLAG_MPI, &ai->flags)) {
2422 if (ai->pci)
2423 mpi_unmap_card(ai->pci);
2424 if (ai->pcimem)
2425 iounmap(ai->pcimem);
2426 if (ai->pciaux)
2427 iounmap(ai->pciaux);
2428 pci_free_consistent(ai->pci, PCI_SHARED_LEN,
2429 ai->shared, ai->shared_dma);
2430 }
2431 }
Herbert Xuf12cc202006-08-22 20:36:13 +10002432 crypto_free_cipher(ai->tfm);
Michal Schmidtaf5b5c9a2007-03-16 12:44:40 +01002433 del_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434 free_netdev( dev );
2435}
2436
2437EXPORT_SYMBOL(stop_airo_card);
2438
Stephen Hemmingerb95cce32007-09-26 22:13:38 -07002439static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440{
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -07002441 memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002442 return ETH_ALEN;
2443}
2444
2445static void mpi_unmap_card(struct pci_dev *pci)
2446{
2447 unsigned long mem_start = pci_resource_start(pci, 1);
2448 unsigned long mem_len = pci_resource_len(pci, 1);
2449 unsigned long aux_start = pci_resource_start(pci, 2);
2450 unsigned long aux_len = AUXMEMSIZE;
2451
2452 release_mem_region(aux_start, aux_len);
2453 release_mem_region(mem_start, mem_len);
2454}
2455
2456/*************************************************************
2457 * This routine assumes that descriptors have been setup .
2458 * Run at insmod time or after reset when the decriptors
2459 * have been initialized . Returns 0 if all is well nz
2460 * otherwise . Does not allocate memory but sets up card
2461 * using previously allocated descriptors.
2462 */
2463static int mpi_init_descriptors (struct airo_info *ai)
2464{
2465 Cmd cmd;
2466 Resp rsp;
2467 int i;
2468 int rc = SUCCESS;
2469
2470 /* Alloc card RX descriptors */
2471 netif_stop_queue(ai->dev);
2472
2473 memset(&rsp,0,sizeof(rsp));
2474 memset(&cmd,0,sizeof(cmd));
2475
2476 cmd.cmd = CMD_ALLOCATEAUX;
2477 cmd.parm0 = FID_RX;
2478 cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux);
2479 cmd.parm2 = MPI_MAX_FIDS;
2480 rc=issuecommand(ai, &cmd, &rsp);
2481 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002482 airo_print_err(ai->dev->name, "Couldn't allocate RX FID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483 return rc;
2484 }
2485
2486 for (i=0; i<MPI_MAX_FIDS; i++) {
2487 memcpy_toio(ai->rxfids[i].card_ram_off,
2488 &ai->rxfids[i].rx_desc, sizeof(RxFid));
2489 }
2490
2491 /* Alloc card TX descriptors */
2492
2493 memset(&rsp,0,sizeof(rsp));
2494 memset(&cmd,0,sizeof(cmd));
2495
2496 cmd.cmd = CMD_ALLOCATEAUX;
2497 cmd.parm0 = FID_TX;
2498 cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux);
2499 cmd.parm2 = MPI_MAX_FIDS;
2500
2501 for (i=0; i<MPI_MAX_FIDS; i++) {
2502 ai->txfids[i].tx_desc.valid = 1;
2503 memcpy_toio(ai->txfids[i].card_ram_off,
2504 &ai->txfids[i].tx_desc, sizeof(TxFid));
2505 }
2506 ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
2507
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 TX FID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511 return rc;
2512 }
2513
2514 /* Alloc card Rid descriptor */
2515 memset(&rsp,0,sizeof(rsp));
2516 memset(&cmd,0,sizeof(cmd));
2517
2518 cmd.cmd = CMD_ALLOCATEAUX;
2519 cmd.parm0 = RID_RW;
2520 cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux);
2521 cmd.parm2 = 1; /* Magic number... */
2522 rc=issuecommand(ai, &cmd, &rsp);
2523 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002524 airo_print_err(ai->dev->name, "Couldn't allocate RID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525 return rc;
2526 }
2527
2528 memcpy_toio(ai->config_desc.card_ram_off,
2529 &ai->config_desc.rid_desc, sizeof(Rid));
2530
2531 return rc;
2532}
2533
2534/*
2535 * We are setting up three things here:
2536 * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid.
Lucas De Marchi25985ed2011-03-30 22:57:33 -03002537 * 2) Map PCI memory for issuing commands.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538 * 3) Allocate memory (shared) to send and receive ethernet frames.
2539 */
Michal Schmidt1138c372007-06-29 15:33:41 +02002540static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541{
2542 unsigned long mem_start, mem_len, aux_start, aux_len;
2543 int rc = -1;
2544 int i;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002545 dma_addr_t busaddroff;
2546 unsigned char *vpackoff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547 unsigned char __iomem *pciaddroff;
2548
2549 mem_start = pci_resource_start(pci, 1);
2550 mem_len = pci_resource_len(pci, 1);
2551 aux_start = pci_resource_start(pci, 2);
2552 aux_len = AUXMEMSIZE;
2553
Michal Schmidt1138c372007-06-29 15:33:41 +02002554 if (!request_mem_region(mem_start, mem_len, DRV_NAME)) {
2555 airo_print_err("", "Couldn't get region %x[%x]",
2556 (int)mem_start, (int)mem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 goto out;
2558 }
Michal Schmidt1138c372007-06-29 15:33:41 +02002559 if (!request_mem_region(aux_start, aux_len, DRV_NAME)) {
2560 airo_print_err("", "Couldn't get region %x[%x]",
2561 (int)aux_start, (int)aux_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 goto free_region1;
2563 }
2564
2565 ai->pcimem = ioremap(mem_start, mem_len);
2566 if (!ai->pcimem) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002567 airo_print_err("", "Couldn't map region %x[%x]",
2568 (int)mem_start, (int)mem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569 goto free_region2;
2570 }
2571 ai->pciaux = ioremap(aux_start, aux_len);
2572 if (!ai->pciaux) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002573 airo_print_err("", "Couldn't map region %x[%x]",
2574 (int)aux_start, (int)aux_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 goto free_memmap;
2576 }
2577
2578 /* Reserve PKTSIZE for each fid and 2K for the Rids */
2579 ai->shared = pci_alloc_consistent(pci, PCI_SHARED_LEN, &ai->shared_dma);
2580 if (!ai->shared) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002581 airo_print_err("", "Couldn't alloc_consistent %d",
2582 PCI_SHARED_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583 goto free_auxmap;
2584 }
2585
2586 /*
2587 * Setup descriptor RX, TX, CONFIG
2588 */
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002589 busaddroff = ai->shared_dma;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590 pciaddroff = ai->pciaux + AUX_OFFSET;
2591 vpackoff = ai->shared;
2592
2593 /* RX descriptor setup */
2594 for(i = 0; i < MPI_MAX_FIDS; i++) {
2595 ai->rxfids[i].pending = 0;
2596 ai->rxfids[i].card_ram_off = pciaddroff;
2597 ai->rxfids[i].virtual_host_addr = vpackoff;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002598 ai->rxfids[i].rx_desc.host_addr = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599 ai->rxfids[i].rx_desc.valid = 1;
2600 ai->rxfids[i].rx_desc.len = PKTSIZE;
2601 ai->rxfids[i].rx_desc.rdy = 0;
2602
2603 pciaddroff += sizeof(RxFid);
2604 busaddroff += PKTSIZE;
2605 vpackoff += PKTSIZE;
2606 }
2607
2608 /* TX descriptor setup */
2609 for(i = 0; i < MPI_MAX_FIDS; i++) {
2610 ai->txfids[i].card_ram_off = pciaddroff;
2611 ai->txfids[i].virtual_host_addr = vpackoff;
2612 ai->txfids[i].tx_desc.valid = 1;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002613 ai->txfids[i].tx_desc.host_addr = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614 memcpy(ai->txfids[i].virtual_host_addr,
2615 &wifictlhdr8023, sizeof(wifictlhdr8023));
2616
2617 pciaddroff += sizeof(TxFid);
2618 busaddroff += PKTSIZE;
2619 vpackoff += PKTSIZE;
2620 }
2621 ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
2622
2623 /* Rid descriptor setup */
2624 ai->config_desc.card_ram_off = pciaddroff;
2625 ai->config_desc.virtual_host_addr = vpackoff;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002626 ai->config_desc.rid_desc.host_addr = busaddroff;
2627 ai->ridbus = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628 ai->config_desc.rid_desc.rid = 0;
2629 ai->config_desc.rid_desc.len = RIDSIZE;
2630 ai->config_desc.rid_desc.valid = 1;
2631 pciaddroff += sizeof(Rid);
2632 busaddroff += RIDSIZE;
2633 vpackoff += RIDSIZE;
2634
2635 /* Tell card about descriptors */
2636 if (mpi_init_descriptors (ai) != SUCCESS)
2637 goto free_shared;
2638
2639 return 0;
2640 free_shared:
2641 pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
2642 free_auxmap:
2643 iounmap(ai->pciaux);
2644 free_memmap:
2645 iounmap(ai->pcimem);
2646 free_region2:
2647 release_mem_region(aux_start, aux_len);
2648 free_region1:
2649 release_mem_region(mem_start, mem_len);
2650 out:
2651 return rc;
2652}
2653
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -07002654static const struct header_ops airo_header_ops = {
2655 .parse = wll_header_parse,
2656};
2657
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002658static const struct net_device_ops airo11_netdev_ops = {
2659 .ndo_open = airo_open,
2660 .ndo_stop = airo_close,
2661 .ndo_start_xmit = airo_start_xmit11,
2662 .ndo_get_stats = airo_get_stats,
2663 .ndo_set_mac_address = airo_set_mac_address,
2664 .ndo_do_ioctl = airo_ioctl,
2665 .ndo_change_mtu = airo_change_mtu,
2666};
2667
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668static void wifi_setup(struct net_device *dev)
2669{
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002670 dev->netdev_ops = &airo11_netdev_ops;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -07002671 dev->header_ops = &airo_header_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672 dev->wireless_handlers = &airo_handler_def;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673
2674 dev->type = ARPHRD_IEEE80211;
2675 dev->hard_header_len = ETH_HLEN;
Dan Williams15db2762006-03-16 13:46:27 -05002676 dev->mtu = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677 dev->addr_len = ETH_ALEN;
2678 dev->tx_queue_len = 100;
2679
Joe Perches93803b32015-03-02 19:54:49 -08002680 eth_broadcast_addr(dev->broadcast);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681
2682 dev->flags = IFF_BROADCAST|IFF_MULTICAST;
2683}
2684
2685static struct net_device *init_wifidev(struct airo_info *ai,
2686 struct net_device *ethdev)
2687{
2688 int err;
Tom Gundersenc835a672014-07-14 16:37:24 +02002689 struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN,
2690 wifi_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002691 if (!dev)
2692 return NULL;
Wang Chenfaf39942008-10-14 13:30:33 +08002693 dev->ml_priv = ethdev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694 dev->irq = ethdev->irq;
2695 dev->base_addr = ethdev->base_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696 dev->wireless_data = ethdev->wireless_data;
Masakazu Mokuno229ce3a2008-05-14 14:16:50 +09002697 SET_NETDEV_DEV(dev, ethdev->dev.parent);
Bjørn Mork252352cb2013-08-30 18:08:49 +02002698 eth_hw_addr_inherit(dev, ethdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699 err = register_netdev(dev);
2700 if (err<0) {
2701 free_netdev(dev);
2702 return NULL;
2703 }
2704 return dev;
2705}
2706
Jouni Malinenff1d2762005-05-12 22:54:16 -04002707static int reset_card( struct net_device *dev , int lock) {
Wang Chenfaf39942008-10-14 13:30:33 +08002708 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709
2710 if (lock && down_interruptible(&ai->sem))
2711 return -1;
2712 waitbusy (ai);
2713 OUT4500(ai,COMMAND,CMD_SOFTRESET);
2714 msleep(200);
2715 waitbusy (ai);
2716 msleep(200);
2717 if (lock)
2718 up(&ai->sem);
2719 return 0;
2720}
2721
Dan Williams3c304952006-04-15 12:26:18 -04002722#define AIRO_MAX_NETWORK_COUNT 64
Dan Williams9e75af32006-03-16 13:46:29 -05002723static int airo_networks_allocate(struct airo_info *ai)
2724{
2725 if (ai->networks)
2726 return 0;
2727
Joe Perchesbaeb2ff2010-08-11 07:02:48 +00002728 ai->networks = kcalloc(AIRO_MAX_NETWORK_COUNT, sizeof(BSSListElement),
2729 GFP_KERNEL);
Dan Williams9e75af32006-03-16 13:46:29 -05002730 if (!ai->networks) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002731 airo_print_warn("", "Out of memory allocating beacons");
Dan Williams9e75af32006-03-16 13:46:29 -05002732 return -ENOMEM;
2733 }
2734
2735 return 0;
2736}
2737
2738static void airo_networks_free(struct airo_info *ai)
2739{
Dan Williams9e75af32006-03-16 13:46:29 -05002740 kfree(ai->networks);
2741 ai->networks = NULL;
2742}
2743
2744static void airo_networks_initialize(struct airo_info *ai)
2745{
2746 int i;
2747
2748 INIT_LIST_HEAD(&ai->network_free_list);
2749 INIT_LIST_HEAD(&ai->network_list);
Dan Williams3c304952006-04-15 12:26:18 -04002750 for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++)
Dan Williams9e75af32006-03-16 13:46:29 -05002751 list_add_tail(&ai->networks[i].list,
2752 &ai->network_free_list);
2753}
2754
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002755static const struct net_device_ops airo_netdev_ops = {
2756 .ndo_open = airo_open,
2757 .ndo_stop = airo_close,
2758 .ndo_start_xmit = airo_start_xmit,
2759 .ndo_get_stats = airo_get_stats,
Jiri Pirkoafc4b132011-08-16 06:29:01 +00002760 .ndo_set_rx_mode = airo_set_multicast_list,
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002761 .ndo_set_mac_address = airo_set_mac_address,
2762 .ndo_do_ioctl = airo_ioctl,
2763 .ndo_change_mtu = airo_change_mtu,
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002764 .ndo_validate_addr = eth_validate_addr,
2765};
2766
2767static const struct net_device_ops mpi_netdev_ops = {
2768 .ndo_open = airo_open,
2769 .ndo_stop = airo_close,
2770 .ndo_start_xmit = mpi_start_xmit,
2771 .ndo_get_stats = airo_get_stats,
Jiri Pirkoafc4b132011-08-16 06:29:01 +00002772 .ndo_set_rx_mode = airo_set_multicast_list,
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002773 .ndo_set_mac_address = airo_set_mac_address,
2774 .ndo_do_ioctl = airo_ioctl,
2775 .ndo_change_mtu = airo_change_mtu,
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002776 .ndo_validate_addr = eth_validate_addr,
2777};
2778
2779
Jouni Malinenff1d2762005-05-12 22:54:16 -04002780static struct net_device *_init_airo_card( unsigned short irq, int port,
2781 int is_pcmcia, struct pci_dev *pci,
2782 struct device *dmdev )
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783{
2784 struct net_device *dev;
2785 struct airo_info *ai;
2786 int i, rc;
Dan Williamsf65b56d2009-01-24 09:10:42 -05002787 CapabilityRid cap_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788
2789 /* Create the network device object. */
Tom Gundersenc835a672014-07-14 16:37:24 +02002790 dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup);
Michal Schmidt1138c372007-06-29 15:33:41 +02002791 if (!dev) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002792 airo_print_err("", "Couldn't alloc_etherdev");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 }
2795
Wang Chenfaf39942008-10-14 13:30:33 +08002796 ai = dev->ml_priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 ai->wifidev = NULL;
Michal Schmidtfb038c22007-06-29 15:33:52 +02002798 ai->flags = 1 << FLAG_RADIO_DOWN;
Dan Williams3c304952006-04-15 12:26:18 -04002799 ai->jobs = 0;
Dan Williams934d8bf2006-03-16 13:46:23 -05002800 ai->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002802 airo_print_dbg("", "Found an MPI350 card");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 set_bit(FLAG_MPI, &ai->flags);
2804 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 spin_lock_init(&ai->aux_lock);
2806 sema_init(&ai->sem, 1);
2807 ai->config.len = 0;
2808 ai->pci = pci;
2809 init_waitqueue_head (&ai->thr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810 ai->tfm = NULL;
Michal Schmidtaf5b5c9a2007-03-16 12:44:40 +01002811 add_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812
Dan Williams9e75af32006-03-16 13:46:29 -05002813 if (airo_networks_allocate (ai))
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002814 goto err_out_free;
Dan Williams9e75af32006-03-16 13:46:29 -05002815 airo_networks_initialize (ai);
2816
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002817 skb_queue_head_init (&ai->txq);
2818
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 /* The Airo-specific entries in the device structure. */
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002820 if (test_bit(FLAG_MPI,&ai->flags))
2821 dev->netdev_ops = &mpi_netdev_ops;
2822 else
2823 dev->netdev_ops = &airo_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824 dev->wireless_handlers = &airo_handler_def;
2825 ai->wireless_data.spy_data = &ai->spy_data;
2826 dev->wireless_data = &ai->wireless_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827 dev->irq = irq;
2828 dev->base_addr = port;
Neil Horman550fd082011-07-26 06:05:38 +00002829 dev->priv_flags &= ~IFF_TX_SKB_SHARING;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830
2831 SET_NETDEV_DEV(dev, dmdev);
2832
Matthieu CASTET1d97f382005-12-01 02:35:26 -05002833 reset_card (dev, 1);
2834 msleep(400);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836 if (!is_pcmcia) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002837 if (!request_region(dev->base_addr, 64, DRV_NAME)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838 rc = -EBUSY;
Dan Williams934d8bf2006-03-16 13:46:23 -05002839 airo_print_err(dev->name, "Couldn't request region");
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002840 goto err_out_nets;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841 }
2842 }
2843
2844 if (test_bit(FLAG_MPI,&ai->flags)) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002845 if (mpi_map_card(ai, pci)) {
2846 airo_print_err("", "Could not map memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847 goto err_out_res;
2848 }
2849 }
2850
2851 if (probe) {
Dan Williamsf65b56d2009-01-24 09:10:42 -05002852 if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002853 airo_print_err(dev->name, "MAC could not be enabled" );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854 rc = -EIO;
2855 goto err_out_map;
2856 }
2857 } else if (!test_bit(FLAG_MPI,&ai->flags)) {
2858 ai->bap_read = fast_bap_read;
2859 set_bit(FLAG_FLASHING, &ai->flags);
2860 }
2861
Michal Schmidt1138c372007-06-29 15:33:41 +02002862 strcpy(dev->name, "eth%d");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863 rc = register_netdev(dev);
2864 if (rc) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002865 airo_print_err(dev->name, "Couldn't register_netdev");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 goto err_out_map;
2867 }
2868 ai->wifidev = init_wifidev(ai, dev);
Florin Malita431aca52006-10-10 16:46:30 -04002869 if (!ai->wifidev)
2870 goto err_out_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871
Dan Williamsf65b56d2009-01-24 09:10:42 -05002872 rc = readCapabilityRid(ai, &cap_rid, 1);
2873 if (rc != SUCCESS) {
2874 rc = -EIO;
2875 goto err_out_wifi;
2876 }
Dan Williams138c0c62009-01-24 09:11:35 -05002877 /* WEP capability discovery */
2878 ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0;
2879 ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0;
Dan Williamsf65b56d2009-01-24 09:10:42 -05002880
matthieu castetf3a981f2010-02-28 15:42:54 +01002881 airo_print_info(dev->name, "Firmware version %x.%x.%02d",
Dan Williamsf65b56d2009-01-24 09:10:42 -05002882 ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF),
2883 (le16_to_cpu(cap_rid.softVer) & 0xFF),
2884 le16_to_cpu(cap_rid.softSubVer));
2885
2886 /* Test for WPA support */
2887 /* Only firmware versions 5.30.17 or better can do WPA */
2888 if (le16_to_cpu(cap_rid.softVer) > 0x530
2889 || (le16_to_cpu(cap_rid.softVer) == 0x530
2890 && le16_to_cpu(cap_rid.softSubVer) >= 17)) {
2891 airo_print_info(ai->dev->name, "WPA supported.");
2892
2893 set_bit(FLAG_WPA_CAPABLE, &ai->flags);
2894 ai->bssListFirst = RID_WPA_BSSLISTFIRST;
2895 ai->bssListNext = RID_WPA_BSSLISTNEXT;
2896 ai->bssListRidLen = sizeof(BSSListRid);
2897 } else {
2898 airo_print_info(ai->dev->name, "WPA unsupported with firmware "
2899 "versions older than 5.30.17.");
2900
2901 ai->bssListFirst = RID_BSSLISTFIRST;
2902 ai->bssListNext = RID_BSSLISTNEXT;
2903 ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra);
2904 }
2905
Linus Torvalds1da177e2005-04-16 15:20:36 -07002906 set_bit(FLAG_REGISTERED,&ai->flags);
Johannes Berge1749612008-10-27 15:59:26 -07002907 airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002908
2909 /* Allocate the transmit buffers */
2910 if (probe && !test_bit(FLAG_MPI,&ai->flags))
2911 for( i = 0; i < MAX_FIDS; i++ )
Dan Williams15db2762006-03-16 13:46:27 -05002912 ai->fids[i] = transmit_allocate(ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002913
Wang Chenfaf39942008-10-14 13:30:33 +08002914 if (setup_proc_entry(dev, dev->ml_priv) < 0)
Florin Malita431aca52006-10-10 16:46:30 -04002915 goto err_out_wifi;
2916
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917 return dev;
2918
Florin Malita431aca52006-10-10 16:46:30 -04002919err_out_wifi:
2920 unregister_netdev(ai->wifidev);
2921 free_netdev(ai->wifidev);
2922err_out_reg:
2923 unregister_netdev(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002924err_out_map:
2925 if (test_bit(FLAG_MPI,&ai->flags) && pci) {
2926 pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
2927 iounmap(ai->pciaux);
2928 iounmap(ai->pcimem);
2929 mpi_unmap_card(ai->pci);
2930 }
2931err_out_res:
2932 if (!is_pcmcia)
2933 release_region( dev->base_addr, 64 );
Michal Schmidt4d881902007-03-16 12:42:59 +01002934err_out_nets:
2935 airo_networks_free(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002936err_out_free:
Kulikov Vasiliyff4bf912010-07-13 15:23:33 +04002937 del_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002938 free_netdev(dev);
2939 return NULL;
2940}
2941
2942struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia,
2943 struct device *dmdev)
2944{
2945 return _init_airo_card ( irq, port, is_pcmcia, NULL, dmdev);
2946}
2947
2948EXPORT_SYMBOL(init_airo_card);
2949
2950static int waitbusy (struct airo_info *ai) {
2951 int delay = 0;
Andrew Mortonb212f332008-05-28 12:40:39 -07002952 while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953 udelay (10);
2954 if ((++delay % 20) == 0)
2955 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
2956 }
2957 return delay < 10000;
2958}
2959
2960int reset_airo_card( struct net_device *dev )
2961{
2962 int i;
Wang Chenfaf39942008-10-14 13:30:33 +08002963 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964
2965 if (reset_card (dev, 1))
2966 return -1;
2967
2968 if ( setup_card(ai, dev->dev_addr, 1 ) != SUCCESS ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002969 airo_print_err(dev->name, "MAC could not be enabled");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002970 return -1;
2971 }
Johannes Berge1749612008-10-27 15:59:26 -07002972 airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002973 /* Allocate the transmit buffers if needed */
2974 if (!test_bit(FLAG_MPI,&ai->flags))
2975 for( i = 0; i < MAX_FIDS; i++ )
Dan Williams15db2762006-03-16 13:46:27 -05002976 ai->fids[i] = transmit_allocate (ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002977
2978 enable_interrupts( ai );
2979 netif_wake_queue(dev);
2980 return 0;
2981}
2982
2983EXPORT_SYMBOL(reset_airo_card);
2984
2985static void airo_send_event(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08002986 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002987 union iwreq_data wrqu;
2988 StatusRid status_rid;
2989
Dan Williams3c304952006-04-15 12:26:18 -04002990 clear_bit(JOB_EVENT, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002991 PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
2992 up(&ai->sem);
2993 wrqu.data.length = 0;
2994 wrqu.data.flags = 0;
2995 memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN);
2996 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
2997
2998 /* Send event to user space */
2999 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
3000}
3001
Dan Williams9e75af32006-03-16 13:46:29 -05003002static void airo_process_scan_results (struct airo_info *ai) {
3003 union iwreq_data wrqu;
Dan Williams3c304952006-04-15 12:26:18 -04003004 BSSListRid bss;
Dan Williams9e75af32006-03-16 13:46:29 -05003005 int rc;
3006 BSSListElement * loop_net;
3007 BSSListElement * tmp_net;
3008
3009 /* Blow away current list of scan results */
3010 list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) {
3011 list_move_tail (&loop_net->list, &ai->network_free_list);
3012 /* Don't blow away ->list, just BSS data */
3013 memset (loop_net, 0, sizeof (loop_net->bss));
3014 }
3015
3016 /* Try to read the first entry of the scan result */
Dan Williams3c304952006-04-15 12:26:18 -04003017 rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0);
Al Viro17e70492007-12-19 18:56:37 -05003018 if((rc) || (bss.index == cpu_to_le16(0xffff))) {
Dan Williams9e75af32006-03-16 13:46:29 -05003019 /* No scan results */
3020 goto out;
3021 }
3022
3023 /* Read and parse all entries */
3024 tmp_net = NULL;
Al Viro17e70492007-12-19 18:56:37 -05003025 while((!rc) && (bss.index != cpu_to_le16(0xffff))) {
Dan Williams9e75af32006-03-16 13:46:29 -05003026 /* Grab a network off the free list */
3027 if (!list_empty(&ai->network_free_list)) {
3028 tmp_net = list_entry(ai->network_free_list.next,
3029 BSSListElement, list);
3030 list_del(ai->network_free_list.next);
3031 }
3032
3033 if (tmp_net != NULL) {
Dan Williams3c304952006-04-15 12:26:18 -04003034 memcpy(tmp_net, &bss, sizeof(tmp_net->bss));
Dan Williams9e75af32006-03-16 13:46:29 -05003035 list_add_tail(&tmp_net->list, &ai->network_list);
3036 tmp_net = NULL;
3037 }
3038
3039 /* Read next entry */
Dan Williams3c304952006-04-15 12:26:18 -04003040 rc = PC4500_readrid(ai, ai->bssListNext,
3041 &bss, ai->bssListRidLen, 0);
Dan Williams9e75af32006-03-16 13:46:29 -05003042 }
3043
3044out:
3045 ai->scan_timeout = 0;
Dan Williams3c304952006-04-15 12:26:18 -04003046 clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
Dan Williams9e75af32006-03-16 13:46:29 -05003047 up(&ai->sem);
3048
3049 /* Send an empty event to user space.
3050 * We don't send the received data on
3051 * the event because it would require
3052 * us to do complex transcoding, and
3053 * we want to minimise the work done in
3054 * the irq handler. Use a request to
3055 * extract the data - Jean II */
3056 wrqu.data.length = 0;
3057 wrqu.data.flags = 0;
3058 wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL);
3059}
3060
Linus Torvalds1da177e2005-04-16 15:20:36 -07003061static int airo_thread(void *data) {
3062 struct net_device *dev = data;
Wang Chenfaf39942008-10-14 13:30:33 +08003063 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003064 int locked;
Rafael J. Wysocki83144182007-07-17 04:03:35 -07003065
3066 set_freezable();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003067 while(1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003068 /* make swsusp happy with our thread */
Christoph Lameter3e1d1d22005-06-24 23:13:50 -07003069 try_to_freeze();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003070
Dan Williams3c304952006-04-15 12:26:18 -04003071 if (test_bit(JOB_DIE, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003072 break;
3073
Dan Williams3c304952006-04-15 12:26:18 -04003074 if (ai->jobs) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003075 locked = down_interruptible(&ai->sem);
3076 } else {
3077 wait_queue_t wait;
3078
3079 init_waitqueue_entry(&wait, current);
3080 add_wait_queue(&ai->thr_wait, &wait);
3081 for (;;) {
3082 set_current_state(TASK_INTERRUPTIBLE);
Dan Williams3c304952006-04-15 12:26:18 -04003083 if (ai->jobs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084 break;
Dan Williams9e75af32006-03-16 13:46:29 -05003085 if (ai->expires || ai->scan_timeout) {
3086 if (ai->scan_timeout &&
3087 time_after_eq(jiffies,ai->scan_timeout)){
Dan Williams3c304952006-04-15 12:26:18 -04003088 set_bit(JOB_SCAN_RESULTS, &ai->jobs);
Dan Williams9e75af32006-03-16 13:46:29 -05003089 break;
3090 } else if (ai->expires &&
3091 time_after_eq(jiffies,ai->expires)){
Dan Williams3c304952006-04-15 12:26:18 -04003092 set_bit(JOB_AUTOWEP, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093 break;
3094 }
Dave Kleikamp5bb85f12006-10-10 14:45:46 -07003095 if (!kthread_should_stop() &&
3096 !freezing(current)) {
Dan Williams9e75af32006-03-16 13:46:29 -05003097 unsigned long wake_at;
3098 if (!ai->expires || !ai->scan_timeout) {
3099 wake_at = max(ai->expires,
3100 ai->scan_timeout);
3101 } else {
3102 wake_at = min(ai->expires,
3103 ai->scan_timeout);
3104 }
3105 schedule_timeout(wake_at - jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003106 continue;
3107 }
Dave Kleikamp5bb85f12006-10-10 14:45:46 -07003108 } else if (!kthread_should_stop() &&
3109 !freezing(current)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003110 schedule();
3111 continue;
3112 }
3113 break;
3114 }
3115 current->state = TASK_RUNNING;
3116 remove_wait_queue(&ai->thr_wait, &wait);
3117 locked = 1;
3118 }
3119
3120 if (locked)
3121 continue;
3122
Dan Williams3c304952006-04-15 12:26:18 -04003123 if (test_bit(JOB_DIE, &ai->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003124 up(&ai->sem);
3125 break;
3126 }
3127
Pavel Machekca078ba2005-09-03 15:56:57 -07003128 if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003129 up(&ai->sem);
3130 continue;
3131 }
3132
Dan Williams3c304952006-04-15 12:26:18 -04003133 if (test_bit(JOB_XMIT, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003134 airo_end_xmit(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003135 else if (test_bit(JOB_XMIT11, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003136 airo_end_xmit11(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003137 else if (test_bit(JOB_STATS, &ai->jobs))
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003138 airo_read_stats(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003139 else if (test_bit(JOB_WSTATS, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003140 airo_read_wireless_stats(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003141 else if (test_bit(JOB_PROMISC, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003142 airo_set_promisc(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003143 else if (test_bit(JOB_MIC, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144 micinit(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003145 else if (test_bit(JOB_EVENT, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003146 airo_send_event(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003147 else if (test_bit(JOB_AUTOWEP, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003148 timer_func(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003149 else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs))
Dan Williams9e75af32006-03-16 13:46:29 -05003150 airo_process_scan_results(ai);
3151 else /* Shouldn't get here, but we make sure to unlock */
3152 up(&ai->sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003153 }
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07003154
3155 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003156}
3157
Al Viro0300b332007-12-19 22:38:33 -05003158static int header_len(__le16 ctl)
3159{
3160 u16 fc = le16_to_cpu(ctl);
3161 switch (fc & 0xc) {
3162 case 4:
3163 if ((fc & 0xe0) == 0xc0)
3164 return 10; /* one-address control packet */
3165 return 16; /* two-address control packet */
3166 case 8:
3167 if ((fc & 0x300) == 0x300)
3168 return 30; /* WDS packet */
3169 }
3170 return 24;
3171}
3172
Dan Williamsf55d4512009-01-24 09:04:12 -05003173static void airo_handle_cisco_mic(struct airo_info *ai)
3174{
3175 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) {
3176 set_bit(JOB_MIC, &ai->jobs);
3177 wake_up_interruptible(&ai->thr_wait);
3178 }
3179}
3180
3181/* Airo Status codes */
3182#define STAT_NOBEACON 0x8000 /* Loss of sync - missed beacons */
3183#define STAT_MAXRETRIES 0x8001 /* Loss of sync - max retries */
3184#define STAT_MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/
3185#define STAT_FORCELOSS 0x8003 /* Loss of sync - host request */
3186#define STAT_TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */
3187#define STAT_DEAUTH 0x8100 /* low byte is 802.11 reason code */
3188#define STAT_DISASSOC 0x8200 /* low byte is 802.11 reason code */
3189#define STAT_ASSOC_FAIL 0x8400 /* low byte is 802.11 reason code */
3190#define STAT_AUTH_FAIL 0x0300 /* low byte is 802.11 reason code */
3191#define STAT_ASSOC 0x0400 /* Associated */
3192#define STAT_REASSOC 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */
3193
3194static void airo_print_status(const char *devname, u16 status)
3195{
3196 u8 reason = status & 0xFF;
3197
matthieu castet3bc819e2010-02-28 15:31:21 +01003198 switch (status & 0xFF00) {
Dan Williamsf55d4512009-01-24 09:04:12 -05003199 case STAT_NOBEACON:
matthieu castet3bc819e2010-02-28 15:31:21 +01003200 switch (status) {
3201 case STAT_NOBEACON:
3202 airo_print_dbg(devname, "link lost (missed beacons)");
3203 break;
3204 case STAT_MAXRETRIES:
3205 case STAT_MAXARL:
3206 airo_print_dbg(devname, "link lost (max retries)");
3207 break;
3208 case STAT_FORCELOSS:
3209 airo_print_dbg(devname, "link lost (local choice)");
3210 break;
3211 case STAT_TSFSYNC:
3212 airo_print_dbg(devname, "link lost (TSF sync lost)");
3213 break;
3214 default:
Masanari Iidaf42cf8d2015-02-24 23:11:26 +09003215 airo_print_dbg(devname, "unknown status %x\n", status);
matthieu castet3bc819e2010-02-28 15:31:21 +01003216 break;
3217 }
Dan Williamsf55d4512009-01-24 09:04:12 -05003218 break;
3219 case STAT_DEAUTH:
3220 airo_print_dbg(devname, "deauthenticated (reason: %d)", reason);
3221 break;
3222 case STAT_DISASSOC:
3223 airo_print_dbg(devname, "disassociated (reason: %d)", reason);
3224 break;
3225 case STAT_ASSOC_FAIL:
3226 airo_print_dbg(devname, "association failed (reason: %d)",
3227 reason);
3228 break;
3229 case STAT_AUTH_FAIL:
3230 airo_print_dbg(devname, "authentication failed (reason: %d)",
3231 reason);
3232 break;
matthieu castet3bc819e2010-02-28 15:31:21 +01003233 case STAT_ASSOC:
3234 case STAT_REASSOC:
3235 break;
Dan Williamsf55d4512009-01-24 09:04:12 -05003236 default:
Masanari Iidaf42cf8d2015-02-24 23:11:26 +09003237 airo_print_dbg(devname, "unknown status %x\n", status);
Dan Williamsf55d4512009-01-24 09:04:12 -05003238 break;
3239 }
3240}
3241
3242static void airo_handle_link(struct airo_info *ai)
3243{
3244 union iwreq_data wrqu;
3245 int scan_forceloss = 0;
3246 u16 status;
3247
3248 /* Get new status and acknowledge the link change */
3249 status = le16_to_cpu(IN4500(ai, LINKSTAT));
3250 OUT4500(ai, EVACK, EV_LINK);
3251
3252 if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0))
3253 scan_forceloss = 1;
3254
3255 airo_print_status(ai->dev->name, status);
3256
3257 if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) {
3258 if (auto_wep)
3259 ai->expires = 0;
3260 if (ai->list_bss_task)
3261 wake_up_process(ai->list_bss_task);
3262 set_bit(FLAG_UPDATE_UNI, &ai->flags);
3263 set_bit(FLAG_UPDATE_MULTI, &ai->flags);
3264
3265 if (down_trylock(&ai->sem) != 0) {
3266 set_bit(JOB_EVENT, &ai->jobs);
3267 wake_up_interruptible(&ai->thr_wait);
3268 } else
3269 airo_send_event(ai->dev);
3270 } else if (!scan_forceloss) {
3271 if (auto_wep && !ai->expires) {
3272 ai->expires = RUN_AT(3*HZ);
3273 wake_up_interruptible(&ai->thr_wait);
3274 }
3275
3276 /* Send event to user space */
Joe Perches93803b32015-03-02 19:54:49 -08003277 eth_zero_addr(wrqu.ap_addr.sa_data);
Dan Williamsf55d4512009-01-24 09:04:12 -05003278 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
3279 wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL);
3280 }
3281}
3282
3283static void airo_handle_rx(struct airo_info *ai)
3284{
3285 struct sk_buff *skb = NULL;
3286 __le16 fc, v, *buffer, tmpbuf[4];
3287 u16 len, hdrlen = 0, gap, fid;
3288 struct rx_hdr hdr;
3289 int success = 0;
3290
3291 if (test_bit(FLAG_MPI, &ai->flags)) {
3292 if (test_bit(FLAG_802_11, &ai->flags))
3293 mpi_receive_802_11(ai);
3294 else
3295 mpi_receive_802_3(ai);
3296 OUT4500(ai, EVACK, EV_RX);
3297 return;
3298 }
3299
3300 fid = IN4500(ai, RXFID);
3301
3302 /* Get the packet length */
3303 if (test_bit(FLAG_802_11, &ai->flags)) {
3304 bap_setup (ai, fid, 4, BAP0);
3305 bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0);
3306 /* Bad CRC. Ignore packet */
3307 if (le16_to_cpu(hdr.status) & 2)
3308 hdr.len = 0;
3309 if (ai->wifidev == NULL)
3310 hdr.len = 0;
3311 } else {
3312 bap_setup(ai, fid, 0x36, BAP0);
3313 bap_read(ai, &hdr.len, 2, BAP0);
3314 }
3315 len = le16_to_cpu(hdr.len);
3316
3317 if (len > AIRO_DEF_MTU) {
3318 airo_print_err(ai->dev->name, "Bad size %d", len);
3319 goto done;
3320 }
3321 if (len == 0)
3322 goto done;
3323
3324 if (test_bit(FLAG_802_11, &ai->flags)) {
3325 bap_read(ai, &fc, sizeof (fc), BAP0);
3326 hdrlen = header_len(fc);
3327 } else
3328 hdrlen = ETH_ALEN * 2;
3329
3330 skb = dev_alloc_skb(len + hdrlen + 2 + 2);
3331 if (!skb) {
3332 ai->dev->stats.rx_dropped++;
3333 goto done;
3334 }
3335
3336 skb_reserve(skb, 2); /* This way the IP header is aligned */
3337 buffer = (__le16 *) skb_put(skb, len + hdrlen);
3338 if (test_bit(FLAG_802_11, &ai->flags)) {
3339 buffer[0] = fc;
3340 bap_read(ai, buffer + 1, hdrlen - 2, BAP0);
3341 if (hdrlen == 24)
3342 bap_read(ai, tmpbuf, 6, BAP0);
3343
3344 bap_read(ai, &v, sizeof(v), BAP0);
3345 gap = le16_to_cpu(v);
3346 if (gap) {
3347 if (gap <= 8) {
3348 bap_read(ai, tmpbuf, gap, BAP0);
3349 } else {
3350 airo_print_err(ai->dev->name, "gaplen too "
3351 "big. Problems will follow...");
3352 }
3353 }
3354 bap_read(ai, buffer + hdrlen/2, len, BAP0);
3355 } else {
3356 MICBuffer micbuf;
3357
3358 bap_read(ai, buffer, ETH_ALEN * 2, BAP0);
3359 if (ai->micstats.enabled) {
3360 bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0);
3361 if (ntohs(micbuf.typelen) > 0x05DC)
3362 bap_setup(ai, fid, 0x44, BAP0);
3363 else {
3364 if (len <= sizeof (micbuf)) {
3365 dev_kfree_skb_irq(skb);
3366 goto done;
3367 }
3368
3369 len -= sizeof(micbuf);
3370 skb_trim(skb, len + hdrlen);
3371 }
3372 }
3373
3374 bap_read(ai, buffer + ETH_ALEN, len, BAP0);
3375 if (decapsulate(ai, &micbuf, (etherHead*) buffer, len))
3376 dev_kfree_skb_irq (skb);
3377 else
3378 success = 1;
3379 }
3380
3381#ifdef WIRELESS_SPY
3382 if (success && (ai->spy_data.spy_number > 0)) {
3383 char *sa;
3384 struct iw_quality wstats;
3385
3386 /* Prepare spy data : addr + qual */
3387 if (!test_bit(FLAG_802_11, &ai->flags)) {
3388 sa = (char *) buffer + 6;
3389 bap_setup(ai, fid, 8, BAP0);
3390 bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0);
3391 } else
3392 sa = (char *) buffer + 10;
3393 wstats.qual = hdr.rssi[0];
3394 if (ai->rssi)
3395 wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
3396 else
3397 wstats.level = (hdr.rssi[1] + 321) / 2;
3398 wstats.noise = ai->wstats.qual.noise;
3399 wstats.updated = IW_QUAL_LEVEL_UPDATED
3400 | IW_QUAL_QUAL_UPDATED
3401 | IW_QUAL_DBM;
3402 /* Update spy records */
3403 wireless_spy_update(ai->dev, sa, &wstats);
3404 }
3405#endif /* WIRELESS_SPY */
3406
3407done:
3408 OUT4500(ai, EVACK, EV_RX);
3409
3410 if (success) {
3411 if (test_bit(FLAG_802_11, &ai->flags)) {
3412 skb_reset_mac_header(skb);
3413 skb->pkt_type = PACKET_OTHERHOST;
3414 skb->dev = ai->wifidev;
3415 skb->protocol = htons(ETH_P_802_2);
3416 } else
3417 skb->protocol = eth_type_trans(skb, ai->dev);
3418 skb->ip_summed = CHECKSUM_NONE;
3419
3420 netif_rx(skb);
3421 }
3422}
3423
3424static void airo_handle_tx(struct airo_info *ai, u16 status)
3425{
3426 int i, len = 0, index = -1;
3427 u16 fid;
3428
3429 if (test_bit(FLAG_MPI, &ai->flags)) {
3430 unsigned long flags;
3431
3432 if (status & EV_TXEXC)
3433 get_tx_error(ai, -1);
3434
3435 spin_lock_irqsave(&ai->aux_lock, flags);
3436 if (!skb_queue_empty(&ai->txq)) {
3437 spin_unlock_irqrestore(&ai->aux_lock,flags);
3438 mpi_send_packet(ai->dev);
3439 } else {
3440 clear_bit(FLAG_PENDING_XMIT, &ai->flags);
3441 spin_unlock_irqrestore(&ai->aux_lock,flags);
3442 netif_wake_queue(ai->dev);
3443 }
3444 OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
3445 return;
3446 }
3447
3448 fid = IN4500(ai, TXCOMPLFID);
3449
3450 for(i = 0; i < MAX_FIDS; i++) {
3451 if ((ai->fids[i] & 0xffff) == fid) {
3452 len = ai->fids[i] >> 16;
3453 index = i;
3454 }
3455 }
3456
3457 if (index != -1) {
3458 if (status & EV_TXEXC)
3459 get_tx_error(ai, index);
3460
3461 OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC));
3462
3463 /* Set up to be used again */
3464 ai->fids[index] &= 0xffff;
3465 if (index < MAX_FIDS / 2) {
3466 if (!test_bit(FLAG_PENDING_XMIT, &ai->flags))
3467 netif_wake_queue(ai->dev);
3468 } else {
3469 if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags))
3470 netif_wake_queue(ai->wifidev);
3471 }
3472 } else {
3473 OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
3474 airo_print_err(ai->dev->name, "Unallocated FID was used to xmit");
3475 }
3476}
3477
Jeff Garzik28fc1f52007-10-29 05:46:16 -04003478static irqreturn_t airo_interrupt(int irq, void *dev_id)
3479{
3480 struct net_device *dev = dev_id;
Dan Williamsf55d4512009-01-24 09:04:12 -05003481 u16 status, savedInterrupts = 0;
3482 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003483 int handled = 0;
3484
3485 if (!netif_device_present(dev))
3486 return IRQ_NONE;
3487
3488 for (;;) {
Dan Williamsf55d4512009-01-24 09:04:12 -05003489 status = IN4500(ai, EVSTAT);
3490 if (!(status & STATUS_INTS) || (status == 0xffff))
3491 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003492
3493 handled = 1;
3494
Dan Williamsf55d4512009-01-24 09:04:12 -05003495 if (status & EV_AWAKE) {
3496 OUT4500(ai, EVACK, EV_AWAKE);
3497 OUT4500(ai, EVACK, EV_AWAKE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003498 }
3499
3500 if (!savedInterrupts) {
Dan Williamsf55d4512009-01-24 09:04:12 -05003501 savedInterrupts = IN4500(ai, EVINTEN);
3502 OUT4500(ai, EVINTEN, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003503 }
3504
Dan Williamsf55d4512009-01-24 09:04:12 -05003505 if (status & EV_MIC) {
3506 OUT4500(ai, EVACK, EV_MIC);
3507 airo_handle_cisco_mic(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003508 }
Dan Williams6fcdf562006-03-31 15:08:46 -05003509
Dan Williamsf55d4512009-01-24 09:04:12 -05003510 if (status & EV_LINK) {
3511 /* Link status changed */
3512 airo_handle_link(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003513 }
3514
3515 /* Check to see if there is something to receive */
Dan Williamsf55d4512009-01-24 09:04:12 -05003516 if (status & EV_RX)
3517 airo_handle_rx(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003518
3519 /* Check to see if a packet has been transmitted */
Dan Williamsf55d4512009-01-24 09:04:12 -05003520 if (status & (EV_TX | EV_TXCPY | EV_TXEXC))
3521 airo_handle_tx(ai, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003522
Dan Williamsf55d4512009-01-24 09:04:12 -05003523 if ( status & ~STATUS_INTS & ~IGNORE_INTS ) {
3524 airo_print_warn(ai->dev->name, "Got weird status %x",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003525 status & ~STATUS_INTS & ~IGNORE_INTS );
Dan Williamsf55d4512009-01-24 09:04:12 -05003526 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003527 }
3528
3529 if (savedInterrupts)
Dan Williamsf55d4512009-01-24 09:04:12 -05003530 OUT4500(ai, EVINTEN, savedInterrupts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003531
Linus Torvalds1da177e2005-04-16 15:20:36 -07003532 return IRQ_RETVAL(handled);
3533}
3534
3535/*
3536 * Routines to talk to the card
3537 */
3538
3539/*
3540 * This was originally written for the 4500, hence the name
3541 * NOTE: If use with 8bit mode and SMP bad things will happen!
3542 * Why would some one do 8 bit IO in an SMP machine?!?
3543 */
3544static void OUT4500( struct airo_info *ai, u16 reg, u16 val ) {
3545 if (test_bit(FLAG_MPI,&ai->flags))
3546 reg <<= 1;
3547 if ( !do8bitIO )
3548 outw( val, ai->dev->base_addr + reg );
3549 else {
3550 outb( val & 0xff, ai->dev->base_addr + reg );
3551 outb( val >> 8, ai->dev->base_addr + reg + 1 );
3552 }
3553}
3554
3555static u16 IN4500( struct airo_info *ai, u16 reg ) {
3556 unsigned short rc;
3557
3558 if (test_bit(FLAG_MPI,&ai->flags))
3559 reg <<= 1;
3560 if ( !do8bitIO )
3561 rc = inw( ai->dev->base_addr + reg );
3562 else {
3563 rc = inb( ai->dev->base_addr + reg );
3564 rc += ((int)inb( ai->dev->base_addr + reg + 1 )) << 8;
3565 }
3566 return rc;
3567}
3568
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003569static int enable_MAC(struct airo_info *ai, int lock)
3570{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003571 int rc;
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003572 Cmd cmd;
3573 Resp rsp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003574
3575 /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions
3576 * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down"
3577 * Note : we could try to use !netif_running(dev) in enable_MAC()
3578 * instead of this flag, but I don't trust it *within* the
3579 * open/close functions, and testing both flags together is
3580 * "cheaper" - Jean II */
3581 if (ai->flags & FLAG_RADIO_MASK) return SUCCESS;
3582
3583 if (lock && down_interruptible(&ai->sem))
3584 return -ERESTARTSYS;
3585
3586 if (!test_bit(FLAG_ENABLED, &ai->flags)) {
3587 memset(&cmd, 0, sizeof(cmd));
3588 cmd.cmd = MAC_ENABLE;
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003589 rc = issuecommand(ai, &cmd, &rsp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003590 if (rc == SUCCESS)
3591 set_bit(FLAG_ENABLED, &ai->flags);
3592 } else
3593 rc = SUCCESS;
3594
3595 if (lock)
3596 up(&ai->sem);
3597
3598 if (rc)
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003599 airo_print_err(ai->dev->name, "Cannot enable MAC");
3600 else if ((rsp.status & 0xFF00) != 0) {
3601 airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, "
3602 "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2);
3603 rc = ERROR;
3604 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003605 return rc;
3606}
3607
3608static void disable_MAC( struct airo_info *ai, int lock ) {
3609 Cmd cmd;
3610 Resp rsp;
3611
3612 if (lock && down_interruptible(&ai->sem))
3613 return;
3614
3615 if (test_bit(FLAG_ENABLED, &ai->flags)) {
3616 memset(&cmd, 0, sizeof(cmd));
3617 cmd.cmd = MAC_DISABLE; // disable in case already enabled
3618 issuecommand(ai, &cmd, &rsp);
3619 clear_bit(FLAG_ENABLED, &ai->flags);
3620 }
3621 if (lock)
3622 up(&ai->sem);
3623}
3624
3625static void enable_interrupts( struct airo_info *ai ) {
3626 /* Enable the interrupts */
3627 OUT4500( ai, EVINTEN, STATUS_INTS );
3628}
3629
3630static void disable_interrupts( struct airo_info *ai ) {
3631 OUT4500( ai, EVINTEN, 0 );
3632}
3633
3634static void mpi_receive_802_3(struct airo_info *ai)
3635{
3636 RxFid rxd;
3637 int len = 0;
3638 struct sk_buff *skb;
3639 char *buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003640 int off = 0;
3641 MICBuffer micbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003642
3643 memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
3644 /* Make sure we got something */
3645 if (rxd.rdy && rxd.valid == 0) {
3646 len = rxd.len + 12;
3647 if (len < 12 || len > 2048)
3648 goto badrx;
3649
3650 skb = dev_alloc_skb(len);
3651 if (!skb) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003652 ai->dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003653 goto badrx;
3654 }
3655 buffer = skb_put(skb,len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003656 memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2);
3657 if (ai->micstats.enabled) {
3658 memcpy(&micbuf,
3659 ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2,
3660 sizeof(micbuf));
3661 if (ntohs(micbuf.typelen) <= 0x05DC) {
3662 if (len <= sizeof(micbuf) + ETH_ALEN * 2)
3663 goto badmic;
3664
3665 off = sizeof(micbuf);
3666 skb_trim (skb, len - off);
3667 }
3668 }
3669 memcpy(buffer + ETH_ALEN * 2,
3670 ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off,
3671 len - ETH_ALEN * 2 - off);
3672 if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) {
3673badmic:
3674 dev_kfree_skb_irq (skb);
3675 goto badrx;
3676 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003677#ifdef WIRELESS_SPY
3678 if (ai->spy_data.spy_number > 0) {
3679 char *sa;
3680 struct iw_quality wstats;
3681 /* Prepare spy data : addr + qual */
3682 sa = buffer + ETH_ALEN;
3683 wstats.qual = 0; /* XXX Where do I get that info from ??? */
3684 wstats.level = 0;
3685 wstats.updated = 0;
3686 /* Update spy records */
3687 wireless_spy_update(ai->dev, sa, &wstats);
3688 }
3689#endif /* WIRELESS_SPY */
3690
Linus Torvalds1da177e2005-04-16 15:20:36 -07003691 skb->ip_summed = CHECKSUM_NONE;
3692 skb->protocol = eth_type_trans(skb, ai->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003693 netif_rx(skb);
3694 }
3695badrx:
3696 if (rxd.valid == 0) {
3697 rxd.valid = 1;
3698 rxd.rdy = 0;
3699 rxd.len = PKTSIZE;
3700 memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
3701 }
3702}
3703
Hannes Eder2ed5ba82008-12-26 00:12:59 -08003704static void mpi_receive_802_11(struct airo_info *ai)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003705{
3706 RxFid rxd;
3707 struct sk_buff *skb = NULL;
Al Viro0300b332007-12-19 22:38:33 -05003708 u16 len, hdrlen = 0;
3709 __le16 fc;
Dan Williamsf55d4512009-01-24 09:04:12 -05003710 struct rx_hdr hdr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003711 u16 gap;
3712 u16 *buffer;
Dan Williamsf55d4512009-01-24 09:04:12 -05003713 char *ptr = ai->rxfids[0].virtual_host_addr + 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003714
3715 memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
3716 memcpy ((char *)&hdr, ptr, sizeof(hdr));
3717 ptr += sizeof(hdr);
3718 /* Bad CRC. Ignore packet */
3719 if (le16_to_cpu(hdr.status) & 2)
3720 hdr.len = 0;
3721 if (ai->wifidev == NULL)
3722 hdr.len = 0;
3723 len = le16_to_cpu(hdr.len);
Dan Williams15db2762006-03-16 13:46:27 -05003724 if (len > AIRO_DEF_MTU) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003725 airo_print_err(ai->dev->name, "Bad size %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003726 goto badrx;
3727 }
3728 if (len == 0)
3729 goto badrx;
3730
Al Viro0300b332007-12-19 22:38:33 -05003731 fc = get_unaligned((__le16 *)ptr);
3732 hdrlen = header_len(fc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733
3734 skb = dev_alloc_skb( len + hdrlen + 2 );
3735 if ( !skb ) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003736 ai->dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003737 goto badrx;
3738 }
3739 buffer = (u16*)skb_put (skb, len + hdrlen);
3740 memcpy ((char *)buffer, ptr, hdrlen);
3741 ptr += hdrlen;
3742 if (hdrlen == 24)
3743 ptr += 6;
Harvey Harrison533dd1b2008-04-29 01:03:36 -07003744 gap = get_unaligned_le16(ptr);
Al Viro593c2b92007-12-17 15:09:34 -05003745 ptr += sizeof(__le16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003746 if (gap) {
3747 if (gap <= 8)
3748 ptr += gap;
3749 else
Dan Williams934d8bf2006-03-16 13:46:23 -05003750 airo_print_err(ai->dev->name,
3751 "gaplen too big. Problems will follow...");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752 }
3753 memcpy ((char *)buffer + hdrlen, ptr, len);
3754 ptr += len;
3755#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
3756 if (ai->spy_data.spy_number > 0) {
3757 char *sa;
3758 struct iw_quality wstats;
3759 /* Prepare spy data : addr + qual */
3760 sa = (char*)buffer + 10;
3761 wstats.qual = hdr.rssi[0];
3762 if (ai->rssi)
3763 wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
3764 else
3765 wstats.level = (hdr.rssi[1] + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04003766 wstats.noise = ai->wstats.qual.noise;
3767 wstats.updated = IW_QUAL_QUAL_UPDATED
3768 | IW_QUAL_LEVEL_UPDATED
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07003769 | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003770 /* Update spy records */
3771 wireless_spy_update(ai->dev, sa, &wstats);
3772 }
3773#endif /* IW_WIRELESS_SPY */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07003774 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003775 skb->pkt_type = PACKET_OTHERHOST;
3776 skb->dev = ai->wifidev;
3777 skb->protocol = htons(ETH_P_802_2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003778 skb->ip_summed = CHECKSUM_NONE;
3779 netif_rx( skb );
Dan Williamsf55d4512009-01-24 09:04:12 -05003780
Linus Torvalds1da177e2005-04-16 15:20:36 -07003781badrx:
3782 if (rxd.valid == 0) {
3783 rxd.valid = 1;
3784 rxd.rdy = 0;
3785 rxd.len = PKTSIZE;
3786 memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
3787 }
3788}
3789
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02003790static inline void set_auth_type(struct airo_info *local, int auth_type)
3791{
3792 local->config.authType = auth_type;
3793 /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT).
3794 * Used by airo_set_auth()
3795 */
3796 if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT)
3797 local->last_auth = auth_type;
3798}
3799
Linus Torvalds1da177e2005-04-16 15:20:36 -07003800static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
3801{
3802 Cmd cmd;
3803 Resp rsp;
3804 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003805 SsidRid mySsid;
Al Viro4293ea32007-12-19 19:21:51 -05003806 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003807 WepKeyRid wkr;
3808 int rc;
3809
3810 memset( &mySsid, 0, sizeof( mySsid ) );
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003811 kfree (ai->flash);
3812 ai->flash = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003813
3814 /* The NOP is the first step in getting the card going */
3815 cmd.cmd = NOP;
3816 cmd.parm0 = cmd.parm1 = cmd.parm2 = 0;
3817 if (lock && down_interruptible(&ai->sem))
3818 return ERROR;
3819 if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) {
3820 if (lock)
3821 up(&ai->sem);
3822 return ERROR;
3823 }
3824 disable_MAC( ai, 0);
3825
3826 // Let's figure out if we need to use the AUX port
3827 if (!test_bit(FLAG_MPI,&ai->flags)) {
3828 cmd.cmd = CMD_ENABLEAUX;
3829 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
3830 if (lock)
3831 up(&ai->sem);
Dan Williams934d8bf2006-03-16 13:46:23 -05003832 airo_print_err(ai->dev->name, "Error checking for AUX port");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003833 return ERROR;
3834 }
3835 if (!aux_bap || rsp.status & 0xff00) {
3836 ai->bap_read = fast_bap_read;
Dan Williams934d8bf2006-03-16 13:46:23 -05003837 airo_print_dbg(ai->dev->name, "Doing fast bap_reads");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003838 } else {
3839 ai->bap_read = aux_bap_read;
Dan Williams934d8bf2006-03-16 13:46:23 -05003840 airo_print_dbg(ai->dev->name, "Doing AUX bap_reads");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003841 }
3842 }
3843 if (lock)
3844 up(&ai->sem);
3845 if (ai->config.len == 0) {
Hannes Eder49c4a5d2009-02-14 11:49:09 +00003846 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003847 tdsRssiRid rssi_rid;
3848 CapabilityRid cap_rid;
3849
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003850 kfree(ai->APList);
3851 ai->APList = NULL;
3852 kfree(ai->SSID);
3853 ai->SSID = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003854 // general configuration (read/modify/write)
3855 status = readConfigRid(ai, lock);
3856 if ( status != SUCCESS ) return ERROR;
3857
3858 status = readCapabilityRid(ai, &cap_rid, lock);
3859 if ( status != SUCCESS ) return ERROR;
3860
3861 status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),lock);
3862 if ( status == SUCCESS ) {
3863 if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
Dan Williams41480af2005-05-10 09:45:51 -04003864 memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003865 }
3866 else {
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003867 kfree(ai->rssi);
3868 ai->rssi = NULL;
Al Viro56d81bd2007-12-20 17:18:35 -05003869 if (cap_rid.softCap & cpu_to_le16(8))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003870 ai->config.rmode |= RXMODE_NORMALIZED_RSSI;
3871 else
Dan Williams934d8bf2006-03-16 13:46:23 -05003872 airo_print_warn(ai->dev->name, "unknown received signal "
3873 "level scale");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003874 }
3875 ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02003876 set_auth_type(ai, AUTH_OPEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003877 ai->config.modulation = MOD_CCK;
3878
Al Viro56d81bd2007-12-20 17:18:35 -05003879 if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) &&
Al Viro15617852007-12-20 17:21:36 -05003880 (cap_rid.extSoftCap & cpu_to_le16(1)) &&
Al Viro56d81bd2007-12-20 17:18:35 -05003881 micsetup(ai) == SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003882 ai->config.opmode |= MODE_MIC;
3883 set_bit(FLAG_MIC_CAPABLE, &ai->flags);
3884 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003885
3886 /* Save off the MAC */
3887 for( i = 0; i < ETH_ALEN; i++ ) {
3888 mac[i] = ai->config.macAddr[i];
3889 }
3890
3891 /* Check to see if there are any insmod configured
3892 rates to add */
3893 if ( rates[0] ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003894 memset(ai->config.rates,0,sizeof(ai->config.rates));
3895 for( i = 0; i < 8 && rates[i]; i++ ) {
3896 ai->config.rates[i] = rates[i];
3897 }
3898 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003899 set_bit (FLAG_COMMIT, &ai->flags);
3900 }
3901
3902 /* Setup the SSIDs if present */
3903 if ( ssids[0] ) {
3904 int i;
3905 for( i = 0; i < 3 && ssids[i]; i++ ) {
Al Viro0dd22122007-12-17 16:11:57 -05003906 size_t len = strlen(ssids[i]);
3907 if (len > 32)
3908 len = 32;
3909 mySsid.ssids[i].len = cpu_to_le16(len);
3910 memcpy(mySsid.ssids[i].ssid, ssids[i], len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003911 }
Al Viro0dd22122007-12-17 16:11:57 -05003912 mySsid.len = cpu_to_le16(sizeof(mySsid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003913 }
3914
3915 status = writeConfigRid(ai, lock);
3916 if ( status != SUCCESS ) return ERROR;
3917
3918 /* Set up the SSID list */
3919 if ( ssids[0] ) {
3920 status = writeSsidRid(ai, &mySsid, lock);
3921 if ( status != SUCCESS ) return ERROR;
3922 }
3923
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003924 status = enable_MAC(ai, lock);
3925 if (status != SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003926 return ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003927
3928 /* Grab the initial wep key, we gotta save it for auto_wep */
3929 rc = readWepKeyRid(ai, &wkr, 1, lock);
3930 if (rc == SUCCESS) do {
3931 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05003932 if (wkr.kindex == cpu_to_le16(0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003933 ai->defindex = wkr.mac[0];
3934 }
3935 rc = readWepKeyRid(ai, &wkr, 0, lock);
3936 } while(lastindex != wkr.kindex);
3937
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02003938 try_auto_wep(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003939
3940 return SUCCESS;
3941}
3942
3943static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) {
3944 // Im really paranoid about letting it run forever!
3945 int max_tries = 600000;
3946
3947 if (IN4500(ai, EVSTAT) & EV_CMD)
3948 OUT4500(ai, EVACK, EV_CMD);
3949
3950 OUT4500(ai, PARAM0, pCmd->parm0);
3951 OUT4500(ai, PARAM1, pCmd->parm1);
3952 OUT4500(ai, PARAM2, pCmd->parm2);
3953 OUT4500(ai, COMMAND, pCmd->cmd);
3954
3955 while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) {
3956 if ((IN4500(ai, COMMAND)) == pCmd->cmd)
3957 // PC4500 didn't notice command, try again
3958 OUT4500(ai, COMMAND, pCmd->cmd);
3959 if (!in_atomic() && (max_tries & 255) == 0)
3960 schedule();
3961 }
3962
3963 if ( max_tries == -1 ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003964 airo_print_err(ai->dev->name,
Lucas De Marchi25985ed2011-03-30 22:57:33 -03003965 "Max tries exceeded when issuing command");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003966 if (IN4500(ai, COMMAND) & COMMAND_BUSY)
3967 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
3968 return ERROR;
3969 }
3970
3971 // command completed
3972 pRsp->status = IN4500(ai, STATUS);
3973 pRsp->rsp0 = IN4500(ai, RESP0);
3974 pRsp->rsp1 = IN4500(ai, RESP1);
3975 pRsp->rsp2 = IN4500(ai, RESP2);
Robert Schulze13dca9b2006-07-10 18:37:44 +02003976 if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET)
3977 airo_print_err(ai->dev->name,
3978 "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x",
3979 pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1,
3980 pRsp->rsp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003981
3982 // clear stuck command busy if necessary
3983 if (IN4500(ai, COMMAND) & COMMAND_BUSY) {
3984 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
3985 }
3986 // acknowledge processing the status/response
3987 OUT4500(ai, EVACK, EV_CMD);
3988
3989 return SUCCESS;
3990}
3991
3992/* Sets up the bap to start exchange data. whichbap should
3993 * be one of the BAP0 or BAP1 defines. Locks should be held before
3994 * calling! */
3995static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap )
3996{
3997 int timeout = 50;
3998 int max_tries = 3;
3999
4000 OUT4500(ai, SELECT0+whichbap, rid);
4001 OUT4500(ai, OFFSET0+whichbap, offset);
4002 while (1) {
4003 int status = IN4500(ai, OFFSET0+whichbap);
4004 if (status & BAP_BUSY) {
4005 /* This isn't really a timeout, but its kinda
4006 close */
4007 if (timeout--) {
4008 continue;
4009 }
4010 } else if ( status & BAP_ERR ) {
4011 /* invalid rid or offset */
Dan Williams934d8bf2006-03-16 13:46:23 -05004012 airo_print_err(ai->dev->name, "BAP error %x %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07004013 status, whichbap );
4014 return ERROR;
4015 } else if (status & BAP_DONE) { // success
4016 return SUCCESS;
4017 }
4018 if ( !(max_tries--) ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004019 airo_print_err(ai->dev->name,
Michal Schmidt1138c372007-06-29 15:33:41 +02004020 "BAP setup error too many retries\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004021 return ERROR;
4022 }
4023 // -- PC4500 missed it, try again
4024 OUT4500(ai, SELECT0+whichbap, rid);
4025 OUT4500(ai, OFFSET0+whichbap, offset);
4026 timeout = 50;
4027 }
4028}
4029
4030/* should only be called by aux_bap_read. This aux function and the
4031 following use concepts not documented in the developers guide. I
4032 got them from a patch given to my by Aironet */
4033static u16 aux_setup(struct airo_info *ai, u16 page,
4034 u16 offset, u16 *len)
4035{
4036 u16 next;
4037
4038 OUT4500(ai, AUXPAGE, page);
4039 OUT4500(ai, AUXOFF, 0);
4040 next = IN4500(ai, AUXDATA);
4041 *len = IN4500(ai, AUXDATA)&0xff;
4042 if (offset != 4) OUT4500(ai, AUXOFF, offset);
4043 return next;
4044}
4045
4046/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004047static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004048 int bytelen, int whichbap)
4049{
4050 u16 len;
4051 u16 page;
4052 u16 offset;
4053 u16 next;
4054 int words;
4055 int i;
4056 unsigned long flags;
4057
4058 spin_lock_irqsave(&ai->aux_lock, flags);
4059 page = IN4500(ai, SWS0+whichbap);
4060 offset = IN4500(ai, SWS2+whichbap);
4061 next = aux_setup(ai, page, offset, &len);
4062 words = (bytelen+1)>>1;
4063
4064 for (i=0; i<words;) {
4065 int count;
4066 count = (len>>1) < (words-i) ? (len>>1) : (words-i);
4067 if ( !do8bitIO )
4068 insw( ai->dev->base_addr+DATA0+whichbap,
4069 pu16Dst+i,count );
4070 else
4071 insb( ai->dev->base_addr+DATA0+whichbap,
4072 pu16Dst+i, count << 1 );
4073 i += count;
4074 if (i<words) {
4075 next = aux_setup(ai, next, 4, &len);
4076 }
4077 }
4078 spin_unlock_irqrestore(&ai->aux_lock, flags);
4079 return SUCCESS;
4080}
4081
4082
4083/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004084static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004085 int bytelen, int whichbap)
4086{
4087 bytelen = (bytelen + 1) & (~1); // round up to even value
4088 if ( !do8bitIO )
4089 insw( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1 );
4090 else
4091 insb( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen );
4092 return SUCCESS;
4093}
4094
4095/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004096static int bap_write(struct airo_info *ai, const __le16 *pu16Src,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004097 int bytelen, int whichbap)
4098{
4099 bytelen = (bytelen + 1) & (~1); // round up to even value
4100 if ( !do8bitIO )
4101 outsw( ai->dev->base_addr+DATA0+whichbap,
4102 pu16Src, bytelen>>1 );
4103 else
4104 outsb( ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen );
4105 return SUCCESS;
4106}
4107
4108static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd)
4109{
4110 Cmd cmd; /* for issuing commands */
4111 Resp rsp; /* response from commands */
4112 u16 status;
4113
4114 memset(&cmd, 0, sizeof(cmd));
4115 cmd.cmd = accmd;
4116 cmd.parm0 = rid;
4117 status = issuecommand(ai, &cmd, &rsp);
4118 if (status != 0) return status;
4119 if ( (rsp.status & 0x7F00) != 0) {
4120 return (accmd << 8) + (rsp.rsp0 & 0xFF);
4121 }
4122 return 0;
4123}
4124
4125/* Note, that we are using BAP1 which is also used by transmit, so
4126 * we must get a lock. */
4127static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock)
4128{
4129 u16 status;
4130 int rc = SUCCESS;
4131
4132 if (lock) {
4133 if (down_interruptible(&ai->sem))
4134 return ERROR;
4135 }
4136 if (test_bit(FLAG_MPI,&ai->flags)) {
4137 Cmd cmd;
4138 Resp rsp;
4139
4140 memset(&cmd, 0, sizeof(cmd));
4141 memset(&rsp, 0, sizeof(rsp));
4142 ai->config_desc.rid_desc.valid = 1;
4143 ai->config_desc.rid_desc.len = RIDSIZE;
4144 ai->config_desc.rid_desc.rid = 0;
4145 ai->config_desc.rid_desc.host_addr = ai->ridbus;
4146
4147 cmd.cmd = CMD_ACCESS;
4148 cmd.parm0 = rid;
4149
4150 memcpy_toio(ai->config_desc.card_ram_off,
4151 &ai->config_desc.rid_desc, sizeof(Rid));
4152
4153 rc = issuecommand(ai, &cmd, &rsp);
4154
4155 if (rsp.status & 0x7f00)
4156 rc = rsp.rsp0;
4157 if (!rc)
4158 memcpy(pBuf, ai->config_desc.virtual_host_addr, len);
4159 goto done;
4160 } else {
4161 if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) {
4162 rc = status;
4163 goto done;
4164 }
4165 if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
4166 rc = ERROR;
4167 goto done;
4168 }
4169 // read the rid length field
4170 bap_read(ai, pBuf, 2, BAP1);
4171 // length for remaining part of rid
Al Viro593c2b92007-12-17 15:09:34 -05004172 len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004173
4174 if ( len <= 2 ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004175 airo_print_err(ai->dev->name,
4176 "Rid %x has a length of %d which is too short",
Linus Torvalds1da177e2005-04-16 15:20:36 -07004177 (int)rid, (int)len );
4178 rc = ERROR;
4179 goto done;
4180 }
4181 // read remainder of the rid
Al Virob8c06bc2007-12-19 17:55:43 -05004182 rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004183 }
4184done:
4185 if (lock)
4186 up(&ai->sem);
4187 return rc;
4188}
4189
4190/* Note, that we are using BAP1 which is also used by transmit, so
Lucas De Marchi25985ed2011-03-30 22:57:33 -03004191 * make sure this isn't called when a transmit is happening */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004192static int PC4500_writerid(struct airo_info *ai, u16 rid,
4193 const void *pBuf, int len, int lock)
4194{
4195 u16 status;
4196 int rc = SUCCESS;
4197
Al Viro593c2b92007-12-17 15:09:34 -05004198 *(__le16*)pBuf = cpu_to_le16((u16)len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004199
4200 if (lock) {
4201 if (down_interruptible(&ai->sem))
4202 return ERROR;
4203 }
4204 if (test_bit(FLAG_MPI,&ai->flags)) {
4205 Cmd cmd;
4206 Resp rsp;
4207
Dan Streetmanf89b2322005-11-11 11:41:42 -05004208 if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid))
Dan Williams934d8bf2006-03-16 13:46:23 -05004209 airo_print_err(ai->dev->name,
4210 "%s: MAC should be disabled (rid=%04x)",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004211 __func__, rid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004212 memset(&cmd, 0, sizeof(cmd));
4213 memset(&rsp, 0, sizeof(rsp));
4214
4215 ai->config_desc.rid_desc.valid = 1;
4216 ai->config_desc.rid_desc.len = *((u16 *)pBuf);
4217 ai->config_desc.rid_desc.rid = 0;
4218
4219 cmd.cmd = CMD_WRITERID;
4220 cmd.parm0 = rid;
4221
4222 memcpy_toio(ai->config_desc.card_ram_off,
4223 &ai->config_desc.rid_desc, sizeof(Rid));
4224
4225 if (len < 4 || len > 2047) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004226 airo_print_err(ai->dev->name, "%s: len=%d", __func__, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004227 rc = -1;
4228 } else {
Joe Perches2c208892012-06-04 12:44:17 +00004229 memcpy(ai->config_desc.virtual_host_addr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004230 pBuf, len);
4231
4232 rc = issuecommand(ai, &cmd, &rsp);
4233 if ((rc & 0xff00) != 0) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004234 airo_print_err(ai->dev->name, "%s: Write rid Error %d",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004235 __func__, rc);
Dan Williams934d8bf2006-03-16 13:46:23 -05004236 airo_print_err(ai->dev->name, "%s: Cmd=%04x",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004237 __func__, cmd.cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004238 }
4239
4240 if ((rsp.status & 0x7f00))
4241 rc = rsp.rsp0;
4242 }
4243 } else {
4244 // --- first access so that we can write the rid data
4245 if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) {
4246 rc = status;
4247 goto done;
4248 }
4249 // --- now write the rid data
4250 if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
4251 rc = ERROR;
4252 goto done;
4253 }
4254 bap_write(ai, pBuf, len, BAP1);
4255 // ---now commit the rid data
4256 rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS);
4257 }
4258done:
4259 if (lock)
4260 up(&ai->sem);
4261 return rc;
4262}
4263
4264/* Allocates a FID to be used for transmitting packets. We only use
4265 one for now. */
4266static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw)
4267{
4268 unsigned int loop = 3000;
4269 Cmd cmd;
4270 Resp rsp;
4271 u16 txFid;
Al Viro593c2b92007-12-17 15:09:34 -05004272 __le16 txControl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004273
4274 cmd.cmd = CMD_ALLOCATETX;
4275 cmd.parm0 = lenPayload;
4276 if (down_interruptible(&ai->sem))
4277 return ERROR;
4278 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
4279 txFid = ERROR;
4280 goto done;
4281 }
4282 if ( (rsp.status & 0xFF00) != 0) {
4283 txFid = ERROR;
4284 goto done;
4285 }
4286 /* wait for the allocate event/indication
4287 * It makes me kind of nervous that this can just sit here and spin,
4288 * but in practice it only loops like four times. */
4289 while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop);
4290 if (!loop) {
4291 txFid = ERROR;
4292 goto done;
4293 }
4294
4295 // get the allocated fid and acknowledge
4296 txFid = IN4500(ai, TXALLOCFID);
4297 OUT4500(ai, EVACK, EV_ALLOC);
4298
4299 /* The CARD is pretty cool since it converts the ethernet packet
4300 * into 802.11. Also note that we don't release the FID since we
4301 * will be using the same one over and over again. */
4302 /* We only have to setup the control once since we are not
4303 * releasing the fid. */
4304 if (raw)
4305 txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11
4306 | TXCTL_ETHERNET | TXCTL_NORELEASE);
4307 else
4308 txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3
4309 | TXCTL_ETHERNET | TXCTL_NORELEASE);
4310 if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS)
4311 txFid = ERROR;
4312 else
4313 bap_write(ai, &txControl, sizeof(txControl), BAP1);
4314
4315done:
4316 up(&ai->sem);
4317
4318 return txFid;
4319}
4320
4321/* In general BAP1 is dedicated to transmiting packets. However,
4322 since we need a BAP when accessing RIDs, we also use BAP1 for that.
4323 Make sure the BAP1 spinlock is held when this is called. */
4324static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket)
4325{
Al Viro593c2b92007-12-17 15:09:34 -05004326 __le16 payloadLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004327 Cmd cmd;
4328 Resp rsp;
4329 int miclen = 0;
4330 u16 txFid = len;
4331 MICBuffer pMic;
4332
4333 len >>= 16;
4334
4335 if (len <= ETH_ALEN * 2) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004336 airo_print_warn(ai->dev->name, "Short packet %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004337 return ERROR;
4338 }
4339 len -= ETH_ALEN * 2;
4340
Linus Torvalds1da177e2005-04-16 15:20:36 -07004341 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
Al Viro593c2b92007-12-17 15:09:34 -05004342 (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004343 if (encapsulate(ai,(etherHead *)pPacket,&pMic,len) != SUCCESS)
4344 return ERROR;
4345 miclen = sizeof(pMic);
4346 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004347 // packet is destination[6], source[6], payload[len-12]
4348 // write the payload length and dst/src/payload
4349 if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR;
4350 /* The hardware addresses aren't counted as part of the payload, so
4351 * we have to subtract the 12 bytes for the addresses off */
4352 payloadLen = cpu_to_le16(len + miclen);
4353 bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
Al Virob8c06bc2007-12-19 17:55:43 -05004354 bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004355 if (miclen)
Al Virob8c06bc2007-12-19 17:55:43 -05004356 bap_write(ai, (__le16*)&pMic, miclen, BAP1);
4357 bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004358 // issue the transmit command
4359 memset( &cmd, 0, sizeof( cmd ) );
4360 cmd.cmd = CMD_TRANSMIT;
4361 cmd.parm0 = txFid;
4362 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
4363 if ( (rsp.status & 0xFF00) != 0) return ERROR;
4364 return SUCCESS;
4365}
4366
4367static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket)
4368{
Al Viro593c2b92007-12-17 15:09:34 -05004369 __le16 fc, payloadLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004370 Cmd cmd;
4371 Resp rsp;
4372 int hdrlen;
Al Viro977b1432007-12-19 16:45:29 -05004373 static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6};
4374 /* padding of header to full size + le16 gaplen (6) + gaplen bytes */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004375 u16 txFid = len;
4376 len >>= 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004377
Al Viro0300b332007-12-19 22:38:33 -05004378 fc = *(__le16*)pPacket;
4379 hdrlen = header_len(fc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004380
4381 if (len < hdrlen) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004382 airo_print_warn(ai->dev->name, "Short packet %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004383 return ERROR;
4384 }
4385
4386 /* packet is 802.11 header + payload
4387 * write the payload length and dst/src/payload */
4388 if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR;
4389 /* The 802.11 header aren't counted as part of the payload, so
4390 * we have to subtract the header bytes off */
4391 payloadLen = cpu_to_le16(len-hdrlen);
4392 bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
4393 if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR;
Al Virob8c06bc2007-12-19 17:55:43 -05004394 bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1);
4395 bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004396
Al Virob8c06bc2007-12-19 17:55:43 -05004397 bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004398 // issue the transmit command
4399 memset( &cmd, 0, sizeof( cmd ) );
4400 cmd.cmd = CMD_TRANSMIT;
4401 cmd.parm0 = txFid;
4402 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
4403 if ( (rsp.status & 0xFF00) != 0) return ERROR;
4404 return SUCCESS;
4405}
4406
4407/*
4408 * This is the proc_fs routines. It is a bit messier than I would
4409 * like! Feel free to clean it up!
4410 */
4411
4412static ssize_t proc_read( struct file *file,
4413 char __user *buffer,
4414 size_t len,
4415 loff_t *offset);
4416
4417static ssize_t proc_write( struct file *file,
4418 const char __user *buffer,
4419 size_t len,
4420 loff_t *offset );
4421static int proc_close( struct inode *inode, struct file *file );
4422
4423static int proc_stats_open( struct inode *inode, struct file *file );
4424static int proc_statsdelta_open( struct inode *inode, struct file *file );
4425static int proc_status_open( struct inode *inode, struct file *file );
4426static int proc_SSID_open( struct inode *inode, struct file *file );
4427static int proc_APList_open( struct inode *inode, struct file *file );
4428static int proc_BSSList_open( struct inode *inode, struct file *file );
4429static int proc_config_open( struct inode *inode, struct file *file );
4430static int proc_wepkey_open( struct inode *inode, struct file *file );
4431
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004432static const struct file_operations proc_statsdelta_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004433 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004434 .read = proc_read,
4435 .open = proc_statsdelta_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +02004436 .release = proc_close,
4437 .llseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004438};
4439
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004440static const struct file_operations proc_stats_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004441 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004442 .read = proc_read,
4443 .open = proc_stats_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +02004444 .release = proc_close,
4445 .llseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004446};
4447
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004448static const struct file_operations proc_status_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004449 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004450 .read = proc_read,
4451 .open = proc_status_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +02004452 .release = proc_close,
4453 .llseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004454};
4455
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004456static const struct file_operations proc_SSID_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004457 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004458 .read = proc_read,
4459 .write = proc_write,
4460 .open = proc_SSID_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +02004461 .release = proc_close,
4462 .llseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004463};
4464
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004465static const struct file_operations proc_BSSList_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004466 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004467 .read = proc_read,
4468 .write = proc_write,
4469 .open = proc_BSSList_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +02004470 .release = proc_close,
4471 .llseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004472};
4473
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004474static const struct file_operations proc_APList_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004475 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004476 .read = proc_read,
4477 .write = proc_write,
4478 .open = proc_APList_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +02004479 .release = proc_close,
4480 .llseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004481};
4482
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004483static const struct file_operations proc_config_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004484 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004485 .read = proc_read,
4486 .write = proc_write,
4487 .open = proc_config_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +02004488 .release = proc_close,
4489 .llseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004490};
4491
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004492static const struct file_operations proc_wepkey_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004493 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004494 .read = proc_read,
4495 .write = proc_write,
4496 .open = proc_wepkey_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +02004497 .release = proc_close,
4498 .llseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004499};
4500
4501static struct proc_dir_entry *airo_entry;
4502
4503struct proc_data {
4504 int release_buffer;
4505 int readlen;
4506 char *rbuffer;
4507 int writelen;
4508 int maxwritelen;
4509 char *wbuffer;
4510 void (*on_close) (struct inode *, struct file *);
4511};
4512
Linus Torvalds1da177e2005-04-16 15:20:36 -07004513static int setup_proc_entry( struct net_device *dev,
4514 struct airo_info *apriv ) {
4515 struct proc_dir_entry *entry;
Eric W. Biederman1efa29c2012-02-10 14:01:03 -08004516
Linus Torvalds1da177e2005-04-16 15:20:36 -07004517 /* First setup the device directory */
4518 strcpy(apriv->proc_name,dev->name);
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004519 apriv->proc_entry = proc_mkdir_mode(apriv->proc_name, airo_perm,
4520 airo_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004521 if (!apriv->proc_entry)
David Howellsb25f7742013-04-12 03:05:20 +01004522 return -ENOMEM;
David Howells271a15e2013-04-12 00:38:51 +01004523 proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004524
4525 /* Setup the StatsDelta */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004526 entry = proc_create_data("StatsDelta", S_IRUGO & proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004527 apriv->proc_entry, &proc_statsdelta_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004528 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004529 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004530 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004531
4532 /* Setup the Stats */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004533 entry = proc_create_data("Stats", S_IRUGO & proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004534 apriv->proc_entry, &proc_stats_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004535 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004536 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004537 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004538
4539 /* Setup the Status */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004540 entry = proc_create_data("Status", S_IRUGO & proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004541 apriv->proc_entry, &proc_status_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004542 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004543 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004544 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004545
4546 /* Setup the Config */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004547 entry = proc_create_data("Config", proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004548 apriv->proc_entry, &proc_config_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004549 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004550 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004551 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004552
4553 /* Setup the SSID */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004554 entry = proc_create_data("SSID", proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004555 apriv->proc_entry, &proc_SSID_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004556 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004557 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004558 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004559
4560 /* Setup the APList */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004561 entry = proc_create_data("APList", proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004562 apriv->proc_entry, &proc_APList_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004563 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004564 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004565 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004566
4567 /* Setup the BSSList */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004568 entry = proc_create_data("BSSList", proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004569 apriv->proc_entry, &proc_BSSList_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004570 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004571 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004572 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004573
4574 /* Setup the WepKey */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004575 entry = proc_create_data("WepKey", proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004576 apriv->proc_entry, &proc_wepkey_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004577 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004578 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004579 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004580 return 0;
Florin Malita431aca52006-10-10 16:46:30 -04004581
Florin Malita431aca52006-10-10 16:46:30 -04004582fail:
David Howellsb25f7742013-04-12 03:05:20 +01004583 remove_proc_subtree(apriv->proc_name, airo_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004584 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004585}
4586
4587static int takedown_proc_entry( struct net_device *dev,
David Howellsb25f7742013-04-12 03:05:20 +01004588 struct airo_info *apriv )
4589{
4590 remove_proc_subtree(apriv->proc_name, airo_entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004591 return 0;
4592}
4593
4594/*
4595 * What we want from the proc_fs is to be able to efficiently read
4596 * and write the configuration. To do this, we want to read the
4597 * configuration when the file is opened and write it when the file is
4598 * closed. So basically we allocate a read buffer at open and fill it
4599 * with data, and allocate a write buffer and read it at close.
4600 */
4601
4602/*
4603 * The read routine is generic, it relies on the preallocated rbuffer
4604 * to supply the data.
4605 */
4606static ssize_t proc_read( struct file *file,
4607 char __user *buffer,
4608 size_t len,
4609 loff_t *offset )
4610{
Akinobu Mitacc0d9ff2008-06-09 16:44:30 -07004611 struct proc_data *priv = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004612
4613 if (!priv->rbuffer)
4614 return -EINVAL;
4615
Akinobu Mitacc0d9ff2008-06-09 16:44:30 -07004616 return simple_read_from_buffer(buffer, len, offset, priv->rbuffer,
4617 priv->readlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004618}
4619
4620/*
4621 * The write routine is generic, it fills in a preallocated rbuffer
4622 * to supply the data.
4623 */
4624static ssize_t proc_write( struct file *file,
4625 const char __user *buffer,
4626 size_t len,
4627 loff_t *offset )
4628{
Akinobu Mitaad9082a2010-12-25 15:03:58 +09004629 ssize_t ret;
Joe Perches57674302010-07-12 13:50:06 -07004630 struct proc_data *priv = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004631
4632 if (!priv->wbuffer)
4633 return -EINVAL;
4634
Akinobu Mitaad9082a2010-12-25 15:03:58 +09004635 ret = simple_write_to_buffer(priv->wbuffer, priv->maxwritelen, offset,
4636 buffer, len);
4637 if (ret > 0)
4638 priv->writelen = max_t(int, priv->writelen, *offset);
4639
4640 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004641}
4642
Al Viro329e2c02007-12-20 22:58:57 -05004643static int proc_status_open(struct inode *inode, struct file *file)
4644{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004645 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04004646 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08004647 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004648 CapabilityRid cap_rid;
4649 StatusRid status_rid;
Al Viro329e2c02007-12-20 22:58:57 -05004650 u16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004651 int i;
4652
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01004653 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004654 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07004655 data = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004656 if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
4657 kfree (file->private_data);
4658 return -ENOMEM;
4659 }
4660
4661 readStatusRid(apriv, &status_rid, 1);
4662 readCapabilityRid(apriv, &cap_rid, 1);
4663
Al Viro329e2c02007-12-20 22:58:57 -05004664 mode = le16_to_cpu(status_rid.mode);
4665
Linus Torvalds1da177e2005-04-16 15:20:36 -07004666 i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n",
Al Viro329e2c02007-12-20 22:58:57 -05004667 mode & 1 ? "CFG ": "",
4668 mode & 2 ? "ACT ": "",
4669 mode & 0x10 ? "SYN ": "",
4670 mode & 0x20 ? "LNK ": "",
4671 mode & 0x40 ? "LEAP ": "",
4672 mode & 0x80 ? "PRIV ": "",
4673 mode & 0x100 ? "KEY ": "",
4674 mode & 0x200 ? "WEP ": "",
4675 mode & 0x8000 ? "ERR ": "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004676 sprintf( data->rbuffer+i, "Mode: %x\n"
4677 "Signal Strength: %d\n"
4678 "Signal Quality: %d\n"
4679 "SSID: %-.*s\n"
4680 "AP: %-.16s\n"
4681 "Freq: %d\n"
4682 "BitRate: %dmbs\n"
4683 "Driver Version: %s\n"
4684 "Device: %s\nManufacturer: %s\nFirmware Version: %s\n"
4685 "Radio type: %x\nCountry: %x\nHardware Version: %x\n"
4686 "Software Version: %x\nSoftware Subversion: %x\n"
4687 "Boot block version: %x\n",
Al Viro329e2c02007-12-20 22:58:57 -05004688 le16_to_cpu(status_rid.mode),
4689 le16_to_cpu(status_rid.normalizedSignalStrength),
4690 le16_to_cpu(status_rid.signalQuality),
4691 le16_to_cpu(status_rid.SSIDlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07004692 status_rid.SSID,
4693 status_rid.apName,
Al Viro329e2c02007-12-20 22:58:57 -05004694 le16_to_cpu(status_rid.channel),
4695 le16_to_cpu(status_rid.currentXmitRate) / 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004696 version,
4697 cap_rid.prodName,
4698 cap_rid.manName,
4699 cap_rid.prodVer,
Al Viro56d81bd2007-12-20 17:18:35 -05004700 le16_to_cpu(cap_rid.radioType),
4701 le16_to_cpu(cap_rid.country),
4702 le16_to_cpu(cap_rid.hardVer),
4703 le16_to_cpu(cap_rid.softVer),
4704 le16_to_cpu(cap_rid.softSubVer),
4705 le16_to_cpu(cap_rid.bootBlockVer));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004706 data->readlen = strlen( data->rbuffer );
4707 return 0;
4708}
4709
4710static int proc_stats_rid_open(struct inode*, struct file*, u16);
4711static int proc_statsdelta_open( struct inode *inode,
4712 struct file *file ) {
4713 if (file->f_mode&FMODE_WRITE) {
4714 return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR);
4715 }
4716 return proc_stats_rid_open(inode, file, RID_STATSDELTA);
4717}
4718
4719static int proc_stats_open( struct inode *inode, struct file *file ) {
4720 return proc_stats_rid_open(inode, file, RID_STATS);
4721}
4722
4723static int proc_stats_rid_open( struct inode *inode,
4724 struct file *file,
Al Viroa23ace52007-12-19 22:24:16 -05004725 u16 rid )
4726{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004727 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04004728 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08004729 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004730 StatsRid stats;
4731 int i, j;
Al Viroa23ace52007-12-19 22:24:16 -05004732 __le32 *vals = stats.vals;
John W. Linvillebc8263f2009-02-10 13:53:01 -05004733 int len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004734
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01004735 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004736 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07004737 data = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004738 if ((data->rbuffer = kmalloc( 4096, GFP_KERNEL )) == NULL) {
4739 kfree (file->private_data);
4740 return -ENOMEM;
4741 }
4742
4743 readStatsRid(apriv, &stats, rid, 1);
John W. Linvillebc8263f2009-02-10 13:53:01 -05004744 len = le16_to_cpu(stats.len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004745
4746 j = 0;
Al Viroa23ace52007-12-19 22:24:16 -05004747 for(i=0; statsLabels[i]!=(char *)-1 && i*4<len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004748 if (!statsLabels[i]) continue;
4749 if (j+strlen(statsLabels[i])+16>4096) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004750 airo_print_warn(apriv->dev->name,
Lucas De Marchi25985ed2011-03-30 22:57:33 -03004751 "Potentially disastrous buffer overflow averted!");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004752 break;
4753 }
Al Viroa23ace52007-12-19 22:24:16 -05004754 j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i],
4755 le32_to_cpu(vals[i]));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004756 }
Al Viroa23ace52007-12-19 22:24:16 -05004757 if (i*4 >= len) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004758 airo_print_warn(apriv->dev->name, "Got a short rid");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004759 }
4760 data->readlen = j;
4761 return 0;
4762}
4763
4764static int get_dec_u16( char *buffer, int *start, int limit ) {
4765 u16 value;
4766 int valid = 0;
Roel Kluin30bd5722009-10-26 15:28:11 +01004767 for (value = 0; *start < limit && buffer[*start] >= '0' &&
4768 buffer[*start] <= '9'; (*start)++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004769 valid = 1;
4770 value *= 10;
4771 value += buffer[*start] - '0';
4772 }
4773 if ( !valid ) return -1;
4774 return value;
4775}
4776
4777static int airo_config_commit(struct net_device *dev,
4778 struct iw_request_info *info, void *zwrq,
4779 char *extra);
4780
Al Viro3eb9b412007-12-21 00:00:35 -05004781static inline int sniffing_mode(struct airo_info *ai)
4782{
Michael Buesch1f351e32009-11-25 22:55:11 +01004783 return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >=
Al Viro3eb9b412007-12-21 00:00:35 -05004784 le16_to_cpu(RXMODE_RFMON);
4785}
4786
4787static void proc_config_on_close(struct inode *inode, struct file *file)
4788{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004789 struct proc_data *data = file->private_data;
Al Virod9dda782013-03-31 18:16:14 -04004790 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08004791 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004792 char *line;
4793
4794 if ( !data->writelen ) return;
4795
4796 readConfigRid(ai, 1);
4797 set_bit (FLAG_COMMIT, &ai->flags);
4798
4799 line = data->wbuffer;
4800 while( line[0] ) {
4801/*** Mode processing */
4802 if ( !strncmp( line, "Mode: ", 6 ) ) {
4803 line += 6;
Al Viro3eb9b412007-12-21 00:00:35 -05004804 if (sniffing_mode(ai))
4805 set_bit (FLAG_RESET, &ai->flags);
4806 ai->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004807 clear_bit (FLAG_802_11, &ai->flags);
Al Viro3eb9b412007-12-21 00:00:35 -05004808 ai->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004809 ai->config.scanMode = SCANMODE_ACTIVE;
4810 if ( line[0] == 'a' ) {
Al Viro3eb9b412007-12-21 00:00:35 -05004811 ai->config.opmode |= MODE_STA_IBSS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004812 } else {
Al Viro3eb9b412007-12-21 00:00:35 -05004813 ai->config.opmode |= MODE_STA_ESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004814 if ( line[0] == 'r' ) {
4815 ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
4816 ai->config.scanMode = SCANMODE_PASSIVE;
4817 set_bit (FLAG_802_11, &ai->flags);
4818 } else if ( line[0] == 'y' ) {
4819 ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER;
4820 ai->config.scanMode = SCANMODE_PASSIVE;
4821 set_bit (FLAG_802_11, &ai->flags);
4822 } else if ( line[0] == 'l' )
4823 ai->config.rmode |= RXMODE_LANMON;
4824 }
4825 set_bit (FLAG_COMMIT, &ai->flags);
4826 }
4827
4828/*** Radio status */
4829 else if (!strncmp(line,"Radio: ", 7)) {
4830 line += 7;
4831 if (!strncmp(line,"off",3)) {
4832 set_bit (FLAG_RADIO_OFF, &ai->flags);
4833 } else {
4834 clear_bit (FLAG_RADIO_OFF, &ai->flags);
4835 }
4836 }
4837/*** NodeName processing */
4838 else if ( !strncmp( line, "NodeName: ", 10 ) ) {
4839 int j;
4840
4841 line += 10;
4842 memset( ai->config.nodeName, 0, 16 );
4843/* Do the name, assume a space between the mode and node name */
4844 for( j = 0; j < 16 && line[j] != '\n'; j++ ) {
4845 ai->config.nodeName[j] = line[j];
4846 }
4847 set_bit (FLAG_COMMIT, &ai->flags);
4848 }
4849
4850/*** PowerMode processing */
4851 else if ( !strncmp( line, "PowerMode: ", 11 ) ) {
4852 line += 11;
4853 if ( !strncmp( line, "PSPCAM", 6 ) ) {
4854 ai->config.powerSaveMode = POWERSAVE_PSPCAM;
4855 set_bit (FLAG_COMMIT, &ai->flags);
4856 } else if ( !strncmp( line, "PSP", 3 ) ) {
4857 ai->config.powerSaveMode = POWERSAVE_PSP;
4858 set_bit (FLAG_COMMIT, &ai->flags);
4859 } else {
4860 ai->config.powerSaveMode = POWERSAVE_CAM;
4861 set_bit (FLAG_COMMIT, &ai->flags);
4862 }
4863 } else if ( !strncmp( line, "DataRates: ", 11 ) ) {
4864 int v, i = 0, k = 0; /* i is index into line,
4865 k is index to rates */
4866
4867 line += 11;
4868 while((v = get_dec_u16(line, &i, 3))!=-1) {
4869 ai->config.rates[k++] = (u8)v;
4870 line += i + 1;
4871 i = 0;
4872 }
4873 set_bit (FLAG_COMMIT, &ai->flags);
4874 } else if ( !strncmp( line, "Channel: ", 9 ) ) {
4875 int v, i = 0;
4876 line += 9;
4877 v = get_dec_u16(line, &i, i+3);
4878 if ( v != -1 ) {
Al Viro3eb9b412007-12-21 00:00:35 -05004879 ai->config.channelSet = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004880 set_bit (FLAG_COMMIT, &ai->flags);
4881 }
4882 } else if ( !strncmp( line, "XmitPower: ", 11 ) ) {
4883 int v, i = 0;
4884 line += 11;
4885 v = get_dec_u16(line, &i, i+3);
4886 if ( v != -1 ) {
Al Viro3eb9b412007-12-21 00:00:35 -05004887 ai->config.txPower = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004888 set_bit (FLAG_COMMIT, &ai->flags);
4889 }
4890 } else if ( !strncmp( line, "WEP: ", 5 ) ) {
4891 line += 5;
4892 switch( line[0] ) {
4893 case 's':
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02004894 set_auth_type(ai, AUTH_SHAREDKEY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004895 break;
4896 case 'e':
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02004897 set_auth_type(ai, AUTH_ENCRYPT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004898 break;
4899 default:
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02004900 set_auth_type(ai, AUTH_OPEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004901 break;
4902 }
4903 set_bit (FLAG_COMMIT, &ai->flags);
4904 } else if ( !strncmp( line, "LongRetryLimit: ", 16 ) ) {
4905 int v, i = 0;
4906
4907 line += 16;
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.longRetryLimit = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004911 set_bit (FLAG_COMMIT, &ai->flags);
4912 } else if ( !strncmp( line, "ShortRetryLimit: ", 17 ) ) {
4913 int v, i = 0;
4914
4915 line += 17;
4916 v = get_dec_u16(line, &i, 3);
4917 v = (v<0) ? 0 : ((v>255) ? 255 : v);
Al Viro3eb9b412007-12-21 00:00:35 -05004918 ai->config.shortRetryLimit = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004919 set_bit (FLAG_COMMIT, &ai->flags);
4920 } else if ( !strncmp( line, "RTSThreshold: ", 14 ) ) {
4921 int v, i = 0;
4922
4923 line += 14;
4924 v = get_dec_u16(line, &i, 4);
Dan Williams15db2762006-03-16 13:46:27 -05004925 v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
Al Viro3eb9b412007-12-21 00:00:35 -05004926 ai->config.rtsThres = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004927 set_bit (FLAG_COMMIT, &ai->flags);
4928 } else if ( !strncmp( line, "TXMSDULifetime: ", 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.txLifetime = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004935 set_bit (FLAG_COMMIT, &ai->flags);
4936 } else if ( !strncmp( line, "RXMSDULifetime: ", 16 ) ) {
4937 int v, i = 0;
4938
4939 line += 16;
4940 v = get_dec_u16(line, &i, 5);
4941 v = (v<0) ? 0 : v;
Al Viro3eb9b412007-12-21 00:00:35 -05004942 ai->config.rxLifetime = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004943 set_bit (FLAG_COMMIT, &ai->flags);
4944 } else if ( !strncmp( line, "TXDiversity: ", 13 ) ) {
4945 ai->config.txDiversity =
4946 (line[13]=='l') ? 1 :
4947 ((line[13]=='r')? 2: 3);
4948 set_bit (FLAG_COMMIT, &ai->flags);
4949 } else if ( !strncmp( line, "RXDiversity: ", 13 ) ) {
4950 ai->config.rxDiversity =
4951 (line[13]=='l') ? 1 :
4952 ((line[13]=='r')? 2: 3);
4953 set_bit (FLAG_COMMIT, &ai->flags);
4954 } else if ( !strncmp( line, "FragThreshold: ", 15 ) ) {
4955 int v, i = 0;
4956
4957 line += 15;
4958 v = get_dec_u16(line, &i, 4);
Dan Williams15db2762006-03-16 13:46:27 -05004959 v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004960 v = v & 0xfffe; /* Make sure its even */
Al Viro3eb9b412007-12-21 00:00:35 -05004961 ai->config.fragThresh = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004962 set_bit (FLAG_COMMIT, &ai->flags);
4963 } else if (!strncmp(line, "Modulation: ", 12)) {
4964 line += 12;
4965 switch(*line) {
4966 case 'd': ai->config.modulation=MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break;
4967 case 'c': ai->config.modulation=MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break;
4968 case 'm': ai->config.modulation=MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break;
Dan Williams934d8bf2006-03-16 13:46:23 -05004969 default: airo_print_warn(ai->dev->name, "Unknown modulation");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004970 }
4971 } else if (!strncmp(line, "Preamble: ", 10)) {
4972 line += 10;
4973 switch(*line) {
4974 case 'a': ai->config.preamble=PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break;
4975 case 'l': ai->config.preamble=PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break;
4976 case 's': ai->config.preamble=PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break;
Dan Williams934d8bf2006-03-16 13:46:23 -05004977 default: airo_print_warn(ai->dev->name, "Unknown preamble");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004978 }
4979 } else {
Dan Williams934d8bf2006-03-16 13:46:23 -05004980 airo_print_warn(ai->dev->name, "Couldn't figure out %s", line);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004981 }
4982 while( line[0] && line[0] != '\n' ) line++;
4983 if ( line[0] ) line++;
4984 }
4985 airo_config_commit(dev, NULL, NULL, NULL);
4986}
4987
Stephen Hemminger7a374d82010-09-01 18:17:21 -07004988static const char *get_rmode(__le16 mode)
Al Viro3eb9b412007-12-21 00:00:35 -05004989{
4990 switch(mode & RXMODE_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004991 case RXMODE_RFMON: return "rfmon";
4992 case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon";
4993 case RXMODE_LANMON: return "lanmon";
4994 }
4995 return "ESS";
4996}
4997
Al Viro3eb9b412007-12-21 00:00:35 -05004998static int proc_config_open(struct inode *inode, struct file *file)
4999{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005000 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005001 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005002 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005003 int i;
Al Viro3eb9b412007-12-21 00:00:35 -05005004 __le16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005005
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005006 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005007 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07005008 data = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005009 if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
5010 kfree (file->private_data);
5011 return -ENOMEM;
5012 }
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005013 if ((data->wbuffer = kzalloc( 2048, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005014 kfree (data->rbuffer);
5015 kfree (file->private_data);
5016 return -ENOMEM;
5017 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005018 data->maxwritelen = 2048;
5019 data->on_close = proc_config_on_close;
5020
5021 readConfigRid(ai, 1);
5022
Al Viro3eb9b412007-12-21 00:00:35 -05005023 mode = ai->config.opmode & MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005024 i = sprintf( data->rbuffer,
5025 "Mode: %s\n"
5026 "Radio: %s\n"
5027 "NodeName: %-16s\n"
5028 "PowerMode: %s\n"
5029 "DataRates: %d %d %d %d %d %d %d %d\n"
5030 "Channel: %d\n"
5031 "XmitPower: %d\n",
Al Viro3eb9b412007-12-21 00:00:35 -05005032 mode == MODE_STA_IBSS ? "adhoc" :
5033 mode == MODE_STA_ESS ? get_rmode(ai->config.rmode):
5034 mode == MODE_AP ? "AP" :
5035 mode == MODE_AP_RPTR ? "AP RPTR" : "Error",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005036 test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on",
5037 ai->config.nodeName,
Al Viro3eb9b412007-12-21 00:00:35 -05005038 ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" :
5039 ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" :
5040 ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" :
5041 "Error",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005042 (int)ai->config.rates[0],
5043 (int)ai->config.rates[1],
5044 (int)ai->config.rates[2],
5045 (int)ai->config.rates[3],
5046 (int)ai->config.rates[4],
5047 (int)ai->config.rates[5],
5048 (int)ai->config.rates[6],
5049 (int)ai->config.rates[7],
Al Viro3eb9b412007-12-21 00:00:35 -05005050 le16_to_cpu(ai->config.channelSet),
5051 le16_to_cpu(ai->config.txPower)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005052 );
5053 sprintf( data->rbuffer + i,
5054 "LongRetryLimit: %d\n"
5055 "ShortRetryLimit: %d\n"
5056 "RTSThreshold: %d\n"
5057 "TXMSDULifetime: %d\n"
5058 "RXMSDULifetime: %d\n"
5059 "TXDiversity: %s\n"
5060 "RXDiversity: %s\n"
5061 "FragThreshold: %d\n"
5062 "WEP: %s\n"
5063 "Modulation: %s\n"
5064 "Preamble: %s\n",
Al Viro3eb9b412007-12-21 00:00:35 -05005065 le16_to_cpu(ai->config.longRetryLimit),
5066 le16_to_cpu(ai->config.shortRetryLimit),
5067 le16_to_cpu(ai->config.rtsThres),
5068 le16_to_cpu(ai->config.txLifetime),
5069 le16_to_cpu(ai->config.rxLifetime),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005070 ai->config.txDiversity == 1 ? "left" :
5071 ai->config.txDiversity == 2 ? "right" : "both",
5072 ai->config.rxDiversity == 1 ? "left" :
5073 ai->config.rxDiversity == 2 ? "right" : "both",
Al Viro3eb9b412007-12-21 00:00:35 -05005074 le16_to_cpu(ai->config.fragThresh),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005075 ai->config.authType == AUTH_ENCRYPT ? "encrypt" :
5076 ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open",
Al Viro3eb9b412007-12-21 00:00:35 -05005077 ai->config.modulation == MOD_DEFAULT ? "default" :
Linus Torvalds1da177e2005-04-16 15:20:36 -07005078 ai->config.modulation == MOD_CCK ? "cck" :
5079 ai->config.modulation == MOD_MOK ? "mok" : "error",
5080 ai->config.preamble == PREAMBLE_AUTO ? "auto" :
5081 ai->config.preamble == PREAMBLE_LONG ? "long" :
5082 ai->config.preamble == PREAMBLE_SHORT ? "short" : "error"
5083 );
5084 data->readlen = strlen( data->rbuffer );
5085 return 0;
5086}
5087
Al Viro0dd22122007-12-17 16:11:57 -05005088static void proc_SSID_on_close(struct inode *inode, struct file *file)
5089{
Joe Perches57674302010-07-12 13:50:06 -07005090 struct proc_data *data = file->private_data;
Al Virod9dda782013-03-31 18:16:14 -04005091 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005092 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005093 SsidRid SSID_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005094 int i;
Al Viro0dd22122007-12-17 16:11:57 -05005095 char *p = data->wbuffer;
5096 char *end = p + data->writelen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005097
Al Viro0dd22122007-12-17 16:11:57 -05005098 if (!data->writelen)
5099 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005100
Al Viro0dd22122007-12-17 16:11:57 -05005101 *end = '\n'; /* sentinel; we have space for it */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005102
Al Viro0dd22122007-12-17 16:11:57 -05005103 memset(&SSID_rid, 0, sizeof(SSID_rid));
5104
5105 for (i = 0; i < 3 && p < end; i++) {
5106 int j = 0;
5107 /* copy up to 32 characters from this line */
5108 while (*p != '\n' && j < 32)
5109 SSID_rid.ssids[i].ssid[j++] = *p++;
5110 if (j == 0)
5111 break;
5112 SSID_rid.ssids[i].len = cpu_to_le16(j);
5113 /* skip to the beginning of the next line */
5114 while (*p++ != '\n')
5115 ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005116 }
5117 if (i)
Al Viro0dd22122007-12-17 16:11:57 -05005118 SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005119 disable_MAC(ai, 1);
5120 writeSsidRid(ai, &SSID_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005121 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005122}
5123
Linus Torvalds1da177e2005-04-16 15:20:36 -07005124static void proc_APList_on_close( struct inode *inode, struct file *file ) {
Joe Perches57674302010-07-12 13:50:06 -07005125 struct proc_data *data = file->private_data;
Al Virod9dda782013-03-31 18:16:14 -04005126 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005127 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005128 APListRid APList_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005129 int i;
5130
5131 if ( !data->writelen ) return;
5132
5133 memset( &APList_rid, 0, sizeof(APList_rid) );
Al Viroa7497162007-12-20 17:49:41 -05005134 APList_rid.len = cpu_to_le16(sizeof(APList_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005135
5136 for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) {
5137 int j;
5138 for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) {
5139 switch(j%3) {
5140 case 0:
5141 APList_rid.ap[i][j/3]=
Andy Shevchenko26355382010-05-24 14:33:28 -07005142 hex_to_bin(data->wbuffer[j+i*6*3])<<4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005143 break;
5144 case 1:
5145 APList_rid.ap[i][j/3]|=
Andy Shevchenko26355382010-05-24 14:33:28 -07005146 hex_to_bin(data->wbuffer[j+i*6*3]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005147 break;
5148 }
5149 }
5150 }
5151 disable_MAC(ai, 1);
5152 writeAPListRid(ai, &APList_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005153 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005154}
5155
5156/* This function wraps PC4500_writerid with a MAC disable */
5157static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data,
5158 int len, int dummy ) {
5159 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005160
5161 disable_MAC(ai, 1);
5162 rc = PC4500_writerid(ai, rid, rid_data, len, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005163 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005164 return rc;
5165}
5166
Dan Williamsc0380692009-01-24 09:12:15 -05005167/* Returns the WEP key at the specified index, or -1 if that key does
5168 * not exist. The buffer is assumed to be at least 16 bytes in length.
Linus Torvalds1da177e2005-04-16 15:20:36 -07005169 */
Dan Williamsc0380692009-01-24 09:12:15 -05005170static int get_wep_key(struct airo_info *ai, u16 index, char *buf, u16 buflen)
5171{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005172 WepKeyRid wkr;
5173 int rc;
Al Viro4293ea32007-12-19 19:21:51 -05005174 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005175
5176 rc = readWepKeyRid(ai, &wkr, 1, 1);
Dan Williamsc0380692009-01-24 09:12:15 -05005177 if (rc != SUCCESS)
5178 return -1;
5179 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005180 lastindex = wkr.kindex;
Dan Williamsc0380692009-01-24 09:12:15 -05005181 if (le16_to_cpu(wkr.kindex) == index) {
5182 int klen = min_t(int, buflen, le16_to_cpu(wkr.klen));
5183 memcpy(buf, wkr.key, klen);
5184 return klen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005185 }
Dan Williamsc0380692009-01-24 09:12:15 -05005186 rc = readWepKeyRid(ai, &wkr, 0, 1);
5187 if (rc != SUCCESS)
5188 return -1;
Al Viro4293ea32007-12-19 19:21:51 -05005189 } while (lastindex != wkr.kindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005190 return -1;
5191}
5192
Dan Williamsc0380692009-01-24 09:12:15 -05005193static int get_wep_tx_idx(struct airo_info *ai)
5194{
5195 WepKeyRid wkr;
5196 int rc;
5197 __le16 lastindex;
5198
5199 rc = readWepKeyRid(ai, &wkr, 1, 1);
5200 if (rc != SUCCESS)
5201 return -1;
5202 do {
5203 lastindex = wkr.kindex;
5204 if (wkr.kindex == cpu_to_le16(0xffff))
5205 return wkr.mac[0];
5206 rc = readWepKeyRid(ai, &wkr, 0, 1);
5207 if (rc != SUCCESS)
5208 return -1;
5209 } while (lastindex != wkr.kindex);
5210 return -1;
5211}
5212
5213static int set_wep_key(struct airo_info *ai, u16 index, const char *key,
5214 u16 keylen, int perm, int lock)
Al Viro4293ea32007-12-19 19:21:51 -05005215{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005216 static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
5217 WepKeyRid wkr;
Dan Williamsc0380692009-01-24 09:12:15 -05005218 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005219
Stanislaw Gruszka6510b892010-02-26 15:10:28 +01005220 if (WARN_ON(keylen == 0))
5221 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005222
Dan Williamsc0380692009-01-24 09:12:15 -05005223 memset(&wkr, 0, sizeof(wkr));
5224 wkr.len = cpu_to_le16(sizeof(wkr));
5225 wkr.kindex = cpu_to_le16(index);
5226 wkr.klen = cpu_to_le16(keylen);
5227 memcpy(wkr.key, key, keylen);
5228 memcpy(wkr.mac, macaddr, ETH_ALEN);
5229
Dan Streetmanf89b2322005-11-11 11:41:42 -05005230 if (perm) disable_MAC(ai, lock);
Dan Williamsc0380692009-01-24 09:12:15 -05005231 rc = writeWepKeyRid(ai, &wkr, perm, lock);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005232 if (perm) enable_MAC(ai, lock);
Dan Williamsc0380692009-01-24 09:12:15 -05005233 return rc;
5234}
5235
5236static int set_wep_tx_idx(struct airo_info *ai, u16 index, int perm, int lock)
5237{
5238 WepKeyRid wkr;
5239 int rc;
5240
5241 memset(&wkr, 0, sizeof(wkr));
5242 wkr.len = cpu_to_le16(sizeof(wkr));
5243 wkr.kindex = cpu_to_le16(0xffff);
5244 wkr.mac[0] = (char)index;
5245
5246 if (perm) {
5247 ai->defindex = (char)index;
5248 disable_MAC(ai, lock);
5249 }
5250
5251 rc = writeWepKeyRid(ai, &wkr, perm, lock);
5252
5253 if (perm)
5254 enable_MAC(ai, lock);
5255 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005256}
5257
5258static void proc_wepkey_on_close( struct inode *inode, struct file *file ) {
5259 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005260 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005261 struct airo_info *ai = dev->ml_priv;
Dan Williamsc0380692009-01-24 09:12:15 -05005262 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005263 char key[16];
5264 u16 index = 0;
5265 int j = 0;
5266
5267 memset(key, 0, sizeof(key));
5268
Joe Perches57674302010-07-12 13:50:06 -07005269 data = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005270 if ( !data->writelen ) return;
5271
5272 if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' &&
5273 (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) {
5274 index = data->wbuffer[0] - '0';
5275 if (data->wbuffer[1] == '\n') {
Dan Williamsc0380692009-01-24 09:12:15 -05005276 rc = set_wep_tx_idx(ai, index, 1, 1);
5277 if (rc < 0) {
5278 airo_print_err(ai->dev->name, "failed to set "
5279 "WEP transmit index to %d: %d.",
5280 index, rc);
5281 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005282 return;
5283 }
5284 j = 2;
5285 } else {
Dan Williams934d8bf2006-03-16 13:46:23 -05005286 airo_print_err(ai->dev->name, "WepKey passed invalid key index");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005287 return;
5288 }
5289
5290 for( i = 0; i < 16*3 && data->wbuffer[i+j]; i++ ) {
5291 switch(i%3) {
5292 case 0:
Andy Shevchenko26355382010-05-24 14:33:28 -07005293 key[i/3] = hex_to_bin(data->wbuffer[i+j])<<4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005294 break;
5295 case 1:
Andy Shevchenko26355382010-05-24 14:33:28 -07005296 key[i/3] |= hex_to_bin(data->wbuffer[i+j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005297 break;
5298 }
5299 }
Dan Williamsc0380692009-01-24 09:12:15 -05005300
5301 rc = set_wep_key(ai, index, key, i/3, 1, 1);
5302 if (rc < 0) {
5303 airo_print_err(ai->dev->name, "failed to set WEP key at index "
5304 "%d: %d.", index, rc);
5305 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005306}
5307
Al Viro4293ea32007-12-19 19:21:51 -05005308static int proc_wepkey_open( struct inode *inode, struct file *file )
5309{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005310 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005311 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005312 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005313 char *ptr;
5314 WepKeyRid wkr;
Al Viro4293ea32007-12-19 19:21:51 -05005315 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005316 int j=0;
5317 int rc;
5318
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005319 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005320 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005321 memset(&wkr, 0, sizeof(wkr));
Joe Perches57674302010-07-12 13:50:06 -07005322 data = file->private_data;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005323 if ((data->rbuffer = kzalloc( 180, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005324 kfree (file->private_data);
5325 return -ENOMEM;
5326 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005327 data->writelen = 0;
5328 data->maxwritelen = 80;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005329 if ((data->wbuffer = kzalloc( 80, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005330 kfree (data->rbuffer);
5331 kfree (file->private_data);
5332 return -ENOMEM;
5333 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005334 data->on_close = proc_wepkey_on_close;
5335
5336 ptr = data->rbuffer;
5337 strcpy(ptr, "No wep keys\n");
5338 rc = readWepKeyRid(ai, &wkr, 1, 1);
5339 if (rc == SUCCESS) do {
5340 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05005341 if (wkr.kindex == cpu_to_le16(0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005342 j += sprintf(ptr+j, "Tx key = %d\n",
5343 (int)wkr.mac[0]);
5344 } else {
5345 j += sprintf(ptr+j, "Key %d set with length = %d\n",
Al Viro4293ea32007-12-19 19:21:51 -05005346 le16_to_cpu(wkr.kindex),
5347 le16_to_cpu(wkr.klen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005348 }
5349 readWepKeyRid(ai, &wkr, 0, 1);
5350 } while((lastindex != wkr.kindex) && (j < 180-30));
5351
5352 data->readlen = strlen( data->rbuffer );
5353 return 0;
5354}
5355
Al Viro0dd22122007-12-17 16:11:57 -05005356static int proc_SSID_open(struct inode *inode, struct file *file)
5357{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005358 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005359 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005360 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005361 int i;
5362 char *ptr;
5363 SsidRid SSID_rid;
5364
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005365 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005366 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07005367 data = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005368 if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
5369 kfree (file->private_data);
5370 return -ENOMEM;
5371 }
5372 data->writelen = 0;
5373 data->maxwritelen = 33*3;
Al Viro0dd22122007-12-17 16:11:57 -05005374 /* allocate maxwritelen + 1; we'll want a sentinel */
5375 if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005376 kfree (data->rbuffer);
5377 kfree (file->private_data);
5378 return -ENOMEM;
5379 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005380 data->on_close = proc_SSID_on_close;
5381
5382 readSsidRid(ai, &SSID_rid);
5383 ptr = data->rbuffer;
Al Viro0dd22122007-12-17 16:11:57 -05005384 for (i = 0; i < 3; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005385 int j;
Al Viro0dd22122007-12-17 16:11:57 -05005386 size_t len = le16_to_cpu(SSID_rid.ssids[i].len);
5387 if (!len)
5388 break;
5389 if (len > 32)
5390 len = 32;
5391 for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005392 *ptr++ = SSID_rid.ssids[i].ssid[j];
Linus Torvalds1da177e2005-04-16 15:20:36 -07005393 *ptr++ = '\n';
5394 }
5395 *ptr = '\0';
5396 data->readlen = strlen( data->rbuffer );
5397 return 0;
5398}
5399
5400static int proc_APList_open( struct inode *inode, struct file *file ) {
5401 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005402 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005403 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005404 int i;
5405 char *ptr;
5406 APListRid APList_rid;
5407
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005408 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005409 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07005410 data = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005411 if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
5412 kfree (file->private_data);
5413 return -ENOMEM;
5414 }
5415 data->writelen = 0;
5416 data->maxwritelen = 4*6*3;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005417 if ((data->wbuffer = kzalloc( data->maxwritelen, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005418 kfree (data->rbuffer);
5419 kfree (file->private_data);
5420 return -ENOMEM;
5421 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005422 data->on_close = proc_APList_on_close;
5423
5424 readAPListRid(ai, &APList_rid);
5425 ptr = data->rbuffer;
5426 for( i = 0; i < 4; i++ ) {
5427// We end when we find a zero MAC
5428 if ( !*(int*)APList_rid.ap[i] &&
5429 !*(int*)&APList_rid.ap[i][2]) break;
Johannes Berge1749612008-10-27 15:59:26 -07005430 ptr += sprintf(ptr, "%pM\n", APList_rid.ap[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005431 }
5432 if (i==0) ptr += sprintf(ptr, "Not using specific APs\n");
5433
5434 *ptr = '\0';
5435 data->readlen = strlen( data->rbuffer );
5436 return 0;
5437}
5438
5439static int proc_BSSList_open( struct inode *inode, struct file *file ) {
5440 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005441 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005442 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005443 char *ptr;
5444 BSSListRid BSSList_rid;
5445 int rc;
5446 /* If doLoseSync is not 1, we won't do a Lose Sync */
5447 int doLoseSync = -1;
5448
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005449 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005450 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07005451 data = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005452 if ((data->rbuffer = kmalloc( 1024, GFP_KERNEL )) == NULL) {
5453 kfree (file->private_data);
5454 return -ENOMEM;
5455 }
5456 data->writelen = 0;
5457 data->maxwritelen = 0;
5458 data->wbuffer = NULL;
5459 data->on_close = NULL;
5460
5461 if (file->f_mode & FMODE_WRITE) {
5462 if (!(file->f_mode & FMODE_READ)) {
5463 Cmd cmd;
5464 Resp rsp;
5465
5466 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
5467 memset(&cmd, 0, sizeof(cmd));
5468 cmd.cmd=CMD_LISTBSS;
5469 if (down_interruptible(&ai->sem))
5470 return -ERESTARTSYS;
5471 issuecommand(ai, &cmd, &rsp);
5472 up(&ai->sem);
5473 data->readlen = 0;
5474 return 0;
5475 }
5476 doLoseSync = 1;
5477 }
5478 ptr = data->rbuffer;
5479 /* There is a race condition here if there are concurrent opens.
5480 Since it is a rare condition, we'll just live with it, otherwise
5481 we have to add a spin lock... */
5482 rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
Al Viro17e70492007-12-19 18:56:37 -05005483 while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
Johannes Berge1749612008-10-27 15:59:26 -07005484 ptr += sprintf(ptr, "%pM %*s rssi = %d",
5485 BSSList_rid.bssid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005486 (int)BSSList_rid.ssidLen,
5487 BSSList_rid.ssid,
Al Viro17e70492007-12-19 18:56:37 -05005488 le16_to_cpu(BSSList_rid.dBm));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005489 ptr += sprintf(ptr, " channel = %d %s %s %s %s\n",
Al Viro17e70492007-12-19 18:56:37 -05005490 le16_to_cpu(BSSList_rid.dsChannel),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005491 BSSList_rid.cap & CAP_ESS ? "ESS" : "",
5492 BSSList_rid.cap & CAP_IBSS ? "adhoc" : "",
5493 BSSList_rid.cap & CAP_PRIVACY ? "wep" : "",
5494 BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : "");
5495 rc = readBSSListRid(ai, 0, &BSSList_rid);
5496 }
5497 *ptr = '\0';
5498 data->readlen = strlen( data->rbuffer );
5499 return 0;
5500}
5501
5502static int proc_close( struct inode *inode, struct file *file )
5503{
Jesper Juhlb4558ea2005-10-28 16:53:13 -04005504 struct proc_data *data = file->private_data;
5505
5506 if (data->on_close != NULL)
5507 data->on_close(inode, file);
5508 kfree(data->rbuffer);
5509 kfree(data->wbuffer);
5510 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005511 return 0;
5512}
5513
Linus Torvalds1da177e2005-04-16 15:20:36 -07005514/* Since the card doesn't automatically switch to the right WEP mode,
5515 we will make it do it. If the card isn't associated, every secs we
5516 will switch WEP modes to see if that will help. If the card is
5517 associated we will check every minute to see if anything has
5518 changed. */
5519static void timer_func( struct net_device *dev ) {
Wang Chenfaf39942008-10-14 13:30:33 +08005520 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005521
5522/* We don't have a link so try changing the authtype */
5523 readConfigRid(apriv, 0);
5524 disable_MAC(apriv, 0);
5525 switch(apriv->config.authType) {
5526 case AUTH_ENCRYPT:
5527/* So drop to OPEN */
5528 apriv->config.authType = AUTH_OPEN;
5529 break;
5530 case AUTH_SHAREDKEY:
5531 if (apriv->keyindex < auto_wep) {
Dan Williamsc0380692009-01-24 09:12:15 -05005532 set_wep_tx_idx(apriv, apriv->keyindex, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005533 apriv->config.authType = AUTH_SHAREDKEY;
5534 apriv->keyindex++;
5535 } else {
5536 /* Drop to ENCRYPT */
5537 apriv->keyindex = 0;
Dan Williamsc0380692009-01-24 09:12:15 -05005538 set_wep_tx_idx(apriv, apriv->defindex, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005539 apriv->config.authType = AUTH_ENCRYPT;
5540 }
5541 break;
5542 default: /* We'll escalate to SHAREDKEY */
5543 apriv->config.authType = AUTH_SHAREDKEY;
5544 }
5545 set_bit (FLAG_COMMIT, &apriv->flags);
5546 writeConfigRid(apriv, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005547 enable_MAC(apriv, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005548 up(&apriv->sem);
5549
5550/* Schedule check to see if the change worked */
Dan Williams3c304952006-04-15 12:26:18 -04005551 clear_bit(JOB_AUTOWEP, &apriv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005552 apriv->expires = RUN_AT(HZ*3);
5553}
5554
Linus Torvalds1da177e2005-04-16 15:20:36 -07005555#ifdef CONFIG_PCI
Bill Pemberton04bfffb2012-12-03 09:56:27 -05005556static int airo_pci_probe(struct pci_dev *pdev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005557 const struct pci_device_id *pent)
5558{
5559 struct net_device *dev;
5560
5561 if (pci_enable_device(pdev))
5562 return -ENODEV;
5563 pci_set_master(pdev);
5564
5565 if (pdev->device == 0x5000 || pdev->device == 0xa504)
5566 dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev);
5567 else
5568 dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev);
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005569 if (!dev) {
5570 pci_disable_device(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005571 return -ENODEV;
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005572 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005573
5574 pci_set_drvdata(pdev, dev);
5575 return 0;
5576}
5577
Bill Pemberton04bfffb2012-12-03 09:56:27 -05005578static void airo_pci_remove(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005579{
Michal Schmidtaf5b5c9a2007-03-16 12:44:40 +01005580 struct net_device *dev = pci_get_drvdata(pdev);
5581
5582 airo_print_info(dev->name, "Unregistering...");
5583 stop_airo_card(dev, 1);
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005584 pci_disable_device(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005585}
5586
Pavel Machek05adc3b2005-04-16 15:25:25 -07005587static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005588{
5589 struct net_device *dev = pci_get_drvdata(pdev);
Wang Chenfaf39942008-10-14 13:30:33 +08005590 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005591 Cmd cmd;
5592 Resp rsp;
5593
Pavel Macheke292c732008-06-25 12:25:53 +02005594 if (!ai->APList)
5595 ai->APList = kmalloc(sizeof(APListRid), GFP_KERNEL);
5596 if (!ai->APList)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005597 return -ENOMEM;
Pavel Macheke292c732008-06-25 12:25:53 +02005598 if (!ai->SSID)
5599 ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL);
5600 if (!ai->SSID)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005601 return -ENOMEM;
5602 readAPListRid(ai, ai->APList);
5603 readSsidRid(ai, ai->SSID);
5604 memset(&cmd, 0, sizeof(cmd));
5605 /* the lock will be released at the end of the resume callback */
5606 if (down_interruptible(&ai->sem))
5607 return -EAGAIN;
5608 disable_MAC(ai, 0);
5609 netif_device_detach(dev);
5610 ai->power = state;
Pavel Macheke292c732008-06-25 12:25:53 +02005611 cmd.cmd = HOSTSLEEP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005612 issuecommand(ai, &cmd, &rsp);
5613
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005614 pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005615 pci_save_state(pdev);
matthieu castet40b93ad2009-10-09 22:12:25 +02005616 pci_set_power_state(pdev, pci_choose_state(pdev, state));
5617 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005618}
5619
5620static int airo_pci_resume(struct pci_dev *pdev)
5621{
5622 struct net_device *dev = pci_get_drvdata(pdev);
Wang Chenfaf39942008-10-14 13:30:33 +08005623 struct airo_info *ai = dev->ml_priv;
Michal Schmidt53232802005-10-04 07:46:21 -04005624 pci_power_t prev_state = pdev->current_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005625
Michal Schmidt53232802005-10-04 07:46:21 -04005626 pci_set_power_state(pdev, PCI_D0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005627 pci_restore_state(pdev);
Michal Schmidt53232802005-10-04 07:46:21 -04005628 pci_enable_wake(pdev, PCI_D0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005629
Michal Schmidt53232802005-10-04 07:46:21 -04005630 if (prev_state != PCI_D1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005631 reset_card(dev, 0);
5632 mpi_init_descriptors(ai);
5633 setup_card(ai, dev->dev_addr, 0);
5634 clear_bit(FLAG_RADIO_OFF, &ai->flags);
5635 clear_bit(FLAG_PENDING_XMIT, &ai->flags);
5636 } else {
5637 OUT4500(ai, EVACK, EV_AWAKEN);
5638 OUT4500(ai, EVACK, EV_AWAKEN);
5639 msleep(100);
5640 }
5641
Pavel Macheke292c732008-06-25 12:25:53 +02005642 set_bit(FLAG_COMMIT, &ai->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005643 disable_MAC(ai, 0);
5644 msleep(200);
5645 if (ai->SSID) {
5646 writeSsidRid(ai, ai->SSID, 0);
5647 kfree(ai->SSID);
5648 ai->SSID = NULL;
5649 }
5650 if (ai->APList) {
5651 writeAPListRid(ai, ai->APList, 0);
5652 kfree(ai->APList);
5653 ai->APList = NULL;
5654 }
5655 writeConfigRid(ai, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005656 enable_MAC(ai, 0);
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005657 ai->power = PMSG_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005658 netif_device_attach(dev);
5659 netif_wake_queue(dev);
5660 enable_interrupts(ai);
5661 up(&ai->sem);
5662 return 0;
5663}
5664#endif
5665
5666static int __init airo_init_module( void )
5667{
Jeff Garzikde897882006-10-01 07:31:09 -04005668 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005669
Eric W. Biederman1efa29c2012-02-10 14:01:03 -08005670 proc_kuid = make_kuid(&init_user_ns, proc_uid);
5671 proc_kgid = make_kgid(&init_user_ns, proc_gid);
5672 if (!uid_valid(proc_kuid) || !gid_valid(proc_kgid))
5673 return -EINVAL;
5674
Alexey Dobriyan011159a2011-05-14 00:12:48 +03005675 airo_entry = proc_mkdir_mode("driver/aironet", airo_perm, NULL);
Jeff Garzikde897882006-10-01 07:31:09 -04005676
David Howells271a15e2013-04-12 00:38:51 +01005677 if (airo_entry)
5678 proc_set_user(airo_entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005679
Pavel Macheke292c732008-06-25 12:25:53 +02005680 for (i = 0; i < 4 && io[i] && irq[i]; i++) {
Dan Williams934d8bf2006-03-16 13:46:23 -05005681 airo_print_info("", "Trying to configure ISA adapter at irq=%d "
5682 "io=0x%x", irq[i], io[i] );
Linus Torvalds1da177e2005-04-16 15:20:36 -07005683 if (init_airo_card( irq[i], io[i], 0, NULL ))
Jeff Garzikde897882006-10-01 07:31:09 -04005684 /* do nothing */ ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005685 }
5686
5687#ifdef CONFIG_PCI
Dan Williams934d8bf2006-03-16 13:46:23 -05005688 airo_print_info("", "Probing for PCI adapters");
Jeff Garzikde897882006-10-01 07:31:09 -04005689 i = pci_register_driver(&airo_driver);
Dan Williams934d8bf2006-03-16 13:46:23 -05005690 airo_print_info("", "Finished probing for PCI adapters");
Jeff Garzikde897882006-10-01 07:31:09 -04005691
5692 if (i) {
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005693 remove_proc_entry("driver/aironet", NULL);
Jeff Garzikde897882006-10-01 07:31:09 -04005694 return i;
5695 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005696#endif
5697
5698 /* Always exit with success, as we are a library module
5699 * as well as a driver module
5700 */
5701 return 0;
5702}
5703
5704static void __exit airo_cleanup_module( void )
5705{
Michal Schmidtaf5b5c9a2007-03-16 12:44:40 +01005706 struct airo_info *ai;
5707 while(!list_empty(&airo_devices)) {
5708 ai = list_entry(airo_devices.next, struct airo_info, dev_list);
5709 airo_print_info(ai->dev->name, "Unregistering...");
5710 stop_airo_card(ai->dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005711 }
5712#ifdef CONFIG_PCI
5713 pci_unregister_driver(&airo_driver);
5714#endif
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005715 remove_proc_entry("driver/aironet", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005716}
5717
Linus Torvalds1da177e2005-04-16 15:20:36 -07005718/*
5719 * Initial Wireless Extension code for Aironet driver by :
5720 * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00
5721 * Conversion to new driver API by :
5722 * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02
5723 * Javier also did a good amount of work here, adding some new extensions
5724 * and fixing my code. Let's just say that without him this code just
5725 * would not work at all... - Jean II
5726 */
5727
Dan Williams41480af2005-05-10 09:45:51 -04005728static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi)
5729{
Pavel Macheke292c732008-06-25 12:25:53 +02005730 if (!rssi_rid)
Dan Williams41480af2005-05-10 09:45:51 -04005731 return 0;
5732
5733 return (0x100 - rssi_rid[rssi].rssidBm);
5734}
5735
5736static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm)
5737{
5738 int i;
5739
Pavel Macheke292c732008-06-25 12:25:53 +02005740 if (!rssi_rid)
Dan Williams41480af2005-05-10 09:45:51 -04005741 return 0;
5742
Pavel Macheke292c732008-06-25 12:25:53 +02005743 for (i = 0; i < 256; i++)
Dan Williams41480af2005-05-10 09:45:51 -04005744 if (rssi_rid[i].rssidBm == dbm)
5745 return rssi_rid[i].rssipct;
5746
5747 return 0;
5748}
5749
5750
Linus Torvalds1da177e2005-04-16 15:20:36 -07005751static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid)
5752{
5753 int quality = 0;
Al Viro329e2c02007-12-20 22:58:57 -05005754 u16 sq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005755
Al Viro329e2c02007-12-20 22:58:57 -05005756 if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f))
Al Viro56d81bd2007-12-20 17:18:35 -05005757 return 0;
5758
5759 if (!(cap_rid->hardCap & cpu_to_le16(8)))
5760 return 0;
5761
Al Viro329e2c02007-12-20 22:58:57 -05005762 sq = le16_to_cpu(status_rid->signalQuality);
Al Viro56d81bd2007-12-20 17:18:35 -05005763 if (memcmp(cap_rid->prodName, "350", 3))
Al Viro329e2c02007-12-20 22:58:57 -05005764 if (sq > 0x20)
Al Viro56d81bd2007-12-20 17:18:35 -05005765 quality = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005766 else
Al Viro329e2c02007-12-20 22:58:57 -05005767 quality = 0x20 - sq;
Al Viro56d81bd2007-12-20 17:18:35 -05005768 else
Al Viro329e2c02007-12-20 22:58:57 -05005769 if (sq > 0xb0)
Al Viro56d81bd2007-12-20 17:18:35 -05005770 quality = 0;
Al Viro329e2c02007-12-20 22:58:57 -05005771 else if (sq < 0x10)
Al Viro56d81bd2007-12-20 17:18:35 -05005772 quality = 0xa0;
5773 else
Al Viro329e2c02007-12-20 22:58:57 -05005774 quality = 0xb0 - sq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005775 return quality;
5776}
5777
5778#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0)
5779#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50);
5780
5781/*------------------------------------------------------------------*/
5782/*
5783 * Wireless Handler : get protocol name
5784 */
5785static int airo_get_name(struct net_device *dev,
5786 struct iw_request_info *info,
5787 char *cwrq,
5788 char *extra)
5789{
5790 strcpy(cwrq, "IEEE 802.11-DS");
5791 return 0;
5792}
5793
5794/*------------------------------------------------------------------*/
5795/*
5796 * Wireless Handler : set frequency
5797 */
5798static int airo_set_freq(struct net_device *dev,
5799 struct iw_request_info *info,
5800 struct iw_freq *fwrq,
5801 char *extra)
5802{
Wang Chenfaf39942008-10-14 13:30:33 +08005803 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005804 int rc = -EINPROGRESS; /* Call commit handler */
5805
5806 /* If setting by frequency, convert to a channel */
David Kilroy9ee677c2008-12-23 14:03:38 +00005807 if(fwrq->e == 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005808 int f = fwrq->m / 100000;
David Kilroy9ee677c2008-12-23 14:03:38 +00005809
Linus Torvalds1da177e2005-04-16 15:20:36 -07005810 /* Hack to fall through... */
5811 fwrq->e = 0;
Zhao, Gange0febf12014-02-18 21:35:57 +08005812 fwrq->m = ieee80211_frequency_to_channel(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005813 }
5814 /* Setting by channel number */
5815 if((fwrq->m > 1000) || (fwrq->e > 0))
5816 rc = -EOPNOTSUPP;
5817 else {
5818 int channel = fwrq->m;
5819 /* We should do a better check than that,
5820 * based on the card capability !!! */
Javier Achirica2610c732006-01-17 08:01:01 -05005821 if((channel < 1) || (channel > 14)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05005822 airo_print_dbg(dev->name, "New channel value of %d is invalid!",
5823 fwrq->m);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005824 rc = -EINVAL;
5825 } else {
5826 readConfigRid(local, 1);
5827 /* Yes ! We can set it !!! */
Al Viro3eb9b412007-12-21 00:00:35 -05005828 local->config.channelSet = cpu_to_le16(channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005829 set_bit (FLAG_COMMIT, &local->flags);
5830 }
5831 }
5832 return rc;
5833}
5834
5835/*------------------------------------------------------------------*/
5836/*
5837 * Wireless Handler : get frequency
5838 */
5839static int airo_get_freq(struct net_device *dev,
5840 struct iw_request_info *info,
5841 struct iw_freq *fwrq,
5842 char *extra)
5843{
Wang Chenfaf39942008-10-14 13:30:33 +08005844 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005845 StatusRid status_rid; /* Card status info */
Javier Achirica2610c732006-01-17 08:01:01 -05005846 int ch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005847
5848 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05005849 if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS)
5850 status_rid.channel = local->config.channelSet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005851 else
5852 readStatusRid(local, &status_rid, 1);
5853
Al Viro329e2c02007-12-20 22:58:57 -05005854 ch = le16_to_cpu(status_rid.channel);
Javier Achirica2610c732006-01-17 08:01:01 -05005855 if((ch > 0) && (ch < 15)) {
Zhao, Gange0febf12014-02-18 21:35:57 +08005856 fwrq->m = 100000 *
5857 ieee80211_channel_to_frequency(ch, IEEE80211_BAND_2GHZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005858 fwrq->e = 1;
Javier Achirica2610c732006-01-17 08:01:01 -05005859 } else {
5860 fwrq->m = ch;
5861 fwrq->e = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005862 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005863
5864 return 0;
5865}
5866
5867/*------------------------------------------------------------------*/
5868/*
5869 * Wireless Handler : set ESSID
5870 */
5871static int airo_set_essid(struct net_device *dev,
5872 struct iw_request_info *info,
5873 struct iw_point *dwrq,
5874 char *extra)
5875{
Wang Chenfaf39942008-10-14 13:30:33 +08005876 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005877 SsidRid SSID_rid; /* SSIDs */
5878
5879 /* Reload the list of current SSID */
5880 readSsidRid(local, &SSID_rid);
5881
5882 /* Check if we asked for `any' */
Roel Kluin3d0ccd02009-07-25 23:02:32 +02005883 if (dwrq->flags == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005884 /* Just send an empty SSID list */
5885 memset(&SSID_rid, 0, sizeof(SSID_rid));
5886 } else {
Roel Kluin3d0ccd02009-07-25 23:02:32 +02005887 unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005888
5889 /* Check the size of the string */
Roel Kluin3d0ccd02009-07-25 23:02:32 +02005890 if (dwrq->length > IW_ESSID_MAX_SIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005891 return -E2BIG ;
Roel Kluin3d0ccd02009-07-25 23:02:32 +02005892
Linus Torvalds1da177e2005-04-16 15:20:36 -07005893 /* Check if index is valid */
Roel Kluin3d0ccd02009-07-25 23:02:32 +02005894 if (index >= ARRAY_SIZE(SSID_rid.ssids))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005895 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005896
5897 /* Set the SSID */
5898 memset(SSID_rid.ssids[index].ssid, 0,
5899 sizeof(SSID_rid.ssids[index].ssid));
5900 memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length);
Al Viro0dd22122007-12-17 16:11:57 -05005901 SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005902 }
Al Viro0dd22122007-12-17 16:11:57 -05005903 SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005904 /* Write it to the card */
5905 disable_MAC(local, 1);
5906 writeSsidRid(local, &SSID_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005907 enable_MAC(local, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005908
5909 return 0;
5910}
5911
5912/*------------------------------------------------------------------*/
5913/*
5914 * Wireless Handler : get ESSID
5915 */
5916static int airo_get_essid(struct net_device *dev,
5917 struct iw_request_info *info,
5918 struct iw_point *dwrq,
5919 char *extra)
5920{
Wang Chenfaf39942008-10-14 13:30:33 +08005921 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005922 StatusRid status_rid; /* Card status info */
5923
5924 readStatusRid(local, &status_rid, 1);
5925
5926 /* Note : if dwrq->flags != 0, we should
5927 * get the relevant SSID from the SSID list... */
5928
5929 /* Get the current SSID */
Al Viro329e2c02007-12-20 22:58:57 -05005930 memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005931 /* If none, we may want to get the one that was set */
5932
5933 /* Push it out ! */
Al Viro329e2c02007-12-20 22:58:57 -05005934 dwrq->length = le16_to_cpu(status_rid.SSIDlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005935 dwrq->flags = 1; /* active */
5936
5937 return 0;
5938}
5939
5940/*------------------------------------------------------------------*/
5941/*
5942 * Wireless Handler : set AP address
5943 */
5944static int airo_set_wap(struct net_device *dev,
5945 struct iw_request_info *info,
5946 struct sockaddr *awrq,
5947 char *extra)
5948{
Wang Chenfaf39942008-10-14 13:30:33 +08005949 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005950 Cmd cmd;
5951 Resp rsp;
5952 APListRid APList_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005953
5954 if (awrq->sa_family != ARPHRD_ETHER)
5955 return -EINVAL;
Wei Yongjun0d1c6d12012-08-23 15:17:09 +08005956 else if (is_broadcast_ether_addr(awrq->sa_data) ||
5957 is_zero_ether_addr(awrq->sa_data)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005958 memset(&cmd, 0, sizeof(cmd));
5959 cmd.cmd=CMD_LOSE_SYNC;
5960 if (down_interruptible(&local->sem))
5961 return -ERESTARTSYS;
5962 issuecommand(local, &cmd, &rsp);
5963 up(&local->sem);
5964 } else {
5965 memset(&APList_rid, 0, sizeof(APList_rid));
Al Viroa7497162007-12-20 17:49:41 -05005966 APList_rid.len = cpu_to_le16(sizeof(APList_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005967 memcpy(APList_rid.ap[0], awrq->sa_data, ETH_ALEN);
5968 disable_MAC(local, 1);
5969 writeAPListRid(local, &APList_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005970 enable_MAC(local, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005971 }
5972 return 0;
5973}
5974
5975/*------------------------------------------------------------------*/
5976/*
5977 * Wireless Handler : get AP address
5978 */
5979static int airo_get_wap(struct net_device *dev,
5980 struct iw_request_info *info,
5981 struct sockaddr *awrq,
5982 char *extra)
5983{
Wang Chenfaf39942008-10-14 13:30:33 +08005984 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005985 StatusRid status_rid; /* Card status info */
5986
5987 readStatusRid(local, &status_rid, 1);
5988
5989 /* Tentative. This seems to work, wow, I'm lucky !!! */
5990 memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN);
5991 awrq->sa_family = ARPHRD_ETHER;
5992
5993 return 0;
5994}
5995
5996/*------------------------------------------------------------------*/
5997/*
5998 * Wireless Handler : set Nickname
5999 */
6000static int airo_set_nick(struct net_device *dev,
6001 struct iw_request_info *info,
6002 struct iw_point *dwrq,
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
6007 /* Check the size of the string */
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006008 if(dwrq->length > 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006009 return -E2BIG;
6010 }
6011 readConfigRid(local, 1);
6012 memset(local->config.nodeName, 0, sizeof(local->config.nodeName));
6013 memcpy(local->config.nodeName, extra, dwrq->length);
6014 set_bit (FLAG_COMMIT, &local->flags);
6015
6016 return -EINPROGRESS; /* Call commit handler */
6017}
6018
6019/*------------------------------------------------------------------*/
6020/*
6021 * Wireless Handler : get Nickname
6022 */
6023static int airo_get_nick(struct net_device *dev,
6024 struct iw_request_info *info,
6025 struct iw_point *dwrq,
6026 char *extra)
6027{
Wang Chenfaf39942008-10-14 13:30:33 +08006028 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006029
6030 readConfigRid(local, 1);
6031 strncpy(extra, local->config.nodeName, 16);
6032 extra[16] = '\0';
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006033 dwrq->length = strlen(extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006034
6035 return 0;
6036}
6037
6038/*------------------------------------------------------------------*/
6039/*
6040 * Wireless Handler : set Bit-Rate
6041 */
6042static int airo_set_rate(struct net_device *dev,
6043 struct iw_request_info *info,
6044 struct iw_param *vwrq,
6045 char *extra)
6046{
Wang Chenfaf39942008-10-14 13:30:33 +08006047 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006048 CapabilityRid cap_rid; /* Card capability info */
6049 u8 brate = 0;
6050 int i;
6051
6052 /* First : get a valid bit rate value */
6053 readCapabilityRid(local, &cap_rid, 1);
6054
6055 /* Which type of value ? */
6056 if((vwrq->value < 8) && (vwrq->value >= 0)) {
6057 /* Setting by rate index */
6058 /* Find value in the magic rate table */
6059 brate = cap_rid.supportedRates[vwrq->value];
6060 } else {
6061 /* Setting by frequency value */
6062 u8 normvalue = (u8) (vwrq->value/500000);
6063
6064 /* Check if rate is valid */
6065 for(i = 0 ; i < 8 ; i++) {
6066 if(normvalue == cap_rid.supportedRates[i]) {
6067 brate = normvalue;
6068 break;
6069 }
6070 }
6071 }
6072 /* -1 designed the max rate (mostly auto mode) */
6073 if(vwrq->value == -1) {
6074 /* Get the highest available rate */
6075 for(i = 0 ; i < 8 ; i++) {
6076 if(cap_rid.supportedRates[i] == 0)
6077 break;
6078 }
6079 if(i != 0)
6080 brate = cap_rid.supportedRates[i - 1];
6081 }
6082 /* Check that it is valid */
6083 if(brate == 0) {
6084 return -EINVAL;
6085 }
6086
6087 readConfigRid(local, 1);
6088 /* Now, check if we want a fixed or auto value */
6089 if(vwrq->fixed == 0) {
6090 /* Fill all the rates up to this max rate */
6091 memset(local->config.rates, 0, 8);
6092 for(i = 0 ; i < 8 ; i++) {
6093 local->config.rates[i] = cap_rid.supportedRates[i];
6094 if(local->config.rates[i] == brate)
6095 break;
6096 }
6097 } else {
6098 /* Fixed mode */
6099 /* One rate, fixed */
6100 memset(local->config.rates, 0, 8);
6101 local->config.rates[0] = brate;
6102 }
6103 set_bit (FLAG_COMMIT, &local->flags);
6104
6105 return -EINPROGRESS; /* Call commit handler */
6106}
6107
6108/*------------------------------------------------------------------*/
6109/*
6110 * Wireless Handler : get Bit-Rate
6111 */
6112static int airo_get_rate(struct net_device *dev,
6113 struct iw_request_info *info,
6114 struct iw_param *vwrq,
6115 char *extra)
6116{
Wang Chenfaf39942008-10-14 13:30:33 +08006117 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006118 StatusRid status_rid; /* Card status info */
6119
6120 readStatusRid(local, &status_rid, 1);
6121
Al Viro329e2c02007-12-20 22:58:57 -05006122 vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006123 /* If more than one rate, set auto */
6124 readConfigRid(local, 1);
6125 vwrq->fixed = (local->config.rates[1] == 0);
6126
6127 return 0;
6128}
6129
6130/*------------------------------------------------------------------*/
6131/*
6132 * Wireless Handler : set RTS threshold
6133 */
6134static int airo_set_rts(struct net_device *dev,
6135 struct iw_request_info *info,
6136 struct iw_param *vwrq,
6137 char *extra)
6138{
Wang Chenfaf39942008-10-14 13:30:33 +08006139 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006140 int rthr = vwrq->value;
6141
6142 if(vwrq->disabled)
Dan Williams15db2762006-03-16 13:46:27 -05006143 rthr = AIRO_DEF_MTU;
6144 if((rthr < 0) || (rthr > AIRO_DEF_MTU)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006145 return -EINVAL;
6146 }
6147 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006148 local->config.rtsThres = cpu_to_le16(rthr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006149 set_bit (FLAG_COMMIT, &local->flags);
6150
6151 return -EINPROGRESS; /* Call commit handler */
6152}
6153
6154/*------------------------------------------------------------------*/
6155/*
6156 * Wireless Handler : get RTS threshold
6157 */
6158static int airo_get_rts(struct net_device *dev,
6159 struct iw_request_info *info,
6160 struct iw_param *vwrq,
6161 char *extra)
6162{
Wang Chenfaf39942008-10-14 13:30:33 +08006163 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006164
6165 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006166 vwrq->value = le16_to_cpu(local->config.rtsThres);
Dan Williams15db2762006-03-16 13:46:27 -05006167 vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006168 vwrq->fixed = 1;
6169
6170 return 0;
6171}
6172
6173/*------------------------------------------------------------------*/
6174/*
6175 * Wireless Handler : set Fragmentation threshold
6176 */
6177static int airo_set_frag(struct net_device *dev,
6178 struct iw_request_info *info,
6179 struct iw_param *vwrq,
6180 char *extra)
6181{
Wang Chenfaf39942008-10-14 13:30:33 +08006182 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006183 int fthr = vwrq->value;
6184
6185 if(vwrq->disabled)
Dan Williams15db2762006-03-16 13:46:27 -05006186 fthr = AIRO_DEF_MTU;
6187 if((fthr < 256) || (fthr > AIRO_DEF_MTU)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006188 return -EINVAL;
6189 }
6190 fthr &= ~0x1; /* Get an even value - is it really needed ??? */
6191 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006192 local->config.fragThresh = cpu_to_le16(fthr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006193 set_bit (FLAG_COMMIT, &local->flags);
6194
6195 return -EINPROGRESS; /* Call commit handler */
6196}
6197
6198/*------------------------------------------------------------------*/
6199/*
6200 * Wireless Handler : get Fragmentation threshold
6201 */
6202static int airo_get_frag(struct net_device *dev,
6203 struct iw_request_info *info,
6204 struct iw_param *vwrq,
6205 char *extra)
6206{
Wang Chenfaf39942008-10-14 13:30:33 +08006207 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006208
6209 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006210 vwrq->value = le16_to_cpu(local->config.fragThresh);
Dan Williams15db2762006-03-16 13:46:27 -05006211 vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006212 vwrq->fixed = 1;
6213
6214 return 0;
6215}
6216
6217/*------------------------------------------------------------------*/
6218/*
6219 * Wireless Handler : set Mode of Operation
6220 */
6221static int airo_set_mode(struct net_device *dev,
6222 struct iw_request_info *info,
6223 __u32 *uwrq,
6224 char *extra)
6225{
Wang Chenfaf39942008-10-14 13:30:33 +08006226 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006227 int reset = 0;
6228
6229 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006230 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006231 reset = 1;
6232
6233 switch(*uwrq) {
6234 case IW_MODE_ADHOC:
Al Viro3eb9b412007-12-21 00:00:35 -05006235 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006236 local->config.opmode |= MODE_STA_IBSS;
Al Viro3eb9b412007-12-21 00:00:35 -05006237 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006238 local->config.scanMode = SCANMODE_ACTIVE;
6239 clear_bit (FLAG_802_11, &local->flags);
6240 break;
6241 case IW_MODE_INFRA:
Al Viro3eb9b412007-12-21 00:00:35 -05006242 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006243 local->config.opmode |= MODE_STA_ESS;
Al Viro3eb9b412007-12-21 00:00:35 -05006244 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006245 local->config.scanMode = SCANMODE_ACTIVE;
6246 clear_bit (FLAG_802_11, &local->flags);
6247 break;
6248 case IW_MODE_MASTER:
Al Viro3eb9b412007-12-21 00:00:35 -05006249 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006250 local->config.opmode |= MODE_AP;
Al Viro3eb9b412007-12-21 00:00:35 -05006251 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006252 local->config.scanMode = SCANMODE_ACTIVE;
6253 clear_bit (FLAG_802_11, &local->flags);
6254 break;
6255 case IW_MODE_REPEAT:
Al Viro3eb9b412007-12-21 00:00:35 -05006256 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006257 local->config.opmode |= MODE_AP_RPTR;
Al Viro3eb9b412007-12-21 00:00:35 -05006258 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006259 local->config.scanMode = SCANMODE_ACTIVE;
6260 clear_bit (FLAG_802_11, &local->flags);
6261 break;
6262 case IW_MODE_MONITOR:
Al Viro3eb9b412007-12-21 00:00:35 -05006263 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006264 local->config.opmode |= MODE_STA_ESS;
Al Viro3eb9b412007-12-21 00:00:35 -05006265 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006266 local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
6267 local->config.scanMode = SCANMODE_PASSIVE;
6268 set_bit (FLAG_802_11, &local->flags);
6269 break;
6270 default:
6271 return -EINVAL;
6272 }
6273 if (reset)
6274 set_bit (FLAG_RESET, &local->flags);
6275 set_bit (FLAG_COMMIT, &local->flags);
6276
6277 return -EINPROGRESS; /* Call commit handler */
6278}
6279
6280/*------------------------------------------------------------------*/
6281/*
6282 * Wireless Handler : get Mode of Operation
6283 */
6284static int airo_get_mode(struct net_device *dev,
6285 struct iw_request_info *info,
6286 __u32 *uwrq,
6287 char *extra)
6288{
Wang Chenfaf39942008-10-14 13:30:33 +08006289 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006290
6291 readConfigRid(local, 1);
6292 /* If not managed, assume it's ad-hoc */
Al Viro3eb9b412007-12-21 00:00:35 -05006293 switch (local->config.opmode & MODE_CFG_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006294 case MODE_STA_ESS:
6295 *uwrq = IW_MODE_INFRA;
6296 break;
6297 case MODE_AP:
6298 *uwrq = IW_MODE_MASTER;
6299 break;
6300 case MODE_AP_RPTR:
6301 *uwrq = IW_MODE_REPEAT;
6302 break;
6303 default:
6304 *uwrq = IW_MODE_ADHOC;
6305 }
6306
6307 return 0;
6308}
6309
Dan Williams138c0c62009-01-24 09:11:35 -05006310static inline int valid_index(struct airo_info *ai, int index)
Al Viro56d81bd2007-12-20 17:18:35 -05006311{
Dan Williams138c0c62009-01-24 09:11:35 -05006312 return (index >= 0) && (index <= ai->max_wep_idx);
Al Viro56d81bd2007-12-20 17:18:35 -05006313}
6314
Linus Torvalds1da177e2005-04-16 15:20:36 -07006315/*------------------------------------------------------------------*/
6316/*
6317 * Wireless Handler : set Encryption Key
6318 */
6319static int airo_set_encode(struct net_device *dev,
6320 struct iw_request_info *info,
6321 struct iw_point *dwrq,
6322 char *extra)
6323{
Wang Chenfaf39942008-10-14 13:30:33 +08006324 struct airo_info *local = dev->ml_priv;
Dan Williamsc0380692009-01-24 09:12:15 -05006325 int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006326 __le16 currentAuthType = local->config.authType;
Dan Williamsc0380692009-01-24 09:12:15 -05006327 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006328
Dan Williams138c0c62009-01-24 09:11:35 -05006329 if (!local->wep_capable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006330 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006331
Linus Torvalds1da177e2005-04-16 15:20:36 -07006332 readConfigRid(local, 1);
6333
6334 /* Basic checking: do we have a key to set ?
6335 * Note : with the new API, it's impossible to get a NULL pointer.
6336 * Therefore, we need to check a key size == 0 instead.
6337 * New version of iwconfig properly set the IW_ENCODE_NOKEY flag
6338 * when no key is present (only change flags), but older versions
6339 * don't do it. - Jean II */
6340 if (dwrq->length > 0) {
6341 wep_key_t key;
6342 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Dan Williamsc0380692009-01-24 09:12:15 -05006343 int current_index;
Dan Williams138c0c62009-01-24 09:11:35 -05006344
Linus Torvalds1da177e2005-04-16 15:20:36 -07006345 /* Check the size of the key */
6346 if (dwrq->length > MAX_KEY_SIZE) {
6347 return -EINVAL;
6348 }
Dan Williams138c0c62009-01-24 09:11:35 -05006349
Dan Williamsc0380692009-01-24 09:12:15 -05006350 current_index = get_wep_tx_idx(local);
6351 if (current_index < 0)
6352 current_index = 0;
6353
Linus Torvalds1da177e2005-04-16 15:20:36 -07006354 /* Check the index (none -> use current) */
Dan Williams138c0c62009-01-24 09:11:35 -05006355 if (!valid_index(local, index))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006356 index = current_index;
Dan Williams138c0c62009-01-24 09:11:35 -05006357
Linus Torvalds1da177e2005-04-16 15:20:36 -07006358 /* Set the length */
6359 if (dwrq->length > MIN_KEY_SIZE)
6360 key.len = MAX_KEY_SIZE;
6361 else
Stanislaw Gruszkaf09c2562010-02-02 15:34:50 +01006362 key.len = MIN_KEY_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006363 /* Check if the key is not marked as invalid */
6364 if(!(dwrq->flags & IW_ENCODE_NOKEY)) {
6365 /* Cleanup */
6366 memset(key.key, 0, MAX_KEY_SIZE);
6367 /* Copy the key in the driver */
6368 memcpy(key.key, extra, dwrq->length);
6369 /* Send the key to the card */
Dan Williamsc0380692009-01-24 09:12:15 -05006370 rc = set_wep_key(local, index, key.key, key.len, perm, 1);
6371 if (rc < 0) {
6372 airo_print_err(local->dev->name, "failed to set"
6373 " WEP key at index %d: %d.",
6374 index, rc);
6375 return rc;
6376 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006377 }
6378 /* WE specify that if a valid key is set, encryption
6379 * should be enabled (user may turn it off later)
6380 * This is also how "iwconfig ethX key on" works */
6381 if((index == current_index) && (key.len > 0) &&
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006382 (local->config.authType == AUTH_OPEN))
6383 set_auth_type(local, AUTH_ENCRYPT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006384 } else {
6385 /* Do we want to just set the transmit key index ? */
6386 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Dan Williamsc0380692009-01-24 09:12:15 -05006387 if (valid_index(local, index)) {
6388 rc = set_wep_tx_idx(local, index, perm, 1);
6389 if (rc < 0) {
6390 airo_print_err(local->dev->name, "failed to set"
6391 " WEP transmit index to %d: %d.",
6392 index, rc);
6393 return rc;
6394 }
6395 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006396 /* Don't complain if only change the mode */
Jeff Garzik93a3b602007-11-23 21:50:20 -05006397 if (!(dwrq->flags & IW_ENCODE_MODE))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006398 return -EINVAL;
Dan Williams138c0c62009-01-24 09:11:35 -05006399 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006400 }
6401 /* Read the flags */
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006402 if (dwrq->flags & IW_ENCODE_DISABLED)
6403 set_auth_type(local, AUTH_OPEN); /* disable encryption */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006404 if(dwrq->flags & IW_ENCODE_RESTRICTED)
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006405 set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */
6406 if (dwrq->flags & IW_ENCODE_OPEN)
6407 set_auth_type(local, AUTH_ENCRYPT); /* Only Wep */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006408 /* Commit the changes to flags if needed */
Dan Streetmanf89b2322005-11-11 11:41:42 -05006409 if (local->config.authType != currentAuthType)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006410 set_bit (FLAG_COMMIT, &local->flags);
6411 return -EINPROGRESS; /* Call commit handler */
6412}
6413
6414/*------------------------------------------------------------------*/
6415/*
6416 * Wireless Handler : get Encryption Key
6417 */
6418static int airo_get_encode(struct net_device *dev,
6419 struct iw_request_info *info,
6420 struct iw_point *dwrq,
6421 char *extra)
6422{
Wang Chenfaf39942008-10-14 13:30:33 +08006423 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006424 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
John W. Linville267d4932009-05-20 10:51:41 -04006425 int wep_key_len;
Dan Williamsc0380692009-01-24 09:12:15 -05006426 u8 buf[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -07006427
Dan Williams138c0c62009-01-24 09:11:35 -05006428 if (!local->wep_capable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006429 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006430
Linus Torvalds1da177e2005-04-16 15:20:36 -07006431 readConfigRid(local, 1);
Dan Williams138c0c62009-01-24 09:11:35 -05006432
Linus Torvalds1da177e2005-04-16 15:20:36 -07006433 /* Check encryption mode */
6434 switch(local->config.authType) {
6435 case AUTH_ENCRYPT:
6436 dwrq->flags = IW_ENCODE_OPEN;
6437 break;
6438 case AUTH_SHAREDKEY:
6439 dwrq->flags = IW_ENCODE_RESTRICTED;
6440 break;
6441 default:
6442 case AUTH_OPEN:
6443 dwrq->flags = IW_ENCODE_DISABLED;
6444 break;
6445 }
6446 /* We can't return the key, so set the proper flag and return zero */
6447 dwrq->flags |= IW_ENCODE_NOKEY;
6448 memset(extra, 0, 16);
6449
6450 /* Which key do we want ? -1 -> tx index */
Dan Williamsc0380692009-01-24 09:12:15 -05006451 if (!valid_index(local, index)) {
6452 index = get_wep_tx_idx(local);
6453 if (index < 0)
6454 index = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006455 }
Dan Williamsc0380692009-01-24 09:12:15 -05006456 dwrq->flags |= index + 1;
6457
6458 /* Copy the key to the user buffer */
John W. Linville267d4932009-05-20 10:51:41 -04006459 wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf));
6460 if (wep_key_len < 0) {
John W. Linvilleaedec922009-05-04 11:18:57 -04006461 dwrq->length = 0;
John W. Linville267d4932009-05-20 10:51:41 -04006462 } else {
6463 dwrq->length = wep_key_len;
6464 memcpy(extra, buf, dwrq->length);
6465 }
Dan Williamsc0380692009-01-24 09:12:15 -05006466
Linus Torvalds1da177e2005-04-16 15:20:36 -07006467 return 0;
6468}
6469
6470/*------------------------------------------------------------------*/
6471/*
Dan Williams4be757d2006-01-30 11:58:00 -05006472 * Wireless Handler : set extended Encryption parameters
6473 */
6474static int airo_set_encodeext(struct net_device *dev,
6475 struct iw_request_info *info,
6476 union iwreq_data *wrqu,
6477 char *extra)
6478{
Wang Chenfaf39942008-10-14 13:30:33 +08006479 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006480 struct iw_point *encoding = &wrqu->encoding;
6481 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
Dan Williams4be757d2006-01-30 11:58:00 -05006482 int perm = ( encoding->flags & IW_ENCODE_TEMP ? 0 : 1 );
Al Viro3eb9b412007-12-21 00:00:35 -05006483 __le16 currentAuthType = local->config.authType;
Dan Williamsc0380692009-01-24 09:12:15 -05006484 int idx, key_len, alg = ext->alg, set_key = 1, rc;
Dan Williams4be757d2006-01-30 11:58:00 -05006485 wep_key_t key;
6486
Dan Williams138c0c62009-01-24 09:11:35 -05006487 if (!local->wep_capable)
Dan Williams4be757d2006-01-30 11:58:00 -05006488 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006489
Dan Williams4be757d2006-01-30 11:58:00 -05006490 readConfigRid(local, 1);
6491
6492 /* Determine and validate the key index */
6493 idx = encoding->flags & IW_ENCODE_INDEX;
6494 if (idx) {
Dan Williams138c0c62009-01-24 09:11:35 -05006495 if (!valid_index(local, idx - 1))
Dan Williams4be757d2006-01-30 11:58:00 -05006496 return -EINVAL;
6497 idx--;
Dan Williamsc0380692009-01-24 09:12:15 -05006498 } else {
6499 idx = get_wep_tx_idx(local);
6500 if (idx < 0)
6501 idx = 0;
6502 }
Dan Williams4be757d2006-01-30 11:58:00 -05006503
6504 if (encoding->flags & IW_ENCODE_DISABLED)
6505 alg = IW_ENCODE_ALG_NONE;
6506
Dan Williams4be757d2006-01-30 11:58:00 -05006507 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
Dan Williams22d88462006-02-05 18:00:30 -05006508 /* Only set transmit key index here, actual
6509 * key is set below if needed.
6510 */
Dan Williamsc0380692009-01-24 09:12:15 -05006511 rc = set_wep_tx_idx(local, idx, perm, 1);
6512 if (rc < 0) {
6513 airo_print_err(local->dev->name, "failed to set "
6514 "WEP transmit index to %d: %d.",
6515 idx, rc);
6516 return rc;
6517 }
Dan Williams22d88462006-02-05 18:00:30 -05006518 set_key = ext->key_len > 0 ? 1 : 0;
6519 }
6520
6521 if (set_key) {
Dan Williams4be757d2006-01-30 11:58:00 -05006522 /* Set the requested key first */
6523 memset(key.key, 0, MAX_KEY_SIZE);
6524 switch (alg) {
6525 case IW_ENCODE_ALG_NONE:
6526 key.len = 0;
6527 break;
6528 case IW_ENCODE_ALG_WEP:
6529 if (ext->key_len > MIN_KEY_SIZE) {
6530 key.len = MAX_KEY_SIZE;
6531 } else if (ext->key_len > 0) {
6532 key.len = MIN_KEY_SIZE;
6533 } else {
6534 return -EINVAL;
6535 }
6536 key_len = min (ext->key_len, key.len);
6537 memcpy(key.key, ext->key, key_len);
6538 break;
6539 default:
6540 return -EINVAL;
6541 }
Stanislaw Gruszkaf09c2562010-02-02 15:34:50 +01006542 if (key.len == 0) {
6543 rc = set_wep_tx_idx(local, idx, perm, 1);
6544 if (rc < 0) {
6545 airo_print_err(local->dev->name,
6546 "failed to set WEP transmit index to %d: %d.",
6547 idx, rc);
6548 return rc;
6549 }
6550 } else {
6551 rc = set_wep_key(local, idx, key.key, key.len, perm, 1);
6552 if (rc < 0) {
6553 airo_print_err(local->dev->name,
6554 "failed to set WEP key at index %d: %d.",
6555 idx, rc);
6556 return rc;
6557 }
Dan Williamsc0380692009-01-24 09:12:15 -05006558 }
Dan Williams4be757d2006-01-30 11:58:00 -05006559 }
6560
6561 /* Read the flags */
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006562 if (encoding->flags & IW_ENCODE_DISABLED)
6563 set_auth_type(local, AUTH_OPEN); /* disable encryption */
Dan Williams4be757d2006-01-30 11:58:00 -05006564 if(encoding->flags & IW_ENCODE_RESTRICTED)
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006565 set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */
6566 if (encoding->flags & IW_ENCODE_OPEN)
6567 set_auth_type(local, AUTH_ENCRYPT);
Dan Williams4be757d2006-01-30 11:58:00 -05006568 /* Commit the changes to flags if needed */
6569 if (local->config.authType != currentAuthType)
6570 set_bit (FLAG_COMMIT, &local->flags);
6571
6572 return -EINPROGRESS;
6573}
6574
6575
6576/*------------------------------------------------------------------*/
6577/*
6578 * Wireless Handler : get extended Encryption parameters
6579 */
6580static int airo_get_encodeext(struct net_device *dev,
6581 struct iw_request_info *info,
6582 union iwreq_data *wrqu,
6583 char *extra)
6584{
Wang Chenfaf39942008-10-14 13:30:33 +08006585 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006586 struct iw_point *encoding = &wrqu->encoding;
6587 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
John W. Linville267d4932009-05-20 10:51:41 -04006588 int idx, max_key_len, wep_key_len;
Dan Williamsc0380692009-01-24 09:12:15 -05006589 u8 buf[16];
Dan Williams4be757d2006-01-30 11:58:00 -05006590
Dan Williams138c0c62009-01-24 09:11:35 -05006591 if (!local->wep_capable)
Dan Williams4be757d2006-01-30 11:58:00 -05006592 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006593
Dan Williams4be757d2006-01-30 11:58:00 -05006594 readConfigRid(local, 1);
6595
6596 max_key_len = encoding->length - sizeof(*ext);
6597 if (max_key_len < 0)
6598 return -EINVAL;
6599
6600 idx = encoding->flags & IW_ENCODE_INDEX;
6601 if (idx) {
Dan Williams138c0c62009-01-24 09:11:35 -05006602 if (!valid_index(local, idx - 1))
Dan Williams4be757d2006-01-30 11:58:00 -05006603 return -EINVAL;
6604 idx--;
Dan Williamsc0380692009-01-24 09:12:15 -05006605 } else {
6606 idx = get_wep_tx_idx(local);
6607 if (idx < 0)
6608 idx = 0;
6609 }
Dan Williams4be757d2006-01-30 11:58:00 -05006610
6611 encoding->flags = idx + 1;
6612 memset(ext, 0, sizeof(*ext));
6613
6614 /* Check encryption mode */
6615 switch(local->config.authType) {
6616 case AUTH_ENCRYPT:
6617 encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
6618 break;
6619 case AUTH_SHAREDKEY:
6620 encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
6621 break;
6622 default:
6623 case AUTH_OPEN:
6624 encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED;
6625 break;
6626 }
6627 /* We can't return the key, so set the proper flag and return zero */
6628 encoding->flags |= IW_ENCODE_NOKEY;
6629 memset(extra, 0, 16);
6630
6631 /* Copy the key to the user buffer */
John W. Linville267d4932009-05-20 10:51:41 -04006632 wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf));
6633 if (wep_key_len < 0) {
John W. Linvilleaedec922009-05-04 11:18:57 -04006634 ext->key_len = 0;
John W. Linville267d4932009-05-20 10:51:41 -04006635 } else {
6636 ext->key_len = wep_key_len;
6637 memcpy(extra, buf, ext->key_len);
6638 }
Dan Williams4be757d2006-01-30 11:58:00 -05006639
6640 return 0;
6641}
6642
6643
6644/*------------------------------------------------------------------*/
6645/*
6646 * Wireless Handler : set extended authentication parameters
6647 */
6648static int airo_set_auth(struct net_device *dev,
6649 struct iw_request_info *info,
6650 union iwreq_data *wrqu, char *extra)
6651{
Wang Chenfaf39942008-10-14 13:30:33 +08006652 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006653 struct iw_param *param = &wrqu->param;
Al Viro3eb9b412007-12-21 00:00:35 -05006654 __le16 currentAuthType = local->config.authType;
Dan Williams4be757d2006-01-30 11:58:00 -05006655
6656 switch (param->flags & IW_AUTH_INDEX) {
6657 case IW_AUTH_WPA_VERSION:
6658 case IW_AUTH_CIPHER_PAIRWISE:
6659 case IW_AUTH_CIPHER_GROUP:
6660 case IW_AUTH_KEY_MGMT:
6661 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6662 case IW_AUTH_PRIVACY_INVOKED:
6663 /*
6664 * airo does not use these parameters
6665 */
6666 break;
6667
6668 case IW_AUTH_DROP_UNENCRYPTED:
6669 if (param->value) {
6670 /* Only change auth type if unencrypted */
6671 if (currentAuthType == AUTH_OPEN)
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006672 set_auth_type(local, AUTH_ENCRYPT);
Dan Williams4be757d2006-01-30 11:58:00 -05006673 } else {
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006674 set_auth_type(local, AUTH_OPEN);
Dan Williams4be757d2006-01-30 11:58:00 -05006675 }
6676
6677 /* Commit the changes to flags if needed */
6678 if (local->config.authType != currentAuthType)
6679 set_bit (FLAG_COMMIT, &local->flags);
6680 break;
6681
6682 case IW_AUTH_80211_AUTH_ALG: {
Dan Williams4be757d2006-01-30 11:58:00 -05006683 if (param->value & IW_AUTH_ALG_SHARED_KEY) {
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006684 set_auth_type(local, AUTH_SHAREDKEY);
Dan Williams4be757d2006-01-30 11:58:00 -05006685 } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006686 /* We don't know here if WEP open system or
6687 * unencrypted mode was requested - so use the
6688 * last mode (of these two) used last time
6689 */
6690 set_auth_type(local, local->last_auth);
Dan Williams4be757d2006-01-30 11:58:00 -05006691 } else
6692 return -EINVAL;
Dan Williams4be757d2006-01-30 11:58:00 -05006693
6694 /* Commit the changes to flags if needed */
6695 if (local->config.authType != currentAuthType)
6696 set_bit (FLAG_COMMIT, &local->flags);
Dan Williams011f5c52009-04-08 10:15:17 -04006697 break;
Dan Williams4be757d2006-01-30 11:58:00 -05006698 }
6699
6700 case IW_AUTH_WPA_ENABLED:
6701 /* Silently accept disable of WPA */
6702 if (param->value > 0)
6703 return -EOPNOTSUPP;
6704 break;
6705
6706 default:
6707 return -EOPNOTSUPP;
6708 }
6709 return -EINPROGRESS;
6710}
6711
6712
6713/*------------------------------------------------------------------*/
6714/*
6715 * Wireless Handler : get extended authentication parameters
6716 */
6717static int airo_get_auth(struct net_device *dev,
6718 struct iw_request_info *info,
6719 union iwreq_data *wrqu, char *extra)
6720{
Wang Chenfaf39942008-10-14 13:30:33 +08006721 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006722 struct iw_param *param = &wrqu->param;
Al Viro3eb9b412007-12-21 00:00:35 -05006723 __le16 currentAuthType = local->config.authType;
Dan Williams4be757d2006-01-30 11:58:00 -05006724
6725 switch (param->flags & IW_AUTH_INDEX) {
6726 case IW_AUTH_DROP_UNENCRYPTED:
6727 switch (currentAuthType) {
6728 case AUTH_SHAREDKEY:
6729 case AUTH_ENCRYPT:
6730 param->value = 1;
6731 break;
6732 default:
6733 param->value = 0;
6734 break;
6735 }
6736 break;
6737
6738 case IW_AUTH_80211_AUTH_ALG:
6739 switch (currentAuthType) {
6740 case AUTH_SHAREDKEY:
6741 param->value = IW_AUTH_ALG_SHARED_KEY;
6742 break;
6743 case AUTH_ENCRYPT:
6744 default:
6745 param->value = IW_AUTH_ALG_OPEN_SYSTEM;
6746 break;
6747 }
6748 break;
6749
6750 case IW_AUTH_WPA_ENABLED:
6751 param->value = 0;
6752 break;
6753
6754 default:
6755 return -EOPNOTSUPP;
6756 }
6757 return 0;
6758}
6759
6760
6761/*------------------------------------------------------------------*/
6762/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07006763 * Wireless Handler : set Tx-Power
6764 */
6765static int airo_set_txpow(struct net_device *dev,
6766 struct iw_request_info *info,
6767 struct iw_param *vwrq,
6768 char *extra)
6769{
Wang Chenfaf39942008-10-14 13:30:33 +08006770 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006771 CapabilityRid cap_rid; /* Card capability info */
6772 int i;
6773 int rc = -EINVAL;
Al Viro3eb9b412007-12-21 00:00:35 -05006774 __le16 v = cpu_to_le16(vwrq->value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006775
6776 readCapabilityRid(local, &cap_rid, 1);
6777
6778 if (vwrq->disabled) {
6779 set_bit (FLAG_RADIO_OFF, &local->flags);
6780 set_bit (FLAG_COMMIT, &local->flags);
6781 return -EINPROGRESS; /* Call commit handler */
6782 }
6783 if (vwrq->flags != IW_TXPOW_MWATT) {
6784 return -EINVAL;
6785 }
6786 clear_bit (FLAG_RADIO_OFF, &local->flags);
Roel Kluin3d0ccd02009-07-25 23:02:32 +02006787 for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++)
Al Viro3eb9b412007-12-21 00:00:35 -05006788 if (v == cap_rid.txPowerLevels[i]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006789 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006790 local->config.txPower = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006791 set_bit (FLAG_COMMIT, &local->flags);
6792 rc = -EINPROGRESS; /* Call commit handler */
6793 break;
6794 }
6795 return rc;
6796}
6797
6798/*------------------------------------------------------------------*/
6799/*
6800 * Wireless Handler : get Tx-Power
6801 */
6802static int airo_get_txpow(struct net_device *dev,
6803 struct iw_request_info *info,
6804 struct iw_param *vwrq,
6805 char *extra)
6806{
Wang Chenfaf39942008-10-14 13:30:33 +08006807 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006808
6809 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006810 vwrq->value = le16_to_cpu(local->config.txPower);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006811 vwrq->fixed = 1; /* No power control */
6812 vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags);
6813 vwrq->flags = IW_TXPOW_MWATT;
6814
6815 return 0;
6816}
6817
6818/*------------------------------------------------------------------*/
6819/*
6820 * Wireless Handler : set Retry limits
6821 */
6822static int airo_set_retry(struct net_device *dev,
6823 struct iw_request_info *info,
6824 struct iw_param *vwrq,
6825 char *extra)
6826{
Wang Chenfaf39942008-10-14 13:30:33 +08006827 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006828 int rc = -EINVAL;
6829
6830 if(vwrq->disabled) {
6831 return -EINVAL;
6832 }
6833 readConfigRid(local, 1);
6834 if(vwrq->flags & IW_RETRY_LIMIT) {
Al Viro3eb9b412007-12-21 00:00:35 -05006835 __le16 v = cpu_to_le16(vwrq->value);
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006836 if(vwrq->flags & IW_RETRY_LONG)
Al Viro3eb9b412007-12-21 00:00:35 -05006837 local->config.longRetryLimit = v;
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006838 else if (vwrq->flags & IW_RETRY_SHORT)
Al Viro3eb9b412007-12-21 00:00:35 -05006839 local->config.shortRetryLimit = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006840 else {
6841 /* No modifier : set both */
Al Viro3eb9b412007-12-21 00:00:35 -05006842 local->config.longRetryLimit = v;
6843 local->config.shortRetryLimit = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006844 }
6845 set_bit (FLAG_COMMIT, &local->flags);
6846 rc = -EINPROGRESS; /* Call commit handler */
6847 }
6848 if(vwrq->flags & IW_RETRY_LIFETIME) {
Al Viro3eb9b412007-12-21 00:00:35 -05006849 local->config.txLifetime = cpu_to_le16(vwrq->value / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006850 set_bit (FLAG_COMMIT, &local->flags);
6851 rc = -EINPROGRESS; /* Call commit handler */
6852 }
6853 return rc;
6854}
6855
6856/*------------------------------------------------------------------*/
6857/*
6858 * Wireless Handler : get Retry limits
6859 */
6860static int airo_get_retry(struct net_device *dev,
6861 struct iw_request_info *info,
6862 struct iw_param *vwrq,
6863 char *extra)
6864{
Wang Chenfaf39942008-10-14 13:30:33 +08006865 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006866
6867 vwrq->disabled = 0; /* Can't be disabled */
6868
6869 readConfigRid(local, 1);
6870 /* Note : by default, display the min retry number */
6871 if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
6872 vwrq->flags = IW_RETRY_LIFETIME;
Al Viro3eb9b412007-12-21 00:00:35 -05006873 vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024;
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006874 } else if((vwrq->flags & IW_RETRY_LONG)) {
6875 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
Al Viro3eb9b412007-12-21 00:00:35 -05006876 vwrq->value = le16_to_cpu(local->config.longRetryLimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006877 } else {
6878 vwrq->flags = IW_RETRY_LIMIT;
Al Viro3eb9b412007-12-21 00:00:35 -05006879 vwrq->value = le16_to_cpu(local->config.shortRetryLimit);
6880 if(local->config.shortRetryLimit != local->config.longRetryLimit)
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006881 vwrq->flags |= IW_RETRY_SHORT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006882 }
6883
6884 return 0;
6885}
6886
6887/*------------------------------------------------------------------*/
6888/*
6889 * Wireless Handler : get range info
6890 */
6891static int airo_get_range(struct net_device *dev,
6892 struct iw_request_info *info,
6893 struct iw_point *dwrq,
6894 char *extra)
6895{
Wang Chenfaf39942008-10-14 13:30:33 +08006896 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006897 struct iw_range *range = (struct iw_range *) extra;
6898 CapabilityRid cap_rid; /* Card capability info */
6899 int i;
6900 int k;
6901
6902 readCapabilityRid(local, &cap_rid, 1);
6903
6904 dwrq->length = sizeof(struct iw_range);
6905 memset(range, 0, sizeof(*range));
6906 range->min_nwid = 0x0000;
6907 range->max_nwid = 0x0000;
6908 range->num_channels = 14;
6909 /* Should be based on cap_rid.country to give only
6910 * what the current card support */
6911 k = 0;
6912 for(i = 0; i < 14; i++) {
6913 range->freq[k].i = i + 1; /* List index */
Zhao, Gange0febf12014-02-18 21:35:57 +08006914 range->freq[k].m = 100000 *
6915 ieee80211_channel_to_frequency(i + 1, IEEE80211_BAND_2GHZ);
David Kilroy9ee677c2008-12-23 14:03:38 +00006916 range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006917 }
6918 range->num_frequency = k;
6919
Linus Torvalds1da177e2005-04-16 15:20:36 -07006920 range->sensitivity = 65535;
6921
Dan Williams41480af2005-05-10 09:45:51 -04006922 /* Hum... Should put the right values there */
6923 if (local->rssi)
6924 range->max_qual.qual = 100; /* % */
6925 else
6926 range->max_qual.qual = airo_get_max_quality(&cap_rid);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006927 range->max_qual.level = 0x100 - 120; /* -120 dBm */
6928 range->max_qual.noise = 0x100 - 120; /* -120 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006929
6930 /* Experimental measurements - boundary 11/5.5 Mb/s */
6931 /* Note : with or without the (local->rssi), results
6932 * are somewhat different. - Jean II */
6933 if (local->rssi) {
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006934 range->avg_qual.qual = 50; /* % */
6935 range->avg_qual.level = 0x100 - 70; /* -70 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006936 } else {
6937 range->avg_qual.qual = airo_get_avg_quality(&cap_rid);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006938 range->avg_qual.level = 0x100 - 80; /* -80 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006939 }
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006940 range->avg_qual.noise = 0x100 - 85; /* -85 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006941
Linus Torvalds1da177e2005-04-16 15:20:36 -07006942 for(i = 0 ; i < 8 ; i++) {
6943 range->bitrate[i] = cap_rid.supportedRates[i] * 500000;
6944 if(range->bitrate[i] == 0)
6945 break;
6946 }
6947 range->num_bitrates = i;
6948
6949 /* Set an indication of the max TCP throughput
6950 * in bit/s that we can expect using this interface.
6951 * May be use for QoS stuff... Jean II */
6952 if(i > 2)
6953 range->throughput = 5000 * 1000;
6954 else
6955 range->throughput = 1500 * 1000;
6956
6957 range->min_rts = 0;
Dan Williams15db2762006-03-16 13:46:27 -05006958 range->max_rts = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006959 range->min_frag = 256;
Dan Williams15db2762006-03-16 13:46:27 -05006960 range->max_frag = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006961
Al Viro56d81bd2007-12-20 17:18:35 -05006962 if(cap_rid.softCap & cpu_to_le16(2)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006963 // WEP: RC4 40 bits
6964 range->encoding_size[0] = 5;
6965 // RC4 ~128 bits
Al Viro56d81bd2007-12-20 17:18:35 -05006966 if (cap_rid.softCap & cpu_to_le16(0x100)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006967 range->encoding_size[1] = 13;
6968 range->num_encoding_sizes = 2;
6969 } else
6970 range->num_encoding_sizes = 1;
Al Viro56d81bd2007-12-20 17:18:35 -05006971 range->max_encoding_tokens =
6972 cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006973 } else {
6974 range->num_encoding_sizes = 0;
6975 range->max_encoding_tokens = 0;
6976 }
6977 range->min_pmp = 0;
6978 range->max_pmp = 5000000; /* 5 secs */
6979 range->min_pmt = 0;
6980 range->max_pmt = 65535 * 1024; /* ??? */
6981 range->pmp_flags = IW_POWER_PERIOD;
6982 range->pmt_flags = IW_POWER_TIMEOUT;
6983 range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
6984
6985 /* Transmit Power - values are in mW */
6986 for(i = 0 ; i < 8 ; i++) {
Al Viro56d81bd2007-12-20 17:18:35 -05006987 range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006988 if(range->txpower[i] == 0)
6989 break;
6990 }
6991 range->num_txpower = i;
6992 range->txpower_capa = IW_TXPOW_MWATT;
Dan Williams3c304952006-04-15 12:26:18 -04006993 range->we_version_source = 19;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006994 range->we_version_compiled = WIRELESS_EXT;
6995 range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
6996 range->retry_flags = IW_RETRY_LIMIT;
6997 range->r_time_flags = IW_RETRY_LIFETIME;
6998 range->min_retry = 1;
6999 range->max_retry = 65535;
7000 range->min_r_time = 1024;
7001 range->max_r_time = 65535 * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007002
7003 /* Event capability (kernel + driver) */
7004 range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
7005 IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
7006 IW_EVENT_CAPA_MASK(SIOCGIWAP) |
7007 IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
7008 range->event_capa[1] = IW_EVENT_CAPA_K_1;
7009 range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP);
7010 return 0;
7011}
7012
7013/*------------------------------------------------------------------*/
7014/*
7015 * Wireless Handler : set Power Management
7016 */
7017static int airo_set_power(struct net_device *dev,
7018 struct iw_request_info *info,
7019 struct iw_param *vwrq,
7020 char *extra)
7021{
Wang Chenfaf39942008-10-14 13:30:33 +08007022 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007023
7024 readConfigRid(local, 1);
7025 if (vwrq->disabled) {
Al Viro3eb9b412007-12-21 00:00:35 -05007026 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007027 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007028 local->config.powerSaveMode = POWERSAVE_CAM;
Al Viro3eb9b412007-12-21 00:00:35 -05007029 local->config.rmode &= ~RXMODE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007030 local->config.rmode |= RXMODE_BC_MC_ADDR;
7031 set_bit (FLAG_COMMIT, &local->flags);
7032 return -EINPROGRESS; /* Call commit handler */
7033 }
7034 if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
Al Viro3eb9b412007-12-21 00:00:35 -05007035 local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007036 local->config.powerSaveMode = POWERSAVE_PSPCAM;
7037 set_bit (FLAG_COMMIT, &local->flags);
7038 } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
Al Viro3eb9b412007-12-21 00:00:35 -05007039 local->config.fastListenInterval =
7040 local->config.listenInterval =
7041 cpu_to_le16((vwrq->value + 500) / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007042 local->config.powerSaveMode = POWERSAVE_PSPCAM;
7043 set_bit (FLAG_COMMIT, &local->flags);
7044 }
7045 switch (vwrq->flags & IW_POWER_MODE) {
7046 case IW_POWER_UNICAST_R:
Al Viro3eb9b412007-12-21 00:00:35 -05007047 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007048 return -EINVAL;
Al Viro3eb9b412007-12-21 00:00:35 -05007049 local->config.rmode &= ~RXMODE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007050 local->config.rmode |= RXMODE_ADDR;
7051 set_bit (FLAG_COMMIT, &local->flags);
7052 break;
7053 case IW_POWER_ALL_R:
Al Viro3eb9b412007-12-21 00:00:35 -05007054 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007055 return -EINVAL;
Al Viro3eb9b412007-12-21 00:00:35 -05007056 local->config.rmode &= ~RXMODE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007057 local->config.rmode |= RXMODE_BC_MC_ADDR;
7058 set_bit (FLAG_COMMIT, &local->flags);
7059 case IW_POWER_ON:
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07007060 /* This is broken, fixme ;-) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07007061 break;
7062 default:
7063 return -EINVAL;
7064 }
7065 // Note : we may want to factor local->need_commit here
7066 // Note2 : may also want to factor RXMODE_RFMON test
7067 return -EINPROGRESS; /* Call commit handler */
7068}
7069
7070/*------------------------------------------------------------------*/
7071/*
7072 * Wireless Handler : get Power Management
7073 */
7074static int airo_get_power(struct net_device *dev,
7075 struct iw_request_info *info,
7076 struct iw_param *vwrq,
7077 char *extra)
7078{
Wang Chenfaf39942008-10-14 13:30:33 +08007079 struct airo_info *local = dev->ml_priv;
Al Viro3eb9b412007-12-21 00:00:35 -05007080 __le16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007081
7082 readConfigRid(local, 1);
7083 mode = local->config.powerSaveMode;
7084 if ((vwrq->disabled = (mode == POWERSAVE_CAM)))
7085 return 0;
7086 if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
Al Viro3eb9b412007-12-21 00:00:35 -05007087 vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007088 vwrq->flags = IW_POWER_TIMEOUT;
7089 } else {
Al Viro3eb9b412007-12-21 00:00:35 -05007090 vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007091 vwrq->flags = IW_POWER_PERIOD;
7092 }
Al Viro3eb9b412007-12-21 00:00:35 -05007093 if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007094 vwrq->flags |= IW_POWER_UNICAST_R;
7095 else
7096 vwrq->flags |= IW_POWER_ALL_R;
7097
7098 return 0;
7099}
7100
7101/*------------------------------------------------------------------*/
7102/*
7103 * Wireless Handler : set Sensitivity
7104 */
7105static int airo_set_sens(struct net_device *dev,
7106 struct iw_request_info *info,
7107 struct iw_param *vwrq,
7108 char *extra)
7109{
Wang Chenfaf39942008-10-14 13:30:33 +08007110 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007111
7112 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05007113 local->config.rssiThreshold =
7114 cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007115 set_bit (FLAG_COMMIT, &local->flags);
7116
7117 return -EINPROGRESS; /* Call commit handler */
7118}
7119
7120/*------------------------------------------------------------------*/
7121/*
7122 * Wireless Handler : get Sensitivity
7123 */
7124static int airo_get_sens(struct net_device *dev,
7125 struct iw_request_info *info,
7126 struct iw_param *vwrq,
7127 char *extra)
7128{
Wang Chenfaf39942008-10-14 13:30:33 +08007129 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007130
7131 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05007132 vwrq->value = le16_to_cpu(local->config.rssiThreshold);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007133 vwrq->disabled = (vwrq->value == 0);
7134 vwrq->fixed = 1;
7135
7136 return 0;
7137}
7138
7139/*------------------------------------------------------------------*/
7140/*
7141 * Wireless Handler : get AP List
7142 * Note : this is deprecated in favor of IWSCAN
7143 */
7144static int airo_get_aplist(struct net_device *dev,
7145 struct iw_request_info *info,
7146 struct iw_point *dwrq,
7147 char *extra)
7148{
Wang Chenfaf39942008-10-14 13:30:33 +08007149 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007150 struct sockaddr *address = (struct sockaddr *) extra;
Frank Seidel998a5a72009-02-25 15:39:57 +01007151 struct iw_quality *qual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007152 BSSListRid BSSList;
7153 int i;
7154 int loseSync = capable(CAP_NET_ADMIN) ? 1: -1;
7155
Frank Seidel998a5a72009-02-25 15:39:57 +01007156 qual = kmalloc(IW_MAX_AP * sizeof(*qual), GFP_KERNEL);
7157 if (!qual)
7158 return -ENOMEM;
7159
Linus Torvalds1da177e2005-04-16 15:20:36 -07007160 for (i = 0; i < IW_MAX_AP; i++) {
Al Viro17e70492007-12-19 18:56:37 -05007161 u16 dBm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007162 if (readBSSListRid(local, loseSync, &BSSList))
7163 break;
7164 loseSync = 0;
7165 memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN);
7166 address[i].sa_family = ARPHRD_ETHER;
Al Viro17e70492007-12-19 18:56:37 -05007167 dBm = le16_to_cpu(BSSList.dBm);
Dan Williams41480af2005-05-10 09:45:51 -04007168 if (local->rssi) {
Al Viro17e70492007-12-19 18:56:37 -05007169 qual[i].level = 0x100 - dBm;
7170 qual[i].qual = airo_dbm_to_pct(local->rssi, dBm);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007171 qual[i].updated = IW_QUAL_QUAL_UPDATED
7172 | IW_QUAL_LEVEL_UPDATED
7173 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007174 } else {
Al Viro17e70492007-12-19 18:56:37 -05007175 qual[i].level = (dBm + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007176 qual[i].qual = 0;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007177 qual[i].updated = IW_QUAL_QUAL_INVALID
7178 | IW_QUAL_LEVEL_UPDATED
7179 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007180 }
7181 qual[i].noise = local->wstats.qual.noise;
Al Viro17e70492007-12-19 18:56:37 -05007182 if (BSSList.index == cpu_to_le16(0xffff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007183 break;
7184 }
7185 if (!i) {
7186 StatusRid status_rid; /* Card status info */
7187 readStatusRid(local, &status_rid, 1);
7188 for (i = 0;
7189 i < min(IW_MAX_AP, 4) &&
7190 (status_rid.bssid[i][0]
7191 & status_rid.bssid[i][1]
7192 & status_rid.bssid[i][2]
7193 & status_rid.bssid[i][3]
7194 & status_rid.bssid[i][4]
7195 & status_rid.bssid[i][5])!=0xff &&
7196 (status_rid.bssid[i][0]
7197 | status_rid.bssid[i][1]
7198 | status_rid.bssid[i][2]
7199 | status_rid.bssid[i][3]
7200 | status_rid.bssid[i][4]
7201 | status_rid.bssid[i][5]);
7202 i++) {
7203 memcpy(address[i].sa_data,
7204 status_rid.bssid[i], ETH_ALEN);
7205 address[i].sa_family = ARPHRD_ETHER;
7206 }
7207 } else {
7208 dwrq->flags = 1; /* Should be define'd */
Dan Carpenter7f59ebb2012-06-18 10:48:14 +03007209 memcpy(extra + sizeof(struct sockaddr) * i, qual,
7210 sizeof(struct iw_quality) * i);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007211 }
7212 dwrq->length = i;
7213
Frank Seidel998a5a72009-02-25 15:39:57 +01007214 kfree(qual);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007215 return 0;
7216}
7217
7218/*------------------------------------------------------------------*/
7219/*
7220 * Wireless Handler : Initiate Scan
7221 */
7222static int airo_set_scan(struct net_device *dev,
7223 struct iw_request_info *info,
David Kilroy9930cce2008-09-13 12:22:05 +01007224 struct iw_point *dwrq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007225 char *extra)
7226{
Wang Chenfaf39942008-10-14 13:30:33 +08007227 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007228 Cmd cmd;
7229 Resp rsp;
Dan Williams9e75af32006-03-16 13:46:29 -05007230 int wake = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007231
7232 /* Note : you may have realised that, as this is a SET operation,
7233 * this is privileged and therefore a normal user can't
7234 * perform scanning.
7235 * This is not an error, while the device perform scanning,
7236 * traffic doesn't flow, so it's a perfect DoS...
7237 * Jean II */
7238 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
7239
Dan Williams9e75af32006-03-16 13:46:29 -05007240 if (down_interruptible(&ai->sem))
7241 return -ERESTARTSYS;
7242
7243 /* If there's already a scan in progress, don't
7244 * trigger another one. */
7245 if (ai->scan_timeout > 0)
7246 goto out;
7247
Linus Torvalds1da177e2005-04-16 15:20:36 -07007248 /* Initiate a scan command */
Dan Williams6fcdf562006-03-31 15:08:46 -05007249 ai->scan_timeout = RUN_AT(3*HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007250 memset(&cmd, 0, sizeof(cmd));
7251 cmd.cmd=CMD_LISTBSS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007252 issuecommand(ai, &cmd, &rsp);
Dan Williams9e75af32006-03-16 13:46:29 -05007253 wake = 1;
7254
7255out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07007256 up(&ai->sem);
Dan Williams9e75af32006-03-16 13:46:29 -05007257 if (wake)
7258 wake_up_interruptible(&ai->thr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007259 return 0;
7260}
7261
7262/*------------------------------------------------------------------*/
7263/*
7264 * Translate scan data returned from the card to a card independent
7265 * format that the Wireless Tools will understand - Jean II
7266 */
7267static inline char *airo_translate_scan(struct net_device *dev,
David S. Millerccc58052008-06-16 18:50:49 -07007268 struct iw_request_info *info,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007269 char *current_ev,
7270 char *end_buf,
Dan Williams41480af2005-05-10 09:45:51 -04007271 BSSListRid *bss)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007272{
Wang Chenfaf39942008-10-14 13:30:33 +08007273 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007274 struct iw_event iwe; /* Temporary buffer */
Al Viro17e70492007-12-19 18:56:37 -05007275 __le16 capabilities;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007276 char * current_val; /* For rates */
7277 int i;
Dan Williams3c304952006-04-15 12:26:18 -04007278 char * buf;
Al Viro851b3e52007-12-19 19:20:12 -05007279 u16 dBm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007280
7281 /* First entry *MUST* be the AP MAC address */
7282 iwe.cmd = SIOCGIWAP;
7283 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
Dan Williams41480af2005-05-10 09:45:51 -04007284 memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -07007285 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7286 &iwe, IW_EV_ADDR_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007287
7288 /* Other entries will be displayed in the order we give them */
7289
7290 /* Add the ESSID */
Dan Williams41480af2005-05-10 09:45:51 -04007291 iwe.u.data.length = bss->ssidLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007292 if(iwe.u.data.length > 32)
7293 iwe.u.data.length = 32;
7294 iwe.cmd = SIOCGIWESSID;
7295 iwe.u.data.flags = 1;
David S. Millerccc58052008-06-16 18:50:49 -07007296 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7297 &iwe, bss->ssid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007298
7299 /* Add mode */
7300 iwe.cmd = SIOCGIWMODE;
Al Viro17e70492007-12-19 18:56:37 -05007301 capabilities = bss->cap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007302 if(capabilities & (CAP_ESS | CAP_IBSS)) {
7303 if(capabilities & CAP_ESS)
7304 iwe.u.mode = IW_MODE_MASTER;
7305 else
7306 iwe.u.mode = IW_MODE_ADHOC;
David S. Millerccc58052008-06-16 18:50:49 -07007307 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7308 &iwe, IW_EV_UINT_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007309 }
7310
7311 /* Add frequency */
7312 iwe.cmd = SIOCGIWFREQ;
Dan Williams41480af2005-05-10 09:45:51 -04007313 iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
Zhao, Gange0febf12014-02-18 21:35:57 +08007314 iwe.u.freq.m = 100000 *
7315 ieee80211_channel_to_frequency(iwe.u.freq.m, IEEE80211_BAND_2GHZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007316 iwe.u.freq.e = 1;
David S. Millerccc58052008-06-16 18:50:49 -07007317 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7318 &iwe, IW_EV_FREQ_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007319
Al Viro851b3e52007-12-19 19:20:12 -05007320 dBm = le16_to_cpu(bss->dBm);
7321
Linus Torvalds1da177e2005-04-16 15:20:36 -07007322 /* Add quality statistics */
7323 iwe.cmd = IWEVQUAL;
Dan Williams41480af2005-05-10 09:45:51 -04007324 if (ai->rssi) {
Al Viro851b3e52007-12-19 19:20:12 -05007325 iwe.u.qual.level = 0x100 - dBm;
7326 iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007327 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
7328 | IW_QUAL_LEVEL_UPDATED
7329 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007330 } else {
Al Viro851b3e52007-12-19 19:20:12 -05007331 iwe.u.qual.level = (dBm + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007332 iwe.u.qual.qual = 0;
Jeff Garzikbbeec902005-09-07 00:27:54 -04007333 iwe.u.qual.updated = IW_QUAL_QUAL_INVALID
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007334 | IW_QUAL_LEVEL_UPDATED
7335 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007336 }
7337 iwe.u.qual.noise = ai->wstats.qual.noise;
David S. Millerccc58052008-06-16 18:50:49 -07007338 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7339 &iwe, IW_EV_QUAL_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007340
7341 /* Add encryption capability */
7342 iwe.cmd = SIOCGIWENCODE;
7343 if(capabilities & CAP_PRIVACY)
7344 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
7345 else
7346 iwe.u.data.flags = IW_ENCODE_DISABLED;
7347 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -07007348 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7349 &iwe, bss->ssid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007350
7351 /* Rate : stuffing multiple values in a single event require a bit
7352 * more of magic - Jean II */
David S. Millerccc58052008-06-16 18:50:49 -07007353 current_val = current_ev + iwe_stream_lcp_len(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007354
7355 iwe.cmd = SIOCGIWRATE;
7356 /* Those two flags are ignored... */
7357 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
7358 /* Max 8 values */
7359 for(i = 0 ; i < 8 ; i++) {
7360 /* NULL terminated */
Dan Williams41480af2005-05-10 09:45:51 -04007361 if(bss->rates[i] == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007362 break;
7363 /* Bit rate given in 500 kb/s units (+ 0x80) */
Dan Williams41480af2005-05-10 09:45:51 -04007364 iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007365 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -07007366 current_val = iwe_stream_add_value(info, current_ev,
7367 current_val, end_buf,
7368 &iwe, IW_EV_PARAM_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007369 }
7370 /* Check if we added any event */
David S. Millerccc58052008-06-16 18:50:49 -07007371 if ((current_val - current_ev) > iwe_stream_lcp_len(info))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007372 current_ev = current_val;
7373
Dan Williams3c304952006-04-15 12:26:18 -04007374 /* Beacon interval */
7375 buf = kmalloc(30, GFP_KERNEL);
7376 if (buf) {
7377 iwe.cmd = IWEVCUSTOM;
7378 sprintf(buf, "bcn_int=%d", bss->beaconInterval);
7379 iwe.u.data.length = strlen(buf);
David S. Millerccc58052008-06-16 18:50:49 -07007380 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7381 &iwe, buf);
Dan Williams3c304952006-04-15 12:26:18 -04007382 kfree(buf);
7383 }
7384
7385 /* Put WPA/RSN Information Elements into the event stream */
7386 if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
7387 unsigned int num_null_ies = 0;
7388 u16 length = sizeof (bss->extra.iep);
Johannes Berg2c7060022008-10-30 22:09:54 +01007389 u8 *ie = (void *)&bss->extra.iep;
Dan Williams3c304952006-04-15 12:26:18 -04007390
Johannes Berg2c7060022008-10-30 22:09:54 +01007391 while ((length >= 2) && (num_null_ies < 2)) {
7392 if (2 + ie[1] > length) {
Dan Williams3c304952006-04-15 12:26:18 -04007393 /* Invalid element, don't continue parsing IE */
7394 break;
7395 }
7396
Johannes Berg2c7060022008-10-30 22:09:54 +01007397 switch (ie[0]) {
7398 case WLAN_EID_SSID:
Dan Williams3c304952006-04-15 12:26:18 -04007399 /* Two zero-length SSID elements
7400 * mean we're done parsing elements */
Johannes Berg2c7060022008-10-30 22:09:54 +01007401 if (!ie[1])
Dan Williams3c304952006-04-15 12:26:18 -04007402 num_null_ies++;
7403 break;
7404
Arend van Spriel04b23122012-10-12 12:28:14 +02007405 case WLAN_EID_VENDOR_SPECIFIC:
Johannes Berg2c7060022008-10-30 22:09:54 +01007406 if (ie[1] >= 4 &&
7407 ie[2] == 0x00 &&
7408 ie[3] == 0x50 &&
7409 ie[4] == 0xf2 &&
7410 ie[5] == 0x01) {
Dan Williams3c304952006-04-15 12:26:18 -04007411 iwe.cmd = IWEVGENIE;
Johannes Berg2c7060022008-10-30 22:09:54 +01007412 /* 64 is an arbitrary cut-off */
7413 iwe.u.data.length = min(ie[1] + 2,
7414 64);
David S. Millerccc58052008-06-16 18:50:49 -07007415 current_ev = iwe_stream_add_point(
7416 info, current_ev,
Johannes Berg2c7060022008-10-30 22:09:54 +01007417 end_buf, &iwe, ie);
Dan Williams3c304952006-04-15 12:26:18 -04007418 }
7419 break;
7420
Johannes Berg2c7060022008-10-30 22:09:54 +01007421 case WLAN_EID_RSN:
Dan Williams3c304952006-04-15 12:26:18 -04007422 iwe.cmd = IWEVGENIE;
Johannes Berg2c7060022008-10-30 22:09:54 +01007423 /* 64 is an arbitrary cut-off */
7424 iwe.u.data.length = min(ie[1] + 2, 64);
David S. Millerccc58052008-06-16 18:50:49 -07007425 current_ev = iwe_stream_add_point(
7426 info, current_ev, end_buf,
Johannes Berg2c7060022008-10-30 22:09:54 +01007427 &iwe, ie);
Dan Williams3c304952006-04-15 12:26:18 -04007428 break;
7429
7430 default:
7431 break;
7432 }
7433
Johannes Berg2c7060022008-10-30 22:09:54 +01007434 length -= 2 + ie[1];
7435 ie += 2 + ie[1];
Dan Williams3c304952006-04-15 12:26:18 -04007436 }
7437 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07007438 return current_ev;
7439}
7440
7441/*------------------------------------------------------------------*/
7442/*
7443 * Wireless Handler : Read Scan Results
7444 */
7445static int airo_get_scan(struct net_device *dev,
7446 struct iw_request_info *info,
7447 struct iw_point *dwrq,
7448 char *extra)
7449{
Wang Chenfaf39942008-10-14 13:30:33 +08007450 struct airo_info *ai = dev->ml_priv;
Dan Williams9e75af32006-03-16 13:46:29 -05007451 BSSListElement *net;
7452 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007453 char *current_ev = extra;
7454
Dan Williams9e75af32006-03-16 13:46:29 -05007455 /* If a scan is in-progress, return -EAGAIN */
7456 if (ai->scan_timeout > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007457 return -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007458
Dan Williams9e75af32006-03-16 13:46:29 -05007459 if (down_interruptible(&ai->sem))
7460 return -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007461
Dan Williams9e75af32006-03-16 13:46:29 -05007462 list_for_each_entry (net, &ai->network_list, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007463 /* Translate to WE format this entry */
David S. Millerccc58052008-06-16 18:50:49 -07007464 current_ev = airo_translate_scan(dev, info, current_ev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007465 extra + dwrq->length,
Dan Williams9e75af32006-03-16 13:46:29 -05007466 &net->bss);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007467
7468 /* Check if there is space for one more entry */
7469 if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
7470 /* Ask user space to try again with a bigger buffer */
Dan Williams9e75af32006-03-16 13:46:29 -05007471 err = -E2BIG;
7472 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007473 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07007474 }
Dan Williams9e75af32006-03-16 13:46:29 -05007475
Linus Torvalds1da177e2005-04-16 15:20:36 -07007476 /* Length of data */
7477 dwrq->length = (current_ev - extra);
7478 dwrq->flags = 0; /* todo */
7479
Dan Williams9e75af32006-03-16 13:46:29 -05007480out:
7481 up(&ai->sem);
7482 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007483}
7484
7485/*------------------------------------------------------------------*/
7486/*
7487 * Commit handler : called after a bunch of SET operations
7488 */
7489static int airo_config_commit(struct net_device *dev,
7490 struct iw_request_info *info, /* NULL */
7491 void *zwrq, /* NULL */
7492 char *extra) /* NULL */
7493{
Wang Chenfaf39942008-10-14 13:30:33 +08007494 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007495
7496 if (!test_bit (FLAG_COMMIT, &local->flags))
7497 return 0;
7498
7499 /* Some of the "SET" function may have modified some of the
7500 * parameters. It's now time to commit them in the card */
7501 disable_MAC(local, 1);
7502 if (test_bit (FLAG_RESET, &local->flags)) {
7503 APListRid APList_rid;
7504 SsidRid SSID_rid;
7505
7506 readAPListRid(local, &APList_rid);
7507 readSsidRid(local, &SSID_rid);
7508 if (test_bit(FLAG_MPI,&local->flags))
7509 setup_card(local, dev->dev_addr, 1 );
7510 else
7511 reset_airo_card(dev);
7512 disable_MAC(local, 1);
7513 writeSsidRid(local, &SSID_rid, 1);
7514 writeAPListRid(local, &APList_rid, 1);
7515 }
7516 if (down_interruptible(&local->sem))
7517 return -ERESTARTSYS;
7518 writeConfigRid(local, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007519 enable_MAC(local, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007520 if (test_bit (FLAG_RESET, &local->flags))
7521 airo_set_promisc(local);
7522 else
7523 up(&local->sem);
7524
7525 return 0;
7526}
7527
7528/*------------------------------------------------------------------*/
7529/*
7530 * Structures to export the Wireless Handlers
7531 */
7532
7533static const struct iw_priv_args airo_private_args[] = {
7534/*{ cmd, set_args, get_args, name } */
7535 { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
7536 IW_PRIV_TYPE_BYTE | 2047, "airoioctl" },
7537 { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
7538 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" },
7539};
7540
7541static const iw_handler airo_handler[] =
7542{
7543 (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */
7544 (iw_handler) airo_get_name, /* SIOCGIWNAME */
7545 (iw_handler) NULL, /* SIOCSIWNWID */
7546 (iw_handler) NULL, /* SIOCGIWNWID */
7547 (iw_handler) airo_set_freq, /* SIOCSIWFREQ */
7548 (iw_handler) airo_get_freq, /* SIOCGIWFREQ */
7549 (iw_handler) airo_set_mode, /* SIOCSIWMODE */
7550 (iw_handler) airo_get_mode, /* SIOCGIWMODE */
7551 (iw_handler) airo_set_sens, /* SIOCSIWSENS */
7552 (iw_handler) airo_get_sens, /* SIOCGIWSENS */
7553 (iw_handler) NULL, /* SIOCSIWRANGE */
7554 (iw_handler) airo_get_range, /* SIOCGIWRANGE */
7555 (iw_handler) NULL, /* SIOCSIWPRIV */
7556 (iw_handler) NULL, /* SIOCGIWPRIV */
7557 (iw_handler) NULL, /* SIOCSIWSTATS */
7558 (iw_handler) NULL, /* SIOCGIWSTATS */
7559 iw_handler_set_spy, /* SIOCSIWSPY */
7560 iw_handler_get_spy, /* SIOCGIWSPY */
7561 iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
7562 iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
7563 (iw_handler) airo_set_wap, /* SIOCSIWAP */
7564 (iw_handler) airo_get_wap, /* SIOCGIWAP */
7565 (iw_handler) NULL, /* -- hole -- */
7566 (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */
7567 (iw_handler) airo_set_scan, /* SIOCSIWSCAN */
7568 (iw_handler) airo_get_scan, /* SIOCGIWSCAN */
7569 (iw_handler) airo_set_essid, /* SIOCSIWESSID */
7570 (iw_handler) airo_get_essid, /* SIOCGIWESSID */
7571 (iw_handler) airo_set_nick, /* SIOCSIWNICKN */
7572 (iw_handler) airo_get_nick, /* SIOCGIWNICKN */
7573 (iw_handler) NULL, /* -- hole -- */
7574 (iw_handler) NULL, /* -- hole -- */
7575 (iw_handler) airo_set_rate, /* SIOCSIWRATE */
7576 (iw_handler) airo_get_rate, /* SIOCGIWRATE */
7577 (iw_handler) airo_set_rts, /* SIOCSIWRTS */
7578 (iw_handler) airo_get_rts, /* SIOCGIWRTS */
7579 (iw_handler) airo_set_frag, /* SIOCSIWFRAG */
7580 (iw_handler) airo_get_frag, /* SIOCGIWFRAG */
7581 (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */
7582 (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */
7583 (iw_handler) airo_set_retry, /* SIOCSIWRETRY */
7584 (iw_handler) airo_get_retry, /* SIOCGIWRETRY */
7585 (iw_handler) airo_set_encode, /* SIOCSIWENCODE */
7586 (iw_handler) airo_get_encode, /* SIOCGIWENCODE */
7587 (iw_handler) airo_set_power, /* SIOCSIWPOWER */
7588 (iw_handler) airo_get_power, /* SIOCGIWPOWER */
Dan Williams4be757d2006-01-30 11:58:00 -05007589 (iw_handler) NULL, /* -- hole -- */
7590 (iw_handler) NULL, /* -- hole -- */
7591 (iw_handler) NULL, /* SIOCSIWGENIE */
7592 (iw_handler) NULL, /* SIOCGIWGENIE */
7593 (iw_handler) airo_set_auth, /* SIOCSIWAUTH */
7594 (iw_handler) airo_get_auth, /* SIOCGIWAUTH */
7595 (iw_handler) airo_set_encodeext, /* SIOCSIWENCODEEXT */
7596 (iw_handler) airo_get_encodeext, /* SIOCGIWENCODEEXT */
7597 (iw_handler) NULL, /* SIOCSIWPMKSA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07007598};
7599
7600/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here.
7601 * We want to force the use of the ioctl code, because those can't be
7602 * won't work the iw_handler code (because they simultaneously read
7603 * and write data and iw_handler can't do that).
7604 * Note that it's perfectly legal to read/write on a single ioctl command,
7605 * you just can't use iwpriv and need to force it via the ioctl handler.
7606 * Jean II */
7607static const iw_handler airo_private_handler[] =
7608{
7609 NULL, /* SIOCIWFIRSTPRIV */
7610};
7611
7612static const struct iw_handler_def airo_handler_def =
7613{
Denis Chengff8ac602007-09-02 18:30:18 +08007614 .num_standard = ARRAY_SIZE(airo_handler),
7615 .num_private = ARRAY_SIZE(airo_private_handler),
7616 .num_private_args = ARRAY_SIZE(airo_private_args),
Linus Torvalds1da177e2005-04-16 15:20:36 -07007617 .standard = airo_handler,
7618 .private = airo_private_handler,
7619 .private_args = airo_private_args,
7620 .get_wireless_stats = airo_get_wireless_stats,
7621};
7622
Linus Torvalds1da177e2005-04-16 15:20:36 -07007623/*
7624 * This defines the configuration part of the Wireless Extensions
7625 * Note : irq and spinlock protection will occur in the subroutines
7626 *
7627 * TODO :
7628 * o Check input value more carefully and fill correct values in range
7629 * o Test and shakeout the bugs (if any)
7630 *
7631 * Jean II
7632 *
7633 * Javier Achirica did a great job of merging code from the unnamed CISCO
7634 * developer that added support for flashing the card.
7635 */
7636static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
7637{
7638 int rc = 0;
Wang Chenfaf39942008-10-14 13:30:33 +08007639 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007640
Pavel Machekca078ba2005-09-03 15:56:57 -07007641 if (ai->power.event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007642 return 0;
7643
7644 switch (cmd) {
7645#ifdef CISCO_EXT
7646 case AIROIDIFC:
7647#ifdef AIROOLDIDIFC
7648 case AIROOLDIDIFC:
7649#endif
7650 {
7651 int val = AIROMAGIC;
7652 aironet_ioctl com;
7653 if (copy_from_user(&com,rq->ifr_data,sizeof(com)))
7654 rc = -EFAULT;
7655 else if (copy_to_user(com.data,(char *)&val,sizeof(val)))
7656 rc = -EFAULT;
7657 }
7658 break;
7659
7660 case AIROIOCTL:
7661#ifdef AIROOLDIOCTL
7662 case AIROOLDIOCTL:
7663#endif
7664 /* Get the command struct and hand it off for evaluation by
7665 * the proper subfunction
7666 */
7667 {
7668 aironet_ioctl com;
7669 if (copy_from_user(&com,rq->ifr_data,sizeof(com))) {
7670 rc = -EFAULT;
7671 break;
7672 }
7673
7674 /* Separate R/W functions bracket legality here
7675 */
7676 if ( com.command == AIRORSWVERSION ) {
7677 if (copy_to_user(com.data, swversion, sizeof(swversion)))
7678 rc = -EFAULT;
7679 else
7680 rc = 0;
7681 }
7682 else if ( com.command <= AIRORRID)
7683 rc = readrids(dev,&com);
7684 else if ( com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2) )
7685 rc = writerids(dev,&com);
7686 else if ( com.command >= AIROFLSHRST && com.command <= AIRORESTART )
7687 rc = flashcard(dev,&com);
7688 else
7689 rc = -EINVAL; /* Bad command in ioctl */
7690 }
7691 break;
7692#endif /* CISCO_EXT */
7693
7694 // All other calls are currently unsupported
7695 default:
7696 rc = -EOPNOTSUPP;
7697 }
7698 return rc;
7699}
7700
Linus Torvalds1da177e2005-04-16 15:20:36 -07007701/*
7702 * Get the Wireless stats out of the driver
7703 * Note : irq and spinlock protection will occur in the subroutines
7704 *
7705 * TODO :
7706 * o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs)
7707 *
7708 * Jean
7709 */
7710static void airo_read_wireless_stats(struct airo_info *local)
7711{
7712 StatusRid status_rid;
7713 StatsRid stats_rid;
7714 CapabilityRid cap_rid;
Al Viroa23ace52007-12-19 22:24:16 -05007715 __le32 *vals = stats_rid.vals;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007716
7717 /* Get stats out of the card */
Dan Williams3c304952006-04-15 12:26:18 -04007718 clear_bit(JOB_WSTATS, &local->jobs);
Pavel Machekca078ba2005-09-03 15:56:57 -07007719 if (local->power.event) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007720 up(&local->sem);
7721 return;
7722 }
7723 readCapabilityRid(local, &cap_rid, 0);
7724 readStatusRid(local, &status_rid, 0);
7725 readStatsRid(local, &stats_rid, RID_STATS, 0);
7726 up(&local->sem);
7727
7728 /* The status */
Al Viro329e2c02007-12-20 22:58:57 -05007729 local->wstats.status = le16_to_cpu(status_rid.mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007730
Dan Williams41480af2005-05-10 09:45:51 -04007731 /* Signal quality and co */
7732 if (local->rssi) {
Al Viro329e2c02007-12-20 22:58:57 -05007733 local->wstats.qual.level =
7734 airo_rssi_to_dbm(local->rssi,
7735 le16_to_cpu(status_rid.sigQuality));
Dan Williams41480af2005-05-10 09:45:51 -04007736 /* normalizedSignalStrength appears to be a percentage */
Al Viro329e2c02007-12-20 22:58:57 -05007737 local->wstats.qual.qual =
7738 le16_to_cpu(status_rid.normalizedSignalStrength);
Dan Williams41480af2005-05-10 09:45:51 -04007739 } else {
Al Viro329e2c02007-12-20 22:58:57 -05007740 local->wstats.qual.level =
7741 (le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007742 local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid);
7743 }
Al Viro329e2c02007-12-20 22:58:57 -05007744 if (le16_to_cpu(status_rid.len) >= 124) {
Dan Williams41480af2005-05-10 09:45:51 -04007745 local->wstats.qual.noise = 0x100 - status_rid.noisedBm;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007746 local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007747 } else {
7748 local->wstats.qual.noise = 0;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007749 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 -07007750 }
7751
7752 /* Packets discarded in the wireless adapter due to wireless
7753 * specific problems */
Al Viroa23ace52007-12-19 22:24:16 -05007754 local->wstats.discard.nwid = le32_to_cpu(vals[56]) +
7755 le32_to_cpu(vals[57]) +
7756 le32_to_cpu(vals[58]); /* SSID Mismatch */
7757 local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */
7758 local->wstats.discard.fragment = le32_to_cpu(vals[30]);
7759 local->wstats.discard.retries = le32_to_cpu(vals[10]);
7760 local->wstats.discard.misc = le32_to_cpu(vals[1]) +
7761 le32_to_cpu(vals[32]);
7762 local->wstats.miss.beacon = le32_to_cpu(vals[34]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007763}
7764
Jouni Malinenff1d2762005-05-12 22:54:16 -04007765static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007766{
Wang Chenfaf39942008-10-14 13:30:33 +08007767 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007768
Dan Williams3c304952006-04-15 12:26:18 -04007769 if (!test_bit(JOB_WSTATS, &local->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007770 /* Get stats out of the card if available */
7771 if (down_trylock(&local->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04007772 set_bit(JOB_WSTATS, &local->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007773 wake_up_interruptible(&local->thr_wait);
7774 } else
7775 airo_read_wireless_stats(local);
7776 }
7777
7778 return &local->wstats;
7779}
Linus Torvalds1da177e2005-04-16 15:20:36 -07007780
7781#ifdef CISCO_EXT
7782/*
7783 * This just translates from driver IOCTL codes to the command codes to
7784 * feed to the radio's host interface. Things can be added/deleted
7785 * as needed. This represents the READ side of control I/O to
7786 * the card
7787 */
7788static int readrids(struct net_device *dev, aironet_ioctl *comp) {
7789 unsigned short ridcode;
7790 unsigned char *iobuf;
7791 int len;
Wang Chenfaf39942008-10-14 13:30:33 +08007792 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007793
7794 if (test_bit(FLAG_FLASHING, &ai->flags))
7795 return -EIO;
7796
7797 switch(comp->command)
7798 {
7799 case AIROGCAP: ridcode = RID_CAPABILITIES; break;
7800 case AIROGCFG: ridcode = RID_CONFIG;
7801 if (test_bit(FLAG_COMMIT, &ai->flags)) {
7802 disable_MAC (ai, 1);
7803 writeConfigRid (ai, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007804 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007805 }
7806 break;
7807 case AIROGSLIST: ridcode = RID_SSID; break;
7808 case AIROGVLIST: ridcode = RID_APLIST; break;
7809 case AIROGDRVNAM: ridcode = RID_DRVNAME; break;
7810 case AIROGEHTENC: ridcode = RID_ETHERENCAP; break;
7811 case AIROGWEPKTMP: ridcode = RID_WEP_TEMP;
7812 /* Only super-user can read WEP keys */
7813 if (!capable(CAP_NET_ADMIN))
7814 return -EPERM;
7815 break;
7816 case AIROGWEPKNV: ridcode = RID_WEP_PERM;
7817 /* Only super-user can read WEP keys */
7818 if (!capable(CAP_NET_ADMIN))
7819 return -EPERM;
7820 break;
7821 case AIROGSTAT: ridcode = RID_STATUS; break;
7822 case AIROGSTATSD32: ridcode = RID_STATSDELTA; break;
7823 case AIROGSTATSC32: ridcode = RID_STATS; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007824 case AIROGMICSTATS:
7825 if (copy_to_user(comp->data, &ai->micstats,
7826 min((int)comp->len,(int)sizeof(ai->micstats))))
7827 return -EFAULT;
7828 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007829 case AIRORRID: ridcode = comp->ridnum; break;
7830 default:
7831 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007832 }
7833
7834 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7835 return -ENOMEM;
7836
7837 PC4500_readrid(ai,ridcode,iobuf,RIDSIZE, 1);
7838 /* get the count of bytes in the rid docs say 1st 2 bytes is it.
7839 * then return it to the user
7840 * 9/22/2000 Honor user given length
7841 */
7842 len = comp->len;
7843
7844 if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) {
7845 kfree (iobuf);
7846 return -EFAULT;
7847 }
7848 kfree (iobuf);
7849 return 0;
7850}
7851
7852/*
7853 * Danger Will Robinson write the rids here
7854 */
7855
7856static int writerids(struct net_device *dev, aironet_ioctl *comp) {
Wang Chenfaf39942008-10-14 13:30:33 +08007857 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007858 int ridcode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007859 int enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007860 static int (* writer)(struct airo_info *, u16 rid, const void *, int, int);
7861 unsigned char *iobuf;
7862
7863 /* Only super-user can write RIDs */
7864 if (!capable(CAP_NET_ADMIN))
7865 return -EPERM;
7866
7867 if (test_bit(FLAG_FLASHING, &ai->flags))
7868 return -EIO;
7869
7870 ridcode = 0;
7871 writer = do_writerid;
7872
7873 switch(comp->command)
7874 {
7875 case AIROPSIDS: ridcode = RID_SSID; break;
7876 case AIROPCAP: ridcode = RID_CAPABILITIES; break;
7877 case AIROPAPLIST: ridcode = RID_APLIST; break;
7878 case AIROPCFG: ai->config.len = 0;
7879 clear_bit(FLAG_COMMIT, &ai->flags);
7880 ridcode = RID_CONFIG; break;
7881 case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break;
7882 case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break;
7883 case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break;
7884 case AIROPWEPKEY: ridcode = RID_WEP_TEMP; writer = PC4500_writerid;
7885 break;
7886 case AIROPLEAPUSR+1: ridcode = 0xFF2A; break;
7887 case AIROPLEAPUSR+2: ridcode = 0xFF2B; break;
7888
7889 /* this is not really a rid but a command given to the card
7890 * same with MAC off
7891 */
7892 case AIROPMACON:
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007893 if (enable_MAC(ai, 1) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007894 return -EIO;
7895 return 0;
7896
7897 /*
7898 * Evidently this code in the airo driver does not get a symbol
7899 * as disable_MAC. it's probably so short the compiler does not gen one.
7900 */
7901 case AIROPMACOFF:
7902 disable_MAC(ai, 1);
7903 return 0;
7904
7905 /* This command merely clears the counts does not actually store any data
7906 * only reads rid. But as it changes the cards state, I put it in the
7907 * writerid routines.
7908 */
7909 case AIROPSTCLR:
7910 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7911 return -ENOMEM;
7912
7913 PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDSIZE, 1);
7914
Linus Torvalds1da177e2005-04-16 15:20:36 -07007915 enabled = ai->micstats.enabled;
7916 memset(&ai->micstats,0,sizeof(ai->micstats));
7917 ai->micstats.enabled = enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007918
7919 if (copy_to_user(comp->data, iobuf,
7920 min((int)comp->len, (int)RIDSIZE))) {
7921 kfree (iobuf);
7922 return -EFAULT;
7923 }
7924 kfree (iobuf);
7925 return 0;
7926
7927 default:
7928 return -EOPNOTSUPP; /* Blarg! */
7929 }
7930 if(comp->len > RIDSIZE)
7931 return -EINVAL;
7932
7933 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7934 return -ENOMEM;
7935
7936 if (copy_from_user(iobuf,comp->data,comp->len)) {
7937 kfree (iobuf);
7938 return -EFAULT;
7939 }
7940
7941 if (comp->command == AIROPCFG) {
7942 ConfigRid *cfg = (ConfigRid *)iobuf;
7943
7944 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags))
Al Viro3eb9b412007-12-21 00:00:35 -05007945 cfg->opmode |= MODE_MIC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007946
Al Viro3eb9b412007-12-21 00:00:35 -05007947 if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007948 set_bit (FLAG_ADHOC, &ai->flags);
7949 else
7950 clear_bit (FLAG_ADHOC, &ai->flags);
7951 }
7952
7953 if((*writer)(ai, ridcode, iobuf,comp->len,1)) {
7954 kfree (iobuf);
7955 return -EIO;
7956 }
7957 kfree (iobuf);
7958 return 0;
7959}
7960
7961/*****************************************************************************
7962 * Ancillary flash / mod functions much black magic lurkes here *
7963 *****************************************************************************
7964 */
7965
7966/*
7967 * Flash command switch table
7968 */
7969
Jouni Malinenff1d2762005-05-12 22:54:16 -04007970static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007971 int z;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007972
7973 /* Only super-user can modify flash */
7974 if (!capable(CAP_NET_ADMIN))
7975 return -EPERM;
7976
7977 switch(comp->command)
7978 {
7979 case AIROFLSHRST:
Wang Chenfaf39942008-10-14 13:30:33 +08007980 return cmdreset((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007981
7982 case AIROFLSHSTFL:
Wang Chenfaf39942008-10-14 13:30:33 +08007983 if (!AIRO_FLASH(dev) &&
7984 (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007985 return -ENOMEM;
Wang Chenfaf39942008-10-14 13:30:33 +08007986 return setflashmode((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007987
7988 case AIROFLSHGCHR: /* Get char from aux */
7989 if(comp->len != sizeof(int))
7990 return -EINVAL;
7991 if (copy_from_user(&z,comp->data,comp->len))
7992 return -EFAULT;
Wang Chenfaf39942008-10-14 13:30:33 +08007993 return flashgchar((struct airo_info *)dev->ml_priv, z, 8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007994
7995 case AIROFLSHPCHR: /* Send char to card. */
7996 if(comp->len != sizeof(int))
7997 return -EINVAL;
7998 if (copy_from_user(&z,comp->data,comp->len))
7999 return -EFAULT;
Wang Chenfaf39942008-10-14 13:30:33 +08008000 return flashpchar((struct airo_info *)dev->ml_priv, z, 8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008001
8002 case AIROFLPUTBUF: /* Send 32k to card */
Wang Chenfaf39942008-10-14 13:30:33 +08008003 if (!AIRO_FLASH(dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008004 return -ENOMEM;
8005 if(comp->len > FLASHSIZE)
8006 return -EINVAL;
Wang Chenfaf39942008-10-14 13:30:33 +08008007 if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008008 return -EFAULT;
8009
Wang Chenfaf39942008-10-14 13:30:33 +08008010 flashputbuf((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008011 return 0;
8012
8013 case AIRORESTART:
Wang Chenfaf39942008-10-14 13:30:33 +08008014 if (flashrestart((struct airo_info *)dev->ml_priv, dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008015 return -EIO;
8016 return 0;
8017 }
8018 return -EINVAL;
8019}
8020
8021#define FLASH_COMMAND 0x7e7e
8022
8023/*
8024 * STEP 1)
8025 * Disable MAC and do soft reset on
8026 * card.
8027 */
8028
Jouni Malinenff1d2762005-05-12 22:54:16 -04008029static int cmdreset(struct airo_info *ai) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07008030 disable_MAC(ai, 1);
8031
8032 if(!waitbusy (ai)){
Dan Williams934d8bf2006-03-16 13:46:23 -05008033 airo_print_info(ai->dev->name, "Waitbusy hang before RESET");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008034 return -EBUSY;
8035 }
8036
8037 OUT4500(ai,COMMAND,CMD_SOFTRESET);
8038
8039 ssleep(1); /* WAS 600 12/7/00 */
8040
8041 if(!waitbusy (ai)){
Dan Williams934d8bf2006-03-16 13:46:23 -05008042 airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008043 return -EBUSY;
8044 }
8045 return 0;
8046}
8047
8048/* STEP 2)
8049 * Put the card in legendary flash
8050 * mode
8051 */
8052
Jouni Malinenff1d2762005-05-12 22:54:16 -04008053static int setflashmode (struct airo_info *ai) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07008054 set_bit (FLAG_FLASHING, &ai->flags);
8055
8056 OUT4500(ai, SWS0, FLASH_COMMAND);
8057 OUT4500(ai, SWS1, FLASH_COMMAND);
8058 if (probe) {
8059 OUT4500(ai, SWS0, FLASH_COMMAND);
8060 OUT4500(ai, COMMAND,0x10);
8061 } else {
8062 OUT4500(ai, SWS2, FLASH_COMMAND);
8063 OUT4500(ai, SWS3, FLASH_COMMAND);
8064 OUT4500(ai, COMMAND,0);
8065 }
8066 msleep(500); /* 500ms delay */
8067
8068 if(!waitbusy(ai)) {
8069 clear_bit (FLAG_FLASHING, &ai->flags);
Dan Williams934d8bf2006-03-16 13:46:23 -05008070 airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008071 return -EIO;
8072 }
8073 return 0;
8074}
8075
8076/* Put character to SWS0 wait for dwelltime
8077 * x 50us for echo .
8078 */
8079
Jouni Malinenff1d2762005-05-12 22:54:16 -04008080static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07008081 int echo;
8082 int waittime;
8083
8084 byte |= 0x8000;
8085
8086 if(dwelltime == 0 )
8087 dwelltime = 200;
8088
8089 waittime=dwelltime;
8090
8091 /* Wait for busy bit d15 to go false indicating buffer empty */
8092 while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) {
8093 udelay (50);
8094 waittime -= 50;
8095 }
8096
8097 /* timeout for busy clear wait */
8098 if(waittime <= 0 ){
Dan Williams934d8bf2006-03-16 13:46:23 -05008099 airo_print_info(ai->dev->name, "flash putchar busywait timeout!");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008100 return -EBUSY;
8101 }
8102
8103 /* Port is clear now write byte and wait for it to echo back */
8104 do {
8105 OUT4500(ai,SWS0,byte);
8106 udelay(50);
8107 dwelltime -= 50;
8108 echo = IN4500(ai,SWS1);
8109 } while (dwelltime >= 0 && echo != byte);
8110
8111 OUT4500(ai,SWS1,0);
8112
8113 return (echo == byte) ? 0 : -EIO;
8114}
8115
8116/*
8117 * Get a character from the card matching matchbyte
8118 * Step 3)
8119 */
Jouni Malinenff1d2762005-05-12 22:54:16 -04008120static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008121 int rchar;
8122 unsigned char rbyte=0;
8123
8124 do {
8125 rchar = IN4500(ai,SWS1);
8126
8127 if(dwelltime && !(0x8000 & rchar)){
8128 dwelltime -= 10;
8129 mdelay(10);
8130 continue;
8131 }
8132 rbyte = 0xff & rchar;
8133
8134 if( (rbyte == matchbyte) && (0x8000 & rchar) ){
8135 OUT4500(ai,SWS1,0);
8136 return 0;
8137 }
8138 if( rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar)
8139 break;
8140 OUT4500(ai,SWS1,0);
8141
8142 }while(dwelltime > 0);
8143 return -EIO;
8144}
8145
8146/*
8147 * Transfer 32k of firmware data from user buffer to our buffer and
8148 * send to the card
8149 */
8150
Jouni Malinenff1d2762005-05-12 22:54:16 -04008151static int flashputbuf(struct airo_info *ai){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008152 int nwords;
8153
8154 /* Write stuff */
8155 if (test_bit(FLAG_MPI,&ai->flags))
8156 memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE);
8157 else {
8158 OUT4500(ai,AUXPAGE,0x100);
8159 OUT4500(ai,AUXOFF,0);
8160
8161 for(nwords=0;nwords != FLASHSIZE / 2;nwords++){
8162 OUT4500(ai,AUXDATA,ai->flash[nwords] & 0xffff);
8163 }
8164 }
8165 OUT4500(ai,SWS0,0x8000);
8166
8167 return 0;
8168}
8169
8170/*
8171 *
8172 */
Jouni Malinenff1d2762005-05-12 22:54:16 -04008173static int flashrestart(struct airo_info *ai,struct net_device *dev){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008174 int i,status;
8175
8176 ssleep(1); /* Added 12/7/00 */
8177 clear_bit (FLAG_FLASHING, &ai->flags);
8178 if (test_bit(FLAG_MPI, &ai->flags)) {
8179 status = mpi_init_descriptors(ai);
8180 if (status != SUCCESS)
8181 return status;
8182 }
8183 status = setup_card(ai, dev->dev_addr, 1);
8184
8185 if (!test_bit(FLAG_MPI,&ai->flags))
8186 for( i = 0; i < MAX_FIDS; i++ ) {
8187 ai->fids[i] = transmit_allocate
Dan Williams15db2762006-03-16 13:46:27 -05008188 ( ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07008189 }
8190
8191 ssleep(1); /* Added 12/7/00 */
8192 return status;
8193}
8194#endif /* CISCO_EXT */
8195
8196/*
8197 This program is free software; you can redistribute it and/or
8198 modify it under the terms of the GNU General Public License
8199 as published by the Free Software Foundation; either version 2
8200 of the License, or (at your option) any later version.
8201
8202 This program is distributed in the hope that it will be useful,
8203 but WITHOUT ANY WARRANTY; without even the implied warranty of
8204 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8205 GNU General Public License for more details.
8206
8207 In addition:
8208
8209 Redistribution and use in source and binary forms, with or without
8210 modification, are permitted provided that the following conditions
8211 are met:
8212
8213 1. Redistributions of source code must retain the above copyright
8214 notice, this list of conditions and the following disclaimer.
8215 2. Redistributions in binary form must reproduce the above copyright
8216 notice, this list of conditions and the following disclaimer in the
8217 documentation and/or other materials provided with the distribution.
8218 3. The name of the author may not be used to endorse or promote
8219 products derived from this software without specific prior written
8220 permission.
8221
8222 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
8223 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8224 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
8225 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
8226 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
8227 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
8228 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
8229 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
8230 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
8231 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
8232 POSSIBILITY OF SUCH DAMAGE.
8233*/
8234
8235module_init(airo_init_module);
8236module_exit(airo_cleanup_module);