blob: 49872db895146729b62e6eeb5f78dcd0a023a474 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*======================================================================
2
3 Aironet driver for 4500 and 4800 series cards
4
5 This code is released under both the GPL version 2 and BSD licenses.
6 Either license may be used. The respective licenses are found at
7 the end of this file.
8
9 This code was developed by Benjamin Reed <breed@users.sourceforge.net>
10 including portions of which come from the Aironet PC4500
11 Developer's Reference Manual and used with permission. Copyright
12 (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use
13 code in the Developer's manual was granted for this driver by
14 Aironet. Major code contributions were received from Javier Achirica
15 <achirica@users.sourceforge.net> and Jean Tourrilhes <jt@hpl.hp.com>.
16 Code was also integrated from the Cisco Aironet driver for Linux.
17 Support for MPI350 cards was added by Fabrice Bellet
18 <fabrice@bellet.info>.
19
20======================================================================*/
21
Herbert Xuf12cc202006-08-22 20:36:13 +100022#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/init.h>
24
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/proc_fs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#include <linux/sched.h>
30#include <linux/ptrace.h>
31#include <linux/slab.h>
32#include <linux/string.h>
33#include <linux/timer.h>
34#include <linux/interrupt.h>
35#include <linux/in.h>
36#include <linux/bitops.h>
David Hardeman378f0582005-09-17 17:55:31 +100037#include <linux/scatterlist.h>
Adrian Bunka39d3e72006-01-21 01:35:15 +010038#include <linux/crypto.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <asm/io.h>
40#include <asm/system.h>
Al Viro593c2b92007-12-17 15:09:34 -050041#include <asm/unaligned.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
43#include <linux/netdevice.h>
44#include <linux/etherdevice.h>
45#include <linux/skbuff.h>
46#include <linux/if_arp.h>
47#include <linux/ioport.h>
48#include <linux/pci.h>
49#include <asm/uaccess.h>
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -070050#include <linux/kthread.h>
Nigel Cunningham7dfb7102006-12-06 20:34:23 -080051#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
Johannes Berg2c7060022008-10-30 22:09:54 +010053#include <linux/ieee80211.h>
54
Adrian Bunkd3808762005-11-05 17:42:27 +010055#include "airo.h"
56
Michal Schmidt1138c372007-06-29 15:33:41 +020057#define DRV_NAME "airo"
58
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#ifdef CONFIG_PCI
60static struct pci_device_id card_ids[] = {
61 { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, },
62 { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID },
63 { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, },
64 { 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, },
65 { 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, },
66 { 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, },
67 { 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, },
68 { 0, }
69};
70MODULE_DEVICE_TABLE(pci, card_ids);
71
72static int airo_pci_probe(struct pci_dev *, const struct pci_device_id *);
73static void airo_pci_remove(struct pci_dev *);
Pavel Machek05adc3b2005-04-16 15:25:25 -070074static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state);
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static int airo_pci_resume(struct pci_dev *pdev);
76
77static struct pci_driver airo_driver = {
Michal Schmidt1138c372007-06-29 15:33:41 +020078 .name = DRV_NAME,
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 .id_table = card_ids,
80 .probe = airo_pci_probe,
81 .remove = __devexit_p(airo_pci_remove),
82 .suspend = airo_pci_suspend,
83 .resume = airo_pci_resume,
84};
85#endif /* CONFIG_PCI */
86
87/* Include Wireless Extension definition and check version - Jean II */
88#include <linux/wireless.h>
Pavel Macheke292c732008-06-25 12:25:53 +020089#define WIRELESS_SPY /* enable iwspy support */
90#include <net/iw_handler.h> /* New driver API */
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Pavel Macheke292c732008-06-25 12:25:53 +020092#define CISCO_EXT /* enable Cisco extensions */
Linus Torvalds1da177e2005-04-16 15:20:36 -070093#ifdef CISCO_EXT
94#include <linux/delay.h>
95#endif
96
Linus Torvalds1da177e2005-04-16 15:20:36 -070097/* Hack to do some power saving */
98#define POWER_ON_DOWN
99
100/* As you can see this list is HUGH!
101 I really don't know what a lot of these counts are about, but they
102 are all here for completeness. If the IGNLABEL macro is put in
103 infront of the label, that statistic will not be included in the list
104 of statistics in the /proc filesystem */
105
106#define IGNLABEL(comment) NULL
107static char *statsLabels[] = {
108 "RxOverrun",
109 IGNLABEL("RxPlcpCrcErr"),
110 IGNLABEL("RxPlcpFormatErr"),
111 IGNLABEL("RxPlcpLengthErr"),
112 "RxMacCrcErr",
113 "RxMacCrcOk",
114 "RxWepErr",
115 "RxWepOk",
116 "RetryLong",
117 "RetryShort",
118 "MaxRetries",
119 "NoAck",
120 "NoCts",
121 "RxAck",
122 "RxCts",
123 "TxAck",
124 "TxRts",
125 "TxCts",
126 "TxMc",
127 "TxBc",
128 "TxUcFrags",
129 "TxUcPackets",
130 "TxBeacon",
131 "RxBeacon",
132 "TxSinColl",
133 "TxMulColl",
134 "DefersNo",
135 "DefersProt",
136 "DefersEngy",
137 "DupFram",
138 "RxFragDisc",
139 "TxAged",
140 "RxAged",
141 "LostSync-MaxRetry",
142 "LostSync-MissedBeacons",
143 "LostSync-ArlExceeded",
144 "LostSync-Deauth",
145 "LostSync-Disassoced",
146 "LostSync-TsfTiming",
147 "HostTxMc",
148 "HostTxBc",
149 "HostTxUc",
150 "HostTxFail",
151 "HostRxMc",
152 "HostRxBc",
153 "HostRxUc",
154 "HostRxDiscard",
155 IGNLABEL("HmacTxMc"),
156 IGNLABEL("HmacTxBc"),
157 IGNLABEL("HmacTxUc"),
158 IGNLABEL("HmacTxFail"),
159 IGNLABEL("HmacRxMc"),
160 IGNLABEL("HmacRxBc"),
161 IGNLABEL("HmacRxUc"),
162 IGNLABEL("HmacRxDiscard"),
163 IGNLABEL("HmacRxAccepted"),
164 "SsidMismatch",
165 "ApMismatch",
166 "RatesMismatch",
167 "AuthReject",
168 "AuthTimeout",
169 "AssocReject",
170 "AssocTimeout",
171 IGNLABEL("ReasonOutsideTable"),
172 IGNLABEL("ReasonStatus1"),
173 IGNLABEL("ReasonStatus2"),
174 IGNLABEL("ReasonStatus3"),
175 IGNLABEL("ReasonStatus4"),
176 IGNLABEL("ReasonStatus5"),
177 IGNLABEL("ReasonStatus6"),
178 IGNLABEL("ReasonStatus7"),
179 IGNLABEL("ReasonStatus8"),
180 IGNLABEL("ReasonStatus9"),
181 IGNLABEL("ReasonStatus10"),
182 IGNLABEL("ReasonStatus11"),
183 IGNLABEL("ReasonStatus12"),
184 IGNLABEL("ReasonStatus13"),
185 IGNLABEL("ReasonStatus14"),
186 IGNLABEL("ReasonStatus15"),
187 IGNLABEL("ReasonStatus16"),
188 IGNLABEL("ReasonStatus17"),
189 IGNLABEL("ReasonStatus18"),
190 IGNLABEL("ReasonStatus19"),
191 "RxMan",
192 "TxMan",
193 "RxRefresh",
194 "TxRefresh",
195 "RxPoll",
196 "TxPoll",
197 "HostRetries",
198 "LostSync-HostReq",
199 "HostTxBytes",
200 "HostRxBytes",
201 "ElapsedUsec",
202 "ElapsedSec",
203 "LostSyncBetterAP",
204 "PrivacyMismatch",
205 "Jammed",
206 "DiscRxNotWepped",
207 "PhyEleMismatch",
208 (char*)-1 };
209#ifndef RUN_AT
210#define RUN_AT(x) (jiffies+(x))
211#endif
212
213
214/* These variables are for insmod, since it seems that the rates
215 can only be set in setup_card. Rates should be a comma separated
216 (no spaces) list of rates (up to 8). */
217
218static int rates[8];
219static int basic_rate;
220static char *ssids[3];
221
222static int io[4];
223static int irq[4];
224
225static
226int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at.
227 0 means no limit. For old cards this was 4 */
228
229static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */
230static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read
231 the bap, needed on some older cards and buses. */
232static int adhoc;
233
234static int probe = 1;
235
236static int proc_uid /* = 0 */;
237
238static int proc_gid /* = 0 */;
239
240static int airo_perm = 0555;
241
242static int proc_perm = 0644;
243
244MODULE_AUTHOR("Benjamin Reed");
245MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet \
Bill Nottinghamd73ae552007-07-27 19:43:17 -0400246cards. Direct support for ISA/PCI/MPI cards and support \
247for PCMCIA when used with airo_cs.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248MODULE_LICENSE("Dual BSD/GPL");
249MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350");
250module_param_array(io, int, NULL, 0);
251module_param_array(irq, int, NULL, 0);
252module_param(basic_rate, int, 0);
253module_param_array(rates, int, NULL, 0);
254module_param_array(ssids, charp, NULL, 0);
255module_param(auto_wep, int, 0);
256MODULE_PARM_DESC(auto_wep, "If non-zero, the driver will keep looping through \
257the authentication options until an association is made. The value of \
258auto_wep is number of the wep keys to check. A value of 2 will try using \
259the key at index 0 and index 1.");
260module_param(aux_bap, int, 0);
261MODULE_PARM_DESC(aux_bap, "If non-zero, the driver will switch into a mode \
262than seems to work better for older cards with some older buses. Before \
263switching it checks that the switch is needed.");
264module_param(maxencrypt, int, 0);
265MODULE_PARM_DESC(maxencrypt, "The maximum speed that the card can do \
266encryption. Units are in 512kbs. Zero (default) means there is no limit. \
267Older cards used to be limited to 2mbs (4).");
268module_param(adhoc, int, 0);
269MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode.");
270module_param(probe, int, 0);
271MODULE_PARM_DESC(probe, "If zero, the driver won't start the card.");
272
273module_param(proc_uid, int, 0);
274MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to.");
275module_param(proc_gid, int, 0);
276MODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to.");
277module_param(airo_perm, int, 0);
278MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet.");
279module_param(proc_perm, int, 0);
280MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc");
281
282/* This is a kind of sloppy hack to get this information to OUT4500 and
283 IN4500. I would be extremely interested in the situation where this
284 doesn't work though!!! */
Pavel Macheke292c732008-06-25 12:25:53 +0200285static int do8bitIO /* = 0 */;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
287/* Return codes */
288#define SUCCESS 0
289#define ERROR -1
290#define NO_PACKET -2
291
292/* Commands */
293#define NOP2 0x0000
294#define MAC_ENABLE 0x0001
295#define MAC_DISABLE 0x0002
296#define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */
297#define CMD_SOFTRESET 0x0004
298#define HOSTSLEEP 0x0005
299#define CMD_MAGIC_PKT 0x0006
300#define CMD_SETWAKEMASK 0x0007
301#define CMD_READCFG 0x0008
302#define CMD_SETMODE 0x0009
303#define CMD_ALLOCATETX 0x000a
304#define CMD_TRANSMIT 0x000b
305#define CMD_DEALLOCATETX 0x000c
306#define NOP 0x0010
307#define CMD_WORKAROUND 0x0011
308#define CMD_ALLOCATEAUX 0x0020
309#define CMD_ACCESS 0x0021
310#define CMD_PCIBAP 0x0022
311#define CMD_PCIAUX 0x0023
312#define CMD_ALLOCBUF 0x0028
313#define CMD_GETTLV 0x0029
314#define CMD_PUTTLV 0x002a
315#define CMD_DELTLV 0x002b
316#define CMD_FINDNEXTTLV 0x002c
317#define CMD_PSPNODES 0x0030
318#define CMD_SETCW 0x0031
319#define CMD_SETPCF 0x0032
320#define CMD_SETPHYREG 0x003e
321#define CMD_TXTEST 0x003f
322#define MAC_ENABLETX 0x0101
323#define CMD_LISTBSS 0x0103
324#define CMD_SAVECFG 0x0108
325#define CMD_ENABLEAUX 0x0111
326#define CMD_WRITERID 0x0121
327#define CMD_USEPSPNODES 0x0130
328#define MAC_ENABLERX 0x0201
329
330/* Command errors */
331#define ERROR_QUALIF 0x00
332#define ERROR_ILLCMD 0x01
333#define ERROR_ILLFMT 0x02
334#define ERROR_INVFID 0x03
335#define ERROR_INVRID 0x04
336#define ERROR_LARGE 0x05
337#define ERROR_NDISABL 0x06
338#define ERROR_ALLOCBSY 0x07
339#define ERROR_NORD 0x0B
340#define ERROR_NOWR 0x0C
341#define ERROR_INVFIDTX 0x0D
342#define ERROR_TESTACT 0x0E
343#define ERROR_TAGNFND 0x12
344#define ERROR_DECODE 0x20
345#define ERROR_DESCUNAV 0x21
346#define ERROR_BADLEN 0x22
347#define ERROR_MODE 0x80
348#define ERROR_HOP 0x81
349#define ERROR_BINTER 0x82
350#define ERROR_RXMODE 0x83
351#define ERROR_MACADDR 0x84
352#define ERROR_RATES 0x85
353#define ERROR_ORDER 0x86
354#define ERROR_SCAN 0x87
355#define ERROR_AUTH 0x88
356#define ERROR_PSMODE 0x89
357#define ERROR_RTYPE 0x8A
358#define ERROR_DIVER 0x8B
359#define ERROR_SSID 0x8C
360#define ERROR_APLIST 0x8D
361#define ERROR_AUTOWAKE 0x8E
362#define ERROR_LEAP 0x8F
363
364/* Registers */
365#define COMMAND 0x00
366#define PARAM0 0x02
367#define PARAM1 0x04
368#define PARAM2 0x06
369#define STATUS 0x08
370#define RESP0 0x0a
371#define RESP1 0x0c
372#define RESP2 0x0e
373#define LINKSTAT 0x10
374#define SELECT0 0x18
375#define OFFSET0 0x1c
376#define RXFID 0x20
377#define TXALLOCFID 0x22
378#define TXCOMPLFID 0x24
379#define DATA0 0x36
380#define EVSTAT 0x30
381#define EVINTEN 0x32
382#define EVACK 0x34
383#define SWS0 0x28
384#define SWS1 0x2a
385#define SWS2 0x2c
386#define SWS3 0x2e
387#define AUXPAGE 0x3A
388#define AUXOFF 0x3C
389#define AUXDATA 0x3E
390
391#define FID_TX 1
392#define FID_RX 2
393/* Offset into aux memory for descriptors */
394#define AUX_OFFSET 0x800
395/* Size of allocated packets */
396#define PKTSIZE 1840
397#define RIDSIZE 2048
398/* Size of the transmit queue */
399#define MAXTXQ 64
400
401/* BAP selectors */
Pavel Macheke292c732008-06-25 12:25:53 +0200402#define BAP0 0 /* Used for receiving packets */
403#define BAP1 2 /* Used for xmiting packets and working with RIDS */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
405/* Flags */
406#define COMMAND_BUSY 0x8000
407
408#define BAP_BUSY 0x8000
409#define BAP_ERR 0x4000
410#define BAP_DONE 0x2000
411
412#define PROMISC 0xffff
413#define NOPROMISC 0x0000
414
415#define EV_CMD 0x10
416#define EV_CLEARCOMMANDBUSY 0x4000
417#define EV_RX 0x01
418#define EV_TX 0x02
419#define EV_TXEXC 0x04
420#define EV_ALLOC 0x08
421#define EV_LINK 0x80
422#define EV_AWAKE 0x100
423#define EV_TXCPY 0x400
424#define EV_UNKNOWN 0x800
425#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */
426#define EV_AWAKEN 0x2000
427#define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC)
428
429#ifdef CHECK_UNKNOWN_INTS
430#define IGNORE_INTS ( EV_CMD | EV_UNKNOWN)
431#else
432#define IGNORE_INTS (~STATUS_INTS)
433#endif
434
435/* RID TYPES */
436#define RID_RW 0x20
437
438/* The RIDs */
439#define RID_CAPABILITIES 0xFF00
440#define RID_APINFO 0xFF01
441#define RID_RADIOINFO 0xFF02
442#define RID_UNKNOWN3 0xFF03
443#define RID_RSSI 0xFF04
444#define RID_CONFIG 0xFF10
445#define RID_SSID 0xFF11
446#define RID_APLIST 0xFF12
447#define RID_DRVNAME 0xFF13
448#define RID_ETHERENCAP 0xFF14
449#define RID_WEP_TEMP 0xFF15
450#define RID_WEP_PERM 0xFF16
451#define RID_MODULATION 0xFF17
452#define RID_OPTIONS 0xFF18
453#define RID_ACTUALCONFIG 0xFF20 /*readonly*/
454#define RID_FACTORYCONFIG 0xFF21
455#define RID_UNKNOWN22 0xFF22
456#define RID_LEAPUSERNAME 0xFF23
457#define RID_LEAPPASSWORD 0xFF24
458#define RID_STATUS 0xFF50
459#define RID_BEACON_HST 0xFF51
460#define RID_BUSY_HST 0xFF52
461#define RID_RETRIES_HST 0xFF53
462#define RID_UNKNOWN54 0xFF54
463#define RID_UNKNOWN55 0xFF55
464#define RID_UNKNOWN56 0xFF56
465#define RID_MIC 0xFF57
466#define RID_STATS16 0xFF60
467#define RID_STATS16DELTA 0xFF61
468#define RID_STATS16DELTACLEAR 0xFF62
469#define RID_STATS 0xFF68
470#define RID_STATSDELTA 0xFF69
471#define RID_STATSDELTACLEAR 0xFF6A
472#define RID_ECHOTEST_RID 0xFF70
473#define RID_ECHOTEST_RESULTS 0xFF71
474#define RID_BSSLISTFIRST 0xFF72
475#define RID_BSSLISTNEXT 0xFF73
Dan Williams3c304952006-04-15 12:26:18 -0400476#define RID_WPA_BSSLISTFIRST 0xFF74
477#define RID_WPA_BSSLISTNEXT 0xFF75
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478
479typedef struct {
480 u16 cmd;
481 u16 parm0;
482 u16 parm1;
483 u16 parm2;
484} Cmd;
485
486typedef struct {
487 u16 status;
488 u16 rsp0;
489 u16 rsp1;
490 u16 rsp2;
491} Resp;
492
493/*
494 * Rids and endian-ness: The Rids will always be in cpu endian, since
495 * this all the patches from the big-endian guys end up doing that.
496 * so all rid access should use the read/writeXXXRid routines.
497 */
498
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];
Dan Williams99590ff2009-01-24 09:12:58 -0500508} __attribute__ ((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];
Dan Williams99590ff2009-01-24 09:12:58 -0500515} __attribute__ ((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];
Dan Williams99590ff2009-01-24 09:12:58 -0500521} __attribute__ ((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)
Dan Williams99590ff2009-01-24 09:12:58 -0500530} __attribute__ ((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;
Dan Williams99590ff2009-01-24 09:12:58 -0500654} __attribute__ ((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
Dan Williams99590ff2009-01-24 09:12:58 -0500713} __attribute__ ((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];
Dan Williams99590ff2009-01-24 09:12:58 -0500720} __attribute__ ((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];
Dan Williams99590ff2009-01-24 09:12:58 -0500726} __attribute__ ((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;
Dan Williams99590ff2009-01-24 09:12:58 -0500756} __attribute__ ((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];
Dan Williams99590ff2009-01-24 09:12:58 -0500764} __attribute__ ((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;
Dan Williams99590ff2009-01-24 09:12:58 -0500798} __attribute__ ((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;
Dan Williams99590ff2009-01-24 09:12:58 -0500809} __attribute__ ((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];
Dan Williams99590ff2009-01-24 09:12:58 -0500815} __attribute__ ((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];
Dan Williams99590ff2009-01-24 09:12:58 -0500825} __attribute__ ((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;
Dan Williams99590ff2009-01-24 09:12:58 -0500843} __attribute__ ((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
Domen Puncer62595eb2005-06-20 23:54:37 +0200934static 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];
998} __attribute__ ((packed));
999
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
1097/* Backward compatibility */
1098#ifndef IW_ENCODE_NOKEY
1099#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */
1100#define IW_ENCODE_MODE (IW_ENCODE_DISABLED | IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN)
1101#endif /* IW_ENCODE_NOKEY */
1102
1103/* List of Wireless Handlers (new API) */
1104static const struct iw_handler_def airo_handler_def;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105
1106static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)";
1107
1108struct airo_info;
1109
1110static int get_dec_u16( char *buffer, int *start, int limit );
1111static void OUT4500( struct airo_info *, u16 register, u16 value );
1112static unsigned short IN4500( struct airo_info *, u16 register );
1113static u16 setup_card(struct airo_info*, u8 *mac, int lock);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02001114static int enable_MAC(struct airo_info *ai, int lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115static void disable_MAC(struct airo_info *ai, int lock);
1116static void enable_interrupts(struct airo_info*);
1117static void disable_interrupts(struct airo_info*);
1118static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp);
1119static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001120static int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001122static int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001124static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 int whichbap);
1126static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd);
1127static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock);
1128static int PC4500_writerid(struct airo_info*, u16 rid, const void
1129 *pBuf, int len, int lock);
1130static int do_writerid( struct airo_info*, u16 rid, const void *rid_data,
1131 int len, int dummy );
1132static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw);
1133static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket);
1134static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket);
1135
1136static int mpi_send_packet (struct net_device *dev);
1137static void mpi_unmap_card(struct pci_dev *pci);
1138static void mpi_receive_802_3(struct airo_info *ai);
1139static void mpi_receive_802_11(struct airo_info *ai);
1140static int waitbusy (struct airo_info *ai);
1141
David Howells7d12e782006-10-05 14:55:46 +01001142static irqreturn_t airo_interrupt( int irq, void* dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143static int airo_thread(void *data);
1144static void timer_func( struct net_device *dev );
1145static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001146static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147static void airo_read_wireless_stats (struct airo_info *local);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148#ifdef CISCO_EXT
1149static int readrids(struct net_device *dev, aironet_ioctl *comp);
1150static int writerids(struct net_device *dev, aironet_ioctl *comp);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001151static int flashcard(struct net_device *dev, aironet_ioctl *comp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152#endif /* CISCO_EXT */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153static void micinit(struct airo_info *ai);
1154static int micsetup(struct airo_info *ai);
1155static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len);
1156static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen);
1157
Dan Williams41480af2005-05-10 09:45:51 -04001158static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi);
1159static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm);
1160
Dan Williams9e75af32006-03-16 13:46:29 -05001161static void airo_networks_free(struct airo_info *ai);
1162
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163struct airo_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 struct net_device *dev;
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01001165 struct list_head dev_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we
1167 use the high bit to mark whether it is in use. */
1168#define MAX_FIDS 6
1169#define MPI_MAX_FIDS 1
1170 int fids[MAX_FIDS];
1171 ConfigRid config;
1172 char keyindex; // Used with auto wep
1173 char defindex; // Used with auto wep
1174 struct proc_dir_entry *proc_entry;
1175 spinlock_t aux_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176#define FLAG_RADIO_OFF 0 /* User disabling of MAC */
1177#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */
1178#define FLAG_RADIO_MASK 0x03
1179#define FLAG_ENABLED 2
1180#define FLAG_ADHOC 3 /* Needed by MIC */
1181#define FLAG_MIC_CAPABLE 4
1182#define FLAG_UPDATE_MULTI 5
1183#define FLAG_UPDATE_UNI 6
1184#define FLAG_802_11 7
Dan Williams3c304952006-04-15 12:26:18 -04001185#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186#define FLAG_PENDING_XMIT 9
1187#define FLAG_PENDING_XMIT11 10
1188#define FLAG_MPI 11
1189#define FLAG_REGISTERED 12
1190#define FLAG_COMMIT 13
1191#define FLAG_RESET 14
1192#define FLAG_FLASHING 15
Dan Williams3c304952006-04-15 12:26:18 -04001193#define FLAG_WPA_CAPABLE 16
1194 unsigned long flags;
1195#define JOB_DIE 0
1196#define JOB_XMIT 1
1197#define JOB_XMIT11 2
1198#define JOB_STATS 3
1199#define JOB_PROMISC 4
1200#define JOB_MIC 5
1201#define JOB_EVENT 6
1202#define JOB_AUTOWEP 7
1203#define JOB_WSTATS 8
1204#define JOB_SCAN_RESULTS 9
1205 unsigned long jobs;
Al Virob8c06bc2007-12-19 17:55:43 -05001206 int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 int whichbap);
1208 unsigned short *flash;
1209 tdsRssiEntry *rssi;
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001210 struct task_struct *list_bss_task;
1211 struct task_struct *airo_thread_task;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 struct semaphore sem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 wait_queue_head_t thr_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 unsigned long expires;
1215 struct {
1216 struct sk_buff *skb;
1217 int fid;
1218 } xmit, xmit11;
1219 struct net_device *wifidev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 struct iw_statistics wstats; // wireless stats
Dan Williams9e75af32006-03-16 13:46:29 -05001221 unsigned long scan_timeout; /* Time scan should be read */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 struct iw_spy_data spy_data;
1223 struct iw_public_data wireless_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 /* MIC stuff */
Herbert Xuf12cc202006-08-22 20:36:13 +10001225 struct crypto_cipher *tfm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 mic_module mod[2];
1227 mic_statistics micstats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors
1229 HostTxDesc txfids[MPI_MAX_FIDS];
1230 HostRidDesc config_desc;
1231 unsigned long ridbus; // phys addr of config_desc
1232 struct sk_buff_head txq;// tx queue used by mpi350 code
1233 struct pci_dev *pci;
1234 unsigned char __iomem *pcimem;
1235 unsigned char __iomem *pciaux;
1236 unsigned char *shared;
1237 dma_addr_t shared_dma;
Pavel Machek1cc68ae2005-06-20 15:33:04 -07001238 pm_message_t power;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 SsidRid *SSID;
1240 APListRid *APList;
1241#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
1242 char proc_name[IFNAMSIZ];
Dan Williams9e75af32006-03-16 13:46:29 -05001243
Dan Williams138c0c62009-01-24 09:11:35 -05001244 int wep_capable;
1245 int max_wep_idx;
1246
Dan Williams3c304952006-04-15 12:26:18 -04001247 /* WPA-related stuff */
1248 unsigned int bssListFirst;
1249 unsigned int bssListNext;
1250 unsigned int bssListRidLen;
1251
Dan Williams9e75af32006-03-16 13:46:29 -05001252 struct list_head network_list;
1253 struct list_head network_free_list;
1254 BSSListElement *networks;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255};
1256
Al Virob8c06bc2007-12-19 17:55:43 -05001257static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen,
1258 int whichbap)
1259{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 return ai->bap_read(ai, pu16Dst, bytelen, whichbap);
1261}
1262
1263static int setup_proc_entry( struct net_device *dev,
1264 struct airo_info *apriv );
1265static int takedown_proc_entry( struct net_device *dev,
1266 struct airo_info *apriv );
1267
Jouni Malinenff1d2762005-05-12 22:54:16 -04001268static int cmdreset(struct airo_info *ai);
1269static int setflashmode (struct airo_info *ai);
1270static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
1271static int flashputbuf(struct airo_info *ai);
1272static int flashrestart(struct airo_info *ai,struct net_device *dev);
1273
Dan Williams934d8bf2006-03-16 13:46:23 -05001274#define airo_print(type, name, fmt, args...) \
Michal Schmidt1138c372007-06-29 15:33:41 +02001275 printk(type DRV_NAME "(%s): " fmt "\n", name, ##args)
Dan Williams934d8bf2006-03-16 13:46:23 -05001276
1277#define airo_print_info(name, fmt, args...) \
1278 airo_print(KERN_INFO, name, fmt, ##args)
1279
1280#define airo_print_dbg(name, fmt, args...) \
1281 airo_print(KERN_DEBUG, name, fmt, ##args)
1282
1283#define airo_print_warn(name, fmt, args...) \
1284 airo_print(KERN_WARNING, name, fmt, ##args)
1285
1286#define airo_print_err(name, fmt, args...) \
1287 airo_print(KERN_ERR, name, fmt, ##args)
1288
Wang Chenfaf39942008-10-14 13:30:33 +08001289#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash)
Dan Williams934d8bf2006-03-16 13:46:23 -05001290
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291/***********************************************************************
1292 * MIC ROUTINES *
1293 ***********************************************************************
1294 */
1295
1296static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq);
1297static void MoveWindow(miccntx *context, u32 micSeq);
Herbert Xuf12cc202006-08-22 20:36:13 +10001298static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
1299 struct crypto_cipher *tfm);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001300static void emmh32_init(emmh32_context *context);
1301static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
1302static void emmh32_final(emmh32_context *context, u8 digest[4]);
1303static int flashpchar(struct airo_info *ai,int byte,int dwelltime);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304
1305/* micinit - Initialize mic seed */
1306
1307static void micinit(struct airo_info *ai)
1308{
1309 MICRid mic_rid;
1310
Dan Williams3c304952006-04-15 12:26:18 -04001311 clear_bit(JOB_MIC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
1313 up(&ai->sem);
1314
1315 ai->micstats.enabled = (mic_rid.state & 0x00FF) ? 1 : 0;
1316
1317 if (ai->micstats.enabled) {
1318 /* Key must be valid and different */
1319 if (mic_rid.multicastValid && (!ai->mod[0].mCtx.valid ||
1320 (memcmp (ai->mod[0].mCtx.key, mic_rid.multicast,
1321 sizeof(ai->mod[0].mCtx.key)) != 0))) {
1322 /* Age current mic Context */
1323 memcpy(&ai->mod[1].mCtx,&ai->mod[0].mCtx,sizeof(miccntx));
1324 /* Initialize new context */
1325 memcpy(&ai->mod[0].mCtx.key,mic_rid.multicast,sizeof(mic_rid.multicast));
1326 ai->mod[0].mCtx.window = 33; //Window always points to the middle
1327 ai->mod[0].mCtx.rx = 0; //Rx Sequence numbers
1328 ai->mod[0].mCtx.tx = 0; //Tx sequence numbers
1329 ai->mod[0].mCtx.valid = 1; //Key is now valid
1330
1331 /* Give key to mic seed */
1332 emmh32_setseed(&ai->mod[0].mCtx.seed,mic_rid.multicast,sizeof(mic_rid.multicast), ai->tfm);
1333 }
1334
1335 /* Key must be valid and different */
1336 if (mic_rid.unicastValid && (!ai->mod[0].uCtx.valid ||
1337 (memcmp(ai->mod[0].uCtx.key, mic_rid.unicast,
1338 sizeof(ai->mod[0].uCtx.key)) != 0))) {
1339 /* Age current mic Context */
1340 memcpy(&ai->mod[1].uCtx,&ai->mod[0].uCtx,sizeof(miccntx));
1341 /* Initialize new context */
1342 memcpy(&ai->mod[0].uCtx.key,mic_rid.unicast,sizeof(mic_rid.unicast));
1343
1344 ai->mod[0].uCtx.window = 33; //Window always points to the middle
1345 ai->mod[0].uCtx.rx = 0; //Rx Sequence numbers
1346 ai->mod[0].uCtx.tx = 0; //Tx sequence numbers
1347 ai->mod[0].uCtx.valid = 1; //Key is now valid
1348
1349 //Give key to mic seed
1350 emmh32_setseed(&ai->mod[0].uCtx.seed, mic_rid.unicast, sizeof(mic_rid.unicast), ai->tfm);
1351 }
1352 } else {
1353 /* So next time we have a valid key and mic is enabled, we will update
1354 * the sequence number if the key is the same as before.
1355 */
1356 ai->mod[0].uCtx.valid = 0;
1357 ai->mod[0].mCtx.valid = 0;
1358 }
1359}
1360
1361/* micsetup - Get ready for business */
1362
1363static int micsetup(struct airo_info *ai) {
1364 int i;
1365
1366 if (ai->tfm == NULL)
Herbert Xuf12cc202006-08-22 20:36:13 +10001367 ai->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
Herbert Xuf12cc202006-08-22 20:36:13 +10001369 if (IS_ERR(ai->tfm)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05001370 airo_print_err(ai->dev->name, "failed to load transform for AES");
Herbert Xuf12cc202006-08-22 20:36:13 +10001371 ai->tfm = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 return ERROR;
1373 }
1374
1375 for (i=0; i < NUM_MODULES; i++) {
1376 memset(&ai->mod[i].mCtx,0,sizeof(miccntx));
1377 memset(&ai->mod[i].uCtx,0,sizeof(miccntx));
1378 }
1379 return SUCCESS;
1380}
1381
Jouni Malinenff1d2762005-05-12 22:54:16 -04001382static char micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383
1384/*===========================================================================
1385 * Description: Mic a packet
1386 *
1387 * Inputs: etherHead * pointer to an 802.3 frame
1388 *
1389 * Returns: BOOLEAN if successful, otherwise false.
1390 * PacketTxLen will be updated with the mic'd packets size.
1391 *
1392 * Caveats: It is assumed that the frame buffer will already
1393 * be big enough to hold the largets mic message possible.
1394 * (No memory allocation is done here).
1395 *
1396 * Author: sbraneky (10/15/01)
1397 * Merciless hacks by rwilcher (1/14/02)
1398 */
1399
1400static int encapsulate(struct airo_info *ai ,etherHead *frame, MICBuffer *mic, int payLen)
1401{
1402 miccntx *context;
1403
1404 // Determine correct context
1405 // If not adhoc, always use unicast key
1406
1407 if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1))
1408 context = &ai->mod[0].mCtx;
1409 else
1410 context = &ai->mod[0].uCtx;
1411
1412 if (!context->valid)
1413 return ERROR;
1414
1415 mic->typelen = htons(payLen + 16); //Length of Mic'd packet
1416
1417 memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap
1418
1419 // Add Tx sequence
1420 mic->seq = htonl(context->tx);
1421 context->tx += 2;
1422
1423 emmh32_init(&context->seed); // Mic the packet
1424 emmh32_update(&context->seed,frame->da,ETH_ALEN * 2); // DA,SA
1425 emmh32_update(&context->seed,(u8*)&mic->typelen,10); // Type/Length and Snap
1426 emmh32_update(&context->seed,(u8*)&mic->seq,sizeof(mic->seq)); //SEQ
1427 emmh32_update(&context->seed,frame->da + ETH_ALEN * 2,payLen); //payload
1428 emmh32_final(&context->seed, (u8*)&mic->mic);
1429
1430 /* New Type/length ?????????? */
1431 mic->typelen = 0; //Let NIC know it could be an oversized packet
1432 return SUCCESS;
1433}
1434
1435typedef enum {
1436 NONE,
1437 NOMIC,
1438 NOMICPLUMMED,
1439 SEQUENCE,
1440 INCORRECTMIC,
1441} mic_error;
1442
1443/*===========================================================================
1444 * Description: Decapsulates a MIC'd packet and returns the 802.3 packet
1445 * (removes the MIC stuff) if packet is a valid packet.
1446 *
1447 * Inputs: etherHead pointer to the 802.3 packet
1448 *
1449 * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE
1450 *
1451 * Author: sbraneky (10/15/01)
1452 * Merciless hacks by rwilcher (1/14/02)
1453 *---------------------------------------------------------------------------
1454 */
1455
1456static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen)
1457{
1458 int i;
1459 u32 micSEQ;
1460 miccntx *context;
1461 u8 digest[4];
1462 mic_error micError = NONE;
1463
1464 // Check if the packet is a Mic'd packet
1465
1466 if (!ai->micstats.enabled) {
1467 //No Mic set or Mic OFF but we received a MIC'd packet.
1468 if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) {
1469 ai->micstats.rxMICPlummed++;
1470 return ERROR;
1471 }
1472 return SUCCESS;
1473 }
1474
1475 if (ntohs(mic->typelen) == 0x888E)
1476 return SUCCESS;
1477
1478 if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) {
1479 // Mic enabled but packet isn't Mic'd
1480 ai->micstats.rxMICPlummed++;
1481 return ERROR;
1482 }
1483
1484 micSEQ = ntohl(mic->seq); //store SEQ as CPU order
1485
1486 //At this point we a have a mic'd packet and mic is enabled
1487 //Now do the mic error checking.
1488
1489 //Receive seq must be odd
1490 if ( (micSEQ & 1) == 0 ) {
1491 ai->micstats.rxWrongSequence++;
1492 return ERROR;
1493 }
1494
1495 for (i = 0; i < NUM_MODULES; i++) {
1496 int mcast = eth->da[0] & 1;
1497 //Determine proper context
1498 context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx;
1499
1500 //Make sure context is valid
1501 if (!context->valid) {
1502 if (i == 0)
1503 micError = NOMICPLUMMED;
1504 continue;
1505 }
1506 //DeMic it
1507
1508 if (!mic->typelen)
1509 mic->typelen = htons(payLen + sizeof(MICBuffer) - 2);
1510
1511 emmh32_init(&context->seed);
1512 emmh32_update(&context->seed, eth->da, ETH_ALEN*2);
1513 emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap));
1514 emmh32_update(&context->seed, (u8 *)&mic->seq,sizeof(mic->seq));
1515 emmh32_update(&context->seed, eth->da + ETH_ALEN*2,payLen);
1516 //Calculate MIC
1517 emmh32_final(&context->seed, digest);
1518
1519 if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match
1520 //Invalid Mic
1521 if (i == 0)
1522 micError = INCORRECTMIC;
1523 continue;
1524 }
1525
1526 //Check Sequence number if mics pass
1527 if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) {
1528 ai->micstats.rxSuccess++;
1529 return SUCCESS;
1530 }
1531 if (i == 0)
1532 micError = SEQUENCE;
1533 }
1534
1535 // Update statistics
1536 switch (micError) {
1537 case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break;
1538 case SEQUENCE: ai->micstats.rxWrongSequence++; break;
1539 case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break;
1540 case NONE: break;
1541 case NOMIC: break;
1542 }
1543 return ERROR;
1544}
1545
1546/*===========================================================================
1547 * Description: Checks the Rx Seq number to make sure it is valid
1548 * and hasn't already been received
1549 *
1550 * Inputs: miccntx - mic context to check seq against
1551 * micSeq - the Mic seq number
1552 *
1553 * Returns: TRUE if valid otherwise FALSE.
1554 *
1555 * Author: sbraneky (10/15/01)
1556 * Merciless hacks by rwilcher (1/14/02)
1557 *---------------------------------------------------------------------------
1558 */
1559
1560static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq)
1561{
1562 u32 seq,index;
1563
1564 //Allow for the ap being rebooted - if it is then use the next
1565 //sequence number of the current sequence number - might go backwards
1566
1567 if (mcast) {
1568 if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) {
1569 clear_bit (FLAG_UPDATE_MULTI, &ai->flags);
1570 context->window = (micSeq > 33) ? micSeq : 33;
1571 context->rx = 0; // Reset rx
1572 }
1573 } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) {
1574 clear_bit (FLAG_UPDATE_UNI, &ai->flags);
1575 context->window = (micSeq > 33) ? micSeq : 33; // Move window
1576 context->rx = 0; // Reset rx
1577 }
1578
1579 //Make sequence number relative to START of window
1580 seq = micSeq - (context->window - 33);
1581
1582 //Too old of a SEQ number to check.
1583 if ((s32)seq < 0)
1584 return ERROR;
1585
1586 if ( seq > 64 ) {
1587 //Window is infinite forward
1588 MoveWindow(context,micSeq);
1589 return SUCCESS;
1590 }
1591
1592 // We are in the window. Now check the context rx bit to see if it was already sent
1593 seq >>= 1; //divide by 2 because we only have odd numbers
1594 index = 1 << seq; //Get an index number
1595
1596 if (!(context->rx & index)) {
1597 //micSEQ falls inside the window.
1598 //Add seqence number to the list of received numbers.
1599 context->rx |= index;
1600
1601 MoveWindow(context,micSeq);
1602
1603 return SUCCESS;
1604 }
1605 return ERROR;
1606}
1607
1608static void MoveWindow(miccntx *context, u32 micSeq)
1609{
1610 u32 shift;
1611
1612 //Move window if seq greater than the middle of the window
1613 if (micSeq > context->window) {
1614 shift = (micSeq - context->window) >> 1;
1615
1616 //Shift out old
1617 if (shift < 32)
1618 context->rx >>= shift;
1619 else
1620 context->rx = 0;
1621
1622 context->window = micSeq; //Move window
1623 }
1624}
1625
1626/*==============================================*/
1627/*========== EMMH ROUTINES ====================*/
1628/*==============================================*/
1629
1630/* mic accumulate */
1631#define MIC_ACCUM(val) \
1632 context->accum += (u64)(val) * context->coeff[coeff_position++];
1633
1634static unsigned char aes_counter[16];
1635
1636/* expand the key to fill the MMH coefficient array */
Herbert Xuf12cc202006-08-22 20:36:13 +10001637static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
1638 struct crypto_cipher *tfm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639{
1640 /* take the keying material, expand if necessary, truncate at 16-bytes */
1641 /* run through AES counter mode to generate context->coeff[] */
1642
1643 int i,j;
1644 u32 counter;
1645 u8 *cipher, plain[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646
1647 crypto_cipher_setkey(tfm, pkey, 16);
1648 counter = 0;
Ahmed S. Darwishe7c04fd2007-02-05 18:58:29 +02001649 for (i = 0; i < ARRAY_SIZE(context->coeff); ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 aes_counter[15] = (u8)(counter >> 0);
1651 aes_counter[14] = (u8)(counter >> 8);
1652 aes_counter[13] = (u8)(counter >> 16);
1653 aes_counter[12] = (u8)(counter >> 24);
1654 counter++;
1655 memcpy (plain, aes_counter, 16);
Herbert Xuf12cc202006-08-22 20:36:13 +10001656 crypto_cipher_encrypt_one(tfm, plain, plain);
1657 cipher = plain;
Ahmed S. Darwishe7c04fd2007-02-05 18:58:29 +02001658 for (j = 0; (j < 16) && (i < ARRAY_SIZE(context->coeff)); ) {
Al Viro593c2b92007-12-17 15:09:34 -05001659 context->coeff[i++] = ntohl(*(__be32 *)&cipher[j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 j += 4;
1661 }
1662 }
1663}
1664
1665/* prepare for calculation of a new mic */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001666static void emmh32_init(emmh32_context *context)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667{
1668 /* prepare for new mic calculation */
1669 context->accum = 0;
1670 context->position = 0;
1671}
1672
1673/* add some bytes to the mic calculation */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001674static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675{
1676 int coeff_position, byte_position;
1677
1678 if (len == 0) return;
1679
1680 coeff_position = context->position >> 2;
1681
1682 /* deal with partial 32-bit word left over from last update */
1683 byte_position = context->position & 3;
1684 if (byte_position) {
1685 /* have a partial word in part to deal with */
1686 do {
1687 if (len == 0) return;
1688 context->part.d8[byte_position++] = *pOctets++;
1689 context->position++;
1690 len--;
1691 } while (byte_position < 4);
Al Viro593c2b92007-12-17 15:09:34 -05001692 MIC_ACCUM(ntohl(context->part.d32));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 }
1694
1695 /* deal with full 32-bit words */
1696 while (len >= 4) {
Al Viro593c2b92007-12-17 15:09:34 -05001697 MIC_ACCUM(ntohl(*(__be32 *)pOctets));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 context->position += 4;
1699 pOctets += 4;
1700 len -= 4;
1701 }
1702
1703 /* deal with partial 32-bit word that will be left over from this update */
1704 byte_position = 0;
1705 while (len > 0) {
1706 context->part.d8[byte_position++] = *pOctets++;
1707 context->position++;
1708 len--;
1709 }
1710}
1711
1712/* mask used to zero empty bytes for final partial word */
1713static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
1714
1715/* calculate the mic */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001716static void emmh32_final(emmh32_context *context, u8 digest[4])
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717{
1718 int coeff_position, byte_position;
1719 u32 val;
1720
1721 u64 sum, utmp;
1722 s64 stmp;
1723
1724 coeff_position = context->position >> 2;
1725
1726 /* deal with partial 32-bit word left over from last update */
1727 byte_position = context->position & 3;
1728 if (byte_position) {
1729 /* have a partial word in part to deal with */
Al Viro593c2b92007-12-17 15:09:34 -05001730 val = ntohl(context->part.d32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731 MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */
1732 }
1733
1734 /* reduce the accumulated u64 to a 32-bit MIC */
1735 sum = context->accum;
1736 stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15);
1737 utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15);
1738 sum = utmp & 0xffffffffLL;
1739 if (utmp > 0x10000000fLL)
1740 sum -= 15;
1741
1742 val = (u32)sum;
1743 digest[0] = (val>>24) & 0xFF;
1744 digest[1] = (val>>16) & 0xFF;
1745 digest[2] = (val>>8) & 0xFF;
1746 digest[3] = val & 0xFF;
1747}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748
1749static int readBSSListRid(struct airo_info *ai, int first,
Al Viro17e70492007-12-19 18:56:37 -05001750 BSSListRid *list)
1751{
Dan Williams3c304952006-04-15 12:26:18 -04001752 Cmd cmd;
1753 Resp rsp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754
1755 if (first == 1) {
Dan Williams3c304952006-04-15 12:26:18 -04001756 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
1757 memset(&cmd, 0, sizeof(cmd));
1758 cmd.cmd=CMD_LISTBSS;
1759 if (down_interruptible(&ai->sem))
1760 return -ERESTARTSYS;
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001761 ai->list_bss_task = current;
Dan Williams3c304952006-04-15 12:26:18 -04001762 issuecommand(ai, &cmd, &rsp);
1763 up(&ai->sem);
1764 /* Let the command take effect */
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001765 schedule_timeout_uninterruptible(3 * HZ);
1766 ai->list_bss_task = NULL;
Dan Williams3c304952006-04-15 12:26:18 -04001767 }
Al Viro17e70492007-12-19 18:56:37 -05001768 return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext,
Dan Williams3c304952006-04-15 12:26:18 -04001769 list, ai->bssListRidLen, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770}
1771
Al Viro4293ea32007-12-19 19:21:51 -05001772static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock)
1773{
1774 return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775 wkr, sizeof(*wkr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
Al Viro4293ea32007-12-19 19:21:51 -05001778static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock)
1779{
1780 int rc;
1781 rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock);
1782 if (rc!=SUCCESS)
1783 airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 if (perm) {
Al Viro4293ea32007-12-19 19:21:51 -05001785 rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock);
1786 if (rc!=SUCCESS)
Dan Williams934d8bf2006-03-16 13:46:23 -05001787 airo_print_err(ai->dev->name, "WEP_PERM set %x", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 }
1789 return rc;
1790}
1791
Al Viro0dd22122007-12-17 16:11:57 -05001792static int readSsidRid(struct airo_info*ai, SsidRid *ssidr)
1793{
1794 return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796
Al Viro0dd22122007-12-17 16:11:57 -05001797static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock)
1798{
1799 return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800}
Al Viro0dd22122007-12-17 16:11:57 -05001801
Al Viro3eb9b412007-12-21 00:00:35 -05001802static int readConfigRid(struct airo_info *ai, int lock)
1803{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805 ConfigRid cfg;
1806
1807 if (ai->config.len)
1808 return SUCCESS;
1809
1810 rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock);
1811 if (rc != SUCCESS)
1812 return rc;
1813
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814 ai->config = cfg;
1815 return SUCCESS;
1816}
Al Viro3eb9b412007-12-21 00:00:35 -05001817
1818static inline void checkThrottle(struct airo_info *ai)
1819{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820 int i;
1821/* Old hardware had a limit on encryption speed */
1822 if (ai->config.authType != AUTH_OPEN && maxencrypt) {
1823 for(i=0; i<8; i++) {
1824 if (ai->config.rates[i] > maxencrypt) {
1825 ai->config.rates[i] = 0;
1826 }
1827 }
1828 }
1829}
Al Viro3eb9b412007-12-21 00:00:35 -05001830
1831static int writeConfigRid(struct airo_info *ai, int lock)
1832{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 ConfigRid cfgr;
1834
1835 if (!test_bit (FLAG_COMMIT, &ai->flags))
1836 return SUCCESS;
1837
1838 clear_bit (FLAG_COMMIT, &ai->flags);
1839 clear_bit (FLAG_RESET, &ai->flags);
1840 checkThrottle(ai);
1841 cfgr = ai->config;
1842
Al Viro3eb9b412007-12-21 00:00:35 -05001843 if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844 set_bit(FLAG_ADHOC, &ai->flags);
1845 else
1846 clear_bit(FLAG_ADHOC, &ai->flags);
1847
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848 return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock);
1849}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850
Al Viro329e2c02007-12-20 22:58:57 -05001851static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock)
1852{
1853 return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854}
Al Viroa7497162007-12-20 17:49:41 -05001855
1856static int readAPListRid(struct airo_info *ai, APListRid *aplr)
1857{
1858 return PC4500_readrid(ai, RID_APLIST, aplr, sizeof(*aplr), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859}
Al Viroa7497162007-12-20 17:49:41 -05001860
1861static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock)
1862{
1863 return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865
Al Viro56d81bd2007-12-20 17:18:35 -05001866static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock)
1867{
1868 return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870
Al Viroa23ace52007-12-19 22:24:16 -05001871static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock)
1872{
1873 return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874}
1875
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001876static void try_auto_wep(struct airo_info *ai)
1877{
1878 if (auto_wep && !(ai->flags & FLAG_RADIO_DOWN)) {
1879 ai->expires = RUN_AT(3*HZ);
1880 wake_up_interruptible(&ai->thr_wait);
1881 }
1882}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001884static int airo_open(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08001885 struct airo_info *ai = dev->ml_priv;
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001886 int rc = 0;
1887
1888 if (test_bit(FLAG_FLASHING, &ai->flags))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 return -EIO;
1890
1891 /* Make sure the card is configured.
1892 * Wireless Extensions may postpone config changes until the card
1893 * is open (to pipeline changes and speed-up card setup). If
1894 * those changes are not yet commited, do it now - Jean II */
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001895 if (test_bit(FLAG_COMMIT, &ai->flags)) {
1896 disable_MAC(ai, 1);
1897 writeConfigRid(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898 }
1899
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001900 if (ai->wifidev != dev) {
1901 clear_bit(JOB_DIE, &ai->jobs);
1902 ai->airo_thread_task = kthread_run(airo_thread, dev, dev->name);
1903 if (IS_ERR(ai->airo_thread_task))
1904 return (int)PTR_ERR(ai->airo_thread_task);
1905
1906 rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED,
1907 dev->name, dev);
1908 if (rc) {
1909 airo_print_err(dev->name,
1910 "register interrupt %d failed, rc %d",
1911 dev->irq, rc);
1912 set_bit(JOB_DIE, &ai->jobs);
1913 kthread_stop(ai->airo_thread_task);
1914 return rc;
1915 }
1916
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917 /* Power on the MAC controller (which may have been disabled) */
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001918 clear_bit(FLAG_RADIO_DOWN, &ai->flags);
1919 enable_interrupts(ai);
1920
1921 try_auto_wep(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922 }
Michal Schmidt175ec1a2007-06-29 15:33:47 +02001923 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924
1925 netif_start_queue(dev);
1926 return 0;
1927}
1928
1929static int mpi_start_xmit(struct sk_buff *skb, struct net_device *dev) {
1930 int npacks, pending;
1931 unsigned long flags;
Wang Chenfaf39942008-10-14 13:30:33 +08001932 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
1934 if (!skb) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07001935 airo_print_err(dev->name, "%s: skb == NULL!",__func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 return 0;
1937 }
1938 npacks = skb_queue_len (&ai->txq);
1939
1940 if (npacks >= MAXTXQ - 1) {
1941 netif_stop_queue (dev);
1942 if (npacks > MAXTXQ) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03001943 dev->stats.tx_fifo_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 return 1;
1945 }
1946 skb_queue_tail (&ai->txq, skb);
1947 return 0;
1948 }
1949
1950 spin_lock_irqsave(&ai->aux_lock, flags);
1951 skb_queue_tail (&ai->txq, skb);
1952 pending = test_bit(FLAG_PENDING_XMIT, &ai->flags);
1953 spin_unlock_irqrestore(&ai->aux_lock,flags);
1954 netif_wake_queue (dev);
1955
1956 if (pending == 0) {
1957 set_bit(FLAG_PENDING_XMIT, &ai->flags);
1958 mpi_send_packet (dev);
1959 }
1960 return 0;
1961}
1962
1963/*
1964 * @mpi_send_packet
1965 *
1966 * Attempt to transmit a packet. Can be called from interrupt
1967 * or transmit . return number of packets we tried to send
1968 */
1969
1970static int mpi_send_packet (struct net_device *dev)
1971{
1972 struct sk_buff *skb;
1973 unsigned char *buffer;
Al Viro593c2b92007-12-17 15:09:34 -05001974 s16 len;
1975 __le16 *payloadLen;
Wang Chenfaf39942008-10-14 13:30:33 +08001976 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 u8 *sendbuf;
1978
1979 /* get a packet to send */
1980
Al Viro79ea13c2008-01-24 02:06:46 -08001981 if ((skb = skb_dequeue(&ai->txq)) == NULL) {
Dan Williams934d8bf2006-03-16 13:46:23 -05001982 airo_print_err(dev->name,
1983 "%s: Dequeue'd zero in send_packet()",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07001984 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 return 0;
1986 }
1987
1988 /* check min length*/
1989 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
1990 buffer = skb->data;
1991
1992 ai->txfids[0].tx_desc.offset = 0;
1993 ai->txfids[0].tx_desc.valid = 1;
1994 ai->txfids[0].tx_desc.eoc = 1;
1995 ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr);
1996
1997/*
1998 * Magic, the cards firmware needs a length count (2 bytes) in the host buffer
1999 * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen
2000 * is immediatly after it. ------------------------------------------------
2001 * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA|
2002 * ------------------------------------------------
2003 */
2004
2005 memcpy((char *)ai->txfids[0].virtual_host_addr,
2006 (char *)&wifictlhdr8023, sizeof(wifictlhdr8023));
2007
Al Viro593c2b92007-12-17 15:09:34 -05002008 payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 sizeof(wifictlhdr8023));
2010 sendbuf = ai->txfids[0].virtual_host_addr +
2011 sizeof(wifictlhdr8023) + 2 ;
2012
2013 /*
2014 * Firmware automaticly puts 802 header on so
2015 * we don't need to account for it in the length
2016 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
Al Viro593c2b92007-12-17 15:09:34 -05002018 (ntohs(((__be16 *)buffer)[6]) != 0x888E)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 MICBuffer pMic;
2020
2021 if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS)
2022 return ERROR;
2023
2024 *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic));
2025 ai->txfids[0].tx_desc.len += sizeof(pMic);
2026 /* copy data into airo dma buffer */
2027 memcpy (sendbuf, buffer, sizeof(etherHead));
2028 buffer += sizeof(etherHead);
2029 sendbuf += sizeof(etherHead);
2030 memcpy (sendbuf, &pMic, sizeof(pMic));
2031 sendbuf += sizeof(pMic);
2032 memcpy (sendbuf, buffer, len - sizeof(etherHead));
Adrian Bunka39d3e72006-01-21 01:35:15 +01002033 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034 *payloadLen = cpu_to_le16(len - sizeof(etherHead));
2035
2036 dev->trans_start = jiffies;
2037
2038 /* copy data into airo dma buffer */
2039 memcpy(sendbuf, buffer, len);
2040 }
2041
2042 memcpy_toio(ai->txfids[0].card_ram_off,
2043 &ai->txfids[0].tx_desc, sizeof(TxFid));
2044
2045 OUT4500(ai, EVACK, 8);
2046
2047 dev_kfree_skb_any(skb);
2048 return 1;
2049}
2050
Gabriel A. Devenyi29b09fc2005-11-03 19:30:47 -05002051static void get_tx_error(struct airo_info *ai, s32 fid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052{
Al Viro593c2b92007-12-17 15:09:34 -05002053 __le16 status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054
2055 if (fid < 0)
2056 status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status;
2057 else {
2058 if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS)
2059 return;
2060 bap_read(ai, &status, 2, BAP0);
2061 }
2062 if (le16_to_cpu(status) & 2) /* Too many retries */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002063 ai->dev->stats.tx_aborted_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002065 ai->dev->stats.tx_heartbeat_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 if (le16_to_cpu(status) & 8) /* Aid fail */
2067 { }
2068 if (le16_to_cpu(status) & 0x10) /* MAC disabled */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002069 ai->dev->stats.tx_carrier_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070 if (le16_to_cpu(status) & 0x20) /* Association lost */
2071 { }
2072 /* We produce a TXDROP event only for retry or lifetime
2073 * exceeded, because that's the only status that really mean
2074 * that this particular node went away.
2075 * Other errors means that *we* screwed up. - Jean II */
2076 if ((le16_to_cpu(status) & 2) ||
2077 (le16_to_cpu(status) & 4)) {
2078 union iwreq_data wrqu;
2079 char junk[0x18];
2080
2081 /* Faster to skip over useless data than to do
2082 * another bap_setup(). We are at offset 0x6 and
2083 * need to go to 0x18 and read 6 bytes - Jean II */
Al Virob8c06bc2007-12-19 17:55:43 -05002084 bap_read(ai, (__le16 *) junk, 0x18, BAP0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085
2086 /* Copy 802.11 dest address.
2087 * We use the 802.11 header because the frame may
2088 * not be 802.3 or may be mangled...
2089 * In Ad-Hoc mode, it will be the node address.
2090 * In managed mode, it will be most likely the AP addr
2091 * User space will figure out how to convert it to
2092 * whatever it needs (IP address or else).
2093 * - Jean II */
2094 memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN);
2095 wrqu.addr.sa_family = ARPHRD_ETHER;
2096
2097 /* Send event to user space */
2098 wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL);
2099 }
2100}
2101
2102static void airo_end_xmit(struct net_device *dev) {
2103 u16 status;
2104 int i;
Wang Chenfaf39942008-10-14 13:30:33 +08002105 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 struct sk_buff *skb = priv->xmit.skb;
2107 int fid = priv->xmit.fid;
2108 u32 *fids = priv->fids;
2109
Dan Williams3c304952006-04-15 12:26:18 -04002110 clear_bit(JOB_XMIT, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 clear_bit(FLAG_PENDING_XMIT, &priv->flags);
2112 status = transmit_802_3_packet (priv, fids[fid], skb->data);
2113 up(&priv->sem);
2114
2115 i = 0;
2116 if ( status == SUCCESS ) {
2117 dev->trans_start = jiffies;
2118 for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++);
2119 } else {
2120 priv->fids[fid] &= 0xffff;
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002121 dev->stats.tx_window_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122 }
2123 if (i < MAX_FIDS / 2)
2124 netif_wake_queue(dev);
2125 dev_kfree_skb(skb);
2126}
2127
2128static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) {
2129 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__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136 return 0;
2137 }
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++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148 return 1;
2149 }
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);
2164 return 0;
2165}
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
2193static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) {
2194 s16 len;
2195 int i, j;
Wang Chenfaf39942008-10-14 13:30:33 +08002196 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197 u32 *fids = priv->fids;
2198
2199 if (test_bit(FLAG_MPI, &priv->flags)) {
2200 /* Not implemented yet for MPI350 */
2201 netif_stop_queue(dev);
2202 return -ENETDOWN;
2203 }
2204
2205 if ( skb == NULL ) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07002206 airo_print_err(dev->name, "%s: skb == NULL!", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207 return 0;
2208 }
2209
2210 /* Find a vacant FID */
2211 for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ );
2212 for( j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++ );
2213
2214 if ( j >= MAX_FIDS ) {
2215 netif_stop_queue(dev);
2216
2217 if (i == MAX_FIDS) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002218 dev->stats.tx_fifo_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219 return 1;
2220 }
2221 }
2222 /* check min length*/
2223 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
2224 /* Mark fid as used & save length for later */
2225 fids[i] |= (len << 16);
2226 priv->xmit11.skb = skb;
2227 priv->xmit11.fid = i;
2228 if (down_trylock(&priv->sem) != 0) {
2229 set_bit(FLAG_PENDING_XMIT11, &priv->flags);
2230 netif_stop_queue(dev);
Dan Williams3c304952006-04-15 12:26:18 -04002231 set_bit(JOB_XMIT11, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232 wake_up_interruptible(&priv->thr_wait);
2233 } else
2234 airo_end_xmit11(dev);
2235 return 0;
2236}
2237
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002238static void airo_read_stats(struct net_device *dev)
Al Viroa23ace52007-12-19 22:24:16 -05002239{
Wang Chenfaf39942008-10-14 13:30:33 +08002240 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 StatsRid stats_rid;
Al Viroa23ace52007-12-19 22:24:16 -05002242 __le32 *vals = stats_rid.vals;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243
Dan Williams3c304952006-04-15 12:26:18 -04002244 clear_bit(JOB_STATS, &ai->jobs);
Pavel Machekca078ba2005-09-03 15:56:57 -07002245 if (ai->power.event) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 up(&ai->sem);
2247 return;
2248 }
2249 readStatsRid(ai, &stats_rid, RID_STATS, 0);
2250 up(&ai->sem);
2251
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002252 dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) +
Al Viroa23ace52007-12-19 22:24:16 -05002253 le32_to_cpu(vals[45]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002254 dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) +
Al Viroa23ace52007-12-19 22:24:16 -05002255 le32_to_cpu(vals[41]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002256 dev->stats.rx_bytes = le32_to_cpu(vals[92]);
2257 dev->stats.tx_bytes = le32_to_cpu(vals[91]);
2258 dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) +
Al Viroa23ace52007-12-19 22:24:16 -05002259 le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002260 dev->stats.tx_errors = le32_to_cpu(vals[42]) +
2261 dev->stats.tx_fifo_errors;
2262 dev->stats.multicast = le32_to_cpu(vals[43]);
2263 dev->stats.collisions = le32_to_cpu(vals[89]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264
2265 /* detailed rx_errors: */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002266 dev->stats.rx_length_errors = le32_to_cpu(vals[3]);
2267 dev->stats.rx_crc_errors = le32_to_cpu(vals[4]);
2268 dev->stats.rx_frame_errors = le32_to_cpu(vals[2]);
2269 dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270}
2271
Jouni Malinenff1d2762005-05-12 22:54:16 -04002272static struct net_device_stats *airo_get_stats(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273{
Wang Chenfaf39942008-10-14 13:30:33 +08002274 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275
Dan Williams3c304952006-04-15 12:26:18 -04002276 if (!test_bit(JOB_STATS, &local->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 /* Get stats out of the card if available */
2278 if (down_trylock(&local->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04002279 set_bit(JOB_STATS, &local->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 wake_up_interruptible(&local->thr_wait);
2281 } else
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002282 airo_read_stats(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 }
2284
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002285 return &dev->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286}
2287
2288static void airo_set_promisc(struct airo_info *ai) {
2289 Cmd cmd;
2290 Resp rsp;
2291
2292 memset(&cmd, 0, sizeof(cmd));
2293 cmd.cmd=CMD_SETMODE;
Dan Williams3c304952006-04-15 12:26:18 -04002294 clear_bit(JOB_PROMISC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC;
2296 issuecommand(ai, &cmd, &rsp);
2297 up(&ai->sem);
2298}
2299
2300static void airo_set_multicast_list(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08002301 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302
2303 if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
2304 change_bit(FLAG_PROMISC, &ai->flags);
2305 if (down_trylock(&ai->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04002306 set_bit(JOB_PROMISC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 wake_up_interruptible(&ai->thr_wait);
2308 } else
2309 airo_set_promisc(ai);
2310 }
2311
2312 if ((dev->flags&IFF_ALLMULTI)||dev->mc_count>0) {
2313 /* Turn on multicast. (Should be already setup...) */
2314 }
2315}
2316
2317static int airo_set_mac_address(struct net_device *dev, void *p)
2318{
Wang Chenfaf39942008-10-14 13:30:33 +08002319 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320 struct sockaddr *addr = p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321
2322 readConfigRid(ai, 1);
2323 memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len);
2324 set_bit (FLAG_COMMIT, &ai->flags);
2325 disable_MAC(ai, 1);
2326 writeConfigRid (ai, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02002327 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328 memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len);
2329 if (ai->wifidev)
2330 memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len);
2331 return 0;
2332}
2333
2334static int airo_change_mtu(struct net_device *dev, int new_mtu)
2335{
2336 if ((new_mtu < 68) || (new_mtu > 2400))
2337 return -EINVAL;
2338 dev->mtu = new_mtu;
2339 return 0;
2340}
2341
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002342static LIST_HEAD(airo_devices);
2343
2344static void add_airo_dev(struct airo_info *ai)
2345{
2346 /* Upper layers already keep track of PCI devices,
2347 * so we only need to remember our non-PCI cards. */
2348 if (!ai->pci)
2349 list_add_tail(&ai->dev_list, &airo_devices);
2350}
2351
2352static void del_airo_dev(struct airo_info *ai)
2353{
2354 if (!ai->pci)
2355 list_del(&ai->dev_list);
2356}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357
2358static int airo_close(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08002359 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360
2361 netif_stop_queue(dev);
2362
2363 if (ai->wifidev != dev) {
2364#ifdef POWER_ON_DOWN
2365 /* Shut power to the card. The idea is that the user can save
2366 * power when he doesn't need the card with "ifconfig down".
2367 * That's the method that is most friendly towards the network
2368 * stack (i.e. the network stack won't try to broadcast
2369 * anything on the interface and routes are gone. Jean II */
2370 set_bit(FLAG_RADIO_DOWN, &ai->flags);
2371 disable_MAC(ai, 1);
2372#endif
2373 disable_interrupts( ai );
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002374
2375 free_irq(dev->irq, dev);
2376
2377 set_bit(JOB_DIE, &ai->jobs);
2378 kthread_stop(ai->airo_thread_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379 }
2380 return 0;
2381}
2382
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383void stop_airo_card( struct net_device *dev, int freeres )
2384{
Wang Chenfaf39942008-10-14 13:30:33 +08002385 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002386
2387 set_bit(FLAG_RADIO_DOWN, &ai->flags);
2388 disable_MAC(ai, 1);
2389 disable_interrupts(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390 takedown_proc_entry( dev, ai );
2391 if (test_bit(FLAG_REGISTERED, &ai->flags)) {
2392 unregister_netdev( dev );
2393 if (ai->wifidev) {
2394 unregister_netdev(ai->wifidev);
2395 free_netdev(ai->wifidev);
2396 ai->wifidev = NULL;
2397 }
2398 clear_bit(FLAG_REGISTERED, &ai->flags);
2399 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 /*
2401 * Clean out tx queue
2402 */
David S. Millerb03efcf2005-07-08 14:57:23 -07002403 if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404 struct sk_buff *skb = NULL;
2405 for (;(skb = skb_dequeue(&ai->txq));)
2406 dev_kfree_skb(skb);
2407 }
2408
Dan Williams9e75af32006-03-16 13:46:29 -05002409 airo_networks_free (ai);
2410
Jesper Juhlb4558ea2005-10-28 16:53:13 -04002411 kfree(ai->flash);
2412 kfree(ai->rssi);
2413 kfree(ai->APList);
2414 kfree(ai->SSID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415 if (freeres) {
2416 /* PCMCIA frees this stuff, so only for PCI and ISA */
2417 release_region( dev->base_addr, 64 );
2418 if (test_bit(FLAG_MPI, &ai->flags)) {
2419 if (ai->pci)
2420 mpi_unmap_card(ai->pci);
2421 if (ai->pcimem)
2422 iounmap(ai->pcimem);
2423 if (ai->pciaux)
2424 iounmap(ai->pciaux);
2425 pci_free_consistent(ai->pci, PCI_SHARED_LEN,
2426 ai->shared, ai->shared_dma);
2427 }
2428 }
Herbert Xuf12cc202006-08-22 20:36:13 +10002429 crypto_free_cipher(ai->tfm);
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002430 del_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431 free_netdev( dev );
2432}
2433
2434EXPORT_SYMBOL(stop_airo_card);
2435
Stephen Hemmingerb95cce32007-09-26 22:13:38 -07002436static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437{
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -07002438 memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439 return ETH_ALEN;
2440}
2441
2442static void mpi_unmap_card(struct pci_dev *pci)
2443{
2444 unsigned long mem_start = pci_resource_start(pci, 1);
2445 unsigned long mem_len = pci_resource_len(pci, 1);
2446 unsigned long aux_start = pci_resource_start(pci, 2);
2447 unsigned long aux_len = AUXMEMSIZE;
2448
2449 release_mem_region(aux_start, aux_len);
2450 release_mem_region(mem_start, mem_len);
2451}
2452
2453/*************************************************************
2454 * This routine assumes that descriptors have been setup .
2455 * Run at insmod time or after reset when the decriptors
2456 * have been initialized . Returns 0 if all is well nz
2457 * otherwise . Does not allocate memory but sets up card
2458 * using previously allocated descriptors.
2459 */
2460static int mpi_init_descriptors (struct airo_info *ai)
2461{
2462 Cmd cmd;
2463 Resp rsp;
2464 int i;
2465 int rc = SUCCESS;
2466
2467 /* Alloc card RX descriptors */
2468 netif_stop_queue(ai->dev);
2469
2470 memset(&rsp,0,sizeof(rsp));
2471 memset(&cmd,0,sizeof(cmd));
2472
2473 cmd.cmd = CMD_ALLOCATEAUX;
2474 cmd.parm0 = FID_RX;
2475 cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux);
2476 cmd.parm2 = MPI_MAX_FIDS;
2477 rc=issuecommand(ai, &cmd, &rsp);
2478 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002479 airo_print_err(ai->dev->name, "Couldn't allocate RX FID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480 return rc;
2481 }
2482
2483 for (i=0; i<MPI_MAX_FIDS; i++) {
2484 memcpy_toio(ai->rxfids[i].card_ram_off,
2485 &ai->rxfids[i].rx_desc, sizeof(RxFid));
2486 }
2487
2488 /* Alloc card TX descriptors */
2489
2490 memset(&rsp,0,sizeof(rsp));
2491 memset(&cmd,0,sizeof(cmd));
2492
2493 cmd.cmd = CMD_ALLOCATEAUX;
2494 cmd.parm0 = FID_TX;
2495 cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux);
2496 cmd.parm2 = MPI_MAX_FIDS;
2497
2498 for (i=0; i<MPI_MAX_FIDS; i++) {
2499 ai->txfids[i].tx_desc.valid = 1;
2500 memcpy_toio(ai->txfids[i].card_ram_off,
2501 &ai->txfids[i].tx_desc, sizeof(TxFid));
2502 }
2503 ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
2504
2505 rc=issuecommand(ai, &cmd, &rsp);
2506 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002507 airo_print_err(ai->dev->name, "Couldn't allocate TX FID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002508 return rc;
2509 }
2510
2511 /* Alloc card Rid descriptor */
2512 memset(&rsp,0,sizeof(rsp));
2513 memset(&cmd,0,sizeof(cmd));
2514
2515 cmd.cmd = CMD_ALLOCATEAUX;
2516 cmd.parm0 = RID_RW;
2517 cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux);
2518 cmd.parm2 = 1; /* Magic number... */
2519 rc=issuecommand(ai, &cmd, &rsp);
2520 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002521 airo_print_err(ai->dev->name, "Couldn't allocate RID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522 return rc;
2523 }
2524
2525 memcpy_toio(ai->config_desc.card_ram_off,
2526 &ai->config_desc.rid_desc, sizeof(Rid));
2527
2528 return rc;
2529}
2530
2531/*
2532 * We are setting up three things here:
2533 * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid.
2534 * 2) Map PCI memory for issueing commands.
2535 * 3) Allocate memory (shared) to send and receive ethernet frames.
2536 */
Michal Schmidt1138c372007-06-29 15:33:41 +02002537static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538{
2539 unsigned long mem_start, mem_len, aux_start, aux_len;
2540 int rc = -1;
2541 int i;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002542 dma_addr_t busaddroff;
2543 unsigned char *vpackoff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544 unsigned char __iomem *pciaddroff;
2545
2546 mem_start = pci_resource_start(pci, 1);
2547 mem_len = pci_resource_len(pci, 1);
2548 aux_start = pci_resource_start(pci, 2);
2549 aux_len = AUXMEMSIZE;
2550
Michal Schmidt1138c372007-06-29 15:33:41 +02002551 if (!request_mem_region(mem_start, mem_len, DRV_NAME)) {
2552 airo_print_err("", "Couldn't get region %x[%x]",
2553 (int)mem_start, (int)mem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554 goto out;
2555 }
Michal Schmidt1138c372007-06-29 15:33:41 +02002556 if (!request_mem_region(aux_start, aux_len, DRV_NAME)) {
2557 airo_print_err("", "Couldn't get region %x[%x]",
2558 (int)aux_start, (int)aux_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 goto free_region1;
2560 }
2561
2562 ai->pcimem = ioremap(mem_start, mem_len);
2563 if (!ai->pcimem) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002564 airo_print_err("", "Couldn't map region %x[%x]",
2565 (int)mem_start, (int)mem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 goto free_region2;
2567 }
2568 ai->pciaux = ioremap(aux_start, aux_len);
2569 if (!ai->pciaux) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002570 airo_print_err("", "Couldn't map region %x[%x]",
2571 (int)aux_start, (int)aux_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 goto free_memmap;
2573 }
2574
2575 /* Reserve PKTSIZE for each fid and 2K for the Rids */
2576 ai->shared = pci_alloc_consistent(pci, PCI_SHARED_LEN, &ai->shared_dma);
2577 if (!ai->shared) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002578 airo_print_err("", "Couldn't alloc_consistent %d",
2579 PCI_SHARED_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 goto free_auxmap;
2581 }
2582
2583 /*
2584 * Setup descriptor RX, TX, CONFIG
2585 */
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002586 busaddroff = ai->shared_dma;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587 pciaddroff = ai->pciaux + AUX_OFFSET;
2588 vpackoff = ai->shared;
2589
2590 /* RX descriptor setup */
2591 for(i = 0; i < MPI_MAX_FIDS; i++) {
2592 ai->rxfids[i].pending = 0;
2593 ai->rxfids[i].card_ram_off = pciaddroff;
2594 ai->rxfids[i].virtual_host_addr = vpackoff;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002595 ai->rxfids[i].rx_desc.host_addr = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596 ai->rxfids[i].rx_desc.valid = 1;
2597 ai->rxfids[i].rx_desc.len = PKTSIZE;
2598 ai->rxfids[i].rx_desc.rdy = 0;
2599
2600 pciaddroff += sizeof(RxFid);
2601 busaddroff += PKTSIZE;
2602 vpackoff += PKTSIZE;
2603 }
2604
2605 /* TX descriptor setup */
2606 for(i = 0; i < MPI_MAX_FIDS; i++) {
2607 ai->txfids[i].card_ram_off = pciaddroff;
2608 ai->txfids[i].virtual_host_addr = vpackoff;
2609 ai->txfids[i].tx_desc.valid = 1;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002610 ai->txfids[i].tx_desc.host_addr = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611 memcpy(ai->txfids[i].virtual_host_addr,
2612 &wifictlhdr8023, sizeof(wifictlhdr8023));
2613
2614 pciaddroff += sizeof(TxFid);
2615 busaddroff += PKTSIZE;
2616 vpackoff += PKTSIZE;
2617 }
2618 ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
2619
2620 /* Rid descriptor setup */
2621 ai->config_desc.card_ram_off = pciaddroff;
2622 ai->config_desc.virtual_host_addr = vpackoff;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002623 ai->config_desc.rid_desc.host_addr = busaddroff;
2624 ai->ridbus = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625 ai->config_desc.rid_desc.rid = 0;
2626 ai->config_desc.rid_desc.len = RIDSIZE;
2627 ai->config_desc.rid_desc.valid = 1;
2628 pciaddroff += sizeof(Rid);
2629 busaddroff += RIDSIZE;
2630 vpackoff += RIDSIZE;
2631
2632 /* Tell card about descriptors */
2633 if (mpi_init_descriptors (ai) != SUCCESS)
2634 goto free_shared;
2635
2636 return 0;
2637 free_shared:
2638 pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
2639 free_auxmap:
2640 iounmap(ai->pciaux);
2641 free_memmap:
2642 iounmap(ai->pcimem);
2643 free_region2:
2644 release_mem_region(aux_start, aux_len);
2645 free_region1:
2646 release_mem_region(mem_start, mem_len);
2647 out:
2648 return rc;
2649}
2650
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -07002651static const struct header_ops airo_header_ops = {
2652 .parse = wll_header_parse,
2653};
2654
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655static void wifi_setup(struct net_device *dev)
2656{
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -07002657 dev->header_ops = &airo_header_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658 dev->hard_start_xmit = &airo_start_xmit11;
2659 dev->get_stats = &airo_get_stats;
2660 dev->set_mac_address = &airo_set_mac_address;
2661 dev->do_ioctl = &airo_ioctl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662 dev->wireless_handlers = &airo_handler_def;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663 dev->change_mtu = &airo_change_mtu;
2664 dev->open = &airo_open;
2665 dev->stop = &airo_close;
2666
2667 dev->type = ARPHRD_IEEE80211;
2668 dev->hard_header_len = ETH_HLEN;
Dan Williams15db2762006-03-16 13:46:27 -05002669 dev->mtu = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670 dev->addr_len = ETH_ALEN;
2671 dev->tx_queue_len = 100;
2672
2673 memset(dev->broadcast,0xFF, ETH_ALEN);
2674
2675 dev->flags = IFF_BROADCAST|IFF_MULTICAST;
2676}
2677
2678static struct net_device *init_wifidev(struct airo_info *ai,
2679 struct net_device *ethdev)
2680{
2681 int err;
2682 struct net_device *dev = alloc_netdev(0, "wifi%d", wifi_setup);
2683 if (!dev)
2684 return NULL;
Wang Chenfaf39942008-10-14 13:30:33 +08002685 dev->ml_priv = ethdev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686 dev->irq = ethdev->irq;
2687 dev->base_addr = ethdev->base_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688 dev->wireless_data = ethdev->wireless_data;
Masakazu Mokuno229ce3a2008-05-14 14:16:50 +09002689 SET_NETDEV_DEV(dev, ethdev->dev.parent);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690 memcpy(dev->dev_addr, ethdev->dev_addr, dev->addr_len);
2691 err = register_netdev(dev);
2692 if (err<0) {
2693 free_netdev(dev);
2694 return NULL;
2695 }
2696 return dev;
2697}
2698
Jouni Malinenff1d2762005-05-12 22:54:16 -04002699static int reset_card( struct net_device *dev , int lock) {
Wang Chenfaf39942008-10-14 13:30:33 +08002700 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701
2702 if (lock && down_interruptible(&ai->sem))
2703 return -1;
2704 waitbusy (ai);
2705 OUT4500(ai,COMMAND,CMD_SOFTRESET);
2706 msleep(200);
2707 waitbusy (ai);
2708 msleep(200);
2709 if (lock)
2710 up(&ai->sem);
2711 return 0;
2712}
2713
Dan Williams3c304952006-04-15 12:26:18 -04002714#define AIRO_MAX_NETWORK_COUNT 64
Dan Williams9e75af32006-03-16 13:46:29 -05002715static int airo_networks_allocate(struct airo_info *ai)
2716{
2717 if (ai->networks)
2718 return 0;
2719
2720 ai->networks =
Dan Williams3c304952006-04-15 12:26:18 -04002721 kzalloc(AIRO_MAX_NETWORK_COUNT * sizeof(BSSListElement),
Dan Williams9e75af32006-03-16 13:46:29 -05002722 GFP_KERNEL);
2723 if (!ai->networks) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002724 airo_print_warn("", "Out of memory allocating beacons");
Dan Williams9e75af32006-03-16 13:46:29 -05002725 return -ENOMEM;
2726 }
2727
2728 return 0;
2729}
2730
2731static void airo_networks_free(struct airo_info *ai)
2732{
Dan Williams9e75af32006-03-16 13:46:29 -05002733 kfree(ai->networks);
2734 ai->networks = NULL;
2735}
2736
2737static void airo_networks_initialize(struct airo_info *ai)
2738{
2739 int i;
2740
2741 INIT_LIST_HEAD(&ai->network_free_list);
2742 INIT_LIST_HEAD(&ai->network_list);
Dan Williams3c304952006-04-15 12:26:18 -04002743 for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++)
Dan Williams9e75af32006-03-16 13:46:29 -05002744 list_add_tail(&ai->networks[i].list,
2745 &ai->network_free_list);
2746}
2747
Jouni Malinenff1d2762005-05-12 22:54:16 -04002748static struct net_device *_init_airo_card( unsigned short irq, int port,
2749 int is_pcmcia, struct pci_dev *pci,
2750 struct device *dmdev )
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751{
2752 struct net_device *dev;
2753 struct airo_info *ai;
2754 int i, rc;
Dan Williamsf65b56d2009-01-24 09:10:42 -05002755 CapabilityRid cap_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756
2757 /* Create the network device object. */
Michal Schmidt1138c372007-06-29 15:33:41 +02002758 dev = alloc_netdev(sizeof(*ai), "", ether_setup);
2759 if (!dev) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002760 airo_print_err("", "Couldn't alloc_etherdev");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762 }
2763
Wang Chenfaf39942008-10-14 13:30:33 +08002764 ai = dev->ml_priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 ai->wifidev = NULL;
Michal Schmidtfb038c22007-06-29 15:33:52 +02002766 ai->flags = 1 << FLAG_RADIO_DOWN;
Dan Williams3c304952006-04-15 12:26:18 -04002767 ai->jobs = 0;
Dan Williams934d8bf2006-03-16 13:46:23 -05002768 ai->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002770 airo_print_dbg("", "Found an MPI350 card");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 set_bit(FLAG_MPI, &ai->flags);
2772 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773 spin_lock_init(&ai->aux_lock);
2774 sema_init(&ai->sem, 1);
2775 ai->config.len = 0;
2776 ai->pci = pci;
2777 init_waitqueue_head (&ai->thr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 ai->tfm = NULL;
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002779 add_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780
Dan Williams9e75af32006-03-16 13:46:29 -05002781 if (airo_networks_allocate (ai))
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002782 goto err_out_free;
Dan Williams9e75af32006-03-16 13:46:29 -05002783 airo_networks_initialize (ai);
2784
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 /* The Airo-specific entries in the device structure. */
2786 if (test_bit(FLAG_MPI,&ai->flags)) {
2787 skb_queue_head_init (&ai->txq);
2788 dev->hard_start_xmit = &mpi_start_xmit;
2789 } else
2790 dev->hard_start_xmit = &airo_start_xmit;
2791 dev->get_stats = &airo_get_stats;
2792 dev->set_multicast_list = &airo_set_multicast_list;
2793 dev->set_mac_address = &airo_set_mac_address;
2794 dev->do_ioctl = &airo_ioctl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 dev->wireless_handlers = &airo_handler_def;
2796 ai->wireless_data.spy_data = &ai->spy_data;
2797 dev->wireless_data = &ai->wireless_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 dev->change_mtu = &airo_change_mtu;
2799 dev->open = &airo_open;
2800 dev->stop = &airo_close;
2801 dev->irq = irq;
2802 dev->base_addr = port;
2803
2804 SET_NETDEV_DEV(dev, dmdev);
2805
Matthieu CASTET1d97f382005-12-01 02:35:26 -05002806 reset_card (dev, 1);
2807 msleep(400);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 if (!is_pcmcia) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002810 if (!request_region(dev->base_addr, 64, DRV_NAME)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 rc = -EBUSY;
Dan Williams934d8bf2006-03-16 13:46:23 -05002812 airo_print_err(dev->name, "Couldn't request region");
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002813 goto err_out_nets;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 }
2815 }
2816
2817 if (test_bit(FLAG_MPI,&ai->flags)) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002818 if (mpi_map_card(ai, pci)) {
2819 airo_print_err("", "Could not map memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820 goto err_out_res;
2821 }
2822 }
2823
2824 if (probe) {
Dan Williamsf65b56d2009-01-24 09:10:42 -05002825 if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002826 airo_print_err(dev->name, "MAC could not be enabled" );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827 rc = -EIO;
2828 goto err_out_map;
2829 }
2830 } else if (!test_bit(FLAG_MPI,&ai->flags)) {
2831 ai->bap_read = fast_bap_read;
2832 set_bit(FLAG_FLASHING, &ai->flags);
2833 }
2834
Michal Schmidt1138c372007-06-29 15:33:41 +02002835 strcpy(dev->name, "eth%d");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836 rc = register_netdev(dev);
2837 if (rc) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002838 airo_print_err(dev->name, "Couldn't register_netdev");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839 goto err_out_map;
2840 }
2841 ai->wifidev = init_wifidev(ai, dev);
Florin Malita431aca52006-10-10 16:46:30 -04002842 if (!ai->wifidev)
2843 goto err_out_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844
Dan Williamsf65b56d2009-01-24 09:10:42 -05002845 rc = readCapabilityRid(ai, &cap_rid, 1);
2846 if (rc != SUCCESS) {
2847 rc = -EIO;
2848 goto err_out_wifi;
2849 }
Dan Williams138c0c62009-01-24 09:11:35 -05002850 /* WEP capability discovery */
2851 ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0;
2852 ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0;
Dan Williamsf65b56d2009-01-24 09:10:42 -05002853
2854 airo_print_info(dev->name, "Firmware version %x.%x.%02x",
2855 ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF),
2856 (le16_to_cpu(cap_rid.softVer) & 0xFF),
2857 le16_to_cpu(cap_rid.softSubVer));
2858
2859 /* Test for WPA support */
2860 /* Only firmware versions 5.30.17 or better can do WPA */
2861 if (le16_to_cpu(cap_rid.softVer) > 0x530
2862 || (le16_to_cpu(cap_rid.softVer) == 0x530
2863 && le16_to_cpu(cap_rid.softSubVer) >= 17)) {
2864 airo_print_info(ai->dev->name, "WPA supported.");
2865
2866 set_bit(FLAG_WPA_CAPABLE, &ai->flags);
2867 ai->bssListFirst = RID_WPA_BSSLISTFIRST;
2868 ai->bssListNext = RID_WPA_BSSLISTNEXT;
2869 ai->bssListRidLen = sizeof(BSSListRid);
2870 } else {
2871 airo_print_info(ai->dev->name, "WPA unsupported with firmware "
2872 "versions older than 5.30.17.");
2873
2874 ai->bssListFirst = RID_BSSLISTFIRST;
2875 ai->bssListNext = RID_BSSLISTNEXT;
2876 ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra);
2877 }
2878
Linus Torvalds1da177e2005-04-16 15:20:36 -07002879 set_bit(FLAG_REGISTERED,&ai->flags);
Johannes Berge1749612008-10-27 15:59:26 -07002880 airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881
2882 /* Allocate the transmit buffers */
2883 if (probe && !test_bit(FLAG_MPI,&ai->flags))
2884 for( i = 0; i < MAX_FIDS; i++ )
Dan Williams15db2762006-03-16 13:46:27 -05002885 ai->fids[i] = transmit_allocate(ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002886
Wang Chenfaf39942008-10-14 13:30:33 +08002887 if (setup_proc_entry(dev, dev->ml_priv) < 0)
Florin Malita431aca52006-10-10 16:46:30 -04002888 goto err_out_wifi;
2889
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890 return dev;
2891
Florin Malita431aca52006-10-10 16:46:30 -04002892err_out_wifi:
2893 unregister_netdev(ai->wifidev);
2894 free_netdev(ai->wifidev);
2895err_out_reg:
2896 unregister_netdev(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897err_out_map:
2898 if (test_bit(FLAG_MPI,&ai->flags) && pci) {
2899 pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
2900 iounmap(ai->pciaux);
2901 iounmap(ai->pcimem);
2902 mpi_unmap_card(ai->pci);
2903 }
2904err_out_res:
2905 if (!is_pcmcia)
2906 release_region( dev->base_addr, 64 );
Michal Schmidt4d881902007-03-16 12:42:59 +01002907err_out_nets:
2908 airo_networks_free(ai);
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002909 del_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910err_out_free:
2911 free_netdev(dev);
2912 return NULL;
2913}
2914
2915struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia,
2916 struct device *dmdev)
2917{
2918 return _init_airo_card ( irq, port, is_pcmcia, NULL, dmdev);
2919}
2920
2921EXPORT_SYMBOL(init_airo_card);
2922
2923static int waitbusy (struct airo_info *ai) {
2924 int delay = 0;
Andrew Mortonb212f332008-05-28 12:40:39 -07002925 while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002926 udelay (10);
2927 if ((++delay % 20) == 0)
2928 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
2929 }
2930 return delay < 10000;
2931}
2932
2933int reset_airo_card( struct net_device *dev )
2934{
2935 int i;
Wang Chenfaf39942008-10-14 13:30:33 +08002936 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002937
2938 if (reset_card (dev, 1))
2939 return -1;
2940
2941 if ( setup_card(ai, dev->dev_addr, 1 ) != SUCCESS ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002942 airo_print_err(dev->name, "MAC could not be enabled");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943 return -1;
2944 }
Johannes Berge1749612008-10-27 15:59:26 -07002945 airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946 /* Allocate the transmit buffers if needed */
2947 if (!test_bit(FLAG_MPI,&ai->flags))
2948 for( i = 0; i < MAX_FIDS; i++ )
Dan Williams15db2762006-03-16 13:46:27 -05002949 ai->fids[i] = transmit_allocate (ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002950
2951 enable_interrupts( ai );
2952 netif_wake_queue(dev);
2953 return 0;
2954}
2955
2956EXPORT_SYMBOL(reset_airo_card);
2957
2958static void airo_send_event(struct net_device *dev) {
Wang Chenfaf39942008-10-14 13:30:33 +08002959 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002960 union iwreq_data wrqu;
2961 StatusRid status_rid;
2962
Dan Williams3c304952006-04-15 12:26:18 -04002963 clear_bit(JOB_EVENT, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964 PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
2965 up(&ai->sem);
2966 wrqu.data.length = 0;
2967 wrqu.data.flags = 0;
2968 memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN);
2969 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
2970
2971 /* Send event to user space */
2972 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
2973}
2974
Dan Williams9e75af32006-03-16 13:46:29 -05002975static void airo_process_scan_results (struct airo_info *ai) {
2976 union iwreq_data wrqu;
Dan Williams3c304952006-04-15 12:26:18 -04002977 BSSListRid bss;
Dan Williams9e75af32006-03-16 13:46:29 -05002978 int rc;
2979 BSSListElement * loop_net;
2980 BSSListElement * tmp_net;
2981
2982 /* Blow away current list of scan results */
2983 list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) {
2984 list_move_tail (&loop_net->list, &ai->network_free_list);
2985 /* Don't blow away ->list, just BSS data */
2986 memset (loop_net, 0, sizeof (loop_net->bss));
2987 }
2988
2989 /* Try to read the first entry of the scan result */
Dan Williams3c304952006-04-15 12:26:18 -04002990 rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0);
Al Viro17e70492007-12-19 18:56:37 -05002991 if((rc) || (bss.index == cpu_to_le16(0xffff))) {
Dan Williams9e75af32006-03-16 13:46:29 -05002992 /* No scan results */
2993 goto out;
2994 }
2995
2996 /* Read and parse all entries */
2997 tmp_net = NULL;
Al Viro17e70492007-12-19 18:56:37 -05002998 while((!rc) && (bss.index != cpu_to_le16(0xffff))) {
Dan Williams9e75af32006-03-16 13:46:29 -05002999 /* Grab a network off the free list */
3000 if (!list_empty(&ai->network_free_list)) {
3001 tmp_net = list_entry(ai->network_free_list.next,
3002 BSSListElement, list);
3003 list_del(ai->network_free_list.next);
3004 }
3005
3006 if (tmp_net != NULL) {
Dan Williams3c304952006-04-15 12:26:18 -04003007 memcpy(tmp_net, &bss, sizeof(tmp_net->bss));
Dan Williams9e75af32006-03-16 13:46:29 -05003008 list_add_tail(&tmp_net->list, &ai->network_list);
3009 tmp_net = NULL;
3010 }
3011
3012 /* Read next entry */
Dan Williams3c304952006-04-15 12:26:18 -04003013 rc = PC4500_readrid(ai, ai->bssListNext,
3014 &bss, ai->bssListRidLen, 0);
Dan Williams9e75af32006-03-16 13:46:29 -05003015 }
3016
3017out:
3018 ai->scan_timeout = 0;
Dan Williams3c304952006-04-15 12:26:18 -04003019 clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
Dan Williams9e75af32006-03-16 13:46:29 -05003020 up(&ai->sem);
3021
3022 /* Send an empty event to user space.
3023 * We don't send the received data on
3024 * the event because it would require
3025 * us to do complex transcoding, and
3026 * we want to minimise the work done in
3027 * the irq handler. Use a request to
3028 * extract the data - Jean II */
3029 wrqu.data.length = 0;
3030 wrqu.data.flags = 0;
3031 wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL);
3032}
3033
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034static int airo_thread(void *data) {
3035 struct net_device *dev = data;
Wang Chenfaf39942008-10-14 13:30:33 +08003036 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003037 int locked;
Rafael J. Wysocki83144182007-07-17 04:03:35 -07003038
3039 set_freezable();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003040 while(1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003041 /* make swsusp happy with our thread */
Christoph Lameter3e1d1d22005-06-24 23:13:50 -07003042 try_to_freeze();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003043
Dan Williams3c304952006-04-15 12:26:18 -04003044 if (test_bit(JOB_DIE, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003045 break;
3046
Dan Williams3c304952006-04-15 12:26:18 -04003047 if (ai->jobs) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003048 locked = down_interruptible(&ai->sem);
3049 } else {
3050 wait_queue_t wait;
3051
3052 init_waitqueue_entry(&wait, current);
3053 add_wait_queue(&ai->thr_wait, &wait);
3054 for (;;) {
3055 set_current_state(TASK_INTERRUPTIBLE);
Dan Williams3c304952006-04-15 12:26:18 -04003056 if (ai->jobs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003057 break;
Dan Williams9e75af32006-03-16 13:46:29 -05003058 if (ai->expires || ai->scan_timeout) {
3059 if (ai->scan_timeout &&
3060 time_after_eq(jiffies,ai->scan_timeout)){
Dan Williams3c304952006-04-15 12:26:18 -04003061 set_bit(JOB_SCAN_RESULTS, &ai->jobs);
Dan Williams9e75af32006-03-16 13:46:29 -05003062 break;
3063 } else if (ai->expires &&
3064 time_after_eq(jiffies,ai->expires)){
Dan Williams3c304952006-04-15 12:26:18 -04003065 set_bit(JOB_AUTOWEP, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003066 break;
3067 }
Dave Kleikamp5bb85f12006-10-10 14:45:46 -07003068 if (!kthread_should_stop() &&
3069 !freezing(current)) {
Dan Williams9e75af32006-03-16 13:46:29 -05003070 unsigned long wake_at;
3071 if (!ai->expires || !ai->scan_timeout) {
3072 wake_at = max(ai->expires,
3073 ai->scan_timeout);
3074 } else {
3075 wake_at = min(ai->expires,
3076 ai->scan_timeout);
3077 }
3078 schedule_timeout(wake_at - jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003079 continue;
3080 }
Dave Kleikamp5bb85f12006-10-10 14:45:46 -07003081 } else if (!kthread_should_stop() &&
3082 !freezing(current)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003083 schedule();
3084 continue;
3085 }
3086 break;
3087 }
3088 current->state = TASK_RUNNING;
3089 remove_wait_queue(&ai->thr_wait, &wait);
3090 locked = 1;
3091 }
3092
3093 if (locked)
3094 continue;
3095
Dan Williams3c304952006-04-15 12:26:18 -04003096 if (test_bit(JOB_DIE, &ai->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003097 up(&ai->sem);
3098 break;
3099 }
3100
Pavel Machekca078ba2005-09-03 15:56:57 -07003101 if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003102 up(&ai->sem);
3103 continue;
3104 }
3105
Dan Williams3c304952006-04-15 12:26:18 -04003106 if (test_bit(JOB_XMIT, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003107 airo_end_xmit(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003108 else if (test_bit(JOB_XMIT11, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003109 airo_end_xmit11(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003110 else if (test_bit(JOB_STATS, &ai->jobs))
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003111 airo_read_stats(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003112 else if (test_bit(JOB_WSTATS, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003113 airo_read_wireless_stats(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003114 else if (test_bit(JOB_PROMISC, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003115 airo_set_promisc(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003116 else if (test_bit(JOB_MIC, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003117 micinit(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003118 else if (test_bit(JOB_EVENT, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003119 airo_send_event(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003120 else if (test_bit(JOB_AUTOWEP, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003121 timer_func(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003122 else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs))
Dan Williams9e75af32006-03-16 13:46:29 -05003123 airo_process_scan_results(ai);
3124 else /* Shouldn't get here, but we make sure to unlock */
3125 up(&ai->sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003126 }
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07003127
3128 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003129}
3130
Al Viro0300b332007-12-19 22:38:33 -05003131static int header_len(__le16 ctl)
3132{
3133 u16 fc = le16_to_cpu(ctl);
3134 switch (fc & 0xc) {
3135 case 4:
3136 if ((fc & 0xe0) == 0xc0)
3137 return 10; /* one-address control packet */
3138 return 16; /* two-address control packet */
3139 case 8:
3140 if ((fc & 0x300) == 0x300)
3141 return 30; /* WDS packet */
3142 }
3143 return 24;
3144}
3145
Dan Williamsf55d4512009-01-24 09:04:12 -05003146static void airo_handle_cisco_mic(struct airo_info *ai)
3147{
3148 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) {
3149 set_bit(JOB_MIC, &ai->jobs);
3150 wake_up_interruptible(&ai->thr_wait);
3151 }
3152}
3153
3154/* Airo Status codes */
3155#define STAT_NOBEACON 0x8000 /* Loss of sync - missed beacons */
3156#define STAT_MAXRETRIES 0x8001 /* Loss of sync - max retries */
3157#define STAT_MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/
3158#define STAT_FORCELOSS 0x8003 /* Loss of sync - host request */
3159#define STAT_TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */
3160#define STAT_DEAUTH 0x8100 /* low byte is 802.11 reason code */
3161#define STAT_DISASSOC 0x8200 /* low byte is 802.11 reason code */
3162#define STAT_ASSOC_FAIL 0x8400 /* low byte is 802.11 reason code */
3163#define STAT_AUTH_FAIL 0x0300 /* low byte is 802.11 reason code */
3164#define STAT_ASSOC 0x0400 /* Associated */
3165#define STAT_REASSOC 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */
3166
3167static void airo_print_status(const char *devname, u16 status)
3168{
3169 u8 reason = status & 0xFF;
3170
3171 switch (status) {
3172 case STAT_NOBEACON:
3173 airo_print_dbg(devname, "link lost (missed beacons)");
3174 break;
3175 case STAT_MAXRETRIES:
3176 case STAT_MAXARL:
3177 airo_print_dbg(devname, "link lost (max retries)");
3178 break;
3179 case STAT_FORCELOSS:
3180 airo_print_dbg(devname, "link lost (local choice)");
3181 break;
3182 case STAT_TSFSYNC:
3183 airo_print_dbg(devname, "link lost (TSF sync lost)");
3184 break;
3185 case STAT_DEAUTH:
3186 airo_print_dbg(devname, "deauthenticated (reason: %d)", reason);
3187 break;
3188 case STAT_DISASSOC:
3189 airo_print_dbg(devname, "disassociated (reason: %d)", reason);
3190 break;
3191 case STAT_ASSOC_FAIL:
3192 airo_print_dbg(devname, "association failed (reason: %d)",
3193 reason);
3194 break;
3195 case STAT_AUTH_FAIL:
3196 airo_print_dbg(devname, "authentication failed (reason: %d)",
3197 reason);
3198 break;
3199 default:
3200 break;
3201 }
3202}
3203
3204static void airo_handle_link(struct airo_info *ai)
3205{
3206 union iwreq_data wrqu;
3207 int scan_forceloss = 0;
3208 u16 status;
3209
3210 /* Get new status and acknowledge the link change */
3211 status = le16_to_cpu(IN4500(ai, LINKSTAT));
3212 OUT4500(ai, EVACK, EV_LINK);
3213
3214 if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0))
3215 scan_forceloss = 1;
3216
3217 airo_print_status(ai->dev->name, status);
3218
3219 if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) {
3220 if (auto_wep)
3221 ai->expires = 0;
3222 if (ai->list_bss_task)
3223 wake_up_process(ai->list_bss_task);
3224 set_bit(FLAG_UPDATE_UNI, &ai->flags);
3225 set_bit(FLAG_UPDATE_MULTI, &ai->flags);
3226
3227 if (down_trylock(&ai->sem) != 0) {
3228 set_bit(JOB_EVENT, &ai->jobs);
3229 wake_up_interruptible(&ai->thr_wait);
3230 } else
3231 airo_send_event(ai->dev);
3232 } else if (!scan_forceloss) {
3233 if (auto_wep && !ai->expires) {
3234 ai->expires = RUN_AT(3*HZ);
3235 wake_up_interruptible(&ai->thr_wait);
3236 }
3237
3238 /* Send event to user space */
3239 memset(wrqu.ap_addr.sa_data, '\0', ETH_ALEN);
3240 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
3241 wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL);
3242 }
3243}
3244
3245static void airo_handle_rx(struct airo_info *ai)
3246{
3247 struct sk_buff *skb = NULL;
3248 __le16 fc, v, *buffer, tmpbuf[4];
3249 u16 len, hdrlen = 0, gap, fid;
3250 struct rx_hdr hdr;
3251 int success = 0;
3252
3253 if (test_bit(FLAG_MPI, &ai->flags)) {
3254 if (test_bit(FLAG_802_11, &ai->flags))
3255 mpi_receive_802_11(ai);
3256 else
3257 mpi_receive_802_3(ai);
3258 OUT4500(ai, EVACK, EV_RX);
3259 return;
3260 }
3261
3262 fid = IN4500(ai, RXFID);
3263
3264 /* Get the packet length */
3265 if (test_bit(FLAG_802_11, &ai->flags)) {
3266 bap_setup (ai, fid, 4, BAP0);
3267 bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0);
3268 /* Bad CRC. Ignore packet */
3269 if (le16_to_cpu(hdr.status) & 2)
3270 hdr.len = 0;
3271 if (ai->wifidev == NULL)
3272 hdr.len = 0;
3273 } else {
3274 bap_setup(ai, fid, 0x36, BAP0);
3275 bap_read(ai, &hdr.len, 2, BAP0);
3276 }
3277 len = le16_to_cpu(hdr.len);
3278
3279 if (len > AIRO_DEF_MTU) {
3280 airo_print_err(ai->dev->name, "Bad size %d", len);
3281 goto done;
3282 }
3283 if (len == 0)
3284 goto done;
3285
3286 if (test_bit(FLAG_802_11, &ai->flags)) {
3287 bap_read(ai, &fc, sizeof (fc), BAP0);
3288 hdrlen = header_len(fc);
3289 } else
3290 hdrlen = ETH_ALEN * 2;
3291
3292 skb = dev_alloc_skb(len + hdrlen + 2 + 2);
3293 if (!skb) {
3294 ai->dev->stats.rx_dropped++;
3295 goto done;
3296 }
3297
3298 skb_reserve(skb, 2); /* This way the IP header is aligned */
3299 buffer = (__le16 *) skb_put(skb, len + hdrlen);
3300 if (test_bit(FLAG_802_11, &ai->flags)) {
3301 buffer[0] = fc;
3302 bap_read(ai, buffer + 1, hdrlen - 2, BAP0);
3303 if (hdrlen == 24)
3304 bap_read(ai, tmpbuf, 6, BAP0);
3305
3306 bap_read(ai, &v, sizeof(v), BAP0);
3307 gap = le16_to_cpu(v);
3308 if (gap) {
3309 if (gap <= 8) {
3310 bap_read(ai, tmpbuf, gap, BAP0);
3311 } else {
3312 airo_print_err(ai->dev->name, "gaplen too "
3313 "big. Problems will follow...");
3314 }
3315 }
3316 bap_read(ai, buffer + hdrlen/2, len, BAP0);
3317 } else {
3318 MICBuffer micbuf;
3319
3320 bap_read(ai, buffer, ETH_ALEN * 2, BAP0);
3321 if (ai->micstats.enabled) {
3322 bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0);
3323 if (ntohs(micbuf.typelen) > 0x05DC)
3324 bap_setup(ai, fid, 0x44, BAP0);
3325 else {
3326 if (len <= sizeof (micbuf)) {
3327 dev_kfree_skb_irq(skb);
3328 goto done;
3329 }
3330
3331 len -= sizeof(micbuf);
3332 skb_trim(skb, len + hdrlen);
3333 }
3334 }
3335
3336 bap_read(ai, buffer + ETH_ALEN, len, BAP0);
3337 if (decapsulate(ai, &micbuf, (etherHead*) buffer, len))
3338 dev_kfree_skb_irq (skb);
3339 else
3340 success = 1;
3341 }
3342
3343#ifdef WIRELESS_SPY
3344 if (success && (ai->spy_data.spy_number > 0)) {
3345 char *sa;
3346 struct iw_quality wstats;
3347
3348 /* Prepare spy data : addr + qual */
3349 if (!test_bit(FLAG_802_11, &ai->flags)) {
3350 sa = (char *) buffer + 6;
3351 bap_setup(ai, fid, 8, BAP0);
3352 bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0);
3353 } else
3354 sa = (char *) buffer + 10;
3355 wstats.qual = hdr.rssi[0];
3356 if (ai->rssi)
3357 wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
3358 else
3359 wstats.level = (hdr.rssi[1] + 321) / 2;
3360 wstats.noise = ai->wstats.qual.noise;
3361 wstats.updated = IW_QUAL_LEVEL_UPDATED
3362 | IW_QUAL_QUAL_UPDATED
3363 | IW_QUAL_DBM;
3364 /* Update spy records */
3365 wireless_spy_update(ai->dev, sa, &wstats);
3366 }
3367#endif /* WIRELESS_SPY */
3368
3369done:
3370 OUT4500(ai, EVACK, EV_RX);
3371
3372 if (success) {
3373 if (test_bit(FLAG_802_11, &ai->flags)) {
3374 skb_reset_mac_header(skb);
3375 skb->pkt_type = PACKET_OTHERHOST;
3376 skb->dev = ai->wifidev;
3377 skb->protocol = htons(ETH_P_802_2);
3378 } else
3379 skb->protocol = eth_type_trans(skb, ai->dev);
3380 skb->ip_summed = CHECKSUM_NONE;
3381
3382 netif_rx(skb);
3383 }
3384}
3385
3386static void airo_handle_tx(struct airo_info *ai, u16 status)
3387{
3388 int i, len = 0, index = -1;
3389 u16 fid;
3390
3391 if (test_bit(FLAG_MPI, &ai->flags)) {
3392 unsigned long flags;
3393
3394 if (status & EV_TXEXC)
3395 get_tx_error(ai, -1);
3396
3397 spin_lock_irqsave(&ai->aux_lock, flags);
3398 if (!skb_queue_empty(&ai->txq)) {
3399 spin_unlock_irqrestore(&ai->aux_lock,flags);
3400 mpi_send_packet(ai->dev);
3401 } else {
3402 clear_bit(FLAG_PENDING_XMIT, &ai->flags);
3403 spin_unlock_irqrestore(&ai->aux_lock,flags);
3404 netif_wake_queue(ai->dev);
3405 }
3406 OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
3407 return;
3408 }
3409
3410 fid = IN4500(ai, TXCOMPLFID);
3411
3412 for(i = 0; i < MAX_FIDS; i++) {
3413 if ((ai->fids[i] & 0xffff) == fid) {
3414 len = ai->fids[i] >> 16;
3415 index = i;
3416 }
3417 }
3418
3419 if (index != -1) {
3420 if (status & EV_TXEXC)
3421 get_tx_error(ai, index);
3422
3423 OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC));
3424
3425 /* Set up to be used again */
3426 ai->fids[index] &= 0xffff;
3427 if (index < MAX_FIDS / 2) {
3428 if (!test_bit(FLAG_PENDING_XMIT, &ai->flags))
3429 netif_wake_queue(ai->dev);
3430 } else {
3431 if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags))
3432 netif_wake_queue(ai->wifidev);
3433 }
3434 } else {
3435 OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
3436 airo_print_err(ai->dev->name, "Unallocated FID was used to xmit");
3437 }
3438}
3439
Jeff Garzik28fc1f52007-10-29 05:46:16 -04003440static irqreturn_t airo_interrupt(int irq, void *dev_id)
3441{
3442 struct net_device *dev = dev_id;
Dan Williamsf55d4512009-01-24 09:04:12 -05003443 u16 status, savedInterrupts = 0;
3444 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003445 int handled = 0;
3446
3447 if (!netif_device_present(dev))
3448 return IRQ_NONE;
3449
3450 for (;;) {
Dan Williamsf55d4512009-01-24 09:04:12 -05003451 status = IN4500(ai, EVSTAT);
3452 if (!(status & STATUS_INTS) || (status == 0xffff))
3453 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003454
3455 handled = 1;
3456
Dan Williamsf55d4512009-01-24 09:04:12 -05003457 if (status & EV_AWAKE) {
3458 OUT4500(ai, EVACK, EV_AWAKE);
3459 OUT4500(ai, EVACK, EV_AWAKE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003460 }
3461
3462 if (!savedInterrupts) {
Dan Williamsf55d4512009-01-24 09:04:12 -05003463 savedInterrupts = IN4500(ai, EVINTEN);
3464 OUT4500(ai, EVINTEN, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003465 }
3466
Dan Williamsf55d4512009-01-24 09:04:12 -05003467 if (status & EV_MIC) {
3468 OUT4500(ai, EVACK, EV_MIC);
3469 airo_handle_cisco_mic(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003470 }
Dan Williams6fcdf562006-03-31 15:08:46 -05003471
Dan Williamsf55d4512009-01-24 09:04:12 -05003472 if (status & EV_LINK) {
3473 /* Link status changed */
3474 airo_handle_link(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003475 }
3476
3477 /* Check to see if there is something to receive */
Dan Williamsf55d4512009-01-24 09:04:12 -05003478 if (status & EV_RX)
3479 airo_handle_rx(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003480
3481 /* Check to see if a packet has been transmitted */
Dan Williamsf55d4512009-01-24 09:04:12 -05003482 if (status & (EV_TX | EV_TXCPY | EV_TXEXC))
3483 airo_handle_tx(ai, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003484
Dan Williamsf55d4512009-01-24 09:04:12 -05003485 if ( status & ~STATUS_INTS & ~IGNORE_INTS ) {
3486 airo_print_warn(ai->dev->name, "Got weird status %x",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003487 status & ~STATUS_INTS & ~IGNORE_INTS );
Dan Williamsf55d4512009-01-24 09:04:12 -05003488 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003489 }
3490
3491 if (savedInterrupts)
Dan Williamsf55d4512009-01-24 09:04:12 -05003492 OUT4500(ai, EVINTEN, savedInterrupts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003493
Linus Torvalds1da177e2005-04-16 15:20:36 -07003494 return IRQ_RETVAL(handled);
3495}
3496
3497/*
3498 * Routines to talk to the card
3499 */
3500
3501/*
3502 * This was originally written for the 4500, hence the name
3503 * NOTE: If use with 8bit mode and SMP bad things will happen!
3504 * Why would some one do 8 bit IO in an SMP machine?!?
3505 */
3506static void OUT4500( struct airo_info *ai, u16 reg, u16 val ) {
3507 if (test_bit(FLAG_MPI,&ai->flags))
3508 reg <<= 1;
3509 if ( !do8bitIO )
3510 outw( val, ai->dev->base_addr + reg );
3511 else {
3512 outb( val & 0xff, ai->dev->base_addr + reg );
3513 outb( val >> 8, ai->dev->base_addr + reg + 1 );
3514 }
3515}
3516
3517static u16 IN4500( struct airo_info *ai, u16 reg ) {
3518 unsigned short rc;
3519
3520 if (test_bit(FLAG_MPI,&ai->flags))
3521 reg <<= 1;
3522 if ( !do8bitIO )
3523 rc = inw( ai->dev->base_addr + reg );
3524 else {
3525 rc = inb( ai->dev->base_addr + reg );
3526 rc += ((int)inb( ai->dev->base_addr + reg + 1 )) << 8;
3527 }
3528 return rc;
3529}
3530
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003531static int enable_MAC(struct airo_info *ai, int lock)
3532{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003533 int rc;
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003534 Cmd cmd;
3535 Resp rsp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003536
3537 /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions
3538 * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down"
3539 * Note : we could try to use !netif_running(dev) in enable_MAC()
3540 * instead of this flag, but I don't trust it *within* the
3541 * open/close functions, and testing both flags together is
3542 * "cheaper" - Jean II */
3543 if (ai->flags & FLAG_RADIO_MASK) return SUCCESS;
3544
3545 if (lock && down_interruptible(&ai->sem))
3546 return -ERESTARTSYS;
3547
3548 if (!test_bit(FLAG_ENABLED, &ai->flags)) {
3549 memset(&cmd, 0, sizeof(cmd));
3550 cmd.cmd = MAC_ENABLE;
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003551 rc = issuecommand(ai, &cmd, &rsp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003552 if (rc == SUCCESS)
3553 set_bit(FLAG_ENABLED, &ai->flags);
3554 } else
3555 rc = SUCCESS;
3556
3557 if (lock)
3558 up(&ai->sem);
3559
3560 if (rc)
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003561 airo_print_err(ai->dev->name, "Cannot enable MAC");
3562 else if ((rsp.status & 0xFF00) != 0) {
3563 airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, "
3564 "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2);
3565 rc = ERROR;
3566 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003567 return rc;
3568}
3569
3570static void disable_MAC( struct airo_info *ai, int lock ) {
3571 Cmd cmd;
3572 Resp rsp;
3573
3574 if (lock && down_interruptible(&ai->sem))
3575 return;
3576
3577 if (test_bit(FLAG_ENABLED, &ai->flags)) {
3578 memset(&cmd, 0, sizeof(cmd));
3579 cmd.cmd = MAC_DISABLE; // disable in case already enabled
3580 issuecommand(ai, &cmd, &rsp);
3581 clear_bit(FLAG_ENABLED, &ai->flags);
3582 }
3583 if (lock)
3584 up(&ai->sem);
3585}
3586
3587static void enable_interrupts( struct airo_info *ai ) {
3588 /* Enable the interrupts */
3589 OUT4500( ai, EVINTEN, STATUS_INTS );
3590}
3591
3592static void disable_interrupts( struct airo_info *ai ) {
3593 OUT4500( ai, EVINTEN, 0 );
3594}
3595
3596static void mpi_receive_802_3(struct airo_info *ai)
3597{
3598 RxFid rxd;
3599 int len = 0;
3600 struct sk_buff *skb;
3601 char *buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003602 int off = 0;
3603 MICBuffer micbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003604
3605 memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
3606 /* Make sure we got something */
3607 if (rxd.rdy && rxd.valid == 0) {
3608 len = rxd.len + 12;
3609 if (len < 12 || len > 2048)
3610 goto badrx;
3611
3612 skb = dev_alloc_skb(len);
3613 if (!skb) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003614 ai->dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003615 goto badrx;
3616 }
3617 buffer = skb_put(skb,len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003618 memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2);
3619 if (ai->micstats.enabled) {
3620 memcpy(&micbuf,
3621 ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2,
3622 sizeof(micbuf));
3623 if (ntohs(micbuf.typelen) <= 0x05DC) {
3624 if (len <= sizeof(micbuf) + ETH_ALEN * 2)
3625 goto badmic;
3626
3627 off = sizeof(micbuf);
3628 skb_trim (skb, len - off);
3629 }
3630 }
3631 memcpy(buffer + ETH_ALEN * 2,
3632 ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off,
3633 len - ETH_ALEN * 2 - off);
3634 if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) {
3635badmic:
3636 dev_kfree_skb_irq (skb);
3637 goto badrx;
3638 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003639#ifdef WIRELESS_SPY
3640 if (ai->spy_data.spy_number > 0) {
3641 char *sa;
3642 struct iw_quality wstats;
3643 /* Prepare spy data : addr + qual */
3644 sa = buffer + ETH_ALEN;
3645 wstats.qual = 0; /* XXX Where do I get that info from ??? */
3646 wstats.level = 0;
3647 wstats.updated = 0;
3648 /* Update spy records */
3649 wireless_spy_update(ai->dev, sa, &wstats);
3650 }
3651#endif /* WIRELESS_SPY */
3652
Linus Torvalds1da177e2005-04-16 15:20:36 -07003653 skb->ip_summed = CHECKSUM_NONE;
3654 skb->protocol = eth_type_trans(skb, ai->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003655 netif_rx(skb);
3656 }
3657badrx:
3658 if (rxd.valid == 0) {
3659 rxd.valid = 1;
3660 rxd.rdy = 0;
3661 rxd.len = PKTSIZE;
3662 memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
3663 }
3664}
3665
Hannes Eder2ed5ba82008-12-26 00:12:59 -08003666static void mpi_receive_802_11(struct airo_info *ai)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003667{
3668 RxFid rxd;
3669 struct sk_buff *skb = NULL;
Al Viro0300b332007-12-19 22:38:33 -05003670 u16 len, hdrlen = 0;
3671 __le16 fc;
Dan Williamsf55d4512009-01-24 09:04:12 -05003672 struct rx_hdr hdr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003673 u16 gap;
3674 u16 *buffer;
Dan Williamsf55d4512009-01-24 09:04:12 -05003675 char *ptr = ai->rxfids[0].virtual_host_addr + 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003676
3677 memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
3678 memcpy ((char *)&hdr, ptr, sizeof(hdr));
3679 ptr += sizeof(hdr);
3680 /* Bad CRC. Ignore packet */
3681 if (le16_to_cpu(hdr.status) & 2)
3682 hdr.len = 0;
3683 if (ai->wifidev == NULL)
3684 hdr.len = 0;
3685 len = le16_to_cpu(hdr.len);
Dan Williams15db2762006-03-16 13:46:27 -05003686 if (len > AIRO_DEF_MTU) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003687 airo_print_err(ai->dev->name, "Bad size %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003688 goto badrx;
3689 }
3690 if (len == 0)
3691 goto badrx;
3692
Al Viro0300b332007-12-19 22:38:33 -05003693 fc = get_unaligned((__le16 *)ptr);
3694 hdrlen = header_len(fc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003695
3696 skb = dev_alloc_skb( len + hdrlen + 2 );
3697 if ( !skb ) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003698 ai->dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003699 goto badrx;
3700 }
3701 buffer = (u16*)skb_put (skb, len + hdrlen);
3702 memcpy ((char *)buffer, ptr, hdrlen);
3703 ptr += hdrlen;
3704 if (hdrlen == 24)
3705 ptr += 6;
Harvey Harrison533dd1b2008-04-29 01:03:36 -07003706 gap = get_unaligned_le16(ptr);
Al Viro593c2b92007-12-17 15:09:34 -05003707 ptr += sizeof(__le16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003708 if (gap) {
3709 if (gap <= 8)
3710 ptr += gap;
3711 else
Dan Williams934d8bf2006-03-16 13:46:23 -05003712 airo_print_err(ai->dev->name,
3713 "gaplen too big. Problems will follow...");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003714 }
3715 memcpy ((char *)buffer + hdrlen, ptr, len);
3716 ptr += len;
3717#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
3718 if (ai->spy_data.spy_number > 0) {
3719 char *sa;
3720 struct iw_quality wstats;
3721 /* Prepare spy data : addr + qual */
3722 sa = (char*)buffer + 10;
3723 wstats.qual = hdr.rssi[0];
3724 if (ai->rssi)
3725 wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
3726 else
3727 wstats.level = (hdr.rssi[1] + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04003728 wstats.noise = ai->wstats.qual.noise;
3729 wstats.updated = IW_QUAL_QUAL_UPDATED
3730 | IW_QUAL_LEVEL_UPDATED
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07003731 | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003732 /* Update spy records */
3733 wireless_spy_update(ai->dev, sa, &wstats);
3734 }
3735#endif /* IW_WIRELESS_SPY */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07003736 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003737 skb->pkt_type = PACKET_OTHERHOST;
3738 skb->dev = ai->wifidev;
3739 skb->protocol = htons(ETH_P_802_2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003740 skb->ip_summed = CHECKSUM_NONE;
3741 netif_rx( skb );
Dan Williamsf55d4512009-01-24 09:04:12 -05003742
Linus Torvalds1da177e2005-04-16 15:20:36 -07003743badrx:
3744 if (rxd.valid == 0) {
3745 rxd.valid = 1;
3746 rxd.rdy = 0;
3747 rxd.len = PKTSIZE;
3748 memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
3749 }
3750}
3751
3752static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
3753{
3754 Cmd cmd;
3755 Resp rsp;
3756 int status;
3757 int i;
3758 SsidRid mySsid;
Al Viro4293ea32007-12-19 19:21:51 -05003759 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003760 WepKeyRid wkr;
3761 int rc;
3762
3763 memset( &mySsid, 0, sizeof( mySsid ) );
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003764 kfree (ai->flash);
3765 ai->flash = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003766
3767 /* The NOP is the first step in getting the card going */
3768 cmd.cmd = NOP;
3769 cmd.parm0 = cmd.parm1 = cmd.parm2 = 0;
3770 if (lock && down_interruptible(&ai->sem))
3771 return ERROR;
3772 if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) {
3773 if (lock)
3774 up(&ai->sem);
3775 return ERROR;
3776 }
3777 disable_MAC( ai, 0);
3778
3779 // Let's figure out if we need to use the AUX port
3780 if (!test_bit(FLAG_MPI,&ai->flags)) {
3781 cmd.cmd = CMD_ENABLEAUX;
3782 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
3783 if (lock)
3784 up(&ai->sem);
Dan Williams934d8bf2006-03-16 13:46:23 -05003785 airo_print_err(ai->dev->name, "Error checking for AUX port");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003786 return ERROR;
3787 }
3788 if (!aux_bap || rsp.status & 0xff00) {
3789 ai->bap_read = fast_bap_read;
Dan Williams934d8bf2006-03-16 13:46:23 -05003790 airo_print_dbg(ai->dev->name, "Doing fast bap_reads");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003791 } else {
3792 ai->bap_read = aux_bap_read;
Dan Williams934d8bf2006-03-16 13:46:23 -05003793 airo_print_dbg(ai->dev->name, "Doing AUX bap_reads");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003794 }
3795 }
3796 if (lock)
3797 up(&ai->sem);
3798 if (ai->config.len == 0) {
3799 tdsRssiRid rssi_rid;
3800 CapabilityRid cap_rid;
3801
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003802 kfree(ai->APList);
3803 ai->APList = NULL;
3804 kfree(ai->SSID);
3805 ai->SSID = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003806 // general configuration (read/modify/write)
3807 status = readConfigRid(ai, lock);
3808 if ( status != SUCCESS ) return ERROR;
3809
3810 status = readCapabilityRid(ai, &cap_rid, lock);
3811 if ( status != SUCCESS ) return ERROR;
3812
3813 status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),lock);
3814 if ( status == SUCCESS ) {
3815 if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
Dan Williams41480af2005-05-10 09:45:51 -04003816 memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003817 }
3818 else {
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003819 kfree(ai->rssi);
3820 ai->rssi = NULL;
Al Viro56d81bd2007-12-20 17:18:35 -05003821 if (cap_rid.softCap & cpu_to_le16(8))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003822 ai->config.rmode |= RXMODE_NORMALIZED_RSSI;
3823 else
Dan Williams934d8bf2006-03-16 13:46:23 -05003824 airo_print_warn(ai->dev->name, "unknown received signal "
3825 "level scale");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003826 }
3827 ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
3828 ai->config.authType = AUTH_OPEN;
3829 ai->config.modulation = MOD_CCK;
3830
Al Viro56d81bd2007-12-20 17:18:35 -05003831 if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) &&
Al Viro156178582007-12-20 17:21:36 -05003832 (cap_rid.extSoftCap & cpu_to_le16(1)) &&
Al Viro56d81bd2007-12-20 17:18:35 -05003833 micsetup(ai) == SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003834 ai->config.opmode |= MODE_MIC;
3835 set_bit(FLAG_MIC_CAPABLE, &ai->flags);
3836 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003837
3838 /* Save off the MAC */
3839 for( i = 0; i < ETH_ALEN; i++ ) {
3840 mac[i] = ai->config.macAddr[i];
3841 }
3842
3843 /* Check to see if there are any insmod configured
3844 rates to add */
3845 if ( rates[0] ) {
3846 int i = 0;
3847 memset(ai->config.rates,0,sizeof(ai->config.rates));
3848 for( i = 0; i < 8 && rates[i]; i++ ) {
3849 ai->config.rates[i] = rates[i];
3850 }
3851 }
3852 if ( basic_rate > 0 ) {
3853 int i;
3854 for( i = 0; i < 8; i++ ) {
3855 if ( ai->config.rates[i] == basic_rate ||
3856 !ai->config.rates ) {
3857 ai->config.rates[i] = basic_rate | 0x80;
3858 break;
3859 }
3860 }
3861 }
3862 set_bit (FLAG_COMMIT, &ai->flags);
3863 }
3864
3865 /* Setup the SSIDs if present */
3866 if ( ssids[0] ) {
3867 int i;
3868 for( i = 0; i < 3 && ssids[i]; i++ ) {
Al Viro0dd22122007-12-17 16:11:57 -05003869 size_t len = strlen(ssids[i]);
3870 if (len > 32)
3871 len = 32;
3872 mySsid.ssids[i].len = cpu_to_le16(len);
3873 memcpy(mySsid.ssids[i].ssid, ssids[i], len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003874 }
Al Viro0dd22122007-12-17 16:11:57 -05003875 mySsid.len = cpu_to_le16(sizeof(mySsid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003876 }
3877
3878 status = writeConfigRid(ai, lock);
3879 if ( status != SUCCESS ) return ERROR;
3880
3881 /* Set up the SSID list */
3882 if ( ssids[0] ) {
3883 status = writeSsidRid(ai, &mySsid, lock);
3884 if ( status != SUCCESS ) return ERROR;
3885 }
3886
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003887 status = enable_MAC(ai, lock);
3888 if (status != SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003889 return ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003890
3891 /* Grab the initial wep key, we gotta save it for auto_wep */
3892 rc = readWepKeyRid(ai, &wkr, 1, lock);
3893 if (rc == SUCCESS) do {
3894 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05003895 if (wkr.kindex == cpu_to_le16(0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003896 ai->defindex = wkr.mac[0];
3897 }
3898 rc = readWepKeyRid(ai, &wkr, 0, lock);
3899 } while(lastindex != wkr.kindex);
3900
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02003901 try_auto_wep(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003902
3903 return SUCCESS;
3904}
3905
3906static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) {
3907 // Im really paranoid about letting it run forever!
3908 int max_tries = 600000;
3909
3910 if (IN4500(ai, EVSTAT) & EV_CMD)
3911 OUT4500(ai, EVACK, EV_CMD);
3912
3913 OUT4500(ai, PARAM0, pCmd->parm0);
3914 OUT4500(ai, PARAM1, pCmd->parm1);
3915 OUT4500(ai, PARAM2, pCmd->parm2);
3916 OUT4500(ai, COMMAND, pCmd->cmd);
3917
3918 while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) {
3919 if ((IN4500(ai, COMMAND)) == pCmd->cmd)
3920 // PC4500 didn't notice command, try again
3921 OUT4500(ai, COMMAND, pCmd->cmd);
3922 if (!in_atomic() && (max_tries & 255) == 0)
3923 schedule();
3924 }
3925
3926 if ( max_tries == -1 ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003927 airo_print_err(ai->dev->name,
3928 "Max tries exceeded when issueing command");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003929 if (IN4500(ai, COMMAND) & COMMAND_BUSY)
3930 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
3931 return ERROR;
3932 }
3933
3934 // command completed
3935 pRsp->status = IN4500(ai, STATUS);
3936 pRsp->rsp0 = IN4500(ai, RESP0);
3937 pRsp->rsp1 = IN4500(ai, RESP1);
3938 pRsp->rsp2 = IN4500(ai, RESP2);
Robert Schulze13dca9b2006-07-10 18:37:44 +02003939 if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET)
3940 airo_print_err(ai->dev->name,
3941 "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x",
3942 pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1,
3943 pRsp->rsp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003944
3945 // clear stuck command busy if necessary
3946 if (IN4500(ai, COMMAND) & COMMAND_BUSY) {
3947 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
3948 }
3949 // acknowledge processing the status/response
3950 OUT4500(ai, EVACK, EV_CMD);
3951
3952 return SUCCESS;
3953}
3954
3955/* Sets up the bap to start exchange data. whichbap should
3956 * be one of the BAP0 or BAP1 defines. Locks should be held before
3957 * calling! */
3958static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap )
3959{
3960 int timeout = 50;
3961 int max_tries = 3;
3962
3963 OUT4500(ai, SELECT0+whichbap, rid);
3964 OUT4500(ai, OFFSET0+whichbap, offset);
3965 while (1) {
3966 int status = IN4500(ai, OFFSET0+whichbap);
3967 if (status & BAP_BUSY) {
3968 /* This isn't really a timeout, but its kinda
3969 close */
3970 if (timeout--) {
3971 continue;
3972 }
3973 } else if ( status & BAP_ERR ) {
3974 /* invalid rid or offset */
Dan Williams934d8bf2006-03-16 13:46:23 -05003975 airo_print_err(ai->dev->name, "BAP error %x %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003976 status, whichbap );
3977 return ERROR;
3978 } else if (status & BAP_DONE) { // success
3979 return SUCCESS;
3980 }
3981 if ( !(max_tries--) ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003982 airo_print_err(ai->dev->name,
Michal Schmidt1138c372007-06-29 15:33:41 +02003983 "BAP setup error too many retries\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003984 return ERROR;
3985 }
3986 // -- PC4500 missed it, try again
3987 OUT4500(ai, SELECT0+whichbap, rid);
3988 OUT4500(ai, OFFSET0+whichbap, offset);
3989 timeout = 50;
3990 }
3991}
3992
3993/* should only be called by aux_bap_read. This aux function and the
3994 following use concepts not documented in the developers guide. I
3995 got them from a patch given to my by Aironet */
3996static u16 aux_setup(struct airo_info *ai, u16 page,
3997 u16 offset, u16 *len)
3998{
3999 u16 next;
4000
4001 OUT4500(ai, AUXPAGE, page);
4002 OUT4500(ai, AUXOFF, 0);
4003 next = IN4500(ai, AUXDATA);
4004 *len = IN4500(ai, AUXDATA)&0xff;
4005 if (offset != 4) OUT4500(ai, AUXOFF, offset);
4006 return next;
4007}
4008
4009/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004010static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004011 int bytelen, int whichbap)
4012{
4013 u16 len;
4014 u16 page;
4015 u16 offset;
4016 u16 next;
4017 int words;
4018 int i;
4019 unsigned long flags;
4020
4021 spin_lock_irqsave(&ai->aux_lock, flags);
4022 page = IN4500(ai, SWS0+whichbap);
4023 offset = IN4500(ai, SWS2+whichbap);
4024 next = aux_setup(ai, page, offset, &len);
4025 words = (bytelen+1)>>1;
4026
4027 for (i=0; i<words;) {
4028 int count;
4029 count = (len>>1) < (words-i) ? (len>>1) : (words-i);
4030 if ( !do8bitIO )
4031 insw( ai->dev->base_addr+DATA0+whichbap,
4032 pu16Dst+i,count );
4033 else
4034 insb( ai->dev->base_addr+DATA0+whichbap,
4035 pu16Dst+i, count << 1 );
4036 i += count;
4037 if (i<words) {
4038 next = aux_setup(ai, next, 4, &len);
4039 }
4040 }
4041 spin_unlock_irqrestore(&ai->aux_lock, flags);
4042 return SUCCESS;
4043}
4044
4045
4046/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004047static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004048 int bytelen, int whichbap)
4049{
4050 bytelen = (bytelen + 1) & (~1); // round up to even value
4051 if ( !do8bitIO )
4052 insw( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1 );
4053 else
4054 insb( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen );
4055 return SUCCESS;
4056}
4057
4058/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004059static int bap_write(struct airo_info *ai, const __le16 *pu16Src,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004060 int bytelen, int whichbap)
4061{
4062 bytelen = (bytelen + 1) & (~1); // round up to even value
4063 if ( !do8bitIO )
4064 outsw( ai->dev->base_addr+DATA0+whichbap,
4065 pu16Src, bytelen>>1 );
4066 else
4067 outsb( ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen );
4068 return SUCCESS;
4069}
4070
4071static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd)
4072{
4073 Cmd cmd; /* for issuing commands */
4074 Resp rsp; /* response from commands */
4075 u16 status;
4076
4077 memset(&cmd, 0, sizeof(cmd));
4078 cmd.cmd = accmd;
4079 cmd.parm0 = rid;
4080 status = issuecommand(ai, &cmd, &rsp);
4081 if (status != 0) return status;
4082 if ( (rsp.status & 0x7F00) != 0) {
4083 return (accmd << 8) + (rsp.rsp0 & 0xFF);
4084 }
4085 return 0;
4086}
4087
4088/* Note, that we are using BAP1 which is also used by transmit, so
4089 * we must get a lock. */
4090static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock)
4091{
4092 u16 status;
4093 int rc = SUCCESS;
4094
4095 if (lock) {
4096 if (down_interruptible(&ai->sem))
4097 return ERROR;
4098 }
4099 if (test_bit(FLAG_MPI,&ai->flags)) {
4100 Cmd cmd;
4101 Resp rsp;
4102
4103 memset(&cmd, 0, sizeof(cmd));
4104 memset(&rsp, 0, sizeof(rsp));
4105 ai->config_desc.rid_desc.valid = 1;
4106 ai->config_desc.rid_desc.len = RIDSIZE;
4107 ai->config_desc.rid_desc.rid = 0;
4108 ai->config_desc.rid_desc.host_addr = ai->ridbus;
4109
4110 cmd.cmd = CMD_ACCESS;
4111 cmd.parm0 = rid;
4112
4113 memcpy_toio(ai->config_desc.card_ram_off,
4114 &ai->config_desc.rid_desc, sizeof(Rid));
4115
4116 rc = issuecommand(ai, &cmd, &rsp);
4117
4118 if (rsp.status & 0x7f00)
4119 rc = rsp.rsp0;
4120 if (!rc)
4121 memcpy(pBuf, ai->config_desc.virtual_host_addr, len);
4122 goto done;
4123 } else {
4124 if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) {
4125 rc = status;
4126 goto done;
4127 }
4128 if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
4129 rc = ERROR;
4130 goto done;
4131 }
4132 // read the rid length field
4133 bap_read(ai, pBuf, 2, BAP1);
4134 // length for remaining part of rid
Al Viro593c2b92007-12-17 15:09:34 -05004135 len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004136
4137 if ( len <= 2 ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004138 airo_print_err(ai->dev->name,
4139 "Rid %x has a length of %d which is too short",
Linus Torvalds1da177e2005-04-16 15:20:36 -07004140 (int)rid, (int)len );
4141 rc = ERROR;
4142 goto done;
4143 }
4144 // read remainder of the rid
Al Virob8c06bc2007-12-19 17:55:43 -05004145 rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004146 }
4147done:
4148 if (lock)
4149 up(&ai->sem);
4150 return rc;
4151}
4152
4153/* Note, that we are using BAP1 which is also used by transmit, so
4154 * make sure this isnt called when a transmit is happening */
4155static int PC4500_writerid(struct airo_info *ai, u16 rid,
4156 const void *pBuf, int len, int lock)
4157{
4158 u16 status;
4159 int rc = SUCCESS;
4160
Al Viro593c2b92007-12-17 15:09:34 -05004161 *(__le16*)pBuf = cpu_to_le16((u16)len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004162
4163 if (lock) {
4164 if (down_interruptible(&ai->sem))
4165 return ERROR;
4166 }
4167 if (test_bit(FLAG_MPI,&ai->flags)) {
4168 Cmd cmd;
4169 Resp rsp;
4170
Dan Streetmanf89b2322005-11-11 11:41:42 -05004171 if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid))
Dan Williams934d8bf2006-03-16 13:46:23 -05004172 airo_print_err(ai->dev->name,
4173 "%s: MAC should be disabled (rid=%04x)",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004174 __func__, rid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004175 memset(&cmd, 0, sizeof(cmd));
4176 memset(&rsp, 0, sizeof(rsp));
4177
4178 ai->config_desc.rid_desc.valid = 1;
4179 ai->config_desc.rid_desc.len = *((u16 *)pBuf);
4180 ai->config_desc.rid_desc.rid = 0;
4181
4182 cmd.cmd = CMD_WRITERID;
4183 cmd.parm0 = rid;
4184
4185 memcpy_toio(ai->config_desc.card_ram_off,
4186 &ai->config_desc.rid_desc, sizeof(Rid));
4187
4188 if (len < 4 || len > 2047) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004189 airo_print_err(ai->dev->name, "%s: len=%d", __func__, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004190 rc = -1;
4191 } else {
4192 memcpy((char *)ai->config_desc.virtual_host_addr,
4193 pBuf, len);
4194
4195 rc = issuecommand(ai, &cmd, &rsp);
4196 if ((rc & 0xff00) != 0) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004197 airo_print_err(ai->dev->name, "%s: Write rid Error %d",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004198 __func__, rc);
Dan Williams934d8bf2006-03-16 13:46:23 -05004199 airo_print_err(ai->dev->name, "%s: Cmd=%04x",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004200 __func__, cmd.cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004201 }
4202
4203 if ((rsp.status & 0x7f00))
4204 rc = rsp.rsp0;
4205 }
4206 } else {
4207 // --- first access so that we can write the rid data
4208 if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) {
4209 rc = status;
4210 goto done;
4211 }
4212 // --- now write the rid data
4213 if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
4214 rc = ERROR;
4215 goto done;
4216 }
4217 bap_write(ai, pBuf, len, BAP1);
4218 // ---now commit the rid data
4219 rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS);
4220 }
4221done:
4222 if (lock)
4223 up(&ai->sem);
4224 return rc;
4225}
4226
4227/* Allocates a FID to be used for transmitting packets. We only use
4228 one for now. */
4229static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw)
4230{
4231 unsigned int loop = 3000;
4232 Cmd cmd;
4233 Resp rsp;
4234 u16 txFid;
Al Viro593c2b92007-12-17 15:09:34 -05004235 __le16 txControl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004236
4237 cmd.cmd = CMD_ALLOCATETX;
4238 cmd.parm0 = lenPayload;
4239 if (down_interruptible(&ai->sem))
4240 return ERROR;
4241 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
4242 txFid = ERROR;
4243 goto done;
4244 }
4245 if ( (rsp.status & 0xFF00) != 0) {
4246 txFid = ERROR;
4247 goto done;
4248 }
4249 /* wait for the allocate event/indication
4250 * It makes me kind of nervous that this can just sit here and spin,
4251 * but in practice it only loops like four times. */
4252 while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop);
4253 if (!loop) {
4254 txFid = ERROR;
4255 goto done;
4256 }
4257
4258 // get the allocated fid and acknowledge
4259 txFid = IN4500(ai, TXALLOCFID);
4260 OUT4500(ai, EVACK, EV_ALLOC);
4261
4262 /* The CARD is pretty cool since it converts the ethernet packet
4263 * into 802.11. Also note that we don't release the FID since we
4264 * will be using the same one over and over again. */
4265 /* We only have to setup the control once since we are not
4266 * releasing the fid. */
4267 if (raw)
4268 txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11
4269 | TXCTL_ETHERNET | TXCTL_NORELEASE);
4270 else
4271 txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3
4272 | TXCTL_ETHERNET | TXCTL_NORELEASE);
4273 if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS)
4274 txFid = ERROR;
4275 else
4276 bap_write(ai, &txControl, sizeof(txControl), BAP1);
4277
4278done:
4279 up(&ai->sem);
4280
4281 return txFid;
4282}
4283
4284/* In general BAP1 is dedicated to transmiting packets. However,
4285 since we need a BAP when accessing RIDs, we also use BAP1 for that.
4286 Make sure the BAP1 spinlock is held when this is called. */
4287static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket)
4288{
Al Viro593c2b92007-12-17 15:09:34 -05004289 __le16 payloadLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004290 Cmd cmd;
4291 Resp rsp;
4292 int miclen = 0;
4293 u16 txFid = len;
4294 MICBuffer pMic;
4295
4296 len >>= 16;
4297
4298 if (len <= ETH_ALEN * 2) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004299 airo_print_warn(ai->dev->name, "Short packet %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004300 return ERROR;
4301 }
4302 len -= ETH_ALEN * 2;
4303
Linus Torvalds1da177e2005-04-16 15:20:36 -07004304 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
Al Viro593c2b92007-12-17 15:09:34 -05004305 (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004306 if (encapsulate(ai,(etherHead *)pPacket,&pMic,len) != SUCCESS)
4307 return ERROR;
4308 miclen = sizeof(pMic);
4309 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004310 // packet is destination[6], source[6], payload[len-12]
4311 // write the payload length and dst/src/payload
4312 if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR;
4313 /* The hardware addresses aren't counted as part of the payload, so
4314 * we have to subtract the 12 bytes for the addresses off */
4315 payloadLen = cpu_to_le16(len + miclen);
4316 bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
Al Virob8c06bc2007-12-19 17:55:43 -05004317 bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004318 if (miclen)
Al Virob8c06bc2007-12-19 17:55:43 -05004319 bap_write(ai, (__le16*)&pMic, miclen, BAP1);
4320 bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004321 // issue the transmit command
4322 memset( &cmd, 0, sizeof( cmd ) );
4323 cmd.cmd = CMD_TRANSMIT;
4324 cmd.parm0 = txFid;
4325 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
4326 if ( (rsp.status & 0xFF00) != 0) return ERROR;
4327 return SUCCESS;
4328}
4329
4330static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket)
4331{
Al Viro593c2b92007-12-17 15:09:34 -05004332 __le16 fc, payloadLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004333 Cmd cmd;
4334 Resp rsp;
4335 int hdrlen;
Al Viro977b1432007-12-19 16:45:29 -05004336 static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6};
4337 /* padding of header to full size + le16 gaplen (6) + gaplen bytes */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004338 u16 txFid = len;
4339 len >>= 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004340
Al Viro0300b332007-12-19 22:38:33 -05004341 fc = *(__le16*)pPacket;
4342 hdrlen = header_len(fc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004343
4344 if (len < hdrlen) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004345 airo_print_warn(ai->dev->name, "Short packet %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004346 return ERROR;
4347 }
4348
4349 /* packet is 802.11 header + payload
4350 * write the payload length and dst/src/payload */
4351 if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR;
4352 /* The 802.11 header aren't counted as part of the payload, so
4353 * we have to subtract the header bytes off */
4354 payloadLen = cpu_to_le16(len-hdrlen);
4355 bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
4356 if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR;
Al Virob8c06bc2007-12-19 17:55:43 -05004357 bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1);
4358 bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004359
Al Virob8c06bc2007-12-19 17:55:43 -05004360 bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004361 // issue the transmit command
4362 memset( &cmd, 0, sizeof( cmd ) );
4363 cmd.cmd = CMD_TRANSMIT;
4364 cmd.parm0 = txFid;
4365 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
4366 if ( (rsp.status & 0xFF00) != 0) return ERROR;
4367 return SUCCESS;
4368}
4369
4370/*
4371 * This is the proc_fs routines. It is a bit messier than I would
4372 * like! Feel free to clean it up!
4373 */
4374
4375static ssize_t proc_read( struct file *file,
4376 char __user *buffer,
4377 size_t len,
4378 loff_t *offset);
4379
4380static ssize_t proc_write( struct file *file,
4381 const char __user *buffer,
4382 size_t len,
4383 loff_t *offset );
4384static int proc_close( struct inode *inode, struct file *file );
4385
4386static int proc_stats_open( struct inode *inode, struct file *file );
4387static int proc_statsdelta_open( struct inode *inode, struct file *file );
4388static int proc_status_open( struct inode *inode, struct file *file );
4389static int proc_SSID_open( struct inode *inode, struct file *file );
4390static int proc_APList_open( struct inode *inode, struct file *file );
4391static int proc_BSSList_open( struct inode *inode, struct file *file );
4392static int proc_config_open( struct inode *inode, struct file *file );
4393static int proc_wepkey_open( struct inode *inode, struct file *file );
4394
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004395static const struct file_operations proc_statsdelta_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004396 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004397 .read = proc_read,
4398 .open = proc_statsdelta_open,
4399 .release = proc_close
4400};
4401
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004402static const struct file_operations proc_stats_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004403 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004404 .read = proc_read,
4405 .open = proc_stats_open,
4406 .release = proc_close
4407};
4408
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004409static const struct file_operations proc_status_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004410 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004411 .read = proc_read,
4412 .open = proc_status_open,
4413 .release = proc_close
4414};
4415
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004416static const struct file_operations proc_SSID_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004417 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004418 .read = proc_read,
4419 .write = proc_write,
4420 .open = proc_SSID_open,
4421 .release = proc_close
4422};
4423
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004424static const struct file_operations proc_BSSList_ops = {
Denis V. Luneva95609c2008-04-29 01:02:29 -07004425 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004426 .read = proc_read,
4427 .write = proc_write,
4428 .open = proc_BSSList_open,
4429 .release = proc_close
4430};
4431
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004432static const struct file_operations proc_APList_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 .write = proc_write,
4436 .open = proc_APList_open,
4437 .release = proc_close
4438};
4439
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004440static const struct file_operations proc_config_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 .write = proc_write,
4444 .open = proc_config_open,
4445 .release = proc_close
4446};
4447
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004448static const struct file_operations proc_wepkey_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 .write = proc_write,
4452 .open = proc_wepkey_open,
4453 .release = proc_close
4454};
4455
4456static struct proc_dir_entry *airo_entry;
4457
4458struct proc_data {
4459 int release_buffer;
4460 int readlen;
4461 char *rbuffer;
4462 int writelen;
4463 int maxwritelen;
4464 char *wbuffer;
4465 void (*on_close) (struct inode *, struct file *);
4466};
4467
Linus Torvalds1da177e2005-04-16 15:20:36 -07004468static int setup_proc_entry( struct net_device *dev,
4469 struct airo_info *apriv ) {
4470 struct proc_dir_entry *entry;
4471 /* First setup the device directory */
4472 strcpy(apriv->proc_name,dev->name);
4473 apriv->proc_entry = create_proc_entry(apriv->proc_name,
4474 S_IFDIR|airo_perm,
4475 airo_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004476 if (!apriv->proc_entry)
4477 goto fail;
4478 apriv->proc_entry->uid = proc_uid;
4479 apriv->proc_entry->gid = proc_gid;
4480 apriv->proc_entry->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004481
4482 /* Setup the StatsDelta */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004483 entry = proc_create_data("StatsDelta",
4484 S_IFREG | (S_IRUGO&proc_perm),
4485 apriv->proc_entry, &proc_statsdelta_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004486 if (!entry)
4487 goto fail_stats_delta;
4488 entry->uid = proc_uid;
4489 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004490
4491 /* Setup the Stats */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004492 entry = proc_create_data("Stats",
4493 S_IFREG | (S_IRUGO&proc_perm),
4494 apriv->proc_entry, &proc_stats_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004495 if (!entry)
4496 goto fail_stats;
4497 entry->uid = proc_uid;
4498 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004499
4500 /* Setup the Status */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004501 entry = proc_create_data("Status",
4502 S_IFREG | (S_IRUGO&proc_perm),
4503 apriv->proc_entry, &proc_status_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004504 if (!entry)
4505 goto fail_status;
4506 entry->uid = proc_uid;
4507 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004508
4509 /* Setup the Config */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004510 entry = proc_create_data("Config",
4511 S_IFREG | proc_perm,
4512 apriv->proc_entry, &proc_config_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004513 if (!entry)
4514 goto fail_config;
4515 entry->uid = proc_uid;
4516 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004517
4518 /* Setup the SSID */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004519 entry = proc_create_data("SSID",
4520 S_IFREG | proc_perm,
4521 apriv->proc_entry, &proc_SSID_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004522 if (!entry)
4523 goto fail_ssid;
4524 entry->uid = proc_uid;
4525 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004526
4527 /* Setup the APList */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004528 entry = proc_create_data("APList",
4529 S_IFREG | proc_perm,
4530 apriv->proc_entry, &proc_APList_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004531 if (!entry)
4532 goto fail_aplist;
4533 entry->uid = proc_uid;
4534 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004535
4536 /* Setup the BSSList */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004537 entry = proc_create_data("BSSList",
4538 S_IFREG | proc_perm,
4539 apriv->proc_entry, &proc_BSSList_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004540 if (!entry)
4541 goto fail_bsslist;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004542 entry->uid = proc_uid;
4543 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004544
4545 /* Setup the WepKey */
Denis V. Luneva95609c2008-04-29 01:02:29 -07004546 entry = proc_create_data("WepKey",
4547 S_IFREG | proc_perm,
4548 apriv->proc_entry, &proc_wepkey_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004549 if (!entry)
4550 goto fail_wepkey;
4551 entry->uid = proc_uid;
4552 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004553
4554 return 0;
Florin Malita431aca52006-10-10 16:46:30 -04004555
4556fail_wepkey:
4557 remove_proc_entry("BSSList", apriv->proc_entry);
4558fail_bsslist:
4559 remove_proc_entry("APList", apriv->proc_entry);
4560fail_aplist:
4561 remove_proc_entry("SSID", apriv->proc_entry);
4562fail_ssid:
4563 remove_proc_entry("Config", apriv->proc_entry);
4564fail_config:
4565 remove_proc_entry("Status", apriv->proc_entry);
4566fail_status:
4567 remove_proc_entry("Stats", apriv->proc_entry);
4568fail_stats:
4569 remove_proc_entry("StatsDelta", apriv->proc_entry);
4570fail_stats_delta:
4571 remove_proc_entry(apriv->proc_name, airo_entry);
4572fail:
4573 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004574}
4575
4576static int takedown_proc_entry( struct net_device *dev,
4577 struct airo_info *apriv ) {
4578 if ( !apriv->proc_entry->namelen ) return 0;
4579 remove_proc_entry("Stats",apriv->proc_entry);
4580 remove_proc_entry("StatsDelta",apriv->proc_entry);
4581 remove_proc_entry("Status",apriv->proc_entry);
4582 remove_proc_entry("Config",apriv->proc_entry);
4583 remove_proc_entry("SSID",apriv->proc_entry);
4584 remove_proc_entry("APList",apriv->proc_entry);
4585 remove_proc_entry("BSSList",apriv->proc_entry);
4586 remove_proc_entry("WepKey",apriv->proc_entry);
4587 remove_proc_entry(apriv->proc_name,airo_entry);
4588 return 0;
4589}
4590
4591/*
4592 * What we want from the proc_fs is to be able to efficiently read
4593 * and write the configuration. To do this, we want to read the
4594 * configuration when the file is opened and write it when the file is
4595 * closed. So basically we allocate a read buffer at open and fill it
4596 * with data, and allocate a write buffer and read it at close.
4597 */
4598
4599/*
4600 * The read routine is generic, it relies on the preallocated rbuffer
4601 * to supply the data.
4602 */
4603static ssize_t proc_read( struct file *file,
4604 char __user *buffer,
4605 size_t len,
4606 loff_t *offset )
4607{
Akinobu Mitacc0d9ff2008-06-09 16:44:30 -07004608 struct proc_data *priv = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004609
4610 if (!priv->rbuffer)
4611 return -EINVAL;
4612
Akinobu Mitacc0d9ff2008-06-09 16:44:30 -07004613 return simple_read_from_buffer(buffer, len, offset, priv->rbuffer,
4614 priv->readlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004615}
4616
4617/*
4618 * The write routine is generic, it fills in a preallocated rbuffer
4619 * to supply the data.
4620 */
4621static ssize_t proc_write( struct file *file,
4622 const char __user *buffer,
4623 size_t len,
4624 loff_t *offset )
4625{
4626 loff_t pos = *offset;
4627 struct proc_data *priv = (struct proc_data*)file->private_data;
4628
4629 if (!priv->wbuffer)
4630 return -EINVAL;
4631
4632 if (pos < 0)
4633 return -EINVAL;
4634 if (pos >= priv->maxwritelen)
4635 return 0;
4636 if (len > priv->maxwritelen - pos)
4637 len = priv->maxwritelen - pos;
4638 if (copy_from_user(priv->wbuffer + pos, buffer, len))
4639 return -EFAULT;
4640 if ( pos + len > priv->writelen )
4641 priv->writelen = len + file->f_pos;
4642 *offset = pos + len;
4643 return len;
4644}
4645
Al Viro329e2c02007-12-20 22:58:57 -05004646static int proc_status_open(struct inode *inode, struct file *file)
4647{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004648 struct proc_data *data;
4649 struct proc_dir_entry *dp = PDE(inode);
4650 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08004651 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004652 CapabilityRid cap_rid;
4653 StatusRid status_rid;
Al Viro329e2c02007-12-20 22:58:57 -05004654 u16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004655 int i;
4656
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01004657 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004658 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004659 data = (struct proc_data *)file->private_data;
4660 if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
4661 kfree (file->private_data);
4662 return -ENOMEM;
4663 }
4664
4665 readStatusRid(apriv, &status_rid, 1);
4666 readCapabilityRid(apriv, &cap_rid, 1);
4667
Al Viro329e2c02007-12-20 22:58:57 -05004668 mode = le16_to_cpu(status_rid.mode);
4669
Linus Torvalds1da177e2005-04-16 15:20:36 -07004670 i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n",
Al Viro329e2c02007-12-20 22:58:57 -05004671 mode & 1 ? "CFG ": "",
4672 mode & 2 ? "ACT ": "",
4673 mode & 0x10 ? "SYN ": "",
4674 mode & 0x20 ? "LNK ": "",
4675 mode & 0x40 ? "LEAP ": "",
4676 mode & 0x80 ? "PRIV ": "",
4677 mode & 0x100 ? "KEY ": "",
4678 mode & 0x200 ? "WEP ": "",
4679 mode & 0x8000 ? "ERR ": "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004680 sprintf( data->rbuffer+i, "Mode: %x\n"
4681 "Signal Strength: %d\n"
4682 "Signal Quality: %d\n"
4683 "SSID: %-.*s\n"
4684 "AP: %-.16s\n"
4685 "Freq: %d\n"
4686 "BitRate: %dmbs\n"
4687 "Driver Version: %s\n"
4688 "Device: %s\nManufacturer: %s\nFirmware Version: %s\n"
4689 "Radio type: %x\nCountry: %x\nHardware Version: %x\n"
4690 "Software Version: %x\nSoftware Subversion: %x\n"
4691 "Boot block version: %x\n",
Al Viro329e2c02007-12-20 22:58:57 -05004692 le16_to_cpu(status_rid.mode),
4693 le16_to_cpu(status_rid.normalizedSignalStrength),
4694 le16_to_cpu(status_rid.signalQuality),
4695 le16_to_cpu(status_rid.SSIDlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07004696 status_rid.SSID,
4697 status_rid.apName,
Al Viro329e2c02007-12-20 22:58:57 -05004698 le16_to_cpu(status_rid.channel),
4699 le16_to_cpu(status_rid.currentXmitRate) / 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004700 version,
4701 cap_rid.prodName,
4702 cap_rid.manName,
4703 cap_rid.prodVer,
Al Viro56d81bd2007-12-20 17:18:35 -05004704 le16_to_cpu(cap_rid.radioType),
4705 le16_to_cpu(cap_rid.country),
4706 le16_to_cpu(cap_rid.hardVer),
4707 le16_to_cpu(cap_rid.softVer),
4708 le16_to_cpu(cap_rid.softSubVer),
4709 le16_to_cpu(cap_rid.bootBlockVer));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004710 data->readlen = strlen( data->rbuffer );
4711 return 0;
4712}
4713
4714static int proc_stats_rid_open(struct inode*, struct file*, u16);
4715static int proc_statsdelta_open( struct inode *inode,
4716 struct file *file ) {
4717 if (file->f_mode&FMODE_WRITE) {
4718 return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR);
4719 }
4720 return proc_stats_rid_open(inode, file, RID_STATSDELTA);
4721}
4722
4723static int proc_stats_open( struct inode *inode, struct file *file ) {
4724 return proc_stats_rid_open(inode, file, RID_STATS);
4725}
4726
4727static int proc_stats_rid_open( struct inode *inode,
4728 struct file *file,
Al Viroa23ace52007-12-19 22:24:16 -05004729 u16 rid )
4730{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004731 struct proc_data *data;
4732 struct proc_dir_entry *dp = PDE(inode);
4733 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08004734 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004735 StatsRid stats;
4736 int i, j;
Al Viroa23ace52007-12-19 22:24:16 -05004737 __le32 *vals = stats.vals;
4738 int len = le16_to_cpu(stats.len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004739
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01004740 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004741 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004742 data = (struct proc_data *)file->private_data;
4743 if ((data->rbuffer = kmalloc( 4096, GFP_KERNEL )) == NULL) {
4744 kfree (file->private_data);
4745 return -ENOMEM;
4746 }
4747
4748 readStatsRid(apriv, &stats, rid, 1);
4749
4750 j = 0;
Al Viroa23ace52007-12-19 22:24:16 -05004751 for(i=0; statsLabels[i]!=(char *)-1 && i*4<len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004752 if (!statsLabels[i]) continue;
4753 if (j+strlen(statsLabels[i])+16>4096) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004754 airo_print_warn(apriv->dev->name,
4755 "Potentially disasterous buffer overflow averted!");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004756 break;
4757 }
Al Viroa23ace52007-12-19 22:24:16 -05004758 j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i],
4759 le32_to_cpu(vals[i]));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004760 }
Al Viroa23ace52007-12-19 22:24:16 -05004761 if (i*4 >= len) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004762 airo_print_warn(apriv->dev->name, "Got a short rid");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004763 }
4764 data->readlen = j;
4765 return 0;
4766}
4767
4768static int get_dec_u16( char *buffer, int *start, int limit ) {
4769 u16 value;
4770 int valid = 0;
4771 for( value = 0; buffer[*start] >= '0' &&
4772 buffer[*start] <= '9' &&
4773 *start < limit; (*start)++ ) {
4774 valid = 1;
4775 value *= 10;
4776 value += buffer[*start] - '0';
4777 }
4778 if ( !valid ) return -1;
4779 return value;
4780}
4781
4782static int airo_config_commit(struct net_device *dev,
4783 struct iw_request_info *info, void *zwrq,
4784 char *extra);
4785
Al Viro3eb9b412007-12-21 00:00:35 -05004786static inline int sniffing_mode(struct airo_info *ai)
4787{
4788 return le16_to_cpu(ai->config.rmode & RXMODE_MASK) >=
4789 le16_to_cpu(RXMODE_RFMON);
4790}
4791
4792static void proc_config_on_close(struct inode *inode, struct file *file)
4793{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004794 struct proc_data *data = file->private_data;
4795 struct proc_dir_entry *dp = PDE(inode);
4796 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08004797 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004798 char *line;
4799
4800 if ( !data->writelen ) return;
4801
4802 readConfigRid(ai, 1);
4803 set_bit (FLAG_COMMIT, &ai->flags);
4804
4805 line = data->wbuffer;
4806 while( line[0] ) {
4807/*** Mode processing */
4808 if ( !strncmp( line, "Mode: ", 6 ) ) {
4809 line += 6;
Al Viro3eb9b412007-12-21 00:00:35 -05004810 if (sniffing_mode(ai))
4811 set_bit (FLAG_RESET, &ai->flags);
4812 ai->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004813 clear_bit (FLAG_802_11, &ai->flags);
Al Viro3eb9b412007-12-21 00:00:35 -05004814 ai->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004815 ai->config.scanMode = SCANMODE_ACTIVE;
4816 if ( line[0] == 'a' ) {
Al Viro3eb9b412007-12-21 00:00:35 -05004817 ai->config.opmode |= MODE_STA_IBSS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004818 } else {
Al Viro3eb9b412007-12-21 00:00:35 -05004819 ai->config.opmode |= MODE_STA_ESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004820 if ( line[0] == 'r' ) {
4821 ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
4822 ai->config.scanMode = SCANMODE_PASSIVE;
4823 set_bit (FLAG_802_11, &ai->flags);
4824 } else if ( line[0] == 'y' ) {
4825 ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER;
4826 ai->config.scanMode = SCANMODE_PASSIVE;
4827 set_bit (FLAG_802_11, &ai->flags);
4828 } else if ( line[0] == 'l' )
4829 ai->config.rmode |= RXMODE_LANMON;
4830 }
4831 set_bit (FLAG_COMMIT, &ai->flags);
4832 }
4833
4834/*** Radio status */
4835 else if (!strncmp(line,"Radio: ", 7)) {
4836 line += 7;
4837 if (!strncmp(line,"off",3)) {
4838 set_bit (FLAG_RADIO_OFF, &ai->flags);
4839 } else {
4840 clear_bit (FLAG_RADIO_OFF, &ai->flags);
4841 }
4842 }
4843/*** NodeName processing */
4844 else if ( !strncmp( line, "NodeName: ", 10 ) ) {
4845 int j;
4846
4847 line += 10;
4848 memset( ai->config.nodeName, 0, 16 );
4849/* Do the name, assume a space between the mode and node name */
4850 for( j = 0; j < 16 && line[j] != '\n'; j++ ) {
4851 ai->config.nodeName[j] = line[j];
4852 }
4853 set_bit (FLAG_COMMIT, &ai->flags);
4854 }
4855
4856/*** PowerMode processing */
4857 else if ( !strncmp( line, "PowerMode: ", 11 ) ) {
4858 line += 11;
4859 if ( !strncmp( line, "PSPCAM", 6 ) ) {
4860 ai->config.powerSaveMode = POWERSAVE_PSPCAM;
4861 set_bit (FLAG_COMMIT, &ai->flags);
4862 } else if ( !strncmp( line, "PSP", 3 ) ) {
4863 ai->config.powerSaveMode = POWERSAVE_PSP;
4864 set_bit (FLAG_COMMIT, &ai->flags);
4865 } else {
4866 ai->config.powerSaveMode = POWERSAVE_CAM;
4867 set_bit (FLAG_COMMIT, &ai->flags);
4868 }
4869 } else if ( !strncmp( line, "DataRates: ", 11 ) ) {
4870 int v, i = 0, k = 0; /* i is index into line,
4871 k is index to rates */
4872
4873 line += 11;
4874 while((v = get_dec_u16(line, &i, 3))!=-1) {
4875 ai->config.rates[k++] = (u8)v;
4876 line += i + 1;
4877 i = 0;
4878 }
4879 set_bit (FLAG_COMMIT, &ai->flags);
4880 } else if ( !strncmp( line, "Channel: ", 9 ) ) {
4881 int v, i = 0;
4882 line += 9;
4883 v = get_dec_u16(line, &i, i+3);
4884 if ( v != -1 ) {
Al Viro3eb9b412007-12-21 00:00:35 -05004885 ai->config.channelSet = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004886 set_bit (FLAG_COMMIT, &ai->flags);
4887 }
4888 } else if ( !strncmp( line, "XmitPower: ", 11 ) ) {
4889 int v, i = 0;
4890 line += 11;
4891 v = get_dec_u16(line, &i, i+3);
4892 if ( v != -1 ) {
Al Viro3eb9b412007-12-21 00:00:35 -05004893 ai->config.txPower = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004894 set_bit (FLAG_COMMIT, &ai->flags);
4895 }
4896 } else if ( !strncmp( line, "WEP: ", 5 ) ) {
4897 line += 5;
4898 switch( line[0] ) {
4899 case 's':
Al Viro3eb9b412007-12-21 00:00:35 -05004900 ai->config.authType = AUTH_SHAREDKEY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004901 break;
4902 case 'e':
Al Viro3eb9b412007-12-21 00:00:35 -05004903 ai->config.authType = AUTH_ENCRYPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004904 break;
4905 default:
Al Viro3eb9b412007-12-21 00:00:35 -05004906 ai->config.authType = AUTH_OPEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004907 break;
4908 }
4909 set_bit (FLAG_COMMIT, &ai->flags);
4910 } else if ( !strncmp( line, "LongRetryLimit: ", 16 ) ) {
4911 int v, i = 0;
4912
4913 line += 16;
4914 v = get_dec_u16(line, &i, 3);
4915 v = (v<0) ? 0 : ((v>255) ? 255 : v);
Al Viro3eb9b412007-12-21 00:00:35 -05004916 ai->config.longRetryLimit = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004917 set_bit (FLAG_COMMIT, &ai->flags);
4918 } else if ( !strncmp( line, "ShortRetryLimit: ", 17 ) ) {
4919 int v, i = 0;
4920
4921 line += 17;
4922 v = get_dec_u16(line, &i, 3);
4923 v = (v<0) ? 0 : ((v>255) ? 255 : v);
Al Viro3eb9b412007-12-21 00:00:35 -05004924 ai->config.shortRetryLimit = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004925 set_bit (FLAG_COMMIT, &ai->flags);
4926 } else if ( !strncmp( line, "RTSThreshold: ", 14 ) ) {
4927 int v, i = 0;
4928
4929 line += 14;
4930 v = get_dec_u16(line, &i, 4);
Dan Williams15db2762006-03-16 13:46:27 -05004931 v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
Al Viro3eb9b412007-12-21 00:00:35 -05004932 ai->config.rtsThres = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004933 set_bit (FLAG_COMMIT, &ai->flags);
4934 } else if ( !strncmp( line, "TXMSDULifetime: ", 16 ) ) {
4935 int v, i = 0;
4936
4937 line += 16;
4938 v = get_dec_u16(line, &i, 5);
4939 v = (v<0) ? 0 : v;
Al Viro3eb9b412007-12-21 00:00:35 -05004940 ai->config.txLifetime = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004941 set_bit (FLAG_COMMIT, &ai->flags);
4942 } else if ( !strncmp( line, "RXMSDULifetime: ", 16 ) ) {
4943 int v, i = 0;
4944
4945 line += 16;
4946 v = get_dec_u16(line, &i, 5);
4947 v = (v<0) ? 0 : v;
Al Viro3eb9b412007-12-21 00:00:35 -05004948 ai->config.rxLifetime = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004949 set_bit (FLAG_COMMIT, &ai->flags);
4950 } else if ( !strncmp( line, "TXDiversity: ", 13 ) ) {
4951 ai->config.txDiversity =
4952 (line[13]=='l') ? 1 :
4953 ((line[13]=='r')? 2: 3);
4954 set_bit (FLAG_COMMIT, &ai->flags);
4955 } else if ( !strncmp( line, "RXDiversity: ", 13 ) ) {
4956 ai->config.rxDiversity =
4957 (line[13]=='l') ? 1 :
4958 ((line[13]=='r')? 2: 3);
4959 set_bit (FLAG_COMMIT, &ai->flags);
4960 } else if ( !strncmp( line, "FragThreshold: ", 15 ) ) {
4961 int v, i = 0;
4962
4963 line += 15;
4964 v = get_dec_u16(line, &i, 4);
Dan Williams15db2762006-03-16 13:46:27 -05004965 v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004966 v = v & 0xfffe; /* Make sure its even */
Al Viro3eb9b412007-12-21 00:00:35 -05004967 ai->config.fragThresh = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004968 set_bit (FLAG_COMMIT, &ai->flags);
4969 } else if (!strncmp(line, "Modulation: ", 12)) {
4970 line += 12;
4971 switch(*line) {
4972 case 'd': ai->config.modulation=MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break;
4973 case 'c': ai->config.modulation=MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break;
4974 case 'm': ai->config.modulation=MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break;
Dan Williams934d8bf2006-03-16 13:46:23 -05004975 default: airo_print_warn(ai->dev->name, "Unknown modulation");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004976 }
4977 } else if (!strncmp(line, "Preamble: ", 10)) {
4978 line += 10;
4979 switch(*line) {
4980 case 'a': ai->config.preamble=PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break;
4981 case 'l': ai->config.preamble=PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break;
4982 case 's': ai->config.preamble=PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break;
Dan Williams934d8bf2006-03-16 13:46:23 -05004983 default: airo_print_warn(ai->dev->name, "Unknown preamble");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004984 }
4985 } else {
Dan Williams934d8bf2006-03-16 13:46:23 -05004986 airo_print_warn(ai->dev->name, "Couldn't figure out %s", line);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004987 }
4988 while( line[0] && line[0] != '\n' ) line++;
4989 if ( line[0] ) line++;
4990 }
4991 airo_config_commit(dev, NULL, NULL, NULL);
4992}
4993
Al Viro3eb9b412007-12-21 00:00:35 -05004994static char *get_rmode(__le16 mode)
4995{
4996 switch(mode & RXMODE_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004997 case RXMODE_RFMON: return "rfmon";
4998 case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon";
4999 case RXMODE_LANMON: return "lanmon";
5000 }
5001 return "ESS";
5002}
5003
Al Viro3eb9b412007-12-21 00:00:35 -05005004static int proc_config_open(struct inode *inode, struct file *file)
5005{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005006 struct proc_data *data;
5007 struct proc_dir_entry *dp = PDE(inode);
5008 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005009 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005010 int i;
Al Viro3eb9b412007-12-21 00:00:35 -05005011 __le16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005012
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005013 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005014 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005015 data = (struct proc_data *)file->private_data;
5016 if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
5017 kfree (file->private_data);
5018 return -ENOMEM;
5019 }
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005020 if ((data->wbuffer = kzalloc( 2048, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005021 kfree (data->rbuffer);
5022 kfree (file->private_data);
5023 return -ENOMEM;
5024 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005025 data->maxwritelen = 2048;
5026 data->on_close = proc_config_on_close;
5027
5028 readConfigRid(ai, 1);
5029
Al Viro3eb9b412007-12-21 00:00:35 -05005030 mode = ai->config.opmode & MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005031 i = sprintf( data->rbuffer,
5032 "Mode: %s\n"
5033 "Radio: %s\n"
5034 "NodeName: %-16s\n"
5035 "PowerMode: %s\n"
5036 "DataRates: %d %d %d %d %d %d %d %d\n"
5037 "Channel: %d\n"
5038 "XmitPower: %d\n",
Al Viro3eb9b412007-12-21 00:00:35 -05005039 mode == MODE_STA_IBSS ? "adhoc" :
5040 mode == MODE_STA_ESS ? get_rmode(ai->config.rmode):
5041 mode == MODE_AP ? "AP" :
5042 mode == MODE_AP_RPTR ? "AP RPTR" : "Error",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005043 test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on",
5044 ai->config.nodeName,
Al Viro3eb9b412007-12-21 00:00:35 -05005045 ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" :
5046 ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" :
5047 ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" :
5048 "Error",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005049 (int)ai->config.rates[0],
5050 (int)ai->config.rates[1],
5051 (int)ai->config.rates[2],
5052 (int)ai->config.rates[3],
5053 (int)ai->config.rates[4],
5054 (int)ai->config.rates[5],
5055 (int)ai->config.rates[6],
5056 (int)ai->config.rates[7],
Al Viro3eb9b412007-12-21 00:00:35 -05005057 le16_to_cpu(ai->config.channelSet),
5058 le16_to_cpu(ai->config.txPower)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005059 );
5060 sprintf( data->rbuffer + i,
5061 "LongRetryLimit: %d\n"
5062 "ShortRetryLimit: %d\n"
5063 "RTSThreshold: %d\n"
5064 "TXMSDULifetime: %d\n"
5065 "RXMSDULifetime: %d\n"
5066 "TXDiversity: %s\n"
5067 "RXDiversity: %s\n"
5068 "FragThreshold: %d\n"
5069 "WEP: %s\n"
5070 "Modulation: %s\n"
5071 "Preamble: %s\n",
Al Viro3eb9b412007-12-21 00:00:35 -05005072 le16_to_cpu(ai->config.longRetryLimit),
5073 le16_to_cpu(ai->config.shortRetryLimit),
5074 le16_to_cpu(ai->config.rtsThres),
5075 le16_to_cpu(ai->config.txLifetime),
5076 le16_to_cpu(ai->config.rxLifetime),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005077 ai->config.txDiversity == 1 ? "left" :
5078 ai->config.txDiversity == 2 ? "right" : "both",
5079 ai->config.rxDiversity == 1 ? "left" :
5080 ai->config.rxDiversity == 2 ? "right" : "both",
Al Viro3eb9b412007-12-21 00:00:35 -05005081 le16_to_cpu(ai->config.fragThresh),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005082 ai->config.authType == AUTH_ENCRYPT ? "encrypt" :
5083 ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open",
Al Viro3eb9b412007-12-21 00:00:35 -05005084 ai->config.modulation == MOD_DEFAULT ? "default" :
Linus Torvalds1da177e2005-04-16 15:20:36 -07005085 ai->config.modulation == MOD_CCK ? "cck" :
5086 ai->config.modulation == MOD_MOK ? "mok" : "error",
5087 ai->config.preamble == PREAMBLE_AUTO ? "auto" :
5088 ai->config.preamble == PREAMBLE_LONG ? "long" :
5089 ai->config.preamble == PREAMBLE_SHORT ? "short" : "error"
5090 );
5091 data->readlen = strlen( data->rbuffer );
5092 return 0;
5093}
5094
Al Viro0dd22122007-12-17 16:11:57 -05005095static void proc_SSID_on_close(struct inode *inode, struct file *file)
5096{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005097 struct proc_data *data = (struct proc_data *)file->private_data;
5098 struct proc_dir_entry *dp = PDE(inode);
5099 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005100 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005101 SsidRid SSID_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005102 int i;
Al Viro0dd22122007-12-17 16:11:57 -05005103 char *p = data->wbuffer;
5104 char *end = p + data->writelen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005105
Al Viro0dd22122007-12-17 16:11:57 -05005106 if (!data->writelen)
5107 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005108
Al Viro0dd22122007-12-17 16:11:57 -05005109 *end = '\n'; /* sentinel; we have space for it */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005110
Al Viro0dd22122007-12-17 16:11:57 -05005111 memset(&SSID_rid, 0, sizeof(SSID_rid));
5112
5113 for (i = 0; i < 3 && p < end; i++) {
5114 int j = 0;
5115 /* copy up to 32 characters from this line */
5116 while (*p != '\n' && j < 32)
5117 SSID_rid.ssids[i].ssid[j++] = *p++;
5118 if (j == 0)
5119 break;
5120 SSID_rid.ssids[i].len = cpu_to_le16(j);
5121 /* skip to the beginning of the next line */
5122 while (*p++ != '\n')
5123 ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005124 }
5125 if (i)
Al Viro0dd22122007-12-17 16:11:57 -05005126 SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005127 disable_MAC(ai, 1);
5128 writeSsidRid(ai, &SSID_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005129 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005130}
5131
Jesper Juhl77933d72005-07-27 11:46:09 -07005132static inline u8 hexVal(char c) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005133 if (c>='0' && c<='9') return c -= '0';
5134 if (c>='a' && c<='f') return c -= 'a'-10;
5135 if (c>='A' && c<='F') return c -= 'A'-10;
5136 return 0;
5137}
5138
5139static void proc_APList_on_close( struct inode *inode, struct file *file ) {
5140 struct proc_data *data = (struct proc_data *)file->private_data;
5141 struct proc_dir_entry *dp = PDE(inode);
5142 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005143 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005144 APListRid APList_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005145 int i;
5146
5147 if ( !data->writelen ) return;
5148
5149 memset( &APList_rid, 0, sizeof(APList_rid) );
Al Viroa7497162007-12-20 17:49:41 -05005150 APList_rid.len = cpu_to_le16(sizeof(APList_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005151
5152 for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) {
5153 int j;
5154 for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) {
5155 switch(j%3) {
5156 case 0:
5157 APList_rid.ap[i][j/3]=
5158 hexVal(data->wbuffer[j+i*6*3])<<4;
5159 break;
5160 case 1:
5161 APList_rid.ap[i][j/3]|=
5162 hexVal(data->wbuffer[j+i*6*3]);
5163 break;
5164 }
5165 }
5166 }
5167 disable_MAC(ai, 1);
5168 writeAPListRid(ai, &APList_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005169 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005170}
5171
5172/* This function wraps PC4500_writerid with a MAC disable */
5173static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data,
5174 int len, int dummy ) {
5175 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005176
5177 disable_MAC(ai, 1);
5178 rc = PC4500_writerid(ai, rid, rid_data, len, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005179 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005180 return rc;
5181}
5182
Dan Williamsc0380692009-01-24 09:12:15 -05005183/* Returns the WEP key at the specified index, or -1 if that key does
5184 * not exist. The buffer is assumed to be at least 16 bytes in length.
Linus Torvalds1da177e2005-04-16 15:20:36 -07005185 */
Dan Williamsc0380692009-01-24 09:12:15 -05005186static int get_wep_key(struct airo_info *ai, u16 index, char *buf, u16 buflen)
5187{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005188 WepKeyRid wkr;
5189 int rc;
Al Viro4293ea32007-12-19 19:21:51 -05005190 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005191
5192 rc = readWepKeyRid(ai, &wkr, 1, 1);
Dan Williamsc0380692009-01-24 09:12:15 -05005193 if (rc != SUCCESS)
5194 return -1;
5195 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005196 lastindex = wkr.kindex;
Dan Williamsc0380692009-01-24 09:12:15 -05005197 if (le16_to_cpu(wkr.kindex) == index) {
5198 int klen = min_t(int, buflen, le16_to_cpu(wkr.klen));
5199 memcpy(buf, wkr.key, klen);
5200 return klen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005201 }
Dan Williamsc0380692009-01-24 09:12:15 -05005202 rc = readWepKeyRid(ai, &wkr, 0, 1);
5203 if (rc != SUCCESS)
5204 return -1;
Al Viro4293ea32007-12-19 19:21:51 -05005205 } while (lastindex != wkr.kindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005206 return -1;
5207}
5208
Dan Williamsc0380692009-01-24 09:12:15 -05005209static int get_wep_tx_idx(struct airo_info *ai)
5210{
5211 WepKeyRid wkr;
5212 int rc;
5213 __le16 lastindex;
5214
5215 rc = readWepKeyRid(ai, &wkr, 1, 1);
5216 if (rc != SUCCESS)
5217 return -1;
5218 do {
5219 lastindex = wkr.kindex;
5220 if (wkr.kindex == cpu_to_le16(0xffff))
5221 return wkr.mac[0];
5222 rc = readWepKeyRid(ai, &wkr, 0, 1);
5223 if (rc != SUCCESS)
5224 return -1;
5225 } while (lastindex != wkr.kindex);
5226 return -1;
5227}
5228
5229static int set_wep_key(struct airo_info *ai, u16 index, const char *key,
5230 u16 keylen, int perm, int lock)
Al Viro4293ea32007-12-19 19:21:51 -05005231{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005232 static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
5233 WepKeyRid wkr;
Dan Williamsc0380692009-01-24 09:12:15 -05005234 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005235
Linus Torvalds1da177e2005-04-16 15:20:36 -07005236 if (keylen == 0) {
Dan Williamsc0380692009-01-24 09:12:15 -05005237 airo_print_err(ai->dev->name, "%s: key length to set was zero",
5238 __func__);
5239 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005240 }
5241
Dan Williamsc0380692009-01-24 09:12:15 -05005242 memset(&wkr, 0, sizeof(wkr));
5243 wkr.len = cpu_to_le16(sizeof(wkr));
5244 wkr.kindex = cpu_to_le16(index);
5245 wkr.klen = cpu_to_le16(keylen);
5246 memcpy(wkr.key, key, keylen);
5247 memcpy(wkr.mac, macaddr, ETH_ALEN);
5248
Dan Streetmanf89b2322005-11-11 11:41:42 -05005249 if (perm) disable_MAC(ai, lock);
Dan Williamsc0380692009-01-24 09:12:15 -05005250 rc = writeWepKeyRid(ai, &wkr, perm, lock);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005251 if (perm) enable_MAC(ai, lock);
Dan Williamsc0380692009-01-24 09:12:15 -05005252 return rc;
5253}
5254
5255static int set_wep_tx_idx(struct airo_info *ai, u16 index, int perm, int lock)
5256{
5257 WepKeyRid wkr;
5258 int rc;
5259
5260 memset(&wkr, 0, sizeof(wkr));
5261 wkr.len = cpu_to_le16(sizeof(wkr));
5262 wkr.kindex = cpu_to_le16(0xffff);
5263 wkr.mac[0] = (char)index;
5264
5265 if (perm) {
5266 ai->defindex = (char)index;
5267 disable_MAC(ai, lock);
5268 }
5269
5270 rc = writeWepKeyRid(ai, &wkr, perm, lock);
5271
5272 if (perm)
5273 enable_MAC(ai, lock);
5274 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005275}
5276
5277static void proc_wepkey_on_close( struct inode *inode, struct file *file ) {
5278 struct proc_data *data;
5279 struct proc_dir_entry *dp = PDE(inode);
5280 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005281 struct airo_info *ai = dev->ml_priv;
Dan Williamsc0380692009-01-24 09:12:15 -05005282 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005283 char key[16];
5284 u16 index = 0;
5285 int j = 0;
5286
5287 memset(key, 0, sizeof(key));
5288
5289 data = (struct proc_data *)file->private_data;
5290 if ( !data->writelen ) return;
5291
5292 if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' &&
5293 (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) {
5294 index = data->wbuffer[0] - '0';
5295 if (data->wbuffer[1] == '\n') {
Dan Williamsc0380692009-01-24 09:12:15 -05005296 rc = set_wep_tx_idx(ai, index, 1, 1);
5297 if (rc < 0) {
5298 airo_print_err(ai->dev->name, "failed to set "
5299 "WEP transmit index to %d: %d.",
5300 index, rc);
5301 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005302 return;
5303 }
5304 j = 2;
5305 } else {
Dan Williams934d8bf2006-03-16 13:46:23 -05005306 airo_print_err(ai->dev->name, "WepKey passed invalid key index");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005307 return;
5308 }
5309
5310 for( i = 0; i < 16*3 && data->wbuffer[i+j]; i++ ) {
5311 switch(i%3) {
5312 case 0:
5313 key[i/3] = hexVal(data->wbuffer[i+j])<<4;
5314 break;
5315 case 1:
5316 key[i/3] |= hexVal(data->wbuffer[i+j]);
5317 break;
5318 }
5319 }
Dan Williamsc0380692009-01-24 09:12:15 -05005320
5321 rc = set_wep_key(ai, index, key, i/3, 1, 1);
5322 if (rc < 0) {
5323 airo_print_err(ai->dev->name, "failed to set WEP key at index "
5324 "%d: %d.", index, rc);
5325 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005326}
5327
Al Viro4293ea32007-12-19 19:21:51 -05005328static int proc_wepkey_open( struct inode *inode, struct file *file )
5329{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005330 struct proc_data *data;
5331 struct proc_dir_entry *dp = PDE(inode);
5332 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005333 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005334 char *ptr;
5335 WepKeyRid wkr;
Al Viro4293ea32007-12-19 19:21:51 -05005336 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005337 int j=0;
5338 int rc;
5339
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005340 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005341 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005342 memset(&wkr, 0, sizeof(wkr));
5343 data = (struct proc_data *)file->private_data;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005344 if ((data->rbuffer = kzalloc( 180, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005345 kfree (file->private_data);
5346 return -ENOMEM;
5347 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005348 data->writelen = 0;
5349 data->maxwritelen = 80;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005350 if ((data->wbuffer = kzalloc( 80, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005351 kfree (data->rbuffer);
5352 kfree (file->private_data);
5353 return -ENOMEM;
5354 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005355 data->on_close = proc_wepkey_on_close;
5356
5357 ptr = data->rbuffer;
5358 strcpy(ptr, "No wep keys\n");
5359 rc = readWepKeyRid(ai, &wkr, 1, 1);
5360 if (rc == SUCCESS) do {
5361 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05005362 if (wkr.kindex == cpu_to_le16(0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005363 j += sprintf(ptr+j, "Tx key = %d\n",
5364 (int)wkr.mac[0]);
5365 } else {
5366 j += sprintf(ptr+j, "Key %d set with length = %d\n",
Al Viro4293ea32007-12-19 19:21:51 -05005367 le16_to_cpu(wkr.kindex),
5368 le16_to_cpu(wkr.klen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005369 }
5370 readWepKeyRid(ai, &wkr, 0, 1);
5371 } while((lastindex != wkr.kindex) && (j < 180-30));
5372
5373 data->readlen = strlen( data->rbuffer );
5374 return 0;
5375}
5376
Al Viro0dd22122007-12-17 16:11:57 -05005377static int proc_SSID_open(struct inode *inode, struct file *file)
5378{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005379 struct proc_data *data;
5380 struct proc_dir_entry *dp = PDE(inode);
5381 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005382 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005383 int i;
5384 char *ptr;
5385 SsidRid SSID_rid;
5386
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005387 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005388 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005389 data = (struct proc_data *)file->private_data;
5390 if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
5391 kfree (file->private_data);
5392 return -ENOMEM;
5393 }
5394 data->writelen = 0;
5395 data->maxwritelen = 33*3;
Al Viro0dd22122007-12-17 16:11:57 -05005396 /* allocate maxwritelen + 1; we'll want a sentinel */
5397 if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005398 kfree (data->rbuffer);
5399 kfree (file->private_data);
5400 return -ENOMEM;
5401 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005402 data->on_close = proc_SSID_on_close;
5403
5404 readSsidRid(ai, &SSID_rid);
5405 ptr = data->rbuffer;
Al Viro0dd22122007-12-17 16:11:57 -05005406 for (i = 0; i < 3; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005407 int j;
Al Viro0dd22122007-12-17 16:11:57 -05005408 size_t len = le16_to_cpu(SSID_rid.ssids[i].len);
5409 if (!len)
5410 break;
5411 if (len > 32)
5412 len = 32;
5413 for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005414 *ptr++ = SSID_rid.ssids[i].ssid[j];
Linus Torvalds1da177e2005-04-16 15:20:36 -07005415 *ptr++ = '\n';
5416 }
5417 *ptr = '\0';
5418 data->readlen = strlen( data->rbuffer );
5419 return 0;
5420}
5421
5422static int proc_APList_open( struct inode *inode, struct file *file ) {
5423 struct proc_data *data;
5424 struct proc_dir_entry *dp = PDE(inode);
5425 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005426 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005427 int i;
5428 char *ptr;
5429 APListRid APList_rid;
5430
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005431 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005432 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005433 data = (struct proc_data *)file->private_data;
5434 if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
5435 kfree (file->private_data);
5436 return -ENOMEM;
5437 }
5438 data->writelen = 0;
5439 data->maxwritelen = 4*6*3;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005440 if ((data->wbuffer = kzalloc( data->maxwritelen, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005441 kfree (data->rbuffer);
5442 kfree (file->private_data);
5443 return -ENOMEM;
5444 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005445 data->on_close = proc_APList_on_close;
5446
5447 readAPListRid(ai, &APList_rid);
5448 ptr = data->rbuffer;
5449 for( i = 0; i < 4; i++ ) {
5450// We end when we find a zero MAC
5451 if ( !*(int*)APList_rid.ap[i] &&
5452 !*(int*)&APList_rid.ap[i][2]) break;
Johannes Berge1749612008-10-27 15:59:26 -07005453 ptr += sprintf(ptr, "%pM\n", APList_rid.ap[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005454 }
5455 if (i==0) ptr += sprintf(ptr, "Not using specific APs\n");
5456
5457 *ptr = '\0';
5458 data->readlen = strlen( data->rbuffer );
5459 return 0;
5460}
5461
5462static int proc_BSSList_open( struct inode *inode, struct file *file ) {
5463 struct proc_data *data;
5464 struct proc_dir_entry *dp = PDE(inode);
5465 struct net_device *dev = dp->data;
Wang Chenfaf39942008-10-14 13:30:33 +08005466 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005467 char *ptr;
5468 BSSListRid BSSList_rid;
5469 int rc;
5470 /* If doLoseSync is not 1, we won't do a Lose Sync */
5471 int doLoseSync = -1;
5472
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005473 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005474 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005475 data = (struct proc_data *)file->private_data;
5476 if ((data->rbuffer = kmalloc( 1024, GFP_KERNEL )) == NULL) {
5477 kfree (file->private_data);
5478 return -ENOMEM;
5479 }
5480 data->writelen = 0;
5481 data->maxwritelen = 0;
5482 data->wbuffer = NULL;
5483 data->on_close = NULL;
5484
5485 if (file->f_mode & FMODE_WRITE) {
5486 if (!(file->f_mode & FMODE_READ)) {
5487 Cmd cmd;
5488 Resp rsp;
5489
5490 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
5491 memset(&cmd, 0, sizeof(cmd));
5492 cmd.cmd=CMD_LISTBSS;
5493 if (down_interruptible(&ai->sem))
5494 return -ERESTARTSYS;
5495 issuecommand(ai, &cmd, &rsp);
5496 up(&ai->sem);
5497 data->readlen = 0;
5498 return 0;
5499 }
5500 doLoseSync = 1;
5501 }
5502 ptr = data->rbuffer;
5503 /* There is a race condition here if there are concurrent opens.
5504 Since it is a rare condition, we'll just live with it, otherwise
5505 we have to add a spin lock... */
5506 rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
Al Viro17e70492007-12-19 18:56:37 -05005507 while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
Johannes Berge1749612008-10-27 15:59:26 -07005508 ptr += sprintf(ptr, "%pM %*s rssi = %d",
5509 BSSList_rid.bssid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005510 (int)BSSList_rid.ssidLen,
5511 BSSList_rid.ssid,
Al Viro17e70492007-12-19 18:56:37 -05005512 le16_to_cpu(BSSList_rid.dBm));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005513 ptr += sprintf(ptr, " channel = %d %s %s %s %s\n",
Al Viro17e70492007-12-19 18:56:37 -05005514 le16_to_cpu(BSSList_rid.dsChannel),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005515 BSSList_rid.cap & CAP_ESS ? "ESS" : "",
5516 BSSList_rid.cap & CAP_IBSS ? "adhoc" : "",
5517 BSSList_rid.cap & CAP_PRIVACY ? "wep" : "",
5518 BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : "");
5519 rc = readBSSListRid(ai, 0, &BSSList_rid);
5520 }
5521 *ptr = '\0';
5522 data->readlen = strlen( data->rbuffer );
5523 return 0;
5524}
5525
5526static int proc_close( struct inode *inode, struct file *file )
5527{
Jesper Juhlb4558ea2005-10-28 16:53:13 -04005528 struct proc_data *data = file->private_data;
5529
5530 if (data->on_close != NULL)
5531 data->on_close(inode, file);
5532 kfree(data->rbuffer);
5533 kfree(data->wbuffer);
5534 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005535 return 0;
5536}
5537
Linus Torvalds1da177e2005-04-16 15:20:36 -07005538/* Since the card doesn't automatically switch to the right WEP mode,
5539 we will make it do it. If the card isn't associated, every secs we
5540 will switch WEP modes to see if that will help. If the card is
5541 associated we will check every minute to see if anything has
5542 changed. */
5543static void timer_func( struct net_device *dev ) {
Wang Chenfaf39942008-10-14 13:30:33 +08005544 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005545
5546/* We don't have a link so try changing the authtype */
5547 readConfigRid(apriv, 0);
5548 disable_MAC(apriv, 0);
5549 switch(apriv->config.authType) {
5550 case AUTH_ENCRYPT:
5551/* So drop to OPEN */
5552 apriv->config.authType = AUTH_OPEN;
5553 break;
5554 case AUTH_SHAREDKEY:
5555 if (apriv->keyindex < auto_wep) {
Dan Williamsc0380692009-01-24 09:12:15 -05005556 set_wep_tx_idx(apriv, apriv->keyindex, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005557 apriv->config.authType = AUTH_SHAREDKEY;
5558 apriv->keyindex++;
5559 } else {
5560 /* Drop to ENCRYPT */
5561 apriv->keyindex = 0;
Dan Williamsc0380692009-01-24 09:12:15 -05005562 set_wep_tx_idx(apriv, apriv->defindex, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005563 apriv->config.authType = AUTH_ENCRYPT;
5564 }
5565 break;
5566 default: /* We'll escalate to SHAREDKEY */
5567 apriv->config.authType = AUTH_SHAREDKEY;
5568 }
5569 set_bit (FLAG_COMMIT, &apriv->flags);
5570 writeConfigRid(apriv, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005571 enable_MAC(apriv, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005572 up(&apriv->sem);
5573
5574/* Schedule check to see if the change worked */
Dan Williams3c304952006-04-15 12:26:18 -04005575 clear_bit(JOB_AUTOWEP, &apriv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005576 apriv->expires = RUN_AT(HZ*3);
5577}
5578
Linus Torvalds1da177e2005-04-16 15:20:36 -07005579#ifdef CONFIG_PCI
5580static int __devinit airo_pci_probe(struct pci_dev *pdev,
5581 const struct pci_device_id *pent)
5582{
5583 struct net_device *dev;
5584
5585 if (pci_enable_device(pdev))
5586 return -ENODEV;
5587 pci_set_master(pdev);
5588
5589 if (pdev->device == 0x5000 || pdev->device == 0xa504)
5590 dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev);
5591 else
5592 dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev);
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005593 if (!dev) {
5594 pci_disable_device(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005595 return -ENODEV;
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005596 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005597
5598 pci_set_drvdata(pdev, dev);
5599 return 0;
5600}
5601
5602static void __devexit airo_pci_remove(struct pci_dev *pdev)
5603{
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01005604 struct net_device *dev = pci_get_drvdata(pdev);
5605
5606 airo_print_info(dev->name, "Unregistering...");
5607 stop_airo_card(dev, 1);
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005608 pci_disable_device(pdev);
5609 pci_set_drvdata(pdev, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005610}
5611
Pavel Machek05adc3b2005-04-16 15:25:25 -07005612static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005613{
5614 struct net_device *dev = pci_get_drvdata(pdev);
Wang Chenfaf39942008-10-14 13:30:33 +08005615 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005616 Cmd cmd;
5617 Resp rsp;
5618
Pavel Macheke292c732008-06-25 12:25:53 +02005619 if (!ai->APList)
5620 ai->APList = kmalloc(sizeof(APListRid), GFP_KERNEL);
5621 if (!ai->APList)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005622 return -ENOMEM;
Pavel Macheke292c732008-06-25 12:25:53 +02005623 if (!ai->SSID)
5624 ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL);
5625 if (!ai->SSID)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005626 return -ENOMEM;
5627 readAPListRid(ai, ai->APList);
5628 readSsidRid(ai, ai->SSID);
5629 memset(&cmd, 0, sizeof(cmd));
5630 /* the lock will be released at the end of the resume callback */
5631 if (down_interruptible(&ai->sem))
5632 return -EAGAIN;
5633 disable_MAC(ai, 0);
5634 netif_device_detach(dev);
5635 ai->power = state;
Pavel Macheke292c732008-06-25 12:25:53 +02005636 cmd.cmd = HOSTSLEEP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005637 issuecommand(ai, &cmd, &rsp);
5638
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005639 pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005640 pci_save_state(pdev);
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005641 return pci_set_power_state(pdev, pci_choose_state(pdev, state));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005642}
5643
5644static int airo_pci_resume(struct pci_dev *pdev)
5645{
5646 struct net_device *dev = pci_get_drvdata(pdev);
Wang Chenfaf39942008-10-14 13:30:33 +08005647 struct airo_info *ai = dev->ml_priv;
Michal Schmidt53232802005-10-04 07:46:21 -04005648 pci_power_t prev_state = pdev->current_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005649
Michal Schmidt53232802005-10-04 07:46:21 -04005650 pci_set_power_state(pdev, PCI_D0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005651 pci_restore_state(pdev);
Michal Schmidt53232802005-10-04 07:46:21 -04005652 pci_enable_wake(pdev, PCI_D0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005653
Michal Schmidt53232802005-10-04 07:46:21 -04005654 if (prev_state != PCI_D1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005655 reset_card(dev, 0);
5656 mpi_init_descriptors(ai);
5657 setup_card(ai, dev->dev_addr, 0);
5658 clear_bit(FLAG_RADIO_OFF, &ai->flags);
5659 clear_bit(FLAG_PENDING_XMIT, &ai->flags);
5660 } else {
5661 OUT4500(ai, EVACK, EV_AWAKEN);
5662 OUT4500(ai, EVACK, EV_AWAKEN);
5663 msleep(100);
5664 }
5665
Pavel Macheke292c732008-06-25 12:25:53 +02005666 set_bit(FLAG_COMMIT, &ai->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005667 disable_MAC(ai, 0);
5668 msleep(200);
5669 if (ai->SSID) {
5670 writeSsidRid(ai, ai->SSID, 0);
5671 kfree(ai->SSID);
5672 ai->SSID = NULL;
5673 }
5674 if (ai->APList) {
5675 writeAPListRid(ai, ai->APList, 0);
5676 kfree(ai->APList);
5677 ai->APList = NULL;
5678 }
5679 writeConfigRid(ai, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005680 enable_MAC(ai, 0);
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005681 ai->power = PMSG_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005682 netif_device_attach(dev);
5683 netif_wake_queue(dev);
5684 enable_interrupts(ai);
5685 up(&ai->sem);
5686 return 0;
5687}
5688#endif
5689
5690static int __init airo_init_module( void )
5691{
Jeff Garzikde897882006-10-01 07:31:09 -04005692 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005693
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005694 airo_entry = create_proc_entry("driver/aironet",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005695 S_IFDIR | airo_perm,
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005696 NULL);
Jeff Garzikde897882006-10-01 07:31:09 -04005697
5698 if (airo_entry) {
5699 airo_entry->uid = proc_uid;
5700 airo_entry->gid = proc_gid;
5701 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005702
Pavel Macheke292c732008-06-25 12:25:53 +02005703 for (i = 0; i < 4 && io[i] && irq[i]; i++) {
Dan Williams934d8bf2006-03-16 13:46:23 -05005704 airo_print_info("", "Trying to configure ISA adapter at irq=%d "
5705 "io=0x%x", irq[i], io[i] );
Linus Torvalds1da177e2005-04-16 15:20:36 -07005706 if (init_airo_card( irq[i], io[i], 0, NULL ))
Jeff Garzikde897882006-10-01 07:31:09 -04005707 /* do nothing */ ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005708 }
5709
5710#ifdef CONFIG_PCI
Dan Williams934d8bf2006-03-16 13:46:23 -05005711 airo_print_info("", "Probing for PCI adapters");
Jeff Garzikde897882006-10-01 07:31:09 -04005712 i = pci_register_driver(&airo_driver);
Dan Williams934d8bf2006-03-16 13:46:23 -05005713 airo_print_info("", "Finished probing for PCI adapters");
Jeff Garzikde897882006-10-01 07:31:09 -04005714
5715 if (i) {
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005716 remove_proc_entry("driver/aironet", NULL);
Jeff Garzikde897882006-10-01 07:31:09 -04005717 return i;
5718 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005719#endif
5720
5721 /* Always exit with success, as we are a library module
5722 * as well as a driver module
5723 */
5724 return 0;
5725}
5726
5727static void __exit airo_cleanup_module( void )
5728{
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01005729 struct airo_info *ai;
5730 while(!list_empty(&airo_devices)) {
5731 ai = list_entry(airo_devices.next, struct airo_info, dev_list);
5732 airo_print_info(ai->dev->name, "Unregistering...");
5733 stop_airo_card(ai->dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005734 }
5735#ifdef CONFIG_PCI
5736 pci_unregister_driver(&airo_driver);
5737#endif
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005738 remove_proc_entry("driver/aironet", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005739}
5740
Linus Torvalds1da177e2005-04-16 15:20:36 -07005741/*
5742 * Initial Wireless Extension code for Aironet driver by :
5743 * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00
5744 * Conversion to new driver API by :
5745 * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02
5746 * Javier also did a good amount of work here, adding some new extensions
5747 * and fixing my code. Let's just say that without him this code just
5748 * would not work at all... - Jean II
5749 */
5750
Dan Williams41480af2005-05-10 09:45:51 -04005751static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi)
5752{
Pavel Macheke292c732008-06-25 12:25:53 +02005753 if (!rssi_rid)
Dan Williams41480af2005-05-10 09:45:51 -04005754 return 0;
5755
5756 return (0x100 - rssi_rid[rssi].rssidBm);
5757}
5758
5759static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm)
5760{
5761 int i;
5762
Pavel Macheke292c732008-06-25 12:25:53 +02005763 if (!rssi_rid)
Dan Williams41480af2005-05-10 09:45:51 -04005764 return 0;
5765
Pavel Macheke292c732008-06-25 12:25:53 +02005766 for (i = 0; i < 256; i++)
Dan Williams41480af2005-05-10 09:45:51 -04005767 if (rssi_rid[i].rssidBm == dbm)
5768 return rssi_rid[i].rssipct;
5769
5770 return 0;
5771}
5772
5773
Linus Torvalds1da177e2005-04-16 15:20:36 -07005774static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid)
5775{
5776 int quality = 0;
Al Viro329e2c02007-12-20 22:58:57 -05005777 u16 sq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005778
Al Viro329e2c02007-12-20 22:58:57 -05005779 if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f))
Al Viro56d81bd2007-12-20 17:18:35 -05005780 return 0;
5781
5782 if (!(cap_rid->hardCap & cpu_to_le16(8)))
5783 return 0;
5784
Al Viro329e2c02007-12-20 22:58:57 -05005785 sq = le16_to_cpu(status_rid->signalQuality);
Al Viro56d81bd2007-12-20 17:18:35 -05005786 if (memcmp(cap_rid->prodName, "350", 3))
Al Viro329e2c02007-12-20 22:58:57 -05005787 if (sq > 0x20)
Al Viro56d81bd2007-12-20 17:18:35 -05005788 quality = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005789 else
Al Viro329e2c02007-12-20 22:58:57 -05005790 quality = 0x20 - sq;
Al Viro56d81bd2007-12-20 17:18:35 -05005791 else
Al Viro329e2c02007-12-20 22:58:57 -05005792 if (sq > 0xb0)
Al Viro56d81bd2007-12-20 17:18:35 -05005793 quality = 0;
Al Viro329e2c02007-12-20 22:58:57 -05005794 else if (sq < 0x10)
Al Viro56d81bd2007-12-20 17:18:35 -05005795 quality = 0xa0;
5796 else
Al Viro329e2c02007-12-20 22:58:57 -05005797 quality = 0xb0 - sq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005798 return quality;
5799}
5800
5801#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0)
5802#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50);
5803
5804/*------------------------------------------------------------------*/
5805/*
5806 * Wireless Handler : get protocol name
5807 */
5808static int airo_get_name(struct net_device *dev,
5809 struct iw_request_info *info,
5810 char *cwrq,
5811 char *extra)
5812{
5813 strcpy(cwrq, "IEEE 802.11-DS");
5814 return 0;
5815}
5816
5817/*------------------------------------------------------------------*/
5818/*
5819 * Wireless Handler : set frequency
5820 */
5821static int airo_set_freq(struct net_device *dev,
5822 struct iw_request_info *info,
5823 struct iw_freq *fwrq,
5824 char *extra)
5825{
Wang Chenfaf39942008-10-14 13:30:33 +08005826 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005827 int rc = -EINPROGRESS; /* Call commit handler */
5828
5829 /* If setting by frequency, convert to a channel */
David Kilroy9ee677c2008-12-23 14:03:38 +00005830 if(fwrq->e == 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005831 int f = fwrq->m / 100000;
David Kilroy9ee677c2008-12-23 14:03:38 +00005832
Linus Torvalds1da177e2005-04-16 15:20:36 -07005833 /* Hack to fall through... */
5834 fwrq->e = 0;
David Kilroy9ee677c2008-12-23 14:03:38 +00005835 fwrq->m = ieee80211_freq_to_dsss_chan(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005836 }
5837 /* Setting by channel number */
5838 if((fwrq->m > 1000) || (fwrq->e > 0))
5839 rc = -EOPNOTSUPP;
5840 else {
5841 int channel = fwrq->m;
5842 /* We should do a better check than that,
5843 * based on the card capability !!! */
Javier Achirica2610c732006-01-17 08:01:01 -05005844 if((channel < 1) || (channel > 14)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05005845 airo_print_dbg(dev->name, "New channel value of %d is invalid!",
5846 fwrq->m);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005847 rc = -EINVAL;
5848 } else {
5849 readConfigRid(local, 1);
5850 /* Yes ! We can set it !!! */
Al Viro3eb9b412007-12-21 00:00:35 -05005851 local->config.channelSet = cpu_to_le16(channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005852 set_bit (FLAG_COMMIT, &local->flags);
5853 }
5854 }
5855 return rc;
5856}
5857
5858/*------------------------------------------------------------------*/
5859/*
5860 * Wireless Handler : get frequency
5861 */
5862static int airo_get_freq(struct net_device *dev,
5863 struct iw_request_info *info,
5864 struct iw_freq *fwrq,
5865 char *extra)
5866{
Wang Chenfaf39942008-10-14 13:30:33 +08005867 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005868 StatusRid status_rid; /* Card status info */
Javier Achirica2610c732006-01-17 08:01:01 -05005869 int ch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005870
5871 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05005872 if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS)
5873 status_rid.channel = local->config.channelSet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005874 else
5875 readStatusRid(local, &status_rid, 1);
5876
Al Viro329e2c02007-12-20 22:58:57 -05005877 ch = le16_to_cpu(status_rid.channel);
Javier Achirica2610c732006-01-17 08:01:01 -05005878 if((ch > 0) && (ch < 15)) {
David Kilroy9ee677c2008-12-23 14:03:38 +00005879 fwrq->m = ieee80211_dsss_chan_to_freq(ch) * 100000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005880 fwrq->e = 1;
Javier Achirica2610c732006-01-17 08:01:01 -05005881 } else {
5882 fwrq->m = ch;
5883 fwrq->e = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005884 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005885
5886 return 0;
5887}
5888
5889/*------------------------------------------------------------------*/
5890/*
5891 * Wireless Handler : set ESSID
5892 */
5893static int airo_set_essid(struct net_device *dev,
5894 struct iw_request_info *info,
5895 struct iw_point *dwrq,
5896 char *extra)
5897{
Wang Chenfaf39942008-10-14 13:30:33 +08005898 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005899 SsidRid SSID_rid; /* SSIDs */
5900
5901 /* Reload the list of current SSID */
5902 readSsidRid(local, &SSID_rid);
5903
5904 /* Check if we asked for `any' */
5905 if(dwrq->flags == 0) {
5906 /* Just send an empty SSID list */
5907 memset(&SSID_rid, 0, sizeof(SSID_rid));
5908 } else {
5909 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
5910
5911 /* Check the size of the string */
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07005912 if(dwrq->length > IW_ESSID_MAX_SIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005913 return -E2BIG ;
5914 }
5915 /* Check if index is valid */
5916 if((index < 0) || (index >= 4)) {
5917 return -EINVAL;
5918 }
5919
5920 /* Set the SSID */
5921 memset(SSID_rid.ssids[index].ssid, 0,
5922 sizeof(SSID_rid.ssids[index].ssid));
5923 memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length);
Al Viro0dd22122007-12-17 16:11:57 -05005924 SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005925 }
Al Viro0dd22122007-12-17 16:11:57 -05005926 SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005927 /* Write it to the card */
5928 disable_MAC(local, 1);
5929 writeSsidRid(local, &SSID_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005930 enable_MAC(local, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005931
5932 return 0;
5933}
5934
5935/*------------------------------------------------------------------*/
5936/*
5937 * Wireless Handler : get ESSID
5938 */
5939static int airo_get_essid(struct net_device *dev,
5940 struct iw_request_info *info,
5941 struct iw_point *dwrq,
5942 char *extra)
5943{
Wang Chenfaf39942008-10-14 13:30:33 +08005944 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005945 StatusRid status_rid; /* Card status info */
5946
5947 readStatusRid(local, &status_rid, 1);
5948
5949 /* Note : if dwrq->flags != 0, we should
5950 * get the relevant SSID from the SSID list... */
5951
5952 /* Get the current SSID */
Al Viro329e2c02007-12-20 22:58:57 -05005953 memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005954 /* If none, we may want to get the one that was set */
5955
5956 /* Push it out ! */
Al Viro329e2c02007-12-20 22:58:57 -05005957 dwrq->length = le16_to_cpu(status_rid.SSIDlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005958 dwrq->flags = 1; /* active */
5959
5960 return 0;
5961}
5962
5963/*------------------------------------------------------------------*/
5964/*
5965 * Wireless Handler : set AP address
5966 */
5967static int airo_set_wap(struct net_device *dev,
5968 struct iw_request_info *info,
5969 struct sockaddr *awrq,
5970 char *extra)
5971{
Wang Chenfaf39942008-10-14 13:30:33 +08005972 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005973 Cmd cmd;
5974 Resp rsp;
5975 APListRid APList_rid;
Dan Williams4be757d2006-01-30 11:58:00 -05005976 static const u8 any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
5977 static const u8 off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07005978
5979 if (awrq->sa_family != ARPHRD_ETHER)
5980 return -EINVAL;
Dan Williams4be757d2006-01-30 11:58:00 -05005981 else if (!memcmp(any, awrq->sa_data, ETH_ALEN) ||
5982 !memcmp(off, awrq->sa_data, ETH_ALEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005983 memset(&cmd, 0, sizeof(cmd));
5984 cmd.cmd=CMD_LOSE_SYNC;
5985 if (down_interruptible(&local->sem))
5986 return -ERESTARTSYS;
5987 issuecommand(local, &cmd, &rsp);
5988 up(&local->sem);
5989 } else {
5990 memset(&APList_rid, 0, sizeof(APList_rid));
Al Viroa7497162007-12-20 17:49:41 -05005991 APList_rid.len = cpu_to_le16(sizeof(APList_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005992 memcpy(APList_rid.ap[0], awrq->sa_data, ETH_ALEN);
5993 disable_MAC(local, 1);
5994 writeAPListRid(local, &APList_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005995 enable_MAC(local, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005996 }
5997 return 0;
5998}
5999
6000/*------------------------------------------------------------------*/
6001/*
6002 * Wireless Handler : get AP address
6003 */
6004static int airo_get_wap(struct net_device *dev,
6005 struct iw_request_info *info,
6006 struct sockaddr *awrq,
6007 char *extra)
6008{
Wang Chenfaf39942008-10-14 13:30:33 +08006009 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006010 StatusRid status_rid; /* Card status info */
6011
6012 readStatusRid(local, &status_rid, 1);
6013
6014 /* Tentative. This seems to work, wow, I'm lucky !!! */
6015 memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN);
6016 awrq->sa_family = ARPHRD_ETHER;
6017
6018 return 0;
6019}
6020
6021/*------------------------------------------------------------------*/
6022/*
6023 * Wireless Handler : set Nickname
6024 */
6025static int airo_set_nick(struct net_device *dev,
6026 struct iw_request_info *info,
6027 struct iw_point *dwrq,
6028 char *extra)
6029{
Wang Chenfaf39942008-10-14 13:30:33 +08006030 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006031
6032 /* Check the size of the string */
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006033 if(dwrq->length > 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006034 return -E2BIG;
6035 }
6036 readConfigRid(local, 1);
6037 memset(local->config.nodeName, 0, sizeof(local->config.nodeName));
6038 memcpy(local->config.nodeName, extra, dwrq->length);
6039 set_bit (FLAG_COMMIT, &local->flags);
6040
6041 return -EINPROGRESS; /* Call commit handler */
6042}
6043
6044/*------------------------------------------------------------------*/
6045/*
6046 * Wireless Handler : get Nickname
6047 */
6048static int airo_get_nick(struct net_device *dev,
6049 struct iw_request_info *info,
6050 struct iw_point *dwrq,
6051 char *extra)
6052{
Wang Chenfaf39942008-10-14 13:30:33 +08006053 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006054
6055 readConfigRid(local, 1);
6056 strncpy(extra, local->config.nodeName, 16);
6057 extra[16] = '\0';
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006058 dwrq->length = strlen(extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006059
6060 return 0;
6061}
6062
6063/*------------------------------------------------------------------*/
6064/*
6065 * Wireless Handler : set Bit-Rate
6066 */
6067static int airo_set_rate(struct net_device *dev,
6068 struct iw_request_info *info,
6069 struct iw_param *vwrq,
6070 char *extra)
6071{
Wang Chenfaf39942008-10-14 13:30:33 +08006072 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006073 CapabilityRid cap_rid; /* Card capability info */
6074 u8 brate = 0;
6075 int i;
6076
6077 /* First : get a valid bit rate value */
6078 readCapabilityRid(local, &cap_rid, 1);
6079
6080 /* Which type of value ? */
6081 if((vwrq->value < 8) && (vwrq->value >= 0)) {
6082 /* Setting by rate index */
6083 /* Find value in the magic rate table */
6084 brate = cap_rid.supportedRates[vwrq->value];
6085 } else {
6086 /* Setting by frequency value */
6087 u8 normvalue = (u8) (vwrq->value/500000);
6088
6089 /* Check if rate is valid */
6090 for(i = 0 ; i < 8 ; i++) {
6091 if(normvalue == cap_rid.supportedRates[i]) {
6092 brate = normvalue;
6093 break;
6094 }
6095 }
6096 }
6097 /* -1 designed the max rate (mostly auto mode) */
6098 if(vwrq->value == -1) {
6099 /* Get the highest available rate */
6100 for(i = 0 ; i < 8 ; i++) {
6101 if(cap_rid.supportedRates[i] == 0)
6102 break;
6103 }
6104 if(i != 0)
6105 brate = cap_rid.supportedRates[i - 1];
6106 }
6107 /* Check that it is valid */
6108 if(brate == 0) {
6109 return -EINVAL;
6110 }
6111
6112 readConfigRid(local, 1);
6113 /* Now, check if we want a fixed or auto value */
6114 if(vwrq->fixed == 0) {
6115 /* Fill all the rates up to this max rate */
6116 memset(local->config.rates, 0, 8);
6117 for(i = 0 ; i < 8 ; i++) {
6118 local->config.rates[i] = cap_rid.supportedRates[i];
6119 if(local->config.rates[i] == brate)
6120 break;
6121 }
6122 } else {
6123 /* Fixed mode */
6124 /* One rate, fixed */
6125 memset(local->config.rates, 0, 8);
6126 local->config.rates[0] = brate;
6127 }
6128 set_bit (FLAG_COMMIT, &local->flags);
6129
6130 return -EINPROGRESS; /* Call commit handler */
6131}
6132
6133/*------------------------------------------------------------------*/
6134/*
6135 * Wireless Handler : get Bit-Rate
6136 */
6137static int airo_get_rate(struct net_device *dev,
6138 struct iw_request_info *info,
6139 struct iw_param *vwrq,
6140 char *extra)
6141{
Wang Chenfaf39942008-10-14 13:30:33 +08006142 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006143 StatusRid status_rid; /* Card status info */
6144
6145 readStatusRid(local, &status_rid, 1);
6146
Al Viro329e2c02007-12-20 22:58:57 -05006147 vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006148 /* If more than one rate, set auto */
6149 readConfigRid(local, 1);
6150 vwrq->fixed = (local->config.rates[1] == 0);
6151
6152 return 0;
6153}
6154
6155/*------------------------------------------------------------------*/
6156/*
6157 * Wireless Handler : set RTS threshold
6158 */
6159static int airo_set_rts(struct net_device *dev,
6160 struct iw_request_info *info,
6161 struct iw_param *vwrq,
6162 char *extra)
6163{
Wang Chenfaf39942008-10-14 13:30:33 +08006164 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006165 int rthr = vwrq->value;
6166
6167 if(vwrq->disabled)
Dan Williams15db2762006-03-16 13:46:27 -05006168 rthr = AIRO_DEF_MTU;
6169 if((rthr < 0) || (rthr > AIRO_DEF_MTU)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006170 return -EINVAL;
6171 }
6172 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006173 local->config.rtsThres = cpu_to_le16(rthr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006174 set_bit (FLAG_COMMIT, &local->flags);
6175
6176 return -EINPROGRESS; /* Call commit handler */
6177}
6178
6179/*------------------------------------------------------------------*/
6180/*
6181 * Wireless Handler : get RTS threshold
6182 */
6183static int airo_get_rts(struct net_device *dev,
6184 struct iw_request_info *info,
6185 struct iw_param *vwrq,
6186 char *extra)
6187{
Wang Chenfaf39942008-10-14 13:30:33 +08006188 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006189
6190 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006191 vwrq->value = le16_to_cpu(local->config.rtsThres);
Dan Williams15db2762006-03-16 13:46:27 -05006192 vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006193 vwrq->fixed = 1;
6194
6195 return 0;
6196}
6197
6198/*------------------------------------------------------------------*/
6199/*
6200 * Wireless Handler : set Fragmentation threshold
6201 */
6202static int airo_set_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 int fthr = vwrq->value;
6209
6210 if(vwrq->disabled)
Dan Williams15db2762006-03-16 13:46:27 -05006211 fthr = AIRO_DEF_MTU;
6212 if((fthr < 256) || (fthr > AIRO_DEF_MTU)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006213 return -EINVAL;
6214 }
6215 fthr &= ~0x1; /* Get an even value - is it really needed ??? */
6216 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006217 local->config.fragThresh = cpu_to_le16(fthr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006218 set_bit (FLAG_COMMIT, &local->flags);
6219
6220 return -EINPROGRESS; /* Call commit handler */
6221}
6222
6223/*------------------------------------------------------------------*/
6224/*
6225 * Wireless Handler : get Fragmentation threshold
6226 */
6227static int airo_get_frag(struct net_device *dev,
6228 struct iw_request_info *info,
6229 struct iw_param *vwrq,
6230 char *extra)
6231{
Wang Chenfaf39942008-10-14 13:30:33 +08006232 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006233
6234 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006235 vwrq->value = le16_to_cpu(local->config.fragThresh);
Dan Williams15db2762006-03-16 13:46:27 -05006236 vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006237 vwrq->fixed = 1;
6238
6239 return 0;
6240}
6241
6242/*------------------------------------------------------------------*/
6243/*
6244 * Wireless Handler : set Mode of Operation
6245 */
6246static int airo_set_mode(struct net_device *dev,
6247 struct iw_request_info *info,
6248 __u32 *uwrq,
6249 char *extra)
6250{
Wang Chenfaf39942008-10-14 13:30:33 +08006251 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006252 int reset = 0;
6253
6254 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006255 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006256 reset = 1;
6257
6258 switch(*uwrq) {
6259 case IW_MODE_ADHOC:
Al Viro3eb9b412007-12-21 00:00:35 -05006260 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006261 local->config.opmode |= MODE_STA_IBSS;
Al Viro3eb9b412007-12-21 00:00:35 -05006262 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006263 local->config.scanMode = SCANMODE_ACTIVE;
6264 clear_bit (FLAG_802_11, &local->flags);
6265 break;
6266 case IW_MODE_INFRA:
Al Viro3eb9b412007-12-21 00:00:35 -05006267 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006268 local->config.opmode |= MODE_STA_ESS;
Al Viro3eb9b412007-12-21 00:00:35 -05006269 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006270 local->config.scanMode = SCANMODE_ACTIVE;
6271 clear_bit (FLAG_802_11, &local->flags);
6272 break;
6273 case IW_MODE_MASTER:
Al Viro3eb9b412007-12-21 00:00:35 -05006274 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006275 local->config.opmode |= MODE_AP;
Al Viro3eb9b412007-12-21 00:00:35 -05006276 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006277 local->config.scanMode = SCANMODE_ACTIVE;
6278 clear_bit (FLAG_802_11, &local->flags);
6279 break;
6280 case IW_MODE_REPEAT:
Al Viro3eb9b412007-12-21 00:00:35 -05006281 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006282 local->config.opmode |= MODE_AP_RPTR;
Al Viro3eb9b412007-12-21 00:00:35 -05006283 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006284 local->config.scanMode = SCANMODE_ACTIVE;
6285 clear_bit (FLAG_802_11, &local->flags);
6286 break;
6287 case IW_MODE_MONITOR:
Al Viro3eb9b412007-12-21 00:00:35 -05006288 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006289 local->config.opmode |= MODE_STA_ESS;
Al Viro3eb9b412007-12-21 00:00:35 -05006290 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006291 local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
6292 local->config.scanMode = SCANMODE_PASSIVE;
6293 set_bit (FLAG_802_11, &local->flags);
6294 break;
6295 default:
6296 return -EINVAL;
6297 }
6298 if (reset)
6299 set_bit (FLAG_RESET, &local->flags);
6300 set_bit (FLAG_COMMIT, &local->flags);
6301
6302 return -EINPROGRESS; /* Call commit handler */
6303}
6304
6305/*------------------------------------------------------------------*/
6306/*
6307 * Wireless Handler : get Mode of Operation
6308 */
6309static int airo_get_mode(struct net_device *dev,
6310 struct iw_request_info *info,
6311 __u32 *uwrq,
6312 char *extra)
6313{
Wang Chenfaf39942008-10-14 13:30:33 +08006314 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006315
6316 readConfigRid(local, 1);
6317 /* If not managed, assume it's ad-hoc */
Al Viro3eb9b412007-12-21 00:00:35 -05006318 switch (local->config.opmode & MODE_CFG_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006319 case MODE_STA_ESS:
6320 *uwrq = IW_MODE_INFRA;
6321 break;
6322 case MODE_AP:
6323 *uwrq = IW_MODE_MASTER;
6324 break;
6325 case MODE_AP_RPTR:
6326 *uwrq = IW_MODE_REPEAT;
6327 break;
6328 default:
6329 *uwrq = IW_MODE_ADHOC;
6330 }
6331
6332 return 0;
6333}
6334
Dan Williams138c0c62009-01-24 09:11:35 -05006335static inline int valid_index(struct airo_info *ai, int index)
Al Viro56d81bd2007-12-20 17:18:35 -05006336{
Dan Williams138c0c62009-01-24 09:11:35 -05006337 return (index >= 0) && (index <= ai->max_wep_idx);
Al Viro56d81bd2007-12-20 17:18:35 -05006338}
6339
Linus Torvalds1da177e2005-04-16 15:20:36 -07006340/*------------------------------------------------------------------*/
6341/*
6342 * Wireless Handler : set Encryption Key
6343 */
6344static int airo_set_encode(struct net_device *dev,
6345 struct iw_request_info *info,
6346 struct iw_point *dwrq,
6347 char *extra)
6348{
Wang Chenfaf39942008-10-14 13:30:33 +08006349 struct airo_info *local = dev->ml_priv;
Dan Williamsc0380692009-01-24 09:12:15 -05006350 int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006351 __le16 currentAuthType = local->config.authType;
Dan Williamsc0380692009-01-24 09:12:15 -05006352 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006353
Dan Williams138c0c62009-01-24 09:11:35 -05006354 if (!local->wep_capable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006355 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006356
Linus Torvalds1da177e2005-04-16 15:20:36 -07006357 readConfigRid(local, 1);
6358
6359 /* Basic checking: do we have a key to set ?
6360 * Note : with the new API, it's impossible to get a NULL pointer.
6361 * Therefore, we need to check a key size == 0 instead.
6362 * New version of iwconfig properly set the IW_ENCODE_NOKEY flag
6363 * when no key is present (only change flags), but older versions
6364 * don't do it. - Jean II */
6365 if (dwrq->length > 0) {
6366 wep_key_t key;
6367 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Dan Williamsc0380692009-01-24 09:12:15 -05006368 int current_index;
Dan Williams138c0c62009-01-24 09:11:35 -05006369
Linus Torvalds1da177e2005-04-16 15:20:36 -07006370 /* Check the size of the key */
6371 if (dwrq->length > MAX_KEY_SIZE) {
6372 return -EINVAL;
6373 }
Dan Williams138c0c62009-01-24 09:11:35 -05006374
Dan Williamsc0380692009-01-24 09:12:15 -05006375 current_index = get_wep_tx_idx(local);
6376 if (current_index < 0)
6377 current_index = 0;
6378
Linus Torvalds1da177e2005-04-16 15:20:36 -07006379 /* Check the index (none -> use current) */
Dan Williams138c0c62009-01-24 09:11:35 -05006380 if (!valid_index(local, index))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006381 index = current_index;
Dan Williams138c0c62009-01-24 09:11:35 -05006382
Linus Torvalds1da177e2005-04-16 15:20:36 -07006383 /* Set the length */
6384 if (dwrq->length > MIN_KEY_SIZE)
6385 key.len = MAX_KEY_SIZE;
6386 else
6387 if (dwrq->length > 0)
6388 key.len = MIN_KEY_SIZE;
6389 else
6390 /* Disable the key */
6391 key.len = 0;
6392 /* Check if the key is not marked as invalid */
6393 if(!(dwrq->flags & IW_ENCODE_NOKEY)) {
6394 /* Cleanup */
6395 memset(key.key, 0, MAX_KEY_SIZE);
6396 /* Copy the key in the driver */
6397 memcpy(key.key, extra, dwrq->length);
6398 /* Send the key to the card */
Dan Williamsc0380692009-01-24 09:12:15 -05006399 rc = set_wep_key(local, index, key.key, key.len, perm, 1);
6400 if (rc < 0) {
6401 airo_print_err(local->dev->name, "failed to set"
6402 " WEP key at index %d: %d.",
6403 index, rc);
6404 return rc;
6405 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006406 }
6407 /* WE specify that if a valid key is set, encryption
6408 * should be enabled (user may turn it off later)
6409 * This is also how "iwconfig ethX key on" works */
6410 if((index == current_index) && (key.len > 0) &&
6411 (local->config.authType == AUTH_OPEN)) {
6412 local->config.authType = AUTH_ENCRYPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006413 }
6414 } else {
6415 /* Do we want to just set the transmit key index ? */
6416 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Dan Williamsc0380692009-01-24 09:12:15 -05006417 if (valid_index(local, index)) {
6418 rc = set_wep_tx_idx(local, index, perm, 1);
6419 if (rc < 0) {
6420 airo_print_err(local->dev->name, "failed to set"
6421 " WEP transmit index to %d: %d.",
6422 index, rc);
6423 return rc;
6424 }
6425 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006426 /* Don't complain if only change the mode */
Jeff Garzik93a3b602007-11-23 21:50:20 -05006427 if (!(dwrq->flags & IW_ENCODE_MODE))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006428 return -EINVAL;
Dan Williams138c0c62009-01-24 09:11:35 -05006429 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006430 }
6431 /* Read the flags */
6432 if(dwrq->flags & IW_ENCODE_DISABLED)
6433 local->config.authType = AUTH_OPEN; // disable encryption
6434 if(dwrq->flags & IW_ENCODE_RESTRICTED)
6435 local->config.authType = AUTH_SHAREDKEY; // Only Both
6436 if(dwrq->flags & IW_ENCODE_OPEN)
6437 local->config.authType = AUTH_ENCRYPT; // Only Wep
6438 /* Commit the changes to flags if needed */
Dan Streetmanf89b2322005-11-11 11:41:42 -05006439 if (local->config.authType != currentAuthType)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006440 set_bit (FLAG_COMMIT, &local->flags);
6441 return -EINPROGRESS; /* Call commit handler */
6442}
6443
6444/*------------------------------------------------------------------*/
6445/*
6446 * Wireless Handler : get Encryption Key
6447 */
6448static int airo_get_encode(struct net_device *dev,
6449 struct iw_request_info *info,
6450 struct iw_point *dwrq,
6451 char *extra)
6452{
Wang Chenfaf39942008-10-14 13:30:33 +08006453 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006454 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Dan Williamsc0380692009-01-24 09:12:15 -05006455 u8 buf[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -07006456
Dan Williams138c0c62009-01-24 09:11:35 -05006457 if (!local->wep_capable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006458 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006459
Linus Torvalds1da177e2005-04-16 15:20:36 -07006460 readConfigRid(local, 1);
Dan Williams138c0c62009-01-24 09:11:35 -05006461
Linus Torvalds1da177e2005-04-16 15:20:36 -07006462 /* Check encryption mode */
6463 switch(local->config.authType) {
6464 case AUTH_ENCRYPT:
6465 dwrq->flags = IW_ENCODE_OPEN;
6466 break;
6467 case AUTH_SHAREDKEY:
6468 dwrq->flags = IW_ENCODE_RESTRICTED;
6469 break;
6470 default:
6471 case AUTH_OPEN:
6472 dwrq->flags = IW_ENCODE_DISABLED;
6473 break;
6474 }
6475 /* We can't return the key, so set the proper flag and return zero */
6476 dwrq->flags |= IW_ENCODE_NOKEY;
6477 memset(extra, 0, 16);
6478
6479 /* Which key do we want ? -1 -> tx index */
Dan Williamsc0380692009-01-24 09:12:15 -05006480 if (!valid_index(local, index)) {
6481 index = get_wep_tx_idx(local);
6482 if (index < 0)
6483 index = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006484 }
Dan Williamsc0380692009-01-24 09:12:15 -05006485 dwrq->flags |= index + 1;
6486
6487 /* Copy the key to the user buffer */
6488 dwrq->length = get_wep_key(local, index, &buf[0], sizeof(buf));
6489 memcpy(extra, buf, dwrq->length);
6490
Linus Torvalds1da177e2005-04-16 15:20:36 -07006491 return 0;
6492}
6493
6494/*------------------------------------------------------------------*/
6495/*
Dan Williams4be757d2006-01-30 11:58:00 -05006496 * Wireless Handler : set extended Encryption parameters
6497 */
6498static int airo_set_encodeext(struct net_device *dev,
6499 struct iw_request_info *info,
6500 union iwreq_data *wrqu,
6501 char *extra)
6502{
Wang Chenfaf39942008-10-14 13:30:33 +08006503 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006504 struct iw_point *encoding = &wrqu->encoding;
6505 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
Dan Williams4be757d2006-01-30 11:58:00 -05006506 int perm = ( encoding->flags & IW_ENCODE_TEMP ? 0 : 1 );
Al Viro3eb9b412007-12-21 00:00:35 -05006507 __le16 currentAuthType = local->config.authType;
Dan Williamsc0380692009-01-24 09:12:15 -05006508 int idx, key_len, alg = ext->alg, set_key = 1, rc;
Dan Williams4be757d2006-01-30 11:58:00 -05006509 wep_key_t key;
6510
Dan Williams138c0c62009-01-24 09:11:35 -05006511 if (!local->wep_capable)
Dan Williams4be757d2006-01-30 11:58:00 -05006512 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006513
Dan Williams4be757d2006-01-30 11:58:00 -05006514 readConfigRid(local, 1);
6515
6516 /* Determine and validate the key index */
6517 idx = encoding->flags & IW_ENCODE_INDEX;
6518 if (idx) {
Dan Williams138c0c62009-01-24 09:11:35 -05006519 if (!valid_index(local, idx - 1))
Dan Williams4be757d2006-01-30 11:58:00 -05006520 return -EINVAL;
6521 idx--;
Dan Williamsc0380692009-01-24 09:12:15 -05006522 } else {
6523 idx = get_wep_tx_idx(local);
6524 if (idx < 0)
6525 idx = 0;
6526 }
Dan Williams4be757d2006-01-30 11:58:00 -05006527
6528 if (encoding->flags & IW_ENCODE_DISABLED)
6529 alg = IW_ENCODE_ALG_NONE;
6530
Dan Williams4be757d2006-01-30 11:58:00 -05006531 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
Dan Williams22d88462006-02-05 18:00:30 -05006532 /* Only set transmit key index here, actual
6533 * key is set below if needed.
6534 */
Dan Williamsc0380692009-01-24 09:12:15 -05006535 rc = set_wep_tx_idx(local, idx, perm, 1);
6536 if (rc < 0) {
6537 airo_print_err(local->dev->name, "failed to set "
6538 "WEP transmit index to %d: %d.",
6539 idx, rc);
6540 return rc;
6541 }
Dan Williams22d88462006-02-05 18:00:30 -05006542 set_key = ext->key_len > 0 ? 1 : 0;
6543 }
6544
6545 if (set_key) {
Dan Williams4be757d2006-01-30 11:58:00 -05006546 /* Set the requested key first */
6547 memset(key.key, 0, MAX_KEY_SIZE);
6548 switch (alg) {
6549 case IW_ENCODE_ALG_NONE:
6550 key.len = 0;
6551 break;
6552 case IW_ENCODE_ALG_WEP:
6553 if (ext->key_len > MIN_KEY_SIZE) {
6554 key.len = MAX_KEY_SIZE;
6555 } else if (ext->key_len > 0) {
6556 key.len = MIN_KEY_SIZE;
6557 } else {
6558 return -EINVAL;
6559 }
6560 key_len = min (ext->key_len, key.len);
6561 memcpy(key.key, ext->key, key_len);
6562 break;
6563 default:
6564 return -EINVAL;
6565 }
6566 /* Send the key to the card */
Dan Williamsc0380692009-01-24 09:12:15 -05006567 rc = set_wep_key(local, idx, key.key, key.len, perm, 1);
6568 if (rc < 0) {
6569 airo_print_err(local->dev->name, "failed to set WEP key"
6570 " at index %d: %d.", idx, rc);
6571 return rc;
6572 }
Dan Williams4be757d2006-01-30 11:58:00 -05006573 }
6574
6575 /* Read the flags */
6576 if(encoding->flags & IW_ENCODE_DISABLED)
6577 local->config.authType = AUTH_OPEN; // disable encryption
6578 if(encoding->flags & IW_ENCODE_RESTRICTED)
6579 local->config.authType = AUTH_SHAREDKEY; // Only Both
6580 if(encoding->flags & IW_ENCODE_OPEN)
6581 local->config.authType = AUTH_ENCRYPT; // Only Wep
6582 /* Commit the changes to flags if needed */
6583 if (local->config.authType != currentAuthType)
6584 set_bit (FLAG_COMMIT, &local->flags);
6585
6586 return -EINPROGRESS;
6587}
6588
6589
6590/*------------------------------------------------------------------*/
6591/*
6592 * Wireless Handler : get extended Encryption parameters
6593 */
6594static int airo_get_encodeext(struct net_device *dev,
6595 struct iw_request_info *info,
6596 union iwreq_data *wrqu,
6597 char *extra)
6598{
Wang Chenfaf39942008-10-14 13:30:33 +08006599 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006600 struct iw_point *encoding = &wrqu->encoding;
6601 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
Dan Williams4be757d2006-01-30 11:58:00 -05006602 int idx, max_key_len;
Dan Williamsc0380692009-01-24 09:12:15 -05006603 u8 buf[16];
Dan Williams4be757d2006-01-30 11:58:00 -05006604
Dan Williams138c0c62009-01-24 09:11:35 -05006605 if (!local->wep_capable)
Dan Williams4be757d2006-01-30 11:58:00 -05006606 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006607
Dan Williams4be757d2006-01-30 11:58:00 -05006608 readConfigRid(local, 1);
6609
6610 max_key_len = encoding->length - sizeof(*ext);
6611 if (max_key_len < 0)
6612 return -EINVAL;
6613
6614 idx = encoding->flags & IW_ENCODE_INDEX;
6615 if (idx) {
Dan Williams138c0c62009-01-24 09:11:35 -05006616 if (!valid_index(local, idx - 1))
Dan Williams4be757d2006-01-30 11:58:00 -05006617 return -EINVAL;
6618 idx--;
Dan Williamsc0380692009-01-24 09:12:15 -05006619 } else {
6620 idx = get_wep_tx_idx(local);
6621 if (idx < 0)
6622 idx = 0;
6623 }
Dan Williams4be757d2006-01-30 11:58:00 -05006624
6625 encoding->flags = idx + 1;
6626 memset(ext, 0, sizeof(*ext));
6627
6628 /* Check encryption mode */
6629 switch(local->config.authType) {
6630 case AUTH_ENCRYPT:
6631 encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
6632 break;
6633 case AUTH_SHAREDKEY:
6634 encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
6635 break;
6636 default:
6637 case AUTH_OPEN:
6638 encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED;
6639 break;
6640 }
6641 /* We can't return the key, so set the proper flag and return zero */
6642 encoding->flags |= IW_ENCODE_NOKEY;
6643 memset(extra, 0, 16);
6644
6645 /* Copy the key to the user buffer */
Dan Williamsc0380692009-01-24 09:12:15 -05006646 ext->key_len = get_wep_key(local, idx, &buf[0], sizeof(buf));
6647 memcpy(extra, buf, ext->key_len);
Dan Williams4be757d2006-01-30 11:58:00 -05006648
6649 return 0;
6650}
6651
6652
6653/*------------------------------------------------------------------*/
6654/*
6655 * Wireless Handler : set extended authentication parameters
6656 */
6657static int airo_set_auth(struct net_device *dev,
6658 struct iw_request_info *info,
6659 union iwreq_data *wrqu, char *extra)
6660{
Wang Chenfaf39942008-10-14 13:30:33 +08006661 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006662 struct iw_param *param = &wrqu->param;
Al Viro3eb9b412007-12-21 00:00:35 -05006663 __le16 currentAuthType = local->config.authType;
Dan Williams4be757d2006-01-30 11:58:00 -05006664
6665 switch (param->flags & IW_AUTH_INDEX) {
6666 case IW_AUTH_WPA_VERSION:
6667 case IW_AUTH_CIPHER_PAIRWISE:
6668 case IW_AUTH_CIPHER_GROUP:
6669 case IW_AUTH_KEY_MGMT:
6670 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6671 case IW_AUTH_PRIVACY_INVOKED:
6672 /*
6673 * airo does not use these parameters
6674 */
6675 break;
6676
6677 case IW_AUTH_DROP_UNENCRYPTED:
6678 if (param->value) {
6679 /* Only change auth type if unencrypted */
6680 if (currentAuthType == AUTH_OPEN)
6681 local->config.authType = AUTH_ENCRYPT;
6682 } else {
6683 local->config.authType = AUTH_OPEN;
6684 }
6685
6686 /* Commit the changes to flags if needed */
6687 if (local->config.authType != currentAuthType)
6688 set_bit (FLAG_COMMIT, &local->flags);
6689 break;
6690
6691 case IW_AUTH_80211_AUTH_ALG: {
6692 /* FIXME: What about AUTH_OPEN? This API seems to
6693 * disallow setting our auth to AUTH_OPEN.
6694 */
6695 if (param->value & IW_AUTH_ALG_SHARED_KEY) {
6696 local->config.authType = AUTH_SHAREDKEY;
6697 } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
6698 local->config.authType = AUTH_ENCRYPT;
6699 } else
6700 return -EINVAL;
6701 break;
6702
6703 /* Commit the changes to flags if needed */
6704 if (local->config.authType != currentAuthType)
6705 set_bit (FLAG_COMMIT, &local->flags);
6706 }
6707
6708 case IW_AUTH_WPA_ENABLED:
6709 /* Silently accept disable of WPA */
6710 if (param->value > 0)
6711 return -EOPNOTSUPP;
6712 break;
6713
6714 default:
6715 return -EOPNOTSUPP;
6716 }
6717 return -EINPROGRESS;
6718}
6719
6720
6721/*------------------------------------------------------------------*/
6722/*
6723 * Wireless Handler : get extended authentication parameters
6724 */
6725static int airo_get_auth(struct net_device *dev,
6726 struct iw_request_info *info,
6727 union iwreq_data *wrqu, char *extra)
6728{
Wang Chenfaf39942008-10-14 13:30:33 +08006729 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006730 struct iw_param *param = &wrqu->param;
Al Viro3eb9b412007-12-21 00:00:35 -05006731 __le16 currentAuthType = local->config.authType;
Dan Williams4be757d2006-01-30 11:58:00 -05006732
6733 switch (param->flags & IW_AUTH_INDEX) {
6734 case IW_AUTH_DROP_UNENCRYPTED:
6735 switch (currentAuthType) {
6736 case AUTH_SHAREDKEY:
6737 case AUTH_ENCRYPT:
6738 param->value = 1;
6739 break;
6740 default:
6741 param->value = 0;
6742 break;
6743 }
6744 break;
6745
6746 case IW_AUTH_80211_AUTH_ALG:
6747 switch (currentAuthType) {
6748 case AUTH_SHAREDKEY:
6749 param->value = IW_AUTH_ALG_SHARED_KEY;
6750 break;
6751 case AUTH_ENCRYPT:
6752 default:
6753 param->value = IW_AUTH_ALG_OPEN_SYSTEM;
6754 break;
6755 }
6756 break;
6757
6758 case IW_AUTH_WPA_ENABLED:
6759 param->value = 0;
6760 break;
6761
6762 default:
6763 return -EOPNOTSUPP;
6764 }
6765 return 0;
6766}
6767
6768
6769/*------------------------------------------------------------------*/
6770/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07006771 * Wireless Handler : set Tx-Power
6772 */
6773static int airo_set_txpow(struct net_device *dev,
6774 struct iw_request_info *info,
6775 struct iw_param *vwrq,
6776 char *extra)
6777{
Wang Chenfaf39942008-10-14 13:30:33 +08006778 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006779 CapabilityRid cap_rid; /* Card capability info */
6780 int i;
6781 int rc = -EINVAL;
Al Viro3eb9b412007-12-21 00:00:35 -05006782 __le16 v = cpu_to_le16(vwrq->value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006783
6784 readCapabilityRid(local, &cap_rid, 1);
6785
6786 if (vwrq->disabled) {
6787 set_bit (FLAG_RADIO_OFF, &local->flags);
6788 set_bit (FLAG_COMMIT, &local->flags);
6789 return -EINPROGRESS; /* Call commit handler */
6790 }
6791 if (vwrq->flags != IW_TXPOW_MWATT) {
6792 return -EINVAL;
6793 }
6794 clear_bit (FLAG_RADIO_OFF, &local->flags);
6795 for (i = 0; cap_rid.txPowerLevels[i] && (i < 8); i++)
Al Viro3eb9b412007-12-21 00:00:35 -05006796 if (v == cap_rid.txPowerLevels[i]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006797 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006798 local->config.txPower = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006799 set_bit (FLAG_COMMIT, &local->flags);
6800 rc = -EINPROGRESS; /* Call commit handler */
6801 break;
6802 }
6803 return rc;
6804}
6805
6806/*------------------------------------------------------------------*/
6807/*
6808 * Wireless Handler : get Tx-Power
6809 */
6810static int airo_get_txpow(struct net_device *dev,
6811 struct iw_request_info *info,
6812 struct iw_param *vwrq,
6813 char *extra)
6814{
Wang Chenfaf39942008-10-14 13:30:33 +08006815 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006816
6817 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006818 vwrq->value = le16_to_cpu(local->config.txPower);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006819 vwrq->fixed = 1; /* No power control */
6820 vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags);
6821 vwrq->flags = IW_TXPOW_MWATT;
6822
6823 return 0;
6824}
6825
6826/*------------------------------------------------------------------*/
6827/*
6828 * Wireless Handler : set Retry limits
6829 */
6830static int airo_set_retry(struct net_device *dev,
6831 struct iw_request_info *info,
6832 struct iw_param *vwrq,
6833 char *extra)
6834{
Wang Chenfaf39942008-10-14 13:30:33 +08006835 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006836 int rc = -EINVAL;
6837
6838 if(vwrq->disabled) {
6839 return -EINVAL;
6840 }
6841 readConfigRid(local, 1);
6842 if(vwrq->flags & IW_RETRY_LIMIT) {
Al Viro3eb9b412007-12-21 00:00:35 -05006843 __le16 v = cpu_to_le16(vwrq->value);
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006844 if(vwrq->flags & IW_RETRY_LONG)
Al Viro3eb9b412007-12-21 00:00:35 -05006845 local->config.longRetryLimit = v;
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006846 else if (vwrq->flags & IW_RETRY_SHORT)
Al Viro3eb9b412007-12-21 00:00:35 -05006847 local->config.shortRetryLimit = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006848 else {
6849 /* No modifier : set both */
Al Viro3eb9b412007-12-21 00:00:35 -05006850 local->config.longRetryLimit = v;
6851 local->config.shortRetryLimit = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006852 }
6853 set_bit (FLAG_COMMIT, &local->flags);
6854 rc = -EINPROGRESS; /* Call commit handler */
6855 }
6856 if(vwrq->flags & IW_RETRY_LIFETIME) {
Al Viro3eb9b412007-12-21 00:00:35 -05006857 local->config.txLifetime = cpu_to_le16(vwrq->value / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006858 set_bit (FLAG_COMMIT, &local->flags);
6859 rc = -EINPROGRESS; /* Call commit handler */
6860 }
6861 return rc;
6862}
6863
6864/*------------------------------------------------------------------*/
6865/*
6866 * Wireless Handler : get Retry limits
6867 */
6868static int airo_get_retry(struct net_device *dev,
6869 struct iw_request_info *info,
6870 struct iw_param *vwrq,
6871 char *extra)
6872{
Wang Chenfaf39942008-10-14 13:30:33 +08006873 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006874
6875 vwrq->disabled = 0; /* Can't be disabled */
6876
6877 readConfigRid(local, 1);
6878 /* Note : by default, display the min retry number */
6879 if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
6880 vwrq->flags = IW_RETRY_LIFETIME;
Al Viro3eb9b412007-12-21 00:00:35 -05006881 vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024;
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006882 } else if((vwrq->flags & IW_RETRY_LONG)) {
6883 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
Al Viro3eb9b412007-12-21 00:00:35 -05006884 vwrq->value = le16_to_cpu(local->config.longRetryLimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006885 } else {
6886 vwrq->flags = IW_RETRY_LIMIT;
Al Viro3eb9b412007-12-21 00:00:35 -05006887 vwrq->value = le16_to_cpu(local->config.shortRetryLimit);
6888 if(local->config.shortRetryLimit != local->config.longRetryLimit)
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006889 vwrq->flags |= IW_RETRY_SHORT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006890 }
6891
6892 return 0;
6893}
6894
6895/*------------------------------------------------------------------*/
6896/*
6897 * Wireless Handler : get range info
6898 */
6899static int airo_get_range(struct net_device *dev,
6900 struct iw_request_info *info,
6901 struct iw_point *dwrq,
6902 char *extra)
6903{
Wang Chenfaf39942008-10-14 13:30:33 +08006904 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006905 struct iw_range *range = (struct iw_range *) extra;
6906 CapabilityRid cap_rid; /* Card capability info */
6907 int i;
6908 int k;
6909
6910 readCapabilityRid(local, &cap_rid, 1);
6911
6912 dwrq->length = sizeof(struct iw_range);
6913 memset(range, 0, sizeof(*range));
6914 range->min_nwid = 0x0000;
6915 range->max_nwid = 0x0000;
6916 range->num_channels = 14;
6917 /* Should be based on cap_rid.country to give only
6918 * what the current card support */
6919 k = 0;
6920 for(i = 0; i < 14; i++) {
6921 range->freq[k].i = i + 1; /* List index */
David Kilroy9ee677c2008-12-23 14:03:38 +00006922 range->freq[k].m = ieee80211_dsss_chan_to_freq(i + 1) * 100000;
6923 range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006924 }
6925 range->num_frequency = k;
6926
Linus Torvalds1da177e2005-04-16 15:20:36 -07006927 range->sensitivity = 65535;
6928
Dan Williams41480af2005-05-10 09:45:51 -04006929 /* Hum... Should put the right values there */
6930 if (local->rssi)
6931 range->max_qual.qual = 100; /* % */
6932 else
6933 range->max_qual.qual = airo_get_max_quality(&cap_rid);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006934 range->max_qual.level = 0x100 - 120; /* -120 dBm */
6935 range->max_qual.noise = 0x100 - 120; /* -120 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006936
6937 /* Experimental measurements - boundary 11/5.5 Mb/s */
6938 /* Note : with or without the (local->rssi), results
6939 * are somewhat different. - Jean II */
6940 if (local->rssi) {
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006941 range->avg_qual.qual = 50; /* % */
6942 range->avg_qual.level = 0x100 - 70; /* -70 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006943 } else {
6944 range->avg_qual.qual = airo_get_avg_quality(&cap_rid);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006945 range->avg_qual.level = 0x100 - 80; /* -80 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006946 }
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006947 range->avg_qual.noise = 0x100 - 85; /* -85 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006948
Linus Torvalds1da177e2005-04-16 15:20:36 -07006949 for(i = 0 ; i < 8 ; i++) {
6950 range->bitrate[i] = cap_rid.supportedRates[i] * 500000;
6951 if(range->bitrate[i] == 0)
6952 break;
6953 }
6954 range->num_bitrates = i;
6955
6956 /* Set an indication of the max TCP throughput
6957 * in bit/s that we can expect using this interface.
6958 * May be use for QoS stuff... Jean II */
6959 if(i > 2)
6960 range->throughput = 5000 * 1000;
6961 else
6962 range->throughput = 1500 * 1000;
6963
6964 range->min_rts = 0;
Dan Williams15db2762006-03-16 13:46:27 -05006965 range->max_rts = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006966 range->min_frag = 256;
Dan Williams15db2762006-03-16 13:46:27 -05006967 range->max_frag = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006968
Al Viro56d81bd2007-12-20 17:18:35 -05006969 if(cap_rid.softCap & cpu_to_le16(2)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006970 // WEP: RC4 40 bits
6971 range->encoding_size[0] = 5;
6972 // RC4 ~128 bits
Al Viro56d81bd2007-12-20 17:18:35 -05006973 if (cap_rid.softCap & cpu_to_le16(0x100)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006974 range->encoding_size[1] = 13;
6975 range->num_encoding_sizes = 2;
6976 } else
6977 range->num_encoding_sizes = 1;
Al Viro56d81bd2007-12-20 17:18:35 -05006978 range->max_encoding_tokens =
6979 cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006980 } else {
6981 range->num_encoding_sizes = 0;
6982 range->max_encoding_tokens = 0;
6983 }
6984 range->min_pmp = 0;
6985 range->max_pmp = 5000000; /* 5 secs */
6986 range->min_pmt = 0;
6987 range->max_pmt = 65535 * 1024; /* ??? */
6988 range->pmp_flags = IW_POWER_PERIOD;
6989 range->pmt_flags = IW_POWER_TIMEOUT;
6990 range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
6991
6992 /* Transmit Power - values are in mW */
6993 for(i = 0 ; i < 8 ; i++) {
Al Viro56d81bd2007-12-20 17:18:35 -05006994 range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006995 if(range->txpower[i] == 0)
6996 break;
6997 }
6998 range->num_txpower = i;
6999 range->txpower_capa = IW_TXPOW_MWATT;
Dan Williams3c304952006-04-15 12:26:18 -04007000 range->we_version_source = 19;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007001 range->we_version_compiled = WIRELESS_EXT;
7002 range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
7003 range->retry_flags = IW_RETRY_LIMIT;
7004 range->r_time_flags = IW_RETRY_LIFETIME;
7005 range->min_retry = 1;
7006 range->max_retry = 65535;
7007 range->min_r_time = 1024;
7008 range->max_r_time = 65535 * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007009
7010 /* Event capability (kernel + driver) */
7011 range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
7012 IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
7013 IW_EVENT_CAPA_MASK(SIOCGIWAP) |
7014 IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
7015 range->event_capa[1] = IW_EVENT_CAPA_K_1;
7016 range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP);
7017 return 0;
7018}
7019
7020/*------------------------------------------------------------------*/
7021/*
7022 * Wireless Handler : set Power Management
7023 */
7024static int airo_set_power(struct net_device *dev,
7025 struct iw_request_info *info,
7026 struct iw_param *vwrq,
7027 char *extra)
7028{
Wang Chenfaf39942008-10-14 13:30:33 +08007029 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007030
7031 readConfigRid(local, 1);
7032 if (vwrq->disabled) {
Al Viro3eb9b412007-12-21 00:00:35 -05007033 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007034 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007035 local->config.powerSaveMode = POWERSAVE_CAM;
Al Viro3eb9b412007-12-21 00:00:35 -05007036 local->config.rmode &= ~RXMODE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007037 local->config.rmode |= RXMODE_BC_MC_ADDR;
7038 set_bit (FLAG_COMMIT, &local->flags);
7039 return -EINPROGRESS; /* Call commit handler */
7040 }
7041 if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
Al Viro3eb9b412007-12-21 00:00:35 -05007042 local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007043 local->config.powerSaveMode = POWERSAVE_PSPCAM;
7044 set_bit (FLAG_COMMIT, &local->flags);
7045 } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
Al Viro3eb9b412007-12-21 00:00:35 -05007046 local->config.fastListenInterval =
7047 local->config.listenInterval =
7048 cpu_to_le16((vwrq->value + 500) / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007049 local->config.powerSaveMode = POWERSAVE_PSPCAM;
7050 set_bit (FLAG_COMMIT, &local->flags);
7051 }
7052 switch (vwrq->flags & IW_POWER_MODE) {
7053 case IW_POWER_UNICAST_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_ADDR;
7058 set_bit (FLAG_COMMIT, &local->flags);
7059 break;
7060 case IW_POWER_ALL_R:
Al Viro3eb9b412007-12-21 00:00:35 -05007061 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007062 return -EINVAL;
Al Viro3eb9b412007-12-21 00:00:35 -05007063 local->config.rmode &= ~RXMODE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007064 local->config.rmode |= RXMODE_BC_MC_ADDR;
7065 set_bit (FLAG_COMMIT, &local->flags);
7066 case IW_POWER_ON:
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07007067 /* This is broken, fixme ;-) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07007068 break;
7069 default:
7070 return -EINVAL;
7071 }
7072 // Note : we may want to factor local->need_commit here
7073 // Note2 : may also want to factor RXMODE_RFMON test
7074 return -EINPROGRESS; /* Call commit handler */
7075}
7076
7077/*------------------------------------------------------------------*/
7078/*
7079 * Wireless Handler : get Power Management
7080 */
7081static int airo_get_power(struct net_device *dev,
7082 struct iw_request_info *info,
7083 struct iw_param *vwrq,
7084 char *extra)
7085{
Wang Chenfaf39942008-10-14 13:30:33 +08007086 struct airo_info *local = dev->ml_priv;
Al Viro3eb9b412007-12-21 00:00:35 -05007087 __le16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007088
7089 readConfigRid(local, 1);
7090 mode = local->config.powerSaveMode;
7091 if ((vwrq->disabled = (mode == POWERSAVE_CAM)))
7092 return 0;
7093 if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
Al Viro3eb9b412007-12-21 00:00:35 -05007094 vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007095 vwrq->flags = IW_POWER_TIMEOUT;
7096 } else {
Al Viro3eb9b412007-12-21 00:00:35 -05007097 vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007098 vwrq->flags = IW_POWER_PERIOD;
7099 }
Al Viro3eb9b412007-12-21 00:00:35 -05007100 if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007101 vwrq->flags |= IW_POWER_UNICAST_R;
7102 else
7103 vwrq->flags |= IW_POWER_ALL_R;
7104
7105 return 0;
7106}
7107
7108/*------------------------------------------------------------------*/
7109/*
7110 * Wireless Handler : set Sensitivity
7111 */
7112static int airo_set_sens(struct net_device *dev,
7113 struct iw_request_info *info,
7114 struct iw_param *vwrq,
7115 char *extra)
7116{
Wang Chenfaf39942008-10-14 13:30:33 +08007117 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007118
7119 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05007120 local->config.rssiThreshold =
7121 cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007122 set_bit (FLAG_COMMIT, &local->flags);
7123
7124 return -EINPROGRESS; /* Call commit handler */
7125}
7126
7127/*------------------------------------------------------------------*/
7128/*
7129 * Wireless Handler : get Sensitivity
7130 */
7131static int airo_get_sens(struct net_device *dev,
7132 struct iw_request_info *info,
7133 struct iw_param *vwrq,
7134 char *extra)
7135{
Wang Chenfaf39942008-10-14 13:30:33 +08007136 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007137
7138 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05007139 vwrq->value = le16_to_cpu(local->config.rssiThreshold);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007140 vwrq->disabled = (vwrq->value == 0);
7141 vwrq->fixed = 1;
7142
7143 return 0;
7144}
7145
7146/*------------------------------------------------------------------*/
7147/*
7148 * Wireless Handler : get AP List
7149 * Note : this is deprecated in favor of IWSCAN
7150 */
7151static int airo_get_aplist(struct net_device *dev,
7152 struct iw_request_info *info,
7153 struct iw_point *dwrq,
7154 char *extra)
7155{
Wang Chenfaf39942008-10-14 13:30:33 +08007156 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007157 struct sockaddr *address = (struct sockaddr *) extra;
7158 struct iw_quality qual[IW_MAX_AP];
7159 BSSListRid BSSList;
7160 int i;
7161 int loseSync = capable(CAP_NET_ADMIN) ? 1: -1;
7162
7163 for (i = 0; i < IW_MAX_AP; i++) {
Al Viro17e70492007-12-19 18:56:37 -05007164 u16 dBm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007165 if (readBSSListRid(local, loseSync, &BSSList))
7166 break;
7167 loseSync = 0;
7168 memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN);
7169 address[i].sa_family = ARPHRD_ETHER;
Al Viro17e70492007-12-19 18:56:37 -05007170 dBm = le16_to_cpu(BSSList.dBm);
Dan Williams41480af2005-05-10 09:45:51 -04007171 if (local->rssi) {
Al Viro17e70492007-12-19 18:56:37 -05007172 qual[i].level = 0x100 - dBm;
7173 qual[i].qual = airo_dbm_to_pct(local->rssi, dBm);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007174 qual[i].updated = IW_QUAL_QUAL_UPDATED
7175 | IW_QUAL_LEVEL_UPDATED
7176 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007177 } else {
Al Viro17e70492007-12-19 18:56:37 -05007178 qual[i].level = (dBm + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007179 qual[i].qual = 0;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007180 qual[i].updated = IW_QUAL_QUAL_INVALID
7181 | IW_QUAL_LEVEL_UPDATED
7182 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007183 }
7184 qual[i].noise = local->wstats.qual.noise;
Al Viro17e70492007-12-19 18:56:37 -05007185 if (BSSList.index == cpu_to_le16(0xffff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007186 break;
7187 }
7188 if (!i) {
7189 StatusRid status_rid; /* Card status info */
7190 readStatusRid(local, &status_rid, 1);
7191 for (i = 0;
7192 i < min(IW_MAX_AP, 4) &&
7193 (status_rid.bssid[i][0]
7194 & status_rid.bssid[i][1]
7195 & status_rid.bssid[i][2]
7196 & status_rid.bssid[i][3]
7197 & status_rid.bssid[i][4]
7198 & status_rid.bssid[i][5])!=0xff &&
7199 (status_rid.bssid[i][0]
7200 | status_rid.bssid[i][1]
7201 | status_rid.bssid[i][2]
7202 | status_rid.bssid[i][3]
7203 | status_rid.bssid[i][4]
7204 | status_rid.bssid[i][5]);
7205 i++) {
7206 memcpy(address[i].sa_data,
7207 status_rid.bssid[i], ETH_ALEN);
7208 address[i].sa_family = ARPHRD_ETHER;
7209 }
7210 } else {
7211 dwrq->flags = 1; /* Should be define'd */
7212 memcpy(extra + sizeof(struct sockaddr)*i,
7213 &qual, sizeof(struct iw_quality)*i);
7214 }
7215 dwrq->length = i;
7216
7217 return 0;
7218}
7219
7220/*------------------------------------------------------------------*/
7221/*
7222 * Wireless Handler : Initiate Scan
7223 */
7224static int airo_set_scan(struct net_device *dev,
7225 struct iw_request_info *info,
David Kilroy9930cce2008-09-13 12:22:05 +01007226 struct iw_point *dwrq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007227 char *extra)
7228{
Wang Chenfaf39942008-10-14 13:30:33 +08007229 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007230 Cmd cmd;
7231 Resp rsp;
Dan Williams9e75af32006-03-16 13:46:29 -05007232 int wake = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007233
7234 /* Note : you may have realised that, as this is a SET operation,
7235 * this is privileged and therefore a normal user can't
7236 * perform scanning.
7237 * This is not an error, while the device perform scanning,
7238 * traffic doesn't flow, so it's a perfect DoS...
7239 * Jean II */
7240 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
7241
Dan Williams9e75af32006-03-16 13:46:29 -05007242 if (down_interruptible(&ai->sem))
7243 return -ERESTARTSYS;
7244
7245 /* If there's already a scan in progress, don't
7246 * trigger another one. */
7247 if (ai->scan_timeout > 0)
7248 goto out;
7249
Linus Torvalds1da177e2005-04-16 15:20:36 -07007250 /* Initiate a scan command */
Dan Williams6fcdf562006-03-31 15:08:46 -05007251 ai->scan_timeout = RUN_AT(3*HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007252 memset(&cmd, 0, sizeof(cmd));
7253 cmd.cmd=CMD_LISTBSS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007254 issuecommand(ai, &cmd, &rsp);
Dan Williams9e75af32006-03-16 13:46:29 -05007255 wake = 1;
7256
7257out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07007258 up(&ai->sem);
Dan Williams9e75af32006-03-16 13:46:29 -05007259 if (wake)
7260 wake_up_interruptible(&ai->thr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007261 return 0;
7262}
7263
7264/*------------------------------------------------------------------*/
7265/*
7266 * Translate scan data returned from the card to a card independent
7267 * format that the Wireless Tools will understand - Jean II
7268 */
7269static inline char *airo_translate_scan(struct net_device *dev,
David S. Millerccc58052008-06-16 18:50:49 -07007270 struct iw_request_info *info,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007271 char *current_ev,
7272 char *end_buf,
Dan Williams41480af2005-05-10 09:45:51 -04007273 BSSListRid *bss)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007274{
Wang Chenfaf39942008-10-14 13:30:33 +08007275 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007276 struct iw_event iwe; /* Temporary buffer */
Al Viro17e70492007-12-19 18:56:37 -05007277 __le16 capabilities;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007278 char * current_val; /* For rates */
7279 int i;
Dan Williams3c304952006-04-15 12:26:18 -04007280 char * buf;
Al Viro851b3e52007-12-19 19:20:12 -05007281 u16 dBm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007282
7283 /* First entry *MUST* be the AP MAC address */
7284 iwe.cmd = SIOCGIWAP;
7285 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
Dan Williams41480af2005-05-10 09:45:51 -04007286 memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -07007287 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7288 &iwe, IW_EV_ADDR_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007289
7290 /* Other entries will be displayed in the order we give them */
7291
7292 /* Add the ESSID */
Dan Williams41480af2005-05-10 09:45:51 -04007293 iwe.u.data.length = bss->ssidLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007294 if(iwe.u.data.length > 32)
7295 iwe.u.data.length = 32;
7296 iwe.cmd = SIOCGIWESSID;
7297 iwe.u.data.flags = 1;
David S. Millerccc58052008-06-16 18:50:49 -07007298 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7299 &iwe, bss->ssid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007300
7301 /* Add mode */
7302 iwe.cmd = SIOCGIWMODE;
Al Viro17e70492007-12-19 18:56:37 -05007303 capabilities = bss->cap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007304 if(capabilities & (CAP_ESS | CAP_IBSS)) {
7305 if(capabilities & CAP_ESS)
7306 iwe.u.mode = IW_MODE_MASTER;
7307 else
7308 iwe.u.mode = IW_MODE_ADHOC;
David S. Millerccc58052008-06-16 18:50:49 -07007309 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7310 &iwe, IW_EV_UINT_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007311 }
7312
7313 /* Add frequency */
7314 iwe.cmd = SIOCGIWFREQ;
Dan Williams41480af2005-05-10 09:45:51 -04007315 iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
David Kilroy9ee677c2008-12-23 14:03:38 +00007316 iwe.u.freq.m = ieee80211_dsss_chan_to_freq(iwe.u.freq.m) * 100000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007317 iwe.u.freq.e = 1;
David S. Millerccc58052008-06-16 18:50:49 -07007318 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7319 &iwe, IW_EV_FREQ_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007320
Al Viro851b3e52007-12-19 19:20:12 -05007321 dBm = le16_to_cpu(bss->dBm);
7322
Linus Torvalds1da177e2005-04-16 15:20:36 -07007323 /* Add quality statistics */
7324 iwe.cmd = IWEVQUAL;
Dan Williams41480af2005-05-10 09:45:51 -04007325 if (ai->rssi) {
Al Viro851b3e52007-12-19 19:20:12 -05007326 iwe.u.qual.level = 0x100 - dBm;
7327 iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007328 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
7329 | IW_QUAL_LEVEL_UPDATED
7330 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007331 } else {
Al Viro851b3e52007-12-19 19:20:12 -05007332 iwe.u.qual.level = (dBm + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007333 iwe.u.qual.qual = 0;
Jeff Garzikbbeec902005-09-07 00:27:54 -04007334 iwe.u.qual.updated = IW_QUAL_QUAL_INVALID
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007335 | IW_QUAL_LEVEL_UPDATED
7336 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007337 }
7338 iwe.u.qual.noise = ai->wstats.qual.noise;
David S. Millerccc58052008-06-16 18:50:49 -07007339 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7340 &iwe, IW_EV_QUAL_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007341
7342 /* Add encryption capability */
7343 iwe.cmd = SIOCGIWENCODE;
7344 if(capabilities & CAP_PRIVACY)
7345 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
7346 else
7347 iwe.u.data.flags = IW_ENCODE_DISABLED;
7348 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -07007349 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7350 &iwe, bss->ssid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007351
7352 /* Rate : stuffing multiple values in a single event require a bit
7353 * more of magic - Jean II */
David S. Millerccc58052008-06-16 18:50:49 -07007354 current_val = current_ev + iwe_stream_lcp_len(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007355
7356 iwe.cmd = SIOCGIWRATE;
7357 /* Those two flags are ignored... */
7358 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
7359 /* Max 8 values */
7360 for(i = 0 ; i < 8 ; i++) {
7361 /* NULL terminated */
Dan Williams41480af2005-05-10 09:45:51 -04007362 if(bss->rates[i] == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007363 break;
7364 /* Bit rate given in 500 kb/s units (+ 0x80) */
Dan Williams41480af2005-05-10 09:45:51 -04007365 iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007366 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -07007367 current_val = iwe_stream_add_value(info, current_ev,
7368 current_val, end_buf,
7369 &iwe, IW_EV_PARAM_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007370 }
7371 /* Check if we added any event */
David S. Millerccc58052008-06-16 18:50:49 -07007372 if ((current_val - current_ev) > iwe_stream_lcp_len(info))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007373 current_ev = current_val;
7374
Dan Williams3c304952006-04-15 12:26:18 -04007375 /* Beacon interval */
7376 buf = kmalloc(30, GFP_KERNEL);
7377 if (buf) {
7378 iwe.cmd = IWEVCUSTOM;
7379 sprintf(buf, "bcn_int=%d", bss->beaconInterval);
7380 iwe.u.data.length = strlen(buf);
David S. Millerccc58052008-06-16 18:50:49 -07007381 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7382 &iwe, buf);
Dan Williams3c304952006-04-15 12:26:18 -04007383 kfree(buf);
7384 }
7385
7386 /* Put WPA/RSN Information Elements into the event stream */
7387 if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
7388 unsigned int num_null_ies = 0;
7389 u16 length = sizeof (bss->extra.iep);
Johannes Berg2c7060022008-10-30 22:09:54 +01007390 u8 *ie = (void *)&bss->extra.iep;
Dan Williams3c304952006-04-15 12:26:18 -04007391
Johannes Berg2c7060022008-10-30 22:09:54 +01007392 while ((length >= 2) && (num_null_ies < 2)) {
7393 if (2 + ie[1] > length) {
Dan Williams3c304952006-04-15 12:26:18 -04007394 /* Invalid element, don't continue parsing IE */
7395 break;
7396 }
7397
Johannes Berg2c7060022008-10-30 22:09:54 +01007398 switch (ie[0]) {
7399 case WLAN_EID_SSID:
Dan Williams3c304952006-04-15 12:26:18 -04007400 /* Two zero-length SSID elements
7401 * mean we're done parsing elements */
Johannes Berg2c7060022008-10-30 22:09:54 +01007402 if (!ie[1])
Dan Williams3c304952006-04-15 12:26:18 -04007403 num_null_ies++;
7404 break;
7405
Johannes Berg2c7060022008-10-30 22:09:54 +01007406 case WLAN_EID_GENERIC:
7407 if (ie[1] >= 4 &&
7408 ie[2] == 0x00 &&
7409 ie[3] == 0x50 &&
7410 ie[4] == 0xf2 &&
7411 ie[5] == 0x01) {
Dan Williams3c304952006-04-15 12:26:18 -04007412 iwe.cmd = IWEVGENIE;
Johannes Berg2c7060022008-10-30 22:09:54 +01007413 /* 64 is an arbitrary cut-off */
7414 iwe.u.data.length = min(ie[1] + 2,
7415 64);
David S. Millerccc58052008-06-16 18:50:49 -07007416 current_ev = iwe_stream_add_point(
7417 info, current_ev,
Johannes Berg2c7060022008-10-30 22:09:54 +01007418 end_buf, &iwe, ie);
Dan Williams3c304952006-04-15 12:26:18 -04007419 }
7420 break;
7421
Johannes Berg2c7060022008-10-30 22:09:54 +01007422 case WLAN_EID_RSN:
Dan Williams3c304952006-04-15 12:26:18 -04007423 iwe.cmd = IWEVGENIE;
Johannes Berg2c7060022008-10-30 22:09:54 +01007424 /* 64 is an arbitrary cut-off */
7425 iwe.u.data.length = min(ie[1] + 2, 64);
David S. Millerccc58052008-06-16 18:50:49 -07007426 current_ev = iwe_stream_add_point(
7427 info, current_ev, end_buf,
Johannes Berg2c7060022008-10-30 22:09:54 +01007428 &iwe, ie);
Dan Williams3c304952006-04-15 12:26:18 -04007429 break;
7430
7431 default:
7432 break;
7433 }
7434
Johannes Berg2c7060022008-10-30 22:09:54 +01007435 length -= 2 + ie[1];
7436 ie += 2 + ie[1];
Dan Williams3c304952006-04-15 12:26:18 -04007437 }
7438 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07007439 return current_ev;
7440}
7441
7442/*------------------------------------------------------------------*/
7443/*
7444 * Wireless Handler : Read Scan Results
7445 */
7446static int airo_get_scan(struct net_device *dev,
7447 struct iw_request_info *info,
7448 struct iw_point *dwrq,
7449 char *extra)
7450{
Wang Chenfaf39942008-10-14 13:30:33 +08007451 struct airo_info *ai = dev->ml_priv;
Dan Williams9e75af32006-03-16 13:46:29 -05007452 BSSListElement *net;
7453 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007454 char *current_ev = extra;
7455
Dan Williams9e75af32006-03-16 13:46:29 -05007456 /* If a scan is in-progress, return -EAGAIN */
7457 if (ai->scan_timeout > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007458 return -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007459
Dan Williams9e75af32006-03-16 13:46:29 -05007460 if (down_interruptible(&ai->sem))
7461 return -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007462
Dan Williams9e75af32006-03-16 13:46:29 -05007463 list_for_each_entry (net, &ai->network_list, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007464 /* Translate to WE format this entry */
David S. Millerccc58052008-06-16 18:50:49 -07007465 current_ev = airo_translate_scan(dev, info, current_ev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007466 extra + dwrq->length,
Dan Williams9e75af32006-03-16 13:46:29 -05007467 &net->bss);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007468
7469 /* Check if there is space for one more entry */
7470 if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
7471 /* Ask user space to try again with a bigger buffer */
Dan Williams9e75af32006-03-16 13:46:29 -05007472 err = -E2BIG;
7473 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007474 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07007475 }
Dan Williams9e75af32006-03-16 13:46:29 -05007476
Linus Torvalds1da177e2005-04-16 15:20:36 -07007477 /* Length of data */
7478 dwrq->length = (current_ev - extra);
7479 dwrq->flags = 0; /* todo */
7480
Dan Williams9e75af32006-03-16 13:46:29 -05007481out:
7482 up(&ai->sem);
7483 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007484}
7485
7486/*------------------------------------------------------------------*/
7487/*
7488 * Commit handler : called after a bunch of SET operations
7489 */
7490static int airo_config_commit(struct net_device *dev,
7491 struct iw_request_info *info, /* NULL */
7492 void *zwrq, /* NULL */
7493 char *extra) /* NULL */
7494{
Wang Chenfaf39942008-10-14 13:30:33 +08007495 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007496
7497 if (!test_bit (FLAG_COMMIT, &local->flags))
7498 return 0;
7499
7500 /* Some of the "SET" function may have modified some of the
7501 * parameters. It's now time to commit them in the card */
7502 disable_MAC(local, 1);
7503 if (test_bit (FLAG_RESET, &local->flags)) {
7504 APListRid APList_rid;
7505 SsidRid SSID_rid;
7506
7507 readAPListRid(local, &APList_rid);
7508 readSsidRid(local, &SSID_rid);
7509 if (test_bit(FLAG_MPI,&local->flags))
7510 setup_card(local, dev->dev_addr, 1 );
7511 else
7512 reset_airo_card(dev);
7513 disable_MAC(local, 1);
7514 writeSsidRid(local, &SSID_rid, 1);
7515 writeAPListRid(local, &APList_rid, 1);
7516 }
7517 if (down_interruptible(&local->sem))
7518 return -ERESTARTSYS;
7519 writeConfigRid(local, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007520 enable_MAC(local, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007521 if (test_bit (FLAG_RESET, &local->flags))
7522 airo_set_promisc(local);
7523 else
7524 up(&local->sem);
7525
7526 return 0;
7527}
7528
7529/*------------------------------------------------------------------*/
7530/*
7531 * Structures to export the Wireless Handlers
7532 */
7533
7534static const struct iw_priv_args airo_private_args[] = {
7535/*{ cmd, set_args, get_args, name } */
7536 { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
7537 IW_PRIV_TYPE_BYTE | 2047, "airoioctl" },
7538 { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
7539 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" },
7540};
7541
7542static const iw_handler airo_handler[] =
7543{
7544 (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */
7545 (iw_handler) airo_get_name, /* SIOCGIWNAME */
7546 (iw_handler) NULL, /* SIOCSIWNWID */
7547 (iw_handler) NULL, /* SIOCGIWNWID */
7548 (iw_handler) airo_set_freq, /* SIOCSIWFREQ */
7549 (iw_handler) airo_get_freq, /* SIOCGIWFREQ */
7550 (iw_handler) airo_set_mode, /* SIOCSIWMODE */
7551 (iw_handler) airo_get_mode, /* SIOCGIWMODE */
7552 (iw_handler) airo_set_sens, /* SIOCSIWSENS */
7553 (iw_handler) airo_get_sens, /* SIOCGIWSENS */
7554 (iw_handler) NULL, /* SIOCSIWRANGE */
7555 (iw_handler) airo_get_range, /* SIOCGIWRANGE */
7556 (iw_handler) NULL, /* SIOCSIWPRIV */
7557 (iw_handler) NULL, /* SIOCGIWPRIV */
7558 (iw_handler) NULL, /* SIOCSIWSTATS */
7559 (iw_handler) NULL, /* SIOCGIWSTATS */
7560 iw_handler_set_spy, /* SIOCSIWSPY */
7561 iw_handler_get_spy, /* SIOCGIWSPY */
7562 iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
7563 iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
7564 (iw_handler) airo_set_wap, /* SIOCSIWAP */
7565 (iw_handler) airo_get_wap, /* SIOCGIWAP */
7566 (iw_handler) NULL, /* -- hole -- */
7567 (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */
7568 (iw_handler) airo_set_scan, /* SIOCSIWSCAN */
7569 (iw_handler) airo_get_scan, /* SIOCGIWSCAN */
7570 (iw_handler) airo_set_essid, /* SIOCSIWESSID */
7571 (iw_handler) airo_get_essid, /* SIOCGIWESSID */
7572 (iw_handler) airo_set_nick, /* SIOCSIWNICKN */
7573 (iw_handler) airo_get_nick, /* SIOCGIWNICKN */
7574 (iw_handler) NULL, /* -- hole -- */
7575 (iw_handler) NULL, /* -- hole -- */
7576 (iw_handler) airo_set_rate, /* SIOCSIWRATE */
7577 (iw_handler) airo_get_rate, /* SIOCGIWRATE */
7578 (iw_handler) airo_set_rts, /* SIOCSIWRTS */
7579 (iw_handler) airo_get_rts, /* SIOCGIWRTS */
7580 (iw_handler) airo_set_frag, /* SIOCSIWFRAG */
7581 (iw_handler) airo_get_frag, /* SIOCGIWFRAG */
7582 (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */
7583 (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */
7584 (iw_handler) airo_set_retry, /* SIOCSIWRETRY */
7585 (iw_handler) airo_get_retry, /* SIOCGIWRETRY */
7586 (iw_handler) airo_set_encode, /* SIOCSIWENCODE */
7587 (iw_handler) airo_get_encode, /* SIOCGIWENCODE */
7588 (iw_handler) airo_set_power, /* SIOCSIWPOWER */
7589 (iw_handler) airo_get_power, /* SIOCGIWPOWER */
Dan Williams4be757d2006-01-30 11:58:00 -05007590 (iw_handler) NULL, /* -- hole -- */
7591 (iw_handler) NULL, /* -- hole -- */
7592 (iw_handler) NULL, /* SIOCSIWGENIE */
7593 (iw_handler) NULL, /* SIOCGIWGENIE */
7594 (iw_handler) airo_set_auth, /* SIOCSIWAUTH */
7595 (iw_handler) airo_get_auth, /* SIOCGIWAUTH */
7596 (iw_handler) airo_set_encodeext, /* SIOCSIWENCODEEXT */
7597 (iw_handler) airo_get_encodeext, /* SIOCGIWENCODEEXT */
7598 (iw_handler) NULL, /* SIOCSIWPMKSA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07007599};
7600
7601/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here.
7602 * We want to force the use of the ioctl code, because those can't be
7603 * won't work the iw_handler code (because they simultaneously read
7604 * and write data and iw_handler can't do that).
7605 * Note that it's perfectly legal to read/write on a single ioctl command,
7606 * you just can't use iwpriv and need to force it via the ioctl handler.
7607 * Jean II */
7608static const iw_handler airo_private_handler[] =
7609{
7610 NULL, /* SIOCIWFIRSTPRIV */
7611};
7612
7613static const struct iw_handler_def airo_handler_def =
7614{
Denis Chengff8ac602007-09-02 18:30:18 +08007615 .num_standard = ARRAY_SIZE(airo_handler),
7616 .num_private = ARRAY_SIZE(airo_private_handler),
7617 .num_private_args = ARRAY_SIZE(airo_private_args),
Linus Torvalds1da177e2005-04-16 15:20:36 -07007618 .standard = airo_handler,
7619 .private = airo_private_handler,
7620 .private_args = airo_private_args,
7621 .get_wireless_stats = airo_get_wireless_stats,
7622};
7623
Linus Torvalds1da177e2005-04-16 15:20:36 -07007624/*
7625 * This defines the configuration part of the Wireless Extensions
7626 * Note : irq and spinlock protection will occur in the subroutines
7627 *
7628 * TODO :
7629 * o Check input value more carefully and fill correct values in range
7630 * o Test and shakeout the bugs (if any)
7631 *
7632 * Jean II
7633 *
7634 * Javier Achirica did a great job of merging code from the unnamed CISCO
7635 * developer that added support for flashing the card.
7636 */
7637static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
7638{
7639 int rc = 0;
Wang Chenfaf39942008-10-14 13:30:33 +08007640 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007641
Pavel Machekca078ba2005-09-03 15:56:57 -07007642 if (ai->power.event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007643 return 0;
7644
7645 switch (cmd) {
7646#ifdef CISCO_EXT
7647 case AIROIDIFC:
7648#ifdef AIROOLDIDIFC
7649 case AIROOLDIDIFC:
7650#endif
7651 {
7652 int val = AIROMAGIC;
7653 aironet_ioctl com;
7654 if (copy_from_user(&com,rq->ifr_data,sizeof(com)))
7655 rc = -EFAULT;
7656 else if (copy_to_user(com.data,(char *)&val,sizeof(val)))
7657 rc = -EFAULT;
7658 }
7659 break;
7660
7661 case AIROIOCTL:
7662#ifdef AIROOLDIOCTL
7663 case AIROOLDIOCTL:
7664#endif
7665 /* Get the command struct and hand it off for evaluation by
7666 * the proper subfunction
7667 */
7668 {
7669 aironet_ioctl com;
7670 if (copy_from_user(&com,rq->ifr_data,sizeof(com))) {
7671 rc = -EFAULT;
7672 break;
7673 }
7674
7675 /* Separate R/W functions bracket legality here
7676 */
7677 if ( com.command == AIRORSWVERSION ) {
7678 if (copy_to_user(com.data, swversion, sizeof(swversion)))
7679 rc = -EFAULT;
7680 else
7681 rc = 0;
7682 }
7683 else if ( com.command <= AIRORRID)
7684 rc = readrids(dev,&com);
7685 else if ( com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2) )
7686 rc = writerids(dev,&com);
7687 else if ( com.command >= AIROFLSHRST && com.command <= AIRORESTART )
7688 rc = flashcard(dev,&com);
7689 else
7690 rc = -EINVAL; /* Bad command in ioctl */
7691 }
7692 break;
7693#endif /* CISCO_EXT */
7694
7695 // All other calls are currently unsupported
7696 default:
7697 rc = -EOPNOTSUPP;
7698 }
7699 return rc;
7700}
7701
Linus Torvalds1da177e2005-04-16 15:20:36 -07007702/*
7703 * Get the Wireless stats out of the driver
7704 * Note : irq and spinlock protection will occur in the subroutines
7705 *
7706 * TODO :
7707 * o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs)
7708 *
7709 * Jean
7710 */
7711static void airo_read_wireless_stats(struct airo_info *local)
7712{
7713 StatusRid status_rid;
7714 StatsRid stats_rid;
7715 CapabilityRid cap_rid;
Al Viroa23ace52007-12-19 22:24:16 -05007716 __le32 *vals = stats_rid.vals;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007717
7718 /* Get stats out of the card */
Dan Williams3c304952006-04-15 12:26:18 -04007719 clear_bit(JOB_WSTATS, &local->jobs);
Pavel Machekca078ba2005-09-03 15:56:57 -07007720 if (local->power.event) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007721 up(&local->sem);
7722 return;
7723 }
7724 readCapabilityRid(local, &cap_rid, 0);
7725 readStatusRid(local, &status_rid, 0);
7726 readStatsRid(local, &stats_rid, RID_STATS, 0);
7727 up(&local->sem);
7728
7729 /* The status */
Al Viro329e2c02007-12-20 22:58:57 -05007730 local->wstats.status = le16_to_cpu(status_rid.mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007731
Dan Williams41480af2005-05-10 09:45:51 -04007732 /* Signal quality and co */
7733 if (local->rssi) {
Al Viro329e2c02007-12-20 22:58:57 -05007734 local->wstats.qual.level =
7735 airo_rssi_to_dbm(local->rssi,
7736 le16_to_cpu(status_rid.sigQuality));
Dan Williams41480af2005-05-10 09:45:51 -04007737 /* normalizedSignalStrength appears to be a percentage */
Al Viro329e2c02007-12-20 22:58:57 -05007738 local->wstats.qual.qual =
7739 le16_to_cpu(status_rid.normalizedSignalStrength);
Dan Williams41480af2005-05-10 09:45:51 -04007740 } else {
Al Viro329e2c02007-12-20 22:58:57 -05007741 local->wstats.qual.level =
7742 (le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007743 local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid);
7744 }
Al Viro329e2c02007-12-20 22:58:57 -05007745 if (le16_to_cpu(status_rid.len) >= 124) {
Dan Williams41480af2005-05-10 09:45:51 -04007746 local->wstats.qual.noise = 0x100 - status_rid.noisedBm;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007747 local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007748 } else {
7749 local->wstats.qual.noise = 0;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007750 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 -07007751 }
7752
7753 /* Packets discarded in the wireless adapter due to wireless
7754 * specific problems */
Al Viroa23ace52007-12-19 22:24:16 -05007755 local->wstats.discard.nwid = le32_to_cpu(vals[56]) +
7756 le32_to_cpu(vals[57]) +
7757 le32_to_cpu(vals[58]); /* SSID Mismatch */
7758 local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */
7759 local->wstats.discard.fragment = le32_to_cpu(vals[30]);
7760 local->wstats.discard.retries = le32_to_cpu(vals[10]);
7761 local->wstats.discard.misc = le32_to_cpu(vals[1]) +
7762 le32_to_cpu(vals[32]);
7763 local->wstats.miss.beacon = le32_to_cpu(vals[34]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007764}
7765
Jouni Malinenff1d2762005-05-12 22:54:16 -04007766static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007767{
Wang Chenfaf39942008-10-14 13:30:33 +08007768 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007769
Dan Williams3c304952006-04-15 12:26:18 -04007770 if (!test_bit(JOB_WSTATS, &local->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007771 /* Get stats out of the card if available */
7772 if (down_trylock(&local->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04007773 set_bit(JOB_WSTATS, &local->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007774 wake_up_interruptible(&local->thr_wait);
7775 } else
7776 airo_read_wireless_stats(local);
7777 }
7778
7779 return &local->wstats;
7780}
Linus Torvalds1da177e2005-04-16 15:20:36 -07007781
7782#ifdef CISCO_EXT
7783/*
7784 * This just translates from driver IOCTL codes to the command codes to
7785 * feed to the radio's host interface. Things can be added/deleted
7786 * as needed. This represents the READ side of control I/O to
7787 * the card
7788 */
7789static int readrids(struct net_device *dev, aironet_ioctl *comp) {
7790 unsigned short ridcode;
7791 unsigned char *iobuf;
7792 int len;
Wang Chenfaf39942008-10-14 13:30:33 +08007793 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007794
7795 if (test_bit(FLAG_FLASHING, &ai->flags))
7796 return -EIO;
7797
7798 switch(comp->command)
7799 {
7800 case AIROGCAP: ridcode = RID_CAPABILITIES; break;
7801 case AIROGCFG: ridcode = RID_CONFIG;
7802 if (test_bit(FLAG_COMMIT, &ai->flags)) {
7803 disable_MAC (ai, 1);
7804 writeConfigRid (ai, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007805 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007806 }
7807 break;
7808 case AIROGSLIST: ridcode = RID_SSID; break;
7809 case AIROGVLIST: ridcode = RID_APLIST; break;
7810 case AIROGDRVNAM: ridcode = RID_DRVNAME; break;
7811 case AIROGEHTENC: ridcode = RID_ETHERENCAP; break;
7812 case AIROGWEPKTMP: ridcode = RID_WEP_TEMP;
7813 /* Only super-user can read WEP keys */
7814 if (!capable(CAP_NET_ADMIN))
7815 return -EPERM;
7816 break;
7817 case AIROGWEPKNV: ridcode = RID_WEP_PERM;
7818 /* Only super-user can read WEP keys */
7819 if (!capable(CAP_NET_ADMIN))
7820 return -EPERM;
7821 break;
7822 case AIROGSTAT: ridcode = RID_STATUS; break;
7823 case AIROGSTATSD32: ridcode = RID_STATSDELTA; break;
7824 case AIROGSTATSC32: ridcode = RID_STATS; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007825 case AIROGMICSTATS:
7826 if (copy_to_user(comp->data, &ai->micstats,
7827 min((int)comp->len,(int)sizeof(ai->micstats))))
7828 return -EFAULT;
7829 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007830 case AIRORRID: ridcode = comp->ridnum; break;
7831 default:
7832 return -EINVAL;
7833 break;
7834 }
7835
7836 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7837 return -ENOMEM;
7838
7839 PC4500_readrid(ai,ridcode,iobuf,RIDSIZE, 1);
7840 /* get the count of bytes in the rid docs say 1st 2 bytes is it.
7841 * then return it to the user
7842 * 9/22/2000 Honor user given length
7843 */
7844 len = comp->len;
7845
7846 if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) {
7847 kfree (iobuf);
7848 return -EFAULT;
7849 }
7850 kfree (iobuf);
7851 return 0;
7852}
7853
7854/*
7855 * Danger Will Robinson write the rids here
7856 */
7857
7858static int writerids(struct net_device *dev, aironet_ioctl *comp) {
Wang Chenfaf39942008-10-14 13:30:33 +08007859 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007860 int ridcode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007861 int enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007862 static int (* writer)(struct airo_info *, u16 rid, const void *, int, int);
7863 unsigned char *iobuf;
7864
7865 /* Only super-user can write RIDs */
7866 if (!capable(CAP_NET_ADMIN))
7867 return -EPERM;
7868
7869 if (test_bit(FLAG_FLASHING, &ai->flags))
7870 return -EIO;
7871
7872 ridcode = 0;
7873 writer = do_writerid;
7874
7875 switch(comp->command)
7876 {
7877 case AIROPSIDS: ridcode = RID_SSID; break;
7878 case AIROPCAP: ridcode = RID_CAPABILITIES; break;
7879 case AIROPAPLIST: ridcode = RID_APLIST; break;
7880 case AIROPCFG: ai->config.len = 0;
7881 clear_bit(FLAG_COMMIT, &ai->flags);
7882 ridcode = RID_CONFIG; break;
7883 case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break;
7884 case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break;
7885 case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break;
7886 case AIROPWEPKEY: ridcode = RID_WEP_TEMP; writer = PC4500_writerid;
7887 break;
7888 case AIROPLEAPUSR+1: ridcode = 0xFF2A; break;
7889 case AIROPLEAPUSR+2: ridcode = 0xFF2B; break;
7890
7891 /* this is not really a rid but a command given to the card
7892 * same with MAC off
7893 */
7894 case AIROPMACON:
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007895 if (enable_MAC(ai, 1) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007896 return -EIO;
7897 return 0;
7898
7899 /*
7900 * Evidently this code in the airo driver does not get a symbol
7901 * as disable_MAC. it's probably so short the compiler does not gen one.
7902 */
7903 case AIROPMACOFF:
7904 disable_MAC(ai, 1);
7905 return 0;
7906
7907 /* This command merely clears the counts does not actually store any data
7908 * only reads rid. But as it changes the cards state, I put it in the
7909 * writerid routines.
7910 */
7911 case AIROPSTCLR:
7912 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7913 return -ENOMEM;
7914
7915 PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDSIZE, 1);
7916
Linus Torvalds1da177e2005-04-16 15:20:36 -07007917 enabled = ai->micstats.enabled;
7918 memset(&ai->micstats,0,sizeof(ai->micstats));
7919 ai->micstats.enabled = enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007920
7921 if (copy_to_user(comp->data, iobuf,
7922 min((int)comp->len, (int)RIDSIZE))) {
7923 kfree (iobuf);
7924 return -EFAULT;
7925 }
7926 kfree (iobuf);
7927 return 0;
7928
7929 default:
7930 return -EOPNOTSUPP; /* Blarg! */
7931 }
7932 if(comp->len > RIDSIZE)
7933 return -EINVAL;
7934
7935 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7936 return -ENOMEM;
7937
7938 if (copy_from_user(iobuf,comp->data,comp->len)) {
7939 kfree (iobuf);
7940 return -EFAULT;
7941 }
7942
7943 if (comp->command == AIROPCFG) {
7944 ConfigRid *cfg = (ConfigRid *)iobuf;
7945
7946 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags))
Al Viro3eb9b412007-12-21 00:00:35 -05007947 cfg->opmode |= MODE_MIC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007948
Al Viro3eb9b412007-12-21 00:00:35 -05007949 if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007950 set_bit (FLAG_ADHOC, &ai->flags);
7951 else
7952 clear_bit (FLAG_ADHOC, &ai->flags);
7953 }
7954
7955 if((*writer)(ai, ridcode, iobuf,comp->len,1)) {
7956 kfree (iobuf);
7957 return -EIO;
7958 }
7959 kfree (iobuf);
7960 return 0;
7961}
7962
7963/*****************************************************************************
7964 * Ancillary flash / mod functions much black magic lurkes here *
7965 *****************************************************************************
7966 */
7967
7968/*
7969 * Flash command switch table
7970 */
7971
Jouni Malinenff1d2762005-05-12 22:54:16 -04007972static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007973 int z;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007974
7975 /* Only super-user can modify flash */
7976 if (!capable(CAP_NET_ADMIN))
7977 return -EPERM;
7978
7979 switch(comp->command)
7980 {
7981 case AIROFLSHRST:
Wang Chenfaf39942008-10-14 13:30:33 +08007982 return cmdreset((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007983
7984 case AIROFLSHSTFL:
Wang Chenfaf39942008-10-14 13:30:33 +08007985 if (!AIRO_FLASH(dev) &&
7986 (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007987 return -ENOMEM;
Wang Chenfaf39942008-10-14 13:30:33 +08007988 return setflashmode((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007989
7990 case AIROFLSHGCHR: /* Get char from aux */
7991 if(comp->len != sizeof(int))
7992 return -EINVAL;
7993 if (copy_from_user(&z,comp->data,comp->len))
7994 return -EFAULT;
Wang Chenfaf39942008-10-14 13:30:33 +08007995 return flashgchar((struct airo_info *)dev->ml_priv, z, 8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007996
7997 case AIROFLSHPCHR: /* Send char to card. */
7998 if(comp->len != sizeof(int))
7999 return -EINVAL;
8000 if (copy_from_user(&z,comp->data,comp->len))
8001 return -EFAULT;
Wang Chenfaf39942008-10-14 13:30:33 +08008002 return flashpchar((struct airo_info *)dev->ml_priv, z, 8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008003
8004 case AIROFLPUTBUF: /* Send 32k to card */
Wang Chenfaf39942008-10-14 13:30:33 +08008005 if (!AIRO_FLASH(dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008006 return -ENOMEM;
8007 if(comp->len > FLASHSIZE)
8008 return -EINVAL;
Wang Chenfaf39942008-10-14 13:30:33 +08008009 if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008010 return -EFAULT;
8011
Wang Chenfaf39942008-10-14 13:30:33 +08008012 flashputbuf((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008013 return 0;
8014
8015 case AIRORESTART:
Wang Chenfaf39942008-10-14 13:30:33 +08008016 if (flashrestart((struct airo_info *)dev->ml_priv, dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008017 return -EIO;
8018 return 0;
8019 }
8020 return -EINVAL;
8021}
8022
8023#define FLASH_COMMAND 0x7e7e
8024
8025/*
8026 * STEP 1)
8027 * Disable MAC and do soft reset on
8028 * card.
8029 */
8030
Jouni Malinenff1d2762005-05-12 22:54:16 -04008031static int cmdreset(struct airo_info *ai) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07008032 disable_MAC(ai, 1);
8033
8034 if(!waitbusy (ai)){
Dan Williams934d8bf2006-03-16 13:46:23 -05008035 airo_print_info(ai->dev->name, "Waitbusy hang before RESET");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008036 return -EBUSY;
8037 }
8038
8039 OUT4500(ai,COMMAND,CMD_SOFTRESET);
8040
8041 ssleep(1); /* WAS 600 12/7/00 */
8042
8043 if(!waitbusy (ai)){
Dan Williams934d8bf2006-03-16 13:46:23 -05008044 airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008045 return -EBUSY;
8046 }
8047 return 0;
8048}
8049
8050/* STEP 2)
8051 * Put the card in legendary flash
8052 * mode
8053 */
8054
Jouni Malinenff1d2762005-05-12 22:54:16 -04008055static int setflashmode (struct airo_info *ai) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07008056 set_bit (FLAG_FLASHING, &ai->flags);
8057
8058 OUT4500(ai, SWS0, FLASH_COMMAND);
8059 OUT4500(ai, SWS1, FLASH_COMMAND);
8060 if (probe) {
8061 OUT4500(ai, SWS0, FLASH_COMMAND);
8062 OUT4500(ai, COMMAND,0x10);
8063 } else {
8064 OUT4500(ai, SWS2, FLASH_COMMAND);
8065 OUT4500(ai, SWS3, FLASH_COMMAND);
8066 OUT4500(ai, COMMAND,0);
8067 }
8068 msleep(500); /* 500ms delay */
8069
8070 if(!waitbusy(ai)) {
8071 clear_bit (FLAG_FLASHING, &ai->flags);
Dan Williams934d8bf2006-03-16 13:46:23 -05008072 airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008073 return -EIO;
8074 }
8075 return 0;
8076}
8077
8078/* Put character to SWS0 wait for dwelltime
8079 * x 50us for echo .
8080 */
8081
Jouni Malinenff1d2762005-05-12 22:54:16 -04008082static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07008083 int echo;
8084 int waittime;
8085
8086 byte |= 0x8000;
8087
8088 if(dwelltime == 0 )
8089 dwelltime = 200;
8090
8091 waittime=dwelltime;
8092
8093 /* Wait for busy bit d15 to go false indicating buffer empty */
8094 while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) {
8095 udelay (50);
8096 waittime -= 50;
8097 }
8098
8099 /* timeout for busy clear wait */
8100 if(waittime <= 0 ){
Dan Williams934d8bf2006-03-16 13:46:23 -05008101 airo_print_info(ai->dev->name, "flash putchar busywait timeout!");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008102 return -EBUSY;
8103 }
8104
8105 /* Port is clear now write byte and wait for it to echo back */
8106 do {
8107 OUT4500(ai,SWS0,byte);
8108 udelay(50);
8109 dwelltime -= 50;
8110 echo = IN4500(ai,SWS1);
8111 } while (dwelltime >= 0 && echo != byte);
8112
8113 OUT4500(ai,SWS1,0);
8114
8115 return (echo == byte) ? 0 : -EIO;
8116}
8117
8118/*
8119 * Get a character from the card matching matchbyte
8120 * Step 3)
8121 */
Jouni Malinenff1d2762005-05-12 22:54:16 -04008122static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008123 int rchar;
8124 unsigned char rbyte=0;
8125
8126 do {
8127 rchar = IN4500(ai,SWS1);
8128
8129 if(dwelltime && !(0x8000 & rchar)){
8130 dwelltime -= 10;
8131 mdelay(10);
8132 continue;
8133 }
8134 rbyte = 0xff & rchar;
8135
8136 if( (rbyte == matchbyte) && (0x8000 & rchar) ){
8137 OUT4500(ai,SWS1,0);
8138 return 0;
8139 }
8140 if( rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar)
8141 break;
8142 OUT4500(ai,SWS1,0);
8143
8144 }while(dwelltime > 0);
8145 return -EIO;
8146}
8147
8148/*
8149 * Transfer 32k of firmware data from user buffer to our buffer and
8150 * send to the card
8151 */
8152
Jouni Malinenff1d2762005-05-12 22:54:16 -04008153static int flashputbuf(struct airo_info *ai){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008154 int nwords;
8155
8156 /* Write stuff */
8157 if (test_bit(FLAG_MPI,&ai->flags))
8158 memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE);
8159 else {
8160 OUT4500(ai,AUXPAGE,0x100);
8161 OUT4500(ai,AUXOFF,0);
8162
8163 for(nwords=0;nwords != FLASHSIZE / 2;nwords++){
8164 OUT4500(ai,AUXDATA,ai->flash[nwords] & 0xffff);
8165 }
8166 }
8167 OUT4500(ai,SWS0,0x8000);
8168
8169 return 0;
8170}
8171
8172/*
8173 *
8174 */
Jouni Malinenff1d2762005-05-12 22:54:16 -04008175static int flashrestart(struct airo_info *ai,struct net_device *dev){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008176 int i,status;
8177
8178 ssleep(1); /* Added 12/7/00 */
8179 clear_bit (FLAG_FLASHING, &ai->flags);
8180 if (test_bit(FLAG_MPI, &ai->flags)) {
8181 status = mpi_init_descriptors(ai);
8182 if (status != SUCCESS)
8183 return status;
8184 }
8185 status = setup_card(ai, dev->dev_addr, 1);
8186
8187 if (!test_bit(FLAG_MPI,&ai->flags))
8188 for( i = 0; i < MAX_FIDS; i++ ) {
8189 ai->fids[i] = transmit_allocate
Dan Williams15db2762006-03-16 13:46:27 -05008190 ( ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07008191 }
8192
8193 ssleep(1); /* Added 12/7/00 */
8194 return status;
8195}
8196#endif /* CISCO_EXT */
8197
8198/*
8199 This program is free software; you can redistribute it and/or
8200 modify it under the terms of the GNU General Public License
8201 as published by the Free Software Foundation; either version 2
8202 of the License, or (at your option) any later version.
8203
8204 This program is distributed in the hope that it will be useful,
8205 but WITHOUT ANY WARRANTY; without even the implied warranty of
8206 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8207 GNU General Public License for more details.
8208
8209 In addition:
8210
8211 Redistribution and use in source and binary forms, with or without
8212 modification, are permitted provided that the following conditions
8213 are met:
8214
8215 1. Redistributions of source code must retain the above copyright
8216 notice, this list of conditions and the following disclaimer.
8217 2. Redistributions in binary form must reproduce the above copyright
8218 notice, this list of conditions and the following disclaimer in the
8219 documentation and/or other materials provided with the distribution.
8220 3. The name of the author may not be used to endorse or promote
8221 products derived from this software without specific prior written
8222 permission.
8223
8224 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
8225 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8226 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
8227 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
8228 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
8229 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
8230 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
8231 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
8232 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
8233 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
8234 POSSIBILITY OF SUCH DAMAGE.
8235*/
8236
8237module_init(airo_init_module);
8238module_exit(airo_cleanup_module);