blob: dd78c415d6e76c00c1fa4a1264d0084d75e165b7 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*======================================================================
2
3 Aironet driver for 4500 and 4800 series cards
4
5 This code is released under both the GPL version 2 and BSD licenses.
6 Either license may be used. The respective licenses are found at
7 the end of this file.
8
9 This code was developed by Benjamin Reed <breed@users.sourceforge.net>
10 including portions of which come from the Aironet PC4500
11 Developer's Reference Manual and used with permission. Copyright
12 (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use
13 code in the Developer's manual was granted for this driver by
14 Aironet. Major code contributions were received from Javier Achirica
15 <achirica@users.sourceforge.net> and Jean Tourrilhes <jt@hpl.hp.com>.
16 Code was also integrated from the Cisco Aironet driver for Linux.
17 Support for MPI350 cards was added by Fabrice Bellet
18 <fabrice@bellet.info>.
19
20======================================================================*/
21
Herbert Xuf12cc202006-08-22 20:36:13 +100022#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/init.h>
24
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/proc_fs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#include <linux/sched.h>
30#include <linux/ptrace.h>
31#include <linux/slab.h>
32#include <linux/string.h>
33#include <linux/timer.h>
34#include <linux/interrupt.h>
35#include <linux/in.h>
36#include <linux/bitops.h>
David Hardeman378f0582005-09-17 17:55:31 +100037#include <linux/scatterlist.h>
Adrian Bunka39d3e72006-01-21 01:35:15 +010038#include <linux/crypto.h>
Avinash kumar16847f42014-02-18 17:04:10 +053039#include <linux/io.h>
Al Viro593c2b92007-12-17 15:09:34 -050040#include <asm/unaligned.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42#include <linux/netdevice.h>
43#include <linux/etherdevice.h>
44#include <linux/skbuff.h>
45#include <linux/if_arp.h>
46#include <linux/ioport.h>
47#include <linux/pci.h>
Avinash kumar16847f42014-02-18 17:04:10 +053048#include <linux/uaccess.h>
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -070049#include <linux/kthread.h>
Nigel Cunningham7dfb7102006-12-06 20:34:23 -080050#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
Ard Biesheuvele5db0ad2019-06-17 10:43:38 +020052#include <crypto/aes.h>
53#include <crypto/skcipher.h>
54
Zhao, Gange0febf12014-02-18 21:35:57 +080055#include <net/cfg80211.h>
Randy Dunlap2bf9fa62010-02-12 13:02:23 -080056#include <net/iw_handler.h>
Johannes Berg2c7060022008-10-30 22:09:54 +010057
Adrian Bunkd3808762005-11-05 17:42:27 +010058#include "airo.h"
59
Michal Schmidt1138c372007-06-29 15:33:41 +020060#define DRV_NAME "airo"
61
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#ifdef CONFIG_PCI
Benoit Taine9baa3c32014-08-08 15:56:03 +020063static const struct pci_device_id card_ids[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, },
65 { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID },
66 { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, },
67 { 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, },
68 { 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, },
69 { 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, },
70 { 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, },
71 { 0, }
72};
73MODULE_DEVICE_TABLE(pci, card_ids);
74
75static int airo_pci_probe(struct pci_dev *, const struct pci_device_id *);
76static void airo_pci_remove(struct pci_dev *);
Vaibhav Guptac3ab1802020-07-28 17:11:28 +053077static int __maybe_unused airo_pci_suspend(struct device *dev);
78static int __maybe_unused airo_pci_resume(struct device *dev);
79
80static SIMPLE_DEV_PM_OPS(airo_pci_pm_ops,
81 airo_pci_suspend,
82 airo_pci_resume);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
84static struct pci_driver airo_driver = {
Vaibhav Guptac3ab1802020-07-28 17:11:28 +053085 .name = DRV_NAME,
86 .id_table = card_ids,
87 .probe = airo_pci_probe,
88 .remove = airo_pci_remove,
89 .driver.pm = &airo_pci_pm_ops,
Linus Torvalds1da177e2005-04-16 15:20:36 -070090};
91#endif /* CONFIG_PCI */
92
93/* Include Wireless Extension definition and check version - Jean II */
94#include <linux/wireless.h>
Pavel Macheke292c732008-06-25 12:25:53 +020095#define WIRELESS_SPY /* enable iwspy support */
Linus Torvalds1da177e2005-04-16 15:20:36 -070096
Pavel Macheke292c732008-06-25 12:25:53 +020097#define CISCO_EXT /* enable Cisco extensions */
Linus Torvalds1da177e2005-04-16 15:20:36 -070098#ifdef CISCO_EXT
99#include <linux/delay.h>
100#endif
101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102/* Hack to do some power saving */
103#define POWER_ON_DOWN
104
105/* As you can see this list is HUGH!
106 I really don't know what a lot of these counts are about, but they
107 are all here for completeness. If the IGNLABEL macro is put in
108 infront of the label, that statistic will not be included in the list
109 of statistics in the /proc filesystem */
110
111#define IGNLABEL(comment) NULL
Stephen Hemminger7a374d82010-09-01 18:17:21 -0700112static const char *statsLabels[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 "RxOverrun",
114 IGNLABEL("RxPlcpCrcErr"),
115 IGNLABEL("RxPlcpFormatErr"),
116 IGNLABEL("RxPlcpLengthErr"),
117 "RxMacCrcErr",
118 "RxMacCrcOk",
119 "RxWepErr",
120 "RxWepOk",
121 "RetryLong",
122 "RetryShort",
123 "MaxRetries",
124 "NoAck",
125 "NoCts",
126 "RxAck",
127 "RxCts",
128 "TxAck",
129 "TxRts",
130 "TxCts",
131 "TxMc",
132 "TxBc",
133 "TxUcFrags",
134 "TxUcPackets",
135 "TxBeacon",
136 "RxBeacon",
137 "TxSinColl",
138 "TxMulColl",
139 "DefersNo",
140 "DefersProt",
141 "DefersEngy",
142 "DupFram",
143 "RxFragDisc",
144 "TxAged",
145 "RxAged",
146 "LostSync-MaxRetry",
147 "LostSync-MissedBeacons",
148 "LostSync-ArlExceeded",
149 "LostSync-Deauth",
150 "LostSync-Disassoced",
151 "LostSync-TsfTiming",
152 "HostTxMc",
153 "HostTxBc",
154 "HostTxUc",
155 "HostTxFail",
156 "HostRxMc",
157 "HostRxBc",
158 "HostRxUc",
159 "HostRxDiscard",
160 IGNLABEL("HmacTxMc"),
161 IGNLABEL("HmacTxBc"),
162 IGNLABEL("HmacTxUc"),
163 IGNLABEL("HmacTxFail"),
164 IGNLABEL("HmacRxMc"),
165 IGNLABEL("HmacRxBc"),
166 IGNLABEL("HmacRxUc"),
167 IGNLABEL("HmacRxDiscard"),
168 IGNLABEL("HmacRxAccepted"),
169 "SsidMismatch",
170 "ApMismatch",
171 "RatesMismatch",
172 "AuthReject",
173 "AuthTimeout",
174 "AssocReject",
175 "AssocTimeout",
176 IGNLABEL("ReasonOutsideTable"),
177 IGNLABEL("ReasonStatus1"),
178 IGNLABEL("ReasonStatus2"),
179 IGNLABEL("ReasonStatus3"),
180 IGNLABEL("ReasonStatus4"),
181 IGNLABEL("ReasonStatus5"),
182 IGNLABEL("ReasonStatus6"),
183 IGNLABEL("ReasonStatus7"),
184 IGNLABEL("ReasonStatus8"),
185 IGNLABEL("ReasonStatus9"),
186 IGNLABEL("ReasonStatus10"),
187 IGNLABEL("ReasonStatus11"),
188 IGNLABEL("ReasonStatus12"),
189 IGNLABEL("ReasonStatus13"),
190 IGNLABEL("ReasonStatus14"),
191 IGNLABEL("ReasonStatus15"),
192 IGNLABEL("ReasonStatus16"),
193 IGNLABEL("ReasonStatus17"),
194 IGNLABEL("ReasonStatus18"),
195 IGNLABEL("ReasonStatus19"),
196 "RxMan",
197 "TxMan",
198 "RxRefresh",
199 "TxRefresh",
200 "RxPoll",
201 "TxPoll",
202 "HostRetries",
203 "LostSync-HostReq",
204 "HostTxBytes",
205 "HostRxBytes",
206 "ElapsedUsec",
207 "ElapsedSec",
208 "LostSyncBetterAP",
209 "PrivacyMismatch",
210 "Jammed",
211 "DiscRxNotWepped",
212 "PhyEleMismatch",
213 (char*)-1 };
214#ifndef RUN_AT
215#define RUN_AT(x) (jiffies+(x))
216#endif
217
218
219/* These variables are for insmod, since it seems that the rates
220 can only be set in setup_card. Rates should be a comma separated
221 (no spaces) list of rates (up to 8). */
222
223static int rates[8];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224static char *ssids[3];
225
226static int io[4];
227static int irq[4];
228
229static
230int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at.
231 0 means no limit. For old cards this was 4 */
232
233static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */
234static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read
235 the bap, needed on some older cards and buses. */
236static int adhoc;
237
238static int probe = 1;
239
Eric W. Biederman1efa29c2012-02-10 14:01:03 -0800240static kuid_t proc_kuid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241static int proc_uid /* = 0 */;
242
Eric W. Biederman1efa29c2012-02-10 14:01:03 -0800243static kgid_t proc_kgid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244static int proc_gid /* = 0 */;
245
246static int airo_perm = 0555;
247
248static int proc_perm = 0644;
249
250MODULE_AUTHOR("Benjamin Reed");
Joe Perches85ee7a12011-04-23 20:38:19 -0700251MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet cards. "
252 "Direct support for ISA/PCI/MPI cards and support for PCMCIA when used with airo_cs.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253MODULE_LICENSE("Dual BSD/GPL");
254MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350");
David Howells767c13e2017-04-04 16:54:27 +0100255module_param_hw_array(io, int, ioport, NULL, 0);
256module_param_hw_array(irq, int, irq, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257module_param_array(rates, int, NULL, 0);
258module_param_array(ssids, charp, NULL, 0);
259module_param(auto_wep, int, 0);
Joe Perches85ee7a12011-04-23 20:38:19 -0700260MODULE_PARM_DESC(auto_wep,
261 "If non-zero, the driver will keep looping through the authentication options until an association is made. "
262 "The value of auto_wep is number of the wep keys to check. "
263 "A value of 2 will try using the key at index 0 and index 1.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264module_param(aux_bap, int, 0);
Joe Perches85ee7a12011-04-23 20:38:19 -0700265MODULE_PARM_DESC(aux_bap,
266 "If non-zero, the driver will switch into a mode that seems to work better for older cards with some older buses. "
267 "Before switching it checks that the switch is needed.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268module_param(maxencrypt, int, 0);
Joe Perches85ee7a12011-04-23 20:38:19 -0700269MODULE_PARM_DESC(maxencrypt,
270 "The maximum speed that the card can do encryption. "
271 "Units are in 512kbs. "
272 "Zero (default) means there is no limit. "
273 "Older cards used to be limited to 2mbs (4).");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274module_param(adhoc, int, 0);
275MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode.");
276module_param(probe, int, 0);
277MODULE_PARM_DESC(probe, "If zero, the driver won't start the card.");
278
279module_param(proc_uid, int, 0);
280MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to.");
281module_param(proc_gid, int, 0);
282MODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to.");
283module_param(airo_perm, int, 0);
284MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet.");
285module_param(proc_perm, int, 0);
286MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc");
287
288/* This is a kind of sloppy hack to get this information to OUT4500 and
289 IN4500. I would be extremely interested in the situation where this
290 doesn't work though!!! */
Pavel Macheke292c732008-06-25 12:25:53 +0200291static int do8bitIO /* = 0 */;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
293/* Return codes */
294#define SUCCESS 0
295#define ERROR -1
296#define NO_PACKET -2
297
298/* Commands */
299#define NOP2 0x0000
300#define MAC_ENABLE 0x0001
301#define MAC_DISABLE 0x0002
302#define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */
303#define CMD_SOFTRESET 0x0004
304#define HOSTSLEEP 0x0005
305#define CMD_MAGIC_PKT 0x0006
306#define CMD_SETWAKEMASK 0x0007
307#define CMD_READCFG 0x0008
308#define CMD_SETMODE 0x0009
309#define CMD_ALLOCATETX 0x000a
310#define CMD_TRANSMIT 0x000b
311#define CMD_DEALLOCATETX 0x000c
312#define NOP 0x0010
313#define CMD_WORKAROUND 0x0011
314#define CMD_ALLOCATEAUX 0x0020
315#define CMD_ACCESS 0x0021
316#define CMD_PCIBAP 0x0022
317#define CMD_PCIAUX 0x0023
318#define CMD_ALLOCBUF 0x0028
319#define CMD_GETTLV 0x0029
320#define CMD_PUTTLV 0x002a
321#define CMD_DELTLV 0x002b
322#define CMD_FINDNEXTTLV 0x002c
323#define CMD_PSPNODES 0x0030
Lee Jonesba4d6512020-08-14 12:39:15 +0100324#define CMD_SETCW 0x0031
325#define CMD_SETPCF 0x0032
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326#define CMD_SETPHYREG 0x003e
327#define CMD_TXTEST 0x003f
328#define MAC_ENABLETX 0x0101
329#define CMD_LISTBSS 0x0103
330#define CMD_SAVECFG 0x0108
331#define CMD_ENABLEAUX 0x0111
332#define CMD_WRITERID 0x0121
333#define CMD_USEPSPNODES 0x0130
334#define MAC_ENABLERX 0x0201
335
336/* Command errors */
337#define ERROR_QUALIF 0x00
338#define ERROR_ILLCMD 0x01
339#define ERROR_ILLFMT 0x02
340#define ERROR_INVFID 0x03
341#define ERROR_INVRID 0x04
342#define ERROR_LARGE 0x05
343#define ERROR_NDISABL 0x06
344#define ERROR_ALLOCBSY 0x07
345#define ERROR_NORD 0x0B
346#define ERROR_NOWR 0x0C
347#define ERROR_INVFIDTX 0x0D
348#define ERROR_TESTACT 0x0E
349#define ERROR_TAGNFND 0x12
350#define ERROR_DECODE 0x20
351#define ERROR_DESCUNAV 0x21
352#define ERROR_BADLEN 0x22
353#define ERROR_MODE 0x80
354#define ERROR_HOP 0x81
355#define ERROR_BINTER 0x82
356#define ERROR_RXMODE 0x83
357#define ERROR_MACADDR 0x84
358#define ERROR_RATES 0x85
359#define ERROR_ORDER 0x86
360#define ERROR_SCAN 0x87
361#define ERROR_AUTH 0x88
362#define ERROR_PSMODE 0x89
363#define ERROR_RTYPE 0x8A
364#define ERROR_DIVER 0x8B
365#define ERROR_SSID 0x8C
366#define ERROR_APLIST 0x8D
367#define ERROR_AUTOWAKE 0x8E
368#define ERROR_LEAP 0x8F
369
370/* Registers */
371#define COMMAND 0x00
372#define PARAM0 0x02
373#define PARAM1 0x04
374#define PARAM2 0x06
375#define STATUS 0x08
376#define RESP0 0x0a
377#define RESP1 0x0c
378#define RESP2 0x0e
379#define LINKSTAT 0x10
380#define SELECT0 0x18
381#define OFFSET0 0x1c
382#define RXFID 0x20
383#define TXALLOCFID 0x22
384#define TXCOMPLFID 0x24
385#define DATA0 0x36
386#define EVSTAT 0x30
387#define EVINTEN 0x32
388#define EVACK 0x34
389#define SWS0 0x28
390#define SWS1 0x2a
391#define SWS2 0x2c
392#define SWS3 0x2e
393#define AUXPAGE 0x3A
394#define AUXOFF 0x3C
395#define AUXDATA 0x3E
396
397#define FID_TX 1
398#define FID_RX 2
399/* Offset into aux memory for descriptors */
400#define AUX_OFFSET 0x800
401/* Size of allocated packets */
402#define PKTSIZE 1840
403#define RIDSIZE 2048
404/* Size of the transmit queue */
405#define MAXTXQ 64
406
407/* BAP selectors */
Pavel Macheke292c732008-06-25 12:25:53 +0200408#define BAP0 0 /* Used for receiving packets */
409#define BAP1 2 /* Used for xmiting packets and working with RIDS */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
411/* Flags */
412#define COMMAND_BUSY 0x8000
413
414#define BAP_BUSY 0x8000
415#define BAP_ERR 0x4000
416#define BAP_DONE 0x2000
417
418#define PROMISC 0xffff
419#define NOPROMISC 0x0000
420
421#define EV_CMD 0x10
422#define EV_CLEARCOMMANDBUSY 0x4000
423#define EV_RX 0x01
424#define EV_TX 0x02
425#define EV_TXEXC 0x04
426#define EV_ALLOC 0x08
427#define EV_LINK 0x80
428#define EV_AWAKE 0x100
429#define EV_TXCPY 0x400
430#define EV_UNKNOWN 0x800
431#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */
432#define EV_AWAKEN 0x2000
433#define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC)
434
435#ifdef CHECK_UNKNOWN_INTS
Lee Jonesba4d6512020-08-14 12:39:15 +0100436#define IGNORE_INTS (EV_CMD | EV_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437#else
438#define IGNORE_INTS (~STATUS_INTS)
439#endif
440
441/* RID TYPES */
442#define RID_RW 0x20
443
444/* The RIDs */
445#define RID_CAPABILITIES 0xFF00
446#define RID_APINFO 0xFF01
447#define RID_RADIOINFO 0xFF02
448#define RID_UNKNOWN3 0xFF03
449#define RID_RSSI 0xFF04
450#define RID_CONFIG 0xFF10
451#define RID_SSID 0xFF11
452#define RID_APLIST 0xFF12
453#define RID_DRVNAME 0xFF13
454#define RID_ETHERENCAP 0xFF14
455#define RID_WEP_TEMP 0xFF15
456#define RID_WEP_PERM 0xFF16
457#define RID_MODULATION 0xFF17
458#define RID_OPTIONS 0xFF18
459#define RID_ACTUALCONFIG 0xFF20 /*readonly*/
460#define RID_FACTORYCONFIG 0xFF21
461#define RID_UNKNOWN22 0xFF22
462#define RID_LEAPUSERNAME 0xFF23
463#define RID_LEAPPASSWORD 0xFF24
464#define RID_STATUS 0xFF50
465#define RID_BEACON_HST 0xFF51
466#define RID_BUSY_HST 0xFF52
467#define RID_RETRIES_HST 0xFF53
468#define RID_UNKNOWN54 0xFF54
469#define RID_UNKNOWN55 0xFF55
470#define RID_UNKNOWN56 0xFF56
471#define RID_MIC 0xFF57
472#define RID_STATS16 0xFF60
473#define RID_STATS16DELTA 0xFF61
474#define RID_STATS16DELTACLEAR 0xFF62
475#define RID_STATS 0xFF68
476#define RID_STATSDELTA 0xFF69
477#define RID_STATSDELTACLEAR 0xFF6A
478#define RID_ECHOTEST_RID 0xFF70
479#define RID_ECHOTEST_RESULTS 0xFF71
480#define RID_BSSLISTFIRST 0xFF72
481#define RID_BSSLISTNEXT 0xFF73
Dan Williams3c304952006-04-15 12:26:18 -0400482#define RID_WPA_BSSLISTFIRST 0xFF74
483#define RID_WPA_BSSLISTNEXT 0xFF75
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
485typedef struct {
486 u16 cmd;
487 u16 parm0;
488 u16 parm1;
489 u16 parm2;
490} Cmd;
491
492typedef struct {
493 u16 status;
494 u16 rsp0;
495 u16 rsp1;
496 u16 rsp2;
497} Resp;
498
499/*
500 * Rids and endian-ness: The Rids will always be in cpu endian, since
501 * this all the patches from the big-endian guys end up doing that.
502 * so all rid access should use the read/writeXXXRid routines.
503 */
504
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505/* This structure came from an email sent to me from an engineer at
506 aironet for inclusion into this driver */
Dan Williams99590ff2009-01-24 09:12:58 -0500507typedef struct WepKeyRid WepKeyRid;
508struct WepKeyRid {
Al Viro4293ea32007-12-19 19:21:51 -0500509 __le16 len;
510 __le16 kindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 u8 mac[ETH_ALEN];
Al Viro4293ea32007-12-19 19:21:51 -0500512 __le16 klen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 u8 key[16];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000514} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
516/* These structures are from the Aironet's PC4500 Developers Manual */
Dan Williams99590ff2009-01-24 09:12:58 -0500517typedef struct Ssid Ssid;
518struct Ssid {
Al Viro0dd22122007-12-17 16:11:57 -0500519 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 u8 ssid[32];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000521} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522
Dan Williams99590ff2009-01-24 09:12:58 -0500523typedef struct SsidRid SsidRid;
524struct SsidRid {
Al Viro0dd22122007-12-17 16:11:57 -0500525 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 Ssid ssids[3];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000527} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
Dan Williams99590ff2009-01-24 09:12:58 -0500529typedef struct ModulationRid ModulationRid;
530struct ModulationRid {
Al Viro3eb9b412007-12-21 00:00:35 -0500531 __le16 len;
532 __le16 modulation;
533#define MOD_DEFAULT cpu_to_le16(0)
534#define MOD_CCK cpu_to_le16(1)
535#define MOD_MOK cpu_to_le16(2)
Eric Dumazetba2d3582010-06-02 18:10:09 +0000536} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Dan Williams99590ff2009-01-24 09:12:58 -0500538typedef struct ConfigRid ConfigRid;
539struct ConfigRid {
Al Viro3eb9b412007-12-21 00:00:35 -0500540 __le16 len; /* sizeof(ConfigRid) */
541 __le16 opmode; /* operating mode */
542#define MODE_STA_IBSS cpu_to_le16(0)
543#define MODE_STA_ESS cpu_to_le16(1)
544#define MODE_AP cpu_to_le16(2)
545#define MODE_AP_RPTR cpu_to_le16(3)
546#define MODE_CFG_MASK cpu_to_le16(0xff)
547#define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */
548#define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */
549#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extenstions */
550#define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */
551#define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */
552#define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */
553#define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */
554#define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */
555#define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */
556 __le16 rmode; /* receive mode */
557#define RXMODE_BC_MC_ADDR cpu_to_le16(0)
558#define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */
559#define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */
560#define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */
561#define RXMODE_RFMON_ANYBSS cpu_to_le16(4)
562#define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */
563#define RXMODE_MASK cpu_to_le16(255)
564#define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */
565#define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER)
566#define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */
567 __le16 fragThresh;
568 __le16 rtsThres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 u8 macAddr[ETH_ALEN];
570 u8 rates[8];
Al Viro3eb9b412007-12-21 00:00:35 -0500571 __le16 shortRetryLimit;
572 __le16 longRetryLimit;
573 __le16 txLifetime; /* in kusec */
574 __le16 rxLifetime; /* in kusec */
575 __le16 stationary;
576 __le16 ordering;
577 __le16 u16deviceType; /* for overriding device type */
578 __le16 cfpRate;
579 __le16 cfpDuration;
580 __le16 _reserved1[3];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 /*---------- Scanning/Associating ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500582 __le16 scanMode;
583#define SCANMODE_ACTIVE cpu_to_le16(0)
584#define SCANMODE_PASSIVE cpu_to_le16(1)
585#define SCANMODE_AIROSCAN cpu_to_le16(2)
586 __le16 probeDelay; /* in kusec */
587 __le16 probeEnergyTimeout; /* in kusec */
588 __le16 probeResponseTimeout;
589 __le16 beaconListenTimeout;
590 __le16 joinNetTimeout;
591 __le16 authTimeout;
592 __le16 authType;
593#define AUTH_OPEN cpu_to_le16(0x1)
594#define AUTH_ENCRYPT cpu_to_le16(0x101)
595#define AUTH_SHAREDKEY cpu_to_le16(0x102)
596#define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200)
597 __le16 associationTimeout;
598 __le16 specifiedApTimeout;
599 __le16 offlineScanInterval;
600 __le16 offlineScanDuration;
601 __le16 linkLossDelay;
602 __le16 maxBeaconLostTime;
603 __le16 refreshInterval;
604#define DISABLE_REFRESH cpu_to_le16(0xFFFF)
605 __le16 _reserved1a[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 /*---------- Power save operation ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500607 __le16 powerSaveMode;
608#define POWERSAVE_CAM cpu_to_le16(0)
609#define POWERSAVE_PSP cpu_to_le16(1)
610#define POWERSAVE_PSPCAM cpu_to_le16(2)
611 __le16 sleepForDtims;
612 __le16 listenInterval;
613 __le16 fastListenInterval;
614 __le16 listenDecay;
615 __le16 fastListenDelay;
616 __le16 _reserved2[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 /*---------- Ap/Ibss config items ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500618 __le16 beaconPeriod;
619 __le16 atimDuration;
620 __le16 hopPeriod;
621 __le16 channelSet;
622 __le16 channel;
623 __le16 dtimPeriod;
624 __le16 bridgeDistance;
625 __le16 radioID;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 /*---------- Radio configuration ----------*/
Al Viro3eb9b412007-12-21 00:00:35 -0500627 __le16 radioType;
628#define RADIOTYPE_DEFAULT cpu_to_le16(0)
629#define RADIOTYPE_802_11 cpu_to_le16(1)
630#define RADIOTYPE_LEGACY cpu_to_le16(2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 u8 rxDiversity;
632 u8 txDiversity;
Al Viro3eb9b412007-12-21 00:00:35 -0500633 __le16 txPower;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634#define TXPOWER_DEFAULT 0
Al Viro3eb9b412007-12-21 00:00:35 -0500635 __le16 rssiThreshold;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636#define RSSI_DEFAULT 0
Al Viro3eb9b412007-12-21 00:00:35 -0500637 __le16 modulation;
638#define PREAMBLE_AUTO cpu_to_le16(0)
639#define PREAMBLE_LONG cpu_to_le16(1)
640#define PREAMBLE_SHORT cpu_to_le16(2)
641 __le16 preamble;
642 __le16 homeProduct;
643 __le16 radioSpecific;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 /*---------- Aironet Extensions ----------*/
645 u8 nodeName[16];
Al Viro3eb9b412007-12-21 00:00:35 -0500646 __le16 arlThreshold;
647 __le16 arlDecay;
648 __le16 arlDelay;
649 __le16 _reserved4[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 /*---------- Aironet Extensions ----------*/
651 u8 magicAction;
652#define MAGIC_ACTION_STSCHG 1
653#define MAGIC_ACTION_RESUME 2
654#define MAGIC_IGNORE_MCAST (1<<8)
655#define MAGIC_IGNORE_BCAST (1<<9)
656#define MAGIC_SWITCH_TO_PSP (0<<10)
657#define MAGIC_STAY_IN_CAM (1<<10)
658 u8 magicControl;
Al Viro3eb9b412007-12-21 00:00:35 -0500659 __le16 autoWake;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000660} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
Dan Williams99590ff2009-01-24 09:12:58 -0500662typedef struct StatusRid StatusRid;
663struct StatusRid {
Al Viro329e2c02007-12-20 22:58:57 -0500664 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 u8 mac[ETH_ALEN];
Al Viro329e2c02007-12-20 22:58:57 -0500666 __le16 mode;
667 __le16 errorCode;
668 __le16 sigQuality;
669 __le16 SSIDlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 char SSID[32];
671 char apName[16];
672 u8 bssid[4][ETH_ALEN];
Al Viro329e2c02007-12-20 22:58:57 -0500673 __le16 beaconPeriod;
674 __le16 dimPeriod;
675 __le16 atimDuration;
676 __le16 hopPeriod;
677 __le16 channelSet;
678 __le16 channel;
679 __le16 hopsToBackbone;
680 __le16 apTotalLoad;
681 __le16 generatedLoad;
682 __le16 accumulatedArl;
683 __le16 signalQuality;
684 __le16 currentXmitRate;
685 __le16 apDevExtensions;
686 __le16 normalizedSignalStrength;
687 __le16 shortPreamble;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 u8 apIP[4];
689 u8 noisePercent; /* Noise percent in last second */
690 u8 noisedBm; /* Noise dBm in last second */
691 u8 noiseAvePercent; /* Noise percent in last minute */
692 u8 noiseAvedBm; /* Noise dBm in last minute */
693 u8 noiseMaxPercent; /* Highest noise percent in last minute */
694 u8 noiseMaxdBm; /* Highest noise dbm in last minute */
Al Viro329e2c02007-12-20 22:58:57 -0500695 __le16 load;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 u8 carrier[4];
Al Viro329e2c02007-12-20 22:58:57 -0500697 __le16 assocStatus;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698#define STAT_NOPACKETS 0
699#define STAT_NOCARRIERSET 10
700#define STAT_GOTCARRIERSET 11
701#define STAT_WRONGSSID 20
702#define STAT_BADCHANNEL 25
703#define STAT_BADBITRATES 30
704#define STAT_BADPRIVACY 35
705#define STAT_APFOUND 40
706#define STAT_APREJECTED 50
707#define STAT_AUTHENTICATING 60
708#define STAT_DEAUTHENTICATED 61
709#define STAT_AUTHTIMEOUT 62
710#define STAT_ASSOCIATING 70
711#define STAT_DEASSOCIATED 71
712#define STAT_ASSOCTIMEOUT 72
713#define STAT_NOTAIROAP 73
714#define STAT_ASSOCIATED 80
715#define STAT_LEAPING 90
716#define STAT_LEAPFAILED 91
717#define STAT_LEAPTIMEDOUT 92
718#define STAT_LEAPCOMPLETE 93
Eric Dumazetba2d3582010-06-02 18:10:09 +0000719} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
Dan Williams99590ff2009-01-24 09:12:58 -0500721typedef struct StatsRid StatsRid;
722struct StatsRid {
Al Viroa23ace52007-12-19 22:24:16 -0500723 __le16 len;
724 __le16 spacer;
725 __le32 vals[100];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000726} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
Dan Williams99590ff2009-01-24 09:12:58 -0500728typedef struct APListRid APListRid;
729struct APListRid {
Al Viroa7497162007-12-20 17:49:41 -0500730 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 u8 ap[4][ETH_ALEN];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000732} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
Dan Williams99590ff2009-01-24 09:12:58 -0500734typedef struct CapabilityRid CapabilityRid;
735struct CapabilityRid {
Al Viro56d81bd2007-12-20 17:18:35 -0500736 __le16 len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 char oui[3];
738 char zero;
Al Viro56d81bd2007-12-20 17:18:35 -0500739 __le16 prodNum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 char manName[32];
741 char prodName[16];
742 char prodVer[8];
743 char factoryAddr[ETH_ALEN];
744 char aironetAddr[ETH_ALEN];
Al Viro56d81bd2007-12-20 17:18:35 -0500745 __le16 radioType;
746 __le16 country;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 char callid[ETH_ALEN];
748 char supportedRates[8];
749 char rxDiversity;
750 char txDiversity;
Al Viro56d81bd2007-12-20 17:18:35 -0500751 __le16 txPowerLevels[8];
752 __le16 hardVer;
753 __le16 hardCap;
754 __le16 tempRange;
755 __le16 softVer;
756 __le16 softSubVer;
757 __le16 interfaceVer;
758 __le16 softCap;
759 __le16 bootBlockVer;
760 __le16 requiredHard;
761 __le16 extSoftCap;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000762} __packed;
Dan Williams3c304952006-04-15 12:26:18 -0400763
764/* Only present on firmware >= 5.30.17 */
Dan Williams99590ff2009-01-24 09:12:58 -0500765typedef struct BSSListRidExtra BSSListRidExtra;
766struct BSSListRidExtra {
Al Viro17e70492007-12-19 18:56:37 -0500767 __le16 unknown[4];
Dan Williams3c304952006-04-15 12:26:18 -0400768 u8 fixed[12]; /* WLAN management frame */
769 u8 iep[624];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000770} __packed;
Dan Williams3c304952006-04-15 12:26:18 -0400771
Dan Williams99590ff2009-01-24 09:12:58 -0500772typedef struct BSSListRid BSSListRid;
773struct BSSListRid {
Al Viro17e70492007-12-19 18:56:37 -0500774 __le16 len;
775 __le16 index; /* First is 0 and 0xffff means end of list */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776#define RADIO_FH 1 /* Frequency hopping radio type */
777#define RADIO_DS 2 /* Direct sequence radio type */
778#define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */
Al Viro17e70492007-12-19 18:56:37 -0500779 __le16 radioType;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 u8 bssid[ETH_ALEN]; /* Mac address of the BSS */
781 u8 zero;
782 u8 ssidLen;
783 u8 ssid[32];
Al Viro17e70492007-12-19 18:56:37 -0500784 __le16 dBm;
785#define CAP_ESS cpu_to_le16(1<<0)
786#define CAP_IBSS cpu_to_le16(1<<1)
787#define CAP_PRIVACY cpu_to_le16(1<<4)
788#define CAP_SHORTHDR cpu_to_le16(1<<5)
789 __le16 cap;
790 __le16 beaconInterval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 u8 rates[8]; /* Same as rates for config rid */
792 struct { /* For frequency hopping only */
Al Viro17e70492007-12-19 18:56:37 -0500793 __le16 dwell;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 u8 hopSet;
795 u8 hopPattern;
796 u8 hopIndex;
797 u8 fill;
798 } fh;
Al Viro17e70492007-12-19 18:56:37 -0500799 __le16 dsChannel;
800 __le16 atimWindow;
Dan Williams3c304952006-04-15 12:26:18 -0400801
802 /* Only present on firmware >= 5.30.17 */
803 BSSListRidExtra extra;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000804} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
806typedef struct {
Dan Williams9e75af32006-03-16 13:46:29 -0500807 BSSListRid bss;
808 struct list_head list;
809} BSSListElement;
810
Dan Williams99590ff2009-01-24 09:12:58 -0500811typedef struct tdsRssiEntry tdsRssiEntry;
812struct tdsRssiEntry {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 u8 rssipct;
814 u8 rssidBm;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000815} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
Dan Williams99590ff2009-01-24 09:12:58 -0500817typedef struct tdsRssiRid tdsRssiRid;
818struct tdsRssiRid {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 u16 len;
820 tdsRssiEntry x[256];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000821} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
Dan Williams99590ff2009-01-24 09:12:58 -0500823typedef struct MICRid MICRid;
824struct MICRid {
825 __le16 len;
826 __le16 state;
827 __le16 multicastValid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 u8 multicast[16];
Dan Williams99590ff2009-01-24 09:12:58 -0500829 __le16 unicastValid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 u8 unicast[16];
Eric Dumazetba2d3582010-06-02 18:10:09 +0000831} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
Dan Williams99590ff2009-01-24 09:12:58 -0500833typedef struct MICBuffer MICBuffer;
834struct MICBuffer {
Al Viro593c2b92007-12-17 15:09:34 -0500835 __be16 typelen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
837 union {
838 u8 snap[8];
839 struct {
840 u8 dsap;
841 u8 ssap;
842 u8 control;
843 u8 orgcode[3];
844 u8 fieldtype[2];
845 } llc;
846 } u;
Al Viro593c2b92007-12-17 15:09:34 -0500847 __be32 mic;
848 __be32 seq;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000849} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850
851typedef struct {
852 u8 da[ETH_ALEN];
853 u8 sa[ETH_ALEN];
854} etherHead;
855
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856#define TXCTL_TXOK (1<<1) /* report if tx is ok */
857#define TXCTL_TXEX (1<<2) /* report if tx fails */
858#define TXCTL_802_3 (0<<3) /* 802.3 packet */
859#define TXCTL_802_11 (1<<3) /* 802.11 mac packet */
860#define TXCTL_ETHERNET (0<<4) /* payload has ethertype */
861#define TXCTL_LLC (1<<4) /* payload is llc */
862#define TXCTL_RELEASE (0<<5) /* release after completion */
863#define TXCTL_NORELEASE (1<<5) /* on completion returns to host */
864
865#define BUSY_FID 0x10000
866
867#ifdef CISCO_EXT
868#define AIROMAGIC 0xa55a
869/* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */
870#ifdef SIOCIWFIRSTPRIV
871#ifdef SIOCDEVPRIVATE
872#define AIROOLDIOCTL SIOCDEVPRIVATE
873#define AIROOLDIDIFC AIROOLDIOCTL + 1
874#endif /* SIOCDEVPRIVATE */
875#else /* SIOCIWFIRSTPRIV */
876#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
877#endif /* SIOCIWFIRSTPRIV */
878/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably
879 * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root
880 * only and don't return the modified struct ifreq to the application which
881 * is usually a problem. - Jean II */
882#define AIROIOCTL SIOCIWFIRSTPRIV
883#define AIROIDIFC AIROIOCTL + 1
884
885/* Ioctl constants to be used in airo_ioctl.command */
886
887#define AIROGCAP 0 // Capability rid
888#define AIROGCFG 1 // USED A LOT
889#define AIROGSLIST 2 // System ID list
890#define AIROGVLIST 3 // List of specified AP's
891#define AIROGDRVNAM 4 // NOTUSED
892#define AIROGEHTENC 5 // NOTUSED
893#define AIROGWEPKTMP 6
894#define AIROGWEPKNV 7
895#define AIROGSTAT 8
896#define AIROGSTATSC32 9
897#define AIROGSTATSD32 10
898#define AIROGMICRID 11
899#define AIROGMICSTATS 12
900#define AIROGFLAGS 13
901#define AIROGID 14
902#define AIRORRID 15
903#define AIRORSWVERSION 17
904
905/* Leave gap of 40 commands after AIROGSTATSD32 for future */
906
907#define AIROPCAP AIROGSTATSD32 + 40
908#define AIROPVLIST AIROPCAP + 1
909#define AIROPSLIST AIROPVLIST + 1
910#define AIROPCFG AIROPSLIST + 1
911#define AIROPSIDS AIROPCFG + 1
912#define AIROPAPLIST AIROPSIDS + 1
913#define AIROPMACON AIROPAPLIST + 1 /* Enable mac */
914#define AIROPMACOFF AIROPMACON + 1 /* Disable mac */
915#define AIROPSTCLR AIROPMACOFF + 1
916#define AIROPWEPKEY AIROPSTCLR + 1
917#define AIROPWEPKEYNV AIROPWEPKEY + 1
918#define AIROPLEAPPWD AIROPWEPKEYNV + 1
919#define AIROPLEAPUSR AIROPLEAPPWD + 1
920
921/* Flash codes */
922
923#define AIROFLSHRST AIROPWEPKEYNV + 40
924#define AIROFLSHGCHR AIROFLSHRST + 1
925#define AIROFLSHSTFL AIROFLSHGCHR + 1
926#define AIROFLSHPCHR AIROFLSHSTFL + 1
927#define AIROFLPUTBUF AIROFLSHPCHR + 1
928#define AIRORESTART AIROFLPUTBUF + 1
929
930#define FLASHSIZE 32768
931#define AUXMEMSIZE (256 * 1024)
932
933typedef struct aironet_ioctl {
934 unsigned short command; // What to do
935 unsigned short len; // Len of data
936 unsigned short ridnum; // rid number
937 unsigned char __user *data; // d-data
938} aironet_ioctl;
939
Stephen Hemminger7a374d82010-09-01 18:17:21 -0700940static const char swversion[] = "2.1";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941#endif /* CISCO_EXT */
942
943#define NUM_MODULES 2
944#define MIC_MSGLEN_MAX 2400
945#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX
Dan Williams15db2762006-03-16 13:46:27 -0500946#define AIRO_DEF_MTU 2312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947
948typedef struct {
949 u32 size; // size
950 u8 enabled; // MIC enabled or not
951 u32 rxSuccess; // successful packets received
952 u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison
953 u32 rxNotMICed; // pkts dropped due to not being MIC'd
954 u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed
955 u32 rxWrongSequence; // pkts dropped due to sequence number violation
956 u32 reserve[32];
957} mic_statistics;
958
959typedef struct {
Ard Biesheuvele5db0ad2019-06-17 10:43:38 +0200960 __be32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 u64 accum; // accumulated mic, reduced to u32 in final()
962 int position; // current position (byte offset) in message
963 union {
964 u8 d8[4];
Al Viro593c2b92007-12-17 15:09:34 -0500965 __be32 d32;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 } part; // saves partial message word across update() calls
967} emmh32_context;
968
969typedef struct {
970 emmh32_context seed; // Context - the seed
971 u32 rx; // Received sequence number
972 u32 tx; // Tx sequence number
973 u32 window; // Start of window
974 u8 valid; // Flag to say if context is valid or not
975 u8 key[16];
976} miccntx;
977
978typedef struct {
979 miccntx mCtx; // Multicast context
980 miccntx uCtx; // Unicast context
981} mic_module;
982
983typedef struct {
984 unsigned int rid: 16;
985 unsigned int len: 15;
986 unsigned int valid: 1;
987 dma_addr_t host_addr;
988} Rid;
989
990typedef struct {
991 unsigned int offset: 15;
992 unsigned int eoc: 1;
993 unsigned int len: 15;
994 unsigned int valid: 1;
995 dma_addr_t host_addr;
996} TxFid;
997
Dan Williamsf55d4512009-01-24 09:04:12 -0500998struct rx_hdr {
999 __le16 status, len;
1000 u8 rssi[2];
1001 u8 rate;
1002 u8 freq;
1003 __le16 tmp[4];
Eric Dumazetba2d3582010-06-02 18:10:09 +00001004} __packed;
Dan Williamsf55d4512009-01-24 09:04:12 -05001005
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006typedef struct {
1007 unsigned int ctl: 15;
1008 unsigned int rdy: 1;
1009 unsigned int len: 15;
1010 unsigned int valid: 1;
1011 dma_addr_t host_addr;
1012} RxFid;
1013
1014/*
1015 * Host receive descriptor
1016 */
1017typedef struct {
1018 unsigned char __iomem *card_ram_off; /* offset into card memory of the
1019 desc */
1020 RxFid rx_desc; /* card receive descriptor */
1021 char *virtual_host_addr; /* virtual address of host receive
1022 buffer */
1023 int pending;
1024} HostRxDesc;
1025
1026/*
1027 * Host transmit descriptor
1028 */
1029typedef struct {
1030 unsigned char __iomem *card_ram_off; /* offset into card memory of the
1031 desc */
1032 TxFid tx_desc; /* card transmit descriptor */
1033 char *virtual_host_addr; /* virtual address of host receive
1034 buffer */
1035 int pending;
1036} HostTxDesc;
1037
1038/*
1039 * Host RID descriptor
1040 */
1041typedef struct {
1042 unsigned char __iomem *card_ram_off; /* offset into card memory of the
1043 descriptor */
1044 Rid rid_desc; /* card RID descriptor */
1045 char *virtual_host_addr; /* virtual address of host receive
1046 buffer */
1047} HostRidDesc;
1048
1049typedef struct {
1050 u16 sw0;
1051 u16 sw1;
1052 u16 status;
1053 u16 len;
1054#define HOST_SET (1 << 0)
1055#define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */
1056#define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */
1057#define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */
1058#define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */
1059#define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */
1060#define HOST_CLR_AID (1 << 7) /* clear AID failure */
1061#define HOST_RTS (1 << 9) /* Force RTS use */
1062#define HOST_SHORT (1 << 10) /* Do short preamble */
1063 u16 ctl;
1064 u16 aid;
1065 u16 retries;
1066 u16 fill;
1067} TxCtlHdr;
1068
1069typedef struct {
1070 u16 ctl;
1071 u16 duration;
1072 char addr1[6];
1073 char addr2[6];
1074 char addr3[6];
1075 u16 seq;
1076 char addr4[6];
1077} WifiHdr;
1078
1079
1080typedef struct {
1081 TxCtlHdr ctlhdr;
1082 u16 fill1;
1083 u16 fill2;
1084 WifiHdr wifihdr;
1085 u16 gaplen;
1086 u16 status;
1087} WifiCtlHdr;
1088
Jouni Malinenff1d2762005-05-12 22:54:16 -04001089static WifiCtlHdr wifictlhdr8023 = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 .ctlhdr = {
1091 .ctl = HOST_DONT_RLSE,
1092 }
1093};
1094
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095// A few details needed for WEP (Wireless Equivalent Privacy)
1096#define MAX_KEY_SIZE 13 // 128 (?) bits
1097#define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP
1098typedef struct wep_key_t {
1099 u16 len;
1100 u8 key[16]; /* 40-bit and 104-bit keys */
1101} wep_key_t;
1102
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103/* List of Wireless Handlers (new API) */
1104static const struct iw_handler_def airo_handler_def;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105
1106static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)";
1107
1108struct airo_info;
1109
Lee Jonesba4d6512020-08-14 12:39:15 +01001110static int get_dec_u16(char *buffer, int *start, int limit);
1111static void OUT4500(struct airo_info *, u16 reg, u16 value);
1112static unsigned short IN4500(struct airo_info *, u16 reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113static u16 setup_card(struct airo_info*, u8 *mac, int lock);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02001114static int enable_MAC(struct airo_info *ai, int lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115static void disable_MAC(struct airo_info *ai, int lock);
1116static void enable_interrupts(struct airo_info*);
1117static void disable_interrupts(struct airo_info*);
1118static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp);
1119static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001120static int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001122static int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 int whichbap);
Al Virob8c06bc2007-12-19 17:55:43 -05001124static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 int whichbap);
1126static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd);
1127static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock);
1128static int PC4500_writerid(struct airo_info*, u16 rid, const void
1129 *pBuf, int len, int lock);
Lee Jonesba4d6512020-08-14 12:39:15 +01001130static int do_writerid(struct airo_info*, u16 rid, const void *rid_data,
1131 int len, int dummy);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw);
1133static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket);
1134static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket);
1135
Lee Jonesba4d6512020-08-14 12:39:15 +01001136static int mpi_send_packet(struct net_device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137static void mpi_unmap_card(struct pci_dev *pci);
1138static void mpi_receive_802_3(struct airo_info *ai);
1139static void mpi_receive_802_11(struct airo_info *ai);
Lee Jonesba4d6512020-08-14 12:39:15 +01001140static int waitbusy(struct airo_info *ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141
Lee Jonesba4d6512020-08-14 12:39:15 +01001142static irqreturn_t airo_interrupt(int irq, void* dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143static int airo_thread(void *data);
Lee Jonesba4d6512020-08-14 12:39:15 +01001144static void timer_func(struct net_device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
Lee Jonesba4d6512020-08-14 12:39:15 +01001146static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev);
1147static void airo_read_wireless_stats(struct airo_info *local);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148#ifdef CISCO_EXT
1149static int readrids(struct net_device *dev, aironet_ioctl *comp);
1150static int writerids(struct net_device *dev, aironet_ioctl *comp);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001151static int flashcard(struct net_device *dev, aironet_ioctl *comp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152#endif /* CISCO_EXT */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153static void micinit(struct airo_info *ai);
1154static int micsetup(struct airo_info *ai);
1155static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len);
1156static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen);
1157
Lee Jonesba4d6512020-08-14 12:39:15 +01001158static u8 airo_rssi_to_dbm(tdsRssiEntry *rssi_rid, u8 rssi);
1159static u8 airo_dbm_to_pct(tdsRssiEntry *rssi_rid, u8 dbm);
Dan Williams41480af2005-05-10 09:45:51 -04001160
Dan Williams9e75af32006-03-16 13:46:29 -05001161static void airo_networks_free(struct airo_info *ai);
1162
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163struct airo_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 struct net_device *dev;
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01001165 struct list_head dev_list;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we
1167 use the high bit to mark whether it is in use. */
1168#define MAX_FIDS 6
1169#define MPI_MAX_FIDS 1
Hannes Eder9e05a2d2009-02-14 11:49:26 +00001170 u32 fids[MAX_FIDS];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 ConfigRid config;
1172 char keyindex; // Used with auto wep
1173 char defindex; // Used with auto wep
1174 struct proc_dir_entry *proc_entry;
1175 spinlock_t aux_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176#define FLAG_RADIO_OFF 0 /* User disabling of MAC */
1177#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */
1178#define FLAG_RADIO_MASK 0x03
1179#define FLAG_ENABLED 2
1180#define FLAG_ADHOC 3 /* Needed by MIC */
1181#define FLAG_MIC_CAPABLE 4
1182#define FLAG_UPDATE_MULTI 5
1183#define FLAG_UPDATE_UNI 6
1184#define FLAG_802_11 7
Dan Williams3c304952006-04-15 12:26:18 -04001185#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186#define FLAG_PENDING_XMIT 9
1187#define FLAG_PENDING_XMIT11 10
1188#define FLAG_MPI 11
1189#define FLAG_REGISTERED 12
1190#define FLAG_COMMIT 13
1191#define FLAG_RESET 14
1192#define FLAG_FLASHING 15
Dan Williams3c304952006-04-15 12:26:18 -04001193#define FLAG_WPA_CAPABLE 16
1194 unsigned long flags;
1195#define JOB_DIE 0
1196#define JOB_XMIT 1
1197#define JOB_XMIT11 2
1198#define JOB_STATS 3
1199#define JOB_PROMISC 4
1200#define JOB_MIC 5
1201#define JOB_EVENT 6
1202#define JOB_AUTOWEP 7
1203#define JOB_WSTATS 8
1204#define JOB_SCAN_RESULTS 9
1205 unsigned long jobs;
Al Virob8c06bc2007-12-19 17:55:43 -05001206 int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 int whichbap);
1208 unsigned short *flash;
1209 tdsRssiEntry *rssi;
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001210 struct task_struct *list_bss_task;
1211 struct task_struct *airo_thread_task;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 struct semaphore sem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 wait_queue_head_t thr_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 unsigned long expires;
1215 struct {
1216 struct sk_buff *skb;
1217 int fid;
1218 } xmit, xmit11;
1219 struct net_device *wifidev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 struct iw_statistics wstats; // wireless stats
Dan Williams9e75af32006-03-16 13:46:29 -05001221 unsigned long scan_timeout; /* Time scan should be read */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 struct iw_spy_data spy_data;
1223 struct iw_public_data wireless_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 /* MIC stuff */
Ard Biesheuvele5db0ad2019-06-17 10:43:38 +02001225 struct crypto_sync_skcipher *tfm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 mic_module mod[2];
1227 mic_statistics micstats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors
1229 HostTxDesc txfids[MPI_MAX_FIDS];
1230 HostRidDesc config_desc;
1231 unsigned long ridbus; // phys addr of config_desc
1232 struct sk_buff_head txq;// tx queue used by mpi350 code
1233 struct pci_dev *pci;
1234 unsigned char __iomem *pcimem;
1235 unsigned char __iomem *pciaux;
1236 unsigned char *shared;
1237 dma_addr_t shared_dma;
Pavel Machek1cc68ae2005-06-20 15:33:04 -07001238 pm_message_t power;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 SsidRid *SSID;
Ondrej Zaryf675f932015-10-16 21:04:13 +02001240 APListRid APList;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
1242 char proc_name[IFNAMSIZ];
Dan Williams9e75af32006-03-16 13:46:29 -05001243
Dan Williams138c0c62009-01-24 09:11:35 -05001244 int wep_capable;
1245 int max_wep_idx;
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02001246 int last_auth;
Dan Williams138c0c62009-01-24 09:11:35 -05001247
Dan Williams3c304952006-04-15 12:26:18 -04001248 /* WPA-related stuff */
1249 unsigned int bssListFirst;
1250 unsigned int bssListNext;
1251 unsigned int bssListRidLen;
1252
Dan Williams9e75af32006-03-16 13:46:29 -05001253 struct list_head network_list;
1254 struct list_head network_free_list;
1255 BSSListElement *networks;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256};
1257
Al Virob8c06bc2007-12-19 17:55:43 -05001258static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen,
1259 int whichbap)
1260{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 return ai->bap_read(ai, pu16Dst, bytelen, whichbap);
1262}
1263
Lee Jonesba4d6512020-08-14 12:39:15 +01001264static int setup_proc_entry(struct net_device *dev,
1265 struct airo_info *apriv);
1266static int takedown_proc_entry(struct net_device *dev,
1267 struct airo_info *apriv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268
Jouni Malinenff1d2762005-05-12 22:54:16 -04001269static int cmdreset(struct airo_info *ai);
Lee Jonesba4d6512020-08-14 12:39:15 +01001270static int setflashmode(struct airo_info *ai);
1271static int flashgchar(struct airo_info *ai, int matchbyte, int dwelltime);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001272static int flashputbuf(struct airo_info *ai);
Lee Jonesba4d6512020-08-14 12:39:15 +01001273static int flashrestart(struct airo_info *ai, struct net_device *dev);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001274
Dan Williams934d8bf2006-03-16 13:46:23 -05001275#define airo_print(type, name, fmt, args...) \
Michal Schmidt1138c372007-06-29 15:33:41 +02001276 printk(type DRV_NAME "(%s): " fmt "\n", name, ##args)
Dan Williams934d8bf2006-03-16 13:46:23 -05001277
1278#define airo_print_info(name, fmt, args...) \
1279 airo_print(KERN_INFO, name, fmt, ##args)
1280
1281#define airo_print_dbg(name, fmt, args...) \
1282 airo_print(KERN_DEBUG, name, fmt, ##args)
1283
1284#define airo_print_warn(name, fmt, args...) \
1285 airo_print(KERN_WARNING, name, fmt, ##args)
1286
1287#define airo_print_err(name, fmt, args...) \
1288 airo_print(KERN_ERR, name, fmt, ##args)
1289
Wang Chenfaf39942008-10-14 13:30:33 +08001290#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash)
Dan Williams934d8bf2006-03-16 13:46:23 -05001291
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292/***********************************************************************
1293 * MIC ROUTINES *
1294 ***********************************************************************
1295 */
1296
Lee Jonesba4d6512020-08-14 12:39:15 +01001297static int RxSeqValid(struct airo_info *ai, miccntx *context, int mcast, u32 micSeq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298static void MoveWindow(miccntx *context, u32 micSeq);
Herbert Xuf12cc202006-08-22 20:36:13 +10001299static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
Ard Biesheuvele5db0ad2019-06-17 10:43:38 +02001300 struct crypto_sync_skcipher *tfm);
Jouni Malinenff1d2762005-05-12 22:54:16 -04001301static void emmh32_init(emmh32_context *context);
1302static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
1303static void emmh32_final(emmh32_context *context, u8 digest[4]);
Lee Jonesba4d6512020-08-14 12:39:15 +01001304static int flashpchar(struct airo_info *ai, int byte, int dwelltime);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305
Dan Williams018697d2009-01-24 09:13:32 -05001306static void age_mic_context(miccntx *cur, miccntx *old, u8 *key, int key_len,
Ard Biesheuvele5db0ad2019-06-17 10:43:38 +02001307 struct crypto_sync_skcipher *tfm)
Dan Williams018697d2009-01-24 09:13:32 -05001308{
1309 /* If the current MIC context is valid and its key is the same as
1310 * the MIC register, there's nothing to do.
1311 */
1312 if (cur->valid && (memcmp(cur->key, key, key_len) == 0))
1313 return;
1314
1315 /* Age current mic Context */
1316 memcpy(old, cur, sizeof(*cur));
1317
1318 /* Initialize new context */
1319 memcpy(cur->key, key, key_len);
1320 cur->window = 33; /* Window always points to the middle */
1321 cur->rx = 0; /* Rx Sequence numbers */
1322 cur->tx = 0; /* Tx sequence numbers */
1323 cur->valid = 1; /* Key is now valid */
1324
1325 /* Give key to mic seed */
1326 emmh32_setseed(&cur->seed, key, key_len, tfm);
1327}
1328
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329/* micinit - Initialize mic seed */
1330
1331static void micinit(struct airo_info *ai)
1332{
1333 MICRid mic_rid;
1334
Dan Williams3c304952006-04-15 12:26:18 -04001335 clear_bit(JOB_MIC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
1337 up(&ai->sem);
1338
Dan Williams018697d2009-01-24 09:13:32 -05001339 ai->micstats.enabled = (le16_to_cpu(mic_rid.state) & 0x00FF) ? 1 : 0;
1340 if (!ai->micstats.enabled) {
1341 /* So next time we have a valid key and mic is enabled, we will
1342 * update the sequence number if the key is the same as before.
1343 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 ai->mod[0].uCtx.valid = 0;
1345 ai->mod[0].mCtx.valid = 0;
Dan Williams018697d2009-01-24 09:13:32 -05001346 return;
1347 }
1348
1349 if (mic_rid.multicastValid) {
1350 age_mic_context(&ai->mod[0].mCtx, &ai->mod[1].mCtx,
1351 mic_rid.multicast, sizeof(mic_rid.multicast),
1352 ai->tfm);
1353 }
1354
1355 if (mic_rid.unicastValid) {
1356 age_mic_context(&ai->mod[0].uCtx, &ai->mod[1].uCtx,
1357 mic_rid.unicast, sizeof(mic_rid.unicast),
1358 ai->tfm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 }
1360}
1361
1362/* micsetup - Get ready for business */
1363
Lee Jonesba4d6512020-08-14 12:39:15 +01001364static int micsetup(struct airo_info *ai)
1365{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 int i;
1367
1368 if (ai->tfm == NULL)
Ard Biesheuvele5db0ad2019-06-17 10:43:38 +02001369 ai->tfm = crypto_alloc_sync_skcipher("ctr(aes)", 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
Herbert Xuf12cc202006-08-22 20:36:13 +10001371 if (IS_ERR(ai->tfm)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05001372 airo_print_err(ai->dev->name, "failed to load transform for AES");
Herbert Xuf12cc202006-08-22 20:36:13 +10001373 ai->tfm = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 return ERROR;
1375 }
1376
Lee Jonesba4d6512020-08-14 12:39:15 +01001377 for (i = 0; i < NUM_MODULES; i++) {
1378 memset(&ai->mod[i].mCtx, 0, sizeof(miccntx));
1379 memset(&ai->mod[i].uCtx, 0, sizeof(miccntx));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 }
1381 return SUCCESS;
1382}
1383
Lee Jonesba4d6512020-08-14 12:39:15 +01001384static const u8 micsnap[] = {0xAA, 0xAA, 0x03, 0x00, 0x40, 0x96, 0x00, 0x02};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385
1386/*===========================================================================
1387 * Description: Mic a packet
Lee Jonesba4d6512020-08-14 12:39:15 +01001388 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 * Inputs: etherHead * pointer to an 802.3 frame
Lee Jonesba4d6512020-08-14 12:39:15 +01001390 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 * Returns: BOOLEAN if successful, otherwise false.
1392 * PacketTxLen will be updated with the mic'd packets size.
1393 *
1394 * Caveats: It is assumed that the frame buffer will already
1395 * be big enough to hold the largets mic message possible.
1396 * (No memory allocation is done here).
Lee Jonesba4d6512020-08-14 12:39:15 +01001397 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 * Author: sbraneky (10/15/01)
1399 * Merciless hacks by rwilcher (1/14/02)
1400 */
1401
Lee Jonesba4d6512020-08-14 12:39:15 +01001402static int encapsulate(struct airo_info *ai, etherHead *frame, MICBuffer *mic, int payLen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403{
1404 miccntx *context;
1405
1406 // Determine correct context
1407 // If not adhoc, always use unicast key
1408
1409 if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1))
1410 context = &ai->mod[0].mCtx;
1411 else
1412 context = &ai->mod[0].uCtx;
Lee Jonesba4d6512020-08-14 12:39:15 +01001413
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 if (!context->valid)
1415 return ERROR;
1416
1417 mic->typelen = htons(payLen + 16); //Length of Mic'd packet
1418
1419 memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap
1420
1421 // Add Tx sequence
1422 mic->seq = htonl(context->tx);
1423 context->tx += 2;
1424
1425 emmh32_init(&context->seed); // Mic the packet
Lee Jonesba4d6512020-08-14 12:39:15 +01001426 emmh32_update(&context->seed, frame->da, ETH_ALEN * 2); // DA, SA
1427 emmh32_update(&context->seed, (u8*)&mic->typelen, 10); // Type/Length and Snap
1428 emmh32_update(&context->seed, (u8*)&mic->seq, sizeof(mic->seq)); //SEQ
1429 emmh32_update(&context->seed, (u8*)(frame + 1), payLen); //payload
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 emmh32_final(&context->seed, (u8*)&mic->mic);
1431
1432 /* New Type/length ?????????? */
1433 mic->typelen = 0; //Let NIC know it could be an oversized packet
1434 return SUCCESS;
1435}
1436
1437typedef enum {
1438 NONE,
1439 NOMIC,
1440 NOMICPLUMMED,
1441 SEQUENCE,
1442 INCORRECTMIC,
1443} mic_error;
1444
1445/*===========================================================================
1446 * Description: Decapsulates a MIC'd packet and returns the 802.3 packet
1447 * (removes the MIC stuff) if packet is a valid packet.
Lee Jonesba4d6512020-08-14 12:39:15 +01001448 *
1449 * Inputs: etherHead pointer to the 802.3 packet
1450 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE
Lee Jonesba4d6512020-08-14 12:39:15 +01001452 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 * Author: sbraneky (10/15/01)
1454 * Merciless hacks by rwilcher (1/14/02)
1455 *---------------------------------------------------------------------------
1456 */
1457
1458static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen)
1459{
1460 int i;
1461 u32 micSEQ;
1462 miccntx *context;
1463 u8 digest[4];
1464 mic_error micError = NONE;
1465
1466 // Check if the packet is a Mic'd packet
1467
1468 if (!ai->micstats.enabled) {
1469 //No Mic set or Mic OFF but we received a MIC'd packet.
1470 if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) {
1471 ai->micstats.rxMICPlummed++;
1472 return ERROR;
1473 }
1474 return SUCCESS;
1475 }
1476
1477 if (ntohs(mic->typelen) == 0x888E)
1478 return SUCCESS;
1479
1480 if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) {
1481 // Mic enabled but packet isn't Mic'd
1482 ai->micstats.rxMICPlummed++;
1483 return ERROR;
1484 }
1485
1486 micSEQ = ntohl(mic->seq); //store SEQ as CPU order
1487
1488 //At this point we a have a mic'd packet and mic is enabled
1489 //Now do the mic error checking.
1490
1491 //Receive seq must be odd
Lee Jonesba4d6512020-08-14 12:39:15 +01001492 if ((micSEQ & 1) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 ai->micstats.rxWrongSequence++;
1494 return ERROR;
1495 }
1496
1497 for (i = 0; i < NUM_MODULES; i++) {
1498 int mcast = eth->da[0] & 1;
Lee Jonesba4d6512020-08-14 12:39:15 +01001499 //Determine proper context
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx;
Lee Jonesba4d6512020-08-14 12:39:15 +01001501
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 //Make sure context is valid
1503 if (!context->valid) {
1504 if (i == 0)
1505 micError = NOMICPLUMMED;
Lee Jonesba4d6512020-08-14 12:39:15 +01001506 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 }
Lee Jonesba4d6512020-08-14 12:39:15 +01001508 //DeMic it
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509
1510 if (!mic->typelen)
1511 mic->typelen = htons(payLen + sizeof(MICBuffer) - 2);
Lee Jonesba4d6512020-08-14 12:39:15 +01001512
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 emmh32_init(&context->seed);
Lee Jonesba4d6512020-08-14 12:39:15 +01001514 emmh32_update(&context->seed, eth->da, ETH_ALEN*2);
1515 emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap));
1516 emmh32_update(&context->seed, (u8 *)&mic->seq, sizeof(mic->seq));
1517 emmh32_update(&context->seed, (u8 *)(eth + 1), payLen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 //Calculate MIC
1519 emmh32_final(&context->seed, digest);
Lee Jonesba4d6512020-08-14 12:39:15 +01001520
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match
1522 //Invalid Mic
1523 if (i == 0)
1524 micError = INCORRECTMIC;
1525 continue;
1526 }
1527
1528 //Check Sequence number if mics pass
1529 if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) {
1530 ai->micstats.rxSuccess++;
1531 return SUCCESS;
1532 }
1533 if (i == 0)
1534 micError = SEQUENCE;
1535 }
1536
1537 // Update statistics
1538 switch (micError) {
1539 case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break;
1540 case SEQUENCE: ai->micstats.rxWrongSequence++; break;
1541 case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break;
1542 case NONE: break;
1543 case NOMIC: break;
1544 }
1545 return ERROR;
1546}
1547
1548/*===========================================================================
1549 * Description: Checks the Rx Seq number to make sure it is valid
1550 * and hasn't already been received
Lee Jonesba4d6512020-08-14 12:39:15 +01001551 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 * Inputs: miccntx - mic context to check seq against
1553 * micSeq - the Mic seq number
Lee Jonesba4d6512020-08-14 12:39:15 +01001554 *
1555 * Returns: TRUE if valid otherwise FALSE.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 *
1557 * Author: sbraneky (10/15/01)
1558 * Merciless hacks by rwilcher (1/14/02)
1559 *---------------------------------------------------------------------------
1560 */
1561
Lee Jonesba4d6512020-08-14 12:39:15 +01001562static int RxSeqValid(struct airo_info *ai, miccntx *context, int mcast, u32 micSeq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563{
Lee Jonesba4d6512020-08-14 12:39:15 +01001564 u32 seq, index;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565
Lee Jonesba4d6512020-08-14 12:39:15 +01001566 //Allow for the ap being rebooted - if it is then use the next
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 //sequence number of the current sequence number - might go backwards
1568
1569 if (mcast) {
1570 if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) {
1571 clear_bit (FLAG_UPDATE_MULTI, &ai->flags);
1572 context->window = (micSeq > 33) ? micSeq : 33;
1573 context->rx = 0; // Reset rx
1574 }
1575 } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) {
1576 clear_bit (FLAG_UPDATE_UNI, &ai->flags);
1577 context->window = (micSeq > 33) ? micSeq : 33; // Move window
1578 context->rx = 0; // Reset rx
1579 }
1580
1581 //Make sequence number relative to START of window
1582 seq = micSeq - (context->window - 33);
1583
1584 //Too old of a SEQ number to check.
1585 if ((s32)seq < 0)
1586 return ERROR;
Lee Jonesba4d6512020-08-14 12:39:15 +01001587
1588 if (seq > 64) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 //Window is infinite forward
Lee Jonesba4d6512020-08-14 12:39:15 +01001590 MoveWindow(context, micSeq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 return SUCCESS;
1592 }
1593
1594 // We are in the window. Now check the context rx bit to see if it was already sent
1595 seq >>= 1; //divide by 2 because we only have odd numbers
1596 index = 1 << seq; //Get an index number
1597
1598 if (!(context->rx & index)) {
1599 //micSEQ falls inside the window.
1600 //Add seqence number to the list of received numbers.
1601 context->rx |= index;
1602
Lee Jonesba4d6512020-08-14 12:39:15 +01001603 MoveWindow(context, micSeq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
1605 return SUCCESS;
1606 }
1607 return ERROR;
1608}
1609
1610static void MoveWindow(miccntx *context, u32 micSeq)
1611{
1612 u32 shift;
1613
1614 //Move window if seq greater than the middle of the window
1615 if (micSeq > context->window) {
1616 shift = (micSeq - context->window) >> 1;
Lee Jonesba4d6512020-08-14 12:39:15 +01001617
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 //Shift out old
1619 if (shift < 32)
1620 context->rx >>= shift;
1621 else
1622 context->rx = 0;
1623
1624 context->window = micSeq; //Move window
1625 }
1626}
1627
1628/*==============================================*/
1629/*========== EMMH ROUTINES ====================*/
1630/*==============================================*/
1631
1632/* mic accumulate */
1633#define MIC_ACCUM(val) \
Ard Biesheuvele5db0ad2019-06-17 10:43:38 +02001634 context->accum += (u64)(val) * be32_to_cpu(context->coeff[coeff_position++]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635
1636/* expand the key to fill the MMH coefficient array */
Herbert Xuf12cc202006-08-22 20:36:13 +10001637static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
Ard Biesheuvele5db0ad2019-06-17 10:43:38 +02001638 struct crypto_sync_skcipher *tfm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639{
1640 /* take the keying material, expand if necessary, truncate at 16-bytes */
1641 /* run through AES counter mode to generate context->coeff[] */
Lee Jonesba4d6512020-08-14 12:39:15 +01001642
Ard Biesheuvele5db0ad2019-06-17 10:43:38 +02001643 SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
1644 struct scatterlist sg;
1645 u8 iv[AES_BLOCK_SIZE] = {};
1646 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647
Ard Biesheuvele5db0ad2019-06-17 10:43:38 +02001648 crypto_sync_skcipher_setkey(tfm, pkey, 16);
1649
1650 memset(context->coeff, 0, sizeof(context->coeff));
1651 sg_init_one(&sg, context->coeff, sizeof(context->coeff));
1652
1653 skcipher_request_set_sync_tfm(req, tfm);
1654 skcipher_request_set_callback(req, 0, NULL, NULL);
1655 skcipher_request_set_crypt(req, &sg, &sg, sizeof(context->coeff), iv);
1656
1657 ret = crypto_skcipher_encrypt(req);
1658 WARN_ON_ONCE(ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659}
1660
1661/* prepare for calculation of a new mic */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001662static void emmh32_init(emmh32_context *context)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663{
1664 /* prepare for new mic calculation */
1665 context->accum = 0;
1666 context->position = 0;
1667}
1668
1669/* add some bytes to the mic calculation */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001670static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671{
1672 int coeff_position, byte_position;
Lee Jonesba4d6512020-08-14 12:39:15 +01001673
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 if (len == 0) return;
Lee Jonesba4d6512020-08-14 12:39:15 +01001675
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 coeff_position = context->position >> 2;
Lee Jonesba4d6512020-08-14 12:39:15 +01001677
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678 /* deal with partial 32-bit word left over from last update */
1679 byte_position = context->position & 3;
1680 if (byte_position) {
1681 /* have a partial word in part to deal with */
1682 do {
1683 if (len == 0) return;
1684 context->part.d8[byte_position++] = *pOctets++;
1685 context->position++;
1686 len--;
1687 } while (byte_position < 4);
Al Viro593c2b92007-12-17 15:09:34 -05001688 MIC_ACCUM(ntohl(context->part.d32));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 }
1690
1691 /* deal with full 32-bit words */
1692 while (len >= 4) {
Al Viro593c2b92007-12-17 15:09:34 -05001693 MIC_ACCUM(ntohl(*(__be32 *)pOctets));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 context->position += 4;
1695 pOctets += 4;
1696 len -= 4;
1697 }
1698
1699 /* deal with partial 32-bit word that will be left over from this update */
1700 byte_position = 0;
1701 while (len > 0) {
1702 context->part.d8[byte_position++] = *pOctets++;
1703 context->position++;
1704 len--;
1705 }
1706}
1707
1708/* mask used to zero empty bytes for final partial word */
1709static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
1710
1711/* calculate the mic */
Jouni Malinenff1d2762005-05-12 22:54:16 -04001712static void emmh32_final(emmh32_context *context, u8 digest[4])
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713{
1714 int coeff_position, byte_position;
1715 u32 val;
Lee Jonesba4d6512020-08-14 12:39:15 +01001716
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 u64 sum, utmp;
1718 s64 stmp;
1719
1720 coeff_position = context->position >> 2;
Lee Jonesba4d6512020-08-14 12:39:15 +01001721
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 /* deal with partial 32-bit word left over from last update */
1723 byte_position = context->position & 3;
1724 if (byte_position) {
1725 /* have a partial word in part to deal with */
Al Viro593c2b92007-12-17 15:09:34 -05001726 val = ntohl(context->part.d32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */
1728 }
1729
1730 /* reduce the accumulated u64 to a 32-bit MIC */
1731 sum = context->accum;
1732 stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15);
1733 utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15);
1734 sum = utmp & 0xffffffffLL;
1735 if (utmp > 0x10000000fLL)
1736 sum -= 15;
1737
1738 val = (u32)sum;
1739 digest[0] = (val>>24) & 0xFF;
1740 digest[1] = (val>>16) & 0xFF;
1741 digest[2] = (val>>8) & 0xFF;
1742 digest[3] = val & 0xFF;
1743}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744
1745static int readBSSListRid(struct airo_info *ai, int first,
Al Viro17e70492007-12-19 18:56:37 -05001746 BSSListRid *list)
1747{
Dan Williams3c304952006-04-15 12:26:18 -04001748 Cmd cmd;
1749 Resp rsp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750
1751 if (first == 1) {
Dan Williams3c304952006-04-15 12:26:18 -04001752 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
1753 memset(&cmd, 0, sizeof(cmd));
Lee Jonesba4d6512020-08-14 12:39:15 +01001754 cmd.cmd = CMD_LISTBSS;
Dan Williams3c304952006-04-15 12:26:18 -04001755 if (down_interruptible(&ai->sem))
1756 return -ERESTARTSYS;
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001757 ai->list_bss_task = current;
Dan Williams3c304952006-04-15 12:26:18 -04001758 issuecommand(ai, &cmd, &rsp);
1759 up(&ai->sem);
1760 /* Let the command take effect */
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07001761 schedule_timeout_uninterruptible(3 * HZ);
1762 ai->list_bss_task = NULL;
Dan Williams3c304952006-04-15 12:26:18 -04001763 }
Al Viro17e70492007-12-19 18:56:37 -05001764 return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext,
Dan Williams3c304952006-04-15 12:26:18 -04001765 list, ai->bssListRidLen, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766}
1767
Al Viro4293ea32007-12-19 19:21:51 -05001768static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock)
1769{
1770 return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 wkr, sizeof(*wkr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773
Al Viro4293ea32007-12-19 19:21:51 -05001774static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock)
1775{
1776 int rc;
1777 rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock);
1778 if (rc!=SUCCESS)
1779 airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780 if (perm) {
Al Viro4293ea32007-12-19 19:21:51 -05001781 rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock);
1782 if (rc!=SUCCESS)
Dan Williams934d8bf2006-03-16 13:46:23 -05001783 airo_print_err(ai->dev->name, "WEP_PERM set %x", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 }
1785 return rc;
1786}
1787
Al Viro0dd22122007-12-17 16:11:57 -05001788static int readSsidRid(struct airo_info*ai, SsidRid *ssidr)
1789{
1790 return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792
Al Viro0dd22122007-12-17 16:11:57 -05001793static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock)
1794{
1795 return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796}
Al Viro0dd22122007-12-17 16:11:57 -05001797
Al Viro3eb9b412007-12-21 00:00:35 -05001798static int readConfigRid(struct airo_info *ai, int lock)
1799{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801 ConfigRid cfg;
1802
1803 if (ai->config.len)
1804 return SUCCESS;
1805
1806 rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock);
1807 if (rc != SUCCESS)
1808 return rc;
1809
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810 ai->config = cfg;
1811 return SUCCESS;
1812}
Al Viro3eb9b412007-12-21 00:00:35 -05001813
1814static inline void checkThrottle(struct airo_info *ai)
1815{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 int i;
1817/* Old hardware had a limit on encryption speed */
1818 if (ai->config.authType != AUTH_OPEN && maxencrypt) {
Lee Jonesba4d6512020-08-14 12:39:15 +01001819 for (i = 0; i<8; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820 if (ai->config.rates[i] > maxencrypt) {
1821 ai->config.rates[i] = 0;
1822 }
1823 }
1824 }
1825}
Al Viro3eb9b412007-12-21 00:00:35 -05001826
1827static int writeConfigRid(struct airo_info *ai, int lock)
1828{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829 ConfigRid cfgr;
1830
1831 if (!test_bit (FLAG_COMMIT, &ai->flags))
1832 return SUCCESS;
1833
1834 clear_bit (FLAG_COMMIT, &ai->flags);
1835 clear_bit (FLAG_RESET, &ai->flags);
1836 checkThrottle(ai);
1837 cfgr = ai->config;
1838
Al Viro3eb9b412007-12-21 00:00:35 -05001839 if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840 set_bit(FLAG_ADHOC, &ai->flags);
1841 else
1842 clear_bit(FLAG_ADHOC, &ai->flags);
1843
Lee Jonesba4d6512020-08-14 12:39:15 +01001844 return PC4500_writerid(ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846
Al Viro329e2c02007-12-20 22:58:57 -05001847static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock)
1848{
1849 return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850}
Al Viroa7497162007-12-20 17:49:41 -05001851
Al Viroa7497162007-12-20 17:49:41 -05001852static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock)
1853{
1854 return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856
Al Viro56d81bd2007-12-20 17:18:35 -05001857static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock)
1858{
1859 return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861
Al Viroa23ace52007-12-19 22:24:16 -05001862static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock)
1863{
1864 return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865}
1866
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001867static void try_auto_wep(struct airo_info *ai)
1868{
Dan Carpentere9a8e8ec2012-03-05 21:10:07 +03001869 if (auto_wep && !test_bit(FLAG_RADIO_DOWN, &ai->flags)) {
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001870 ai->expires = RUN_AT(3*HZ);
1871 wake_up_interruptible(&ai->thr_wait);
1872 }
1873}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874
Lee Jonesba4d6512020-08-14 12:39:15 +01001875static int airo_open(struct net_device *dev)
1876{
Wang Chenfaf39942008-10-14 13:30:33 +08001877 struct airo_info *ai = dev->ml_priv;
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001878 int rc = 0;
1879
1880 if (test_bit(FLAG_FLASHING, &ai->flags))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 return -EIO;
1882
1883 /* Make sure the card is configured.
1884 * Wireless Extensions may postpone config changes until the card
1885 * is open (to pipeline changes and speed-up card setup). If
Lucas De Marchi25985ed2011-03-30 22:57:33 -03001886 * those changes are not yet committed, do it now - Jean II */
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001887 if (test_bit(FLAG_COMMIT, &ai->flags)) {
1888 disable_MAC(ai, 1);
1889 writeConfigRid(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 }
1891
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001892 if (ai->wifidev != dev) {
1893 clear_bit(JOB_DIE, &ai->jobs);
Kees Cookf1701682013-07-03 15:04:58 -07001894 ai->airo_thread_task = kthread_run(airo_thread, dev, "%s",
1895 dev->name);
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001896 if (IS_ERR(ai->airo_thread_task))
1897 return (int)PTR_ERR(ai->airo_thread_task);
1898
1899 rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED,
1900 dev->name, dev);
1901 if (rc) {
1902 airo_print_err(dev->name,
1903 "register interrupt %d failed, rc %d",
1904 dev->irq, rc);
1905 set_bit(JOB_DIE, &ai->jobs);
1906 kthread_stop(ai->airo_thread_task);
1907 return rc;
1908 }
1909
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910 /* Power on the MAC controller (which may have been disabled) */
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02001911 clear_bit(FLAG_RADIO_DOWN, &ai->flags);
1912 enable_interrupts(ai);
1913
1914 try_auto_wep(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915 }
Michal Schmidt175ec1a2007-06-29 15:33:47 +02001916 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917
1918 netif_start_queue(dev);
1919 return 0;
1920}
1921
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +00001922static netdev_tx_t mpi_start_xmit(struct sk_buff *skb,
1923 struct net_device *dev)
1924{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925 int npacks, pending;
1926 unsigned long flags;
Wang Chenfaf39942008-10-14 13:30:33 +08001927 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928
1929 if (!skb) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07001930 airo_print_err(dev->name, "%s: skb == NULL!",__func__);
Patrick McHardy6ed10652009-06-23 06:03:08 +00001931 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 }
Dan Carpenter11e7a912020-05-27 21:48:30 +03001933 if (skb_padto(skb, ETH_ZLEN)) {
1934 dev->stats.tx_dropped++;
1935 return NETDEV_TX_OK;
1936 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 npacks = skb_queue_len (&ai->txq);
1938
1939 if (npacks >= MAXTXQ - 1) {
1940 netif_stop_queue (dev);
1941 if (npacks > MAXTXQ) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03001942 dev->stats.tx_fifo_errors++;
Patrick McHardy5b548142009-06-12 06:22:29 +00001943 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 }
1945 skb_queue_tail (&ai->txq, skb);
Patrick McHardy6ed10652009-06-23 06:03:08 +00001946 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 }
1948
1949 spin_lock_irqsave(&ai->aux_lock, flags);
1950 skb_queue_tail (&ai->txq, skb);
1951 pending = test_bit(FLAG_PENDING_XMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01001952 spin_unlock_irqrestore(&ai->aux_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953 netif_wake_queue (dev);
1954
1955 if (pending == 0) {
1956 set_bit(FLAG_PENDING_XMIT, &ai->flags);
1957 mpi_send_packet (dev);
1958 }
Patrick McHardy6ed10652009-06-23 06:03:08 +00001959 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960}
1961
1962/*
1963 * @mpi_send_packet
1964 *
1965 * Attempt to transmit a packet. Can be called from interrupt
1966 * or transmit . return number of packets we tried to send
1967 */
1968
1969static int mpi_send_packet (struct net_device *dev)
1970{
1971 struct sk_buff *skb;
1972 unsigned char *buffer;
Al Viro593c2b92007-12-17 15:09:34 -05001973 s16 len;
1974 __le16 *payloadLen;
Wang Chenfaf39942008-10-14 13:30:33 +08001975 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976 u8 *sendbuf;
1977
1978 /* get a packet to send */
1979
Al Viro79ea13c2008-01-24 02:06:46 -08001980 if ((skb = skb_dequeue(&ai->txq)) == NULL) {
Dan Williams934d8bf2006-03-16 13:46:23 -05001981 airo_print_err(dev->name,
1982 "%s: Dequeue'd zero in send_packet()",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07001983 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 return 0;
1985 }
1986
1987 /* check min length*/
1988 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
1989 buffer = skb->data;
1990
1991 ai->txfids[0].tx_desc.offset = 0;
1992 ai->txfids[0].tx_desc.valid = 1;
1993 ai->txfids[0].tx_desc.eoc = 1;
1994 ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr);
1995
1996/*
1997 * Magic, the cards firmware needs a length count (2 bytes) in the host buffer
1998 * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen
Lucas De Marchi25985ed2011-03-30 22:57:33 -03001999 * is immediately after it. ------------------------------------------------
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA|
2001 * ------------------------------------------------
2002 */
2003
Joe Perches2c208892012-06-04 12:44:17 +00002004 memcpy(ai->txfids[0].virtual_host_addr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 (char *)&wifictlhdr8023, sizeof(wifictlhdr8023));
2006
Al Viro593c2b92007-12-17 15:09:34 -05002007 payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 sizeof(wifictlhdr8023));
2009 sendbuf = ai->txfids[0].virtual_host_addr +
2010 sizeof(wifictlhdr8023) + 2 ;
2011
2012 /*
Lucas De Marchi25985ed2011-03-30 22:57:33 -03002013 * Firmware automatically puts 802 header on so
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 * we don't need to account for it in the length
2015 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
Al Viro593c2b92007-12-17 15:09:34 -05002017 (ntohs(((__be16 *)buffer)[6]) != 0x888E)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 MICBuffer pMic;
2019
2020 if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS)
2021 return ERROR;
2022
2023 *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic));
2024 ai->txfids[0].tx_desc.len += sizeof(pMic);
2025 /* copy data into airo dma buffer */
2026 memcpy (sendbuf, buffer, sizeof(etherHead));
2027 buffer += sizeof(etherHead);
2028 sendbuf += sizeof(etherHead);
2029 memcpy (sendbuf, &pMic, sizeof(pMic));
2030 sendbuf += sizeof(pMic);
2031 memcpy (sendbuf, buffer, len - sizeof(etherHead));
Adrian Bunka39d3e72006-01-21 01:35:15 +01002032 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033 *payloadLen = cpu_to_le16(len - sizeof(etherHead));
2034
Florian Westphal860e9532016-05-03 16:33:13 +02002035 netif_trans_update(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036
2037 /* copy data into airo dma buffer */
2038 memcpy(sendbuf, buffer, len);
2039 }
2040
2041 memcpy_toio(ai->txfids[0].card_ram_off,
2042 &ai->txfids[0].tx_desc, sizeof(TxFid));
2043
2044 OUT4500(ai, EVACK, 8);
2045
2046 dev_kfree_skb_any(skb);
2047 return 1;
2048}
2049
Gabriel A. Devenyi29b09fc2005-11-03 19:30:47 -05002050static void get_tx_error(struct airo_info *ai, s32 fid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051{
Al Viro593c2b92007-12-17 15:09:34 -05002052 __le16 status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053
2054 if (fid < 0)
2055 status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status;
2056 else {
2057 if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS)
2058 return;
2059 bap_read(ai, &status, 2, BAP0);
2060 }
2061 if (le16_to_cpu(status) & 2) /* Too many retries */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002062 ai->dev->stats.tx_aborted_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002064 ai->dev->stats.tx_heartbeat_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 if (le16_to_cpu(status) & 8) /* Aid fail */
2066 { }
2067 if (le16_to_cpu(status) & 0x10) /* MAC disabled */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002068 ai->dev->stats.tx_carrier_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 if (le16_to_cpu(status) & 0x20) /* Association lost */
2070 { }
2071 /* We produce a TXDROP event only for retry or lifetime
2072 * exceeded, because that's the only status that really mean
2073 * that this particular node went away.
2074 * Other errors means that *we* screwed up. - Jean II */
2075 if ((le16_to_cpu(status) & 2) ||
2076 (le16_to_cpu(status) & 4)) {
2077 union iwreq_data wrqu;
2078 char junk[0x18];
2079
2080 /* Faster to skip over useless data than to do
2081 * another bap_setup(). We are at offset 0x6 and
2082 * need to go to 0x18 and read 6 bytes - Jean II */
Al Virob8c06bc2007-12-19 17:55:43 -05002083 bap_read(ai, (__le16 *) junk, 0x18, BAP0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084
2085 /* Copy 802.11 dest address.
2086 * We use the 802.11 header because the frame may
2087 * not be 802.3 or may be mangled...
2088 * In Ad-Hoc mode, it will be the node address.
2089 * In managed mode, it will be most likely the AP addr
2090 * User space will figure out how to convert it to
2091 * whatever it needs (IP address or else).
2092 * - Jean II */
2093 memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN);
2094 wrqu.addr.sa_family = ARPHRD_ETHER;
2095
2096 /* Send event to user space */
2097 wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL);
2098 }
2099}
2100
Lee Jonesba4d6512020-08-14 12:39:15 +01002101static void airo_end_xmit(struct net_device *dev)
2102{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 u16 status;
2104 int i;
Wang Chenfaf39942008-10-14 13:30:33 +08002105 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 struct sk_buff *skb = priv->xmit.skb;
2107 int fid = priv->xmit.fid;
2108 u32 *fids = priv->fids;
2109
Dan Williams3c304952006-04-15 12:26:18 -04002110 clear_bit(JOB_XMIT, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 clear_bit(FLAG_PENDING_XMIT, &priv->flags);
2112 status = transmit_802_3_packet (priv, fids[fid], skb->data);
2113 up(&priv->sem);
2114
2115 i = 0;
Lee Jonesba4d6512020-08-14 12:39:15 +01002116 if (status == SUCCESS) {
Florian Westphal860e9532016-05-03 16:33:13 +02002117 netif_trans_update(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118 for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++);
2119 } else {
2120 priv->fids[fid] &= 0xffff;
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002121 dev->stats.tx_window_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122 }
2123 if (i < MAX_FIDS / 2)
2124 netif_wake_queue(dev);
2125 dev_kfree_skb(skb);
2126}
2127
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +00002128static netdev_tx_t airo_start_xmit(struct sk_buff *skb,
2129 struct net_device *dev)
2130{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 s16 len;
2132 int i, j;
Wang Chenfaf39942008-10-14 13:30:33 +08002133 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134 u32 *fids = priv->fids;
2135
Lee Jonesba4d6512020-08-14 12:39:15 +01002136 if (skb == NULL) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07002137 airo_print_err(dev->name, "%s: skb == NULL!", __func__);
Patrick McHardy6ed10652009-06-23 06:03:08 +00002138 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139 }
Dan Carpenter11e7a912020-05-27 21:48:30 +03002140 if (skb_padto(skb, ETH_ZLEN)) {
2141 dev->stats.tx_dropped++;
2142 return NETDEV_TX_OK;
2143 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144
2145 /* Find a vacant FID */
Lee Jonesba4d6512020-08-14 12:39:15 +01002146 for (i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++);
2147 for (j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148
Lee Jonesba4d6512020-08-14 12:39:15 +01002149 if (j >= MAX_FIDS / 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150 netif_stop_queue(dev);
2151
2152 if (i == MAX_FIDS / 2) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002153 dev->stats.tx_fifo_errors++;
Patrick McHardy5b548142009-06-12 06:22:29 +00002154 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 }
2156 }
2157 /* check min length*/
2158 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
2159 /* Mark fid as used & save length for later */
2160 fids[i] |= (len << 16);
2161 priv->xmit.skb = skb;
2162 priv->xmit.fid = i;
2163 if (down_trylock(&priv->sem) != 0) {
2164 set_bit(FLAG_PENDING_XMIT, &priv->flags);
2165 netif_stop_queue(dev);
Dan Williams3c304952006-04-15 12:26:18 -04002166 set_bit(JOB_XMIT, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 wake_up_interruptible(&priv->thr_wait);
2168 } else
2169 airo_end_xmit(dev);
Patrick McHardy6ed10652009-06-23 06:03:08 +00002170 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171}
2172
Lee Jonesba4d6512020-08-14 12:39:15 +01002173static void airo_end_xmit11(struct net_device *dev)
2174{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175 u16 status;
2176 int i;
Wang Chenfaf39942008-10-14 13:30:33 +08002177 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 struct sk_buff *skb = priv->xmit11.skb;
2179 int fid = priv->xmit11.fid;
2180 u32 *fids = priv->fids;
2181
Dan Williams3c304952006-04-15 12:26:18 -04002182 clear_bit(JOB_XMIT11, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 clear_bit(FLAG_PENDING_XMIT11, &priv->flags);
2184 status = transmit_802_11_packet (priv, fids[fid], skb->data);
2185 up(&priv->sem);
2186
2187 i = MAX_FIDS / 2;
Lee Jonesba4d6512020-08-14 12:39:15 +01002188 if (status == SUCCESS) {
Florian Westphal860e9532016-05-03 16:33:13 +02002189 netif_trans_update(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190 for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++);
2191 } else {
2192 priv->fids[fid] &= 0xffff;
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002193 dev->stats.tx_window_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 }
2195 if (i < MAX_FIDS)
2196 netif_wake_queue(dev);
2197 dev_kfree_skb(skb);
2198}
2199
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +00002200static netdev_tx_t airo_start_xmit11(struct sk_buff *skb,
2201 struct net_device *dev)
2202{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203 s16 len;
2204 int i, j;
Wang Chenfaf39942008-10-14 13:30:33 +08002205 struct airo_info *priv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206 u32 *fids = priv->fids;
2207
2208 if (test_bit(FLAG_MPI, &priv->flags)) {
2209 /* Not implemented yet for MPI350 */
2210 netif_stop_queue(dev);
Patrick McHardy4153e772009-06-12 04:08:02 +00002211 dev_kfree_skb_any(skb);
2212 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213 }
2214
Lee Jonesba4d6512020-08-14 12:39:15 +01002215 if (skb == NULL) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07002216 airo_print_err(dev->name, "%s: skb == NULL!", __func__);
Patrick McHardy6ed10652009-06-23 06:03:08 +00002217 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218 }
Dan Carpenter11e7a912020-05-27 21:48:30 +03002219 if (skb_padto(skb, ETH_ZLEN)) {
2220 dev->stats.tx_dropped++;
2221 return NETDEV_TX_OK;
2222 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223
2224 /* Find a vacant FID */
Lee Jonesba4d6512020-08-14 12:39:15 +01002225 for (i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++);
2226 for (j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227
Lee Jonesba4d6512020-08-14 12:39:15 +01002228 if (j >= MAX_FIDS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229 netif_stop_queue(dev);
2230
2231 if (i == MAX_FIDS) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002232 dev->stats.tx_fifo_errors++;
Patrick McHardy5b548142009-06-12 06:22:29 +00002233 return NETDEV_TX_BUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 }
2235 }
2236 /* check min length*/
2237 len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
2238 /* Mark fid as used & save length for later */
2239 fids[i] |= (len << 16);
2240 priv->xmit11.skb = skb;
2241 priv->xmit11.fid = i;
2242 if (down_trylock(&priv->sem) != 0) {
2243 set_bit(FLAG_PENDING_XMIT11, &priv->flags);
2244 netif_stop_queue(dev);
Dan Williams3c304952006-04-15 12:26:18 -04002245 set_bit(JOB_XMIT11, &priv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 wake_up_interruptible(&priv->thr_wait);
2247 } else
2248 airo_end_xmit11(dev);
Patrick McHardy6ed10652009-06-23 06:03:08 +00002249 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250}
2251
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002252static void airo_read_stats(struct net_device *dev)
Al Viroa23ace52007-12-19 22:24:16 -05002253{
Wang Chenfaf39942008-10-14 13:30:33 +08002254 struct airo_info *ai = dev->ml_priv;
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
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002266 dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) +
Al Viroa23ace52007-12-19 22:24:16 -05002267 le32_to_cpu(vals[45]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002268 dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) +
Al Viroa23ace52007-12-19 22:24:16 -05002269 le32_to_cpu(vals[41]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002270 dev->stats.rx_bytes = le32_to_cpu(vals[92]);
2271 dev->stats.tx_bytes = le32_to_cpu(vals[91]);
2272 dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) +
Al Viroa23ace52007-12-19 22:24:16 -05002273 le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]);
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002274 dev->stats.tx_errors = le32_to_cpu(vals[42]) +
2275 dev->stats.tx_fifo_errors;
2276 dev->stats.multicast = le32_to_cpu(vals[43]);
2277 dev->stats.collisions = le32_to_cpu(vals[89]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278
2279 /* detailed rx_errors: */
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002280 dev->stats.rx_length_errors = le32_to_cpu(vals[3]);
2281 dev->stats.rx_crc_errors = le32_to_cpu(vals[4]);
2282 dev->stats.rx_frame_errors = le32_to_cpu(vals[2]);
2283 dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284}
2285
Jouni Malinenff1d2762005-05-12 22:54:16 -04002286static struct net_device_stats *airo_get_stats(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287{
Wang Chenfaf39942008-10-14 13:30:33 +08002288 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289
Dan Williams3c304952006-04-15 12:26:18 -04002290 if (!test_bit(JOB_STATS, &local->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 /* Get stats out of the card if available */
2292 if (down_trylock(&local->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04002293 set_bit(JOB_STATS, &local->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294 wake_up_interruptible(&local->thr_wait);
2295 } else
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002296 airo_read_stats(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297 }
2298
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03002299 return &dev->stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300}
2301
Lee Jonesba4d6512020-08-14 12:39:15 +01002302static void airo_set_promisc(struct airo_info *ai)
2303{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 Cmd cmd;
2305 Resp rsp;
2306
2307 memset(&cmd, 0, sizeof(cmd));
Lee Jonesba4d6512020-08-14 12:39:15 +01002308 cmd.cmd = CMD_SETMODE;
Dan Williams3c304952006-04-15 12:26:18 -04002309 clear_bit(JOB_PROMISC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC;
2311 issuecommand(ai, &cmd, &rsp);
2312 up(&ai->sem);
2313}
2314
Lee Jonesba4d6512020-08-14 12:39:15 +01002315static void airo_set_multicast_list(struct net_device *dev)
2316{
Wang Chenfaf39942008-10-14 13:30:33 +08002317 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318
2319 if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
2320 change_bit(FLAG_PROMISC, &ai->flags);
2321 if (down_trylock(&ai->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04002322 set_bit(JOB_PROMISC, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323 wake_up_interruptible(&ai->thr_wait);
2324 } else
2325 airo_set_promisc(ai);
2326 }
2327
Jiri Pirko4cd24ea2010-02-08 04:30:35 +00002328 if ((dev->flags&IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329 /* Turn on multicast. (Should be already setup...) */
2330 }
2331}
2332
2333static int airo_set_mac_address(struct net_device *dev, void *p)
2334{
Wang Chenfaf39942008-10-14 13:30:33 +08002335 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336 struct sockaddr *addr = p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337
2338 readConfigRid(ai, 1);
2339 memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len);
2340 set_bit (FLAG_COMMIT, &ai->flags);
2341 disable_MAC(ai, 1);
2342 writeConfigRid (ai, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02002343 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344 memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len);
2345 if (ai->wifidev)
2346 memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len);
2347 return 0;
2348}
2349
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002350static LIST_HEAD(airo_devices);
2351
2352static void add_airo_dev(struct airo_info *ai)
2353{
2354 /* Upper layers already keep track of PCI devices,
2355 * so we only need to remember our non-PCI cards. */
2356 if (!ai->pci)
2357 list_add_tail(&ai->dev_list, &airo_devices);
2358}
2359
2360static void del_airo_dev(struct airo_info *ai)
2361{
2362 if (!ai->pci)
2363 list_del(&ai->dev_list);
2364}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365
Lee Jonesba4d6512020-08-14 12:39:15 +01002366static int airo_close(struct net_device *dev)
2367{
Wang Chenfaf39942008-10-14 13:30:33 +08002368 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369
2370 netif_stop_queue(dev);
2371
2372 if (ai->wifidev != dev) {
2373#ifdef POWER_ON_DOWN
2374 /* Shut power to the card. The idea is that the user can save
2375 * power when he doesn't need the card with "ifconfig down".
2376 * That's the method that is most friendly towards the network
2377 * stack (i.e. the network stack won't try to broadcast
2378 * anything on the interface and routes are gone. Jean II */
2379 set_bit(FLAG_RADIO_DOWN, &ai->flags);
2380 disable_MAC(ai, 1);
2381#endif
Lee Jonesba4d6512020-08-14 12:39:15 +01002382 disable_interrupts(ai);
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002383
2384 free_irq(dev->irq, dev);
2385
2386 set_bit(JOB_DIE, &ai->jobs);
2387 kthread_stop(ai->airo_thread_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388 }
2389 return 0;
2390}
2391
Lee Jonesba4d6512020-08-14 12:39:15 +01002392void stop_airo_card(struct net_device *dev, int freeres)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393{
Wang Chenfaf39942008-10-14 13:30:33 +08002394 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395
2396 set_bit(FLAG_RADIO_DOWN, &ai->flags);
2397 disable_MAC(ai, 1);
2398 disable_interrupts(ai);
Lee Jonesba4d6512020-08-14 12:39:15 +01002399 takedown_proc_entry(dev, ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 if (test_bit(FLAG_REGISTERED, &ai->flags)) {
Lee Jonesba4d6512020-08-14 12:39:15 +01002401 unregister_netdev(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402 if (ai->wifidev) {
2403 unregister_netdev(ai->wifidev);
2404 free_netdev(ai->wifidev);
2405 ai->wifidev = NULL;
2406 }
2407 clear_bit(FLAG_REGISTERED, &ai->flags);
2408 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409 /*
2410 * Clean out tx queue
2411 */
David S. Millerb03efcf2005-07-08 14:57:23 -07002412 if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413 struct sk_buff *skb = NULL;
2414 for (;(skb = skb_dequeue(&ai->txq));)
2415 dev_kfree_skb(skb);
2416 }
2417
Dan Williams9e75af32006-03-16 13:46:29 -05002418 airo_networks_free (ai);
2419
Jesper Juhlb4558ea2005-10-28 16:53:13 -04002420 kfree(ai->flash);
2421 kfree(ai->rssi);
Jesper Juhlb4558ea2005-10-28 16:53:13 -04002422 kfree(ai->SSID);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 if (freeres) {
2424 /* PCMCIA frees this stuff, so only for PCI and ISA */
Lee Jonesba4d6512020-08-14 12:39:15 +01002425 release_region(dev->base_addr, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426 if (test_bit(FLAG_MPI, &ai->flags)) {
2427 if (ai->pci)
2428 mpi_unmap_card(ai->pci);
2429 if (ai->pcimem)
2430 iounmap(ai->pcimem);
2431 if (ai->pciaux)
2432 iounmap(ai->pciaux);
2433 pci_free_consistent(ai->pci, PCI_SHARED_LEN,
2434 ai->shared, ai->shared_dma);
2435 }
2436 }
Ard Biesheuvele5db0ad2019-06-17 10:43:38 +02002437 crypto_free_sync_skcipher(ai->tfm);
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002438 del_airo_dev(ai);
Lee Jonesba4d6512020-08-14 12:39:15 +01002439 free_netdev(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440}
2441
2442EXPORT_SYMBOL(stop_airo_card);
2443
Stephen Hemmingerb95cce32007-09-26 22:13:38 -07002444static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445{
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -07002446 memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 return ETH_ALEN;
2448}
2449
2450static void mpi_unmap_card(struct pci_dev *pci)
2451{
2452 unsigned long mem_start = pci_resource_start(pci, 1);
2453 unsigned long mem_len = pci_resource_len(pci, 1);
2454 unsigned long aux_start = pci_resource_start(pci, 2);
2455 unsigned long aux_len = AUXMEMSIZE;
2456
2457 release_mem_region(aux_start, aux_len);
2458 release_mem_region(mem_start, mem_len);
2459}
2460
2461/*************************************************************
2462 * This routine assumes that descriptors have been setup .
Kieran Bingham0e20c3e2020-07-15 13:48:35 +01002463 * Run at insmod time or after reset when the descriptors
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464 * have been initialized . Returns 0 if all is well nz
2465 * otherwise . Does not allocate memory but sets up card
2466 * using previously allocated descriptors.
2467 */
2468static int mpi_init_descriptors (struct airo_info *ai)
2469{
2470 Cmd cmd;
2471 Resp rsp;
2472 int i;
2473 int rc = SUCCESS;
2474
2475 /* Alloc card RX descriptors */
2476 netif_stop_queue(ai->dev);
2477
Lee Jonesba4d6512020-08-14 12:39:15 +01002478 memset(&rsp, 0, sizeof(rsp));
2479 memset(&cmd, 0, sizeof(cmd));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480
2481 cmd.cmd = CMD_ALLOCATEAUX;
2482 cmd.parm0 = FID_RX;
2483 cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux);
2484 cmd.parm2 = MPI_MAX_FIDS;
Lee Jonesba4d6512020-08-14 12:39:15 +01002485 rc = issuecommand(ai, &cmd, &rsp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002487 airo_print_err(ai->dev->name, "Couldn't allocate RX FID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488 return rc;
2489 }
2490
Lee Jonesba4d6512020-08-14 12:39:15 +01002491 for (i = 0; i<MPI_MAX_FIDS; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492 memcpy_toio(ai->rxfids[i].card_ram_off,
2493 &ai->rxfids[i].rx_desc, sizeof(RxFid));
2494 }
2495
2496 /* Alloc card TX descriptors */
2497
Lee Jonesba4d6512020-08-14 12:39:15 +01002498 memset(&rsp, 0, sizeof(rsp));
2499 memset(&cmd, 0, sizeof(cmd));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500
2501 cmd.cmd = CMD_ALLOCATEAUX;
2502 cmd.parm0 = FID_TX;
2503 cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux);
2504 cmd.parm2 = MPI_MAX_FIDS;
2505
Lee Jonesba4d6512020-08-14 12:39:15 +01002506 for (i = 0; i<MPI_MAX_FIDS; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 ai->txfids[i].tx_desc.valid = 1;
2508 memcpy_toio(ai->txfids[i].card_ram_off,
2509 &ai->txfids[i].tx_desc, sizeof(TxFid));
2510 }
2511 ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
2512
Lee Jonesba4d6512020-08-14 12:39:15 +01002513 rc = issuecommand(ai, &cmd, &rsp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002515 airo_print_err(ai->dev->name, "Couldn't allocate TX FID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516 return rc;
2517 }
2518
2519 /* Alloc card Rid descriptor */
Lee Jonesba4d6512020-08-14 12:39:15 +01002520 memset(&rsp, 0, sizeof(rsp));
2521 memset(&cmd, 0, sizeof(cmd));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522
2523 cmd.cmd = CMD_ALLOCATEAUX;
2524 cmd.parm0 = RID_RW;
2525 cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux);
2526 cmd.parm2 = 1; /* Magic number... */
Lee Jonesba4d6512020-08-14 12:39:15 +01002527 rc = issuecommand(ai, &cmd, &rsp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528 if (rc != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002529 airo_print_err(ai->dev->name, "Couldn't allocate RID");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530 return rc;
2531 }
2532
2533 memcpy_toio(ai->config_desc.card_ram_off,
2534 &ai->config_desc.rid_desc, sizeof(Rid));
2535
2536 return rc;
2537}
2538
2539/*
2540 * We are setting up three things here:
2541 * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid.
Lucas De Marchi25985ed2011-03-30 22:57:33 -03002542 * 2) Map PCI memory for issuing commands.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543 * 3) Allocate memory (shared) to send and receive ethernet frames.
2544 */
Michal Schmidt1138c372007-06-29 15:33:41 +02002545static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546{
2547 unsigned long mem_start, mem_len, aux_start, aux_len;
2548 int rc = -1;
2549 int i;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002550 dma_addr_t busaddroff;
2551 unsigned char *vpackoff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552 unsigned char __iomem *pciaddroff;
2553
2554 mem_start = pci_resource_start(pci, 1);
2555 mem_len = pci_resource_len(pci, 1);
2556 aux_start = pci_resource_start(pci, 2);
2557 aux_len = AUXMEMSIZE;
2558
Michal Schmidt1138c372007-06-29 15:33:41 +02002559 if (!request_mem_region(mem_start, mem_len, DRV_NAME)) {
2560 airo_print_err("", "Couldn't get region %x[%x]",
2561 (int)mem_start, (int)mem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 goto out;
2563 }
Michal Schmidt1138c372007-06-29 15:33:41 +02002564 if (!request_mem_region(aux_start, aux_len, DRV_NAME)) {
2565 airo_print_err("", "Couldn't get region %x[%x]",
2566 (int)aux_start, (int)aux_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567 goto free_region1;
2568 }
2569
2570 ai->pcimem = ioremap(mem_start, mem_len);
2571 if (!ai->pcimem) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002572 airo_print_err("", "Couldn't map region %x[%x]",
2573 (int)mem_start, (int)mem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 goto free_region2;
2575 }
2576 ai->pciaux = ioremap(aux_start, aux_len);
2577 if (!ai->pciaux) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002578 airo_print_err("", "Couldn't map region %x[%x]",
2579 (int)aux_start, (int)aux_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 goto free_memmap;
2581 }
2582
2583 /* Reserve PKTSIZE for each fid and 2K for the Rids */
2584 ai->shared = pci_alloc_consistent(pci, PCI_SHARED_LEN, &ai->shared_dma);
2585 if (!ai->shared) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002586 airo_print_err("", "Couldn't alloc_consistent %d",
2587 PCI_SHARED_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 goto free_auxmap;
2589 }
2590
2591 /*
2592 * Setup descriptor RX, TX, CONFIG
2593 */
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002594 busaddroff = ai->shared_dma;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002595 pciaddroff = ai->pciaux + AUX_OFFSET;
2596 vpackoff = ai->shared;
2597
2598 /* RX descriptor setup */
Lee Jonesba4d6512020-08-14 12:39:15 +01002599 for (i = 0; i < MPI_MAX_FIDS; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600 ai->rxfids[i].pending = 0;
2601 ai->rxfids[i].card_ram_off = pciaddroff;
2602 ai->rxfids[i].virtual_host_addr = vpackoff;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002603 ai->rxfids[i].rx_desc.host_addr = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604 ai->rxfids[i].rx_desc.valid = 1;
2605 ai->rxfids[i].rx_desc.len = PKTSIZE;
2606 ai->rxfids[i].rx_desc.rdy = 0;
2607
2608 pciaddroff += sizeof(RxFid);
2609 busaddroff += PKTSIZE;
2610 vpackoff += PKTSIZE;
2611 }
2612
2613 /* TX descriptor setup */
Lee Jonesba4d6512020-08-14 12:39:15 +01002614 for (i = 0; i < MPI_MAX_FIDS; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615 ai->txfids[i].card_ram_off = pciaddroff;
2616 ai->txfids[i].virtual_host_addr = vpackoff;
2617 ai->txfids[i].tx_desc.valid = 1;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002618 ai->txfids[i].tx_desc.host_addr = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619 memcpy(ai->txfids[i].virtual_host_addr,
2620 &wifictlhdr8023, sizeof(wifictlhdr8023));
2621
2622 pciaddroff += sizeof(TxFid);
2623 busaddroff += PKTSIZE;
2624 vpackoff += PKTSIZE;
2625 }
2626 ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
2627
2628 /* Rid descriptor setup */
2629 ai->config_desc.card_ram_off = pciaddroff;
2630 ai->config_desc.virtual_host_addr = vpackoff;
Jeff Garzik2759c8d2005-09-24 04:09:04 -04002631 ai->config_desc.rid_desc.host_addr = busaddroff;
2632 ai->ridbus = busaddroff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633 ai->config_desc.rid_desc.rid = 0;
2634 ai->config_desc.rid_desc.len = RIDSIZE;
2635 ai->config_desc.rid_desc.valid = 1;
2636 pciaddroff += sizeof(Rid);
2637 busaddroff += RIDSIZE;
2638 vpackoff += RIDSIZE;
2639
2640 /* Tell card about descriptors */
2641 if (mpi_init_descriptors (ai) != SUCCESS)
2642 goto free_shared;
2643
2644 return 0;
2645 free_shared:
2646 pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
2647 free_auxmap:
2648 iounmap(ai->pciaux);
2649 free_memmap:
2650 iounmap(ai->pcimem);
2651 free_region2:
2652 release_mem_region(aux_start, aux_len);
2653 free_region1:
2654 release_mem_region(mem_start, mem_len);
2655 out:
2656 return rc;
2657}
2658
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -07002659static const struct header_ops airo_header_ops = {
2660 .parse = wll_header_parse,
2661};
2662
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002663static const struct net_device_ops airo11_netdev_ops = {
2664 .ndo_open = airo_open,
2665 .ndo_stop = airo_close,
2666 .ndo_start_xmit = airo_start_xmit11,
2667 .ndo_get_stats = airo_get_stats,
2668 .ndo_set_mac_address = airo_set_mac_address,
2669 .ndo_do_ioctl = airo_ioctl,
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002670};
2671
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672static void wifi_setup(struct net_device *dev)
2673{
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002674 dev->netdev_ops = &airo11_netdev_ops;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -07002675 dev->header_ops = &airo_header_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676 dev->wireless_handlers = &airo_handler_def;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677
2678 dev->type = ARPHRD_IEEE80211;
2679 dev->hard_header_len = ETH_HLEN;
Dan Williams15db2762006-03-16 13:46:27 -05002680 dev->mtu = AIRO_DEF_MTU;
Jarod Wilson9c22b4a2016-10-20 13:55:18 -04002681 dev->min_mtu = 68;
2682 dev->max_mtu = MIC_MSGLEN_MAX;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683 dev->addr_len = ETH_ALEN;
Lee Jonesba4d6512020-08-14 12:39:15 +01002684 dev->tx_queue_len = 100;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002685
Joe Perches93803b32015-03-02 19:54:49 -08002686 eth_broadcast_addr(dev->broadcast);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687
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;
Tom Gundersenc835a672014-07-14 16:37:24 +02002695 struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN,
2696 wifi_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697 if (!dev)
2698 return NULL;
Wang Chenfaf39942008-10-14 13:30:33 +08002699 dev->ml_priv = ethdev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700 dev->irq = ethdev->irq;
2701 dev->base_addr = ethdev->base_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702 dev->wireless_data = ethdev->wireless_data;
Masakazu Mokuno229ce3a2008-05-14 14:16:50 +09002703 SET_NETDEV_DEV(dev, ethdev->dev.parent);
Bjørn Mork252352cb2013-08-30 18:08:49 +02002704 eth_hw_addr_inherit(dev, ethdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 err = register_netdev(dev);
2706 if (err<0) {
2707 free_netdev(dev);
2708 return NULL;
2709 }
2710 return dev;
2711}
2712
Lee Jonesba4d6512020-08-14 12:39:15 +01002713static int reset_card(struct net_device *dev, int lock)
2714{
Wang Chenfaf39942008-10-14 13:30:33 +08002715 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716
2717 if (lock && down_interruptible(&ai->sem))
2718 return -1;
2719 waitbusy (ai);
Lee Jonesba4d6512020-08-14 12:39:15 +01002720 OUT4500(ai, COMMAND, CMD_SOFTRESET);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721 msleep(200);
2722 waitbusy (ai);
2723 msleep(200);
2724 if (lock)
2725 up(&ai->sem);
2726 return 0;
2727}
2728
Dan Williams3c304952006-04-15 12:26:18 -04002729#define AIRO_MAX_NETWORK_COUNT 64
Dan Williams9e75af32006-03-16 13:46:29 -05002730static int airo_networks_allocate(struct airo_info *ai)
2731{
2732 if (ai->networks)
2733 return 0;
2734
Joe Perchesbaeb2ff2010-08-11 07:02:48 +00002735 ai->networks = kcalloc(AIRO_MAX_NETWORK_COUNT, sizeof(BSSListElement),
2736 GFP_KERNEL);
Dan Williams9e75af32006-03-16 13:46:29 -05002737 if (!ai->networks) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002738 airo_print_warn("", "Out of memory allocating beacons");
Dan Williams9e75af32006-03-16 13:46:29 -05002739 return -ENOMEM;
2740 }
2741
2742 return 0;
2743}
2744
2745static void airo_networks_free(struct airo_info *ai)
2746{
Dan Williams9e75af32006-03-16 13:46:29 -05002747 kfree(ai->networks);
2748 ai->networks = NULL;
2749}
2750
2751static void airo_networks_initialize(struct airo_info *ai)
2752{
2753 int i;
2754
2755 INIT_LIST_HEAD(&ai->network_free_list);
2756 INIT_LIST_HEAD(&ai->network_list);
Dan Williams3c304952006-04-15 12:26:18 -04002757 for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++)
Dan Williams9e75af32006-03-16 13:46:29 -05002758 list_add_tail(&ai->networks[i].list,
2759 &ai->network_free_list);
2760}
2761
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002762static const struct net_device_ops airo_netdev_ops = {
2763 .ndo_open = airo_open,
2764 .ndo_stop = airo_close,
2765 .ndo_start_xmit = airo_start_xmit,
2766 .ndo_get_stats = airo_get_stats,
Jiri Pirkoafc4b132011-08-16 06:29:01 +00002767 .ndo_set_rx_mode = airo_set_multicast_list,
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002768 .ndo_set_mac_address = airo_set_mac_address,
2769 .ndo_do_ioctl = airo_ioctl,
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002770 .ndo_validate_addr = eth_validate_addr,
2771};
2772
2773static const struct net_device_ops mpi_netdev_ops = {
2774 .ndo_open = airo_open,
2775 .ndo_stop = airo_close,
2776 .ndo_start_xmit = mpi_start_xmit,
2777 .ndo_get_stats = airo_get_stats,
Jiri Pirkoafc4b132011-08-16 06:29:01 +00002778 .ndo_set_rx_mode = airo_set_multicast_list,
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002779 .ndo_set_mac_address = airo_set_mac_address,
2780 .ndo_do_ioctl = airo_ioctl,
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002781 .ndo_validate_addr = eth_validate_addr,
2782};
2783
2784
Lee Jonesba4d6512020-08-14 12:39:15 +01002785static struct net_device *_init_airo_card(unsigned short irq, int port,
Jouni Malinenff1d2762005-05-12 22:54:16 -04002786 int is_pcmcia, struct pci_dev *pci,
Lee Jonesba4d6512020-08-14 12:39:15 +01002787 struct device *dmdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788{
2789 struct net_device *dev;
2790 struct airo_info *ai;
2791 int i, rc;
Dan Williamsf65b56d2009-01-24 09:10:42 -05002792 CapabilityRid cap_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793
2794 /* Create the network device object. */
Tom Gundersenc835a672014-07-14 16:37:24 +02002795 dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup);
Michal Schmidt1138c372007-06-29 15:33:41 +02002796 if (!dev) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002797 airo_print_err("", "Couldn't alloc_etherdev");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 }
2800
Wang Chenfaf39942008-10-14 13:30:33 +08002801 ai = dev->ml_priv = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 ai->wifidev = NULL;
Michal Schmidtfb038c22007-06-29 15:33:52 +02002803 ai->flags = 1 << FLAG_RADIO_DOWN;
Dan Williams3c304952006-04-15 12:26:18 -04002804 ai->jobs = 0;
Dan Williams934d8bf2006-03-16 13:46:23 -05002805 ai->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806 if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
Michal Schmidt1138c372007-06-29 15:33:41 +02002807 airo_print_dbg("", "Found an MPI350 card");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808 set_bit(FLAG_MPI, &ai->flags);
2809 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810 spin_lock_init(&ai->aux_lock);
2811 sema_init(&ai->sem, 1);
2812 ai->config.len = 0;
2813 ai->pci = pci;
2814 init_waitqueue_head (&ai->thr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815 ai->tfm = NULL;
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01002816 add_airo_dev(ai);
Ondrej Zaryf675f932015-10-16 21:04:13 +02002817 ai->APList.len = cpu_to_le16(sizeof(struct APListRid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002818
Dan Williams9e75af32006-03-16 13:46:29 -05002819 if (airo_networks_allocate (ai))
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02002820 goto err_out_free;
Dan Williams9e75af32006-03-16 13:46:29 -05002821 airo_networks_initialize (ai);
2822
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002823 skb_queue_head_init (&ai->txq);
2824
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 /* The Airo-specific entries in the device structure. */
Stephen Hemminger7ae41cc2009-03-20 19:36:26 +00002826 if (test_bit(FLAG_MPI,&ai->flags))
2827 dev->netdev_ops = &mpi_netdev_ops;
2828 else
2829 dev->netdev_ops = &airo_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830 dev->wireless_handlers = &airo_handler_def;
2831 ai->wireless_data.spy_data = &ai->spy_data;
2832 dev->wireless_data = &ai->wireless_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833 dev->irq = irq;
2834 dev->base_addr = port;
Neil Horman550fd082011-07-26 06:05:38 +00002835 dev->priv_flags &= ~IFF_TX_SKB_SHARING;
Jarod Wilson9c22b4a2016-10-20 13:55:18 -04002836 dev->max_mtu = MIC_MSGLEN_MAX;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837
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) {
Dan Williamsf65b56d2009-01-24 09:10:42 -05002859 if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) {
Lee Jonesba4d6512020-08-14 12:39:15 +01002860 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
Michal Schmidt1138c372007-06-29 15:33:41 +02002869 strcpy(dev->name, "eth%d");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870 rc = register_netdev(dev);
2871 if (rc) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002872 airo_print_err(dev->name, "Couldn't register_netdev");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873 goto err_out_map;
2874 }
2875 ai->wifidev = init_wifidev(ai, dev);
Florin Malita431aca52006-10-10 16:46:30 -04002876 if (!ai->wifidev)
2877 goto err_out_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878
Dan Williamsf65b56d2009-01-24 09:10:42 -05002879 rc = readCapabilityRid(ai, &cap_rid, 1);
2880 if (rc != SUCCESS) {
2881 rc = -EIO;
2882 goto err_out_wifi;
2883 }
Dan Williams138c0c62009-01-24 09:11:35 -05002884 /* WEP capability discovery */
2885 ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0;
2886 ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0;
Dan Williamsf65b56d2009-01-24 09:10:42 -05002887
matthieu castetf3a981f2010-02-28 15:42:54 +01002888 airo_print_info(dev->name, "Firmware version %x.%x.%02d",
Dan Williamsf65b56d2009-01-24 09:10:42 -05002889 ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF),
2890 (le16_to_cpu(cap_rid.softVer) & 0xFF),
2891 le16_to_cpu(cap_rid.softSubVer));
2892
2893 /* Test for WPA support */
2894 /* Only firmware versions 5.30.17 or better can do WPA */
2895 if (le16_to_cpu(cap_rid.softVer) > 0x530
2896 || (le16_to_cpu(cap_rid.softVer) == 0x530
2897 && le16_to_cpu(cap_rid.softSubVer) >= 17)) {
2898 airo_print_info(ai->dev->name, "WPA supported.");
2899
2900 set_bit(FLAG_WPA_CAPABLE, &ai->flags);
2901 ai->bssListFirst = RID_WPA_BSSLISTFIRST;
2902 ai->bssListNext = RID_WPA_BSSLISTNEXT;
2903 ai->bssListRidLen = sizeof(BSSListRid);
2904 } else {
2905 airo_print_info(ai->dev->name, "WPA unsupported with firmware "
2906 "versions older than 5.30.17.");
2907
2908 ai->bssListFirst = RID_BSSLISTFIRST;
2909 ai->bssListNext = RID_BSSLISTNEXT;
2910 ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra);
2911 }
2912
Linus Torvalds1da177e2005-04-16 15:20:36 -07002913 set_bit(FLAG_REGISTERED,&ai->flags);
Johannes Berge1749612008-10-27 15:59:26 -07002914 airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002915
2916 /* Allocate the transmit buffers */
2917 if (probe && !test_bit(FLAG_MPI,&ai->flags))
Lee Jonesba4d6512020-08-14 12:39:15 +01002918 for (i = 0; i < MAX_FIDS; i++)
2919 ai->fids[i] = transmit_allocate(ai, AIRO_DEF_MTU, i>=MAX_FIDS/2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002920
Wang Chenfaf39942008-10-14 13:30:33 +08002921 if (setup_proc_entry(dev, dev->ml_priv) < 0)
Florin Malita431aca52006-10-10 16:46:30 -04002922 goto err_out_wifi;
2923
Linus Torvalds1da177e2005-04-16 15:20:36 -07002924 return dev;
2925
Florin Malita431aca52006-10-10 16:46:30 -04002926err_out_wifi:
2927 unregister_netdev(ai->wifidev);
2928 free_netdev(ai->wifidev);
2929err_out_reg:
2930 unregister_netdev(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002931err_out_map:
2932 if (test_bit(FLAG_MPI,&ai->flags) && pci) {
2933 pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma);
2934 iounmap(ai->pciaux);
2935 iounmap(ai->pcimem);
2936 mpi_unmap_card(ai->pci);
2937 }
2938err_out_res:
2939 if (!is_pcmcia)
Lee Jonesba4d6512020-08-14 12:39:15 +01002940 release_region(dev->base_addr, 64);
Michal Schmidt4d881902007-03-16 12:42:59 +01002941err_out_nets:
2942 airo_networks_free(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943err_out_free:
Kulikov Vasiliyff4bf912010-07-13 15:23:33 +04002944 del_airo_dev(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002945 free_netdev(dev);
2946 return NULL;
2947}
2948
Lee Jonesba4d6512020-08-14 12:39:15 +01002949struct net_device *init_airo_card(unsigned short irq, int port, int is_pcmcia,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002950 struct device *dmdev)
2951{
Lee Jonesba4d6512020-08-14 12:39:15 +01002952 return _init_airo_card (irq, port, is_pcmcia, NULL, dmdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953}
2954
2955EXPORT_SYMBOL(init_airo_card);
2956
Lee Jonesba4d6512020-08-14 12:39:15 +01002957static int waitbusy (struct airo_info *ai)
2958{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002959 int delay = 0;
Andrew Mortonb212f332008-05-28 12:40:39 -07002960 while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961 udelay (10);
2962 if ((++delay % 20) == 0)
2963 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
2964 }
2965 return delay < 10000;
2966}
2967
Lee Jonesba4d6512020-08-14 12:39:15 +01002968int reset_airo_card(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002969{
2970 int i;
Wang Chenfaf39942008-10-14 13:30:33 +08002971 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972
2973 if (reset_card (dev, 1))
2974 return -1;
2975
Lee Jonesba4d6512020-08-14 12:39:15 +01002976 if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) {
Dan Williams934d8bf2006-03-16 13:46:23 -05002977 airo_print_err(dev->name, "MAC could not be enabled");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002978 return -1;
2979 }
Johannes Berge1749612008-10-27 15:59:26 -07002980 airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002981 /* Allocate the transmit buffers if needed */
2982 if (!test_bit(FLAG_MPI,&ai->flags))
Lee Jonesba4d6512020-08-14 12:39:15 +01002983 for (i = 0; i < MAX_FIDS; i++)
2984 ai->fids[i] = transmit_allocate (ai, AIRO_DEF_MTU, i>=MAX_FIDS/2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985
Lee Jonesba4d6512020-08-14 12:39:15 +01002986 enable_interrupts(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002987 netif_wake_queue(dev);
2988 return 0;
2989}
2990
2991EXPORT_SYMBOL(reset_airo_card);
2992
Lee Jonesba4d6512020-08-14 12:39:15 +01002993static void airo_send_event(struct net_device *dev)
2994{
Wang Chenfaf39942008-10-14 13:30:33 +08002995 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002996 union iwreq_data wrqu;
2997 StatusRid status_rid;
2998
Dan Williams3c304952006-04-15 12:26:18 -04002999 clear_bit(JOB_EVENT, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003000 PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
3001 up(&ai->sem);
3002 wrqu.data.length = 0;
3003 wrqu.data.flags = 0;
3004 memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN);
3005 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
3006
3007 /* Send event to user space */
3008 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
3009}
3010
Lee Jonesba4d6512020-08-14 12:39:15 +01003011static void airo_process_scan_results (struct airo_info *ai)
3012{
Dan Williams9e75af32006-03-16 13:46:29 -05003013 union iwreq_data wrqu;
Dan Williams3c304952006-04-15 12:26:18 -04003014 BSSListRid bss;
Dan Williams9e75af32006-03-16 13:46:29 -05003015 int rc;
3016 BSSListElement * loop_net;
3017 BSSListElement * tmp_net;
3018
3019 /* Blow away current list of scan results */
3020 list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) {
3021 list_move_tail (&loop_net->list, &ai->network_free_list);
3022 /* Don't blow away ->list, just BSS data */
3023 memset (loop_net, 0, sizeof (loop_net->bss));
3024 }
3025
3026 /* Try to read the first entry of the scan result */
Dan Williams3c304952006-04-15 12:26:18 -04003027 rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0);
Lee Jonesba4d6512020-08-14 12:39:15 +01003028 if ((rc) || (bss.index == cpu_to_le16(0xffff))) {
Dan Williams9e75af32006-03-16 13:46:29 -05003029 /* No scan results */
3030 goto out;
3031 }
3032
3033 /* Read and parse all entries */
3034 tmp_net = NULL;
Lee Jonesba4d6512020-08-14 12:39:15 +01003035 while ((!rc) && (bss.index != cpu_to_le16(0xffff))) {
Dan Williams9e75af32006-03-16 13:46:29 -05003036 /* Grab a network off the free list */
3037 if (!list_empty(&ai->network_free_list)) {
3038 tmp_net = list_entry(ai->network_free_list.next,
3039 BSSListElement, list);
3040 list_del(ai->network_free_list.next);
3041 }
3042
3043 if (tmp_net != NULL) {
Dan Williams3c304952006-04-15 12:26:18 -04003044 memcpy(tmp_net, &bss, sizeof(tmp_net->bss));
Dan Williams9e75af32006-03-16 13:46:29 -05003045 list_add_tail(&tmp_net->list, &ai->network_list);
3046 tmp_net = NULL;
3047 }
3048
3049 /* Read next entry */
Dan Williams3c304952006-04-15 12:26:18 -04003050 rc = PC4500_readrid(ai, ai->bssListNext,
3051 &bss, ai->bssListRidLen, 0);
Dan Williams9e75af32006-03-16 13:46:29 -05003052 }
3053
3054out:
Ondrej Zarydae04122015-10-16 21:04:14 +02003055 /* write APList back (we cleared it in airo_set_scan) */
3056 disable_MAC(ai, 2);
3057 writeAPListRid(ai, &ai->APList, 0);
3058 enable_MAC(ai, 0);
3059
Dan Williams9e75af32006-03-16 13:46:29 -05003060 ai->scan_timeout = 0;
Dan Williams3c304952006-04-15 12:26:18 -04003061 clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
Dan Williams9e75af32006-03-16 13:46:29 -05003062 up(&ai->sem);
3063
3064 /* Send an empty event to user space.
3065 * We don't send the received data on
3066 * the event because it would require
3067 * us to do complex transcoding, and
3068 * we want to minimise the work done in
3069 * the irq handler. Use a request to
3070 * extract the data - Jean II */
3071 wrqu.data.length = 0;
3072 wrqu.data.flags = 0;
3073 wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL);
3074}
3075
Lee Jonesba4d6512020-08-14 12:39:15 +01003076static int airo_thread(void *data)
3077{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003078 struct net_device *dev = data;
Wang Chenfaf39942008-10-14 13:30:33 +08003079 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080 int locked;
Rafael J. Wysocki83144182007-07-17 04:03:35 -07003081
3082 set_freezable();
Lee Jonesba4d6512020-08-14 12:39:15 +01003083 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084 /* make swsusp happy with our thread */
Christoph Lameter3e1d1d22005-06-24 23:13:50 -07003085 try_to_freeze();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003086
Dan Williams3c304952006-04-15 12:26:18 -04003087 if (test_bit(JOB_DIE, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003088 break;
3089
Dan Williams3c304952006-04-15 12:26:18 -04003090 if (ai->jobs) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003091 locked = down_interruptible(&ai->sem);
3092 } else {
Ingo Molnarac6424b2017-06-20 12:06:13 +02003093 wait_queue_entry_t wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003094
3095 init_waitqueue_entry(&wait, current);
3096 add_wait_queue(&ai->thr_wait, &wait);
3097 for (;;) {
3098 set_current_state(TASK_INTERRUPTIBLE);
Dan Williams3c304952006-04-15 12:26:18 -04003099 if (ai->jobs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003100 break;
Dan Williams9e75af32006-03-16 13:46:29 -05003101 if (ai->expires || ai->scan_timeout) {
3102 if (ai->scan_timeout &&
Lee Jonesba4d6512020-08-14 12:39:15 +01003103 time_after_eq(jiffies, ai->scan_timeout)) {
Dan Williams3c304952006-04-15 12:26:18 -04003104 set_bit(JOB_SCAN_RESULTS, &ai->jobs);
Dan Williams9e75af32006-03-16 13:46:29 -05003105 break;
3106 } else if (ai->expires &&
Lee Jonesba4d6512020-08-14 12:39:15 +01003107 time_after_eq(jiffies, ai->expires)) {
Dan Williams3c304952006-04-15 12:26:18 -04003108 set_bit(JOB_AUTOWEP, &ai->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003109 break;
3110 }
Dave Kleikamp5bb85f12006-10-10 14:45:46 -07003111 if (!kthread_should_stop() &&
3112 !freezing(current)) {
Dan Williams9e75af32006-03-16 13:46:29 -05003113 unsigned long wake_at;
3114 if (!ai->expires || !ai->scan_timeout) {
3115 wake_at = max(ai->expires,
3116 ai->scan_timeout);
3117 } else {
3118 wake_at = min(ai->expires,
3119 ai->scan_timeout);
3120 }
3121 schedule_timeout(wake_at - jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003122 continue;
3123 }
Dave Kleikamp5bb85f12006-10-10 14:45:46 -07003124 } else if (!kthread_should_stop() &&
3125 !freezing(current)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003126 schedule();
3127 continue;
3128 }
3129 break;
3130 }
Xu Wangb28bd972020-07-02 01:57:01 +00003131 __set_current_state(TASK_RUNNING);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003132 remove_wait_queue(&ai->thr_wait, &wait);
3133 locked = 1;
3134 }
3135
3136 if (locked)
3137 continue;
3138
Dan Williams3c304952006-04-15 12:26:18 -04003139 if (test_bit(JOB_DIE, &ai->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003140 up(&ai->sem);
3141 break;
3142 }
3143
Pavel Machekca078ba2005-09-03 15:56:57 -07003144 if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003145 up(&ai->sem);
3146 continue;
3147 }
3148
Dan Williams3c304952006-04-15 12:26:18 -04003149 if (test_bit(JOB_XMIT, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003150 airo_end_xmit(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003151 else if (test_bit(JOB_XMIT11, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003152 airo_end_xmit11(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003153 else if (test_bit(JOB_STATS, &ai->jobs))
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003154 airo_read_stats(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003155 else if (test_bit(JOB_WSTATS, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003156 airo_read_wireless_stats(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003157 else if (test_bit(JOB_PROMISC, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003158 airo_set_promisc(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003159 else if (test_bit(JOB_MIC, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003160 micinit(ai);
Dan Williams3c304952006-04-15 12:26:18 -04003161 else if (test_bit(JOB_EVENT, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003162 airo_send_event(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003163 else if (test_bit(JOB_AUTOWEP, &ai->jobs))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003164 timer_func(dev);
Dan Williams3c304952006-04-15 12:26:18 -04003165 else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs))
Dan Williams9e75af32006-03-16 13:46:29 -05003166 airo_process_scan_results(ai);
3167 else /* Shouldn't get here, but we make sure to unlock */
3168 up(&ai->sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003169 }
Sukadev Bhattiprolu3b4c7d642006-08-14 23:12:03 -07003170
3171 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003172}
3173
Al Viro0300b332007-12-19 22:38:33 -05003174static int header_len(__le16 ctl)
3175{
3176 u16 fc = le16_to_cpu(ctl);
3177 switch (fc & 0xc) {
3178 case 4:
3179 if ((fc & 0xe0) == 0xc0)
3180 return 10; /* one-address control packet */
3181 return 16; /* two-address control packet */
3182 case 8:
3183 if ((fc & 0x300) == 0x300)
3184 return 30; /* WDS packet */
3185 }
3186 return 24;
3187}
3188
Dan Williamsf55d4512009-01-24 09:04:12 -05003189static void airo_handle_cisco_mic(struct airo_info *ai)
3190{
3191 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) {
3192 set_bit(JOB_MIC, &ai->jobs);
3193 wake_up_interruptible(&ai->thr_wait);
3194 }
3195}
3196
3197/* Airo Status codes */
3198#define STAT_NOBEACON 0x8000 /* Loss of sync - missed beacons */
3199#define STAT_MAXRETRIES 0x8001 /* Loss of sync - max retries */
3200#define STAT_MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/
3201#define STAT_FORCELOSS 0x8003 /* Loss of sync - host request */
3202#define STAT_TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */
3203#define STAT_DEAUTH 0x8100 /* low byte is 802.11 reason code */
3204#define STAT_DISASSOC 0x8200 /* low byte is 802.11 reason code */
3205#define STAT_ASSOC_FAIL 0x8400 /* low byte is 802.11 reason code */
3206#define STAT_AUTH_FAIL 0x0300 /* low byte is 802.11 reason code */
3207#define STAT_ASSOC 0x0400 /* Associated */
3208#define STAT_REASSOC 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */
3209
3210static void airo_print_status(const char *devname, u16 status)
3211{
3212 u8 reason = status & 0xFF;
3213
matthieu castet3bc819e2010-02-28 15:31:21 +01003214 switch (status & 0xFF00) {
Dan Williamsf55d4512009-01-24 09:04:12 -05003215 case STAT_NOBEACON:
matthieu castet3bc819e2010-02-28 15:31:21 +01003216 switch (status) {
3217 case STAT_NOBEACON:
3218 airo_print_dbg(devname, "link lost (missed beacons)");
3219 break;
3220 case STAT_MAXRETRIES:
3221 case STAT_MAXARL:
3222 airo_print_dbg(devname, "link lost (max retries)");
3223 break;
3224 case STAT_FORCELOSS:
3225 airo_print_dbg(devname, "link lost (local choice)");
3226 break;
3227 case STAT_TSFSYNC:
3228 airo_print_dbg(devname, "link lost (TSF sync lost)");
3229 break;
3230 default:
Masanari Iidaf42cf8d2015-02-24 23:11:26 +09003231 airo_print_dbg(devname, "unknown status %x\n", status);
matthieu castet3bc819e2010-02-28 15:31:21 +01003232 break;
3233 }
Dan Williamsf55d4512009-01-24 09:04:12 -05003234 break;
3235 case STAT_DEAUTH:
3236 airo_print_dbg(devname, "deauthenticated (reason: %d)", reason);
3237 break;
3238 case STAT_DISASSOC:
3239 airo_print_dbg(devname, "disassociated (reason: %d)", reason);
3240 break;
3241 case STAT_ASSOC_FAIL:
3242 airo_print_dbg(devname, "association failed (reason: %d)",
3243 reason);
3244 break;
3245 case STAT_AUTH_FAIL:
3246 airo_print_dbg(devname, "authentication failed (reason: %d)",
3247 reason);
3248 break;
matthieu castet3bc819e2010-02-28 15:31:21 +01003249 case STAT_ASSOC:
3250 case STAT_REASSOC:
3251 break;
Dan Williamsf55d4512009-01-24 09:04:12 -05003252 default:
Masanari Iidaf42cf8d2015-02-24 23:11:26 +09003253 airo_print_dbg(devname, "unknown status %x\n", status);
Dan Williamsf55d4512009-01-24 09:04:12 -05003254 break;
3255 }
3256}
3257
3258static void airo_handle_link(struct airo_info *ai)
3259{
3260 union iwreq_data wrqu;
3261 int scan_forceloss = 0;
3262 u16 status;
3263
3264 /* Get new status and acknowledge the link change */
3265 status = le16_to_cpu(IN4500(ai, LINKSTAT));
3266 OUT4500(ai, EVACK, EV_LINK);
3267
3268 if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0))
3269 scan_forceloss = 1;
3270
3271 airo_print_status(ai->dev->name, status);
3272
3273 if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) {
3274 if (auto_wep)
3275 ai->expires = 0;
3276 if (ai->list_bss_task)
3277 wake_up_process(ai->list_bss_task);
3278 set_bit(FLAG_UPDATE_UNI, &ai->flags);
3279 set_bit(FLAG_UPDATE_MULTI, &ai->flags);
3280
3281 if (down_trylock(&ai->sem) != 0) {
3282 set_bit(JOB_EVENT, &ai->jobs);
3283 wake_up_interruptible(&ai->thr_wait);
3284 } else
3285 airo_send_event(ai->dev);
Ondrej Zary2b8fa9e2015-09-21 15:44:25 +02003286 netif_carrier_on(ai->dev);
Dan Williamsf55d4512009-01-24 09:04:12 -05003287 } else if (!scan_forceloss) {
3288 if (auto_wep && !ai->expires) {
3289 ai->expires = RUN_AT(3*HZ);
3290 wake_up_interruptible(&ai->thr_wait);
3291 }
3292
3293 /* Send event to user space */
Joe Perches93803b32015-03-02 19:54:49 -08003294 eth_zero_addr(wrqu.ap_addr.sa_data);
Dan Williamsf55d4512009-01-24 09:04:12 -05003295 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
3296 wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL);
Ondrej Zary2b8fa9e2015-09-21 15:44:25 +02003297 netif_carrier_off(ai->dev);
3298 } else {
3299 netif_carrier_off(ai->dev);
Dan Williamsf55d4512009-01-24 09:04:12 -05003300 }
3301}
3302
3303static void airo_handle_rx(struct airo_info *ai)
3304{
3305 struct sk_buff *skb = NULL;
3306 __le16 fc, v, *buffer, tmpbuf[4];
3307 u16 len, hdrlen = 0, gap, fid;
3308 struct rx_hdr hdr;
3309 int success = 0;
3310
3311 if (test_bit(FLAG_MPI, &ai->flags)) {
3312 if (test_bit(FLAG_802_11, &ai->flags))
3313 mpi_receive_802_11(ai);
3314 else
3315 mpi_receive_802_3(ai);
3316 OUT4500(ai, EVACK, EV_RX);
3317 return;
3318 }
3319
3320 fid = IN4500(ai, RXFID);
3321
3322 /* Get the packet length */
3323 if (test_bit(FLAG_802_11, &ai->flags)) {
3324 bap_setup (ai, fid, 4, BAP0);
3325 bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0);
3326 /* Bad CRC. Ignore packet */
3327 if (le16_to_cpu(hdr.status) & 2)
3328 hdr.len = 0;
3329 if (ai->wifidev == NULL)
3330 hdr.len = 0;
3331 } else {
3332 bap_setup(ai, fid, 0x36, BAP0);
3333 bap_read(ai, &hdr.len, 2, BAP0);
3334 }
3335 len = le16_to_cpu(hdr.len);
3336
3337 if (len > AIRO_DEF_MTU) {
3338 airo_print_err(ai->dev->name, "Bad size %d", len);
3339 goto done;
3340 }
3341 if (len == 0)
3342 goto done;
3343
3344 if (test_bit(FLAG_802_11, &ai->flags)) {
3345 bap_read(ai, &fc, sizeof (fc), BAP0);
3346 hdrlen = header_len(fc);
3347 } else
3348 hdrlen = ETH_ALEN * 2;
3349
3350 skb = dev_alloc_skb(len + hdrlen + 2 + 2);
3351 if (!skb) {
3352 ai->dev->stats.rx_dropped++;
3353 goto done;
3354 }
3355
3356 skb_reserve(skb, 2); /* This way the IP header is aligned */
Johannes Berg4df864c2017-06-16 14:29:21 +02003357 buffer = skb_put(skb, len + hdrlen);
Dan Williamsf55d4512009-01-24 09:04:12 -05003358 if (test_bit(FLAG_802_11, &ai->flags)) {
3359 buffer[0] = fc;
3360 bap_read(ai, buffer + 1, hdrlen - 2, BAP0);
3361 if (hdrlen == 24)
3362 bap_read(ai, tmpbuf, 6, BAP0);
3363
3364 bap_read(ai, &v, sizeof(v), BAP0);
3365 gap = le16_to_cpu(v);
3366 if (gap) {
3367 if (gap <= 8) {
3368 bap_read(ai, tmpbuf, gap, BAP0);
3369 } else {
3370 airo_print_err(ai->dev->name, "gaplen too "
3371 "big. Problems will follow...");
3372 }
3373 }
3374 bap_read(ai, buffer + hdrlen/2, len, BAP0);
3375 } else {
3376 MICBuffer micbuf;
3377
3378 bap_read(ai, buffer, ETH_ALEN * 2, BAP0);
3379 if (ai->micstats.enabled) {
3380 bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0);
3381 if (ntohs(micbuf.typelen) > 0x05DC)
3382 bap_setup(ai, fid, 0x44, BAP0);
3383 else {
3384 if (len <= sizeof (micbuf)) {
3385 dev_kfree_skb_irq(skb);
3386 goto done;
3387 }
3388
3389 len -= sizeof(micbuf);
3390 skb_trim(skb, len + hdrlen);
3391 }
3392 }
3393
3394 bap_read(ai, buffer + ETH_ALEN, len, BAP0);
3395 if (decapsulate(ai, &micbuf, (etherHead*) buffer, len))
3396 dev_kfree_skb_irq (skb);
3397 else
3398 success = 1;
3399 }
3400
3401#ifdef WIRELESS_SPY
3402 if (success && (ai->spy_data.spy_number > 0)) {
3403 char *sa;
3404 struct iw_quality wstats;
3405
3406 /* Prepare spy data : addr + qual */
3407 if (!test_bit(FLAG_802_11, &ai->flags)) {
3408 sa = (char *) buffer + 6;
3409 bap_setup(ai, fid, 8, BAP0);
3410 bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0);
3411 } else
3412 sa = (char *) buffer + 10;
3413 wstats.qual = hdr.rssi[0];
3414 if (ai->rssi)
3415 wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
3416 else
3417 wstats.level = (hdr.rssi[1] + 321) / 2;
3418 wstats.noise = ai->wstats.qual.noise;
3419 wstats.updated = IW_QUAL_LEVEL_UPDATED
3420 | IW_QUAL_QUAL_UPDATED
3421 | IW_QUAL_DBM;
3422 /* Update spy records */
3423 wireless_spy_update(ai->dev, sa, &wstats);
3424 }
3425#endif /* WIRELESS_SPY */
3426
3427done:
3428 OUT4500(ai, EVACK, EV_RX);
3429
3430 if (success) {
3431 if (test_bit(FLAG_802_11, &ai->flags)) {
3432 skb_reset_mac_header(skb);
3433 skb->pkt_type = PACKET_OTHERHOST;
3434 skb->dev = ai->wifidev;
3435 skb->protocol = htons(ETH_P_802_2);
3436 } else
3437 skb->protocol = eth_type_trans(skb, ai->dev);
3438 skb->ip_summed = CHECKSUM_NONE;
3439
3440 netif_rx(skb);
3441 }
3442}
3443
3444static void airo_handle_tx(struct airo_info *ai, u16 status)
3445{
Colin Ian Kingd01a4e02018-07-09 14:38:43 +01003446 int i, index = -1;
Dan Williamsf55d4512009-01-24 09:04:12 -05003447 u16 fid;
3448
3449 if (test_bit(FLAG_MPI, &ai->flags)) {
3450 unsigned long flags;
3451
3452 if (status & EV_TXEXC)
3453 get_tx_error(ai, -1);
3454
3455 spin_lock_irqsave(&ai->aux_lock, flags);
3456 if (!skb_queue_empty(&ai->txq)) {
Lee Jonesba4d6512020-08-14 12:39:15 +01003457 spin_unlock_irqrestore(&ai->aux_lock, flags);
Dan Williamsf55d4512009-01-24 09:04:12 -05003458 mpi_send_packet(ai->dev);
3459 } else {
3460 clear_bit(FLAG_PENDING_XMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01003461 spin_unlock_irqrestore(&ai->aux_lock, flags);
Dan Williamsf55d4512009-01-24 09:04:12 -05003462 netif_wake_queue(ai->dev);
3463 }
3464 OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
3465 return;
3466 }
3467
3468 fid = IN4500(ai, TXCOMPLFID);
3469
Colin Ian Kingd01a4e02018-07-09 14:38:43 +01003470 for (i = 0; i < MAX_FIDS; i++) {
3471 if ((ai->fids[i] & 0xffff) == fid)
Dan Williamsf55d4512009-01-24 09:04:12 -05003472 index = i;
Dan Williamsf55d4512009-01-24 09:04:12 -05003473 }
3474
3475 if (index != -1) {
3476 if (status & EV_TXEXC)
3477 get_tx_error(ai, index);
3478
3479 OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC));
3480
3481 /* Set up to be used again */
3482 ai->fids[index] &= 0xffff;
3483 if (index < MAX_FIDS / 2) {
3484 if (!test_bit(FLAG_PENDING_XMIT, &ai->flags))
3485 netif_wake_queue(ai->dev);
3486 } else {
3487 if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags))
3488 netif_wake_queue(ai->wifidev);
3489 }
3490 } else {
3491 OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
3492 airo_print_err(ai->dev->name, "Unallocated FID was used to xmit");
3493 }
3494}
3495
Jeff Garzik28fc1f52007-10-29 05:46:16 -04003496static irqreturn_t airo_interrupt(int irq, void *dev_id)
3497{
3498 struct net_device *dev = dev_id;
Dan Williamsf55d4512009-01-24 09:04:12 -05003499 u16 status, savedInterrupts = 0;
3500 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003501 int handled = 0;
3502
3503 if (!netif_device_present(dev))
3504 return IRQ_NONE;
3505
3506 for (;;) {
Dan Williamsf55d4512009-01-24 09:04:12 -05003507 status = IN4500(ai, EVSTAT);
3508 if (!(status & STATUS_INTS) || (status == 0xffff))
3509 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003510
3511 handled = 1;
3512
Dan Williamsf55d4512009-01-24 09:04:12 -05003513 if (status & EV_AWAKE) {
3514 OUT4500(ai, EVACK, EV_AWAKE);
3515 OUT4500(ai, EVACK, EV_AWAKE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516 }
3517
3518 if (!savedInterrupts) {
Dan Williamsf55d4512009-01-24 09:04:12 -05003519 savedInterrupts = IN4500(ai, EVINTEN);
3520 OUT4500(ai, EVINTEN, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003521 }
3522
Dan Williamsf55d4512009-01-24 09:04:12 -05003523 if (status & EV_MIC) {
3524 OUT4500(ai, EVACK, EV_MIC);
3525 airo_handle_cisco_mic(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003526 }
Dan Williams6fcdf562006-03-31 15:08:46 -05003527
Dan Williamsf55d4512009-01-24 09:04:12 -05003528 if (status & EV_LINK) {
3529 /* Link status changed */
3530 airo_handle_link(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003531 }
3532
3533 /* Check to see if there is something to receive */
Dan Williamsf55d4512009-01-24 09:04:12 -05003534 if (status & EV_RX)
3535 airo_handle_rx(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003536
3537 /* Check to see if a packet has been transmitted */
Dan Williamsf55d4512009-01-24 09:04:12 -05003538 if (status & (EV_TX | EV_TXCPY | EV_TXEXC))
3539 airo_handle_tx(ai, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003540
Lee Jonesba4d6512020-08-14 12:39:15 +01003541 if (status & ~STATUS_INTS & ~IGNORE_INTS) {
Dan Williamsf55d4512009-01-24 09:04:12 -05003542 airo_print_warn(ai->dev->name, "Got weird status %x",
Lee Jonesba4d6512020-08-14 12:39:15 +01003543 status & ~STATUS_INTS & ~IGNORE_INTS);
Dan Williamsf55d4512009-01-24 09:04:12 -05003544 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003545 }
3546
3547 if (savedInterrupts)
Dan Williamsf55d4512009-01-24 09:04:12 -05003548 OUT4500(ai, EVINTEN, savedInterrupts);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003549
Linus Torvalds1da177e2005-04-16 15:20:36 -07003550 return IRQ_RETVAL(handled);
3551}
3552
3553/*
3554 * Routines to talk to the card
3555 */
3556
3557/*
3558 * This was originally written for the 4500, hence the name
3559 * NOTE: If use with 8bit mode and SMP bad things will happen!
3560 * Why would some one do 8 bit IO in an SMP machine?!?
3561 */
Lee Jonesba4d6512020-08-14 12:39:15 +01003562static void OUT4500(struct airo_info *ai, u16 reg, u16 val)
3563{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003564 if (test_bit(FLAG_MPI,&ai->flags))
3565 reg <<= 1;
Lee Jonesba4d6512020-08-14 12:39:15 +01003566 if (!do8bitIO)
3567 outw(val, ai->dev->base_addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003568 else {
Lee Jonesba4d6512020-08-14 12:39:15 +01003569 outb(val & 0xff, ai->dev->base_addr + reg);
3570 outb(val >> 8, ai->dev->base_addr + reg + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003571 }
3572}
3573
Lee Jonesba4d6512020-08-14 12:39:15 +01003574static u16 IN4500(struct airo_info *ai, u16 reg)
3575{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003576 unsigned short rc;
3577
3578 if (test_bit(FLAG_MPI,&ai->flags))
3579 reg <<= 1;
Lee Jonesba4d6512020-08-14 12:39:15 +01003580 if (!do8bitIO)
3581 rc = inw(ai->dev->base_addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003582 else {
Lee Jonesba4d6512020-08-14 12:39:15 +01003583 rc = inb(ai->dev->base_addr + reg);
3584 rc += ((int)inb(ai->dev->base_addr + reg + 1)) << 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003585 }
3586 return rc;
3587}
3588
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003589static int enable_MAC(struct airo_info *ai, int lock)
3590{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003591 int rc;
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003592 Cmd cmd;
3593 Resp rsp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003594
3595 /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions
3596 * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down"
3597 * Note : we could try to use !netif_running(dev) in enable_MAC()
3598 * instead of this flag, but I don't trust it *within* the
3599 * open/close functions, and testing both flags together is
3600 * "cheaper" - Jean II */
3601 if (ai->flags & FLAG_RADIO_MASK) return SUCCESS;
3602
3603 if (lock && down_interruptible(&ai->sem))
3604 return -ERESTARTSYS;
3605
3606 if (!test_bit(FLAG_ENABLED, &ai->flags)) {
3607 memset(&cmd, 0, sizeof(cmd));
3608 cmd.cmd = MAC_ENABLE;
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003609 rc = issuecommand(ai, &cmd, &rsp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003610 if (rc == SUCCESS)
3611 set_bit(FLAG_ENABLED, &ai->flags);
3612 } else
3613 rc = SUCCESS;
3614
3615 if (lock)
3616 up(&ai->sem);
3617
3618 if (rc)
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003619 airo_print_err(ai->dev->name, "Cannot enable MAC");
3620 else if ((rsp.status & 0xFF00) != 0) {
3621 airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, "
3622 "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2);
3623 rc = ERROR;
3624 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003625 return rc;
3626}
3627
Lee Jonesba4d6512020-08-14 12:39:15 +01003628static void disable_MAC(struct airo_info *ai, int lock)
3629{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003630 Cmd cmd;
3631 Resp rsp;
3632
Ondrej Zarydae04122015-10-16 21:04:14 +02003633 if (lock == 1 && down_interruptible(&ai->sem))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003634 return;
3635
3636 if (test_bit(FLAG_ENABLED, &ai->flags)) {
Ondrej Zarydae04122015-10-16 21:04:14 +02003637 if (lock != 2) /* lock == 2 means don't disable carrier */
3638 netif_carrier_off(ai->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003639 memset(&cmd, 0, sizeof(cmd));
3640 cmd.cmd = MAC_DISABLE; // disable in case already enabled
3641 issuecommand(ai, &cmd, &rsp);
3642 clear_bit(FLAG_ENABLED, &ai->flags);
3643 }
Ondrej Zarydae04122015-10-16 21:04:14 +02003644 if (lock == 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003645 up(&ai->sem);
3646}
3647
Lee Jonesba4d6512020-08-14 12:39:15 +01003648static void enable_interrupts(struct airo_info *ai)
3649{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003650 /* Enable the interrupts */
Lee Jonesba4d6512020-08-14 12:39:15 +01003651 OUT4500(ai, EVINTEN, STATUS_INTS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003652}
3653
Lee Jonesba4d6512020-08-14 12:39:15 +01003654static void disable_interrupts(struct airo_info *ai)
3655{
3656 OUT4500(ai, EVINTEN, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003657}
3658
3659static void mpi_receive_802_3(struct airo_info *ai)
3660{
3661 RxFid rxd;
3662 int len = 0;
3663 struct sk_buff *skb;
3664 char *buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003665 int off = 0;
3666 MICBuffer micbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003667
3668 memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
3669 /* Make sure we got something */
3670 if (rxd.rdy && rxd.valid == 0) {
3671 len = rxd.len + 12;
3672 if (len < 12 || len > 2048)
3673 goto badrx;
3674
3675 skb = dev_alloc_skb(len);
3676 if (!skb) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003677 ai->dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003678 goto badrx;
3679 }
Lee Jonesba4d6512020-08-14 12:39:15 +01003680 buffer = skb_put(skb, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003681 memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2);
3682 if (ai->micstats.enabled) {
3683 memcpy(&micbuf,
3684 ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2,
3685 sizeof(micbuf));
3686 if (ntohs(micbuf.typelen) <= 0x05DC) {
3687 if (len <= sizeof(micbuf) + ETH_ALEN * 2)
3688 goto badmic;
3689
3690 off = sizeof(micbuf);
3691 skb_trim (skb, len - off);
3692 }
3693 }
3694 memcpy(buffer + ETH_ALEN * 2,
3695 ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off,
3696 len - ETH_ALEN * 2 - off);
3697 if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) {
3698badmic:
3699 dev_kfree_skb_irq (skb);
3700 goto badrx;
3701 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003702#ifdef WIRELESS_SPY
3703 if (ai->spy_data.spy_number > 0) {
3704 char *sa;
3705 struct iw_quality wstats;
3706 /* Prepare spy data : addr + qual */
3707 sa = buffer + ETH_ALEN;
3708 wstats.qual = 0; /* XXX Where do I get that info from ??? */
3709 wstats.level = 0;
3710 wstats.updated = 0;
3711 /* Update spy records */
3712 wireless_spy_update(ai->dev, sa, &wstats);
3713 }
3714#endif /* WIRELESS_SPY */
3715
Linus Torvalds1da177e2005-04-16 15:20:36 -07003716 skb->ip_summed = CHECKSUM_NONE;
3717 skb->protocol = eth_type_trans(skb, ai->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003718 netif_rx(skb);
3719 }
3720badrx:
3721 if (rxd.valid == 0) {
3722 rxd.valid = 1;
3723 rxd.rdy = 0;
3724 rxd.len = PKTSIZE;
3725 memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
3726 }
3727}
3728
Hannes Eder2ed5ba82008-12-26 00:12:59 -08003729static void mpi_receive_802_11(struct airo_info *ai)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003730{
3731 RxFid rxd;
3732 struct sk_buff *skb = NULL;
Al Viro0300b332007-12-19 22:38:33 -05003733 u16 len, hdrlen = 0;
3734 __le16 fc;
Dan Williamsf55d4512009-01-24 09:04:12 -05003735 struct rx_hdr hdr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003736 u16 gap;
3737 u16 *buffer;
Dan Williamsf55d4512009-01-24 09:04:12 -05003738 char *ptr = ai->rxfids[0].virtual_host_addr + 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003739
3740 memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
3741 memcpy ((char *)&hdr, ptr, sizeof(hdr));
3742 ptr += sizeof(hdr);
3743 /* Bad CRC. Ignore packet */
3744 if (le16_to_cpu(hdr.status) & 2)
3745 hdr.len = 0;
3746 if (ai->wifidev == NULL)
3747 hdr.len = 0;
3748 len = le16_to_cpu(hdr.len);
Dan Williams15db2762006-03-16 13:46:27 -05003749 if (len > AIRO_DEF_MTU) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003750 airo_print_err(ai->dev->name, "Bad size %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003751 goto badrx;
3752 }
3753 if (len == 0)
3754 goto badrx;
3755
Al Viro0300b332007-12-19 22:38:33 -05003756 fc = get_unaligned((__le16 *)ptr);
3757 hdrlen = header_len(fc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003758
Lee Jonesba4d6512020-08-14 12:39:15 +01003759 skb = dev_alloc_skb(len + hdrlen + 2);
3760 if (!skb) {
Paulius Zaleckas5d9276d2008-05-05 22:38:34 +03003761 ai->dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003762 goto badrx;
3763 }
Johannes Berg4df864c2017-06-16 14:29:21 +02003764 buffer = skb_put(skb, len + hdrlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003765 memcpy ((char *)buffer, ptr, hdrlen);
3766 ptr += hdrlen;
3767 if (hdrlen == 24)
3768 ptr += 6;
Harvey Harrison533dd1b2008-04-29 01:03:36 -07003769 gap = get_unaligned_le16(ptr);
Al Viro593c2b92007-12-17 15:09:34 -05003770 ptr += sizeof(__le16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003771 if (gap) {
3772 if (gap <= 8)
3773 ptr += gap;
3774 else
Dan Williams934d8bf2006-03-16 13:46:23 -05003775 airo_print_err(ai->dev->name,
3776 "gaplen too big. Problems will follow...");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003777 }
3778 memcpy ((char *)buffer + hdrlen, ptr, len);
3779 ptr += len;
3780#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
3781 if (ai->spy_data.spy_number > 0) {
3782 char *sa;
3783 struct iw_quality wstats;
3784 /* Prepare spy data : addr + qual */
3785 sa = (char*)buffer + 10;
3786 wstats.qual = hdr.rssi[0];
3787 if (ai->rssi)
3788 wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
3789 else
3790 wstats.level = (hdr.rssi[1] + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04003791 wstats.noise = ai->wstats.qual.noise;
3792 wstats.updated = IW_QUAL_QUAL_UPDATED
3793 | IW_QUAL_LEVEL_UPDATED
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07003794 | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003795 /* Update spy records */
3796 wireless_spy_update(ai->dev, sa, &wstats);
3797 }
3798#endif /* IW_WIRELESS_SPY */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07003799 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003800 skb->pkt_type = PACKET_OTHERHOST;
3801 skb->dev = ai->wifidev;
3802 skb->protocol = htons(ETH_P_802_2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003803 skb->ip_summed = CHECKSUM_NONE;
Lee Jonesba4d6512020-08-14 12:39:15 +01003804 netif_rx(skb);
Dan Williamsf55d4512009-01-24 09:04:12 -05003805
Linus Torvalds1da177e2005-04-16 15:20:36 -07003806badrx:
3807 if (rxd.valid == 0) {
3808 rxd.valid = 1;
3809 rxd.rdy = 0;
3810 rxd.len = PKTSIZE;
3811 memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
3812 }
3813}
3814
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02003815static inline void set_auth_type(struct airo_info *local, int auth_type)
3816{
3817 local->config.authType = auth_type;
3818 /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT).
3819 * Used by airo_set_auth()
3820 */
3821 if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT)
3822 local->last_auth = auth_type;
3823}
3824
Linus Torvalds1da177e2005-04-16 15:20:36 -07003825static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
3826{
3827 Cmd cmd;
3828 Resp rsp;
3829 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003830 SsidRid mySsid;
Al Viro4293ea32007-12-19 19:21:51 -05003831 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003832 WepKeyRid wkr;
3833 int rc;
3834
Lee Jonesba4d6512020-08-14 12:39:15 +01003835 memset(&mySsid, 0, sizeof(mySsid));
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003836 kfree (ai->flash);
3837 ai->flash = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003838
3839 /* The NOP is the first step in getting the card going */
3840 cmd.cmd = NOP;
3841 cmd.parm0 = cmd.parm1 = cmd.parm2 = 0;
3842 if (lock && down_interruptible(&ai->sem))
3843 return ERROR;
Lee Jonesba4d6512020-08-14 12:39:15 +01003844 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003845 if (lock)
3846 up(&ai->sem);
3847 return ERROR;
3848 }
Lee Jonesba4d6512020-08-14 12:39:15 +01003849 disable_MAC(ai, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003850
3851 // Let's figure out if we need to use the AUX port
3852 if (!test_bit(FLAG_MPI,&ai->flags)) {
3853 cmd.cmd = CMD_ENABLEAUX;
3854 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
3855 if (lock)
3856 up(&ai->sem);
Dan Williams934d8bf2006-03-16 13:46:23 -05003857 airo_print_err(ai->dev->name, "Error checking for AUX port");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003858 return ERROR;
3859 }
3860 if (!aux_bap || rsp.status & 0xff00) {
3861 ai->bap_read = fast_bap_read;
Dan Williams934d8bf2006-03-16 13:46:23 -05003862 airo_print_dbg(ai->dev->name, "Doing fast bap_reads");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003863 } else {
3864 ai->bap_read = aux_bap_read;
Dan Williams934d8bf2006-03-16 13:46:23 -05003865 airo_print_dbg(ai->dev->name, "Doing AUX bap_reads");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003866 }
3867 }
3868 if (lock)
3869 up(&ai->sem);
3870 if (ai->config.len == 0) {
Hannes Eder49c4a5d2009-02-14 11:49:09 +00003871 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003872 tdsRssiRid rssi_rid;
3873 CapabilityRid cap_rid;
3874
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003875 kfree(ai->SSID);
3876 ai->SSID = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003877 // general configuration (read/modify/write)
3878 status = readConfigRid(ai, lock);
Lee Jonesba4d6512020-08-14 12:39:15 +01003879 if (status != SUCCESS) return ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003880
3881 status = readCapabilityRid(ai, &cap_rid, lock);
Lee Jonesba4d6512020-08-14 12:39:15 +01003882 if (status != SUCCESS) return ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003883
Lee Jonesba4d6512020-08-14 12:39:15 +01003884 status = PC4500_readrid(ai, RID_RSSI,&rssi_rid, sizeof(rssi_rid), lock);
3885 if (status == SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003886 if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
Dan Williams41480af2005-05-10 09:45:51 -04003887 memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003888 }
3889 else {
Jesper Juhlb4558ea2005-10-28 16:53:13 -04003890 kfree(ai->rssi);
3891 ai->rssi = NULL;
Al Viro56d81bd2007-12-20 17:18:35 -05003892 if (cap_rid.softCap & cpu_to_le16(8))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003893 ai->config.rmode |= RXMODE_NORMALIZED_RSSI;
3894 else
Dan Williams934d8bf2006-03-16 13:46:23 -05003895 airo_print_warn(ai->dev->name, "unknown received signal "
3896 "level scale");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003897 }
3898 ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02003899 set_auth_type(ai, AUTH_OPEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003900 ai->config.modulation = MOD_CCK;
3901
Al Viro56d81bd2007-12-20 17:18:35 -05003902 if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) &&
Al Viro15617852007-12-20 17:21:36 -05003903 (cap_rid.extSoftCap & cpu_to_le16(1)) &&
Al Viro56d81bd2007-12-20 17:18:35 -05003904 micsetup(ai) == SUCCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003905 ai->config.opmode |= MODE_MIC;
3906 set_bit(FLAG_MIC_CAPABLE, &ai->flags);
3907 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003908
3909 /* Save off the MAC */
Lee Jonesba4d6512020-08-14 12:39:15 +01003910 for (i = 0; i < ETH_ALEN; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003911 mac[i] = ai->config.macAddr[i];
3912 }
3913
3914 /* Check to see if there are any insmod configured
3915 rates to add */
Lee Jonesba4d6512020-08-14 12:39:15 +01003916 if (rates[0]) {
3917 memset(ai->config.rates, 0, sizeof(ai->config.rates));
3918 for (i = 0; i < 8 && rates[i]; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003919 ai->config.rates[i] = rates[i];
3920 }
3921 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003922 set_bit (FLAG_COMMIT, &ai->flags);
3923 }
3924
3925 /* Setup the SSIDs if present */
Lee Jonesba4d6512020-08-14 12:39:15 +01003926 if (ssids[0]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003927 int i;
Lee Jonesba4d6512020-08-14 12:39:15 +01003928 for (i = 0; i < 3 && ssids[i]; i++) {
Al Viro0dd22122007-12-17 16:11:57 -05003929 size_t len = strlen(ssids[i]);
3930 if (len > 32)
3931 len = 32;
3932 mySsid.ssids[i].len = cpu_to_le16(len);
3933 memcpy(mySsid.ssids[i].ssid, ssids[i], len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003934 }
Al Viro0dd22122007-12-17 16:11:57 -05003935 mySsid.len = cpu_to_le16(sizeof(mySsid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003936 }
3937
3938 status = writeConfigRid(ai, lock);
Lee Jonesba4d6512020-08-14 12:39:15 +01003939 if (status != SUCCESS) return ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003940
3941 /* Set up the SSID list */
Lee Jonesba4d6512020-08-14 12:39:15 +01003942 if (ssids[0]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003943 status = writeSsidRid(ai, &mySsid, lock);
Lee Jonesba4d6512020-08-14 12:39:15 +01003944 if (status != SUCCESS) return ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003945 }
3946
Michal Schmidt175ec1a2007-06-29 15:33:47 +02003947 status = enable_MAC(ai, lock);
3948 if (status != SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003949 return ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003950
3951 /* Grab the initial wep key, we gotta save it for auto_wep */
3952 rc = readWepKeyRid(ai, &wkr, 1, lock);
3953 if (rc == SUCCESS) do {
3954 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05003955 if (wkr.kindex == cpu_to_le16(0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003956 ai->defindex = wkr.mac[0];
3957 }
3958 rc = readWepKeyRid(ai, &wkr, 0, lock);
Lee Jonesba4d6512020-08-14 12:39:15 +01003959 } while (lastindex != wkr.kindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003960
Michal Schmidt1c2b7db2007-06-29 15:33:36 +02003961 try_auto_wep(ai);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003962
3963 return SUCCESS;
3964}
3965
Lee Jonesba4d6512020-08-14 12:39:15 +01003966static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp)
3967{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003968 // Im really paranoid about letting it run forever!
3969 int max_tries = 600000;
3970
3971 if (IN4500(ai, EVSTAT) & EV_CMD)
3972 OUT4500(ai, EVACK, EV_CMD);
3973
3974 OUT4500(ai, PARAM0, pCmd->parm0);
3975 OUT4500(ai, PARAM1, pCmd->parm1);
3976 OUT4500(ai, PARAM2, pCmd->parm2);
3977 OUT4500(ai, COMMAND, pCmd->cmd);
3978
3979 while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) {
3980 if ((IN4500(ai, COMMAND)) == pCmd->cmd)
3981 // PC4500 didn't notice command, try again
3982 OUT4500(ai, COMMAND, pCmd->cmd);
3983 if (!in_atomic() && (max_tries & 255) == 0)
3984 schedule();
3985 }
3986
Lee Jonesba4d6512020-08-14 12:39:15 +01003987 if (max_tries == -1) {
Dan Williams934d8bf2006-03-16 13:46:23 -05003988 airo_print_err(ai->dev->name,
Lucas De Marchi25985ed2011-03-30 22:57:33 -03003989 "Max tries exceeded when issuing command");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003990 if (IN4500(ai, COMMAND) & COMMAND_BUSY)
3991 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
3992 return ERROR;
3993 }
3994
3995 // command completed
3996 pRsp->status = IN4500(ai, STATUS);
3997 pRsp->rsp0 = IN4500(ai, RESP0);
3998 pRsp->rsp1 = IN4500(ai, RESP1);
3999 pRsp->rsp2 = IN4500(ai, RESP2);
Robert Schulze13dca9b2006-07-10 18:37:44 +02004000 if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET)
4001 airo_print_err(ai->dev->name,
4002 "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x",
4003 pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1,
4004 pRsp->rsp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004005
4006 // clear stuck command busy if necessary
4007 if (IN4500(ai, COMMAND) & COMMAND_BUSY) {
4008 OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
4009 }
4010 // acknowledge processing the status/response
4011 OUT4500(ai, EVACK, EV_CMD);
4012
4013 return SUCCESS;
4014}
4015
4016/* Sets up the bap to start exchange data. whichbap should
4017 * be one of the BAP0 or BAP1 defines. Locks should be held before
4018 * calling! */
Lee Jonesba4d6512020-08-14 12:39:15 +01004019static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004020{
4021 int timeout = 50;
4022 int max_tries = 3;
4023
4024 OUT4500(ai, SELECT0+whichbap, rid);
4025 OUT4500(ai, OFFSET0+whichbap, offset);
4026 while (1) {
4027 int status = IN4500(ai, OFFSET0+whichbap);
4028 if (status & BAP_BUSY) {
4029 /* This isn't really a timeout, but its kinda
4030 close */
4031 if (timeout--) {
4032 continue;
4033 }
Lee Jonesba4d6512020-08-14 12:39:15 +01004034 } else if (status & BAP_ERR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004035 /* invalid rid or offset */
Dan Williams934d8bf2006-03-16 13:46:23 -05004036 airo_print_err(ai->dev->name, "BAP error %x %d",
Lee Jonesba4d6512020-08-14 12:39:15 +01004037 status, whichbap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004038 return ERROR;
4039 } else if (status & BAP_DONE) { // success
4040 return SUCCESS;
4041 }
Lee Jonesba4d6512020-08-14 12:39:15 +01004042 if (!(max_tries--)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004043 airo_print_err(ai->dev->name,
Michal Schmidt1138c372007-06-29 15:33:41 +02004044 "BAP setup error too many retries\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004045 return ERROR;
4046 }
4047 // -- PC4500 missed it, try again
4048 OUT4500(ai, SELECT0+whichbap, rid);
4049 OUT4500(ai, OFFSET0+whichbap, offset);
4050 timeout = 50;
4051 }
4052}
4053
4054/* should only be called by aux_bap_read. This aux function and the
4055 following use concepts not documented in the developers guide. I
4056 got them from a patch given to my by Aironet */
4057static u16 aux_setup(struct airo_info *ai, u16 page,
4058 u16 offset, u16 *len)
4059{
4060 u16 next;
4061
4062 OUT4500(ai, AUXPAGE, page);
4063 OUT4500(ai, AUXOFF, 0);
4064 next = IN4500(ai, AUXDATA);
4065 *len = IN4500(ai, AUXDATA)&0xff;
4066 if (offset != 4) OUT4500(ai, AUXOFF, offset);
4067 return next;
4068}
4069
4070/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004071static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004072 int bytelen, int whichbap)
4073{
4074 u16 len;
4075 u16 page;
4076 u16 offset;
4077 u16 next;
4078 int words;
4079 int i;
4080 unsigned long flags;
4081
4082 spin_lock_irqsave(&ai->aux_lock, flags);
4083 page = IN4500(ai, SWS0+whichbap);
4084 offset = IN4500(ai, SWS2+whichbap);
4085 next = aux_setup(ai, page, offset, &len);
4086 words = (bytelen+1)>>1;
4087
Lee Jonesba4d6512020-08-14 12:39:15 +01004088 for (i = 0; i<words;) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004089 int count;
4090 count = (len>>1) < (words-i) ? (len>>1) : (words-i);
Lee Jonesba4d6512020-08-14 12:39:15 +01004091 if (!do8bitIO)
4092 insw(ai->dev->base_addr+DATA0+whichbap,
4093 pu16Dst+i, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004094 else
Lee Jonesba4d6512020-08-14 12:39:15 +01004095 insb(ai->dev->base_addr+DATA0+whichbap,
4096 pu16Dst+i, count << 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004097 i += count;
4098 if (i<words) {
4099 next = aux_setup(ai, next, 4, &len);
4100 }
4101 }
4102 spin_unlock_irqrestore(&ai->aux_lock, flags);
4103 return SUCCESS;
4104}
4105
4106
4107/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004108static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004109 int bytelen, int whichbap)
4110{
4111 bytelen = (bytelen + 1) & (~1); // round up to even value
Lee Jonesba4d6512020-08-14 12:39:15 +01004112 if (!do8bitIO)
4113 insw(ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004114 else
Lee Jonesba4d6512020-08-14 12:39:15 +01004115 insb(ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004116 return SUCCESS;
4117}
4118
4119/* requires call to bap_setup() first */
Al Virob8c06bc2007-12-19 17:55:43 -05004120static int bap_write(struct airo_info *ai, const __le16 *pu16Src,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004121 int bytelen, int whichbap)
4122{
4123 bytelen = (bytelen + 1) & (~1); // round up to even value
Lee Jonesba4d6512020-08-14 12:39:15 +01004124 if (!do8bitIO)
4125 outsw(ai->dev->base_addr+DATA0+whichbap,
4126 pu16Src, bytelen>>1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004127 else
Lee Jonesba4d6512020-08-14 12:39:15 +01004128 outsb(ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004129 return SUCCESS;
4130}
4131
4132static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd)
4133{
4134 Cmd cmd; /* for issuing commands */
4135 Resp rsp; /* response from commands */
4136 u16 status;
4137
4138 memset(&cmd, 0, sizeof(cmd));
4139 cmd.cmd = accmd;
4140 cmd.parm0 = rid;
4141 status = issuecommand(ai, &cmd, &rsp);
4142 if (status != 0) return status;
Lee Jonesba4d6512020-08-14 12:39:15 +01004143 if ((rsp.status & 0x7F00) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004144 return (accmd << 8) + (rsp.rsp0 & 0xFF);
4145 }
4146 return 0;
4147}
4148
4149/* Note, that we are using BAP1 which is also used by transmit, so
4150 * we must get a lock. */
4151static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock)
4152{
4153 u16 status;
4154 int rc = SUCCESS;
4155
4156 if (lock) {
4157 if (down_interruptible(&ai->sem))
4158 return ERROR;
4159 }
4160 if (test_bit(FLAG_MPI,&ai->flags)) {
4161 Cmd cmd;
4162 Resp rsp;
4163
4164 memset(&cmd, 0, sizeof(cmd));
4165 memset(&rsp, 0, sizeof(rsp));
4166 ai->config_desc.rid_desc.valid = 1;
4167 ai->config_desc.rid_desc.len = RIDSIZE;
4168 ai->config_desc.rid_desc.rid = 0;
4169 ai->config_desc.rid_desc.host_addr = ai->ridbus;
4170
4171 cmd.cmd = CMD_ACCESS;
4172 cmd.parm0 = rid;
4173
4174 memcpy_toio(ai->config_desc.card_ram_off,
4175 &ai->config_desc.rid_desc, sizeof(Rid));
4176
4177 rc = issuecommand(ai, &cmd, &rsp);
4178
4179 if (rsp.status & 0x7f00)
4180 rc = rsp.rsp0;
4181 if (!rc)
4182 memcpy(pBuf, ai->config_desc.virtual_host_addr, len);
4183 goto done;
4184 } else {
4185 if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) {
4186 rc = status;
4187 goto done;
4188 }
4189 if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
4190 rc = ERROR;
4191 goto done;
4192 }
4193 // read the rid length field
4194 bap_read(ai, pBuf, 2, BAP1);
4195 // length for remaining part of rid
Al Viro593c2b92007-12-17 15:09:34 -05004196 len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004197
Lee Jonesba4d6512020-08-14 12:39:15 +01004198 if (len <= 2) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004199 airo_print_err(ai->dev->name,
4200 "Rid %x has a length of %d which is too short",
Lee Jonesba4d6512020-08-14 12:39:15 +01004201 (int)rid, (int)len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004202 rc = ERROR;
4203 goto done;
4204 }
4205 // read remainder of the rid
Al Virob8c06bc2007-12-19 17:55:43 -05004206 rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004207 }
4208done:
4209 if (lock)
4210 up(&ai->sem);
4211 return rc;
4212}
4213
4214/* Note, that we are using BAP1 which is also used by transmit, so
Lucas De Marchi25985ed2011-03-30 22:57:33 -03004215 * make sure this isn't called when a transmit is happening */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004216static int PC4500_writerid(struct airo_info *ai, u16 rid,
4217 const void *pBuf, int len, int lock)
4218{
4219 u16 status;
4220 int rc = SUCCESS;
4221
Al Viro593c2b92007-12-17 15:09:34 -05004222 *(__le16*)pBuf = cpu_to_le16((u16)len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004223
4224 if (lock) {
4225 if (down_interruptible(&ai->sem))
4226 return ERROR;
4227 }
4228 if (test_bit(FLAG_MPI,&ai->flags)) {
4229 Cmd cmd;
4230 Resp rsp;
4231
Dan Streetmanf89b2322005-11-11 11:41:42 -05004232 if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid))
Dan Williams934d8bf2006-03-16 13:46:23 -05004233 airo_print_err(ai->dev->name,
4234 "%s: MAC should be disabled (rid=%04x)",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004235 __func__, rid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004236 memset(&cmd, 0, sizeof(cmd));
4237 memset(&rsp, 0, sizeof(rsp));
4238
4239 ai->config_desc.rid_desc.valid = 1;
4240 ai->config_desc.rid_desc.len = *((u16 *)pBuf);
4241 ai->config_desc.rid_desc.rid = 0;
4242
4243 cmd.cmd = CMD_WRITERID;
4244 cmd.parm0 = rid;
4245
4246 memcpy_toio(ai->config_desc.card_ram_off,
4247 &ai->config_desc.rid_desc, sizeof(Rid));
4248
4249 if (len < 4 || len > 2047) {
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004250 airo_print_err(ai->dev->name, "%s: len=%d", __func__, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004251 rc = -1;
4252 } else {
Joe Perches2c208892012-06-04 12:44:17 +00004253 memcpy(ai->config_desc.virtual_host_addr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004254 pBuf, len);
4255
4256 rc = issuecommand(ai, &cmd, &rsp);
4257 if ((rc & 0xff00) != 0) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004258 airo_print_err(ai->dev->name, "%s: Write rid Error %d",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004259 __func__, rc);
Dan Williams934d8bf2006-03-16 13:46:23 -05004260 airo_print_err(ai->dev->name, "%s: Cmd=%04x",
Harvey Harrisonc94c93d2008-07-28 23:01:34 -07004261 __func__, cmd.cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004262 }
4263
4264 if ((rsp.status & 0x7f00))
4265 rc = rsp.rsp0;
4266 }
4267 } else {
4268 // --- first access so that we can write the rid data
Lee Jonesba4d6512020-08-14 12:39:15 +01004269 if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004270 rc = status;
4271 goto done;
4272 }
4273 // --- now write the rid data
4274 if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
4275 rc = ERROR;
4276 goto done;
4277 }
4278 bap_write(ai, pBuf, len, BAP1);
4279 // ---now commit the rid data
4280 rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS);
4281 }
4282done:
4283 if (lock)
4284 up(&ai->sem);
4285 return rc;
4286}
4287
4288/* Allocates a FID to be used for transmitting packets. We only use
4289 one for now. */
4290static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw)
4291{
4292 unsigned int loop = 3000;
4293 Cmd cmd;
4294 Resp rsp;
4295 u16 txFid;
Al Viro593c2b92007-12-17 15:09:34 -05004296 __le16 txControl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004297
4298 cmd.cmd = CMD_ALLOCATETX;
4299 cmd.parm0 = lenPayload;
4300 if (down_interruptible(&ai->sem))
4301 return ERROR;
4302 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
4303 txFid = ERROR;
4304 goto done;
4305 }
Lee Jonesba4d6512020-08-14 12:39:15 +01004306 if ((rsp.status & 0xFF00) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004307 txFid = ERROR;
4308 goto done;
4309 }
4310 /* wait for the allocate event/indication
4311 * It makes me kind of nervous that this can just sit here and spin,
4312 * but in practice it only loops like four times. */
4313 while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop);
4314 if (!loop) {
4315 txFid = ERROR;
4316 goto done;
4317 }
4318
4319 // get the allocated fid and acknowledge
4320 txFid = IN4500(ai, TXALLOCFID);
4321 OUT4500(ai, EVACK, EV_ALLOC);
4322
4323 /* The CARD is pretty cool since it converts the ethernet packet
4324 * into 802.11. Also note that we don't release the FID since we
4325 * will be using the same one over and over again. */
4326 /* We only have to setup the control once since we are not
4327 * releasing the fid. */
4328 if (raw)
4329 txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11
4330 | TXCTL_ETHERNET | TXCTL_NORELEASE);
4331 else
4332 txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3
4333 | TXCTL_ETHERNET | TXCTL_NORELEASE);
4334 if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS)
4335 txFid = ERROR;
4336 else
4337 bap_write(ai, &txControl, sizeof(txControl), BAP1);
4338
4339done:
4340 up(&ai->sem);
4341
4342 return txFid;
4343}
4344
4345/* In general BAP1 is dedicated to transmiting packets. However,
4346 since we need a BAP when accessing RIDs, we also use BAP1 for that.
4347 Make sure the BAP1 spinlock is held when this is called. */
4348static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket)
4349{
Al Viro593c2b92007-12-17 15:09:34 -05004350 __le16 payloadLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004351 Cmd cmd;
4352 Resp rsp;
4353 int miclen = 0;
4354 u16 txFid = len;
4355 MICBuffer pMic;
4356
4357 len >>= 16;
4358
4359 if (len <= ETH_ALEN * 2) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004360 airo_print_warn(ai->dev->name, "Short packet %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004361 return ERROR;
4362 }
4363 len -= ETH_ALEN * 2;
4364
Lee Jonesba4d6512020-08-14 12:39:15 +01004365 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
Al Viro593c2b92007-12-17 15:09:34 -05004366 (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) {
Lee Jonesba4d6512020-08-14 12:39:15 +01004367 if (encapsulate(ai, (etherHead *)pPacket,&pMic, len) != SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004368 return ERROR;
4369 miclen = sizeof(pMic);
4370 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004371 // packet is destination[6], source[6], payload[len-12]
4372 // write the payload length and dst/src/payload
4373 if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR;
4374 /* The hardware addresses aren't counted as part of the payload, so
4375 * we have to subtract the 12 bytes for the addresses off */
4376 payloadLen = cpu_to_le16(len + miclen);
Lee Jonesba4d6512020-08-14 12:39:15 +01004377 bap_write(ai, &payloadLen, sizeof(payloadLen), BAP1);
Al Virob8c06bc2007-12-19 17:55:43 -05004378 bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004379 if (miclen)
Al Virob8c06bc2007-12-19 17:55:43 -05004380 bap_write(ai, (__le16*)&pMic, miclen, BAP1);
4381 bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004382 // issue the transmit command
Lee Jonesba4d6512020-08-14 12:39:15 +01004383 memset(&cmd, 0, sizeof(cmd));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004384 cmd.cmd = CMD_TRANSMIT;
4385 cmd.parm0 = txFid;
4386 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
Lee Jonesba4d6512020-08-14 12:39:15 +01004387 if ((rsp.status & 0xFF00) != 0) return ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004388 return SUCCESS;
4389}
4390
4391static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket)
4392{
Al Viro593c2b92007-12-17 15:09:34 -05004393 __le16 fc, payloadLen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004394 Cmd cmd;
4395 Resp rsp;
4396 int hdrlen;
Al Viro977b1432007-12-19 16:45:29 -05004397 static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6};
4398 /* padding of header to full size + le16 gaplen (6) + gaplen bytes */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004399 u16 txFid = len;
4400 len >>= 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004401
Al Viro0300b332007-12-19 22:38:33 -05004402 fc = *(__le16*)pPacket;
4403 hdrlen = header_len(fc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004404
4405 if (len < hdrlen) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004406 airo_print_warn(ai->dev->name, "Short packet %d", len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004407 return ERROR;
4408 }
4409
4410 /* packet is 802.11 header + payload
4411 * write the payload length and dst/src/payload */
4412 if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR;
4413 /* The 802.11 header aren't counted as part of the payload, so
4414 * we have to subtract the header bytes off */
4415 payloadLen = cpu_to_le16(len-hdrlen);
Lee Jonesba4d6512020-08-14 12:39:15 +01004416 bap_write(ai, &payloadLen, sizeof(payloadLen), BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004417 if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR;
Al Virob8c06bc2007-12-19 17:55:43 -05004418 bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1);
4419 bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004420
Al Virob8c06bc2007-12-19 17:55:43 -05004421 bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004422 // issue the transmit command
Lee Jonesba4d6512020-08-14 12:39:15 +01004423 memset(&cmd, 0, sizeof(cmd));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004424 cmd.cmd = CMD_TRANSMIT;
4425 cmd.parm0 = txFid;
4426 if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
Lee Jonesba4d6512020-08-14 12:39:15 +01004427 if ((rsp.status & 0xFF00) != 0) return ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004428 return SUCCESS;
4429}
4430
4431/*
4432 * This is the proc_fs routines. It is a bit messier than I would
4433 * like! Feel free to clean it up!
4434 */
4435
Lee Jonesba4d6512020-08-14 12:39:15 +01004436static ssize_t proc_read(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004437 char __user *buffer,
4438 size_t len,
4439 loff_t *offset);
4440
Lee Jonesba4d6512020-08-14 12:39:15 +01004441static ssize_t proc_write(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004442 const char __user *buffer,
4443 size_t len,
Lee Jonesba4d6512020-08-14 12:39:15 +01004444 loff_t *offset);
4445static int proc_close(struct inode *inode, struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004446
Lee Jonesba4d6512020-08-14 12:39:15 +01004447static int proc_stats_open(struct inode *inode, struct file *file);
4448static int proc_statsdelta_open(struct inode *inode, struct file *file);
4449static int proc_status_open(struct inode *inode, struct file *file);
4450static int proc_SSID_open(struct inode *inode, struct file *file);
4451static int proc_APList_open(struct inode *inode, struct file *file);
4452static int proc_BSSList_open(struct inode *inode, struct file *file);
4453static int proc_config_open(struct inode *inode, struct file *file);
4454static int proc_wepkey_open(struct inode *inode, struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004455
Alexey Dobriyan97a32532020-02-03 17:37:17 -08004456static const struct proc_ops proc_statsdelta_ops = {
4457 .proc_read = proc_read,
4458 .proc_open = proc_statsdelta_open,
4459 .proc_release = proc_close,
4460 .proc_lseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004461};
4462
Alexey Dobriyan97a32532020-02-03 17:37:17 -08004463static const struct proc_ops proc_stats_ops = {
4464 .proc_read = proc_read,
4465 .proc_open = proc_stats_open,
4466 .proc_release = proc_close,
4467 .proc_lseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004468};
4469
Alexey Dobriyan97a32532020-02-03 17:37:17 -08004470static const struct proc_ops proc_status_ops = {
4471 .proc_read = proc_read,
4472 .proc_open = proc_status_open,
4473 .proc_release = proc_close,
4474 .proc_lseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004475};
4476
Alexey Dobriyan97a32532020-02-03 17:37:17 -08004477static const struct proc_ops proc_SSID_ops = {
4478 .proc_read = proc_read,
4479 .proc_write = proc_write,
4480 .proc_open = proc_SSID_open,
4481 .proc_release = proc_close,
4482 .proc_lseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004483};
4484
Alexey Dobriyan97a32532020-02-03 17:37:17 -08004485static const struct proc_ops proc_BSSList_ops = {
4486 .proc_read = proc_read,
4487 .proc_write = proc_write,
4488 .proc_open = proc_BSSList_open,
4489 .proc_release = proc_close,
4490 .proc_lseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004491};
4492
Alexey Dobriyan97a32532020-02-03 17:37:17 -08004493static const struct proc_ops proc_APList_ops = {
4494 .proc_read = proc_read,
4495 .proc_write = proc_write,
4496 .proc_open = proc_APList_open,
4497 .proc_release = proc_close,
4498 .proc_lseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004499};
4500
Alexey Dobriyan97a32532020-02-03 17:37:17 -08004501static const struct proc_ops proc_config_ops = {
4502 .proc_read = proc_read,
4503 .proc_write = proc_write,
4504 .proc_open = proc_config_open,
4505 .proc_release = proc_close,
4506 .proc_lseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004507};
4508
Alexey Dobriyan97a32532020-02-03 17:37:17 -08004509static const struct proc_ops proc_wepkey_ops = {
4510 .proc_read = proc_read,
4511 .proc_write = proc_write,
4512 .proc_open = proc_wepkey_open,
4513 .proc_release = proc_close,
4514 .proc_lseek = default_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004515};
4516
4517static struct proc_dir_entry *airo_entry;
4518
4519struct proc_data {
4520 int release_buffer;
4521 int readlen;
4522 char *rbuffer;
4523 int writelen;
4524 int maxwritelen;
4525 char *wbuffer;
4526 void (*on_close) (struct inode *, struct file *);
4527};
4528
Lee Jonesba4d6512020-08-14 12:39:15 +01004529static int setup_proc_entry(struct net_device *dev,
4530 struct airo_info *apriv)
4531{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004532 struct proc_dir_entry *entry;
Eric W. Biederman1efa29c2012-02-10 14:01:03 -08004533
Linus Torvalds1da177e2005-04-16 15:20:36 -07004534 /* First setup the device directory */
Lee Jonesba4d6512020-08-14 12:39:15 +01004535 strcpy(apriv->proc_name, dev->name);
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004536 apriv->proc_entry = proc_mkdir_mode(apriv->proc_name, airo_perm,
4537 airo_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004538 if (!apriv->proc_entry)
David Howellsb25f7742013-04-12 03:05:20 +01004539 return -ENOMEM;
David Howells271a15e2013-04-12 00:38:51 +01004540 proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004541
4542 /* Setup the StatsDelta */
Joe Perches2ef00c52018-03-23 15:54:37 -07004543 entry = proc_create_data("StatsDelta", 0444 & proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004544 apriv->proc_entry, &proc_statsdelta_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004545 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004546 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004547 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004548
4549 /* Setup the Stats */
Joe Perches2ef00c52018-03-23 15:54:37 -07004550 entry = proc_create_data("Stats", 0444 & proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004551 apriv->proc_entry, &proc_stats_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004552 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004553 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004554 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004555
4556 /* Setup the Status */
Joe Perches2ef00c52018-03-23 15:54:37 -07004557 entry = proc_create_data("Status", 0444 & proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004558 apriv->proc_entry, &proc_status_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004559 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004560 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004561 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004562
4563 /* Setup the Config */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004564 entry = proc_create_data("Config", proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004565 apriv->proc_entry, &proc_config_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004566 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004567 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004568 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004569
4570 /* Setup the SSID */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004571 entry = proc_create_data("SSID", proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004572 apriv->proc_entry, &proc_SSID_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004573 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004574 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004575 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004576
4577 /* Setup the APList */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004578 entry = proc_create_data("APList", proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004579 apriv->proc_entry, &proc_APList_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004580 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004581 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004582 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004583
4584 /* Setup the BSSList */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004585 entry = proc_create_data("BSSList", proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004586 apriv->proc_entry, &proc_BSSList_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004587 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004588 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004589 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004590
4591 /* Setup the WepKey */
Alexey Dobriyan011159a2011-05-14 00:12:48 +03004592 entry = proc_create_data("WepKey", proc_perm,
Denis V. Luneva95609c2008-04-29 01:02:29 -07004593 apriv->proc_entry, &proc_wepkey_ops, dev);
Florin Malita431aca52006-10-10 16:46:30 -04004594 if (!entry)
David Howellsb25f7742013-04-12 03:05:20 +01004595 goto fail;
David Howells271a15e2013-04-12 00:38:51 +01004596 proc_set_user(entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004597 return 0;
Florin Malita431aca52006-10-10 16:46:30 -04004598
Florin Malita431aca52006-10-10 16:46:30 -04004599fail:
David Howellsb25f7742013-04-12 03:05:20 +01004600 remove_proc_subtree(apriv->proc_name, airo_entry);
Florin Malita431aca52006-10-10 16:46:30 -04004601 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004602}
4603
Lee Jonesba4d6512020-08-14 12:39:15 +01004604static int takedown_proc_entry(struct net_device *dev,
4605 struct airo_info *apriv)
David Howellsb25f7742013-04-12 03:05:20 +01004606{
4607 remove_proc_subtree(apriv->proc_name, airo_entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004608 return 0;
4609}
4610
4611/*
4612 * What we want from the proc_fs is to be able to efficiently read
4613 * and write the configuration. To do this, we want to read the
4614 * configuration when the file is opened and write it when the file is
4615 * closed. So basically we allocate a read buffer at open and fill it
4616 * with data, and allocate a write buffer and read it at close.
4617 */
4618
4619/*
4620 * The read routine is generic, it relies on the preallocated rbuffer
4621 * to supply the data.
4622 */
Lee Jonesba4d6512020-08-14 12:39:15 +01004623static ssize_t proc_read(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004624 char __user *buffer,
4625 size_t len,
Lee Jonesba4d6512020-08-14 12:39:15 +01004626 loff_t *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004627{
Akinobu Mitacc0d9ff2008-06-09 16:44:30 -07004628 struct proc_data *priv = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004629
4630 if (!priv->rbuffer)
4631 return -EINVAL;
4632
Akinobu Mitacc0d9ff2008-06-09 16:44:30 -07004633 return simple_read_from_buffer(buffer, len, offset, priv->rbuffer,
4634 priv->readlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004635}
4636
4637/*
4638 * The write routine is generic, it fills in a preallocated rbuffer
4639 * to supply the data.
4640 */
Lee Jonesba4d6512020-08-14 12:39:15 +01004641static ssize_t proc_write(struct file *file,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004642 const char __user *buffer,
4643 size_t len,
Lee Jonesba4d6512020-08-14 12:39:15 +01004644 loff_t *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004645{
Akinobu Mitaad9082a2010-12-25 15:03:58 +09004646 ssize_t ret;
Joe Perches57674302010-07-12 13:50:06 -07004647 struct proc_data *priv = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004648
4649 if (!priv->wbuffer)
4650 return -EINVAL;
4651
Akinobu Mitaad9082a2010-12-25 15:03:58 +09004652 ret = simple_write_to_buffer(priv->wbuffer, priv->maxwritelen, offset,
4653 buffer, len);
4654 if (ret > 0)
4655 priv->writelen = max_t(int, priv->writelen, *offset);
4656
4657 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004658}
4659
Al Viro329e2c02007-12-20 22:58:57 -05004660static int proc_status_open(struct inode *inode, struct file *file)
4661{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004662 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04004663 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08004664 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004665 CapabilityRid cap_rid;
4666 StatusRid status_rid;
Al Viro329e2c02007-12-20 22:58:57 -05004667 u16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004668 int i;
4669
Lee Jonesba4d6512020-08-14 12:39:15 +01004670 if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004671 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07004672 data = file->private_data;
Lee Jonesba4d6512020-08-14 12:39:15 +01004673 if ((data->rbuffer = kmalloc(2048, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004674 kfree (file->private_data);
4675 return -ENOMEM;
4676 }
4677
4678 readStatusRid(apriv, &status_rid, 1);
4679 readCapabilityRid(apriv, &cap_rid, 1);
4680
Al Viro329e2c02007-12-20 22:58:57 -05004681 mode = le16_to_cpu(status_rid.mode);
4682
Linus Torvalds1da177e2005-04-16 15:20:36 -07004683 i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n",
Al Viro329e2c02007-12-20 22:58:57 -05004684 mode & 1 ? "CFG ": "",
4685 mode & 2 ? "ACT ": "",
4686 mode & 0x10 ? "SYN ": "",
4687 mode & 0x20 ? "LNK ": "",
4688 mode & 0x40 ? "LEAP ": "",
4689 mode & 0x80 ? "PRIV ": "",
4690 mode & 0x100 ? "KEY ": "",
4691 mode & 0x200 ? "WEP ": "",
4692 mode & 0x8000 ? "ERR ": "");
Lee Jonesba4d6512020-08-14 12:39:15 +01004693 sprintf(data->rbuffer+i, "Mode: %x\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -07004694 "Signal Strength: %d\n"
4695 "Signal Quality: %d\n"
4696 "SSID: %-.*s\n"
4697 "AP: %-.16s\n"
4698 "Freq: %d\n"
4699 "BitRate: %dmbs\n"
4700 "Driver Version: %s\n"
4701 "Device: %s\nManufacturer: %s\nFirmware Version: %s\n"
4702 "Radio type: %x\nCountry: %x\nHardware Version: %x\n"
4703 "Software Version: %x\nSoftware Subversion: %x\n"
4704 "Boot block version: %x\n",
Al Viro329e2c02007-12-20 22:58:57 -05004705 le16_to_cpu(status_rid.mode),
4706 le16_to_cpu(status_rid.normalizedSignalStrength),
4707 le16_to_cpu(status_rid.signalQuality),
4708 le16_to_cpu(status_rid.SSIDlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07004709 status_rid.SSID,
4710 status_rid.apName,
Al Viro329e2c02007-12-20 22:58:57 -05004711 le16_to_cpu(status_rid.channel),
4712 le16_to_cpu(status_rid.currentXmitRate) / 2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004713 version,
4714 cap_rid.prodName,
4715 cap_rid.manName,
4716 cap_rid.prodVer,
Al Viro56d81bd2007-12-20 17:18:35 -05004717 le16_to_cpu(cap_rid.radioType),
4718 le16_to_cpu(cap_rid.country),
4719 le16_to_cpu(cap_rid.hardVer),
4720 le16_to_cpu(cap_rid.softVer),
4721 le16_to_cpu(cap_rid.softSubVer),
4722 le16_to_cpu(cap_rid.bootBlockVer));
Lee Jonesba4d6512020-08-14 12:39:15 +01004723 data->readlen = strlen(data->rbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004724 return 0;
4725}
4726
4727static int proc_stats_rid_open(struct inode*, struct file*, u16);
Lee Jonesba4d6512020-08-14 12:39:15 +01004728static int proc_statsdelta_open(struct inode *inode,
4729 struct file *file)
4730{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004731 if (file->f_mode&FMODE_WRITE) {
4732 return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR);
4733 }
4734 return proc_stats_rid_open(inode, file, RID_STATSDELTA);
4735}
4736
Lee Jonesba4d6512020-08-14 12:39:15 +01004737static int proc_stats_open(struct inode *inode, struct file *file)
4738{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004739 return proc_stats_rid_open(inode, file, RID_STATS);
4740}
4741
Lee Jonesba4d6512020-08-14 12:39:15 +01004742static int proc_stats_rid_open(struct inode *inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004743 struct file *file,
Lee Jonesba4d6512020-08-14 12:39:15 +01004744 u16 rid)
Al Viroa23ace52007-12-19 22:24:16 -05004745{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004746 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04004747 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08004748 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004749 StatsRid stats;
4750 int i, j;
Al Viroa23ace52007-12-19 22:24:16 -05004751 __le32 *vals = stats.vals;
John W. Linvillebc8263f2009-02-10 13:53:01 -05004752 int len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004753
Lee Jonesba4d6512020-08-14 12:39:15 +01004754 if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004755 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07004756 data = file->private_data;
Lee Jonesba4d6512020-08-14 12:39:15 +01004757 if ((data->rbuffer = kmalloc(4096, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004758 kfree (file->private_data);
4759 return -ENOMEM;
4760 }
4761
4762 readStatsRid(apriv, &stats, rid, 1);
John W. Linvillebc8263f2009-02-10 13:53:01 -05004763 len = le16_to_cpu(stats.len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004764
4765 j = 0;
Lee Jonesba4d6512020-08-14 12:39:15 +01004766 for (i = 0; statsLabels[i]!=(char *)-1 && i*4<len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004767 if (!statsLabels[i]) continue;
4768 if (j+strlen(statsLabels[i])+16>4096) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004769 airo_print_warn(apriv->dev->name,
Lucas De Marchi25985ed2011-03-30 22:57:33 -03004770 "Potentially disastrous buffer overflow averted!");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004771 break;
4772 }
Al Viroa23ace52007-12-19 22:24:16 -05004773 j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i],
4774 le32_to_cpu(vals[i]));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004775 }
Al Viroa23ace52007-12-19 22:24:16 -05004776 if (i*4 >= len) {
Dan Williams934d8bf2006-03-16 13:46:23 -05004777 airo_print_warn(apriv->dev->name, "Got a short rid");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004778 }
4779 data->readlen = j;
4780 return 0;
4781}
4782
Lee Jonesba4d6512020-08-14 12:39:15 +01004783static int get_dec_u16(char *buffer, int *start, int limit)
4784{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004785 u16 value;
4786 int valid = 0;
Roel Kluin30bd5722009-10-26 15:28:11 +01004787 for (value = 0; *start < limit && buffer[*start] >= '0' &&
4788 buffer[*start] <= '9'; (*start)++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004789 valid = 1;
4790 value *= 10;
4791 value += buffer[*start] - '0';
4792 }
Lee Jonesba4d6512020-08-14 12:39:15 +01004793 if (!valid) return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004794 return value;
4795}
4796
4797static int airo_config_commit(struct net_device *dev,
4798 struct iw_request_info *info, void *zwrq,
4799 char *extra);
4800
Al Viro3eb9b412007-12-21 00:00:35 -05004801static inline int sniffing_mode(struct airo_info *ai)
4802{
Michael Buesch1f351e32009-11-25 22:55:11 +01004803 return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >=
Al Viro3eb9b412007-12-21 00:00:35 -05004804 le16_to_cpu(RXMODE_RFMON);
4805}
4806
4807static void proc_config_on_close(struct inode *inode, struct file *file)
4808{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004809 struct proc_data *data = file->private_data;
Al Virod9dda782013-03-31 18:16:14 -04004810 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08004811 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004812 char *line;
4813
Lee Jonesba4d6512020-08-14 12:39:15 +01004814 if (!data->writelen) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004815
4816 readConfigRid(ai, 1);
4817 set_bit (FLAG_COMMIT, &ai->flags);
4818
4819 line = data->wbuffer;
Lee Jonesba4d6512020-08-14 12:39:15 +01004820 while (line[0]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004821/*** Mode processing */
Lee Jonesba4d6512020-08-14 12:39:15 +01004822 if (!strncmp(line, "Mode: ", 6)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004823 line += 6;
Al Viro3eb9b412007-12-21 00:00:35 -05004824 if (sniffing_mode(ai))
4825 set_bit (FLAG_RESET, &ai->flags);
4826 ai->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004827 clear_bit (FLAG_802_11, &ai->flags);
Al Viro3eb9b412007-12-21 00:00:35 -05004828 ai->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004829 ai->config.scanMode = SCANMODE_ACTIVE;
Lee Jonesba4d6512020-08-14 12:39:15 +01004830 if (line[0] == 'a') {
Al Viro3eb9b412007-12-21 00:00:35 -05004831 ai->config.opmode |= MODE_STA_IBSS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004832 } else {
Al Viro3eb9b412007-12-21 00:00:35 -05004833 ai->config.opmode |= MODE_STA_ESS;
Lee Jonesba4d6512020-08-14 12:39:15 +01004834 if (line[0] == 'r') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004835 ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
4836 ai->config.scanMode = SCANMODE_PASSIVE;
4837 set_bit (FLAG_802_11, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004838 } else if (line[0] == 'y') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004839 ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER;
4840 ai->config.scanMode = SCANMODE_PASSIVE;
4841 set_bit (FLAG_802_11, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004842 } else if (line[0] == 'l')
Linus Torvalds1da177e2005-04-16 15:20:36 -07004843 ai->config.rmode |= RXMODE_LANMON;
4844 }
4845 set_bit (FLAG_COMMIT, &ai->flags);
4846 }
4847
4848/*** Radio status */
4849 else if (!strncmp(line,"Radio: ", 7)) {
4850 line += 7;
Lee Jonesba4d6512020-08-14 12:39:15 +01004851 if (!strncmp(line,"off", 3)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004852 set_bit (FLAG_RADIO_OFF, &ai->flags);
4853 } else {
4854 clear_bit (FLAG_RADIO_OFF, &ai->flags);
4855 }
4856 }
4857/*** NodeName processing */
Lee Jonesba4d6512020-08-14 12:39:15 +01004858 else if (!strncmp(line, "NodeName: ", 10)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004859 int j;
4860
4861 line += 10;
Lee Jonesba4d6512020-08-14 12:39:15 +01004862 memset(ai->config.nodeName, 0, 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004863/* Do the name, assume a space between the mode and node name */
Lee Jonesba4d6512020-08-14 12:39:15 +01004864 for (j = 0; j < 16 && line[j] != '\n'; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004865 ai->config.nodeName[j] = line[j];
4866 }
4867 set_bit (FLAG_COMMIT, &ai->flags);
4868 }
4869
4870/*** PowerMode processing */
Lee Jonesba4d6512020-08-14 12:39:15 +01004871 else if (!strncmp(line, "PowerMode: ", 11)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004872 line += 11;
Lee Jonesba4d6512020-08-14 12:39:15 +01004873 if (!strncmp(line, "PSPCAM", 6)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004874 ai->config.powerSaveMode = POWERSAVE_PSPCAM;
4875 set_bit (FLAG_COMMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004876 } else if (!strncmp(line, "PSP", 3)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004877 ai->config.powerSaveMode = POWERSAVE_PSP;
4878 set_bit (FLAG_COMMIT, &ai->flags);
4879 } else {
4880 ai->config.powerSaveMode = POWERSAVE_CAM;
4881 set_bit (FLAG_COMMIT, &ai->flags);
4882 }
Lee Jonesba4d6512020-08-14 12:39:15 +01004883 } else if (!strncmp(line, "DataRates: ", 11)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004884 int v, i = 0, k = 0; /* i is index into line,
4885 k is index to rates */
4886
4887 line += 11;
Lee Jonesba4d6512020-08-14 12:39:15 +01004888 while ((v = get_dec_u16(line, &i, 3))!=-1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004889 ai->config.rates[k++] = (u8)v;
4890 line += i + 1;
4891 i = 0;
4892 }
4893 set_bit (FLAG_COMMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004894 } else if (!strncmp(line, "Channel: ", 9)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004895 int v, i = 0;
4896 line += 9;
4897 v = get_dec_u16(line, &i, i+3);
Lee Jonesba4d6512020-08-14 12:39:15 +01004898 if (v != -1) {
Al Viro3eb9b412007-12-21 00:00:35 -05004899 ai->config.channelSet = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004900 set_bit (FLAG_COMMIT, &ai->flags);
4901 }
Lee Jonesba4d6512020-08-14 12:39:15 +01004902 } else if (!strncmp(line, "XmitPower: ", 11)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004903 int v, i = 0;
4904 line += 11;
4905 v = get_dec_u16(line, &i, i+3);
Lee Jonesba4d6512020-08-14 12:39:15 +01004906 if (v != -1) {
Al Viro3eb9b412007-12-21 00:00:35 -05004907 ai->config.txPower = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004908 set_bit (FLAG_COMMIT, &ai->flags);
4909 }
Lee Jonesba4d6512020-08-14 12:39:15 +01004910 } else if (!strncmp(line, "WEP: ", 5)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004911 line += 5;
Lee Jonesba4d6512020-08-14 12:39:15 +01004912 switch(line[0]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004913 case 's':
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02004914 set_auth_type(ai, AUTH_SHAREDKEY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004915 break;
4916 case 'e':
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02004917 set_auth_type(ai, AUTH_ENCRYPT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004918 break;
4919 default:
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02004920 set_auth_type(ai, AUTH_OPEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004921 break;
4922 }
4923 set_bit (FLAG_COMMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004924 } else if (!strncmp(line, "LongRetryLimit: ", 16)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004925 int v, i = 0;
4926
4927 line += 16;
4928 v = get_dec_u16(line, &i, 3);
4929 v = (v<0) ? 0 : ((v>255) ? 255 : v);
Al Viro3eb9b412007-12-21 00:00:35 -05004930 ai->config.longRetryLimit = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004931 set_bit (FLAG_COMMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004932 } else if (!strncmp(line, "ShortRetryLimit: ", 17)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004933 int v, i = 0;
4934
4935 line += 17;
4936 v = get_dec_u16(line, &i, 3);
4937 v = (v<0) ? 0 : ((v>255) ? 255 : v);
Al Viro3eb9b412007-12-21 00:00:35 -05004938 ai->config.shortRetryLimit = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004939 set_bit (FLAG_COMMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004940 } else if (!strncmp(line, "RTSThreshold: ", 14)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004941 int v, i = 0;
4942
4943 line += 14;
4944 v = get_dec_u16(line, &i, 4);
Dan Williams15db2762006-03-16 13:46:27 -05004945 v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
Al Viro3eb9b412007-12-21 00:00:35 -05004946 ai->config.rtsThres = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004947 set_bit (FLAG_COMMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004948 } else if (!strncmp(line, "TXMSDULifetime: ", 16)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004949 int v, i = 0;
4950
4951 line += 16;
4952 v = get_dec_u16(line, &i, 5);
4953 v = (v<0) ? 0 : v;
Al Viro3eb9b412007-12-21 00:00:35 -05004954 ai->config.txLifetime = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004955 set_bit (FLAG_COMMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004956 } else if (!strncmp(line, "RXMSDULifetime: ", 16)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004957 int v, i = 0;
4958
4959 line += 16;
4960 v = get_dec_u16(line, &i, 5);
4961 v = (v<0) ? 0 : v;
Al Viro3eb9b412007-12-21 00:00:35 -05004962 ai->config.rxLifetime = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004963 set_bit (FLAG_COMMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004964 } else if (!strncmp(line, "TXDiversity: ", 13)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004965 ai->config.txDiversity =
4966 (line[13]=='l') ? 1 :
4967 ((line[13]=='r')? 2: 3);
4968 set_bit (FLAG_COMMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004969 } else if (!strncmp(line, "RXDiversity: ", 13)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004970 ai->config.rxDiversity =
4971 (line[13]=='l') ? 1 :
4972 ((line[13]=='r')? 2: 3);
4973 set_bit (FLAG_COMMIT, &ai->flags);
Lee Jonesba4d6512020-08-14 12:39:15 +01004974 } else if (!strncmp(line, "FragThreshold: ", 15)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004975 int v, i = 0;
4976
4977 line += 15;
4978 v = get_dec_u16(line, &i, 4);
Dan Williams15db2762006-03-16 13:46:27 -05004979 v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004980 v = v & 0xfffe; /* Make sure its even */
Al Viro3eb9b412007-12-21 00:00:35 -05004981 ai->config.fragThresh = cpu_to_le16(v);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004982 set_bit (FLAG_COMMIT, &ai->flags);
4983 } else if (!strncmp(line, "Modulation: ", 12)) {
4984 line += 12;
4985 switch(*line) {
Lee Jonesba4d6512020-08-14 12:39:15 +01004986 case 'd': ai->config.modulation = MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break;
4987 case 'c': ai->config.modulation = MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break;
4988 case 'm': ai->config.modulation = MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break;
Dan Williams934d8bf2006-03-16 13:46:23 -05004989 default: airo_print_warn(ai->dev->name, "Unknown modulation");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004990 }
4991 } else if (!strncmp(line, "Preamble: ", 10)) {
4992 line += 10;
4993 switch(*line) {
Lee Jonesba4d6512020-08-14 12:39:15 +01004994 case 'a': ai->config.preamble = PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break;
4995 case 'l': ai->config.preamble = PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break;
4996 case 's': ai->config.preamble = PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break;
Dan Williams934d8bf2006-03-16 13:46:23 -05004997 default: airo_print_warn(ai->dev->name, "Unknown preamble");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004998 }
4999 } else {
Dan Williams934d8bf2006-03-16 13:46:23 -05005000 airo_print_warn(ai->dev->name, "Couldn't figure out %s", line);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005001 }
Lee Jonesba4d6512020-08-14 12:39:15 +01005002 while (line[0] && line[0] != '\n') line++;
5003 if (line[0]) line++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005004 }
5005 airo_config_commit(dev, NULL, NULL, NULL);
5006}
5007
Stephen Hemminger7a374d82010-09-01 18:17:21 -07005008static const char *get_rmode(__le16 mode)
Al Viro3eb9b412007-12-21 00:00:35 -05005009{
5010 switch(mode & RXMODE_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005011 case RXMODE_RFMON: return "rfmon";
5012 case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon";
5013 case RXMODE_LANMON: return "lanmon";
5014 }
5015 return "ESS";
5016}
5017
Al Viro3eb9b412007-12-21 00:00:35 -05005018static int proc_config_open(struct inode *inode, struct file *file)
5019{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005020 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005021 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005022 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005023 int i;
Al Viro3eb9b412007-12-21 00:00:35 -05005024 __le16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005025
Lee Jonesba4d6512020-08-14 12:39:15 +01005026 if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005027 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07005028 data = file->private_data;
Lee Jonesba4d6512020-08-14 12:39:15 +01005029 if ((data->rbuffer = kmalloc(2048, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005030 kfree (file->private_data);
5031 return -ENOMEM;
5032 }
Lee Jonesba4d6512020-08-14 12:39:15 +01005033 if ((data->wbuffer = kzalloc(2048, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005034 kfree (data->rbuffer);
5035 kfree (file->private_data);
5036 return -ENOMEM;
5037 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005038 data->maxwritelen = 2048;
5039 data->on_close = proc_config_on_close;
5040
5041 readConfigRid(ai, 1);
5042
Al Viro3eb9b412007-12-21 00:00:35 -05005043 mode = ai->config.opmode & MODE_CFG_MASK;
Lee Jonesba4d6512020-08-14 12:39:15 +01005044 i = sprintf(data->rbuffer,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005045 "Mode: %s\n"
5046 "Radio: %s\n"
5047 "NodeName: %-16s\n"
5048 "PowerMode: %s\n"
5049 "DataRates: %d %d %d %d %d %d %d %d\n"
5050 "Channel: %d\n"
5051 "XmitPower: %d\n",
Al Viro3eb9b412007-12-21 00:00:35 -05005052 mode == MODE_STA_IBSS ? "adhoc" :
5053 mode == MODE_STA_ESS ? get_rmode(ai->config.rmode):
5054 mode == MODE_AP ? "AP" :
5055 mode == MODE_AP_RPTR ? "AP RPTR" : "Error",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005056 test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on",
5057 ai->config.nodeName,
Al Viro3eb9b412007-12-21 00:00:35 -05005058 ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" :
5059 ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" :
5060 ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" :
5061 "Error",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005062 (int)ai->config.rates[0],
5063 (int)ai->config.rates[1],
5064 (int)ai->config.rates[2],
5065 (int)ai->config.rates[3],
5066 (int)ai->config.rates[4],
5067 (int)ai->config.rates[5],
5068 (int)ai->config.rates[6],
5069 (int)ai->config.rates[7],
Al Viro3eb9b412007-12-21 00:00:35 -05005070 le16_to_cpu(ai->config.channelSet),
5071 le16_to_cpu(ai->config.txPower)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005072 );
Lee Jonesba4d6512020-08-14 12:39:15 +01005073 sprintf(data->rbuffer + i,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005074 "LongRetryLimit: %d\n"
5075 "ShortRetryLimit: %d\n"
5076 "RTSThreshold: %d\n"
5077 "TXMSDULifetime: %d\n"
5078 "RXMSDULifetime: %d\n"
5079 "TXDiversity: %s\n"
5080 "RXDiversity: %s\n"
5081 "FragThreshold: %d\n"
5082 "WEP: %s\n"
5083 "Modulation: %s\n"
5084 "Preamble: %s\n",
Al Viro3eb9b412007-12-21 00:00:35 -05005085 le16_to_cpu(ai->config.longRetryLimit),
5086 le16_to_cpu(ai->config.shortRetryLimit),
5087 le16_to_cpu(ai->config.rtsThres),
5088 le16_to_cpu(ai->config.txLifetime),
5089 le16_to_cpu(ai->config.rxLifetime),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005090 ai->config.txDiversity == 1 ? "left" :
5091 ai->config.txDiversity == 2 ? "right" : "both",
5092 ai->config.rxDiversity == 1 ? "left" :
5093 ai->config.rxDiversity == 2 ? "right" : "both",
Al Viro3eb9b412007-12-21 00:00:35 -05005094 le16_to_cpu(ai->config.fragThresh),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005095 ai->config.authType == AUTH_ENCRYPT ? "encrypt" :
5096 ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open",
Al Viro3eb9b412007-12-21 00:00:35 -05005097 ai->config.modulation == MOD_DEFAULT ? "default" :
Linus Torvalds1da177e2005-04-16 15:20:36 -07005098 ai->config.modulation == MOD_CCK ? "cck" :
5099 ai->config.modulation == MOD_MOK ? "mok" : "error",
5100 ai->config.preamble == PREAMBLE_AUTO ? "auto" :
5101 ai->config.preamble == PREAMBLE_LONG ? "long" :
5102 ai->config.preamble == PREAMBLE_SHORT ? "short" : "error"
5103 );
Lee Jonesba4d6512020-08-14 12:39:15 +01005104 data->readlen = strlen(data->rbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005105 return 0;
5106}
5107
Al Viro0dd22122007-12-17 16:11:57 -05005108static void proc_SSID_on_close(struct inode *inode, struct file *file)
5109{
Joe Perches57674302010-07-12 13:50:06 -07005110 struct proc_data *data = file->private_data;
Al Virod9dda782013-03-31 18:16:14 -04005111 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005112 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005113 SsidRid SSID_rid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005114 int i;
Al Viro0dd22122007-12-17 16:11:57 -05005115 char *p = data->wbuffer;
5116 char *end = p + data->writelen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005117
Al Viro0dd22122007-12-17 16:11:57 -05005118 if (!data->writelen)
5119 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005120
Al Viro0dd22122007-12-17 16:11:57 -05005121 *end = '\n'; /* sentinel; we have space for it */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005122
Al Viro0dd22122007-12-17 16:11:57 -05005123 memset(&SSID_rid, 0, sizeof(SSID_rid));
5124
5125 for (i = 0; i < 3 && p < end; i++) {
5126 int j = 0;
5127 /* copy up to 32 characters from this line */
5128 while (*p != '\n' && j < 32)
5129 SSID_rid.ssids[i].ssid[j++] = *p++;
5130 if (j == 0)
5131 break;
5132 SSID_rid.ssids[i].len = cpu_to_le16(j);
5133 /* skip to the beginning of the next line */
5134 while (*p++ != '\n')
5135 ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005136 }
5137 if (i)
Al Viro0dd22122007-12-17 16:11:57 -05005138 SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005139 disable_MAC(ai, 1);
5140 writeSsidRid(ai, &SSID_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005141 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005142}
5143
Lee Jonesba4d6512020-08-14 12:39:15 +01005144static void proc_APList_on_close(struct inode *inode, struct file *file)
5145{
Joe Perches57674302010-07-12 13:50:06 -07005146 struct proc_data *data = file->private_data;
Al Virod9dda782013-03-31 18:16:14 -04005147 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005148 struct airo_info *ai = dev->ml_priv;
Ondrej Zaryf675f932015-10-16 21:04:13 +02005149 APListRid *APList_rid = &ai->APList;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005150 int i;
5151
Lee Jonesba4d6512020-08-14 12:39:15 +01005152 if (!data->writelen) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005153
Ondrej Zaryf675f932015-10-16 21:04:13 +02005154 memset(APList_rid, 0, sizeof(*APList_rid));
5155 APList_rid->len = cpu_to_le16(sizeof(*APList_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005156
Andy Shevchenkob31fa552015-11-27 18:29:31 +02005157 for (i = 0; i < 4 && data->writelen >= (i + 1) * 6 * 3; i++)
5158 mac_pton(data->wbuffer + i * 6 * 3, APList_rid->ap[i]);
5159
Linus Torvalds1da177e2005-04-16 15:20:36 -07005160 disable_MAC(ai, 1);
Ondrej Zaryf675f932015-10-16 21:04:13 +02005161 writeAPListRid(ai, APList_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005162 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005163}
5164
5165/* This function wraps PC4500_writerid with a MAC disable */
Lee Jonesba4d6512020-08-14 12:39:15 +01005166static int do_writerid(struct airo_info *ai, u16 rid, const void *rid_data,
5167 int len, int dummy)
5168{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005169 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005170
5171 disable_MAC(ai, 1);
5172 rc = PC4500_writerid(ai, rid, rid_data, len, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005173 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005174 return rc;
5175}
5176
Dan Williamsc0380692009-01-24 09:12:15 -05005177/* Returns the WEP key at the specified index, or -1 if that key does
5178 * not exist. The buffer is assumed to be at least 16 bytes in length.
Linus Torvalds1da177e2005-04-16 15:20:36 -07005179 */
Dan Williamsc0380692009-01-24 09:12:15 -05005180static int get_wep_key(struct airo_info *ai, u16 index, char *buf, u16 buflen)
5181{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005182 WepKeyRid wkr;
5183 int rc;
Al Viro4293ea32007-12-19 19:21:51 -05005184 __le16 lastindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005185
5186 rc = readWepKeyRid(ai, &wkr, 1, 1);
Dan Williamsc0380692009-01-24 09:12:15 -05005187 if (rc != SUCCESS)
5188 return -1;
5189 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005190 lastindex = wkr.kindex;
Dan Williamsc0380692009-01-24 09:12:15 -05005191 if (le16_to_cpu(wkr.kindex) == index) {
5192 int klen = min_t(int, buflen, le16_to_cpu(wkr.klen));
5193 memcpy(buf, wkr.key, klen);
5194 return klen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005195 }
Dan Williamsc0380692009-01-24 09:12:15 -05005196 rc = readWepKeyRid(ai, &wkr, 0, 1);
5197 if (rc != SUCCESS)
5198 return -1;
Al Viro4293ea32007-12-19 19:21:51 -05005199 } while (lastindex != wkr.kindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005200 return -1;
5201}
5202
Dan Williamsc0380692009-01-24 09:12:15 -05005203static int get_wep_tx_idx(struct airo_info *ai)
5204{
5205 WepKeyRid wkr;
5206 int rc;
5207 __le16 lastindex;
5208
5209 rc = readWepKeyRid(ai, &wkr, 1, 1);
5210 if (rc != SUCCESS)
5211 return -1;
5212 do {
5213 lastindex = wkr.kindex;
5214 if (wkr.kindex == cpu_to_le16(0xffff))
5215 return wkr.mac[0];
5216 rc = readWepKeyRid(ai, &wkr, 0, 1);
5217 if (rc != SUCCESS)
5218 return -1;
5219 } while (lastindex != wkr.kindex);
5220 return -1;
5221}
5222
5223static int set_wep_key(struct airo_info *ai, u16 index, const char *key,
5224 u16 keylen, int perm, int lock)
Al Viro4293ea32007-12-19 19:21:51 -05005225{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005226 static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
5227 WepKeyRid wkr;
Dan Williamsc0380692009-01-24 09:12:15 -05005228 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005229
Stanislaw Gruszka6510b892010-02-26 15:10:28 +01005230 if (WARN_ON(keylen == 0))
5231 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005232
Dan Williamsc0380692009-01-24 09:12:15 -05005233 memset(&wkr, 0, sizeof(wkr));
5234 wkr.len = cpu_to_le16(sizeof(wkr));
5235 wkr.kindex = cpu_to_le16(index);
5236 wkr.klen = cpu_to_le16(keylen);
5237 memcpy(wkr.key, key, keylen);
5238 memcpy(wkr.mac, macaddr, ETH_ALEN);
5239
Dan Streetmanf89b2322005-11-11 11:41:42 -05005240 if (perm) disable_MAC(ai, lock);
Dan Williamsc0380692009-01-24 09:12:15 -05005241 rc = writeWepKeyRid(ai, &wkr, perm, lock);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005242 if (perm) enable_MAC(ai, lock);
Dan Williamsc0380692009-01-24 09:12:15 -05005243 return rc;
5244}
5245
5246static int set_wep_tx_idx(struct airo_info *ai, u16 index, int perm, int lock)
5247{
5248 WepKeyRid wkr;
5249 int rc;
5250
5251 memset(&wkr, 0, sizeof(wkr));
5252 wkr.len = cpu_to_le16(sizeof(wkr));
5253 wkr.kindex = cpu_to_le16(0xffff);
5254 wkr.mac[0] = (char)index;
5255
5256 if (perm) {
5257 ai->defindex = (char)index;
5258 disable_MAC(ai, lock);
5259 }
5260
5261 rc = writeWepKeyRid(ai, &wkr, perm, lock);
5262
5263 if (perm)
5264 enable_MAC(ai, lock);
5265 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005266}
5267
Lee Jonesba4d6512020-08-14 12:39:15 +01005268static void proc_wepkey_on_close(struct inode *inode, struct file *file)
5269{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005270 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005271 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005272 struct airo_info *ai = dev->ml_priv;
Dan Williamsc0380692009-01-24 09:12:15 -05005273 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005274 char key[16];
5275 u16 index = 0;
5276 int j = 0;
5277
5278 memset(key, 0, sizeof(key));
5279
Joe Perches57674302010-07-12 13:50:06 -07005280 data = file->private_data;
Lee Jonesba4d6512020-08-14 12:39:15 +01005281 if (!data->writelen) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005282
5283 if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' &&
5284 (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) {
5285 index = data->wbuffer[0] - '0';
5286 if (data->wbuffer[1] == '\n') {
Dan Williamsc0380692009-01-24 09:12:15 -05005287 rc = set_wep_tx_idx(ai, index, 1, 1);
5288 if (rc < 0) {
5289 airo_print_err(ai->dev->name, "failed to set "
5290 "WEP transmit index to %d: %d.",
5291 index, rc);
5292 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005293 return;
5294 }
5295 j = 2;
5296 } else {
Dan Williams934d8bf2006-03-16 13:46:23 -05005297 airo_print_err(ai->dev->name, "WepKey passed invalid key index");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005298 return;
5299 }
5300
Lee Jonesba4d6512020-08-14 12:39:15 +01005301 for (i = 0; i < 16*3 && data->wbuffer[i+j]; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005302 switch(i%3) {
5303 case 0:
Andy Shevchenko26355382010-05-24 14:33:28 -07005304 key[i/3] = hex_to_bin(data->wbuffer[i+j])<<4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005305 break;
5306 case 1:
Andy Shevchenko26355382010-05-24 14:33:28 -07005307 key[i/3] |= hex_to_bin(data->wbuffer[i+j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005308 break;
5309 }
5310 }
Dan Williamsc0380692009-01-24 09:12:15 -05005311
5312 rc = set_wep_key(ai, index, key, i/3, 1, 1);
5313 if (rc < 0) {
5314 airo_print_err(ai->dev->name, "failed to set WEP key at index "
5315 "%d: %d.", index, rc);
5316 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005317}
5318
Lee Jonesba4d6512020-08-14 12:39:15 +01005319static int proc_wepkey_open(struct inode *inode, struct file *file)
Al Viro4293ea32007-12-19 19:21:51 -05005320{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005321 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005322 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005323 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005324 char *ptr;
5325 WepKeyRid wkr;
Al Viro4293ea32007-12-19 19:21:51 -05005326 __le16 lastindex;
Lee Jonesba4d6512020-08-14 12:39:15 +01005327 int j = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005328 int rc;
5329
Lee Jonesba4d6512020-08-14 12:39:15 +01005330 if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005331 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005332 memset(&wkr, 0, sizeof(wkr));
Joe Perches57674302010-07-12 13:50:06 -07005333 data = file->private_data;
Lee Jonesba4d6512020-08-14 12:39:15 +01005334 if ((data->rbuffer = kzalloc(180, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005335 kfree (file->private_data);
5336 return -ENOMEM;
5337 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005338 data->writelen = 0;
5339 data->maxwritelen = 80;
Lee Jonesba4d6512020-08-14 12:39:15 +01005340 if ((data->wbuffer = kzalloc(80, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005341 kfree (data->rbuffer);
5342 kfree (file->private_data);
5343 return -ENOMEM;
5344 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005345 data->on_close = proc_wepkey_on_close;
5346
5347 ptr = data->rbuffer;
5348 strcpy(ptr, "No wep keys\n");
5349 rc = readWepKeyRid(ai, &wkr, 1, 1);
5350 if (rc == SUCCESS) do {
5351 lastindex = wkr.kindex;
Al Viro4293ea32007-12-19 19:21:51 -05005352 if (wkr.kindex == cpu_to_le16(0xffff)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005353 j += sprintf(ptr+j, "Tx key = %d\n",
5354 (int)wkr.mac[0]);
5355 } else {
5356 j += sprintf(ptr+j, "Key %d set with length = %d\n",
Al Viro4293ea32007-12-19 19:21:51 -05005357 le16_to_cpu(wkr.kindex),
5358 le16_to_cpu(wkr.klen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005359 }
5360 readWepKeyRid(ai, &wkr, 0, 1);
Lee Jonesba4d6512020-08-14 12:39:15 +01005361 } while ((lastindex != wkr.kindex) && (j < 180-30));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005362
Lee Jonesba4d6512020-08-14 12:39:15 +01005363 data->readlen = strlen(data->rbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005364 return 0;
5365}
5366
Al Viro0dd22122007-12-17 16:11:57 -05005367static int proc_SSID_open(struct inode *inode, struct file *file)
5368{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005369 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005370 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005371 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005372 int i;
5373 char *ptr;
5374 SsidRid SSID_rid;
5375
Lee Jonesba4d6512020-08-14 12:39:15 +01005376 if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005377 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07005378 data = file->private_data;
Lee Jonesba4d6512020-08-14 12:39:15 +01005379 if ((data->rbuffer = kmalloc(104, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005380 kfree (file->private_data);
5381 return -ENOMEM;
5382 }
5383 data->writelen = 0;
5384 data->maxwritelen = 33*3;
Al Viro0dd22122007-12-17 16:11:57 -05005385 /* allocate maxwritelen + 1; we'll want a sentinel */
5386 if ((data->wbuffer = kzalloc(33*3 + 1, 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_SSID_on_close;
5392
5393 readSsidRid(ai, &SSID_rid);
5394 ptr = data->rbuffer;
Al Viro0dd22122007-12-17 16:11:57 -05005395 for (i = 0; i < 3; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005396 int j;
Al Viro0dd22122007-12-17 16:11:57 -05005397 size_t len = le16_to_cpu(SSID_rid.ssids[i].len);
5398 if (!len)
5399 break;
5400 if (len > 32)
5401 len = 32;
5402 for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005403 *ptr++ = SSID_rid.ssids[i].ssid[j];
Linus Torvalds1da177e2005-04-16 15:20:36 -07005404 *ptr++ = '\n';
5405 }
5406 *ptr = '\0';
Lee Jonesba4d6512020-08-14 12:39:15 +01005407 data->readlen = strlen(data->rbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005408 return 0;
5409}
5410
Lee Jonesba4d6512020-08-14 12:39:15 +01005411static int proc_APList_open(struct inode *inode, struct file *file)
5412{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005413 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005414 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005415 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005416 int i;
5417 char *ptr;
Ondrej Zaryf675f932015-10-16 21:04:13 +02005418 APListRid *APList_rid = &ai->APList;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005419
Lee Jonesba4d6512020-08-14 12:39:15 +01005420 if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005421 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07005422 data = file->private_data;
Lee Jonesba4d6512020-08-14 12:39:15 +01005423 if ((data->rbuffer = kmalloc(104, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005424 kfree (file->private_data);
5425 return -ENOMEM;
5426 }
5427 data->writelen = 0;
5428 data->maxwritelen = 4*6*3;
Lee Jonesba4d6512020-08-14 12:39:15 +01005429 if ((data->wbuffer = kzalloc(data->maxwritelen, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005430 kfree (data->rbuffer);
5431 kfree (file->private_data);
5432 return -ENOMEM;
5433 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005434 data->on_close = proc_APList_on_close;
5435
Linus Torvalds1da177e2005-04-16 15:20:36 -07005436 ptr = data->rbuffer;
Lee Jonesba4d6512020-08-14 12:39:15 +01005437 for (i = 0; i < 4; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005438// We end when we find a zero MAC
Lee Jonesba4d6512020-08-14 12:39:15 +01005439 if (!*(int*)APList_rid->ap[i] &&
Ondrej Zaryf675f932015-10-16 21:04:13 +02005440 !*(int*)&APList_rid->ap[i][2]) break;
5441 ptr += sprintf(ptr, "%pM\n", APList_rid->ap[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005442 }
5443 if (i==0) ptr += sprintf(ptr, "Not using specific APs\n");
5444
5445 *ptr = '\0';
Lee Jonesba4d6512020-08-14 12:39:15 +01005446 data->readlen = strlen(data->rbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005447 return 0;
5448}
5449
Lee Jonesba4d6512020-08-14 12:39:15 +01005450static int proc_BSSList_open(struct inode *inode, struct file *file)
5451{
Linus Torvalds1da177e2005-04-16 15:20:36 -07005452 struct proc_data *data;
Al Virod9dda782013-03-31 18:16:14 -04005453 struct net_device *dev = PDE_DATA(inode);
Wang Chenfaf39942008-10-14 13:30:33 +08005454 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005455 char *ptr;
5456 BSSListRid BSSList_rid;
5457 int rc;
5458 /* If doLoseSync is not 1, we won't do a Lose Sync */
5459 int doLoseSync = -1;
5460
Lee Jonesba4d6512020-08-14 12:39:15 +01005461 if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005462 return -ENOMEM;
Joe Perches57674302010-07-12 13:50:06 -07005463 data = file->private_data;
Lee Jonesba4d6512020-08-14 12:39:15 +01005464 if ((data->rbuffer = kmalloc(1024, GFP_KERNEL)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005465 kfree (file->private_data);
5466 return -ENOMEM;
5467 }
5468 data->writelen = 0;
5469 data->maxwritelen = 0;
5470 data->wbuffer = NULL;
5471 data->on_close = NULL;
5472
5473 if (file->f_mode & FMODE_WRITE) {
5474 if (!(file->f_mode & FMODE_READ)) {
5475 Cmd cmd;
5476 Resp rsp;
5477
Wenwen Wang145a32f2019-08-15 22:50:02 -05005478 if (ai->flags & FLAG_RADIO_MASK) {
5479 kfree(data->rbuffer);
5480 kfree(file->private_data);
5481 return -ENETDOWN;
5482 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005483 memset(&cmd, 0, sizeof(cmd));
Lee Jonesba4d6512020-08-14 12:39:15 +01005484 cmd.cmd = CMD_LISTBSS;
Wenwen Wang145a32f2019-08-15 22:50:02 -05005485 if (down_interruptible(&ai->sem)) {
5486 kfree(data->rbuffer);
5487 kfree(file->private_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005488 return -ERESTARTSYS;
Wenwen Wang145a32f2019-08-15 22:50:02 -05005489 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005490 issuecommand(ai, &cmd, &rsp);
5491 up(&ai->sem);
5492 data->readlen = 0;
5493 return 0;
5494 }
5495 doLoseSync = 1;
5496 }
5497 ptr = data->rbuffer;
5498 /* There is a race condition here if there are concurrent opens.
5499 Since it is a rare condition, we'll just live with it, otherwise
5500 we have to add a spin lock... */
5501 rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
Lee Jonesba4d6512020-08-14 12:39:15 +01005502 while (rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
Dan Carpenter3d39e1b2018-10-24 11:33:34 +03005503 ptr += sprintf(ptr, "%pM %.*s rssi = %d",
Johannes Berge1749612008-10-27 15:59:26 -07005504 BSSList_rid.bssid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005505 (int)BSSList_rid.ssidLen,
5506 BSSList_rid.ssid,
Al Viro17e70492007-12-19 18:56:37 -05005507 le16_to_cpu(BSSList_rid.dBm));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005508 ptr += sprintf(ptr, " channel = %d %s %s %s %s\n",
Al Viro17e70492007-12-19 18:56:37 -05005509 le16_to_cpu(BSSList_rid.dsChannel),
Linus Torvalds1da177e2005-04-16 15:20:36 -07005510 BSSList_rid.cap & CAP_ESS ? "ESS" : "",
5511 BSSList_rid.cap & CAP_IBSS ? "adhoc" : "",
5512 BSSList_rid.cap & CAP_PRIVACY ? "wep" : "",
5513 BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : "");
5514 rc = readBSSListRid(ai, 0, &BSSList_rid);
5515 }
5516 *ptr = '\0';
Lee Jonesba4d6512020-08-14 12:39:15 +01005517 data->readlen = strlen(data->rbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005518 return 0;
5519}
5520
Lee Jonesba4d6512020-08-14 12:39:15 +01005521static int proc_close(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005522{
Jesper Juhlb4558ea2005-10-28 16:53:13 -04005523 struct proc_data *data = file->private_data;
5524
5525 if (data->on_close != NULL)
5526 data->on_close(inode, file);
5527 kfree(data->rbuffer);
5528 kfree(data->wbuffer);
5529 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005530 return 0;
5531}
5532
Linus Torvalds1da177e2005-04-16 15:20:36 -07005533/* Since the card doesn't automatically switch to the right WEP mode,
5534 we will make it do it. If the card isn't associated, every secs we
5535 will switch WEP modes to see if that will help. If the card is
5536 associated we will check every minute to see if anything has
5537 changed. */
Lee Jonesba4d6512020-08-14 12:39:15 +01005538static void timer_func(struct net_device *dev)
5539{
Wang Chenfaf39942008-10-14 13:30:33 +08005540 struct airo_info *apriv = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005541
5542/* We don't have a link so try changing the authtype */
5543 readConfigRid(apriv, 0);
5544 disable_MAC(apriv, 0);
5545 switch(apriv->config.authType) {
5546 case AUTH_ENCRYPT:
5547/* So drop to OPEN */
5548 apriv->config.authType = AUTH_OPEN;
5549 break;
5550 case AUTH_SHAREDKEY:
5551 if (apriv->keyindex < auto_wep) {
Dan Williamsc0380692009-01-24 09:12:15 -05005552 set_wep_tx_idx(apriv, apriv->keyindex, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005553 apriv->config.authType = AUTH_SHAREDKEY;
5554 apriv->keyindex++;
5555 } else {
5556 /* Drop to ENCRYPT */
5557 apriv->keyindex = 0;
Dan Williamsc0380692009-01-24 09:12:15 -05005558 set_wep_tx_idx(apriv, apriv->defindex, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005559 apriv->config.authType = AUTH_ENCRYPT;
5560 }
5561 break;
5562 default: /* We'll escalate to SHAREDKEY */
5563 apriv->config.authType = AUTH_SHAREDKEY;
5564 }
5565 set_bit (FLAG_COMMIT, &apriv->flags);
5566 writeConfigRid(apriv, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005567 enable_MAC(apriv, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005568 up(&apriv->sem);
5569
5570/* Schedule check to see if the change worked */
Dan Williams3c304952006-04-15 12:26:18 -04005571 clear_bit(JOB_AUTOWEP, &apriv->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005572 apriv->expires = RUN_AT(HZ*3);
5573}
5574
Linus Torvalds1da177e2005-04-16 15:20:36 -07005575#ifdef CONFIG_PCI
Bill Pemberton04bfffb2012-12-03 09:56:27 -05005576static int airo_pci_probe(struct pci_dev *pdev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005577 const struct pci_device_id *pent)
5578{
5579 struct net_device *dev;
5580
5581 if (pci_enable_device(pdev))
5582 return -ENODEV;
5583 pci_set_master(pdev);
5584
5585 if (pdev->device == 0x5000 || pdev->device == 0xa504)
5586 dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev);
5587 else
5588 dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev);
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005589 if (!dev) {
5590 pci_disable_device(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005591 return -ENODEV;
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005592 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005593
5594 pci_set_drvdata(pdev, dev);
5595 return 0;
5596}
5597
Bill Pemberton04bfffb2012-12-03 09:56:27 -05005598static void airo_pci_remove(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005599{
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01005600 struct net_device *dev = pci_get_drvdata(pdev);
5601
5602 airo_print_info(dev->name, "Unregistering...");
5603 stop_airo_card(dev, 1);
Michal Schmidt777ec5e2007-06-29 15:33:30 +02005604 pci_disable_device(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005605}
5606
Vaibhav Guptac3ab1802020-07-28 17:11:28 +05305607static int __maybe_unused airo_pci_suspend(struct device *dev_d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005608{
Vaibhav Guptac3ab1802020-07-28 17:11:28 +05305609 struct net_device *dev = dev_get_drvdata(dev_d);
Wang Chenfaf39942008-10-14 13:30:33 +08005610 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005611 Cmd cmd;
5612 Resp rsp;
5613
Pavel Macheke292c732008-06-25 12:25:53 +02005614 if (!ai->SSID)
5615 ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL);
5616 if (!ai->SSID)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005617 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005618 readSsidRid(ai, ai->SSID);
5619 memset(&cmd, 0, sizeof(cmd));
5620 /* the lock will be released at the end of the resume callback */
5621 if (down_interruptible(&ai->sem))
5622 return -EAGAIN;
5623 disable_MAC(ai, 0);
5624 netif_device_detach(dev);
Vaibhav Guptac3ab1802020-07-28 17:11:28 +05305625 ai->power = PMSG_SUSPEND;
Pavel Macheke292c732008-06-25 12:25:53 +02005626 cmd.cmd = HOSTSLEEP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005627 issuecommand(ai, &cmd, &rsp);
5628
Vaibhav Guptac3ab1802020-07-28 17:11:28 +05305629 device_wakeup_enable(dev_d);
matthieu castet40b93ad2009-10-09 22:12:25 +02005630 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005631}
5632
Vaibhav Guptac3ab1802020-07-28 17:11:28 +05305633static int __maybe_unused airo_pci_resume(struct device *dev_d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005634{
Vaibhav Guptac3ab1802020-07-28 17:11:28 +05305635 struct net_device *dev = dev_get_drvdata(dev_d);
Wang Chenfaf39942008-10-14 13:30:33 +08005636 struct airo_info *ai = dev->ml_priv;
Vaibhav Guptac3ab1802020-07-28 17:11:28 +05305637 pci_power_t prev_state = to_pci_dev(dev_d)->current_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005638
Vaibhav Guptac3ab1802020-07-28 17:11:28 +05305639 device_wakeup_disable(dev_d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005640
Michal Schmidt53232802005-10-04 07:46:21 -04005641 if (prev_state != PCI_D1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005642 reset_card(dev, 0);
5643 mpi_init_descriptors(ai);
5644 setup_card(ai, dev->dev_addr, 0);
5645 clear_bit(FLAG_RADIO_OFF, &ai->flags);
5646 clear_bit(FLAG_PENDING_XMIT, &ai->flags);
5647 } else {
5648 OUT4500(ai, EVACK, EV_AWAKEN);
5649 OUT4500(ai, EVACK, EV_AWAKEN);
5650 msleep(100);
5651 }
5652
Pavel Macheke292c732008-06-25 12:25:53 +02005653 set_bit(FLAG_COMMIT, &ai->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005654 disable_MAC(ai, 0);
5655 msleep(200);
5656 if (ai->SSID) {
5657 writeSsidRid(ai, ai->SSID, 0);
5658 kfree(ai->SSID);
5659 ai->SSID = NULL;
5660 }
Ondrej Zaryf675f932015-10-16 21:04:13 +02005661 writeAPListRid(ai, &ai->APList, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005662 writeConfigRid(ai, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005663 enable_MAC(ai, 0);
Pavel Machek1cc68ae2005-06-20 15:33:04 -07005664 ai->power = PMSG_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005665 netif_device_attach(dev);
5666 netif_wake_queue(dev);
5667 enable_interrupts(ai);
5668 up(&ai->sem);
5669 return 0;
5670}
5671#endif
5672
Lee Jonesba4d6512020-08-14 12:39:15 +01005673static int __init airo_init_module(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005674{
Jeff Garzikde897882006-10-01 07:31:09 -04005675 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005676
Eric W. Biederman1efa29c2012-02-10 14:01:03 -08005677 proc_kuid = make_kuid(&init_user_ns, proc_uid);
5678 proc_kgid = make_kgid(&init_user_ns, proc_gid);
5679 if (!uid_valid(proc_kuid) || !gid_valid(proc_kgid))
5680 return -EINVAL;
5681
Alexey Dobriyan011159a2011-05-14 00:12:48 +03005682 airo_entry = proc_mkdir_mode("driver/aironet", airo_perm, NULL);
Jeff Garzikde897882006-10-01 07:31:09 -04005683
David Howells271a15e2013-04-12 00:38:51 +01005684 if (airo_entry)
5685 proc_set_user(airo_entry, proc_kuid, proc_kgid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005686
Pavel Macheke292c732008-06-25 12:25:53 +02005687 for (i = 0; i < 4 && io[i] && irq[i]; i++) {
Dan Williams934d8bf2006-03-16 13:46:23 -05005688 airo_print_info("", "Trying to configure ISA adapter at irq=%d "
Lee Jonesba4d6512020-08-14 12:39:15 +01005689 "io = 0x%x", irq[i], io[i]);
5690 if (init_airo_card(irq[i], io[i], 0, NULL)) {
Jeff Garzikde897882006-10-01 07:31:09 -04005691 /* do nothing */ ;
Lee Jones0b6a4242020-08-14 12:39:14 +01005692 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005693 }
5694
5695#ifdef CONFIG_PCI
Dan Williams934d8bf2006-03-16 13:46:23 -05005696 airo_print_info("", "Probing for PCI adapters");
Jeff Garzikde897882006-10-01 07:31:09 -04005697 i = pci_register_driver(&airo_driver);
Dan Williams934d8bf2006-03-16 13:46:23 -05005698 airo_print_info("", "Finished probing for PCI adapters");
Jeff Garzikde897882006-10-01 07:31:09 -04005699
5700 if (i) {
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005701 remove_proc_entry("driver/aironet", NULL);
Jeff Garzikde897882006-10-01 07:31:09 -04005702 return i;
5703 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005704#endif
5705
5706 /* Always exit with success, as we are a library module
5707 * as well as a driver module
5708 */
5709 return 0;
5710}
5711
Lee Jonesba4d6512020-08-14 12:39:15 +01005712static void __exit airo_cleanup_module(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005713{
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01005714 struct airo_info *ai;
Lee Jonesba4d6512020-08-14 12:39:15 +01005715 while (!list_empty(&airo_devices)) {
Michal Schmidtaf5b5c92007-03-16 12:44:40 +01005716 ai = list_entry(airo_devices.next, struct airo_info, dev_list);
5717 airo_print_info(ai->dev->name, "Unregistering...");
5718 stop_airo_card(ai->dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005719 }
5720#ifdef CONFIG_PCI
5721 pci_unregister_driver(&airo_driver);
5722#endif
Alexey Dobriyan928b4d82008-04-29 01:01:44 -07005723 remove_proc_entry("driver/aironet", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005724}
5725
Linus Torvalds1da177e2005-04-16 15:20:36 -07005726/*
5727 * Initial Wireless Extension code for Aironet driver by :
5728 * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00
5729 * Conversion to new driver API by :
5730 * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02
5731 * Javier also did a good amount of work here, adding some new extensions
5732 * and fixing my code. Let's just say that without him this code just
5733 * would not work at all... - Jean II
5734 */
5735
Dan Williams41480af2005-05-10 09:45:51 -04005736static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi)
5737{
Pavel Macheke292c732008-06-25 12:25:53 +02005738 if (!rssi_rid)
Dan Williams41480af2005-05-10 09:45:51 -04005739 return 0;
5740
5741 return (0x100 - rssi_rid[rssi].rssidBm);
5742}
5743
5744static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm)
5745{
5746 int i;
5747
Pavel Macheke292c732008-06-25 12:25:53 +02005748 if (!rssi_rid)
Dan Williams41480af2005-05-10 09:45:51 -04005749 return 0;
5750
Pavel Macheke292c732008-06-25 12:25:53 +02005751 for (i = 0; i < 256; i++)
Dan Williams41480af2005-05-10 09:45:51 -04005752 if (rssi_rid[i].rssidBm == dbm)
5753 return rssi_rid[i].rssipct;
5754
5755 return 0;
5756}
5757
5758
Linus Torvalds1da177e2005-04-16 15:20:36 -07005759static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid)
5760{
5761 int quality = 0;
Al Viro329e2c02007-12-20 22:58:57 -05005762 u16 sq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005763
Al Viro329e2c02007-12-20 22:58:57 -05005764 if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f))
Al Viro56d81bd2007-12-20 17:18:35 -05005765 return 0;
5766
5767 if (!(cap_rid->hardCap & cpu_to_le16(8)))
5768 return 0;
5769
Al Viro329e2c02007-12-20 22:58:57 -05005770 sq = le16_to_cpu(status_rid->signalQuality);
Al Viro56d81bd2007-12-20 17:18:35 -05005771 if (memcmp(cap_rid->prodName, "350", 3))
Al Viro329e2c02007-12-20 22:58:57 -05005772 if (sq > 0x20)
Al Viro56d81bd2007-12-20 17:18:35 -05005773 quality = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005774 else
Al Viro329e2c02007-12-20 22:58:57 -05005775 quality = 0x20 - sq;
Al Viro56d81bd2007-12-20 17:18:35 -05005776 else
Al Viro329e2c02007-12-20 22:58:57 -05005777 if (sq > 0xb0)
Al Viro56d81bd2007-12-20 17:18:35 -05005778 quality = 0;
Al Viro329e2c02007-12-20 22:58:57 -05005779 else if (sq < 0x10)
Al Viro56d81bd2007-12-20 17:18:35 -05005780 quality = 0xa0;
5781 else
Al Viro329e2c02007-12-20 22:58:57 -05005782 quality = 0xb0 - sq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005783 return quality;
5784}
5785
5786#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0)
5787#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50);
5788
5789/*------------------------------------------------------------------*/
5790/*
5791 * Wireless Handler : get protocol name
5792 */
5793static int airo_get_name(struct net_device *dev,
5794 struct iw_request_info *info,
5795 char *cwrq,
5796 char *extra)
5797{
5798 strcpy(cwrq, "IEEE 802.11-DS");
5799 return 0;
5800}
5801
5802/*------------------------------------------------------------------*/
5803/*
5804 * Wireless Handler : set frequency
5805 */
5806static int airo_set_freq(struct net_device *dev,
5807 struct iw_request_info *info,
5808 struct iw_freq *fwrq,
5809 char *extra)
5810{
Wang Chenfaf39942008-10-14 13:30:33 +08005811 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005812 int rc = -EINPROGRESS; /* Call commit handler */
5813
5814 /* If setting by frequency, convert to a channel */
Lee Jonesba4d6512020-08-14 12:39:15 +01005815 if (fwrq->e == 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005816 int f = fwrq->m / 100000;
David Kilroy9ee677c2008-12-23 14:03:38 +00005817
Linus Torvalds1da177e2005-04-16 15:20:36 -07005818 /* Hack to fall through... */
5819 fwrq->e = 0;
Zhao, Gange0febf12014-02-18 21:35:57 +08005820 fwrq->m = ieee80211_frequency_to_channel(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005821 }
5822 /* Setting by channel number */
Dan Carpenter03ba4a12016-05-10 22:20:37 +03005823 if (fwrq->m < 0 || fwrq->m > 1000 || fwrq->e > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005824 rc = -EOPNOTSUPP;
5825 else {
5826 int channel = fwrq->m;
5827 /* We should do a better check than that,
5828 * based on the card capability !!! */
Lee Jonesba4d6512020-08-14 12:39:15 +01005829 if ((channel < 1) || (channel > 14)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05005830 airo_print_dbg(dev->name, "New channel value of %d is invalid!",
5831 fwrq->m);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005832 rc = -EINVAL;
5833 } else {
5834 readConfigRid(local, 1);
5835 /* Yes ! We can set it !!! */
Al Viro3eb9b412007-12-21 00:00:35 -05005836 local->config.channelSet = cpu_to_le16(channel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005837 set_bit (FLAG_COMMIT, &local->flags);
5838 }
5839 }
5840 return rc;
5841}
5842
5843/*------------------------------------------------------------------*/
5844/*
5845 * Wireless Handler : get frequency
5846 */
5847static int airo_get_freq(struct net_device *dev,
5848 struct iw_request_info *info,
5849 struct iw_freq *fwrq,
5850 char *extra)
5851{
Wang Chenfaf39942008-10-14 13:30:33 +08005852 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005853 StatusRid status_rid; /* Card status info */
Javier Achirica2610c732006-01-17 08:01:01 -05005854 int ch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005855
5856 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05005857 if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS)
5858 status_rid.channel = local->config.channelSet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005859 else
5860 readStatusRid(local, &status_rid, 1);
5861
Al Viro329e2c02007-12-20 22:58:57 -05005862 ch = le16_to_cpu(status_rid.channel);
Lee Jonesba4d6512020-08-14 12:39:15 +01005863 if ((ch > 0) && (ch < 15)) {
Zhao, Gange0febf12014-02-18 21:35:57 +08005864 fwrq->m = 100000 *
Johannes Berg57fbcce2016-04-12 15:56:15 +02005865 ieee80211_channel_to_frequency(ch, NL80211_BAND_2GHZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005866 fwrq->e = 1;
Javier Achirica2610c732006-01-17 08:01:01 -05005867 } else {
5868 fwrq->m = ch;
5869 fwrq->e = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005870 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005871
5872 return 0;
5873}
5874
5875/*------------------------------------------------------------------*/
5876/*
5877 * Wireless Handler : set ESSID
5878 */
5879static int airo_set_essid(struct net_device *dev,
5880 struct iw_request_info *info,
5881 struct iw_point *dwrq,
5882 char *extra)
5883{
Wang Chenfaf39942008-10-14 13:30:33 +08005884 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005885 SsidRid SSID_rid; /* SSIDs */
5886
5887 /* Reload the list of current SSID */
5888 readSsidRid(local, &SSID_rid);
5889
5890 /* Check if we asked for `any' */
Roel Kluin3d0ccd02009-07-25 23:02:32 +02005891 if (dwrq->flags == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005892 /* Just send an empty SSID list */
5893 memset(&SSID_rid, 0, sizeof(SSID_rid));
5894 } else {
Roel Kluin3d0ccd02009-07-25 23:02:32 +02005895 unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005896
5897 /* Check the size of the string */
Roel Kluin3d0ccd02009-07-25 23:02:32 +02005898 if (dwrq->length > IW_ESSID_MAX_SIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005899 return -E2BIG ;
Roel Kluin3d0ccd02009-07-25 23:02:32 +02005900
Linus Torvalds1da177e2005-04-16 15:20:36 -07005901 /* Check if index is valid */
Roel Kluin3d0ccd02009-07-25 23:02:32 +02005902 if (index >= ARRAY_SIZE(SSID_rid.ssids))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005903 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005904
5905 /* Set the SSID */
5906 memset(SSID_rid.ssids[index].ssid, 0,
5907 sizeof(SSID_rid.ssids[index].ssid));
5908 memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length);
Al Viro0dd22122007-12-17 16:11:57 -05005909 SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005910 }
Al Viro0dd22122007-12-17 16:11:57 -05005911 SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005912 /* Write it to the card */
5913 disable_MAC(local, 1);
5914 writeSsidRid(local, &SSID_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005915 enable_MAC(local, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005916
5917 return 0;
5918}
5919
5920/*------------------------------------------------------------------*/
5921/*
5922 * Wireless Handler : get ESSID
5923 */
5924static int airo_get_essid(struct net_device *dev,
5925 struct iw_request_info *info,
5926 struct iw_point *dwrq,
5927 char *extra)
5928{
Wang Chenfaf39942008-10-14 13:30:33 +08005929 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005930 StatusRid status_rid; /* Card status info */
5931
5932 readStatusRid(local, &status_rid, 1);
5933
5934 /* Note : if dwrq->flags != 0, we should
5935 * get the relevant SSID from the SSID list... */
5936
5937 /* Get the current SSID */
Al Viro329e2c02007-12-20 22:58:57 -05005938 memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005939 /* If none, we may want to get the one that was set */
5940
5941 /* Push it out ! */
Al Viro329e2c02007-12-20 22:58:57 -05005942 dwrq->length = le16_to_cpu(status_rid.SSIDlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005943 dwrq->flags = 1; /* active */
5944
5945 return 0;
5946}
5947
5948/*------------------------------------------------------------------*/
5949/*
5950 * Wireless Handler : set AP address
5951 */
5952static int airo_set_wap(struct net_device *dev,
5953 struct iw_request_info *info,
5954 struct sockaddr *awrq,
5955 char *extra)
5956{
Wang Chenfaf39942008-10-14 13:30:33 +08005957 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005958 Cmd cmd;
5959 Resp rsp;
Ondrej Zaryf675f932015-10-16 21:04:13 +02005960 APListRid *APList_rid = &local->APList;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005961
5962 if (awrq->sa_family != ARPHRD_ETHER)
5963 return -EINVAL;
Wei Yongjun0d1c6d12012-08-23 15:17:09 +08005964 else if (is_broadcast_ether_addr(awrq->sa_data) ||
5965 is_zero_ether_addr(awrq->sa_data)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005966 memset(&cmd, 0, sizeof(cmd));
Lee Jonesba4d6512020-08-14 12:39:15 +01005967 cmd.cmd = CMD_LOSE_SYNC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005968 if (down_interruptible(&local->sem))
5969 return -ERESTARTSYS;
5970 issuecommand(local, &cmd, &rsp);
5971 up(&local->sem);
5972 } else {
Ondrej Zaryf675f932015-10-16 21:04:13 +02005973 memset(APList_rid, 0, sizeof(*APList_rid));
5974 APList_rid->len = cpu_to_le16(sizeof(*APList_rid));
5975 memcpy(APList_rid->ap[0], awrq->sa_data, ETH_ALEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005976 disable_MAC(local, 1);
Ondrej Zaryf675f932015-10-16 21:04:13 +02005977 writeAPListRid(local, APList_rid, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02005978 enable_MAC(local, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005979 }
5980 return 0;
5981}
5982
5983/*------------------------------------------------------------------*/
5984/*
5985 * Wireless Handler : get AP address
5986 */
5987static int airo_get_wap(struct net_device *dev,
5988 struct iw_request_info *info,
5989 struct sockaddr *awrq,
5990 char *extra)
5991{
Wang Chenfaf39942008-10-14 13:30:33 +08005992 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005993 StatusRid status_rid; /* Card status info */
5994
5995 readStatusRid(local, &status_rid, 1);
5996
5997 /* Tentative. This seems to work, wow, I'm lucky !!! */
5998 memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN);
5999 awrq->sa_family = ARPHRD_ETHER;
6000
6001 return 0;
6002}
6003
6004/*------------------------------------------------------------------*/
6005/*
6006 * Wireless Handler : set Nickname
6007 */
6008static int airo_set_nick(struct net_device *dev,
6009 struct iw_request_info *info,
6010 struct iw_point *dwrq,
6011 char *extra)
6012{
Wang Chenfaf39942008-10-14 13:30:33 +08006013 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006014
6015 /* Check the size of the string */
Lee Jonesba4d6512020-08-14 12:39:15 +01006016 if (dwrq->length > 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006017 return -E2BIG;
6018 }
6019 readConfigRid(local, 1);
6020 memset(local->config.nodeName, 0, sizeof(local->config.nodeName));
6021 memcpy(local->config.nodeName, extra, dwrq->length);
6022 set_bit (FLAG_COMMIT, &local->flags);
6023
6024 return -EINPROGRESS; /* Call commit handler */
6025}
6026
6027/*------------------------------------------------------------------*/
6028/*
6029 * Wireless Handler : get Nickname
6030 */
6031static int airo_get_nick(struct net_device *dev,
6032 struct iw_request_info *info,
6033 struct iw_point *dwrq,
6034 char *extra)
6035{
Wang Chenfaf39942008-10-14 13:30:33 +08006036 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006037
6038 readConfigRid(local, 1);
6039 strncpy(extra, local->config.nodeName, 16);
6040 extra[16] = '\0';
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006041 dwrq->length = strlen(extra);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006042
6043 return 0;
6044}
6045
6046/*------------------------------------------------------------------*/
6047/*
6048 * Wireless Handler : set Bit-Rate
6049 */
6050static int airo_set_rate(struct net_device *dev,
6051 struct iw_request_info *info,
6052 struct iw_param *vwrq,
6053 char *extra)
6054{
Wang Chenfaf39942008-10-14 13:30:33 +08006055 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006056 CapabilityRid cap_rid; /* Card capability info */
6057 u8 brate = 0;
6058 int i;
6059
6060 /* First : get a valid bit rate value */
6061 readCapabilityRid(local, &cap_rid, 1);
6062
6063 /* Which type of value ? */
Lee Jonesba4d6512020-08-14 12:39:15 +01006064 if ((vwrq->value < 8) && (vwrq->value >= 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006065 /* Setting by rate index */
6066 /* Find value in the magic rate table */
6067 brate = cap_rid.supportedRates[vwrq->value];
6068 } else {
6069 /* Setting by frequency value */
6070 u8 normvalue = (u8) (vwrq->value/500000);
6071
6072 /* Check if rate is valid */
Lee Jonesba4d6512020-08-14 12:39:15 +01006073 for (i = 0 ; i < 8 ; i++) {
6074 if (normvalue == cap_rid.supportedRates[i]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006075 brate = normvalue;
6076 break;
6077 }
6078 }
6079 }
6080 /* -1 designed the max rate (mostly auto mode) */
Lee Jonesba4d6512020-08-14 12:39:15 +01006081 if (vwrq->value == -1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006082 /* Get the highest available rate */
Lee Jonesba4d6512020-08-14 12:39:15 +01006083 for (i = 0 ; i < 8 ; i++) {
6084 if (cap_rid.supportedRates[i] == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006085 break;
6086 }
Lee Jonesba4d6512020-08-14 12:39:15 +01006087 if (i != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006088 brate = cap_rid.supportedRates[i - 1];
6089 }
6090 /* Check that it is valid */
Lee Jonesba4d6512020-08-14 12:39:15 +01006091 if (brate == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006092 return -EINVAL;
6093 }
6094
6095 readConfigRid(local, 1);
6096 /* Now, check if we want a fixed or auto value */
Lee Jonesba4d6512020-08-14 12:39:15 +01006097 if (vwrq->fixed == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006098 /* Fill all the rates up to this max rate */
6099 memset(local->config.rates, 0, 8);
Lee Jonesba4d6512020-08-14 12:39:15 +01006100 for (i = 0 ; i < 8 ; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006101 local->config.rates[i] = cap_rid.supportedRates[i];
Lee Jonesba4d6512020-08-14 12:39:15 +01006102 if (local->config.rates[i] == brate)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006103 break;
6104 }
6105 } else {
6106 /* Fixed mode */
6107 /* One rate, fixed */
6108 memset(local->config.rates, 0, 8);
6109 local->config.rates[0] = brate;
6110 }
6111 set_bit (FLAG_COMMIT, &local->flags);
6112
6113 return -EINPROGRESS; /* Call commit handler */
6114}
6115
6116/*------------------------------------------------------------------*/
6117/*
6118 * Wireless Handler : get Bit-Rate
6119 */
6120static int airo_get_rate(struct net_device *dev,
6121 struct iw_request_info *info,
6122 struct iw_param *vwrq,
6123 char *extra)
6124{
Wang Chenfaf39942008-10-14 13:30:33 +08006125 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006126 StatusRid status_rid; /* Card status info */
6127
6128 readStatusRid(local, &status_rid, 1);
6129
Al Viro329e2c02007-12-20 22:58:57 -05006130 vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006131 /* If more than one rate, set auto */
6132 readConfigRid(local, 1);
6133 vwrq->fixed = (local->config.rates[1] == 0);
6134
6135 return 0;
6136}
6137
6138/*------------------------------------------------------------------*/
6139/*
6140 * Wireless Handler : set RTS threshold
6141 */
6142static int airo_set_rts(struct net_device *dev,
6143 struct iw_request_info *info,
6144 struct iw_param *vwrq,
6145 char *extra)
6146{
Wang Chenfaf39942008-10-14 13:30:33 +08006147 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006148 int rthr = vwrq->value;
6149
Lee Jonesba4d6512020-08-14 12:39:15 +01006150 if (vwrq->disabled)
Dan Williams15db2762006-03-16 13:46:27 -05006151 rthr = AIRO_DEF_MTU;
Lee Jonesba4d6512020-08-14 12:39:15 +01006152 if ((rthr < 0) || (rthr > AIRO_DEF_MTU)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006153 return -EINVAL;
6154 }
6155 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006156 local->config.rtsThres = cpu_to_le16(rthr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006157 set_bit (FLAG_COMMIT, &local->flags);
6158
6159 return -EINPROGRESS; /* Call commit handler */
6160}
6161
6162/*------------------------------------------------------------------*/
6163/*
6164 * Wireless Handler : get RTS threshold
6165 */
6166static int airo_get_rts(struct net_device *dev,
6167 struct iw_request_info *info,
6168 struct iw_param *vwrq,
6169 char *extra)
6170{
Wang Chenfaf39942008-10-14 13:30:33 +08006171 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006172
6173 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006174 vwrq->value = le16_to_cpu(local->config.rtsThres);
Dan Williams15db2762006-03-16 13:46:27 -05006175 vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006176 vwrq->fixed = 1;
6177
6178 return 0;
6179}
6180
6181/*------------------------------------------------------------------*/
6182/*
6183 * Wireless Handler : set Fragmentation threshold
6184 */
6185static int airo_set_frag(struct net_device *dev,
6186 struct iw_request_info *info,
6187 struct iw_param *vwrq,
6188 char *extra)
6189{
Wang Chenfaf39942008-10-14 13:30:33 +08006190 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006191 int fthr = vwrq->value;
6192
Lee Jonesba4d6512020-08-14 12:39:15 +01006193 if (vwrq->disabled)
Dan Williams15db2762006-03-16 13:46:27 -05006194 fthr = AIRO_DEF_MTU;
Lee Jonesba4d6512020-08-14 12:39:15 +01006195 if ((fthr < 256) || (fthr > AIRO_DEF_MTU)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006196 return -EINVAL;
6197 }
6198 fthr &= ~0x1; /* Get an even value - is it really needed ??? */
6199 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006200 local->config.fragThresh = cpu_to_le16(fthr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006201 set_bit (FLAG_COMMIT, &local->flags);
6202
6203 return -EINPROGRESS; /* Call commit handler */
6204}
6205
6206/*------------------------------------------------------------------*/
6207/*
6208 * Wireless Handler : get Fragmentation threshold
6209 */
6210static int airo_get_frag(struct net_device *dev,
6211 struct iw_request_info *info,
6212 struct iw_param *vwrq,
6213 char *extra)
6214{
Wang Chenfaf39942008-10-14 13:30:33 +08006215 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006216
6217 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006218 vwrq->value = le16_to_cpu(local->config.fragThresh);
Dan Williams15db2762006-03-16 13:46:27 -05006219 vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006220 vwrq->fixed = 1;
6221
6222 return 0;
6223}
6224
6225/*------------------------------------------------------------------*/
6226/*
6227 * Wireless Handler : set Mode of Operation
6228 */
6229static int airo_set_mode(struct net_device *dev,
6230 struct iw_request_info *info,
6231 __u32 *uwrq,
6232 char *extra)
6233{
Wang Chenfaf39942008-10-14 13:30:33 +08006234 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006235 int reset = 0;
6236
6237 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006238 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006239 reset = 1;
6240
6241 switch(*uwrq) {
6242 case IW_MODE_ADHOC:
Al Viro3eb9b412007-12-21 00:00:35 -05006243 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006244 local->config.opmode |= MODE_STA_IBSS;
Al Viro3eb9b412007-12-21 00:00:35 -05006245 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006246 local->config.scanMode = SCANMODE_ACTIVE;
6247 clear_bit (FLAG_802_11, &local->flags);
6248 break;
6249 case IW_MODE_INFRA:
Al Viro3eb9b412007-12-21 00:00:35 -05006250 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006251 local->config.opmode |= MODE_STA_ESS;
Al Viro3eb9b412007-12-21 00:00:35 -05006252 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006253 local->config.scanMode = SCANMODE_ACTIVE;
6254 clear_bit (FLAG_802_11, &local->flags);
6255 break;
6256 case IW_MODE_MASTER:
Al Viro3eb9b412007-12-21 00:00:35 -05006257 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006258 local->config.opmode |= MODE_AP;
Al Viro3eb9b412007-12-21 00:00:35 -05006259 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006260 local->config.scanMode = SCANMODE_ACTIVE;
6261 clear_bit (FLAG_802_11, &local->flags);
6262 break;
6263 case IW_MODE_REPEAT:
Al Viro3eb9b412007-12-21 00:00:35 -05006264 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006265 local->config.opmode |= MODE_AP_RPTR;
Al Viro3eb9b412007-12-21 00:00:35 -05006266 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006267 local->config.scanMode = SCANMODE_ACTIVE;
6268 clear_bit (FLAG_802_11, &local->flags);
6269 break;
6270 case IW_MODE_MONITOR:
Al Viro3eb9b412007-12-21 00:00:35 -05006271 local->config.opmode &= ~MODE_CFG_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006272 local->config.opmode |= MODE_STA_ESS;
Al Viro3eb9b412007-12-21 00:00:35 -05006273 local->config.rmode &= ~RXMODE_FULL_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006274 local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
6275 local->config.scanMode = SCANMODE_PASSIVE;
6276 set_bit (FLAG_802_11, &local->flags);
6277 break;
6278 default:
6279 return -EINVAL;
6280 }
6281 if (reset)
6282 set_bit (FLAG_RESET, &local->flags);
6283 set_bit (FLAG_COMMIT, &local->flags);
6284
6285 return -EINPROGRESS; /* Call commit handler */
6286}
6287
6288/*------------------------------------------------------------------*/
6289/*
6290 * Wireless Handler : get Mode of Operation
6291 */
6292static int airo_get_mode(struct net_device *dev,
6293 struct iw_request_info *info,
6294 __u32 *uwrq,
6295 char *extra)
6296{
Wang Chenfaf39942008-10-14 13:30:33 +08006297 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006298
6299 readConfigRid(local, 1);
6300 /* If not managed, assume it's ad-hoc */
Al Viro3eb9b412007-12-21 00:00:35 -05006301 switch (local->config.opmode & MODE_CFG_MASK) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006302 case MODE_STA_ESS:
6303 *uwrq = IW_MODE_INFRA;
6304 break;
6305 case MODE_AP:
6306 *uwrq = IW_MODE_MASTER;
6307 break;
6308 case MODE_AP_RPTR:
6309 *uwrq = IW_MODE_REPEAT;
6310 break;
6311 default:
6312 *uwrq = IW_MODE_ADHOC;
6313 }
6314
6315 return 0;
6316}
6317
Dan Williams138c0c62009-01-24 09:11:35 -05006318static inline int valid_index(struct airo_info *ai, int index)
Al Viro56d81bd2007-12-20 17:18:35 -05006319{
Dan Williams138c0c62009-01-24 09:11:35 -05006320 return (index >= 0) && (index <= ai->max_wep_idx);
Al Viro56d81bd2007-12-20 17:18:35 -05006321}
6322
Linus Torvalds1da177e2005-04-16 15:20:36 -07006323/*------------------------------------------------------------------*/
6324/*
6325 * Wireless Handler : set Encryption Key
6326 */
6327static int airo_set_encode(struct net_device *dev,
6328 struct iw_request_info *info,
6329 struct iw_point *dwrq,
6330 char *extra)
6331{
Wang Chenfaf39942008-10-14 13:30:33 +08006332 struct airo_info *local = dev->ml_priv;
Dan Williamsc0380692009-01-24 09:12:15 -05006333 int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006334 __le16 currentAuthType = local->config.authType;
Dan Williamsc0380692009-01-24 09:12:15 -05006335 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006336
Dan Williams138c0c62009-01-24 09:11:35 -05006337 if (!local->wep_capable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006338 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006339
Linus Torvalds1da177e2005-04-16 15:20:36 -07006340 readConfigRid(local, 1);
6341
6342 /* Basic checking: do we have a key to set ?
6343 * Note : with the new API, it's impossible to get a NULL pointer.
6344 * Therefore, we need to check a key size == 0 instead.
6345 * New version of iwconfig properly set the IW_ENCODE_NOKEY flag
6346 * when no key is present (only change flags), but older versions
6347 * don't do it. - Jean II */
6348 if (dwrq->length > 0) {
6349 wep_key_t key;
6350 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Dan Williamsc0380692009-01-24 09:12:15 -05006351 int current_index;
Dan Williams138c0c62009-01-24 09:11:35 -05006352
Linus Torvalds1da177e2005-04-16 15:20:36 -07006353 /* Check the size of the key */
6354 if (dwrq->length > MAX_KEY_SIZE) {
6355 return -EINVAL;
6356 }
Dan Williams138c0c62009-01-24 09:11:35 -05006357
Dan Williamsc0380692009-01-24 09:12:15 -05006358 current_index = get_wep_tx_idx(local);
6359 if (current_index < 0)
6360 current_index = 0;
6361
Linus Torvalds1da177e2005-04-16 15:20:36 -07006362 /* Check the index (none -> use current) */
Dan Williams138c0c62009-01-24 09:11:35 -05006363 if (!valid_index(local, index))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006364 index = current_index;
Dan Williams138c0c62009-01-24 09:11:35 -05006365
Linus Torvalds1da177e2005-04-16 15:20:36 -07006366 /* Set the length */
6367 if (dwrq->length > MIN_KEY_SIZE)
6368 key.len = MAX_KEY_SIZE;
6369 else
Stanislaw Gruszkaf09c2562010-02-02 15:34:50 +01006370 key.len = MIN_KEY_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006371 /* Check if the key is not marked as invalid */
Lee Jonesba4d6512020-08-14 12:39:15 +01006372 if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006373 /* Cleanup */
6374 memset(key.key, 0, MAX_KEY_SIZE);
6375 /* Copy the key in the driver */
6376 memcpy(key.key, extra, dwrq->length);
6377 /* Send the key to the card */
Dan Williamsc0380692009-01-24 09:12:15 -05006378 rc = set_wep_key(local, index, key.key, key.len, perm, 1);
6379 if (rc < 0) {
6380 airo_print_err(local->dev->name, "failed to set"
6381 " WEP key at index %d: %d.",
6382 index, rc);
6383 return rc;
6384 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006385 }
6386 /* WE specify that if a valid key is set, encryption
6387 * should be enabled (user may turn it off later)
6388 * This is also how "iwconfig ethX key on" works */
Lee Jonesba4d6512020-08-14 12:39:15 +01006389 if ((index == current_index) && (key.len > 0) &&
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006390 (local->config.authType == AUTH_OPEN))
6391 set_auth_type(local, AUTH_ENCRYPT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006392 } else {
6393 /* Do we want to just set the transmit key index ? */
6394 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
Dan Williamsc0380692009-01-24 09:12:15 -05006395 if (valid_index(local, index)) {
6396 rc = set_wep_tx_idx(local, index, perm, 1);
6397 if (rc < 0) {
6398 airo_print_err(local->dev->name, "failed to set"
6399 " WEP transmit index to %d: %d.",
6400 index, rc);
6401 return rc;
6402 }
6403 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006404 /* Don't complain if only change the mode */
Jeff Garzik93a3b602007-11-23 21:50:20 -05006405 if (!(dwrq->flags & IW_ENCODE_MODE))
Linus Torvalds1da177e2005-04-16 15:20:36 -07006406 return -EINVAL;
Dan Williams138c0c62009-01-24 09:11:35 -05006407 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07006408 }
6409 /* Read the flags */
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006410 if (dwrq->flags & IW_ENCODE_DISABLED)
6411 set_auth_type(local, AUTH_OPEN); /* disable encryption */
Lee Jonesba4d6512020-08-14 12:39:15 +01006412 if (dwrq->flags & IW_ENCODE_RESTRICTED)
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006413 set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */
6414 if (dwrq->flags & IW_ENCODE_OPEN)
6415 set_auth_type(local, AUTH_ENCRYPT); /* Only Wep */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006416 /* Commit the changes to flags if needed */
Dan Streetmanf89b2322005-11-11 11:41:42 -05006417 if (local->config.authType != currentAuthType)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006418 set_bit (FLAG_COMMIT, &local->flags);
6419 return -EINPROGRESS; /* Call commit handler */
6420}
6421
6422/*------------------------------------------------------------------*/
6423/*
6424 * Wireless Handler : get Encryption Key
6425 */
6426static int airo_get_encode(struct net_device *dev,
6427 struct iw_request_info *info,
6428 struct iw_point *dwrq,
6429 char *extra)
6430{
Wang Chenfaf39942008-10-14 13:30:33 +08006431 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006432 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
John W. Linville267d4932009-05-20 10:51:41 -04006433 int wep_key_len;
Dan Williamsc0380692009-01-24 09:12:15 -05006434 u8 buf[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -07006435
Dan Williams138c0c62009-01-24 09:11:35 -05006436 if (!local->wep_capable)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006437 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006438
Linus Torvalds1da177e2005-04-16 15:20:36 -07006439 readConfigRid(local, 1);
Dan Williams138c0c62009-01-24 09:11:35 -05006440
Linus Torvalds1da177e2005-04-16 15:20:36 -07006441 /* Check encryption mode */
6442 switch(local->config.authType) {
6443 case AUTH_ENCRYPT:
6444 dwrq->flags = IW_ENCODE_OPEN;
6445 break;
6446 case AUTH_SHAREDKEY:
6447 dwrq->flags = IW_ENCODE_RESTRICTED;
6448 break;
6449 default:
6450 case AUTH_OPEN:
6451 dwrq->flags = IW_ENCODE_DISABLED;
6452 break;
6453 }
6454 /* We can't return the key, so set the proper flag and return zero */
6455 dwrq->flags |= IW_ENCODE_NOKEY;
6456 memset(extra, 0, 16);
6457
6458 /* Which key do we want ? -1 -> tx index */
Dan Williamsc0380692009-01-24 09:12:15 -05006459 if (!valid_index(local, index)) {
6460 index = get_wep_tx_idx(local);
6461 if (index < 0)
6462 index = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006463 }
Dan Williamsc0380692009-01-24 09:12:15 -05006464 dwrq->flags |= index + 1;
6465
6466 /* Copy the key to the user buffer */
John W. Linville267d4932009-05-20 10:51:41 -04006467 wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf));
6468 if (wep_key_len < 0) {
John W. Linvilleaedec922009-05-04 11:18:57 -04006469 dwrq->length = 0;
John W. Linville267d4932009-05-20 10:51:41 -04006470 } else {
6471 dwrq->length = wep_key_len;
6472 memcpy(extra, buf, dwrq->length);
6473 }
Dan Williamsc0380692009-01-24 09:12:15 -05006474
Linus Torvalds1da177e2005-04-16 15:20:36 -07006475 return 0;
6476}
6477
6478/*------------------------------------------------------------------*/
6479/*
Dan Williams4be757d2006-01-30 11:58:00 -05006480 * Wireless Handler : set extended Encryption parameters
6481 */
6482static int airo_set_encodeext(struct net_device *dev,
6483 struct iw_request_info *info,
6484 union iwreq_data *wrqu,
6485 char *extra)
6486{
Wang Chenfaf39942008-10-14 13:30:33 +08006487 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006488 struct iw_point *encoding = &wrqu->encoding;
6489 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
Lee Jonesba4d6512020-08-14 12:39:15 +01006490 int perm = (encoding->flags & IW_ENCODE_TEMP ? 0 : 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006491 __le16 currentAuthType = local->config.authType;
Dan Williamsc0380692009-01-24 09:12:15 -05006492 int idx, key_len, alg = ext->alg, set_key = 1, rc;
Dan Williams4be757d2006-01-30 11:58:00 -05006493 wep_key_t key;
6494
Dan Williams138c0c62009-01-24 09:11:35 -05006495 if (!local->wep_capable)
Dan Williams4be757d2006-01-30 11:58:00 -05006496 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006497
Dan Williams4be757d2006-01-30 11:58:00 -05006498 readConfigRid(local, 1);
6499
6500 /* Determine and validate the key index */
6501 idx = encoding->flags & IW_ENCODE_INDEX;
6502 if (idx) {
Dan Williams138c0c62009-01-24 09:11:35 -05006503 if (!valid_index(local, idx - 1))
Dan Williams4be757d2006-01-30 11:58:00 -05006504 return -EINVAL;
6505 idx--;
Dan Williamsc0380692009-01-24 09:12:15 -05006506 } else {
6507 idx = get_wep_tx_idx(local);
6508 if (idx < 0)
6509 idx = 0;
6510 }
Dan Williams4be757d2006-01-30 11:58:00 -05006511
6512 if (encoding->flags & IW_ENCODE_DISABLED)
6513 alg = IW_ENCODE_ALG_NONE;
6514
Dan Williams4be757d2006-01-30 11:58:00 -05006515 if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
Dan Williams22d88462006-02-05 18:00:30 -05006516 /* Only set transmit key index here, actual
6517 * key is set below if needed.
6518 */
Dan Williamsc0380692009-01-24 09:12:15 -05006519 rc = set_wep_tx_idx(local, idx, perm, 1);
6520 if (rc < 0) {
6521 airo_print_err(local->dev->name, "failed to set "
6522 "WEP transmit index to %d: %d.",
6523 idx, rc);
6524 return rc;
6525 }
Dan Williams22d88462006-02-05 18:00:30 -05006526 set_key = ext->key_len > 0 ? 1 : 0;
6527 }
6528
6529 if (set_key) {
Dan Williams4be757d2006-01-30 11:58:00 -05006530 /* Set the requested key first */
6531 memset(key.key, 0, MAX_KEY_SIZE);
6532 switch (alg) {
6533 case IW_ENCODE_ALG_NONE:
6534 key.len = 0;
6535 break;
6536 case IW_ENCODE_ALG_WEP:
6537 if (ext->key_len > MIN_KEY_SIZE) {
6538 key.len = MAX_KEY_SIZE;
6539 } else if (ext->key_len > 0) {
6540 key.len = MIN_KEY_SIZE;
6541 } else {
6542 return -EINVAL;
6543 }
6544 key_len = min (ext->key_len, key.len);
6545 memcpy(key.key, ext->key, key_len);
6546 break;
6547 default:
6548 return -EINVAL;
6549 }
Stanislaw Gruszkaf09c2562010-02-02 15:34:50 +01006550 if (key.len == 0) {
6551 rc = set_wep_tx_idx(local, idx, perm, 1);
6552 if (rc < 0) {
6553 airo_print_err(local->dev->name,
6554 "failed to set WEP transmit index to %d: %d.",
6555 idx, rc);
6556 return rc;
6557 }
6558 } else {
6559 rc = set_wep_key(local, idx, key.key, key.len, perm, 1);
6560 if (rc < 0) {
6561 airo_print_err(local->dev->name,
6562 "failed to set WEP key at index %d: %d.",
6563 idx, rc);
6564 return rc;
6565 }
Dan Williamsc0380692009-01-24 09:12:15 -05006566 }
Dan Williams4be757d2006-01-30 11:58:00 -05006567 }
6568
6569 /* Read the flags */
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006570 if (encoding->flags & IW_ENCODE_DISABLED)
6571 set_auth_type(local, AUTH_OPEN); /* disable encryption */
Lee Jonesba4d6512020-08-14 12:39:15 +01006572 if (encoding->flags & IW_ENCODE_RESTRICTED)
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006573 set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */
6574 if (encoding->flags & IW_ENCODE_OPEN)
6575 set_auth_type(local, AUTH_ENCRYPT);
Dan Williams4be757d2006-01-30 11:58:00 -05006576 /* Commit the changes to flags if needed */
6577 if (local->config.authType != currentAuthType)
6578 set_bit (FLAG_COMMIT, &local->flags);
6579
6580 return -EINPROGRESS;
6581}
6582
6583
6584/*------------------------------------------------------------------*/
6585/*
6586 * Wireless Handler : get extended Encryption parameters
6587 */
6588static int airo_get_encodeext(struct net_device *dev,
6589 struct iw_request_info *info,
6590 union iwreq_data *wrqu,
6591 char *extra)
6592{
Wang Chenfaf39942008-10-14 13:30:33 +08006593 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006594 struct iw_point *encoding = &wrqu->encoding;
6595 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
John W. Linville267d4932009-05-20 10:51:41 -04006596 int idx, max_key_len, wep_key_len;
Dan Williamsc0380692009-01-24 09:12:15 -05006597 u8 buf[16];
Dan Williams4be757d2006-01-30 11:58:00 -05006598
Dan Williams138c0c62009-01-24 09:11:35 -05006599 if (!local->wep_capable)
Dan Williams4be757d2006-01-30 11:58:00 -05006600 return -EOPNOTSUPP;
Dan Williams138c0c62009-01-24 09:11:35 -05006601
Dan Williams4be757d2006-01-30 11:58:00 -05006602 readConfigRid(local, 1);
6603
6604 max_key_len = encoding->length - sizeof(*ext);
6605 if (max_key_len < 0)
6606 return -EINVAL;
6607
6608 idx = encoding->flags & IW_ENCODE_INDEX;
6609 if (idx) {
Dan Williams138c0c62009-01-24 09:11:35 -05006610 if (!valid_index(local, idx - 1))
Dan Williams4be757d2006-01-30 11:58:00 -05006611 return -EINVAL;
6612 idx--;
Dan Williamsc0380692009-01-24 09:12:15 -05006613 } else {
6614 idx = get_wep_tx_idx(local);
6615 if (idx < 0)
6616 idx = 0;
6617 }
Dan Williams4be757d2006-01-30 11:58:00 -05006618
6619 encoding->flags = idx + 1;
6620 memset(ext, 0, sizeof(*ext));
6621
6622 /* Check encryption mode */
6623 switch(local->config.authType) {
6624 case AUTH_ENCRYPT:
6625 encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
6626 break;
6627 case AUTH_SHAREDKEY:
6628 encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
6629 break;
6630 default:
6631 case AUTH_OPEN:
6632 encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED;
6633 break;
6634 }
6635 /* We can't return the key, so set the proper flag and return zero */
6636 encoding->flags |= IW_ENCODE_NOKEY;
6637 memset(extra, 0, 16);
Lee Jonesba4d6512020-08-14 12:39:15 +01006638
Dan Williams4be757d2006-01-30 11:58:00 -05006639 /* Copy the key to the user buffer */
John W. Linville267d4932009-05-20 10:51:41 -04006640 wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf));
6641 if (wep_key_len < 0) {
John W. Linvilleaedec922009-05-04 11:18:57 -04006642 ext->key_len = 0;
John W. Linville267d4932009-05-20 10:51:41 -04006643 } else {
6644 ext->key_len = wep_key_len;
6645 memcpy(extra, buf, ext->key_len);
6646 }
Dan Williams4be757d2006-01-30 11:58:00 -05006647
6648 return 0;
6649}
6650
6651
6652/*------------------------------------------------------------------*/
6653/*
6654 * Wireless Handler : set extended authentication parameters
6655 */
6656static int airo_set_auth(struct net_device *dev,
6657 struct iw_request_info *info,
6658 union iwreq_data *wrqu, char *extra)
6659{
Wang Chenfaf39942008-10-14 13:30:33 +08006660 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006661 struct iw_param *param = &wrqu->param;
Al Viro3eb9b412007-12-21 00:00:35 -05006662 __le16 currentAuthType = local->config.authType;
Dan Williams4be757d2006-01-30 11:58:00 -05006663
6664 switch (param->flags & IW_AUTH_INDEX) {
6665 case IW_AUTH_WPA_VERSION:
6666 case IW_AUTH_CIPHER_PAIRWISE:
6667 case IW_AUTH_CIPHER_GROUP:
6668 case IW_AUTH_KEY_MGMT:
6669 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6670 case IW_AUTH_PRIVACY_INVOKED:
6671 /*
6672 * airo does not use these parameters
6673 */
6674 break;
6675
6676 case IW_AUTH_DROP_UNENCRYPTED:
6677 if (param->value) {
6678 /* Only change auth type if unencrypted */
6679 if (currentAuthType == AUTH_OPEN)
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006680 set_auth_type(local, AUTH_ENCRYPT);
Dan Williams4be757d2006-01-30 11:58:00 -05006681 } else {
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006682 set_auth_type(local, AUTH_OPEN);
Dan Williams4be757d2006-01-30 11:58:00 -05006683 }
6684
6685 /* Commit the changes to flags if needed */
6686 if (local->config.authType != currentAuthType)
6687 set_bit (FLAG_COMMIT, &local->flags);
6688 break;
6689
6690 case IW_AUTH_80211_AUTH_ALG: {
Dan Williams4be757d2006-01-30 11:58:00 -05006691 if (param->value & IW_AUTH_ALG_SHARED_KEY) {
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006692 set_auth_type(local, AUTH_SHAREDKEY);
Dan Williams4be757d2006-01-30 11:58:00 -05006693 } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
Ondrej Zary4a0f2ea2015-09-21 15:44:24 +02006694 /* We don't know here if WEP open system or
6695 * unencrypted mode was requested - so use the
6696 * last mode (of these two) used last time
6697 */
6698 set_auth_type(local, local->last_auth);
Dan Williams4be757d2006-01-30 11:58:00 -05006699 } else
6700 return -EINVAL;
Dan Williams4be757d2006-01-30 11:58:00 -05006701
6702 /* Commit the changes to flags if needed */
6703 if (local->config.authType != currentAuthType)
6704 set_bit (FLAG_COMMIT, &local->flags);
Dan Williams011f5c52009-04-08 10:15:17 -04006705 break;
Dan Williams4be757d2006-01-30 11:58:00 -05006706 }
6707
6708 case IW_AUTH_WPA_ENABLED:
6709 /* Silently accept disable of WPA */
6710 if (param->value > 0)
6711 return -EOPNOTSUPP;
6712 break;
6713
6714 default:
6715 return -EOPNOTSUPP;
6716 }
6717 return -EINPROGRESS;
6718}
6719
6720
6721/*------------------------------------------------------------------*/
6722/*
6723 * Wireless Handler : get extended authentication parameters
6724 */
6725static int airo_get_auth(struct net_device *dev,
6726 struct iw_request_info *info,
6727 union iwreq_data *wrqu, char *extra)
6728{
Wang Chenfaf39942008-10-14 13:30:33 +08006729 struct airo_info *local = dev->ml_priv;
Dan Williams4be757d2006-01-30 11:58:00 -05006730 struct iw_param *param = &wrqu->param;
Al Viro3eb9b412007-12-21 00:00:35 -05006731 __le16 currentAuthType = local->config.authType;
Dan Williams4be757d2006-01-30 11:58:00 -05006732
6733 switch (param->flags & IW_AUTH_INDEX) {
6734 case IW_AUTH_DROP_UNENCRYPTED:
6735 switch (currentAuthType) {
6736 case AUTH_SHAREDKEY:
6737 case AUTH_ENCRYPT:
6738 param->value = 1;
6739 break;
6740 default:
6741 param->value = 0;
6742 break;
6743 }
6744 break;
6745
6746 case IW_AUTH_80211_AUTH_ALG:
6747 switch (currentAuthType) {
6748 case AUTH_SHAREDKEY:
6749 param->value = IW_AUTH_ALG_SHARED_KEY;
6750 break;
6751 case AUTH_ENCRYPT:
6752 default:
6753 param->value = IW_AUTH_ALG_OPEN_SYSTEM;
6754 break;
6755 }
6756 break;
6757
6758 case IW_AUTH_WPA_ENABLED:
6759 param->value = 0;
6760 break;
6761
6762 default:
6763 return -EOPNOTSUPP;
6764 }
6765 return 0;
6766}
6767
6768
6769/*------------------------------------------------------------------*/
6770/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07006771 * Wireless Handler : set Tx-Power
6772 */
6773static int airo_set_txpow(struct net_device *dev,
6774 struct iw_request_info *info,
6775 struct iw_param *vwrq,
6776 char *extra)
6777{
Wang Chenfaf39942008-10-14 13:30:33 +08006778 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006779 CapabilityRid cap_rid; /* Card capability info */
6780 int i;
6781 int rc = -EINVAL;
Al Viro3eb9b412007-12-21 00:00:35 -05006782 __le16 v = cpu_to_le16(vwrq->value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006783
6784 readCapabilityRid(local, &cap_rid, 1);
6785
6786 if (vwrq->disabled) {
6787 set_bit (FLAG_RADIO_OFF, &local->flags);
6788 set_bit (FLAG_COMMIT, &local->flags);
6789 return -EINPROGRESS; /* Call commit handler */
6790 }
6791 if (vwrq->flags != IW_TXPOW_MWATT) {
6792 return -EINVAL;
6793 }
6794 clear_bit (FLAG_RADIO_OFF, &local->flags);
Roel Kluin3d0ccd02009-07-25 23:02:32 +02006795 for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++)
Al Viro3eb9b412007-12-21 00:00:35 -05006796 if (v == cap_rid.txPowerLevels[i]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006797 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006798 local->config.txPower = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006799 set_bit (FLAG_COMMIT, &local->flags);
6800 rc = -EINPROGRESS; /* Call commit handler */
6801 break;
6802 }
6803 return rc;
6804}
6805
6806/*------------------------------------------------------------------*/
6807/*
6808 * Wireless Handler : get Tx-Power
6809 */
6810static int airo_get_txpow(struct net_device *dev,
6811 struct iw_request_info *info,
6812 struct iw_param *vwrq,
6813 char *extra)
6814{
Wang Chenfaf39942008-10-14 13:30:33 +08006815 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006816
6817 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05006818 vwrq->value = le16_to_cpu(local->config.txPower);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006819 vwrq->fixed = 1; /* No power control */
6820 vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags);
6821 vwrq->flags = IW_TXPOW_MWATT;
6822
6823 return 0;
6824}
6825
6826/*------------------------------------------------------------------*/
6827/*
6828 * Wireless Handler : set Retry limits
6829 */
6830static int airo_set_retry(struct net_device *dev,
6831 struct iw_request_info *info,
6832 struct iw_param *vwrq,
6833 char *extra)
6834{
Wang Chenfaf39942008-10-14 13:30:33 +08006835 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006836 int rc = -EINVAL;
6837
Lee Jonesba4d6512020-08-14 12:39:15 +01006838 if (vwrq->disabled) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006839 return -EINVAL;
6840 }
6841 readConfigRid(local, 1);
Lee Jonesba4d6512020-08-14 12:39:15 +01006842 if (vwrq->flags & IW_RETRY_LIMIT) {
Al Viro3eb9b412007-12-21 00:00:35 -05006843 __le16 v = cpu_to_le16(vwrq->value);
Lee Jonesba4d6512020-08-14 12:39:15 +01006844 if (vwrq->flags & IW_RETRY_LONG)
Al Viro3eb9b412007-12-21 00:00:35 -05006845 local->config.longRetryLimit = v;
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006846 else if (vwrq->flags & IW_RETRY_SHORT)
Al Viro3eb9b412007-12-21 00:00:35 -05006847 local->config.shortRetryLimit = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006848 else {
6849 /* No modifier : set both */
Al Viro3eb9b412007-12-21 00:00:35 -05006850 local->config.longRetryLimit = v;
6851 local->config.shortRetryLimit = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006852 }
6853 set_bit (FLAG_COMMIT, &local->flags);
6854 rc = -EINPROGRESS; /* Call commit handler */
6855 }
Lee Jonesba4d6512020-08-14 12:39:15 +01006856 if (vwrq->flags & IW_RETRY_LIFETIME) {
Al Viro3eb9b412007-12-21 00:00:35 -05006857 local->config.txLifetime = cpu_to_le16(vwrq->value / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006858 set_bit (FLAG_COMMIT, &local->flags);
6859 rc = -EINPROGRESS; /* Call commit handler */
6860 }
6861 return rc;
6862}
6863
6864/*------------------------------------------------------------------*/
6865/*
6866 * Wireless Handler : get Retry limits
6867 */
6868static int airo_get_retry(struct net_device *dev,
6869 struct iw_request_info *info,
6870 struct iw_param *vwrq,
6871 char *extra)
6872{
Wang Chenfaf39942008-10-14 13:30:33 +08006873 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006874
6875 vwrq->disabled = 0; /* Can't be disabled */
6876
6877 readConfigRid(local, 1);
6878 /* Note : by default, display the min retry number */
Lee Jonesba4d6512020-08-14 12:39:15 +01006879 if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006880 vwrq->flags = IW_RETRY_LIFETIME;
Al Viro3eb9b412007-12-21 00:00:35 -05006881 vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024;
Lee Jonesba4d6512020-08-14 12:39:15 +01006882 } else if ((vwrq->flags & IW_RETRY_LONG)) {
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006883 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
Al Viro3eb9b412007-12-21 00:00:35 -05006884 vwrq->value = le16_to_cpu(local->config.longRetryLimit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07006885 } else {
6886 vwrq->flags = IW_RETRY_LIMIT;
Al Viro3eb9b412007-12-21 00:00:35 -05006887 vwrq->value = le16_to_cpu(local->config.shortRetryLimit);
Lee Jonesba4d6512020-08-14 12:39:15 +01006888 if (local->config.shortRetryLimit != local->config.longRetryLimit)
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07006889 vwrq->flags |= IW_RETRY_SHORT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006890 }
6891
6892 return 0;
6893}
6894
6895/*------------------------------------------------------------------*/
6896/*
6897 * Wireless Handler : get range info
6898 */
6899static int airo_get_range(struct net_device *dev,
6900 struct iw_request_info *info,
6901 struct iw_point *dwrq,
6902 char *extra)
6903{
Wang Chenfaf39942008-10-14 13:30:33 +08006904 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006905 struct iw_range *range = (struct iw_range *) extra;
6906 CapabilityRid cap_rid; /* Card capability info */
6907 int i;
6908 int k;
6909
6910 readCapabilityRid(local, &cap_rid, 1);
6911
6912 dwrq->length = sizeof(struct iw_range);
6913 memset(range, 0, sizeof(*range));
6914 range->min_nwid = 0x0000;
6915 range->max_nwid = 0x0000;
6916 range->num_channels = 14;
6917 /* Should be based on cap_rid.country to give only
6918 * what the current card support */
6919 k = 0;
Lee Jonesba4d6512020-08-14 12:39:15 +01006920 for (i = 0; i < 14; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006921 range->freq[k].i = i + 1; /* List index */
Zhao, Gange0febf12014-02-18 21:35:57 +08006922 range->freq[k].m = 100000 *
Johannes Berg57fbcce2016-04-12 15:56:15 +02006923 ieee80211_channel_to_frequency(i + 1, NL80211_BAND_2GHZ);
David Kilroy9ee677c2008-12-23 14:03:38 +00006924 range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006925 }
6926 range->num_frequency = k;
6927
Linus Torvalds1da177e2005-04-16 15:20:36 -07006928 range->sensitivity = 65535;
6929
Dan Williams41480af2005-05-10 09:45:51 -04006930 /* Hum... Should put the right values there */
6931 if (local->rssi)
6932 range->max_qual.qual = 100; /* % */
6933 else
6934 range->max_qual.qual = airo_get_max_quality(&cap_rid);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006935 range->max_qual.level = 0x100 - 120; /* -120 dBm */
6936 range->max_qual.noise = 0x100 - 120; /* -120 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006937
6938 /* Experimental measurements - boundary 11/5.5 Mb/s */
6939 /* Note : with or without the (local->rssi), results
6940 * are somewhat different. - Jean II */
6941 if (local->rssi) {
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006942 range->avg_qual.qual = 50; /* % */
6943 range->avg_qual.level = 0x100 - 70; /* -70 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006944 } else {
6945 range->avg_qual.qual = airo_get_avg_quality(&cap_rid);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006946 range->avg_qual.level = 0x100 - 80; /* -80 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006947 }
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07006948 range->avg_qual.noise = 0x100 - 85; /* -85 dBm */
Dan Williams41480af2005-05-10 09:45:51 -04006949
Lee Jonesba4d6512020-08-14 12:39:15 +01006950 for (i = 0 ; i < 8 ; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006951 range->bitrate[i] = cap_rid.supportedRates[i] * 500000;
Lee Jonesba4d6512020-08-14 12:39:15 +01006952 if (range->bitrate[i] == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006953 break;
6954 }
6955 range->num_bitrates = i;
6956
6957 /* Set an indication of the max TCP throughput
6958 * in bit/s that we can expect using this interface.
6959 * May be use for QoS stuff... Jean II */
Lee Jonesba4d6512020-08-14 12:39:15 +01006960 if (i > 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006961 range->throughput = 5000 * 1000;
6962 else
6963 range->throughput = 1500 * 1000;
6964
6965 range->min_rts = 0;
Dan Williams15db2762006-03-16 13:46:27 -05006966 range->max_rts = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006967 range->min_frag = 256;
Dan Williams15db2762006-03-16 13:46:27 -05006968 range->max_frag = AIRO_DEF_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006969
Lee Jonesba4d6512020-08-14 12:39:15 +01006970 if (cap_rid.softCap & cpu_to_le16(2)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006971 // WEP: RC4 40 bits
6972 range->encoding_size[0] = 5;
6973 // RC4 ~128 bits
Al Viro56d81bd2007-12-20 17:18:35 -05006974 if (cap_rid.softCap & cpu_to_le16(0x100)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07006975 range->encoding_size[1] = 13;
6976 range->num_encoding_sizes = 2;
6977 } else
6978 range->num_encoding_sizes = 1;
Al Viro56d81bd2007-12-20 17:18:35 -05006979 range->max_encoding_tokens =
6980 cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07006981 } else {
6982 range->num_encoding_sizes = 0;
6983 range->max_encoding_tokens = 0;
6984 }
6985 range->min_pmp = 0;
6986 range->max_pmp = 5000000; /* 5 secs */
6987 range->min_pmt = 0;
6988 range->max_pmt = 65535 * 1024; /* ??? */
6989 range->pmp_flags = IW_POWER_PERIOD;
6990 range->pmt_flags = IW_POWER_TIMEOUT;
6991 range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
6992
6993 /* Transmit Power - values are in mW */
Lee Jonesba4d6512020-08-14 12:39:15 +01006994 for (i = 0 ; i < 8 ; i++) {
Al Viro56d81bd2007-12-20 17:18:35 -05006995 range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]);
Lee Jonesba4d6512020-08-14 12:39:15 +01006996 if (range->txpower[i] == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07006997 break;
6998 }
6999 range->num_txpower = i;
7000 range->txpower_capa = IW_TXPOW_MWATT;
Dan Williams3c304952006-04-15 12:26:18 -04007001 range->we_version_source = 19;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007002 range->we_version_compiled = WIRELESS_EXT;
7003 range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
7004 range->retry_flags = IW_RETRY_LIMIT;
7005 range->r_time_flags = IW_RETRY_LIFETIME;
7006 range->min_retry = 1;
7007 range->max_retry = 65535;
7008 range->min_r_time = 1024;
7009 range->max_r_time = 65535 * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007010
7011 /* Event capability (kernel + driver) */
7012 range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
7013 IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
7014 IW_EVENT_CAPA_MASK(SIOCGIWAP) |
7015 IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
7016 range->event_capa[1] = IW_EVENT_CAPA_K_1;
7017 range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP);
7018 return 0;
7019}
7020
7021/*------------------------------------------------------------------*/
7022/*
7023 * Wireless Handler : set Power Management
7024 */
7025static int airo_set_power(struct net_device *dev,
7026 struct iw_request_info *info,
7027 struct iw_param *vwrq,
7028 char *extra)
7029{
Wang Chenfaf39942008-10-14 13:30:33 +08007030 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007031
7032 readConfigRid(local, 1);
7033 if (vwrq->disabled) {
Al Viro3eb9b412007-12-21 00:00:35 -05007034 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007035 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007036 local->config.powerSaveMode = POWERSAVE_CAM;
Al Viro3eb9b412007-12-21 00:00:35 -05007037 local->config.rmode &= ~RXMODE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007038 local->config.rmode |= RXMODE_BC_MC_ADDR;
7039 set_bit (FLAG_COMMIT, &local->flags);
7040 return -EINPROGRESS; /* Call commit handler */
7041 }
7042 if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
Al Viro3eb9b412007-12-21 00:00:35 -05007043 local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007044 local->config.powerSaveMode = POWERSAVE_PSPCAM;
7045 set_bit (FLAG_COMMIT, &local->flags);
7046 } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
Al Viro3eb9b412007-12-21 00:00:35 -05007047 local->config.fastListenInterval =
7048 local->config.listenInterval =
7049 cpu_to_le16((vwrq->value + 500) / 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007050 local->config.powerSaveMode = POWERSAVE_PSPCAM;
7051 set_bit (FLAG_COMMIT, &local->flags);
7052 }
7053 switch (vwrq->flags & IW_POWER_MODE) {
7054 case IW_POWER_UNICAST_R:
Al Viro3eb9b412007-12-21 00:00:35 -05007055 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007056 return -EINVAL;
Al Viro3eb9b412007-12-21 00:00:35 -05007057 local->config.rmode &= ~RXMODE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007058 local->config.rmode |= RXMODE_ADDR;
7059 set_bit (FLAG_COMMIT, &local->flags);
7060 break;
7061 case IW_POWER_ALL_R:
Al Viro3eb9b412007-12-21 00:00:35 -05007062 if (sniffing_mode(local))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007063 return -EINVAL;
Al Viro3eb9b412007-12-21 00:00:35 -05007064 local->config.rmode &= ~RXMODE_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007065 local->config.rmode |= RXMODE_BC_MC_ADDR;
7066 set_bit (FLAG_COMMIT, &local->flags);
7067 case IW_POWER_ON:
Jean Tourrilhes7f8544c2006-08-29 17:58:11 -07007068 /* This is broken, fixme ;-) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07007069 break;
7070 default:
7071 return -EINVAL;
7072 }
7073 // Note : we may want to factor local->need_commit here
7074 // Note2 : may also want to factor RXMODE_RFMON test
7075 return -EINPROGRESS; /* Call commit handler */
7076}
7077
7078/*------------------------------------------------------------------*/
7079/*
7080 * Wireless Handler : get Power Management
7081 */
7082static int airo_get_power(struct net_device *dev,
7083 struct iw_request_info *info,
7084 struct iw_param *vwrq,
7085 char *extra)
7086{
Wang Chenfaf39942008-10-14 13:30:33 +08007087 struct airo_info *local = dev->ml_priv;
Al Viro3eb9b412007-12-21 00:00:35 -05007088 __le16 mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007089
7090 readConfigRid(local, 1);
7091 mode = local->config.powerSaveMode;
7092 if ((vwrq->disabled = (mode == POWERSAVE_CAM)))
7093 return 0;
7094 if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
Al Viro3eb9b412007-12-21 00:00:35 -05007095 vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007096 vwrq->flags = IW_POWER_TIMEOUT;
7097 } else {
Al Viro3eb9b412007-12-21 00:00:35 -05007098 vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007099 vwrq->flags = IW_POWER_PERIOD;
7100 }
Al Viro3eb9b412007-12-21 00:00:35 -05007101 if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007102 vwrq->flags |= IW_POWER_UNICAST_R;
7103 else
7104 vwrq->flags |= IW_POWER_ALL_R;
7105
7106 return 0;
7107}
7108
7109/*------------------------------------------------------------------*/
7110/*
7111 * Wireless Handler : set Sensitivity
7112 */
7113static int airo_set_sens(struct net_device *dev,
7114 struct iw_request_info *info,
7115 struct iw_param *vwrq,
7116 char *extra)
7117{
Wang Chenfaf39942008-10-14 13:30:33 +08007118 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007119
7120 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05007121 local->config.rssiThreshold =
7122 cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007123 set_bit (FLAG_COMMIT, &local->flags);
7124
7125 return -EINPROGRESS; /* Call commit handler */
7126}
7127
7128/*------------------------------------------------------------------*/
7129/*
7130 * Wireless Handler : get Sensitivity
7131 */
7132static int airo_get_sens(struct net_device *dev,
7133 struct iw_request_info *info,
7134 struct iw_param *vwrq,
7135 char *extra)
7136{
Wang Chenfaf39942008-10-14 13:30:33 +08007137 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007138
7139 readConfigRid(local, 1);
Al Viro3eb9b412007-12-21 00:00:35 -05007140 vwrq->value = le16_to_cpu(local->config.rssiThreshold);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007141 vwrq->disabled = (vwrq->value == 0);
7142 vwrq->fixed = 1;
7143
7144 return 0;
7145}
7146
7147/*------------------------------------------------------------------*/
7148/*
7149 * Wireless Handler : get AP List
7150 * Note : this is deprecated in favor of IWSCAN
7151 */
7152static int airo_get_aplist(struct net_device *dev,
7153 struct iw_request_info *info,
7154 struct iw_point *dwrq,
7155 char *extra)
7156{
Wang Chenfaf39942008-10-14 13:30:33 +08007157 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007158 struct sockaddr *address = (struct sockaddr *) extra;
Frank Seidel998a5a72009-02-25 15:39:57 +01007159 struct iw_quality *qual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007160 BSSListRid BSSList;
7161 int i;
7162 int loseSync = capable(CAP_NET_ADMIN) ? 1: -1;
7163
Kees Cook6da2ec52018-06-12 13:55:00 -07007164 qual = kmalloc_array(IW_MAX_AP, sizeof(*qual), GFP_KERNEL);
Frank Seidel998a5a72009-02-25 15:39:57 +01007165 if (!qual)
7166 return -ENOMEM;
7167
Linus Torvalds1da177e2005-04-16 15:20:36 -07007168 for (i = 0; i < IW_MAX_AP; i++) {
Al Viro17e70492007-12-19 18:56:37 -05007169 u16 dBm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007170 if (readBSSListRid(local, loseSync, &BSSList))
7171 break;
7172 loseSync = 0;
7173 memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN);
7174 address[i].sa_family = ARPHRD_ETHER;
Al Viro17e70492007-12-19 18:56:37 -05007175 dBm = le16_to_cpu(BSSList.dBm);
Dan Williams41480af2005-05-10 09:45:51 -04007176 if (local->rssi) {
Al Viro17e70492007-12-19 18:56:37 -05007177 qual[i].level = 0x100 - dBm;
7178 qual[i].qual = airo_dbm_to_pct(local->rssi, dBm);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007179 qual[i].updated = IW_QUAL_QUAL_UPDATED
7180 | IW_QUAL_LEVEL_UPDATED
7181 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007182 } else {
Al Viro17e70492007-12-19 18:56:37 -05007183 qual[i].level = (dBm + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007184 qual[i].qual = 0;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007185 qual[i].updated = IW_QUAL_QUAL_INVALID
7186 | IW_QUAL_LEVEL_UPDATED
7187 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007188 }
7189 qual[i].noise = local->wstats.qual.noise;
Al Viro17e70492007-12-19 18:56:37 -05007190 if (BSSList.index == cpu_to_le16(0xffff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007191 break;
7192 }
7193 if (!i) {
7194 StatusRid status_rid; /* Card status info */
7195 readStatusRid(local, &status_rid, 1);
7196 for (i = 0;
7197 i < min(IW_MAX_AP, 4) &&
7198 (status_rid.bssid[i][0]
7199 & status_rid.bssid[i][1]
7200 & status_rid.bssid[i][2]
7201 & status_rid.bssid[i][3]
7202 & status_rid.bssid[i][4]
7203 & status_rid.bssid[i][5])!=0xff &&
7204 (status_rid.bssid[i][0]
7205 | status_rid.bssid[i][1]
7206 | status_rid.bssid[i][2]
7207 | status_rid.bssid[i][3]
7208 | status_rid.bssid[i][4]
7209 | status_rid.bssid[i][5]);
7210 i++) {
7211 memcpy(address[i].sa_data,
7212 status_rid.bssid[i], ETH_ALEN);
7213 address[i].sa_family = ARPHRD_ETHER;
7214 }
7215 } else {
7216 dwrq->flags = 1; /* Should be define'd */
Dan Carpenter7f59ebb2012-06-18 10:48:14 +03007217 memcpy(extra + sizeof(struct sockaddr) * i, qual,
7218 sizeof(struct iw_quality) * i);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007219 }
7220 dwrq->length = i;
7221
Frank Seidel998a5a72009-02-25 15:39:57 +01007222 kfree(qual);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007223 return 0;
7224}
7225
7226/*------------------------------------------------------------------*/
7227/*
7228 * Wireless Handler : Initiate Scan
7229 */
7230static int airo_set_scan(struct net_device *dev,
7231 struct iw_request_info *info,
David Kilroy9930cce2008-09-13 12:22:05 +01007232 struct iw_point *dwrq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007233 char *extra)
7234{
Wang Chenfaf39942008-10-14 13:30:33 +08007235 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007236 Cmd cmd;
7237 Resp rsp;
Dan Williams9e75af32006-03-16 13:46:29 -05007238 int wake = 0;
Ondrej Zarydae04122015-10-16 21:04:14 +02007239 APListRid APList_rid_empty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007240
7241 /* Note : you may have realised that, as this is a SET operation,
7242 * this is privileged and therefore a normal user can't
7243 * perform scanning.
7244 * This is not an error, while the device perform scanning,
7245 * traffic doesn't flow, so it's a perfect DoS...
7246 * Jean II */
7247 if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
7248
Dan Williams9e75af32006-03-16 13:46:29 -05007249 if (down_interruptible(&ai->sem))
7250 return -ERESTARTSYS;
7251
7252 /* If there's already a scan in progress, don't
7253 * trigger another one. */
7254 if (ai->scan_timeout > 0)
7255 goto out;
7256
Ondrej Zarydae04122015-10-16 21:04:14 +02007257 /* Clear APList as it affects scan results */
7258 memset(&APList_rid_empty, 0, sizeof(APList_rid_empty));
7259 APList_rid_empty.len = cpu_to_le16(sizeof(APList_rid_empty));
7260 disable_MAC(ai, 2);
7261 writeAPListRid(ai, &APList_rid_empty, 0);
7262 enable_MAC(ai, 0);
7263
Linus Torvalds1da177e2005-04-16 15:20:36 -07007264 /* Initiate a scan command */
Dan Williams6fcdf562006-03-31 15:08:46 -05007265 ai->scan_timeout = RUN_AT(3*HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007266 memset(&cmd, 0, sizeof(cmd));
Lee Jonesba4d6512020-08-14 12:39:15 +01007267 cmd.cmd = CMD_LISTBSS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007268 issuecommand(ai, &cmd, &rsp);
Dan Williams9e75af32006-03-16 13:46:29 -05007269 wake = 1;
7270
7271out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07007272 up(&ai->sem);
Dan Williams9e75af32006-03-16 13:46:29 -05007273 if (wake)
7274 wake_up_interruptible(&ai->thr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007275 return 0;
7276}
7277
7278/*------------------------------------------------------------------*/
7279/*
7280 * Translate scan data returned from the card to a card independent
7281 * format that the Wireless Tools will understand - Jean II
7282 */
7283static inline char *airo_translate_scan(struct net_device *dev,
David S. Millerccc58052008-06-16 18:50:49 -07007284 struct iw_request_info *info,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007285 char *current_ev,
7286 char *end_buf,
Dan Williams41480af2005-05-10 09:45:51 -04007287 BSSListRid *bss)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007288{
Wang Chenfaf39942008-10-14 13:30:33 +08007289 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007290 struct iw_event iwe; /* Temporary buffer */
Al Viro17e70492007-12-19 18:56:37 -05007291 __le16 capabilities;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007292 char * current_val; /* For rates */
7293 int i;
Dan Williams3c304952006-04-15 12:26:18 -04007294 char * buf;
Al Viro851b3e52007-12-19 19:20:12 -05007295 u16 dBm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007296
7297 /* First entry *MUST* be the AP MAC address */
7298 iwe.cmd = SIOCGIWAP;
7299 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
Dan Williams41480af2005-05-10 09:45:51 -04007300 memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
David S. Millerccc58052008-06-16 18:50:49 -07007301 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7302 &iwe, IW_EV_ADDR_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007303
7304 /* Other entries will be displayed in the order we give them */
7305
7306 /* Add the ESSID */
Dan Williams41480af2005-05-10 09:45:51 -04007307 iwe.u.data.length = bss->ssidLen;
Lee Jonesba4d6512020-08-14 12:39:15 +01007308 if (iwe.u.data.length > 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007309 iwe.u.data.length = 32;
7310 iwe.cmd = SIOCGIWESSID;
7311 iwe.u.data.flags = 1;
David S. Millerccc58052008-06-16 18:50:49 -07007312 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7313 &iwe, bss->ssid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007314
7315 /* Add mode */
7316 iwe.cmd = SIOCGIWMODE;
Al Viro17e70492007-12-19 18:56:37 -05007317 capabilities = bss->cap;
Lee Jonesba4d6512020-08-14 12:39:15 +01007318 if (capabilities & (CAP_ESS | CAP_IBSS)) {
7319 if (capabilities & CAP_ESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007320 iwe.u.mode = IW_MODE_MASTER;
7321 else
7322 iwe.u.mode = IW_MODE_ADHOC;
David S. Millerccc58052008-06-16 18:50:49 -07007323 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7324 &iwe, IW_EV_UINT_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007325 }
7326
7327 /* Add frequency */
7328 iwe.cmd = SIOCGIWFREQ;
Dan Williams41480af2005-05-10 09:45:51 -04007329 iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
Zhao, Gange0febf12014-02-18 21:35:57 +08007330 iwe.u.freq.m = 100000 *
Johannes Berg57fbcce2016-04-12 15:56:15 +02007331 ieee80211_channel_to_frequency(iwe.u.freq.m, NL80211_BAND_2GHZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007332 iwe.u.freq.e = 1;
David S. Millerccc58052008-06-16 18:50:49 -07007333 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7334 &iwe, IW_EV_FREQ_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007335
Al Viro851b3e52007-12-19 19:20:12 -05007336 dBm = le16_to_cpu(bss->dBm);
7337
Linus Torvalds1da177e2005-04-16 15:20:36 -07007338 /* Add quality statistics */
7339 iwe.cmd = IWEVQUAL;
Dan Williams41480af2005-05-10 09:45:51 -04007340 if (ai->rssi) {
Al Viro851b3e52007-12-19 19:20:12 -05007341 iwe.u.qual.level = 0x100 - dBm;
7342 iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm);
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007343 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
7344 | IW_QUAL_LEVEL_UPDATED
7345 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007346 } else {
Al Viro851b3e52007-12-19 19:20:12 -05007347 iwe.u.qual.level = (dBm + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007348 iwe.u.qual.qual = 0;
Jeff Garzikbbeec902005-09-07 00:27:54 -04007349 iwe.u.qual.updated = IW_QUAL_QUAL_INVALID
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007350 | IW_QUAL_LEVEL_UPDATED
7351 | IW_QUAL_DBM;
Dan Williams41480af2005-05-10 09:45:51 -04007352 }
7353 iwe.u.qual.noise = ai->wstats.qual.noise;
David S. Millerccc58052008-06-16 18:50:49 -07007354 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
7355 &iwe, IW_EV_QUAL_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007356
7357 /* Add encryption capability */
7358 iwe.cmd = SIOCGIWENCODE;
Lee Jonesba4d6512020-08-14 12:39:15 +01007359 if (capabilities & CAP_PRIVACY)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007360 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
7361 else
7362 iwe.u.data.flags = IW_ENCODE_DISABLED;
7363 iwe.u.data.length = 0;
David S. Millerccc58052008-06-16 18:50:49 -07007364 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7365 &iwe, bss->ssid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007366
7367 /* Rate : stuffing multiple values in a single event require a bit
7368 * more of magic - Jean II */
David S. Millerccc58052008-06-16 18:50:49 -07007369 current_val = current_ev + iwe_stream_lcp_len(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007370
7371 iwe.cmd = SIOCGIWRATE;
7372 /* Those two flags are ignored... */
7373 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
7374 /* Max 8 values */
Lee Jonesba4d6512020-08-14 12:39:15 +01007375 for (i = 0 ; i < 8 ; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007376 /* NULL terminated */
Lee Jonesba4d6512020-08-14 12:39:15 +01007377 if (bss->rates[i] == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007378 break;
7379 /* Bit rate given in 500 kb/s units (+ 0x80) */
Dan Williams41480af2005-05-10 09:45:51 -04007380 iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007381 /* Add new value to event */
David S. Millerccc58052008-06-16 18:50:49 -07007382 current_val = iwe_stream_add_value(info, current_ev,
7383 current_val, end_buf,
7384 &iwe, IW_EV_PARAM_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007385 }
7386 /* Check if we added any event */
David S. Millerccc58052008-06-16 18:50:49 -07007387 if ((current_val - current_ev) > iwe_stream_lcp_len(info))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007388 current_ev = current_val;
7389
Dan Williams3c304952006-04-15 12:26:18 -04007390 /* Beacon interval */
7391 buf = kmalloc(30, GFP_KERNEL);
7392 if (buf) {
7393 iwe.cmd = IWEVCUSTOM;
7394 sprintf(buf, "bcn_int=%d", bss->beaconInterval);
7395 iwe.u.data.length = strlen(buf);
David S. Millerccc58052008-06-16 18:50:49 -07007396 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
7397 &iwe, buf);
Dan Williams3c304952006-04-15 12:26:18 -04007398 kfree(buf);
7399 }
7400
7401 /* Put WPA/RSN Information Elements into the event stream */
7402 if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
7403 unsigned int num_null_ies = 0;
7404 u16 length = sizeof (bss->extra.iep);
Johannes Berg2c7060022008-10-30 22:09:54 +01007405 u8 *ie = (void *)&bss->extra.iep;
Dan Williams3c304952006-04-15 12:26:18 -04007406
Johannes Berg2c7060022008-10-30 22:09:54 +01007407 while ((length >= 2) && (num_null_ies < 2)) {
7408 if (2 + ie[1] > length) {
Dan Williams3c304952006-04-15 12:26:18 -04007409 /* Invalid element, don't continue parsing IE */
7410 break;
7411 }
7412
Johannes Berg2c7060022008-10-30 22:09:54 +01007413 switch (ie[0]) {
7414 case WLAN_EID_SSID:
Dan Williams3c304952006-04-15 12:26:18 -04007415 /* Two zero-length SSID elements
7416 * mean we're done parsing elements */
Johannes Berg2c7060022008-10-30 22:09:54 +01007417 if (!ie[1])
Dan Williams3c304952006-04-15 12:26:18 -04007418 num_null_ies++;
7419 break;
7420
Arend van Spriel04b23122012-10-12 12:28:14 +02007421 case WLAN_EID_VENDOR_SPECIFIC:
Johannes Berg2c7060022008-10-30 22:09:54 +01007422 if (ie[1] >= 4 &&
7423 ie[2] == 0x00 &&
7424 ie[3] == 0x50 &&
7425 ie[4] == 0xf2 &&
7426 ie[5] == 0x01) {
Dan Williams3c304952006-04-15 12:26:18 -04007427 iwe.cmd = IWEVGENIE;
Johannes Berg2c7060022008-10-30 22:09:54 +01007428 /* 64 is an arbitrary cut-off */
7429 iwe.u.data.length = min(ie[1] + 2,
7430 64);
David S. Millerccc58052008-06-16 18:50:49 -07007431 current_ev = iwe_stream_add_point(
7432 info, current_ev,
Johannes Berg2c7060022008-10-30 22:09:54 +01007433 end_buf, &iwe, ie);
Dan Williams3c304952006-04-15 12:26:18 -04007434 }
7435 break;
7436
Johannes Berg2c7060022008-10-30 22:09:54 +01007437 case WLAN_EID_RSN:
Dan Williams3c304952006-04-15 12:26:18 -04007438 iwe.cmd = IWEVGENIE;
Johannes Berg2c7060022008-10-30 22:09:54 +01007439 /* 64 is an arbitrary cut-off */
7440 iwe.u.data.length = min(ie[1] + 2, 64);
David S. Millerccc58052008-06-16 18:50:49 -07007441 current_ev = iwe_stream_add_point(
7442 info, current_ev, end_buf,
Johannes Berg2c7060022008-10-30 22:09:54 +01007443 &iwe, ie);
Dan Williams3c304952006-04-15 12:26:18 -04007444 break;
7445
7446 default:
7447 break;
7448 }
7449
Johannes Berg2c7060022008-10-30 22:09:54 +01007450 length -= 2 + ie[1];
7451 ie += 2 + ie[1];
Dan Williams3c304952006-04-15 12:26:18 -04007452 }
7453 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07007454 return current_ev;
7455}
7456
7457/*------------------------------------------------------------------*/
7458/*
7459 * Wireless Handler : Read Scan Results
7460 */
7461static int airo_get_scan(struct net_device *dev,
7462 struct iw_request_info *info,
7463 struct iw_point *dwrq,
7464 char *extra)
7465{
Wang Chenfaf39942008-10-14 13:30:33 +08007466 struct airo_info *ai = dev->ml_priv;
Dan Williams9e75af32006-03-16 13:46:29 -05007467 BSSListElement *net;
7468 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007469 char *current_ev = extra;
7470
Dan Williams9e75af32006-03-16 13:46:29 -05007471 /* If a scan is in-progress, return -EAGAIN */
7472 if (ai->scan_timeout > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007473 return -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007474
Dan Williams9e75af32006-03-16 13:46:29 -05007475 if (down_interruptible(&ai->sem))
7476 return -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007477
Dan Williams9e75af32006-03-16 13:46:29 -05007478 list_for_each_entry (net, &ai->network_list, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007479 /* Translate to WE format this entry */
David S. Millerccc58052008-06-16 18:50:49 -07007480 current_ev = airo_translate_scan(dev, info, current_ev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07007481 extra + dwrq->length,
Dan Williams9e75af32006-03-16 13:46:29 -05007482 &net->bss);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007483
7484 /* Check if there is space for one more entry */
Lee Jonesba4d6512020-08-14 12:39:15 +01007485 if ((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007486 /* Ask user space to try again with a bigger buffer */
Dan Williams9e75af32006-03-16 13:46:29 -05007487 err = -E2BIG;
7488 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007489 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07007490 }
Dan Williams9e75af32006-03-16 13:46:29 -05007491
Linus Torvalds1da177e2005-04-16 15:20:36 -07007492 /* Length of data */
7493 dwrq->length = (current_ev - extra);
7494 dwrq->flags = 0; /* todo */
7495
Dan Williams9e75af32006-03-16 13:46:29 -05007496out:
7497 up(&ai->sem);
7498 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007499}
7500
7501/*------------------------------------------------------------------*/
7502/*
7503 * Commit handler : called after a bunch of SET operations
7504 */
7505static int airo_config_commit(struct net_device *dev,
7506 struct iw_request_info *info, /* NULL */
7507 void *zwrq, /* NULL */
7508 char *extra) /* NULL */
7509{
Wang Chenfaf39942008-10-14 13:30:33 +08007510 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007511
7512 if (!test_bit (FLAG_COMMIT, &local->flags))
7513 return 0;
7514
7515 /* Some of the "SET" function may have modified some of the
7516 * parameters. It's now time to commit them in the card */
7517 disable_MAC(local, 1);
7518 if (test_bit (FLAG_RESET, &local->flags)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007519 SsidRid SSID_rid;
7520
Linus Torvalds1da177e2005-04-16 15:20:36 -07007521 readSsidRid(local, &SSID_rid);
7522 if (test_bit(FLAG_MPI,&local->flags))
Lee Jonesba4d6512020-08-14 12:39:15 +01007523 setup_card(local, dev->dev_addr, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007524 else
7525 reset_airo_card(dev);
7526 disable_MAC(local, 1);
7527 writeSsidRid(local, &SSID_rid, 1);
Ondrej Zaryf675f932015-10-16 21:04:13 +02007528 writeAPListRid(local, &local->APList, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007529 }
7530 if (down_interruptible(&local->sem))
7531 return -ERESTARTSYS;
7532 writeConfigRid(local, 0);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007533 enable_MAC(local, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007534 if (test_bit (FLAG_RESET, &local->flags))
7535 airo_set_promisc(local);
7536 else
7537 up(&local->sem);
7538
7539 return 0;
7540}
7541
7542/*------------------------------------------------------------------*/
7543/*
7544 * Structures to export the Wireless Handlers
7545 */
7546
7547static const struct iw_priv_args airo_private_args[] = {
7548/*{ cmd, set_args, get_args, name } */
7549 { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
7550 IW_PRIV_TYPE_BYTE | 2047, "airoioctl" },
7551 { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
7552 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" },
7553};
7554
7555static const iw_handler airo_handler[] =
7556{
7557 (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */
7558 (iw_handler) airo_get_name, /* SIOCGIWNAME */
7559 (iw_handler) NULL, /* SIOCSIWNWID */
7560 (iw_handler) NULL, /* SIOCGIWNWID */
7561 (iw_handler) airo_set_freq, /* SIOCSIWFREQ */
7562 (iw_handler) airo_get_freq, /* SIOCGIWFREQ */
7563 (iw_handler) airo_set_mode, /* SIOCSIWMODE */
7564 (iw_handler) airo_get_mode, /* SIOCGIWMODE */
7565 (iw_handler) airo_set_sens, /* SIOCSIWSENS */
7566 (iw_handler) airo_get_sens, /* SIOCGIWSENS */
7567 (iw_handler) NULL, /* SIOCSIWRANGE */
7568 (iw_handler) airo_get_range, /* SIOCGIWRANGE */
7569 (iw_handler) NULL, /* SIOCSIWPRIV */
7570 (iw_handler) NULL, /* SIOCGIWPRIV */
7571 (iw_handler) NULL, /* SIOCSIWSTATS */
7572 (iw_handler) NULL, /* SIOCGIWSTATS */
7573 iw_handler_set_spy, /* SIOCSIWSPY */
7574 iw_handler_get_spy, /* SIOCGIWSPY */
7575 iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
7576 iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
7577 (iw_handler) airo_set_wap, /* SIOCSIWAP */
7578 (iw_handler) airo_get_wap, /* SIOCGIWAP */
7579 (iw_handler) NULL, /* -- hole -- */
7580 (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */
7581 (iw_handler) airo_set_scan, /* SIOCSIWSCAN */
7582 (iw_handler) airo_get_scan, /* SIOCGIWSCAN */
7583 (iw_handler) airo_set_essid, /* SIOCSIWESSID */
7584 (iw_handler) airo_get_essid, /* SIOCGIWESSID */
7585 (iw_handler) airo_set_nick, /* SIOCSIWNICKN */
7586 (iw_handler) airo_get_nick, /* SIOCGIWNICKN */
7587 (iw_handler) NULL, /* -- hole -- */
7588 (iw_handler) NULL, /* -- hole -- */
7589 (iw_handler) airo_set_rate, /* SIOCSIWRATE */
7590 (iw_handler) airo_get_rate, /* SIOCGIWRATE */
7591 (iw_handler) airo_set_rts, /* SIOCSIWRTS */
7592 (iw_handler) airo_get_rts, /* SIOCGIWRTS */
7593 (iw_handler) airo_set_frag, /* SIOCSIWFRAG */
7594 (iw_handler) airo_get_frag, /* SIOCGIWFRAG */
7595 (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */
7596 (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */
7597 (iw_handler) airo_set_retry, /* SIOCSIWRETRY */
7598 (iw_handler) airo_get_retry, /* SIOCGIWRETRY */
7599 (iw_handler) airo_set_encode, /* SIOCSIWENCODE */
7600 (iw_handler) airo_get_encode, /* SIOCGIWENCODE */
7601 (iw_handler) airo_set_power, /* SIOCSIWPOWER */
7602 (iw_handler) airo_get_power, /* SIOCGIWPOWER */
Dan Williams4be757d2006-01-30 11:58:00 -05007603 (iw_handler) NULL, /* -- hole -- */
7604 (iw_handler) NULL, /* -- hole -- */
7605 (iw_handler) NULL, /* SIOCSIWGENIE */
7606 (iw_handler) NULL, /* SIOCGIWGENIE */
7607 (iw_handler) airo_set_auth, /* SIOCSIWAUTH */
7608 (iw_handler) airo_get_auth, /* SIOCGIWAUTH */
7609 (iw_handler) airo_set_encodeext, /* SIOCSIWENCODEEXT */
7610 (iw_handler) airo_get_encodeext, /* SIOCGIWENCODEEXT */
7611 (iw_handler) NULL, /* SIOCSIWPMKSA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07007612};
7613
7614/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here.
7615 * We want to force the use of the ioctl code, because those can't be
7616 * won't work the iw_handler code (because they simultaneously read
7617 * and write data and iw_handler can't do that).
7618 * Note that it's perfectly legal to read/write on a single ioctl command,
7619 * you just can't use iwpriv and need to force it via the ioctl handler.
7620 * Jean II */
7621static const iw_handler airo_private_handler[] =
7622{
7623 NULL, /* SIOCIWFIRSTPRIV */
7624};
7625
7626static const struct iw_handler_def airo_handler_def =
7627{
Denis Chengff8ac602007-09-02 18:30:18 +08007628 .num_standard = ARRAY_SIZE(airo_handler),
7629 .num_private = ARRAY_SIZE(airo_private_handler),
7630 .num_private_args = ARRAY_SIZE(airo_private_args),
Linus Torvalds1da177e2005-04-16 15:20:36 -07007631 .standard = airo_handler,
7632 .private = airo_private_handler,
7633 .private_args = airo_private_args,
7634 .get_wireless_stats = airo_get_wireless_stats,
7635};
7636
Linus Torvalds1da177e2005-04-16 15:20:36 -07007637/*
7638 * This defines the configuration part of the Wireless Extensions
7639 * Note : irq and spinlock protection will occur in the subroutines
7640 *
7641 * TODO :
7642 * o Check input value more carefully and fill correct values in range
7643 * o Test and shakeout the bugs (if any)
7644 *
7645 * Jean II
7646 *
7647 * Javier Achirica did a great job of merging code from the unnamed CISCO
7648 * developer that added support for flashing the card.
7649 */
7650static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
7651{
7652 int rc = 0;
Wang Chenfaf39942008-10-14 13:30:33 +08007653 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007654
Pavel Machekca078ba2005-09-03 15:56:57 -07007655 if (ai->power.event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007656 return 0;
7657
7658 switch (cmd) {
7659#ifdef CISCO_EXT
7660 case AIROIDIFC:
7661#ifdef AIROOLDIDIFC
7662 case AIROOLDIDIFC:
7663#endif
7664 {
7665 int val = AIROMAGIC;
7666 aironet_ioctl com;
Lee Jonesba4d6512020-08-14 12:39:15 +01007667 if (copy_from_user(&com, rq->ifr_data, sizeof(com)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007668 rc = -EFAULT;
Lee Jonesba4d6512020-08-14 12:39:15 +01007669 else if (copy_to_user(com.data, (char *)&val, sizeof(val)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007670 rc = -EFAULT;
7671 }
7672 break;
7673
7674 case AIROIOCTL:
7675#ifdef AIROOLDIOCTL
7676 case AIROOLDIOCTL:
7677#endif
7678 /* Get the command struct and hand it off for evaluation by
7679 * the proper subfunction
7680 */
7681 {
7682 aironet_ioctl com;
Lee Jonesba4d6512020-08-14 12:39:15 +01007683 if (copy_from_user(&com, rq->ifr_data, sizeof(com))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007684 rc = -EFAULT;
7685 break;
7686 }
7687
7688 /* Separate R/W functions bracket legality here
7689 */
Lee Jonesba4d6512020-08-14 12:39:15 +01007690 if (com.command == AIRORSWVERSION) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007691 if (copy_to_user(com.data, swversion, sizeof(swversion)))
7692 rc = -EFAULT;
7693 else
7694 rc = 0;
7695 }
Lee Jonesba4d6512020-08-14 12:39:15 +01007696 else if (com.command <= AIRORRID)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007697 rc = readrids(dev,&com);
Lee Jonesba4d6512020-08-14 12:39:15 +01007698 else if (com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007699 rc = writerids(dev,&com);
Lee Jonesba4d6512020-08-14 12:39:15 +01007700 else if (com.command >= AIROFLSHRST && com.command <= AIRORESTART)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007701 rc = flashcard(dev,&com);
7702 else
7703 rc = -EINVAL; /* Bad command in ioctl */
7704 }
7705 break;
7706#endif /* CISCO_EXT */
7707
7708 // All other calls are currently unsupported
7709 default:
7710 rc = -EOPNOTSUPP;
7711 }
7712 return rc;
7713}
7714
Linus Torvalds1da177e2005-04-16 15:20:36 -07007715/*
7716 * Get the Wireless stats out of the driver
7717 * Note : irq and spinlock protection will occur in the subroutines
7718 *
7719 * TODO :
7720 * o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs)
7721 *
7722 * Jean
7723 */
7724static void airo_read_wireless_stats(struct airo_info *local)
7725{
7726 StatusRid status_rid;
7727 StatsRid stats_rid;
7728 CapabilityRid cap_rid;
Al Viroa23ace52007-12-19 22:24:16 -05007729 __le32 *vals = stats_rid.vals;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007730
7731 /* Get stats out of the card */
Dan Williams3c304952006-04-15 12:26:18 -04007732 clear_bit(JOB_WSTATS, &local->jobs);
Pavel Machekca078ba2005-09-03 15:56:57 -07007733 if (local->power.event) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007734 up(&local->sem);
7735 return;
7736 }
7737 readCapabilityRid(local, &cap_rid, 0);
7738 readStatusRid(local, &status_rid, 0);
7739 readStatsRid(local, &stats_rid, RID_STATS, 0);
7740 up(&local->sem);
7741
7742 /* The status */
Al Viro329e2c02007-12-20 22:58:57 -05007743 local->wstats.status = le16_to_cpu(status_rid.mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007744
Dan Williams41480af2005-05-10 09:45:51 -04007745 /* Signal quality and co */
7746 if (local->rssi) {
Al Viro329e2c02007-12-20 22:58:57 -05007747 local->wstats.qual.level =
7748 airo_rssi_to_dbm(local->rssi,
7749 le16_to_cpu(status_rid.sigQuality));
Dan Williams41480af2005-05-10 09:45:51 -04007750 /* normalizedSignalStrength appears to be a percentage */
Al Viro329e2c02007-12-20 22:58:57 -05007751 local->wstats.qual.qual =
7752 le16_to_cpu(status_rid.normalizedSignalStrength);
Dan Williams41480af2005-05-10 09:45:51 -04007753 } else {
Al Viro329e2c02007-12-20 22:58:57 -05007754 local->wstats.qual.level =
7755 (le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2;
Dan Williams41480af2005-05-10 09:45:51 -04007756 local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid);
7757 }
Al Viro329e2c02007-12-20 22:58:57 -05007758 if (le16_to_cpu(status_rid.len) >= 124) {
Dan Williams41480af2005-05-10 09:45:51 -04007759 local->wstats.qual.noise = 0x100 - status_rid.noisedBm;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007760 local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007761 } else {
7762 local->wstats.qual.noise = 0;
Jean Tourrilhesce6623c2005-09-02 11:45:10 -07007763 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 -07007764 }
7765
7766 /* Packets discarded in the wireless adapter due to wireless
7767 * specific problems */
Al Viroa23ace52007-12-19 22:24:16 -05007768 local->wstats.discard.nwid = le32_to_cpu(vals[56]) +
7769 le32_to_cpu(vals[57]) +
7770 le32_to_cpu(vals[58]); /* SSID Mismatch */
7771 local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */
7772 local->wstats.discard.fragment = le32_to_cpu(vals[30]);
7773 local->wstats.discard.retries = le32_to_cpu(vals[10]);
7774 local->wstats.discard.misc = le32_to_cpu(vals[1]) +
7775 le32_to_cpu(vals[32]);
7776 local->wstats.miss.beacon = le32_to_cpu(vals[34]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007777}
7778
Jouni Malinenff1d2762005-05-12 22:54:16 -04007779static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007780{
Wang Chenfaf39942008-10-14 13:30:33 +08007781 struct airo_info *local = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007782
Dan Williams3c304952006-04-15 12:26:18 -04007783 if (!test_bit(JOB_WSTATS, &local->jobs)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007784 /* Get stats out of the card if available */
7785 if (down_trylock(&local->sem) != 0) {
Dan Williams3c304952006-04-15 12:26:18 -04007786 set_bit(JOB_WSTATS, &local->jobs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007787 wake_up_interruptible(&local->thr_wait);
7788 } else
7789 airo_read_wireless_stats(local);
7790 }
7791
7792 return &local->wstats;
7793}
Linus Torvalds1da177e2005-04-16 15:20:36 -07007794
7795#ifdef CISCO_EXT
7796/*
7797 * This just translates from driver IOCTL codes to the command codes to
7798 * feed to the radio's host interface. Things can be added/deleted
7799 * as needed. This represents the READ side of control I/O to
7800 * the card
7801 */
Lee Jonesba4d6512020-08-14 12:39:15 +01007802static int readrids(struct net_device *dev, aironet_ioctl *comp)
7803{
Linus Torvalds1da177e2005-04-16 15:20:36 -07007804 unsigned short ridcode;
7805 unsigned char *iobuf;
7806 int len;
Wang Chenfaf39942008-10-14 13:30:33 +08007807 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007808
7809 if (test_bit(FLAG_FLASHING, &ai->flags))
7810 return -EIO;
7811
7812 switch(comp->command)
7813 {
7814 case AIROGCAP: ridcode = RID_CAPABILITIES; break;
7815 case AIROGCFG: ridcode = RID_CONFIG;
7816 if (test_bit(FLAG_COMMIT, &ai->flags)) {
7817 disable_MAC (ai, 1);
7818 writeConfigRid (ai, 1);
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007819 enable_MAC(ai, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007820 }
7821 break;
7822 case AIROGSLIST: ridcode = RID_SSID; break;
7823 case AIROGVLIST: ridcode = RID_APLIST; break;
7824 case AIROGDRVNAM: ridcode = RID_DRVNAME; break;
7825 case AIROGEHTENC: ridcode = RID_ETHERENCAP; break;
Michael Ellerman78f7a752020-01-22 15:07:28 +11007826 case AIROGWEPKTMP: ridcode = RID_WEP_TEMP; break;
7827 case AIROGWEPKNV: ridcode = RID_WEP_PERM; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007828 case AIROGSTAT: ridcode = RID_STATUS; break;
7829 case AIROGSTATSD32: ridcode = RID_STATSDELTA; break;
7830 case AIROGSTATSC32: ridcode = RID_STATS; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007831 case AIROGMICSTATS:
7832 if (copy_to_user(comp->data, &ai->micstats,
Lee Jonesba4d6512020-08-14 12:39:15 +01007833 min((int)comp->len, (int)sizeof(ai->micstats))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07007834 return -EFAULT;
7835 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007836 case AIRORRID: ridcode = comp->ridnum; break;
7837 default:
7838 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007839 }
7840
Michael Ellerman78f7a752020-01-22 15:07:28 +11007841 if (ridcode == RID_WEP_TEMP || ridcode == RID_WEP_PERM) {
7842 /* Only super-user can read WEP keys */
7843 if (!capable(CAP_NET_ADMIN))
7844 return -EPERM;
7845 }
7846
Michael Ellermand6bce212020-01-22 15:07:27 +11007847 if ((iobuf = kzalloc(RIDSIZE, GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007848 return -ENOMEM;
7849
Lee Jonesba4d6512020-08-14 12:39:15 +01007850 PC4500_readrid(ai, ridcode, iobuf, RIDSIZE, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007851 /* get the count of bytes in the rid docs say 1st 2 bytes is it.
7852 * then return it to the user
7853 * 9/22/2000 Honor user given length
7854 */
7855 len = comp->len;
7856
7857 if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) {
7858 kfree (iobuf);
7859 return -EFAULT;
7860 }
7861 kfree (iobuf);
7862 return 0;
7863}
7864
7865/*
7866 * Danger Will Robinson write the rids here
7867 */
7868
Lee Jonesba4d6512020-08-14 12:39:15 +01007869static int writerids(struct net_device *dev, aironet_ioctl *comp)
7870{
Wang Chenfaf39942008-10-14 13:30:33 +08007871 struct airo_info *ai = dev->ml_priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007872 int ridcode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007873 int enabled;
Gustavo A. R. Silva06548fb2017-07-18 15:37:11 -05007874 int (*writer)(struct airo_info *, u16 rid, const void *, int, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007875 unsigned char *iobuf;
7876
7877 /* Only super-user can write RIDs */
7878 if (!capable(CAP_NET_ADMIN))
7879 return -EPERM;
7880
7881 if (test_bit(FLAG_FLASHING, &ai->flags))
7882 return -EIO;
7883
7884 ridcode = 0;
7885 writer = do_writerid;
7886
7887 switch(comp->command)
7888 {
7889 case AIROPSIDS: ridcode = RID_SSID; break;
7890 case AIROPCAP: ridcode = RID_CAPABILITIES; break;
7891 case AIROPAPLIST: ridcode = RID_APLIST; break;
7892 case AIROPCFG: ai->config.len = 0;
7893 clear_bit(FLAG_COMMIT, &ai->flags);
7894 ridcode = RID_CONFIG; break;
7895 case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break;
7896 case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break;
7897 case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break;
7898 case AIROPWEPKEY: ridcode = RID_WEP_TEMP; writer = PC4500_writerid;
7899 break;
7900 case AIROPLEAPUSR+1: ridcode = 0xFF2A; break;
7901 case AIROPLEAPUSR+2: ridcode = 0xFF2B; break;
7902
7903 /* this is not really a rid but a command given to the card
7904 * same with MAC off
7905 */
7906 case AIROPMACON:
Michal Schmidt175ec1a2007-06-29 15:33:47 +02007907 if (enable_MAC(ai, 1) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007908 return -EIO;
7909 return 0;
7910
7911 /*
7912 * Evidently this code in the airo driver does not get a symbol
7913 * as disable_MAC. it's probably so short the compiler does not gen one.
7914 */
7915 case AIROPMACOFF:
7916 disable_MAC(ai, 1);
7917 return 0;
7918
7919 /* This command merely clears the counts does not actually store any data
7920 * only reads rid. But as it changes the cards state, I put it in the
7921 * writerid routines.
7922 */
7923 case AIROPSTCLR:
7924 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7925 return -ENOMEM;
7926
Lee Jonesba4d6512020-08-14 12:39:15 +01007927 PC4500_readrid(ai, RID_STATSDELTACLEAR, iobuf, RIDSIZE, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007928
Linus Torvalds1da177e2005-04-16 15:20:36 -07007929 enabled = ai->micstats.enabled;
Lee Jonesba4d6512020-08-14 12:39:15 +01007930 memset(&ai->micstats, 0, sizeof(ai->micstats));
Linus Torvalds1da177e2005-04-16 15:20:36 -07007931 ai->micstats.enabled = enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007932
7933 if (copy_to_user(comp->data, iobuf,
7934 min((int)comp->len, (int)RIDSIZE))) {
7935 kfree (iobuf);
7936 return -EFAULT;
7937 }
7938 kfree (iobuf);
7939 return 0;
7940
7941 default:
7942 return -EOPNOTSUPP; /* Blarg! */
7943 }
Lee Jonesba4d6512020-08-14 12:39:15 +01007944 if (comp->len > RIDSIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007945 return -EINVAL;
7946
7947 if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
7948 return -ENOMEM;
7949
Lee Jonesba4d6512020-08-14 12:39:15 +01007950 if (copy_from_user(iobuf, comp->data, comp->len)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007951 kfree (iobuf);
7952 return -EFAULT;
7953 }
7954
7955 if (comp->command == AIROPCFG) {
7956 ConfigRid *cfg = (ConfigRid *)iobuf;
7957
7958 if (test_bit(FLAG_MIC_CAPABLE, &ai->flags))
Al Viro3eb9b412007-12-21 00:00:35 -05007959 cfg->opmode |= MODE_MIC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007960
Al Viro3eb9b412007-12-21 00:00:35 -05007961 if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007962 set_bit (FLAG_ADHOC, &ai->flags);
7963 else
7964 clear_bit (FLAG_ADHOC, &ai->flags);
7965 }
7966
Lee Jonesba4d6512020-08-14 12:39:15 +01007967 if ((*writer)(ai, ridcode, iobuf, comp->len, 1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07007968 kfree (iobuf);
7969 return -EIO;
7970 }
7971 kfree (iobuf);
7972 return 0;
7973}
7974
7975/*****************************************************************************
7976 * Ancillary flash / mod functions much black magic lurkes here *
7977 *****************************************************************************
7978 */
7979
7980/*
7981 * Flash command switch table
7982 */
7983
Lee Jonesba4d6512020-08-14 12:39:15 +01007984static int flashcard(struct net_device *dev, aironet_ioctl *comp)
7985{
Linus Torvalds1da177e2005-04-16 15:20:36 -07007986 int z;
Linus Torvalds1da177e2005-04-16 15:20:36 -07007987
7988 /* Only super-user can modify flash */
7989 if (!capable(CAP_NET_ADMIN))
7990 return -EPERM;
7991
7992 switch(comp->command)
7993 {
7994 case AIROFLSHRST:
Wang Chenfaf39942008-10-14 13:30:33 +08007995 return cmdreset((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07007996
7997 case AIROFLSHSTFL:
Wang Chenfaf39942008-10-14 13:30:33 +08007998 if (!AIRO_FLASH(dev) &&
7999 (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07008000 return -ENOMEM;
Wang Chenfaf39942008-10-14 13:30:33 +08008001 return setflashmode((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008002
8003 case AIROFLSHGCHR: /* Get char from aux */
Lee Jonesba4d6512020-08-14 12:39:15 +01008004 if (comp->len != sizeof(int))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008005 return -EINVAL;
Lee Jonesba4d6512020-08-14 12:39:15 +01008006 if (copy_from_user(&z, comp->data, comp->len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008007 return -EFAULT;
Wang Chenfaf39942008-10-14 13:30:33 +08008008 return flashgchar((struct airo_info *)dev->ml_priv, z, 8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008009
8010 case AIROFLSHPCHR: /* Send char to card. */
Lee Jonesba4d6512020-08-14 12:39:15 +01008011 if (comp->len != sizeof(int))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008012 return -EINVAL;
Lee Jonesba4d6512020-08-14 12:39:15 +01008013 if (copy_from_user(&z, comp->data, comp->len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008014 return -EFAULT;
Wang Chenfaf39942008-10-14 13:30:33 +08008015 return flashpchar((struct airo_info *)dev->ml_priv, z, 8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008016
8017 case AIROFLPUTBUF: /* Send 32k to card */
Wang Chenfaf39942008-10-14 13:30:33 +08008018 if (!AIRO_FLASH(dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008019 return -ENOMEM;
Lee Jonesba4d6512020-08-14 12:39:15 +01008020 if (comp->len > FLASHSIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07008021 return -EINVAL;
Wang Chenfaf39942008-10-14 13:30:33 +08008022 if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008023 return -EFAULT;
8024
Wang Chenfaf39942008-10-14 13:30:33 +08008025 flashputbuf((struct airo_info *)dev->ml_priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008026 return 0;
8027
8028 case AIRORESTART:
Wang Chenfaf39942008-10-14 13:30:33 +08008029 if (flashrestart((struct airo_info *)dev->ml_priv, dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07008030 return -EIO;
8031 return 0;
8032 }
8033 return -EINVAL;
8034}
8035
8036#define FLASH_COMMAND 0x7e7e
8037
8038/*
8039 * STEP 1)
8040 * Disable MAC and do soft reset on
8041 * card.
8042 */
8043
Lee Jonesba4d6512020-08-14 12:39:15 +01008044static int cmdreset(struct airo_info *ai)
8045{
Linus Torvalds1da177e2005-04-16 15:20:36 -07008046 disable_MAC(ai, 1);
8047
Lee Jonesba4d6512020-08-14 12:39:15 +01008048 if (!waitbusy (ai)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05008049 airo_print_info(ai->dev->name, "Waitbusy hang before RESET");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008050 return -EBUSY;
8051 }
8052
Lee Jonesba4d6512020-08-14 12:39:15 +01008053 OUT4500(ai, COMMAND, CMD_SOFTRESET);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008054
8055 ssleep(1); /* WAS 600 12/7/00 */
8056
Lee Jonesba4d6512020-08-14 12:39:15 +01008057 if (!waitbusy (ai)) {
Dan Williams934d8bf2006-03-16 13:46:23 -05008058 airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008059 return -EBUSY;
8060 }
8061 return 0;
8062}
8063
8064/* STEP 2)
8065 * Put the card in legendary flash
8066 * mode
8067 */
8068
Lee Jonesba4d6512020-08-14 12:39:15 +01008069static int setflashmode (struct airo_info *ai)
8070{
Linus Torvalds1da177e2005-04-16 15:20:36 -07008071 set_bit (FLAG_FLASHING, &ai->flags);
8072
8073 OUT4500(ai, SWS0, FLASH_COMMAND);
8074 OUT4500(ai, SWS1, FLASH_COMMAND);
8075 if (probe) {
8076 OUT4500(ai, SWS0, FLASH_COMMAND);
Lee Jonesba4d6512020-08-14 12:39:15 +01008077 OUT4500(ai, COMMAND, 0x10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008078 } else {
8079 OUT4500(ai, SWS2, FLASH_COMMAND);
8080 OUT4500(ai, SWS3, FLASH_COMMAND);
Lee Jonesba4d6512020-08-14 12:39:15 +01008081 OUT4500(ai, COMMAND, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008082 }
8083 msleep(500); /* 500ms delay */
8084
Lee Jonesba4d6512020-08-14 12:39:15 +01008085 if (!waitbusy(ai)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07008086 clear_bit (FLAG_FLASHING, &ai->flags);
Dan Williams934d8bf2006-03-16 13:46:23 -05008087 airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008088 return -EIO;
8089 }
8090 return 0;
8091}
8092
8093/* Put character to SWS0 wait for dwelltime
8094 * x 50us for echo .
8095 */
8096
Lee Jonesba4d6512020-08-14 12:39:15 +01008097static int flashpchar(struct airo_info *ai, int byte, int dwelltime)
8098{
Linus Torvalds1da177e2005-04-16 15:20:36 -07008099 int echo;
8100 int waittime;
8101
8102 byte |= 0x8000;
8103
Lee Jonesba4d6512020-08-14 12:39:15 +01008104 if (dwelltime == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07008105 dwelltime = 200;
8106
Lee Jonesba4d6512020-08-14 12:39:15 +01008107 waittime = dwelltime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07008108
8109 /* Wait for busy bit d15 to go false indicating buffer empty */
8110 while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) {
8111 udelay (50);
8112 waittime -= 50;
8113 }
8114
8115 /* timeout for busy clear wait */
Lee Jonesba4d6512020-08-14 12:39:15 +01008116 if (waittime <= 0) {
Dan Williams934d8bf2006-03-16 13:46:23 -05008117 airo_print_info(ai->dev->name, "flash putchar busywait timeout!");
Linus Torvalds1da177e2005-04-16 15:20:36 -07008118 return -EBUSY;
8119 }
8120
8121 /* Port is clear now write byte and wait for it to echo back */
8122 do {
Lee Jonesba4d6512020-08-14 12:39:15 +01008123 OUT4500(ai, SWS0, byte);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008124 udelay(50);
8125 dwelltime -= 50;
Lee Jonesba4d6512020-08-14 12:39:15 +01008126 echo = IN4500(ai, SWS1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008127 } while (dwelltime >= 0 && echo != byte);
8128
Lee Jonesba4d6512020-08-14 12:39:15 +01008129 OUT4500(ai, SWS1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008130
8131 return (echo == byte) ? 0 : -EIO;
8132}
8133
8134/*
8135 * Get a character from the card matching matchbyte
8136 * Step 3)
8137 */
Lee Jonesba4d6512020-08-14 12:39:15 +01008138static int flashgchar(struct airo_info *ai, int matchbyte, int dwelltime)
8139{
Linus Torvalds1da177e2005-04-16 15:20:36 -07008140 int rchar;
Lee Jonesba4d6512020-08-14 12:39:15 +01008141 unsigned char rbyte = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07008142
8143 do {
Lee Jonesba4d6512020-08-14 12:39:15 +01008144 rchar = IN4500(ai, SWS1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008145
Lee Jonesba4d6512020-08-14 12:39:15 +01008146 if (dwelltime && !(0x8000 & rchar)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07008147 dwelltime -= 10;
8148 mdelay(10);
8149 continue;
8150 }
8151 rbyte = 0xff & rchar;
8152
Lee Jonesba4d6512020-08-14 12:39:15 +01008153 if ((rbyte == matchbyte) && (0x8000 & rchar)) {
8154 OUT4500(ai, SWS1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008155 return 0;
8156 }
Lee Jonesba4d6512020-08-14 12:39:15 +01008157 if (rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar)
Linus Torvalds1da177e2005-04-16 15:20:36 -07008158 break;
Lee Jonesba4d6512020-08-14 12:39:15 +01008159 OUT4500(ai, SWS1, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008160
Lee Jonesba4d6512020-08-14 12:39:15 +01008161 } while (dwelltime > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008162 return -EIO;
8163}
8164
8165/*
8166 * Transfer 32k of firmware data from user buffer to our buffer and
8167 * send to the card
8168 */
8169
Lee Jonesba4d6512020-08-14 12:39:15 +01008170static int flashputbuf(struct airo_info *ai)
8171{
Linus Torvalds1da177e2005-04-16 15:20:36 -07008172 int nwords;
8173
8174 /* Write stuff */
8175 if (test_bit(FLAG_MPI,&ai->flags))
8176 memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE);
8177 else {
Lee Jonesba4d6512020-08-14 12:39:15 +01008178 OUT4500(ai, AUXPAGE, 0x100);
8179 OUT4500(ai, AUXOFF, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008180
Lee Jonesba4d6512020-08-14 12:39:15 +01008181 for (nwords = 0; nwords != FLASHSIZE / 2; nwords++) {
8182 OUT4500(ai, AUXDATA, ai->flash[nwords] & 0xffff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008183 }
8184 }
Lee Jonesba4d6512020-08-14 12:39:15 +01008185 OUT4500(ai, SWS0, 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008186
8187 return 0;
8188}
8189
8190/*
8191 *
8192 */
Lee Jonesba4d6512020-08-14 12:39:15 +01008193static int flashrestart(struct airo_info *ai, struct net_device *dev)
8194{
8195 int i, status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07008196
8197 ssleep(1); /* Added 12/7/00 */
8198 clear_bit (FLAG_FLASHING, &ai->flags);
8199 if (test_bit(FLAG_MPI, &ai->flags)) {
8200 status = mpi_init_descriptors(ai);
8201 if (status != SUCCESS)
8202 return status;
8203 }
8204 status = setup_card(ai, dev->dev_addr, 1);
8205
8206 if (!test_bit(FLAG_MPI,&ai->flags))
Lee Jonesba4d6512020-08-14 12:39:15 +01008207 for (i = 0; i < MAX_FIDS; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07008208 ai->fids[i] = transmit_allocate
Lee Jonesba4d6512020-08-14 12:39:15 +01008209 (ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07008210 }
8211
8212 ssleep(1); /* Added 12/7/00 */
8213 return status;
8214}
8215#endif /* CISCO_EXT */
8216
8217/*
8218 This program is free software; you can redistribute it and/or
8219 modify it under the terms of the GNU General Public License
8220 as published by the Free Software Foundation; either version 2
8221 of the License, or (at your option) any later version.
8222
8223 This program is distributed in the hope that it will be useful,
8224 but WITHOUT ANY WARRANTY; without even the implied warranty of
8225 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8226 GNU General Public License for more details.
8227
8228 In addition:
8229
8230 Redistribution and use in source and binary forms, with or without
8231 modification, are permitted provided that the following conditions
8232 are met:
8233
8234 1. Redistributions of source code must retain the above copyright
8235 notice, this list of conditions and the following disclaimer.
8236 2. Redistributions in binary form must reproduce the above copyright
8237 notice, this list of conditions and the following disclaimer in the
8238 documentation and/or other materials provided with the distribution.
8239 3. The name of the author may not be used to endorse or promote
8240 products derived from this software without specific prior written
8241 permission.
8242
8243 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
8244 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8245 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
8246 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
8247 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
8248 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
8249 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
8250 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
8251 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
8252 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
8253 POSSIBILITY OF SUCH DAMAGE.
8254*/
8255
8256module_init(airo_init_module);
8257module_exit(airo_cleanup_module);