blob: e139a18c6acd2c53b4be05d513b22c8e30193f60 [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>
Dan Williams3c304952006-04-15 12:26:18 -040050#include <net/ieee80211.h>
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -070051#include <linux/kthread.h>
Nigel Cunningham7dfb7102006-12-06 20:34:23 -080052#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
Adrian Bunkd3808762005-11-05 17:42:27 +010054#include "airo.h"
55
Michal Schmidt1138c372007-06-29 15:33:41 +020056#define DRV_NAME "airo"
57
Linus Torvalds1da177e2005-04-16 15:20:36 -070058#ifdef CONFIG_PCI
59static struct pci_device_id card_ids[] = {
60 { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, },
61 { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID },
62 { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, },
63 { 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, },
64 { 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, },
65 { 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, },
66 { 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, },
67 { 0, }
68};
69MODULE_DEVICE_TABLE(pci, card_ids);
70
71static int airo_pci_probe(struct pci_dev *, const struct pci_device_id *);
72static void airo_pci_remove(struct pci_dev *);
Pavel Machek05adc3b2005-04-16 15:25:25 -070073static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state);
Linus Torvalds1da177e2005-04-16 15:20:36 -070074static int airo_pci_resume(struct pci_dev *pdev);
75
76static struct pci_driver airo_driver = {
Michal Schmidt1138c372007-06-29 15:33:41 +020077 .name = DRV_NAME,
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 .id_table = card_ids,
79 .probe = airo_pci_probe,
80 .remove = __devexit_p(airo_pci_remove),
81 .suspend = airo_pci_suspend,
82 .resume = airo_pci_resume,
83};
84#endif /* CONFIG_PCI */
85
86/* Include Wireless Extension definition and check version - Jean II */
87#include <linux/wireless.h>
88#define WIRELESS_SPY // enable iwspy support
89#include <net/iw_handler.h> // New driver API
90
91#define CISCO_EXT // enable Cisco extensions
92#ifdef CISCO_EXT
93#include <linux/delay.h>
94#endif
95
Linus Torvalds1da177e2005-04-16 15:20:36 -070096/* Hack to do some power saving */
97#define POWER_ON_DOWN
98
99/* As you can see this list is HUGH!
100 I really don't know what a lot of these counts are about, but they
101 are all here for completeness. If the IGNLABEL macro is put in
102 infront of the label, that statistic will not be included in the list
103 of statistics in the /proc filesystem */
104
105#define IGNLABEL(comment) NULL
106static char *statsLabels[] = {
107 "RxOverrun",
108 IGNLABEL("RxPlcpCrcErr"),
109 IGNLABEL("RxPlcpFormatErr"),
110 IGNLABEL("RxPlcpLengthErr"),
111 "RxMacCrcErr",
112 "RxMacCrcOk",
113 "RxWepErr",
114 "RxWepOk",
115 "RetryLong",
116 "RetryShort",
117 "MaxRetries",
118 "NoAck",
119 "NoCts",
120 "RxAck",
121 "RxCts",
122 "TxAck",
123 "TxRts",
124 "TxCts",
125 "TxMc",
126 "TxBc",
127 "TxUcFrags",
128 "TxUcPackets",
129 "TxBeacon",
130 "RxBeacon",
131 "TxSinColl",
132 "TxMulColl",
133 "DefersNo",
134 "DefersProt",
135 "DefersEngy",
136 "DupFram",
137 "RxFragDisc",
138 "TxAged",
139 "RxAged",
140 "LostSync-MaxRetry",
141 "LostSync-MissedBeacons",
142 "LostSync-ArlExceeded",
143 "LostSync-Deauth",
144 "LostSync-Disassoced",
145 "LostSync-TsfTiming",
146 "HostTxMc",
147 "HostTxBc",
148 "HostTxUc",
149 "HostTxFail",
150 "HostRxMc",
151 "HostRxBc",
152 "HostRxUc",
153 "HostRxDiscard",
154 IGNLABEL("HmacTxMc"),
155 IGNLABEL("HmacTxBc"),
156 IGNLABEL("HmacTxUc"),
157 IGNLABEL("HmacTxFail"),
158 IGNLABEL("HmacRxMc"),
159 IGNLABEL("HmacRxBc"),
160 IGNLABEL("HmacRxUc"),
161 IGNLABEL("HmacRxDiscard"),
162 IGNLABEL("HmacRxAccepted"),
163 "SsidMismatch",
164 "ApMismatch",
165 "RatesMismatch",
166 "AuthReject",
167 "AuthTimeout",
168 "AssocReject",
169 "AssocTimeout",
170 IGNLABEL("ReasonOutsideTable"),
171 IGNLABEL("ReasonStatus1"),
172 IGNLABEL("ReasonStatus2"),
173 IGNLABEL("ReasonStatus3"),
174 IGNLABEL("ReasonStatus4"),
175 IGNLABEL("ReasonStatus5"),
176 IGNLABEL("ReasonStatus6"),
177 IGNLABEL("ReasonStatus7"),
178 IGNLABEL("ReasonStatus8"),
179 IGNLABEL("ReasonStatus9"),
180 IGNLABEL("ReasonStatus10"),
181 IGNLABEL("ReasonStatus11"),
182 IGNLABEL("ReasonStatus12"),
183 IGNLABEL("ReasonStatus13"),
184 IGNLABEL("ReasonStatus14"),
185 IGNLABEL("ReasonStatus15"),
186 IGNLABEL("ReasonStatus16"),
187 IGNLABEL("ReasonStatus17"),
188 IGNLABEL("ReasonStatus18"),
189 IGNLABEL("ReasonStatus19"),
190 "RxMan",
191 "TxMan",
192 "RxRefresh",
193 "TxRefresh",
194 "RxPoll",
195 "TxPoll",
196 "HostRetries",
197 "LostSync-HostReq",
198 "HostTxBytes",
199 "HostRxBytes",
200 "ElapsedUsec",
201 "ElapsedSec",
202 "LostSyncBetterAP",
203 "PrivacyMismatch",
204 "Jammed",
205 "DiscRxNotWepped",
206 "PhyEleMismatch",
207 (char*)-1 };
208#ifndef RUN_AT
209#define RUN_AT(x) (jiffies+(x))
210#endif
211
212
213/* These variables are for insmod, since it seems that the rates
214 can only be set in setup_card. Rates should be a comma separated
215 (no spaces) list of rates (up to 8). */
216
217static int rates[8];
218static int basic_rate;
219static char *ssids[3];
220
221static int io[4];
222static int irq[4];
223
224static
225int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at.
226 0 means no limit. For old cards this was 4 */
227
228static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */
229static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read
230 the bap, needed on some older cards and buses. */
231static int adhoc;
232
233static int probe = 1;
234
235static int proc_uid /* = 0 */;
236
237static int proc_gid /* = 0 */;
238
239static int airo_perm = 0555;
240
241static int proc_perm = 0644;
242
243MODULE_AUTHOR("Benjamin Reed");
244MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet \
Bill Nottinghamd73ae552007-07-27 19:43:17 -0400245cards. Direct support for ISA/PCI/MPI cards and support \
246for PCMCIA when used with airo_cs.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247MODULE_LICENSE("Dual BSD/GPL");
248MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350");
249module_param_array(io, int, NULL, 0);
250module_param_array(irq, int, NULL, 0);
251module_param(basic_rate, int, 0);
252module_param_array(rates, int, NULL, 0);
253module_param_array(ssids, charp, NULL, 0);
254module_param(auto_wep, int, 0);
255MODULE_PARM_DESC(auto_wep, "If non-zero, the driver will keep looping through \
256the authentication options until an association is made. The value of \
257auto_wep is number of the wep keys to check. A value of 2 will try using \
258the key at index 0 and index 1.");
259module_param(aux_bap, int, 0);
260MODULE_PARM_DESC(aux_bap, "If non-zero, the driver will switch into a mode \
261than seems to work better for older cards with some older buses. Before \
262switching it checks that the switch is needed.");
263module_param(maxencrypt, int, 0);
264MODULE_PARM_DESC(maxencrypt, "The maximum speed that the card can do \
265encryption. Units are in 512kbs. Zero (default) means there is no limit. \
266Older cards used to be limited to 2mbs (4).");
267module_param(adhoc, int, 0);
268MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode.");
269module_param(probe, int, 0);
270MODULE_PARM_DESC(probe, "If zero, the driver won't start the card.");
271
272module_param(proc_uid, int, 0);
273MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to.");
274module_param(proc_gid, int, 0);
275MODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to.");
276module_param(airo_perm, int, 0);
277MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet.");
278module_param(proc_perm, int, 0);
279MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc");
280
281/* This is a kind of sloppy hack to get this information to OUT4500 and
282 IN4500. I would be extremely interested in the situation where this
283 doesn't work though!!! */
284static int do8bitIO = 0;
285
286/* Return codes */
287#define SUCCESS 0
288#define ERROR -1
289#define NO_PACKET -2
290
291/* Commands */
292#define NOP2 0x0000
293#define MAC_ENABLE 0x0001
294#define MAC_DISABLE 0x0002
295#define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */
296#define CMD_SOFTRESET 0x0004
297#define HOSTSLEEP 0x0005
298#define CMD_MAGIC_PKT 0x0006
299#define CMD_SETWAKEMASK 0x0007
300#define CMD_READCFG 0x0008
301#define CMD_SETMODE 0x0009
302#define CMD_ALLOCATETX 0x000a
303#define CMD_TRANSMIT 0x000b
304#define CMD_DEALLOCATETX 0x000c
305#define NOP 0x0010
306#define CMD_WORKAROUND 0x0011
307#define CMD_ALLOCATEAUX 0x0020
308#define CMD_ACCESS 0x0021
309#define CMD_PCIBAP 0x0022
310#define CMD_PCIAUX 0x0023
311#define CMD_ALLOCBUF 0x0028
312#define CMD_GETTLV 0x0029
313#define CMD_PUTTLV 0x002a
314#define CMD_DELTLV 0x002b
315#define CMD_FINDNEXTTLV 0x002c
316#define CMD_PSPNODES 0x0030
317#define CMD_SETCW 0x0031
318#define CMD_SETPCF 0x0032
319#define CMD_SETPHYREG 0x003e
320#define CMD_TXTEST 0x003f
321#define MAC_ENABLETX 0x0101
322#define CMD_LISTBSS 0x0103
323#define CMD_SAVECFG 0x0108
324#define CMD_ENABLEAUX 0x0111
325#define CMD_WRITERID 0x0121
326#define CMD_USEPSPNODES 0x0130
327#define MAC_ENABLERX 0x0201
328
329/* Command errors */
330#define ERROR_QUALIF 0x00
331#define ERROR_ILLCMD 0x01
332#define ERROR_ILLFMT 0x02
333#define ERROR_INVFID 0x03
334#define ERROR_INVRID 0x04
335#define ERROR_LARGE 0x05
336#define ERROR_NDISABL 0x06
337#define ERROR_ALLOCBSY 0x07
338#define ERROR_NORD 0x0B
339#define ERROR_NOWR 0x0C
340#define ERROR_INVFIDTX 0x0D
341#define ERROR_TESTACT 0x0E
342#define ERROR_TAGNFND 0x12
343#define ERROR_DECODE 0x20
344#define ERROR_DESCUNAV 0x21
345#define ERROR_BADLEN 0x22
346#define ERROR_MODE 0x80
347#define ERROR_HOP 0x81
348#define ERROR_BINTER 0x82
349#define ERROR_RXMODE 0x83
350#define ERROR_MACADDR 0x84
351#define ERROR_RATES 0x85
352#define ERROR_ORDER 0x86
353#define ERROR_SCAN 0x87
354#define ERROR_AUTH 0x88
355#define ERROR_PSMODE 0x89
356#define ERROR_RTYPE 0x8A
357#define ERROR_DIVER 0x8B
358#define ERROR_SSID 0x8C
359#define ERROR_APLIST 0x8D
360#define ERROR_AUTOWAKE 0x8E
361#define ERROR_LEAP 0x8F
362
363/* Registers */
364#define COMMAND 0x00
365#define PARAM0 0x02
366#define PARAM1 0x04
367#define PARAM2 0x06
368#define STATUS 0x08
369#define RESP0 0x0a
370#define RESP1 0x0c
371#define RESP2 0x0e
372#define LINKSTAT 0x10
373#define SELECT0 0x18
374#define OFFSET0 0x1c
375#define RXFID 0x20
376#define TXALLOCFID 0x22
377#define TXCOMPLFID 0x24
378#define DATA0 0x36
379#define EVSTAT 0x30
380#define EVINTEN 0x32
381#define EVACK 0x34
382#define SWS0 0x28
383#define SWS1 0x2a
384#define SWS2 0x2c
385#define SWS3 0x2e
386#define AUXPAGE 0x3A
387#define AUXOFF 0x3C
388#define AUXDATA 0x3E
389
390#define FID_TX 1
391#define FID_RX 2
392/* Offset into aux memory for descriptors */
393#define AUX_OFFSET 0x800
394/* Size of allocated packets */
395#define PKTSIZE 1840
396#define RIDSIZE 2048
397/* Size of the transmit queue */
398#define MAXTXQ 64
399
400/* BAP selectors */
401#define BAP0 0 // Used for receiving packets
402#define BAP1 2 // Used for xmiting packets and working with RIDS
403
404/* Flags */
405#define COMMAND_BUSY 0x8000
406
407#define BAP_BUSY 0x8000
408#define BAP_ERR 0x4000
409#define BAP_DONE 0x2000
410
411#define PROMISC 0xffff
412#define NOPROMISC 0x0000
413
414#define EV_CMD 0x10
415#define EV_CLEARCOMMANDBUSY 0x4000
416#define EV_RX 0x01
417#define EV_TX 0x02
418#define EV_TXEXC 0x04
419#define EV_ALLOC 0x08
420#define EV_LINK 0x80
421#define EV_AWAKE 0x100
422#define EV_TXCPY 0x400
423#define EV_UNKNOWN 0x800
424#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */
425#define EV_AWAKEN 0x2000
426#define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC)
427
428#ifdef CHECK_UNKNOWN_INTS
429#define IGNORE_INTS ( EV_CMD | EV_UNKNOWN)
430#else
431#define IGNORE_INTS (~STATUS_INTS)
432#endif
433
434/* RID TYPES */
435#define RID_RW 0x20
436
437/* The RIDs */
438#define RID_CAPABILITIES 0xFF00
439#define RID_APINFO 0xFF01
440#define RID_RADIOINFO 0xFF02
441#define RID_UNKNOWN3 0xFF03
442#define RID_RSSI 0xFF04
443#define RID_CONFIG 0xFF10
444#define RID_SSID 0xFF11
445#define RID_APLIST 0xFF12
446#define RID_DRVNAME 0xFF13
447#define RID_ETHERENCAP 0xFF14
448#define RID_WEP_TEMP 0xFF15
449#define RID_WEP_PERM 0xFF16
450#define RID_MODULATION 0xFF17
451#define RID_OPTIONS 0xFF18
452#define RID_ACTUALCONFIG 0xFF20 /*readonly*/
453#define RID_FACTORYCONFIG 0xFF21
454#define RID_UNKNOWN22 0xFF22
455#define RID_LEAPUSERNAME 0xFF23
456#define RID_LEAPPASSWORD 0xFF24
457#define RID_STATUS 0xFF50
458#define RID_BEACON_HST 0xFF51
459#define RID_BUSY_HST 0xFF52
460#define RID_RETRIES_HST 0xFF53
461#define RID_UNKNOWN54 0xFF54
462#define RID_UNKNOWN55 0xFF55
463#define RID_UNKNOWN56 0xFF56
464#define RID_MIC 0xFF57
465#define RID_STATS16 0xFF60
466#define RID_STATS16DELTA 0xFF61
467#define RID_STATS16DELTACLEAR 0xFF62
468#define RID_STATS 0xFF68
469#define RID_STATSDELTA 0xFF69
470#define RID_STATSDELTACLEAR 0xFF6A
471#define RID_ECHOTEST_RID 0xFF70
472#define RID_ECHOTEST_RESULTS 0xFF71
473#define RID_BSSLISTFIRST 0xFF72
474#define RID_BSSLISTNEXT 0xFF73
Dan Williams3c304952006-04-15 12:26:18 -0400475#define RID_WPA_BSSLISTFIRST 0xFF74
476#define RID_WPA_BSSLISTNEXT 0xFF75
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
478typedef struct {
479 u16 cmd;
480 u16 parm0;
481 u16 parm1;
482 u16 parm2;
483} Cmd;
484
485typedef struct {
486 u16 status;
487 u16 rsp0;
488 u16 rsp1;
489 u16 rsp2;
490} Resp;
491
492/*
493 * Rids and endian-ness: The Rids will always be in cpu endian, since
494 * this all the patches from the big-endian guys end up doing that.
495 * so all rid access should use the read/writeXXXRid routines.
496 */
497
498/* This is redundant for x86 archs, but it seems necessary for ARM */
499#pragma pack(1)
500
501/* This structure came from an email sent to me from an engineer at
502 aironet for inclusion into this driver */
503typedef struct {
Al Viro4293ea32007-12-19 19:21:51 -0500504 __le16 len;
505 __le16 kindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 u8 mac[ETH_ALEN];
Al Viro4293ea32007-12-19 19:21:51 -0500507 __le16 klen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 u8 key[16];
509} WepKeyRid;
510
511/* These structures are from the Aironet's PC4500 Developers Manual */
512typedef struct {
Al Viro0dd22122007-12-17 16:11:57 -0500513 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 u8 ssid[32];
515} Ssid;
516
517typedef struct {
Al Viro0dd22122007-12-17 16:11:57 -0500518 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 Ssid ssids[3];
520} SsidRid;
521
522typedef struct {
523 u16 len;
524 u16 modulation;
525#define MOD_DEFAULT 0
526#define MOD_CCK 1
527#define MOD_MOK 2
528} ModulationRid;
529
530typedef struct {
531 u16 len; /* sizeof(ConfigRid) */
532 u16 opmode; /* operating mode */
533#define MODE_STA_IBSS 0
534#define MODE_STA_ESS 1
535#define MODE_AP 2
536#define MODE_AP_RPTR 3
537#define MODE_ETHERNET_HOST (0<<8) /* rx payloads converted */
538#define MODE_LLC_HOST (1<<8) /* rx payloads left as is */
539#define MODE_AIRONET_EXTEND (1<<9) /* enable Aironet extenstions */
540#define MODE_AP_INTERFACE (1<<10) /* enable ap interface extensions */
541#define MODE_ANTENNA_ALIGN (1<<11) /* enable antenna alignment */
542#define MODE_ETHER_LLC (1<<12) /* enable ethernet LLC */
543#define MODE_LEAF_NODE (1<<13) /* enable leaf node bridge */
544#define MODE_CF_POLLABLE (1<<14) /* enable CF pollable */
545#define MODE_MIC (1<<15) /* enable MIC */
546 u16 rmode; /* receive mode */
547#define RXMODE_BC_MC_ADDR 0
548#define RXMODE_BC_ADDR 1 /* ignore multicasts */
549#define RXMODE_ADDR 2 /* ignore multicast and broadcast */
550#define RXMODE_RFMON 3 /* wireless monitor mode */
551#define RXMODE_RFMON_ANYBSS 4
552#define RXMODE_LANMON 5 /* lan style monitor -- data packets only */
553#define RXMODE_DISABLE_802_3_HEADER (1<<8) /* disables 802.3 header on rx */
554#define RXMODE_NORMALIZED_RSSI (1<<9) /* return normalized RSSI */
555 u16 fragThresh;
556 u16 rtsThres;
557 u8 macAddr[ETH_ALEN];
558 u8 rates[8];
559 u16 shortRetryLimit;
560 u16 longRetryLimit;
561 u16 txLifetime; /* in kusec */
562 u16 rxLifetime; /* in kusec */
563 u16 stationary;
564 u16 ordering;
565 u16 u16deviceType; /* for overriding device type */
566 u16 cfpRate;
567 u16 cfpDuration;
568 u16 _reserved1[3];
569 /*---------- Scanning/Associating ----------*/
570 u16 scanMode;
571#define SCANMODE_ACTIVE 0
572#define SCANMODE_PASSIVE 1
573#define SCANMODE_AIROSCAN 2
574 u16 probeDelay; /* in kusec */
575 u16 probeEnergyTimeout; /* in kusec */
576 u16 probeResponseTimeout;
577 u16 beaconListenTimeout;
578 u16 joinNetTimeout;
579 u16 authTimeout;
580 u16 authType;
581#define AUTH_OPEN 0x1
582#define AUTH_ENCRYPT 0x101
583#define AUTH_SHAREDKEY 0x102
584#define AUTH_ALLOW_UNENCRYPTED 0x200
585 u16 associationTimeout;
586 u16 specifiedApTimeout;
587 u16 offlineScanInterval;
588 u16 offlineScanDuration;
589 u16 linkLossDelay;
590 u16 maxBeaconLostTime;
591 u16 refreshInterval;
592#define DISABLE_REFRESH 0xFFFF
593 u16 _reserved1a[1];
594 /*---------- Power save operation ----------*/
595 u16 powerSaveMode;
596#define POWERSAVE_CAM 0
597#define POWERSAVE_PSP 1
598#define POWERSAVE_PSPCAM 2
599 u16 sleepForDtims;
600 u16 listenInterval;
601 u16 fastListenInterval;
602 u16 listenDecay;
603 u16 fastListenDelay;
604 u16 _reserved2[2];
605 /*---------- Ap/Ibss config items ----------*/
606 u16 beaconPeriod;
607 u16 atimDuration;
608 u16 hopPeriod;
609 u16 channelSet;
610 u16 channel;
611 u16 dtimPeriod;
612 u16 bridgeDistance;
613 u16 radioID;
614 /*---------- Radio configuration ----------*/
615 u16 radioType;
616#define RADIOTYPE_DEFAULT 0
617#define RADIOTYPE_802_11 1
618#define RADIOTYPE_LEGACY 2
619 u8 rxDiversity;
620 u8 txDiversity;
621 u16 txPower;
622#define TXPOWER_DEFAULT 0
623 u16 rssiThreshold;
624#define RSSI_DEFAULT 0
625 u16 modulation;
626#define PREAMBLE_AUTO 0
627#define PREAMBLE_LONG 1
628#define PREAMBLE_SHORT 2
629 u16 preamble;
630 u16 homeProduct;
631 u16 radioSpecific;
632 /*---------- Aironet Extensions ----------*/
633 u8 nodeName[16];
634 u16 arlThreshold;
635 u16 arlDecay;
636 u16 arlDelay;
637 u16 _reserved4[1];
638 /*---------- Aironet Extensions ----------*/
639 u8 magicAction;
640#define MAGIC_ACTION_STSCHG 1
641#define MAGIC_ACTION_RESUME 2
642#define MAGIC_IGNORE_MCAST (1<<8)
643#define MAGIC_IGNORE_BCAST (1<<9)
644#define MAGIC_SWITCH_TO_PSP (0<<10)
645#define MAGIC_STAY_IN_CAM (1<<10)
646 u8 magicControl;
647 u16 autoWake;
648} ConfigRid;
649
650typedef struct {
651 u16 len;
652 u8 mac[ETH_ALEN];
653 u16 mode;
654 u16 errorCode;
655 u16 sigQuality;
656 u16 SSIDlen;
657 char SSID[32];
658 char apName[16];
659 u8 bssid[4][ETH_ALEN];
660 u16 beaconPeriod;
661 u16 dimPeriod;
662 u16 atimDuration;
663 u16 hopPeriod;
664 u16 channelSet;
665 u16 channel;
666 u16 hopsToBackbone;
667 u16 apTotalLoad;
668 u16 generatedLoad;
669 u16 accumulatedArl;
670 u16 signalQuality;
671 u16 currentXmitRate;
672 u16 apDevExtensions;
673 u16 normalizedSignalStrength;
674 u16 shortPreamble;
675 u8 apIP[4];
676 u8 noisePercent; /* Noise percent in last second */
677 u8 noisedBm; /* Noise dBm in last second */
678 u8 noiseAvePercent; /* Noise percent in last minute */
679 u8 noiseAvedBm; /* Noise dBm in last minute */
680 u8 noiseMaxPercent; /* Highest noise percent in last minute */
681 u8 noiseMaxdBm; /* Highest noise dbm in last minute */
682 u16 load;
683 u8 carrier[4];
684 u16 assocStatus;
685#define STAT_NOPACKETS 0
686#define STAT_NOCARRIERSET 10
687#define STAT_GOTCARRIERSET 11
688#define STAT_WRONGSSID 20
689#define STAT_BADCHANNEL 25
690#define STAT_BADBITRATES 30
691#define STAT_BADPRIVACY 35
692#define STAT_APFOUND 40
693#define STAT_APREJECTED 50
694#define STAT_AUTHENTICATING 60
695#define STAT_DEAUTHENTICATED 61
696#define STAT_AUTHTIMEOUT 62
697#define STAT_ASSOCIATING 70
698#define STAT_DEASSOCIATED 71
699#define STAT_ASSOCTIMEOUT 72
700#define STAT_NOTAIROAP 73
701#define STAT_ASSOCIATED 80
702#define STAT_LEAPING 90
703#define STAT_LEAPFAILED 91
704#define STAT_LEAPTIMEDOUT 92
705#define STAT_LEAPCOMPLETE 93
706} StatusRid;
707
708typedef struct {
Al Viroa23ace52007-12-19 22:24:16 -0500709 __le16 len;
710 __le16 spacer;
711 __le32 vals[100];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712} StatsRid;
713
714
715typedef struct {
716 u16 len;
717 u8 ap[4][ETH_ALEN];
718} APListRid;
719
720typedef struct {
Al Viro56d81bd2007-12-20 17:18:35 -0500721 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 char oui[3];
723 char zero;
Al Viro56d81bd2007-12-20 17:18:35 -0500724 __le16 prodNum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 char manName[32];
726 char prodName[16];
727 char prodVer[8];
728 char factoryAddr[ETH_ALEN];
729 char aironetAddr[ETH_ALEN];
Al Viro56d81bd2007-12-20 17:18:35 -0500730 __le16 radioType;
731 __le16 country;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 char callid[ETH_ALEN];
733 char supportedRates[8];
734 char rxDiversity;
735 char txDiversity;
Al Viro56d81bd2007-12-20 17:18:35 -0500736 __le16 txPowerLevels[8];
737 __le16 hardVer;
738 __le16 hardCap;
739 __le16 tempRange;
740 __le16 softVer;
741 __le16 softSubVer;
742 __le16 interfaceVer;
743 __le16 softCap;
744 __le16 bootBlockVer;
745 __le16 requiredHard;
746 __le16 extSoftCap;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747} CapabilityRid;
748
Dan Williams3c304952006-04-15 12:26:18 -0400749
750/* Only present on firmware >= 5.30.17 */
751typedef struct {
Al Viro17e70492007-12-19 18:56:37 -0500752 __le16 unknown[4];
Dan Williams3c304952006-04-15 12:26:18 -0400753 u8 fixed[12]; /* WLAN management frame */
754 u8 iep[624];
755} BSSListRidExtra;
756
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757typedef struct {
Al Viro17e70492007-12-19 18:56:37 -0500758 __le16 len;
759 __le16 index; /* First is 0 and 0xffff means end of list */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760#define RADIO_FH 1 /* Frequency hopping radio type */
761#define RADIO_DS 2 /* Direct sequence radio type */
762#define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */
Al Viro17e70492007-12-19 18:56:37 -0500763 __le16 radioType;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 u8 bssid[ETH_ALEN]; /* Mac address of the BSS */
765 u8 zero;
766 u8 ssidLen;
767 u8 ssid[32];
Al Viro17e70492007-12-19 18:56:37 -0500768 __le16 dBm;
769#define CAP_ESS cpu_to_le16(1<<0)
770#define CAP_IBSS cpu_to_le16(1<<1)
771#define CAP_PRIVACY cpu_to_le16(1<<4)
772#define CAP_SHORTHDR cpu_to_le16(1<<5)
773 __le16 cap;
774 __le16 beaconInterval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 u8 rates[8]; /* Same as rates for config rid */
776 struct { /* For frequency hopping only */
Al Viro17e70492007-12-19 18:56:37 -0500777 __le16 dwell;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 u8 hopSet;
779 u8 hopPattern;
780 u8 hopIndex;
781 u8 fill;
782 } fh;
Al Viro17e70492007-12-19 18:56:37 -0500783 __le16 dsChannel;
784 __le16 atimWindow;
Dan Williams3c304952006-04-15 12:26:18 -0400785
786 /* Only present on firmware >= 5.30.17 */
787 BSSListRidExtra extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788} BSSListRid;
789
790typedef struct {
Dan Williams9e75af32006-03-16 13:46:29 -0500791 BSSListRid bss;
792 struct list_head list;
793} BSSListElement;
794
795typedef struct {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 u8 rssipct;
797 u8 rssidBm;
798} tdsRssiEntry;
799
800typedef struct {
801 u16 len;
802 tdsRssiEntry x[256];
803} tdsRssiRid;
804
805typedef struct {
806 u16 len;
807 u16 state;
808 u16 multicastValid;
809 u8 multicast[16];
810 u16 unicastValid;
811 u8 unicast[16];
812} MICRid;
813
814typedef struct {
Al Viro593c2b92007-12-17 15:09:34 -0500815 __be16 typelen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
817 union {
818 u8 snap[8];
819 struct {
820 u8 dsap;
821 u8 ssap;
822 u8 control;
823 u8 orgcode[3];
824 u8 fieldtype[2];
825 } llc;
826 } u;
Al Viro593c2b92007-12-17 15:09:34 -0500827 __be32 mic;
828 __be32 seq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829} MICBuffer;
830
831typedef struct {
832 u8 da[ETH_ALEN];
833 u8 sa[ETH_ALEN];
834} etherHead;
835
836#pragma pack()
837
838#define TXCTL_TXOK (1<<1) /* report if tx is ok */
839#define TXCTL_TXEX (1<<2) /* report if tx fails */
840#define TXCTL_802_3 (0<<3) /* 802.3 packet */
841#define TXCTL_802_11 (1<<3) /* 802.11 mac packet */
842#define TXCTL_ETHERNET (0<<4) /* payload has ethertype */
843#define TXCTL_LLC (1<<4) /* payload is llc */
844#define TXCTL_RELEASE (0<<5) /* release after completion */
845#define TXCTL_NORELEASE (1<<5) /* on completion returns to host */
846
847#define BUSY_FID 0x10000
848
849#ifdef CISCO_EXT
850#define AIROMAGIC 0xa55a
851/* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */
852#ifdef SIOCIWFIRSTPRIV
853#ifdef SIOCDEVPRIVATE
854#define AIROOLDIOCTL SIOCDEVPRIVATE
855#define AIROOLDIDIFC AIROOLDIOCTL + 1
856#endif /* SIOCDEVPRIVATE */
857#else /* SIOCIWFIRSTPRIV */
858#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
859#endif /* SIOCIWFIRSTPRIV */
860/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably
861 * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root
862 * only and don't return the modified struct ifreq to the application which
863 * is usually a problem. - Jean II */
864#define AIROIOCTL SIOCIWFIRSTPRIV
865#define AIROIDIFC AIROIOCTL + 1
866
867/* Ioctl constants to be used in airo_ioctl.command */
868
869#define AIROGCAP 0 // Capability rid
870#define AIROGCFG 1 // USED A LOT
871#define AIROGSLIST 2 // System ID list
872#define AIROGVLIST 3 // List of specified AP's
873#define AIROGDRVNAM 4 // NOTUSED
874#define AIROGEHTENC 5 // NOTUSED
875#define AIROGWEPKTMP 6
876#define AIROGWEPKNV 7
877#define AIROGSTAT 8
878#define AIROGSTATSC32 9
879#define AIROGSTATSD32 10
880#define AIROGMICRID 11
881#define AIROGMICSTATS 12
882#define AIROGFLAGS 13
883#define AIROGID 14
884#define AIRORRID 15
885#define AIRORSWVERSION 17
886
887/* Leave gap of 40 commands after AIROGSTATSD32 for future */
888
889#define AIROPCAP AIROGSTATSD32 + 40
890#define AIROPVLIST AIROPCAP + 1
891#define AIROPSLIST AIROPVLIST + 1
892#define AIROPCFG AIROPSLIST + 1
893#define AIROPSIDS AIROPCFG + 1
894#define AIROPAPLIST AIROPSIDS + 1
895#define AIROPMACON AIROPAPLIST + 1 /* Enable mac */
896#define AIROPMACOFF AIROPMACON + 1 /* Disable mac */
897#define AIROPSTCLR AIROPMACOFF + 1
898#define AIROPWEPKEY AIROPSTCLR + 1
899#define AIROPWEPKEYNV AIROPWEPKEY + 1
900#define AIROPLEAPPWD AIROPWEPKEYNV + 1
901#define AIROPLEAPUSR AIROPLEAPPWD + 1
902
903/* Flash codes */
904
905#define AIROFLSHRST AIROPWEPKEYNV + 40
906#define AIROFLSHGCHR AIROFLSHRST + 1
907#define AIROFLSHSTFL AIROFLSHGCHR + 1
908#define AIROFLSHPCHR AIROFLSHSTFL + 1
909#define AIROFLPUTBUF AIROFLSHPCHR + 1
910#define AIRORESTART AIROFLPUTBUF + 1
911
912#define FLASHSIZE 32768
913#define AUXMEMSIZE (256 * 1024)
914
915typedef struct aironet_ioctl {
916 unsigned short command; // What to do
917 unsigned short len; // Len of data
918 unsigned short ridnum; // rid number
919 unsigned char __user *data; // d-data
920} aironet_ioctl;
921
Domen Puncer62595eb2005-06-20 23:54:37 +0200922static char swversion[] = "2.1";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923#endif /* CISCO_EXT */
924
925#define NUM_MODULES 2
926#define MIC_MSGLEN_MAX 2400
927#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX
Dan Williams15db2762006-03-16 13:46:27 -0500928#define AIRO_DEF_MTU 2312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929
930typedef struct {
931 u32 size; // size
932 u8 enabled; // MIC enabled or not
933 u32 rxSuccess; // successful packets received
934 u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison
935 u32 rxNotMICed; // pkts dropped due to not being MIC'd
936 u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed
937 u32 rxWrongSequence; // pkts dropped due to sequence number violation
938 u32 reserve[32];
939} mic_statistics;
940
941typedef struct {
942 u32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2];
943 u64 accum; // accumulated mic, reduced to u32 in final()
944 int position; // current position (byte offset) in message
945 union {
946 u8 d8[4];
Al Viro593c2b92007-12-17 15:09:34 -0500947 __be32 d32;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 } part; // saves partial message word across update() calls
949} emmh32_context;
950
951typedef struct {
952 emmh32_context seed; // Context - the seed
953 u32 rx; // Received sequence number
954 u32 tx; // Tx sequence number
955 u32 window; // Start of window
956 u8 valid; // Flag to say if context is valid or not
957 u8 key[16];
958} miccntx;
959
960typedef struct {
961 miccntx mCtx; // Multicast context
962 miccntx uCtx; // Unicast context
963} mic_module;
964
965typedef struct {
966 unsigned int rid: 16;
967 unsigned int len: 15;
968 unsigned int valid: 1;
969 dma_addr_t host_addr;
970} Rid;
971
972typedef struct {
973 unsigned int offset: 15;
974 unsigned int eoc: 1;
975 unsigned int len: 15;
976 unsigned int valid: 1;
977 dma_addr_t host_addr;
978} TxFid;
979
980typedef struct {
981 unsigned int ctl: 15;
982 unsigned int rdy: 1;
983 unsigned int len: 15;
984 unsigned int valid: 1;
985 dma_addr_t host_addr;
986} RxFid;
987
988/*
989 * Host receive descriptor
990 */
991typedef struct {
992 unsigned char __iomem *card_ram_off; /* offset into card memory of the
993 desc */
994 RxFid rx_desc; /* card receive descriptor */
995 char *virtual_host_addr; /* virtual address of host receive
996 buffer */
997 int pending;
998} HostRxDesc;
999
1000/*
1001 * Host transmit descriptor
1002 */
1003typedef struct {
1004 unsigned char __iomem *card_ram_off; /* offset into card memory of the
1005 desc */
1006 TxFid tx_desc; /* card transmit descriptor */
1007 char *virtual_host_addr; /* virtual address of host receive
1008 buffer */
1009 int pending;
1010} HostTxDesc;
1011
1012/*
1013 * Host RID descriptor
1014 */
1015typedef struct {
1016 unsigned char __iomem *card_ram_off; /* offset into card memory of the
1017 descriptor */
1018 Rid rid_desc; /* card RID descriptor */
1019 char *virtual_host_addr; /* virtual address of host receive
1020 buffer */
1021} HostRidDesc;
1022
1023typedef struct {
1024 u16 sw0;
1025 u16 sw1;
1026 u16 status;
1027 u16 len;
1028#define HOST_SET (1 << 0)
1029#define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */
1030#define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */
1031#define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */
1032#define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */
1033#define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */
1034#define HOST_CLR_AID (1 << 7) /* clear AID failure */
1035#define HOST_RTS (1 << 9) /* Force RTS use */
1036#define HOST_SHORT (1 << 10) /* Do short preamble */
1037 u16 ctl;
1038 u16 aid;
1039 u16 retries;
1040 u16 fill;
1041} TxCtlHdr;
1042
1043typedef struct {
1044 u16 ctl;
1045 u16 duration;
1046 char addr1[6];
1047 char addr2[6];
1048 char addr3[6];
1049 u16 seq;
1050 char addr4[6];
1051} WifiHdr;
1052
1053
1054typedef struct {
1055 TxCtlHdr ctlhdr;
1056 u16 fill1;
1057 u16 fill2;
1058 WifiHdr wifihdr;
1059 u16 gaplen;
1060 u16 status;
1061} WifiCtlHdr;
1062
Jouni Malinenff1d2762005-05-12 22:54:16 -04001063static WifiCtlHdr wifictlhdr8023 = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 .ctlhdr = {
1065 .ctl = HOST_DONT_RLSE,
1066 }
1067};
1068
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069// Frequency list (map channels to frequencies)
1070static const long frequency_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
1071 2447, 2452, 2457, 2462, 2467, 2472, 2484 };
1072
1073// A few details needed for WEP (Wireless Equivalent Privacy)
1074#define MAX_KEY_SIZE 13 // 128 (?) bits
1075#define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP
1076typedef struct wep_key_t {
1077 u16 len;
1078 u8 key[16]; /* 40-bit and 104-bit keys */
1079} wep_key_t;
1080
1081/* Backward compatibility */
1082#ifndef IW_ENCODE_NOKEY
1083#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */
1084#define IW_ENCODE_MODE (IW_ENCODE_DISABLED | IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN)
1085#endif /* IW_ENCODE_NOKEY */
1086
1087/* List of Wireless Handlers (new API) */
1088static const struct iw_handler_def airo_handler_def;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
1090static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)";
1091
1092struct airo_info;
1093
1094static int get_dec_u16( char *buffer, int *start, int limit );
1095static void OUT4500( struct airo_info *, u16 register, u16 value );
1096static unsigned short IN4500( struct airo_info *, u16 register );
1097static u16 setup_card(struct airo_info*, u8 *mac, int lock);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02001098static int enable_MAC(struct airo_info *ai, int lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099static void disable_MAC(struct airo_info *ai, int lock);
1100static void enable_interrupts(struct airo_info*);
1101static void disable_interrupts(struct airo_info*);
1102static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp);
1103static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001104static int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001106static int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001108static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 int whichbap);
1110static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd);
1111static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock);
1112static int PC4500_writerid(struct airo_info*, u16 rid, const void
1113 *pBuf, int len, int lock);
1114static int do_writerid( struct airo_info*, u16 rid, const void *rid_data,
1115 int len, int dummy );
1116static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw);
1117static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket);
1118static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket);
1119
1120static int mpi_send_packet (struct net_device *dev);
1121static void mpi_unmap_card(struct pci_dev *pci);
1122static void mpi_receive_802_3(struct airo_info *ai);
1123static void mpi_receive_802_11(struct airo_info *ai);
1124static int waitbusy (struct airo_info *ai);
1125
David Howells7d12e782006-10-05 14:55:46 +01001126static irqreturn_t airo_interrupt( int irq, void* dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127static int airo_thread(void *data);
1128static void timer_func( struct net_device *dev );
1129static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001130static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131static void airo_read_wireless_stats (struct airo_info *local);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132#ifdef CISCO_EXT
1133static int readrids(struct net_device *dev, aironet_ioctl *comp);
1134static int writerids(struct net_device *dev, aironet_ioctl *comp);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001135static int flashcard(struct net_device *dev, aironet_ioctl *comp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136#endif /* CISCO_EXT */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137static void micinit(struct airo_info *ai);
1138static int micsetup(struct airo_info *ai);
1139static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len);
1140static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen);
1141
Dan Williams41480af2005-05-10 09:45:51 -04001142static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi);
1143static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm);
1144
Dan Williams9e75af32006-03-16 13:46:29 -05001145static void airo_networks_free(struct airo_info *ai);
1146
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147struct airo_info {
1148 struct net_device_stats stats;
1149 struct net_device *dev;
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01001150 struct list_head dev_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we
1152 use the high bit to mark whether it is in use. */
1153#define MAX_FIDS 6
1154#define MPI_MAX_FIDS 1
1155 int fids[MAX_FIDS];
1156 ConfigRid config;
1157 char keyindex; // Used with auto wep
1158 char defindex; // Used with auto wep
1159 struct proc_dir_entry *proc_entry;
1160 spinlock_t aux_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161#define FLAG_RADIO_OFF 0 /* User disabling of MAC */
1162#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */
1163#define FLAG_RADIO_MASK 0x03
1164#define FLAG_ENABLED 2
1165#define FLAG_ADHOC 3 /* Needed by MIC */
1166#define FLAG_MIC_CAPABLE 4
1167#define FLAG_UPDATE_MULTI 5
1168#define FLAG_UPDATE_UNI 6
1169#define FLAG_802_11 7
Dan Williams3c304952006-04-15 12:26:18 -04001170#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171#define FLAG_PENDING_XMIT 9
1172#define FLAG_PENDING_XMIT11 10
1173#define FLAG_MPI 11
1174#define FLAG_REGISTERED 12
1175#define FLAG_COMMIT 13
1176#define FLAG_RESET 14
1177#define FLAG_FLASHING 15
Dan Williams3c304952006-04-15 12:26:18 -04001178#define FLAG_WPA_CAPABLE 16
1179 unsigned long flags;
1180#define JOB_DIE 0
1181#define JOB_XMIT 1
1182#define JOB_XMIT11 2
1183#define JOB_STATS 3
1184#define JOB_PROMISC 4
1185#define JOB_MIC 5
1186#define JOB_EVENT 6
1187#define JOB_AUTOWEP 7
1188#define JOB_WSTATS 8
1189#define JOB_SCAN_RESULTS 9
1190 unsigned long jobs;
Al Virob8c06bc2007-12-19 17:55:43 -05001191 int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 int whichbap);
1193 unsigned short *flash;
1194 tdsRssiEntry *rssi;
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001195 struct task_struct *list_bss_task;
1196 struct task_struct *airo_thread_task;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 struct semaphore sem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 wait_queue_head_t thr_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 unsigned long expires;
1200 struct {
1201 struct sk_buff *skb;
1202 int fid;
1203 } xmit, xmit11;
1204 struct net_device *wifidev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 struct iw_statistics wstats; // wireless stats
Dan Williams9e75af32006-03-16 13:46:29 -05001206 unsigned long scan_timeout; /* Time scan should be read */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 struct iw_spy_data spy_data;
1208 struct iw_public_data wireless_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 /* MIC stuff */
Herbert Xuf12cc202006-08-22 20:36:13 +10001210 struct crypto_cipher *tfm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 mic_module mod[2];
1212 mic_statistics micstats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors
1214 HostTxDesc txfids[MPI_MAX_FIDS];
1215 HostRidDesc config_desc;
1216 unsigned long ridbus; // phys addr of config_desc
1217 struct sk_buff_head txq;// tx queue used by mpi350 code
1218 struct pci_dev *pci;
1219 unsigned char __iomem *pcimem;
1220 unsigned char __iomem *pciaux;
1221 unsigned char *shared;
1222 dma_addr_t shared_dma;
Pavel Machek1cc68ae2005-06-20 15:33:04 -07001223 pm_message_t power;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 SsidRid *SSID;
1225 APListRid *APList;
1226#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
1227 char proc_name[IFNAMSIZ];
Dan Williams9e75af32006-03-16 13:46:29 -05001228
Dan Williams3c304952006-04-15 12:26:18 -04001229 /* WPA-related stuff */
1230 unsigned int bssListFirst;
1231 unsigned int bssListNext;
1232 unsigned int bssListRidLen;
1233
Dan Williams9e75af32006-03-16 13:46:29 -05001234 struct list_head network_list;
1235 struct list_head network_free_list;
1236 BSSListElement *networks;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237};
1238
Al Virob8c06bc2007-12-19 17:55:43 -05001239static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen,
1240 int whichbap)
1241{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 return ai->bap_read(ai, pu16Dst, bytelen, whichbap);
1243}
1244
1245static int setup_proc_entry( struct net_device *dev,
1246 struct airo_info *apriv );
1247static int takedown_proc_entry( struct net_device *dev,
1248 struct airo_info *apriv );
1249
Jouni Malinenff1d2762005-05-12 22:54:16 -04001250static int cmdreset(struct airo_info *ai);
1251static int setflashmode (struct airo_info *ai);
1252static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
1253static int flashputbuf(struct airo_info *ai);
1254static int flashrestart(struct airo_info *ai,struct net_device *dev);
1255
Dan Williams934d8bf2006-03-16 13:46:23 -05001256#define airo_print(type, name, fmt, args...) \
Michal Schmidt1138c372007-06-29 15:33:41 +02001257 printk(type DRV_NAME "(%s): " fmt "\n", name, ##args)
Dan Williams934d8bf2006-03-16 13:46:23 -05001258
1259#define airo_print_info(name, fmt, args...) \
1260 airo_print(KERN_INFO, name, fmt, ##args)
1261
1262#define airo_print_dbg(name, fmt, args...) \
1263 airo_print(KERN_DEBUG, name, fmt, ##args)
1264
1265#define airo_print_warn(name, fmt, args...) \
1266 airo_print(KERN_WARNING, name, fmt, ##args)
1267
1268#define airo_print_err(name, fmt, args...) \
1269 airo_print(KERN_ERR, name, fmt, ##args)
1270
1271
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272/***********************************************************************
1273 * MIC ROUTINES *
1274 ***********************************************************************
1275 */
1276
1277static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq);
1278static void MoveWindow(miccntx *context, u32 micSeq);
Herbert Xuf12cc202006-08-22 20:36:13 +10001279static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
1280 struct crypto_cipher *tfm);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001281static void emmh32_init(emmh32_context *context);
1282static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
1283static void emmh32_final(emmh32_context *context, u8 digest[4]);
1284static int flashpchar(struct airo_info *ai,int byte,int dwelltime);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
1286/* micinit - Initialize mic seed */
1287
1288static void micinit(struct airo_info *ai)
1289{
1290 MICRid mic_rid;
1291
Dan Williams3c304952006-04-15 12:26:18 -04001292 clear_bit(JOB_MIC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
1294 up(&ai->sem);
1295
1296 ai->micstats.enabled = (mic_rid.state & 0x00FF) ? 1 : 0;
1297
1298 if (ai->micstats.enabled) {
1299 /* Key must be valid and different */
1300 if (mic_rid.multicastValid && (!ai->mod[0].mCtx.valid ||
1301 (memcmp (ai->mod[0].mCtx.key, mic_rid.multicast,
1302 sizeof(ai->mod[0].mCtx.key)) != 0))) {
1303 /* Age current mic Context */
1304 memcpy(&ai->mod[1].mCtx,&ai->mod[0].mCtx,sizeof(miccntx));
1305 /* Initialize new context */
1306 memcpy(&ai->mod[0].mCtx.key,mic_rid.multicast,sizeof(mic_rid.multicast));
1307 ai->mod[0].mCtx.window = 33; //Window always points to the middle
1308 ai->mod[0].mCtx.rx = 0; //Rx Sequence numbers
1309 ai->mod[0].mCtx.tx = 0; //Tx sequence numbers
1310 ai->mod[0].mCtx.valid = 1; //Key is now valid
1311
1312 /* Give key to mic seed */
1313 emmh32_setseed(&ai->mod[0].mCtx.seed,mic_rid.multicast,sizeof(mic_rid.multicast), ai->tfm);
1314 }
1315
1316 /* Key must be valid and different */
1317 if (mic_rid.unicastValid && (!ai->mod[0].uCtx.valid ||
1318 (memcmp(ai->mod[0].uCtx.key, mic_rid.unicast,
1319 sizeof(ai->mod[0].uCtx.key)) != 0))) {
1320 /* Age current mic Context */
1321 memcpy(&ai->mod[1].uCtx,&ai->mod[0].uCtx,sizeof(miccntx));
1322 /* Initialize new context */
1323 memcpy(&ai->mod[0].uCtx.key,mic_rid.unicast,sizeof(mic_rid.unicast));
1324
1325 ai->mod[0].uCtx.window = 33; //Window always points to the middle
1326 ai->mod[0].uCtx.rx = 0; //Rx Sequence numbers
1327 ai->mod[0].uCtx.tx = 0; //Tx sequence numbers
1328 ai->mod[0].uCtx.valid = 1; //Key is now valid
1329
1330 //Give key to mic seed
1331 emmh32_setseed(&ai->mod[0].uCtx.seed, mic_rid.unicast, sizeof(mic_rid.unicast), ai->tfm);
1332 }
1333 } else {
1334 /* So next time we have a valid key and mic is enabled, we will update
1335 * the sequence number if the key is the same as before.
1336 */
1337 ai->mod[0].uCtx.valid = 0;
1338 ai->mod[0].mCtx.valid = 0;
1339 }
1340}
1341
1342/* micsetup - Get ready for business */
1343
1344static int micsetup(struct airo_info *ai) {
1345 int i;
1346
1347 if (ai->tfm == NULL)
Herbert Xuf12cc202006-08-22 20:36:13 +10001348 ai->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
Herbert Xuf12cc202006-08-22 20:36:13 +10001350 if (IS_ERR(ai->tfm)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05001351 airo_print_err(ai->dev->name, "failed to load transform for AES");
Herbert Xuf12cc202006-08-22 20:36:13 +10001352 ai->tfm = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 return ERROR;
1354 }
1355
1356 for (i=0; i < NUM_MODULES; i++) {
1357 memset(&ai->mod[i].mCtx,0,sizeof(miccntx));
1358 memset(&ai->mod[i].uCtx,0,sizeof(miccntx));
1359 }
1360 return SUCCESS;
1361}
1362
Jouni Malinenff1d2762005-05-12 22:54:16 -04001363static char micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
1365/*===========================================================================
1366 * Description: Mic a packet
1367 *
1368 * Inputs: etherHead * pointer to an 802.3 frame
1369 *
1370 * Returns: BOOLEAN if successful, otherwise false.
1371 * PacketTxLen will be updated with the mic'd packets size.
1372 *
1373 * Caveats: It is assumed that the frame buffer will already
1374 * be big enough to hold the largets mic message possible.
1375 * (No memory allocation is done here).
1376 *
1377 * Author: sbraneky (10/15/01)
1378 * Merciless hacks by rwilcher (1/14/02)
1379 */
1380
1381static int encapsulate(struct airo_info *ai ,etherHead *frame, MICBuffer *mic, int payLen)
1382{
1383 miccntx *context;
1384
1385 // Determine correct context
1386 // If not adhoc, always use unicast key
1387
1388 if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1))
1389 context = &ai->mod[0].mCtx;
1390 else
1391 context = &ai->mod[0].uCtx;
1392
1393 if (!context->valid)
1394 return ERROR;
1395
1396 mic->typelen = htons(payLen + 16); //Length of Mic'd packet
1397
1398 memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap
1399
1400 // Add Tx sequence
1401 mic->seq = htonl(context->tx);
1402 context->tx += 2;
1403
1404 emmh32_init(&context->seed); // Mic the packet
1405 emmh32_update(&context->seed,frame->da,ETH_ALEN * 2); // DA,SA
1406 emmh32_update(&context->seed,(u8*)&mic->typelen,10); // Type/Length and Snap
1407 emmh32_update(&context->seed,(u8*)&mic->seq,sizeof(mic->seq)); //SEQ
1408 emmh32_update(&context->seed,frame->da + ETH_ALEN * 2,payLen); //payload
1409 emmh32_final(&context->seed, (u8*)&mic->mic);
1410
1411 /* New Type/length ?????????? */
1412 mic->typelen = 0; //Let NIC know it could be an oversized packet
1413 return SUCCESS;
1414}
1415
1416typedef enum {
1417 NONE,
1418 NOMIC,
1419 NOMICPLUMMED,
1420 SEQUENCE,
1421 INCORRECTMIC,
1422} mic_error;
1423
1424/*===========================================================================
1425 * Description: Decapsulates a MIC'd packet and returns the 802.3 packet
1426 * (removes the MIC stuff) if packet is a valid packet.
1427 *
1428 * Inputs: etherHead pointer to the 802.3 packet
1429 *
1430 * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE
1431 *
1432 * Author: sbraneky (10/15/01)
1433 * Merciless hacks by rwilcher (1/14/02)
1434 *---------------------------------------------------------------------------
1435 */
1436
1437static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen)
1438{
1439 int i;
1440 u32 micSEQ;
1441 miccntx *context;
1442 u8 digest[4];
1443 mic_error micError = NONE;
1444
1445 // Check if the packet is a Mic'd packet
1446
1447 if (!ai->micstats.enabled) {
1448 //No Mic set or Mic OFF but we received a MIC'd packet.
1449 if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) {
1450 ai->micstats.rxMICPlummed++;
1451 return ERROR;
1452 }
1453 return SUCCESS;
1454 }
1455
1456 if (ntohs(mic->typelen) == 0x888E)
1457 return SUCCESS;
1458
1459 if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) {
1460 // Mic enabled but packet isn't Mic'd
1461 ai->micstats.rxMICPlummed++;
1462 return ERROR;
1463 }
1464
1465 micSEQ = ntohl(mic->seq); //store SEQ as CPU order
1466
1467 //At this point we a have a mic'd packet and mic is enabled
1468 //Now do the mic error checking.
1469
1470 //Receive seq must be odd
1471 if ( (micSEQ & 1) == 0 ) {
1472 ai->micstats.rxWrongSequence++;
1473 return ERROR;
1474 }
1475
1476 for (i = 0; i < NUM_MODULES; i++) {
1477 int mcast = eth->da[0] & 1;
1478 //Determine proper context
1479 context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx;
1480
1481 //Make sure context is valid
1482 if (!context->valid) {
1483 if (i == 0)
1484 micError = NOMICPLUMMED;
1485 continue;
1486 }
1487 //DeMic it
1488
1489 if (!mic->typelen)
1490 mic->typelen = htons(payLen + sizeof(MICBuffer) - 2);
1491
1492 emmh32_init(&context->seed);
1493 emmh32_update(&context->seed, eth->da, ETH_ALEN*2);
1494 emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap));
1495 emmh32_update(&context->seed, (u8 *)&mic->seq,sizeof(mic->seq));
1496 emmh32_update(&context->seed, eth->da + ETH_ALEN*2,payLen);
1497 //Calculate MIC
1498 emmh32_final(&context->seed, digest);
1499
1500 if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match
1501 //Invalid Mic
1502 if (i == 0)
1503 micError = INCORRECTMIC;
1504 continue;
1505 }
1506
1507 //Check Sequence number if mics pass
1508 if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) {
1509 ai->micstats.rxSuccess++;
1510 return SUCCESS;
1511 }
1512 if (i == 0)
1513 micError = SEQUENCE;
1514 }
1515
1516 // Update statistics
1517 switch (micError) {
1518 case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break;
1519 case SEQUENCE: ai->micstats.rxWrongSequence++; break;
1520 case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break;
1521 case NONE: break;
1522 case NOMIC: break;
1523 }
1524 return ERROR;
1525}
1526
1527/*===========================================================================
1528 * Description: Checks the Rx Seq number to make sure it is valid
1529 * and hasn't already been received
1530 *
1531 * Inputs: miccntx - mic context to check seq against
1532 * micSeq - the Mic seq number
1533 *
1534 * Returns: TRUE if valid otherwise FALSE.
1535 *
1536 * Author: sbraneky (10/15/01)
1537 * Merciless hacks by rwilcher (1/14/02)
1538 *---------------------------------------------------------------------------
1539 */
1540
1541static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq)
1542{
1543 u32 seq,index;
1544
1545 //Allow for the ap being rebooted - if it is then use the next
1546 //sequence number of the current sequence number - might go backwards
1547
1548 if (mcast) {
1549 if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) {
1550 clear_bit (FLAG_UPDATE_MULTI, &ai->flags);
1551 context->window = (micSeq > 33) ? micSeq : 33;
1552 context->rx = 0; // Reset rx
1553 }
1554 } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) {
1555 clear_bit (FLAG_UPDATE_UNI, &ai->flags);
1556 context->window = (micSeq > 33) ? micSeq : 33; // Move window
1557 context->rx = 0; // Reset rx
1558 }
1559
1560 //Make sequence number relative to START of window
1561 seq = micSeq - (context->window - 33);
1562
1563 //Too old of a SEQ number to check.
1564 if ((s32)seq < 0)
1565 return ERROR;
1566
1567 if ( seq > 64 ) {
1568 //Window is infinite forward
1569 MoveWindow(context,micSeq);
1570 return SUCCESS;
1571 }
1572
1573 // We are in the window. Now check the context rx bit to see if it was already sent
1574 seq >>= 1; //divide by 2 because we only have odd numbers
1575 index = 1 << seq; //Get an index number
1576
1577 if (!(context->rx & index)) {
1578 //micSEQ falls inside the window.
1579 //Add seqence number to the list of received numbers.
1580 context->rx |= index;
1581
1582 MoveWindow(context,micSeq);
1583
1584 return SUCCESS;
1585 }
1586 return ERROR;
1587}
1588
1589static void MoveWindow(miccntx *context, u32 micSeq)
1590{
1591 u32 shift;
1592
1593 //Move window if seq greater than the middle of the window
1594 if (micSeq > context->window) {
1595 shift = (micSeq - context->window) >> 1;
1596
1597 //Shift out old
1598 if (shift < 32)
1599 context->rx >>= shift;
1600 else
1601 context->rx = 0;
1602
1603 context->window = micSeq; //Move window
1604 }
1605}
1606
1607/*==============================================*/
1608/*========== EMMH ROUTINES ====================*/
1609/*==============================================*/
1610
1611/* mic accumulate */
1612#define MIC_ACCUM(val) \
1613 context->accum += (u64)(val) * context->coeff[coeff_position++];
1614
1615static unsigned char aes_counter[16];
1616
1617/* expand the key to fill the MMH coefficient array */
Herbert Xuf12cc202006-08-22 20:36:13 +10001618static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
1619 struct crypto_cipher *tfm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620{
1621 /* take the keying material, expand if necessary, truncate at 16-bytes */
1622 /* run through AES counter mode to generate context->coeff[] */
1623
1624 int i,j;
1625 u32 counter;
1626 u8 *cipher, plain[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627
1628 crypto_cipher_setkey(tfm, pkey, 16);
1629 counter = 0;
Ahmed S. Darwishe7c04fd2007-02-05 18:58:29 +02001630 for (i = 0; i < ARRAY_SIZE(context->coeff); ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 aes_counter[15] = (u8)(counter >> 0);
1632 aes_counter[14] = (u8)(counter >> 8);
1633 aes_counter[13] = (u8)(counter >> 16);
1634 aes_counter[12] = (u8)(counter >> 24);
1635 counter++;
1636 memcpy (plain, aes_counter, 16);
Herbert Xuf12cc202006-08-22 20:36:13 +10001637 crypto_cipher_encrypt_one(tfm, plain, plain);
1638 cipher = plain;
Ahmed S. Darwishe7c04fd2007-02-05 18:58:29 +02001639 for (j = 0; (j < 16) && (i < ARRAY_SIZE(context->coeff)); ) {
Al Viro593c2b92007-12-17 15:09:34 -05001640 context->coeff[i++] = ntohl(*(__be32 *)&cipher[j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 j += 4;
1642 }
1643 }
1644}
1645
1646/* prepare for calculation of a new mic */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001647static void emmh32_init(emmh32_context *context)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648{
1649 /* prepare for new mic calculation */
1650 context->accum = 0;
1651 context->position = 0;
1652}
1653
1654/* add some bytes to the mic calculation */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001655static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656{
1657 int coeff_position, byte_position;
1658
1659 if (len == 0) return;
1660
1661 coeff_position = context->position >> 2;
1662
1663 /* deal with partial 32-bit word left over from last update */
1664 byte_position = context->position & 3;
1665 if (byte_position) {
1666 /* have a partial word in part to deal with */
1667 do {
1668 if (len == 0) return;
1669 context->part.d8[byte_position++] = *pOctets++;
1670 context->position++;
1671 len--;
1672 } while (byte_position < 4);
Al Viro593c2b92007-12-17 15:09:34 -05001673 MIC_ACCUM(ntohl(context->part.d32));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 }
1675
1676 /* deal with full 32-bit words */
1677 while (len >= 4) {
Al Viro593c2b92007-12-17 15:09:34 -05001678 MIC_ACCUM(ntohl(*(__be32 *)pOctets));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 context->position += 4;
1680 pOctets += 4;
1681 len -= 4;
1682 }
1683
1684 /* deal with partial 32-bit word that will be left over from this update */
1685 byte_position = 0;
1686 while (len > 0) {
1687 context->part.d8[byte_position++] = *pOctets++;
1688 context->position++;
1689 len--;
1690 }
1691}
1692
1693/* mask used to zero empty bytes for final partial word */
1694static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
1695
1696/* calculate the mic */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001697static void emmh32_final(emmh32_context *context, u8 digest[4])
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698{
1699 int coeff_position, byte_position;
1700 u32 val;
1701
1702 u64 sum, utmp;
1703 s64 stmp;
1704
1705 coeff_position = context->position >> 2;
1706
1707 /* deal with partial 32-bit word left over from last update */
1708 byte_position = context->position & 3;
1709 if (byte_position) {
1710 /* have a partial word in part to deal with */
Al Viro593c2b92007-12-17 15:09:34 -05001711 val = ntohl(context->part.d32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */
1713 }
1714
1715 /* reduce the accumulated u64 to a 32-bit MIC */
1716 sum = context->accum;
1717 stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15);
1718 utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15);
1719 sum = utmp & 0xffffffffLL;
1720 if (utmp > 0x10000000fLL)
1721 sum -= 15;
1722
1723 val = (u32)sum;
1724 digest[0] = (val>>24) & 0xFF;
1725 digest[1] = (val>>16) & 0xFF;
1726 digest[2] = (val>>8) & 0xFF;
1727 digest[3] = val & 0xFF;
1728}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729
1730static int readBSSListRid(struct airo_info *ai, int first,
Al Viro17e70492007-12-19 18:56:37 -05001731 BSSListRid *list)
1732{
Dan Williams3c304952006-04-15 12:26:18 -04001733 Cmd cmd;
1734 Resp rsp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735
1736 if (first == 1) {
Dan Williams3c304952006-04-15 12:26:18 -04001737 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
1738 memset(&cmd, 0, sizeof(cmd));
1739 cmd.cmd=CMD_LISTBSS;
1740 if (down_interruptible(&ai->sem))
1741 return -ERESTARTSYS;
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001742 ai->list_bss_task = current;
Dan Williams3c304952006-04-15 12:26:18 -04001743 issuecommand(ai, &cmd, &rsp);
1744 up(&ai->sem);
1745 /* Let the command take effect */
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001746 schedule_timeout_uninterruptible(3 * HZ);
1747 ai->list_bss_task = NULL;
Dan Williams3c304952006-04-15 12:26:18 -04001748 }
Al Viro17e70492007-12-19 18:56:37 -05001749 return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext,
Dan Williams3c304952006-04-15 12:26:18 -04001750 list, ai->bssListRidLen, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751}
1752
Al Viro4293ea32007-12-19 19:21:51 -05001753static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock)
1754{
1755 return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 wkr, sizeof(*wkr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758
Al Viro4293ea32007-12-19 19:21:51 -05001759static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock)
1760{
1761 int rc;
1762 rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock);
1763 if (rc!=SUCCESS)
1764 airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765 if (perm) {
Al Viro4293ea32007-12-19 19:21:51 -05001766 rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock);
1767 if (rc!=SUCCESS)
Dan Williams934d8bf2006-03-16 13:46:23 -05001768 airo_print_err(ai->dev->name, "WEP_PERM set %x", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769 }
1770 return rc;
1771}
1772
Al Viro0dd22122007-12-17 16:11:57 -05001773static int readSsidRid(struct airo_info*ai, SsidRid *ssidr)
1774{
1775 return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
Al Viro0dd22122007-12-17 16:11:57 -05001778static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock)
1779{
1780 return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781}
Al Viro0dd22122007-12-17 16:11:57 -05001782
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783static int readConfigRid(struct airo_info*ai, int lock) {
1784 int rc;
1785 u16 *s;
1786 ConfigRid cfg;
1787
1788 if (ai->config.len)
1789 return SUCCESS;
1790
1791 rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock);
1792 if (rc != SUCCESS)
1793 return rc;
1794
1795 for(s = &cfg.len; s <= &cfg.rtsThres; s++) *s = le16_to_cpu(*s);
1796
1797 for(s = &cfg.shortRetryLimit; s <= &cfg.radioType; s++)
1798 *s = le16_to_cpu(*s);
1799
1800 for(s = &cfg.txPower; s <= &cfg.radioSpecific; s++)
1801 *s = le16_to_cpu(*s);
1802
1803 for(s = &cfg.arlThreshold; s <= &cfg._reserved4[0]; s++)
1804 *s = cpu_to_le16(*s);
1805
1806 for(s = &cfg.autoWake; s <= &cfg.autoWake; s++)
1807 *s = cpu_to_le16(*s);
1808
1809 ai->config = cfg;
1810 return SUCCESS;
1811}
1812static inline void checkThrottle(struct airo_info *ai) {
1813 int i;
1814/* Old hardware had a limit on encryption speed */
1815 if (ai->config.authType != AUTH_OPEN && maxencrypt) {
1816 for(i=0; i<8; i++) {
1817 if (ai->config.rates[i] > maxencrypt) {
1818 ai->config.rates[i] = 0;
1819 }
1820 }
1821 }
1822}
1823static int writeConfigRid(struct airo_info*ai, int lock) {
1824 u16 *s;
1825 ConfigRid cfgr;
1826
1827 if (!test_bit (FLAG_COMMIT, &ai->flags))
1828 return SUCCESS;
1829
1830 clear_bit (FLAG_COMMIT, &ai->flags);
1831 clear_bit (FLAG_RESET, &ai->flags);
1832 checkThrottle(ai);
1833 cfgr = ai->config;
1834
1835 if ((cfgr.opmode & 0xFF) == MODE_STA_IBSS)
1836 set_bit(FLAG_ADHOC, &ai->flags);
1837 else
1838 clear_bit(FLAG_ADHOC, &ai->flags);
1839
1840 for(s = &cfgr.len; s <= &cfgr.rtsThres; s++) *s = cpu_to_le16(*s);
1841
1842 for(s = &cfgr.shortRetryLimit; s <= &cfgr.radioType; s++)
1843 *s = cpu_to_le16(*s);
1844
1845 for(s = &cfgr.txPower; s <= &cfgr.radioSpecific; s++)
1846 *s = cpu_to_le16(*s);
1847
1848 for(s = &cfgr.arlThreshold; s <= &cfgr._reserved4[0]; s++)
1849 *s = cpu_to_le16(*s);
1850
1851 for(s = &cfgr.autoWake; s <= &cfgr.autoWake; s++)
1852 *s = cpu_to_le16(*s);
1853
1854 return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock);
1855}
1856static int readStatusRid(struct airo_info*ai, StatusRid *statr, int lock) {
1857 int rc = PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock);
1858 u16 *s;
1859
1860 statr->len = le16_to_cpu(statr->len);
1861 for(s = &statr->mode; s <= &statr->SSIDlen; s++) *s = le16_to_cpu(*s);
1862
1863 for(s = &statr->beaconPeriod; s <= &statr->shortPreamble; s++)
1864 *s = le16_to_cpu(*s);
1865 statr->load = le16_to_cpu(statr->load);
1866 statr->assocStatus = le16_to_cpu(statr->assocStatus);
1867 return rc;
1868}
1869static int readAPListRid(struct airo_info*ai, APListRid *aplr) {
1870 int rc = PC4500_readrid(ai, RID_APLIST, aplr, sizeof(*aplr), 1);
1871 aplr->len = le16_to_cpu(aplr->len);
1872 return rc;
1873}
1874static int writeAPListRid(struct airo_info*ai, APListRid *aplr, int lock) {
1875 int rc;
1876 aplr->len = cpu_to_le16(aplr->len);
1877 rc = PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock);
1878 return rc;
1879}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880
Al Viro56d81bd2007-12-20 17:18:35 -05001881static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock)
1882{
1883 return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885
Al Viroa23ace52007-12-19 22:24:16 -05001886static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock)
1887{
1888 return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889}
1890
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001891static void try_auto_wep(struct airo_info *ai)
1892{
1893 if (auto_wep && !(ai->flags & FLAG_RADIO_DOWN)) {
1894 ai->expires = RUN_AT(3*HZ);
1895 wake_up_interruptible(&ai->thr_wait);
1896 }
1897}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001899static int airo_open(struct net_device *dev) {
1900 struct airo_info *ai = dev->priv;
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001901 int rc = 0;
1902
1903 if (test_bit(FLAG_FLASHING, &ai->flags))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904 return -EIO;
1905
1906 /* Make sure the card is configured.
1907 * Wireless Extensions may postpone config changes until the card
1908 * is open (to pipeline changes and speed-up card setup). If
1909 * those changes are not yet commited, do it now - Jean II */
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001910 if (test_bit(FLAG_COMMIT, &ai->flags)) {
1911 disable_MAC(ai, 1);
1912 writeConfigRid(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 }
1914
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001915 if (ai->wifidev != dev) {
1916 clear_bit(JOB_DIE, &ai->jobs);
1917 ai->airo_thread_task = kthread_run(airo_thread, dev, dev->name);
1918 if (IS_ERR(ai->airo_thread_task))
1919 return (int)PTR_ERR(ai->airo_thread_task);
1920
1921 rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED,
1922 dev->name, dev);
1923 if (rc) {
1924 airo_print_err(dev->name,
1925 "register interrupt %d failed, rc %d",
1926 dev->irq, rc);
1927 set_bit(JOB_DIE, &ai->jobs);
1928 kthread_stop(ai->airo_thread_task);
1929 return rc;
1930 }
1931
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 /* Power on the MAC controller (which may have been disabled) */
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001933 clear_bit(FLAG_RADIO_DOWN, &ai->flags);
1934 enable_interrupts(ai);
1935
1936 try_auto_wep(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 }
Michal Schmidt175ec1a2007-06-29 15:33:47 +02001938 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939
1940 netif_start_queue(dev);
1941 return 0;
1942}
1943
1944static int mpi_start_xmit(struct sk_buff *skb, struct net_device *dev) {
1945 int npacks, pending;
1946 unsigned long flags;
1947 struct airo_info *ai = dev->priv;
1948
1949 if (!skb) {
Dan Williams934d8bf2006-03-16 13:46:23 -05001950 airo_print_err(dev->name, "%s: skb == NULL!",__FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951 return 0;
1952 }
1953 npacks = skb_queue_len (&ai->txq);
1954
1955 if (npacks >= MAXTXQ - 1) {
1956 netif_stop_queue (dev);
1957 if (npacks > MAXTXQ) {
1958 ai->stats.tx_fifo_errors++;
1959 return 1;
1960 }
1961 skb_queue_tail (&ai->txq, skb);
1962 return 0;
1963 }
1964
1965 spin_lock_irqsave(&ai->aux_lock, flags);
1966 skb_queue_tail (&ai->txq, skb);
1967 pending = test_bit(FLAG_PENDING_XMIT, &ai->flags);
1968 spin_unlock_irqrestore(&ai->aux_lock,flags);
1969 netif_wake_queue (dev);
1970
1971 if (pending == 0) {
1972 set_bit(FLAG_PENDING_XMIT, &ai->flags);
1973 mpi_send_packet (dev);
1974 }
1975 return 0;
1976}
1977
1978/*
1979 * @mpi_send_packet
1980 *
1981 * Attempt to transmit a packet. Can be called from interrupt
1982 * or transmit . return number of packets we tried to send
1983 */
1984
1985static int mpi_send_packet (struct net_device *dev)
1986{
1987 struct sk_buff *skb;
1988 unsigned char *buffer;
Al Viro593c2b92007-12-17 15:09:34 -05001989 s16 len;
1990 __le16 *payloadLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 struct airo_info *ai = dev->priv;
1992 u8 *sendbuf;
1993
1994 /* get a packet to send */
1995
Al Viro79ea13c2008-01-24 02:06:46 -08001996 if ((skb = skb_dequeue(&ai->txq)) == NULL) {
Dan Williams934d8bf2006-03-16 13:46:23 -05001997 airo_print_err(dev->name,
1998 "%s: Dequeue'd zero in send_packet()",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 __FUNCTION__);
2000 return 0;
2001 }
2002
2003 /* check min length*/
2004 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
2005 buffer = skb->data;
2006
2007 ai->txfids[0].tx_desc.offset = 0;
2008 ai->txfids[0].tx_desc.valid = 1;
2009 ai->txfids[0].tx_desc.eoc = 1;
2010 ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr);
2011
2012/*
2013 * Magic, the cards firmware needs a length count (2 bytes) in the host buffer
2014 * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen
2015 * is immediatly after it. ------------------------------------------------
2016 * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA|
2017 * ------------------------------------------------
2018 */
2019
2020 memcpy((char *)ai->txfids[0].virtual_host_addr,
2021 (char *)&wifictlhdr8023, sizeof(wifictlhdr8023));
2022
Al Viro593c2b92007-12-17 15:09:34 -05002023 payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 sizeof(wifictlhdr8023));
2025 sendbuf = ai->txfids[0].virtual_host_addr +
2026 sizeof(wifictlhdr8023) + 2 ;
2027
2028 /*
2029 * Firmware automaticly puts 802 header on so
2030 * we don't need to account for it in the length
2031 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
Al Viro593c2b92007-12-17 15:09:34 -05002033 (ntohs(((__be16 *)buffer)[6]) != 0x888E)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034 MICBuffer pMic;
2035
2036 if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS)
2037 return ERROR;
2038
2039 *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic));
2040 ai->txfids[0].tx_desc.len += sizeof(pMic);
2041 /* copy data into airo dma buffer */
2042 memcpy (sendbuf, buffer, sizeof(etherHead));
2043 buffer += sizeof(etherHead);
2044 sendbuf += sizeof(etherHead);
2045 memcpy (sendbuf, &pMic, sizeof(pMic));
2046 sendbuf += sizeof(pMic);
2047 memcpy (sendbuf, buffer, len - sizeof(etherHead));
Adrian Bunka39d3e72006-01-21 01:35:15 +01002048 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049 *payloadLen = cpu_to_le16(len - sizeof(etherHead));
2050
2051 dev->trans_start = jiffies;
2052
2053 /* copy data into airo dma buffer */
2054 memcpy(sendbuf, buffer, len);
2055 }
2056
2057 memcpy_toio(ai->txfids[0].card_ram_off,
2058 &ai->txfids[0].tx_desc, sizeof(TxFid));
2059
2060 OUT4500(ai, EVACK, 8);
2061
2062 dev_kfree_skb_any(skb);
2063 return 1;
2064}
2065
Gabriel A. Devenyi29b09fc2005-11-03 19:30:47 -05002066static void get_tx_error(struct airo_info *ai, s32 fid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067{
Al Viro593c2b92007-12-17 15:09:34 -05002068 __le16 status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069
2070 if (fid < 0)
2071 status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status;
2072 else {
2073 if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS)
2074 return;
2075 bap_read(ai, &status, 2, BAP0);
2076 }
2077 if (le16_to_cpu(status) & 2) /* Too many retries */
2078 ai->stats.tx_aborted_errors++;
2079 if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */
2080 ai->stats.tx_heartbeat_errors++;
2081 if (le16_to_cpu(status) & 8) /* Aid fail */
2082 { }
2083 if (le16_to_cpu(status) & 0x10) /* MAC disabled */
2084 ai->stats.tx_carrier_errors++;
2085 if (le16_to_cpu(status) & 0x20) /* Association lost */
2086 { }
2087 /* We produce a TXDROP event only for retry or lifetime
2088 * exceeded, because that's the only status that really mean
2089 * that this particular node went away.
2090 * Other errors means that *we* screwed up. - Jean II */
2091 if ((le16_to_cpu(status) & 2) ||
2092 (le16_to_cpu(status) & 4)) {
2093 union iwreq_data wrqu;
2094 char junk[0x18];
2095
2096 /* Faster to skip over useless data than to do
2097 * another bap_setup(). We are at offset 0x6 and
2098 * need to go to 0x18 and read 6 bytes - Jean II */
Al Virob8c06bc2007-12-19 17:55:43 -05002099 bap_read(ai, (__le16 *) junk, 0x18, BAP0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100
2101 /* Copy 802.11 dest address.
2102 * We use the 802.11 header because the frame may
2103 * not be 802.3 or may be mangled...
2104 * In Ad-Hoc mode, it will be the node address.
2105 * In managed mode, it will be most likely the AP addr
2106 * User space will figure out how to convert it to
2107 * whatever it needs (IP address or else).
2108 * - Jean II */
2109 memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN);
2110 wrqu.addr.sa_family = ARPHRD_ETHER;
2111
2112 /* Send event to user space */
2113 wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL);
2114 }
2115}
2116
2117static void airo_end_xmit(struct net_device *dev) {
2118 u16 status;
2119 int i;
2120 struct airo_info *priv = dev->priv;
2121 struct sk_buff *skb = priv->xmit.skb;
2122 int fid = priv->xmit.fid;
2123 u32 *fids = priv->fids;
2124
Dan Williams3c304952006-04-15 12:26:18 -04002125 clear_bit(JOB_XMIT, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 clear_bit(FLAG_PENDING_XMIT, &priv->flags);
2127 status = transmit_802_3_packet (priv, fids[fid], skb->data);
2128 up(&priv->sem);
2129
2130 i = 0;
2131 if ( status == SUCCESS ) {
2132 dev->trans_start = jiffies;
2133 for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++);
2134 } else {
2135 priv->fids[fid] &= 0xffff;
2136 priv->stats.tx_window_errors++;
2137 }
2138 if (i < MAX_FIDS / 2)
2139 netif_wake_queue(dev);
2140 dev_kfree_skb(skb);
2141}
2142
2143static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) {
2144 s16 len;
2145 int i, j;
2146 struct airo_info *priv = dev->priv;
2147 u32 *fids = priv->fids;
2148
2149 if ( skb == NULL ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002150 airo_print_err(dev->name, "%s: skb == NULL!", __FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 return 0;
2152 }
2153
2154 /* Find a vacant FID */
2155 for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ );
2156 for( j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++ );
2157
2158 if ( j >= MAX_FIDS / 2 ) {
2159 netif_stop_queue(dev);
2160
2161 if (i == MAX_FIDS / 2) {
2162 priv->stats.tx_fifo_errors++;
2163 return 1;
2164 }
2165 }
2166 /* check min length*/
2167 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
2168 /* Mark fid as used & save length for later */
2169 fids[i] |= (len << 16);
2170 priv->xmit.skb = skb;
2171 priv->xmit.fid = i;
2172 if (down_trylock(&priv->sem) != 0) {
2173 set_bit(FLAG_PENDING_XMIT, &priv->flags);
2174 netif_stop_queue(dev);
Dan Williams3c304952006-04-15 12:26:18 -04002175 set_bit(JOB_XMIT, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 wake_up_interruptible(&priv->thr_wait);
2177 } else
2178 airo_end_xmit(dev);
2179 return 0;
2180}
2181
2182static void airo_end_xmit11(struct net_device *dev) {
2183 u16 status;
2184 int i;
2185 struct airo_info *priv = dev->priv;
2186 struct sk_buff *skb = priv->xmit11.skb;
2187 int fid = priv->xmit11.fid;
2188 u32 *fids = priv->fids;
2189
Dan Williams3c304952006-04-15 12:26:18 -04002190 clear_bit(JOB_XMIT11, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191 clear_bit(FLAG_PENDING_XMIT11, &priv->flags);
2192 status = transmit_802_11_packet (priv, fids[fid], skb->data);
2193 up(&priv->sem);
2194
2195 i = MAX_FIDS / 2;
2196 if ( status == SUCCESS ) {
2197 dev->trans_start = jiffies;
2198 for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++);
2199 } else {
2200 priv->fids[fid] &= 0xffff;
2201 priv->stats.tx_window_errors++;
2202 }
2203 if (i < MAX_FIDS)
2204 netif_wake_queue(dev);
2205 dev_kfree_skb(skb);
2206}
2207
2208static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) {
2209 s16 len;
2210 int i, j;
2211 struct airo_info *priv = dev->priv;
2212 u32 *fids = priv->fids;
2213
2214 if (test_bit(FLAG_MPI, &priv->flags)) {
2215 /* Not implemented yet for MPI350 */
2216 netif_stop_queue(dev);
2217 return -ENETDOWN;
2218 }
2219
2220 if ( skb == NULL ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002221 airo_print_err(dev->name, "%s: skb == NULL!", __FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222 return 0;
2223 }
2224
2225 /* Find a vacant FID */
2226 for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ );
2227 for( j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++ );
2228
2229 if ( j >= MAX_FIDS ) {
2230 netif_stop_queue(dev);
2231
2232 if (i == MAX_FIDS) {
2233 priv->stats.tx_fifo_errors++;
2234 return 1;
2235 }
2236 }
2237 /* check min length*/
2238 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
2239 /* Mark fid as used & save length for later */
2240 fids[i] |= (len << 16);
2241 priv->xmit11.skb = skb;
2242 priv->xmit11.fid = i;
2243 if (down_trylock(&priv->sem) != 0) {
2244 set_bit(FLAG_PENDING_XMIT11, &priv->flags);
2245 netif_stop_queue(dev);
Dan Williams3c304952006-04-15 12:26:18 -04002246 set_bit(JOB_XMIT11, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 wake_up_interruptible(&priv->thr_wait);
2248 } else
2249 airo_end_xmit11(dev);
2250 return 0;
2251}
2252
Al Viroa23ace52007-12-19 22:24:16 -05002253static void airo_read_stats(struct airo_info *ai)
2254{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 StatsRid stats_rid;
Al Viroa23ace52007-12-19 22:24:16 -05002256 __le32 *vals = stats_rid.vals;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257
Dan Williams3c304952006-04-15 12:26:18 -04002258 clear_bit(JOB_STATS, &ai->jobs);
Pavel Machekca078ba2005-09-03 15:56:57 -07002259 if (ai->power.event) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260 up(&ai->sem);
2261 return;
2262 }
2263 readStatsRid(ai, &stats_rid, RID_STATS, 0);
2264 up(&ai->sem);
2265
Al Viroa23ace52007-12-19 22:24:16 -05002266 ai->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) +
2267 le32_to_cpu(vals[45]);
2268 ai->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) +
2269 le32_to_cpu(vals[41]);
2270 ai->stats.rx_bytes = le32_to_cpu(vals[92]);
2271 ai->stats.tx_bytes = le32_to_cpu(vals[91]);
2272 ai->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) +
2273 le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]);
2274 ai->stats.tx_errors = le32_to_cpu(vals[42]) + ai->stats.tx_fifo_errors;
2275 ai->stats.multicast = le32_to_cpu(vals[43]);
2276 ai->stats.collisions = le32_to_cpu(vals[89]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277
2278 /* detailed rx_errors: */
Al Viroa23ace52007-12-19 22:24:16 -05002279 ai->stats.rx_length_errors = le32_to_cpu(vals[3]);
2280 ai->stats.rx_crc_errors = le32_to_cpu(vals[4]);
2281 ai->stats.rx_frame_errors = le32_to_cpu(vals[2]);
2282 ai->stats.rx_fifo_errors = le32_to_cpu(vals[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283}
2284
Jouni Malinenff1d2762005-05-12 22:54:16 -04002285static struct net_device_stats *airo_get_stats(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286{
2287 struct airo_info *local = dev->priv;
2288
Dan Williams3c304952006-04-15 12:26:18 -04002289 if (!test_bit(JOB_STATS, &local->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290 /* Get stats out of the card if available */
2291 if (down_trylock(&local->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04002292 set_bit(JOB_STATS, &local->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 wake_up_interruptible(&local->thr_wait);
2294 } else
2295 airo_read_stats(local);
2296 }
2297
2298 return &local->stats;
2299}
2300
2301static void airo_set_promisc(struct airo_info *ai) {
2302 Cmd cmd;
2303 Resp rsp;
2304
2305 memset(&cmd, 0, sizeof(cmd));
2306 cmd.cmd=CMD_SETMODE;
Dan Williams3c304952006-04-15 12:26:18 -04002307 clear_bit(JOB_PROMISC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308 cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC;
2309 issuecommand(ai, &cmd, &rsp);
2310 up(&ai->sem);
2311}
2312
2313static void airo_set_multicast_list(struct net_device *dev) {
2314 struct airo_info *ai = dev->priv;
2315
2316 if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
2317 change_bit(FLAG_PROMISC, &ai->flags);
2318 if (down_trylock(&ai->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04002319 set_bit(JOB_PROMISC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320 wake_up_interruptible(&ai->thr_wait);
2321 } else
2322 airo_set_promisc(ai);
2323 }
2324
2325 if ((dev->flags&IFF_ALLMULTI)||dev->mc_count>0) {
2326 /* Turn on multicast. (Should be already setup...) */
2327 }
2328}
2329
2330static int airo_set_mac_address(struct net_device *dev, void *p)
2331{
2332 struct airo_info *ai = dev->priv;
2333 struct sockaddr *addr = p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334
2335 readConfigRid(ai, 1);
2336 memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len);
2337 set_bit (FLAG_COMMIT, &ai->flags);
2338 disable_MAC(ai, 1);
2339 writeConfigRid (ai, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02002340 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341 memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len);
2342 if (ai->wifidev)
2343 memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len);
2344 return 0;
2345}
2346
2347static int airo_change_mtu(struct net_device *dev, int new_mtu)
2348{
2349 if ((new_mtu < 68) || (new_mtu > 2400))
2350 return -EINVAL;
2351 dev->mtu = new_mtu;
2352 return 0;
2353}
2354
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002355static LIST_HEAD(airo_devices);
2356
2357static void add_airo_dev(struct airo_info *ai)
2358{
2359 /* Upper layers already keep track of PCI devices,
2360 * so we only need to remember our non-PCI cards. */
2361 if (!ai->pci)
2362 list_add_tail(&ai->dev_list, &airo_devices);
2363}
2364
2365static void del_airo_dev(struct airo_info *ai)
2366{
2367 if (!ai->pci)
2368 list_del(&ai->dev_list);
2369}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370
2371static int airo_close(struct net_device *dev) {
2372 struct airo_info *ai = dev->priv;
2373
2374 netif_stop_queue(dev);
2375
2376 if (ai->wifidev != dev) {
2377#ifdef POWER_ON_DOWN
2378 /* Shut power to the card. The idea is that the user can save
2379 * power when he doesn't need the card with "ifconfig down".
2380 * That's the method that is most friendly towards the network
2381 * stack (i.e. the network stack won't try to broadcast
2382 * anything on the interface and routes are gone. Jean II */
2383 set_bit(FLAG_RADIO_DOWN, &ai->flags);
2384 disable_MAC(ai, 1);
2385#endif
2386 disable_interrupts( ai );
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002387
2388 free_irq(dev->irq, dev);
2389
2390 set_bit(JOB_DIE, &ai->jobs);
2391 kthread_stop(ai->airo_thread_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392 }
2393 return 0;
2394}
2395
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396void stop_airo_card( struct net_device *dev, int freeres )
2397{
2398 struct airo_info *ai = dev->priv;
2399
2400 set_bit(FLAG_RADIO_DOWN, &ai->flags);
2401 disable_MAC(ai, 1);
2402 disable_interrupts(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403 takedown_proc_entry( dev, ai );
2404 if (test_bit(FLAG_REGISTERED, &ai->flags)) {
2405 unregister_netdev( dev );
2406 if (ai->wifidev) {
2407 unregister_netdev(ai->wifidev);
2408 free_netdev(ai->wifidev);
2409 ai->wifidev = NULL;
2410 }
2411 clear_bit(FLAG_REGISTERED, &ai->flags);
2412 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413 /*
2414 * Clean out tx queue
2415 */
David S. Millerb03efcf2005-07-08 14:57:23 -07002416 if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417 struct sk_buff *skb = NULL;
2418 for (;(skb = skb_dequeue(&ai->txq));)
2419 dev_kfree_skb(skb);
2420 }
2421
Dan Williams9e75af32006-03-16 13:46:29 -05002422 airo_networks_free (ai);
2423
Jesper Juhlb4558ea2005-10-28 16:53:13 -04002424 kfree(ai->flash);
2425 kfree(ai->rssi);
2426 kfree(ai->APList);
2427 kfree(ai->SSID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 if (freeres) {
2429 /* PCMCIA frees this stuff, so only for PCI and ISA */
2430 release_region( dev->base_addr, 64 );
2431 if (test_bit(FLAG_MPI, &ai->flags)) {
2432 if (ai->pci)
2433 mpi_unmap_card(ai->pci);
2434 if (ai->pcimem)
2435 iounmap(ai->pcimem);
2436 if (ai->pciaux)
2437 iounmap(ai->pciaux);
2438 pci_free_consistent(ai->pci, PCI_SHARED_LEN,
2439 ai->shared, ai->shared_dma);
2440 }
2441 }
Herbert Xuf12cc202006-08-22 20:36:13 +10002442 crypto_free_cipher(ai->tfm);
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002443 del_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444 free_netdev( dev );
2445}
2446
2447EXPORT_SYMBOL(stop_airo_card);
2448
Stephen Hemmingerb95cce32007-09-26 22:13:38 -07002449static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450{
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -07002451 memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 return ETH_ALEN;
2453}
2454
2455static void mpi_unmap_card(struct pci_dev *pci)
2456{
2457 unsigned long mem_start = pci_resource_start(pci, 1);
2458 unsigned long mem_len = pci_resource_len(pci, 1);
2459 unsigned long aux_start = pci_resource_start(pci, 2);
2460 unsigned long aux_len = AUXMEMSIZE;
2461
2462 release_mem_region(aux_start, aux_len);
2463 release_mem_region(mem_start, mem_len);
2464}
2465
2466/*************************************************************
2467 * This routine assumes that descriptors have been setup .
2468 * Run at insmod time or after reset when the decriptors
2469 * have been initialized . Returns 0 if all is well nz
2470 * otherwise . Does not allocate memory but sets up card
2471 * using previously allocated descriptors.
2472 */
2473static int mpi_init_descriptors (struct airo_info *ai)
2474{
2475 Cmd cmd;
2476 Resp rsp;
2477 int i;
2478 int rc = SUCCESS;
2479
2480 /* Alloc card RX descriptors */
2481 netif_stop_queue(ai->dev);
2482
2483 memset(&rsp,0,sizeof(rsp));
2484 memset(&cmd,0,sizeof(cmd));
2485
2486 cmd.cmd = CMD_ALLOCATEAUX;
2487 cmd.parm0 = FID_RX;
2488 cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux);
2489 cmd.parm2 = MPI_MAX_FIDS;
2490 rc=issuecommand(ai, &cmd, &rsp);
2491 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002492 airo_print_err(ai->dev->name, "Couldn't allocate RX FID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493 return rc;
2494 }
2495
2496 for (i=0; i<MPI_MAX_FIDS; i++) {
2497 memcpy_toio(ai->rxfids[i].card_ram_off,
2498 &ai->rxfids[i].rx_desc, sizeof(RxFid));
2499 }
2500
2501 /* Alloc card TX descriptors */
2502
2503 memset(&rsp,0,sizeof(rsp));
2504 memset(&cmd,0,sizeof(cmd));
2505
2506 cmd.cmd = CMD_ALLOCATEAUX;
2507 cmd.parm0 = FID_TX;
2508 cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux);
2509 cmd.parm2 = MPI_MAX_FIDS;
2510
2511 for (i=0; i<MPI_MAX_FIDS; i++) {
2512 ai->txfids[i].tx_desc.valid = 1;
2513 memcpy_toio(ai->txfids[i].card_ram_off,
2514 &ai->txfids[i].tx_desc, sizeof(TxFid));
2515 }
2516 ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
2517
2518 rc=issuecommand(ai, &cmd, &rsp);
2519 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002520 airo_print_err(ai->dev->name, "Couldn't allocate TX FID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521 return rc;
2522 }
2523
2524 /* Alloc card Rid descriptor */
2525 memset(&rsp,0,sizeof(rsp));
2526 memset(&cmd,0,sizeof(cmd));
2527
2528 cmd.cmd = CMD_ALLOCATEAUX;
2529 cmd.parm0 = RID_RW;
2530 cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux);
2531 cmd.parm2 = 1; /* Magic number... */
2532 rc=issuecommand(ai, &cmd, &rsp);
2533 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002534 airo_print_err(ai->dev->name, "Couldn't allocate RID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535 return rc;
2536 }
2537
2538 memcpy_toio(ai->config_desc.card_ram_off,
2539 &ai->config_desc.rid_desc, sizeof(Rid));
2540
2541 return rc;
2542}
2543
2544/*
2545 * We are setting up three things here:
2546 * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid.
2547 * 2) Map PCI memory for issueing commands.
2548 * 3) Allocate memory (shared) to send and receive ethernet frames.
2549 */
Michal Schmidt1138c372007-06-29 15:33:41 +02002550static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551{
2552 unsigned long mem_start, mem_len, aux_start, aux_len;
2553 int rc = -1;
2554 int i;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002555 dma_addr_t busaddroff;
2556 unsigned char *vpackoff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 unsigned char __iomem *pciaddroff;
2558
2559 mem_start = pci_resource_start(pci, 1);
2560 mem_len = pci_resource_len(pci, 1);
2561 aux_start = pci_resource_start(pci, 2);
2562 aux_len = AUXMEMSIZE;
2563
Michal Schmidt1138c372007-06-29 15:33:41 +02002564 if (!request_mem_region(mem_start, mem_len, DRV_NAME)) {
2565 airo_print_err("", "Couldn't get region %x[%x]",
2566 (int)mem_start, (int)mem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567 goto out;
2568 }
Michal Schmidt1138c372007-06-29 15:33:41 +02002569 if (!request_mem_region(aux_start, aux_len, DRV_NAME)) {
2570 airo_print_err("", "Couldn't get region %x[%x]",
2571 (int)aux_start, (int)aux_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 goto free_region1;
2573 }
2574
2575 ai->pcimem = ioremap(mem_start, mem_len);
2576 if (!ai->pcimem) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002577 airo_print_err("", "Couldn't map region %x[%x]",
2578 (int)mem_start, (int)mem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579 goto free_region2;
2580 }
2581 ai->pciaux = ioremap(aux_start, aux_len);
2582 if (!ai->pciaux) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002583 airo_print_err("", "Couldn't map region %x[%x]",
2584 (int)aux_start, (int)aux_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 goto free_memmap;
2586 }
2587
2588 /* Reserve PKTSIZE for each fid and 2K for the Rids */
2589 ai->shared = pci_alloc_consistent(pci, PCI_SHARED_LEN, &ai->shared_dma);
2590 if (!ai->shared) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002591 airo_print_err("", "Couldn't alloc_consistent %d",
2592 PCI_SHARED_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593 goto free_auxmap;
2594 }
2595
2596 /*
2597 * Setup descriptor RX, TX, CONFIG
2598 */
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002599 busaddroff = ai->shared_dma;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600 pciaddroff = ai->pciaux + AUX_OFFSET;
2601 vpackoff = ai->shared;
2602
2603 /* RX descriptor setup */
2604 for(i = 0; i < MPI_MAX_FIDS; i++) {
2605 ai->rxfids[i].pending = 0;
2606 ai->rxfids[i].card_ram_off = pciaddroff;
2607 ai->rxfids[i].virtual_host_addr = vpackoff;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002608 ai->rxfids[i].rx_desc.host_addr = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609 ai->rxfids[i].rx_desc.valid = 1;
2610 ai->rxfids[i].rx_desc.len = PKTSIZE;
2611 ai->rxfids[i].rx_desc.rdy = 0;
2612
2613 pciaddroff += sizeof(RxFid);
2614 busaddroff += PKTSIZE;
2615 vpackoff += PKTSIZE;
2616 }
2617
2618 /* TX descriptor setup */
2619 for(i = 0; i < MPI_MAX_FIDS; i++) {
2620 ai->txfids[i].card_ram_off = pciaddroff;
2621 ai->txfids[i].virtual_host_addr = vpackoff;
2622 ai->txfids[i].tx_desc.valid = 1;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002623 ai->txfids[i].tx_desc.host_addr = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624 memcpy(ai->txfids[i].virtual_host_addr,
2625 &wifictlhdr8023, sizeof(wifictlhdr8023));
2626
2627 pciaddroff += sizeof(TxFid);
2628 busaddroff += PKTSIZE;
2629 vpackoff += PKTSIZE;
2630 }
2631 ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
2632
2633 /* Rid descriptor setup */
2634 ai->config_desc.card_ram_off = pciaddroff;
2635 ai->config_desc.virtual_host_addr = vpackoff;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002636 ai->config_desc.rid_desc.host_addr = busaddroff;
2637 ai->ridbus = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002638 ai->config_desc.rid_desc.rid = 0;
2639 ai->config_desc.rid_desc.len = RIDSIZE;
2640 ai->config_desc.rid_desc.valid = 1;
2641 pciaddroff += sizeof(Rid);
2642 busaddroff += RIDSIZE;
2643 vpackoff += RIDSIZE;
2644
2645 /* Tell card about descriptors */
2646 if (mpi_init_descriptors (ai) != SUCCESS)
2647 goto free_shared;
2648
2649 return 0;
2650 free_shared:
2651 pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
2652 free_auxmap:
2653 iounmap(ai->pciaux);
2654 free_memmap:
2655 iounmap(ai->pcimem);
2656 free_region2:
2657 release_mem_region(aux_start, aux_len);
2658 free_region1:
2659 release_mem_region(mem_start, mem_len);
2660 out:
2661 return rc;
2662}
2663
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -07002664static const struct header_ops airo_header_ops = {
2665 .parse = wll_header_parse,
2666};
2667
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668static void wifi_setup(struct net_device *dev)
2669{
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -07002670 dev->header_ops = &airo_header_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671 dev->hard_start_xmit = &airo_start_xmit11;
2672 dev->get_stats = &airo_get_stats;
2673 dev->set_mac_address = &airo_set_mac_address;
2674 dev->do_ioctl = &airo_ioctl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675 dev->wireless_handlers = &airo_handler_def;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676 dev->change_mtu = &airo_change_mtu;
2677 dev->open = &airo_open;
2678 dev->stop = &airo_close;
2679
2680 dev->type = ARPHRD_IEEE80211;
2681 dev->hard_header_len = ETH_HLEN;
Dan Williams15db2762006-03-16 13:46:27 -05002682 dev->mtu = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683 dev->addr_len = ETH_ALEN;
2684 dev->tx_queue_len = 100;
2685
2686 memset(dev->broadcast,0xFF, ETH_ALEN);
2687
2688 dev->flags = IFF_BROADCAST|IFF_MULTICAST;
2689}
2690
2691static struct net_device *init_wifidev(struct airo_info *ai,
2692 struct net_device *ethdev)
2693{
2694 int err;
2695 struct net_device *dev = alloc_netdev(0, "wifi%d", wifi_setup);
2696 if (!dev)
2697 return NULL;
2698 dev->priv = ethdev->priv;
2699 dev->irq = ethdev->irq;
2700 dev->base_addr = ethdev->base_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701 dev->wireless_data = ethdev->wireless_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702 memcpy(dev->dev_addr, ethdev->dev_addr, dev->addr_len);
2703 err = register_netdev(dev);
2704 if (err<0) {
2705 free_netdev(dev);
2706 return NULL;
2707 }
2708 return dev;
2709}
2710
Jouni Malinenff1d2762005-05-12 22:54:16 -04002711static int reset_card( struct net_device *dev , int lock) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712 struct airo_info *ai = dev->priv;
2713
2714 if (lock && down_interruptible(&ai->sem))
2715 return -1;
2716 waitbusy (ai);
2717 OUT4500(ai,COMMAND,CMD_SOFTRESET);
2718 msleep(200);
2719 waitbusy (ai);
2720 msleep(200);
2721 if (lock)
2722 up(&ai->sem);
2723 return 0;
2724}
2725
Dan Williams3c304952006-04-15 12:26:18 -04002726#define AIRO_MAX_NETWORK_COUNT 64
Dan Williams9e75af32006-03-16 13:46:29 -05002727static int airo_networks_allocate(struct airo_info *ai)
2728{
2729 if (ai->networks)
2730 return 0;
2731
2732 ai->networks =
Dan Williams3c304952006-04-15 12:26:18 -04002733 kzalloc(AIRO_MAX_NETWORK_COUNT * sizeof(BSSListElement),
Dan Williams9e75af32006-03-16 13:46:29 -05002734 GFP_KERNEL);
2735 if (!ai->networks) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002736 airo_print_warn("", "Out of memory allocating beacons");
Dan Williams9e75af32006-03-16 13:46:29 -05002737 return -ENOMEM;
2738 }
2739
2740 return 0;
2741}
2742
2743static void airo_networks_free(struct airo_info *ai)
2744{
Dan Williams9e75af32006-03-16 13:46:29 -05002745 kfree(ai->networks);
2746 ai->networks = NULL;
2747}
2748
2749static void airo_networks_initialize(struct airo_info *ai)
2750{
2751 int i;
2752
2753 INIT_LIST_HEAD(&ai->network_free_list);
2754 INIT_LIST_HEAD(&ai->network_list);
Dan Williams3c304952006-04-15 12:26:18 -04002755 for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++)
Dan Williams9e75af32006-03-16 13:46:29 -05002756 list_add_tail(&ai->networks[i].list,
2757 &ai->network_free_list);
2758}
2759
Dan Williams3c304952006-04-15 12:26:18 -04002760static int airo_test_wpa_capable(struct airo_info *ai)
2761{
2762 int status;
2763 CapabilityRid cap_rid;
Dan Williams3c304952006-04-15 12:26:18 -04002764
2765 status = readCapabilityRid(ai, &cap_rid, 1);
2766 if (status != SUCCESS) return 0;
2767
2768 /* Only firmware versions 5.30.17 or better can do WPA */
Al Viro56d81bd2007-12-20 17:18:35 -05002769 if (le16_to_cpu(cap_rid.softVer) > 0x530
2770 || (le16_to_cpu(cap_rid.softVer) == 0x530
2771 && le16_to_cpu(cap_rid.softSubVer) >= 17)) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002772 airo_print_info("", "WPA is supported.");
Dan Williams3c304952006-04-15 12:26:18 -04002773 return 1;
2774 }
2775
2776 /* No WPA support */
Michal Schmidt1138c372007-06-29 15:33:41 +02002777 airo_print_info("", "WPA unsupported (only firmware versions 5.30.17"
Dan Williams3c304952006-04-15 12:26:18 -04002778 " and greater support WPA. Detected %s)", cap_rid.prodVer);
2779 return 0;
2780}
2781
Jouni Malinenff1d2762005-05-12 22:54:16 -04002782static struct net_device *_init_airo_card( unsigned short irq, int port,
2783 int is_pcmcia, struct pci_dev *pci,
2784 struct device *dmdev )
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785{
2786 struct net_device *dev;
2787 struct airo_info *ai;
2788 int i, rc;
Joe Perches0795af52007-10-03 17:59:30 -07002789 DECLARE_MAC_BUF(mac);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790
2791 /* Create the network device object. */
Michal Schmidt1138c372007-06-29 15:33:41 +02002792 dev = alloc_netdev(sizeof(*ai), "", ether_setup);
2793 if (!dev) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002794 airo_print_err("", "Couldn't alloc_etherdev");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 }
2797
2798 ai = dev->priv;
2799 ai->wifidev = NULL;
Michal Schmidtfb038c22007-06-29 15:33:52 +02002800 ai->flags = 1 << FLAG_RADIO_DOWN;
Dan Williams3c304952006-04-15 12:26:18 -04002801 ai->jobs = 0;
Dan Williams934d8bf2006-03-16 13:46:23 -05002802 ai->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002804 airo_print_dbg("", "Found an MPI350 card");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 set_bit(FLAG_MPI, &ai->flags);
2806 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 spin_lock_init(&ai->aux_lock);
2808 sema_init(&ai->sem, 1);
2809 ai->config.len = 0;
2810 ai->pci = pci;
2811 init_waitqueue_head (&ai->thr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812 ai->tfm = NULL;
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002813 add_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814
Dan Williams9e75af32006-03-16 13:46:29 -05002815 if (airo_networks_allocate (ai))
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002816 goto err_out_free;
Dan Williams9e75af32006-03-16 13:46:29 -05002817 airo_networks_initialize (ai);
2818
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 /* The Airo-specific entries in the device structure. */
2820 if (test_bit(FLAG_MPI,&ai->flags)) {
2821 skb_queue_head_init (&ai->txq);
2822 dev->hard_start_xmit = &mpi_start_xmit;
2823 } else
2824 dev->hard_start_xmit = &airo_start_xmit;
2825 dev->get_stats = &airo_get_stats;
2826 dev->set_multicast_list = &airo_set_multicast_list;
2827 dev->set_mac_address = &airo_set_mac_address;
2828 dev->do_ioctl = &airo_ioctl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829 dev->wireless_handlers = &airo_handler_def;
2830 ai->wireless_data.spy_data = &ai->spy_data;
2831 dev->wireless_data = &ai->wireless_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 dev->change_mtu = &airo_change_mtu;
2833 dev->open = &airo_open;
2834 dev->stop = &airo_close;
2835 dev->irq = irq;
2836 dev->base_addr = port;
2837
2838 SET_NETDEV_DEV(dev, dmdev);
2839
Matthieu CASTET1d97f382005-12-01 02:35:26 -05002840 reset_card (dev, 1);
2841 msleep(400);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843 if (!is_pcmcia) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002844 if (!request_region(dev->base_addr, 64, DRV_NAME)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002845 rc = -EBUSY;
Dan Williams934d8bf2006-03-16 13:46:23 -05002846 airo_print_err(dev->name, "Couldn't request region");
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002847 goto err_out_nets;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848 }
2849 }
2850
2851 if (test_bit(FLAG_MPI,&ai->flags)) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002852 if (mpi_map_card(ai, pci)) {
2853 airo_print_err("", "Could not map memory");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854 goto err_out_res;
2855 }
2856 }
2857
2858 if (probe) {
2859 if ( setup_card( ai, dev->dev_addr, 1 ) != SUCCESS ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002860 airo_print_err(dev->name, "MAC could not be enabled" );
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861 rc = -EIO;
2862 goto err_out_map;
2863 }
2864 } else if (!test_bit(FLAG_MPI,&ai->flags)) {
2865 ai->bap_read = fast_bap_read;
2866 set_bit(FLAG_FLASHING, &ai->flags);
2867 }
2868
Dan Williams3c304952006-04-15 12:26:18 -04002869 /* Test for WPA support */
2870 if (airo_test_wpa_capable(ai)) {
2871 set_bit(FLAG_WPA_CAPABLE, &ai->flags);
2872 ai->bssListFirst = RID_WPA_BSSLISTFIRST;
2873 ai->bssListNext = RID_WPA_BSSLISTNEXT;
2874 ai->bssListRidLen = sizeof(BSSListRid);
2875 } else {
2876 ai->bssListFirst = RID_BSSLISTFIRST;
2877 ai->bssListNext = RID_BSSLISTNEXT;
2878 ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra);
2879 }
2880
Michal Schmidt1138c372007-06-29 15:33:41 +02002881 strcpy(dev->name, "eth%d");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882 rc = register_netdev(dev);
2883 if (rc) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002884 airo_print_err(dev->name, "Couldn't register_netdev");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002885 goto err_out_map;
2886 }
2887 ai->wifidev = init_wifidev(ai, dev);
Florin Malita431aca52006-10-10 16:46:30 -04002888 if (!ai->wifidev)
2889 goto err_out_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890
2891 set_bit(FLAG_REGISTERED,&ai->flags);
Joe Perches0795af52007-10-03 17:59:30 -07002892 airo_print_info(dev->name, "MAC enabled %s",
2893 print_mac(mac, dev->dev_addr));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894
2895 /* Allocate the transmit buffers */
2896 if (probe && !test_bit(FLAG_MPI,&ai->flags))
2897 for( i = 0; i < MAX_FIDS; i++ )
Dan Williams15db2762006-03-16 13:46:27 -05002898 ai->fids[i] = transmit_allocate(ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002899
Florin Malita431aca52006-10-10 16:46:30 -04002900 if (setup_proc_entry(dev, dev->priv) < 0)
2901 goto err_out_wifi;
2902
Linus Torvalds1da177e2005-04-16 15:20:36 -07002903 return dev;
2904
Florin Malita431aca52006-10-10 16:46:30 -04002905err_out_wifi:
2906 unregister_netdev(ai->wifidev);
2907 free_netdev(ai->wifidev);
2908err_out_reg:
2909 unregister_netdev(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910err_out_map:
2911 if (test_bit(FLAG_MPI,&ai->flags) && pci) {
2912 pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
2913 iounmap(ai->pciaux);
2914 iounmap(ai->pcimem);
2915 mpi_unmap_card(ai->pci);
2916 }
2917err_out_res:
2918 if (!is_pcmcia)
2919 release_region( dev->base_addr, 64 );
Michal Schmidt4d881902007-03-16 12:42:59 +01002920err_out_nets:
2921 airo_networks_free(ai);
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002922 del_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002923err_out_free:
2924 free_netdev(dev);
2925 return NULL;
2926}
2927
2928struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia,
2929 struct device *dmdev)
2930{
2931 return _init_airo_card ( irq, port, is_pcmcia, NULL, dmdev);
2932}
2933
2934EXPORT_SYMBOL(init_airo_card);
2935
2936static int waitbusy (struct airo_info *ai) {
2937 int delay = 0;
2938 while ((IN4500 (ai, COMMAND) & COMMAND_BUSY) & (delay < 10000)) {
2939 udelay (10);
2940 if ((++delay % 20) == 0)
2941 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
2942 }
2943 return delay < 10000;
2944}
2945
2946int reset_airo_card( struct net_device *dev )
2947{
2948 int i;
2949 struct airo_info *ai = dev->priv;
Joe Perches0795af52007-10-03 17:59:30 -07002950 DECLARE_MAC_BUF(mac);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002951
2952 if (reset_card (dev, 1))
2953 return -1;
2954
2955 if ( setup_card(ai, dev->dev_addr, 1 ) != SUCCESS ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002956 airo_print_err(dev->name, "MAC could not be enabled");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957 return -1;
2958 }
Joe Perches0795af52007-10-03 17:59:30 -07002959 airo_print_info(dev->name, "MAC enabled %s",
2960 print_mac(mac, dev->dev_addr));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961 /* Allocate the transmit buffers if needed */
2962 if (!test_bit(FLAG_MPI,&ai->flags))
2963 for( i = 0; i < MAX_FIDS; i++ )
Dan Williams15db2762006-03-16 13:46:27 -05002964 ai->fids[i] = transmit_allocate (ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002965
2966 enable_interrupts( ai );
2967 netif_wake_queue(dev);
2968 return 0;
2969}
2970
2971EXPORT_SYMBOL(reset_airo_card);
2972
2973static void airo_send_event(struct net_device *dev) {
2974 struct airo_info *ai = dev->priv;
2975 union iwreq_data wrqu;
2976 StatusRid status_rid;
2977
Dan Williams3c304952006-04-15 12:26:18 -04002978 clear_bit(JOB_EVENT, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979 PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
2980 up(&ai->sem);
2981 wrqu.data.length = 0;
2982 wrqu.data.flags = 0;
2983 memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN);
2984 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
2985
2986 /* Send event to user space */
2987 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
2988}
2989
Dan Williams9e75af32006-03-16 13:46:29 -05002990static void airo_process_scan_results (struct airo_info *ai) {
2991 union iwreq_data wrqu;
Dan Williams3c304952006-04-15 12:26:18 -04002992 BSSListRid bss;
Dan Williams9e75af32006-03-16 13:46:29 -05002993 int rc;
2994 BSSListElement * loop_net;
2995 BSSListElement * tmp_net;
2996
2997 /* Blow away current list of scan results */
2998 list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) {
2999 list_move_tail (&loop_net->list, &ai->network_free_list);
3000 /* Don't blow away ->list, just BSS data */
3001 memset (loop_net, 0, sizeof (loop_net->bss));
3002 }
3003
3004 /* Try to read the first entry of the scan result */
Dan Williams3c304952006-04-15 12:26:18 -04003005 rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0);
Al Viro17e70492007-12-19 18:56:37 -05003006 if((rc) || (bss.index == cpu_to_le16(0xffff))) {
Dan Williams9e75af32006-03-16 13:46:29 -05003007 /* No scan results */
3008 goto out;
3009 }
3010
3011 /* Read and parse all entries */
3012 tmp_net = NULL;
Al Viro17e70492007-12-19 18:56:37 -05003013 while((!rc) && (bss.index != cpu_to_le16(0xffff))) {
Dan Williams9e75af32006-03-16 13:46:29 -05003014 /* Grab a network off the free list */
3015 if (!list_empty(&ai->network_free_list)) {
3016 tmp_net = list_entry(ai->network_free_list.next,
3017 BSSListElement, list);
3018 list_del(ai->network_free_list.next);
3019 }
3020
3021 if (tmp_net != NULL) {
Dan Williams3c304952006-04-15 12:26:18 -04003022 memcpy(tmp_net, &bss, sizeof(tmp_net->bss));
Dan Williams9e75af32006-03-16 13:46:29 -05003023 list_add_tail(&tmp_net->list, &ai->network_list);
3024 tmp_net = NULL;
3025 }
3026
3027 /* Read next entry */
Dan Williams3c304952006-04-15 12:26:18 -04003028 rc = PC4500_readrid(ai, ai->bssListNext,
3029 &bss, ai->bssListRidLen, 0);
Dan Williams9e75af32006-03-16 13:46:29 -05003030 }
3031
3032out:
3033 ai->scan_timeout = 0;
Dan Williams3c304952006-04-15 12:26:18 -04003034 clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
Dan Williams9e75af32006-03-16 13:46:29 -05003035 up(&ai->sem);
3036
3037 /* Send an empty event to user space.
3038 * We don't send the received data on
3039 * the event because it would require
3040 * us to do complex transcoding, and
3041 * we want to minimise the work done in
3042 * the irq handler. Use a request to
3043 * extract the data - Jean II */
3044 wrqu.data.length = 0;
3045 wrqu.data.flags = 0;
3046 wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL);
3047}
3048
Linus Torvalds1da177e2005-04-16 15:20:36 -07003049static int airo_thread(void *data) {
3050 struct net_device *dev = data;
3051 struct airo_info *ai = dev->priv;
3052 int locked;
Rafael J. Wysocki83144182007-07-17 04:03:35 -07003053
3054 set_freezable();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003055 while(1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003056 /* make swsusp happy with our thread */
Christoph Lameter3e1d1d22005-06-24 23:13:50 -07003057 try_to_freeze();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003058
Dan Williams3c304952006-04-15 12:26:18 -04003059 if (test_bit(JOB_DIE, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003060 break;
3061
Dan Williams3c304952006-04-15 12:26:18 -04003062 if (ai->jobs) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003063 locked = down_interruptible(&ai->sem);
3064 } else {
3065 wait_queue_t wait;
3066
3067 init_waitqueue_entry(&wait, current);
3068 add_wait_queue(&ai->thr_wait, &wait);
3069 for (;;) {
3070 set_current_state(TASK_INTERRUPTIBLE);
Dan Williams3c304952006-04-15 12:26:18 -04003071 if (ai->jobs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003072 break;
Dan Williams9e75af32006-03-16 13:46:29 -05003073 if (ai->expires || ai->scan_timeout) {
3074 if (ai->scan_timeout &&
3075 time_after_eq(jiffies,ai->scan_timeout)){
Dan Williams3c304952006-04-15 12:26:18 -04003076 set_bit(JOB_SCAN_RESULTS, &ai->jobs);
Dan Williams9e75af32006-03-16 13:46:29 -05003077 break;
3078 } else if (ai->expires &&
3079 time_after_eq(jiffies,ai->expires)){
Dan Williams3c304952006-04-15 12:26:18 -04003080 set_bit(JOB_AUTOWEP, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003081 break;
3082 }
Dave Kleikamp5bb85f12006-10-10 14:45:46 -07003083 if (!kthread_should_stop() &&
3084 !freezing(current)) {
Dan Williams9e75af32006-03-16 13:46:29 -05003085 unsigned long wake_at;
3086 if (!ai->expires || !ai->scan_timeout) {
3087 wake_at = max(ai->expires,
3088 ai->scan_timeout);
3089 } else {
3090 wake_at = min(ai->expires,
3091 ai->scan_timeout);
3092 }
3093 schedule_timeout(wake_at - jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003094 continue;
3095 }
Dave Kleikamp5bb85f12006-10-10 14:45:46 -07003096 } else if (!kthread_should_stop() &&
3097 !freezing(current)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003098 schedule();
3099 continue;
3100 }
3101 break;
3102 }
3103 current->state = TASK_RUNNING;
3104 remove_wait_queue(&ai->thr_wait, &wait);
3105 locked = 1;
3106 }
3107
3108 if (locked)
3109 continue;
3110
Dan Williams3c304952006-04-15 12:26:18 -04003111 if (test_bit(JOB_DIE, &ai->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003112 up(&ai->sem);
3113 break;
3114 }
3115
Pavel Machekca078ba2005-09-03 15:56:57 -07003116 if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003117 up(&ai->sem);
3118 continue;
3119 }
3120
Dan Williams3c304952006-04-15 12:26:18 -04003121 if (test_bit(JOB_XMIT, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003122 airo_end_xmit(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003123 else if (test_bit(JOB_XMIT11, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003124 airo_end_xmit11(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003125 else if (test_bit(JOB_STATS, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003126 airo_read_stats(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003127 else if (test_bit(JOB_WSTATS, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003128 airo_read_wireless_stats(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003129 else if (test_bit(JOB_PROMISC, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003130 airo_set_promisc(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003131 else if (test_bit(JOB_MIC, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003132 micinit(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003133 else if (test_bit(JOB_EVENT, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003134 airo_send_event(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003135 else if (test_bit(JOB_AUTOWEP, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003136 timer_func(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003137 else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs))
Dan Williams9e75af32006-03-16 13:46:29 -05003138 airo_process_scan_results(ai);
3139 else /* Shouldn't get here, but we make sure to unlock */
3140 up(&ai->sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141 }
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07003142
3143 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144}
3145
Al Viro0300b332007-12-19 22:38:33 -05003146static int header_len(__le16 ctl)
3147{
3148 u16 fc = le16_to_cpu(ctl);
3149 switch (fc & 0xc) {
3150 case 4:
3151 if ((fc & 0xe0) == 0xc0)
3152 return 10; /* one-address control packet */
3153 return 16; /* two-address control packet */
3154 case 8:
3155 if ((fc & 0x300) == 0x300)
3156 return 30; /* WDS packet */
3157 }
3158 return 24;
3159}
3160
Jeff Garzik28fc1f52007-10-29 05:46:16 -04003161static irqreturn_t airo_interrupt(int irq, void *dev_id)
3162{
3163 struct net_device *dev = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003164 u16 status;
3165 u16 fid;
3166 struct airo_info *apriv = dev->priv;
3167 u16 savedInterrupts = 0;
3168 int handled = 0;
3169
3170 if (!netif_device_present(dev))
3171 return IRQ_NONE;
3172
3173 for (;;) {
3174 status = IN4500( apriv, EVSTAT );
3175 if ( !(status & STATUS_INTS) || status == 0xffff ) break;
3176
3177 handled = 1;
3178
3179 if ( status & EV_AWAKE ) {
3180 OUT4500( apriv, EVACK, EV_AWAKE );
3181 OUT4500( apriv, EVACK, EV_AWAKE );
3182 }
3183
3184 if (!savedInterrupts) {
3185 savedInterrupts = IN4500( apriv, EVINTEN );
3186 OUT4500( apriv, EVINTEN, 0 );
3187 }
3188
3189 if ( status & EV_MIC ) {
3190 OUT4500( apriv, EVACK, EV_MIC );
Linus Torvalds1da177e2005-04-16 15:20:36 -07003191 if (test_bit(FLAG_MIC_CAPABLE, &apriv->flags)) {
Dan Williams3c304952006-04-15 12:26:18 -04003192 set_bit(JOB_MIC, &apriv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003193 wake_up_interruptible(&apriv->thr_wait);
3194 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003195 }
3196 if ( status & EV_LINK ) {
3197 union iwreq_data wrqu;
Dan Williams6fcdf562006-03-31 15:08:46 -05003198 int scan_forceloss = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003199 /* The link status has changed, if you want to put a
3200 monitor hook in, do it here. (Remember that
3201 interrupts are still disabled!)
3202 */
3203 u16 newStatus = IN4500(apriv, LINKSTAT);
3204 OUT4500( apriv, EVACK, EV_LINK);
3205 /* Here is what newStatus means: */
3206#define NOBEACON 0x8000 /* Loss of sync - missed beacons */
3207#define MAXRETRIES 0x8001 /* Loss of sync - max retries */
3208#define MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/
3209#define FORCELOSS 0x8003 /* Loss of sync - host request */
3210#define TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */
3211#define DEAUTH 0x8100 /* Deauthentication (low byte is reason code) */
3212#define DISASS 0x8200 /* Disassociation (low byte is reason code) */
3213#define ASSFAIL 0x8400 /* Association failure (low byte is reason
3214 code) */
3215#define AUTHFAIL 0x0300 /* Authentication failure (low byte is reason
3216 code) */
Dan Williams6fcdf562006-03-31 15:08:46 -05003217#define ASSOCIATED 0x0400 /* Associated */
3218#define REASSOCIATED 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003219#define RC_RESERVED 0 /* Reserved return code */
3220#define RC_NOREASON 1 /* Unspecified reason */
3221#define RC_AUTHINV 2 /* Previous authentication invalid */
3222#define RC_DEAUTH 3 /* Deauthenticated because sending station is
3223 leaving */
3224#define RC_NOACT 4 /* Disassociated due to inactivity */
3225#define RC_MAXLOAD 5 /* Disassociated because AP is unable to handle
3226 all currently associated stations */
3227#define RC_BADCLASS2 6 /* Class 2 frame received from
3228 non-Authenticated station */
3229#define RC_BADCLASS3 7 /* Class 3 frame received from
3230 non-Associated station */
3231#define RC_STATLEAVE 8 /* Disassociated because sending station is
3232 leaving BSS */
3233#define RC_NOAUTH 9 /* Station requesting (Re)Association is not
3234 Authenticated with the responding station */
Dan Williams6fcdf562006-03-31 15:08:46 -05003235 if (newStatus == FORCELOSS && apriv->scan_timeout > 0)
3236 scan_forceloss = 1;
3237 if(newStatus == ASSOCIATED || newStatus == REASSOCIATED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003238 if (auto_wep)
3239 apriv->expires = 0;
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07003240 if (apriv->list_bss_task)
3241 wake_up_process(apriv->list_bss_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003242 set_bit(FLAG_UPDATE_UNI, &apriv->flags);
3243 set_bit(FLAG_UPDATE_MULTI, &apriv->flags);
Dan Williams6fcdf562006-03-31 15:08:46 -05003244
Linus Torvalds1da177e2005-04-16 15:20:36 -07003245 if (down_trylock(&apriv->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04003246 set_bit(JOB_EVENT, &apriv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003247 wake_up_interruptible(&apriv->thr_wait);
3248 } else
3249 airo_send_event(dev);
Dan Williams6fcdf562006-03-31 15:08:46 -05003250 } else if (!scan_forceloss) {
3251 if (auto_wep && !apriv->expires) {
3252 apriv->expires = RUN_AT(3*HZ);
3253 wake_up_interruptible(&apriv->thr_wait);
3254 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003255
3256 /* Send event to user space */
Dan Williams6fcdf562006-03-31 15:08:46 -05003257 memset(wrqu.ap_addr.sa_data, '\0', ETH_ALEN);
3258 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003259 wireless_send_event(dev, SIOCGIWAP, &wrqu,NULL);
3260 }
3261 }
3262
3263 /* Check to see if there is something to receive */
3264 if ( status & EV_RX ) {
3265 struct sk_buff *skb = NULL;
Al Virob8c06bc2007-12-19 17:55:43 -05003266 __le16 fc, v;
3267 u16 len, hdrlen = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003268#pragma pack(1)
3269 struct {
Al Viro593c2b92007-12-17 15:09:34 -05003270 __le16 status, len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003271 u8 rssi[2];
3272 u8 rate;
3273 u8 freq;
Al Viro593c2b92007-12-17 15:09:34 -05003274 __le16 tmp[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -07003275 } hdr;
3276#pragma pack()
3277 u16 gap;
Al Virob8c06bc2007-12-19 17:55:43 -05003278 __le16 tmpbuf[4];
3279 __le16 *buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003280
3281 if (test_bit(FLAG_MPI,&apriv->flags)) {
3282 if (test_bit(FLAG_802_11, &apriv->flags))
3283 mpi_receive_802_11(apriv);
3284 else
3285 mpi_receive_802_3(apriv);
3286 OUT4500(apriv, EVACK, EV_RX);
3287 goto exitrx;
3288 }
3289
3290 fid = IN4500( apriv, RXFID );
3291
3292 /* Get the packet length */
3293 if (test_bit(FLAG_802_11, &apriv->flags)) {
3294 bap_setup (apriv, fid, 4, BAP0);
Al Virob8c06bc2007-12-19 17:55:43 -05003295 bap_read (apriv, (__le16*)&hdr, sizeof(hdr), BAP0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003296 /* Bad CRC. Ignore packet */
3297 if (le16_to_cpu(hdr.status) & 2)
3298 hdr.len = 0;
3299 if (apriv->wifidev == NULL)
3300 hdr.len = 0;
3301 } else {
3302 bap_setup (apriv, fid, 0x36, BAP0);
Al Virob8c06bc2007-12-19 17:55:43 -05003303 bap_read (apriv, &hdr.len, 2, BAP0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003304 }
3305 len = le16_to_cpu(hdr.len);
3306
Dan Williams15db2762006-03-16 13:46:27 -05003307 if (len > AIRO_DEF_MTU) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003308 airo_print_err(apriv->dev->name, "Bad size %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003309 goto badrx;
3310 }
3311 if (len == 0)
3312 goto badrx;
3313
3314 if (test_bit(FLAG_802_11, &apriv->flags)) {
Al Viro0300b332007-12-19 22:38:33 -05003315 bap_read (apriv, &fc, sizeof(fc), BAP0);
3316 hdrlen = header_len(fc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003317 } else
3318 hdrlen = ETH_ALEN * 2;
3319
3320 skb = dev_alloc_skb( len + hdrlen + 2 + 2 );
3321 if ( !skb ) {
3322 apriv->stats.rx_dropped++;
3323 goto badrx;
3324 }
3325 skb_reserve(skb, 2); /* This way the IP header is aligned */
Al Virob8c06bc2007-12-19 17:55:43 -05003326 buffer = (__le16*)skb_put (skb, len + hdrlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003327 if (test_bit(FLAG_802_11, &apriv->flags)) {
3328 buffer[0] = fc;
3329 bap_read (apriv, buffer + 1, hdrlen - 2, BAP0);
3330 if (hdrlen == 24)
3331 bap_read (apriv, tmpbuf, 6, BAP0);
3332
Al Virob8c06bc2007-12-19 17:55:43 -05003333 bap_read (apriv, &v, sizeof(v), BAP0);
3334 gap = le16_to_cpu(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003335 if (gap) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003336 if (gap <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003337 bap_read (apriv, tmpbuf, gap, BAP0);
Dan Williams934d8bf2006-03-16 13:46:23 -05003338 } else {
3339 airo_print_err(apriv->dev->name, "gaplen too "
3340 "big. Problems will follow...");
3341 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003342 }
3343 bap_read (apriv, buffer + hdrlen/2, len, BAP0);
3344 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003345 MICBuffer micbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003346 bap_read (apriv, buffer, ETH_ALEN*2, BAP0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003347 if (apriv->micstats.enabled) {
Al Virob8c06bc2007-12-19 17:55:43 -05003348 bap_read (apriv,(__le16*)&micbuf,sizeof(micbuf),BAP0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003349 if (ntohs(micbuf.typelen) > 0x05DC)
3350 bap_setup (apriv, fid, 0x44, BAP0);
3351 else {
3352 if (len <= sizeof(micbuf))
3353 goto badmic;
3354
3355 len -= sizeof(micbuf);
3356 skb_trim (skb, len + hdrlen);
3357 }
3358 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003359 bap_read(apriv,buffer+ETH_ALEN,len,BAP0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003360 if (decapsulate(apriv,&micbuf,(etherHead*)buffer,len)) {
3361badmic:
3362 dev_kfree_skb_irq (skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003363badrx:
3364 OUT4500( apriv, EVACK, EV_RX);
3365 goto exitrx;
3366 }
3367 }
3368#ifdef WIRELESS_SPY
3369 if (apriv->spy_data.spy_number > 0) {
3370 char *sa;
3371 struct iw_quality wstats;
3372 /* Prepare spy data : addr + qual */
3373 if (!test_bit(FLAG_802_11, &apriv->flags)) {
3374 sa = (char*)buffer + 6;
3375 bap_setup (apriv, fid, 8, BAP0);
Al Virob8c06bc2007-12-19 17:55:43 -05003376 bap_read (apriv, (__le16*)hdr.rssi, 2, BAP0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003377 } else
3378 sa = (char*)buffer + 10;
3379 wstats.qual = hdr.rssi[0];
3380 if (apriv->rssi)
3381 wstats.level = 0x100 - apriv->rssi[hdr.rssi[1]].rssidBm;
3382 else
3383 wstats.level = (hdr.rssi[1] + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04003384 wstats.noise = apriv->wstats.qual.noise;
3385 wstats.updated = IW_QUAL_LEVEL_UPDATED
3386 | IW_QUAL_QUAL_UPDATED
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07003387 | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388 /* Update spy records */
3389 wireless_spy_update(dev, sa, &wstats);
3390 }
3391#endif /* WIRELESS_SPY */
3392 OUT4500( apriv, EVACK, EV_RX);
3393
3394 if (test_bit(FLAG_802_11, &apriv->flags)) {
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07003395 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003396 skb->pkt_type = PACKET_OTHERHOST;
3397 skb->dev = apriv->wifidev;
3398 skb->protocol = htons(ETH_P_802_2);
Arnaldo Carvalho de Melo4c13eb62007-04-25 17:40:23 -07003399 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 skb->protocol = eth_type_trans(skb,dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003401 skb->dev->last_rx = jiffies;
3402 skb->ip_summed = CHECKSUM_NONE;
3403
3404 netif_rx( skb );
3405 }
3406exitrx:
3407
3408 /* Check to see if a packet has been transmitted */
3409 if ( status & ( EV_TX|EV_TXCPY|EV_TXEXC ) ) {
3410 int i;
3411 int len = 0;
3412 int index = -1;
3413
3414 if (test_bit(FLAG_MPI,&apriv->flags)) {
3415 unsigned long flags;
3416
3417 if (status & EV_TXEXC)
3418 get_tx_error(apriv, -1);
3419 spin_lock_irqsave(&apriv->aux_lock, flags);
David S. Millerb03efcf2005-07-08 14:57:23 -07003420 if (!skb_queue_empty(&apriv->txq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003421 spin_unlock_irqrestore(&apriv->aux_lock,flags);
3422 mpi_send_packet (dev);
3423 } else {
3424 clear_bit(FLAG_PENDING_XMIT, &apriv->flags);
3425 spin_unlock_irqrestore(&apriv->aux_lock,flags);
3426 netif_wake_queue (dev);
3427 }
3428 OUT4500( apriv, EVACK,
3429 status & (EV_TX|EV_TXCPY|EV_TXEXC));
3430 goto exittx;
3431 }
3432
3433 fid = IN4500(apriv, TXCOMPLFID);
3434
3435 for( i = 0; i < MAX_FIDS; i++ ) {
3436 if ( ( apriv->fids[i] & 0xffff ) == fid ) {
3437 len = apriv->fids[i] >> 16;
3438 index = i;
3439 }
3440 }
3441 if (index != -1) {
3442 if (status & EV_TXEXC)
3443 get_tx_error(apriv, index);
3444 OUT4500( apriv, EVACK, status & (EV_TX | EV_TXEXC));
3445 /* Set up to be used again */
3446 apriv->fids[index] &= 0xffff;
3447 if (index < MAX_FIDS / 2) {
3448 if (!test_bit(FLAG_PENDING_XMIT, &apriv->flags))
3449 netif_wake_queue(dev);
3450 } else {
3451 if (!test_bit(FLAG_PENDING_XMIT11, &apriv->flags))
3452 netif_wake_queue(apriv->wifidev);
3453 }
3454 } else {
3455 OUT4500( apriv, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
Dan Williams934d8bf2006-03-16 13:46:23 -05003456 airo_print_err(apriv->dev->name, "Unallocated FID was "
3457 "used to xmit" );
Linus Torvalds1da177e2005-04-16 15:20:36 -07003458 }
3459 }
3460exittx:
3461 if ( status & ~STATUS_INTS & ~IGNORE_INTS )
Dan Williams934d8bf2006-03-16 13:46:23 -05003462 airo_print_warn(apriv->dev->name, "Got weird status %x",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003463 status & ~STATUS_INTS & ~IGNORE_INTS );
3464 }
3465
3466 if (savedInterrupts)
3467 OUT4500( apriv, EVINTEN, savedInterrupts );
3468
3469 /* done.. */
3470 return IRQ_RETVAL(handled);
3471}
3472
3473/*
3474 * Routines to talk to the card
3475 */
3476
3477/*
3478 * This was originally written for the 4500, hence the name
3479 * NOTE: If use with 8bit mode and SMP bad things will happen!
3480 * Why would some one do 8 bit IO in an SMP machine?!?
3481 */
3482static void OUT4500( struct airo_info *ai, u16 reg, u16 val ) {
3483 if (test_bit(FLAG_MPI,&ai->flags))
3484 reg <<= 1;
3485 if ( !do8bitIO )
3486 outw( val, ai->dev->base_addr + reg );
3487 else {
3488 outb( val & 0xff, ai->dev->base_addr + reg );
3489 outb( val >> 8, ai->dev->base_addr + reg + 1 );
3490 }
3491}
3492
3493static u16 IN4500( struct airo_info *ai, u16 reg ) {
3494 unsigned short rc;
3495
3496 if (test_bit(FLAG_MPI,&ai->flags))
3497 reg <<= 1;
3498 if ( !do8bitIO )
3499 rc = inw( ai->dev->base_addr + reg );
3500 else {
3501 rc = inb( ai->dev->base_addr + reg );
3502 rc += ((int)inb( ai->dev->base_addr + reg + 1 )) << 8;
3503 }
3504 return rc;
3505}
3506
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003507static int enable_MAC(struct airo_info *ai, int lock)
3508{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003509 int rc;
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003510 Cmd cmd;
3511 Resp rsp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003512
3513 /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions
3514 * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down"
3515 * Note : we could try to use !netif_running(dev) in enable_MAC()
3516 * instead of this flag, but I don't trust it *within* the
3517 * open/close functions, and testing both flags together is
3518 * "cheaper" - Jean II */
3519 if (ai->flags & FLAG_RADIO_MASK) return SUCCESS;
3520
3521 if (lock && down_interruptible(&ai->sem))
3522 return -ERESTARTSYS;
3523
3524 if (!test_bit(FLAG_ENABLED, &ai->flags)) {
3525 memset(&cmd, 0, sizeof(cmd));
3526 cmd.cmd = MAC_ENABLE;
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003527 rc = issuecommand(ai, &cmd, &rsp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003528 if (rc == SUCCESS)
3529 set_bit(FLAG_ENABLED, &ai->flags);
3530 } else
3531 rc = SUCCESS;
3532
3533 if (lock)
3534 up(&ai->sem);
3535
3536 if (rc)
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003537 airo_print_err(ai->dev->name, "Cannot enable MAC");
3538 else if ((rsp.status & 0xFF00) != 0) {
3539 airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, "
3540 "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2);
3541 rc = ERROR;
3542 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003543 return rc;
3544}
3545
3546static void disable_MAC( struct airo_info *ai, int lock ) {
3547 Cmd cmd;
3548 Resp rsp;
3549
3550 if (lock && down_interruptible(&ai->sem))
3551 return;
3552
3553 if (test_bit(FLAG_ENABLED, &ai->flags)) {
3554 memset(&cmd, 0, sizeof(cmd));
3555 cmd.cmd = MAC_DISABLE; // disable in case already enabled
3556 issuecommand(ai, &cmd, &rsp);
3557 clear_bit(FLAG_ENABLED, &ai->flags);
3558 }
3559 if (lock)
3560 up(&ai->sem);
3561}
3562
3563static void enable_interrupts( struct airo_info *ai ) {
3564 /* Enable the interrupts */
3565 OUT4500( ai, EVINTEN, STATUS_INTS );
3566}
3567
3568static void disable_interrupts( struct airo_info *ai ) {
3569 OUT4500( ai, EVINTEN, 0 );
3570}
3571
3572static void mpi_receive_802_3(struct airo_info *ai)
3573{
3574 RxFid rxd;
3575 int len = 0;
3576 struct sk_buff *skb;
3577 char *buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003578 int off = 0;
3579 MICBuffer micbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003580
3581 memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
3582 /* Make sure we got something */
3583 if (rxd.rdy && rxd.valid == 0) {
3584 len = rxd.len + 12;
3585 if (len < 12 || len > 2048)
3586 goto badrx;
3587
3588 skb = dev_alloc_skb(len);
3589 if (!skb) {
3590 ai->stats.rx_dropped++;
3591 goto badrx;
3592 }
3593 buffer = skb_put(skb,len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003594 memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2);
3595 if (ai->micstats.enabled) {
3596 memcpy(&micbuf,
3597 ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2,
3598 sizeof(micbuf));
3599 if (ntohs(micbuf.typelen) <= 0x05DC) {
3600 if (len <= sizeof(micbuf) + ETH_ALEN * 2)
3601 goto badmic;
3602
3603 off = sizeof(micbuf);
3604 skb_trim (skb, len - off);
3605 }
3606 }
3607 memcpy(buffer + ETH_ALEN * 2,
3608 ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off,
3609 len - ETH_ALEN * 2 - off);
3610 if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) {
3611badmic:
3612 dev_kfree_skb_irq (skb);
3613 goto badrx;
3614 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003615#ifdef WIRELESS_SPY
3616 if (ai->spy_data.spy_number > 0) {
3617 char *sa;
3618 struct iw_quality wstats;
3619 /* Prepare spy data : addr + qual */
3620 sa = buffer + ETH_ALEN;
3621 wstats.qual = 0; /* XXX Where do I get that info from ??? */
3622 wstats.level = 0;
3623 wstats.updated = 0;
3624 /* Update spy records */
3625 wireless_spy_update(ai->dev, sa, &wstats);
3626 }
3627#endif /* WIRELESS_SPY */
3628
Linus Torvalds1da177e2005-04-16 15:20:36 -07003629 skb->ip_summed = CHECKSUM_NONE;
3630 skb->protocol = eth_type_trans(skb, ai->dev);
3631 skb->dev->last_rx = jiffies;
3632 netif_rx(skb);
3633 }
3634badrx:
3635 if (rxd.valid == 0) {
3636 rxd.valid = 1;
3637 rxd.rdy = 0;
3638 rxd.len = PKTSIZE;
3639 memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
3640 }
3641}
3642
3643void mpi_receive_802_11 (struct airo_info *ai)
3644{
3645 RxFid rxd;
3646 struct sk_buff *skb = NULL;
Al Viro0300b332007-12-19 22:38:33 -05003647 u16 len, hdrlen = 0;
3648 __le16 fc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003649#pragma pack(1)
3650 struct {
Al Viro593c2b92007-12-17 15:09:34 -05003651 __le16 status, len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003652 u8 rssi[2];
3653 u8 rate;
3654 u8 freq;
Al Viro593c2b92007-12-17 15:09:34 -05003655 __le16 tmp[4];
Linus Torvalds1da177e2005-04-16 15:20:36 -07003656 } hdr;
3657#pragma pack()
3658 u16 gap;
3659 u16 *buffer;
3660 char *ptr = ai->rxfids[0].virtual_host_addr+4;
3661
3662 memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
3663 memcpy ((char *)&hdr, ptr, sizeof(hdr));
3664 ptr += sizeof(hdr);
3665 /* Bad CRC. Ignore packet */
3666 if (le16_to_cpu(hdr.status) & 2)
3667 hdr.len = 0;
3668 if (ai->wifidev == NULL)
3669 hdr.len = 0;
3670 len = le16_to_cpu(hdr.len);
Dan Williams15db2762006-03-16 13:46:27 -05003671 if (len > AIRO_DEF_MTU) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003672 airo_print_err(ai->dev->name, "Bad size %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003673 goto badrx;
3674 }
3675 if (len == 0)
3676 goto badrx;
3677
Al Viro0300b332007-12-19 22:38:33 -05003678 fc = get_unaligned((__le16 *)ptr);
3679 hdrlen = header_len(fc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003680
3681 skb = dev_alloc_skb( len + hdrlen + 2 );
3682 if ( !skb ) {
3683 ai->stats.rx_dropped++;
3684 goto badrx;
3685 }
3686 buffer = (u16*)skb_put (skb, len + hdrlen);
3687 memcpy ((char *)buffer, ptr, hdrlen);
3688 ptr += hdrlen;
3689 if (hdrlen == 24)
3690 ptr += 6;
Al Viro593c2b92007-12-17 15:09:34 -05003691 gap = le16_to_cpu(get_unaligned((__le16 *)ptr));
3692 ptr += sizeof(__le16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003693 if (gap) {
3694 if (gap <= 8)
3695 ptr += gap;
3696 else
Dan Williams934d8bf2006-03-16 13:46:23 -05003697 airo_print_err(ai->dev->name,
3698 "gaplen too big. Problems will follow...");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003699 }
3700 memcpy ((char *)buffer + hdrlen, ptr, len);
3701 ptr += len;
3702#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
3703 if (ai->spy_data.spy_number > 0) {
3704 char *sa;
3705 struct iw_quality wstats;
3706 /* Prepare spy data : addr + qual */
3707 sa = (char*)buffer + 10;
3708 wstats.qual = hdr.rssi[0];
3709 if (ai->rssi)
3710 wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
3711 else
3712 wstats.level = (hdr.rssi[1] + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04003713 wstats.noise = ai->wstats.qual.noise;
3714 wstats.updated = IW_QUAL_QUAL_UPDATED
3715 | IW_QUAL_LEVEL_UPDATED
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07003716 | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003717 /* Update spy records */
3718 wireless_spy_update(ai->dev, sa, &wstats);
3719 }
3720#endif /* IW_WIRELESS_SPY */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07003721 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003722 skb->pkt_type = PACKET_OTHERHOST;
3723 skb->dev = ai->wifidev;
3724 skb->protocol = htons(ETH_P_802_2);
3725 skb->dev->last_rx = jiffies;
3726 skb->ip_summed = CHECKSUM_NONE;
3727 netif_rx( skb );
3728badrx:
3729 if (rxd.valid == 0) {
3730 rxd.valid = 1;
3731 rxd.rdy = 0;
3732 rxd.len = PKTSIZE;
3733 memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
3734 }
3735}
3736
3737static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
3738{
3739 Cmd cmd;
3740 Resp rsp;
3741 int status;
3742 int i;
3743 SsidRid mySsid;
Al Viro4293ea32007-12-19 19:21:51 -05003744 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003745 WepKeyRid wkr;
3746 int rc;
3747
3748 memset( &mySsid, 0, sizeof( mySsid ) );
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003749 kfree (ai->flash);
3750 ai->flash = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003751
3752 /* The NOP is the first step in getting the card going */
3753 cmd.cmd = NOP;
3754 cmd.parm0 = cmd.parm1 = cmd.parm2 = 0;
3755 if (lock && down_interruptible(&ai->sem))
3756 return ERROR;
3757 if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) {
3758 if (lock)
3759 up(&ai->sem);
3760 return ERROR;
3761 }
3762 disable_MAC( ai, 0);
3763
3764 // Let's figure out if we need to use the AUX port
3765 if (!test_bit(FLAG_MPI,&ai->flags)) {
3766 cmd.cmd = CMD_ENABLEAUX;
3767 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
3768 if (lock)
3769 up(&ai->sem);
Dan Williams934d8bf2006-03-16 13:46:23 -05003770 airo_print_err(ai->dev->name, "Error checking for AUX port");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003771 return ERROR;
3772 }
3773 if (!aux_bap || rsp.status & 0xff00) {
3774 ai->bap_read = fast_bap_read;
Dan Williams934d8bf2006-03-16 13:46:23 -05003775 airo_print_dbg(ai->dev->name, "Doing fast bap_reads");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003776 } else {
3777 ai->bap_read = aux_bap_read;
Dan Williams934d8bf2006-03-16 13:46:23 -05003778 airo_print_dbg(ai->dev->name, "Doing AUX bap_reads");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003779 }
3780 }
3781 if (lock)
3782 up(&ai->sem);
3783 if (ai->config.len == 0) {
3784 tdsRssiRid rssi_rid;
3785 CapabilityRid cap_rid;
3786
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003787 kfree(ai->APList);
3788 ai->APList = NULL;
3789 kfree(ai->SSID);
3790 ai->SSID = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003791 // general configuration (read/modify/write)
3792 status = readConfigRid(ai, lock);
3793 if ( status != SUCCESS ) return ERROR;
3794
3795 status = readCapabilityRid(ai, &cap_rid, lock);
3796 if ( status != SUCCESS ) return ERROR;
3797
3798 status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),lock);
3799 if ( status == SUCCESS ) {
3800 if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
Dan Williams41480af2005-05-10 09:45:51 -04003801 memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003802 }
3803 else {
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003804 kfree(ai->rssi);
3805 ai->rssi = NULL;
Al Viro56d81bd2007-12-20 17:18:35 -05003806 if (cap_rid.softCap & cpu_to_le16(8))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003807 ai->config.rmode |= RXMODE_NORMALIZED_RSSI;
3808 else
Dan Williams934d8bf2006-03-16 13:46:23 -05003809 airo_print_warn(ai->dev->name, "unknown received signal "
3810 "level scale");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003811 }
3812 ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
3813 ai->config.authType = AUTH_OPEN;
3814 ai->config.modulation = MOD_CCK;
3815
Al Viro56d81bd2007-12-20 17:18:35 -05003816 if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) &&
Al Viro156178582007-12-20 17:21:36 -05003817 (cap_rid.extSoftCap & cpu_to_le16(1)) &&
Al Viro56d81bd2007-12-20 17:18:35 -05003818 micsetup(ai) == SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003819 ai->config.opmode |= MODE_MIC;
3820 set_bit(FLAG_MIC_CAPABLE, &ai->flags);
3821 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003822
3823 /* Save off the MAC */
3824 for( i = 0; i < ETH_ALEN; i++ ) {
3825 mac[i] = ai->config.macAddr[i];
3826 }
3827
3828 /* Check to see if there are any insmod configured
3829 rates to add */
3830 if ( rates[0] ) {
3831 int i = 0;
3832 memset(ai->config.rates,0,sizeof(ai->config.rates));
3833 for( i = 0; i < 8 && rates[i]; i++ ) {
3834 ai->config.rates[i] = rates[i];
3835 }
3836 }
3837 if ( basic_rate > 0 ) {
3838 int i;
3839 for( i = 0; i < 8; i++ ) {
3840 if ( ai->config.rates[i] == basic_rate ||
3841 !ai->config.rates ) {
3842 ai->config.rates[i] = basic_rate | 0x80;
3843 break;
3844 }
3845 }
3846 }
3847 set_bit (FLAG_COMMIT, &ai->flags);
3848 }
3849
3850 /* Setup the SSIDs if present */
3851 if ( ssids[0] ) {
3852 int i;
3853 for( i = 0; i < 3 && ssids[i]; i++ ) {
Al Viro0dd22122007-12-17 16:11:57 -05003854 size_t len = strlen(ssids[i]);
3855 if (len > 32)
3856 len = 32;
3857 mySsid.ssids[i].len = cpu_to_le16(len);
3858 memcpy(mySsid.ssids[i].ssid, ssids[i], len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003859 }
Al Viro0dd22122007-12-17 16:11:57 -05003860 mySsid.len = cpu_to_le16(sizeof(mySsid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003861 }
3862
3863 status = writeConfigRid(ai, lock);
3864 if ( status != SUCCESS ) return ERROR;
3865
3866 /* Set up the SSID list */
3867 if ( ssids[0] ) {
3868 status = writeSsidRid(ai, &mySsid, lock);
3869 if ( status != SUCCESS ) return ERROR;
3870 }
3871
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003872 status = enable_MAC(ai, lock);
3873 if (status != SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003874 return ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003875
3876 /* Grab the initial wep key, we gotta save it for auto_wep */
3877 rc = readWepKeyRid(ai, &wkr, 1, lock);
3878 if (rc == SUCCESS) do {
3879 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05003880 if (wkr.kindex == cpu_to_le16(0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003881 ai->defindex = wkr.mac[0];
3882 }
3883 rc = readWepKeyRid(ai, &wkr, 0, lock);
3884 } while(lastindex != wkr.kindex);
3885
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02003886 try_auto_wep(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003887
3888 return SUCCESS;
3889}
3890
3891static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) {
3892 // Im really paranoid about letting it run forever!
3893 int max_tries = 600000;
3894
3895 if (IN4500(ai, EVSTAT) & EV_CMD)
3896 OUT4500(ai, EVACK, EV_CMD);
3897
3898 OUT4500(ai, PARAM0, pCmd->parm0);
3899 OUT4500(ai, PARAM1, pCmd->parm1);
3900 OUT4500(ai, PARAM2, pCmd->parm2);
3901 OUT4500(ai, COMMAND, pCmd->cmd);
3902
3903 while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) {
3904 if ((IN4500(ai, COMMAND)) == pCmd->cmd)
3905 // PC4500 didn't notice command, try again
3906 OUT4500(ai, COMMAND, pCmd->cmd);
3907 if (!in_atomic() && (max_tries & 255) == 0)
3908 schedule();
3909 }
3910
3911 if ( max_tries == -1 ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003912 airo_print_err(ai->dev->name,
3913 "Max tries exceeded when issueing command");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003914 if (IN4500(ai, COMMAND) & COMMAND_BUSY)
3915 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
3916 return ERROR;
3917 }
3918
3919 // command completed
3920 pRsp->status = IN4500(ai, STATUS);
3921 pRsp->rsp0 = IN4500(ai, RESP0);
3922 pRsp->rsp1 = IN4500(ai, RESP1);
3923 pRsp->rsp2 = IN4500(ai, RESP2);
Robert Schulze13dca9b2006-07-10 18:37:44 +02003924 if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET)
3925 airo_print_err(ai->dev->name,
3926 "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x",
3927 pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1,
3928 pRsp->rsp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003929
3930 // clear stuck command busy if necessary
3931 if (IN4500(ai, COMMAND) & COMMAND_BUSY) {
3932 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
3933 }
3934 // acknowledge processing the status/response
3935 OUT4500(ai, EVACK, EV_CMD);
3936
3937 return SUCCESS;
3938}
3939
3940/* Sets up the bap to start exchange data. whichbap should
3941 * be one of the BAP0 or BAP1 defines. Locks should be held before
3942 * calling! */
3943static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap )
3944{
3945 int timeout = 50;
3946 int max_tries = 3;
3947
3948 OUT4500(ai, SELECT0+whichbap, rid);
3949 OUT4500(ai, OFFSET0+whichbap, offset);
3950 while (1) {
3951 int status = IN4500(ai, OFFSET0+whichbap);
3952 if (status & BAP_BUSY) {
3953 /* This isn't really a timeout, but its kinda
3954 close */
3955 if (timeout--) {
3956 continue;
3957 }
3958 } else if ( status & BAP_ERR ) {
3959 /* invalid rid or offset */
Dan Williams934d8bf2006-03-16 13:46:23 -05003960 airo_print_err(ai->dev->name, "BAP error %x %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003961 status, whichbap );
3962 return ERROR;
3963 } else if (status & BAP_DONE) { // success
3964 return SUCCESS;
3965 }
3966 if ( !(max_tries--) ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003967 airo_print_err(ai->dev->name,
Michal Schmidt1138c372007-06-29 15:33:41 +02003968 "BAP setup error too many retries\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003969 return ERROR;
3970 }
3971 // -- PC4500 missed it, try again
3972 OUT4500(ai, SELECT0+whichbap, rid);
3973 OUT4500(ai, OFFSET0+whichbap, offset);
3974 timeout = 50;
3975 }
3976}
3977
3978/* should only be called by aux_bap_read. This aux function and the
3979 following use concepts not documented in the developers guide. I
3980 got them from a patch given to my by Aironet */
3981static u16 aux_setup(struct airo_info *ai, u16 page,
3982 u16 offset, u16 *len)
3983{
3984 u16 next;
3985
3986 OUT4500(ai, AUXPAGE, page);
3987 OUT4500(ai, AUXOFF, 0);
3988 next = IN4500(ai, AUXDATA);
3989 *len = IN4500(ai, AUXDATA)&0xff;
3990 if (offset != 4) OUT4500(ai, AUXOFF, offset);
3991 return next;
3992}
3993
3994/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05003995static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003996 int bytelen, int whichbap)
3997{
3998 u16 len;
3999 u16 page;
4000 u16 offset;
4001 u16 next;
4002 int words;
4003 int i;
4004 unsigned long flags;
4005
4006 spin_lock_irqsave(&ai->aux_lock, flags);
4007 page = IN4500(ai, SWS0+whichbap);
4008 offset = IN4500(ai, SWS2+whichbap);
4009 next = aux_setup(ai, page, offset, &len);
4010 words = (bytelen+1)>>1;
4011
4012 for (i=0; i<words;) {
4013 int count;
4014 count = (len>>1) < (words-i) ? (len>>1) : (words-i);
4015 if ( !do8bitIO )
4016 insw( ai->dev->base_addr+DATA0+whichbap,
4017 pu16Dst+i,count );
4018 else
4019 insb( ai->dev->base_addr+DATA0+whichbap,
4020 pu16Dst+i, count << 1 );
4021 i += count;
4022 if (i<words) {
4023 next = aux_setup(ai, next, 4, &len);
4024 }
4025 }
4026 spin_unlock_irqrestore(&ai->aux_lock, flags);
4027 return SUCCESS;
4028}
4029
4030
4031/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004032static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004033 int bytelen, int whichbap)
4034{
4035 bytelen = (bytelen + 1) & (~1); // round up to even value
4036 if ( !do8bitIO )
4037 insw( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1 );
4038 else
4039 insb( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen );
4040 return SUCCESS;
4041}
4042
4043/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004044static int bap_write(struct airo_info *ai, const __le16 *pu16Src,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004045 int bytelen, int whichbap)
4046{
4047 bytelen = (bytelen + 1) & (~1); // round up to even value
4048 if ( !do8bitIO )
4049 outsw( ai->dev->base_addr+DATA0+whichbap,
4050 pu16Src, bytelen>>1 );
4051 else
4052 outsb( ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen );
4053 return SUCCESS;
4054}
4055
4056static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd)
4057{
4058 Cmd cmd; /* for issuing commands */
4059 Resp rsp; /* response from commands */
4060 u16 status;
4061
4062 memset(&cmd, 0, sizeof(cmd));
4063 cmd.cmd = accmd;
4064 cmd.parm0 = rid;
4065 status = issuecommand(ai, &cmd, &rsp);
4066 if (status != 0) return status;
4067 if ( (rsp.status & 0x7F00) != 0) {
4068 return (accmd << 8) + (rsp.rsp0 & 0xFF);
4069 }
4070 return 0;
4071}
4072
4073/* Note, that we are using BAP1 which is also used by transmit, so
4074 * we must get a lock. */
4075static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock)
4076{
4077 u16 status;
4078 int rc = SUCCESS;
4079
4080 if (lock) {
4081 if (down_interruptible(&ai->sem))
4082 return ERROR;
4083 }
4084 if (test_bit(FLAG_MPI,&ai->flags)) {
4085 Cmd cmd;
4086 Resp rsp;
4087
4088 memset(&cmd, 0, sizeof(cmd));
4089 memset(&rsp, 0, sizeof(rsp));
4090 ai->config_desc.rid_desc.valid = 1;
4091 ai->config_desc.rid_desc.len = RIDSIZE;
4092 ai->config_desc.rid_desc.rid = 0;
4093 ai->config_desc.rid_desc.host_addr = ai->ridbus;
4094
4095 cmd.cmd = CMD_ACCESS;
4096 cmd.parm0 = rid;
4097
4098 memcpy_toio(ai->config_desc.card_ram_off,
4099 &ai->config_desc.rid_desc, sizeof(Rid));
4100
4101 rc = issuecommand(ai, &cmd, &rsp);
4102
4103 if (rsp.status & 0x7f00)
4104 rc = rsp.rsp0;
4105 if (!rc)
4106 memcpy(pBuf, ai->config_desc.virtual_host_addr, len);
4107 goto done;
4108 } else {
4109 if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) {
4110 rc = status;
4111 goto done;
4112 }
4113 if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
4114 rc = ERROR;
4115 goto done;
4116 }
4117 // read the rid length field
4118 bap_read(ai, pBuf, 2, BAP1);
4119 // length for remaining part of rid
Al Viro593c2b92007-12-17 15:09:34 -05004120 len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004121
4122 if ( len <= 2 ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004123 airo_print_err(ai->dev->name,
4124 "Rid %x has a length of %d which is too short",
Linus Torvalds1da177e2005-04-16 15:20:36 -07004125 (int)rid, (int)len );
4126 rc = ERROR;
4127 goto done;
4128 }
4129 // read remainder of the rid
Al Virob8c06bc2007-12-19 17:55:43 -05004130 rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004131 }
4132done:
4133 if (lock)
4134 up(&ai->sem);
4135 return rc;
4136}
4137
4138/* Note, that we are using BAP1 which is also used by transmit, so
4139 * make sure this isnt called when a transmit is happening */
4140static int PC4500_writerid(struct airo_info *ai, u16 rid,
4141 const void *pBuf, int len, int lock)
4142{
4143 u16 status;
4144 int rc = SUCCESS;
4145
Al Viro593c2b92007-12-17 15:09:34 -05004146 *(__le16*)pBuf = cpu_to_le16((u16)len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004147
4148 if (lock) {
4149 if (down_interruptible(&ai->sem))
4150 return ERROR;
4151 }
4152 if (test_bit(FLAG_MPI,&ai->flags)) {
4153 Cmd cmd;
4154 Resp rsp;
4155
Dan Streetmanf89b2322005-11-11 11:41:42 -05004156 if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid))
Dan Williams934d8bf2006-03-16 13:46:23 -05004157 airo_print_err(ai->dev->name,
4158 "%s: MAC should be disabled (rid=%04x)",
Linus Torvalds1da177e2005-04-16 15:20:36 -07004159 __FUNCTION__, rid);
4160 memset(&cmd, 0, sizeof(cmd));
4161 memset(&rsp, 0, sizeof(rsp));
4162
4163 ai->config_desc.rid_desc.valid = 1;
4164 ai->config_desc.rid_desc.len = *((u16 *)pBuf);
4165 ai->config_desc.rid_desc.rid = 0;
4166
4167 cmd.cmd = CMD_WRITERID;
4168 cmd.parm0 = rid;
4169
4170 memcpy_toio(ai->config_desc.card_ram_off,
4171 &ai->config_desc.rid_desc, sizeof(Rid));
4172
4173 if (len < 4 || len > 2047) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004174 airo_print_err(ai->dev->name, "%s: len=%d", __FUNCTION__, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004175 rc = -1;
4176 } else {
4177 memcpy((char *)ai->config_desc.virtual_host_addr,
4178 pBuf, len);
4179
4180 rc = issuecommand(ai, &cmd, &rsp);
4181 if ((rc & 0xff00) != 0) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004182 airo_print_err(ai->dev->name, "%s: Write rid Error %d",
4183 __FUNCTION__, rc);
4184 airo_print_err(ai->dev->name, "%s: Cmd=%04x",
4185 __FUNCTION__, cmd.cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004186 }
4187
4188 if ((rsp.status & 0x7f00))
4189 rc = rsp.rsp0;
4190 }
4191 } else {
4192 // --- first access so that we can write the rid data
4193 if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) {
4194 rc = status;
4195 goto done;
4196 }
4197 // --- now write the rid data
4198 if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
4199 rc = ERROR;
4200 goto done;
4201 }
4202 bap_write(ai, pBuf, len, BAP1);
4203 // ---now commit the rid data
4204 rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS);
4205 }
4206done:
4207 if (lock)
4208 up(&ai->sem);
4209 return rc;
4210}
4211
4212/* Allocates a FID to be used for transmitting packets. We only use
4213 one for now. */
4214static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw)
4215{
4216 unsigned int loop = 3000;
4217 Cmd cmd;
4218 Resp rsp;
4219 u16 txFid;
Al Viro593c2b92007-12-17 15:09:34 -05004220 __le16 txControl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004221
4222 cmd.cmd = CMD_ALLOCATETX;
4223 cmd.parm0 = lenPayload;
4224 if (down_interruptible(&ai->sem))
4225 return ERROR;
4226 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
4227 txFid = ERROR;
4228 goto done;
4229 }
4230 if ( (rsp.status & 0xFF00) != 0) {
4231 txFid = ERROR;
4232 goto done;
4233 }
4234 /* wait for the allocate event/indication
4235 * It makes me kind of nervous that this can just sit here and spin,
4236 * but in practice it only loops like four times. */
4237 while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop);
4238 if (!loop) {
4239 txFid = ERROR;
4240 goto done;
4241 }
4242
4243 // get the allocated fid and acknowledge
4244 txFid = IN4500(ai, TXALLOCFID);
4245 OUT4500(ai, EVACK, EV_ALLOC);
4246
4247 /* The CARD is pretty cool since it converts the ethernet packet
4248 * into 802.11. Also note that we don't release the FID since we
4249 * will be using the same one over and over again. */
4250 /* We only have to setup the control once since we are not
4251 * releasing the fid. */
4252 if (raw)
4253 txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11
4254 | TXCTL_ETHERNET | TXCTL_NORELEASE);
4255 else
4256 txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3
4257 | TXCTL_ETHERNET | TXCTL_NORELEASE);
4258 if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS)
4259 txFid = ERROR;
4260 else
4261 bap_write(ai, &txControl, sizeof(txControl), BAP1);
4262
4263done:
4264 up(&ai->sem);
4265
4266 return txFid;
4267}
4268
4269/* In general BAP1 is dedicated to transmiting packets. However,
4270 since we need a BAP when accessing RIDs, we also use BAP1 for that.
4271 Make sure the BAP1 spinlock is held when this is called. */
4272static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket)
4273{
Al Viro593c2b92007-12-17 15:09:34 -05004274 __le16 payloadLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004275 Cmd cmd;
4276 Resp rsp;
4277 int miclen = 0;
4278 u16 txFid = len;
4279 MICBuffer pMic;
4280
4281 len >>= 16;
4282
4283 if (len <= ETH_ALEN * 2) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004284 airo_print_warn(ai->dev->name, "Short packet %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004285 return ERROR;
4286 }
4287 len -= ETH_ALEN * 2;
4288
Linus Torvalds1da177e2005-04-16 15:20:36 -07004289 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
Al Viro593c2b92007-12-17 15:09:34 -05004290 (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004291 if (encapsulate(ai,(etherHead *)pPacket,&pMic,len) != SUCCESS)
4292 return ERROR;
4293 miclen = sizeof(pMic);
4294 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004295 // packet is destination[6], source[6], payload[len-12]
4296 // write the payload length and dst/src/payload
4297 if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR;
4298 /* The hardware addresses aren't counted as part of the payload, so
4299 * we have to subtract the 12 bytes for the addresses off */
4300 payloadLen = cpu_to_le16(len + miclen);
4301 bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
Al Virob8c06bc2007-12-19 17:55:43 -05004302 bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004303 if (miclen)
Al Virob8c06bc2007-12-19 17:55:43 -05004304 bap_write(ai, (__le16*)&pMic, miclen, BAP1);
4305 bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004306 // issue the transmit command
4307 memset( &cmd, 0, sizeof( cmd ) );
4308 cmd.cmd = CMD_TRANSMIT;
4309 cmd.parm0 = txFid;
4310 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
4311 if ( (rsp.status & 0xFF00) != 0) return ERROR;
4312 return SUCCESS;
4313}
4314
4315static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket)
4316{
Al Viro593c2b92007-12-17 15:09:34 -05004317 __le16 fc, payloadLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004318 Cmd cmd;
4319 Resp rsp;
4320 int hdrlen;
Al Viro977b1432007-12-19 16:45:29 -05004321 static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6};
4322 /* padding of header to full size + le16 gaplen (6) + gaplen bytes */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004323 u16 txFid = len;
4324 len >>= 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004325
Al Viro0300b332007-12-19 22:38:33 -05004326 fc = *(__le16*)pPacket;
4327 hdrlen = header_len(fc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004328
4329 if (len < hdrlen) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004330 airo_print_warn(ai->dev->name, "Short packet %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004331 return ERROR;
4332 }
4333
4334 /* packet is 802.11 header + payload
4335 * write the payload length and dst/src/payload */
4336 if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR;
4337 /* The 802.11 header aren't counted as part of the payload, so
4338 * we have to subtract the header bytes off */
4339 payloadLen = cpu_to_le16(len-hdrlen);
4340 bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
4341 if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR;
Al Virob8c06bc2007-12-19 17:55:43 -05004342 bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1);
4343 bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004344
Al Virob8c06bc2007-12-19 17:55:43 -05004345 bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004346 // issue the transmit command
4347 memset( &cmd, 0, sizeof( cmd ) );
4348 cmd.cmd = CMD_TRANSMIT;
4349 cmd.parm0 = txFid;
4350 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
4351 if ( (rsp.status & 0xFF00) != 0) return ERROR;
4352 return SUCCESS;
4353}
4354
4355/*
4356 * This is the proc_fs routines. It is a bit messier than I would
4357 * like! Feel free to clean it up!
4358 */
4359
4360static ssize_t proc_read( struct file *file,
4361 char __user *buffer,
4362 size_t len,
4363 loff_t *offset);
4364
4365static ssize_t proc_write( struct file *file,
4366 const char __user *buffer,
4367 size_t len,
4368 loff_t *offset );
4369static int proc_close( struct inode *inode, struct file *file );
4370
4371static int proc_stats_open( struct inode *inode, struct file *file );
4372static int proc_statsdelta_open( struct inode *inode, struct file *file );
4373static int proc_status_open( struct inode *inode, struct file *file );
4374static int proc_SSID_open( struct inode *inode, struct file *file );
4375static int proc_APList_open( struct inode *inode, struct file *file );
4376static int proc_BSSList_open( struct inode *inode, struct file *file );
4377static int proc_config_open( struct inode *inode, struct file *file );
4378static int proc_wepkey_open( struct inode *inode, struct file *file );
4379
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004380static const struct file_operations proc_statsdelta_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004381 .read = proc_read,
4382 .open = proc_statsdelta_open,
4383 .release = proc_close
4384};
4385
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004386static const struct file_operations proc_stats_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004387 .read = proc_read,
4388 .open = proc_stats_open,
4389 .release = proc_close
4390};
4391
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004392static const struct file_operations proc_status_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004393 .read = proc_read,
4394 .open = proc_status_open,
4395 .release = proc_close
4396};
4397
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004398static const struct file_operations proc_SSID_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004399 .read = proc_read,
4400 .write = proc_write,
4401 .open = proc_SSID_open,
4402 .release = proc_close
4403};
4404
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004405static const struct file_operations proc_BSSList_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004406 .read = proc_read,
4407 .write = proc_write,
4408 .open = proc_BSSList_open,
4409 .release = proc_close
4410};
4411
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004412static const struct file_operations proc_APList_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004413 .read = proc_read,
4414 .write = proc_write,
4415 .open = proc_APList_open,
4416 .release = proc_close
4417};
4418
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004419static const struct file_operations proc_config_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004420 .read = proc_read,
4421 .write = proc_write,
4422 .open = proc_config_open,
4423 .release = proc_close
4424};
4425
Arjan van de Vend54b1fd2007-02-12 00:55:34 -08004426static const struct file_operations proc_wepkey_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004427 .read = proc_read,
4428 .write = proc_write,
4429 .open = proc_wepkey_open,
4430 .release = proc_close
4431};
4432
4433static struct proc_dir_entry *airo_entry;
4434
4435struct proc_data {
4436 int release_buffer;
4437 int readlen;
4438 char *rbuffer;
4439 int writelen;
4440 int maxwritelen;
4441 char *wbuffer;
4442 void (*on_close) (struct inode *, struct file *);
4443};
4444
4445#ifndef SETPROC_OPS
4446#define SETPROC_OPS(entry, ops) (entry)->proc_fops = &(ops)
4447#endif
4448
4449static int setup_proc_entry( struct net_device *dev,
4450 struct airo_info *apriv ) {
4451 struct proc_dir_entry *entry;
4452 /* First setup the device directory */
4453 strcpy(apriv->proc_name,dev->name);
4454 apriv->proc_entry = create_proc_entry(apriv->proc_name,
4455 S_IFDIR|airo_perm,
4456 airo_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004457 if (!apriv->proc_entry)
4458 goto fail;
4459 apriv->proc_entry->uid = proc_uid;
4460 apriv->proc_entry->gid = proc_gid;
4461 apriv->proc_entry->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004462
4463 /* Setup the StatsDelta */
4464 entry = create_proc_entry("StatsDelta",
4465 S_IFREG | (S_IRUGO&proc_perm),
4466 apriv->proc_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004467 if (!entry)
4468 goto fail_stats_delta;
4469 entry->uid = proc_uid;
4470 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004471 entry->data = dev;
Florin Malita431aca52006-10-10 16:46:30 -04004472 entry->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004473 SETPROC_OPS(entry, proc_statsdelta_ops);
4474
4475 /* Setup the Stats */
4476 entry = create_proc_entry("Stats",
4477 S_IFREG | (S_IRUGO&proc_perm),
4478 apriv->proc_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004479 if (!entry)
4480 goto fail_stats;
4481 entry->uid = proc_uid;
4482 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004483 entry->data = dev;
Florin Malita431aca52006-10-10 16:46:30 -04004484 entry->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004485 SETPROC_OPS(entry, proc_stats_ops);
4486
4487 /* Setup the Status */
4488 entry = create_proc_entry("Status",
4489 S_IFREG | (S_IRUGO&proc_perm),
4490 apriv->proc_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004491 if (!entry)
4492 goto fail_status;
4493 entry->uid = proc_uid;
4494 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004495 entry->data = dev;
Florin Malita431aca52006-10-10 16:46:30 -04004496 entry->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004497 SETPROC_OPS(entry, proc_status_ops);
4498
4499 /* Setup the Config */
4500 entry = create_proc_entry("Config",
4501 S_IFREG | proc_perm,
4502 apriv->proc_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004503 if (!entry)
4504 goto fail_config;
4505 entry->uid = proc_uid;
4506 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004507 entry->data = dev;
Florin Malita431aca52006-10-10 16:46:30 -04004508 entry->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004509 SETPROC_OPS(entry, proc_config_ops);
4510
4511 /* Setup the SSID */
4512 entry = create_proc_entry("SSID",
4513 S_IFREG | proc_perm,
4514 apriv->proc_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004515 if (!entry)
4516 goto fail_ssid;
4517 entry->uid = proc_uid;
4518 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004519 entry->data = dev;
Florin Malita431aca52006-10-10 16:46:30 -04004520 entry->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004521 SETPROC_OPS(entry, proc_SSID_ops);
4522
4523 /* Setup the APList */
4524 entry = create_proc_entry("APList",
4525 S_IFREG | proc_perm,
4526 apriv->proc_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004527 if (!entry)
4528 goto fail_aplist;
4529 entry->uid = proc_uid;
4530 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004531 entry->data = dev;
Florin Malita431aca52006-10-10 16:46:30 -04004532 entry->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004533 SETPROC_OPS(entry, proc_APList_ops);
4534
4535 /* Setup the BSSList */
4536 entry = create_proc_entry("BSSList",
4537 S_IFREG | proc_perm,
4538 apriv->proc_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004539 if (!entry)
4540 goto fail_bsslist;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004541 entry->uid = proc_uid;
4542 entry->gid = proc_gid;
4543 entry->data = dev;
Florin Malita431aca52006-10-10 16:46:30 -04004544 entry->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004545 SETPROC_OPS(entry, proc_BSSList_ops);
4546
4547 /* Setup the WepKey */
4548 entry = create_proc_entry("WepKey",
4549 S_IFREG | proc_perm,
4550 apriv->proc_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004551 if (!entry)
4552 goto fail_wepkey;
4553 entry->uid = proc_uid;
4554 entry->gid = proc_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004555 entry->data = dev;
Florin Malita431aca52006-10-10 16:46:30 -04004556 entry->owner = THIS_MODULE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004557 SETPROC_OPS(entry, proc_wepkey_ops);
4558
4559 return 0;
Florin Malita431aca52006-10-10 16:46:30 -04004560
4561fail_wepkey:
4562 remove_proc_entry("BSSList", apriv->proc_entry);
4563fail_bsslist:
4564 remove_proc_entry("APList", apriv->proc_entry);
4565fail_aplist:
4566 remove_proc_entry("SSID", apriv->proc_entry);
4567fail_ssid:
4568 remove_proc_entry("Config", apriv->proc_entry);
4569fail_config:
4570 remove_proc_entry("Status", apriv->proc_entry);
4571fail_status:
4572 remove_proc_entry("Stats", apriv->proc_entry);
4573fail_stats:
4574 remove_proc_entry("StatsDelta", apriv->proc_entry);
4575fail_stats_delta:
4576 remove_proc_entry(apriv->proc_name, airo_entry);
4577fail:
4578 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004579}
4580
4581static int takedown_proc_entry( struct net_device *dev,
4582 struct airo_info *apriv ) {
4583 if ( !apriv->proc_entry->namelen ) return 0;
4584 remove_proc_entry("Stats",apriv->proc_entry);
4585 remove_proc_entry("StatsDelta",apriv->proc_entry);
4586 remove_proc_entry("Status",apriv->proc_entry);
4587 remove_proc_entry("Config",apriv->proc_entry);
4588 remove_proc_entry("SSID",apriv->proc_entry);
4589 remove_proc_entry("APList",apriv->proc_entry);
4590 remove_proc_entry("BSSList",apriv->proc_entry);
4591 remove_proc_entry("WepKey",apriv->proc_entry);
4592 remove_proc_entry(apriv->proc_name,airo_entry);
4593 return 0;
4594}
4595
4596/*
4597 * What we want from the proc_fs is to be able to efficiently read
4598 * and write the configuration. To do this, we want to read the
4599 * configuration when the file is opened and write it when the file is
4600 * closed. So basically we allocate a read buffer at open and fill it
4601 * with data, and allocate a write buffer and read it at close.
4602 */
4603
4604/*
4605 * The read routine is generic, it relies on the preallocated rbuffer
4606 * to supply the data.
4607 */
4608static ssize_t proc_read( struct file *file,
4609 char __user *buffer,
4610 size_t len,
4611 loff_t *offset )
4612{
4613 loff_t pos = *offset;
4614 struct proc_data *priv = (struct proc_data*)file->private_data;
4615
4616 if (!priv->rbuffer)
4617 return -EINVAL;
4618
4619 if (pos < 0)
4620 return -EINVAL;
4621 if (pos >= priv->readlen)
4622 return 0;
4623 if (len > priv->readlen - pos)
4624 len = priv->readlen - pos;
4625 if (copy_to_user(buffer, priv->rbuffer + pos, len))
4626 return -EFAULT;
4627 *offset = pos + len;
4628 return len;
4629}
4630
4631/*
4632 * The write routine is generic, it fills in a preallocated rbuffer
4633 * to supply the data.
4634 */
4635static ssize_t proc_write( struct file *file,
4636 const char __user *buffer,
4637 size_t len,
4638 loff_t *offset )
4639{
4640 loff_t pos = *offset;
4641 struct proc_data *priv = (struct proc_data*)file->private_data;
4642
4643 if (!priv->wbuffer)
4644 return -EINVAL;
4645
4646 if (pos < 0)
4647 return -EINVAL;
4648 if (pos >= priv->maxwritelen)
4649 return 0;
4650 if (len > priv->maxwritelen - pos)
4651 len = priv->maxwritelen - pos;
4652 if (copy_from_user(priv->wbuffer + pos, buffer, len))
4653 return -EFAULT;
4654 if ( pos + len > priv->writelen )
4655 priv->writelen = len + file->f_pos;
4656 *offset = pos + len;
4657 return len;
4658}
4659
4660static int proc_status_open( struct inode *inode, struct file *file ) {
4661 struct proc_data *data;
4662 struct proc_dir_entry *dp = PDE(inode);
4663 struct net_device *dev = dp->data;
4664 struct airo_info *apriv = dev->priv;
4665 CapabilityRid cap_rid;
4666 StatusRid status_rid;
4667 int i;
4668
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01004669 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004670 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004671 data = (struct proc_data *)file->private_data;
4672 if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
4673 kfree (file->private_data);
4674 return -ENOMEM;
4675 }
4676
4677 readStatusRid(apriv, &status_rid, 1);
4678 readCapabilityRid(apriv, &cap_rid, 1);
4679
4680 i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n",
4681 status_rid.mode & 1 ? "CFG ": "",
4682 status_rid.mode & 2 ? "ACT ": "",
4683 status_rid.mode & 0x10 ? "SYN ": "",
4684 status_rid.mode & 0x20 ? "LNK ": "",
4685 status_rid.mode & 0x40 ? "LEAP ": "",
4686 status_rid.mode & 0x80 ? "PRIV ": "",
4687 status_rid.mode & 0x100 ? "KEY ": "",
4688 status_rid.mode & 0x200 ? "WEP ": "",
4689 status_rid.mode & 0x8000 ? "ERR ": "");
4690 sprintf( data->rbuffer+i, "Mode: %x\n"
4691 "Signal Strength: %d\n"
4692 "Signal Quality: %d\n"
4693 "SSID: %-.*s\n"
4694 "AP: %-.16s\n"
4695 "Freq: %d\n"
4696 "BitRate: %dmbs\n"
4697 "Driver Version: %s\n"
4698 "Device: %s\nManufacturer: %s\nFirmware Version: %s\n"
4699 "Radio type: %x\nCountry: %x\nHardware Version: %x\n"
4700 "Software Version: %x\nSoftware Subversion: %x\n"
4701 "Boot block version: %x\n",
4702 (int)status_rid.mode,
4703 (int)status_rid.normalizedSignalStrength,
4704 (int)status_rid.signalQuality,
4705 (int)status_rid.SSIDlen,
4706 status_rid.SSID,
4707 status_rid.apName,
4708 (int)status_rid.channel,
4709 (int)status_rid.currentXmitRate/2,
4710 version,
4711 cap_rid.prodName,
4712 cap_rid.manName,
4713 cap_rid.prodVer,
Al Viro56d81bd2007-12-20 17:18:35 -05004714 le16_to_cpu(cap_rid.radioType),
4715 le16_to_cpu(cap_rid.country),
4716 le16_to_cpu(cap_rid.hardVer),
4717 le16_to_cpu(cap_rid.softVer),
4718 le16_to_cpu(cap_rid.softSubVer),
4719 le16_to_cpu(cap_rid.bootBlockVer));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004720 data->readlen = strlen( data->rbuffer );
4721 return 0;
4722}
4723
4724static int proc_stats_rid_open(struct inode*, struct file*, u16);
4725static int proc_statsdelta_open( struct inode *inode,
4726 struct file *file ) {
4727 if (file->f_mode&FMODE_WRITE) {
4728 return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR);
4729 }
4730 return proc_stats_rid_open(inode, file, RID_STATSDELTA);
4731}
4732
4733static int proc_stats_open( struct inode *inode, struct file *file ) {
4734 return proc_stats_rid_open(inode, file, RID_STATS);
4735}
4736
4737static int proc_stats_rid_open( struct inode *inode,
4738 struct file *file,
Al Viroa23ace52007-12-19 22:24:16 -05004739 u16 rid )
4740{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004741 struct proc_data *data;
4742 struct proc_dir_entry *dp = PDE(inode);
4743 struct net_device *dev = dp->data;
4744 struct airo_info *apriv = dev->priv;
4745 StatsRid stats;
4746 int i, j;
Al Viroa23ace52007-12-19 22:24:16 -05004747 __le32 *vals = stats.vals;
4748 int len = le16_to_cpu(stats.len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004749
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01004750 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004751 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004752 data = (struct proc_data *)file->private_data;
4753 if ((data->rbuffer = kmalloc( 4096, GFP_KERNEL )) == NULL) {
4754 kfree (file->private_data);
4755 return -ENOMEM;
4756 }
4757
4758 readStatsRid(apriv, &stats, rid, 1);
4759
4760 j = 0;
Al Viroa23ace52007-12-19 22:24:16 -05004761 for(i=0; statsLabels[i]!=(char *)-1 && i*4<len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004762 if (!statsLabels[i]) continue;
4763 if (j+strlen(statsLabels[i])+16>4096) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004764 airo_print_warn(apriv->dev->name,
4765 "Potentially disasterous buffer overflow averted!");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004766 break;
4767 }
Al Viroa23ace52007-12-19 22:24:16 -05004768 j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i],
4769 le32_to_cpu(vals[i]));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004770 }
Al Viroa23ace52007-12-19 22:24:16 -05004771 if (i*4 >= len) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004772 airo_print_warn(apriv->dev->name, "Got a short rid");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004773 }
4774 data->readlen = j;
4775 return 0;
4776}
4777
4778static int get_dec_u16( char *buffer, int *start, int limit ) {
4779 u16 value;
4780 int valid = 0;
4781 for( value = 0; buffer[*start] >= '0' &&
4782 buffer[*start] <= '9' &&
4783 *start < limit; (*start)++ ) {
4784 valid = 1;
4785 value *= 10;
4786 value += buffer[*start] - '0';
4787 }
4788 if ( !valid ) return -1;
4789 return value;
4790}
4791
4792static int airo_config_commit(struct net_device *dev,
4793 struct iw_request_info *info, void *zwrq,
4794 char *extra);
4795
4796static void proc_config_on_close( struct inode *inode, struct file *file ) {
4797 struct proc_data *data = file->private_data;
4798 struct proc_dir_entry *dp = PDE(inode);
4799 struct net_device *dev = dp->data;
4800 struct airo_info *ai = dev->priv;
4801 char *line;
4802
4803 if ( !data->writelen ) return;
4804
4805 readConfigRid(ai, 1);
4806 set_bit (FLAG_COMMIT, &ai->flags);
4807
4808 line = data->wbuffer;
4809 while( line[0] ) {
4810/*** Mode processing */
4811 if ( !strncmp( line, "Mode: ", 6 ) ) {
4812 line += 6;
4813 if ((ai->config.rmode & 0xff) >= RXMODE_RFMON)
4814 set_bit (FLAG_RESET, &ai->flags);
4815 ai->config.rmode &= 0xfe00;
4816 clear_bit (FLAG_802_11, &ai->flags);
4817 ai->config.opmode &= 0xFF00;
4818 ai->config.scanMode = SCANMODE_ACTIVE;
4819 if ( line[0] == 'a' ) {
4820 ai->config.opmode |= 0;
4821 } else {
4822 ai->config.opmode |= 1;
4823 if ( line[0] == 'r' ) {
4824 ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
4825 ai->config.scanMode = SCANMODE_PASSIVE;
4826 set_bit (FLAG_802_11, &ai->flags);
4827 } else if ( line[0] == 'y' ) {
4828 ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER;
4829 ai->config.scanMode = SCANMODE_PASSIVE;
4830 set_bit (FLAG_802_11, &ai->flags);
4831 } else if ( line[0] == 'l' )
4832 ai->config.rmode |= RXMODE_LANMON;
4833 }
4834 set_bit (FLAG_COMMIT, &ai->flags);
4835 }
4836
4837/*** Radio status */
4838 else if (!strncmp(line,"Radio: ", 7)) {
4839 line += 7;
4840 if (!strncmp(line,"off",3)) {
4841 set_bit (FLAG_RADIO_OFF, &ai->flags);
4842 } else {
4843 clear_bit (FLAG_RADIO_OFF, &ai->flags);
4844 }
4845 }
4846/*** NodeName processing */
4847 else if ( !strncmp( line, "NodeName: ", 10 ) ) {
4848 int j;
4849
4850 line += 10;
4851 memset( ai->config.nodeName, 0, 16 );
4852/* Do the name, assume a space between the mode and node name */
4853 for( j = 0; j < 16 && line[j] != '\n'; j++ ) {
4854 ai->config.nodeName[j] = line[j];
4855 }
4856 set_bit (FLAG_COMMIT, &ai->flags);
4857 }
4858
4859/*** PowerMode processing */
4860 else if ( !strncmp( line, "PowerMode: ", 11 ) ) {
4861 line += 11;
4862 if ( !strncmp( line, "PSPCAM", 6 ) ) {
4863 ai->config.powerSaveMode = POWERSAVE_PSPCAM;
4864 set_bit (FLAG_COMMIT, &ai->flags);
4865 } else if ( !strncmp( line, "PSP", 3 ) ) {
4866 ai->config.powerSaveMode = POWERSAVE_PSP;
4867 set_bit (FLAG_COMMIT, &ai->flags);
4868 } else {
4869 ai->config.powerSaveMode = POWERSAVE_CAM;
4870 set_bit (FLAG_COMMIT, &ai->flags);
4871 }
4872 } else if ( !strncmp( line, "DataRates: ", 11 ) ) {
4873 int v, i = 0, k = 0; /* i is index into line,
4874 k is index to rates */
4875
4876 line += 11;
4877 while((v = get_dec_u16(line, &i, 3))!=-1) {
4878 ai->config.rates[k++] = (u8)v;
4879 line += i + 1;
4880 i = 0;
4881 }
4882 set_bit (FLAG_COMMIT, &ai->flags);
4883 } else if ( !strncmp( line, "Channel: ", 9 ) ) {
4884 int v, i = 0;
4885 line += 9;
4886 v = get_dec_u16(line, &i, i+3);
4887 if ( v != -1 ) {
4888 ai->config.channelSet = (u16)v;
4889 set_bit (FLAG_COMMIT, &ai->flags);
4890 }
4891 } else if ( !strncmp( line, "XmitPower: ", 11 ) ) {
4892 int v, i = 0;
4893 line += 11;
4894 v = get_dec_u16(line, &i, i+3);
4895 if ( v != -1 ) {
4896 ai->config.txPower = (u16)v;
4897 set_bit (FLAG_COMMIT, &ai->flags);
4898 }
4899 } else if ( !strncmp( line, "WEP: ", 5 ) ) {
4900 line += 5;
4901 switch( line[0] ) {
4902 case 's':
4903 ai->config.authType = (u16)AUTH_SHAREDKEY;
4904 break;
4905 case 'e':
4906 ai->config.authType = (u16)AUTH_ENCRYPT;
4907 break;
4908 default:
4909 ai->config.authType = (u16)AUTH_OPEN;
4910 break;
4911 }
4912 set_bit (FLAG_COMMIT, &ai->flags);
4913 } else if ( !strncmp( line, "LongRetryLimit: ", 16 ) ) {
4914 int v, i = 0;
4915
4916 line += 16;
4917 v = get_dec_u16(line, &i, 3);
4918 v = (v<0) ? 0 : ((v>255) ? 255 : v);
4919 ai->config.longRetryLimit = (u16)v;
4920 set_bit (FLAG_COMMIT, &ai->flags);
4921 } else if ( !strncmp( line, "ShortRetryLimit: ", 17 ) ) {
4922 int v, i = 0;
4923
4924 line += 17;
4925 v = get_dec_u16(line, &i, 3);
4926 v = (v<0) ? 0 : ((v>255) ? 255 : v);
4927 ai->config.shortRetryLimit = (u16)v;
4928 set_bit (FLAG_COMMIT, &ai->flags);
4929 } else if ( !strncmp( line, "RTSThreshold: ", 14 ) ) {
4930 int v, i = 0;
4931
4932 line += 14;
4933 v = get_dec_u16(line, &i, 4);
Dan Williams15db2762006-03-16 13:46:27 -05004934 v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004935 ai->config.rtsThres = (u16)v;
4936 set_bit (FLAG_COMMIT, &ai->flags);
4937 } else if ( !strncmp( line, "TXMSDULifetime: ", 16 ) ) {
4938 int v, i = 0;
4939
4940 line += 16;
4941 v = get_dec_u16(line, &i, 5);
4942 v = (v<0) ? 0 : v;
4943 ai->config.txLifetime = (u16)v;
4944 set_bit (FLAG_COMMIT, &ai->flags);
4945 } else if ( !strncmp( line, "RXMSDULifetime: ", 16 ) ) {
4946 int v, i = 0;
4947
4948 line += 16;
4949 v = get_dec_u16(line, &i, 5);
4950 v = (v<0) ? 0 : v;
4951 ai->config.rxLifetime = (u16)v;
4952 set_bit (FLAG_COMMIT, &ai->flags);
4953 } else if ( !strncmp( line, "TXDiversity: ", 13 ) ) {
4954 ai->config.txDiversity =
4955 (line[13]=='l') ? 1 :
4956 ((line[13]=='r')? 2: 3);
4957 set_bit (FLAG_COMMIT, &ai->flags);
4958 } else if ( !strncmp( line, "RXDiversity: ", 13 ) ) {
4959 ai->config.rxDiversity =
4960 (line[13]=='l') ? 1 :
4961 ((line[13]=='r')? 2: 3);
4962 set_bit (FLAG_COMMIT, &ai->flags);
4963 } else if ( !strncmp( line, "FragThreshold: ", 15 ) ) {
4964 int v, i = 0;
4965
4966 line += 15;
4967 v = get_dec_u16(line, &i, 4);
Dan Williams15db2762006-03-16 13:46:27 -05004968 v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004969 v = v & 0xfffe; /* Make sure its even */
4970 ai->config.fragThresh = (u16)v;
4971 set_bit (FLAG_COMMIT, &ai->flags);
4972 } else if (!strncmp(line, "Modulation: ", 12)) {
4973 line += 12;
4974 switch(*line) {
4975 case 'd': ai->config.modulation=MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break;
4976 case 'c': ai->config.modulation=MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break;
4977 case 'm': ai->config.modulation=MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break;
Dan Williams934d8bf2006-03-16 13:46:23 -05004978 default: airo_print_warn(ai->dev->name, "Unknown modulation");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004979 }
4980 } else if (!strncmp(line, "Preamble: ", 10)) {
4981 line += 10;
4982 switch(*line) {
4983 case 'a': ai->config.preamble=PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break;
4984 case 'l': ai->config.preamble=PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break;
4985 case 's': ai->config.preamble=PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break;
Dan Williams934d8bf2006-03-16 13:46:23 -05004986 default: airo_print_warn(ai->dev->name, "Unknown preamble");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004987 }
4988 } else {
Dan Williams934d8bf2006-03-16 13:46:23 -05004989 airo_print_warn(ai->dev->name, "Couldn't figure out %s", line);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004990 }
4991 while( line[0] && line[0] != '\n' ) line++;
4992 if ( line[0] ) line++;
4993 }
4994 airo_config_commit(dev, NULL, NULL, NULL);
4995}
4996
4997static char *get_rmode(u16 mode) {
4998 switch(mode&0xff) {
4999 case RXMODE_RFMON: return "rfmon";
5000 case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon";
5001 case RXMODE_LANMON: return "lanmon";
5002 }
5003 return "ESS";
5004}
5005
5006static int proc_config_open( struct inode *inode, struct file *file ) {
5007 struct proc_data *data;
5008 struct proc_dir_entry *dp = PDE(inode);
5009 struct net_device *dev = dp->data;
5010 struct airo_info *ai = dev->priv;
5011 int i;
5012
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
5030 i = sprintf( data->rbuffer,
5031 "Mode: %s\n"
5032 "Radio: %s\n"
5033 "NodeName: %-16s\n"
5034 "PowerMode: %s\n"
5035 "DataRates: %d %d %d %d %d %d %d %d\n"
5036 "Channel: %d\n"
5037 "XmitPower: %d\n",
5038 (ai->config.opmode & 0xFF) == 0 ? "adhoc" :
5039 (ai->config.opmode & 0xFF) == 1 ? get_rmode(ai->config.rmode):
5040 (ai->config.opmode & 0xFF) == 2 ? "AP" :
5041 (ai->config.opmode & 0xFF) == 3 ? "AP RPTR" : "Error",
5042 test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on",
5043 ai->config.nodeName,
5044 ai->config.powerSaveMode == 0 ? "CAM" :
5045 ai->config.powerSaveMode == 1 ? "PSP" :
5046 ai->config.powerSaveMode == 2 ? "PSPCAM" : "Error",
5047 (int)ai->config.rates[0],
5048 (int)ai->config.rates[1],
5049 (int)ai->config.rates[2],
5050 (int)ai->config.rates[3],
5051 (int)ai->config.rates[4],
5052 (int)ai->config.rates[5],
5053 (int)ai->config.rates[6],
5054 (int)ai->config.rates[7],
5055 (int)ai->config.channelSet,
5056 (int)ai->config.txPower
5057 );
5058 sprintf( data->rbuffer + i,
5059 "LongRetryLimit: %d\n"
5060 "ShortRetryLimit: %d\n"
5061 "RTSThreshold: %d\n"
5062 "TXMSDULifetime: %d\n"
5063 "RXMSDULifetime: %d\n"
5064 "TXDiversity: %s\n"
5065 "RXDiversity: %s\n"
5066 "FragThreshold: %d\n"
5067 "WEP: %s\n"
5068 "Modulation: %s\n"
5069 "Preamble: %s\n",
5070 (int)ai->config.longRetryLimit,
5071 (int)ai->config.shortRetryLimit,
5072 (int)ai->config.rtsThres,
5073 (int)ai->config.txLifetime,
5074 (int)ai->config.rxLifetime,
5075 ai->config.txDiversity == 1 ? "left" :
5076 ai->config.txDiversity == 2 ? "right" : "both",
5077 ai->config.rxDiversity == 1 ? "left" :
5078 ai->config.rxDiversity == 2 ? "right" : "both",
5079 (int)ai->config.fragThresh,
5080 ai->config.authType == AUTH_ENCRYPT ? "encrypt" :
5081 ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open",
5082 ai->config.modulation == 0 ? "default" :
5083 ai->config.modulation == MOD_CCK ? "cck" :
5084 ai->config.modulation == MOD_MOK ? "mok" : "error",
5085 ai->config.preamble == PREAMBLE_AUTO ? "auto" :
5086 ai->config.preamble == PREAMBLE_LONG ? "long" :
5087 ai->config.preamble == PREAMBLE_SHORT ? "short" : "error"
5088 );
5089 data->readlen = strlen( data->rbuffer );
5090 return 0;
5091}
5092
Al Viro0dd22122007-12-17 16:11:57 -05005093static void proc_SSID_on_close(struct inode *inode, struct file *file)
5094{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005095 struct proc_data *data = (struct proc_data *)file->private_data;
5096 struct proc_dir_entry *dp = PDE(inode);
5097 struct net_device *dev = dp->data;
5098 struct airo_info *ai = dev->priv;
5099 SsidRid SSID_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005100 int i;
Al Viro0dd22122007-12-17 16:11:57 -05005101 char *p = data->wbuffer;
5102 char *end = p + data->writelen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005103
Al Viro0dd22122007-12-17 16:11:57 -05005104 if (!data->writelen)
5105 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005106
Al Viro0dd22122007-12-17 16:11:57 -05005107 *end = '\n'; /* sentinel; we have space for it */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005108
Al Viro0dd22122007-12-17 16:11:57 -05005109 memset(&SSID_rid, 0, sizeof(SSID_rid));
5110
5111 for (i = 0; i < 3 && p < end; i++) {
5112 int j = 0;
5113 /* copy up to 32 characters from this line */
5114 while (*p != '\n' && j < 32)
5115 SSID_rid.ssids[i].ssid[j++] = *p++;
5116 if (j == 0)
5117 break;
5118 SSID_rid.ssids[i].len = cpu_to_le16(j);
5119 /* skip to the beginning of the next line */
5120 while (*p++ != '\n')
5121 ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005122 }
5123 if (i)
Al Viro0dd22122007-12-17 16:11:57 -05005124 SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005125 disable_MAC(ai, 1);
5126 writeSsidRid(ai, &SSID_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005127 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005128}
5129
Jesper Juhl77933d72005-07-27 11:46:09 -07005130static inline u8 hexVal(char c) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005131 if (c>='0' && c<='9') return c -= '0';
5132 if (c>='a' && c<='f') return c -= 'a'-10;
5133 if (c>='A' && c<='F') return c -= 'A'-10;
5134 return 0;
5135}
5136
5137static void proc_APList_on_close( struct inode *inode, struct file *file ) {
5138 struct proc_data *data = (struct proc_data *)file->private_data;
5139 struct proc_dir_entry *dp = PDE(inode);
5140 struct net_device *dev = dp->data;
5141 struct airo_info *ai = dev->priv;
5142 APListRid APList_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005143 int i;
5144
5145 if ( !data->writelen ) return;
5146
5147 memset( &APList_rid, 0, sizeof(APList_rid) );
5148 APList_rid.len = sizeof(APList_rid);
5149
5150 for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) {
5151 int j;
5152 for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) {
5153 switch(j%3) {
5154 case 0:
5155 APList_rid.ap[i][j/3]=
5156 hexVal(data->wbuffer[j+i*6*3])<<4;
5157 break;
5158 case 1:
5159 APList_rid.ap[i][j/3]|=
5160 hexVal(data->wbuffer[j+i*6*3]);
5161 break;
5162 }
5163 }
5164 }
5165 disable_MAC(ai, 1);
5166 writeAPListRid(ai, &APList_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005167 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005168}
5169
5170/* This function wraps PC4500_writerid with a MAC disable */
5171static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data,
5172 int len, int dummy ) {
5173 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005174
5175 disable_MAC(ai, 1);
5176 rc = PC4500_writerid(ai, rid, rid_data, len, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005177 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005178 return rc;
5179}
5180
5181/* Returns the length of the key at the index. If index == 0xffff
5182 * the index of the transmit key is returned. If the key doesn't exist,
5183 * -1 will be returned.
5184 */
5185static int get_wep_key(struct airo_info *ai, u16 index) {
5186 WepKeyRid wkr;
5187 int rc;
Al Viro4293ea32007-12-19 19:21:51 -05005188 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005189
5190 rc = readWepKeyRid(ai, &wkr, 1, 1);
5191 if (rc == SUCCESS) do {
5192 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05005193 if (wkr.kindex == cpu_to_le16(index)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005194 if (index == 0xffff) {
5195 return wkr.mac[0];
5196 }
Al Viro4293ea32007-12-19 19:21:51 -05005197 return le16_to_cpu(wkr.klen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005198 }
5199 readWepKeyRid(ai, &wkr, 0, 1);
Al Viro4293ea32007-12-19 19:21:51 -05005200 } while (lastindex != wkr.kindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005201 return -1;
5202}
5203
5204static int set_wep_key(struct airo_info *ai, u16 index,
Al Viro4293ea32007-12-19 19:21:51 -05005205 const char *key, u16 keylen, int perm, int lock )
5206{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005207 static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
5208 WepKeyRid wkr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005209
5210 memset(&wkr, 0, sizeof(wkr));
5211 if (keylen == 0) {
5212// We are selecting which key to use
Al Viro4293ea32007-12-19 19:21:51 -05005213 wkr.len = cpu_to_le16(sizeof(wkr));
5214 wkr.kindex = cpu_to_le16(0xffff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005215 wkr.mac[0] = (char)index;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005216 if (perm) ai->defindex = (char)index;
5217 } else {
5218// We are actually setting the key
Al Viro4293ea32007-12-19 19:21:51 -05005219 wkr.len = cpu_to_le16(sizeof(wkr));
5220 wkr.kindex = cpu_to_le16(index);
5221 wkr.klen = cpu_to_le16(keylen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005222 memcpy( wkr.key, key, keylen );
5223 memcpy( wkr.mac, macaddr, ETH_ALEN );
Linus Torvalds1da177e2005-04-16 15:20:36 -07005224 }
5225
Dan Streetmanf89b2322005-11-11 11:41:42 -05005226 if (perm) disable_MAC(ai, lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005227 writeWepKeyRid(ai, &wkr, perm, lock);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005228 if (perm) enable_MAC(ai, lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005229 return 0;
5230}
5231
5232static void proc_wepkey_on_close( struct inode *inode, struct file *file ) {
5233 struct proc_data *data;
5234 struct proc_dir_entry *dp = PDE(inode);
5235 struct net_device *dev = dp->data;
5236 struct airo_info *ai = dev->priv;
5237 int i;
5238 char key[16];
5239 u16 index = 0;
5240 int j = 0;
5241
5242 memset(key, 0, sizeof(key));
5243
5244 data = (struct proc_data *)file->private_data;
5245 if ( !data->writelen ) return;
5246
5247 if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' &&
5248 (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) {
5249 index = data->wbuffer[0] - '0';
5250 if (data->wbuffer[1] == '\n') {
5251 set_wep_key(ai, index, NULL, 0, 1, 1);
5252 return;
5253 }
5254 j = 2;
5255 } else {
Dan Williams934d8bf2006-03-16 13:46:23 -05005256 airo_print_err(ai->dev->name, "WepKey passed invalid key index");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005257 return;
5258 }
5259
5260 for( i = 0; i < 16*3 && data->wbuffer[i+j]; i++ ) {
5261 switch(i%3) {
5262 case 0:
5263 key[i/3] = hexVal(data->wbuffer[i+j])<<4;
5264 break;
5265 case 1:
5266 key[i/3] |= hexVal(data->wbuffer[i+j]);
5267 break;
5268 }
5269 }
5270 set_wep_key(ai, index, key, i/3, 1, 1);
5271}
5272
Al Viro4293ea32007-12-19 19:21:51 -05005273static int proc_wepkey_open( struct inode *inode, struct file *file )
5274{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005275 struct proc_data *data;
5276 struct proc_dir_entry *dp = PDE(inode);
5277 struct net_device *dev = dp->data;
5278 struct airo_info *ai = dev->priv;
5279 char *ptr;
5280 WepKeyRid wkr;
Al Viro4293ea32007-12-19 19:21:51 -05005281 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005282 int j=0;
5283 int rc;
5284
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005285 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005286 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005287 memset(&wkr, 0, sizeof(wkr));
5288 data = (struct proc_data *)file->private_data;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005289 if ((data->rbuffer = kzalloc( 180, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005290 kfree (file->private_data);
5291 return -ENOMEM;
5292 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005293 data->writelen = 0;
5294 data->maxwritelen = 80;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005295 if ((data->wbuffer = kzalloc( 80, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005296 kfree (data->rbuffer);
5297 kfree (file->private_data);
5298 return -ENOMEM;
5299 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005300 data->on_close = proc_wepkey_on_close;
5301
5302 ptr = data->rbuffer;
5303 strcpy(ptr, "No wep keys\n");
5304 rc = readWepKeyRid(ai, &wkr, 1, 1);
5305 if (rc == SUCCESS) do {
5306 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05005307 if (wkr.kindex == cpu_to_le16(0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005308 j += sprintf(ptr+j, "Tx key = %d\n",
5309 (int)wkr.mac[0]);
5310 } else {
5311 j += sprintf(ptr+j, "Key %d set with length = %d\n",
Al Viro4293ea32007-12-19 19:21:51 -05005312 le16_to_cpu(wkr.kindex),
5313 le16_to_cpu(wkr.klen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005314 }
5315 readWepKeyRid(ai, &wkr, 0, 1);
5316 } while((lastindex != wkr.kindex) && (j < 180-30));
5317
5318 data->readlen = strlen( data->rbuffer );
5319 return 0;
5320}
5321
Al Viro0dd22122007-12-17 16:11:57 -05005322static int proc_SSID_open(struct inode *inode, struct file *file)
5323{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005324 struct proc_data *data;
5325 struct proc_dir_entry *dp = PDE(inode);
5326 struct net_device *dev = dp->data;
5327 struct airo_info *ai = dev->priv;
5328 int i;
5329 char *ptr;
5330 SsidRid SSID_rid;
5331
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005332 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005333 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005334 data = (struct proc_data *)file->private_data;
5335 if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
5336 kfree (file->private_data);
5337 return -ENOMEM;
5338 }
5339 data->writelen = 0;
5340 data->maxwritelen = 33*3;
Al Viro0dd22122007-12-17 16:11:57 -05005341 /* allocate maxwritelen + 1; we'll want a sentinel */
5342 if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005343 kfree (data->rbuffer);
5344 kfree (file->private_data);
5345 return -ENOMEM;
5346 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005347 data->on_close = proc_SSID_on_close;
5348
5349 readSsidRid(ai, &SSID_rid);
5350 ptr = data->rbuffer;
Al Viro0dd22122007-12-17 16:11:57 -05005351 for (i = 0; i < 3; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005352 int j;
Al Viro0dd22122007-12-17 16:11:57 -05005353 size_t len = le16_to_cpu(SSID_rid.ssids[i].len);
5354 if (!len)
5355 break;
5356 if (len > 32)
5357 len = 32;
5358 for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005359 *ptr++ = SSID_rid.ssids[i].ssid[j];
Linus Torvalds1da177e2005-04-16 15:20:36 -07005360 *ptr++ = '\n';
5361 }
5362 *ptr = '\0';
5363 data->readlen = strlen( data->rbuffer );
5364 return 0;
5365}
5366
5367static int proc_APList_open( struct inode *inode, struct file *file ) {
5368 struct proc_data *data;
5369 struct proc_dir_entry *dp = PDE(inode);
5370 struct net_device *dev = dp->data;
5371 struct airo_info *ai = dev->priv;
5372 int i;
5373 char *ptr;
5374 APListRid APList_rid;
Joe Perches0795af52007-10-03 17:59:30 -07005375 DECLARE_MAC_BUF(mac);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005376
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005377 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005378 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005379 data = (struct proc_data *)file->private_data;
5380 if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) {
5381 kfree (file->private_data);
5382 return -ENOMEM;
5383 }
5384 data->writelen = 0;
5385 data->maxwritelen = 4*6*3;
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005386 if ((data->wbuffer = kzalloc( data->maxwritelen, GFP_KERNEL )) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005387 kfree (data->rbuffer);
5388 kfree (file->private_data);
5389 return -ENOMEM;
5390 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005391 data->on_close = proc_APList_on_close;
5392
5393 readAPListRid(ai, &APList_rid);
5394 ptr = data->rbuffer;
5395 for( i = 0; i < 4; i++ ) {
5396// We end when we find a zero MAC
5397 if ( !*(int*)APList_rid.ap[i] &&
5398 !*(int*)&APList_rid.ap[i][2]) break;
Joe Perches0795af52007-10-03 17:59:30 -07005399 ptr += sprintf(ptr, "%s\n",
5400 print_mac(mac, APList_rid.ap[i]));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005401 }
5402 if (i==0) ptr += sprintf(ptr, "Not using specific APs\n");
5403
5404 *ptr = '\0';
5405 data->readlen = strlen( data->rbuffer );
5406 return 0;
5407}
5408
5409static int proc_BSSList_open( struct inode *inode, struct file *file ) {
5410 struct proc_data *data;
5411 struct proc_dir_entry *dp = PDE(inode);
5412 struct net_device *dev = dp->data;
5413 struct airo_info *ai = dev->priv;
5414 char *ptr;
5415 BSSListRid BSSList_rid;
5416 int rc;
5417 /* If doLoseSync is not 1, we won't do a Lose Sync */
5418 int doLoseSync = -1;
Joe Perches0795af52007-10-03 17:59:30 -07005419 DECLARE_MAC_BUF(mac);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005420
Panagiotis Issarisb69a3aa2005-11-08 00:03:15 +01005421 if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005422 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005423 data = (struct proc_data *)file->private_data;
5424 if ((data->rbuffer = kmalloc( 1024, GFP_KERNEL )) == NULL) {
5425 kfree (file->private_data);
5426 return -ENOMEM;
5427 }
5428 data->writelen = 0;
5429 data->maxwritelen = 0;
5430 data->wbuffer = NULL;
5431 data->on_close = NULL;
5432
5433 if (file->f_mode & FMODE_WRITE) {
5434 if (!(file->f_mode & FMODE_READ)) {
5435 Cmd cmd;
5436 Resp rsp;
5437
5438 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
5439 memset(&cmd, 0, sizeof(cmd));
5440 cmd.cmd=CMD_LISTBSS;
5441 if (down_interruptible(&ai->sem))
5442 return -ERESTARTSYS;
5443 issuecommand(ai, &cmd, &rsp);
5444 up(&ai->sem);
5445 data->readlen = 0;
5446 return 0;
5447 }
5448 doLoseSync = 1;
5449 }
5450 ptr = data->rbuffer;
5451 /* There is a race condition here if there are concurrent opens.
5452 Since it is a rare condition, we'll just live with it, otherwise
5453 we have to add a spin lock... */
5454 rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
Al Viro17e70492007-12-19 18:56:37 -05005455 while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
Joe Perches0795af52007-10-03 17:59:30 -07005456 ptr += sprintf(ptr, "%s %*s rssi = %d",
5457 print_mac(mac, BSSList_rid.bssid),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005458 (int)BSSList_rid.ssidLen,
5459 BSSList_rid.ssid,
Al Viro17e70492007-12-19 18:56:37 -05005460 le16_to_cpu(BSSList_rid.dBm));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005461 ptr += sprintf(ptr, " channel = %d %s %s %s %s\n",
Al Viro17e70492007-12-19 18:56:37 -05005462 le16_to_cpu(BSSList_rid.dsChannel),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005463 BSSList_rid.cap & CAP_ESS ? "ESS" : "",
5464 BSSList_rid.cap & CAP_IBSS ? "adhoc" : "",
5465 BSSList_rid.cap & CAP_PRIVACY ? "wep" : "",
5466 BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : "");
5467 rc = readBSSListRid(ai, 0, &BSSList_rid);
5468 }
5469 *ptr = '\0';
5470 data->readlen = strlen( data->rbuffer );
5471 return 0;
5472}
5473
5474static int proc_close( struct inode *inode, struct file *file )
5475{
Jesper Juhlb4558ea2005-10-28 16:53:13 -04005476 struct proc_data *data = file->private_data;
5477
5478 if (data->on_close != NULL)
5479 data->on_close(inode, file);
5480 kfree(data->rbuffer);
5481 kfree(data->wbuffer);
5482 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005483 return 0;
5484}
5485
Linus Torvalds1da177e2005-04-16 15:20:36 -07005486/* Since the card doesn't automatically switch to the right WEP mode,
5487 we will make it do it. If the card isn't associated, every secs we
5488 will switch WEP modes to see if that will help. If the card is
5489 associated we will check every minute to see if anything has
5490 changed. */
5491static void timer_func( struct net_device *dev ) {
5492 struct airo_info *apriv = dev->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005493
5494/* We don't have a link so try changing the authtype */
5495 readConfigRid(apriv, 0);
5496 disable_MAC(apriv, 0);
5497 switch(apriv->config.authType) {
5498 case AUTH_ENCRYPT:
5499/* So drop to OPEN */
5500 apriv->config.authType = AUTH_OPEN;
5501 break;
5502 case AUTH_SHAREDKEY:
5503 if (apriv->keyindex < auto_wep) {
5504 set_wep_key(apriv, apriv->keyindex, NULL, 0, 0, 0);
5505 apriv->config.authType = AUTH_SHAREDKEY;
5506 apriv->keyindex++;
5507 } else {
5508 /* Drop to ENCRYPT */
5509 apriv->keyindex = 0;
5510 set_wep_key(apriv, apriv->defindex, NULL, 0, 0, 0);
5511 apriv->config.authType = AUTH_ENCRYPT;
5512 }
5513 break;
5514 default: /* We'll escalate to SHAREDKEY */
5515 apriv->config.authType = AUTH_SHAREDKEY;
5516 }
5517 set_bit (FLAG_COMMIT, &apriv->flags);
5518 writeConfigRid(apriv, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005519 enable_MAC(apriv, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005520 up(&apriv->sem);
5521
5522/* Schedule check to see if the change worked */
Dan Williams3c304952006-04-15 12:26:18 -04005523 clear_bit(JOB_AUTOWEP, &apriv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005524 apriv->expires = RUN_AT(HZ*3);
5525}
5526
Linus Torvalds1da177e2005-04-16 15:20:36 -07005527#ifdef CONFIG_PCI
5528static int __devinit airo_pci_probe(struct pci_dev *pdev,
5529 const struct pci_device_id *pent)
5530{
5531 struct net_device *dev;
5532
5533 if (pci_enable_device(pdev))
5534 return -ENODEV;
5535 pci_set_master(pdev);
5536
5537 if (pdev->device == 0x5000 || pdev->device == 0xa504)
5538 dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev);
5539 else
5540 dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev);
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005541 if (!dev) {
5542 pci_disable_device(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005543 return -ENODEV;
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005544 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005545
5546 pci_set_drvdata(pdev, dev);
5547 return 0;
5548}
5549
5550static void __devexit airo_pci_remove(struct pci_dev *pdev)
5551{
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01005552 struct net_device *dev = pci_get_drvdata(pdev);
5553
5554 airo_print_info(dev->name, "Unregistering...");
5555 stop_airo_card(dev, 1);
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005556 pci_disable_device(pdev);
5557 pci_set_drvdata(pdev, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005558}
5559
Pavel Machek05adc3b2005-04-16 15:25:25 -07005560static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005561{
5562 struct net_device *dev = pci_get_drvdata(pdev);
5563 struct airo_info *ai = dev->priv;
5564 Cmd cmd;
5565 Resp rsp;
5566
5567 if ((ai->APList == NULL) &&
5568 (ai->APList = kmalloc(sizeof(APListRid), GFP_KERNEL)) == NULL)
5569 return -ENOMEM;
5570 if ((ai->SSID == NULL) &&
5571 (ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL)) == NULL)
5572 return -ENOMEM;
5573 readAPListRid(ai, ai->APList);
5574 readSsidRid(ai, ai->SSID);
5575 memset(&cmd, 0, sizeof(cmd));
5576 /* the lock will be released at the end of the resume callback */
5577 if (down_interruptible(&ai->sem))
5578 return -EAGAIN;
5579 disable_MAC(ai, 0);
5580 netif_device_detach(dev);
5581 ai->power = state;
5582 cmd.cmd=HOSTSLEEP;
5583 issuecommand(ai, &cmd, &rsp);
5584
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005585 pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005586 pci_save_state(pdev);
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005587 return pci_set_power_state(pdev, pci_choose_state(pdev, state));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005588}
5589
5590static int airo_pci_resume(struct pci_dev *pdev)
5591{
5592 struct net_device *dev = pci_get_drvdata(pdev);
5593 struct airo_info *ai = dev->priv;
Michal Schmidt53232802005-10-04 07:46:21 -04005594 pci_power_t prev_state = pdev->current_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005595
Michal Schmidt53232802005-10-04 07:46:21 -04005596 pci_set_power_state(pdev, PCI_D0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005597 pci_restore_state(pdev);
Michal Schmidt53232802005-10-04 07:46:21 -04005598 pci_enable_wake(pdev, PCI_D0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005599
Michal Schmidt53232802005-10-04 07:46:21 -04005600 if (prev_state != PCI_D1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005601 reset_card(dev, 0);
5602 mpi_init_descriptors(ai);
5603 setup_card(ai, dev->dev_addr, 0);
5604 clear_bit(FLAG_RADIO_OFF, &ai->flags);
5605 clear_bit(FLAG_PENDING_XMIT, &ai->flags);
5606 } else {
5607 OUT4500(ai, EVACK, EV_AWAKEN);
5608 OUT4500(ai, EVACK, EV_AWAKEN);
5609 msleep(100);
5610 }
5611
5612 set_bit (FLAG_COMMIT, &ai->flags);
5613 disable_MAC(ai, 0);
5614 msleep(200);
5615 if (ai->SSID) {
5616 writeSsidRid(ai, ai->SSID, 0);
5617 kfree(ai->SSID);
5618 ai->SSID = NULL;
5619 }
5620 if (ai->APList) {
5621 writeAPListRid(ai, ai->APList, 0);
5622 kfree(ai->APList);
5623 ai->APList = NULL;
5624 }
5625 writeConfigRid(ai, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005626 enable_MAC(ai, 0);
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005627 ai->power = PMSG_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005628 netif_device_attach(dev);
5629 netif_wake_queue(dev);
5630 enable_interrupts(ai);
5631 up(&ai->sem);
5632 return 0;
5633}
5634#endif
5635
5636static int __init airo_init_module( void )
5637{
Jeff Garzikde897882006-10-01 07:31:09 -04005638 int i;
5639#if 0
5640 int have_isa_dev = 0;
5641#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07005642
5643 airo_entry = create_proc_entry("aironet",
5644 S_IFDIR | airo_perm,
5645 proc_root_driver);
Jeff Garzikde897882006-10-01 07:31:09 -04005646
5647 if (airo_entry) {
5648 airo_entry->uid = proc_uid;
5649 airo_entry->gid = proc_gid;
5650 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005651
5652 for( i = 0; i < 4 && io[i] && irq[i]; i++ ) {
Dan Williams934d8bf2006-03-16 13:46:23 -05005653 airo_print_info("", "Trying to configure ISA adapter at irq=%d "
5654 "io=0x%x", irq[i], io[i] );
Linus Torvalds1da177e2005-04-16 15:20:36 -07005655 if (init_airo_card( irq[i], io[i], 0, NULL ))
Jeff Garzikde897882006-10-01 07:31:09 -04005656#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07005657 have_isa_dev = 1;
Jeff Garzikde897882006-10-01 07:31:09 -04005658#else
5659 /* do nothing */ ;
5660#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07005661 }
5662
5663#ifdef CONFIG_PCI
Dan Williams934d8bf2006-03-16 13:46:23 -05005664 airo_print_info("", "Probing for PCI adapters");
Jeff Garzikde897882006-10-01 07:31:09 -04005665 i = pci_register_driver(&airo_driver);
Dan Williams934d8bf2006-03-16 13:46:23 -05005666 airo_print_info("", "Finished probing for PCI adapters");
Jeff Garzikde897882006-10-01 07:31:09 -04005667
5668 if (i) {
5669 remove_proc_entry("aironet", proc_root_driver);
5670 return i;
5671 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005672#endif
5673
5674 /* Always exit with success, as we are a library module
5675 * as well as a driver module
5676 */
5677 return 0;
5678}
5679
5680static void __exit airo_cleanup_module( void )
5681{
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01005682 struct airo_info *ai;
5683 while(!list_empty(&airo_devices)) {
5684 ai = list_entry(airo_devices.next, struct airo_info, dev_list);
5685 airo_print_info(ai->dev->name, "Unregistering...");
5686 stop_airo_card(ai->dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005687 }
5688#ifdef CONFIG_PCI
5689 pci_unregister_driver(&airo_driver);
5690#endif
5691 remove_proc_entry("aironet", proc_root_driver);
5692}
5693
Linus Torvalds1da177e2005-04-16 15:20:36 -07005694/*
5695 * Initial Wireless Extension code for Aironet driver by :
5696 * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00
5697 * Conversion to new driver API by :
5698 * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02
5699 * Javier also did a good amount of work here, adding some new extensions
5700 * and fixing my code. Let's just say that without him this code just
5701 * would not work at all... - Jean II
5702 */
5703
Dan Williams41480af2005-05-10 09:45:51 -04005704static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi)
5705{
5706 if( !rssi_rid )
5707 return 0;
5708
5709 return (0x100 - rssi_rid[rssi].rssidBm);
5710}
5711
5712static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm)
5713{
5714 int i;
5715
5716 if( !rssi_rid )
5717 return 0;
5718
5719 for( i = 0; i < 256; i++ )
5720 if (rssi_rid[i].rssidBm == dbm)
5721 return rssi_rid[i].rssipct;
5722
5723 return 0;
5724}
5725
5726
Linus Torvalds1da177e2005-04-16 15:20:36 -07005727static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid)
5728{
5729 int quality = 0;
5730
Al Viro56d81bd2007-12-20 17:18:35 -05005731 if ((status_rid->mode & 0x3f) != 0x3f)
5732 return 0;
5733
5734 if (!(cap_rid->hardCap & cpu_to_le16(8)))
5735 return 0;
5736
5737 if (memcmp(cap_rid->prodName, "350", 3))
5738 if (status_rid->signalQuality > 0x20)
5739 quality = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005740 else
Al Viro56d81bd2007-12-20 17:18:35 -05005741 quality = 0x20 - status_rid->signalQuality;
5742 else
5743 if (status_rid->signalQuality > 0xb0)
5744 quality = 0;
5745 else if (status_rid->signalQuality < 0x10)
5746 quality = 0xa0;
5747 else
5748 quality = 0xb0 - status_rid->signalQuality;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005749 return quality;
5750}
5751
5752#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0)
5753#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50);
5754
5755/*------------------------------------------------------------------*/
5756/*
5757 * Wireless Handler : get protocol name
5758 */
5759static int airo_get_name(struct net_device *dev,
5760 struct iw_request_info *info,
5761 char *cwrq,
5762 char *extra)
5763{
5764 strcpy(cwrq, "IEEE 802.11-DS");
5765 return 0;
5766}
5767
5768/*------------------------------------------------------------------*/
5769/*
5770 * Wireless Handler : set frequency
5771 */
5772static int airo_set_freq(struct net_device *dev,
5773 struct iw_request_info *info,
5774 struct iw_freq *fwrq,
5775 char *extra)
5776{
5777 struct airo_info *local = dev->priv;
5778 int rc = -EINPROGRESS; /* Call commit handler */
5779
5780 /* If setting by frequency, convert to a channel */
5781 if((fwrq->e == 1) &&
5782 (fwrq->m >= (int) 2.412e8) &&
5783 (fwrq->m <= (int) 2.487e8)) {
5784 int f = fwrq->m / 100000;
5785 int c = 0;
5786 while((c < 14) && (f != frequency_list[c]))
5787 c++;
5788 /* Hack to fall through... */
5789 fwrq->e = 0;
5790 fwrq->m = c + 1;
5791 }
5792 /* Setting by channel number */
5793 if((fwrq->m > 1000) || (fwrq->e > 0))
5794 rc = -EOPNOTSUPP;
5795 else {
5796 int channel = fwrq->m;
5797 /* We should do a better check than that,
5798 * based on the card capability !!! */
Javier Achirica2610c732006-01-17 08:01:01 -05005799 if((channel < 1) || (channel > 14)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05005800 airo_print_dbg(dev->name, "New channel value of %d is invalid!",
5801 fwrq->m);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005802 rc = -EINVAL;
5803 } else {
5804 readConfigRid(local, 1);
5805 /* Yes ! We can set it !!! */
Javier Achirica2610c732006-01-17 08:01:01 -05005806 local->config.channelSet = (u16) channel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005807 set_bit (FLAG_COMMIT, &local->flags);
5808 }
5809 }
5810 return rc;
5811}
5812
5813/*------------------------------------------------------------------*/
5814/*
5815 * Wireless Handler : get frequency
5816 */
5817static int airo_get_freq(struct net_device *dev,
5818 struct iw_request_info *info,
5819 struct iw_freq *fwrq,
5820 char *extra)
5821{
5822 struct airo_info *local = dev->priv;
5823 StatusRid status_rid; /* Card status info */
Javier Achirica2610c732006-01-17 08:01:01 -05005824 int ch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005825
5826 readConfigRid(local, 1);
5827 if ((local->config.opmode & 0xFF) == MODE_STA_ESS)
5828 status_rid.channel = local->config.channelSet;
5829 else
5830 readStatusRid(local, &status_rid, 1);
5831
Javier Achirica2610c732006-01-17 08:01:01 -05005832 ch = (int)status_rid.channel;
5833 if((ch > 0) && (ch < 15)) {
5834 fwrq->m = frequency_list[ch - 1] * 100000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005835 fwrq->e = 1;
Javier Achirica2610c732006-01-17 08:01:01 -05005836 } else {
5837 fwrq->m = ch;
5838 fwrq->e = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005839 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005840
5841 return 0;
5842}
5843
5844/*------------------------------------------------------------------*/
5845/*
5846 * Wireless Handler : set ESSID
5847 */
5848static int airo_set_essid(struct net_device *dev,
5849 struct iw_request_info *info,
5850 struct iw_point *dwrq,
5851 char *extra)
5852{
5853 struct airo_info *local = dev->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005854 SsidRid SSID_rid; /* SSIDs */
5855
5856 /* Reload the list of current SSID */
5857 readSsidRid(local, &SSID_rid);
5858
5859 /* Check if we asked for `any' */
5860 if(dwrq->flags == 0) {
5861 /* Just send an empty SSID list */
5862 memset(&SSID_rid, 0, sizeof(SSID_rid));
5863 } else {
5864 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
5865
5866 /* Check the size of the string */
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07005867 if(dwrq->length > IW_ESSID_MAX_SIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005868 return -E2BIG ;
5869 }
5870 /* Check if index is valid */
5871 if((index < 0) || (index >= 4)) {
5872 return -EINVAL;
5873 }
5874
5875 /* Set the SSID */
5876 memset(SSID_rid.ssids[index].ssid, 0,
5877 sizeof(SSID_rid.ssids[index].ssid));
5878 memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length);
Al Viro0dd22122007-12-17 16:11:57 -05005879 SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005880 }
Al Viro0dd22122007-12-17 16:11:57 -05005881 SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005882 /* Write it to the card */
5883 disable_MAC(local, 1);
5884 writeSsidRid(local, &SSID_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005885 enable_MAC(local, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005886
5887 return 0;
5888}
5889
5890/*------------------------------------------------------------------*/
5891/*
5892 * Wireless Handler : get ESSID
5893 */
5894static int airo_get_essid(struct net_device *dev,
5895 struct iw_request_info *info,
5896 struct iw_point *dwrq,
5897 char *extra)
5898{
5899 struct airo_info *local = dev->priv;
5900 StatusRid status_rid; /* Card status info */
5901
5902 readStatusRid(local, &status_rid, 1);
5903
5904 /* Note : if dwrq->flags != 0, we should
5905 * get the relevant SSID from the SSID list... */
5906
5907 /* Get the current SSID */
5908 memcpy(extra, status_rid.SSID, status_rid.SSIDlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005909 /* If none, we may want to get the one that was set */
5910
5911 /* Push it out ! */
Dan Williamsd6a13a22006-01-12 15:00:58 -05005912 dwrq->length = status_rid.SSIDlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005913 dwrq->flags = 1; /* active */
5914
5915 return 0;
5916}
5917
5918/*------------------------------------------------------------------*/
5919/*
5920 * Wireless Handler : set AP address
5921 */
5922static int airo_set_wap(struct net_device *dev,
5923 struct iw_request_info *info,
5924 struct sockaddr *awrq,
5925 char *extra)
5926{
5927 struct airo_info *local = dev->priv;
5928 Cmd cmd;
5929 Resp rsp;
5930 APListRid APList_rid;
Dan Williams4be757d2006-01-30 11:58:00 -05005931 static const u8 any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
5932 static const u8 off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07005933
5934 if (awrq->sa_family != ARPHRD_ETHER)
5935 return -EINVAL;
Dan Williams4be757d2006-01-30 11:58:00 -05005936 else if (!memcmp(any, awrq->sa_data, ETH_ALEN) ||
5937 !memcmp(off, awrq->sa_data, ETH_ALEN)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005938 memset(&cmd, 0, sizeof(cmd));
5939 cmd.cmd=CMD_LOSE_SYNC;
5940 if (down_interruptible(&local->sem))
5941 return -ERESTARTSYS;
5942 issuecommand(local, &cmd, &rsp);
5943 up(&local->sem);
5944 } else {
5945 memset(&APList_rid, 0, sizeof(APList_rid));
5946 APList_rid.len = sizeof(APList_rid);
5947 memcpy(APList_rid.ap[0], awrq->sa_data, ETH_ALEN);
5948 disable_MAC(local, 1);
5949 writeAPListRid(local, &APList_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005950 enable_MAC(local, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005951 }
5952 return 0;
5953}
5954
5955/*------------------------------------------------------------------*/
5956/*
5957 * Wireless Handler : get AP address
5958 */
5959static int airo_get_wap(struct net_device *dev,
5960 struct iw_request_info *info,
5961 struct sockaddr *awrq,
5962 char *extra)
5963{
5964 struct airo_info *local = dev->priv;
5965 StatusRid status_rid; /* Card status info */
5966
5967 readStatusRid(local, &status_rid, 1);
5968
5969 /* Tentative. This seems to work, wow, I'm lucky !!! */
5970 memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN);
5971 awrq->sa_family = ARPHRD_ETHER;
5972
5973 return 0;
5974}
5975
5976/*------------------------------------------------------------------*/
5977/*
5978 * Wireless Handler : set Nickname
5979 */
5980static int airo_set_nick(struct net_device *dev,
5981 struct iw_request_info *info,
5982 struct iw_point *dwrq,
5983 char *extra)
5984{
5985 struct airo_info *local = dev->priv;
5986
5987 /* Check the size of the string */
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07005988 if(dwrq->length > 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005989 return -E2BIG;
5990 }
5991 readConfigRid(local, 1);
5992 memset(local->config.nodeName, 0, sizeof(local->config.nodeName));
5993 memcpy(local->config.nodeName, extra, dwrq->length);
5994 set_bit (FLAG_COMMIT, &local->flags);
5995
5996 return -EINPROGRESS; /* Call commit handler */
5997}
5998
5999/*------------------------------------------------------------------*/
6000/*
6001 * Wireless Handler : get Nickname
6002 */
6003static int airo_get_nick(struct net_device *dev,
6004 struct iw_request_info *info,
6005 struct iw_point *dwrq,
6006 char *extra)
6007{
6008 struct airo_info *local = dev->priv;
6009
6010 readConfigRid(local, 1);
6011 strncpy(extra, local->config.nodeName, 16);
6012 extra[16] = '\0';
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006013 dwrq->length = strlen(extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006014
6015 return 0;
6016}
6017
6018/*------------------------------------------------------------------*/
6019/*
6020 * Wireless Handler : set Bit-Rate
6021 */
6022static int airo_set_rate(struct net_device *dev,
6023 struct iw_request_info *info,
6024 struct iw_param *vwrq,
6025 char *extra)
6026{
6027 struct airo_info *local = dev->priv;
6028 CapabilityRid cap_rid; /* Card capability info */
6029 u8 brate = 0;
6030 int i;
6031
6032 /* First : get a valid bit rate value */
6033 readCapabilityRid(local, &cap_rid, 1);
6034
6035 /* Which type of value ? */
6036 if((vwrq->value < 8) && (vwrq->value >= 0)) {
6037 /* Setting by rate index */
6038 /* Find value in the magic rate table */
6039 brate = cap_rid.supportedRates[vwrq->value];
6040 } else {
6041 /* Setting by frequency value */
6042 u8 normvalue = (u8) (vwrq->value/500000);
6043
6044 /* Check if rate is valid */
6045 for(i = 0 ; i < 8 ; i++) {
6046 if(normvalue == cap_rid.supportedRates[i]) {
6047 brate = normvalue;
6048 break;
6049 }
6050 }
6051 }
6052 /* -1 designed the max rate (mostly auto mode) */
6053 if(vwrq->value == -1) {
6054 /* Get the highest available rate */
6055 for(i = 0 ; i < 8 ; i++) {
6056 if(cap_rid.supportedRates[i] == 0)
6057 break;
6058 }
6059 if(i != 0)
6060 brate = cap_rid.supportedRates[i - 1];
6061 }
6062 /* Check that it is valid */
6063 if(brate == 0) {
6064 return -EINVAL;
6065 }
6066
6067 readConfigRid(local, 1);
6068 /* Now, check if we want a fixed or auto value */
6069 if(vwrq->fixed == 0) {
6070 /* Fill all the rates up to this max rate */
6071 memset(local->config.rates, 0, 8);
6072 for(i = 0 ; i < 8 ; i++) {
6073 local->config.rates[i] = cap_rid.supportedRates[i];
6074 if(local->config.rates[i] == brate)
6075 break;
6076 }
6077 } else {
6078 /* Fixed mode */
6079 /* One rate, fixed */
6080 memset(local->config.rates, 0, 8);
6081 local->config.rates[0] = brate;
6082 }
6083 set_bit (FLAG_COMMIT, &local->flags);
6084
6085 return -EINPROGRESS; /* Call commit handler */
6086}
6087
6088/*------------------------------------------------------------------*/
6089/*
6090 * Wireless Handler : get Bit-Rate
6091 */
6092static int airo_get_rate(struct net_device *dev,
6093 struct iw_request_info *info,
6094 struct iw_param *vwrq,
6095 char *extra)
6096{
6097 struct airo_info *local = dev->priv;
6098 StatusRid status_rid; /* Card status info */
6099
6100 readStatusRid(local, &status_rid, 1);
6101
6102 vwrq->value = status_rid.currentXmitRate * 500000;
6103 /* If more than one rate, set auto */
6104 readConfigRid(local, 1);
6105 vwrq->fixed = (local->config.rates[1] == 0);
6106
6107 return 0;
6108}
6109
6110/*------------------------------------------------------------------*/
6111/*
6112 * Wireless Handler : set RTS threshold
6113 */
6114static int airo_set_rts(struct net_device *dev,
6115 struct iw_request_info *info,
6116 struct iw_param *vwrq,
6117 char *extra)
6118{
6119 struct airo_info *local = dev->priv;
6120 int rthr = vwrq->value;
6121
6122 if(vwrq->disabled)
Dan Williams15db2762006-03-16 13:46:27 -05006123 rthr = AIRO_DEF_MTU;
6124 if((rthr < 0) || (rthr > AIRO_DEF_MTU)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006125 return -EINVAL;
6126 }
6127 readConfigRid(local, 1);
6128 local->config.rtsThres = rthr;
6129 set_bit (FLAG_COMMIT, &local->flags);
6130
6131 return -EINPROGRESS; /* Call commit handler */
6132}
6133
6134/*------------------------------------------------------------------*/
6135/*
6136 * Wireless Handler : get RTS threshold
6137 */
6138static int airo_get_rts(struct net_device *dev,
6139 struct iw_request_info *info,
6140 struct iw_param *vwrq,
6141 char *extra)
6142{
6143 struct airo_info *local = dev->priv;
6144
6145 readConfigRid(local, 1);
6146 vwrq->value = local->config.rtsThres;
Dan Williams15db2762006-03-16 13:46:27 -05006147 vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006148 vwrq->fixed = 1;
6149
6150 return 0;
6151}
6152
6153/*------------------------------------------------------------------*/
6154/*
6155 * Wireless Handler : set Fragmentation threshold
6156 */
6157static int airo_set_frag(struct net_device *dev,
6158 struct iw_request_info *info,
6159 struct iw_param *vwrq,
6160 char *extra)
6161{
6162 struct airo_info *local = dev->priv;
6163 int fthr = vwrq->value;
6164
6165 if(vwrq->disabled)
Dan Williams15db2762006-03-16 13:46:27 -05006166 fthr = AIRO_DEF_MTU;
6167 if((fthr < 256) || (fthr > AIRO_DEF_MTU)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006168 return -EINVAL;
6169 }
6170 fthr &= ~0x1; /* Get an even value - is it really needed ??? */
6171 readConfigRid(local, 1);
6172 local->config.fragThresh = (u16)fthr;
6173 set_bit (FLAG_COMMIT, &local->flags);
6174
6175 return -EINPROGRESS; /* Call commit handler */
6176}
6177
6178/*------------------------------------------------------------------*/
6179/*
6180 * Wireless Handler : get Fragmentation threshold
6181 */
6182static int airo_get_frag(struct net_device *dev,
6183 struct iw_request_info *info,
6184 struct iw_param *vwrq,
6185 char *extra)
6186{
6187 struct airo_info *local = dev->priv;
6188
6189 readConfigRid(local, 1);
6190 vwrq->value = local->config.fragThresh;
Dan Williams15db2762006-03-16 13:46:27 -05006191 vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006192 vwrq->fixed = 1;
6193
6194 return 0;
6195}
6196
6197/*------------------------------------------------------------------*/
6198/*
6199 * Wireless Handler : set Mode of Operation
6200 */
6201static int airo_set_mode(struct net_device *dev,
6202 struct iw_request_info *info,
6203 __u32 *uwrq,
6204 char *extra)
6205{
6206 struct airo_info *local = dev->priv;
6207 int reset = 0;
6208
6209 readConfigRid(local, 1);
6210 if ((local->config.rmode & 0xff) >= RXMODE_RFMON)
6211 reset = 1;
6212
6213 switch(*uwrq) {
6214 case IW_MODE_ADHOC:
6215 local->config.opmode &= 0xFF00;
6216 local->config.opmode |= MODE_STA_IBSS;
6217 local->config.rmode &= 0xfe00;
6218 local->config.scanMode = SCANMODE_ACTIVE;
6219 clear_bit (FLAG_802_11, &local->flags);
6220 break;
6221 case IW_MODE_INFRA:
6222 local->config.opmode &= 0xFF00;
6223 local->config.opmode |= MODE_STA_ESS;
6224 local->config.rmode &= 0xfe00;
6225 local->config.scanMode = SCANMODE_ACTIVE;
6226 clear_bit (FLAG_802_11, &local->flags);
6227 break;
6228 case IW_MODE_MASTER:
6229 local->config.opmode &= 0xFF00;
6230 local->config.opmode |= MODE_AP;
6231 local->config.rmode &= 0xfe00;
6232 local->config.scanMode = SCANMODE_ACTIVE;
6233 clear_bit (FLAG_802_11, &local->flags);
6234 break;
6235 case IW_MODE_REPEAT:
6236 local->config.opmode &= 0xFF00;
6237 local->config.opmode |= MODE_AP_RPTR;
6238 local->config.rmode &= 0xfe00;
6239 local->config.scanMode = SCANMODE_ACTIVE;
6240 clear_bit (FLAG_802_11, &local->flags);
6241 break;
6242 case IW_MODE_MONITOR:
6243 local->config.opmode &= 0xFF00;
6244 local->config.opmode |= MODE_STA_ESS;
6245 local->config.rmode &= 0xfe00;
6246 local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
6247 local->config.scanMode = SCANMODE_PASSIVE;
6248 set_bit (FLAG_802_11, &local->flags);
6249 break;
6250 default:
6251 return -EINVAL;
6252 }
6253 if (reset)
6254 set_bit (FLAG_RESET, &local->flags);
6255 set_bit (FLAG_COMMIT, &local->flags);
6256
6257 return -EINPROGRESS; /* Call commit handler */
6258}
6259
6260/*------------------------------------------------------------------*/
6261/*
6262 * Wireless Handler : get Mode of Operation
6263 */
6264static int airo_get_mode(struct net_device *dev,
6265 struct iw_request_info *info,
6266 __u32 *uwrq,
6267 char *extra)
6268{
6269 struct airo_info *local = dev->priv;
6270
6271 readConfigRid(local, 1);
6272 /* If not managed, assume it's ad-hoc */
6273 switch (local->config.opmode & 0xFF) {
6274 case MODE_STA_ESS:
6275 *uwrq = IW_MODE_INFRA;
6276 break;
6277 case MODE_AP:
6278 *uwrq = IW_MODE_MASTER;
6279 break;
6280 case MODE_AP_RPTR:
6281 *uwrq = IW_MODE_REPEAT;
6282 break;
6283 default:
6284 *uwrq = IW_MODE_ADHOC;
6285 }
6286
6287 return 0;
6288}
6289
Al Viro56d81bd2007-12-20 17:18:35 -05006290static inline int valid_index(CapabilityRid *p, int index)
6291{
6292 if (index < 0)
6293 return 0;
6294 return index < (p->softCap & cpu_to_le16(0x80) ? 4 : 1);
6295}
6296
Linus Torvalds1da177e2005-04-16 15:20:36 -07006297/*------------------------------------------------------------------*/
6298/*
6299 * Wireless Handler : set Encryption Key
6300 */
6301static int airo_set_encode(struct net_device *dev,
6302 struct iw_request_info *info,
6303 struct iw_point *dwrq,
6304 char *extra)
6305{
6306 struct airo_info *local = dev->priv;
6307 CapabilityRid cap_rid; /* Card capability info */
Dan Streetmanf89b2322005-11-11 11:41:42 -05006308 int perm = ( dwrq->flags & IW_ENCODE_TEMP ? 0 : 1 );
6309 u16 currentAuthType = local->config.authType;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006310
6311 /* Is WEP supported ? */
6312 readCapabilityRid(local, &cap_rid, 1);
6313 /* Older firmware doesn't support this...
Al Viro56d81bd2007-12-20 17:18:35 -05006314 if(!(cap_rid.softCap & cpu_to_le16(2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006315 return -EOPNOTSUPP;
6316 } */
6317 readConfigRid(local, 1);
6318
6319 /* Basic checking: do we have a key to set ?
6320 * Note : with the new API, it's impossible to get a NULL pointer.
6321 * Therefore, we need to check a key size == 0 instead.
6322 * New version of iwconfig properly set the IW_ENCODE_NOKEY flag
6323 * when no key is present (only change flags), but older versions
6324 * don't do it. - Jean II */
6325 if (dwrq->length > 0) {
6326 wep_key_t key;
6327 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
6328 int current_index = get_wep_key(local, 0xffff);
6329 /* Check the size of the key */
6330 if (dwrq->length > MAX_KEY_SIZE) {
6331 return -EINVAL;
6332 }
6333 /* Check the index (none -> use current) */
Al Viro56d81bd2007-12-20 17:18:35 -05006334 if (!valid_index(&cap_rid, index))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006335 index = current_index;
6336 /* Set the length */
6337 if (dwrq->length > MIN_KEY_SIZE)
6338 key.len = MAX_KEY_SIZE;
6339 else
6340 if (dwrq->length > 0)
6341 key.len = MIN_KEY_SIZE;
6342 else
6343 /* Disable the key */
6344 key.len = 0;
6345 /* Check if the key is not marked as invalid */
6346 if(!(dwrq->flags & IW_ENCODE_NOKEY)) {
6347 /* Cleanup */
6348 memset(key.key, 0, MAX_KEY_SIZE);
6349 /* Copy the key in the driver */
6350 memcpy(key.key, extra, dwrq->length);
6351 /* Send the key to the card */
Dan Streetmanf89b2322005-11-11 11:41:42 -05006352 set_wep_key(local, index, key.key, key.len, perm, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006353 }
6354 /* WE specify that if a valid key is set, encryption
6355 * should be enabled (user may turn it off later)
6356 * This is also how "iwconfig ethX key on" works */
6357 if((index == current_index) && (key.len > 0) &&
6358 (local->config.authType == AUTH_OPEN)) {
6359 local->config.authType = AUTH_ENCRYPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006360 }
6361 } else {
6362 /* Do we want to just set the transmit key index ? */
6363 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Al Viro56d81bd2007-12-20 17:18:35 -05006364 if (valid_index(&cap_rid, index)) {
Dan Streetmanf89b2322005-11-11 11:41:42 -05006365 set_wep_key(local, index, NULL, 0, perm, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006366 } else
6367 /* Don't complain if only change the mode */
Jeff Garzik93a3b602007-11-23 21:50:20 -05006368 if (!(dwrq->flags & IW_ENCODE_MODE))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006369 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006370 }
6371 /* Read the flags */
6372 if(dwrq->flags & IW_ENCODE_DISABLED)
6373 local->config.authType = AUTH_OPEN; // disable encryption
6374 if(dwrq->flags & IW_ENCODE_RESTRICTED)
6375 local->config.authType = AUTH_SHAREDKEY; // Only Both
6376 if(dwrq->flags & IW_ENCODE_OPEN)
6377 local->config.authType = AUTH_ENCRYPT; // Only Wep
6378 /* Commit the changes to flags if needed */
Dan Streetmanf89b2322005-11-11 11:41:42 -05006379 if (local->config.authType != currentAuthType)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006380 set_bit (FLAG_COMMIT, &local->flags);
6381 return -EINPROGRESS; /* Call commit handler */
6382}
6383
6384/*------------------------------------------------------------------*/
6385/*
6386 * Wireless Handler : get Encryption Key
6387 */
6388static int airo_get_encode(struct net_device *dev,
6389 struct iw_request_info *info,
6390 struct iw_point *dwrq,
6391 char *extra)
6392{
6393 struct airo_info *local = dev->priv;
6394 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
6395 CapabilityRid cap_rid; /* Card capability info */
6396
6397 /* Is it supported ? */
6398 readCapabilityRid(local, &cap_rid, 1);
Al Viro56d81bd2007-12-20 17:18:35 -05006399 if(!(cap_rid.softCap & cpu_to_le16(2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006400 return -EOPNOTSUPP;
6401 }
6402 readConfigRid(local, 1);
6403 /* Check encryption mode */
6404 switch(local->config.authType) {
6405 case AUTH_ENCRYPT:
6406 dwrq->flags = IW_ENCODE_OPEN;
6407 break;
6408 case AUTH_SHAREDKEY:
6409 dwrq->flags = IW_ENCODE_RESTRICTED;
6410 break;
6411 default:
6412 case AUTH_OPEN:
6413 dwrq->flags = IW_ENCODE_DISABLED;
6414 break;
6415 }
6416 /* We can't return the key, so set the proper flag and return zero */
6417 dwrq->flags |= IW_ENCODE_NOKEY;
6418 memset(extra, 0, 16);
6419
6420 /* Which key do we want ? -1 -> tx index */
Al Viro56d81bd2007-12-20 17:18:35 -05006421 if (!valid_index(&cap_rid, index))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006422 index = get_wep_key(local, 0xffff);
6423 dwrq->flags |= index + 1;
6424 /* Copy the key to the user buffer */
6425 dwrq->length = get_wep_key(local, index);
6426 if (dwrq->length > 16) {
6427 dwrq->length=0;
6428 }
6429 return 0;
6430}
6431
6432/*------------------------------------------------------------------*/
6433/*
Dan Williams4be757d2006-01-30 11:58:00 -05006434 * Wireless Handler : set extended Encryption parameters
6435 */
6436static int airo_set_encodeext(struct net_device *dev,
6437 struct iw_request_info *info,
6438 union iwreq_data *wrqu,
6439 char *extra)
6440{
6441 struct airo_info *local = dev->priv;
6442 struct iw_point *encoding = &wrqu->encoding;
6443 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
6444 CapabilityRid cap_rid; /* Card capability info */
6445 int perm = ( encoding->flags & IW_ENCODE_TEMP ? 0 : 1 );
6446 u16 currentAuthType = local->config.authType;
Dan Williams22d88462006-02-05 18:00:30 -05006447 int idx, key_len, alg = ext->alg, set_key = 1;
Dan Williams4be757d2006-01-30 11:58:00 -05006448 wep_key_t key;
6449
6450 /* Is WEP supported ? */
6451 readCapabilityRid(local, &cap_rid, 1);
6452 /* Older firmware doesn't support this...
Al Viro56d81bd2007-12-20 17:18:35 -05006453 if(!(cap_rid.softCap & cpu_to_le16(2))) {
Dan Williams4be757d2006-01-30 11:58:00 -05006454 return -EOPNOTSUPP;
6455 } */
6456 readConfigRid(local, 1);
6457
6458 /* Determine and validate the key index */
6459 idx = encoding->flags & IW_ENCODE_INDEX;
6460 if (idx) {
Al Viro56d81bd2007-12-20 17:18:35 -05006461 if (!valid_index(&cap_rid, idx - 1))
Dan Williams4be757d2006-01-30 11:58:00 -05006462 return -EINVAL;
6463 idx--;
6464 } else
6465 idx = get_wep_key(local, 0xffff);
6466
6467 if (encoding->flags & IW_ENCODE_DISABLED)
6468 alg = IW_ENCODE_ALG_NONE;
6469
Dan Williams4be757d2006-01-30 11:58:00 -05006470 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
Dan Williams22d88462006-02-05 18:00:30 -05006471 /* Only set transmit key index here, actual
6472 * key is set below if needed.
6473 */
Dan Williams4be757d2006-01-30 11:58:00 -05006474 set_wep_key(local, idx, NULL, 0, perm, 1);
Dan Williams22d88462006-02-05 18:00:30 -05006475 set_key = ext->key_len > 0 ? 1 : 0;
6476 }
6477
6478 if (set_key) {
Dan Williams4be757d2006-01-30 11:58:00 -05006479 /* Set the requested key first */
6480 memset(key.key, 0, MAX_KEY_SIZE);
6481 switch (alg) {
6482 case IW_ENCODE_ALG_NONE:
6483 key.len = 0;
6484 break;
6485 case IW_ENCODE_ALG_WEP:
6486 if (ext->key_len > MIN_KEY_SIZE) {
6487 key.len = MAX_KEY_SIZE;
6488 } else if (ext->key_len > 0) {
6489 key.len = MIN_KEY_SIZE;
6490 } else {
6491 return -EINVAL;
6492 }
6493 key_len = min (ext->key_len, key.len);
6494 memcpy(key.key, ext->key, key_len);
6495 break;
6496 default:
6497 return -EINVAL;
6498 }
6499 /* Send the key to the card */
6500 set_wep_key(local, idx, key.key, key.len, perm, 1);
6501 }
6502
6503 /* Read the flags */
6504 if(encoding->flags & IW_ENCODE_DISABLED)
6505 local->config.authType = AUTH_OPEN; // disable encryption
6506 if(encoding->flags & IW_ENCODE_RESTRICTED)
6507 local->config.authType = AUTH_SHAREDKEY; // Only Both
6508 if(encoding->flags & IW_ENCODE_OPEN)
6509 local->config.authType = AUTH_ENCRYPT; // Only Wep
6510 /* Commit the changes to flags if needed */
6511 if (local->config.authType != currentAuthType)
6512 set_bit (FLAG_COMMIT, &local->flags);
6513
6514 return -EINPROGRESS;
6515}
6516
6517
6518/*------------------------------------------------------------------*/
6519/*
6520 * Wireless Handler : get extended Encryption parameters
6521 */
6522static int airo_get_encodeext(struct net_device *dev,
6523 struct iw_request_info *info,
6524 union iwreq_data *wrqu,
6525 char *extra)
6526{
6527 struct airo_info *local = dev->priv;
6528 struct iw_point *encoding = &wrqu->encoding;
6529 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
6530 CapabilityRid cap_rid; /* Card capability info */
6531 int idx, max_key_len;
6532
6533 /* Is it supported ? */
6534 readCapabilityRid(local, &cap_rid, 1);
Al Viro56d81bd2007-12-20 17:18:35 -05006535 if(!(cap_rid.softCap & cpu_to_le16(2))) {
Dan Williams4be757d2006-01-30 11:58:00 -05006536 return -EOPNOTSUPP;
6537 }
6538 readConfigRid(local, 1);
6539
6540 max_key_len = encoding->length - sizeof(*ext);
6541 if (max_key_len < 0)
6542 return -EINVAL;
6543
6544 idx = encoding->flags & IW_ENCODE_INDEX;
6545 if (idx) {
Al Viro56d81bd2007-12-20 17:18:35 -05006546 if (!valid_index(&cap_rid, idx - 1))
Dan Williams4be757d2006-01-30 11:58:00 -05006547 return -EINVAL;
6548 idx--;
6549 } else
6550 idx = get_wep_key(local, 0xffff);
6551
6552 encoding->flags = idx + 1;
6553 memset(ext, 0, sizeof(*ext));
6554
6555 /* Check encryption mode */
6556 switch(local->config.authType) {
6557 case AUTH_ENCRYPT:
6558 encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
6559 break;
6560 case AUTH_SHAREDKEY:
6561 encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
6562 break;
6563 default:
6564 case AUTH_OPEN:
6565 encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED;
6566 break;
6567 }
6568 /* We can't return the key, so set the proper flag and return zero */
6569 encoding->flags |= IW_ENCODE_NOKEY;
6570 memset(extra, 0, 16);
6571
6572 /* Copy the key to the user buffer */
6573 ext->key_len = get_wep_key(local, idx);
6574 if (ext->key_len > 16) {
6575 ext->key_len=0;
6576 }
6577
6578 return 0;
6579}
6580
6581
6582/*------------------------------------------------------------------*/
6583/*
6584 * Wireless Handler : set extended authentication parameters
6585 */
6586static int airo_set_auth(struct net_device *dev,
6587 struct iw_request_info *info,
6588 union iwreq_data *wrqu, char *extra)
6589{
6590 struct airo_info *local = dev->priv;
6591 struct iw_param *param = &wrqu->param;
6592 u16 currentAuthType = local->config.authType;
6593
6594 switch (param->flags & IW_AUTH_INDEX) {
6595 case IW_AUTH_WPA_VERSION:
6596 case IW_AUTH_CIPHER_PAIRWISE:
6597 case IW_AUTH_CIPHER_GROUP:
6598 case IW_AUTH_KEY_MGMT:
6599 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6600 case IW_AUTH_PRIVACY_INVOKED:
6601 /*
6602 * airo does not use these parameters
6603 */
6604 break;
6605
6606 case IW_AUTH_DROP_UNENCRYPTED:
6607 if (param->value) {
6608 /* Only change auth type if unencrypted */
6609 if (currentAuthType == AUTH_OPEN)
6610 local->config.authType = AUTH_ENCRYPT;
6611 } else {
6612 local->config.authType = AUTH_OPEN;
6613 }
6614
6615 /* Commit the changes to flags if needed */
6616 if (local->config.authType != currentAuthType)
6617 set_bit (FLAG_COMMIT, &local->flags);
6618 break;
6619
6620 case IW_AUTH_80211_AUTH_ALG: {
6621 /* FIXME: What about AUTH_OPEN? This API seems to
6622 * disallow setting our auth to AUTH_OPEN.
6623 */
6624 if (param->value & IW_AUTH_ALG_SHARED_KEY) {
6625 local->config.authType = AUTH_SHAREDKEY;
6626 } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
6627 local->config.authType = AUTH_ENCRYPT;
6628 } else
6629 return -EINVAL;
6630 break;
6631
6632 /* Commit the changes to flags if needed */
6633 if (local->config.authType != currentAuthType)
6634 set_bit (FLAG_COMMIT, &local->flags);
6635 }
6636
6637 case IW_AUTH_WPA_ENABLED:
6638 /* Silently accept disable of WPA */
6639 if (param->value > 0)
6640 return -EOPNOTSUPP;
6641 break;
6642
6643 default:
6644 return -EOPNOTSUPP;
6645 }
6646 return -EINPROGRESS;
6647}
6648
6649
6650/*------------------------------------------------------------------*/
6651/*
6652 * Wireless Handler : get extended authentication parameters
6653 */
6654static int airo_get_auth(struct net_device *dev,
6655 struct iw_request_info *info,
6656 union iwreq_data *wrqu, char *extra)
6657{
6658 struct airo_info *local = dev->priv;
6659 struct iw_param *param = &wrqu->param;
6660 u16 currentAuthType = local->config.authType;
6661
6662 switch (param->flags & IW_AUTH_INDEX) {
6663 case IW_AUTH_DROP_UNENCRYPTED:
6664 switch (currentAuthType) {
6665 case AUTH_SHAREDKEY:
6666 case AUTH_ENCRYPT:
6667 param->value = 1;
6668 break;
6669 default:
6670 param->value = 0;
6671 break;
6672 }
6673 break;
6674
6675 case IW_AUTH_80211_AUTH_ALG:
6676 switch (currentAuthType) {
6677 case AUTH_SHAREDKEY:
6678 param->value = IW_AUTH_ALG_SHARED_KEY;
6679 break;
6680 case AUTH_ENCRYPT:
6681 default:
6682 param->value = IW_AUTH_ALG_OPEN_SYSTEM;
6683 break;
6684 }
6685 break;
6686
6687 case IW_AUTH_WPA_ENABLED:
6688 param->value = 0;
6689 break;
6690
6691 default:
6692 return -EOPNOTSUPP;
6693 }
6694 return 0;
6695}
6696
6697
6698/*------------------------------------------------------------------*/
6699/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07006700 * Wireless Handler : set Tx-Power
6701 */
6702static int airo_set_txpow(struct net_device *dev,
6703 struct iw_request_info *info,
6704 struct iw_param *vwrq,
6705 char *extra)
6706{
6707 struct airo_info *local = dev->priv;
6708 CapabilityRid cap_rid; /* Card capability info */
6709 int i;
6710 int rc = -EINVAL;
6711
6712 readCapabilityRid(local, &cap_rid, 1);
6713
6714 if (vwrq->disabled) {
6715 set_bit (FLAG_RADIO_OFF, &local->flags);
6716 set_bit (FLAG_COMMIT, &local->flags);
6717 return -EINPROGRESS; /* Call commit handler */
6718 }
6719 if (vwrq->flags != IW_TXPOW_MWATT) {
6720 return -EINVAL;
6721 }
6722 clear_bit (FLAG_RADIO_OFF, &local->flags);
6723 for (i = 0; cap_rid.txPowerLevels[i] && (i < 8); i++)
Al Viro56d81bd2007-12-20 17:18:35 -05006724 if (vwrq->value == le16_to_cpu(cap_rid.txPowerLevels[i])) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006725 readConfigRid(local, 1);
6726 local->config.txPower = vwrq->value;
6727 set_bit (FLAG_COMMIT, &local->flags);
6728 rc = -EINPROGRESS; /* Call commit handler */
6729 break;
6730 }
6731 return rc;
6732}
6733
6734/*------------------------------------------------------------------*/
6735/*
6736 * Wireless Handler : get Tx-Power
6737 */
6738static int airo_get_txpow(struct net_device *dev,
6739 struct iw_request_info *info,
6740 struct iw_param *vwrq,
6741 char *extra)
6742{
6743 struct airo_info *local = dev->priv;
6744
6745 readConfigRid(local, 1);
6746 vwrq->value = local->config.txPower;
6747 vwrq->fixed = 1; /* No power control */
6748 vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags);
6749 vwrq->flags = IW_TXPOW_MWATT;
6750
6751 return 0;
6752}
6753
6754/*------------------------------------------------------------------*/
6755/*
6756 * Wireless Handler : set Retry limits
6757 */
6758static int airo_set_retry(struct net_device *dev,
6759 struct iw_request_info *info,
6760 struct iw_param *vwrq,
6761 char *extra)
6762{
6763 struct airo_info *local = dev->priv;
6764 int rc = -EINVAL;
6765
6766 if(vwrq->disabled) {
6767 return -EINVAL;
6768 }
6769 readConfigRid(local, 1);
6770 if(vwrq->flags & IW_RETRY_LIMIT) {
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006771 if(vwrq->flags & IW_RETRY_LONG)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006772 local->config.longRetryLimit = vwrq->value;
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006773 else if (vwrq->flags & IW_RETRY_SHORT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006774 local->config.shortRetryLimit = vwrq->value;
6775 else {
6776 /* No modifier : set both */
6777 local->config.longRetryLimit = vwrq->value;
6778 local->config.shortRetryLimit = vwrq->value;
6779 }
6780 set_bit (FLAG_COMMIT, &local->flags);
6781 rc = -EINPROGRESS; /* Call commit handler */
6782 }
6783 if(vwrq->flags & IW_RETRY_LIFETIME) {
6784 local->config.txLifetime = vwrq->value / 1024;
6785 set_bit (FLAG_COMMIT, &local->flags);
6786 rc = -EINPROGRESS; /* Call commit handler */
6787 }
6788 return rc;
6789}
6790
6791/*------------------------------------------------------------------*/
6792/*
6793 * Wireless Handler : get Retry limits
6794 */
6795static int airo_get_retry(struct net_device *dev,
6796 struct iw_request_info *info,
6797 struct iw_param *vwrq,
6798 char *extra)
6799{
6800 struct airo_info *local = dev->priv;
6801
6802 vwrq->disabled = 0; /* Can't be disabled */
6803
6804 readConfigRid(local, 1);
6805 /* Note : by default, display the min retry number */
6806 if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
6807 vwrq->flags = IW_RETRY_LIFETIME;
6808 vwrq->value = (int)local->config.txLifetime * 1024;
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006809 } else if((vwrq->flags & IW_RETRY_LONG)) {
6810 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006811 vwrq->value = (int)local->config.longRetryLimit;
6812 } else {
6813 vwrq->flags = IW_RETRY_LIMIT;
6814 vwrq->value = (int)local->config.shortRetryLimit;
6815 if((int)local->config.shortRetryLimit != (int)local->config.longRetryLimit)
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006816 vwrq->flags |= IW_RETRY_SHORT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006817 }
6818
6819 return 0;
6820}
6821
6822/*------------------------------------------------------------------*/
6823/*
6824 * Wireless Handler : get range info
6825 */
6826static int airo_get_range(struct net_device *dev,
6827 struct iw_request_info *info,
6828 struct iw_point *dwrq,
6829 char *extra)
6830{
6831 struct airo_info *local = dev->priv;
6832 struct iw_range *range = (struct iw_range *) extra;
6833 CapabilityRid cap_rid; /* Card capability info */
6834 int i;
6835 int k;
6836
6837 readCapabilityRid(local, &cap_rid, 1);
6838
6839 dwrq->length = sizeof(struct iw_range);
6840 memset(range, 0, sizeof(*range));
6841 range->min_nwid = 0x0000;
6842 range->max_nwid = 0x0000;
6843 range->num_channels = 14;
6844 /* Should be based on cap_rid.country to give only
6845 * what the current card support */
6846 k = 0;
6847 for(i = 0; i < 14; i++) {
6848 range->freq[k].i = i + 1; /* List index */
6849 range->freq[k].m = frequency_list[i] * 100000;
6850 range->freq[k++].e = 1; /* Values in table in MHz -> * 10^5 * 10 */
6851 }
6852 range->num_frequency = k;
6853
Linus Torvalds1da177e2005-04-16 15:20:36 -07006854 range->sensitivity = 65535;
6855
Dan Williams41480af2005-05-10 09:45:51 -04006856 /* Hum... Should put the right values there */
6857 if (local->rssi)
6858 range->max_qual.qual = 100; /* % */
6859 else
6860 range->max_qual.qual = airo_get_max_quality(&cap_rid);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006861 range->max_qual.level = 0x100 - 120; /* -120 dBm */
6862 range->max_qual.noise = 0x100 - 120; /* -120 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006863
6864 /* Experimental measurements - boundary 11/5.5 Mb/s */
6865 /* Note : with or without the (local->rssi), results
6866 * are somewhat different. - Jean II */
6867 if (local->rssi) {
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006868 range->avg_qual.qual = 50; /* % */
6869 range->avg_qual.level = 0x100 - 70; /* -70 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006870 } else {
6871 range->avg_qual.qual = airo_get_avg_quality(&cap_rid);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006872 range->avg_qual.level = 0x100 - 80; /* -80 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006873 }
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006874 range->avg_qual.noise = 0x100 - 85; /* -85 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006875
Linus Torvalds1da177e2005-04-16 15:20:36 -07006876 for(i = 0 ; i < 8 ; i++) {
6877 range->bitrate[i] = cap_rid.supportedRates[i] * 500000;
6878 if(range->bitrate[i] == 0)
6879 break;
6880 }
6881 range->num_bitrates = i;
6882
6883 /* Set an indication of the max TCP throughput
6884 * in bit/s that we can expect using this interface.
6885 * May be use for QoS stuff... Jean II */
6886 if(i > 2)
6887 range->throughput = 5000 * 1000;
6888 else
6889 range->throughput = 1500 * 1000;
6890
6891 range->min_rts = 0;
Dan Williams15db2762006-03-16 13:46:27 -05006892 range->max_rts = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006893 range->min_frag = 256;
Dan Williams15db2762006-03-16 13:46:27 -05006894 range->max_frag = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006895
Al Viro56d81bd2007-12-20 17:18:35 -05006896 if(cap_rid.softCap & cpu_to_le16(2)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006897 // WEP: RC4 40 bits
6898 range->encoding_size[0] = 5;
6899 // RC4 ~128 bits
Al Viro56d81bd2007-12-20 17:18:35 -05006900 if (cap_rid.softCap & cpu_to_le16(0x100)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006901 range->encoding_size[1] = 13;
6902 range->num_encoding_sizes = 2;
6903 } else
6904 range->num_encoding_sizes = 1;
Al Viro56d81bd2007-12-20 17:18:35 -05006905 range->max_encoding_tokens =
6906 cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006907 } else {
6908 range->num_encoding_sizes = 0;
6909 range->max_encoding_tokens = 0;
6910 }
6911 range->min_pmp = 0;
6912 range->max_pmp = 5000000; /* 5 secs */
6913 range->min_pmt = 0;
6914 range->max_pmt = 65535 * 1024; /* ??? */
6915 range->pmp_flags = IW_POWER_PERIOD;
6916 range->pmt_flags = IW_POWER_TIMEOUT;
6917 range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
6918
6919 /* Transmit Power - values are in mW */
6920 for(i = 0 ; i < 8 ; i++) {
Al Viro56d81bd2007-12-20 17:18:35 -05006921 range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006922 if(range->txpower[i] == 0)
6923 break;
6924 }
6925 range->num_txpower = i;
6926 range->txpower_capa = IW_TXPOW_MWATT;
Dan Williams3c304952006-04-15 12:26:18 -04006927 range->we_version_source = 19;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006928 range->we_version_compiled = WIRELESS_EXT;
6929 range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
6930 range->retry_flags = IW_RETRY_LIMIT;
6931 range->r_time_flags = IW_RETRY_LIFETIME;
6932 range->min_retry = 1;
6933 range->max_retry = 65535;
6934 range->min_r_time = 1024;
6935 range->max_r_time = 65535 * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006936
6937 /* Event capability (kernel + driver) */
6938 range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
6939 IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
6940 IW_EVENT_CAPA_MASK(SIOCGIWAP) |
6941 IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
6942 range->event_capa[1] = IW_EVENT_CAPA_K_1;
6943 range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP);
6944 return 0;
6945}
6946
6947/*------------------------------------------------------------------*/
6948/*
6949 * Wireless Handler : set Power Management
6950 */
6951static int airo_set_power(struct net_device *dev,
6952 struct iw_request_info *info,
6953 struct iw_param *vwrq,
6954 char *extra)
6955{
6956 struct airo_info *local = dev->priv;
6957
6958 readConfigRid(local, 1);
6959 if (vwrq->disabled) {
6960 if ((local->config.rmode & 0xFF) >= RXMODE_RFMON) {
6961 return -EINVAL;
6962 }
6963 local->config.powerSaveMode = POWERSAVE_CAM;
6964 local->config.rmode &= 0xFF00;
6965 local->config.rmode |= RXMODE_BC_MC_ADDR;
6966 set_bit (FLAG_COMMIT, &local->flags);
6967 return -EINPROGRESS; /* Call commit handler */
6968 }
6969 if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
6970 local->config.fastListenDelay = (vwrq->value + 500) / 1024;
6971 local->config.powerSaveMode = POWERSAVE_PSPCAM;
6972 set_bit (FLAG_COMMIT, &local->flags);
6973 } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
6974 local->config.fastListenInterval = local->config.listenInterval = (vwrq->value + 500) / 1024;
6975 local->config.powerSaveMode = POWERSAVE_PSPCAM;
6976 set_bit (FLAG_COMMIT, &local->flags);
6977 }
6978 switch (vwrq->flags & IW_POWER_MODE) {
6979 case IW_POWER_UNICAST_R:
6980 if ((local->config.rmode & 0xFF) >= RXMODE_RFMON) {
6981 return -EINVAL;
6982 }
6983 local->config.rmode &= 0xFF00;
6984 local->config.rmode |= RXMODE_ADDR;
6985 set_bit (FLAG_COMMIT, &local->flags);
6986 break;
6987 case IW_POWER_ALL_R:
6988 if ((local->config.rmode & 0xFF) >= RXMODE_RFMON) {
6989 return -EINVAL;
6990 }
6991 local->config.rmode &= 0xFF00;
6992 local->config.rmode |= RXMODE_BC_MC_ADDR;
6993 set_bit (FLAG_COMMIT, &local->flags);
6994 case IW_POWER_ON:
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006995 /* This is broken, fixme ;-) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006996 break;
6997 default:
6998 return -EINVAL;
6999 }
7000 // Note : we may want to factor local->need_commit here
7001 // Note2 : may also want to factor RXMODE_RFMON test
7002 return -EINPROGRESS; /* Call commit handler */
7003}
7004
7005/*------------------------------------------------------------------*/
7006/*
7007 * Wireless Handler : get Power Management
7008 */
7009static int airo_get_power(struct net_device *dev,
7010 struct iw_request_info *info,
7011 struct iw_param *vwrq,
7012 char *extra)
7013{
7014 struct airo_info *local = dev->priv;
7015 int mode;
7016
7017 readConfigRid(local, 1);
7018 mode = local->config.powerSaveMode;
7019 if ((vwrq->disabled = (mode == POWERSAVE_CAM)))
7020 return 0;
7021 if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
7022 vwrq->value = (int)local->config.fastListenDelay * 1024;
7023 vwrq->flags = IW_POWER_TIMEOUT;
7024 } else {
7025 vwrq->value = (int)local->config.fastListenInterval * 1024;
7026 vwrq->flags = IW_POWER_PERIOD;
7027 }
7028 if ((local->config.rmode & 0xFF) == RXMODE_ADDR)
7029 vwrq->flags |= IW_POWER_UNICAST_R;
7030 else
7031 vwrq->flags |= IW_POWER_ALL_R;
7032
7033 return 0;
7034}
7035
7036/*------------------------------------------------------------------*/
7037/*
7038 * Wireless Handler : set Sensitivity
7039 */
7040static int airo_set_sens(struct net_device *dev,
7041 struct iw_request_info *info,
7042 struct iw_param *vwrq,
7043 char *extra)
7044{
7045 struct airo_info *local = dev->priv;
7046
7047 readConfigRid(local, 1);
7048 local->config.rssiThreshold = vwrq->disabled ? RSSI_DEFAULT : vwrq->value;
7049 set_bit (FLAG_COMMIT, &local->flags);
7050
7051 return -EINPROGRESS; /* Call commit handler */
7052}
7053
7054/*------------------------------------------------------------------*/
7055/*
7056 * Wireless Handler : get Sensitivity
7057 */
7058static int airo_get_sens(struct net_device *dev,
7059 struct iw_request_info *info,
7060 struct iw_param *vwrq,
7061 char *extra)
7062{
7063 struct airo_info *local = dev->priv;
7064
7065 readConfigRid(local, 1);
7066 vwrq->value = local->config.rssiThreshold;
7067 vwrq->disabled = (vwrq->value == 0);
7068 vwrq->fixed = 1;
7069
7070 return 0;
7071}
7072
7073/*------------------------------------------------------------------*/
7074/*
7075 * Wireless Handler : get AP List
7076 * Note : this is deprecated in favor of IWSCAN
7077 */
7078static int airo_get_aplist(struct net_device *dev,
7079 struct iw_request_info *info,
7080 struct iw_point *dwrq,
7081 char *extra)
7082{
7083 struct airo_info *local = dev->priv;
7084 struct sockaddr *address = (struct sockaddr *) extra;
7085 struct iw_quality qual[IW_MAX_AP];
7086 BSSListRid BSSList;
7087 int i;
7088 int loseSync = capable(CAP_NET_ADMIN) ? 1: -1;
7089
7090 for (i = 0; i < IW_MAX_AP; i++) {
Al Viro17e70492007-12-19 18:56:37 -05007091 u16 dBm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007092 if (readBSSListRid(local, loseSync, &BSSList))
7093 break;
7094 loseSync = 0;
7095 memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN);
7096 address[i].sa_family = ARPHRD_ETHER;
Al Viro17e70492007-12-19 18:56:37 -05007097 dBm = le16_to_cpu(BSSList.dBm);
Dan Williams41480af2005-05-10 09:45:51 -04007098 if (local->rssi) {
Al Viro17e70492007-12-19 18:56:37 -05007099 qual[i].level = 0x100 - dBm;
7100 qual[i].qual = airo_dbm_to_pct(local->rssi, dBm);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007101 qual[i].updated = IW_QUAL_QUAL_UPDATED
7102 | IW_QUAL_LEVEL_UPDATED
7103 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007104 } else {
Al Viro17e70492007-12-19 18:56:37 -05007105 qual[i].level = (dBm + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007106 qual[i].qual = 0;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007107 qual[i].updated = IW_QUAL_QUAL_INVALID
7108 | IW_QUAL_LEVEL_UPDATED
7109 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007110 }
7111 qual[i].noise = local->wstats.qual.noise;
Al Viro17e70492007-12-19 18:56:37 -05007112 if (BSSList.index == cpu_to_le16(0xffff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007113 break;
7114 }
7115 if (!i) {
7116 StatusRid status_rid; /* Card status info */
7117 readStatusRid(local, &status_rid, 1);
7118 for (i = 0;
7119 i < min(IW_MAX_AP, 4) &&
7120 (status_rid.bssid[i][0]
7121 & status_rid.bssid[i][1]
7122 & status_rid.bssid[i][2]
7123 & status_rid.bssid[i][3]
7124 & status_rid.bssid[i][4]
7125 & status_rid.bssid[i][5])!=0xff &&
7126 (status_rid.bssid[i][0]
7127 | status_rid.bssid[i][1]
7128 | status_rid.bssid[i][2]
7129 | status_rid.bssid[i][3]
7130 | status_rid.bssid[i][4]
7131 | status_rid.bssid[i][5]);
7132 i++) {
7133 memcpy(address[i].sa_data,
7134 status_rid.bssid[i], ETH_ALEN);
7135 address[i].sa_family = ARPHRD_ETHER;
7136 }
7137 } else {
7138 dwrq->flags = 1; /* Should be define'd */
7139 memcpy(extra + sizeof(struct sockaddr)*i,
7140 &qual, sizeof(struct iw_quality)*i);
7141 }
7142 dwrq->length = i;
7143
7144 return 0;
7145}
7146
7147/*------------------------------------------------------------------*/
7148/*
7149 * Wireless Handler : Initiate Scan
7150 */
7151static int airo_set_scan(struct net_device *dev,
7152 struct iw_request_info *info,
7153 struct iw_param *vwrq,
7154 char *extra)
7155{
7156 struct airo_info *ai = dev->priv;
7157 Cmd cmd;
7158 Resp rsp;
Dan Williams9e75af32006-03-16 13:46:29 -05007159 int wake = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007160
7161 /* Note : you may have realised that, as this is a SET operation,
7162 * this is privileged and therefore a normal user can't
7163 * perform scanning.
7164 * This is not an error, while the device perform scanning,
7165 * traffic doesn't flow, so it's a perfect DoS...
7166 * Jean II */
7167 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
7168
Dan Williams9e75af32006-03-16 13:46:29 -05007169 if (down_interruptible(&ai->sem))
7170 return -ERESTARTSYS;
7171
7172 /* If there's already a scan in progress, don't
7173 * trigger another one. */
7174 if (ai->scan_timeout > 0)
7175 goto out;
7176
Linus Torvalds1da177e2005-04-16 15:20:36 -07007177 /* Initiate a scan command */
Dan Williams6fcdf562006-03-31 15:08:46 -05007178 ai->scan_timeout = RUN_AT(3*HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007179 memset(&cmd, 0, sizeof(cmd));
7180 cmd.cmd=CMD_LISTBSS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007181 issuecommand(ai, &cmd, &rsp);
Dan Williams9e75af32006-03-16 13:46:29 -05007182 wake = 1;
7183
7184out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07007185 up(&ai->sem);
Dan Williams9e75af32006-03-16 13:46:29 -05007186 if (wake)
7187 wake_up_interruptible(&ai->thr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007188 return 0;
7189}
7190
7191/*------------------------------------------------------------------*/
7192/*
7193 * Translate scan data returned from the card to a card independent
7194 * format that the Wireless Tools will understand - Jean II
7195 */
7196static inline char *airo_translate_scan(struct net_device *dev,
7197 char *current_ev,
7198 char *end_buf,
Dan Williams41480af2005-05-10 09:45:51 -04007199 BSSListRid *bss)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007200{
7201 struct airo_info *ai = dev->priv;
7202 struct iw_event iwe; /* Temporary buffer */
Al Viro17e70492007-12-19 18:56:37 -05007203 __le16 capabilities;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007204 char * current_val; /* For rates */
7205 int i;
Dan Williams3c304952006-04-15 12:26:18 -04007206 char * buf;
Al Viro851b3e52007-12-19 19:20:12 -05007207 u16 dBm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007208
7209 /* First entry *MUST* be the AP MAC address */
7210 iwe.cmd = SIOCGIWAP;
7211 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
Dan Williams41480af2005-05-10 09:45:51 -04007212 memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007213 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
7214
7215 /* Other entries will be displayed in the order we give them */
7216
7217 /* Add the ESSID */
Dan Williams41480af2005-05-10 09:45:51 -04007218 iwe.u.data.length = bss->ssidLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007219 if(iwe.u.data.length > 32)
7220 iwe.u.data.length = 32;
7221 iwe.cmd = SIOCGIWESSID;
7222 iwe.u.data.flags = 1;
Dan Williams41480af2005-05-10 09:45:51 -04007223 current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007224
7225 /* Add mode */
7226 iwe.cmd = SIOCGIWMODE;
Al Viro17e70492007-12-19 18:56:37 -05007227 capabilities = bss->cap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007228 if(capabilities & (CAP_ESS | CAP_IBSS)) {
7229 if(capabilities & CAP_ESS)
7230 iwe.u.mode = IW_MODE_MASTER;
7231 else
7232 iwe.u.mode = IW_MODE_ADHOC;
7233 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN);
7234 }
7235
7236 /* Add frequency */
7237 iwe.cmd = SIOCGIWFREQ;
Dan Williams41480af2005-05-10 09:45:51 -04007238 iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
matthieu castet11414552005-09-12 23:31:39 +02007239 /* iwe.u.freq.m containt the channel (starting 1), our
7240 * frequency_list array start at index 0...
7241 */
7242 iwe.u.freq.m = frequency_list[iwe.u.freq.m - 1] * 100000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007243 iwe.u.freq.e = 1;
7244 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
7245
Al Viro851b3e52007-12-19 19:20:12 -05007246 dBm = le16_to_cpu(bss->dBm);
7247
Linus Torvalds1da177e2005-04-16 15:20:36 -07007248 /* Add quality statistics */
7249 iwe.cmd = IWEVQUAL;
Dan Williams41480af2005-05-10 09:45:51 -04007250 if (ai->rssi) {
Al Viro851b3e52007-12-19 19:20:12 -05007251 iwe.u.qual.level = 0x100 - dBm;
7252 iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007253 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
7254 | IW_QUAL_LEVEL_UPDATED
7255 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007256 } else {
Al Viro851b3e52007-12-19 19:20:12 -05007257 iwe.u.qual.level = (dBm + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007258 iwe.u.qual.qual = 0;
Jeff Garzikbbeec902005-09-07 00:27:54 -04007259 iwe.u.qual.updated = IW_QUAL_QUAL_INVALID
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007260 | IW_QUAL_LEVEL_UPDATED
7261 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007262 }
7263 iwe.u.qual.noise = ai->wstats.qual.noise;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007264 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
7265
7266 /* Add encryption capability */
7267 iwe.cmd = SIOCGIWENCODE;
7268 if(capabilities & CAP_PRIVACY)
7269 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
7270 else
7271 iwe.u.data.flags = IW_ENCODE_DISABLED;
7272 iwe.u.data.length = 0;
Dan Williams41480af2005-05-10 09:45:51 -04007273 current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007274
7275 /* Rate : stuffing multiple values in a single event require a bit
7276 * more of magic - Jean II */
7277 current_val = current_ev + IW_EV_LCP_LEN;
7278
7279 iwe.cmd = SIOCGIWRATE;
7280 /* Those two flags are ignored... */
7281 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
7282 /* Max 8 values */
7283 for(i = 0 ; i < 8 ; i++) {
7284 /* NULL terminated */
Dan Williams41480af2005-05-10 09:45:51 -04007285 if(bss->rates[i] == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007286 break;
7287 /* Bit rate given in 500 kb/s units (+ 0x80) */
Dan Williams41480af2005-05-10 09:45:51 -04007288 iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007289 /* Add new value to event */
7290 current_val = iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN);
7291 }
7292 /* Check if we added any event */
7293 if((current_val - current_ev) > IW_EV_LCP_LEN)
7294 current_ev = current_val;
7295
Dan Williams3c304952006-04-15 12:26:18 -04007296 /* Beacon interval */
7297 buf = kmalloc(30, GFP_KERNEL);
7298 if (buf) {
7299 iwe.cmd = IWEVCUSTOM;
7300 sprintf(buf, "bcn_int=%d", bss->beaconInterval);
7301 iwe.u.data.length = strlen(buf);
7302 current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf);
7303 kfree(buf);
7304 }
7305
7306 /* Put WPA/RSN Information Elements into the event stream */
7307 if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
7308 unsigned int num_null_ies = 0;
7309 u16 length = sizeof (bss->extra.iep);
7310 struct ieee80211_info_element *info_element =
7311 (struct ieee80211_info_element *) &bss->extra.iep;
7312
7313 while ((length >= sizeof(*info_element)) && (num_null_ies < 2)) {
7314 if (sizeof(*info_element) + info_element->len > length) {
7315 /* Invalid element, don't continue parsing IE */
7316 break;
7317 }
7318
7319 switch (info_element->id) {
7320 case MFIE_TYPE_SSID:
7321 /* Two zero-length SSID elements
7322 * mean we're done parsing elements */
7323 if (!info_element->len)
7324 num_null_ies++;
7325 break;
7326
7327 case MFIE_TYPE_GENERIC:
7328 if (info_element->len >= 4 &&
7329 info_element->data[0] == 0x00 &&
7330 info_element->data[1] == 0x50 &&
7331 info_element->data[2] == 0xf2 &&
7332 info_element->data[3] == 0x01) {
7333 iwe.cmd = IWEVGENIE;
7334 iwe.u.data.length = min(info_element->len + 2,
7335 MAX_WPA_IE_LEN);
7336 current_ev = iwe_stream_add_point(current_ev, end_buf,
7337 &iwe, (char *) info_element);
7338 }
7339 break;
7340
7341 case MFIE_TYPE_RSN:
7342 iwe.cmd = IWEVGENIE;
7343 iwe.u.data.length = min(info_element->len + 2,
7344 MAX_WPA_IE_LEN);
7345 current_ev = iwe_stream_add_point(current_ev, end_buf,
7346 &iwe, (char *) info_element);
7347 break;
7348
7349 default:
7350 break;
7351 }
7352
7353 length -= sizeof(*info_element) + info_element->len;
7354 info_element =
7355 (struct ieee80211_info_element *)&info_element->
7356 data[info_element->len];
7357 }
7358 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07007359 return current_ev;
7360}
7361
7362/*------------------------------------------------------------------*/
7363/*
7364 * Wireless Handler : Read Scan Results
7365 */
7366static int airo_get_scan(struct net_device *dev,
7367 struct iw_request_info *info,
7368 struct iw_point *dwrq,
7369 char *extra)
7370{
7371 struct airo_info *ai = dev->priv;
Dan Williams9e75af32006-03-16 13:46:29 -05007372 BSSListElement *net;
7373 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007374 char *current_ev = extra;
7375
Dan Williams9e75af32006-03-16 13:46:29 -05007376 /* If a scan is in-progress, return -EAGAIN */
7377 if (ai->scan_timeout > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007378 return -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007379
Dan Williams9e75af32006-03-16 13:46:29 -05007380 if (down_interruptible(&ai->sem))
7381 return -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007382
Dan Williams9e75af32006-03-16 13:46:29 -05007383 list_for_each_entry (net, &ai->network_list, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007384 /* Translate to WE format this entry */
7385 current_ev = airo_translate_scan(dev, current_ev,
7386 extra + dwrq->length,
Dan Williams9e75af32006-03-16 13:46:29 -05007387 &net->bss);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007388
7389 /* Check if there is space for one more entry */
7390 if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
7391 /* Ask user space to try again with a bigger buffer */
Dan Williams9e75af32006-03-16 13:46:29 -05007392 err = -E2BIG;
7393 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007394 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07007395 }
Dan Williams9e75af32006-03-16 13:46:29 -05007396
Linus Torvalds1da177e2005-04-16 15:20:36 -07007397 /* Length of data */
7398 dwrq->length = (current_ev - extra);
7399 dwrq->flags = 0; /* todo */
7400
Dan Williams9e75af32006-03-16 13:46:29 -05007401out:
7402 up(&ai->sem);
7403 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007404}
7405
7406/*------------------------------------------------------------------*/
7407/*
7408 * Commit handler : called after a bunch of SET operations
7409 */
7410static int airo_config_commit(struct net_device *dev,
7411 struct iw_request_info *info, /* NULL */
7412 void *zwrq, /* NULL */
7413 char *extra) /* NULL */
7414{
7415 struct airo_info *local = dev->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007416
7417 if (!test_bit (FLAG_COMMIT, &local->flags))
7418 return 0;
7419
7420 /* Some of the "SET" function may have modified some of the
7421 * parameters. It's now time to commit them in the card */
7422 disable_MAC(local, 1);
7423 if (test_bit (FLAG_RESET, &local->flags)) {
7424 APListRid APList_rid;
7425 SsidRid SSID_rid;
7426
7427 readAPListRid(local, &APList_rid);
7428 readSsidRid(local, &SSID_rid);
7429 if (test_bit(FLAG_MPI,&local->flags))
7430 setup_card(local, dev->dev_addr, 1 );
7431 else
7432 reset_airo_card(dev);
7433 disable_MAC(local, 1);
7434 writeSsidRid(local, &SSID_rid, 1);
7435 writeAPListRid(local, &APList_rid, 1);
7436 }
7437 if (down_interruptible(&local->sem))
7438 return -ERESTARTSYS;
7439 writeConfigRid(local, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007440 enable_MAC(local, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007441 if (test_bit (FLAG_RESET, &local->flags))
7442 airo_set_promisc(local);
7443 else
7444 up(&local->sem);
7445
7446 return 0;
7447}
7448
7449/*------------------------------------------------------------------*/
7450/*
7451 * Structures to export the Wireless Handlers
7452 */
7453
7454static const struct iw_priv_args airo_private_args[] = {
7455/*{ cmd, set_args, get_args, name } */
7456 { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
7457 IW_PRIV_TYPE_BYTE | 2047, "airoioctl" },
7458 { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
7459 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" },
7460};
7461
7462static const iw_handler airo_handler[] =
7463{
7464 (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */
7465 (iw_handler) airo_get_name, /* SIOCGIWNAME */
7466 (iw_handler) NULL, /* SIOCSIWNWID */
7467 (iw_handler) NULL, /* SIOCGIWNWID */
7468 (iw_handler) airo_set_freq, /* SIOCSIWFREQ */
7469 (iw_handler) airo_get_freq, /* SIOCGIWFREQ */
7470 (iw_handler) airo_set_mode, /* SIOCSIWMODE */
7471 (iw_handler) airo_get_mode, /* SIOCGIWMODE */
7472 (iw_handler) airo_set_sens, /* SIOCSIWSENS */
7473 (iw_handler) airo_get_sens, /* SIOCGIWSENS */
7474 (iw_handler) NULL, /* SIOCSIWRANGE */
7475 (iw_handler) airo_get_range, /* SIOCGIWRANGE */
7476 (iw_handler) NULL, /* SIOCSIWPRIV */
7477 (iw_handler) NULL, /* SIOCGIWPRIV */
7478 (iw_handler) NULL, /* SIOCSIWSTATS */
7479 (iw_handler) NULL, /* SIOCGIWSTATS */
7480 iw_handler_set_spy, /* SIOCSIWSPY */
7481 iw_handler_get_spy, /* SIOCGIWSPY */
7482 iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
7483 iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
7484 (iw_handler) airo_set_wap, /* SIOCSIWAP */
7485 (iw_handler) airo_get_wap, /* SIOCGIWAP */
7486 (iw_handler) NULL, /* -- hole -- */
7487 (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */
7488 (iw_handler) airo_set_scan, /* SIOCSIWSCAN */
7489 (iw_handler) airo_get_scan, /* SIOCGIWSCAN */
7490 (iw_handler) airo_set_essid, /* SIOCSIWESSID */
7491 (iw_handler) airo_get_essid, /* SIOCGIWESSID */
7492 (iw_handler) airo_set_nick, /* SIOCSIWNICKN */
7493 (iw_handler) airo_get_nick, /* SIOCGIWNICKN */
7494 (iw_handler) NULL, /* -- hole -- */
7495 (iw_handler) NULL, /* -- hole -- */
7496 (iw_handler) airo_set_rate, /* SIOCSIWRATE */
7497 (iw_handler) airo_get_rate, /* SIOCGIWRATE */
7498 (iw_handler) airo_set_rts, /* SIOCSIWRTS */
7499 (iw_handler) airo_get_rts, /* SIOCGIWRTS */
7500 (iw_handler) airo_set_frag, /* SIOCSIWFRAG */
7501 (iw_handler) airo_get_frag, /* SIOCGIWFRAG */
7502 (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */
7503 (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */
7504 (iw_handler) airo_set_retry, /* SIOCSIWRETRY */
7505 (iw_handler) airo_get_retry, /* SIOCGIWRETRY */
7506 (iw_handler) airo_set_encode, /* SIOCSIWENCODE */
7507 (iw_handler) airo_get_encode, /* SIOCGIWENCODE */
7508 (iw_handler) airo_set_power, /* SIOCSIWPOWER */
7509 (iw_handler) airo_get_power, /* SIOCGIWPOWER */
Dan Williams4be757d2006-01-30 11:58:00 -05007510 (iw_handler) NULL, /* -- hole -- */
7511 (iw_handler) NULL, /* -- hole -- */
7512 (iw_handler) NULL, /* SIOCSIWGENIE */
7513 (iw_handler) NULL, /* SIOCGIWGENIE */
7514 (iw_handler) airo_set_auth, /* SIOCSIWAUTH */
7515 (iw_handler) airo_get_auth, /* SIOCGIWAUTH */
7516 (iw_handler) airo_set_encodeext, /* SIOCSIWENCODEEXT */
7517 (iw_handler) airo_get_encodeext, /* SIOCGIWENCODEEXT */
7518 (iw_handler) NULL, /* SIOCSIWPMKSA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07007519};
7520
7521/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here.
7522 * We want to force the use of the ioctl code, because those can't be
7523 * won't work the iw_handler code (because they simultaneously read
7524 * and write data and iw_handler can't do that).
7525 * Note that it's perfectly legal to read/write on a single ioctl command,
7526 * you just can't use iwpriv and need to force it via the ioctl handler.
7527 * Jean II */
7528static const iw_handler airo_private_handler[] =
7529{
7530 NULL, /* SIOCIWFIRSTPRIV */
7531};
7532
7533static const struct iw_handler_def airo_handler_def =
7534{
Denis Chengff8ac602007-09-02 18:30:18 +08007535 .num_standard = ARRAY_SIZE(airo_handler),
7536 .num_private = ARRAY_SIZE(airo_private_handler),
7537 .num_private_args = ARRAY_SIZE(airo_private_args),
Linus Torvalds1da177e2005-04-16 15:20:36 -07007538 .standard = airo_handler,
7539 .private = airo_private_handler,
7540 .private_args = airo_private_args,
7541 .get_wireless_stats = airo_get_wireless_stats,
7542};
7543
Linus Torvalds1da177e2005-04-16 15:20:36 -07007544/*
7545 * This defines the configuration part of the Wireless Extensions
7546 * Note : irq and spinlock protection will occur in the subroutines
7547 *
7548 * TODO :
7549 * o Check input value more carefully and fill correct values in range
7550 * o Test and shakeout the bugs (if any)
7551 *
7552 * Jean II
7553 *
7554 * Javier Achirica did a great job of merging code from the unnamed CISCO
7555 * developer that added support for flashing the card.
7556 */
7557static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
7558{
7559 int rc = 0;
7560 struct airo_info *ai = (struct airo_info *)dev->priv;
7561
Pavel Machekca078ba2005-09-03 15:56:57 -07007562 if (ai->power.event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007563 return 0;
7564
7565 switch (cmd) {
7566#ifdef CISCO_EXT
7567 case AIROIDIFC:
7568#ifdef AIROOLDIDIFC
7569 case AIROOLDIDIFC:
7570#endif
7571 {
7572 int val = AIROMAGIC;
7573 aironet_ioctl com;
7574 if (copy_from_user(&com,rq->ifr_data,sizeof(com)))
7575 rc = -EFAULT;
7576 else if (copy_to_user(com.data,(char *)&val,sizeof(val)))
7577 rc = -EFAULT;
7578 }
7579 break;
7580
7581 case AIROIOCTL:
7582#ifdef AIROOLDIOCTL
7583 case AIROOLDIOCTL:
7584#endif
7585 /* Get the command struct and hand it off for evaluation by
7586 * the proper subfunction
7587 */
7588 {
7589 aironet_ioctl com;
7590 if (copy_from_user(&com,rq->ifr_data,sizeof(com))) {
7591 rc = -EFAULT;
7592 break;
7593 }
7594
7595 /* Separate R/W functions bracket legality here
7596 */
7597 if ( com.command == AIRORSWVERSION ) {
7598 if (copy_to_user(com.data, swversion, sizeof(swversion)))
7599 rc = -EFAULT;
7600 else
7601 rc = 0;
7602 }
7603 else if ( com.command <= AIRORRID)
7604 rc = readrids(dev,&com);
7605 else if ( com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2) )
7606 rc = writerids(dev,&com);
7607 else if ( com.command >= AIROFLSHRST && com.command <= AIRORESTART )
7608 rc = flashcard(dev,&com);
7609 else
7610 rc = -EINVAL; /* Bad command in ioctl */
7611 }
7612 break;
7613#endif /* CISCO_EXT */
7614
7615 // All other calls are currently unsupported
7616 default:
7617 rc = -EOPNOTSUPP;
7618 }
7619 return rc;
7620}
7621
Linus Torvalds1da177e2005-04-16 15:20:36 -07007622/*
7623 * Get the Wireless stats out of the driver
7624 * Note : irq and spinlock protection will occur in the subroutines
7625 *
7626 * TODO :
7627 * o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs)
7628 *
7629 * Jean
7630 */
7631static void airo_read_wireless_stats(struct airo_info *local)
7632{
7633 StatusRid status_rid;
7634 StatsRid stats_rid;
7635 CapabilityRid cap_rid;
Al Viroa23ace52007-12-19 22:24:16 -05007636 __le32 *vals = stats_rid.vals;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007637
7638 /* Get stats out of the card */
Dan Williams3c304952006-04-15 12:26:18 -04007639 clear_bit(JOB_WSTATS, &local->jobs);
Pavel Machekca078ba2005-09-03 15:56:57 -07007640 if (local->power.event) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007641 up(&local->sem);
7642 return;
7643 }
7644 readCapabilityRid(local, &cap_rid, 0);
7645 readStatusRid(local, &status_rid, 0);
7646 readStatsRid(local, &stats_rid, RID_STATS, 0);
7647 up(&local->sem);
7648
7649 /* The status */
7650 local->wstats.status = status_rid.mode;
7651
Dan Williams41480af2005-05-10 09:45:51 -04007652 /* Signal quality and co */
7653 if (local->rssi) {
7654 local->wstats.qual.level = airo_rssi_to_dbm( local->rssi, status_rid.sigQuality );
7655 /* normalizedSignalStrength appears to be a percentage */
7656 local->wstats.qual.qual = status_rid.normalizedSignalStrength;
7657 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007658 local->wstats.qual.level = (status_rid.normalizedSignalStrength + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007659 local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid);
7660 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07007661 if (status_rid.len >= 124) {
Dan Williams41480af2005-05-10 09:45:51 -04007662 local->wstats.qual.noise = 0x100 - status_rid.noisedBm;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007663 local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007664 } else {
7665 local->wstats.qual.noise = 0;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007666 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 -07007667 }
7668
7669 /* Packets discarded in the wireless adapter due to wireless
7670 * specific problems */
Al Viroa23ace52007-12-19 22:24:16 -05007671 local->wstats.discard.nwid = le32_to_cpu(vals[56]) +
7672 le32_to_cpu(vals[57]) +
7673 le32_to_cpu(vals[58]); /* SSID Mismatch */
7674 local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */
7675 local->wstats.discard.fragment = le32_to_cpu(vals[30]);
7676 local->wstats.discard.retries = le32_to_cpu(vals[10]);
7677 local->wstats.discard.misc = le32_to_cpu(vals[1]) +
7678 le32_to_cpu(vals[32]);
7679 local->wstats.miss.beacon = le32_to_cpu(vals[34]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007680}
7681
Jouni Malinenff1d2762005-05-12 22:54:16 -04007682static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007683{
7684 struct airo_info *local = dev->priv;
7685
Dan Williams3c304952006-04-15 12:26:18 -04007686 if (!test_bit(JOB_WSTATS, &local->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007687 /* Get stats out of the card if available */
7688 if (down_trylock(&local->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04007689 set_bit(JOB_WSTATS, &local->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007690 wake_up_interruptible(&local->thr_wait);
7691 } else
7692 airo_read_wireless_stats(local);
7693 }
7694
7695 return &local->wstats;
7696}
Linus Torvalds1da177e2005-04-16 15:20:36 -07007697
7698#ifdef CISCO_EXT
7699/*
7700 * This just translates from driver IOCTL codes to the command codes to
7701 * feed to the radio's host interface. Things can be added/deleted
7702 * as needed. This represents the READ side of control I/O to
7703 * the card
7704 */
7705static int readrids(struct net_device *dev, aironet_ioctl *comp) {
7706 unsigned short ridcode;
7707 unsigned char *iobuf;
7708 int len;
7709 struct airo_info *ai = dev->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007710
7711 if (test_bit(FLAG_FLASHING, &ai->flags))
7712 return -EIO;
7713
7714 switch(comp->command)
7715 {
7716 case AIROGCAP: ridcode = RID_CAPABILITIES; break;
7717 case AIROGCFG: ridcode = RID_CONFIG;
7718 if (test_bit(FLAG_COMMIT, &ai->flags)) {
7719 disable_MAC (ai, 1);
7720 writeConfigRid (ai, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007721 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007722 }
7723 break;
7724 case AIROGSLIST: ridcode = RID_SSID; break;
7725 case AIROGVLIST: ridcode = RID_APLIST; break;
7726 case AIROGDRVNAM: ridcode = RID_DRVNAME; break;
7727 case AIROGEHTENC: ridcode = RID_ETHERENCAP; break;
7728 case AIROGWEPKTMP: ridcode = RID_WEP_TEMP;
7729 /* Only super-user can read WEP keys */
7730 if (!capable(CAP_NET_ADMIN))
7731 return -EPERM;
7732 break;
7733 case AIROGWEPKNV: ridcode = RID_WEP_PERM;
7734 /* Only super-user can read WEP keys */
7735 if (!capable(CAP_NET_ADMIN))
7736 return -EPERM;
7737 break;
7738 case AIROGSTAT: ridcode = RID_STATUS; break;
7739 case AIROGSTATSD32: ridcode = RID_STATSDELTA; break;
7740 case AIROGSTATSC32: ridcode = RID_STATS; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007741 case AIROGMICSTATS:
7742 if (copy_to_user(comp->data, &ai->micstats,
7743 min((int)comp->len,(int)sizeof(ai->micstats))))
7744 return -EFAULT;
7745 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007746 case AIRORRID: ridcode = comp->ridnum; break;
7747 default:
7748 return -EINVAL;
7749 break;
7750 }
7751
7752 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7753 return -ENOMEM;
7754
7755 PC4500_readrid(ai,ridcode,iobuf,RIDSIZE, 1);
7756 /* get the count of bytes in the rid docs say 1st 2 bytes is it.
7757 * then return it to the user
7758 * 9/22/2000 Honor user given length
7759 */
7760 len = comp->len;
7761
7762 if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) {
7763 kfree (iobuf);
7764 return -EFAULT;
7765 }
7766 kfree (iobuf);
7767 return 0;
7768}
7769
7770/*
7771 * Danger Will Robinson write the rids here
7772 */
7773
7774static int writerids(struct net_device *dev, aironet_ioctl *comp) {
7775 struct airo_info *ai = dev->priv;
7776 int ridcode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007777 int enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007778 static int (* writer)(struct airo_info *, u16 rid, const void *, int, int);
7779 unsigned char *iobuf;
7780
7781 /* Only super-user can write RIDs */
7782 if (!capable(CAP_NET_ADMIN))
7783 return -EPERM;
7784
7785 if (test_bit(FLAG_FLASHING, &ai->flags))
7786 return -EIO;
7787
7788 ridcode = 0;
7789 writer = do_writerid;
7790
7791 switch(comp->command)
7792 {
7793 case AIROPSIDS: ridcode = RID_SSID; break;
7794 case AIROPCAP: ridcode = RID_CAPABILITIES; break;
7795 case AIROPAPLIST: ridcode = RID_APLIST; break;
7796 case AIROPCFG: ai->config.len = 0;
7797 clear_bit(FLAG_COMMIT, &ai->flags);
7798 ridcode = RID_CONFIG; break;
7799 case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break;
7800 case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break;
7801 case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break;
7802 case AIROPWEPKEY: ridcode = RID_WEP_TEMP; writer = PC4500_writerid;
7803 break;
7804 case AIROPLEAPUSR+1: ridcode = 0xFF2A; break;
7805 case AIROPLEAPUSR+2: ridcode = 0xFF2B; break;
7806
7807 /* this is not really a rid but a command given to the card
7808 * same with MAC off
7809 */
7810 case AIROPMACON:
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007811 if (enable_MAC(ai, 1) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007812 return -EIO;
7813 return 0;
7814
7815 /*
7816 * Evidently this code in the airo driver does not get a symbol
7817 * as disable_MAC. it's probably so short the compiler does not gen one.
7818 */
7819 case AIROPMACOFF:
7820 disable_MAC(ai, 1);
7821 return 0;
7822
7823 /* This command merely clears the counts does not actually store any data
7824 * only reads rid. But as it changes the cards state, I put it in the
7825 * writerid routines.
7826 */
7827 case AIROPSTCLR:
7828 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7829 return -ENOMEM;
7830
7831 PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDSIZE, 1);
7832
Linus Torvalds1da177e2005-04-16 15:20:36 -07007833 enabled = ai->micstats.enabled;
7834 memset(&ai->micstats,0,sizeof(ai->micstats));
7835 ai->micstats.enabled = enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007836
7837 if (copy_to_user(comp->data, iobuf,
7838 min((int)comp->len, (int)RIDSIZE))) {
7839 kfree (iobuf);
7840 return -EFAULT;
7841 }
7842 kfree (iobuf);
7843 return 0;
7844
7845 default:
7846 return -EOPNOTSUPP; /* Blarg! */
7847 }
7848 if(comp->len > RIDSIZE)
7849 return -EINVAL;
7850
7851 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7852 return -ENOMEM;
7853
7854 if (copy_from_user(iobuf,comp->data,comp->len)) {
7855 kfree (iobuf);
7856 return -EFAULT;
7857 }
7858
7859 if (comp->command == AIROPCFG) {
7860 ConfigRid *cfg = (ConfigRid *)iobuf;
7861
7862 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags))
Al Viro2ab1f512007-12-20 23:04:35 -05007863 cfg->opmode |= cpu_to_le16(MODE_MIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007864
Al Viro2ab1f512007-12-20 23:04:35 -05007865 if ((le16_to_cpu(cfg->opmode) & 0xFF) == MODE_STA_IBSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007866 set_bit (FLAG_ADHOC, &ai->flags);
7867 else
7868 clear_bit (FLAG_ADHOC, &ai->flags);
7869 }
7870
7871 if((*writer)(ai, ridcode, iobuf,comp->len,1)) {
7872 kfree (iobuf);
7873 return -EIO;
7874 }
7875 kfree (iobuf);
7876 return 0;
7877}
7878
7879/*****************************************************************************
7880 * Ancillary flash / mod functions much black magic lurkes here *
7881 *****************************************************************************
7882 */
7883
7884/*
7885 * Flash command switch table
7886 */
7887
Jouni Malinenff1d2762005-05-12 22:54:16 -04007888static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007889 int z;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007890
7891 /* Only super-user can modify flash */
7892 if (!capable(CAP_NET_ADMIN))
7893 return -EPERM;
7894
7895 switch(comp->command)
7896 {
7897 case AIROFLSHRST:
7898 return cmdreset((struct airo_info *)dev->priv);
7899
7900 case AIROFLSHSTFL:
7901 if (!((struct airo_info *)dev->priv)->flash &&
7902 (((struct airo_info *)dev->priv)->flash = kmalloc (FLASHSIZE, GFP_KERNEL)) == NULL)
7903 return -ENOMEM;
7904 return setflashmode((struct airo_info *)dev->priv);
7905
7906 case AIROFLSHGCHR: /* Get char from aux */
7907 if(comp->len != sizeof(int))
7908 return -EINVAL;
7909 if (copy_from_user(&z,comp->data,comp->len))
7910 return -EFAULT;
7911 return flashgchar((struct airo_info *)dev->priv,z,8000);
7912
7913 case AIROFLSHPCHR: /* Send char to card. */
7914 if(comp->len != sizeof(int))
7915 return -EINVAL;
7916 if (copy_from_user(&z,comp->data,comp->len))
7917 return -EFAULT;
7918 return flashpchar((struct airo_info *)dev->priv,z,8000);
7919
7920 case AIROFLPUTBUF: /* Send 32k to card */
7921 if (!((struct airo_info *)dev->priv)->flash)
7922 return -ENOMEM;
7923 if(comp->len > FLASHSIZE)
7924 return -EINVAL;
7925 if(copy_from_user(((struct airo_info *)dev->priv)->flash,comp->data,comp->len))
7926 return -EFAULT;
7927
7928 flashputbuf((struct airo_info *)dev->priv);
7929 return 0;
7930
7931 case AIRORESTART:
7932 if(flashrestart((struct airo_info *)dev->priv,dev))
7933 return -EIO;
7934 return 0;
7935 }
7936 return -EINVAL;
7937}
7938
7939#define FLASH_COMMAND 0x7e7e
7940
7941/*
7942 * STEP 1)
7943 * Disable MAC and do soft reset on
7944 * card.
7945 */
7946
Jouni Malinenff1d2762005-05-12 22:54:16 -04007947static int cmdreset(struct airo_info *ai) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007948 disable_MAC(ai, 1);
7949
7950 if(!waitbusy (ai)){
Dan Williams934d8bf2006-03-16 13:46:23 -05007951 airo_print_info(ai->dev->name, "Waitbusy hang before RESET");
Linus Torvalds1da177e2005-04-16 15:20:36 -07007952 return -EBUSY;
7953 }
7954
7955 OUT4500(ai,COMMAND,CMD_SOFTRESET);
7956
7957 ssleep(1); /* WAS 600 12/7/00 */
7958
7959 if(!waitbusy (ai)){
Dan Williams934d8bf2006-03-16 13:46:23 -05007960 airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET");
Linus Torvalds1da177e2005-04-16 15:20:36 -07007961 return -EBUSY;
7962 }
7963 return 0;
7964}
7965
7966/* STEP 2)
7967 * Put the card in legendary flash
7968 * mode
7969 */
7970
Jouni Malinenff1d2762005-05-12 22:54:16 -04007971static int setflashmode (struct airo_info *ai) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007972 set_bit (FLAG_FLASHING, &ai->flags);
7973
7974 OUT4500(ai, SWS0, FLASH_COMMAND);
7975 OUT4500(ai, SWS1, FLASH_COMMAND);
7976 if (probe) {
7977 OUT4500(ai, SWS0, FLASH_COMMAND);
7978 OUT4500(ai, COMMAND,0x10);
7979 } else {
7980 OUT4500(ai, SWS2, FLASH_COMMAND);
7981 OUT4500(ai, SWS3, FLASH_COMMAND);
7982 OUT4500(ai, COMMAND,0);
7983 }
7984 msleep(500); /* 500ms delay */
7985
7986 if(!waitbusy(ai)) {
7987 clear_bit (FLAG_FLASHING, &ai->flags);
Dan Williams934d8bf2006-03-16 13:46:23 -05007988 airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode");
Linus Torvalds1da177e2005-04-16 15:20:36 -07007989 return -EIO;
7990 }
7991 return 0;
7992}
7993
7994/* Put character to SWS0 wait for dwelltime
7995 * x 50us for echo .
7996 */
7997
Jouni Malinenff1d2762005-05-12 22:54:16 -04007998static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007999 int echo;
8000 int waittime;
8001
8002 byte |= 0x8000;
8003
8004 if(dwelltime == 0 )
8005 dwelltime = 200;
8006
8007 waittime=dwelltime;
8008
8009 /* Wait for busy bit d15 to go false indicating buffer empty */
8010 while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) {
8011 udelay (50);
8012 waittime -= 50;
8013 }
8014
8015 /* timeout for busy clear wait */
8016 if(waittime <= 0 ){
Dan Williams934d8bf2006-03-16 13:46:23 -05008017 airo_print_info(ai->dev->name, "flash putchar busywait timeout!");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008018 return -EBUSY;
8019 }
8020
8021 /* Port is clear now write byte and wait for it to echo back */
8022 do {
8023 OUT4500(ai,SWS0,byte);
8024 udelay(50);
8025 dwelltime -= 50;
8026 echo = IN4500(ai,SWS1);
8027 } while (dwelltime >= 0 && echo != byte);
8028
8029 OUT4500(ai,SWS1,0);
8030
8031 return (echo == byte) ? 0 : -EIO;
8032}
8033
8034/*
8035 * Get a character from the card matching matchbyte
8036 * Step 3)
8037 */
Jouni Malinenff1d2762005-05-12 22:54:16 -04008038static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008039 int rchar;
8040 unsigned char rbyte=0;
8041
8042 do {
8043 rchar = IN4500(ai,SWS1);
8044
8045 if(dwelltime && !(0x8000 & rchar)){
8046 dwelltime -= 10;
8047 mdelay(10);
8048 continue;
8049 }
8050 rbyte = 0xff & rchar;
8051
8052 if( (rbyte == matchbyte) && (0x8000 & rchar) ){
8053 OUT4500(ai,SWS1,0);
8054 return 0;
8055 }
8056 if( rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar)
8057 break;
8058 OUT4500(ai,SWS1,0);
8059
8060 }while(dwelltime > 0);
8061 return -EIO;
8062}
8063
8064/*
8065 * Transfer 32k of firmware data from user buffer to our buffer and
8066 * send to the card
8067 */
8068
Jouni Malinenff1d2762005-05-12 22:54:16 -04008069static int flashputbuf(struct airo_info *ai){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008070 int nwords;
8071
8072 /* Write stuff */
8073 if (test_bit(FLAG_MPI,&ai->flags))
8074 memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE);
8075 else {
8076 OUT4500(ai,AUXPAGE,0x100);
8077 OUT4500(ai,AUXOFF,0);
8078
8079 for(nwords=0;nwords != FLASHSIZE / 2;nwords++){
8080 OUT4500(ai,AUXDATA,ai->flash[nwords] & 0xffff);
8081 }
8082 }
8083 OUT4500(ai,SWS0,0x8000);
8084
8085 return 0;
8086}
8087
8088/*
8089 *
8090 */
Jouni Malinenff1d2762005-05-12 22:54:16 -04008091static int flashrestart(struct airo_info *ai,struct net_device *dev){
Linus Torvalds1da177e2005-04-16 15:20:36 -07008092 int i,status;
8093
8094 ssleep(1); /* Added 12/7/00 */
8095 clear_bit (FLAG_FLASHING, &ai->flags);
8096 if (test_bit(FLAG_MPI, &ai->flags)) {
8097 status = mpi_init_descriptors(ai);
8098 if (status != SUCCESS)
8099 return status;
8100 }
8101 status = setup_card(ai, dev->dev_addr, 1);
8102
8103 if (!test_bit(FLAG_MPI,&ai->flags))
8104 for( i = 0; i < MAX_FIDS; i++ ) {
8105 ai->fids[i] = transmit_allocate
Dan Williams15db2762006-03-16 13:46:27 -05008106 ( ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2 );
Linus Torvalds1da177e2005-04-16 15:20:36 -07008107 }
8108
8109 ssleep(1); /* Added 12/7/00 */
8110 return status;
8111}
8112#endif /* CISCO_EXT */
8113
8114/*
8115 This program is free software; you can redistribute it and/or
8116 modify it under the terms of the GNU General Public License
8117 as published by the Free Software Foundation; either version 2
8118 of the License, or (at your option) any later version.
8119
8120 This program is distributed in the hope that it will be useful,
8121 but WITHOUT ANY WARRANTY; without even the implied warranty of
8122 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8123 GNU General Public License for more details.
8124
8125 In addition:
8126
8127 Redistribution and use in source and binary forms, with or without
8128 modification, are permitted provided that the following conditions
8129 are met:
8130
8131 1. Redistributions of source code must retain the above copyright
8132 notice, this list of conditions and the following disclaimer.
8133 2. Redistributions in binary form must reproduce the above copyright
8134 notice, this list of conditions and the following disclaimer in the
8135 documentation and/or other materials provided with the distribution.
8136 3. The name of the author may not be used to endorse or promote
8137 products derived from this software without specific prior written
8138 permission.
8139
8140 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
8141 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8142 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
8143 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
8144 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
8145 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
8146 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
8147 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
8148 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
8149 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
8150 POSSIBILITY OF SUCH DAMAGE.
8151*/
8152
8153module_init(airo_init_module);
8154module_exit(airo_cleanup_module);