xref: /openbmc/linux/drivers/net/wireless/cisco/airo.c (revision 49eb0199)
130db0ca8SKalle Valo /*======================================================================
230db0ca8SKalle Valo 
330db0ca8SKalle Valo     Aironet driver for 4500 and 4800 series cards
430db0ca8SKalle Valo 
530db0ca8SKalle Valo     This code is released under both the GPL version 2 and BSD licenses.
630db0ca8SKalle Valo     Either license may be used.  The respective licenses are found at
730db0ca8SKalle Valo     the end of this file.
830db0ca8SKalle Valo 
930db0ca8SKalle Valo     This code was developed by Benjamin Reed <breed@users.sourceforge.net>
1030db0ca8SKalle Valo     including portions of which come from the Aironet PC4500
1130db0ca8SKalle Valo     Developer's Reference Manual and used with permission.  Copyright
1230db0ca8SKalle Valo     (C) 1999 Benjamin Reed.  All Rights Reserved.  Permission to use
1330db0ca8SKalle Valo     code in the Developer's manual was granted for this driver by
1430db0ca8SKalle Valo     Aironet.  Major code contributions were received from Javier Achirica
1530db0ca8SKalle Valo     <achirica@users.sourceforge.net> and Jean Tourrilhes <jt@hpl.hp.com>.
1630db0ca8SKalle Valo     Code was also integrated from the Cisco Aironet driver for Linux.
1730db0ca8SKalle Valo     Support for MPI350 cards was added by Fabrice Bellet
1830db0ca8SKalle Valo     <fabrice@bellet.info>.
1930db0ca8SKalle Valo 
2030db0ca8SKalle Valo ======================================================================*/
2130db0ca8SKalle Valo 
2230db0ca8SKalle Valo #include <linux/err.h>
2330db0ca8SKalle Valo #include <linux/init.h>
2430db0ca8SKalle Valo 
2530db0ca8SKalle Valo #include <linux/kernel.h>
2630db0ca8SKalle Valo #include <linux/module.h>
2730db0ca8SKalle Valo #include <linux/proc_fs.h>
2830db0ca8SKalle Valo 
2930db0ca8SKalle Valo #include <linux/sched.h>
3030db0ca8SKalle Valo #include <linux/ptrace.h>
3130db0ca8SKalle Valo #include <linux/slab.h>
3230db0ca8SKalle Valo #include <linux/string.h>
3330db0ca8SKalle Valo #include <linux/timer.h>
3430db0ca8SKalle Valo #include <linux/interrupt.h>
3530db0ca8SKalle Valo #include <linux/in.h>
3630db0ca8SKalle Valo #include <linux/bitops.h>
3730db0ca8SKalle Valo #include <linux/scatterlist.h>
3830db0ca8SKalle Valo #include <linux/crypto.h>
3930db0ca8SKalle Valo #include <linux/io.h>
4030db0ca8SKalle Valo #include <asm/unaligned.h>
4130db0ca8SKalle Valo 
4230db0ca8SKalle Valo #include <linux/netdevice.h>
4330db0ca8SKalle Valo #include <linux/etherdevice.h>
4430db0ca8SKalle Valo #include <linux/skbuff.h>
4530db0ca8SKalle Valo #include <linux/if_arp.h>
4630db0ca8SKalle Valo #include <linux/ioport.h>
4730db0ca8SKalle Valo #include <linux/pci.h>
4830db0ca8SKalle Valo #include <linux/uaccess.h>
4930db0ca8SKalle Valo #include <linux/kthread.h>
5030db0ca8SKalle Valo #include <linux/freezer.h>
5130db0ca8SKalle Valo 
52e5db0ad7SArd Biesheuvel #include <crypto/aes.h>
53e5db0ad7SArd Biesheuvel #include <crypto/skcipher.h>
54e5db0ad7SArd Biesheuvel 
5530db0ca8SKalle Valo #include <net/cfg80211.h>
5630db0ca8SKalle Valo #include <net/iw_handler.h>
5730db0ca8SKalle Valo 
5830db0ca8SKalle Valo #include "airo.h"
5930db0ca8SKalle Valo 
6030db0ca8SKalle Valo #define DRV_NAME "airo"
6130db0ca8SKalle Valo 
6230db0ca8SKalle Valo #ifdef CONFIG_PCI
6330db0ca8SKalle Valo static const struct pci_device_id card_ids[] = {
6430db0ca8SKalle Valo 	{ 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, },
6530db0ca8SKalle Valo 	{ 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID },
6630db0ca8SKalle Valo 	{ 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, },
6730db0ca8SKalle Valo 	{ 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, },
6830db0ca8SKalle Valo 	{ 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, },
6930db0ca8SKalle Valo 	{ 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, },
7030db0ca8SKalle Valo 	{ 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, },
7130db0ca8SKalle Valo 	{ 0, }
7230db0ca8SKalle Valo };
7330db0ca8SKalle Valo MODULE_DEVICE_TABLE(pci, card_ids);
7430db0ca8SKalle Valo 
7530db0ca8SKalle Valo static int airo_pci_probe(struct pci_dev *, const struct pci_device_id *);
7630db0ca8SKalle Valo static void airo_pci_remove(struct pci_dev *);
77c3ab1804SVaibhav Gupta static int __maybe_unused airo_pci_suspend(struct device *dev);
78c3ab1804SVaibhav Gupta static int __maybe_unused airo_pci_resume(struct device *dev);
79c3ab1804SVaibhav Gupta 
80c3ab1804SVaibhav Gupta static SIMPLE_DEV_PM_OPS(airo_pci_pm_ops,
81c3ab1804SVaibhav Gupta 			 airo_pci_suspend,
82c3ab1804SVaibhav Gupta 			 airo_pci_resume);
8330db0ca8SKalle Valo 
8430db0ca8SKalle Valo static struct pci_driver airo_driver = {
8530db0ca8SKalle Valo 	.name      = DRV_NAME,
8630db0ca8SKalle Valo 	.id_table  = card_ids,
8730db0ca8SKalle Valo 	.probe     = airo_pci_probe,
8830db0ca8SKalle Valo 	.remove    = airo_pci_remove,
89c3ab1804SVaibhav Gupta 	.driver.pm = &airo_pci_pm_ops,
9030db0ca8SKalle Valo };
9130db0ca8SKalle Valo #endif /* CONFIG_PCI */
9230db0ca8SKalle Valo 
9330db0ca8SKalle Valo /* Include Wireless Extension definition and check version - Jean II */
9430db0ca8SKalle Valo #include <linux/wireless.h>
9530db0ca8SKalle Valo #define WIRELESS_SPY		/* enable iwspy support */
9630db0ca8SKalle Valo 
9730db0ca8SKalle Valo #define CISCO_EXT		/* enable Cisco extensions */
9830db0ca8SKalle Valo #ifdef CISCO_EXT
9930db0ca8SKalle Valo #include <linux/delay.h>
10030db0ca8SKalle Valo #endif
10130db0ca8SKalle Valo 
10230db0ca8SKalle Valo /* Hack to do some power saving */
10330db0ca8SKalle Valo #define POWER_ON_DOWN
10430db0ca8SKalle Valo 
10530db0ca8SKalle Valo /* As you can see this list is HUGH!
10630db0ca8SKalle Valo    I really don't know what a lot of these counts are about, but they
10730db0ca8SKalle Valo    are all here for completeness.  If the IGNLABEL macro is put in
10830db0ca8SKalle Valo    infront of the label, that statistic will not be included in the list
10930db0ca8SKalle Valo    of statistics in the /proc filesystem */
11030db0ca8SKalle Valo 
11130db0ca8SKalle Valo #define IGNLABEL(comment) NULL
11230db0ca8SKalle Valo static const char *statsLabels[] = {
11330db0ca8SKalle Valo 	"RxOverrun",
11430db0ca8SKalle Valo 	IGNLABEL("RxPlcpCrcErr"),
11530db0ca8SKalle Valo 	IGNLABEL("RxPlcpFormatErr"),
11630db0ca8SKalle Valo 	IGNLABEL("RxPlcpLengthErr"),
11730db0ca8SKalle Valo 	"RxMacCrcErr",
11830db0ca8SKalle Valo 	"RxMacCrcOk",
11930db0ca8SKalle Valo 	"RxWepErr",
12030db0ca8SKalle Valo 	"RxWepOk",
12130db0ca8SKalle Valo 	"RetryLong",
12230db0ca8SKalle Valo 	"RetryShort",
12330db0ca8SKalle Valo 	"MaxRetries",
12430db0ca8SKalle Valo 	"NoAck",
12530db0ca8SKalle Valo 	"NoCts",
12630db0ca8SKalle Valo 	"RxAck",
12730db0ca8SKalle Valo 	"RxCts",
12830db0ca8SKalle Valo 	"TxAck",
12930db0ca8SKalle Valo 	"TxRts",
13030db0ca8SKalle Valo 	"TxCts",
13130db0ca8SKalle Valo 	"TxMc",
13230db0ca8SKalle Valo 	"TxBc",
13330db0ca8SKalle Valo 	"TxUcFrags",
13430db0ca8SKalle Valo 	"TxUcPackets",
13530db0ca8SKalle Valo 	"TxBeacon",
13630db0ca8SKalle Valo 	"RxBeacon",
13730db0ca8SKalle Valo 	"TxSinColl",
13830db0ca8SKalle Valo 	"TxMulColl",
13930db0ca8SKalle Valo 	"DefersNo",
14030db0ca8SKalle Valo 	"DefersProt",
14130db0ca8SKalle Valo 	"DefersEngy",
14230db0ca8SKalle Valo 	"DupFram",
14330db0ca8SKalle Valo 	"RxFragDisc",
14430db0ca8SKalle Valo 	"TxAged",
14530db0ca8SKalle Valo 	"RxAged",
14630db0ca8SKalle Valo 	"LostSync-MaxRetry",
14730db0ca8SKalle Valo 	"LostSync-MissedBeacons",
14830db0ca8SKalle Valo 	"LostSync-ArlExceeded",
14930db0ca8SKalle Valo 	"LostSync-Deauth",
15030db0ca8SKalle Valo 	"LostSync-Disassoced",
15130db0ca8SKalle Valo 	"LostSync-TsfTiming",
15230db0ca8SKalle Valo 	"HostTxMc",
15330db0ca8SKalle Valo 	"HostTxBc",
15430db0ca8SKalle Valo 	"HostTxUc",
15530db0ca8SKalle Valo 	"HostTxFail",
15630db0ca8SKalle Valo 	"HostRxMc",
15730db0ca8SKalle Valo 	"HostRxBc",
15830db0ca8SKalle Valo 	"HostRxUc",
15930db0ca8SKalle Valo 	"HostRxDiscard",
16030db0ca8SKalle Valo 	IGNLABEL("HmacTxMc"),
16130db0ca8SKalle Valo 	IGNLABEL("HmacTxBc"),
16230db0ca8SKalle Valo 	IGNLABEL("HmacTxUc"),
16330db0ca8SKalle Valo 	IGNLABEL("HmacTxFail"),
16430db0ca8SKalle Valo 	IGNLABEL("HmacRxMc"),
16530db0ca8SKalle Valo 	IGNLABEL("HmacRxBc"),
16630db0ca8SKalle Valo 	IGNLABEL("HmacRxUc"),
16730db0ca8SKalle Valo 	IGNLABEL("HmacRxDiscard"),
16830db0ca8SKalle Valo 	IGNLABEL("HmacRxAccepted"),
16930db0ca8SKalle Valo 	"SsidMismatch",
17030db0ca8SKalle Valo 	"ApMismatch",
17130db0ca8SKalle Valo 	"RatesMismatch",
17230db0ca8SKalle Valo 	"AuthReject",
17330db0ca8SKalle Valo 	"AuthTimeout",
17430db0ca8SKalle Valo 	"AssocReject",
17530db0ca8SKalle Valo 	"AssocTimeout",
17630db0ca8SKalle Valo 	IGNLABEL("ReasonOutsideTable"),
17730db0ca8SKalle Valo 	IGNLABEL("ReasonStatus1"),
17830db0ca8SKalle Valo 	IGNLABEL("ReasonStatus2"),
17930db0ca8SKalle Valo 	IGNLABEL("ReasonStatus3"),
18030db0ca8SKalle Valo 	IGNLABEL("ReasonStatus4"),
18130db0ca8SKalle Valo 	IGNLABEL("ReasonStatus5"),
18230db0ca8SKalle Valo 	IGNLABEL("ReasonStatus6"),
18330db0ca8SKalle Valo 	IGNLABEL("ReasonStatus7"),
18430db0ca8SKalle Valo 	IGNLABEL("ReasonStatus8"),
18530db0ca8SKalle Valo 	IGNLABEL("ReasonStatus9"),
18630db0ca8SKalle Valo 	IGNLABEL("ReasonStatus10"),
18730db0ca8SKalle Valo 	IGNLABEL("ReasonStatus11"),
18830db0ca8SKalle Valo 	IGNLABEL("ReasonStatus12"),
18930db0ca8SKalle Valo 	IGNLABEL("ReasonStatus13"),
19030db0ca8SKalle Valo 	IGNLABEL("ReasonStatus14"),
19130db0ca8SKalle Valo 	IGNLABEL("ReasonStatus15"),
19230db0ca8SKalle Valo 	IGNLABEL("ReasonStatus16"),
19330db0ca8SKalle Valo 	IGNLABEL("ReasonStatus17"),
19430db0ca8SKalle Valo 	IGNLABEL("ReasonStatus18"),
19530db0ca8SKalle Valo 	IGNLABEL("ReasonStatus19"),
19630db0ca8SKalle Valo 	"RxMan",
19730db0ca8SKalle Valo 	"TxMan",
19830db0ca8SKalle Valo 	"RxRefresh",
19930db0ca8SKalle Valo 	"TxRefresh",
20030db0ca8SKalle Valo 	"RxPoll",
20130db0ca8SKalle Valo 	"TxPoll",
20230db0ca8SKalle Valo 	"HostRetries",
20330db0ca8SKalle Valo 	"LostSync-HostReq",
20430db0ca8SKalle Valo 	"HostTxBytes",
20530db0ca8SKalle Valo 	"HostRxBytes",
20630db0ca8SKalle Valo 	"ElapsedUsec",
20730db0ca8SKalle Valo 	"ElapsedSec",
20830db0ca8SKalle Valo 	"LostSyncBetterAP",
20930db0ca8SKalle Valo 	"PrivacyMismatch",
21030db0ca8SKalle Valo 	"Jammed",
21130db0ca8SKalle Valo 	"DiscRxNotWepped",
21230db0ca8SKalle Valo 	"PhyEleMismatch",
21330db0ca8SKalle Valo 	(char*)-1 };
21430db0ca8SKalle Valo #ifndef RUN_AT
21530db0ca8SKalle Valo #define RUN_AT(x) (jiffies+(x))
21630db0ca8SKalle Valo #endif
21730db0ca8SKalle Valo 
21830db0ca8SKalle Valo 
21930db0ca8SKalle Valo /* These variables are for insmod, since it seems that the rates
22030db0ca8SKalle Valo    can only be set in setup_card.  Rates should be a comma separated
22130db0ca8SKalle Valo    (no spaces) list of rates (up to 8). */
22230db0ca8SKalle Valo 
22330db0ca8SKalle Valo static int rates[8];
22430db0ca8SKalle Valo static char *ssids[3];
22530db0ca8SKalle Valo 
22630db0ca8SKalle Valo static int io[4];
22730db0ca8SKalle Valo static int irq[4];
22830db0ca8SKalle Valo 
22930db0ca8SKalle Valo static
23030db0ca8SKalle Valo int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at.
23130db0ca8SKalle Valo 		       0 means no limit.  For old cards this was 4 */
23230db0ca8SKalle Valo 
23330db0ca8SKalle Valo static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */
23430db0ca8SKalle Valo static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read
23530db0ca8SKalle Valo 		    the bap, needed on some older cards and buses. */
23630db0ca8SKalle Valo static int adhoc;
23730db0ca8SKalle Valo 
23830db0ca8SKalle Valo static int probe = 1;
23930db0ca8SKalle Valo 
24030db0ca8SKalle Valo static kuid_t proc_kuid;
24130db0ca8SKalle Valo static int proc_uid /* = 0 */;
24230db0ca8SKalle Valo 
24330db0ca8SKalle Valo static kgid_t proc_kgid;
24430db0ca8SKalle Valo static int proc_gid /* = 0 */;
24530db0ca8SKalle Valo 
24630db0ca8SKalle Valo static int airo_perm = 0555;
24730db0ca8SKalle Valo 
24830db0ca8SKalle Valo static int proc_perm = 0644;
24930db0ca8SKalle Valo 
25030db0ca8SKalle Valo MODULE_AUTHOR("Benjamin Reed");
25130db0ca8SKalle Valo MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet cards.  "
25230db0ca8SKalle Valo 		   "Direct support for ISA/PCI/MPI cards and support for PCMCIA when used with airo_cs.");
25330db0ca8SKalle Valo MODULE_LICENSE("Dual BSD/GPL");
25430db0ca8SKalle Valo MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350");
255767c13e6SDavid Howells module_param_hw_array(io, int, ioport, NULL, 0);
256767c13e6SDavid Howells module_param_hw_array(irq, int, irq, NULL, 0);
25730db0ca8SKalle Valo module_param_array(rates, int, NULL, 0);
25830db0ca8SKalle Valo module_param_array(ssids, charp, NULL, 0);
25930db0ca8SKalle Valo module_param(auto_wep, int, 0);
26030db0ca8SKalle Valo MODULE_PARM_DESC(auto_wep,
26130db0ca8SKalle Valo 		 "If non-zero, the driver will keep looping through the authentication options until an association is made.  "
26230db0ca8SKalle Valo 		 "The value of auto_wep is number of the wep keys to check.  "
26330db0ca8SKalle Valo 		 "A value of 2 will try using the key at index 0 and index 1.");
26430db0ca8SKalle Valo module_param(aux_bap, int, 0);
26530db0ca8SKalle Valo MODULE_PARM_DESC(aux_bap,
26630db0ca8SKalle Valo 		 "If non-zero, the driver will switch into a mode that seems to work better for older cards with some older buses.  "
26730db0ca8SKalle Valo 		 "Before switching it checks that the switch is needed.");
26830db0ca8SKalle Valo module_param(maxencrypt, int, 0);
26930db0ca8SKalle Valo MODULE_PARM_DESC(maxencrypt,
27030db0ca8SKalle Valo 		 "The maximum speed that the card can do encryption.  "
27130db0ca8SKalle Valo 		 "Units are in 512kbs.  "
27230db0ca8SKalle Valo 		 "Zero (default) means there is no limit.  "
27330db0ca8SKalle Valo 		 "Older cards used to be limited to 2mbs (4).");
27430db0ca8SKalle Valo module_param(adhoc, int, 0);
27530db0ca8SKalle Valo MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode.");
27630db0ca8SKalle Valo module_param(probe, int, 0);
27730db0ca8SKalle Valo MODULE_PARM_DESC(probe, "If zero, the driver won't start the card.");
27830db0ca8SKalle Valo 
27930db0ca8SKalle Valo module_param(proc_uid, int, 0);
28030db0ca8SKalle Valo MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to.");
28130db0ca8SKalle Valo module_param(proc_gid, int, 0);
28230db0ca8SKalle Valo MODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to.");
28330db0ca8SKalle Valo module_param(airo_perm, int, 0);
28430db0ca8SKalle Valo MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet.");
28530db0ca8SKalle Valo module_param(proc_perm, int, 0);
28630db0ca8SKalle Valo MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc");
28730db0ca8SKalle Valo 
28830db0ca8SKalle Valo /* This is a kind of sloppy hack to get this information to OUT4500 and
28930db0ca8SKalle Valo    IN4500.  I would be extremely interested in the situation where this
29030db0ca8SKalle Valo    doesn't work though!!! */
29130db0ca8SKalle Valo static int do8bitIO /* = 0 */;
29230db0ca8SKalle Valo 
29330db0ca8SKalle Valo /* Return codes */
29430db0ca8SKalle Valo #define SUCCESS 0
29530db0ca8SKalle Valo #define ERROR -1
29630db0ca8SKalle Valo #define NO_PACKET -2
29730db0ca8SKalle Valo 
29830db0ca8SKalle Valo /* Commands */
29930db0ca8SKalle Valo #define NOP2		0x0000
30030db0ca8SKalle Valo #define MAC_ENABLE	0x0001
30130db0ca8SKalle Valo #define MAC_DISABLE	0x0002
30230db0ca8SKalle Valo #define CMD_LOSE_SYNC	0x0003 /* Not sure what this does... */
30330db0ca8SKalle Valo #define CMD_SOFTRESET	0x0004
30430db0ca8SKalle Valo #define HOSTSLEEP	0x0005
30530db0ca8SKalle Valo #define CMD_MAGIC_PKT	0x0006
30630db0ca8SKalle Valo #define CMD_SETWAKEMASK	0x0007
30730db0ca8SKalle Valo #define CMD_READCFG	0x0008
30830db0ca8SKalle Valo #define CMD_SETMODE	0x0009
30930db0ca8SKalle Valo #define CMD_ALLOCATETX	0x000a
31030db0ca8SKalle Valo #define CMD_TRANSMIT	0x000b
31130db0ca8SKalle Valo #define CMD_DEALLOCATETX 0x000c
31230db0ca8SKalle Valo #define NOP		0x0010
31330db0ca8SKalle Valo #define CMD_WORKAROUND	0x0011
31430db0ca8SKalle Valo #define CMD_ALLOCATEAUX 0x0020
31530db0ca8SKalle Valo #define CMD_ACCESS	0x0021
31630db0ca8SKalle Valo #define CMD_PCIBAP	0x0022
31730db0ca8SKalle Valo #define CMD_PCIAUX	0x0023
31830db0ca8SKalle Valo #define CMD_ALLOCBUF	0x0028
31930db0ca8SKalle Valo #define CMD_GETTLV	0x0029
32030db0ca8SKalle Valo #define CMD_PUTTLV	0x002a
32130db0ca8SKalle Valo #define CMD_DELTLV	0x002b
32230db0ca8SKalle Valo #define CMD_FINDNEXTTLV	0x002c
32330db0ca8SKalle Valo #define CMD_PSPNODES	0x0030
32430db0ca8SKalle Valo #define CMD_SETCW	0x0031
32530db0ca8SKalle Valo #define CMD_SETPCF	0x0032
32630db0ca8SKalle Valo #define CMD_SETPHYREG	0x003e
32730db0ca8SKalle Valo #define CMD_TXTEST	0x003f
32830db0ca8SKalle Valo #define MAC_ENABLETX	0x0101
32930db0ca8SKalle Valo #define CMD_LISTBSS	0x0103
33030db0ca8SKalle Valo #define CMD_SAVECFG	0x0108
33130db0ca8SKalle Valo #define CMD_ENABLEAUX	0x0111
33230db0ca8SKalle Valo #define CMD_WRITERID	0x0121
33330db0ca8SKalle Valo #define CMD_USEPSPNODES	0x0130
33430db0ca8SKalle Valo #define MAC_ENABLERX	0x0201
33530db0ca8SKalle Valo 
33630db0ca8SKalle Valo /* Command errors */
33730db0ca8SKalle Valo #define ERROR_QUALIF 0x00
33830db0ca8SKalle Valo #define ERROR_ILLCMD 0x01
33930db0ca8SKalle Valo #define ERROR_ILLFMT 0x02
34030db0ca8SKalle Valo #define ERROR_INVFID 0x03
34130db0ca8SKalle Valo #define ERROR_INVRID 0x04
34230db0ca8SKalle Valo #define ERROR_LARGE 0x05
34330db0ca8SKalle Valo #define ERROR_NDISABL 0x06
34430db0ca8SKalle Valo #define ERROR_ALLOCBSY 0x07
34530db0ca8SKalle Valo #define ERROR_NORD 0x0B
34630db0ca8SKalle Valo #define ERROR_NOWR 0x0C
34730db0ca8SKalle Valo #define ERROR_INVFIDTX 0x0D
34830db0ca8SKalle Valo #define ERROR_TESTACT 0x0E
34930db0ca8SKalle Valo #define ERROR_TAGNFND 0x12
35030db0ca8SKalle Valo #define ERROR_DECODE 0x20
35130db0ca8SKalle Valo #define ERROR_DESCUNAV 0x21
35230db0ca8SKalle Valo #define ERROR_BADLEN 0x22
35330db0ca8SKalle Valo #define ERROR_MODE 0x80
35430db0ca8SKalle Valo #define ERROR_HOP 0x81
35530db0ca8SKalle Valo #define ERROR_BINTER 0x82
35630db0ca8SKalle Valo #define ERROR_RXMODE 0x83
35730db0ca8SKalle Valo #define ERROR_MACADDR 0x84
35830db0ca8SKalle Valo #define ERROR_RATES 0x85
35930db0ca8SKalle Valo #define ERROR_ORDER 0x86
36030db0ca8SKalle Valo #define ERROR_SCAN 0x87
36130db0ca8SKalle Valo #define ERROR_AUTH 0x88
36230db0ca8SKalle Valo #define ERROR_PSMODE 0x89
36330db0ca8SKalle Valo #define ERROR_RTYPE 0x8A
36430db0ca8SKalle Valo #define ERROR_DIVER 0x8B
36530db0ca8SKalle Valo #define ERROR_SSID 0x8C
36630db0ca8SKalle Valo #define ERROR_APLIST 0x8D
36730db0ca8SKalle Valo #define ERROR_AUTOWAKE 0x8E
36830db0ca8SKalle Valo #define ERROR_LEAP 0x8F
36930db0ca8SKalle Valo 
37030db0ca8SKalle Valo /* Registers */
37130db0ca8SKalle Valo #define COMMAND 0x00
37230db0ca8SKalle Valo #define PARAM0 0x02
37330db0ca8SKalle Valo #define PARAM1 0x04
37430db0ca8SKalle Valo #define PARAM2 0x06
37530db0ca8SKalle Valo #define STATUS 0x08
37630db0ca8SKalle Valo #define RESP0 0x0a
37730db0ca8SKalle Valo #define RESP1 0x0c
37830db0ca8SKalle Valo #define RESP2 0x0e
37930db0ca8SKalle Valo #define LINKSTAT 0x10
38030db0ca8SKalle Valo #define SELECT0 0x18
38130db0ca8SKalle Valo #define OFFSET0 0x1c
38230db0ca8SKalle Valo #define RXFID 0x20
38330db0ca8SKalle Valo #define TXALLOCFID 0x22
38430db0ca8SKalle Valo #define TXCOMPLFID 0x24
38530db0ca8SKalle Valo #define DATA0 0x36
38630db0ca8SKalle Valo #define EVSTAT 0x30
38730db0ca8SKalle Valo #define EVINTEN 0x32
38830db0ca8SKalle Valo #define EVACK 0x34
38930db0ca8SKalle Valo #define SWS0 0x28
39030db0ca8SKalle Valo #define SWS1 0x2a
39130db0ca8SKalle Valo #define SWS2 0x2c
39230db0ca8SKalle Valo #define SWS3 0x2e
39330db0ca8SKalle Valo #define AUXPAGE 0x3A
39430db0ca8SKalle Valo #define AUXOFF 0x3C
39530db0ca8SKalle Valo #define AUXDATA 0x3E
39630db0ca8SKalle Valo 
39730db0ca8SKalle Valo #define FID_TX 1
39830db0ca8SKalle Valo #define FID_RX 2
39930db0ca8SKalle Valo /* Offset into aux memory for descriptors */
40030db0ca8SKalle Valo #define AUX_OFFSET 0x800
40130db0ca8SKalle Valo /* Size of allocated packets */
40230db0ca8SKalle Valo #define PKTSIZE 1840
40330db0ca8SKalle Valo #define RIDSIZE 2048
40430db0ca8SKalle Valo /* Size of the transmit queue */
40530db0ca8SKalle Valo #define MAXTXQ 64
40630db0ca8SKalle Valo 
40730db0ca8SKalle Valo /* BAP selectors */
40830db0ca8SKalle Valo #define BAP0 0 /* Used for receiving packets */
40930db0ca8SKalle Valo #define BAP1 2 /* Used for xmiting packets and working with RIDS */
41030db0ca8SKalle Valo 
41130db0ca8SKalle Valo /* Flags */
41230db0ca8SKalle Valo #define COMMAND_BUSY 0x8000
41330db0ca8SKalle Valo 
41430db0ca8SKalle Valo #define BAP_BUSY 0x8000
41530db0ca8SKalle Valo #define BAP_ERR 0x4000
41630db0ca8SKalle Valo #define BAP_DONE 0x2000
41730db0ca8SKalle Valo 
41830db0ca8SKalle Valo #define PROMISC 0xffff
41930db0ca8SKalle Valo #define NOPROMISC 0x0000
42030db0ca8SKalle Valo 
42130db0ca8SKalle Valo #define EV_CMD 0x10
42230db0ca8SKalle Valo #define EV_CLEARCOMMANDBUSY 0x4000
42330db0ca8SKalle Valo #define EV_RX 0x01
42430db0ca8SKalle Valo #define EV_TX 0x02
42530db0ca8SKalle Valo #define EV_TXEXC 0x04
42630db0ca8SKalle Valo #define EV_ALLOC 0x08
42730db0ca8SKalle Valo #define EV_LINK 0x80
42830db0ca8SKalle Valo #define EV_AWAKE 0x100
42930db0ca8SKalle Valo #define EV_TXCPY 0x400
43030db0ca8SKalle Valo #define EV_UNKNOWN 0x800
43130db0ca8SKalle Valo #define EV_MIC 0x1000 /* Message Integrity Check Interrupt */
43230db0ca8SKalle Valo #define EV_AWAKEN 0x2000
43330db0ca8SKalle Valo #define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC)
43430db0ca8SKalle Valo 
43530db0ca8SKalle Valo #ifdef CHECK_UNKNOWN_INTS
43630db0ca8SKalle Valo #define IGNORE_INTS (EV_CMD | EV_UNKNOWN)
43730db0ca8SKalle Valo #else
43830db0ca8SKalle Valo #define IGNORE_INTS (~STATUS_INTS)
43930db0ca8SKalle Valo #endif
44030db0ca8SKalle Valo 
44130db0ca8SKalle Valo /* RID TYPES */
44230db0ca8SKalle Valo #define RID_RW 0x20
44330db0ca8SKalle Valo 
44430db0ca8SKalle Valo /* The RIDs */
44530db0ca8SKalle Valo #define RID_CAPABILITIES 0xFF00
44630db0ca8SKalle Valo #define RID_APINFO     0xFF01
44730db0ca8SKalle Valo #define RID_RADIOINFO  0xFF02
44830db0ca8SKalle Valo #define RID_UNKNOWN3   0xFF03
44930db0ca8SKalle Valo #define RID_RSSI       0xFF04
45030db0ca8SKalle Valo #define RID_CONFIG     0xFF10
45130db0ca8SKalle Valo #define RID_SSID       0xFF11
45230db0ca8SKalle Valo #define RID_APLIST     0xFF12
45330db0ca8SKalle Valo #define RID_DRVNAME    0xFF13
45430db0ca8SKalle Valo #define RID_ETHERENCAP 0xFF14
45530db0ca8SKalle Valo #define RID_WEP_TEMP   0xFF15
45630db0ca8SKalle Valo #define RID_WEP_PERM   0xFF16
45730db0ca8SKalle Valo #define RID_MODULATION 0xFF17
45830db0ca8SKalle Valo #define RID_OPTIONS    0xFF18
45930db0ca8SKalle Valo #define RID_ACTUALCONFIG 0xFF20 /*readonly*/
46030db0ca8SKalle Valo #define RID_FACTORYCONFIG 0xFF21
46130db0ca8SKalle Valo #define RID_UNKNOWN22  0xFF22
46230db0ca8SKalle Valo #define RID_LEAPUSERNAME 0xFF23
46330db0ca8SKalle Valo #define RID_LEAPPASSWORD 0xFF24
46430db0ca8SKalle Valo #define RID_STATUS     0xFF50
46530db0ca8SKalle Valo #define RID_BEACON_HST 0xFF51
46630db0ca8SKalle Valo #define RID_BUSY_HST   0xFF52
46730db0ca8SKalle Valo #define RID_RETRIES_HST 0xFF53
46830db0ca8SKalle Valo #define RID_UNKNOWN54  0xFF54
46930db0ca8SKalle Valo #define RID_UNKNOWN55  0xFF55
47030db0ca8SKalle Valo #define RID_UNKNOWN56  0xFF56
47130db0ca8SKalle Valo #define RID_MIC        0xFF57
47230db0ca8SKalle Valo #define RID_STATS16    0xFF60
47330db0ca8SKalle Valo #define RID_STATS16DELTA 0xFF61
47430db0ca8SKalle Valo #define RID_STATS16DELTACLEAR 0xFF62
47530db0ca8SKalle Valo #define RID_STATS      0xFF68
47630db0ca8SKalle Valo #define RID_STATSDELTA 0xFF69
47730db0ca8SKalle Valo #define RID_STATSDELTACLEAR 0xFF6A
47830db0ca8SKalle Valo #define RID_ECHOTEST_RID 0xFF70
47930db0ca8SKalle Valo #define RID_ECHOTEST_RESULTS 0xFF71
48030db0ca8SKalle Valo #define RID_BSSLISTFIRST 0xFF72
48130db0ca8SKalle Valo #define RID_BSSLISTNEXT  0xFF73
48230db0ca8SKalle Valo #define RID_WPA_BSSLISTFIRST 0xFF74
48330db0ca8SKalle Valo #define RID_WPA_BSSLISTNEXT  0xFF75
48430db0ca8SKalle Valo 
48530db0ca8SKalle Valo typedef struct {
48630db0ca8SKalle Valo 	u16 cmd;
48730db0ca8SKalle Valo 	u16 parm0;
48830db0ca8SKalle Valo 	u16 parm1;
48930db0ca8SKalle Valo 	u16 parm2;
49030db0ca8SKalle Valo } Cmd;
49130db0ca8SKalle Valo 
49230db0ca8SKalle Valo typedef struct {
49330db0ca8SKalle Valo 	u16 status;
49430db0ca8SKalle Valo 	u16 rsp0;
49530db0ca8SKalle Valo 	u16 rsp1;
49630db0ca8SKalle Valo 	u16 rsp2;
49730db0ca8SKalle Valo } Resp;
49830db0ca8SKalle Valo 
49930db0ca8SKalle Valo /*
50030db0ca8SKalle Valo  * Rids and endian-ness:  The Rids will always be in cpu endian, since
50130db0ca8SKalle Valo  * this all the patches from the big-endian guys end up doing that.
50230db0ca8SKalle Valo  * so all rid access should use the read/writeXXXRid routines.
50330db0ca8SKalle Valo  */
50430db0ca8SKalle Valo 
50530db0ca8SKalle Valo /* This structure came from an email sent to me from an engineer at
50630db0ca8SKalle Valo    aironet for inclusion into this driver */
50730db0ca8SKalle Valo typedef struct WepKeyRid WepKeyRid;
50830db0ca8SKalle Valo struct WepKeyRid {
50930db0ca8SKalle Valo 	__le16 len;
51030db0ca8SKalle Valo 	__le16 kindex;
51130db0ca8SKalle Valo 	u8 mac[ETH_ALEN];
51230db0ca8SKalle Valo 	__le16 klen;
51330db0ca8SKalle Valo 	u8 key[16];
51430db0ca8SKalle Valo } __packed;
51530db0ca8SKalle Valo 
51630db0ca8SKalle Valo /* These structures are from the Aironet's PC4500 Developers Manual */
51730db0ca8SKalle Valo typedef struct Ssid Ssid;
51830db0ca8SKalle Valo struct Ssid {
51930db0ca8SKalle Valo 	__le16 len;
52030db0ca8SKalle Valo 	u8 ssid[32];
52130db0ca8SKalle Valo } __packed;
52230db0ca8SKalle Valo 
52330db0ca8SKalle Valo typedef struct SsidRid SsidRid;
52430db0ca8SKalle Valo struct SsidRid {
52530db0ca8SKalle Valo 	__le16 len;
52630db0ca8SKalle Valo 	Ssid ssids[3];
52730db0ca8SKalle Valo } __packed;
52830db0ca8SKalle Valo 
52930db0ca8SKalle Valo typedef struct ModulationRid ModulationRid;
53030db0ca8SKalle Valo struct ModulationRid {
53130db0ca8SKalle Valo         __le16 len;
53230db0ca8SKalle Valo         __le16 modulation;
53330db0ca8SKalle Valo #define MOD_DEFAULT cpu_to_le16(0)
53430db0ca8SKalle Valo #define MOD_CCK cpu_to_le16(1)
53530db0ca8SKalle Valo #define MOD_MOK cpu_to_le16(2)
53630db0ca8SKalle Valo } __packed;
53730db0ca8SKalle Valo 
53830db0ca8SKalle Valo typedef struct ConfigRid ConfigRid;
53930db0ca8SKalle Valo struct ConfigRid {
54030db0ca8SKalle Valo 	__le16 len; /* sizeof(ConfigRid) */
54130db0ca8SKalle Valo 	__le16 opmode; /* operating mode */
54230db0ca8SKalle Valo #define MODE_STA_IBSS cpu_to_le16(0)
54330db0ca8SKalle Valo #define MODE_STA_ESS cpu_to_le16(1)
54430db0ca8SKalle Valo #define MODE_AP cpu_to_le16(2)
54530db0ca8SKalle Valo #define MODE_AP_RPTR cpu_to_le16(3)
54630db0ca8SKalle Valo #define MODE_CFG_MASK cpu_to_le16(0xff)
54730db0ca8SKalle Valo #define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */
54830db0ca8SKalle Valo #define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */
54930db0ca8SKalle Valo #define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extenstions */
55030db0ca8SKalle Valo #define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */
55130db0ca8SKalle Valo #define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */
55230db0ca8SKalle Valo #define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */
55330db0ca8SKalle Valo #define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */
55430db0ca8SKalle Valo #define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */
55530db0ca8SKalle Valo #define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */
55630db0ca8SKalle Valo 	__le16 rmode; /* receive mode */
55730db0ca8SKalle Valo #define RXMODE_BC_MC_ADDR cpu_to_le16(0)
55830db0ca8SKalle Valo #define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */
55930db0ca8SKalle Valo #define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */
56030db0ca8SKalle Valo #define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */
56130db0ca8SKalle Valo #define RXMODE_RFMON_ANYBSS cpu_to_le16(4)
56230db0ca8SKalle Valo #define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */
56330db0ca8SKalle Valo #define RXMODE_MASK cpu_to_le16(255)
56430db0ca8SKalle Valo #define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */
56530db0ca8SKalle Valo #define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER)
56630db0ca8SKalle Valo #define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */
56730db0ca8SKalle Valo 	__le16 fragThresh;
56830db0ca8SKalle Valo 	__le16 rtsThres;
56930db0ca8SKalle Valo 	u8 macAddr[ETH_ALEN];
57030db0ca8SKalle Valo 	u8 rates[8];
57130db0ca8SKalle Valo 	__le16 shortRetryLimit;
57230db0ca8SKalle Valo 	__le16 longRetryLimit;
57330db0ca8SKalle Valo 	__le16 txLifetime; /* in kusec */
57430db0ca8SKalle Valo 	__le16 rxLifetime; /* in kusec */
57530db0ca8SKalle Valo 	__le16 stationary;
57630db0ca8SKalle Valo 	__le16 ordering;
57730db0ca8SKalle Valo 	__le16 u16deviceType; /* for overriding device type */
57830db0ca8SKalle Valo 	__le16 cfpRate;
57930db0ca8SKalle Valo 	__le16 cfpDuration;
58030db0ca8SKalle Valo 	__le16 _reserved1[3];
58130db0ca8SKalle Valo 	/*---------- Scanning/Associating ----------*/
58230db0ca8SKalle Valo 	__le16 scanMode;
58330db0ca8SKalle Valo #define SCANMODE_ACTIVE cpu_to_le16(0)
58430db0ca8SKalle Valo #define SCANMODE_PASSIVE cpu_to_le16(1)
58530db0ca8SKalle Valo #define SCANMODE_AIROSCAN cpu_to_le16(2)
58630db0ca8SKalle Valo 	__le16 probeDelay; /* in kusec */
58730db0ca8SKalle Valo 	__le16 probeEnergyTimeout; /* in kusec */
58830db0ca8SKalle Valo         __le16 probeResponseTimeout;
58930db0ca8SKalle Valo 	__le16 beaconListenTimeout;
59030db0ca8SKalle Valo 	__le16 joinNetTimeout;
59130db0ca8SKalle Valo 	__le16 authTimeout;
59230db0ca8SKalle Valo 	__le16 authType;
59330db0ca8SKalle Valo #define AUTH_OPEN cpu_to_le16(0x1)
59430db0ca8SKalle Valo #define AUTH_ENCRYPT cpu_to_le16(0x101)
59530db0ca8SKalle Valo #define AUTH_SHAREDKEY cpu_to_le16(0x102)
59630db0ca8SKalle Valo #define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200)
59730db0ca8SKalle Valo 	__le16 associationTimeout;
59830db0ca8SKalle Valo 	__le16 specifiedApTimeout;
59930db0ca8SKalle Valo 	__le16 offlineScanInterval;
60030db0ca8SKalle Valo 	__le16 offlineScanDuration;
60130db0ca8SKalle Valo 	__le16 linkLossDelay;
60230db0ca8SKalle Valo 	__le16 maxBeaconLostTime;
60330db0ca8SKalle Valo 	__le16 refreshInterval;
60430db0ca8SKalle Valo #define DISABLE_REFRESH cpu_to_le16(0xFFFF)
60530db0ca8SKalle Valo 	__le16 _reserved1a[1];
60630db0ca8SKalle Valo 	/*---------- Power save operation ----------*/
60730db0ca8SKalle Valo 	__le16 powerSaveMode;
60830db0ca8SKalle Valo #define POWERSAVE_CAM cpu_to_le16(0)
60930db0ca8SKalle Valo #define POWERSAVE_PSP cpu_to_le16(1)
61030db0ca8SKalle Valo #define POWERSAVE_PSPCAM cpu_to_le16(2)
61130db0ca8SKalle Valo 	__le16 sleepForDtims;
61230db0ca8SKalle Valo 	__le16 listenInterval;
61330db0ca8SKalle Valo 	__le16 fastListenInterval;
61430db0ca8SKalle Valo 	__le16 listenDecay;
61530db0ca8SKalle Valo 	__le16 fastListenDelay;
61630db0ca8SKalle Valo 	__le16 _reserved2[2];
61730db0ca8SKalle Valo 	/*---------- Ap/Ibss config items ----------*/
61830db0ca8SKalle Valo 	__le16 beaconPeriod;
61930db0ca8SKalle Valo 	__le16 atimDuration;
62030db0ca8SKalle Valo 	__le16 hopPeriod;
62130db0ca8SKalle Valo 	__le16 channelSet;
62230db0ca8SKalle Valo 	__le16 channel;
62330db0ca8SKalle Valo 	__le16 dtimPeriod;
62430db0ca8SKalle Valo 	__le16 bridgeDistance;
62530db0ca8SKalle Valo 	__le16 radioID;
62630db0ca8SKalle Valo 	/*---------- Radio configuration ----------*/
62730db0ca8SKalle Valo 	__le16 radioType;
62830db0ca8SKalle Valo #define RADIOTYPE_DEFAULT cpu_to_le16(0)
62930db0ca8SKalle Valo #define RADIOTYPE_802_11 cpu_to_le16(1)
63030db0ca8SKalle Valo #define RADIOTYPE_LEGACY cpu_to_le16(2)
63130db0ca8SKalle Valo 	u8 rxDiversity;
63230db0ca8SKalle Valo 	u8 txDiversity;
63330db0ca8SKalle Valo 	__le16 txPower;
63430db0ca8SKalle Valo #define TXPOWER_DEFAULT 0
63530db0ca8SKalle Valo 	__le16 rssiThreshold;
63630db0ca8SKalle Valo #define RSSI_DEFAULT 0
63730db0ca8SKalle Valo         __le16 modulation;
63830db0ca8SKalle Valo #define PREAMBLE_AUTO cpu_to_le16(0)
63930db0ca8SKalle Valo #define PREAMBLE_LONG cpu_to_le16(1)
64030db0ca8SKalle Valo #define PREAMBLE_SHORT cpu_to_le16(2)
64130db0ca8SKalle Valo 	__le16 preamble;
64230db0ca8SKalle Valo 	__le16 homeProduct;
64330db0ca8SKalle Valo 	__le16 radioSpecific;
64430db0ca8SKalle Valo 	/*---------- Aironet Extensions ----------*/
64530db0ca8SKalle Valo 	u8 nodeName[16];
64630db0ca8SKalle Valo 	__le16 arlThreshold;
64730db0ca8SKalle Valo 	__le16 arlDecay;
64830db0ca8SKalle Valo 	__le16 arlDelay;
64930db0ca8SKalle Valo 	__le16 _reserved4[1];
65030db0ca8SKalle Valo 	/*---------- Aironet Extensions ----------*/
65130db0ca8SKalle Valo 	u8 magicAction;
65230db0ca8SKalle Valo #define MAGIC_ACTION_STSCHG 1
65330db0ca8SKalle Valo #define MAGIC_ACTION_RESUME 2
65430db0ca8SKalle Valo #define MAGIC_IGNORE_MCAST (1<<8)
65530db0ca8SKalle Valo #define MAGIC_IGNORE_BCAST (1<<9)
65630db0ca8SKalle Valo #define MAGIC_SWITCH_TO_PSP (0<<10)
65730db0ca8SKalle Valo #define MAGIC_STAY_IN_CAM (1<<10)
65830db0ca8SKalle Valo 	u8 magicControl;
65930db0ca8SKalle Valo 	__le16 autoWake;
66030db0ca8SKalle Valo } __packed;
66130db0ca8SKalle Valo 
66230db0ca8SKalle Valo typedef struct StatusRid StatusRid;
66330db0ca8SKalle Valo struct StatusRid {
66430db0ca8SKalle Valo 	__le16 len;
66530db0ca8SKalle Valo 	u8 mac[ETH_ALEN];
66630db0ca8SKalle Valo 	__le16 mode;
66730db0ca8SKalle Valo 	__le16 errorCode;
66830db0ca8SKalle Valo 	__le16 sigQuality;
66930db0ca8SKalle Valo 	__le16 SSIDlen;
67030db0ca8SKalle Valo 	char SSID[32];
67130db0ca8SKalle Valo 	char apName[16];
67230db0ca8SKalle Valo 	u8 bssid[4][ETH_ALEN];
67330db0ca8SKalle Valo 	__le16 beaconPeriod;
67430db0ca8SKalle Valo 	__le16 dimPeriod;
67530db0ca8SKalle Valo 	__le16 atimDuration;
67630db0ca8SKalle Valo 	__le16 hopPeriod;
67730db0ca8SKalle Valo 	__le16 channelSet;
67830db0ca8SKalle Valo 	__le16 channel;
67930db0ca8SKalle Valo 	__le16 hopsToBackbone;
68030db0ca8SKalle Valo 	__le16 apTotalLoad;
68130db0ca8SKalle Valo 	__le16 generatedLoad;
68230db0ca8SKalle Valo 	__le16 accumulatedArl;
68330db0ca8SKalle Valo 	__le16 signalQuality;
68430db0ca8SKalle Valo 	__le16 currentXmitRate;
68530db0ca8SKalle Valo 	__le16 apDevExtensions;
68630db0ca8SKalle Valo 	__le16 normalizedSignalStrength;
68730db0ca8SKalle Valo 	__le16 shortPreamble;
68830db0ca8SKalle Valo 	u8 apIP[4];
68930db0ca8SKalle Valo 	u8 noisePercent; /* Noise percent in last second */
69030db0ca8SKalle Valo 	u8 noisedBm; /* Noise dBm in last second */
69130db0ca8SKalle Valo 	u8 noiseAvePercent; /* Noise percent in last minute */
69230db0ca8SKalle Valo 	u8 noiseAvedBm; /* Noise dBm in last minute */
69330db0ca8SKalle Valo 	u8 noiseMaxPercent; /* Highest noise percent in last minute */
69430db0ca8SKalle Valo 	u8 noiseMaxdBm; /* Highest noise dbm in last minute */
69530db0ca8SKalle Valo 	__le16 load;
69630db0ca8SKalle Valo 	u8 carrier[4];
69730db0ca8SKalle Valo 	__le16 assocStatus;
69830db0ca8SKalle Valo #define STAT_NOPACKETS 0
69930db0ca8SKalle Valo #define STAT_NOCARRIERSET 10
70030db0ca8SKalle Valo #define STAT_GOTCARRIERSET 11
70130db0ca8SKalle Valo #define STAT_WRONGSSID 20
70230db0ca8SKalle Valo #define STAT_BADCHANNEL 25
70330db0ca8SKalle Valo #define STAT_BADBITRATES 30
70430db0ca8SKalle Valo #define STAT_BADPRIVACY 35
70530db0ca8SKalle Valo #define STAT_APFOUND 40
70630db0ca8SKalle Valo #define STAT_APREJECTED 50
70730db0ca8SKalle Valo #define STAT_AUTHENTICATING 60
70830db0ca8SKalle Valo #define STAT_DEAUTHENTICATED 61
70930db0ca8SKalle Valo #define STAT_AUTHTIMEOUT 62
71030db0ca8SKalle Valo #define STAT_ASSOCIATING 70
71130db0ca8SKalle Valo #define STAT_DEASSOCIATED 71
71230db0ca8SKalle Valo #define STAT_ASSOCTIMEOUT 72
71330db0ca8SKalle Valo #define STAT_NOTAIROAP 73
71430db0ca8SKalle Valo #define STAT_ASSOCIATED 80
71530db0ca8SKalle Valo #define STAT_LEAPING 90
71630db0ca8SKalle Valo #define STAT_LEAPFAILED 91
71730db0ca8SKalle Valo #define STAT_LEAPTIMEDOUT 92
71830db0ca8SKalle Valo #define STAT_LEAPCOMPLETE 93
71930db0ca8SKalle Valo } __packed;
72030db0ca8SKalle Valo 
72130db0ca8SKalle Valo typedef struct StatsRid StatsRid;
72230db0ca8SKalle Valo struct StatsRid {
72330db0ca8SKalle Valo 	__le16 len;
72430db0ca8SKalle Valo 	__le16 spacer;
72530db0ca8SKalle Valo 	__le32 vals[100];
72630db0ca8SKalle Valo } __packed;
72730db0ca8SKalle Valo 
72830db0ca8SKalle Valo typedef struct APListRid APListRid;
72930db0ca8SKalle Valo struct APListRid {
73030db0ca8SKalle Valo 	__le16 len;
73130db0ca8SKalle Valo 	u8 ap[4][ETH_ALEN];
73230db0ca8SKalle Valo } __packed;
73330db0ca8SKalle Valo 
73430db0ca8SKalle Valo typedef struct CapabilityRid CapabilityRid;
73530db0ca8SKalle Valo struct CapabilityRid {
73630db0ca8SKalle Valo 	__le16 len;
73730db0ca8SKalle Valo 	char oui[3];
73830db0ca8SKalle Valo 	char zero;
73930db0ca8SKalle Valo 	__le16 prodNum;
74030db0ca8SKalle Valo 	char manName[32];
74130db0ca8SKalle Valo 	char prodName[16];
74230db0ca8SKalle Valo 	char prodVer[8];
74330db0ca8SKalle Valo 	char factoryAddr[ETH_ALEN];
74430db0ca8SKalle Valo 	char aironetAddr[ETH_ALEN];
74530db0ca8SKalle Valo 	__le16 radioType;
74630db0ca8SKalle Valo 	__le16 country;
74730db0ca8SKalle Valo 	char callid[ETH_ALEN];
74830db0ca8SKalle Valo 	char supportedRates[8];
74930db0ca8SKalle Valo 	char rxDiversity;
75030db0ca8SKalle Valo 	char txDiversity;
75130db0ca8SKalle Valo 	__le16 txPowerLevels[8];
75230db0ca8SKalle Valo 	__le16 hardVer;
75330db0ca8SKalle Valo 	__le16 hardCap;
75430db0ca8SKalle Valo 	__le16 tempRange;
75530db0ca8SKalle Valo 	__le16 softVer;
75630db0ca8SKalle Valo 	__le16 softSubVer;
75730db0ca8SKalle Valo 	__le16 interfaceVer;
75830db0ca8SKalle Valo 	__le16 softCap;
75930db0ca8SKalle Valo 	__le16 bootBlockVer;
76030db0ca8SKalle Valo 	__le16 requiredHard;
76130db0ca8SKalle Valo 	__le16 extSoftCap;
76230db0ca8SKalle Valo } __packed;
76330db0ca8SKalle Valo 
76430db0ca8SKalle Valo /* Only present on firmware >= 5.30.17 */
76530db0ca8SKalle Valo typedef struct BSSListRidExtra BSSListRidExtra;
76630db0ca8SKalle Valo struct BSSListRidExtra {
76730db0ca8SKalle Valo   __le16 unknown[4];
76830db0ca8SKalle Valo   u8 fixed[12]; /* WLAN management frame */
76930db0ca8SKalle Valo   u8 iep[624];
77030db0ca8SKalle Valo } __packed;
77130db0ca8SKalle Valo 
77230db0ca8SKalle Valo typedef struct BSSListRid BSSListRid;
77330db0ca8SKalle Valo struct BSSListRid {
77430db0ca8SKalle Valo   __le16 len;
77530db0ca8SKalle Valo   __le16 index; /* First is 0 and 0xffff means end of list */
77630db0ca8SKalle Valo #define RADIO_FH 1 /* Frequency hopping radio type */
77730db0ca8SKalle Valo #define RADIO_DS 2 /* Direct sequence radio type */
77830db0ca8SKalle Valo #define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */
77930db0ca8SKalle Valo   __le16 radioType;
78030db0ca8SKalle Valo   u8 bssid[ETH_ALEN]; /* Mac address of the BSS */
78130db0ca8SKalle Valo   u8 zero;
78230db0ca8SKalle Valo   u8 ssidLen;
78330db0ca8SKalle Valo   u8 ssid[32];
78430db0ca8SKalle Valo   __le16 dBm;
78530db0ca8SKalle Valo #define CAP_ESS cpu_to_le16(1<<0)
78630db0ca8SKalle Valo #define CAP_IBSS cpu_to_le16(1<<1)
78730db0ca8SKalle Valo #define CAP_PRIVACY cpu_to_le16(1<<4)
78830db0ca8SKalle Valo #define CAP_SHORTHDR cpu_to_le16(1<<5)
78930db0ca8SKalle Valo   __le16 cap;
79030db0ca8SKalle Valo   __le16 beaconInterval;
79130db0ca8SKalle Valo   u8 rates[8]; /* Same as rates for config rid */
79230db0ca8SKalle Valo   struct { /* For frequency hopping only */
79330db0ca8SKalle Valo     __le16 dwell;
79430db0ca8SKalle Valo     u8 hopSet;
79530db0ca8SKalle Valo     u8 hopPattern;
79630db0ca8SKalle Valo     u8 hopIndex;
79730db0ca8SKalle Valo     u8 fill;
79830db0ca8SKalle Valo   } fh;
79930db0ca8SKalle Valo   __le16 dsChannel;
80030db0ca8SKalle Valo   __le16 atimWindow;
80130db0ca8SKalle Valo 
80230db0ca8SKalle Valo   /* Only present on firmware >= 5.30.17 */
80330db0ca8SKalle Valo   BSSListRidExtra extra;
80430db0ca8SKalle Valo } __packed;
80530db0ca8SKalle Valo 
80630db0ca8SKalle Valo typedef struct {
80730db0ca8SKalle Valo   BSSListRid bss;
80830db0ca8SKalle Valo   struct list_head list;
80930db0ca8SKalle Valo } BSSListElement;
81030db0ca8SKalle Valo 
81130db0ca8SKalle Valo typedef struct tdsRssiEntry tdsRssiEntry;
81230db0ca8SKalle Valo struct tdsRssiEntry {
81330db0ca8SKalle Valo   u8 rssipct;
81430db0ca8SKalle Valo   u8 rssidBm;
81530db0ca8SKalle Valo } __packed;
81630db0ca8SKalle Valo 
81730db0ca8SKalle Valo typedef struct tdsRssiRid tdsRssiRid;
81830db0ca8SKalle Valo struct tdsRssiRid {
81930db0ca8SKalle Valo   u16 len;
82030db0ca8SKalle Valo   tdsRssiEntry x[256];
82130db0ca8SKalle Valo } __packed;
82230db0ca8SKalle Valo 
82330db0ca8SKalle Valo typedef struct MICRid MICRid;
82430db0ca8SKalle Valo struct MICRid {
82530db0ca8SKalle Valo 	__le16 len;
82630db0ca8SKalle Valo 	__le16 state;
82730db0ca8SKalle Valo 	__le16 multicastValid;
82830db0ca8SKalle Valo 	u8  multicast[16];
82930db0ca8SKalle Valo 	__le16 unicastValid;
83030db0ca8SKalle Valo 	u8  unicast[16];
83130db0ca8SKalle Valo } __packed;
83230db0ca8SKalle Valo 
83330db0ca8SKalle Valo typedef struct MICBuffer MICBuffer;
83430db0ca8SKalle Valo struct MICBuffer {
83530db0ca8SKalle Valo 	__be16 typelen;
83630db0ca8SKalle Valo 
83730db0ca8SKalle Valo 	union {
83830db0ca8SKalle Valo 	    u8 snap[8];
83930db0ca8SKalle Valo 	    struct {
84030db0ca8SKalle Valo 		u8 dsap;
84130db0ca8SKalle Valo 		u8 ssap;
84230db0ca8SKalle Valo 		u8 control;
84330db0ca8SKalle Valo 		u8 orgcode[3];
84430db0ca8SKalle Valo 		u8 fieldtype[2];
84530db0ca8SKalle Valo 	    } llc;
84630db0ca8SKalle Valo 	} u;
84730db0ca8SKalle Valo 	__be32 mic;
84830db0ca8SKalle Valo 	__be32 seq;
84930db0ca8SKalle Valo } __packed;
85030db0ca8SKalle Valo 
85130db0ca8SKalle Valo typedef struct {
85230db0ca8SKalle Valo 	u8 da[ETH_ALEN];
85330db0ca8SKalle Valo 	u8 sa[ETH_ALEN];
85430db0ca8SKalle Valo } etherHead;
85530db0ca8SKalle Valo 
85630db0ca8SKalle Valo #define TXCTL_TXOK (1<<1) /* report if tx is ok */
85730db0ca8SKalle Valo #define TXCTL_TXEX (1<<2) /* report if tx fails */
85830db0ca8SKalle Valo #define TXCTL_802_3 (0<<3) /* 802.3 packet */
85930db0ca8SKalle Valo #define TXCTL_802_11 (1<<3) /* 802.11 mac packet */
86030db0ca8SKalle Valo #define TXCTL_ETHERNET (0<<4) /* payload has ethertype */
86130db0ca8SKalle Valo #define TXCTL_LLC (1<<4) /* payload is llc */
86230db0ca8SKalle Valo #define TXCTL_RELEASE (0<<5) /* release after completion */
86330db0ca8SKalle Valo #define TXCTL_NORELEASE (1<<5) /* on completion returns to host */
86430db0ca8SKalle Valo 
86530db0ca8SKalle Valo #define BUSY_FID 0x10000
86630db0ca8SKalle Valo 
86730db0ca8SKalle Valo #ifdef CISCO_EXT
86830db0ca8SKalle Valo #define AIROMAGIC	0xa55a
86930db0ca8SKalle Valo /* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */
87030db0ca8SKalle Valo #ifdef SIOCIWFIRSTPRIV
87130db0ca8SKalle Valo #ifdef SIOCDEVPRIVATE
87230db0ca8SKalle Valo #define AIROOLDIOCTL	SIOCDEVPRIVATE
87330db0ca8SKalle Valo #define AIROOLDIDIFC 	AIROOLDIOCTL + 1
87430db0ca8SKalle Valo #endif /* SIOCDEVPRIVATE */
87530db0ca8SKalle Valo #else /* SIOCIWFIRSTPRIV */
87630db0ca8SKalle Valo #define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
87730db0ca8SKalle Valo #endif /* SIOCIWFIRSTPRIV */
87830db0ca8SKalle Valo /* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably
87930db0ca8SKalle Valo  * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root
88030db0ca8SKalle Valo  * only and don't return the modified struct ifreq to the application which
88130db0ca8SKalle Valo  * is usually a problem. - Jean II */
88230db0ca8SKalle Valo #define AIROIOCTL	SIOCIWFIRSTPRIV
88330db0ca8SKalle Valo #define AIROIDIFC 	AIROIOCTL + 1
88430db0ca8SKalle Valo 
88530db0ca8SKalle Valo /* Ioctl constants to be used in airo_ioctl.command */
88630db0ca8SKalle Valo 
88730db0ca8SKalle Valo #define	AIROGCAP  		0	// Capability rid
88830db0ca8SKalle Valo #define AIROGCFG		1       // USED A LOT
88930db0ca8SKalle Valo #define AIROGSLIST		2	// System ID list
89030db0ca8SKalle Valo #define AIROGVLIST		3       // List of specified AP's
89130db0ca8SKalle Valo #define AIROGDRVNAM		4	//  NOTUSED
89230db0ca8SKalle Valo #define AIROGEHTENC		5	// NOTUSED
89330db0ca8SKalle Valo #define AIROGWEPKTMP		6
89430db0ca8SKalle Valo #define AIROGWEPKNV		7
89530db0ca8SKalle Valo #define AIROGSTAT		8
89630db0ca8SKalle Valo #define AIROGSTATSC32		9
89730db0ca8SKalle Valo #define AIROGSTATSD32		10
89830db0ca8SKalle Valo #define AIROGMICRID		11
89930db0ca8SKalle Valo #define AIROGMICSTATS		12
90030db0ca8SKalle Valo #define AIROGFLAGS		13
90130db0ca8SKalle Valo #define AIROGID			14
90230db0ca8SKalle Valo #define AIRORRID		15
90330db0ca8SKalle Valo #define AIRORSWVERSION		17
90430db0ca8SKalle Valo 
90530db0ca8SKalle Valo /* Leave gap of 40 commands after AIROGSTATSD32 for future */
90630db0ca8SKalle Valo 
90730db0ca8SKalle Valo #define AIROPCAP               	AIROGSTATSD32 + 40
90830db0ca8SKalle Valo #define AIROPVLIST              AIROPCAP      + 1
90930db0ca8SKalle Valo #define AIROPSLIST		AIROPVLIST    + 1
91030db0ca8SKalle Valo #define AIROPCFG		AIROPSLIST    + 1
91130db0ca8SKalle Valo #define AIROPSIDS		AIROPCFG      + 1
91230db0ca8SKalle Valo #define AIROPAPLIST		AIROPSIDS     + 1
91330db0ca8SKalle Valo #define AIROPMACON		AIROPAPLIST   + 1	/* Enable mac  */
91430db0ca8SKalle Valo #define AIROPMACOFF		AIROPMACON    + 1 	/* Disable mac */
91530db0ca8SKalle Valo #define AIROPSTCLR		AIROPMACOFF   + 1
91630db0ca8SKalle Valo #define AIROPWEPKEY		AIROPSTCLR    + 1
91730db0ca8SKalle Valo #define AIROPWEPKEYNV		AIROPWEPKEY   + 1
91830db0ca8SKalle Valo #define AIROPLEAPPWD            AIROPWEPKEYNV + 1
91930db0ca8SKalle Valo #define AIROPLEAPUSR            AIROPLEAPPWD  + 1
92030db0ca8SKalle Valo 
92130db0ca8SKalle Valo /* Flash codes */
92230db0ca8SKalle Valo 
92330db0ca8SKalle Valo #define AIROFLSHRST	       AIROPWEPKEYNV  + 40
92430db0ca8SKalle Valo #define AIROFLSHGCHR           AIROFLSHRST    + 1
92530db0ca8SKalle Valo #define AIROFLSHSTFL           AIROFLSHGCHR   + 1
92630db0ca8SKalle Valo #define AIROFLSHPCHR           AIROFLSHSTFL   + 1
92730db0ca8SKalle Valo #define AIROFLPUTBUF           AIROFLSHPCHR   + 1
92830db0ca8SKalle Valo #define AIRORESTART            AIROFLPUTBUF   + 1
92930db0ca8SKalle Valo 
93030db0ca8SKalle Valo #define FLASHSIZE	32768
93130db0ca8SKalle Valo #define AUXMEMSIZE	(256 * 1024)
93230db0ca8SKalle Valo 
93330db0ca8SKalle Valo typedef struct aironet_ioctl {
93430db0ca8SKalle Valo 	unsigned short command;		// What to do
93530db0ca8SKalle Valo 	unsigned short len;		// Len of data
93630db0ca8SKalle Valo 	unsigned short ridnum;		// rid number
93730db0ca8SKalle Valo 	unsigned char __user *data;	// d-data
93830db0ca8SKalle Valo } aironet_ioctl;
93930db0ca8SKalle Valo 
94030db0ca8SKalle Valo static const char swversion[] = "2.1";
94130db0ca8SKalle Valo #endif /* CISCO_EXT */
94230db0ca8SKalle Valo 
94330db0ca8SKalle Valo #define NUM_MODULES       2
94430db0ca8SKalle Valo #define MIC_MSGLEN_MAX    2400
94530db0ca8SKalle Valo #define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX
94630db0ca8SKalle Valo #define AIRO_DEF_MTU      2312
94730db0ca8SKalle Valo 
94830db0ca8SKalle Valo typedef struct {
94930db0ca8SKalle Valo 	u32   size;            // size
95030db0ca8SKalle Valo 	u8    enabled;         // MIC enabled or not
95130db0ca8SKalle Valo 	u32   rxSuccess;       // successful packets received
95230db0ca8SKalle Valo 	u32   rxIncorrectMIC;  // pkts dropped due to incorrect MIC comparison
95330db0ca8SKalle Valo 	u32   rxNotMICed;      // pkts dropped due to not being MIC'd
95430db0ca8SKalle Valo 	u32   rxMICPlummed;    // pkts dropped due to not having a MIC plummed
95530db0ca8SKalle Valo 	u32   rxWrongSequence; // pkts dropped due to sequence number violation
95630db0ca8SKalle Valo 	u32   reserve[32];
95730db0ca8SKalle Valo } mic_statistics;
95830db0ca8SKalle Valo 
95930db0ca8SKalle Valo typedef struct {
960e5db0ad7SArd Biesheuvel 	__be32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2];
96130db0ca8SKalle Valo 	u64 accum;	// accumulated mic, reduced to u32 in final()
96230db0ca8SKalle Valo 	int position;	// current position (byte offset) in message
96330db0ca8SKalle Valo 	union {
96430db0ca8SKalle Valo 		u8  d8[4];
96530db0ca8SKalle Valo 		__be32 d32;
96630db0ca8SKalle Valo 	} part;	// saves partial message word across update() calls
96730db0ca8SKalle Valo } emmh32_context;
96830db0ca8SKalle Valo 
96930db0ca8SKalle Valo typedef struct {
97030db0ca8SKalle Valo 	emmh32_context seed;	    // Context - the seed
97130db0ca8SKalle Valo 	u32		 rx;	    // Received sequence number
97230db0ca8SKalle Valo 	u32		 tx;	    // Tx sequence number
97330db0ca8SKalle Valo 	u32		 window;    // Start of window
97430db0ca8SKalle Valo 	u8		 valid;	    // Flag to say if context is valid or not
97530db0ca8SKalle Valo 	u8		 key[16];
97630db0ca8SKalle Valo } miccntx;
97730db0ca8SKalle Valo 
97830db0ca8SKalle Valo typedef struct {
97930db0ca8SKalle Valo 	miccntx mCtx;		// Multicast context
98030db0ca8SKalle Valo 	miccntx uCtx;		// Unicast context
98130db0ca8SKalle Valo } mic_module;
98230db0ca8SKalle Valo 
98330db0ca8SKalle Valo typedef struct {
98430db0ca8SKalle Valo 	unsigned int  rid: 16;
98530db0ca8SKalle Valo 	unsigned int  len: 15;
98630db0ca8SKalle Valo 	unsigned int  valid: 1;
98730db0ca8SKalle Valo 	dma_addr_t host_addr;
98830db0ca8SKalle Valo } Rid;
98930db0ca8SKalle Valo 
99030db0ca8SKalle Valo typedef struct {
99130db0ca8SKalle Valo 	unsigned int  offset: 15;
99230db0ca8SKalle Valo 	unsigned int  eoc: 1;
99330db0ca8SKalle Valo 	unsigned int  len: 15;
99430db0ca8SKalle Valo 	unsigned int  valid: 1;
99530db0ca8SKalle Valo 	dma_addr_t host_addr;
99630db0ca8SKalle Valo } TxFid;
99730db0ca8SKalle Valo 
99830db0ca8SKalle Valo struct rx_hdr {
99930db0ca8SKalle Valo 	__le16 status, len;
100030db0ca8SKalle Valo 	u8 rssi[2];
100130db0ca8SKalle Valo 	u8 rate;
100230db0ca8SKalle Valo 	u8 freq;
100330db0ca8SKalle Valo 	__le16 tmp[4];
100430db0ca8SKalle Valo } __packed;
100530db0ca8SKalle Valo 
100630db0ca8SKalle Valo typedef struct {
100730db0ca8SKalle Valo 	unsigned int  ctl: 15;
100830db0ca8SKalle Valo 	unsigned int  rdy: 1;
100930db0ca8SKalle Valo 	unsigned int  len: 15;
101030db0ca8SKalle Valo 	unsigned int  valid: 1;
101130db0ca8SKalle Valo 	dma_addr_t host_addr;
101230db0ca8SKalle Valo } RxFid;
101330db0ca8SKalle Valo 
101430db0ca8SKalle Valo /*
101530db0ca8SKalle Valo  * Host receive descriptor
101630db0ca8SKalle Valo  */
101730db0ca8SKalle Valo typedef struct {
101830db0ca8SKalle Valo 	unsigned char __iomem *card_ram_off; /* offset into card memory of the
101930db0ca8SKalle Valo 						desc */
102030db0ca8SKalle Valo 	RxFid         rx_desc;		     /* card receive descriptor */
102130db0ca8SKalle Valo 	char          *virtual_host_addr;    /* virtual address of host receive
102230db0ca8SKalle Valo 					        buffer */
102330db0ca8SKalle Valo 	int           pending;
102430db0ca8SKalle Valo } HostRxDesc;
102530db0ca8SKalle Valo 
102630db0ca8SKalle Valo /*
102730db0ca8SKalle Valo  * Host transmit descriptor
102830db0ca8SKalle Valo  */
102930db0ca8SKalle Valo typedef struct {
103030db0ca8SKalle Valo 	unsigned char __iomem *card_ram_off;	     /* offset into card memory of the
103130db0ca8SKalle Valo 						desc */
103230db0ca8SKalle Valo 	TxFid         tx_desc;		     /* card transmit descriptor */
103330db0ca8SKalle Valo 	char          *virtual_host_addr;    /* virtual address of host receive
103430db0ca8SKalle Valo 					        buffer */
103530db0ca8SKalle Valo 	int           pending;
103630db0ca8SKalle Valo } HostTxDesc;
103730db0ca8SKalle Valo 
103830db0ca8SKalle Valo /*
103930db0ca8SKalle Valo  * Host RID descriptor
104030db0ca8SKalle Valo  */
104130db0ca8SKalle Valo typedef struct {
104230db0ca8SKalle Valo 	unsigned char __iomem *card_ram_off;      /* offset into card memory of the
104330db0ca8SKalle Valo 					     descriptor */
104430db0ca8SKalle Valo 	Rid           rid_desc;		  /* card RID descriptor */
104530db0ca8SKalle Valo 	char          *virtual_host_addr; /* virtual address of host receive
104630db0ca8SKalle Valo 					     buffer */
104730db0ca8SKalle Valo } HostRidDesc;
104830db0ca8SKalle Valo 
104930db0ca8SKalle Valo typedef struct {
105030db0ca8SKalle Valo 	u16 sw0;
105130db0ca8SKalle Valo 	u16 sw1;
105230db0ca8SKalle Valo 	u16 status;
105330db0ca8SKalle Valo 	u16 len;
105430db0ca8SKalle Valo #define HOST_SET (1 << 0)
105530db0ca8SKalle Valo #define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */
105630db0ca8SKalle Valo #define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */
105730db0ca8SKalle Valo #define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */
105830db0ca8SKalle Valo #define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */
105930db0ca8SKalle Valo #define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */
106030db0ca8SKalle Valo #define HOST_CLR_AID (1 << 7) /* clear AID failure */
106130db0ca8SKalle Valo #define HOST_RTS (1 << 9) /* Force RTS use */
106230db0ca8SKalle Valo #define HOST_SHORT (1 << 10) /* Do short preamble */
106330db0ca8SKalle Valo 	u16 ctl;
106430db0ca8SKalle Valo 	u16 aid;
106530db0ca8SKalle Valo 	u16 retries;
106630db0ca8SKalle Valo 	u16 fill;
106730db0ca8SKalle Valo } TxCtlHdr;
106830db0ca8SKalle Valo 
106930db0ca8SKalle Valo typedef struct {
107030db0ca8SKalle Valo         u16 ctl;
107130db0ca8SKalle Valo         u16 duration;
107230db0ca8SKalle Valo         char addr1[6];
107330db0ca8SKalle Valo         char addr2[6];
107430db0ca8SKalle Valo         char addr3[6];
107530db0ca8SKalle Valo         u16 seq;
107630db0ca8SKalle Valo         char addr4[6];
107730db0ca8SKalle Valo } WifiHdr;
107830db0ca8SKalle Valo 
107930db0ca8SKalle Valo 
108030db0ca8SKalle Valo typedef struct {
108130db0ca8SKalle Valo 	TxCtlHdr ctlhdr;
108230db0ca8SKalle Valo 	u16 fill1;
108330db0ca8SKalle Valo 	u16 fill2;
108430db0ca8SKalle Valo 	WifiHdr wifihdr;
108530db0ca8SKalle Valo 	u16 gaplen;
108630db0ca8SKalle Valo 	u16 status;
108730db0ca8SKalle Valo } WifiCtlHdr;
108830db0ca8SKalle Valo 
108930db0ca8SKalle Valo static WifiCtlHdr wifictlhdr8023 = {
109030db0ca8SKalle Valo 	.ctlhdr = {
109130db0ca8SKalle Valo 		.ctl	= HOST_DONT_RLSE,
109230db0ca8SKalle Valo 	}
109330db0ca8SKalle Valo };
109430db0ca8SKalle Valo 
109530db0ca8SKalle Valo // A few details needed for WEP (Wireless Equivalent Privacy)
109630db0ca8SKalle Valo #define MAX_KEY_SIZE 13			// 128 (?) bits
109730db0ca8SKalle Valo #define MIN_KEY_SIZE  5			// 40 bits RC4 - WEP
109830db0ca8SKalle Valo typedef struct wep_key_t {
109930db0ca8SKalle Valo 	u16	len;
110030db0ca8SKalle Valo 	u8	key[16];	/* 40-bit and 104-bit keys */
110130db0ca8SKalle Valo } wep_key_t;
110230db0ca8SKalle Valo 
110330db0ca8SKalle Valo /* List of Wireless Handlers (new API) */
110430db0ca8SKalle Valo static const struct iw_handler_def	airo_handler_def;
110530db0ca8SKalle Valo 
110630db0ca8SKalle Valo static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)";
110730db0ca8SKalle Valo 
110830db0ca8SKalle Valo struct airo_info;
110930db0ca8SKalle Valo 
111030db0ca8SKalle Valo static int get_dec_u16(char *buffer, int *start, int limit);
111188e97c32SArnd Bergmann static void OUT4500(struct airo_info *, u16 reg, u16 value);
111288e97c32SArnd Bergmann static unsigned short IN4500(struct airo_info *, u16 reg);
111330db0ca8SKalle Valo static u16 setup_card(struct airo_info*, u8 *mac, int lock);
111430db0ca8SKalle Valo static int enable_MAC(struct airo_info *ai, int lock);
111530db0ca8SKalle Valo static void disable_MAC(struct airo_info *ai, int lock);
111630db0ca8SKalle Valo static void enable_interrupts(struct airo_info*);
111730db0ca8SKalle Valo static void disable_interrupts(struct airo_info*);
111830db0ca8SKalle Valo static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp);
111930db0ca8SKalle Valo static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap);
112030db0ca8SKalle Valo static int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
112130db0ca8SKalle Valo 			int whichbap);
112230db0ca8SKalle Valo static int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen,
112330db0ca8SKalle Valo 			 int whichbap);
112430db0ca8SKalle Valo static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen,
112530db0ca8SKalle Valo 		     int whichbap);
112630db0ca8SKalle Valo static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd);
112730db0ca8SKalle Valo static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock);
112830db0ca8SKalle Valo static int PC4500_writerid(struct airo_info*, u16 rid, const void
112930db0ca8SKalle Valo 			   *pBuf, int len, int lock);
113030db0ca8SKalle Valo static int do_writerid(struct airo_info*, u16 rid, const void *rid_data,
113130db0ca8SKalle Valo 			int len, int dummy);
113230db0ca8SKalle Valo static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw);
113330db0ca8SKalle Valo static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket);
113430db0ca8SKalle Valo static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket);
113530db0ca8SKalle Valo 
113630db0ca8SKalle Valo static int mpi_send_packet(struct net_device *dev);
113730db0ca8SKalle Valo static void mpi_unmap_card(struct pci_dev *pci);
113830db0ca8SKalle Valo static void mpi_receive_802_3(struct airo_info *ai);
113930db0ca8SKalle Valo static void mpi_receive_802_11(struct airo_info *ai);
114030db0ca8SKalle Valo static int waitbusy(struct airo_info *ai);
114130db0ca8SKalle Valo 
114230db0ca8SKalle Valo static irqreturn_t airo_interrupt(int irq, void* dev_id);
114330db0ca8SKalle Valo static int airo_thread(void *data);
114430db0ca8SKalle Valo static void timer_func(struct net_device *dev);
114530db0ca8SKalle Valo static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
114630db0ca8SKalle Valo static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev);
114730db0ca8SKalle Valo #ifdef CISCO_EXT
114830db0ca8SKalle Valo static int readrids(struct net_device *dev, aironet_ioctl *comp);
114930db0ca8SKalle Valo static int writerids(struct net_device *dev, aironet_ioctl *comp);
115030db0ca8SKalle Valo static int flashcard(struct net_device *dev, aironet_ioctl *comp);
115130db0ca8SKalle Valo #endif /* CISCO_EXT */
115230db0ca8SKalle Valo static void micinit(struct airo_info *ai);
115330db0ca8SKalle Valo static int micsetup(struct airo_info *ai);
115430db0ca8SKalle Valo static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len);
115530db0ca8SKalle Valo static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen);
115630db0ca8SKalle Valo 
115730db0ca8SKalle Valo static u8 airo_rssi_to_dbm(tdsRssiEntry *rssi_rid, u8 rssi);
115830db0ca8SKalle Valo static u8 airo_dbm_to_pct(tdsRssiEntry *rssi_rid, u8 dbm);
115930db0ca8SKalle Valo 
116030db0ca8SKalle Valo static void airo_networks_free(struct airo_info *ai);
116130db0ca8SKalle Valo 
116230db0ca8SKalle Valo struct airo_info {
116330db0ca8SKalle Valo 	struct net_device             *dev;
116430db0ca8SKalle Valo 	struct list_head              dev_list;
116530db0ca8SKalle Valo 	/* Note, we can have MAX_FIDS outstanding.  FIDs are 16-bits, so we
116630db0ca8SKalle Valo 	   use the high bit to mark whether it is in use. */
116730db0ca8SKalle Valo #define MAX_FIDS 6
116830db0ca8SKalle Valo #define MPI_MAX_FIDS 1
116930db0ca8SKalle Valo 	u32                           fids[MAX_FIDS];
117030db0ca8SKalle Valo 	ConfigRid config;
117130db0ca8SKalle Valo 	char keyindex; // Used with auto wep
117230db0ca8SKalle Valo 	char defindex; // Used with auto wep
117330db0ca8SKalle Valo 	struct proc_dir_entry *proc_entry;
117430db0ca8SKalle Valo         spinlock_t aux_lock;
117530db0ca8SKalle Valo #define FLAG_RADIO_OFF	0	/* User disabling of MAC */
117630db0ca8SKalle Valo #define FLAG_RADIO_DOWN	1	/* ifup/ifdown disabling of MAC */
117730db0ca8SKalle Valo #define FLAG_RADIO_MASK 0x03
117830db0ca8SKalle Valo #define FLAG_ENABLED	2
117930db0ca8SKalle Valo #define FLAG_ADHOC	3	/* Needed by MIC */
118030db0ca8SKalle Valo #define FLAG_MIC_CAPABLE 4
118130db0ca8SKalle Valo #define FLAG_UPDATE_MULTI 5
118230db0ca8SKalle Valo #define FLAG_UPDATE_UNI 6
118330db0ca8SKalle Valo #define FLAG_802_11	7
118430db0ca8SKalle Valo #define FLAG_PROMISC	8	/* IFF_PROMISC 0x100 - include/linux/if.h */
118530db0ca8SKalle Valo #define FLAG_PENDING_XMIT 9
118630db0ca8SKalle Valo #define FLAG_PENDING_XMIT11 10
118730db0ca8SKalle Valo #define FLAG_MPI	11
118830db0ca8SKalle Valo #define FLAG_REGISTERED	12
118930db0ca8SKalle Valo #define FLAG_COMMIT	13
119030db0ca8SKalle Valo #define FLAG_RESET	14
119130db0ca8SKalle Valo #define FLAG_FLASHING	15
119230db0ca8SKalle Valo #define FLAG_WPA_CAPABLE	16
119330db0ca8SKalle Valo 	unsigned long flags;
119430db0ca8SKalle Valo #define JOB_DIE	0
119530db0ca8SKalle Valo #define JOB_XMIT	1
119630db0ca8SKalle Valo #define JOB_XMIT11	2
119730db0ca8SKalle Valo #define JOB_STATS	3
119830db0ca8SKalle Valo #define JOB_PROMISC	4
119930db0ca8SKalle Valo #define JOB_MIC	5
120030db0ca8SKalle Valo #define JOB_EVENT	6
120130db0ca8SKalle Valo #define JOB_AUTOWEP	7
120230db0ca8SKalle Valo #define JOB_SCAN_RESULTS  9
120330db0ca8SKalle Valo 	unsigned long jobs;
120430db0ca8SKalle Valo 	int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen,
120530db0ca8SKalle Valo 			int whichbap);
120630db0ca8SKalle Valo 	unsigned short *flash;
120730db0ca8SKalle Valo 	tdsRssiEntry *rssi;
120830db0ca8SKalle Valo 	struct task_struct *list_bss_task;
120930db0ca8SKalle Valo 	struct task_struct *airo_thread_task;
121030db0ca8SKalle Valo 	struct semaphore sem;
121130db0ca8SKalle Valo 	wait_queue_head_t thr_wait;
121230db0ca8SKalle Valo 	unsigned long expires;
121330db0ca8SKalle Valo 	struct {
121430db0ca8SKalle Valo 		struct sk_buff *skb;
121530db0ca8SKalle Valo 		int fid;
121630db0ca8SKalle Valo 	} xmit, xmit11;
121730db0ca8SKalle Valo 	struct net_device *wifidev;
121830db0ca8SKalle Valo 	struct iw_statistics	wstats;		// wireless stats
121930db0ca8SKalle Valo 	unsigned long		scan_timeout;	/* Time scan should be read */
122030db0ca8SKalle Valo 	struct iw_spy_data	spy_data;
122130db0ca8SKalle Valo 	struct iw_public_data	wireless_data;
122230db0ca8SKalle Valo 	/* MIC stuff */
1223e5db0ad7SArd Biesheuvel 	struct crypto_sync_skcipher	*tfm;
122430db0ca8SKalle Valo 	mic_module		mod[2];
122530db0ca8SKalle Valo 	mic_statistics		micstats;
122630db0ca8SKalle Valo 	HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors
122730db0ca8SKalle Valo 	HostTxDesc txfids[MPI_MAX_FIDS];
122830db0ca8SKalle Valo 	HostRidDesc config_desc;
122930db0ca8SKalle Valo 	unsigned long ridbus; // phys addr of config_desc
123030db0ca8SKalle Valo 	struct sk_buff_head txq;// tx queue used by mpi350 code
123130db0ca8SKalle Valo 	struct pci_dev          *pci;
123230db0ca8SKalle Valo 	unsigned char		__iomem *pcimem;
123330db0ca8SKalle Valo 	unsigned char		__iomem *pciaux;
123430db0ca8SKalle Valo 	unsigned char		*shared;
123530db0ca8SKalle Valo 	dma_addr_t		shared_dma;
123630db0ca8SKalle Valo 	pm_message_t		power;
123730db0ca8SKalle Valo 	SsidRid			*SSID;
123830db0ca8SKalle Valo 	APListRid		APList;
123930db0ca8SKalle Valo #define	PCI_SHARED_LEN		2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
124030db0ca8SKalle Valo 	char			proc_name[IFNAMSIZ];
124130db0ca8SKalle Valo 
124230db0ca8SKalle Valo 	int			wep_capable;
124330db0ca8SKalle Valo 	int			max_wep_idx;
124430db0ca8SKalle Valo 	int			last_auth;
124530db0ca8SKalle Valo 
124630db0ca8SKalle Valo 	/* WPA-related stuff */
124730db0ca8SKalle Valo 	unsigned int bssListFirst;
124830db0ca8SKalle Valo 	unsigned int bssListNext;
124930db0ca8SKalle Valo 	unsigned int bssListRidLen;
125030db0ca8SKalle Valo 
125130db0ca8SKalle Valo 	struct list_head network_list;
125230db0ca8SKalle Valo 	struct list_head network_free_list;
125330db0ca8SKalle Valo 	BSSListElement *networks;
125430db0ca8SKalle Valo };
125530db0ca8SKalle Valo 
125630db0ca8SKalle Valo static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen,
125730db0ca8SKalle Valo 			   int whichbap)
125830db0ca8SKalle Valo {
125930db0ca8SKalle Valo 	return ai->bap_read(ai, pu16Dst, bytelen, whichbap);
126030db0ca8SKalle Valo }
126130db0ca8SKalle Valo 
126230db0ca8SKalle Valo static int setup_proc_entry(struct net_device *dev,
126330db0ca8SKalle Valo 			     struct airo_info *apriv);
126430db0ca8SKalle Valo static int takedown_proc_entry(struct net_device *dev,
126530db0ca8SKalle Valo 				struct airo_info *apriv);
126630db0ca8SKalle Valo 
126730db0ca8SKalle Valo static int cmdreset(struct airo_info *ai);
126830db0ca8SKalle Valo static int setflashmode(struct airo_info *ai);
126930db0ca8SKalle Valo static int flashgchar(struct airo_info *ai, int matchbyte, int dwelltime);
127030db0ca8SKalle Valo static int flashputbuf(struct airo_info *ai);
127130db0ca8SKalle Valo static int flashrestart(struct airo_info *ai, struct net_device *dev);
127230db0ca8SKalle Valo 
127330db0ca8SKalle Valo #define airo_print(type, name, fmt, args...) \
127430db0ca8SKalle Valo 	printk(type DRV_NAME "(%s): " fmt "\n", name, ##args)
127530db0ca8SKalle Valo 
127630db0ca8SKalle Valo #define airo_print_info(name, fmt, args...) \
127730db0ca8SKalle Valo 	airo_print(KERN_INFO, name, fmt, ##args)
127830db0ca8SKalle Valo 
127930db0ca8SKalle Valo #define airo_print_dbg(name, fmt, args...) \
128030db0ca8SKalle Valo 	airo_print(KERN_DEBUG, name, fmt, ##args)
128130db0ca8SKalle Valo 
128230db0ca8SKalle Valo #define airo_print_warn(name, fmt, args...) \
128330db0ca8SKalle Valo 	airo_print(KERN_WARNING, name, fmt, ##args)
128430db0ca8SKalle Valo 
128530db0ca8SKalle Valo #define airo_print_err(name, fmt, args...) \
128630db0ca8SKalle Valo 	airo_print(KERN_ERR, name, fmt, ##args)
128730db0ca8SKalle Valo 
128830db0ca8SKalle Valo #define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash)
128930db0ca8SKalle Valo 
129030db0ca8SKalle Valo /***********************************************************************
129130db0ca8SKalle Valo  *                              MIC ROUTINES                           *
129230db0ca8SKalle Valo  ***********************************************************************
129330db0ca8SKalle Valo  */
129430db0ca8SKalle Valo 
129530db0ca8SKalle Valo static int RxSeqValid(struct airo_info *ai, miccntx *context, int mcast, u32 micSeq);
129630db0ca8SKalle Valo static void MoveWindow(miccntx *context, u32 micSeq);
129730db0ca8SKalle Valo static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
1298e5db0ad7SArd Biesheuvel 			   struct crypto_sync_skcipher *tfm);
129930db0ca8SKalle Valo static void emmh32_init(emmh32_context *context);
130030db0ca8SKalle Valo static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
130130db0ca8SKalle Valo static void emmh32_final(emmh32_context *context, u8 digest[4]);
130230db0ca8SKalle Valo static int flashpchar(struct airo_info *ai, int byte, int dwelltime);
130330db0ca8SKalle Valo 
130430db0ca8SKalle Valo static void age_mic_context(miccntx *cur, miccntx *old, u8 *key, int key_len,
1305e5db0ad7SArd Biesheuvel 			    struct crypto_sync_skcipher *tfm)
130630db0ca8SKalle Valo {
130730db0ca8SKalle Valo 	/* If the current MIC context is valid and its key is the same as
130830db0ca8SKalle Valo 	 * the MIC register, there's nothing to do.
130930db0ca8SKalle Valo 	 */
131030db0ca8SKalle Valo 	if (cur->valid && (memcmp(cur->key, key, key_len) == 0))
131130db0ca8SKalle Valo 		return;
131230db0ca8SKalle Valo 
131330db0ca8SKalle Valo 	/* Age current mic Context */
131430db0ca8SKalle Valo 	memcpy(old, cur, sizeof(*cur));
131530db0ca8SKalle Valo 
131630db0ca8SKalle Valo 	/* Initialize new context */
131730db0ca8SKalle Valo 	memcpy(cur->key, key, key_len);
131830db0ca8SKalle Valo 	cur->window  = 33; /* Window always points to the middle */
131930db0ca8SKalle Valo 	cur->rx      = 0;  /* Rx Sequence numbers */
132030db0ca8SKalle Valo 	cur->tx      = 0;  /* Tx sequence numbers */
132130db0ca8SKalle Valo 	cur->valid   = 1;  /* Key is now valid */
132230db0ca8SKalle Valo 
132330db0ca8SKalle Valo 	/* Give key to mic seed */
132430db0ca8SKalle Valo 	emmh32_setseed(&cur->seed, key, key_len, tfm);
132530db0ca8SKalle Valo }
132630db0ca8SKalle Valo 
132730db0ca8SKalle Valo /* micinit - Initialize mic seed */
132830db0ca8SKalle Valo 
132930db0ca8SKalle Valo static void micinit(struct airo_info *ai)
133030db0ca8SKalle Valo {
133130db0ca8SKalle Valo 	MICRid mic_rid;
133230db0ca8SKalle Valo 
133330db0ca8SKalle Valo 	clear_bit(JOB_MIC, &ai->jobs);
133430db0ca8SKalle Valo 	PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
133530db0ca8SKalle Valo 	up(&ai->sem);
133630db0ca8SKalle Valo 
133730db0ca8SKalle Valo 	ai->micstats.enabled = (le16_to_cpu(mic_rid.state) & 0x00FF) ? 1 : 0;
133830db0ca8SKalle Valo 	if (!ai->micstats.enabled) {
133930db0ca8SKalle Valo 		/* So next time we have a valid key and mic is enabled, we will
134030db0ca8SKalle Valo 		 * update the sequence number if the key is the same as before.
134130db0ca8SKalle Valo 		 */
134230db0ca8SKalle Valo 		ai->mod[0].uCtx.valid = 0;
134330db0ca8SKalle Valo 		ai->mod[0].mCtx.valid = 0;
134430db0ca8SKalle Valo 		return;
134530db0ca8SKalle Valo 	}
134630db0ca8SKalle Valo 
134730db0ca8SKalle Valo 	if (mic_rid.multicastValid) {
134830db0ca8SKalle Valo 		age_mic_context(&ai->mod[0].mCtx, &ai->mod[1].mCtx,
134930db0ca8SKalle Valo 		                mic_rid.multicast, sizeof(mic_rid.multicast),
135030db0ca8SKalle Valo 		                ai->tfm);
135130db0ca8SKalle Valo 	}
135230db0ca8SKalle Valo 
135330db0ca8SKalle Valo 	if (mic_rid.unicastValid) {
135430db0ca8SKalle Valo 		age_mic_context(&ai->mod[0].uCtx, &ai->mod[1].uCtx,
135530db0ca8SKalle Valo 				mic_rid.unicast, sizeof(mic_rid.unicast),
135630db0ca8SKalle Valo 				ai->tfm);
135730db0ca8SKalle Valo 	}
135830db0ca8SKalle Valo }
135930db0ca8SKalle Valo 
136030db0ca8SKalle Valo /* micsetup - Get ready for business */
136130db0ca8SKalle Valo 
1362ba4d6513SLee Jones static int micsetup(struct airo_info *ai)
1363ba4d6513SLee Jones {
136430db0ca8SKalle Valo 	int i;
136530db0ca8SKalle Valo 
136630db0ca8SKalle Valo 	if (ai->tfm == NULL)
1367e5db0ad7SArd Biesheuvel 		ai->tfm = crypto_alloc_sync_skcipher("ctr(aes)", 0, 0);
136830db0ca8SKalle Valo 
136930db0ca8SKalle Valo         if (IS_ERR(ai->tfm)) {
137030db0ca8SKalle Valo                 airo_print_err(ai->dev->name, "failed to load transform for AES");
137130db0ca8SKalle Valo                 ai->tfm = NULL;
137230db0ca8SKalle Valo                 return ERROR;
137330db0ca8SKalle Valo         }
137430db0ca8SKalle Valo 
137530db0ca8SKalle Valo 	for (i = 0; i < NUM_MODULES; i++) {
137630db0ca8SKalle Valo 		memset(&ai->mod[i].mCtx, 0, sizeof(miccntx));
137730db0ca8SKalle Valo 		memset(&ai->mod[i].uCtx, 0, sizeof(miccntx));
137830db0ca8SKalle Valo 	}
137930db0ca8SKalle Valo 	return SUCCESS;
138030db0ca8SKalle Valo }
138130db0ca8SKalle Valo 
138230db0ca8SKalle Valo static const u8 micsnap[] = {0xAA, 0xAA, 0x03, 0x00, 0x40, 0x96, 0x00, 0x02};
138330db0ca8SKalle Valo 
138430db0ca8SKalle Valo /*===========================================================================
138530db0ca8SKalle Valo  * Description: Mic a packet
138630db0ca8SKalle Valo  *
138730db0ca8SKalle Valo  *      Inputs: etherHead * pointer to an 802.3 frame
138830db0ca8SKalle Valo  *
138930db0ca8SKalle Valo  *     Returns: BOOLEAN if successful, otherwise false.
139030db0ca8SKalle Valo  *             PacketTxLen will be updated with the mic'd packets size.
139130db0ca8SKalle Valo  *
139230db0ca8SKalle Valo  *    Caveats: It is assumed that the frame buffer will already
139330db0ca8SKalle Valo  *             be big enough to hold the largets mic message possible.
139430db0ca8SKalle Valo  *            (No memory allocation is done here).
139530db0ca8SKalle Valo  *
139630db0ca8SKalle Valo  *    Author: sbraneky (10/15/01)
139730db0ca8SKalle Valo  *    Merciless hacks by rwilcher (1/14/02)
139830db0ca8SKalle Valo  */
139930db0ca8SKalle Valo 
140030db0ca8SKalle Valo static int encapsulate(struct airo_info *ai, etherHead *frame, MICBuffer *mic, int payLen)
140130db0ca8SKalle Valo {
140230db0ca8SKalle Valo 	miccntx   *context;
140330db0ca8SKalle Valo 
140430db0ca8SKalle Valo 	// Determine correct context
140530db0ca8SKalle Valo 	// If not adhoc, always use unicast key
140630db0ca8SKalle Valo 
140730db0ca8SKalle Valo 	if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1))
140830db0ca8SKalle Valo 		context = &ai->mod[0].mCtx;
140930db0ca8SKalle Valo 	else
141030db0ca8SKalle Valo 		context = &ai->mod[0].uCtx;
141130db0ca8SKalle Valo 
141230db0ca8SKalle Valo 	if (!context->valid)
141330db0ca8SKalle Valo 		return ERROR;
141430db0ca8SKalle Valo 
141530db0ca8SKalle Valo 	mic->typelen = htons(payLen + 16); //Length of Mic'd packet
141630db0ca8SKalle Valo 
141730db0ca8SKalle Valo 	memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap
141830db0ca8SKalle Valo 
141930db0ca8SKalle Valo 	// Add Tx sequence
142030db0ca8SKalle Valo 	mic->seq = htonl(context->tx);
142130db0ca8SKalle Valo 	context->tx += 2;
142230db0ca8SKalle Valo 
142330db0ca8SKalle Valo 	emmh32_init(&context->seed); // Mic the packet
142430db0ca8SKalle Valo 	emmh32_update(&context->seed, frame->da, ETH_ALEN * 2); // DA, SA
142530db0ca8SKalle Valo 	emmh32_update(&context->seed, (u8*)&mic->typelen, 10); // Type/Length and Snap
142630db0ca8SKalle Valo 	emmh32_update(&context->seed, (u8*)&mic->seq, sizeof(mic->seq)); //SEQ
142730db0ca8SKalle Valo 	emmh32_update(&context->seed, (u8*)(frame + 1), payLen); //payload
142830db0ca8SKalle Valo 	emmh32_final(&context->seed, (u8*)&mic->mic);
142930db0ca8SKalle Valo 
143030db0ca8SKalle Valo 	/*    New Type/length ?????????? */
143130db0ca8SKalle Valo 	mic->typelen = 0; //Let NIC know it could be an oversized packet
143230db0ca8SKalle Valo 	return SUCCESS;
143330db0ca8SKalle Valo }
143430db0ca8SKalle Valo 
143530db0ca8SKalle Valo typedef enum {
143630db0ca8SKalle Valo     NONE,
143730db0ca8SKalle Valo     NOMIC,
143830db0ca8SKalle Valo     NOMICPLUMMED,
143930db0ca8SKalle Valo     SEQUENCE,
144030db0ca8SKalle Valo     INCORRECTMIC,
144130db0ca8SKalle Valo } mic_error;
144230db0ca8SKalle Valo 
144330db0ca8SKalle Valo /*===========================================================================
144430db0ca8SKalle Valo  *  Description: Decapsulates a MIC'd packet and returns the 802.3 packet
144530db0ca8SKalle Valo  *               (removes the MIC stuff) if packet is a valid packet.
144630db0ca8SKalle Valo  *
144730db0ca8SKalle Valo  *       Inputs: etherHead  pointer to the 802.3 packet
144830db0ca8SKalle Valo  *
144930db0ca8SKalle Valo  *      Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE
145030db0ca8SKalle Valo  *
145130db0ca8SKalle Valo  *      Author: sbraneky (10/15/01)
145230db0ca8SKalle Valo  *    Merciless hacks by rwilcher (1/14/02)
145330db0ca8SKalle Valo  *---------------------------------------------------------------------------
145430db0ca8SKalle Valo  */
145530db0ca8SKalle Valo 
145630db0ca8SKalle Valo static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen)
145730db0ca8SKalle Valo {
145830db0ca8SKalle Valo 	int      i;
145930db0ca8SKalle Valo 	u32      micSEQ;
146030db0ca8SKalle Valo 	miccntx  *context;
146130db0ca8SKalle Valo 	u8       digest[4];
146230db0ca8SKalle Valo 	mic_error micError = NONE;
146330db0ca8SKalle Valo 
146430db0ca8SKalle Valo 	// Check if the packet is a Mic'd packet
146530db0ca8SKalle Valo 
146630db0ca8SKalle Valo 	if (!ai->micstats.enabled) {
146730db0ca8SKalle Valo 		//No Mic set or Mic OFF but we received a MIC'd packet.
146830db0ca8SKalle Valo 		if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) {
146930db0ca8SKalle Valo 			ai->micstats.rxMICPlummed++;
147030db0ca8SKalle Valo 			return ERROR;
147130db0ca8SKalle Valo 		}
147230db0ca8SKalle Valo 		return SUCCESS;
147330db0ca8SKalle Valo 	}
147430db0ca8SKalle Valo 
147530db0ca8SKalle Valo 	if (ntohs(mic->typelen) == 0x888E)
147630db0ca8SKalle Valo 		return SUCCESS;
147730db0ca8SKalle Valo 
147830db0ca8SKalle Valo 	if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) {
147930db0ca8SKalle Valo 	    // Mic enabled but packet isn't Mic'd
148030db0ca8SKalle Valo 		ai->micstats.rxMICPlummed++;
148130db0ca8SKalle Valo 	    	return ERROR;
148230db0ca8SKalle Valo 	}
148330db0ca8SKalle Valo 
148430db0ca8SKalle Valo 	micSEQ = ntohl(mic->seq);            //store SEQ as CPU order
148530db0ca8SKalle Valo 
148630db0ca8SKalle Valo 	//At this point we a have a mic'd packet and mic is enabled
148730db0ca8SKalle Valo 	//Now do the mic error checking.
148830db0ca8SKalle Valo 
148930db0ca8SKalle Valo 	//Receive seq must be odd
149030db0ca8SKalle Valo 	if ((micSEQ & 1) == 0) {
149130db0ca8SKalle Valo 		ai->micstats.rxWrongSequence++;
149230db0ca8SKalle Valo 		return ERROR;
149330db0ca8SKalle Valo 	}
149430db0ca8SKalle Valo 
149530db0ca8SKalle Valo 	for (i = 0; i < NUM_MODULES; i++) {
149630db0ca8SKalle Valo 		int mcast = eth->da[0] & 1;
149730db0ca8SKalle Valo 		//Determine proper context
149830db0ca8SKalle Valo 		context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx;
149930db0ca8SKalle Valo 
150030db0ca8SKalle Valo 		//Make sure context is valid
150130db0ca8SKalle Valo 		if (!context->valid) {
150230db0ca8SKalle Valo 			if (i == 0)
150330db0ca8SKalle Valo 				micError = NOMICPLUMMED;
150430db0ca8SKalle Valo 			continue;
150530db0ca8SKalle Valo 		}
150630db0ca8SKalle Valo 		//DeMic it
150730db0ca8SKalle Valo 
150830db0ca8SKalle Valo 		if (!mic->typelen)
150930db0ca8SKalle Valo 			mic->typelen = htons(payLen + sizeof(MICBuffer) - 2);
151030db0ca8SKalle Valo 
151130db0ca8SKalle Valo 		emmh32_init(&context->seed);
151230db0ca8SKalle Valo 		emmh32_update(&context->seed, eth->da, ETH_ALEN*2);
151330db0ca8SKalle Valo 		emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap));
151430db0ca8SKalle Valo 		emmh32_update(&context->seed, (u8 *)&mic->seq, sizeof(mic->seq));
151530db0ca8SKalle Valo 		emmh32_update(&context->seed, (u8 *)(eth + 1), payLen);
151630db0ca8SKalle Valo 		//Calculate MIC
151730db0ca8SKalle Valo 		emmh32_final(&context->seed, digest);
151830db0ca8SKalle Valo 
151930db0ca8SKalle Valo 		if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match
152030db0ca8SKalle Valo 		  //Invalid Mic
152130db0ca8SKalle Valo 			if (i == 0)
152230db0ca8SKalle Valo 				micError = INCORRECTMIC;
152330db0ca8SKalle Valo 			continue;
152430db0ca8SKalle Valo 		}
152530db0ca8SKalle Valo 
152630db0ca8SKalle Valo 		//Check Sequence number if mics pass
152730db0ca8SKalle Valo 		if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) {
152830db0ca8SKalle Valo 			ai->micstats.rxSuccess++;
152930db0ca8SKalle Valo 			return SUCCESS;
153030db0ca8SKalle Valo 		}
153130db0ca8SKalle Valo 		if (i == 0)
153230db0ca8SKalle Valo 			micError = SEQUENCE;
153330db0ca8SKalle Valo 	}
153430db0ca8SKalle Valo 
153530db0ca8SKalle Valo 	// Update statistics
153630db0ca8SKalle Valo 	switch (micError) {
153730db0ca8SKalle Valo 		case NOMICPLUMMED: ai->micstats.rxMICPlummed++;   break;
153830db0ca8SKalle Valo 		case SEQUENCE:    ai->micstats.rxWrongSequence++; break;
153930db0ca8SKalle Valo 		case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break;
154030db0ca8SKalle Valo 		case NONE:  break;
154130db0ca8SKalle Valo 		case NOMIC: break;
154230db0ca8SKalle Valo 	}
154330db0ca8SKalle Valo 	return ERROR;
154430db0ca8SKalle Valo }
154530db0ca8SKalle Valo 
154630db0ca8SKalle Valo /*===========================================================================
154730db0ca8SKalle Valo  * Description:  Checks the Rx Seq number to make sure it is valid
154830db0ca8SKalle Valo  *               and hasn't already been received
154930db0ca8SKalle Valo  *
155030db0ca8SKalle Valo  *     Inputs: miccntx - mic context to check seq against
155130db0ca8SKalle Valo  *             micSeq  - the Mic seq number
155230db0ca8SKalle Valo  *
155330db0ca8SKalle Valo  *    Returns: TRUE if valid otherwise FALSE.
155430db0ca8SKalle Valo  *
155530db0ca8SKalle Valo  *    Author: sbraneky (10/15/01)
155630db0ca8SKalle Valo  *    Merciless hacks by rwilcher (1/14/02)
155730db0ca8SKalle Valo  *---------------------------------------------------------------------------
155830db0ca8SKalle Valo  */
155930db0ca8SKalle Valo 
156030db0ca8SKalle Valo static int RxSeqValid(struct airo_info *ai, miccntx *context, int mcast, u32 micSeq)
156130db0ca8SKalle Valo {
156230db0ca8SKalle Valo 	u32 seq, index;
156330db0ca8SKalle Valo 
156430db0ca8SKalle Valo 	//Allow for the ap being rebooted - if it is then use the next
156530db0ca8SKalle Valo 	//sequence number of the current sequence number - might go backwards
156630db0ca8SKalle Valo 
156730db0ca8SKalle Valo 	if (mcast) {
156830db0ca8SKalle Valo 		if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) {
156930db0ca8SKalle Valo 			clear_bit (FLAG_UPDATE_MULTI, &ai->flags);
157030db0ca8SKalle Valo 			context->window = (micSeq > 33) ? micSeq : 33;
157130db0ca8SKalle Valo 			context->rx     = 0;        // Reset rx
157230db0ca8SKalle Valo 		}
157330db0ca8SKalle Valo 	} else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) {
157430db0ca8SKalle Valo 		clear_bit (FLAG_UPDATE_UNI, &ai->flags);
157530db0ca8SKalle Valo 		context->window = (micSeq > 33) ? micSeq : 33; // Move window
157630db0ca8SKalle Valo 		context->rx     = 0;        // Reset rx
157730db0ca8SKalle Valo 	}
157830db0ca8SKalle Valo 
157930db0ca8SKalle Valo 	//Make sequence number relative to START of window
158030db0ca8SKalle Valo 	seq = micSeq - (context->window - 33);
158130db0ca8SKalle Valo 
158230db0ca8SKalle Valo 	//Too old of a SEQ number to check.
158330db0ca8SKalle Valo 	if ((s32)seq < 0)
158430db0ca8SKalle Valo 		return ERROR;
158530db0ca8SKalle Valo 
158630db0ca8SKalle Valo 	if (seq > 64) {
158730db0ca8SKalle Valo 		//Window is infinite forward
158830db0ca8SKalle Valo 		MoveWindow(context, micSeq);
158930db0ca8SKalle Valo 		return SUCCESS;
159030db0ca8SKalle Valo 	}
159130db0ca8SKalle Valo 
159230db0ca8SKalle Valo 	// We are in the window. Now check the context rx bit to see if it was already sent
159330db0ca8SKalle Valo 	seq >>= 1;         //divide by 2 because we only have odd numbers
159430db0ca8SKalle Valo 	index = 1 << seq;  //Get an index number
159530db0ca8SKalle Valo 
159630db0ca8SKalle Valo 	if (!(context->rx & index)) {
159730db0ca8SKalle Valo 		//micSEQ falls inside the window.
159830db0ca8SKalle Valo 		//Add seqence number to the list of received numbers.
159930db0ca8SKalle Valo 		context->rx |= index;
160030db0ca8SKalle Valo 
160130db0ca8SKalle Valo 		MoveWindow(context, micSeq);
160230db0ca8SKalle Valo 
160330db0ca8SKalle Valo 		return SUCCESS;
160430db0ca8SKalle Valo 	}
160530db0ca8SKalle Valo 	return ERROR;
160630db0ca8SKalle Valo }
160730db0ca8SKalle Valo 
160830db0ca8SKalle Valo static void MoveWindow(miccntx *context, u32 micSeq)
160930db0ca8SKalle Valo {
161030db0ca8SKalle Valo 	u32 shift;
161130db0ca8SKalle Valo 
161230db0ca8SKalle Valo 	//Move window if seq greater than the middle of the window
161330db0ca8SKalle Valo 	if (micSeq > context->window) {
161430db0ca8SKalle Valo 		shift = (micSeq - context->window) >> 1;
161530db0ca8SKalle Valo 
161630db0ca8SKalle Valo 		    //Shift out old
161730db0ca8SKalle Valo 		if (shift < 32)
161830db0ca8SKalle Valo 			context->rx >>= shift;
161930db0ca8SKalle Valo 		else
162030db0ca8SKalle Valo 			context->rx = 0;
162130db0ca8SKalle Valo 
162230db0ca8SKalle Valo 		context->window = micSeq;      //Move window
162330db0ca8SKalle Valo 	}
162430db0ca8SKalle Valo }
162530db0ca8SKalle Valo 
162630db0ca8SKalle Valo /*==============================================*/
162730db0ca8SKalle Valo /*========== EMMH ROUTINES  ====================*/
162830db0ca8SKalle Valo /*==============================================*/
162930db0ca8SKalle Valo 
163030db0ca8SKalle Valo /* mic accumulate */
163130db0ca8SKalle Valo #define MIC_ACCUM(val)	\
1632e5db0ad7SArd Biesheuvel 	context->accum += (u64)(val) * be32_to_cpu(context->coeff[coeff_position++]);
163330db0ca8SKalle Valo 
163430db0ca8SKalle Valo /* expand the key to fill the MMH coefficient array */
163530db0ca8SKalle Valo static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen,
1636e5db0ad7SArd Biesheuvel 			   struct crypto_sync_skcipher *tfm)
163730db0ca8SKalle Valo {
163830db0ca8SKalle Valo   /* take the keying material, expand if necessary, truncate at 16-bytes */
163930db0ca8SKalle Valo   /* run through AES counter mode to generate context->coeff[] */
164030db0ca8SKalle Valo 
1641e5db0ad7SArd Biesheuvel 	SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
1642e5db0ad7SArd Biesheuvel 	struct scatterlist sg;
1643e5db0ad7SArd Biesheuvel 	u8 iv[AES_BLOCK_SIZE] = {};
1644e5db0ad7SArd Biesheuvel 	int ret;
164530db0ca8SKalle Valo 
1646e5db0ad7SArd Biesheuvel 	crypto_sync_skcipher_setkey(tfm, pkey, 16);
1647e5db0ad7SArd Biesheuvel 
1648e5db0ad7SArd Biesheuvel 	memset(context->coeff, 0, sizeof(context->coeff));
1649e5db0ad7SArd Biesheuvel 	sg_init_one(&sg, context->coeff, sizeof(context->coeff));
1650e5db0ad7SArd Biesheuvel 
1651e5db0ad7SArd Biesheuvel 	skcipher_request_set_sync_tfm(req, tfm);
1652e5db0ad7SArd Biesheuvel 	skcipher_request_set_callback(req, 0, NULL, NULL);
1653e5db0ad7SArd Biesheuvel 	skcipher_request_set_crypt(req, &sg, &sg, sizeof(context->coeff), iv);
1654e5db0ad7SArd Biesheuvel 
1655e5db0ad7SArd Biesheuvel 	ret = crypto_skcipher_encrypt(req);
1656e5db0ad7SArd Biesheuvel 	WARN_ON_ONCE(ret);
165730db0ca8SKalle Valo }
165830db0ca8SKalle Valo 
165930db0ca8SKalle Valo /* prepare for calculation of a new mic */
166030db0ca8SKalle Valo static void emmh32_init(emmh32_context *context)
166130db0ca8SKalle Valo {
166230db0ca8SKalle Valo 	/* prepare for new mic calculation */
166330db0ca8SKalle Valo 	context->accum = 0;
166430db0ca8SKalle Valo 	context->position = 0;
166530db0ca8SKalle Valo }
166630db0ca8SKalle Valo 
166730db0ca8SKalle Valo /* add some bytes to the mic calculation */
166830db0ca8SKalle Valo static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
166930db0ca8SKalle Valo {
167030db0ca8SKalle Valo 	int	coeff_position, byte_position;
167130db0ca8SKalle Valo 
167230db0ca8SKalle Valo 	if (len == 0) return;
167330db0ca8SKalle Valo 
167430db0ca8SKalle Valo 	coeff_position = context->position >> 2;
167530db0ca8SKalle Valo 
167630db0ca8SKalle Valo 	/* deal with partial 32-bit word left over from last update */
167730db0ca8SKalle Valo 	byte_position = context->position & 3;
167830db0ca8SKalle Valo 	if (byte_position) {
167930db0ca8SKalle Valo 		/* have a partial word in part to deal with */
168030db0ca8SKalle Valo 		do {
168130db0ca8SKalle Valo 			if (len == 0) return;
168230db0ca8SKalle Valo 			context->part.d8[byte_position++] = *pOctets++;
168330db0ca8SKalle Valo 			context->position++;
168430db0ca8SKalle Valo 			len--;
168530db0ca8SKalle Valo 		} while (byte_position < 4);
168630db0ca8SKalle Valo 		MIC_ACCUM(ntohl(context->part.d32));
168730db0ca8SKalle Valo 	}
168830db0ca8SKalle Valo 
168930db0ca8SKalle Valo 	/* deal with full 32-bit words */
169030db0ca8SKalle Valo 	while (len >= 4) {
169130db0ca8SKalle Valo 		MIC_ACCUM(ntohl(*(__be32 *)pOctets));
169230db0ca8SKalle Valo 		context->position += 4;
169330db0ca8SKalle Valo 		pOctets += 4;
169430db0ca8SKalle Valo 		len -= 4;
169530db0ca8SKalle Valo 	}
169630db0ca8SKalle Valo 
169730db0ca8SKalle Valo 	/* deal with partial 32-bit word that will be left over from this update */
169830db0ca8SKalle Valo 	byte_position = 0;
169930db0ca8SKalle Valo 	while (len > 0) {
170030db0ca8SKalle Valo 		context->part.d8[byte_position++] = *pOctets++;
170130db0ca8SKalle Valo 		context->position++;
170230db0ca8SKalle Valo 		len--;
170330db0ca8SKalle Valo 	}
170430db0ca8SKalle Valo }
170530db0ca8SKalle Valo 
170630db0ca8SKalle Valo /* mask used to zero empty bytes for final partial word */
170730db0ca8SKalle Valo static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
170830db0ca8SKalle Valo 
170930db0ca8SKalle Valo /* calculate the mic */
171030db0ca8SKalle Valo static void emmh32_final(emmh32_context *context, u8 digest[4])
171130db0ca8SKalle Valo {
171230db0ca8SKalle Valo 	int	coeff_position, byte_position;
171330db0ca8SKalle Valo 	u32	val;
171430db0ca8SKalle Valo 
171530db0ca8SKalle Valo 	u64 sum, utmp;
171630db0ca8SKalle Valo 	s64 stmp;
171730db0ca8SKalle Valo 
171830db0ca8SKalle Valo 	coeff_position = context->position >> 2;
171930db0ca8SKalle Valo 
172030db0ca8SKalle Valo 	/* deal with partial 32-bit word left over from last update */
172130db0ca8SKalle Valo 	byte_position = context->position & 3;
172230db0ca8SKalle Valo 	if (byte_position) {
172330db0ca8SKalle Valo 		/* have a partial word in part to deal with */
172430db0ca8SKalle Valo 		val = ntohl(context->part.d32);
172530db0ca8SKalle Valo 		MIC_ACCUM(val & mask32[byte_position]);	/* zero empty bytes */
172630db0ca8SKalle Valo 	}
172730db0ca8SKalle Valo 
172830db0ca8SKalle Valo 	/* reduce the accumulated u64 to a 32-bit MIC */
172930db0ca8SKalle Valo 	sum = context->accum;
173030db0ca8SKalle Valo 	stmp = (sum  & 0xffffffffLL) - ((sum >> 32)  * 15);
173130db0ca8SKalle Valo 	utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15);
173230db0ca8SKalle Valo 	sum = utmp & 0xffffffffLL;
173330db0ca8SKalle Valo 	if (utmp > 0x10000000fLL)
173430db0ca8SKalle Valo 		sum -= 15;
173530db0ca8SKalle Valo 
173630db0ca8SKalle Valo 	val = (u32)sum;
173730db0ca8SKalle Valo 	digest[0] = (val>>24) & 0xFF;
173830db0ca8SKalle Valo 	digest[1] = (val>>16) & 0xFF;
173930db0ca8SKalle Valo 	digest[2] = (val>>8) & 0xFF;
174030db0ca8SKalle Valo 	digest[3] = val & 0xFF;
174130db0ca8SKalle Valo }
174230db0ca8SKalle Valo 
174330db0ca8SKalle Valo static int readBSSListRid(struct airo_info *ai, int first,
174430db0ca8SKalle Valo 		      BSSListRid *list)
174530db0ca8SKalle Valo {
174630db0ca8SKalle Valo 	Cmd cmd;
174730db0ca8SKalle Valo 	Resp rsp;
174830db0ca8SKalle Valo 
174930db0ca8SKalle Valo 	if (first == 1) {
175030db0ca8SKalle Valo 		if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
175130db0ca8SKalle Valo 		memset(&cmd, 0, sizeof(cmd));
175230db0ca8SKalle Valo 		cmd.cmd = CMD_LISTBSS;
175330db0ca8SKalle Valo 		if (down_interruptible(&ai->sem))
175430db0ca8SKalle Valo 			return -ERESTARTSYS;
175530db0ca8SKalle Valo 		ai->list_bss_task = current;
175630db0ca8SKalle Valo 		issuecommand(ai, &cmd, &rsp);
175730db0ca8SKalle Valo 		up(&ai->sem);
175830db0ca8SKalle Valo 		/* Let the command take effect */
175930db0ca8SKalle Valo 		schedule_timeout_uninterruptible(3 * HZ);
176030db0ca8SKalle Valo 		ai->list_bss_task = NULL;
176130db0ca8SKalle Valo 	}
176230db0ca8SKalle Valo 	return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext,
176330db0ca8SKalle Valo 			    list, ai->bssListRidLen, 1);
176430db0ca8SKalle Valo }
176530db0ca8SKalle Valo 
176630db0ca8SKalle Valo static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock)
176730db0ca8SKalle Valo {
176830db0ca8SKalle Valo 	return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM,
176930db0ca8SKalle Valo 				wkr, sizeof(*wkr), lock);
177030db0ca8SKalle Valo }
177130db0ca8SKalle Valo 
177230db0ca8SKalle Valo static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock)
177330db0ca8SKalle Valo {
177430db0ca8SKalle Valo 	int rc;
177530db0ca8SKalle Valo 	rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock);
177630db0ca8SKalle Valo 	if (rc!=SUCCESS)
177730db0ca8SKalle Valo 		airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc);
177830db0ca8SKalle Valo 	if (perm) {
177930db0ca8SKalle Valo 		rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock);
178030db0ca8SKalle Valo 		if (rc!=SUCCESS)
178130db0ca8SKalle Valo 			airo_print_err(ai->dev->name, "WEP_PERM set %x", rc);
178230db0ca8SKalle Valo 	}
178330db0ca8SKalle Valo 	return rc;
178430db0ca8SKalle Valo }
178530db0ca8SKalle Valo 
178630db0ca8SKalle Valo static int readSsidRid(struct airo_info*ai, SsidRid *ssidr)
178730db0ca8SKalle Valo {
178830db0ca8SKalle Valo 	return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1);
178930db0ca8SKalle Valo }
179030db0ca8SKalle Valo 
179130db0ca8SKalle Valo static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock)
179230db0ca8SKalle Valo {
179330db0ca8SKalle Valo 	return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock);
179430db0ca8SKalle Valo }
179530db0ca8SKalle Valo 
179630db0ca8SKalle Valo static int readConfigRid(struct airo_info *ai, int lock)
179730db0ca8SKalle Valo {
179830db0ca8SKalle Valo 	int rc;
179930db0ca8SKalle Valo 	ConfigRid cfg;
180030db0ca8SKalle Valo 
180130db0ca8SKalle Valo 	if (ai->config.len)
180230db0ca8SKalle Valo 		return SUCCESS;
180330db0ca8SKalle Valo 
180430db0ca8SKalle Valo 	rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock);
180530db0ca8SKalle Valo 	if (rc != SUCCESS)
180630db0ca8SKalle Valo 		return rc;
180730db0ca8SKalle Valo 
180830db0ca8SKalle Valo 	ai->config = cfg;
180930db0ca8SKalle Valo 	return SUCCESS;
181030db0ca8SKalle Valo }
181130db0ca8SKalle Valo 
181230db0ca8SKalle Valo static inline void checkThrottle(struct airo_info *ai)
181330db0ca8SKalle Valo {
181430db0ca8SKalle Valo 	int i;
181530db0ca8SKalle Valo /* Old hardware had a limit on encryption speed */
181630db0ca8SKalle Valo 	if (ai->config.authType != AUTH_OPEN && maxencrypt) {
181730db0ca8SKalle Valo 		for (i = 0; i<8; i++) {
181830db0ca8SKalle Valo 			if (ai->config.rates[i] > maxencrypt) {
181930db0ca8SKalle Valo 				ai->config.rates[i] = 0;
182030db0ca8SKalle Valo 			}
182130db0ca8SKalle Valo 		}
182230db0ca8SKalle Valo 	}
182330db0ca8SKalle Valo }
182430db0ca8SKalle Valo 
182530db0ca8SKalle Valo static int writeConfigRid(struct airo_info *ai, int lock)
182630db0ca8SKalle Valo {
182730db0ca8SKalle Valo 	ConfigRid cfgr;
182830db0ca8SKalle Valo 
182930db0ca8SKalle Valo 	if (!test_bit (FLAG_COMMIT, &ai->flags))
183030db0ca8SKalle Valo 		return SUCCESS;
183130db0ca8SKalle Valo 
183230db0ca8SKalle Valo 	clear_bit (FLAG_COMMIT, &ai->flags);
183330db0ca8SKalle Valo 	clear_bit (FLAG_RESET, &ai->flags);
183430db0ca8SKalle Valo 	checkThrottle(ai);
183530db0ca8SKalle Valo 	cfgr = ai->config;
183630db0ca8SKalle Valo 
183730db0ca8SKalle Valo 	if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
183830db0ca8SKalle Valo 		set_bit(FLAG_ADHOC, &ai->flags);
183930db0ca8SKalle Valo 	else
184030db0ca8SKalle Valo 		clear_bit(FLAG_ADHOC, &ai->flags);
184130db0ca8SKalle Valo 
184230db0ca8SKalle Valo 	return PC4500_writerid(ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock);
184330db0ca8SKalle Valo }
184430db0ca8SKalle Valo 
184530db0ca8SKalle Valo static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock)
184630db0ca8SKalle Valo {
184730db0ca8SKalle Valo 	return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock);
184830db0ca8SKalle Valo }
184930db0ca8SKalle Valo 
185030db0ca8SKalle Valo static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock)
185130db0ca8SKalle Valo {
185230db0ca8SKalle Valo 	return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock);
185330db0ca8SKalle Valo }
185430db0ca8SKalle Valo 
185530db0ca8SKalle Valo static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock)
185630db0ca8SKalle Valo {
185730db0ca8SKalle Valo 	return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock);
185830db0ca8SKalle Valo }
185930db0ca8SKalle Valo 
186030db0ca8SKalle Valo static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock)
186130db0ca8SKalle Valo {
186230db0ca8SKalle Valo 	return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock);
186330db0ca8SKalle Valo }
186430db0ca8SKalle Valo 
186530db0ca8SKalle Valo static void try_auto_wep(struct airo_info *ai)
186630db0ca8SKalle Valo {
186730db0ca8SKalle Valo 	if (auto_wep && !test_bit(FLAG_RADIO_DOWN, &ai->flags)) {
186830db0ca8SKalle Valo 		ai->expires = RUN_AT(3*HZ);
186930db0ca8SKalle Valo 		wake_up_interruptible(&ai->thr_wait);
187030db0ca8SKalle Valo 	}
187130db0ca8SKalle Valo }
187230db0ca8SKalle Valo 
1873ba4d6513SLee Jones static int airo_open(struct net_device *dev)
1874ba4d6513SLee Jones {
187530db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
187630db0ca8SKalle Valo 	int rc = 0;
187730db0ca8SKalle Valo 
187830db0ca8SKalle Valo 	if (test_bit(FLAG_FLASHING, &ai->flags))
187930db0ca8SKalle Valo 		return -EIO;
188030db0ca8SKalle Valo 
188130db0ca8SKalle Valo 	/* Make sure the card is configured.
188230db0ca8SKalle Valo 	 * Wireless Extensions may postpone config changes until the card
188330db0ca8SKalle Valo 	 * is open (to pipeline changes and speed-up card setup). If
188430db0ca8SKalle Valo 	 * those changes are not yet committed, do it now - Jean II */
188530db0ca8SKalle Valo 	if (test_bit(FLAG_COMMIT, &ai->flags)) {
188630db0ca8SKalle Valo 		disable_MAC(ai, 1);
188730db0ca8SKalle Valo 		writeConfigRid(ai, 1);
188830db0ca8SKalle Valo 	}
188930db0ca8SKalle Valo 
189030db0ca8SKalle Valo 	if (ai->wifidev != dev) {
189130db0ca8SKalle Valo 		clear_bit(JOB_DIE, &ai->jobs);
189230db0ca8SKalle Valo 		ai->airo_thread_task = kthread_run(airo_thread, dev, "%s",
189330db0ca8SKalle Valo 						   dev->name);
189430db0ca8SKalle Valo 		if (IS_ERR(ai->airo_thread_task))
189530db0ca8SKalle Valo 			return (int)PTR_ERR(ai->airo_thread_task);
189630db0ca8SKalle Valo 
189730db0ca8SKalle Valo 		rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED,
189830db0ca8SKalle Valo 			dev->name, dev);
189930db0ca8SKalle Valo 		if (rc) {
190030db0ca8SKalle Valo 			airo_print_err(dev->name,
190130db0ca8SKalle Valo 				"register interrupt %d failed, rc %d",
190230db0ca8SKalle Valo 				dev->irq, rc);
190330db0ca8SKalle Valo 			set_bit(JOB_DIE, &ai->jobs);
190430db0ca8SKalle Valo 			kthread_stop(ai->airo_thread_task);
190530db0ca8SKalle Valo 			return rc;
190630db0ca8SKalle Valo 		}
190730db0ca8SKalle Valo 
190830db0ca8SKalle Valo 		/* Power on the MAC controller (which may have been disabled) */
190930db0ca8SKalle Valo 		clear_bit(FLAG_RADIO_DOWN, &ai->flags);
191030db0ca8SKalle Valo 		enable_interrupts(ai);
191130db0ca8SKalle Valo 
191230db0ca8SKalle Valo 		try_auto_wep(ai);
191330db0ca8SKalle Valo 	}
191430db0ca8SKalle Valo 	enable_MAC(ai, 1);
191530db0ca8SKalle Valo 
191630db0ca8SKalle Valo 	netif_start_queue(dev);
191730db0ca8SKalle Valo 	return 0;
191830db0ca8SKalle Valo }
191930db0ca8SKalle Valo 
192030db0ca8SKalle Valo static netdev_tx_t mpi_start_xmit(struct sk_buff *skb,
192130db0ca8SKalle Valo 					struct net_device *dev)
192230db0ca8SKalle Valo {
192330db0ca8SKalle Valo 	int npacks, pending;
192430db0ca8SKalle Valo 	unsigned long flags;
192530db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
192630db0ca8SKalle Valo 
192730db0ca8SKalle Valo 	if (!skb) {
192830db0ca8SKalle Valo 		airo_print_err(dev->name, "%s: skb == NULL!",__func__);
192930db0ca8SKalle Valo 		return NETDEV_TX_OK;
193030db0ca8SKalle Valo 	}
193111e7a919SDan Carpenter 	if (skb_padto(skb, ETH_ZLEN)) {
193211e7a919SDan Carpenter 		dev->stats.tx_dropped++;
193311e7a919SDan Carpenter 		return NETDEV_TX_OK;
193411e7a919SDan Carpenter 	}
193530db0ca8SKalle Valo 	npacks = skb_queue_len (&ai->txq);
193630db0ca8SKalle Valo 
193730db0ca8SKalle Valo 	if (npacks >= MAXTXQ - 1) {
193830db0ca8SKalle Valo 		netif_stop_queue (dev);
193930db0ca8SKalle Valo 		if (npacks > MAXTXQ) {
194030db0ca8SKalle Valo 			dev->stats.tx_fifo_errors++;
194130db0ca8SKalle Valo 			return NETDEV_TX_BUSY;
194230db0ca8SKalle Valo 		}
194330db0ca8SKalle Valo 		skb_queue_tail (&ai->txq, skb);
194430db0ca8SKalle Valo 		return NETDEV_TX_OK;
194530db0ca8SKalle Valo 	}
194630db0ca8SKalle Valo 
194730db0ca8SKalle Valo 	spin_lock_irqsave(&ai->aux_lock, flags);
194830db0ca8SKalle Valo 	skb_queue_tail (&ai->txq, skb);
194930db0ca8SKalle Valo 	pending = test_bit(FLAG_PENDING_XMIT, &ai->flags);
195030db0ca8SKalle Valo 	spin_unlock_irqrestore(&ai->aux_lock, flags);
195130db0ca8SKalle Valo 	netif_wake_queue (dev);
195230db0ca8SKalle Valo 
195330db0ca8SKalle Valo 	if (pending == 0) {
195430db0ca8SKalle Valo 		set_bit(FLAG_PENDING_XMIT, &ai->flags);
195530db0ca8SKalle Valo 		mpi_send_packet (dev);
195630db0ca8SKalle Valo 	}
195730db0ca8SKalle Valo 	return NETDEV_TX_OK;
195830db0ca8SKalle Valo }
195930db0ca8SKalle Valo 
196030db0ca8SKalle Valo /*
196130db0ca8SKalle Valo  * @mpi_send_packet
196230db0ca8SKalle Valo  *
196330db0ca8SKalle Valo  * Attempt to transmit a packet. Can be called from interrupt
196430db0ca8SKalle Valo  * or transmit . return number of packets we tried to send
196530db0ca8SKalle Valo  */
196630db0ca8SKalle Valo 
196730db0ca8SKalle Valo static int mpi_send_packet (struct net_device *dev)
196830db0ca8SKalle Valo {
196930db0ca8SKalle Valo 	struct sk_buff *skb;
197030db0ca8SKalle Valo 	unsigned char *buffer;
197130db0ca8SKalle Valo 	s16 len;
197230db0ca8SKalle Valo 	__le16 *payloadLen;
197330db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
197430db0ca8SKalle Valo 	u8 *sendbuf;
197530db0ca8SKalle Valo 
197630db0ca8SKalle Valo 	/* get a packet to send */
197730db0ca8SKalle Valo 
197830db0ca8SKalle Valo 	if ((skb = skb_dequeue(&ai->txq)) == NULL) {
197930db0ca8SKalle Valo 		airo_print_err(dev->name,
198030db0ca8SKalle Valo 			"%s: Dequeue'd zero in send_packet()",
198130db0ca8SKalle Valo 			__func__);
198230db0ca8SKalle Valo 		return 0;
198330db0ca8SKalle Valo 	}
198430db0ca8SKalle Valo 
198530db0ca8SKalle Valo 	/* check min length*/
198630db0ca8SKalle Valo 	len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
198730db0ca8SKalle Valo 	buffer = skb->data;
198830db0ca8SKalle Valo 
198930db0ca8SKalle Valo 	ai->txfids[0].tx_desc.offset = 0;
199030db0ca8SKalle Valo 	ai->txfids[0].tx_desc.valid = 1;
199130db0ca8SKalle Valo 	ai->txfids[0].tx_desc.eoc = 1;
199230db0ca8SKalle Valo 	ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr);
199330db0ca8SKalle Valo 
199430db0ca8SKalle Valo /*
199530db0ca8SKalle Valo  * Magic, the cards firmware needs a length count (2 bytes) in the host buffer
199630db0ca8SKalle Valo  * right after  TXFID_HDR.The TXFID_HDR contains the status short so payloadlen
199730db0ca8SKalle Valo  * is immediately after it. ------------------------------------------------
199830db0ca8SKalle Valo  *                         |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA|
199930db0ca8SKalle Valo  *                         ------------------------------------------------
200030db0ca8SKalle Valo  */
200130db0ca8SKalle Valo 
200230db0ca8SKalle Valo 	memcpy(ai->txfids[0].virtual_host_addr,
200330db0ca8SKalle Valo 		(char *)&wifictlhdr8023, sizeof(wifictlhdr8023));
200430db0ca8SKalle Valo 
200530db0ca8SKalle Valo 	payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr +
200630db0ca8SKalle Valo 		sizeof(wifictlhdr8023));
200730db0ca8SKalle Valo 	sendbuf = ai->txfids[0].virtual_host_addr +
200830db0ca8SKalle Valo 		sizeof(wifictlhdr8023) + 2 ;
200930db0ca8SKalle Valo 
201030db0ca8SKalle Valo 	/*
201130db0ca8SKalle Valo 	 * Firmware automatically puts 802 header on so
201230db0ca8SKalle Valo 	 * we don't need to account for it in the length
201330db0ca8SKalle Valo 	 */
201430db0ca8SKalle Valo 	if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
201530db0ca8SKalle Valo 		(ntohs(((__be16 *)buffer)[6]) != 0x888E)) {
201630db0ca8SKalle Valo 		MICBuffer pMic;
201730db0ca8SKalle Valo 
201830db0ca8SKalle Valo 		if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS)
201930db0ca8SKalle Valo 			return ERROR;
202030db0ca8SKalle Valo 
202130db0ca8SKalle Valo 		*payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic));
202230db0ca8SKalle Valo 		ai->txfids[0].tx_desc.len += sizeof(pMic);
202330db0ca8SKalle Valo 		/* copy data into airo dma buffer */
202430db0ca8SKalle Valo 		memcpy (sendbuf, buffer, sizeof(etherHead));
202530db0ca8SKalle Valo 		buffer += sizeof(etherHead);
202630db0ca8SKalle Valo 		sendbuf += sizeof(etherHead);
202730db0ca8SKalle Valo 		memcpy (sendbuf, &pMic, sizeof(pMic));
202830db0ca8SKalle Valo 		sendbuf += sizeof(pMic);
202930db0ca8SKalle Valo 		memcpy (sendbuf, buffer, len - sizeof(etherHead));
203030db0ca8SKalle Valo 	} else {
203130db0ca8SKalle Valo 		*payloadLen = cpu_to_le16(len - sizeof(etherHead));
203230db0ca8SKalle Valo 
2033860e9538SFlorian Westphal 		netif_trans_update(dev);
203430db0ca8SKalle Valo 
203530db0ca8SKalle Valo 		/* copy data into airo dma buffer */
203630db0ca8SKalle Valo 		memcpy(sendbuf, buffer, len);
203730db0ca8SKalle Valo 	}
203830db0ca8SKalle Valo 
203930db0ca8SKalle Valo 	memcpy_toio(ai->txfids[0].card_ram_off,
204030db0ca8SKalle Valo 		&ai->txfids[0].tx_desc, sizeof(TxFid));
204130db0ca8SKalle Valo 
204230db0ca8SKalle Valo 	OUT4500(ai, EVACK, 8);
204330db0ca8SKalle Valo 
204430db0ca8SKalle Valo 	dev_kfree_skb_any(skb);
204530db0ca8SKalle Valo 	return 1;
204630db0ca8SKalle Valo }
204730db0ca8SKalle Valo 
204830db0ca8SKalle Valo static void get_tx_error(struct airo_info *ai, s32 fid)
204930db0ca8SKalle Valo {
205030db0ca8SKalle Valo 	__le16 status;
205130db0ca8SKalle Valo 
205230db0ca8SKalle Valo 	if (fid < 0)
205330db0ca8SKalle Valo 		status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status;
205430db0ca8SKalle Valo 	else {
205530db0ca8SKalle Valo 		if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS)
205630db0ca8SKalle Valo 			return;
205730db0ca8SKalle Valo 		bap_read(ai, &status, 2, BAP0);
205830db0ca8SKalle Valo 	}
205930db0ca8SKalle Valo 	if (le16_to_cpu(status) & 2) /* Too many retries */
206030db0ca8SKalle Valo 		ai->dev->stats.tx_aborted_errors++;
206130db0ca8SKalle Valo 	if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */
206230db0ca8SKalle Valo 		ai->dev->stats.tx_heartbeat_errors++;
206330db0ca8SKalle Valo 	if (le16_to_cpu(status) & 8) /* Aid fail */
206430db0ca8SKalle Valo 		{ }
206530db0ca8SKalle Valo 	if (le16_to_cpu(status) & 0x10) /* MAC disabled */
206630db0ca8SKalle Valo 		ai->dev->stats.tx_carrier_errors++;
206730db0ca8SKalle Valo 	if (le16_to_cpu(status) & 0x20) /* Association lost */
206830db0ca8SKalle Valo 		{ }
206930db0ca8SKalle Valo 	/* We produce a TXDROP event only for retry or lifetime
207030db0ca8SKalle Valo 	 * exceeded, because that's the only status that really mean
207130db0ca8SKalle Valo 	 * that this particular node went away.
207230db0ca8SKalle Valo 	 * Other errors means that *we* screwed up. - Jean II */
207330db0ca8SKalle Valo 	if ((le16_to_cpu(status) & 2) ||
207430db0ca8SKalle Valo 	     (le16_to_cpu(status) & 4)) {
207530db0ca8SKalle Valo 		union iwreq_data	wrqu;
207630db0ca8SKalle Valo 		char junk[0x18];
207730db0ca8SKalle Valo 
207830db0ca8SKalle Valo 		/* Faster to skip over useless data than to do
207930db0ca8SKalle Valo 		 * another bap_setup(). We are at offset 0x6 and
208030db0ca8SKalle Valo 		 * need to go to 0x18 and read 6 bytes - Jean II */
208130db0ca8SKalle Valo 		bap_read(ai, (__le16 *) junk, 0x18, BAP0);
208230db0ca8SKalle Valo 
208330db0ca8SKalle Valo 		/* Copy 802.11 dest address.
208430db0ca8SKalle Valo 		 * We use the 802.11 header because the frame may
208530db0ca8SKalle Valo 		 * not be 802.3 or may be mangled...
208630db0ca8SKalle Valo 		 * In Ad-Hoc mode, it will be the node address.
208730db0ca8SKalle Valo 		 * In managed mode, it will be most likely the AP addr
208830db0ca8SKalle Valo 		 * User space will figure out how to convert it to
208930db0ca8SKalle Valo 		 * whatever it needs (IP address or else).
209030db0ca8SKalle Valo 		 * - Jean II */
209130db0ca8SKalle Valo 		memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN);
209230db0ca8SKalle Valo 		wrqu.addr.sa_family = ARPHRD_ETHER;
209330db0ca8SKalle Valo 
209430db0ca8SKalle Valo 		/* Send event to user space */
209530db0ca8SKalle Valo 		wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL);
209630db0ca8SKalle Valo 	}
209730db0ca8SKalle Valo }
209830db0ca8SKalle Valo 
2099ba4d6513SLee Jones static void airo_end_xmit(struct net_device *dev)
2100ba4d6513SLee Jones {
210130db0ca8SKalle Valo 	u16 status;
210230db0ca8SKalle Valo 	int i;
210330db0ca8SKalle Valo 	struct airo_info *priv = dev->ml_priv;
210430db0ca8SKalle Valo 	struct sk_buff *skb = priv->xmit.skb;
210530db0ca8SKalle Valo 	int fid = priv->xmit.fid;
210630db0ca8SKalle Valo 	u32 *fids = priv->fids;
210730db0ca8SKalle Valo 
210830db0ca8SKalle Valo 	clear_bit(JOB_XMIT, &priv->jobs);
210930db0ca8SKalle Valo 	clear_bit(FLAG_PENDING_XMIT, &priv->flags);
211030db0ca8SKalle Valo 	status = transmit_802_3_packet (priv, fids[fid], skb->data);
211130db0ca8SKalle Valo 	up(&priv->sem);
211230db0ca8SKalle Valo 
211330db0ca8SKalle Valo 	i = 0;
211430db0ca8SKalle Valo 	if (status == SUCCESS) {
2115860e9538SFlorian Westphal 		netif_trans_update(dev);
211630db0ca8SKalle Valo 		for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++);
211730db0ca8SKalle Valo 	} else {
211830db0ca8SKalle Valo 		priv->fids[fid] &= 0xffff;
211930db0ca8SKalle Valo 		dev->stats.tx_window_errors++;
212030db0ca8SKalle Valo 	}
212130db0ca8SKalle Valo 	if (i < MAX_FIDS / 2)
212230db0ca8SKalle Valo 		netif_wake_queue(dev);
212330db0ca8SKalle Valo 	dev_kfree_skb(skb);
212430db0ca8SKalle Valo }
212530db0ca8SKalle Valo 
212630db0ca8SKalle Valo static netdev_tx_t airo_start_xmit(struct sk_buff *skb,
212730db0ca8SKalle Valo 					 struct net_device *dev)
212830db0ca8SKalle Valo {
212930db0ca8SKalle Valo 	s16 len;
213030db0ca8SKalle Valo 	int i, j;
213130db0ca8SKalle Valo 	struct airo_info *priv = dev->ml_priv;
213230db0ca8SKalle Valo 	u32 *fids = priv->fids;
213330db0ca8SKalle Valo 
213430db0ca8SKalle Valo 	if (skb == NULL) {
213530db0ca8SKalle Valo 		airo_print_err(dev->name, "%s: skb == NULL!", __func__);
213630db0ca8SKalle Valo 		return NETDEV_TX_OK;
213730db0ca8SKalle Valo 	}
213811e7a919SDan Carpenter 	if (skb_padto(skb, ETH_ZLEN)) {
213911e7a919SDan Carpenter 		dev->stats.tx_dropped++;
214011e7a919SDan Carpenter 		return NETDEV_TX_OK;
214111e7a919SDan Carpenter 	}
214230db0ca8SKalle Valo 
214330db0ca8SKalle Valo 	/* Find a vacant FID */
214430db0ca8SKalle Valo 	for (i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++);
214530db0ca8SKalle Valo 	for (j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++);
214630db0ca8SKalle Valo 
214730db0ca8SKalle Valo 	if (j >= MAX_FIDS / 2) {
214830db0ca8SKalle Valo 		netif_stop_queue(dev);
214930db0ca8SKalle Valo 
215030db0ca8SKalle Valo 		if (i == MAX_FIDS / 2) {
215130db0ca8SKalle Valo 			dev->stats.tx_fifo_errors++;
215230db0ca8SKalle Valo 			return NETDEV_TX_BUSY;
215330db0ca8SKalle Valo 		}
215430db0ca8SKalle Valo 	}
215530db0ca8SKalle Valo 	/* check min length*/
215630db0ca8SKalle Valo 	len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
215730db0ca8SKalle Valo         /* Mark fid as used & save length for later */
215830db0ca8SKalle Valo 	fids[i] |= (len << 16);
215930db0ca8SKalle Valo 	priv->xmit.skb = skb;
216030db0ca8SKalle Valo 	priv->xmit.fid = i;
216130db0ca8SKalle Valo 	if (down_trylock(&priv->sem) != 0) {
216230db0ca8SKalle Valo 		set_bit(FLAG_PENDING_XMIT, &priv->flags);
216330db0ca8SKalle Valo 		netif_stop_queue(dev);
216430db0ca8SKalle Valo 		set_bit(JOB_XMIT, &priv->jobs);
216530db0ca8SKalle Valo 		wake_up_interruptible(&priv->thr_wait);
216630db0ca8SKalle Valo 	} else
216730db0ca8SKalle Valo 		airo_end_xmit(dev);
216830db0ca8SKalle Valo 	return NETDEV_TX_OK;
216930db0ca8SKalle Valo }
217030db0ca8SKalle Valo 
2171ba4d6513SLee Jones static void airo_end_xmit11(struct net_device *dev)
2172ba4d6513SLee Jones {
217330db0ca8SKalle Valo 	u16 status;
217430db0ca8SKalle Valo 	int i;
217530db0ca8SKalle Valo 	struct airo_info *priv = dev->ml_priv;
217630db0ca8SKalle Valo 	struct sk_buff *skb = priv->xmit11.skb;
217730db0ca8SKalle Valo 	int fid = priv->xmit11.fid;
217830db0ca8SKalle Valo 	u32 *fids = priv->fids;
217930db0ca8SKalle Valo 
218030db0ca8SKalle Valo 	clear_bit(JOB_XMIT11, &priv->jobs);
218130db0ca8SKalle Valo 	clear_bit(FLAG_PENDING_XMIT11, &priv->flags);
218230db0ca8SKalle Valo 	status = transmit_802_11_packet (priv, fids[fid], skb->data);
218330db0ca8SKalle Valo 	up(&priv->sem);
218430db0ca8SKalle Valo 
218530db0ca8SKalle Valo 	i = MAX_FIDS / 2;
218630db0ca8SKalle Valo 	if (status == SUCCESS) {
2187860e9538SFlorian Westphal 		netif_trans_update(dev);
218830db0ca8SKalle Valo 		for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++);
218930db0ca8SKalle Valo 	} else {
219030db0ca8SKalle Valo 		priv->fids[fid] &= 0xffff;
219130db0ca8SKalle Valo 		dev->stats.tx_window_errors++;
219230db0ca8SKalle Valo 	}
219330db0ca8SKalle Valo 	if (i < MAX_FIDS)
219430db0ca8SKalle Valo 		netif_wake_queue(dev);
219530db0ca8SKalle Valo 	dev_kfree_skb(skb);
219630db0ca8SKalle Valo }
219730db0ca8SKalle Valo 
219830db0ca8SKalle Valo static netdev_tx_t airo_start_xmit11(struct sk_buff *skb,
219930db0ca8SKalle Valo 					   struct net_device *dev)
220030db0ca8SKalle Valo {
220130db0ca8SKalle Valo 	s16 len;
220230db0ca8SKalle Valo 	int i, j;
220330db0ca8SKalle Valo 	struct airo_info *priv = dev->ml_priv;
220430db0ca8SKalle Valo 	u32 *fids = priv->fids;
220530db0ca8SKalle Valo 
220630db0ca8SKalle Valo 	if (test_bit(FLAG_MPI, &priv->flags)) {
220730db0ca8SKalle Valo 		/* Not implemented yet for MPI350 */
220830db0ca8SKalle Valo 		netif_stop_queue(dev);
220930db0ca8SKalle Valo 		dev_kfree_skb_any(skb);
221030db0ca8SKalle Valo 		return NETDEV_TX_OK;
221130db0ca8SKalle Valo 	}
221230db0ca8SKalle Valo 
221330db0ca8SKalle Valo 	if (skb == NULL) {
221430db0ca8SKalle Valo 		airo_print_err(dev->name, "%s: skb == NULL!", __func__);
221530db0ca8SKalle Valo 		return NETDEV_TX_OK;
221630db0ca8SKalle Valo 	}
221711e7a919SDan Carpenter 	if (skb_padto(skb, ETH_ZLEN)) {
221811e7a919SDan Carpenter 		dev->stats.tx_dropped++;
221911e7a919SDan Carpenter 		return NETDEV_TX_OK;
222011e7a919SDan Carpenter 	}
222130db0ca8SKalle Valo 
222230db0ca8SKalle Valo 	/* Find a vacant FID */
222330db0ca8SKalle Valo 	for (i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++);
222430db0ca8SKalle Valo 	for (j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++);
222530db0ca8SKalle Valo 
222630db0ca8SKalle Valo 	if (j >= MAX_FIDS) {
222730db0ca8SKalle Valo 		netif_stop_queue(dev);
222830db0ca8SKalle Valo 
222930db0ca8SKalle Valo 		if (i == MAX_FIDS) {
223030db0ca8SKalle Valo 			dev->stats.tx_fifo_errors++;
223130db0ca8SKalle Valo 			return NETDEV_TX_BUSY;
223230db0ca8SKalle Valo 		}
223330db0ca8SKalle Valo 	}
223430db0ca8SKalle Valo 	/* check min length*/
223530db0ca8SKalle Valo 	len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
223630db0ca8SKalle Valo         /* Mark fid as used & save length for later */
223730db0ca8SKalle Valo 	fids[i] |= (len << 16);
223830db0ca8SKalle Valo 	priv->xmit11.skb = skb;
223930db0ca8SKalle Valo 	priv->xmit11.fid = i;
224030db0ca8SKalle Valo 	if (down_trylock(&priv->sem) != 0) {
224130db0ca8SKalle Valo 		set_bit(FLAG_PENDING_XMIT11, &priv->flags);
224230db0ca8SKalle Valo 		netif_stop_queue(dev);
224330db0ca8SKalle Valo 		set_bit(JOB_XMIT11, &priv->jobs);
224430db0ca8SKalle Valo 		wake_up_interruptible(&priv->thr_wait);
224530db0ca8SKalle Valo 	} else
224630db0ca8SKalle Valo 		airo_end_xmit11(dev);
224730db0ca8SKalle Valo 	return NETDEV_TX_OK;
224830db0ca8SKalle Valo }
224930db0ca8SKalle Valo 
225030db0ca8SKalle Valo static void airo_read_stats(struct net_device *dev)
225130db0ca8SKalle Valo {
225230db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
225330db0ca8SKalle Valo 	StatsRid stats_rid;
225430db0ca8SKalle Valo 	__le32 *vals = stats_rid.vals;
225530db0ca8SKalle Valo 
225630db0ca8SKalle Valo 	clear_bit(JOB_STATS, &ai->jobs);
225730db0ca8SKalle Valo 	if (ai->power.event) {
225830db0ca8SKalle Valo 		up(&ai->sem);
225930db0ca8SKalle Valo 		return;
226030db0ca8SKalle Valo 	}
226130db0ca8SKalle Valo 	readStatsRid(ai, &stats_rid, RID_STATS, 0);
226230db0ca8SKalle Valo 	up(&ai->sem);
226330db0ca8SKalle Valo 
226430db0ca8SKalle Valo 	dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) +
226530db0ca8SKalle Valo 			       le32_to_cpu(vals[45]);
226630db0ca8SKalle Valo 	dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) +
226730db0ca8SKalle Valo 			       le32_to_cpu(vals[41]);
226830db0ca8SKalle Valo 	dev->stats.rx_bytes = le32_to_cpu(vals[92]);
226930db0ca8SKalle Valo 	dev->stats.tx_bytes = le32_to_cpu(vals[91]);
227030db0ca8SKalle Valo 	dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) +
227130db0ca8SKalle Valo 			      le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]);
227230db0ca8SKalle Valo 	dev->stats.tx_errors = le32_to_cpu(vals[42]) +
227330db0ca8SKalle Valo 			      dev->stats.tx_fifo_errors;
227430db0ca8SKalle Valo 	dev->stats.multicast = le32_to_cpu(vals[43]);
227530db0ca8SKalle Valo 	dev->stats.collisions = le32_to_cpu(vals[89]);
227630db0ca8SKalle Valo 
227730db0ca8SKalle Valo 	/* detailed rx_errors: */
227830db0ca8SKalle Valo 	dev->stats.rx_length_errors = le32_to_cpu(vals[3]);
227930db0ca8SKalle Valo 	dev->stats.rx_crc_errors = le32_to_cpu(vals[4]);
228030db0ca8SKalle Valo 	dev->stats.rx_frame_errors = le32_to_cpu(vals[2]);
228130db0ca8SKalle Valo 	dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]);
228230db0ca8SKalle Valo }
228330db0ca8SKalle Valo 
228430db0ca8SKalle Valo static struct net_device_stats *airo_get_stats(struct net_device *dev)
228530db0ca8SKalle Valo {
228630db0ca8SKalle Valo 	struct airo_info *local =  dev->ml_priv;
228730db0ca8SKalle Valo 
228830db0ca8SKalle Valo 	if (!test_bit(JOB_STATS, &local->jobs)) {
228930db0ca8SKalle Valo 		set_bit(JOB_STATS, &local->jobs);
229030db0ca8SKalle Valo 		wake_up_interruptible(&local->thr_wait);
229130db0ca8SKalle Valo 	}
229230db0ca8SKalle Valo 
229330db0ca8SKalle Valo 	return &dev->stats;
229430db0ca8SKalle Valo }
229530db0ca8SKalle Valo 
2296ba4d6513SLee Jones static void airo_set_promisc(struct airo_info *ai)
2297ba4d6513SLee Jones {
229830db0ca8SKalle Valo 	Cmd cmd;
229930db0ca8SKalle Valo 	Resp rsp;
230030db0ca8SKalle Valo 
230130db0ca8SKalle Valo 	memset(&cmd, 0, sizeof(cmd));
230230db0ca8SKalle Valo 	cmd.cmd = CMD_SETMODE;
230330db0ca8SKalle Valo 	clear_bit(JOB_PROMISC, &ai->jobs);
230430db0ca8SKalle Valo 	cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC;
230530db0ca8SKalle Valo 	issuecommand(ai, &cmd, &rsp);
230630db0ca8SKalle Valo 	up(&ai->sem);
230730db0ca8SKalle Valo }
230830db0ca8SKalle Valo 
2309ba4d6513SLee Jones static void airo_set_multicast_list(struct net_device *dev)
2310ba4d6513SLee Jones {
231130db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
231230db0ca8SKalle Valo 
231330db0ca8SKalle Valo 	if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
231430db0ca8SKalle Valo 		change_bit(FLAG_PROMISC, &ai->flags);
231530db0ca8SKalle Valo 		if (down_trylock(&ai->sem) != 0) {
231630db0ca8SKalle Valo 			set_bit(JOB_PROMISC, &ai->jobs);
231730db0ca8SKalle Valo 			wake_up_interruptible(&ai->thr_wait);
231830db0ca8SKalle Valo 		} else
231930db0ca8SKalle Valo 			airo_set_promisc(ai);
232030db0ca8SKalle Valo 	}
232130db0ca8SKalle Valo 
232230db0ca8SKalle Valo 	if ((dev->flags&IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
232330db0ca8SKalle Valo 		/* Turn on multicast.  (Should be already setup...) */
232430db0ca8SKalle Valo 	}
232530db0ca8SKalle Valo }
232630db0ca8SKalle Valo 
232730db0ca8SKalle Valo static int airo_set_mac_address(struct net_device *dev, void *p)
232830db0ca8SKalle Valo {
232930db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
233030db0ca8SKalle Valo 	struct sockaddr *addr = p;
233130db0ca8SKalle Valo 
233230db0ca8SKalle Valo 	readConfigRid(ai, 1);
233330db0ca8SKalle Valo 	memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len);
233430db0ca8SKalle Valo 	set_bit (FLAG_COMMIT, &ai->flags);
233530db0ca8SKalle Valo 	disable_MAC(ai, 1);
233630db0ca8SKalle Valo 	writeConfigRid (ai, 1);
233730db0ca8SKalle Valo 	enable_MAC(ai, 1);
233830db0ca8SKalle Valo 	memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len);
233930db0ca8SKalle Valo 	if (ai->wifidev)
234030db0ca8SKalle Valo 		memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len);
234130db0ca8SKalle Valo 	return 0;
234230db0ca8SKalle Valo }
234330db0ca8SKalle Valo 
234430db0ca8SKalle Valo static LIST_HEAD(airo_devices);
234530db0ca8SKalle Valo 
234630db0ca8SKalle Valo static void add_airo_dev(struct airo_info *ai)
234730db0ca8SKalle Valo {
234830db0ca8SKalle Valo 	/* Upper layers already keep track of PCI devices,
234930db0ca8SKalle Valo 	 * so we only need to remember our non-PCI cards. */
235030db0ca8SKalle Valo 	if (!ai->pci)
235130db0ca8SKalle Valo 		list_add_tail(&ai->dev_list, &airo_devices);
235230db0ca8SKalle Valo }
235330db0ca8SKalle Valo 
235430db0ca8SKalle Valo static void del_airo_dev(struct airo_info *ai)
235530db0ca8SKalle Valo {
235630db0ca8SKalle Valo 	if (!ai->pci)
235730db0ca8SKalle Valo 		list_del(&ai->dev_list);
235830db0ca8SKalle Valo }
235930db0ca8SKalle Valo 
2360ba4d6513SLee Jones static int airo_close(struct net_device *dev)
2361ba4d6513SLee Jones {
236230db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
236330db0ca8SKalle Valo 
236430db0ca8SKalle Valo 	netif_stop_queue(dev);
236530db0ca8SKalle Valo 
236630db0ca8SKalle Valo 	if (ai->wifidev != dev) {
236730db0ca8SKalle Valo #ifdef POWER_ON_DOWN
236830db0ca8SKalle Valo 		/* Shut power to the card. The idea is that the user can save
236930db0ca8SKalle Valo 		 * power when he doesn't need the card with "ifconfig down".
237030db0ca8SKalle Valo 		 * That's the method that is most friendly towards the network
237130db0ca8SKalle Valo 		 * stack (i.e. the network stack won't try to broadcast
237230db0ca8SKalle Valo 		 * anything on the interface and routes are gone. Jean II */
237330db0ca8SKalle Valo 		set_bit(FLAG_RADIO_DOWN, &ai->flags);
237430db0ca8SKalle Valo 		disable_MAC(ai, 1);
237530db0ca8SKalle Valo #endif
237630db0ca8SKalle Valo 		disable_interrupts(ai);
237730db0ca8SKalle Valo 
237830db0ca8SKalle Valo 		free_irq(dev->irq, dev);
237930db0ca8SKalle Valo 
238030db0ca8SKalle Valo 		set_bit(JOB_DIE, &ai->jobs);
238130db0ca8SKalle Valo 		kthread_stop(ai->airo_thread_task);
238230db0ca8SKalle Valo 	}
238330db0ca8SKalle Valo 	return 0;
238430db0ca8SKalle Valo }
238530db0ca8SKalle Valo 
238630db0ca8SKalle Valo void stop_airo_card(struct net_device *dev, int freeres)
238730db0ca8SKalle Valo {
238830db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
238930db0ca8SKalle Valo 
239030db0ca8SKalle Valo 	set_bit(FLAG_RADIO_DOWN, &ai->flags);
239130db0ca8SKalle Valo 	disable_MAC(ai, 1);
239230db0ca8SKalle Valo 	disable_interrupts(ai);
239330db0ca8SKalle Valo 	takedown_proc_entry(dev, ai);
239430db0ca8SKalle Valo 	if (test_bit(FLAG_REGISTERED, &ai->flags)) {
239530db0ca8SKalle Valo 		unregister_netdev(dev);
239630db0ca8SKalle Valo 		if (ai->wifidev) {
239730db0ca8SKalle Valo 			unregister_netdev(ai->wifidev);
239830db0ca8SKalle Valo 			free_netdev(ai->wifidev);
239930db0ca8SKalle Valo 			ai->wifidev = NULL;
240030db0ca8SKalle Valo 		}
240130db0ca8SKalle Valo 		clear_bit(FLAG_REGISTERED, &ai->flags);
240230db0ca8SKalle Valo 	}
240330db0ca8SKalle Valo 	/*
240430db0ca8SKalle Valo 	 * Clean out tx queue
240530db0ca8SKalle Valo 	 */
240630db0ca8SKalle Valo 	if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) {
240730db0ca8SKalle Valo 		struct sk_buff *skb = NULL;
240830db0ca8SKalle Valo 		for (;(skb = skb_dequeue(&ai->txq));)
240930db0ca8SKalle Valo 			dev_kfree_skb(skb);
241030db0ca8SKalle Valo 	}
241130db0ca8SKalle Valo 
241230db0ca8SKalle Valo 	airo_networks_free (ai);
241330db0ca8SKalle Valo 
241430db0ca8SKalle Valo 	kfree(ai->flash);
241530db0ca8SKalle Valo 	kfree(ai->rssi);
241630db0ca8SKalle Valo 	kfree(ai->SSID);
241730db0ca8SKalle Valo 	if (freeres) {
241830db0ca8SKalle Valo 		/* PCMCIA frees this stuff, so only for PCI and ISA */
241930db0ca8SKalle Valo 		release_region(dev->base_addr, 64);
242030db0ca8SKalle Valo 		if (test_bit(FLAG_MPI, &ai->flags)) {
242130db0ca8SKalle Valo 			if (ai->pci)
242230db0ca8SKalle Valo 				mpi_unmap_card(ai->pci);
242330db0ca8SKalle Valo 			if (ai->pcimem)
242430db0ca8SKalle Valo 				iounmap(ai->pcimem);
242530db0ca8SKalle Valo 			if (ai->pciaux)
242630db0ca8SKalle Valo 				iounmap(ai->pciaux);
2427ac4c323cSChristophe JAILLET 			dma_free_coherent(&ai->pci->dev, PCI_SHARED_LEN,
242830db0ca8SKalle Valo 					  ai->shared, ai->shared_dma);
242930db0ca8SKalle Valo 		}
243030db0ca8SKalle Valo         }
2431e5db0ad7SArd Biesheuvel 	crypto_free_sync_skcipher(ai->tfm);
243230db0ca8SKalle Valo 	del_airo_dev(ai);
243330db0ca8SKalle Valo 	free_netdev(dev);
243430db0ca8SKalle Valo }
243530db0ca8SKalle Valo 
243630db0ca8SKalle Valo EXPORT_SYMBOL(stop_airo_card);
243730db0ca8SKalle Valo 
243830db0ca8SKalle Valo static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr)
243930db0ca8SKalle Valo {
244030db0ca8SKalle Valo 	memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
244130db0ca8SKalle Valo 	return ETH_ALEN;
244230db0ca8SKalle Valo }
244330db0ca8SKalle Valo 
244430db0ca8SKalle Valo static void mpi_unmap_card(struct pci_dev *pci)
244530db0ca8SKalle Valo {
244630db0ca8SKalle Valo 	unsigned long mem_start = pci_resource_start(pci, 1);
244730db0ca8SKalle Valo 	unsigned long mem_len = pci_resource_len(pci, 1);
244830db0ca8SKalle Valo 	unsigned long aux_start = pci_resource_start(pci, 2);
244930db0ca8SKalle Valo 	unsigned long aux_len = AUXMEMSIZE;
245030db0ca8SKalle Valo 
245130db0ca8SKalle Valo 	release_mem_region(aux_start, aux_len);
245230db0ca8SKalle Valo 	release_mem_region(mem_start, mem_len);
245330db0ca8SKalle Valo }
245430db0ca8SKalle Valo 
245530db0ca8SKalle Valo /*************************************************************
245630db0ca8SKalle Valo  *  This routine assumes that descriptors have been setup .
24570e20c3e1SKieran Bingham  *  Run at insmod time or after reset when the descriptors
245830db0ca8SKalle Valo  *  have been initialized . Returns 0 if all is well nz
245930db0ca8SKalle Valo  *  otherwise . Does not allocate memory but sets up card
246030db0ca8SKalle Valo  *  using previously allocated descriptors.
246130db0ca8SKalle Valo  */
246230db0ca8SKalle Valo static int mpi_init_descriptors (struct airo_info *ai)
246330db0ca8SKalle Valo {
246430db0ca8SKalle Valo 	Cmd cmd;
246530db0ca8SKalle Valo 	Resp rsp;
246630db0ca8SKalle Valo 	int i;
246730db0ca8SKalle Valo 	int rc = SUCCESS;
246830db0ca8SKalle Valo 
246930db0ca8SKalle Valo 	/* Alloc  card RX descriptors */
247030db0ca8SKalle Valo 	netif_stop_queue(ai->dev);
247130db0ca8SKalle Valo 
247230db0ca8SKalle Valo 	memset(&rsp, 0, sizeof(rsp));
247330db0ca8SKalle Valo 	memset(&cmd, 0, sizeof(cmd));
247430db0ca8SKalle Valo 
247530db0ca8SKalle Valo 	cmd.cmd = CMD_ALLOCATEAUX;
247630db0ca8SKalle Valo 	cmd.parm0 = FID_RX;
247730db0ca8SKalle Valo 	cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux);
247830db0ca8SKalle Valo 	cmd.parm2 = MPI_MAX_FIDS;
247930db0ca8SKalle Valo 	rc = issuecommand(ai, &cmd, &rsp);
248030db0ca8SKalle Valo 	if (rc != SUCCESS) {
248130db0ca8SKalle Valo 		airo_print_err(ai->dev->name, "Couldn't allocate RX FID");
248230db0ca8SKalle Valo 		return rc;
248330db0ca8SKalle Valo 	}
248430db0ca8SKalle Valo 
248530db0ca8SKalle Valo 	for (i = 0; i<MPI_MAX_FIDS; i++) {
248630db0ca8SKalle Valo 		memcpy_toio(ai->rxfids[i].card_ram_off,
248730db0ca8SKalle Valo 			&ai->rxfids[i].rx_desc, sizeof(RxFid));
248830db0ca8SKalle Valo 	}
248930db0ca8SKalle Valo 
249030db0ca8SKalle Valo 	/* Alloc card TX descriptors */
249130db0ca8SKalle Valo 
249230db0ca8SKalle Valo 	memset(&rsp, 0, sizeof(rsp));
249330db0ca8SKalle Valo 	memset(&cmd, 0, sizeof(cmd));
249430db0ca8SKalle Valo 
249530db0ca8SKalle Valo 	cmd.cmd = CMD_ALLOCATEAUX;
249630db0ca8SKalle Valo 	cmd.parm0 = FID_TX;
249730db0ca8SKalle Valo 	cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux);
249830db0ca8SKalle Valo 	cmd.parm2 = MPI_MAX_FIDS;
249930db0ca8SKalle Valo 
250030db0ca8SKalle Valo 	for (i = 0; i<MPI_MAX_FIDS; i++) {
250130db0ca8SKalle Valo 		ai->txfids[i].tx_desc.valid = 1;
250230db0ca8SKalle Valo 		memcpy_toio(ai->txfids[i].card_ram_off,
250330db0ca8SKalle Valo 			&ai->txfids[i].tx_desc, sizeof(TxFid));
250430db0ca8SKalle Valo 	}
250530db0ca8SKalle Valo 	ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
250630db0ca8SKalle Valo 
250730db0ca8SKalle Valo 	rc = issuecommand(ai, &cmd, &rsp);
250830db0ca8SKalle Valo 	if (rc != SUCCESS) {
250930db0ca8SKalle Valo 		airo_print_err(ai->dev->name, "Couldn't allocate TX FID");
251030db0ca8SKalle Valo 		return rc;
251130db0ca8SKalle Valo 	}
251230db0ca8SKalle Valo 
251330db0ca8SKalle Valo 	/* Alloc card Rid descriptor */
251430db0ca8SKalle Valo 	memset(&rsp, 0, sizeof(rsp));
251530db0ca8SKalle Valo 	memset(&cmd, 0, sizeof(cmd));
251630db0ca8SKalle Valo 
251730db0ca8SKalle Valo 	cmd.cmd = CMD_ALLOCATEAUX;
251830db0ca8SKalle Valo 	cmd.parm0 = RID_RW;
251930db0ca8SKalle Valo 	cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux);
252030db0ca8SKalle Valo 	cmd.parm2 = 1; /* Magic number... */
252130db0ca8SKalle Valo 	rc = issuecommand(ai, &cmd, &rsp);
252230db0ca8SKalle Valo 	if (rc != SUCCESS) {
252330db0ca8SKalle Valo 		airo_print_err(ai->dev->name, "Couldn't allocate RID");
252430db0ca8SKalle Valo 		return rc;
252530db0ca8SKalle Valo 	}
252630db0ca8SKalle Valo 
252730db0ca8SKalle Valo 	memcpy_toio(ai->config_desc.card_ram_off,
252830db0ca8SKalle Valo 		&ai->config_desc.rid_desc, sizeof(Rid));
252930db0ca8SKalle Valo 
253030db0ca8SKalle Valo 	return rc;
253130db0ca8SKalle Valo }
253230db0ca8SKalle Valo 
253330db0ca8SKalle Valo /*
253430db0ca8SKalle Valo  * We are setting up three things here:
253530db0ca8SKalle Valo  * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid.
253630db0ca8SKalle Valo  * 2) Map PCI memory for issuing commands.
253730db0ca8SKalle Valo  * 3) Allocate memory (shared) to send and receive ethernet frames.
253830db0ca8SKalle Valo  */
253930db0ca8SKalle Valo static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci)
254030db0ca8SKalle Valo {
254130db0ca8SKalle Valo 	unsigned long mem_start, mem_len, aux_start, aux_len;
254230db0ca8SKalle Valo 	int rc = -1;
254330db0ca8SKalle Valo 	int i;
254430db0ca8SKalle Valo 	dma_addr_t busaddroff;
254530db0ca8SKalle Valo 	unsigned char *vpackoff;
254630db0ca8SKalle Valo 	unsigned char __iomem *pciaddroff;
254730db0ca8SKalle Valo 
254830db0ca8SKalle Valo 	mem_start = pci_resource_start(pci, 1);
254930db0ca8SKalle Valo 	mem_len = pci_resource_len(pci, 1);
255030db0ca8SKalle Valo 	aux_start = pci_resource_start(pci, 2);
255130db0ca8SKalle Valo 	aux_len = AUXMEMSIZE;
255230db0ca8SKalle Valo 
255330db0ca8SKalle Valo 	if (!request_mem_region(mem_start, mem_len, DRV_NAME)) {
255430db0ca8SKalle Valo 		airo_print_err("", "Couldn't get region %x[%x]",
255530db0ca8SKalle Valo 			(int)mem_start, (int)mem_len);
255630db0ca8SKalle Valo 		goto out;
255730db0ca8SKalle Valo 	}
255830db0ca8SKalle Valo 	if (!request_mem_region(aux_start, aux_len, DRV_NAME)) {
255930db0ca8SKalle Valo 		airo_print_err("", "Couldn't get region %x[%x]",
256030db0ca8SKalle Valo 			(int)aux_start, (int)aux_len);
256130db0ca8SKalle Valo 		goto free_region1;
256230db0ca8SKalle Valo 	}
256330db0ca8SKalle Valo 
256430db0ca8SKalle Valo 	ai->pcimem = ioremap(mem_start, mem_len);
256530db0ca8SKalle Valo 	if (!ai->pcimem) {
256630db0ca8SKalle Valo 		airo_print_err("", "Couldn't map region %x[%x]",
256730db0ca8SKalle Valo 			(int)mem_start, (int)mem_len);
256830db0ca8SKalle Valo 		goto free_region2;
256930db0ca8SKalle Valo 	}
257030db0ca8SKalle Valo 	ai->pciaux = ioremap(aux_start, aux_len);
257130db0ca8SKalle Valo 	if (!ai->pciaux) {
257230db0ca8SKalle Valo 		airo_print_err("", "Couldn't map region %x[%x]",
257330db0ca8SKalle Valo 			(int)aux_start, (int)aux_len);
257430db0ca8SKalle Valo 		goto free_memmap;
257530db0ca8SKalle Valo 	}
257630db0ca8SKalle Valo 
257730db0ca8SKalle Valo 	/* Reserve PKTSIZE for each fid and 2K for the Rids */
2578ac4c323cSChristophe JAILLET 	ai->shared = dma_alloc_coherent(&pci->dev, PCI_SHARED_LEN,
2579ac4c323cSChristophe JAILLET 					&ai->shared_dma, GFP_KERNEL);
258030db0ca8SKalle Valo 	if (!ai->shared) {
2581ac4c323cSChristophe JAILLET 		airo_print_err("", "Couldn't alloc_coherent %d",
258230db0ca8SKalle Valo 			PCI_SHARED_LEN);
258330db0ca8SKalle Valo 		goto free_auxmap;
258430db0ca8SKalle Valo 	}
258530db0ca8SKalle Valo 
258630db0ca8SKalle Valo 	/*
258730db0ca8SKalle Valo 	 * Setup descriptor RX, TX, CONFIG
258830db0ca8SKalle Valo 	 */
258930db0ca8SKalle Valo 	busaddroff = ai->shared_dma;
259030db0ca8SKalle Valo 	pciaddroff = ai->pciaux + AUX_OFFSET;
259130db0ca8SKalle Valo 	vpackoff   = ai->shared;
259230db0ca8SKalle Valo 
259330db0ca8SKalle Valo 	/* RX descriptor setup */
259430db0ca8SKalle Valo 	for (i = 0; i < MPI_MAX_FIDS; i++) {
259530db0ca8SKalle Valo 		ai->rxfids[i].pending = 0;
259630db0ca8SKalle Valo 		ai->rxfids[i].card_ram_off = pciaddroff;
259730db0ca8SKalle Valo 		ai->rxfids[i].virtual_host_addr = vpackoff;
259830db0ca8SKalle Valo 		ai->rxfids[i].rx_desc.host_addr = busaddroff;
259930db0ca8SKalle Valo 		ai->rxfids[i].rx_desc.valid = 1;
260030db0ca8SKalle Valo 		ai->rxfids[i].rx_desc.len = PKTSIZE;
260130db0ca8SKalle Valo 		ai->rxfids[i].rx_desc.rdy = 0;
260230db0ca8SKalle Valo 
260330db0ca8SKalle Valo 		pciaddroff += sizeof(RxFid);
260430db0ca8SKalle Valo 		busaddroff += PKTSIZE;
260530db0ca8SKalle Valo 		vpackoff   += PKTSIZE;
260630db0ca8SKalle Valo 	}
260730db0ca8SKalle Valo 
260830db0ca8SKalle Valo 	/* TX descriptor setup */
260930db0ca8SKalle Valo 	for (i = 0; i < MPI_MAX_FIDS; i++) {
261030db0ca8SKalle Valo 		ai->txfids[i].card_ram_off = pciaddroff;
261130db0ca8SKalle Valo 		ai->txfids[i].virtual_host_addr = vpackoff;
261230db0ca8SKalle Valo 		ai->txfids[i].tx_desc.valid = 1;
261330db0ca8SKalle Valo 		ai->txfids[i].tx_desc.host_addr = busaddroff;
261430db0ca8SKalle Valo 		memcpy(ai->txfids[i].virtual_host_addr,
261530db0ca8SKalle Valo 			&wifictlhdr8023, sizeof(wifictlhdr8023));
261630db0ca8SKalle Valo 
261730db0ca8SKalle Valo 		pciaddroff += sizeof(TxFid);
261830db0ca8SKalle Valo 		busaddroff += PKTSIZE;
261930db0ca8SKalle Valo 		vpackoff   += PKTSIZE;
262030db0ca8SKalle Valo 	}
262130db0ca8SKalle Valo 	ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */
262230db0ca8SKalle Valo 
262330db0ca8SKalle Valo 	/* Rid descriptor setup */
262430db0ca8SKalle Valo 	ai->config_desc.card_ram_off = pciaddroff;
262530db0ca8SKalle Valo 	ai->config_desc.virtual_host_addr = vpackoff;
262630db0ca8SKalle Valo 	ai->config_desc.rid_desc.host_addr = busaddroff;
262730db0ca8SKalle Valo 	ai->ridbus = busaddroff;
262830db0ca8SKalle Valo 	ai->config_desc.rid_desc.rid = 0;
262930db0ca8SKalle Valo 	ai->config_desc.rid_desc.len = RIDSIZE;
263030db0ca8SKalle Valo 	ai->config_desc.rid_desc.valid = 1;
263130db0ca8SKalle Valo 	pciaddroff += sizeof(Rid);
263230db0ca8SKalle Valo 	busaddroff += RIDSIZE;
263330db0ca8SKalle Valo 	vpackoff   += RIDSIZE;
263430db0ca8SKalle Valo 
263530db0ca8SKalle Valo 	/* Tell card about descriptors */
263630db0ca8SKalle Valo 	if (mpi_init_descriptors (ai) != SUCCESS)
263730db0ca8SKalle Valo 		goto free_shared;
263830db0ca8SKalle Valo 
263930db0ca8SKalle Valo 	return 0;
264030db0ca8SKalle Valo  free_shared:
2641ac4c323cSChristophe JAILLET 	dma_free_coherent(&pci->dev, PCI_SHARED_LEN, ai->shared,
2642ac4c323cSChristophe JAILLET 			  ai->shared_dma);
264330db0ca8SKalle Valo  free_auxmap:
264430db0ca8SKalle Valo 	iounmap(ai->pciaux);
264530db0ca8SKalle Valo  free_memmap:
264630db0ca8SKalle Valo 	iounmap(ai->pcimem);
264730db0ca8SKalle Valo  free_region2:
264830db0ca8SKalle Valo 	release_mem_region(aux_start, aux_len);
264930db0ca8SKalle Valo  free_region1:
265030db0ca8SKalle Valo 	release_mem_region(mem_start, mem_len);
265130db0ca8SKalle Valo  out:
265230db0ca8SKalle Valo 	return rc;
265330db0ca8SKalle Valo }
265430db0ca8SKalle Valo 
265530db0ca8SKalle Valo static const struct header_ops airo_header_ops = {
265630db0ca8SKalle Valo 	.parse = wll_header_parse,
265730db0ca8SKalle Valo };
265830db0ca8SKalle Valo 
265930db0ca8SKalle Valo static const struct net_device_ops airo11_netdev_ops = {
266030db0ca8SKalle Valo 	.ndo_open 		= airo_open,
266130db0ca8SKalle Valo 	.ndo_stop 		= airo_close,
266230db0ca8SKalle Valo 	.ndo_start_xmit 	= airo_start_xmit11,
266330db0ca8SKalle Valo 	.ndo_get_stats 		= airo_get_stats,
266430db0ca8SKalle Valo 	.ndo_set_mac_address	= airo_set_mac_address,
266530db0ca8SKalle Valo 	.ndo_do_ioctl		= airo_ioctl,
266630db0ca8SKalle Valo };
266730db0ca8SKalle Valo 
266830db0ca8SKalle Valo static void wifi_setup(struct net_device *dev)
266930db0ca8SKalle Valo {
267030db0ca8SKalle Valo 	dev->netdev_ops = &airo11_netdev_ops;
267130db0ca8SKalle Valo 	dev->header_ops = &airo_header_ops;
267230db0ca8SKalle Valo 	dev->wireless_handlers = &airo_handler_def;
267330db0ca8SKalle Valo 
267430db0ca8SKalle Valo 	dev->type               = ARPHRD_IEEE80211;
267530db0ca8SKalle Valo 	dev->hard_header_len    = ETH_HLEN;
267630db0ca8SKalle Valo 	dev->mtu                = AIRO_DEF_MTU;
26779c22b4a3SJarod Wilson 	dev->min_mtu            = 68;
26789c22b4a3SJarod Wilson 	dev->max_mtu            = MIC_MSGLEN_MAX;
267930db0ca8SKalle Valo 	dev->addr_len           = ETH_ALEN;
268030db0ca8SKalle Valo 	dev->tx_queue_len       = 100;
268130db0ca8SKalle Valo 
268230db0ca8SKalle Valo 	eth_broadcast_addr(dev->broadcast);
268330db0ca8SKalle Valo 
268430db0ca8SKalle Valo 	dev->flags              = IFF_BROADCAST|IFF_MULTICAST;
268530db0ca8SKalle Valo }
268630db0ca8SKalle Valo 
268730db0ca8SKalle Valo static struct net_device *init_wifidev(struct airo_info *ai,
268830db0ca8SKalle Valo 					struct net_device *ethdev)
268930db0ca8SKalle Valo {
269030db0ca8SKalle Valo 	int err;
269130db0ca8SKalle Valo 	struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN,
269230db0ca8SKalle Valo 					      wifi_setup);
269330db0ca8SKalle Valo 	if (!dev)
269430db0ca8SKalle Valo 		return NULL;
269530db0ca8SKalle Valo 	dev->ml_priv = ethdev->ml_priv;
269630db0ca8SKalle Valo 	dev->irq = ethdev->irq;
269730db0ca8SKalle Valo 	dev->base_addr = ethdev->base_addr;
269830db0ca8SKalle Valo 	dev->wireless_data = ethdev->wireless_data;
269930db0ca8SKalle Valo 	SET_NETDEV_DEV(dev, ethdev->dev.parent);
270030db0ca8SKalle Valo 	eth_hw_addr_inherit(dev, ethdev);
270130db0ca8SKalle Valo 	err = register_netdev(dev);
270230db0ca8SKalle Valo 	if (err<0) {
270330db0ca8SKalle Valo 		free_netdev(dev);
270430db0ca8SKalle Valo 		return NULL;
270530db0ca8SKalle Valo 	}
270630db0ca8SKalle Valo 	return dev;
270730db0ca8SKalle Valo }
270830db0ca8SKalle Valo 
2709ba4d6513SLee Jones static int reset_card(struct net_device *dev, int lock)
2710ba4d6513SLee Jones {
271130db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
271230db0ca8SKalle Valo 
271330db0ca8SKalle Valo 	if (lock && down_interruptible(&ai->sem))
271430db0ca8SKalle Valo 		return -1;
271530db0ca8SKalle Valo 	waitbusy (ai);
271630db0ca8SKalle Valo 	OUT4500(ai, COMMAND, CMD_SOFTRESET);
271730db0ca8SKalle Valo 	msleep(200);
271830db0ca8SKalle Valo 	waitbusy (ai);
271930db0ca8SKalle Valo 	msleep(200);
272030db0ca8SKalle Valo 	if (lock)
272130db0ca8SKalle Valo 		up(&ai->sem);
272230db0ca8SKalle Valo 	return 0;
272330db0ca8SKalle Valo }
272430db0ca8SKalle Valo 
272530db0ca8SKalle Valo #define AIRO_MAX_NETWORK_COUNT	64
272630db0ca8SKalle Valo static int airo_networks_allocate(struct airo_info *ai)
272730db0ca8SKalle Valo {
272830db0ca8SKalle Valo 	if (ai->networks)
272930db0ca8SKalle Valo 		return 0;
273030db0ca8SKalle Valo 
273130db0ca8SKalle Valo 	ai->networks = kcalloc(AIRO_MAX_NETWORK_COUNT, sizeof(BSSListElement),
273230db0ca8SKalle Valo 			       GFP_KERNEL);
273330db0ca8SKalle Valo 	if (!ai->networks) {
273430db0ca8SKalle Valo 		airo_print_warn("", "Out of memory allocating beacons");
273530db0ca8SKalle Valo 		return -ENOMEM;
273630db0ca8SKalle Valo 	}
273730db0ca8SKalle Valo 
273830db0ca8SKalle Valo 	return 0;
273930db0ca8SKalle Valo }
274030db0ca8SKalle Valo 
274130db0ca8SKalle Valo static void airo_networks_free(struct airo_info *ai)
274230db0ca8SKalle Valo {
274330db0ca8SKalle Valo 	kfree(ai->networks);
274430db0ca8SKalle Valo 	ai->networks = NULL;
274530db0ca8SKalle Valo }
274630db0ca8SKalle Valo 
274730db0ca8SKalle Valo static void airo_networks_initialize(struct airo_info *ai)
274830db0ca8SKalle Valo {
274930db0ca8SKalle Valo 	int i;
275030db0ca8SKalle Valo 
275130db0ca8SKalle Valo 	INIT_LIST_HEAD(&ai->network_free_list);
275230db0ca8SKalle Valo 	INIT_LIST_HEAD(&ai->network_list);
275330db0ca8SKalle Valo 	for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++)
275430db0ca8SKalle Valo 		list_add_tail(&ai->networks[i].list,
275530db0ca8SKalle Valo 			      &ai->network_free_list);
275630db0ca8SKalle Valo }
275730db0ca8SKalle Valo 
275830db0ca8SKalle Valo static const struct net_device_ops airo_netdev_ops = {
275930db0ca8SKalle Valo 	.ndo_open		= airo_open,
276030db0ca8SKalle Valo 	.ndo_stop		= airo_close,
276130db0ca8SKalle Valo 	.ndo_start_xmit		= airo_start_xmit,
276230db0ca8SKalle Valo 	.ndo_get_stats		= airo_get_stats,
276330db0ca8SKalle Valo 	.ndo_set_rx_mode	= airo_set_multicast_list,
276430db0ca8SKalle Valo 	.ndo_set_mac_address	= airo_set_mac_address,
276530db0ca8SKalle Valo 	.ndo_do_ioctl		= airo_ioctl,
276630db0ca8SKalle Valo 	.ndo_validate_addr	= eth_validate_addr,
276730db0ca8SKalle Valo };
276830db0ca8SKalle Valo 
276930db0ca8SKalle Valo static const struct net_device_ops mpi_netdev_ops = {
277030db0ca8SKalle Valo 	.ndo_open		= airo_open,
277130db0ca8SKalle Valo 	.ndo_stop		= airo_close,
277230db0ca8SKalle Valo 	.ndo_start_xmit		= mpi_start_xmit,
277330db0ca8SKalle Valo 	.ndo_get_stats		= airo_get_stats,
277430db0ca8SKalle Valo 	.ndo_set_rx_mode	= airo_set_multicast_list,
277530db0ca8SKalle Valo 	.ndo_set_mac_address	= airo_set_mac_address,
277630db0ca8SKalle Valo 	.ndo_do_ioctl		= airo_ioctl,
277730db0ca8SKalle Valo 	.ndo_validate_addr	= eth_validate_addr,
277830db0ca8SKalle Valo };
277930db0ca8SKalle Valo 
278030db0ca8SKalle Valo 
278130db0ca8SKalle Valo static struct net_device *_init_airo_card(unsigned short irq, int port,
278230db0ca8SKalle Valo 					   int is_pcmcia, struct pci_dev *pci,
278330db0ca8SKalle Valo 					   struct device *dmdev)
278430db0ca8SKalle Valo {
278530db0ca8SKalle Valo 	struct net_device *dev;
278630db0ca8SKalle Valo 	struct airo_info *ai;
278730db0ca8SKalle Valo 	int i, rc;
278830db0ca8SKalle Valo 	CapabilityRid cap_rid;
278930db0ca8SKalle Valo 
279030db0ca8SKalle Valo 	/* Create the network device object. */
279130db0ca8SKalle Valo 	dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup);
279230db0ca8SKalle Valo 	if (!dev) {
279330db0ca8SKalle Valo 		airo_print_err("", "Couldn't alloc_etherdev");
279430db0ca8SKalle Valo 		return NULL;
279530db0ca8SKalle Valo 	}
279630db0ca8SKalle Valo 
279730db0ca8SKalle Valo 	ai = dev->ml_priv = netdev_priv(dev);
279830db0ca8SKalle Valo 	ai->wifidev = NULL;
279930db0ca8SKalle Valo 	ai->flags = 1 << FLAG_RADIO_DOWN;
280030db0ca8SKalle Valo 	ai->jobs = 0;
280130db0ca8SKalle Valo 	ai->dev = dev;
280230db0ca8SKalle Valo 	if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
280330db0ca8SKalle Valo 		airo_print_dbg("", "Found an MPI350 card");
280430db0ca8SKalle Valo 		set_bit(FLAG_MPI, &ai->flags);
280530db0ca8SKalle Valo 	}
280630db0ca8SKalle Valo 	spin_lock_init(&ai->aux_lock);
280730db0ca8SKalle Valo 	sema_init(&ai->sem, 1);
280830db0ca8SKalle Valo 	ai->config.len = 0;
280930db0ca8SKalle Valo 	ai->pci = pci;
281030db0ca8SKalle Valo 	init_waitqueue_head (&ai->thr_wait);
281130db0ca8SKalle Valo 	ai->tfm = NULL;
281230db0ca8SKalle Valo 	add_airo_dev(ai);
281330db0ca8SKalle Valo 	ai->APList.len = cpu_to_le16(sizeof(struct APListRid));
281430db0ca8SKalle Valo 
281530db0ca8SKalle Valo 	if (airo_networks_allocate (ai))
281630db0ca8SKalle Valo 		goto err_out_free;
281730db0ca8SKalle Valo 	airo_networks_initialize (ai);
281830db0ca8SKalle Valo 
281930db0ca8SKalle Valo 	skb_queue_head_init (&ai->txq);
282030db0ca8SKalle Valo 
282130db0ca8SKalle Valo 	/* The Airo-specific entries in the device structure. */
282230db0ca8SKalle Valo 	if (test_bit(FLAG_MPI,&ai->flags))
282330db0ca8SKalle Valo 		dev->netdev_ops = &mpi_netdev_ops;
282430db0ca8SKalle Valo 	else
282530db0ca8SKalle Valo 		dev->netdev_ops = &airo_netdev_ops;
282630db0ca8SKalle Valo 	dev->wireless_handlers = &airo_handler_def;
282730db0ca8SKalle Valo 	ai->wireless_data.spy_data = &ai->spy_data;
282830db0ca8SKalle Valo 	dev->wireless_data = &ai->wireless_data;
282930db0ca8SKalle Valo 	dev->irq = irq;
283030db0ca8SKalle Valo 	dev->base_addr = port;
283130db0ca8SKalle Valo 	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
28329c22b4a3SJarod Wilson 	dev->max_mtu = MIC_MSGLEN_MAX;
283330db0ca8SKalle Valo 
283430db0ca8SKalle Valo 	SET_NETDEV_DEV(dev, dmdev);
283530db0ca8SKalle Valo 
283630db0ca8SKalle Valo 	reset_card (dev, 1);
283730db0ca8SKalle Valo 	msleep(400);
283830db0ca8SKalle Valo 
283930db0ca8SKalle Valo 	if (!is_pcmcia) {
284030db0ca8SKalle Valo 		if (!request_region(dev->base_addr, 64, DRV_NAME)) {
284130db0ca8SKalle Valo 			rc = -EBUSY;
284230db0ca8SKalle Valo 			airo_print_err(dev->name, "Couldn't request region");
284330db0ca8SKalle Valo 			goto err_out_nets;
284430db0ca8SKalle Valo 		}
284530db0ca8SKalle Valo 	}
284630db0ca8SKalle Valo 
284730db0ca8SKalle Valo 	if (test_bit(FLAG_MPI,&ai->flags)) {
284830db0ca8SKalle Valo 		if (mpi_map_card(ai, pci)) {
284930db0ca8SKalle Valo 			airo_print_err("", "Could not map memory");
285030db0ca8SKalle Valo 			goto err_out_res;
285130db0ca8SKalle Valo 		}
285230db0ca8SKalle Valo 	}
285330db0ca8SKalle Valo 
285430db0ca8SKalle Valo 	if (probe) {
285530db0ca8SKalle Valo 		if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) {
285630db0ca8SKalle Valo 			airo_print_err(dev->name, "MAC could not be enabled");
285730db0ca8SKalle Valo 			rc = -EIO;
285830db0ca8SKalle Valo 			goto err_out_map;
285930db0ca8SKalle Valo 		}
286030db0ca8SKalle Valo 	} else if (!test_bit(FLAG_MPI,&ai->flags)) {
286130db0ca8SKalle Valo 		ai->bap_read = fast_bap_read;
286230db0ca8SKalle Valo 		set_bit(FLAG_FLASHING, &ai->flags);
286330db0ca8SKalle Valo 	}
286430db0ca8SKalle Valo 
286530db0ca8SKalle Valo 	strcpy(dev->name, "eth%d");
286630db0ca8SKalle Valo 	rc = register_netdev(dev);
286730db0ca8SKalle Valo 	if (rc) {
286830db0ca8SKalle Valo 		airo_print_err(dev->name, "Couldn't register_netdev");
286930db0ca8SKalle Valo 		goto err_out_map;
287030db0ca8SKalle Valo 	}
287130db0ca8SKalle Valo 	ai->wifidev = init_wifidev(ai, dev);
287230db0ca8SKalle Valo 	if (!ai->wifidev)
287330db0ca8SKalle Valo 		goto err_out_reg;
287430db0ca8SKalle Valo 
287530db0ca8SKalle Valo 	rc = readCapabilityRid(ai, &cap_rid, 1);
287630db0ca8SKalle Valo 	if (rc != SUCCESS) {
287730db0ca8SKalle Valo 		rc = -EIO;
287830db0ca8SKalle Valo 		goto err_out_wifi;
287930db0ca8SKalle Valo 	}
288030db0ca8SKalle Valo 	/* WEP capability discovery */
288130db0ca8SKalle Valo 	ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0;
288230db0ca8SKalle Valo 	ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0;
288330db0ca8SKalle Valo 
288430db0ca8SKalle Valo 	airo_print_info(dev->name, "Firmware version %x.%x.%02d",
288530db0ca8SKalle Valo 	                ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF),
288630db0ca8SKalle Valo 	                (le16_to_cpu(cap_rid.softVer) & 0xFF),
288730db0ca8SKalle Valo 	                le16_to_cpu(cap_rid.softSubVer));
288830db0ca8SKalle Valo 
288930db0ca8SKalle Valo 	/* Test for WPA support */
289030db0ca8SKalle Valo 	/* Only firmware versions 5.30.17 or better can do WPA */
289130db0ca8SKalle Valo 	if (le16_to_cpu(cap_rid.softVer) > 0x530
289230db0ca8SKalle Valo 	 || (le16_to_cpu(cap_rid.softVer) == 0x530
289330db0ca8SKalle Valo 	      && le16_to_cpu(cap_rid.softSubVer) >= 17)) {
289430db0ca8SKalle Valo 		airo_print_info(ai->dev->name, "WPA supported.");
289530db0ca8SKalle Valo 
289630db0ca8SKalle Valo 		set_bit(FLAG_WPA_CAPABLE, &ai->flags);
289730db0ca8SKalle Valo 		ai->bssListFirst = RID_WPA_BSSLISTFIRST;
289830db0ca8SKalle Valo 		ai->bssListNext = RID_WPA_BSSLISTNEXT;
289930db0ca8SKalle Valo 		ai->bssListRidLen = sizeof(BSSListRid);
290030db0ca8SKalle Valo 	} else {
290130db0ca8SKalle Valo 		airo_print_info(ai->dev->name, "WPA unsupported with firmware "
290230db0ca8SKalle Valo 			"versions older than 5.30.17.");
290330db0ca8SKalle Valo 
290430db0ca8SKalle Valo 		ai->bssListFirst = RID_BSSLISTFIRST;
290530db0ca8SKalle Valo 		ai->bssListNext = RID_BSSLISTNEXT;
290630db0ca8SKalle Valo 		ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra);
290730db0ca8SKalle Valo 	}
290830db0ca8SKalle Valo 
290930db0ca8SKalle Valo 	set_bit(FLAG_REGISTERED,&ai->flags);
291030db0ca8SKalle Valo 	airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
291130db0ca8SKalle Valo 
291230db0ca8SKalle Valo 	/* Allocate the transmit buffers */
291330db0ca8SKalle Valo 	if (probe && !test_bit(FLAG_MPI,&ai->flags))
291430db0ca8SKalle Valo 		for (i = 0; i < MAX_FIDS; i++)
291530db0ca8SKalle Valo 			ai->fids[i] = transmit_allocate(ai, AIRO_DEF_MTU, i>=MAX_FIDS/2);
291630db0ca8SKalle Valo 
291730db0ca8SKalle Valo 	if (setup_proc_entry(dev, dev->ml_priv) < 0)
291830db0ca8SKalle Valo 		goto err_out_wifi;
291930db0ca8SKalle Valo 
292030db0ca8SKalle Valo 	return dev;
292130db0ca8SKalle Valo 
292230db0ca8SKalle Valo err_out_wifi:
292330db0ca8SKalle Valo 	unregister_netdev(ai->wifidev);
292430db0ca8SKalle Valo 	free_netdev(ai->wifidev);
292530db0ca8SKalle Valo err_out_reg:
292630db0ca8SKalle Valo 	unregister_netdev(dev);
292730db0ca8SKalle Valo err_out_map:
292830db0ca8SKalle Valo 	if (test_bit(FLAG_MPI,&ai->flags) && pci) {
2929ac4c323cSChristophe JAILLET 		dma_free_coherent(&pci->dev, PCI_SHARED_LEN, ai->shared,
2930ac4c323cSChristophe JAILLET 				  ai->shared_dma);
293130db0ca8SKalle Valo 		iounmap(ai->pciaux);
293230db0ca8SKalle Valo 		iounmap(ai->pcimem);
293330db0ca8SKalle Valo 		mpi_unmap_card(ai->pci);
293430db0ca8SKalle Valo 	}
293530db0ca8SKalle Valo err_out_res:
293630db0ca8SKalle Valo 	if (!is_pcmcia)
293730db0ca8SKalle Valo 		release_region(dev->base_addr, 64);
293830db0ca8SKalle Valo err_out_nets:
293930db0ca8SKalle Valo 	airo_networks_free(ai);
294030db0ca8SKalle Valo err_out_free:
294130db0ca8SKalle Valo 	del_airo_dev(ai);
294230db0ca8SKalle Valo 	free_netdev(dev);
294330db0ca8SKalle Valo 	return NULL;
294430db0ca8SKalle Valo }
294530db0ca8SKalle Valo 
294630db0ca8SKalle Valo struct net_device *init_airo_card(unsigned short irq, int port, int is_pcmcia,
294730db0ca8SKalle Valo 				  struct device *dmdev)
294830db0ca8SKalle Valo {
294930db0ca8SKalle Valo 	return _init_airo_card (irq, port, is_pcmcia, NULL, dmdev);
295030db0ca8SKalle Valo }
295130db0ca8SKalle Valo 
295230db0ca8SKalle Valo EXPORT_SYMBOL(init_airo_card);
295330db0ca8SKalle Valo 
2954ba4d6513SLee Jones static int waitbusy (struct airo_info *ai)
2955ba4d6513SLee Jones {
295630db0ca8SKalle Valo 	int delay = 0;
295730db0ca8SKalle Valo 	while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) {
295830db0ca8SKalle Valo 		udelay (10);
295930db0ca8SKalle Valo 		if ((++delay % 20) == 0)
296030db0ca8SKalle Valo 			OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
296130db0ca8SKalle Valo 	}
296230db0ca8SKalle Valo 	return delay < 10000;
296330db0ca8SKalle Valo }
296430db0ca8SKalle Valo 
296530db0ca8SKalle Valo int reset_airo_card(struct net_device *dev)
296630db0ca8SKalle Valo {
296730db0ca8SKalle Valo 	int i;
296830db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
296930db0ca8SKalle Valo 
297030db0ca8SKalle Valo 	if (reset_card (dev, 1))
297130db0ca8SKalle Valo 		return -1;
297230db0ca8SKalle Valo 
297330db0ca8SKalle Valo 	if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) {
297430db0ca8SKalle Valo 		airo_print_err(dev->name, "MAC could not be enabled");
297530db0ca8SKalle Valo 		return -1;
297630db0ca8SKalle Valo 	}
297730db0ca8SKalle Valo 	airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
297830db0ca8SKalle Valo 	/* Allocate the transmit buffers if needed */
297930db0ca8SKalle Valo 	if (!test_bit(FLAG_MPI,&ai->flags))
298030db0ca8SKalle Valo 		for (i = 0; i < MAX_FIDS; i++)
298130db0ca8SKalle Valo 			ai->fids[i] = transmit_allocate (ai, AIRO_DEF_MTU, i>=MAX_FIDS/2);
298230db0ca8SKalle Valo 
298330db0ca8SKalle Valo 	enable_interrupts(ai);
298430db0ca8SKalle Valo 	netif_wake_queue(dev);
298530db0ca8SKalle Valo 	return 0;
298630db0ca8SKalle Valo }
298730db0ca8SKalle Valo 
298830db0ca8SKalle Valo EXPORT_SYMBOL(reset_airo_card);
298930db0ca8SKalle Valo 
2990ba4d6513SLee Jones static void airo_send_event(struct net_device *dev)
2991ba4d6513SLee Jones {
299230db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
299330db0ca8SKalle Valo 	union iwreq_data wrqu;
299430db0ca8SKalle Valo 	StatusRid status_rid;
299530db0ca8SKalle Valo 
299630db0ca8SKalle Valo 	clear_bit(JOB_EVENT, &ai->jobs);
299730db0ca8SKalle Valo 	PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
299830db0ca8SKalle Valo 	up(&ai->sem);
299930db0ca8SKalle Valo 	wrqu.data.length = 0;
300030db0ca8SKalle Valo 	wrqu.data.flags = 0;
300130db0ca8SKalle Valo 	memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN);
300230db0ca8SKalle Valo 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
300330db0ca8SKalle Valo 
300430db0ca8SKalle Valo 	/* Send event to user space */
300530db0ca8SKalle Valo 	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
300630db0ca8SKalle Valo }
300730db0ca8SKalle Valo 
3008ba4d6513SLee Jones static void airo_process_scan_results (struct airo_info *ai)
3009ba4d6513SLee Jones {
301030db0ca8SKalle Valo 	union iwreq_data	wrqu;
301130db0ca8SKalle Valo 	BSSListRid bss;
301230db0ca8SKalle Valo 	int rc;
301330db0ca8SKalle Valo 	BSSListElement * loop_net;
301430db0ca8SKalle Valo 	BSSListElement * tmp_net;
301530db0ca8SKalle Valo 
301630db0ca8SKalle Valo 	/* Blow away current list of scan results */
301730db0ca8SKalle Valo 	list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) {
301830db0ca8SKalle Valo 		list_move_tail (&loop_net->list, &ai->network_free_list);
301930db0ca8SKalle Valo 		/* Don't blow away ->list, just BSS data */
302030db0ca8SKalle Valo 		memset (loop_net, 0, sizeof (loop_net->bss));
302130db0ca8SKalle Valo 	}
302230db0ca8SKalle Valo 
302330db0ca8SKalle Valo 	/* Try to read the first entry of the scan result */
302430db0ca8SKalle Valo 	rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0);
302530db0ca8SKalle Valo 	if ((rc) || (bss.index == cpu_to_le16(0xffff))) {
302630db0ca8SKalle Valo 		/* No scan results */
302730db0ca8SKalle Valo 		goto out;
302830db0ca8SKalle Valo 	}
302930db0ca8SKalle Valo 
303030db0ca8SKalle Valo 	/* Read and parse all entries */
303130db0ca8SKalle Valo 	tmp_net = NULL;
303230db0ca8SKalle Valo 	while ((!rc) && (bss.index != cpu_to_le16(0xffff))) {
303330db0ca8SKalle Valo 		/* Grab a network off the free list */
303430db0ca8SKalle Valo 		if (!list_empty(&ai->network_free_list)) {
303530db0ca8SKalle Valo 			tmp_net = list_entry(ai->network_free_list.next,
303630db0ca8SKalle Valo 					    BSSListElement, list);
303730db0ca8SKalle Valo 			list_del(ai->network_free_list.next);
303830db0ca8SKalle Valo 		}
303930db0ca8SKalle Valo 
304030db0ca8SKalle Valo 		if (tmp_net != NULL) {
304130db0ca8SKalle Valo 			memcpy(tmp_net, &bss, sizeof(tmp_net->bss));
304230db0ca8SKalle Valo 			list_add_tail(&tmp_net->list, &ai->network_list);
304330db0ca8SKalle Valo 			tmp_net = NULL;
304430db0ca8SKalle Valo 		}
304530db0ca8SKalle Valo 
304630db0ca8SKalle Valo 		/* Read next entry */
304730db0ca8SKalle Valo 		rc = PC4500_readrid(ai, ai->bssListNext,
304830db0ca8SKalle Valo 				    &bss, ai->bssListRidLen, 0);
304930db0ca8SKalle Valo 	}
305030db0ca8SKalle Valo 
305130db0ca8SKalle Valo out:
305230db0ca8SKalle Valo 	/* write APList back (we cleared it in airo_set_scan) */
305330db0ca8SKalle Valo 	disable_MAC(ai, 2);
305430db0ca8SKalle Valo 	writeAPListRid(ai, &ai->APList, 0);
305530db0ca8SKalle Valo 	enable_MAC(ai, 0);
305630db0ca8SKalle Valo 
305730db0ca8SKalle Valo 	ai->scan_timeout = 0;
305830db0ca8SKalle Valo 	clear_bit(JOB_SCAN_RESULTS, &ai->jobs);
305930db0ca8SKalle Valo 	up(&ai->sem);
306030db0ca8SKalle Valo 
306130db0ca8SKalle Valo 	/* Send an empty event to user space.
306230db0ca8SKalle Valo 	 * We don't send the received data on
306330db0ca8SKalle Valo 	 * the event because it would require
306430db0ca8SKalle Valo 	 * us to do complex transcoding, and
306530db0ca8SKalle Valo 	 * we want to minimise the work done in
306630db0ca8SKalle Valo 	 * the irq handler. Use a request to
306730db0ca8SKalle Valo 	 * extract the data - Jean II */
306830db0ca8SKalle Valo 	wrqu.data.length = 0;
306930db0ca8SKalle Valo 	wrqu.data.flags = 0;
307030db0ca8SKalle Valo 	wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL);
307130db0ca8SKalle Valo }
307230db0ca8SKalle Valo 
3073ba4d6513SLee Jones static int airo_thread(void *data)
3074ba4d6513SLee Jones {
307530db0ca8SKalle Valo 	struct net_device *dev = data;
307630db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
307730db0ca8SKalle Valo 	int locked;
307830db0ca8SKalle Valo 
307930db0ca8SKalle Valo 	set_freezable();
308030db0ca8SKalle Valo 	while (1) {
308130db0ca8SKalle Valo 		/* make swsusp happy with our thread */
308230db0ca8SKalle Valo 		try_to_freeze();
308330db0ca8SKalle Valo 
308430db0ca8SKalle Valo 		if (test_bit(JOB_DIE, &ai->jobs))
308530db0ca8SKalle Valo 			break;
308630db0ca8SKalle Valo 
308730db0ca8SKalle Valo 		if (ai->jobs) {
308830db0ca8SKalle Valo 			locked = down_interruptible(&ai->sem);
308930db0ca8SKalle Valo 		} else {
3090ac6424b9SIngo Molnar 			wait_queue_entry_t wait;
309130db0ca8SKalle Valo 
309230db0ca8SKalle Valo 			init_waitqueue_entry(&wait, current);
309330db0ca8SKalle Valo 			add_wait_queue(&ai->thr_wait, &wait);
309430db0ca8SKalle Valo 			for (;;) {
309530db0ca8SKalle Valo 				set_current_state(TASK_INTERRUPTIBLE);
309630db0ca8SKalle Valo 				if (ai->jobs)
309730db0ca8SKalle Valo 					break;
309830db0ca8SKalle Valo 				if (ai->expires || ai->scan_timeout) {
309930db0ca8SKalle Valo 					if (ai->scan_timeout &&
310030db0ca8SKalle Valo 							time_after_eq(jiffies, ai->scan_timeout)) {
310130db0ca8SKalle Valo 						set_bit(JOB_SCAN_RESULTS, &ai->jobs);
310230db0ca8SKalle Valo 						break;
310330db0ca8SKalle Valo 					} else if (ai->expires &&
310430db0ca8SKalle Valo 							time_after_eq(jiffies, ai->expires)) {
310530db0ca8SKalle Valo 						set_bit(JOB_AUTOWEP, &ai->jobs);
310630db0ca8SKalle Valo 						break;
310730db0ca8SKalle Valo 					}
310830db0ca8SKalle Valo 					if (!kthread_should_stop() &&
310930db0ca8SKalle Valo 					    !freezing(current)) {
311030db0ca8SKalle Valo 						unsigned long wake_at;
311130db0ca8SKalle Valo 						if (!ai->expires || !ai->scan_timeout) {
311230db0ca8SKalle Valo 							wake_at = max(ai->expires,
311330db0ca8SKalle Valo 								ai->scan_timeout);
311430db0ca8SKalle Valo 						} else {
311530db0ca8SKalle Valo 							wake_at = min(ai->expires,
311630db0ca8SKalle Valo 								ai->scan_timeout);
311730db0ca8SKalle Valo 						}
311830db0ca8SKalle Valo 						schedule_timeout(wake_at - jiffies);
311930db0ca8SKalle Valo 						continue;
312030db0ca8SKalle Valo 					}
312130db0ca8SKalle Valo 				} else if (!kthread_should_stop() &&
312230db0ca8SKalle Valo 					   !freezing(current)) {
312330db0ca8SKalle Valo 					schedule();
312430db0ca8SKalle Valo 					continue;
312530db0ca8SKalle Valo 				}
312630db0ca8SKalle Valo 				break;
312730db0ca8SKalle Valo 			}
3128b28bd97cSXu Wang 			__set_current_state(TASK_RUNNING);
312930db0ca8SKalle Valo 			remove_wait_queue(&ai->thr_wait, &wait);
313030db0ca8SKalle Valo 			locked = 1;
313130db0ca8SKalle Valo 		}
313230db0ca8SKalle Valo 
313330db0ca8SKalle Valo 		if (locked)
313430db0ca8SKalle Valo 			continue;
313530db0ca8SKalle Valo 
313630db0ca8SKalle Valo 		if (test_bit(JOB_DIE, &ai->jobs)) {
313730db0ca8SKalle Valo 			up(&ai->sem);
313830db0ca8SKalle Valo 			break;
313930db0ca8SKalle Valo 		}
314030db0ca8SKalle Valo 
314130db0ca8SKalle Valo 		if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) {
314230db0ca8SKalle Valo 			up(&ai->sem);
314330db0ca8SKalle Valo 			continue;
314430db0ca8SKalle Valo 		}
314530db0ca8SKalle Valo 
314630db0ca8SKalle Valo 		if (test_bit(JOB_XMIT, &ai->jobs))
314730db0ca8SKalle Valo 			airo_end_xmit(dev);
314830db0ca8SKalle Valo 		else if (test_bit(JOB_XMIT11, &ai->jobs))
314930db0ca8SKalle Valo 			airo_end_xmit11(dev);
315030db0ca8SKalle Valo 		else if (test_bit(JOB_STATS, &ai->jobs))
315130db0ca8SKalle Valo 			airo_read_stats(dev);
315230db0ca8SKalle Valo 		else if (test_bit(JOB_PROMISC, &ai->jobs))
315330db0ca8SKalle Valo 			airo_set_promisc(ai);
315430db0ca8SKalle Valo 		else if (test_bit(JOB_MIC, &ai->jobs))
315530db0ca8SKalle Valo 			micinit(ai);
315630db0ca8SKalle Valo 		else if (test_bit(JOB_EVENT, &ai->jobs))
315730db0ca8SKalle Valo 			airo_send_event(dev);
315830db0ca8SKalle Valo 		else if (test_bit(JOB_AUTOWEP, &ai->jobs))
315930db0ca8SKalle Valo 			timer_func(dev);
316030db0ca8SKalle Valo 		else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs))
316130db0ca8SKalle Valo 			airo_process_scan_results(ai);
316230db0ca8SKalle Valo 		else  /* Shouldn't get here, but we make sure to unlock */
316330db0ca8SKalle Valo 			up(&ai->sem);
316430db0ca8SKalle Valo 	}
316530db0ca8SKalle Valo 
316630db0ca8SKalle Valo 	return 0;
316730db0ca8SKalle Valo }
316830db0ca8SKalle Valo 
316930db0ca8SKalle Valo static int header_len(__le16 ctl)
317030db0ca8SKalle Valo {
317130db0ca8SKalle Valo 	u16 fc = le16_to_cpu(ctl);
317230db0ca8SKalle Valo 	switch (fc & 0xc) {
317330db0ca8SKalle Valo 	case 4:
317430db0ca8SKalle Valo 		if ((fc & 0xe0) == 0xc0)
317530db0ca8SKalle Valo 			return 10;	/* one-address control packet */
317630db0ca8SKalle Valo 		return 16;	/* two-address control packet */
317730db0ca8SKalle Valo 	case 8:
317830db0ca8SKalle Valo 		if ((fc & 0x300) == 0x300)
317930db0ca8SKalle Valo 			return 30;	/* WDS packet */
318030db0ca8SKalle Valo 	}
318130db0ca8SKalle Valo 	return 24;
318230db0ca8SKalle Valo }
318330db0ca8SKalle Valo 
318430db0ca8SKalle Valo static void airo_handle_cisco_mic(struct airo_info *ai)
318530db0ca8SKalle Valo {
318630db0ca8SKalle Valo 	if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) {
318730db0ca8SKalle Valo 		set_bit(JOB_MIC, &ai->jobs);
318830db0ca8SKalle Valo 		wake_up_interruptible(&ai->thr_wait);
318930db0ca8SKalle Valo 	}
319030db0ca8SKalle Valo }
319130db0ca8SKalle Valo 
319230db0ca8SKalle Valo /* Airo Status codes */
319330db0ca8SKalle Valo #define STAT_NOBEACON	0x8000 /* Loss of sync - missed beacons */
319430db0ca8SKalle Valo #define STAT_MAXRETRIES	0x8001 /* Loss of sync - max retries */
319530db0ca8SKalle Valo #define STAT_MAXARL	0x8002 /* Loss of sync - average retry level exceeded*/
319630db0ca8SKalle Valo #define STAT_FORCELOSS	0x8003 /* Loss of sync - host request */
319730db0ca8SKalle Valo #define STAT_TSFSYNC	0x8004 /* Loss of sync - TSF synchronization */
319830db0ca8SKalle Valo #define STAT_DEAUTH	0x8100 /* low byte is 802.11 reason code */
319930db0ca8SKalle Valo #define STAT_DISASSOC	0x8200 /* low byte is 802.11 reason code */
320030db0ca8SKalle Valo #define STAT_ASSOC_FAIL	0x8400 /* low byte is 802.11 reason code */
320130db0ca8SKalle Valo #define STAT_AUTH_FAIL	0x0300 /* low byte is 802.11 reason code */
320230db0ca8SKalle Valo #define STAT_ASSOC	0x0400 /* Associated */
320330db0ca8SKalle Valo #define STAT_REASSOC    0x0600 /* Reassociated?  Only on firmware >= 5.30.17 */
320430db0ca8SKalle Valo 
320530db0ca8SKalle Valo static void airo_print_status(const char *devname, u16 status)
320630db0ca8SKalle Valo {
320730db0ca8SKalle Valo 	u8 reason = status & 0xFF;
320830db0ca8SKalle Valo 
320930db0ca8SKalle Valo 	switch (status & 0xFF00) {
321030db0ca8SKalle Valo 	case STAT_NOBEACON:
321130db0ca8SKalle Valo 		switch (status) {
321230db0ca8SKalle Valo 		case STAT_NOBEACON:
321330db0ca8SKalle Valo 			airo_print_dbg(devname, "link lost (missed beacons)");
321430db0ca8SKalle Valo 			break;
321530db0ca8SKalle Valo 		case STAT_MAXRETRIES:
321630db0ca8SKalle Valo 		case STAT_MAXARL:
321730db0ca8SKalle Valo 			airo_print_dbg(devname, "link lost (max retries)");
321830db0ca8SKalle Valo 			break;
321930db0ca8SKalle Valo 		case STAT_FORCELOSS:
322030db0ca8SKalle Valo 			airo_print_dbg(devname, "link lost (local choice)");
322130db0ca8SKalle Valo 			break;
322230db0ca8SKalle Valo 		case STAT_TSFSYNC:
322330db0ca8SKalle Valo 			airo_print_dbg(devname, "link lost (TSF sync lost)");
322430db0ca8SKalle Valo 			break;
322530db0ca8SKalle Valo 		default:
322630db0ca8SKalle Valo 			airo_print_dbg(devname, "unknown status %x\n", status);
322730db0ca8SKalle Valo 			break;
322830db0ca8SKalle Valo 		}
322930db0ca8SKalle Valo 		break;
323030db0ca8SKalle Valo 	case STAT_DEAUTH:
323130db0ca8SKalle Valo 		airo_print_dbg(devname, "deauthenticated (reason: %d)", reason);
323230db0ca8SKalle Valo 		break;
323330db0ca8SKalle Valo 	case STAT_DISASSOC:
323430db0ca8SKalle Valo 		airo_print_dbg(devname, "disassociated (reason: %d)", reason);
323530db0ca8SKalle Valo 		break;
323630db0ca8SKalle Valo 	case STAT_ASSOC_FAIL:
323730db0ca8SKalle Valo 		airo_print_dbg(devname, "association failed (reason: %d)",
323830db0ca8SKalle Valo 			       reason);
323930db0ca8SKalle Valo 		break;
324030db0ca8SKalle Valo 	case STAT_AUTH_FAIL:
324130db0ca8SKalle Valo 		airo_print_dbg(devname, "authentication failed (reason: %d)",
324230db0ca8SKalle Valo 			       reason);
324330db0ca8SKalle Valo 		break;
324430db0ca8SKalle Valo 	case STAT_ASSOC:
324530db0ca8SKalle Valo 	case STAT_REASSOC:
324630db0ca8SKalle Valo 		break;
324730db0ca8SKalle Valo 	default:
324830db0ca8SKalle Valo 		airo_print_dbg(devname, "unknown status %x\n", status);
324930db0ca8SKalle Valo 		break;
325030db0ca8SKalle Valo 	}
325130db0ca8SKalle Valo }
325230db0ca8SKalle Valo 
325330db0ca8SKalle Valo static void airo_handle_link(struct airo_info *ai)
325430db0ca8SKalle Valo {
325530db0ca8SKalle Valo 	union iwreq_data wrqu;
325630db0ca8SKalle Valo 	int scan_forceloss = 0;
325730db0ca8SKalle Valo 	u16 status;
325830db0ca8SKalle Valo 
325930db0ca8SKalle Valo 	/* Get new status and acknowledge the link change */
326030db0ca8SKalle Valo 	status = le16_to_cpu(IN4500(ai, LINKSTAT));
326130db0ca8SKalle Valo 	OUT4500(ai, EVACK, EV_LINK);
326230db0ca8SKalle Valo 
326330db0ca8SKalle Valo 	if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0))
326430db0ca8SKalle Valo 		scan_forceloss = 1;
326530db0ca8SKalle Valo 
326630db0ca8SKalle Valo 	airo_print_status(ai->dev->name, status);
326730db0ca8SKalle Valo 
326830db0ca8SKalle Valo 	if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) {
326930db0ca8SKalle Valo 		if (auto_wep)
327030db0ca8SKalle Valo 			ai->expires = 0;
327130db0ca8SKalle Valo 		if (ai->list_bss_task)
327230db0ca8SKalle Valo 			wake_up_process(ai->list_bss_task);
327330db0ca8SKalle Valo 		set_bit(FLAG_UPDATE_UNI, &ai->flags);
327430db0ca8SKalle Valo 		set_bit(FLAG_UPDATE_MULTI, &ai->flags);
327530db0ca8SKalle Valo 
327630db0ca8SKalle Valo 		set_bit(JOB_EVENT, &ai->jobs);
327730db0ca8SKalle Valo 		wake_up_interruptible(&ai->thr_wait);
3278*49eb0199SSebastian Andrzej Siewior 
327930db0ca8SKalle Valo 		netif_carrier_on(ai->dev);
328030db0ca8SKalle Valo 	} else if (!scan_forceloss) {
328130db0ca8SKalle Valo 		if (auto_wep && !ai->expires) {
328230db0ca8SKalle Valo 			ai->expires = RUN_AT(3*HZ);
328330db0ca8SKalle Valo 			wake_up_interruptible(&ai->thr_wait);
328430db0ca8SKalle Valo 		}
328530db0ca8SKalle Valo 
328630db0ca8SKalle Valo 		/* Send event to user space */
328730db0ca8SKalle Valo 		eth_zero_addr(wrqu.ap_addr.sa_data);
328830db0ca8SKalle Valo 		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
328930db0ca8SKalle Valo 		wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL);
329030db0ca8SKalle Valo 		netif_carrier_off(ai->dev);
329130db0ca8SKalle Valo 	} else {
329230db0ca8SKalle Valo 		netif_carrier_off(ai->dev);
329330db0ca8SKalle Valo 	}
329430db0ca8SKalle Valo }
329530db0ca8SKalle Valo 
329630db0ca8SKalle Valo static void airo_handle_rx(struct airo_info *ai)
329730db0ca8SKalle Valo {
329830db0ca8SKalle Valo 	struct sk_buff *skb = NULL;
329930db0ca8SKalle Valo 	__le16 fc, v, *buffer, tmpbuf[4];
330030db0ca8SKalle Valo 	u16 len, hdrlen = 0, gap, fid;
330130db0ca8SKalle Valo 	struct rx_hdr hdr;
330230db0ca8SKalle Valo 	int success = 0;
330330db0ca8SKalle Valo 
330430db0ca8SKalle Valo 	if (test_bit(FLAG_MPI, &ai->flags)) {
330530db0ca8SKalle Valo 		if (test_bit(FLAG_802_11, &ai->flags))
330630db0ca8SKalle Valo 			mpi_receive_802_11(ai);
330730db0ca8SKalle Valo 		else
330830db0ca8SKalle Valo 			mpi_receive_802_3(ai);
330930db0ca8SKalle Valo 		OUT4500(ai, EVACK, EV_RX);
331030db0ca8SKalle Valo 		return;
331130db0ca8SKalle Valo 	}
331230db0ca8SKalle Valo 
331330db0ca8SKalle Valo 	fid = IN4500(ai, RXFID);
331430db0ca8SKalle Valo 
331530db0ca8SKalle Valo 	/* Get the packet length */
331630db0ca8SKalle Valo 	if (test_bit(FLAG_802_11, &ai->flags)) {
331730db0ca8SKalle Valo 		bap_setup (ai, fid, 4, BAP0);
331830db0ca8SKalle Valo 		bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0);
331930db0ca8SKalle Valo 		/* Bad CRC. Ignore packet */
332030db0ca8SKalle Valo 		if (le16_to_cpu(hdr.status) & 2)
332130db0ca8SKalle Valo 			hdr.len = 0;
332230db0ca8SKalle Valo 		if (ai->wifidev == NULL)
332330db0ca8SKalle Valo 			hdr.len = 0;
332430db0ca8SKalle Valo 	} else {
332530db0ca8SKalle Valo 		bap_setup(ai, fid, 0x36, BAP0);
332630db0ca8SKalle Valo 		bap_read(ai, &hdr.len, 2, BAP0);
332730db0ca8SKalle Valo 	}
332830db0ca8SKalle Valo 	len = le16_to_cpu(hdr.len);
332930db0ca8SKalle Valo 
333030db0ca8SKalle Valo 	if (len > AIRO_DEF_MTU) {
333130db0ca8SKalle Valo 		airo_print_err(ai->dev->name, "Bad size %d", len);
333230db0ca8SKalle Valo 		goto done;
333330db0ca8SKalle Valo 	}
333430db0ca8SKalle Valo 	if (len == 0)
333530db0ca8SKalle Valo 		goto done;
333630db0ca8SKalle Valo 
333730db0ca8SKalle Valo 	if (test_bit(FLAG_802_11, &ai->flags)) {
333830db0ca8SKalle Valo 		bap_read(ai, &fc, sizeof (fc), BAP0);
333930db0ca8SKalle Valo 		hdrlen = header_len(fc);
334030db0ca8SKalle Valo 	} else
334130db0ca8SKalle Valo 		hdrlen = ETH_ALEN * 2;
334230db0ca8SKalle Valo 
334330db0ca8SKalle Valo 	skb = dev_alloc_skb(len + hdrlen + 2 + 2);
334430db0ca8SKalle Valo 	if (!skb) {
334530db0ca8SKalle Valo 		ai->dev->stats.rx_dropped++;
334630db0ca8SKalle Valo 		goto done;
334730db0ca8SKalle Valo 	}
334830db0ca8SKalle Valo 
334930db0ca8SKalle Valo 	skb_reserve(skb, 2); /* This way the IP header is aligned */
33504df864c1SJohannes Berg 	buffer = skb_put(skb, len + hdrlen);
335130db0ca8SKalle Valo 	if (test_bit(FLAG_802_11, &ai->flags)) {
335230db0ca8SKalle Valo 		buffer[0] = fc;
335330db0ca8SKalle Valo 		bap_read(ai, buffer + 1, hdrlen - 2, BAP0);
335430db0ca8SKalle Valo 		if (hdrlen == 24)
335530db0ca8SKalle Valo 			bap_read(ai, tmpbuf, 6, BAP0);
335630db0ca8SKalle Valo 
335730db0ca8SKalle Valo 		bap_read(ai, &v, sizeof(v), BAP0);
335830db0ca8SKalle Valo 		gap = le16_to_cpu(v);
335930db0ca8SKalle Valo 		if (gap) {
336030db0ca8SKalle Valo 			if (gap <= 8) {
336130db0ca8SKalle Valo 				bap_read(ai, tmpbuf, gap, BAP0);
336230db0ca8SKalle Valo 			} else {
336330db0ca8SKalle Valo 				airo_print_err(ai->dev->name, "gaplen too "
336430db0ca8SKalle Valo 					"big. Problems will follow...");
336530db0ca8SKalle Valo 			}
336630db0ca8SKalle Valo 		}
336730db0ca8SKalle Valo 		bap_read(ai, buffer + hdrlen/2, len, BAP0);
336830db0ca8SKalle Valo 	} else {
336930db0ca8SKalle Valo 		MICBuffer micbuf;
337030db0ca8SKalle Valo 
337130db0ca8SKalle Valo 		bap_read(ai, buffer, ETH_ALEN * 2, BAP0);
337230db0ca8SKalle Valo 		if (ai->micstats.enabled) {
337330db0ca8SKalle Valo 			bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0);
337430db0ca8SKalle Valo 			if (ntohs(micbuf.typelen) > 0x05DC)
337530db0ca8SKalle Valo 				bap_setup(ai, fid, 0x44, BAP0);
337630db0ca8SKalle Valo 			else {
337730db0ca8SKalle Valo 				if (len <= sizeof (micbuf)) {
337830db0ca8SKalle Valo 					dev_kfree_skb_irq(skb);
337930db0ca8SKalle Valo 					goto done;
338030db0ca8SKalle Valo 				}
338130db0ca8SKalle Valo 
338230db0ca8SKalle Valo 				len -= sizeof(micbuf);
338330db0ca8SKalle Valo 				skb_trim(skb, len + hdrlen);
338430db0ca8SKalle Valo 			}
338530db0ca8SKalle Valo 		}
338630db0ca8SKalle Valo 
338730db0ca8SKalle Valo 		bap_read(ai, buffer + ETH_ALEN, len, BAP0);
338830db0ca8SKalle Valo 		if (decapsulate(ai, &micbuf, (etherHead*) buffer, len))
338930db0ca8SKalle Valo 			dev_kfree_skb_irq (skb);
339030db0ca8SKalle Valo 		else
339130db0ca8SKalle Valo 			success = 1;
339230db0ca8SKalle Valo 	}
339330db0ca8SKalle Valo 
339430db0ca8SKalle Valo #ifdef WIRELESS_SPY
339530db0ca8SKalle Valo 	if (success && (ai->spy_data.spy_number > 0)) {
339630db0ca8SKalle Valo 		char *sa;
339730db0ca8SKalle Valo 		struct iw_quality wstats;
339830db0ca8SKalle Valo 
339930db0ca8SKalle Valo 		/* Prepare spy data : addr + qual */
340030db0ca8SKalle Valo 		if (!test_bit(FLAG_802_11, &ai->flags)) {
340130db0ca8SKalle Valo 			sa = (char *) buffer + 6;
340230db0ca8SKalle Valo 			bap_setup(ai, fid, 8, BAP0);
340330db0ca8SKalle Valo 			bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0);
340430db0ca8SKalle Valo 		} else
340530db0ca8SKalle Valo 			sa = (char *) buffer + 10;
340630db0ca8SKalle Valo 		wstats.qual = hdr.rssi[0];
340730db0ca8SKalle Valo 		if (ai->rssi)
340830db0ca8SKalle Valo 			wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
340930db0ca8SKalle Valo 		else
341030db0ca8SKalle Valo 			wstats.level = (hdr.rssi[1] + 321) / 2;
341130db0ca8SKalle Valo 		wstats.noise = ai->wstats.qual.noise;
341230db0ca8SKalle Valo 		wstats.updated =  IW_QUAL_LEVEL_UPDATED
341330db0ca8SKalle Valo 				| IW_QUAL_QUAL_UPDATED
341430db0ca8SKalle Valo 				| IW_QUAL_DBM;
341530db0ca8SKalle Valo 		/* Update spy records */
341630db0ca8SKalle Valo 		wireless_spy_update(ai->dev, sa, &wstats);
341730db0ca8SKalle Valo 	}
341830db0ca8SKalle Valo #endif /* WIRELESS_SPY */
341930db0ca8SKalle Valo 
342030db0ca8SKalle Valo done:
342130db0ca8SKalle Valo 	OUT4500(ai, EVACK, EV_RX);
342230db0ca8SKalle Valo 
342330db0ca8SKalle Valo 	if (success) {
342430db0ca8SKalle Valo 		if (test_bit(FLAG_802_11, &ai->flags)) {
342530db0ca8SKalle Valo 			skb_reset_mac_header(skb);
342630db0ca8SKalle Valo 			skb->pkt_type = PACKET_OTHERHOST;
342730db0ca8SKalle Valo 			skb->dev = ai->wifidev;
342830db0ca8SKalle Valo 			skb->protocol = htons(ETH_P_802_2);
342930db0ca8SKalle Valo 		} else
343030db0ca8SKalle Valo 			skb->protocol = eth_type_trans(skb, ai->dev);
343130db0ca8SKalle Valo 		skb->ip_summed = CHECKSUM_NONE;
343230db0ca8SKalle Valo 
343330db0ca8SKalle Valo 		netif_rx(skb);
343430db0ca8SKalle Valo 	}
343530db0ca8SKalle Valo }
343630db0ca8SKalle Valo 
343730db0ca8SKalle Valo static void airo_handle_tx(struct airo_info *ai, u16 status)
343830db0ca8SKalle Valo {
3439d01a4e04SColin Ian King 	int i, index = -1;
344030db0ca8SKalle Valo 	u16 fid;
344130db0ca8SKalle Valo 
344230db0ca8SKalle Valo 	if (test_bit(FLAG_MPI, &ai->flags)) {
344330db0ca8SKalle Valo 		unsigned long flags;
344430db0ca8SKalle Valo 
344530db0ca8SKalle Valo 		if (status & EV_TXEXC)
344630db0ca8SKalle Valo 			get_tx_error(ai, -1);
344730db0ca8SKalle Valo 
344830db0ca8SKalle Valo 		spin_lock_irqsave(&ai->aux_lock, flags);
344930db0ca8SKalle Valo 		if (!skb_queue_empty(&ai->txq)) {
345030db0ca8SKalle Valo 			spin_unlock_irqrestore(&ai->aux_lock, flags);
345130db0ca8SKalle Valo 			mpi_send_packet(ai->dev);
345230db0ca8SKalle Valo 		} else {
345330db0ca8SKalle Valo 			clear_bit(FLAG_PENDING_XMIT, &ai->flags);
345430db0ca8SKalle Valo 			spin_unlock_irqrestore(&ai->aux_lock, flags);
345530db0ca8SKalle Valo 			netif_wake_queue(ai->dev);
345630db0ca8SKalle Valo 		}
345730db0ca8SKalle Valo 		OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
345830db0ca8SKalle Valo 		return;
345930db0ca8SKalle Valo 	}
346030db0ca8SKalle Valo 
346130db0ca8SKalle Valo 	fid = IN4500(ai, TXCOMPLFID);
346230db0ca8SKalle Valo 
346330db0ca8SKalle Valo 	for (i = 0; i < MAX_FIDS; i++) {
3464d01a4e04SColin Ian King 		if ((ai->fids[i] & 0xffff) == fid)
346530db0ca8SKalle Valo 			index = i;
346630db0ca8SKalle Valo 	}
346730db0ca8SKalle Valo 
346830db0ca8SKalle Valo 	if (index != -1) {
346930db0ca8SKalle Valo 		if (status & EV_TXEXC)
347030db0ca8SKalle Valo 			get_tx_error(ai, index);
347130db0ca8SKalle Valo 
347230db0ca8SKalle Valo 		OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC));
347330db0ca8SKalle Valo 
347430db0ca8SKalle Valo 		/* Set up to be used again */
347530db0ca8SKalle Valo 		ai->fids[index] &= 0xffff;
347630db0ca8SKalle Valo 		if (index < MAX_FIDS / 2) {
347730db0ca8SKalle Valo 			if (!test_bit(FLAG_PENDING_XMIT, &ai->flags))
347830db0ca8SKalle Valo 				netif_wake_queue(ai->dev);
347930db0ca8SKalle Valo 		} else {
348030db0ca8SKalle Valo 			if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags))
348130db0ca8SKalle Valo 				netif_wake_queue(ai->wifidev);
348230db0ca8SKalle Valo 		}
348330db0ca8SKalle Valo 	} else {
348430db0ca8SKalle Valo 		OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
348530db0ca8SKalle Valo 		airo_print_err(ai->dev->name, "Unallocated FID was used to xmit");
348630db0ca8SKalle Valo 	}
348730db0ca8SKalle Valo }
348830db0ca8SKalle Valo 
348930db0ca8SKalle Valo static irqreturn_t airo_interrupt(int irq, void *dev_id)
349030db0ca8SKalle Valo {
349130db0ca8SKalle Valo 	struct net_device *dev = dev_id;
349230db0ca8SKalle Valo 	u16 status, savedInterrupts = 0;
349330db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
349430db0ca8SKalle Valo 	int handled = 0;
349530db0ca8SKalle Valo 
349630db0ca8SKalle Valo 	if (!netif_device_present(dev))
349730db0ca8SKalle Valo 		return IRQ_NONE;
349830db0ca8SKalle Valo 
349930db0ca8SKalle Valo 	for (;;) {
350030db0ca8SKalle Valo 		status = IN4500(ai, EVSTAT);
350130db0ca8SKalle Valo 		if (!(status & STATUS_INTS) || (status == 0xffff))
350230db0ca8SKalle Valo 			break;
350330db0ca8SKalle Valo 
350430db0ca8SKalle Valo 		handled = 1;
350530db0ca8SKalle Valo 
350630db0ca8SKalle Valo 		if (status & EV_AWAKE) {
350730db0ca8SKalle Valo 			OUT4500(ai, EVACK, EV_AWAKE);
350830db0ca8SKalle Valo 			OUT4500(ai, EVACK, EV_AWAKE);
350930db0ca8SKalle Valo 		}
351030db0ca8SKalle Valo 
351130db0ca8SKalle Valo 		if (!savedInterrupts) {
351230db0ca8SKalle Valo 			savedInterrupts = IN4500(ai, EVINTEN);
351330db0ca8SKalle Valo 			OUT4500(ai, EVINTEN, 0);
351430db0ca8SKalle Valo 		}
351530db0ca8SKalle Valo 
351630db0ca8SKalle Valo 		if (status & EV_MIC) {
351730db0ca8SKalle Valo 			OUT4500(ai, EVACK, EV_MIC);
351830db0ca8SKalle Valo 			airo_handle_cisco_mic(ai);
351930db0ca8SKalle Valo 		}
352030db0ca8SKalle Valo 
352130db0ca8SKalle Valo 		if (status & EV_LINK) {
352230db0ca8SKalle Valo 			/* Link status changed */
352330db0ca8SKalle Valo 			airo_handle_link(ai);
352430db0ca8SKalle Valo 		}
352530db0ca8SKalle Valo 
352630db0ca8SKalle Valo 		/* Check to see if there is something to receive */
352730db0ca8SKalle Valo 		if (status & EV_RX)
352830db0ca8SKalle Valo 			airo_handle_rx(ai);
352930db0ca8SKalle Valo 
353030db0ca8SKalle Valo 		/* Check to see if a packet has been transmitted */
353130db0ca8SKalle Valo 		if (status & (EV_TX | EV_TXCPY | EV_TXEXC))
353230db0ca8SKalle Valo 			airo_handle_tx(ai, status);
353330db0ca8SKalle Valo 
353430db0ca8SKalle Valo 		if (status & ~STATUS_INTS & ~IGNORE_INTS) {
353530db0ca8SKalle Valo 			airo_print_warn(ai->dev->name, "Got weird status %x",
353630db0ca8SKalle Valo 				status & ~STATUS_INTS & ~IGNORE_INTS);
353730db0ca8SKalle Valo 		}
353830db0ca8SKalle Valo 	}
353930db0ca8SKalle Valo 
354030db0ca8SKalle Valo 	if (savedInterrupts)
354130db0ca8SKalle Valo 		OUT4500(ai, EVINTEN, savedInterrupts);
354230db0ca8SKalle Valo 
354330db0ca8SKalle Valo 	return IRQ_RETVAL(handled);
354430db0ca8SKalle Valo }
354530db0ca8SKalle Valo 
354630db0ca8SKalle Valo /*
354730db0ca8SKalle Valo  *  Routines to talk to the card
354830db0ca8SKalle Valo  */
354930db0ca8SKalle Valo 
355030db0ca8SKalle Valo /*
355130db0ca8SKalle Valo  *  This was originally written for the 4500, hence the name
355230db0ca8SKalle Valo  *  NOTE:  If use with 8bit mode and SMP bad things will happen!
355330db0ca8SKalle Valo  *         Why would some one do 8 bit IO in an SMP machine?!?
355430db0ca8SKalle Valo  */
3555ba4d6513SLee Jones static void OUT4500(struct airo_info *ai, u16 reg, u16 val)
3556ba4d6513SLee Jones {
355730db0ca8SKalle Valo 	if (test_bit(FLAG_MPI,&ai->flags))
355830db0ca8SKalle Valo 		reg <<= 1;
355930db0ca8SKalle Valo 	if (!do8bitIO)
356030db0ca8SKalle Valo 		outw(val, ai->dev->base_addr + reg);
356130db0ca8SKalle Valo 	else {
356230db0ca8SKalle Valo 		outb(val & 0xff, ai->dev->base_addr + reg);
356330db0ca8SKalle Valo 		outb(val >> 8, ai->dev->base_addr + reg + 1);
356430db0ca8SKalle Valo 	}
356530db0ca8SKalle Valo }
356630db0ca8SKalle Valo 
3567ba4d6513SLee Jones static u16 IN4500(struct airo_info *ai, u16 reg)
3568ba4d6513SLee Jones {
356930db0ca8SKalle Valo 	unsigned short rc;
357030db0ca8SKalle Valo 
357130db0ca8SKalle Valo 	if (test_bit(FLAG_MPI,&ai->flags))
357230db0ca8SKalle Valo 		reg <<= 1;
357330db0ca8SKalle Valo 	if (!do8bitIO)
357430db0ca8SKalle Valo 		rc = inw(ai->dev->base_addr + reg);
357530db0ca8SKalle Valo 	else {
357630db0ca8SKalle Valo 		rc = inb(ai->dev->base_addr + reg);
357730db0ca8SKalle Valo 		rc += ((int)inb(ai->dev->base_addr + reg + 1)) << 8;
357830db0ca8SKalle Valo 	}
357930db0ca8SKalle Valo 	return rc;
358030db0ca8SKalle Valo }
358130db0ca8SKalle Valo 
358230db0ca8SKalle Valo static int enable_MAC(struct airo_info *ai, int lock)
358330db0ca8SKalle Valo {
358430db0ca8SKalle Valo 	int rc;
358530db0ca8SKalle Valo 	Cmd cmd;
358630db0ca8SKalle Valo 	Resp rsp;
358730db0ca8SKalle Valo 
358830db0ca8SKalle Valo 	/* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions
358930db0ca8SKalle Valo 	 * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down"
359030db0ca8SKalle Valo 	 * Note : we could try to use !netif_running(dev) in enable_MAC()
359130db0ca8SKalle Valo 	 * instead of this flag, but I don't trust it *within* the
359230db0ca8SKalle Valo 	 * open/close functions, and testing both flags together is
359330db0ca8SKalle Valo 	 * "cheaper" - Jean II */
359430db0ca8SKalle Valo 	if (ai->flags & FLAG_RADIO_MASK) return SUCCESS;
359530db0ca8SKalle Valo 
359630db0ca8SKalle Valo 	if (lock && down_interruptible(&ai->sem))
359730db0ca8SKalle Valo 		return -ERESTARTSYS;
359830db0ca8SKalle Valo 
359930db0ca8SKalle Valo 	if (!test_bit(FLAG_ENABLED, &ai->flags)) {
360030db0ca8SKalle Valo 		memset(&cmd, 0, sizeof(cmd));
360130db0ca8SKalle Valo 		cmd.cmd = MAC_ENABLE;
360230db0ca8SKalle Valo 		rc = issuecommand(ai, &cmd, &rsp);
360330db0ca8SKalle Valo 		if (rc == SUCCESS)
360430db0ca8SKalle Valo 			set_bit(FLAG_ENABLED, &ai->flags);
360530db0ca8SKalle Valo 	} else
360630db0ca8SKalle Valo 		rc = SUCCESS;
360730db0ca8SKalle Valo 
360830db0ca8SKalle Valo 	if (lock)
360930db0ca8SKalle Valo 	    up(&ai->sem);
361030db0ca8SKalle Valo 
361130db0ca8SKalle Valo 	if (rc)
361230db0ca8SKalle Valo 		airo_print_err(ai->dev->name, "Cannot enable MAC");
361330db0ca8SKalle Valo 	else if ((rsp.status & 0xFF00) != 0) {
361430db0ca8SKalle Valo 		airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, "
361530db0ca8SKalle Valo 			"rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2);
361630db0ca8SKalle Valo 		rc = ERROR;
361730db0ca8SKalle Valo 	}
361830db0ca8SKalle Valo 	return rc;
361930db0ca8SKalle Valo }
362030db0ca8SKalle Valo 
3621ba4d6513SLee Jones static void disable_MAC(struct airo_info *ai, int lock)
3622ba4d6513SLee Jones {
362330db0ca8SKalle Valo         Cmd cmd;
362430db0ca8SKalle Valo 	Resp rsp;
362530db0ca8SKalle Valo 
362630db0ca8SKalle Valo 	if (lock == 1 && down_interruptible(&ai->sem))
362730db0ca8SKalle Valo 		return;
362830db0ca8SKalle Valo 
362930db0ca8SKalle Valo 	if (test_bit(FLAG_ENABLED, &ai->flags)) {
363030db0ca8SKalle Valo 		if (lock != 2) /* lock == 2 means don't disable carrier */
363130db0ca8SKalle Valo 			netif_carrier_off(ai->dev);
363230db0ca8SKalle Valo 		memset(&cmd, 0, sizeof(cmd));
363330db0ca8SKalle Valo 		cmd.cmd = MAC_DISABLE; // disable in case already enabled
363430db0ca8SKalle Valo 		issuecommand(ai, &cmd, &rsp);
363530db0ca8SKalle Valo 		clear_bit(FLAG_ENABLED, &ai->flags);
363630db0ca8SKalle Valo 	}
363730db0ca8SKalle Valo 	if (lock == 1)
363830db0ca8SKalle Valo 		up(&ai->sem);
363930db0ca8SKalle Valo }
364030db0ca8SKalle Valo 
3641ba4d6513SLee Jones static void enable_interrupts(struct airo_info *ai)
3642ba4d6513SLee Jones {
364330db0ca8SKalle Valo 	/* Enable the interrupts */
364430db0ca8SKalle Valo 	OUT4500(ai, EVINTEN, STATUS_INTS);
364530db0ca8SKalle Valo }
364630db0ca8SKalle Valo 
3647ba4d6513SLee Jones static void disable_interrupts(struct airo_info *ai)
3648ba4d6513SLee Jones {
364930db0ca8SKalle Valo 	OUT4500(ai, EVINTEN, 0);
365030db0ca8SKalle Valo }
365130db0ca8SKalle Valo 
365230db0ca8SKalle Valo static void mpi_receive_802_3(struct airo_info *ai)
365330db0ca8SKalle Valo {
365430db0ca8SKalle Valo 	RxFid rxd;
365530db0ca8SKalle Valo 	int len = 0;
365630db0ca8SKalle Valo 	struct sk_buff *skb;
365730db0ca8SKalle Valo 	char *buffer;
365830db0ca8SKalle Valo 	int off = 0;
365930db0ca8SKalle Valo 	MICBuffer micbuf;
366030db0ca8SKalle Valo 
366130db0ca8SKalle Valo 	memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
366230db0ca8SKalle Valo 	/* Make sure we got something */
366330db0ca8SKalle Valo 	if (rxd.rdy && rxd.valid == 0) {
366430db0ca8SKalle Valo 		len = rxd.len + 12;
366530db0ca8SKalle Valo 		if (len < 12 || len > 2048)
366630db0ca8SKalle Valo 			goto badrx;
366730db0ca8SKalle Valo 
366830db0ca8SKalle Valo 		skb = dev_alloc_skb(len);
366930db0ca8SKalle Valo 		if (!skb) {
367030db0ca8SKalle Valo 			ai->dev->stats.rx_dropped++;
367130db0ca8SKalle Valo 			goto badrx;
367230db0ca8SKalle Valo 		}
367330db0ca8SKalle Valo 		buffer = skb_put(skb, len);
367430db0ca8SKalle Valo 		memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2);
367530db0ca8SKalle Valo 		if (ai->micstats.enabled) {
367630db0ca8SKalle Valo 			memcpy(&micbuf,
367730db0ca8SKalle Valo 				ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2,
367830db0ca8SKalle Valo 				sizeof(micbuf));
367930db0ca8SKalle Valo 			if (ntohs(micbuf.typelen) <= 0x05DC) {
368030db0ca8SKalle Valo 				if (len <= sizeof(micbuf) + ETH_ALEN * 2)
368130db0ca8SKalle Valo 					goto badmic;
368230db0ca8SKalle Valo 
368330db0ca8SKalle Valo 				off = sizeof(micbuf);
368430db0ca8SKalle Valo 				skb_trim (skb, len - off);
368530db0ca8SKalle Valo 			}
368630db0ca8SKalle Valo 		}
368730db0ca8SKalle Valo 		memcpy(buffer + ETH_ALEN * 2,
368830db0ca8SKalle Valo 			ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off,
368930db0ca8SKalle Valo 			len - ETH_ALEN * 2 - off);
369030db0ca8SKalle Valo 		if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) {
369130db0ca8SKalle Valo badmic:
369230db0ca8SKalle Valo 			dev_kfree_skb_irq (skb);
369330db0ca8SKalle Valo 			goto badrx;
369430db0ca8SKalle Valo 		}
369530db0ca8SKalle Valo #ifdef WIRELESS_SPY
369630db0ca8SKalle Valo 		if (ai->spy_data.spy_number > 0) {
369730db0ca8SKalle Valo 			char *sa;
369830db0ca8SKalle Valo 			struct iw_quality wstats;
369930db0ca8SKalle Valo 			/* Prepare spy data : addr + qual */
370030db0ca8SKalle Valo 			sa = buffer + ETH_ALEN;
370130db0ca8SKalle Valo 			wstats.qual = 0; /* XXX Where do I get that info from ??? */
370230db0ca8SKalle Valo 			wstats.level = 0;
370330db0ca8SKalle Valo 			wstats.updated = 0;
370430db0ca8SKalle Valo 			/* Update spy records */
370530db0ca8SKalle Valo 			wireless_spy_update(ai->dev, sa, &wstats);
370630db0ca8SKalle Valo 		}
370730db0ca8SKalle Valo #endif /* WIRELESS_SPY */
370830db0ca8SKalle Valo 
370930db0ca8SKalle Valo 		skb->ip_summed = CHECKSUM_NONE;
371030db0ca8SKalle Valo 		skb->protocol = eth_type_trans(skb, ai->dev);
371130db0ca8SKalle Valo 		netif_rx(skb);
371230db0ca8SKalle Valo 	}
371330db0ca8SKalle Valo badrx:
371430db0ca8SKalle Valo 	if (rxd.valid == 0) {
371530db0ca8SKalle Valo 		rxd.valid = 1;
371630db0ca8SKalle Valo 		rxd.rdy = 0;
371730db0ca8SKalle Valo 		rxd.len = PKTSIZE;
371830db0ca8SKalle Valo 		memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
371930db0ca8SKalle Valo 	}
372030db0ca8SKalle Valo }
372130db0ca8SKalle Valo 
372230db0ca8SKalle Valo static void mpi_receive_802_11(struct airo_info *ai)
372330db0ca8SKalle Valo {
372430db0ca8SKalle Valo 	RxFid rxd;
372530db0ca8SKalle Valo 	struct sk_buff *skb = NULL;
372630db0ca8SKalle Valo 	u16 len, hdrlen = 0;
372730db0ca8SKalle Valo 	__le16 fc;
372830db0ca8SKalle Valo 	struct rx_hdr hdr;
372930db0ca8SKalle Valo 	u16 gap;
373030db0ca8SKalle Valo 	u16 *buffer;
373130db0ca8SKalle Valo 	char *ptr = ai->rxfids[0].virtual_host_addr + 4;
373230db0ca8SKalle Valo 
373330db0ca8SKalle Valo 	memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd));
373430db0ca8SKalle Valo 	memcpy ((char *)&hdr, ptr, sizeof(hdr));
373530db0ca8SKalle Valo 	ptr += sizeof(hdr);
373630db0ca8SKalle Valo 	/* Bad CRC. Ignore packet */
373730db0ca8SKalle Valo 	if (le16_to_cpu(hdr.status) & 2)
373830db0ca8SKalle Valo 		hdr.len = 0;
373930db0ca8SKalle Valo 	if (ai->wifidev == NULL)
374030db0ca8SKalle Valo 		hdr.len = 0;
374130db0ca8SKalle Valo 	len = le16_to_cpu(hdr.len);
374230db0ca8SKalle Valo 	if (len > AIRO_DEF_MTU) {
374330db0ca8SKalle Valo 		airo_print_err(ai->dev->name, "Bad size %d", len);
374430db0ca8SKalle Valo 		goto badrx;
374530db0ca8SKalle Valo 	}
374630db0ca8SKalle Valo 	if (len == 0)
374730db0ca8SKalle Valo 		goto badrx;
374830db0ca8SKalle Valo 
374930db0ca8SKalle Valo 	fc = get_unaligned((__le16 *)ptr);
375030db0ca8SKalle Valo 	hdrlen = header_len(fc);
375130db0ca8SKalle Valo 
375230db0ca8SKalle Valo 	skb = dev_alloc_skb(len + hdrlen + 2);
375330db0ca8SKalle Valo 	if (!skb) {
375430db0ca8SKalle Valo 		ai->dev->stats.rx_dropped++;
375530db0ca8SKalle Valo 		goto badrx;
375630db0ca8SKalle Valo 	}
37574df864c1SJohannes Berg 	buffer = skb_put(skb, len + hdrlen);
375830db0ca8SKalle Valo 	memcpy ((char *)buffer, ptr, hdrlen);
375930db0ca8SKalle Valo 	ptr += hdrlen;
376030db0ca8SKalle Valo 	if (hdrlen == 24)
376130db0ca8SKalle Valo 		ptr += 6;
376230db0ca8SKalle Valo 	gap = get_unaligned_le16(ptr);
376330db0ca8SKalle Valo 	ptr += sizeof(__le16);
376430db0ca8SKalle Valo 	if (gap) {
376530db0ca8SKalle Valo 		if (gap <= 8)
376630db0ca8SKalle Valo 			ptr += gap;
376730db0ca8SKalle Valo 		else
376830db0ca8SKalle Valo 			airo_print_err(ai->dev->name,
376930db0ca8SKalle Valo 			    "gaplen too big. Problems will follow...");
377030db0ca8SKalle Valo 	}
377130db0ca8SKalle Valo 	memcpy ((char *)buffer + hdrlen, ptr, len);
377230db0ca8SKalle Valo 	ptr += len;
377330db0ca8SKalle Valo #ifdef IW_WIRELESS_SPY	  /* defined in iw_handler.h */
377430db0ca8SKalle Valo 	if (ai->spy_data.spy_number > 0) {
377530db0ca8SKalle Valo 		char *sa;
377630db0ca8SKalle Valo 		struct iw_quality wstats;
377730db0ca8SKalle Valo 		/* Prepare spy data : addr + qual */
377830db0ca8SKalle Valo 		sa = (char*)buffer + 10;
377930db0ca8SKalle Valo 		wstats.qual = hdr.rssi[0];
378030db0ca8SKalle Valo 		if (ai->rssi)
378130db0ca8SKalle Valo 			wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
378230db0ca8SKalle Valo 		else
378330db0ca8SKalle Valo 			wstats.level = (hdr.rssi[1] + 321) / 2;
378430db0ca8SKalle Valo 		wstats.noise = ai->wstats.qual.noise;
378530db0ca8SKalle Valo 		wstats.updated = IW_QUAL_QUAL_UPDATED
378630db0ca8SKalle Valo 			| IW_QUAL_LEVEL_UPDATED
378730db0ca8SKalle Valo 			| IW_QUAL_DBM;
378830db0ca8SKalle Valo 		/* Update spy records */
378930db0ca8SKalle Valo 		wireless_spy_update(ai->dev, sa, &wstats);
379030db0ca8SKalle Valo 	}
379130db0ca8SKalle Valo #endif /* IW_WIRELESS_SPY */
379230db0ca8SKalle Valo 	skb_reset_mac_header(skb);
379330db0ca8SKalle Valo 	skb->pkt_type = PACKET_OTHERHOST;
379430db0ca8SKalle Valo 	skb->dev = ai->wifidev;
379530db0ca8SKalle Valo 	skb->protocol = htons(ETH_P_802_2);
379630db0ca8SKalle Valo 	skb->ip_summed = CHECKSUM_NONE;
379730db0ca8SKalle Valo 	netif_rx(skb);
379830db0ca8SKalle Valo 
379930db0ca8SKalle Valo badrx:
380030db0ca8SKalle Valo 	if (rxd.valid == 0) {
380130db0ca8SKalle Valo 		rxd.valid = 1;
380230db0ca8SKalle Valo 		rxd.rdy = 0;
380330db0ca8SKalle Valo 		rxd.len = PKTSIZE;
380430db0ca8SKalle Valo 		memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd));
380530db0ca8SKalle Valo 	}
380630db0ca8SKalle Valo }
380730db0ca8SKalle Valo 
380830db0ca8SKalle Valo static inline void set_auth_type(struct airo_info *local, int auth_type)
380930db0ca8SKalle Valo {
381030db0ca8SKalle Valo 	local->config.authType = auth_type;
381130db0ca8SKalle Valo 	/* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT).
381230db0ca8SKalle Valo 	 * Used by airo_set_auth()
381330db0ca8SKalle Valo 	 */
381430db0ca8SKalle Valo 	if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT)
381530db0ca8SKalle Valo 		local->last_auth = auth_type;
381630db0ca8SKalle Valo }
381730db0ca8SKalle Valo 
381830db0ca8SKalle Valo static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
381930db0ca8SKalle Valo {
382030db0ca8SKalle Valo 	Cmd cmd;
382130db0ca8SKalle Valo 	Resp rsp;
382230db0ca8SKalle Valo 	int status;
382330db0ca8SKalle Valo 	SsidRid mySsid;
382430db0ca8SKalle Valo 	__le16 lastindex;
382530db0ca8SKalle Valo 	WepKeyRid wkr;
382630db0ca8SKalle Valo 	int rc;
382730db0ca8SKalle Valo 
382830db0ca8SKalle Valo 	memset(&mySsid, 0, sizeof(mySsid));
382930db0ca8SKalle Valo 	kfree (ai->flash);
383030db0ca8SKalle Valo 	ai->flash = NULL;
383130db0ca8SKalle Valo 
383230db0ca8SKalle Valo 	/* The NOP is the first step in getting the card going */
383330db0ca8SKalle Valo 	cmd.cmd = NOP;
383430db0ca8SKalle Valo 	cmd.parm0 = cmd.parm1 = cmd.parm2 = 0;
383530db0ca8SKalle Valo 	if (lock && down_interruptible(&ai->sem))
383630db0ca8SKalle Valo 		return ERROR;
383730db0ca8SKalle Valo 	if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
383830db0ca8SKalle Valo 		if (lock)
383930db0ca8SKalle Valo 			up(&ai->sem);
384030db0ca8SKalle Valo 		return ERROR;
384130db0ca8SKalle Valo 	}
384230db0ca8SKalle Valo 	disable_MAC(ai, 0);
384330db0ca8SKalle Valo 
384430db0ca8SKalle Valo 	// Let's figure out if we need to use the AUX port
384530db0ca8SKalle Valo 	if (!test_bit(FLAG_MPI,&ai->flags)) {
384630db0ca8SKalle Valo 		cmd.cmd = CMD_ENABLEAUX;
384730db0ca8SKalle Valo 		if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
384830db0ca8SKalle Valo 			if (lock)
384930db0ca8SKalle Valo 				up(&ai->sem);
385030db0ca8SKalle Valo 			airo_print_err(ai->dev->name, "Error checking for AUX port");
385130db0ca8SKalle Valo 			return ERROR;
385230db0ca8SKalle Valo 		}
385330db0ca8SKalle Valo 		if (!aux_bap || rsp.status & 0xff00) {
385430db0ca8SKalle Valo 			ai->bap_read = fast_bap_read;
385530db0ca8SKalle Valo 			airo_print_dbg(ai->dev->name, "Doing fast bap_reads");
385630db0ca8SKalle Valo 		} else {
385730db0ca8SKalle Valo 			ai->bap_read = aux_bap_read;
385830db0ca8SKalle Valo 			airo_print_dbg(ai->dev->name, "Doing AUX bap_reads");
385930db0ca8SKalle Valo 		}
386030db0ca8SKalle Valo 	}
386130db0ca8SKalle Valo 	if (lock)
386230db0ca8SKalle Valo 		up(&ai->sem);
386330db0ca8SKalle Valo 	if (ai->config.len == 0) {
386430db0ca8SKalle Valo 		int i;
386530db0ca8SKalle Valo 		tdsRssiRid rssi_rid;
386630db0ca8SKalle Valo 		CapabilityRid cap_rid;
386730db0ca8SKalle Valo 
386830db0ca8SKalle Valo 		kfree(ai->SSID);
386930db0ca8SKalle Valo 		ai->SSID = NULL;
387030db0ca8SKalle Valo 		// general configuration (read/modify/write)
387130db0ca8SKalle Valo 		status = readConfigRid(ai, lock);
387230db0ca8SKalle Valo 		if (status != SUCCESS) return ERROR;
387330db0ca8SKalle Valo 
387430db0ca8SKalle Valo 		status = readCapabilityRid(ai, &cap_rid, lock);
387530db0ca8SKalle Valo 		if (status != SUCCESS) return ERROR;
387630db0ca8SKalle Valo 
387730db0ca8SKalle Valo 		status = PC4500_readrid(ai, RID_RSSI,&rssi_rid, sizeof(rssi_rid), lock);
387830db0ca8SKalle Valo 		if (status == SUCCESS) {
387930db0ca8SKalle Valo 			if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
388030db0ca8SKalle Valo 				memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */
388130db0ca8SKalle Valo 		}
388230db0ca8SKalle Valo 		else {
388330db0ca8SKalle Valo 			kfree(ai->rssi);
388430db0ca8SKalle Valo 			ai->rssi = NULL;
388530db0ca8SKalle Valo 			if (cap_rid.softCap & cpu_to_le16(8))
388630db0ca8SKalle Valo 				ai->config.rmode |= RXMODE_NORMALIZED_RSSI;
388730db0ca8SKalle Valo 			else
388830db0ca8SKalle Valo 				airo_print_warn(ai->dev->name, "unknown received signal "
388930db0ca8SKalle Valo 						"level scale");
389030db0ca8SKalle Valo 		}
389130db0ca8SKalle Valo 		ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
389230db0ca8SKalle Valo 		set_auth_type(ai, AUTH_OPEN);
389330db0ca8SKalle Valo 		ai->config.modulation = MOD_CCK;
389430db0ca8SKalle Valo 
389530db0ca8SKalle Valo 		if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) &&
389630db0ca8SKalle Valo 		    (cap_rid.extSoftCap & cpu_to_le16(1)) &&
389730db0ca8SKalle Valo 		    micsetup(ai) == SUCCESS) {
389830db0ca8SKalle Valo 			ai->config.opmode |= MODE_MIC;
389930db0ca8SKalle Valo 			set_bit(FLAG_MIC_CAPABLE, &ai->flags);
390030db0ca8SKalle Valo 		}
390130db0ca8SKalle Valo 
390230db0ca8SKalle Valo 		/* Save off the MAC */
390330db0ca8SKalle Valo 		for (i = 0; i < ETH_ALEN; i++) {
390430db0ca8SKalle Valo 			mac[i] = ai->config.macAddr[i];
390530db0ca8SKalle Valo 		}
390630db0ca8SKalle Valo 
390730db0ca8SKalle Valo 		/* Check to see if there are any insmod configured
390830db0ca8SKalle Valo 		   rates to add */
390930db0ca8SKalle Valo 		if (rates[0]) {
391030db0ca8SKalle Valo 			memset(ai->config.rates, 0, sizeof(ai->config.rates));
391130db0ca8SKalle Valo 			for (i = 0; i < 8 && rates[i]; i++) {
391230db0ca8SKalle Valo 				ai->config.rates[i] = rates[i];
391330db0ca8SKalle Valo 			}
391430db0ca8SKalle Valo 		}
391530db0ca8SKalle Valo 		set_bit (FLAG_COMMIT, &ai->flags);
391630db0ca8SKalle Valo 	}
391730db0ca8SKalle Valo 
391830db0ca8SKalle Valo 	/* Setup the SSIDs if present */
391930db0ca8SKalle Valo 	if (ssids[0]) {
392030db0ca8SKalle Valo 		int i;
392130db0ca8SKalle Valo 		for (i = 0; i < 3 && ssids[i]; i++) {
392230db0ca8SKalle Valo 			size_t len = strlen(ssids[i]);
392330db0ca8SKalle Valo 			if (len > 32)
392430db0ca8SKalle Valo 				len = 32;
392530db0ca8SKalle Valo 			mySsid.ssids[i].len = cpu_to_le16(len);
392630db0ca8SKalle Valo 			memcpy(mySsid.ssids[i].ssid, ssids[i], len);
392730db0ca8SKalle Valo 		}
392830db0ca8SKalle Valo 		mySsid.len = cpu_to_le16(sizeof(mySsid));
392930db0ca8SKalle Valo 	}
393030db0ca8SKalle Valo 
393130db0ca8SKalle Valo 	status = writeConfigRid(ai, lock);
393230db0ca8SKalle Valo 	if (status != SUCCESS) return ERROR;
393330db0ca8SKalle Valo 
393430db0ca8SKalle Valo 	/* Set up the SSID list */
393530db0ca8SKalle Valo 	if (ssids[0]) {
393630db0ca8SKalle Valo 		status = writeSsidRid(ai, &mySsid, lock);
393730db0ca8SKalle Valo 		if (status != SUCCESS) return ERROR;
393830db0ca8SKalle Valo 	}
393930db0ca8SKalle Valo 
394030db0ca8SKalle Valo 	status = enable_MAC(ai, lock);
394130db0ca8SKalle Valo 	if (status != SUCCESS)
394230db0ca8SKalle Valo 		return ERROR;
394330db0ca8SKalle Valo 
394430db0ca8SKalle Valo 	/* Grab the initial wep key, we gotta save it for auto_wep */
394530db0ca8SKalle Valo 	rc = readWepKeyRid(ai, &wkr, 1, lock);
394630db0ca8SKalle Valo 	if (rc == SUCCESS) do {
394730db0ca8SKalle Valo 		lastindex = wkr.kindex;
394830db0ca8SKalle Valo 		if (wkr.kindex == cpu_to_le16(0xffff)) {
394930db0ca8SKalle Valo 			ai->defindex = wkr.mac[0];
395030db0ca8SKalle Valo 		}
395130db0ca8SKalle Valo 		rc = readWepKeyRid(ai, &wkr, 0, lock);
395230db0ca8SKalle Valo 	} while (lastindex != wkr.kindex);
395330db0ca8SKalle Valo 
395430db0ca8SKalle Valo 	try_auto_wep(ai);
395530db0ca8SKalle Valo 
395630db0ca8SKalle Valo 	return SUCCESS;
395730db0ca8SKalle Valo }
395830db0ca8SKalle Valo 
3959ba4d6513SLee Jones static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp)
3960ba4d6513SLee Jones {
396130db0ca8SKalle Valo         // Im really paranoid about letting it run forever!
396230db0ca8SKalle Valo 	int max_tries = 600000;
396330db0ca8SKalle Valo 
396430db0ca8SKalle Valo 	if (IN4500(ai, EVSTAT) & EV_CMD)
396530db0ca8SKalle Valo 		OUT4500(ai, EVACK, EV_CMD);
396630db0ca8SKalle Valo 
396730db0ca8SKalle Valo 	OUT4500(ai, PARAM0, pCmd->parm0);
396830db0ca8SKalle Valo 	OUT4500(ai, PARAM1, pCmd->parm1);
396930db0ca8SKalle Valo 	OUT4500(ai, PARAM2, pCmd->parm2);
397030db0ca8SKalle Valo 	OUT4500(ai, COMMAND, pCmd->cmd);
397130db0ca8SKalle Valo 
397230db0ca8SKalle Valo 	while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) {
397330db0ca8SKalle Valo 		if ((IN4500(ai, COMMAND)) == pCmd->cmd)
397430db0ca8SKalle Valo 			// PC4500 didn't notice command, try again
397530db0ca8SKalle Valo 			OUT4500(ai, COMMAND, pCmd->cmd);
397630db0ca8SKalle Valo 		if (!in_atomic() && (max_tries & 255) == 0)
397730db0ca8SKalle Valo 			schedule();
397830db0ca8SKalle Valo 	}
397930db0ca8SKalle Valo 
398030db0ca8SKalle Valo 	if (max_tries == -1) {
398130db0ca8SKalle Valo 		airo_print_err(ai->dev->name,
398230db0ca8SKalle Valo 			"Max tries exceeded when issuing command");
398330db0ca8SKalle Valo 		if (IN4500(ai, COMMAND) & COMMAND_BUSY)
398430db0ca8SKalle Valo 			OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
398530db0ca8SKalle Valo 		return ERROR;
398630db0ca8SKalle Valo 	}
398730db0ca8SKalle Valo 
398830db0ca8SKalle Valo 	// command completed
398930db0ca8SKalle Valo 	pRsp->status = IN4500(ai, STATUS);
399030db0ca8SKalle Valo 	pRsp->rsp0 = IN4500(ai, RESP0);
399130db0ca8SKalle Valo 	pRsp->rsp1 = IN4500(ai, RESP1);
399230db0ca8SKalle Valo 	pRsp->rsp2 = IN4500(ai, RESP2);
399330db0ca8SKalle Valo 	if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET)
399430db0ca8SKalle Valo 		airo_print_err(ai->dev->name,
399530db0ca8SKalle Valo 			"cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x",
399630db0ca8SKalle Valo 			pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1,
399730db0ca8SKalle Valo 			pRsp->rsp2);
399830db0ca8SKalle Valo 
399930db0ca8SKalle Valo 	// clear stuck command busy if necessary
400030db0ca8SKalle Valo 	if (IN4500(ai, COMMAND) & COMMAND_BUSY) {
400130db0ca8SKalle Valo 		OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
400230db0ca8SKalle Valo 	}
400330db0ca8SKalle Valo 	// acknowledge processing the status/response
400430db0ca8SKalle Valo 	OUT4500(ai, EVACK, EV_CMD);
400530db0ca8SKalle Valo 
400630db0ca8SKalle Valo 	return SUCCESS;
400730db0ca8SKalle Valo }
400830db0ca8SKalle Valo 
400930db0ca8SKalle Valo /* Sets up the bap to start exchange data.  whichbap should
401030db0ca8SKalle Valo  * be one of the BAP0 or BAP1 defines.  Locks should be held before
401130db0ca8SKalle Valo  * calling! */
401230db0ca8SKalle Valo static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap)
401330db0ca8SKalle Valo {
401430db0ca8SKalle Valo 	int timeout = 50;
401530db0ca8SKalle Valo 	int max_tries = 3;
401630db0ca8SKalle Valo 
401730db0ca8SKalle Valo 	OUT4500(ai, SELECT0+whichbap, rid);
401830db0ca8SKalle Valo 	OUT4500(ai, OFFSET0+whichbap, offset);
401930db0ca8SKalle Valo 	while (1) {
402030db0ca8SKalle Valo 		int status = IN4500(ai, OFFSET0+whichbap);
402130db0ca8SKalle Valo 		if (status & BAP_BUSY) {
402230db0ca8SKalle Valo                         /* This isn't really a timeout, but its kinda
402330db0ca8SKalle Valo 			   close */
402430db0ca8SKalle Valo 			if (timeout--) {
402530db0ca8SKalle Valo 				continue;
402630db0ca8SKalle Valo 			}
402730db0ca8SKalle Valo 		} else if (status & BAP_ERR) {
402830db0ca8SKalle Valo 			/* invalid rid or offset */
402930db0ca8SKalle Valo 			airo_print_err(ai->dev->name, "BAP error %x %d",
403030db0ca8SKalle Valo 				status, whichbap);
403130db0ca8SKalle Valo 			return ERROR;
403230db0ca8SKalle Valo 		} else if (status & BAP_DONE) { // success
403330db0ca8SKalle Valo 			return SUCCESS;
403430db0ca8SKalle Valo 		}
403530db0ca8SKalle Valo 		if (!(max_tries--)) {
403630db0ca8SKalle Valo 			airo_print_err(ai->dev->name,
403730db0ca8SKalle Valo 				"BAP setup error too many retries\n");
403830db0ca8SKalle Valo 			return ERROR;
403930db0ca8SKalle Valo 		}
404030db0ca8SKalle Valo 		// -- PC4500 missed it, try again
404130db0ca8SKalle Valo 		OUT4500(ai, SELECT0+whichbap, rid);
404230db0ca8SKalle Valo 		OUT4500(ai, OFFSET0+whichbap, offset);
404330db0ca8SKalle Valo 		timeout = 50;
404430db0ca8SKalle Valo 	}
404530db0ca8SKalle Valo }
404630db0ca8SKalle Valo 
404730db0ca8SKalle Valo /* should only be called by aux_bap_read.  This aux function and the
404830db0ca8SKalle Valo    following use concepts not documented in the developers guide.  I
404930db0ca8SKalle Valo    got them from a patch given to my by Aironet */
405030db0ca8SKalle Valo static u16 aux_setup(struct airo_info *ai, u16 page,
405130db0ca8SKalle Valo 		     u16 offset, u16 *len)
405230db0ca8SKalle Valo {
405330db0ca8SKalle Valo 	u16 next;
405430db0ca8SKalle Valo 
405530db0ca8SKalle Valo 	OUT4500(ai, AUXPAGE, page);
405630db0ca8SKalle Valo 	OUT4500(ai, AUXOFF, 0);
405730db0ca8SKalle Valo 	next = IN4500(ai, AUXDATA);
405830db0ca8SKalle Valo 	*len = IN4500(ai, AUXDATA)&0xff;
405930db0ca8SKalle Valo 	if (offset != 4) OUT4500(ai, AUXOFF, offset);
406030db0ca8SKalle Valo 	return next;
406130db0ca8SKalle Valo }
406230db0ca8SKalle Valo 
406330db0ca8SKalle Valo /* requires call to bap_setup() first */
406430db0ca8SKalle Valo static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst,
406530db0ca8SKalle Valo 			int bytelen, int whichbap)
406630db0ca8SKalle Valo {
406730db0ca8SKalle Valo 	u16 len;
406830db0ca8SKalle Valo 	u16 page;
406930db0ca8SKalle Valo 	u16 offset;
407030db0ca8SKalle Valo 	u16 next;
407130db0ca8SKalle Valo 	int words;
407230db0ca8SKalle Valo 	int i;
407330db0ca8SKalle Valo 	unsigned long flags;
407430db0ca8SKalle Valo 
407530db0ca8SKalle Valo 	spin_lock_irqsave(&ai->aux_lock, flags);
407630db0ca8SKalle Valo 	page = IN4500(ai, SWS0+whichbap);
407730db0ca8SKalle Valo 	offset = IN4500(ai, SWS2+whichbap);
407830db0ca8SKalle Valo 	next = aux_setup(ai, page, offset, &len);
407930db0ca8SKalle Valo 	words = (bytelen+1)>>1;
408030db0ca8SKalle Valo 
408130db0ca8SKalle Valo 	for (i = 0; i<words;) {
408230db0ca8SKalle Valo 		int count;
408330db0ca8SKalle Valo 		count = (len>>1) < (words-i) ? (len>>1) : (words-i);
408430db0ca8SKalle Valo 		if (!do8bitIO)
408530db0ca8SKalle Valo 			insw(ai->dev->base_addr+DATA0+whichbap,
408630db0ca8SKalle Valo 			      pu16Dst+i, count);
408730db0ca8SKalle Valo 		else
408830db0ca8SKalle Valo 			insb(ai->dev->base_addr+DATA0+whichbap,
408930db0ca8SKalle Valo 			      pu16Dst+i, count << 1);
409030db0ca8SKalle Valo 		i += count;
409130db0ca8SKalle Valo 		if (i<words) {
409230db0ca8SKalle Valo 			next = aux_setup(ai, next, 4, &len);
409330db0ca8SKalle Valo 		}
409430db0ca8SKalle Valo 	}
409530db0ca8SKalle Valo 	spin_unlock_irqrestore(&ai->aux_lock, flags);
409630db0ca8SKalle Valo 	return SUCCESS;
409730db0ca8SKalle Valo }
409830db0ca8SKalle Valo 
409930db0ca8SKalle Valo 
410030db0ca8SKalle Valo /* requires call to bap_setup() first */
410130db0ca8SKalle Valo static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst,
410230db0ca8SKalle Valo 			 int bytelen, int whichbap)
410330db0ca8SKalle Valo {
410430db0ca8SKalle Valo 	bytelen = (bytelen + 1) & (~1); // round up to even value
410530db0ca8SKalle Valo 	if (!do8bitIO)
410630db0ca8SKalle Valo 		insw(ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1);
410730db0ca8SKalle Valo 	else
410830db0ca8SKalle Valo 		insb(ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen);
410930db0ca8SKalle Valo 	return SUCCESS;
411030db0ca8SKalle Valo }
411130db0ca8SKalle Valo 
411230db0ca8SKalle Valo /* requires call to bap_setup() first */
411330db0ca8SKalle Valo static int bap_write(struct airo_info *ai, const __le16 *pu16Src,
411430db0ca8SKalle Valo 		     int bytelen, int whichbap)
411530db0ca8SKalle Valo {
411630db0ca8SKalle Valo 	bytelen = (bytelen + 1) & (~1); // round up to even value
411730db0ca8SKalle Valo 	if (!do8bitIO)
411830db0ca8SKalle Valo 		outsw(ai->dev->base_addr+DATA0+whichbap,
411930db0ca8SKalle Valo 		       pu16Src, bytelen>>1);
412030db0ca8SKalle Valo 	else
412130db0ca8SKalle Valo 		outsb(ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen);
412230db0ca8SKalle Valo 	return SUCCESS;
412330db0ca8SKalle Valo }
412430db0ca8SKalle Valo 
412530db0ca8SKalle Valo static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd)
412630db0ca8SKalle Valo {
412730db0ca8SKalle Valo 	Cmd cmd; /* for issuing commands */
412830db0ca8SKalle Valo 	Resp rsp; /* response from commands */
412930db0ca8SKalle Valo 	u16 status;
413030db0ca8SKalle Valo 
413130db0ca8SKalle Valo 	memset(&cmd, 0, sizeof(cmd));
413230db0ca8SKalle Valo 	cmd.cmd = accmd;
413330db0ca8SKalle Valo 	cmd.parm0 = rid;
413430db0ca8SKalle Valo 	status = issuecommand(ai, &cmd, &rsp);
413530db0ca8SKalle Valo 	if (status != 0) return status;
413630db0ca8SKalle Valo 	if ((rsp.status & 0x7F00) != 0) {
413730db0ca8SKalle Valo 		return (accmd << 8) + (rsp.rsp0 & 0xFF);
413830db0ca8SKalle Valo 	}
413930db0ca8SKalle Valo 	return 0;
414030db0ca8SKalle Valo }
414130db0ca8SKalle Valo 
414230db0ca8SKalle Valo /*  Note, that we are using BAP1 which is also used by transmit, so
414330db0ca8SKalle Valo  *  we must get a lock. */
414430db0ca8SKalle Valo static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock)
414530db0ca8SKalle Valo {
414630db0ca8SKalle Valo 	u16 status;
414730db0ca8SKalle Valo         int rc = SUCCESS;
414830db0ca8SKalle Valo 
414930db0ca8SKalle Valo 	if (lock) {
415030db0ca8SKalle Valo 		if (down_interruptible(&ai->sem))
415130db0ca8SKalle Valo 			return ERROR;
415230db0ca8SKalle Valo 	}
415330db0ca8SKalle Valo 	if (test_bit(FLAG_MPI,&ai->flags)) {
415430db0ca8SKalle Valo 		Cmd cmd;
415530db0ca8SKalle Valo 		Resp rsp;
415630db0ca8SKalle Valo 
415730db0ca8SKalle Valo 		memset(&cmd, 0, sizeof(cmd));
415830db0ca8SKalle Valo 		memset(&rsp, 0, sizeof(rsp));
415930db0ca8SKalle Valo 		ai->config_desc.rid_desc.valid = 1;
416030db0ca8SKalle Valo 		ai->config_desc.rid_desc.len = RIDSIZE;
416130db0ca8SKalle Valo 		ai->config_desc.rid_desc.rid = 0;
416230db0ca8SKalle Valo 		ai->config_desc.rid_desc.host_addr = ai->ridbus;
416330db0ca8SKalle Valo 
416430db0ca8SKalle Valo 		cmd.cmd = CMD_ACCESS;
416530db0ca8SKalle Valo 		cmd.parm0 = rid;
416630db0ca8SKalle Valo 
416730db0ca8SKalle Valo 		memcpy_toio(ai->config_desc.card_ram_off,
416830db0ca8SKalle Valo 			&ai->config_desc.rid_desc, sizeof(Rid));
416930db0ca8SKalle Valo 
417030db0ca8SKalle Valo 		rc = issuecommand(ai, &cmd, &rsp);
417130db0ca8SKalle Valo 
417230db0ca8SKalle Valo 		if (rsp.status & 0x7f00)
417330db0ca8SKalle Valo 			rc = rsp.rsp0;
417430db0ca8SKalle Valo 		if (!rc)
417530db0ca8SKalle Valo 			memcpy(pBuf, ai->config_desc.virtual_host_addr, len);
417630db0ca8SKalle Valo 		goto done;
417730db0ca8SKalle Valo 	} else {
417830db0ca8SKalle Valo 		if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) {
417930db0ca8SKalle Valo 	                rc = status;
418030db0ca8SKalle Valo 	                goto done;
418130db0ca8SKalle Valo 	        }
418230db0ca8SKalle Valo 		if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
418330db0ca8SKalle Valo 			rc = ERROR;
418430db0ca8SKalle Valo 	                goto done;
418530db0ca8SKalle Valo 	        }
418630db0ca8SKalle Valo 		// read the rid length field
418730db0ca8SKalle Valo 		bap_read(ai, pBuf, 2, BAP1);
418830db0ca8SKalle Valo 		// length for remaining part of rid
418930db0ca8SKalle Valo 		len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2;
419030db0ca8SKalle Valo 
419130db0ca8SKalle Valo 		if (len <= 2) {
419230db0ca8SKalle Valo 			airo_print_err(ai->dev->name,
419330db0ca8SKalle Valo 				"Rid %x has a length of %d which is too short",
419430db0ca8SKalle Valo 				(int)rid, (int)len);
419530db0ca8SKalle Valo 			rc = ERROR;
419630db0ca8SKalle Valo 	                goto done;
419730db0ca8SKalle Valo 		}
419830db0ca8SKalle Valo 		// read remainder of the rid
419930db0ca8SKalle Valo 		rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1);
420030db0ca8SKalle Valo 	}
420130db0ca8SKalle Valo done:
420230db0ca8SKalle Valo 	if (lock)
420330db0ca8SKalle Valo 		up(&ai->sem);
420430db0ca8SKalle Valo 	return rc;
420530db0ca8SKalle Valo }
420630db0ca8SKalle Valo 
420730db0ca8SKalle Valo /*  Note, that we are using BAP1 which is also used by transmit, so
420830db0ca8SKalle Valo  *  make sure this isn't called when a transmit is happening */
420930db0ca8SKalle Valo static int PC4500_writerid(struct airo_info *ai, u16 rid,
421030db0ca8SKalle Valo 			   const void *pBuf, int len, int lock)
421130db0ca8SKalle Valo {
421230db0ca8SKalle Valo 	u16 status;
421330db0ca8SKalle Valo 	int rc = SUCCESS;
421430db0ca8SKalle Valo 
421530db0ca8SKalle Valo 	*(__le16*)pBuf = cpu_to_le16((u16)len);
421630db0ca8SKalle Valo 
421730db0ca8SKalle Valo 	if (lock) {
421830db0ca8SKalle Valo 		if (down_interruptible(&ai->sem))
421930db0ca8SKalle Valo 			return ERROR;
422030db0ca8SKalle Valo 	}
422130db0ca8SKalle Valo 	if (test_bit(FLAG_MPI,&ai->flags)) {
422230db0ca8SKalle Valo 		Cmd cmd;
422330db0ca8SKalle Valo 		Resp rsp;
422430db0ca8SKalle Valo 
422530db0ca8SKalle Valo 		if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid))
422630db0ca8SKalle Valo 			airo_print_err(ai->dev->name,
422730db0ca8SKalle Valo 				"%s: MAC should be disabled (rid=%04x)",
422830db0ca8SKalle Valo 				__func__, rid);
422930db0ca8SKalle Valo 		memset(&cmd, 0, sizeof(cmd));
423030db0ca8SKalle Valo 		memset(&rsp, 0, sizeof(rsp));
423130db0ca8SKalle Valo 
423230db0ca8SKalle Valo 		ai->config_desc.rid_desc.valid = 1;
423330db0ca8SKalle Valo 		ai->config_desc.rid_desc.len = *((u16 *)pBuf);
423430db0ca8SKalle Valo 		ai->config_desc.rid_desc.rid = 0;
423530db0ca8SKalle Valo 
423630db0ca8SKalle Valo 		cmd.cmd = CMD_WRITERID;
423730db0ca8SKalle Valo 		cmd.parm0 = rid;
423830db0ca8SKalle Valo 
423930db0ca8SKalle Valo 		memcpy_toio(ai->config_desc.card_ram_off,
424030db0ca8SKalle Valo 			&ai->config_desc.rid_desc, sizeof(Rid));
424130db0ca8SKalle Valo 
424230db0ca8SKalle Valo 		if (len < 4 || len > 2047) {
424330db0ca8SKalle Valo 			airo_print_err(ai->dev->name, "%s: len=%d", __func__, len);
424430db0ca8SKalle Valo 			rc = -1;
424530db0ca8SKalle Valo 		} else {
424630db0ca8SKalle Valo 			memcpy(ai->config_desc.virtual_host_addr,
424730db0ca8SKalle Valo 				pBuf, len);
424830db0ca8SKalle Valo 
424930db0ca8SKalle Valo 			rc = issuecommand(ai, &cmd, &rsp);
425030db0ca8SKalle Valo 			if ((rc & 0xff00) != 0) {
425130db0ca8SKalle Valo 				airo_print_err(ai->dev->name, "%s: Write rid Error %d",
425230db0ca8SKalle Valo 						__func__, rc);
425330db0ca8SKalle Valo 				airo_print_err(ai->dev->name, "%s: Cmd=%04x",
425430db0ca8SKalle Valo 						__func__, cmd.cmd);
425530db0ca8SKalle Valo 			}
425630db0ca8SKalle Valo 
425730db0ca8SKalle Valo 			if ((rsp.status & 0x7f00))
425830db0ca8SKalle Valo 				rc = rsp.rsp0;
425930db0ca8SKalle Valo 		}
426030db0ca8SKalle Valo 	} else {
426130db0ca8SKalle Valo 		// --- first access so that we can write the rid data
426230db0ca8SKalle Valo 		if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) {
426330db0ca8SKalle Valo 	                rc = status;
426430db0ca8SKalle Valo 	                goto done;
426530db0ca8SKalle Valo 	        }
426630db0ca8SKalle Valo 		// --- now write the rid data
426730db0ca8SKalle Valo 		if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) {
426830db0ca8SKalle Valo 	                rc = ERROR;
426930db0ca8SKalle Valo 	                goto done;
427030db0ca8SKalle Valo 	        }
427130db0ca8SKalle Valo 		bap_write(ai, pBuf, len, BAP1);
427230db0ca8SKalle Valo 		// ---now commit the rid data
427330db0ca8SKalle Valo 		rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS);
427430db0ca8SKalle Valo 	}
427530db0ca8SKalle Valo done:
427630db0ca8SKalle Valo 	if (lock)
427730db0ca8SKalle Valo 		up(&ai->sem);
427830db0ca8SKalle Valo         return rc;
427930db0ca8SKalle Valo }
428030db0ca8SKalle Valo 
428130db0ca8SKalle Valo /* Allocates a FID to be used for transmitting packets.  We only use
428230db0ca8SKalle Valo    one for now. */
428330db0ca8SKalle Valo static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw)
428430db0ca8SKalle Valo {
428530db0ca8SKalle Valo 	unsigned int loop = 3000;
428630db0ca8SKalle Valo 	Cmd cmd;
428730db0ca8SKalle Valo 	Resp rsp;
428830db0ca8SKalle Valo 	u16 txFid;
428930db0ca8SKalle Valo 	__le16 txControl;
429030db0ca8SKalle Valo 
429130db0ca8SKalle Valo 	cmd.cmd = CMD_ALLOCATETX;
429230db0ca8SKalle Valo 	cmd.parm0 = lenPayload;
429330db0ca8SKalle Valo 	if (down_interruptible(&ai->sem))
429430db0ca8SKalle Valo 		return ERROR;
429530db0ca8SKalle Valo 	if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
429630db0ca8SKalle Valo 		txFid = ERROR;
429730db0ca8SKalle Valo 		goto done;
429830db0ca8SKalle Valo 	}
429930db0ca8SKalle Valo 	if ((rsp.status & 0xFF00) != 0) {
430030db0ca8SKalle Valo 		txFid = ERROR;
430130db0ca8SKalle Valo 		goto done;
430230db0ca8SKalle Valo 	}
430330db0ca8SKalle Valo 	/* wait for the allocate event/indication
430430db0ca8SKalle Valo 	 * It makes me kind of nervous that this can just sit here and spin,
430530db0ca8SKalle Valo 	 * but in practice it only loops like four times. */
430630db0ca8SKalle Valo 	while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop);
430730db0ca8SKalle Valo 	if (!loop) {
430830db0ca8SKalle Valo 		txFid = ERROR;
430930db0ca8SKalle Valo 		goto done;
431030db0ca8SKalle Valo 	}
431130db0ca8SKalle Valo 
431230db0ca8SKalle Valo 	// get the allocated fid and acknowledge
431330db0ca8SKalle Valo 	txFid = IN4500(ai, TXALLOCFID);
431430db0ca8SKalle Valo 	OUT4500(ai, EVACK, EV_ALLOC);
431530db0ca8SKalle Valo 
431630db0ca8SKalle Valo 	/*  The CARD is pretty cool since it converts the ethernet packet
431730db0ca8SKalle Valo 	 *  into 802.11.  Also note that we don't release the FID since we
431830db0ca8SKalle Valo 	 *  will be using the same one over and over again. */
431930db0ca8SKalle Valo 	/*  We only have to setup the control once since we are not
432030db0ca8SKalle Valo 	 *  releasing the fid. */
432130db0ca8SKalle Valo 	if (raw)
432230db0ca8SKalle Valo 		txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11
432330db0ca8SKalle Valo 			| TXCTL_ETHERNET | TXCTL_NORELEASE);
432430db0ca8SKalle Valo 	else
432530db0ca8SKalle Valo 		txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3
432630db0ca8SKalle Valo 			| TXCTL_ETHERNET | TXCTL_NORELEASE);
432730db0ca8SKalle Valo 	if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS)
432830db0ca8SKalle Valo 		txFid = ERROR;
432930db0ca8SKalle Valo 	else
433030db0ca8SKalle Valo 		bap_write(ai, &txControl, sizeof(txControl), BAP1);
433130db0ca8SKalle Valo 
433230db0ca8SKalle Valo done:
433330db0ca8SKalle Valo 	up(&ai->sem);
433430db0ca8SKalle Valo 
433530db0ca8SKalle Valo 	return txFid;
433630db0ca8SKalle Valo }
433730db0ca8SKalle Valo 
433830db0ca8SKalle Valo /* In general BAP1 is dedicated to transmiting packets.  However,
433930db0ca8SKalle Valo    since we need a BAP when accessing RIDs, we also use BAP1 for that.
434030db0ca8SKalle Valo    Make sure the BAP1 spinlock is held when this is called. */
434130db0ca8SKalle Valo static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket)
434230db0ca8SKalle Valo {
434330db0ca8SKalle Valo 	__le16 payloadLen;
434430db0ca8SKalle Valo 	Cmd cmd;
434530db0ca8SKalle Valo 	Resp rsp;
434630db0ca8SKalle Valo 	int miclen = 0;
434730db0ca8SKalle Valo 	u16 txFid = len;
434830db0ca8SKalle Valo 	MICBuffer pMic;
434930db0ca8SKalle Valo 
435030db0ca8SKalle Valo 	len >>= 16;
435130db0ca8SKalle Valo 
435230db0ca8SKalle Valo 	if (len <= ETH_ALEN * 2) {
435330db0ca8SKalle Valo 		airo_print_warn(ai->dev->name, "Short packet %d", len);
435430db0ca8SKalle Valo 		return ERROR;
435530db0ca8SKalle Valo 	}
435630db0ca8SKalle Valo 	len -= ETH_ALEN * 2;
435730db0ca8SKalle Valo 
435830db0ca8SKalle Valo 	if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled &&
435930db0ca8SKalle Valo 	    (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) {
436030db0ca8SKalle Valo 		if (encapsulate(ai, (etherHead *)pPacket,&pMic, len) != SUCCESS)
436130db0ca8SKalle Valo 			return ERROR;
436230db0ca8SKalle Valo 		miclen = sizeof(pMic);
436330db0ca8SKalle Valo 	}
436430db0ca8SKalle Valo 	// packet is destination[6], source[6], payload[len-12]
436530db0ca8SKalle Valo 	// write the payload length and dst/src/payload
436630db0ca8SKalle Valo 	if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR;
436730db0ca8SKalle Valo 	/* The hardware addresses aren't counted as part of the payload, so
436830db0ca8SKalle Valo 	 * we have to subtract the 12 bytes for the addresses off */
436930db0ca8SKalle Valo 	payloadLen = cpu_to_le16(len + miclen);
437030db0ca8SKalle Valo 	bap_write(ai, &payloadLen, sizeof(payloadLen), BAP1);
437130db0ca8SKalle Valo 	bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1);
437230db0ca8SKalle Valo 	if (miclen)
437330db0ca8SKalle Valo 		bap_write(ai, (__le16*)&pMic, miclen, BAP1);
437430db0ca8SKalle Valo 	bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1);
437530db0ca8SKalle Valo 	// issue the transmit command
437630db0ca8SKalle Valo 	memset(&cmd, 0, sizeof(cmd));
437730db0ca8SKalle Valo 	cmd.cmd = CMD_TRANSMIT;
437830db0ca8SKalle Valo 	cmd.parm0 = txFid;
437930db0ca8SKalle Valo 	if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
438030db0ca8SKalle Valo 	if ((rsp.status & 0xFF00) != 0) return ERROR;
438130db0ca8SKalle Valo 	return SUCCESS;
438230db0ca8SKalle Valo }
438330db0ca8SKalle Valo 
438430db0ca8SKalle Valo static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket)
438530db0ca8SKalle Valo {
438630db0ca8SKalle Valo 	__le16 fc, payloadLen;
438730db0ca8SKalle Valo 	Cmd cmd;
438830db0ca8SKalle Valo 	Resp rsp;
438930db0ca8SKalle Valo 	int hdrlen;
439030db0ca8SKalle Valo 	static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6};
439130db0ca8SKalle Valo 	/* padding of header to full size + le16 gaplen (6) + gaplen bytes */
439230db0ca8SKalle Valo 	u16 txFid = len;
439330db0ca8SKalle Valo 	len >>= 16;
439430db0ca8SKalle Valo 
439530db0ca8SKalle Valo 	fc = *(__le16*)pPacket;
439630db0ca8SKalle Valo 	hdrlen = header_len(fc);
439730db0ca8SKalle Valo 
439830db0ca8SKalle Valo 	if (len < hdrlen) {
439930db0ca8SKalle Valo 		airo_print_warn(ai->dev->name, "Short packet %d", len);
440030db0ca8SKalle Valo 		return ERROR;
440130db0ca8SKalle Valo 	}
440230db0ca8SKalle Valo 
440330db0ca8SKalle Valo 	/* packet is 802.11 header +  payload
440430db0ca8SKalle Valo 	 * write the payload length and dst/src/payload */
440530db0ca8SKalle Valo 	if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR;
440630db0ca8SKalle Valo 	/* The 802.11 header aren't counted as part of the payload, so
440730db0ca8SKalle Valo 	 * we have to subtract the header bytes off */
440830db0ca8SKalle Valo 	payloadLen = cpu_to_le16(len-hdrlen);
440930db0ca8SKalle Valo 	bap_write(ai, &payloadLen, sizeof(payloadLen), BAP1);
441030db0ca8SKalle Valo 	if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR;
441130db0ca8SKalle Valo 	bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1);
441230db0ca8SKalle Valo 	bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1);
441330db0ca8SKalle Valo 
441430db0ca8SKalle Valo 	bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1);
441530db0ca8SKalle Valo 	// issue the transmit command
441630db0ca8SKalle Valo 	memset(&cmd, 0, sizeof(cmd));
441730db0ca8SKalle Valo 	cmd.cmd = CMD_TRANSMIT;
441830db0ca8SKalle Valo 	cmd.parm0 = txFid;
441930db0ca8SKalle Valo 	if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR;
442030db0ca8SKalle Valo 	if ((rsp.status & 0xFF00) != 0) return ERROR;
442130db0ca8SKalle Valo 	return SUCCESS;
442230db0ca8SKalle Valo }
442330db0ca8SKalle Valo 
442430db0ca8SKalle Valo /*
442530db0ca8SKalle Valo  *  This is the proc_fs routines.  It is a bit messier than I would
442630db0ca8SKalle Valo  *  like!  Feel free to clean it up!
442730db0ca8SKalle Valo  */
442830db0ca8SKalle Valo 
442930db0ca8SKalle Valo static ssize_t proc_read(struct file *file,
443030db0ca8SKalle Valo 			  char __user *buffer,
443130db0ca8SKalle Valo 			  size_t len,
443230db0ca8SKalle Valo 			  loff_t *offset);
443330db0ca8SKalle Valo 
443430db0ca8SKalle Valo static ssize_t proc_write(struct file *file,
443530db0ca8SKalle Valo 			   const char __user *buffer,
443630db0ca8SKalle Valo 			   size_t len,
443730db0ca8SKalle Valo 			   loff_t *offset);
443830db0ca8SKalle Valo static int proc_close(struct inode *inode, struct file *file);
443930db0ca8SKalle Valo 
444030db0ca8SKalle Valo static int proc_stats_open(struct inode *inode, struct file *file);
444130db0ca8SKalle Valo static int proc_statsdelta_open(struct inode *inode, struct file *file);
444230db0ca8SKalle Valo static int proc_status_open(struct inode *inode, struct file *file);
444330db0ca8SKalle Valo static int proc_SSID_open(struct inode *inode, struct file *file);
444430db0ca8SKalle Valo static int proc_APList_open(struct inode *inode, struct file *file);
444530db0ca8SKalle Valo static int proc_BSSList_open(struct inode *inode, struct file *file);
444630db0ca8SKalle Valo static int proc_config_open(struct inode *inode, struct file *file);
444730db0ca8SKalle Valo static int proc_wepkey_open(struct inode *inode, struct file *file);
444830db0ca8SKalle Valo 
444997a32539SAlexey Dobriyan static const struct proc_ops proc_statsdelta_ops = {
445097a32539SAlexey Dobriyan 	.proc_read	= proc_read,
445197a32539SAlexey Dobriyan 	.proc_open	= proc_statsdelta_open,
445297a32539SAlexey Dobriyan 	.proc_release	= proc_close,
445397a32539SAlexey Dobriyan 	.proc_lseek	= default_llseek,
445430db0ca8SKalle Valo };
445530db0ca8SKalle Valo 
445697a32539SAlexey Dobriyan static const struct proc_ops proc_stats_ops = {
445797a32539SAlexey Dobriyan 	.proc_read	= proc_read,
445897a32539SAlexey Dobriyan 	.proc_open	= proc_stats_open,
445997a32539SAlexey Dobriyan 	.proc_release	= proc_close,
446097a32539SAlexey Dobriyan 	.proc_lseek	= default_llseek,
446130db0ca8SKalle Valo };
446230db0ca8SKalle Valo 
446397a32539SAlexey Dobriyan static const struct proc_ops proc_status_ops = {
446497a32539SAlexey Dobriyan 	.proc_read	= proc_read,
446597a32539SAlexey Dobriyan 	.proc_open	= proc_status_open,
446697a32539SAlexey Dobriyan 	.proc_release	= proc_close,
446797a32539SAlexey Dobriyan 	.proc_lseek	= default_llseek,
446830db0ca8SKalle Valo };
446930db0ca8SKalle Valo 
447097a32539SAlexey Dobriyan static const struct proc_ops proc_SSID_ops = {
447197a32539SAlexey Dobriyan 	.proc_read	= proc_read,
447297a32539SAlexey Dobriyan 	.proc_write	= proc_write,
447397a32539SAlexey Dobriyan 	.proc_open	= proc_SSID_open,
447497a32539SAlexey Dobriyan 	.proc_release	= proc_close,
447597a32539SAlexey Dobriyan 	.proc_lseek	= default_llseek,
447630db0ca8SKalle Valo };
447730db0ca8SKalle Valo 
447897a32539SAlexey Dobriyan static const struct proc_ops proc_BSSList_ops = {
447997a32539SAlexey Dobriyan 	.proc_read	= proc_read,
448097a32539SAlexey Dobriyan 	.proc_write	= proc_write,
448197a32539SAlexey Dobriyan 	.proc_open	= proc_BSSList_open,
448297a32539SAlexey Dobriyan 	.proc_release	= proc_close,
448397a32539SAlexey Dobriyan 	.proc_lseek	= default_llseek,
448430db0ca8SKalle Valo };
448530db0ca8SKalle Valo 
448697a32539SAlexey Dobriyan static const struct proc_ops proc_APList_ops = {
448797a32539SAlexey Dobriyan 	.proc_read	= proc_read,
448897a32539SAlexey Dobriyan 	.proc_write	= proc_write,
448997a32539SAlexey Dobriyan 	.proc_open	= proc_APList_open,
449097a32539SAlexey Dobriyan 	.proc_release	= proc_close,
449197a32539SAlexey Dobriyan 	.proc_lseek	= default_llseek,
449230db0ca8SKalle Valo };
449330db0ca8SKalle Valo 
449497a32539SAlexey Dobriyan static const struct proc_ops proc_config_ops = {
449597a32539SAlexey Dobriyan 	.proc_read	= proc_read,
449697a32539SAlexey Dobriyan 	.proc_write	= proc_write,
449797a32539SAlexey Dobriyan 	.proc_open	= proc_config_open,
449897a32539SAlexey Dobriyan 	.proc_release	= proc_close,
449997a32539SAlexey Dobriyan 	.proc_lseek	= default_llseek,
450030db0ca8SKalle Valo };
450130db0ca8SKalle Valo 
450297a32539SAlexey Dobriyan static const struct proc_ops proc_wepkey_ops = {
450397a32539SAlexey Dobriyan 	.proc_read	= proc_read,
450497a32539SAlexey Dobriyan 	.proc_write	= proc_write,
450597a32539SAlexey Dobriyan 	.proc_open	= proc_wepkey_open,
450697a32539SAlexey Dobriyan 	.proc_release	= proc_close,
450797a32539SAlexey Dobriyan 	.proc_lseek	= default_llseek,
450830db0ca8SKalle Valo };
450930db0ca8SKalle Valo 
451030db0ca8SKalle Valo static struct proc_dir_entry *airo_entry;
451130db0ca8SKalle Valo 
451230db0ca8SKalle Valo struct proc_data {
451330db0ca8SKalle Valo 	int release_buffer;
451430db0ca8SKalle Valo 	int readlen;
451530db0ca8SKalle Valo 	char *rbuffer;
451630db0ca8SKalle Valo 	int writelen;
451730db0ca8SKalle Valo 	int maxwritelen;
451830db0ca8SKalle Valo 	char *wbuffer;
451930db0ca8SKalle Valo 	void (*on_close) (struct inode *, struct file *);
452030db0ca8SKalle Valo };
452130db0ca8SKalle Valo 
452230db0ca8SKalle Valo static int setup_proc_entry(struct net_device *dev,
4523ba4d6513SLee Jones 			     struct airo_info *apriv)
4524ba4d6513SLee Jones {
452530db0ca8SKalle Valo 	struct proc_dir_entry *entry;
452630db0ca8SKalle Valo 
452730db0ca8SKalle Valo 	/* First setup the device directory */
452830db0ca8SKalle Valo 	strcpy(apriv->proc_name, dev->name);
452930db0ca8SKalle Valo 	apriv->proc_entry = proc_mkdir_mode(apriv->proc_name, airo_perm,
453030db0ca8SKalle Valo 					    airo_entry);
453130db0ca8SKalle Valo 	if (!apriv->proc_entry)
453230db0ca8SKalle Valo 		return -ENOMEM;
453330db0ca8SKalle Valo 	proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid);
453430db0ca8SKalle Valo 
453530db0ca8SKalle Valo 	/* Setup the StatsDelta */
45362ef00c53SJoe Perches 	entry = proc_create_data("StatsDelta", 0444 & proc_perm,
453730db0ca8SKalle Valo 				 apriv->proc_entry, &proc_statsdelta_ops, dev);
453830db0ca8SKalle Valo 	if (!entry)
453930db0ca8SKalle Valo 		goto fail;
454030db0ca8SKalle Valo 	proc_set_user(entry, proc_kuid, proc_kgid);
454130db0ca8SKalle Valo 
454230db0ca8SKalle Valo 	/* Setup the Stats */
45432ef00c53SJoe Perches 	entry = proc_create_data("Stats", 0444 & proc_perm,
454430db0ca8SKalle Valo 				 apriv->proc_entry, &proc_stats_ops, dev);
454530db0ca8SKalle Valo 	if (!entry)
454630db0ca8SKalle Valo 		goto fail;
454730db0ca8SKalle Valo 	proc_set_user(entry, proc_kuid, proc_kgid);
454830db0ca8SKalle Valo 
454930db0ca8SKalle Valo 	/* Setup the Status */
45502ef00c53SJoe Perches 	entry = proc_create_data("Status", 0444 & proc_perm,
455130db0ca8SKalle Valo 				 apriv->proc_entry, &proc_status_ops, dev);
455230db0ca8SKalle Valo 	if (!entry)
455330db0ca8SKalle Valo 		goto fail;
455430db0ca8SKalle Valo 	proc_set_user(entry, proc_kuid, proc_kgid);
455530db0ca8SKalle Valo 
455630db0ca8SKalle Valo 	/* Setup the Config */
455730db0ca8SKalle Valo 	entry = proc_create_data("Config", proc_perm,
455830db0ca8SKalle Valo 				 apriv->proc_entry, &proc_config_ops, dev);
455930db0ca8SKalle Valo 	if (!entry)
456030db0ca8SKalle Valo 		goto fail;
456130db0ca8SKalle Valo 	proc_set_user(entry, proc_kuid, proc_kgid);
456230db0ca8SKalle Valo 
456330db0ca8SKalle Valo 	/* Setup the SSID */
456430db0ca8SKalle Valo 	entry = proc_create_data("SSID", proc_perm,
456530db0ca8SKalle Valo 				 apriv->proc_entry, &proc_SSID_ops, dev);
456630db0ca8SKalle Valo 	if (!entry)
456730db0ca8SKalle Valo 		goto fail;
456830db0ca8SKalle Valo 	proc_set_user(entry, proc_kuid, proc_kgid);
456930db0ca8SKalle Valo 
457030db0ca8SKalle Valo 	/* Setup the APList */
457130db0ca8SKalle Valo 	entry = proc_create_data("APList", proc_perm,
457230db0ca8SKalle Valo 				 apriv->proc_entry, &proc_APList_ops, dev);
457330db0ca8SKalle Valo 	if (!entry)
457430db0ca8SKalle Valo 		goto fail;
457530db0ca8SKalle Valo 	proc_set_user(entry, proc_kuid, proc_kgid);
457630db0ca8SKalle Valo 
457730db0ca8SKalle Valo 	/* Setup the BSSList */
457830db0ca8SKalle Valo 	entry = proc_create_data("BSSList", proc_perm,
457930db0ca8SKalle Valo 				 apriv->proc_entry, &proc_BSSList_ops, dev);
458030db0ca8SKalle Valo 	if (!entry)
458130db0ca8SKalle Valo 		goto fail;
458230db0ca8SKalle Valo 	proc_set_user(entry, proc_kuid, proc_kgid);
458330db0ca8SKalle Valo 
458430db0ca8SKalle Valo 	/* Setup the WepKey */
458530db0ca8SKalle Valo 	entry = proc_create_data("WepKey", proc_perm,
458630db0ca8SKalle Valo 				 apriv->proc_entry, &proc_wepkey_ops, dev);
458730db0ca8SKalle Valo 	if (!entry)
458830db0ca8SKalle Valo 		goto fail;
458930db0ca8SKalle Valo 	proc_set_user(entry, proc_kuid, proc_kgid);
459030db0ca8SKalle Valo 	return 0;
459130db0ca8SKalle Valo 
459230db0ca8SKalle Valo fail:
459330db0ca8SKalle Valo 	remove_proc_subtree(apriv->proc_name, airo_entry);
459430db0ca8SKalle Valo 	return -ENOMEM;
459530db0ca8SKalle Valo }
459630db0ca8SKalle Valo 
459730db0ca8SKalle Valo static int takedown_proc_entry(struct net_device *dev,
459830db0ca8SKalle Valo 				struct airo_info *apriv)
459930db0ca8SKalle Valo {
460030db0ca8SKalle Valo 	remove_proc_subtree(apriv->proc_name, airo_entry);
460130db0ca8SKalle Valo 	return 0;
460230db0ca8SKalle Valo }
460330db0ca8SKalle Valo 
460430db0ca8SKalle Valo /*
460530db0ca8SKalle Valo  *  What we want from the proc_fs is to be able to efficiently read
460630db0ca8SKalle Valo  *  and write the configuration.  To do this, we want to read the
460730db0ca8SKalle Valo  *  configuration when the file is opened and write it when the file is
460830db0ca8SKalle Valo  *  closed.  So basically we allocate a read buffer at open and fill it
460930db0ca8SKalle Valo  *  with data, and allocate a write buffer and read it at close.
461030db0ca8SKalle Valo  */
461130db0ca8SKalle Valo 
461230db0ca8SKalle Valo /*
461330db0ca8SKalle Valo  *  The read routine is generic, it relies on the preallocated rbuffer
461430db0ca8SKalle Valo  *  to supply the data.
461530db0ca8SKalle Valo  */
461630db0ca8SKalle Valo static ssize_t proc_read(struct file *file,
461730db0ca8SKalle Valo 			  char __user *buffer,
461830db0ca8SKalle Valo 			  size_t len,
461930db0ca8SKalle Valo 			  loff_t *offset)
462030db0ca8SKalle Valo {
462130db0ca8SKalle Valo 	struct proc_data *priv = file->private_data;
462230db0ca8SKalle Valo 
462330db0ca8SKalle Valo 	if (!priv->rbuffer)
462430db0ca8SKalle Valo 		return -EINVAL;
462530db0ca8SKalle Valo 
462630db0ca8SKalle Valo 	return simple_read_from_buffer(buffer, len, offset, priv->rbuffer,
462730db0ca8SKalle Valo 					priv->readlen);
462830db0ca8SKalle Valo }
462930db0ca8SKalle Valo 
463030db0ca8SKalle Valo /*
463130db0ca8SKalle Valo  *  The write routine is generic, it fills in a preallocated rbuffer
463230db0ca8SKalle Valo  *  to supply the data.
463330db0ca8SKalle Valo  */
463430db0ca8SKalle Valo static ssize_t proc_write(struct file *file,
463530db0ca8SKalle Valo 			   const char __user *buffer,
463630db0ca8SKalle Valo 			   size_t len,
463730db0ca8SKalle Valo 			   loff_t *offset)
463830db0ca8SKalle Valo {
463930db0ca8SKalle Valo 	ssize_t ret;
464030db0ca8SKalle Valo 	struct proc_data *priv = file->private_data;
464130db0ca8SKalle Valo 
464230db0ca8SKalle Valo 	if (!priv->wbuffer)
464330db0ca8SKalle Valo 		return -EINVAL;
464430db0ca8SKalle Valo 
464530db0ca8SKalle Valo 	ret = simple_write_to_buffer(priv->wbuffer, priv->maxwritelen, offset,
464630db0ca8SKalle Valo 					buffer, len);
464730db0ca8SKalle Valo 	if (ret > 0)
464830db0ca8SKalle Valo 		priv->writelen = max_t(int, priv->writelen, *offset);
464930db0ca8SKalle Valo 
465030db0ca8SKalle Valo 	return ret;
465130db0ca8SKalle Valo }
465230db0ca8SKalle Valo 
465330db0ca8SKalle Valo static int proc_status_open(struct inode *inode, struct file *file)
465430db0ca8SKalle Valo {
465530db0ca8SKalle Valo 	struct proc_data *data;
465630db0ca8SKalle Valo 	struct net_device *dev = PDE_DATA(inode);
465730db0ca8SKalle Valo 	struct airo_info *apriv = dev->ml_priv;
465830db0ca8SKalle Valo 	CapabilityRid cap_rid;
465930db0ca8SKalle Valo 	StatusRid status_rid;
466030db0ca8SKalle Valo 	u16 mode;
466130db0ca8SKalle Valo 	int i;
466230db0ca8SKalle Valo 
466330db0ca8SKalle Valo 	if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
466430db0ca8SKalle Valo 		return -ENOMEM;
466530db0ca8SKalle Valo 	data = file->private_data;
466630db0ca8SKalle Valo 	if ((data->rbuffer = kmalloc(2048, GFP_KERNEL)) == NULL) {
466730db0ca8SKalle Valo 		kfree (file->private_data);
466830db0ca8SKalle Valo 		return -ENOMEM;
466930db0ca8SKalle Valo 	}
467030db0ca8SKalle Valo 
467130db0ca8SKalle Valo 	readStatusRid(apriv, &status_rid, 1);
467230db0ca8SKalle Valo 	readCapabilityRid(apriv, &cap_rid, 1);
467330db0ca8SKalle Valo 
467430db0ca8SKalle Valo 	mode = le16_to_cpu(status_rid.mode);
467530db0ca8SKalle Valo 
467630db0ca8SKalle Valo         i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n",
467730db0ca8SKalle Valo                     mode & 1 ? "CFG ": "",
467830db0ca8SKalle Valo                     mode & 2 ? "ACT ": "",
467930db0ca8SKalle Valo                     mode & 0x10 ? "SYN ": "",
468030db0ca8SKalle Valo                     mode & 0x20 ? "LNK ": "",
468130db0ca8SKalle Valo                     mode & 0x40 ? "LEAP ": "",
468230db0ca8SKalle Valo                     mode & 0x80 ? "PRIV ": "",
468330db0ca8SKalle Valo                     mode & 0x100 ? "KEY ": "",
468430db0ca8SKalle Valo                     mode & 0x200 ? "WEP ": "",
468530db0ca8SKalle Valo                     mode & 0x8000 ? "ERR ": "");
468630db0ca8SKalle Valo 	sprintf(data->rbuffer+i, "Mode: %x\n"
468730db0ca8SKalle Valo 		 "Signal Strength: %d\n"
468830db0ca8SKalle Valo 		 "Signal Quality: %d\n"
468930db0ca8SKalle Valo 		 "SSID: %-.*s\n"
469030db0ca8SKalle Valo 		 "AP: %-.16s\n"
469130db0ca8SKalle Valo 		 "Freq: %d\n"
469230db0ca8SKalle Valo 		 "BitRate: %dmbs\n"
469330db0ca8SKalle Valo 		 "Driver Version: %s\n"
469430db0ca8SKalle Valo 		 "Device: %s\nManufacturer: %s\nFirmware Version: %s\n"
469530db0ca8SKalle Valo 		 "Radio type: %x\nCountry: %x\nHardware Version: %x\n"
469630db0ca8SKalle Valo 		 "Software Version: %x\nSoftware Subversion: %x\n"
469730db0ca8SKalle Valo 		 "Boot block version: %x\n",
469830db0ca8SKalle Valo 		 le16_to_cpu(status_rid.mode),
469930db0ca8SKalle Valo 		 le16_to_cpu(status_rid.normalizedSignalStrength),
470030db0ca8SKalle Valo 		 le16_to_cpu(status_rid.signalQuality),
470130db0ca8SKalle Valo 		 le16_to_cpu(status_rid.SSIDlen),
470230db0ca8SKalle Valo 		 status_rid.SSID,
470330db0ca8SKalle Valo 		 status_rid.apName,
470430db0ca8SKalle Valo 		 le16_to_cpu(status_rid.channel),
470530db0ca8SKalle Valo 		 le16_to_cpu(status_rid.currentXmitRate) / 2,
470630db0ca8SKalle Valo 		 version,
470730db0ca8SKalle Valo 		 cap_rid.prodName,
470830db0ca8SKalle Valo 		 cap_rid.manName,
470930db0ca8SKalle Valo 		 cap_rid.prodVer,
471030db0ca8SKalle Valo 		 le16_to_cpu(cap_rid.radioType),
471130db0ca8SKalle Valo 		 le16_to_cpu(cap_rid.country),
471230db0ca8SKalle Valo 		 le16_to_cpu(cap_rid.hardVer),
471330db0ca8SKalle Valo 		 le16_to_cpu(cap_rid.softVer),
471430db0ca8SKalle Valo 		 le16_to_cpu(cap_rid.softSubVer),
471530db0ca8SKalle Valo 		 le16_to_cpu(cap_rid.bootBlockVer));
471630db0ca8SKalle Valo 	data->readlen = strlen(data->rbuffer);
471730db0ca8SKalle Valo 	return 0;
471830db0ca8SKalle Valo }
471930db0ca8SKalle Valo 
472030db0ca8SKalle Valo static int proc_stats_rid_open(struct inode*, struct file*, u16);
472130db0ca8SKalle Valo static int proc_statsdelta_open(struct inode *inode,
4722ba4d6513SLee Jones 				 struct file *file)
4723ba4d6513SLee Jones {
472430db0ca8SKalle Valo 	if (file->f_mode&FMODE_WRITE) {
472530db0ca8SKalle Valo 		return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR);
472630db0ca8SKalle Valo 	}
472730db0ca8SKalle Valo 	return proc_stats_rid_open(inode, file, RID_STATSDELTA);
472830db0ca8SKalle Valo }
472930db0ca8SKalle Valo 
4730ba4d6513SLee Jones static int proc_stats_open(struct inode *inode, struct file *file)
4731ba4d6513SLee Jones {
473230db0ca8SKalle Valo 	return proc_stats_rid_open(inode, file, RID_STATS);
473330db0ca8SKalle Valo }
473430db0ca8SKalle Valo 
473530db0ca8SKalle Valo static int proc_stats_rid_open(struct inode *inode,
473630db0ca8SKalle Valo 				struct file *file,
473730db0ca8SKalle Valo 				u16 rid)
473830db0ca8SKalle Valo {
473930db0ca8SKalle Valo 	struct proc_data *data;
474030db0ca8SKalle Valo 	struct net_device *dev = PDE_DATA(inode);
474130db0ca8SKalle Valo 	struct airo_info *apriv = dev->ml_priv;
474230db0ca8SKalle Valo 	StatsRid stats;
474330db0ca8SKalle Valo 	int i, j;
474430db0ca8SKalle Valo 	__le32 *vals = stats.vals;
474530db0ca8SKalle Valo 	int len;
474630db0ca8SKalle Valo 
474730db0ca8SKalle Valo 	if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
474830db0ca8SKalle Valo 		return -ENOMEM;
474930db0ca8SKalle Valo 	data = file->private_data;
475030db0ca8SKalle Valo 	if ((data->rbuffer = kmalloc(4096, GFP_KERNEL)) == NULL) {
475130db0ca8SKalle Valo 		kfree (file->private_data);
475230db0ca8SKalle Valo 		return -ENOMEM;
475330db0ca8SKalle Valo 	}
475430db0ca8SKalle Valo 
475530db0ca8SKalle Valo 	readStatsRid(apriv, &stats, rid, 1);
475630db0ca8SKalle Valo 	len = le16_to_cpu(stats.len);
475730db0ca8SKalle Valo 
475830db0ca8SKalle Valo         j = 0;
475930db0ca8SKalle Valo 	for (i = 0; statsLabels[i]!=(char *)-1 && i*4<len; i++) {
476030db0ca8SKalle Valo 		if (!statsLabels[i]) continue;
476130db0ca8SKalle Valo 		if (j+strlen(statsLabels[i])+16>4096) {
476230db0ca8SKalle Valo 			airo_print_warn(apriv->dev->name,
476330db0ca8SKalle Valo 			       "Potentially disastrous buffer overflow averted!");
476430db0ca8SKalle Valo 			break;
476530db0ca8SKalle Valo 		}
476630db0ca8SKalle Valo 		j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i],
476730db0ca8SKalle Valo 				le32_to_cpu(vals[i]));
476830db0ca8SKalle Valo 	}
476930db0ca8SKalle Valo 	if (i*4 >= len) {
477030db0ca8SKalle Valo 		airo_print_warn(apriv->dev->name, "Got a short rid");
477130db0ca8SKalle Valo 	}
477230db0ca8SKalle Valo 	data->readlen = j;
477330db0ca8SKalle Valo 	return 0;
477430db0ca8SKalle Valo }
477530db0ca8SKalle Valo 
4776ba4d6513SLee Jones static int get_dec_u16(char *buffer, int *start, int limit)
4777ba4d6513SLee Jones {
477830db0ca8SKalle Valo 	u16 value;
477930db0ca8SKalle Valo 	int valid = 0;
478030db0ca8SKalle Valo 	for (value = 0; *start < limit && buffer[*start] >= '0' &&
478130db0ca8SKalle Valo 			buffer[*start] <= '9'; (*start)++) {
478230db0ca8SKalle Valo 		valid = 1;
478330db0ca8SKalle Valo 		value *= 10;
478430db0ca8SKalle Valo 		value += buffer[*start] - '0';
478530db0ca8SKalle Valo 	}
478630db0ca8SKalle Valo 	if (!valid) return -1;
478730db0ca8SKalle Valo 	return value;
478830db0ca8SKalle Valo }
478930db0ca8SKalle Valo 
479030db0ca8SKalle Valo static int airo_config_commit(struct net_device *dev,
479130db0ca8SKalle Valo 			      struct iw_request_info *info, void *zwrq,
479230db0ca8SKalle Valo 			      char *extra);
479330db0ca8SKalle Valo 
479430db0ca8SKalle Valo static inline int sniffing_mode(struct airo_info *ai)
479530db0ca8SKalle Valo {
479630db0ca8SKalle Valo 	return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >=
479730db0ca8SKalle Valo 		le16_to_cpu(RXMODE_RFMON);
479830db0ca8SKalle Valo }
479930db0ca8SKalle Valo 
480030db0ca8SKalle Valo static void proc_config_on_close(struct inode *inode, struct file *file)
480130db0ca8SKalle Valo {
480230db0ca8SKalle Valo 	struct proc_data *data = file->private_data;
480330db0ca8SKalle Valo 	struct net_device *dev = PDE_DATA(inode);
480430db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
480530db0ca8SKalle Valo 	char *line;
480630db0ca8SKalle Valo 
480730db0ca8SKalle Valo 	if (!data->writelen) return;
480830db0ca8SKalle Valo 
480930db0ca8SKalle Valo 	readConfigRid(ai, 1);
481030db0ca8SKalle Valo 	set_bit (FLAG_COMMIT, &ai->flags);
481130db0ca8SKalle Valo 
481230db0ca8SKalle Valo 	line = data->wbuffer;
481330db0ca8SKalle Valo 	while (line[0]) {
481430db0ca8SKalle Valo /*** Mode processing */
481530db0ca8SKalle Valo 		if (!strncmp(line, "Mode: ", 6)) {
481630db0ca8SKalle Valo 			line += 6;
481730db0ca8SKalle Valo 			if (sniffing_mode(ai))
481830db0ca8SKalle Valo 				set_bit (FLAG_RESET, &ai->flags);
481930db0ca8SKalle Valo 			ai->config.rmode &= ~RXMODE_FULL_MASK;
482030db0ca8SKalle Valo 			clear_bit (FLAG_802_11, &ai->flags);
482130db0ca8SKalle Valo 			ai->config.opmode &= ~MODE_CFG_MASK;
482230db0ca8SKalle Valo 			ai->config.scanMode = SCANMODE_ACTIVE;
482330db0ca8SKalle Valo 			if (line[0] == 'a') {
482430db0ca8SKalle Valo 				ai->config.opmode |= MODE_STA_IBSS;
482530db0ca8SKalle Valo 			} else {
482630db0ca8SKalle Valo 				ai->config.opmode |= MODE_STA_ESS;
482730db0ca8SKalle Valo 				if (line[0] == 'r') {
482830db0ca8SKalle Valo 					ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
482930db0ca8SKalle Valo 					ai->config.scanMode = SCANMODE_PASSIVE;
483030db0ca8SKalle Valo 					set_bit (FLAG_802_11, &ai->flags);
483130db0ca8SKalle Valo 				} else if (line[0] == 'y') {
483230db0ca8SKalle Valo 					ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER;
483330db0ca8SKalle Valo 					ai->config.scanMode = SCANMODE_PASSIVE;
483430db0ca8SKalle Valo 					set_bit (FLAG_802_11, &ai->flags);
483530db0ca8SKalle Valo 				} else if (line[0] == 'l')
483630db0ca8SKalle Valo 					ai->config.rmode |= RXMODE_LANMON;
483730db0ca8SKalle Valo 			}
483830db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
483930db0ca8SKalle Valo 		}
484030db0ca8SKalle Valo 
484130db0ca8SKalle Valo /*** Radio status */
484230db0ca8SKalle Valo 		else if (!strncmp(line,"Radio: ", 7)) {
484330db0ca8SKalle Valo 			line += 7;
484430db0ca8SKalle Valo 			if (!strncmp(line,"off", 3)) {
484530db0ca8SKalle Valo 				set_bit (FLAG_RADIO_OFF, &ai->flags);
484630db0ca8SKalle Valo 			} else {
484730db0ca8SKalle Valo 				clear_bit (FLAG_RADIO_OFF, &ai->flags);
484830db0ca8SKalle Valo 			}
484930db0ca8SKalle Valo 		}
485030db0ca8SKalle Valo /*** NodeName processing */
485130db0ca8SKalle Valo 		else if (!strncmp(line, "NodeName: ", 10)) {
485230db0ca8SKalle Valo 			int j;
485330db0ca8SKalle Valo 
485430db0ca8SKalle Valo 			line += 10;
485530db0ca8SKalle Valo 			memset(ai->config.nodeName, 0, 16);
485630db0ca8SKalle Valo /* Do the name, assume a space between the mode and node name */
485730db0ca8SKalle Valo 			for (j = 0; j < 16 && line[j] != '\n'; j++) {
485830db0ca8SKalle Valo 				ai->config.nodeName[j] = line[j];
485930db0ca8SKalle Valo 			}
486030db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
486130db0ca8SKalle Valo 		}
486230db0ca8SKalle Valo 
486330db0ca8SKalle Valo /*** PowerMode processing */
486430db0ca8SKalle Valo 		else if (!strncmp(line, "PowerMode: ", 11)) {
486530db0ca8SKalle Valo 			line += 11;
486630db0ca8SKalle Valo 			if (!strncmp(line, "PSPCAM", 6)) {
486730db0ca8SKalle Valo 				ai->config.powerSaveMode = POWERSAVE_PSPCAM;
486830db0ca8SKalle Valo 				set_bit (FLAG_COMMIT, &ai->flags);
486930db0ca8SKalle Valo 			} else if (!strncmp(line, "PSP", 3)) {
487030db0ca8SKalle Valo 				ai->config.powerSaveMode = POWERSAVE_PSP;
487130db0ca8SKalle Valo 				set_bit (FLAG_COMMIT, &ai->flags);
487230db0ca8SKalle Valo 			} else {
487330db0ca8SKalle Valo 				ai->config.powerSaveMode = POWERSAVE_CAM;
487430db0ca8SKalle Valo 				set_bit (FLAG_COMMIT, &ai->flags);
487530db0ca8SKalle Valo 			}
487630db0ca8SKalle Valo 		} else if (!strncmp(line, "DataRates: ", 11)) {
487730db0ca8SKalle Valo 			int v, i = 0, k = 0; /* i is index into line,
487830db0ca8SKalle Valo 						k is index to rates */
487930db0ca8SKalle Valo 
488030db0ca8SKalle Valo 			line += 11;
488130db0ca8SKalle Valo 			while ((v = get_dec_u16(line, &i, 3))!=-1) {
488230db0ca8SKalle Valo 				ai->config.rates[k++] = (u8)v;
488330db0ca8SKalle Valo 				line += i + 1;
488430db0ca8SKalle Valo 				i = 0;
488530db0ca8SKalle Valo 			}
488630db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
488730db0ca8SKalle Valo 		} else if (!strncmp(line, "Channel: ", 9)) {
488830db0ca8SKalle Valo 			int v, i = 0;
488930db0ca8SKalle Valo 			line += 9;
489030db0ca8SKalle Valo 			v = get_dec_u16(line, &i, i+3);
489130db0ca8SKalle Valo 			if (v != -1) {
489230db0ca8SKalle Valo 				ai->config.channelSet = cpu_to_le16(v);
489330db0ca8SKalle Valo 				set_bit (FLAG_COMMIT, &ai->flags);
489430db0ca8SKalle Valo 			}
489530db0ca8SKalle Valo 		} else if (!strncmp(line, "XmitPower: ", 11)) {
489630db0ca8SKalle Valo 			int v, i = 0;
489730db0ca8SKalle Valo 			line += 11;
489830db0ca8SKalle Valo 			v = get_dec_u16(line, &i, i+3);
489930db0ca8SKalle Valo 			if (v != -1) {
490030db0ca8SKalle Valo 				ai->config.txPower = cpu_to_le16(v);
490130db0ca8SKalle Valo 				set_bit (FLAG_COMMIT, &ai->flags);
490230db0ca8SKalle Valo 			}
490330db0ca8SKalle Valo 		} else if (!strncmp(line, "WEP: ", 5)) {
490430db0ca8SKalle Valo 			line += 5;
490530db0ca8SKalle Valo 			switch(line[0]) {
490630db0ca8SKalle Valo 			case 's':
490730db0ca8SKalle Valo 				set_auth_type(ai, AUTH_SHAREDKEY);
490830db0ca8SKalle Valo 				break;
490930db0ca8SKalle Valo 			case 'e':
491030db0ca8SKalle Valo 				set_auth_type(ai, AUTH_ENCRYPT);
491130db0ca8SKalle Valo 				break;
491230db0ca8SKalle Valo 			default:
491330db0ca8SKalle Valo 				set_auth_type(ai, AUTH_OPEN);
491430db0ca8SKalle Valo 				break;
491530db0ca8SKalle Valo 			}
491630db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
491730db0ca8SKalle Valo 		} else if (!strncmp(line, "LongRetryLimit: ", 16)) {
491830db0ca8SKalle Valo 			int v, i = 0;
491930db0ca8SKalle Valo 
492030db0ca8SKalle Valo 			line += 16;
492130db0ca8SKalle Valo 			v = get_dec_u16(line, &i, 3);
492230db0ca8SKalle Valo 			v = (v<0) ? 0 : ((v>255) ? 255 : v);
492330db0ca8SKalle Valo 			ai->config.longRetryLimit = cpu_to_le16(v);
492430db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
492530db0ca8SKalle Valo 		} else if (!strncmp(line, "ShortRetryLimit: ", 17)) {
492630db0ca8SKalle Valo 			int v, i = 0;
492730db0ca8SKalle Valo 
492830db0ca8SKalle Valo 			line += 17;
492930db0ca8SKalle Valo 			v = get_dec_u16(line, &i, 3);
493030db0ca8SKalle Valo 			v = (v<0) ? 0 : ((v>255) ? 255 : v);
493130db0ca8SKalle Valo 			ai->config.shortRetryLimit = cpu_to_le16(v);
493230db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
493330db0ca8SKalle Valo 		} else if (!strncmp(line, "RTSThreshold: ", 14)) {
493430db0ca8SKalle Valo 			int v, i = 0;
493530db0ca8SKalle Valo 
493630db0ca8SKalle Valo 			line += 14;
493730db0ca8SKalle Valo 			v = get_dec_u16(line, &i, 4);
493830db0ca8SKalle Valo 			v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
493930db0ca8SKalle Valo 			ai->config.rtsThres = cpu_to_le16(v);
494030db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
494130db0ca8SKalle Valo 		} else if (!strncmp(line, "TXMSDULifetime: ", 16)) {
494230db0ca8SKalle Valo 			int v, i = 0;
494330db0ca8SKalle Valo 
494430db0ca8SKalle Valo 			line += 16;
494530db0ca8SKalle Valo 			v = get_dec_u16(line, &i, 5);
494630db0ca8SKalle Valo 			v = (v<0) ? 0 : v;
494730db0ca8SKalle Valo 			ai->config.txLifetime = cpu_to_le16(v);
494830db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
494930db0ca8SKalle Valo 		} else if (!strncmp(line, "RXMSDULifetime: ", 16)) {
495030db0ca8SKalle Valo 			int v, i = 0;
495130db0ca8SKalle Valo 
495230db0ca8SKalle Valo 			line += 16;
495330db0ca8SKalle Valo 			v = get_dec_u16(line, &i, 5);
495430db0ca8SKalle Valo 			v = (v<0) ? 0 : v;
495530db0ca8SKalle Valo 			ai->config.rxLifetime = cpu_to_le16(v);
495630db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
495730db0ca8SKalle Valo 		} else if (!strncmp(line, "TXDiversity: ", 13)) {
495830db0ca8SKalle Valo 			ai->config.txDiversity =
495930db0ca8SKalle Valo 				(line[13]=='l') ? 1 :
496030db0ca8SKalle Valo 				((line[13]=='r')? 2: 3);
496130db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
496230db0ca8SKalle Valo 		} else if (!strncmp(line, "RXDiversity: ", 13)) {
496330db0ca8SKalle Valo 			ai->config.rxDiversity =
496430db0ca8SKalle Valo 				(line[13]=='l') ? 1 :
496530db0ca8SKalle Valo 				((line[13]=='r')? 2: 3);
496630db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
496730db0ca8SKalle Valo 		} else if (!strncmp(line, "FragThreshold: ", 15)) {
496830db0ca8SKalle Valo 			int v, i = 0;
496930db0ca8SKalle Valo 
497030db0ca8SKalle Valo 			line += 15;
497130db0ca8SKalle Valo 			v = get_dec_u16(line, &i, 4);
497230db0ca8SKalle Valo 			v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
497330db0ca8SKalle Valo 			v = v & 0xfffe; /* Make sure its even */
497430db0ca8SKalle Valo 			ai->config.fragThresh = cpu_to_le16(v);
497530db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &ai->flags);
497630db0ca8SKalle Valo 		} else if (!strncmp(line, "Modulation: ", 12)) {
497730db0ca8SKalle Valo 			line += 12;
497830db0ca8SKalle Valo 			switch(*line) {
497930db0ca8SKalle Valo 			case 'd':  ai->config.modulation = MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break;
498030db0ca8SKalle Valo 			case 'c':  ai->config.modulation = MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break;
498130db0ca8SKalle Valo 			case 'm':  ai->config.modulation = MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break;
498230db0ca8SKalle Valo 			default: airo_print_warn(ai->dev->name, "Unknown modulation");
498330db0ca8SKalle Valo 			}
498430db0ca8SKalle Valo 		} else if (!strncmp(line, "Preamble: ", 10)) {
498530db0ca8SKalle Valo 			line += 10;
498630db0ca8SKalle Valo 			switch(*line) {
498730db0ca8SKalle Valo 			case 'a': ai->config.preamble = PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break;
498830db0ca8SKalle Valo 			case 'l': ai->config.preamble = PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break;
498930db0ca8SKalle Valo 			case 's': ai->config.preamble = PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break;
499030db0ca8SKalle Valo 			default: airo_print_warn(ai->dev->name, "Unknown preamble");
499130db0ca8SKalle Valo 			}
499230db0ca8SKalle Valo 		} else {
499330db0ca8SKalle Valo 			airo_print_warn(ai->dev->name, "Couldn't figure out %s", line);
499430db0ca8SKalle Valo 		}
499530db0ca8SKalle Valo 		while (line[0] && line[0] != '\n') line++;
499630db0ca8SKalle Valo 		if (line[0]) line++;
499730db0ca8SKalle Valo 	}
499830db0ca8SKalle Valo 	airo_config_commit(dev, NULL, NULL, NULL);
499930db0ca8SKalle Valo }
500030db0ca8SKalle Valo 
500130db0ca8SKalle Valo static const char *get_rmode(__le16 mode)
500230db0ca8SKalle Valo {
500330db0ca8SKalle Valo         switch(mode & RXMODE_MASK) {
500430db0ca8SKalle Valo         case RXMODE_RFMON:  return "rfmon";
500530db0ca8SKalle Valo         case RXMODE_RFMON_ANYBSS:  return "yna (any) bss rfmon";
500630db0ca8SKalle Valo         case RXMODE_LANMON:  return "lanmon";
500730db0ca8SKalle Valo         }
500830db0ca8SKalle Valo         return "ESS";
500930db0ca8SKalle Valo }
501030db0ca8SKalle Valo 
501130db0ca8SKalle Valo static int proc_config_open(struct inode *inode, struct file *file)
501230db0ca8SKalle Valo {
501330db0ca8SKalle Valo 	struct proc_data *data;
501430db0ca8SKalle Valo 	struct net_device *dev = PDE_DATA(inode);
501530db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
501630db0ca8SKalle Valo 	int i;
501730db0ca8SKalle Valo 	__le16 mode;
501830db0ca8SKalle Valo 
501930db0ca8SKalle Valo 	if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
502030db0ca8SKalle Valo 		return -ENOMEM;
502130db0ca8SKalle Valo 	data = file->private_data;
502230db0ca8SKalle Valo 	if ((data->rbuffer = kmalloc(2048, GFP_KERNEL)) == NULL) {
502330db0ca8SKalle Valo 		kfree (file->private_data);
502430db0ca8SKalle Valo 		return -ENOMEM;
502530db0ca8SKalle Valo 	}
502630db0ca8SKalle Valo 	if ((data->wbuffer = kzalloc(2048, GFP_KERNEL)) == NULL) {
502730db0ca8SKalle Valo 		kfree (data->rbuffer);
502830db0ca8SKalle Valo 		kfree (file->private_data);
502930db0ca8SKalle Valo 		return -ENOMEM;
503030db0ca8SKalle Valo 	}
503130db0ca8SKalle Valo 	data->maxwritelen = 2048;
503230db0ca8SKalle Valo 	data->on_close = proc_config_on_close;
503330db0ca8SKalle Valo 
503430db0ca8SKalle Valo 	readConfigRid(ai, 1);
503530db0ca8SKalle Valo 
503630db0ca8SKalle Valo 	mode = ai->config.opmode & MODE_CFG_MASK;
503730db0ca8SKalle Valo 	i = sprintf(data->rbuffer,
503830db0ca8SKalle Valo 		     "Mode: %s\n"
503930db0ca8SKalle Valo 		     "Radio: %s\n"
504030db0ca8SKalle Valo 		     "NodeName: %-16s\n"
504130db0ca8SKalle Valo 		     "PowerMode: %s\n"
504230db0ca8SKalle Valo 		     "DataRates: %d %d %d %d %d %d %d %d\n"
504330db0ca8SKalle Valo 		     "Channel: %d\n"
504430db0ca8SKalle Valo 		     "XmitPower: %d\n",
504530db0ca8SKalle Valo 		     mode == MODE_STA_IBSS ? "adhoc" :
504630db0ca8SKalle Valo 		     mode == MODE_STA_ESS ? get_rmode(ai->config.rmode):
504730db0ca8SKalle Valo 		     mode == MODE_AP ? "AP" :
504830db0ca8SKalle Valo 		     mode == MODE_AP_RPTR ? "AP RPTR" : "Error",
504930db0ca8SKalle Valo 		     test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on",
505030db0ca8SKalle Valo 		     ai->config.nodeName,
505130db0ca8SKalle Valo 		     ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" :
505230db0ca8SKalle Valo 		     ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" :
505330db0ca8SKalle Valo 		     ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" :
505430db0ca8SKalle Valo 		     "Error",
505530db0ca8SKalle Valo 		     (int)ai->config.rates[0],
505630db0ca8SKalle Valo 		     (int)ai->config.rates[1],
505730db0ca8SKalle Valo 		     (int)ai->config.rates[2],
505830db0ca8SKalle Valo 		     (int)ai->config.rates[3],
505930db0ca8SKalle Valo 		     (int)ai->config.rates[4],
506030db0ca8SKalle Valo 		     (int)ai->config.rates[5],
506130db0ca8SKalle Valo 		     (int)ai->config.rates[6],
506230db0ca8SKalle Valo 		     (int)ai->config.rates[7],
506330db0ca8SKalle Valo 		     le16_to_cpu(ai->config.channelSet),
506430db0ca8SKalle Valo 		     le16_to_cpu(ai->config.txPower)
506530db0ca8SKalle Valo 		);
506630db0ca8SKalle Valo 	sprintf(data->rbuffer + i,
506730db0ca8SKalle Valo 		 "LongRetryLimit: %d\n"
506830db0ca8SKalle Valo 		 "ShortRetryLimit: %d\n"
506930db0ca8SKalle Valo 		 "RTSThreshold: %d\n"
507030db0ca8SKalle Valo 		 "TXMSDULifetime: %d\n"
507130db0ca8SKalle Valo 		 "RXMSDULifetime: %d\n"
507230db0ca8SKalle Valo 		 "TXDiversity: %s\n"
507330db0ca8SKalle Valo 		 "RXDiversity: %s\n"
507430db0ca8SKalle Valo 		 "FragThreshold: %d\n"
507530db0ca8SKalle Valo 		 "WEP: %s\n"
507630db0ca8SKalle Valo 		 "Modulation: %s\n"
507730db0ca8SKalle Valo 		 "Preamble: %s\n",
507830db0ca8SKalle Valo 		 le16_to_cpu(ai->config.longRetryLimit),
507930db0ca8SKalle Valo 		 le16_to_cpu(ai->config.shortRetryLimit),
508030db0ca8SKalle Valo 		 le16_to_cpu(ai->config.rtsThres),
508130db0ca8SKalle Valo 		 le16_to_cpu(ai->config.txLifetime),
508230db0ca8SKalle Valo 		 le16_to_cpu(ai->config.rxLifetime),
508330db0ca8SKalle Valo 		 ai->config.txDiversity == 1 ? "left" :
508430db0ca8SKalle Valo 		 ai->config.txDiversity == 2 ? "right" : "both",
508530db0ca8SKalle Valo 		 ai->config.rxDiversity == 1 ? "left" :
508630db0ca8SKalle Valo 		 ai->config.rxDiversity == 2 ? "right" : "both",
508730db0ca8SKalle Valo 		 le16_to_cpu(ai->config.fragThresh),
508830db0ca8SKalle Valo 		 ai->config.authType == AUTH_ENCRYPT ? "encrypt" :
508930db0ca8SKalle Valo 		 ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open",
509030db0ca8SKalle Valo 		 ai->config.modulation == MOD_DEFAULT ? "default" :
509130db0ca8SKalle Valo 		 ai->config.modulation == MOD_CCK ? "cck" :
509230db0ca8SKalle Valo 		 ai->config.modulation == MOD_MOK ? "mok" : "error",
509330db0ca8SKalle Valo 		 ai->config.preamble == PREAMBLE_AUTO ? "auto" :
509430db0ca8SKalle Valo 		 ai->config.preamble == PREAMBLE_LONG ? "long" :
509530db0ca8SKalle Valo 		 ai->config.preamble == PREAMBLE_SHORT ? "short" : "error"
509630db0ca8SKalle Valo 		);
509730db0ca8SKalle Valo 	data->readlen = strlen(data->rbuffer);
509830db0ca8SKalle Valo 	return 0;
509930db0ca8SKalle Valo }
510030db0ca8SKalle Valo 
510130db0ca8SKalle Valo static void proc_SSID_on_close(struct inode *inode, struct file *file)
510230db0ca8SKalle Valo {
510330db0ca8SKalle Valo 	struct proc_data *data = file->private_data;
510430db0ca8SKalle Valo 	struct net_device *dev = PDE_DATA(inode);
510530db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
510630db0ca8SKalle Valo 	SsidRid SSID_rid;
510730db0ca8SKalle Valo 	int i;
510830db0ca8SKalle Valo 	char *p = data->wbuffer;
510930db0ca8SKalle Valo 	char *end = p + data->writelen;
511030db0ca8SKalle Valo 
511130db0ca8SKalle Valo 	if (!data->writelen)
511230db0ca8SKalle Valo 		return;
511330db0ca8SKalle Valo 
511430db0ca8SKalle Valo 	*end = '\n'; /* sentinel; we have space for it */
511530db0ca8SKalle Valo 
511630db0ca8SKalle Valo 	memset(&SSID_rid, 0, sizeof(SSID_rid));
511730db0ca8SKalle Valo 
511830db0ca8SKalle Valo 	for (i = 0; i < 3 && p < end; i++) {
511930db0ca8SKalle Valo 		int j = 0;
512030db0ca8SKalle Valo 		/* copy up to 32 characters from this line */
512130db0ca8SKalle Valo 		while (*p != '\n' && j < 32)
512230db0ca8SKalle Valo 			SSID_rid.ssids[i].ssid[j++] = *p++;
512330db0ca8SKalle Valo 		if (j == 0)
512430db0ca8SKalle Valo 			break;
512530db0ca8SKalle Valo 		SSID_rid.ssids[i].len = cpu_to_le16(j);
512630db0ca8SKalle Valo 		/* skip to the beginning of the next line */
512730db0ca8SKalle Valo 		while (*p++ != '\n')
512830db0ca8SKalle Valo 			;
512930db0ca8SKalle Valo 	}
513030db0ca8SKalle Valo 	if (i)
513130db0ca8SKalle Valo 		SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
513230db0ca8SKalle Valo 	disable_MAC(ai, 1);
513330db0ca8SKalle Valo 	writeSsidRid(ai, &SSID_rid, 1);
513430db0ca8SKalle Valo 	enable_MAC(ai, 1);
513530db0ca8SKalle Valo }
513630db0ca8SKalle Valo 
5137ba4d6513SLee Jones static void proc_APList_on_close(struct inode *inode, struct file *file)
5138ba4d6513SLee Jones {
513930db0ca8SKalle Valo 	struct proc_data *data = file->private_data;
514030db0ca8SKalle Valo 	struct net_device *dev = PDE_DATA(inode);
514130db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
514230db0ca8SKalle Valo 	APListRid *APList_rid = &ai->APList;
514330db0ca8SKalle Valo 	int i;
514430db0ca8SKalle Valo 
514530db0ca8SKalle Valo 	if (!data->writelen) return;
514630db0ca8SKalle Valo 
514730db0ca8SKalle Valo 	memset(APList_rid, 0, sizeof(*APList_rid));
514830db0ca8SKalle Valo 	APList_rid->len = cpu_to_le16(sizeof(*APList_rid));
514930db0ca8SKalle Valo 
5150b31fa550SAndy Shevchenko 	for (i = 0; i < 4 && data->writelen >= (i + 1) * 6 * 3; i++)
5151b31fa550SAndy Shevchenko 		mac_pton(data->wbuffer + i * 6 * 3, APList_rid->ap[i]);
5152b31fa550SAndy Shevchenko 
515330db0ca8SKalle Valo 	disable_MAC(ai, 1);
515430db0ca8SKalle Valo 	writeAPListRid(ai, APList_rid, 1);
515530db0ca8SKalle Valo 	enable_MAC(ai, 1);
515630db0ca8SKalle Valo }
515730db0ca8SKalle Valo 
515830db0ca8SKalle Valo /* This function wraps PC4500_writerid with a MAC disable */
515930db0ca8SKalle Valo static int do_writerid(struct airo_info *ai, u16 rid, const void *rid_data,
5160ba4d6513SLee Jones 			int len, int dummy)
5161ba4d6513SLee Jones {
516230db0ca8SKalle Valo 	int rc;
516330db0ca8SKalle Valo 
516430db0ca8SKalle Valo 	disable_MAC(ai, 1);
516530db0ca8SKalle Valo 	rc = PC4500_writerid(ai, rid, rid_data, len, 1);
516630db0ca8SKalle Valo 	enable_MAC(ai, 1);
516730db0ca8SKalle Valo 	return rc;
516830db0ca8SKalle Valo }
516930db0ca8SKalle Valo 
517030db0ca8SKalle Valo /* Returns the WEP key at the specified index, or -1 if that key does
517130db0ca8SKalle Valo  * not exist.  The buffer is assumed to be at least 16 bytes in length.
517230db0ca8SKalle Valo  */
517330db0ca8SKalle Valo static int get_wep_key(struct airo_info *ai, u16 index, char *buf, u16 buflen)
517430db0ca8SKalle Valo {
517530db0ca8SKalle Valo 	WepKeyRid wkr;
517630db0ca8SKalle Valo 	int rc;
517730db0ca8SKalle Valo 	__le16 lastindex;
517830db0ca8SKalle Valo 
517930db0ca8SKalle Valo 	rc = readWepKeyRid(ai, &wkr, 1, 1);
518030db0ca8SKalle Valo 	if (rc != SUCCESS)
518130db0ca8SKalle Valo 		return -1;
518230db0ca8SKalle Valo 	do {
518330db0ca8SKalle Valo 		lastindex = wkr.kindex;
518430db0ca8SKalle Valo 		if (le16_to_cpu(wkr.kindex) == index) {
518530db0ca8SKalle Valo 			int klen = min_t(int, buflen, le16_to_cpu(wkr.klen));
518630db0ca8SKalle Valo 			memcpy(buf, wkr.key, klen);
518730db0ca8SKalle Valo 			return klen;
518830db0ca8SKalle Valo 		}
518930db0ca8SKalle Valo 		rc = readWepKeyRid(ai, &wkr, 0, 1);
519030db0ca8SKalle Valo 		if (rc != SUCCESS)
519130db0ca8SKalle Valo 			return -1;
519230db0ca8SKalle Valo 	} while (lastindex != wkr.kindex);
519330db0ca8SKalle Valo 	return -1;
519430db0ca8SKalle Valo }
519530db0ca8SKalle Valo 
519630db0ca8SKalle Valo static int get_wep_tx_idx(struct airo_info *ai)
519730db0ca8SKalle Valo {
519830db0ca8SKalle Valo 	WepKeyRid wkr;
519930db0ca8SKalle Valo 	int rc;
520030db0ca8SKalle Valo 	__le16 lastindex;
520130db0ca8SKalle Valo 
520230db0ca8SKalle Valo 	rc = readWepKeyRid(ai, &wkr, 1, 1);
520330db0ca8SKalle Valo 	if (rc != SUCCESS)
520430db0ca8SKalle Valo 		return -1;
520530db0ca8SKalle Valo 	do {
520630db0ca8SKalle Valo 		lastindex = wkr.kindex;
520730db0ca8SKalle Valo 		if (wkr.kindex == cpu_to_le16(0xffff))
520830db0ca8SKalle Valo 			return wkr.mac[0];
520930db0ca8SKalle Valo 		rc = readWepKeyRid(ai, &wkr, 0, 1);
521030db0ca8SKalle Valo 		if (rc != SUCCESS)
521130db0ca8SKalle Valo 			return -1;
521230db0ca8SKalle Valo 	} while (lastindex != wkr.kindex);
521330db0ca8SKalle Valo 	return -1;
521430db0ca8SKalle Valo }
521530db0ca8SKalle Valo 
521630db0ca8SKalle Valo static int set_wep_key(struct airo_info *ai, u16 index, const char *key,
521730db0ca8SKalle Valo 		       u16 keylen, int perm, int lock)
521830db0ca8SKalle Valo {
521930db0ca8SKalle Valo 	static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
522030db0ca8SKalle Valo 	WepKeyRid wkr;
522130db0ca8SKalle Valo 	int rc;
522230db0ca8SKalle Valo 
522330db0ca8SKalle Valo 	if (WARN_ON(keylen == 0))
522430db0ca8SKalle Valo 		return -1;
522530db0ca8SKalle Valo 
522630db0ca8SKalle Valo 	memset(&wkr, 0, sizeof(wkr));
522730db0ca8SKalle Valo 	wkr.len = cpu_to_le16(sizeof(wkr));
522830db0ca8SKalle Valo 	wkr.kindex = cpu_to_le16(index);
522930db0ca8SKalle Valo 	wkr.klen = cpu_to_le16(keylen);
523030db0ca8SKalle Valo 	memcpy(wkr.key, key, keylen);
523130db0ca8SKalle Valo 	memcpy(wkr.mac, macaddr, ETH_ALEN);
523230db0ca8SKalle Valo 
523330db0ca8SKalle Valo 	if (perm) disable_MAC(ai, lock);
523430db0ca8SKalle Valo 	rc = writeWepKeyRid(ai, &wkr, perm, lock);
523530db0ca8SKalle Valo 	if (perm) enable_MAC(ai, lock);
523630db0ca8SKalle Valo 	return rc;
523730db0ca8SKalle Valo }
523830db0ca8SKalle Valo 
523930db0ca8SKalle Valo static int set_wep_tx_idx(struct airo_info *ai, u16 index, int perm, int lock)
524030db0ca8SKalle Valo {
524130db0ca8SKalle Valo 	WepKeyRid wkr;
524230db0ca8SKalle Valo 	int rc;
524330db0ca8SKalle Valo 
524430db0ca8SKalle Valo 	memset(&wkr, 0, sizeof(wkr));
524530db0ca8SKalle Valo 	wkr.len = cpu_to_le16(sizeof(wkr));
524630db0ca8SKalle Valo 	wkr.kindex = cpu_to_le16(0xffff);
524730db0ca8SKalle Valo 	wkr.mac[0] = (char)index;
524830db0ca8SKalle Valo 
524930db0ca8SKalle Valo 	if (perm) {
525030db0ca8SKalle Valo 		ai->defindex = (char)index;
525130db0ca8SKalle Valo 		disable_MAC(ai, lock);
525230db0ca8SKalle Valo 	}
525330db0ca8SKalle Valo 
525430db0ca8SKalle Valo 	rc = writeWepKeyRid(ai, &wkr, perm, lock);
525530db0ca8SKalle Valo 
525630db0ca8SKalle Valo 	if (perm)
525730db0ca8SKalle Valo 		enable_MAC(ai, lock);
525830db0ca8SKalle Valo 	return rc;
525930db0ca8SKalle Valo }
526030db0ca8SKalle Valo 
5261ba4d6513SLee Jones static void proc_wepkey_on_close(struct inode *inode, struct file *file)
5262ba4d6513SLee Jones {
526330db0ca8SKalle Valo 	struct proc_data *data;
526430db0ca8SKalle Valo 	struct net_device *dev = PDE_DATA(inode);
526530db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
526630db0ca8SKalle Valo 	int i, rc;
526730db0ca8SKalle Valo 	char key[16];
526830db0ca8SKalle Valo 	u16 index = 0;
526930db0ca8SKalle Valo 	int j = 0;
527030db0ca8SKalle Valo 
527130db0ca8SKalle Valo 	memset(key, 0, sizeof(key));
527230db0ca8SKalle Valo 
527330db0ca8SKalle Valo 	data = file->private_data;
527430db0ca8SKalle Valo 	if (!data->writelen) return;
527530db0ca8SKalle Valo 
527630db0ca8SKalle Valo 	if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' &&
527730db0ca8SKalle Valo 	    (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) {
527830db0ca8SKalle Valo 		index = data->wbuffer[0] - '0';
527930db0ca8SKalle Valo 		if (data->wbuffer[1] == '\n') {
528030db0ca8SKalle Valo 			rc = set_wep_tx_idx(ai, index, 1, 1);
528130db0ca8SKalle Valo 			if (rc < 0) {
528230db0ca8SKalle Valo 				airo_print_err(ai->dev->name, "failed to set "
528330db0ca8SKalle Valo 				               "WEP transmit index to %d: %d.",
528430db0ca8SKalle Valo 				               index, rc);
528530db0ca8SKalle Valo 			}
528630db0ca8SKalle Valo 			return;
528730db0ca8SKalle Valo 		}
528830db0ca8SKalle Valo 		j = 2;
528930db0ca8SKalle Valo 	} else {
529030db0ca8SKalle Valo 		airo_print_err(ai->dev->name, "WepKey passed invalid key index");
529130db0ca8SKalle Valo 		return;
529230db0ca8SKalle Valo 	}
529330db0ca8SKalle Valo 
529430db0ca8SKalle Valo 	for (i = 0; i < 16*3 && data->wbuffer[i+j]; i++) {
529530db0ca8SKalle Valo 		switch(i%3) {
529630db0ca8SKalle Valo 		case 0:
529730db0ca8SKalle Valo 			key[i/3] = hex_to_bin(data->wbuffer[i+j])<<4;
529830db0ca8SKalle Valo 			break;
529930db0ca8SKalle Valo 		case 1:
530030db0ca8SKalle Valo 			key[i/3] |= hex_to_bin(data->wbuffer[i+j]);
530130db0ca8SKalle Valo 			break;
530230db0ca8SKalle Valo 		}
530330db0ca8SKalle Valo 	}
530430db0ca8SKalle Valo 
530530db0ca8SKalle Valo 	rc = set_wep_key(ai, index, key, i/3, 1, 1);
530630db0ca8SKalle Valo 	if (rc < 0) {
530730db0ca8SKalle Valo 		airo_print_err(ai->dev->name, "failed to set WEP key at index "
530830db0ca8SKalle Valo 		               "%d: %d.", index, rc);
530930db0ca8SKalle Valo 	}
531030db0ca8SKalle Valo }
531130db0ca8SKalle Valo 
531230db0ca8SKalle Valo static int proc_wepkey_open(struct inode *inode, struct file *file)
531330db0ca8SKalle Valo {
531430db0ca8SKalle Valo 	struct proc_data *data;
531530db0ca8SKalle Valo 	struct net_device *dev = PDE_DATA(inode);
531630db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
531730db0ca8SKalle Valo 	char *ptr;
531830db0ca8SKalle Valo 	WepKeyRid wkr;
531930db0ca8SKalle Valo 	__le16 lastindex;
532030db0ca8SKalle Valo 	int j = 0;
532130db0ca8SKalle Valo 	int rc;
532230db0ca8SKalle Valo 
532330db0ca8SKalle Valo 	if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
532430db0ca8SKalle Valo 		return -ENOMEM;
532530db0ca8SKalle Valo 	memset(&wkr, 0, sizeof(wkr));
532630db0ca8SKalle Valo 	data = file->private_data;
532730db0ca8SKalle Valo 	if ((data->rbuffer = kzalloc(180, GFP_KERNEL)) == NULL) {
532830db0ca8SKalle Valo 		kfree (file->private_data);
532930db0ca8SKalle Valo 		return -ENOMEM;
533030db0ca8SKalle Valo 	}
533130db0ca8SKalle Valo 	data->writelen = 0;
533230db0ca8SKalle Valo 	data->maxwritelen = 80;
533330db0ca8SKalle Valo 	if ((data->wbuffer = kzalloc(80, GFP_KERNEL)) == NULL) {
533430db0ca8SKalle Valo 		kfree (data->rbuffer);
533530db0ca8SKalle Valo 		kfree (file->private_data);
533630db0ca8SKalle Valo 		return -ENOMEM;
533730db0ca8SKalle Valo 	}
533830db0ca8SKalle Valo 	data->on_close = proc_wepkey_on_close;
533930db0ca8SKalle Valo 
534030db0ca8SKalle Valo 	ptr = data->rbuffer;
534130db0ca8SKalle Valo 	strcpy(ptr, "No wep keys\n");
534230db0ca8SKalle Valo 	rc = readWepKeyRid(ai, &wkr, 1, 1);
534330db0ca8SKalle Valo 	if (rc == SUCCESS) do {
534430db0ca8SKalle Valo 		lastindex = wkr.kindex;
534530db0ca8SKalle Valo 		if (wkr.kindex == cpu_to_le16(0xffff)) {
534630db0ca8SKalle Valo 			j += sprintf(ptr+j, "Tx key = %d\n",
534730db0ca8SKalle Valo 				     (int)wkr.mac[0]);
534830db0ca8SKalle Valo 		} else {
534930db0ca8SKalle Valo 			j += sprintf(ptr+j, "Key %d set with length = %d\n",
535030db0ca8SKalle Valo 				     le16_to_cpu(wkr.kindex),
535130db0ca8SKalle Valo 				     le16_to_cpu(wkr.klen));
535230db0ca8SKalle Valo 		}
535330db0ca8SKalle Valo 		readWepKeyRid(ai, &wkr, 0, 1);
535430db0ca8SKalle Valo 	} while ((lastindex != wkr.kindex) && (j < 180-30));
535530db0ca8SKalle Valo 
535630db0ca8SKalle Valo 	data->readlen = strlen(data->rbuffer);
535730db0ca8SKalle Valo 	return 0;
535830db0ca8SKalle Valo }
535930db0ca8SKalle Valo 
536030db0ca8SKalle Valo static int proc_SSID_open(struct inode *inode, struct file *file)
536130db0ca8SKalle Valo {
536230db0ca8SKalle Valo 	struct proc_data *data;
536330db0ca8SKalle Valo 	struct net_device *dev = PDE_DATA(inode);
536430db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
536530db0ca8SKalle Valo 	int i;
536630db0ca8SKalle Valo 	char *ptr;
536730db0ca8SKalle Valo 	SsidRid SSID_rid;
536830db0ca8SKalle Valo 
536930db0ca8SKalle Valo 	if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
537030db0ca8SKalle Valo 		return -ENOMEM;
537130db0ca8SKalle Valo 	data = file->private_data;
537230db0ca8SKalle Valo 	if ((data->rbuffer = kmalloc(104, GFP_KERNEL)) == NULL) {
537330db0ca8SKalle Valo 		kfree (file->private_data);
537430db0ca8SKalle Valo 		return -ENOMEM;
537530db0ca8SKalle Valo 	}
537630db0ca8SKalle Valo 	data->writelen = 0;
537730db0ca8SKalle Valo 	data->maxwritelen = 33*3;
537830db0ca8SKalle Valo 	/* allocate maxwritelen + 1; we'll want a sentinel */
537930db0ca8SKalle Valo 	if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) {
538030db0ca8SKalle Valo 		kfree (data->rbuffer);
538130db0ca8SKalle Valo 		kfree (file->private_data);
538230db0ca8SKalle Valo 		return -ENOMEM;
538330db0ca8SKalle Valo 	}
538430db0ca8SKalle Valo 	data->on_close = proc_SSID_on_close;
538530db0ca8SKalle Valo 
538630db0ca8SKalle Valo 	readSsidRid(ai, &SSID_rid);
538730db0ca8SKalle Valo 	ptr = data->rbuffer;
538830db0ca8SKalle Valo 	for (i = 0; i < 3; i++) {
538930db0ca8SKalle Valo 		int j;
539030db0ca8SKalle Valo 		size_t len = le16_to_cpu(SSID_rid.ssids[i].len);
539130db0ca8SKalle Valo 		if (!len)
539230db0ca8SKalle Valo 			break;
539330db0ca8SKalle Valo 		if (len > 32)
539430db0ca8SKalle Valo 			len = 32;
539530db0ca8SKalle Valo 		for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++)
539630db0ca8SKalle Valo 			*ptr++ = SSID_rid.ssids[i].ssid[j];
539730db0ca8SKalle Valo 		*ptr++ = '\n';
539830db0ca8SKalle Valo 	}
539930db0ca8SKalle Valo 	*ptr = '\0';
540030db0ca8SKalle Valo 	data->readlen = strlen(data->rbuffer);
540130db0ca8SKalle Valo 	return 0;
540230db0ca8SKalle Valo }
540330db0ca8SKalle Valo 
5404ba4d6513SLee Jones static int proc_APList_open(struct inode *inode, struct file *file)
5405ba4d6513SLee Jones {
540630db0ca8SKalle Valo 	struct proc_data *data;
540730db0ca8SKalle Valo 	struct net_device *dev = PDE_DATA(inode);
540830db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
540930db0ca8SKalle Valo 	int i;
541030db0ca8SKalle Valo 	char *ptr;
541130db0ca8SKalle Valo 	APListRid *APList_rid = &ai->APList;
541230db0ca8SKalle Valo 
541330db0ca8SKalle Valo 	if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
541430db0ca8SKalle Valo 		return -ENOMEM;
541530db0ca8SKalle Valo 	data = file->private_data;
541630db0ca8SKalle Valo 	if ((data->rbuffer = kmalloc(104, GFP_KERNEL)) == NULL) {
541730db0ca8SKalle Valo 		kfree (file->private_data);
541830db0ca8SKalle Valo 		return -ENOMEM;
541930db0ca8SKalle Valo 	}
542030db0ca8SKalle Valo 	data->writelen = 0;
542130db0ca8SKalle Valo 	data->maxwritelen = 4*6*3;
542230db0ca8SKalle Valo 	if ((data->wbuffer = kzalloc(data->maxwritelen, GFP_KERNEL)) == NULL) {
542330db0ca8SKalle Valo 		kfree (data->rbuffer);
542430db0ca8SKalle Valo 		kfree (file->private_data);
542530db0ca8SKalle Valo 		return -ENOMEM;
542630db0ca8SKalle Valo 	}
542730db0ca8SKalle Valo 	data->on_close = proc_APList_on_close;
542830db0ca8SKalle Valo 
542930db0ca8SKalle Valo 	ptr = data->rbuffer;
543030db0ca8SKalle Valo 	for (i = 0; i < 4; i++) {
543130db0ca8SKalle Valo // We end when we find a zero MAC
543230db0ca8SKalle Valo 		if (!*(int*)APList_rid->ap[i] &&
543330db0ca8SKalle Valo 		     !*(int*)&APList_rid->ap[i][2]) break;
543430db0ca8SKalle Valo 		ptr += sprintf(ptr, "%pM\n", APList_rid->ap[i]);
543530db0ca8SKalle Valo 	}
543630db0ca8SKalle Valo 	if (i==0) ptr += sprintf(ptr, "Not using specific APs\n");
543730db0ca8SKalle Valo 
543830db0ca8SKalle Valo 	*ptr = '\0';
543930db0ca8SKalle Valo 	data->readlen = strlen(data->rbuffer);
544030db0ca8SKalle Valo 	return 0;
544130db0ca8SKalle Valo }
544230db0ca8SKalle Valo 
5443ba4d6513SLee Jones static int proc_BSSList_open(struct inode *inode, struct file *file)
5444ba4d6513SLee Jones {
544530db0ca8SKalle Valo 	struct proc_data *data;
544630db0ca8SKalle Valo 	struct net_device *dev = PDE_DATA(inode);
544730db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
544830db0ca8SKalle Valo 	char *ptr;
544930db0ca8SKalle Valo 	BSSListRid BSSList_rid;
545030db0ca8SKalle Valo 	int rc;
545130db0ca8SKalle Valo 	/* If doLoseSync is not 1, we won't do a Lose Sync */
545230db0ca8SKalle Valo 	int doLoseSync = -1;
545330db0ca8SKalle Valo 
545430db0ca8SKalle Valo 	if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL)
545530db0ca8SKalle Valo 		return -ENOMEM;
545630db0ca8SKalle Valo 	data = file->private_data;
545730db0ca8SKalle Valo 	if ((data->rbuffer = kmalloc(1024, GFP_KERNEL)) == NULL) {
545830db0ca8SKalle Valo 		kfree (file->private_data);
545930db0ca8SKalle Valo 		return -ENOMEM;
546030db0ca8SKalle Valo 	}
546130db0ca8SKalle Valo 	data->writelen = 0;
546230db0ca8SKalle Valo 	data->maxwritelen = 0;
546330db0ca8SKalle Valo 	data->wbuffer = NULL;
546430db0ca8SKalle Valo 	data->on_close = NULL;
546530db0ca8SKalle Valo 
546630db0ca8SKalle Valo 	if (file->f_mode & FMODE_WRITE) {
546730db0ca8SKalle Valo 		if (!(file->f_mode & FMODE_READ)) {
546830db0ca8SKalle Valo 			Cmd cmd;
546930db0ca8SKalle Valo 			Resp rsp;
547030db0ca8SKalle Valo 
5471145a32feSWenwen Wang 			if (ai->flags & FLAG_RADIO_MASK) {
5472145a32feSWenwen Wang 				kfree(data->rbuffer);
5473145a32feSWenwen Wang 				kfree(file->private_data);
5474145a32feSWenwen Wang 				return -ENETDOWN;
5475145a32feSWenwen Wang 			}
547630db0ca8SKalle Valo 			memset(&cmd, 0, sizeof(cmd));
547730db0ca8SKalle Valo 			cmd.cmd = CMD_LISTBSS;
5478145a32feSWenwen Wang 			if (down_interruptible(&ai->sem)) {
5479145a32feSWenwen Wang 				kfree(data->rbuffer);
5480145a32feSWenwen Wang 				kfree(file->private_data);
548130db0ca8SKalle Valo 				return -ERESTARTSYS;
5482145a32feSWenwen Wang 			}
548330db0ca8SKalle Valo 			issuecommand(ai, &cmd, &rsp);
548430db0ca8SKalle Valo 			up(&ai->sem);
548530db0ca8SKalle Valo 			data->readlen = 0;
548630db0ca8SKalle Valo 			return 0;
548730db0ca8SKalle Valo 		}
548830db0ca8SKalle Valo 		doLoseSync = 1;
548930db0ca8SKalle Valo 	}
549030db0ca8SKalle Valo 	ptr = data->rbuffer;
549130db0ca8SKalle Valo 	/* There is a race condition here if there are concurrent opens.
549230db0ca8SKalle Valo            Since it is a rare condition, we'll just live with it, otherwise
549330db0ca8SKalle Valo            we have to add a spin lock... */
549430db0ca8SKalle Valo 	rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
549530db0ca8SKalle Valo 	while (rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
54963d39e1bbSDan Carpenter 		ptr += sprintf(ptr, "%pM %.*s rssi = %d",
549730db0ca8SKalle Valo 			       BSSList_rid.bssid,
549830db0ca8SKalle Valo 				(int)BSSList_rid.ssidLen,
549930db0ca8SKalle Valo 				BSSList_rid.ssid,
550030db0ca8SKalle Valo 				le16_to_cpu(BSSList_rid.dBm));
550130db0ca8SKalle Valo 		ptr += sprintf(ptr, " channel = %d %s %s %s %s\n",
550230db0ca8SKalle Valo 				le16_to_cpu(BSSList_rid.dsChannel),
550330db0ca8SKalle Valo 				BSSList_rid.cap & CAP_ESS ? "ESS" : "",
550430db0ca8SKalle Valo 				BSSList_rid.cap & CAP_IBSS ? "adhoc" : "",
550530db0ca8SKalle Valo 				BSSList_rid.cap & CAP_PRIVACY ? "wep" : "",
550630db0ca8SKalle Valo 				BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : "");
550730db0ca8SKalle Valo 		rc = readBSSListRid(ai, 0, &BSSList_rid);
550830db0ca8SKalle Valo 	}
550930db0ca8SKalle Valo 	*ptr = '\0';
551030db0ca8SKalle Valo 	data->readlen = strlen(data->rbuffer);
551130db0ca8SKalle Valo 	return 0;
551230db0ca8SKalle Valo }
551330db0ca8SKalle Valo 
551430db0ca8SKalle Valo static int proc_close(struct inode *inode, struct file *file)
551530db0ca8SKalle Valo {
551630db0ca8SKalle Valo 	struct proc_data *data = file->private_data;
551730db0ca8SKalle Valo 
551830db0ca8SKalle Valo 	if (data->on_close != NULL)
551930db0ca8SKalle Valo 		data->on_close(inode, file);
552030db0ca8SKalle Valo 	kfree(data->rbuffer);
552130db0ca8SKalle Valo 	kfree(data->wbuffer);
552230db0ca8SKalle Valo 	kfree(data);
552330db0ca8SKalle Valo 	return 0;
552430db0ca8SKalle Valo }
552530db0ca8SKalle Valo 
552630db0ca8SKalle Valo /* Since the card doesn't automatically switch to the right WEP mode,
552730db0ca8SKalle Valo    we will make it do it.  If the card isn't associated, every secs we
552830db0ca8SKalle Valo    will switch WEP modes to see if that will help.  If the card is
552930db0ca8SKalle Valo    associated we will check every minute to see if anything has
553030db0ca8SKalle Valo    changed. */
5531ba4d6513SLee Jones static void timer_func(struct net_device *dev)
5532ba4d6513SLee Jones {
553330db0ca8SKalle Valo 	struct airo_info *apriv = dev->ml_priv;
553430db0ca8SKalle Valo 
553530db0ca8SKalle Valo /* We don't have a link so try changing the authtype */
553630db0ca8SKalle Valo 	readConfigRid(apriv, 0);
553730db0ca8SKalle Valo 	disable_MAC(apriv, 0);
553830db0ca8SKalle Valo 	switch(apriv->config.authType) {
553930db0ca8SKalle Valo 		case AUTH_ENCRYPT:
554030db0ca8SKalle Valo /* So drop to OPEN */
554130db0ca8SKalle Valo 			apriv->config.authType = AUTH_OPEN;
554230db0ca8SKalle Valo 			break;
554330db0ca8SKalle Valo 		case AUTH_SHAREDKEY:
554430db0ca8SKalle Valo 			if (apriv->keyindex < auto_wep) {
554530db0ca8SKalle Valo 				set_wep_tx_idx(apriv, apriv->keyindex, 0, 0);
554630db0ca8SKalle Valo 				apriv->config.authType = AUTH_SHAREDKEY;
554730db0ca8SKalle Valo 				apriv->keyindex++;
554830db0ca8SKalle Valo 			} else {
554930db0ca8SKalle Valo 			        /* Drop to ENCRYPT */
555030db0ca8SKalle Valo 				apriv->keyindex = 0;
555130db0ca8SKalle Valo 				set_wep_tx_idx(apriv, apriv->defindex, 0, 0);
555230db0ca8SKalle Valo 				apriv->config.authType = AUTH_ENCRYPT;
555330db0ca8SKalle Valo 			}
555430db0ca8SKalle Valo 			break;
555530db0ca8SKalle Valo 		default:  /* We'll escalate to SHAREDKEY */
555630db0ca8SKalle Valo 			apriv->config.authType = AUTH_SHAREDKEY;
555730db0ca8SKalle Valo 	}
555830db0ca8SKalle Valo 	set_bit (FLAG_COMMIT, &apriv->flags);
555930db0ca8SKalle Valo 	writeConfigRid(apriv, 0);
556030db0ca8SKalle Valo 	enable_MAC(apriv, 0);
556130db0ca8SKalle Valo 	up(&apriv->sem);
556230db0ca8SKalle Valo 
556330db0ca8SKalle Valo /* Schedule check to see if the change worked */
556430db0ca8SKalle Valo 	clear_bit(JOB_AUTOWEP, &apriv->jobs);
556530db0ca8SKalle Valo 	apriv->expires = RUN_AT(HZ*3);
556630db0ca8SKalle Valo }
556730db0ca8SKalle Valo 
556830db0ca8SKalle Valo #ifdef CONFIG_PCI
556930db0ca8SKalle Valo static int airo_pci_probe(struct pci_dev *pdev,
557030db0ca8SKalle Valo 				    const struct pci_device_id *pent)
557130db0ca8SKalle Valo {
557230db0ca8SKalle Valo 	struct net_device *dev;
557330db0ca8SKalle Valo 
557430db0ca8SKalle Valo 	if (pci_enable_device(pdev))
557530db0ca8SKalle Valo 		return -ENODEV;
557630db0ca8SKalle Valo 	pci_set_master(pdev);
557730db0ca8SKalle Valo 
557830db0ca8SKalle Valo 	if (pdev->device == 0x5000 || pdev->device == 0xa504)
557930db0ca8SKalle Valo 			dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev);
558030db0ca8SKalle Valo 	else
558130db0ca8SKalle Valo 			dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev);
558230db0ca8SKalle Valo 	if (!dev) {
558330db0ca8SKalle Valo 		pci_disable_device(pdev);
558430db0ca8SKalle Valo 		return -ENODEV;
558530db0ca8SKalle Valo 	}
558630db0ca8SKalle Valo 
558730db0ca8SKalle Valo 	pci_set_drvdata(pdev, dev);
558830db0ca8SKalle Valo 	return 0;
558930db0ca8SKalle Valo }
559030db0ca8SKalle Valo 
559130db0ca8SKalle Valo static void airo_pci_remove(struct pci_dev *pdev)
559230db0ca8SKalle Valo {
559330db0ca8SKalle Valo 	struct net_device *dev = pci_get_drvdata(pdev);
559430db0ca8SKalle Valo 
559530db0ca8SKalle Valo 	airo_print_info(dev->name, "Unregistering...");
559630db0ca8SKalle Valo 	stop_airo_card(dev, 1);
559730db0ca8SKalle Valo 	pci_disable_device(pdev);
559830db0ca8SKalle Valo }
559930db0ca8SKalle Valo 
5600c3ab1804SVaibhav Gupta static int __maybe_unused airo_pci_suspend(struct device *dev_d)
560130db0ca8SKalle Valo {
5602c3ab1804SVaibhav Gupta 	struct net_device *dev = dev_get_drvdata(dev_d);
560330db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
560430db0ca8SKalle Valo 	Cmd cmd;
560530db0ca8SKalle Valo 	Resp rsp;
560630db0ca8SKalle Valo 
560730db0ca8SKalle Valo 	if (!ai->SSID)
560830db0ca8SKalle Valo 		ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL);
560930db0ca8SKalle Valo 	if (!ai->SSID)
561030db0ca8SKalle Valo 		return -ENOMEM;
561130db0ca8SKalle Valo 	readSsidRid(ai, ai->SSID);
561230db0ca8SKalle Valo 	memset(&cmd, 0, sizeof(cmd));
561330db0ca8SKalle Valo 	/* the lock will be released at the end of the resume callback */
561430db0ca8SKalle Valo 	if (down_interruptible(&ai->sem))
561530db0ca8SKalle Valo 		return -EAGAIN;
561630db0ca8SKalle Valo 	disable_MAC(ai, 0);
561730db0ca8SKalle Valo 	netif_device_detach(dev);
5618c3ab1804SVaibhav Gupta 	ai->power = PMSG_SUSPEND;
561930db0ca8SKalle Valo 	cmd.cmd = HOSTSLEEP;
562030db0ca8SKalle Valo 	issuecommand(ai, &cmd, &rsp);
562130db0ca8SKalle Valo 
5622c3ab1804SVaibhav Gupta 	device_wakeup_enable(dev_d);
562330db0ca8SKalle Valo 	return 0;
562430db0ca8SKalle Valo }
562530db0ca8SKalle Valo 
5626c3ab1804SVaibhav Gupta static int __maybe_unused airo_pci_resume(struct device *dev_d)
562730db0ca8SKalle Valo {
5628c3ab1804SVaibhav Gupta 	struct net_device *dev = dev_get_drvdata(dev_d);
562930db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
5630c3ab1804SVaibhav Gupta 	pci_power_t prev_state = to_pci_dev(dev_d)->current_state;
563130db0ca8SKalle Valo 
5632c3ab1804SVaibhav Gupta 	device_wakeup_disable(dev_d);
563330db0ca8SKalle Valo 
563430db0ca8SKalle Valo 	if (prev_state != PCI_D1) {
563530db0ca8SKalle Valo 		reset_card(dev, 0);
563630db0ca8SKalle Valo 		mpi_init_descriptors(ai);
563730db0ca8SKalle Valo 		setup_card(ai, dev->dev_addr, 0);
563830db0ca8SKalle Valo 		clear_bit(FLAG_RADIO_OFF, &ai->flags);
563930db0ca8SKalle Valo 		clear_bit(FLAG_PENDING_XMIT, &ai->flags);
564030db0ca8SKalle Valo 	} else {
564130db0ca8SKalle Valo 		OUT4500(ai, EVACK, EV_AWAKEN);
564230db0ca8SKalle Valo 		OUT4500(ai, EVACK, EV_AWAKEN);
564330db0ca8SKalle Valo 		msleep(100);
564430db0ca8SKalle Valo 	}
564530db0ca8SKalle Valo 
564630db0ca8SKalle Valo 	set_bit(FLAG_COMMIT, &ai->flags);
564730db0ca8SKalle Valo 	disable_MAC(ai, 0);
564830db0ca8SKalle Valo         msleep(200);
564930db0ca8SKalle Valo 	if (ai->SSID) {
565030db0ca8SKalle Valo 		writeSsidRid(ai, ai->SSID, 0);
565130db0ca8SKalle Valo 		kfree(ai->SSID);
565230db0ca8SKalle Valo 		ai->SSID = NULL;
565330db0ca8SKalle Valo 	}
565430db0ca8SKalle Valo 	writeAPListRid(ai, &ai->APList, 0);
565530db0ca8SKalle Valo 	writeConfigRid(ai, 0);
565630db0ca8SKalle Valo 	enable_MAC(ai, 0);
565730db0ca8SKalle Valo 	ai->power = PMSG_ON;
565830db0ca8SKalle Valo 	netif_device_attach(dev);
565930db0ca8SKalle Valo 	netif_wake_queue(dev);
566030db0ca8SKalle Valo 	enable_interrupts(ai);
566130db0ca8SKalle Valo 	up(&ai->sem);
566230db0ca8SKalle Valo 	return 0;
566330db0ca8SKalle Valo }
566430db0ca8SKalle Valo #endif
566530db0ca8SKalle Valo 
566630db0ca8SKalle Valo static int __init airo_init_module(void)
566730db0ca8SKalle Valo {
566830db0ca8SKalle Valo 	int i;
566930db0ca8SKalle Valo 
567030db0ca8SKalle Valo 	proc_kuid = make_kuid(&init_user_ns, proc_uid);
567130db0ca8SKalle Valo 	proc_kgid = make_kgid(&init_user_ns, proc_gid);
567230db0ca8SKalle Valo 	if (!uid_valid(proc_kuid) || !gid_valid(proc_kgid))
567330db0ca8SKalle Valo 		return -EINVAL;
567430db0ca8SKalle Valo 
567530db0ca8SKalle Valo 	airo_entry = proc_mkdir_mode("driver/aironet", airo_perm, NULL);
567630db0ca8SKalle Valo 
567730db0ca8SKalle Valo 	if (airo_entry)
567830db0ca8SKalle Valo 		proc_set_user(airo_entry, proc_kuid, proc_kgid);
567930db0ca8SKalle Valo 
568030db0ca8SKalle Valo 	for (i = 0; i < 4 && io[i] && irq[i]; i++) {
568130db0ca8SKalle Valo 		airo_print_info("", "Trying to configure ISA adapter at irq=%d "
568230db0ca8SKalle Valo 			"io = 0x%x", irq[i], io[i]);
56830b6a4247SLee Jones 		if (init_airo_card(irq[i], io[i], 0, NULL)) {
568430db0ca8SKalle Valo 			/* do nothing */ ;
568530db0ca8SKalle Valo 		}
56860b6a4247SLee Jones 	}
568730db0ca8SKalle Valo 
568830db0ca8SKalle Valo #ifdef CONFIG_PCI
568930db0ca8SKalle Valo 	airo_print_info("", "Probing for PCI adapters");
569030db0ca8SKalle Valo 	i = pci_register_driver(&airo_driver);
569130db0ca8SKalle Valo 	airo_print_info("", "Finished probing for PCI adapters");
569230db0ca8SKalle Valo 
569330db0ca8SKalle Valo 	if (i) {
569430db0ca8SKalle Valo 		remove_proc_entry("driver/aironet", NULL);
569530db0ca8SKalle Valo 		return i;
569630db0ca8SKalle Valo 	}
569730db0ca8SKalle Valo #endif
569830db0ca8SKalle Valo 
569930db0ca8SKalle Valo 	/* Always exit with success, as we are a library module
570030db0ca8SKalle Valo 	 * as well as a driver module
570130db0ca8SKalle Valo 	 */
570230db0ca8SKalle Valo 	return 0;
570330db0ca8SKalle Valo }
570430db0ca8SKalle Valo 
570530db0ca8SKalle Valo static void __exit airo_cleanup_module(void)
570630db0ca8SKalle Valo {
570730db0ca8SKalle Valo 	struct airo_info *ai;
570830db0ca8SKalle Valo 	while (!list_empty(&airo_devices)) {
570930db0ca8SKalle Valo 		ai = list_entry(airo_devices.next, struct airo_info, dev_list);
571030db0ca8SKalle Valo 		airo_print_info(ai->dev->name, "Unregistering...");
571130db0ca8SKalle Valo 		stop_airo_card(ai->dev, 1);
571230db0ca8SKalle Valo 	}
571330db0ca8SKalle Valo #ifdef CONFIG_PCI
571430db0ca8SKalle Valo 	pci_unregister_driver(&airo_driver);
571530db0ca8SKalle Valo #endif
571630db0ca8SKalle Valo 	remove_proc_entry("driver/aironet", NULL);
571730db0ca8SKalle Valo }
571830db0ca8SKalle Valo 
571930db0ca8SKalle Valo /*
572030db0ca8SKalle Valo  * Initial Wireless Extension code for Aironet driver by :
572130db0ca8SKalle Valo  *	Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00
572230db0ca8SKalle Valo  * Conversion to new driver API by :
572330db0ca8SKalle Valo  *	Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02
572430db0ca8SKalle Valo  * Javier also did a good amount of work here, adding some new extensions
572530db0ca8SKalle Valo  * and fixing my code. Let's just say that without him this code just
572630db0ca8SKalle Valo  * would not work at all... - Jean II
572730db0ca8SKalle Valo  */
572830db0ca8SKalle Valo 
572930db0ca8SKalle Valo static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi)
573030db0ca8SKalle Valo {
573130db0ca8SKalle Valo 	if (!rssi_rid)
573230db0ca8SKalle Valo 		return 0;
573330db0ca8SKalle Valo 
573430db0ca8SKalle Valo 	return (0x100 - rssi_rid[rssi].rssidBm);
573530db0ca8SKalle Valo }
573630db0ca8SKalle Valo 
573730db0ca8SKalle Valo static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm)
573830db0ca8SKalle Valo {
573930db0ca8SKalle Valo 	int i;
574030db0ca8SKalle Valo 
574130db0ca8SKalle Valo 	if (!rssi_rid)
574230db0ca8SKalle Valo 		return 0;
574330db0ca8SKalle Valo 
574430db0ca8SKalle Valo 	for (i = 0; i < 256; i++)
574530db0ca8SKalle Valo 		if (rssi_rid[i].rssidBm == dbm)
574630db0ca8SKalle Valo 			return rssi_rid[i].rssipct;
574730db0ca8SKalle Valo 
574830db0ca8SKalle Valo 	return 0;
574930db0ca8SKalle Valo }
575030db0ca8SKalle Valo 
575130db0ca8SKalle Valo 
575230db0ca8SKalle Valo static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid)
575330db0ca8SKalle Valo {
575430db0ca8SKalle Valo 	int quality = 0;
575530db0ca8SKalle Valo 	u16 sq;
575630db0ca8SKalle Valo 
575730db0ca8SKalle Valo 	if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f))
575830db0ca8SKalle Valo 		return 0;
575930db0ca8SKalle Valo 
576030db0ca8SKalle Valo 	if (!(cap_rid->hardCap & cpu_to_le16(8)))
576130db0ca8SKalle Valo 		return 0;
576230db0ca8SKalle Valo 
576330db0ca8SKalle Valo 	sq = le16_to_cpu(status_rid->signalQuality);
576430db0ca8SKalle Valo 	if (memcmp(cap_rid->prodName, "350", 3))
576530db0ca8SKalle Valo 		if (sq > 0x20)
576630db0ca8SKalle Valo 			quality = 0;
576730db0ca8SKalle Valo 		else
576830db0ca8SKalle Valo 			quality = 0x20 - sq;
576930db0ca8SKalle Valo 	else
577030db0ca8SKalle Valo 		if (sq > 0xb0)
577130db0ca8SKalle Valo 			quality = 0;
577230db0ca8SKalle Valo 		else if (sq < 0x10)
577330db0ca8SKalle Valo 			quality = 0xa0;
577430db0ca8SKalle Valo 		else
577530db0ca8SKalle Valo 			quality = 0xb0 - sq;
577630db0ca8SKalle Valo 	return quality;
577730db0ca8SKalle Valo }
577830db0ca8SKalle Valo 
577930db0ca8SKalle Valo #define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0)
578030db0ca8SKalle Valo #define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50);
578130db0ca8SKalle Valo 
578230db0ca8SKalle Valo /*------------------------------------------------------------------*/
578330db0ca8SKalle Valo /*
578430db0ca8SKalle Valo  * Wireless Handler : get protocol name
578530db0ca8SKalle Valo  */
578630db0ca8SKalle Valo static int airo_get_name(struct net_device *dev,
578730db0ca8SKalle Valo 			 struct iw_request_info *info,
578830db0ca8SKalle Valo 			 char *cwrq,
578930db0ca8SKalle Valo 			 char *extra)
579030db0ca8SKalle Valo {
579130db0ca8SKalle Valo 	strcpy(cwrq, "IEEE 802.11-DS");
579230db0ca8SKalle Valo 	return 0;
579330db0ca8SKalle Valo }
579430db0ca8SKalle Valo 
579530db0ca8SKalle Valo /*------------------------------------------------------------------*/
579630db0ca8SKalle Valo /*
579730db0ca8SKalle Valo  * Wireless Handler : set frequency
579830db0ca8SKalle Valo  */
579930db0ca8SKalle Valo static int airo_set_freq(struct net_device *dev,
580030db0ca8SKalle Valo 			 struct iw_request_info *info,
580130db0ca8SKalle Valo 			 struct iw_freq *fwrq,
580230db0ca8SKalle Valo 			 char *extra)
580330db0ca8SKalle Valo {
580430db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
580530db0ca8SKalle Valo 	int rc = -EINPROGRESS;		/* Call commit handler */
580630db0ca8SKalle Valo 
580730db0ca8SKalle Valo 	/* If setting by frequency, convert to a channel */
580830db0ca8SKalle Valo 	if (fwrq->e == 1) {
580930db0ca8SKalle Valo 		int f = fwrq->m / 100000;
581030db0ca8SKalle Valo 
581130db0ca8SKalle Valo 		/* Hack to fall through... */
581230db0ca8SKalle Valo 		fwrq->e = 0;
581330db0ca8SKalle Valo 		fwrq->m = ieee80211_frequency_to_channel(f);
581430db0ca8SKalle Valo 	}
581530db0ca8SKalle Valo 	/* Setting by channel number */
581603ba4a1bSDan Carpenter 	if (fwrq->m < 0 || fwrq->m > 1000 || fwrq->e > 0)
581730db0ca8SKalle Valo 		rc = -EOPNOTSUPP;
581830db0ca8SKalle Valo 	else {
581930db0ca8SKalle Valo 		int channel = fwrq->m;
582030db0ca8SKalle Valo 		/* We should do a better check than that,
582130db0ca8SKalle Valo 		 * based on the card capability !!! */
582230db0ca8SKalle Valo 		if ((channel < 1) || (channel > 14)) {
582330db0ca8SKalle Valo 			airo_print_dbg(dev->name, "New channel value of %d is invalid!",
582430db0ca8SKalle Valo 				fwrq->m);
582530db0ca8SKalle Valo 			rc = -EINVAL;
582630db0ca8SKalle Valo 		} else {
582730db0ca8SKalle Valo 			readConfigRid(local, 1);
582830db0ca8SKalle Valo 			/* Yes ! We can set it !!! */
582930db0ca8SKalle Valo 			local->config.channelSet = cpu_to_le16(channel);
583030db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &local->flags);
583130db0ca8SKalle Valo 		}
583230db0ca8SKalle Valo 	}
583330db0ca8SKalle Valo 	return rc;
583430db0ca8SKalle Valo }
583530db0ca8SKalle Valo 
583630db0ca8SKalle Valo /*------------------------------------------------------------------*/
583730db0ca8SKalle Valo /*
583830db0ca8SKalle Valo  * Wireless Handler : get frequency
583930db0ca8SKalle Valo  */
584030db0ca8SKalle Valo static int airo_get_freq(struct net_device *dev,
584130db0ca8SKalle Valo 			 struct iw_request_info *info,
584230db0ca8SKalle Valo 			 struct iw_freq *fwrq,
584330db0ca8SKalle Valo 			 char *extra)
584430db0ca8SKalle Valo {
584530db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
584630db0ca8SKalle Valo 	StatusRid status_rid;		/* Card status info */
584730db0ca8SKalle Valo 	int ch;
584830db0ca8SKalle Valo 
584930db0ca8SKalle Valo 	readConfigRid(local, 1);
585030db0ca8SKalle Valo 	if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS)
585130db0ca8SKalle Valo 		status_rid.channel = local->config.channelSet;
585230db0ca8SKalle Valo 	else
585330db0ca8SKalle Valo 		readStatusRid(local, &status_rid, 1);
585430db0ca8SKalle Valo 
585530db0ca8SKalle Valo 	ch = le16_to_cpu(status_rid.channel);
585630db0ca8SKalle Valo 	if ((ch > 0) && (ch < 15)) {
585730db0ca8SKalle Valo 		fwrq->m = 100000 *
585857fbcce3SJohannes Berg 			ieee80211_channel_to_frequency(ch, NL80211_BAND_2GHZ);
585930db0ca8SKalle Valo 		fwrq->e = 1;
586030db0ca8SKalle Valo 	} else {
586130db0ca8SKalle Valo 		fwrq->m = ch;
586230db0ca8SKalle Valo 		fwrq->e = 0;
586330db0ca8SKalle Valo 	}
586430db0ca8SKalle Valo 
586530db0ca8SKalle Valo 	return 0;
586630db0ca8SKalle Valo }
586730db0ca8SKalle Valo 
586830db0ca8SKalle Valo /*------------------------------------------------------------------*/
586930db0ca8SKalle Valo /*
587030db0ca8SKalle Valo  * Wireless Handler : set ESSID
587130db0ca8SKalle Valo  */
587230db0ca8SKalle Valo static int airo_set_essid(struct net_device *dev,
587330db0ca8SKalle Valo 			  struct iw_request_info *info,
587430db0ca8SKalle Valo 			  struct iw_point *dwrq,
587530db0ca8SKalle Valo 			  char *extra)
587630db0ca8SKalle Valo {
587730db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
587830db0ca8SKalle Valo 	SsidRid SSID_rid;		/* SSIDs */
587930db0ca8SKalle Valo 
588030db0ca8SKalle Valo 	/* Reload the list of current SSID */
588130db0ca8SKalle Valo 	readSsidRid(local, &SSID_rid);
588230db0ca8SKalle Valo 
588330db0ca8SKalle Valo 	/* Check if we asked for `any' */
588430db0ca8SKalle Valo 	if (dwrq->flags == 0) {
588530db0ca8SKalle Valo 		/* Just send an empty SSID list */
588630db0ca8SKalle Valo 		memset(&SSID_rid, 0, sizeof(SSID_rid));
588730db0ca8SKalle Valo 	} else {
588830db0ca8SKalle Valo 		unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
588930db0ca8SKalle Valo 
589030db0ca8SKalle Valo 		/* Check the size of the string */
589130db0ca8SKalle Valo 		if (dwrq->length > IW_ESSID_MAX_SIZE)
589230db0ca8SKalle Valo 			return -E2BIG ;
589330db0ca8SKalle Valo 
589430db0ca8SKalle Valo 		/* Check if index is valid */
589530db0ca8SKalle Valo 		if (index >= ARRAY_SIZE(SSID_rid.ssids))
589630db0ca8SKalle Valo 			return -EINVAL;
589730db0ca8SKalle Valo 
589830db0ca8SKalle Valo 		/* Set the SSID */
589930db0ca8SKalle Valo 		memset(SSID_rid.ssids[index].ssid, 0,
590030db0ca8SKalle Valo 		       sizeof(SSID_rid.ssids[index].ssid));
590130db0ca8SKalle Valo 		memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length);
590230db0ca8SKalle Valo 		SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length);
590330db0ca8SKalle Valo 	}
590430db0ca8SKalle Valo 	SSID_rid.len = cpu_to_le16(sizeof(SSID_rid));
590530db0ca8SKalle Valo 	/* Write it to the card */
590630db0ca8SKalle Valo 	disable_MAC(local, 1);
590730db0ca8SKalle Valo 	writeSsidRid(local, &SSID_rid, 1);
590830db0ca8SKalle Valo 	enable_MAC(local, 1);
590930db0ca8SKalle Valo 
591030db0ca8SKalle Valo 	return 0;
591130db0ca8SKalle Valo }
591230db0ca8SKalle Valo 
591330db0ca8SKalle Valo /*------------------------------------------------------------------*/
591430db0ca8SKalle Valo /*
591530db0ca8SKalle Valo  * Wireless Handler : get ESSID
591630db0ca8SKalle Valo  */
591730db0ca8SKalle Valo static int airo_get_essid(struct net_device *dev,
591830db0ca8SKalle Valo 			  struct iw_request_info *info,
591930db0ca8SKalle Valo 			  struct iw_point *dwrq,
592030db0ca8SKalle Valo 			  char *extra)
592130db0ca8SKalle Valo {
592230db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
592330db0ca8SKalle Valo 	StatusRid status_rid;		/* Card status info */
592430db0ca8SKalle Valo 
592530db0ca8SKalle Valo 	readStatusRid(local, &status_rid, 1);
592630db0ca8SKalle Valo 
592730db0ca8SKalle Valo 	/* Note : if dwrq->flags != 0, we should
592830db0ca8SKalle Valo 	 * get the relevant SSID from the SSID list... */
592930db0ca8SKalle Valo 
593030db0ca8SKalle Valo 	/* Get the current SSID */
593130db0ca8SKalle Valo 	memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen));
593230db0ca8SKalle Valo 	/* If none, we may want to get the one that was set */
593330db0ca8SKalle Valo 
593430db0ca8SKalle Valo 	/* Push it out ! */
593530db0ca8SKalle Valo 	dwrq->length = le16_to_cpu(status_rid.SSIDlen);
593630db0ca8SKalle Valo 	dwrq->flags = 1; /* active */
593730db0ca8SKalle Valo 
593830db0ca8SKalle Valo 	return 0;
593930db0ca8SKalle Valo }
594030db0ca8SKalle Valo 
594130db0ca8SKalle Valo /*------------------------------------------------------------------*/
594230db0ca8SKalle Valo /*
594330db0ca8SKalle Valo  * Wireless Handler : set AP address
594430db0ca8SKalle Valo  */
594530db0ca8SKalle Valo static int airo_set_wap(struct net_device *dev,
594630db0ca8SKalle Valo 			struct iw_request_info *info,
594730db0ca8SKalle Valo 			struct sockaddr *awrq,
594830db0ca8SKalle Valo 			char *extra)
594930db0ca8SKalle Valo {
595030db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
595130db0ca8SKalle Valo 	Cmd cmd;
595230db0ca8SKalle Valo 	Resp rsp;
595330db0ca8SKalle Valo 	APListRid *APList_rid = &local->APList;
595430db0ca8SKalle Valo 
595530db0ca8SKalle Valo 	if (awrq->sa_family != ARPHRD_ETHER)
595630db0ca8SKalle Valo 		return -EINVAL;
595730db0ca8SKalle Valo 	else if (is_broadcast_ether_addr(awrq->sa_data) ||
595830db0ca8SKalle Valo 		 is_zero_ether_addr(awrq->sa_data)) {
595930db0ca8SKalle Valo 		memset(&cmd, 0, sizeof(cmd));
596030db0ca8SKalle Valo 		cmd.cmd = CMD_LOSE_SYNC;
596130db0ca8SKalle Valo 		if (down_interruptible(&local->sem))
596230db0ca8SKalle Valo 			return -ERESTARTSYS;
596330db0ca8SKalle Valo 		issuecommand(local, &cmd, &rsp);
596430db0ca8SKalle Valo 		up(&local->sem);
596530db0ca8SKalle Valo 	} else {
596630db0ca8SKalle Valo 		memset(APList_rid, 0, sizeof(*APList_rid));
596730db0ca8SKalle Valo 		APList_rid->len = cpu_to_le16(sizeof(*APList_rid));
596830db0ca8SKalle Valo 		memcpy(APList_rid->ap[0], awrq->sa_data, ETH_ALEN);
596930db0ca8SKalle Valo 		disable_MAC(local, 1);
597030db0ca8SKalle Valo 		writeAPListRid(local, APList_rid, 1);
597130db0ca8SKalle Valo 		enable_MAC(local, 1);
597230db0ca8SKalle Valo 	}
597330db0ca8SKalle Valo 	return 0;
597430db0ca8SKalle Valo }
597530db0ca8SKalle Valo 
597630db0ca8SKalle Valo /*------------------------------------------------------------------*/
597730db0ca8SKalle Valo /*
597830db0ca8SKalle Valo  * Wireless Handler : get AP address
597930db0ca8SKalle Valo  */
598030db0ca8SKalle Valo static int airo_get_wap(struct net_device *dev,
598130db0ca8SKalle Valo 			struct iw_request_info *info,
598230db0ca8SKalle Valo 			struct sockaddr *awrq,
598330db0ca8SKalle Valo 			char *extra)
598430db0ca8SKalle Valo {
598530db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
598630db0ca8SKalle Valo 	StatusRid status_rid;		/* Card status info */
598730db0ca8SKalle Valo 
598830db0ca8SKalle Valo 	readStatusRid(local, &status_rid, 1);
598930db0ca8SKalle Valo 
599030db0ca8SKalle Valo 	/* Tentative. This seems to work, wow, I'm lucky !!! */
599130db0ca8SKalle Valo 	memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN);
599230db0ca8SKalle Valo 	awrq->sa_family = ARPHRD_ETHER;
599330db0ca8SKalle Valo 
599430db0ca8SKalle Valo 	return 0;
599530db0ca8SKalle Valo }
599630db0ca8SKalle Valo 
599730db0ca8SKalle Valo /*------------------------------------------------------------------*/
599830db0ca8SKalle Valo /*
599930db0ca8SKalle Valo  * Wireless Handler : set Nickname
600030db0ca8SKalle Valo  */
600130db0ca8SKalle Valo static int airo_set_nick(struct net_device *dev,
600230db0ca8SKalle Valo 			 struct iw_request_info *info,
600330db0ca8SKalle Valo 			 struct iw_point *dwrq,
600430db0ca8SKalle Valo 			 char *extra)
600530db0ca8SKalle Valo {
600630db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
600730db0ca8SKalle Valo 
600830db0ca8SKalle Valo 	/* Check the size of the string */
600930db0ca8SKalle Valo 	if (dwrq->length > 16) {
601030db0ca8SKalle Valo 		return -E2BIG;
601130db0ca8SKalle Valo 	}
601230db0ca8SKalle Valo 	readConfigRid(local, 1);
601330db0ca8SKalle Valo 	memset(local->config.nodeName, 0, sizeof(local->config.nodeName));
601430db0ca8SKalle Valo 	memcpy(local->config.nodeName, extra, dwrq->length);
601530db0ca8SKalle Valo 	set_bit (FLAG_COMMIT, &local->flags);
601630db0ca8SKalle Valo 
601730db0ca8SKalle Valo 	return -EINPROGRESS;		/* Call commit handler */
601830db0ca8SKalle Valo }
601930db0ca8SKalle Valo 
602030db0ca8SKalle Valo /*------------------------------------------------------------------*/
602130db0ca8SKalle Valo /*
602230db0ca8SKalle Valo  * Wireless Handler : get Nickname
602330db0ca8SKalle Valo  */
602430db0ca8SKalle Valo static int airo_get_nick(struct net_device *dev,
602530db0ca8SKalle Valo 			 struct iw_request_info *info,
602630db0ca8SKalle Valo 			 struct iw_point *dwrq,
602730db0ca8SKalle Valo 			 char *extra)
602830db0ca8SKalle Valo {
602930db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
603030db0ca8SKalle Valo 
603130db0ca8SKalle Valo 	readConfigRid(local, 1);
603230db0ca8SKalle Valo 	strncpy(extra, local->config.nodeName, 16);
603330db0ca8SKalle Valo 	extra[16] = '\0';
603430db0ca8SKalle Valo 	dwrq->length = strlen(extra);
603530db0ca8SKalle Valo 
603630db0ca8SKalle Valo 	return 0;
603730db0ca8SKalle Valo }
603830db0ca8SKalle Valo 
603930db0ca8SKalle Valo /*------------------------------------------------------------------*/
604030db0ca8SKalle Valo /*
604130db0ca8SKalle Valo  * Wireless Handler : set Bit-Rate
604230db0ca8SKalle Valo  */
604330db0ca8SKalle Valo static int airo_set_rate(struct net_device *dev,
604430db0ca8SKalle Valo 			 struct iw_request_info *info,
604530db0ca8SKalle Valo 			 struct iw_param *vwrq,
604630db0ca8SKalle Valo 			 char *extra)
604730db0ca8SKalle Valo {
604830db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
604930db0ca8SKalle Valo 	CapabilityRid cap_rid;		/* Card capability info */
605030db0ca8SKalle Valo 	u8	brate = 0;
605130db0ca8SKalle Valo 	int	i;
605230db0ca8SKalle Valo 
605330db0ca8SKalle Valo 	/* First : get a valid bit rate value */
605430db0ca8SKalle Valo 	readCapabilityRid(local, &cap_rid, 1);
605530db0ca8SKalle Valo 
605630db0ca8SKalle Valo 	/* Which type of value ? */
605730db0ca8SKalle Valo 	if ((vwrq->value < 8) && (vwrq->value >= 0)) {
605830db0ca8SKalle Valo 		/* Setting by rate index */
605930db0ca8SKalle Valo 		/* Find value in the magic rate table */
606030db0ca8SKalle Valo 		brate = cap_rid.supportedRates[vwrq->value];
606130db0ca8SKalle Valo 	} else {
606230db0ca8SKalle Valo 		/* Setting by frequency value */
606330db0ca8SKalle Valo 		u8	normvalue = (u8) (vwrq->value/500000);
606430db0ca8SKalle Valo 
606530db0ca8SKalle Valo 		/* Check if rate is valid */
606630db0ca8SKalle Valo 		for (i = 0 ; i < 8 ; i++) {
606730db0ca8SKalle Valo 			if (normvalue == cap_rid.supportedRates[i]) {
606830db0ca8SKalle Valo 				brate = normvalue;
606930db0ca8SKalle Valo 				break;
607030db0ca8SKalle Valo 			}
607130db0ca8SKalle Valo 		}
607230db0ca8SKalle Valo 	}
607330db0ca8SKalle Valo 	/* -1 designed the max rate (mostly auto mode) */
607430db0ca8SKalle Valo 	if (vwrq->value == -1) {
607530db0ca8SKalle Valo 		/* Get the highest available rate */
607630db0ca8SKalle Valo 		for (i = 0 ; i < 8 ; i++) {
607730db0ca8SKalle Valo 			if (cap_rid.supportedRates[i] == 0)
607830db0ca8SKalle Valo 				break;
607930db0ca8SKalle Valo 		}
608030db0ca8SKalle Valo 		if (i != 0)
608130db0ca8SKalle Valo 			brate = cap_rid.supportedRates[i - 1];
608230db0ca8SKalle Valo 	}
608330db0ca8SKalle Valo 	/* Check that it is valid */
608430db0ca8SKalle Valo 	if (brate == 0) {
608530db0ca8SKalle Valo 		return -EINVAL;
608630db0ca8SKalle Valo 	}
608730db0ca8SKalle Valo 
608830db0ca8SKalle Valo 	readConfigRid(local, 1);
608930db0ca8SKalle Valo 	/* Now, check if we want a fixed or auto value */
609030db0ca8SKalle Valo 	if (vwrq->fixed == 0) {
609130db0ca8SKalle Valo 		/* Fill all the rates up to this max rate */
609230db0ca8SKalle Valo 		memset(local->config.rates, 0, 8);
609330db0ca8SKalle Valo 		for (i = 0 ; i < 8 ; i++) {
609430db0ca8SKalle Valo 			local->config.rates[i] = cap_rid.supportedRates[i];
609530db0ca8SKalle Valo 			if (local->config.rates[i] == brate)
609630db0ca8SKalle Valo 				break;
609730db0ca8SKalle Valo 		}
609830db0ca8SKalle Valo 	} else {
609930db0ca8SKalle Valo 		/* Fixed mode */
610030db0ca8SKalle Valo 		/* One rate, fixed */
610130db0ca8SKalle Valo 		memset(local->config.rates, 0, 8);
610230db0ca8SKalle Valo 		local->config.rates[0] = brate;
610330db0ca8SKalle Valo 	}
610430db0ca8SKalle Valo 	set_bit (FLAG_COMMIT, &local->flags);
610530db0ca8SKalle Valo 
610630db0ca8SKalle Valo 	return -EINPROGRESS;		/* Call commit handler */
610730db0ca8SKalle Valo }
610830db0ca8SKalle Valo 
610930db0ca8SKalle Valo /*------------------------------------------------------------------*/
611030db0ca8SKalle Valo /*
611130db0ca8SKalle Valo  * Wireless Handler : get Bit-Rate
611230db0ca8SKalle Valo  */
611330db0ca8SKalle Valo static int airo_get_rate(struct net_device *dev,
611430db0ca8SKalle Valo 			 struct iw_request_info *info,
611530db0ca8SKalle Valo 			 struct iw_param *vwrq,
611630db0ca8SKalle Valo 			 char *extra)
611730db0ca8SKalle Valo {
611830db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
611930db0ca8SKalle Valo 	StatusRid status_rid;		/* Card status info */
612030db0ca8SKalle Valo 
612130db0ca8SKalle Valo 	readStatusRid(local, &status_rid, 1);
612230db0ca8SKalle Valo 
612330db0ca8SKalle Valo 	vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000;
612430db0ca8SKalle Valo 	/* If more than one rate, set auto */
612530db0ca8SKalle Valo 	readConfigRid(local, 1);
612630db0ca8SKalle Valo 	vwrq->fixed = (local->config.rates[1] == 0);
612730db0ca8SKalle Valo 
612830db0ca8SKalle Valo 	return 0;
612930db0ca8SKalle Valo }
613030db0ca8SKalle Valo 
613130db0ca8SKalle Valo /*------------------------------------------------------------------*/
613230db0ca8SKalle Valo /*
613330db0ca8SKalle Valo  * Wireless Handler : set RTS threshold
613430db0ca8SKalle Valo  */
613530db0ca8SKalle Valo static int airo_set_rts(struct net_device *dev,
613630db0ca8SKalle Valo 			struct iw_request_info *info,
613730db0ca8SKalle Valo 			struct iw_param *vwrq,
613830db0ca8SKalle Valo 			char *extra)
613930db0ca8SKalle Valo {
614030db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
614130db0ca8SKalle Valo 	int rthr = vwrq->value;
614230db0ca8SKalle Valo 
614330db0ca8SKalle Valo 	if (vwrq->disabled)
614430db0ca8SKalle Valo 		rthr = AIRO_DEF_MTU;
614530db0ca8SKalle Valo 	if ((rthr < 0) || (rthr > AIRO_DEF_MTU)) {
614630db0ca8SKalle Valo 		return -EINVAL;
614730db0ca8SKalle Valo 	}
614830db0ca8SKalle Valo 	readConfigRid(local, 1);
614930db0ca8SKalle Valo 	local->config.rtsThres = cpu_to_le16(rthr);
615030db0ca8SKalle Valo 	set_bit (FLAG_COMMIT, &local->flags);
615130db0ca8SKalle Valo 
615230db0ca8SKalle Valo 	return -EINPROGRESS;		/* Call commit handler */
615330db0ca8SKalle Valo }
615430db0ca8SKalle Valo 
615530db0ca8SKalle Valo /*------------------------------------------------------------------*/
615630db0ca8SKalle Valo /*
615730db0ca8SKalle Valo  * Wireless Handler : get RTS threshold
615830db0ca8SKalle Valo  */
615930db0ca8SKalle Valo static int airo_get_rts(struct net_device *dev,
616030db0ca8SKalle Valo 			struct iw_request_info *info,
616130db0ca8SKalle Valo 			struct iw_param *vwrq,
616230db0ca8SKalle Valo 			char *extra)
616330db0ca8SKalle Valo {
616430db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
616530db0ca8SKalle Valo 
616630db0ca8SKalle Valo 	readConfigRid(local, 1);
616730db0ca8SKalle Valo 	vwrq->value = le16_to_cpu(local->config.rtsThres);
616830db0ca8SKalle Valo 	vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
616930db0ca8SKalle Valo 	vwrq->fixed = 1;
617030db0ca8SKalle Valo 
617130db0ca8SKalle Valo 	return 0;
617230db0ca8SKalle Valo }
617330db0ca8SKalle Valo 
617430db0ca8SKalle Valo /*------------------------------------------------------------------*/
617530db0ca8SKalle Valo /*
617630db0ca8SKalle Valo  * Wireless Handler : set Fragmentation threshold
617730db0ca8SKalle Valo  */
617830db0ca8SKalle Valo static int airo_set_frag(struct net_device *dev,
617930db0ca8SKalle Valo 			 struct iw_request_info *info,
618030db0ca8SKalle Valo 			 struct iw_param *vwrq,
618130db0ca8SKalle Valo 			 char *extra)
618230db0ca8SKalle Valo {
618330db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
618430db0ca8SKalle Valo 	int fthr = vwrq->value;
618530db0ca8SKalle Valo 
618630db0ca8SKalle Valo 	if (vwrq->disabled)
618730db0ca8SKalle Valo 		fthr = AIRO_DEF_MTU;
618830db0ca8SKalle Valo 	if ((fthr < 256) || (fthr > AIRO_DEF_MTU)) {
618930db0ca8SKalle Valo 		return -EINVAL;
619030db0ca8SKalle Valo 	}
619130db0ca8SKalle Valo 	fthr &= ~0x1;	/* Get an even value - is it really needed ??? */
619230db0ca8SKalle Valo 	readConfigRid(local, 1);
619330db0ca8SKalle Valo 	local->config.fragThresh = cpu_to_le16(fthr);
619430db0ca8SKalle Valo 	set_bit (FLAG_COMMIT, &local->flags);
619530db0ca8SKalle Valo 
619630db0ca8SKalle Valo 	return -EINPROGRESS;		/* Call commit handler */
619730db0ca8SKalle Valo }
619830db0ca8SKalle Valo 
619930db0ca8SKalle Valo /*------------------------------------------------------------------*/
620030db0ca8SKalle Valo /*
620130db0ca8SKalle Valo  * Wireless Handler : get Fragmentation threshold
620230db0ca8SKalle Valo  */
620330db0ca8SKalle Valo static int airo_get_frag(struct net_device *dev,
620430db0ca8SKalle Valo 			 struct iw_request_info *info,
620530db0ca8SKalle Valo 			 struct iw_param *vwrq,
620630db0ca8SKalle Valo 			 char *extra)
620730db0ca8SKalle Valo {
620830db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
620930db0ca8SKalle Valo 
621030db0ca8SKalle Valo 	readConfigRid(local, 1);
621130db0ca8SKalle Valo 	vwrq->value = le16_to_cpu(local->config.fragThresh);
621230db0ca8SKalle Valo 	vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
621330db0ca8SKalle Valo 	vwrq->fixed = 1;
621430db0ca8SKalle Valo 
621530db0ca8SKalle Valo 	return 0;
621630db0ca8SKalle Valo }
621730db0ca8SKalle Valo 
621830db0ca8SKalle Valo /*------------------------------------------------------------------*/
621930db0ca8SKalle Valo /*
622030db0ca8SKalle Valo  * Wireless Handler : set Mode of Operation
622130db0ca8SKalle Valo  */
622230db0ca8SKalle Valo static int airo_set_mode(struct net_device *dev,
622330db0ca8SKalle Valo 			 struct iw_request_info *info,
622430db0ca8SKalle Valo 			 __u32 *uwrq,
622530db0ca8SKalle Valo 			 char *extra)
622630db0ca8SKalle Valo {
622730db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
622830db0ca8SKalle Valo 	int reset = 0;
622930db0ca8SKalle Valo 
623030db0ca8SKalle Valo 	readConfigRid(local, 1);
623130db0ca8SKalle Valo 	if (sniffing_mode(local))
623230db0ca8SKalle Valo 		reset = 1;
623330db0ca8SKalle Valo 
623430db0ca8SKalle Valo 	switch(*uwrq) {
623530db0ca8SKalle Valo 		case IW_MODE_ADHOC:
623630db0ca8SKalle Valo 			local->config.opmode &= ~MODE_CFG_MASK;
623730db0ca8SKalle Valo 			local->config.opmode |= MODE_STA_IBSS;
623830db0ca8SKalle Valo 			local->config.rmode &= ~RXMODE_FULL_MASK;
623930db0ca8SKalle Valo 			local->config.scanMode = SCANMODE_ACTIVE;
624030db0ca8SKalle Valo 			clear_bit (FLAG_802_11, &local->flags);
624130db0ca8SKalle Valo 			break;
624230db0ca8SKalle Valo 		case IW_MODE_INFRA:
624330db0ca8SKalle Valo 			local->config.opmode &= ~MODE_CFG_MASK;
624430db0ca8SKalle Valo 			local->config.opmode |= MODE_STA_ESS;
624530db0ca8SKalle Valo 			local->config.rmode &= ~RXMODE_FULL_MASK;
624630db0ca8SKalle Valo 			local->config.scanMode = SCANMODE_ACTIVE;
624730db0ca8SKalle Valo 			clear_bit (FLAG_802_11, &local->flags);
624830db0ca8SKalle Valo 			break;
624930db0ca8SKalle Valo 		case IW_MODE_MASTER:
625030db0ca8SKalle Valo 			local->config.opmode &= ~MODE_CFG_MASK;
625130db0ca8SKalle Valo 			local->config.opmode |= MODE_AP;
625230db0ca8SKalle Valo 			local->config.rmode &= ~RXMODE_FULL_MASK;
625330db0ca8SKalle Valo 			local->config.scanMode = SCANMODE_ACTIVE;
625430db0ca8SKalle Valo 			clear_bit (FLAG_802_11, &local->flags);
625530db0ca8SKalle Valo 			break;
625630db0ca8SKalle Valo 		case IW_MODE_REPEAT:
625730db0ca8SKalle Valo 			local->config.opmode &= ~MODE_CFG_MASK;
625830db0ca8SKalle Valo 			local->config.opmode |= MODE_AP_RPTR;
625930db0ca8SKalle Valo 			local->config.rmode &= ~RXMODE_FULL_MASK;
626030db0ca8SKalle Valo 			local->config.scanMode = SCANMODE_ACTIVE;
626130db0ca8SKalle Valo 			clear_bit (FLAG_802_11, &local->flags);
626230db0ca8SKalle Valo 			break;
626330db0ca8SKalle Valo 		case IW_MODE_MONITOR:
626430db0ca8SKalle Valo 			local->config.opmode &= ~MODE_CFG_MASK;
626530db0ca8SKalle Valo 			local->config.opmode |= MODE_STA_ESS;
626630db0ca8SKalle Valo 			local->config.rmode &= ~RXMODE_FULL_MASK;
626730db0ca8SKalle Valo 			local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
626830db0ca8SKalle Valo 			local->config.scanMode = SCANMODE_PASSIVE;
626930db0ca8SKalle Valo 			set_bit (FLAG_802_11, &local->flags);
627030db0ca8SKalle Valo 			break;
627130db0ca8SKalle Valo 		default:
627230db0ca8SKalle Valo 			return -EINVAL;
627330db0ca8SKalle Valo 	}
627430db0ca8SKalle Valo 	if (reset)
627530db0ca8SKalle Valo 		set_bit (FLAG_RESET, &local->flags);
627630db0ca8SKalle Valo 	set_bit (FLAG_COMMIT, &local->flags);
627730db0ca8SKalle Valo 
627830db0ca8SKalle Valo 	return -EINPROGRESS;		/* Call commit handler */
627930db0ca8SKalle Valo }
628030db0ca8SKalle Valo 
628130db0ca8SKalle Valo /*------------------------------------------------------------------*/
628230db0ca8SKalle Valo /*
628330db0ca8SKalle Valo  * Wireless Handler : get Mode of Operation
628430db0ca8SKalle Valo  */
628530db0ca8SKalle Valo static int airo_get_mode(struct net_device *dev,
628630db0ca8SKalle Valo 			 struct iw_request_info *info,
628730db0ca8SKalle Valo 			 __u32 *uwrq,
628830db0ca8SKalle Valo 			 char *extra)
628930db0ca8SKalle Valo {
629030db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
629130db0ca8SKalle Valo 
629230db0ca8SKalle Valo 	readConfigRid(local, 1);
629330db0ca8SKalle Valo 	/* If not managed, assume it's ad-hoc */
629430db0ca8SKalle Valo 	switch (local->config.opmode & MODE_CFG_MASK) {
629530db0ca8SKalle Valo 		case MODE_STA_ESS:
629630db0ca8SKalle Valo 			*uwrq = IW_MODE_INFRA;
629730db0ca8SKalle Valo 			break;
629830db0ca8SKalle Valo 		case MODE_AP:
629930db0ca8SKalle Valo 			*uwrq = IW_MODE_MASTER;
630030db0ca8SKalle Valo 			break;
630130db0ca8SKalle Valo 		case MODE_AP_RPTR:
630230db0ca8SKalle Valo 			*uwrq = IW_MODE_REPEAT;
630330db0ca8SKalle Valo 			break;
630430db0ca8SKalle Valo 		default:
630530db0ca8SKalle Valo 			*uwrq = IW_MODE_ADHOC;
630630db0ca8SKalle Valo 	}
630730db0ca8SKalle Valo 
630830db0ca8SKalle Valo 	return 0;
630930db0ca8SKalle Valo }
631030db0ca8SKalle Valo 
631130db0ca8SKalle Valo static inline int valid_index(struct airo_info *ai, int index)
631230db0ca8SKalle Valo {
631330db0ca8SKalle Valo 	return (index >= 0) && (index <= ai->max_wep_idx);
631430db0ca8SKalle Valo }
631530db0ca8SKalle Valo 
631630db0ca8SKalle Valo /*------------------------------------------------------------------*/
631730db0ca8SKalle Valo /*
631830db0ca8SKalle Valo  * Wireless Handler : set Encryption Key
631930db0ca8SKalle Valo  */
632030db0ca8SKalle Valo static int airo_set_encode(struct net_device *dev,
632130db0ca8SKalle Valo 			   struct iw_request_info *info,
632230db0ca8SKalle Valo 			   struct iw_point *dwrq,
632330db0ca8SKalle Valo 			   char *extra)
632430db0ca8SKalle Valo {
632530db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
632630db0ca8SKalle Valo 	int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1);
632730db0ca8SKalle Valo 	__le16 currentAuthType = local->config.authType;
632830db0ca8SKalle Valo 	int rc = 0;
632930db0ca8SKalle Valo 
633030db0ca8SKalle Valo 	if (!local->wep_capable)
633130db0ca8SKalle Valo 		return -EOPNOTSUPP;
633230db0ca8SKalle Valo 
633330db0ca8SKalle Valo 	readConfigRid(local, 1);
633430db0ca8SKalle Valo 
633530db0ca8SKalle Valo 	/* Basic checking: do we have a key to set ?
633630db0ca8SKalle Valo 	 * Note : with the new API, it's impossible to get a NULL pointer.
633730db0ca8SKalle Valo 	 * Therefore, we need to check a key size == 0 instead.
633830db0ca8SKalle Valo 	 * New version of iwconfig properly set the IW_ENCODE_NOKEY flag
633930db0ca8SKalle Valo 	 * when no key is present (only change flags), but older versions
634030db0ca8SKalle Valo 	 * don't do it. - Jean II */
634130db0ca8SKalle Valo 	if (dwrq->length > 0) {
634230db0ca8SKalle Valo 		wep_key_t key;
634330db0ca8SKalle Valo 		int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
634430db0ca8SKalle Valo 		int current_index;
634530db0ca8SKalle Valo 
634630db0ca8SKalle Valo 		/* Check the size of the key */
634730db0ca8SKalle Valo 		if (dwrq->length > MAX_KEY_SIZE) {
634830db0ca8SKalle Valo 			return -EINVAL;
634930db0ca8SKalle Valo 		}
635030db0ca8SKalle Valo 
635130db0ca8SKalle Valo 		current_index = get_wep_tx_idx(local);
635230db0ca8SKalle Valo 		if (current_index < 0)
635330db0ca8SKalle Valo 			current_index = 0;
635430db0ca8SKalle Valo 
635530db0ca8SKalle Valo 		/* Check the index (none -> use current) */
635630db0ca8SKalle Valo 		if (!valid_index(local, index))
635730db0ca8SKalle Valo 			index = current_index;
635830db0ca8SKalle Valo 
635930db0ca8SKalle Valo 		/* Set the length */
636030db0ca8SKalle Valo 		if (dwrq->length > MIN_KEY_SIZE)
636130db0ca8SKalle Valo 			key.len = MAX_KEY_SIZE;
636230db0ca8SKalle Valo 		else
636330db0ca8SKalle Valo 			key.len = MIN_KEY_SIZE;
636430db0ca8SKalle Valo 		/* Check if the key is not marked as invalid */
636530db0ca8SKalle Valo 		if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
636630db0ca8SKalle Valo 			/* Cleanup */
636730db0ca8SKalle Valo 			memset(key.key, 0, MAX_KEY_SIZE);
636830db0ca8SKalle Valo 			/* Copy the key in the driver */
636930db0ca8SKalle Valo 			memcpy(key.key, extra, dwrq->length);
637030db0ca8SKalle Valo 			/* Send the key to the card */
637130db0ca8SKalle Valo 			rc = set_wep_key(local, index, key.key, key.len, perm, 1);
637230db0ca8SKalle Valo 			if (rc < 0) {
637330db0ca8SKalle Valo 				airo_print_err(local->dev->name, "failed to set"
637430db0ca8SKalle Valo 				               " WEP key at index %d: %d.",
637530db0ca8SKalle Valo 				               index, rc);
637630db0ca8SKalle Valo 				return rc;
637730db0ca8SKalle Valo 			}
637830db0ca8SKalle Valo 		}
637930db0ca8SKalle Valo 		/* WE specify that if a valid key is set, encryption
638030db0ca8SKalle Valo 		 * should be enabled (user may turn it off later)
638130db0ca8SKalle Valo 		 * This is also how "iwconfig ethX key on" works */
638230db0ca8SKalle Valo 		if ((index == current_index) && (key.len > 0) &&
638330db0ca8SKalle Valo 		   (local->config.authType == AUTH_OPEN))
638430db0ca8SKalle Valo 			set_auth_type(local, AUTH_ENCRYPT);
638530db0ca8SKalle Valo 	} else {
638630db0ca8SKalle Valo 		/* Do we want to just set the transmit key index ? */
638730db0ca8SKalle Valo 		int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
638830db0ca8SKalle Valo 		if (valid_index(local, index)) {
638930db0ca8SKalle Valo 			rc = set_wep_tx_idx(local, index, perm, 1);
639030db0ca8SKalle Valo 			if (rc < 0) {
639130db0ca8SKalle Valo 				airo_print_err(local->dev->name, "failed to set"
639230db0ca8SKalle Valo 				               " WEP transmit index to %d: %d.",
639330db0ca8SKalle Valo 				               index, rc);
639430db0ca8SKalle Valo 				return rc;
639530db0ca8SKalle Valo 			}
639630db0ca8SKalle Valo 		} else {
639730db0ca8SKalle Valo 			/* Don't complain if only change the mode */
639830db0ca8SKalle Valo 			if (!(dwrq->flags & IW_ENCODE_MODE))
639930db0ca8SKalle Valo 				return -EINVAL;
640030db0ca8SKalle Valo 		}
640130db0ca8SKalle Valo 	}
640230db0ca8SKalle Valo 	/* Read the flags */
640330db0ca8SKalle Valo 	if (dwrq->flags & IW_ENCODE_DISABLED)
640430db0ca8SKalle Valo 		set_auth_type(local, AUTH_OPEN);	/* disable encryption */
640530db0ca8SKalle Valo 	if (dwrq->flags & IW_ENCODE_RESTRICTED)
640630db0ca8SKalle Valo 		set_auth_type(local, AUTH_SHAREDKEY);	/* Only Both */
640730db0ca8SKalle Valo 	if (dwrq->flags & IW_ENCODE_OPEN)
640830db0ca8SKalle Valo 		set_auth_type(local, AUTH_ENCRYPT);	/* Only Wep */
640930db0ca8SKalle Valo 	/* Commit the changes to flags if needed */
641030db0ca8SKalle Valo 	if (local->config.authType != currentAuthType)
641130db0ca8SKalle Valo 		set_bit (FLAG_COMMIT, &local->flags);
641230db0ca8SKalle Valo 	return -EINPROGRESS;		/* Call commit handler */
641330db0ca8SKalle Valo }
641430db0ca8SKalle Valo 
641530db0ca8SKalle Valo /*------------------------------------------------------------------*/
641630db0ca8SKalle Valo /*
641730db0ca8SKalle Valo  * Wireless Handler : get Encryption Key
641830db0ca8SKalle Valo  */
641930db0ca8SKalle Valo static int airo_get_encode(struct net_device *dev,
642030db0ca8SKalle Valo 			   struct iw_request_info *info,
642130db0ca8SKalle Valo 			   struct iw_point *dwrq,
642230db0ca8SKalle Valo 			   char *extra)
642330db0ca8SKalle Valo {
642430db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
642530db0ca8SKalle Valo 	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
642630db0ca8SKalle Valo 	int wep_key_len;
642730db0ca8SKalle Valo 	u8 buf[16];
642830db0ca8SKalle Valo 
642930db0ca8SKalle Valo 	if (!local->wep_capable)
643030db0ca8SKalle Valo 		return -EOPNOTSUPP;
643130db0ca8SKalle Valo 
643230db0ca8SKalle Valo 	readConfigRid(local, 1);
643330db0ca8SKalle Valo 
643430db0ca8SKalle Valo 	/* Check encryption mode */
643530db0ca8SKalle Valo 	switch(local->config.authType)	{
643630db0ca8SKalle Valo 		case AUTH_ENCRYPT:
643730db0ca8SKalle Valo 			dwrq->flags = IW_ENCODE_OPEN;
643830db0ca8SKalle Valo 			break;
643930db0ca8SKalle Valo 		case AUTH_SHAREDKEY:
644030db0ca8SKalle Valo 			dwrq->flags = IW_ENCODE_RESTRICTED;
644130db0ca8SKalle Valo 			break;
644230db0ca8SKalle Valo 		default:
644330db0ca8SKalle Valo 		case AUTH_OPEN:
644430db0ca8SKalle Valo 			dwrq->flags = IW_ENCODE_DISABLED;
644530db0ca8SKalle Valo 			break;
644630db0ca8SKalle Valo 	}
644730db0ca8SKalle Valo 	/* We can't return the key, so set the proper flag and return zero */
644830db0ca8SKalle Valo 	dwrq->flags |= IW_ENCODE_NOKEY;
644930db0ca8SKalle Valo 	memset(extra, 0, 16);
645030db0ca8SKalle Valo 
645130db0ca8SKalle Valo 	/* Which key do we want ? -1 -> tx index */
645230db0ca8SKalle Valo 	if (!valid_index(local, index)) {
645330db0ca8SKalle Valo 		index = get_wep_tx_idx(local);
645430db0ca8SKalle Valo 		if (index < 0)
645530db0ca8SKalle Valo 			index = 0;
645630db0ca8SKalle Valo 	}
645730db0ca8SKalle Valo 	dwrq->flags |= index + 1;
645830db0ca8SKalle Valo 
645930db0ca8SKalle Valo 	/* Copy the key to the user buffer */
646030db0ca8SKalle Valo 	wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf));
646130db0ca8SKalle Valo 	if (wep_key_len < 0) {
646230db0ca8SKalle Valo 		dwrq->length = 0;
646330db0ca8SKalle Valo 	} else {
646430db0ca8SKalle Valo 		dwrq->length = wep_key_len;
646530db0ca8SKalle Valo 		memcpy(extra, buf, dwrq->length);
646630db0ca8SKalle Valo 	}
646730db0ca8SKalle Valo 
646830db0ca8SKalle Valo 	return 0;
646930db0ca8SKalle Valo }
647030db0ca8SKalle Valo 
647130db0ca8SKalle Valo /*------------------------------------------------------------------*/
647230db0ca8SKalle Valo /*
647330db0ca8SKalle Valo  * Wireless Handler : set extended Encryption parameters
647430db0ca8SKalle Valo  */
647530db0ca8SKalle Valo static int airo_set_encodeext(struct net_device *dev,
647630db0ca8SKalle Valo 			   struct iw_request_info *info,
647730db0ca8SKalle Valo 			    union iwreq_data *wrqu,
647830db0ca8SKalle Valo 			    char *extra)
647930db0ca8SKalle Valo {
648030db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
648130db0ca8SKalle Valo 	struct iw_point *encoding = &wrqu->encoding;
648230db0ca8SKalle Valo 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
648330db0ca8SKalle Valo 	int perm = (encoding->flags & IW_ENCODE_TEMP ? 0 : 1);
648430db0ca8SKalle Valo 	__le16 currentAuthType = local->config.authType;
648530db0ca8SKalle Valo 	int idx, key_len, alg = ext->alg, set_key = 1, rc;
648630db0ca8SKalle Valo 	wep_key_t key;
648730db0ca8SKalle Valo 
648830db0ca8SKalle Valo 	if (!local->wep_capable)
648930db0ca8SKalle Valo 		return -EOPNOTSUPP;
649030db0ca8SKalle Valo 
649130db0ca8SKalle Valo 	readConfigRid(local, 1);
649230db0ca8SKalle Valo 
649330db0ca8SKalle Valo 	/* Determine and validate the key index */
649430db0ca8SKalle Valo 	idx = encoding->flags & IW_ENCODE_INDEX;
649530db0ca8SKalle Valo 	if (idx) {
649630db0ca8SKalle Valo 		if (!valid_index(local, idx - 1))
649730db0ca8SKalle Valo 			return -EINVAL;
649830db0ca8SKalle Valo 		idx--;
649930db0ca8SKalle Valo 	} else {
650030db0ca8SKalle Valo 		idx = get_wep_tx_idx(local);
650130db0ca8SKalle Valo 		if (idx < 0)
650230db0ca8SKalle Valo 			idx = 0;
650330db0ca8SKalle Valo 	}
650430db0ca8SKalle Valo 
650530db0ca8SKalle Valo 	if (encoding->flags & IW_ENCODE_DISABLED)
650630db0ca8SKalle Valo 		alg = IW_ENCODE_ALG_NONE;
650730db0ca8SKalle Valo 
650830db0ca8SKalle Valo 	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
650930db0ca8SKalle Valo 		/* Only set transmit key index here, actual
651030db0ca8SKalle Valo 		 * key is set below if needed.
651130db0ca8SKalle Valo 		 */
651230db0ca8SKalle Valo 		rc = set_wep_tx_idx(local, idx, perm, 1);
651330db0ca8SKalle Valo 		if (rc < 0) {
651430db0ca8SKalle Valo 			airo_print_err(local->dev->name, "failed to set "
651530db0ca8SKalle Valo 			               "WEP transmit index to %d: %d.",
651630db0ca8SKalle Valo 			               idx, rc);
651730db0ca8SKalle Valo 			return rc;
651830db0ca8SKalle Valo 		}
651930db0ca8SKalle Valo 		set_key = ext->key_len > 0 ? 1 : 0;
652030db0ca8SKalle Valo 	}
652130db0ca8SKalle Valo 
652230db0ca8SKalle Valo 	if (set_key) {
652330db0ca8SKalle Valo 		/* Set the requested key first */
652430db0ca8SKalle Valo 		memset(key.key, 0, MAX_KEY_SIZE);
652530db0ca8SKalle Valo 		switch (alg) {
652630db0ca8SKalle Valo 		case IW_ENCODE_ALG_NONE:
652730db0ca8SKalle Valo 			key.len = 0;
652830db0ca8SKalle Valo 			break;
652930db0ca8SKalle Valo 		case IW_ENCODE_ALG_WEP:
653030db0ca8SKalle Valo 			if (ext->key_len > MIN_KEY_SIZE) {
653130db0ca8SKalle Valo 				key.len = MAX_KEY_SIZE;
653230db0ca8SKalle Valo 			} else if (ext->key_len > 0) {
653330db0ca8SKalle Valo 				key.len = MIN_KEY_SIZE;
653430db0ca8SKalle Valo 			} else {
653530db0ca8SKalle Valo 				return -EINVAL;
653630db0ca8SKalle Valo 			}
653730db0ca8SKalle Valo 			key_len = min (ext->key_len, key.len);
653830db0ca8SKalle Valo 			memcpy(key.key, ext->key, key_len);
653930db0ca8SKalle Valo 			break;
654030db0ca8SKalle Valo 		default:
654130db0ca8SKalle Valo 			return -EINVAL;
654230db0ca8SKalle Valo 		}
654330db0ca8SKalle Valo 		if (key.len == 0) {
654430db0ca8SKalle Valo 			rc = set_wep_tx_idx(local, idx, perm, 1);
654530db0ca8SKalle Valo 			if (rc < 0) {
654630db0ca8SKalle Valo 				airo_print_err(local->dev->name,
654730db0ca8SKalle Valo 					       "failed to set WEP transmit index to %d: %d.",
654830db0ca8SKalle Valo 					       idx, rc);
654930db0ca8SKalle Valo 				return rc;
655030db0ca8SKalle Valo 			}
655130db0ca8SKalle Valo 		} else {
655230db0ca8SKalle Valo 			rc = set_wep_key(local, idx, key.key, key.len, perm, 1);
655330db0ca8SKalle Valo 			if (rc < 0) {
655430db0ca8SKalle Valo 				airo_print_err(local->dev->name,
655530db0ca8SKalle Valo 					       "failed to set WEP key at index %d: %d.",
655630db0ca8SKalle Valo 					       idx, rc);
655730db0ca8SKalle Valo 				return rc;
655830db0ca8SKalle Valo 			}
655930db0ca8SKalle Valo 		}
656030db0ca8SKalle Valo 	}
656130db0ca8SKalle Valo 
656230db0ca8SKalle Valo 	/* Read the flags */
656330db0ca8SKalle Valo 	if (encoding->flags & IW_ENCODE_DISABLED)
656430db0ca8SKalle Valo 		set_auth_type(local, AUTH_OPEN);	/* disable encryption */
656530db0ca8SKalle Valo 	if (encoding->flags & IW_ENCODE_RESTRICTED)
656630db0ca8SKalle Valo 		set_auth_type(local, AUTH_SHAREDKEY);	/* Only Both */
656730db0ca8SKalle Valo 	if (encoding->flags & IW_ENCODE_OPEN)
656830db0ca8SKalle Valo 		set_auth_type(local, AUTH_ENCRYPT);
656930db0ca8SKalle Valo 	/* Commit the changes to flags if needed */
657030db0ca8SKalle Valo 	if (local->config.authType != currentAuthType)
657130db0ca8SKalle Valo 		set_bit (FLAG_COMMIT, &local->flags);
657230db0ca8SKalle Valo 
657330db0ca8SKalle Valo 	return -EINPROGRESS;
657430db0ca8SKalle Valo }
657530db0ca8SKalle Valo 
657630db0ca8SKalle Valo 
657730db0ca8SKalle Valo /*------------------------------------------------------------------*/
657830db0ca8SKalle Valo /*
657930db0ca8SKalle Valo  * Wireless Handler : get extended Encryption parameters
658030db0ca8SKalle Valo  */
658130db0ca8SKalle Valo static int airo_get_encodeext(struct net_device *dev,
658230db0ca8SKalle Valo 			    struct iw_request_info *info,
658330db0ca8SKalle Valo 			    union iwreq_data *wrqu,
658430db0ca8SKalle Valo 			    char *extra)
658530db0ca8SKalle Valo {
658630db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
658730db0ca8SKalle Valo 	struct iw_point *encoding = &wrqu->encoding;
658830db0ca8SKalle Valo 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
658930db0ca8SKalle Valo 	int idx, max_key_len, wep_key_len;
659030db0ca8SKalle Valo 	u8 buf[16];
659130db0ca8SKalle Valo 
659230db0ca8SKalle Valo 	if (!local->wep_capable)
659330db0ca8SKalle Valo 		return -EOPNOTSUPP;
659430db0ca8SKalle Valo 
659530db0ca8SKalle Valo 	readConfigRid(local, 1);
659630db0ca8SKalle Valo 
659730db0ca8SKalle Valo 	max_key_len = encoding->length - sizeof(*ext);
659830db0ca8SKalle Valo 	if (max_key_len < 0)
659930db0ca8SKalle Valo 		return -EINVAL;
660030db0ca8SKalle Valo 
660130db0ca8SKalle Valo 	idx = encoding->flags & IW_ENCODE_INDEX;
660230db0ca8SKalle Valo 	if (idx) {
660330db0ca8SKalle Valo 		if (!valid_index(local, idx - 1))
660430db0ca8SKalle Valo 			return -EINVAL;
660530db0ca8SKalle Valo 		idx--;
660630db0ca8SKalle Valo 	} else {
660730db0ca8SKalle Valo 		idx = get_wep_tx_idx(local);
660830db0ca8SKalle Valo 		if (idx < 0)
660930db0ca8SKalle Valo 			idx = 0;
661030db0ca8SKalle Valo 	}
661130db0ca8SKalle Valo 
661230db0ca8SKalle Valo 	encoding->flags = idx + 1;
661330db0ca8SKalle Valo 	memset(ext, 0, sizeof(*ext));
661430db0ca8SKalle Valo 
661530db0ca8SKalle Valo 	/* Check encryption mode */
661630db0ca8SKalle Valo 	switch(local->config.authType) {
661730db0ca8SKalle Valo 		case AUTH_ENCRYPT:
661830db0ca8SKalle Valo 			encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
661930db0ca8SKalle Valo 			break;
662030db0ca8SKalle Valo 		case AUTH_SHAREDKEY:
662130db0ca8SKalle Valo 			encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED;
662230db0ca8SKalle Valo 			break;
662330db0ca8SKalle Valo 		default:
662430db0ca8SKalle Valo 		case AUTH_OPEN:
662530db0ca8SKalle Valo 			encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED;
662630db0ca8SKalle Valo 			break;
662730db0ca8SKalle Valo 	}
662830db0ca8SKalle Valo 	/* We can't return the key, so set the proper flag and return zero */
662930db0ca8SKalle Valo 	encoding->flags |= IW_ENCODE_NOKEY;
663030db0ca8SKalle Valo 	memset(extra, 0, 16);
663130db0ca8SKalle Valo 
663230db0ca8SKalle Valo 	/* Copy the key to the user buffer */
663330db0ca8SKalle Valo 	wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf));
663430db0ca8SKalle Valo 	if (wep_key_len < 0) {
663530db0ca8SKalle Valo 		ext->key_len = 0;
663630db0ca8SKalle Valo 	} else {
663730db0ca8SKalle Valo 		ext->key_len = wep_key_len;
663830db0ca8SKalle Valo 		memcpy(extra, buf, ext->key_len);
663930db0ca8SKalle Valo 	}
664030db0ca8SKalle Valo 
664130db0ca8SKalle Valo 	return 0;
664230db0ca8SKalle Valo }
664330db0ca8SKalle Valo 
664430db0ca8SKalle Valo 
664530db0ca8SKalle Valo /*------------------------------------------------------------------*/
664630db0ca8SKalle Valo /*
664730db0ca8SKalle Valo  * Wireless Handler : set extended authentication parameters
664830db0ca8SKalle Valo  */
664930db0ca8SKalle Valo static int airo_set_auth(struct net_device *dev,
665030db0ca8SKalle Valo 			       struct iw_request_info *info,
665130db0ca8SKalle Valo 			       union iwreq_data *wrqu, char *extra)
665230db0ca8SKalle Valo {
665330db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
665430db0ca8SKalle Valo 	struct iw_param *param = &wrqu->param;
665530db0ca8SKalle Valo 	__le16 currentAuthType = local->config.authType;
665630db0ca8SKalle Valo 
665730db0ca8SKalle Valo 	switch (param->flags & IW_AUTH_INDEX) {
665830db0ca8SKalle Valo 	case IW_AUTH_WPA_VERSION:
665930db0ca8SKalle Valo 	case IW_AUTH_CIPHER_PAIRWISE:
666030db0ca8SKalle Valo 	case IW_AUTH_CIPHER_GROUP:
666130db0ca8SKalle Valo 	case IW_AUTH_KEY_MGMT:
666230db0ca8SKalle Valo 	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
666330db0ca8SKalle Valo 	case IW_AUTH_PRIVACY_INVOKED:
666430db0ca8SKalle Valo 		/*
666530db0ca8SKalle Valo 		 * airo does not use these parameters
666630db0ca8SKalle Valo 		 */
666730db0ca8SKalle Valo 		break;
666830db0ca8SKalle Valo 
666930db0ca8SKalle Valo 	case IW_AUTH_DROP_UNENCRYPTED:
667030db0ca8SKalle Valo 		if (param->value) {
667130db0ca8SKalle Valo 			/* Only change auth type if unencrypted */
667230db0ca8SKalle Valo 			if (currentAuthType == AUTH_OPEN)
667330db0ca8SKalle Valo 				set_auth_type(local, AUTH_ENCRYPT);
667430db0ca8SKalle Valo 		} else {
667530db0ca8SKalle Valo 			set_auth_type(local, AUTH_OPEN);
667630db0ca8SKalle Valo 		}
667730db0ca8SKalle Valo 
667830db0ca8SKalle Valo 		/* Commit the changes to flags if needed */
667930db0ca8SKalle Valo 		if (local->config.authType != currentAuthType)
668030db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &local->flags);
668130db0ca8SKalle Valo 		break;
668230db0ca8SKalle Valo 
668330db0ca8SKalle Valo 	case IW_AUTH_80211_AUTH_ALG: {
668430db0ca8SKalle Valo 			if (param->value & IW_AUTH_ALG_SHARED_KEY) {
668530db0ca8SKalle Valo 				set_auth_type(local, AUTH_SHAREDKEY);
668630db0ca8SKalle Valo 			} else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
668730db0ca8SKalle Valo 				/* We don't know here if WEP open system or
668830db0ca8SKalle Valo 				 * unencrypted mode was requested - so use the
668930db0ca8SKalle Valo 				 * last mode (of these two) used last time
669030db0ca8SKalle Valo 				 */
669130db0ca8SKalle Valo 				set_auth_type(local, local->last_auth);
669230db0ca8SKalle Valo 			} else
669330db0ca8SKalle Valo 				return -EINVAL;
669430db0ca8SKalle Valo 
669530db0ca8SKalle Valo 			/* Commit the changes to flags if needed */
669630db0ca8SKalle Valo 			if (local->config.authType != currentAuthType)
669730db0ca8SKalle Valo 				set_bit (FLAG_COMMIT, &local->flags);
669830db0ca8SKalle Valo 			break;
669930db0ca8SKalle Valo 		}
670030db0ca8SKalle Valo 
670130db0ca8SKalle Valo 	case IW_AUTH_WPA_ENABLED:
670230db0ca8SKalle Valo 		/* Silently accept disable of WPA */
670330db0ca8SKalle Valo 		if (param->value > 0)
670430db0ca8SKalle Valo 			return -EOPNOTSUPP;
670530db0ca8SKalle Valo 		break;
670630db0ca8SKalle Valo 
670730db0ca8SKalle Valo 	default:
670830db0ca8SKalle Valo 		return -EOPNOTSUPP;
670930db0ca8SKalle Valo 	}
671030db0ca8SKalle Valo 	return -EINPROGRESS;
671130db0ca8SKalle Valo }
671230db0ca8SKalle Valo 
671330db0ca8SKalle Valo 
671430db0ca8SKalle Valo /*------------------------------------------------------------------*/
671530db0ca8SKalle Valo /*
671630db0ca8SKalle Valo  * Wireless Handler : get extended authentication parameters
671730db0ca8SKalle Valo  */
671830db0ca8SKalle Valo static int airo_get_auth(struct net_device *dev,
671930db0ca8SKalle Valo 			       struct iw_request_info *info,
672030db0ca8SKalle Valo 			       union iwreq_data *wrqu, char *extra)
672130db0ca8SKalle Valo {
672230db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
672330db0ca8SKalle Valo 	struct iw_param *param = &wrqu->param;
672430db0ca8SKalle Valo 	__le16 currentAuthType = local->config.authType;
672530db0ca8SKalle Valo 
672630db0ca8SKalle Valo 	switch (param->flags & IW_AUTH_INDEX) {
672730db0ca8SKalle Valo 	case IW_AUTH_DROP_UNENCRYPTED:
672830db0ca8SKalle Valo 		switch (currentAuthType) {
672930db0ca8SKalle Valo 		case AUTH_SHAREDKEY:
673030db0ca8SKalle Valo 		case AUTH_ENCRYPT:
673130db0ca8SKalle Valo 			param->value = 1;
673230db0ca8SKalle Valo 			break;
673330db0ca8SKalle Valo 		default:
673430db0ca8SKalle Valo 			param->value = 0;
673530db0ca8SKalle Valo 			break;
673630db0ca8SKalle Valo 		}
673730db0ca8SKalle Valo 		break;
673830db0ca8SKalle Valo 
673930db0ca8SKalle Valo 	case IW_AUTH_80211_AUTH_ALG:
674030db0ca8SKalle Valo 		switch (currentAuthType) {
674130db0ca8SKalle Valo 		case AUTH_SHAREDKEY:
674230db0ca8SKalle Valo 			param->value = IW_AUTH_ALG_SHARED_KEY;
674330db0ca8SKalle Valo 			break;
674430db0ca8SKalle Valo 		case AUTH_ENCRYPT:
674530db0ca8SKalle Valo 		default:
674630db0ca8SKalle Valo 			param->value = IW_AUTH_ALG_OPEN_SYSTEM;
674730db0ca8SKalle Valo 			break;
674830db0ca8SKalle Valo 		}
674930db0ca8SKalle Valo 		break;
675030db0ca8SKalle Valo 
675130db0ca8SKalle Valo 	case IW_AUTH_WPA_ENABLED:
675230db0ca8SKalle Valo 		param->value = 0;
675330db0ca8SKalle Valo 		break;
675430db0ca8SKalle Valo 
675530db0ca8SKalle Valo 	default:
675630db0ca8SKalle Valo 		return -EOPNOTSUPP;
675730db0ca8SKalle Valo 	}
675830db0ca8SKalle Valo 	return 0;
675930db0ca8SKalle Valo }
676030db0ca8SKalle Valo 
676130db0ca8SKalle Valo 
676230db0ca8SKalle Valo /*------------------------------------------------------------------*/
676330db0ca8SKalle Valo /*
676430db0ca8SKalle Valo  * Wireless Handler : set Tx-Power
676530db0ca8SKalle Valo  */
676630db0ca8SKalle Valo static int airo_set_txpow(struct net_device *dev,
676730db0ca8SKalle Valo 			  struct iw_request_info *info,
676830db0ca8SKalle Valo 			  struct iw_param *vwrq,
676930db0ca8SKalle Valo 			  char *extra)
677030db0ca8SKalle Valo {
677130db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
677230db0ca8SKalle Valo 	CapabilityRid cap_rid;		/* Card capability info */
677330db0ca8SKalle Valo 	int i;
677430db0ca8SKalle Valo 	int rc = -EINVAL;
677530db0ca8SKalle Valo 	__le16 v = cpu_to_le16(vwrq->value);
677630db0ca8SKalle Valo 
677730db0ca8SKalle Valo 	readCapabilityRid(local, &cap_rid, 1);
677830db0ca8SKalle Valo 
677930db0ca8SKalle Valo 	if (vwrq->disabled) {
678030db0ca8SKalle Valo 		set_bit (FLAG_RADIO_OFF, &local->flags);
678130db0ca8SKalle Valo 		set_bit (FLAG_COMMIT, &local->flags);
678230db0ca8SKalle Valo 		return -EINPROGRESS;		/* Call commit handler */
678330db0ca8SKalle Valo 	}
678430db0ca8SKalle Valo 	if (vwrq->flags != IW_TXPOW_MWATT) {
678530db0ca8SKalle Valo 		return -EINVAL;
678630db0ca8SKalle Valo 	}
678730db0ca8SKalle Valo 	clear_bit (FLAG_RADIO_OFF, &local->flags);
678830db0ca8SKalle Valo 	for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++)
678930db0ca8SKalle Valo 		if (v == cap_rid.txPowerLevels[i]) {
679030db0ca8SKalle Valo 			readConfigRid(local, 1);
679130db0ca8SKalle Valo 			local->config.txPower = v;
679230db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &local->flags);
679330db0ca8SKalle Valo 			rc = -EINPROGRESS;	/* Call commit handler */
679430db0ca8SKalle Valo 			break;
679530db0ca8SKalle Valo 		}
679630db0ca8SKalle Valo 	return rc;
679730db0ca8SKalle Valo }
679830db0ca8SKalle Valo 
679930db0ca8SKalle Valo /*------------------------------------------------------------------*/
680030db0ca8SKalle Valo /*
680130db0ca8SKalle Valo  * Wireless Handler : get Tx-Power
680230db0ca8SKalle Valo  */
680330db0ca8SKalle Valo static int airo_get_txpow(struct net_device *dev,
680430db0ca8SKalle Valo 			  struct iw_request_info *info,
680530db0ca8SKalle Valo 			  struct iw_param *vwrq,
680630db0ca8SKalle Valo 			  char *extra)
680730db0ca8SKalle Valo {
680830db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
680930db0ca8SKalle Valo 
681030db0ca8SKalle Valo 	readConfigRid(local, 1);
681130db0ca8SKalle Valo 	vwrq->value = le16_to_cpu(local->config.txPower);
681230db0ca8SKalle Valo 	vwrq->fixed = 1;	/* No power control */
681330db0ca8SKalle Valo 	vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags);
681430db0ca8SKalle Valo 	vwrq->flags = IW_TXPOW_MWATT;
681530db0ca8SKalle Valo 
681630db0ca8SKalle Valo 	return 0;
681730db0ca8SKalle Valo }
681830db0ca8SKalle Valo 
681930db0ca8SKalle Valo /*------------------------------------------------------------------*/
682030db0ca8SKalle Valo /*
682130db0ca8SKalle Valo  * Wireless Handler : set Retry limits
682230db0ca8SKalle Valo  */
682330db0ca8SKalle Valo static int airo_set_retry(struct net_device *dev,
682430db0ca8SKalle Valo 			  struct iw_request_info *info,
682530db0ca8SKalle Valo 			  struct iw_param *vwrq,
682630db0ca8SKalle Valo 			  char *extra)
682730db0ca8SKalle Valo {
682830db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
682930db0ca8SKalle Valo 	int rc = -EINVAL;
683030db0ca8SKalle Valo 
683130db0ca8SKalle Valo 	if (vwrq->disabled) {
683230db0ca8SKalle Valo 		return -EINVAL;
683330db0ca8SKalle Valo 	}
683430db0ca8SKalle Valo 	readConfigRid(local, 1);
683530db0ca8SKalle Valo 	if (vwrq->flags & IW_RETRY_LIMIT) {
683630db0ca8SKalle Valo 		__le16 v = cpu_to_le16(vwrq->value);
683730db0ca8SKalle Valo 		if (vwrq->flags & IW_RETRY_LONG)
683830db0ca8SKalle Valo 			local->config.longRetryLimit = v;
683930db0ca8SKalle Valo 		else if (vwrq->flags & IW_RETRY_SHORT)
684030db0ca8SKalle Valo 			local->config.shortRetryLimit = v;
684130db0ca8SKalle Valo 		else {
684230db0ca8SKalle Valo 			/* No modifier : set both */
684330db0ca8SKalle Valo 			local->config.longRetryLimit = v;
684430db0ca8SKalle Valo 			local->config.shortRetryLimit = v;
684530db0ca8SKalle Valo 		}
684630db0ca8SKalle Valo 		set_bit (FLAG_COMMIT, &local->flags);
684730db0ca8SKalle Valo 		rc = -EINPROGRESS;		/* Call commit handler */
684830db0ca8SKalle Valo 	}
684930db0ca8SKalle Valo 	if (vwrq->flags & IW_RETRY_LIFETIME) {
685030db0ca8SKalle Valo 		local->config.txLifetime = cpu_to_le16(vwrq->value / 1024);
685130db0ca8SKalle Valo 		set_bit (FLAG_COMMIT, &local->flags);
685230db0ca8SKalle Valo 		rc = -EINPROGRESS;		/* Call commit handler */
685330db0ca8SKalle Valo 	}
685430db0ca8SKalle Valo 	return rc;
685530db0ca8SKalle Valo }
685630db0ca8SKalle Valo 
685730db0ca8SKalle Valo /*------------------------------------------------------------------*/
685830db0ca8SKalle Valo /*
685930db0ca8SKalle Valo  * Wireless Handler : get Retry limits
686030db0ca8SKalle Valo  */
686130db0ca8SKalle Valo static int airo_get_retry(struct net_device *dev,
686230db0ca8SKalle Valo 			  struct iw_request_info *info,
686330db0ca8SKalle Valo 			  struct iw_param *vwrq,
686430db0ca8SKalle Valo 			  char *extra)
686530db0ca8SKalle Valo {
686630db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
686730db0ca8SKalle Valo 
686830db0ca8SKalle Valo 	vwrq->disabled = 0;      /* Can't be disabled */
686930db0ca8SKalle Valo 
687030db0ca8SKalle Valo 	readConfigRid(local, 1);
687130db0ca8SKalle Valo 	/* Note : by default, display the min retry number */
687230db0ca8SKalle Valo 	if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
687330db0ca8SKalle Valo 		vwrq->flags = IW_RETRY_LIFETIME;
687430db0ca8SKalle Valo 		vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024;
687530db0ca8SKalle Valo 	} else if ((vwrq->flags & IW_RETRY_LONG)) {
687630db0ca8SKalle Valo 		vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
687730db0ca8SKalle Valo 		vwrq->value = le16_to_cpu(local->config.longRetryLimit);
687830db0ca8SKalle Valo 	} else {
687930db0ca8SKalle Valo 		vwrq->flags = IW_RETRY_LIMIT;
688030db0ca8SKalle Valo 		vwrq->value = le16_to_cpu(local->config.shortRetryLimit);
688130db0ca8SKalle Valo 		if (local->config.shortRetryLimit != local->config.longRetryLimit)
688230db0ca8SKalle Valo 			vwrq->flags |= IW_RETRY_SHORT;
688330db0ca8SKalle Valo 	}
688430db0ca8SKalle Valo 
688530db0ca8SKalle Valo 	return 0;
688630db0ca8SKalle Valo }
688730db0ca8SKalle Valo 
688830db0ca8SKalle Valo /*------------------------------------------------------------------*/
688930db0ca8SKalle Valo /*
689030db0ca8SKalle Valo  * Wireless Handler : get range info
689130db0ca8SKalle Valo  */
689230db0ca8SKalle Valo static int airo_get_range(struct net_device *dev,
689330db0ca8SKalle Valo 			  struct iw_request_info *info,
689430db0ca8SKalle Valo 			  struct iw_point *dwrq,
689530db0ca8SKalle Valo 			  char *extra)
689630db0ca8SKalle Valo {
689730db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
689830db0ca8SKalle Valo 	struct iw_range *range = (struct iw_range *) extra;
689930db0ca8SKalle Valo 	CapabilityRid cap_rid;		/* Card capability info */
690030db0ca8SKalle Valo 	int		i;
690130db0ca8SKalle Valo 	int		k;
690230db0ca8SKalle Valo 
690330db0ca8SKalle Valo 	readCapabilityRid(local, &cap_rid, 1);
690430db0ca8SKalle Valo 
690530db0ca8SKalle Valo 	dwrq->length = sizeof(struct iw_range);
690630db0ca8SKalle Valo 	memset(range, 0, sizeof(*range));
690730db0ca8SKalle Valo 	range->min_nwid = 0x0000;
690830db0ca8SKalle Valo 	range->max_nwid = 0x0000;
690930db0ca8SKalle Valo 	range->num_channels = 14;
691030db0ca8SKalle Valo 	/* Should be based on cap_rid.country to give only
691130db0ca8SKalle Valo 	 * what the current card support */
691230db0ca8SKalle Valo 	k = 0;
691330db0ca8SKalle Valo 	for (i = 0; i < 14; i++) {
691430db0ca8SKalle Valo 		range->freq[k].i = i + 1; /* List index */
691530db0ca8SKalle Valo 		range->freq[k].m = 100000 *
691657fbcce3SJohannes Berg 		     ieee80211_channel_to_frequency(i + 1, NL80211_BAND_2GHZ);
691730db0ca8SKalle Valo 		range->freq[k++].e = 1;	/* Values in MHz -> * 10^5 * 10 */
691830db0ca8SKalle Valo 	}
691930db0ca8SKalle Valo 	range->num_frequency = k;
692030db0ca8SKalle Valo 
692130db0ca8SKalle Valo 	range->sensitivity = 65535;
692230db0ca8SKalle Valo 
692330db0ca8SKalle Valo 	/* Hum... Should put the right values there */
692430db0ca8SKalle Valo 	if (local->rssi)
692530db0ca8SKalle Valo 		range->max_qual.qual = 100;	/* % */
692630db0ca8SKalle Valo 	else
692730db0ca8SKalle Valo 		range->max_qual.qual = airo_get_max_quality(&cap_rid);
692830db0ca8SKalle Valo 	range->max_qual.level = 0x100 - 120;	/* -120 dBm */
692930db0ca8SKalle Valo 	range->max_qual.noise = 0x100 - 120;	/* -120 dBm */
693030db0ca8SKalle Valo 
693130db0ca8SKalle Valo 	/* Experimental measurements - boundary 11/5.5 Mb/s */
693230db0ca8SKalle Valo 	/* Note : with or without the (local->rssi), results
693330db0ca8SKalle Valo 	 * are somewhat different. - Jean II */
693430db0ca8SKalle Valo 	if (local->rssi) {
693530db0ca8SKalle Valo 		range->avg_qual.qual = 50;		/* % */
693630db0ca8SKalle Valo 		range->avg_qual.level = 0x100 - 70;	/* -70 dBm */
693730db0ca8SKalle Valo 	} else {
693830db0ca8SKalle Valo 		range->avg_qual.qual = airo_get_avg_quality(&cap_rid);
693930db0ca8SKalle Valo 		range->avg_qual.level = 0x100 - 80;	/* -80 dBm */
694030db0ca8SKalle Valo 	}
694130db0ca8SKalle Valo 	range->avg_qual.noise = 0x100 - 85;		/* -85 dBm */
694230db0ca8SKalle Valo 
694330db0ca8SKalle Valo 	for (i = 0 ; i < 8 ; i++) {
694430db0ca8SKalle Valo 		range->bitrate[i] = cap_rid.supportedRates[i] * 500000;
694530db0ca8SKalle Valo 		if (range->bitrate[i] == 0)
694630db0ca8SKalle Valo 			break;
694730db0ca8SKalle Valo 	}
694830db0ca8SKalle Valo 	range->num_bitrates = i;
694930db0ca8SKalle Valo 
695030db0ca8SKalle Valo 	/* Set an indication of the max TCP throughput
695130db0ca8SKalle Valo 	 * in bit/s that we can expect using this interface.
695230db0ca8SKalle Valo 	 * May be use for QoS stuff... Jean II */
695330db0ca8SKalle Valo 	if (i > 2)
695430db0ca8SKalle Valo 		range->throughput = 5000 * 1000;
695530db0ca8SKalle Valo 	else
695630db0ca8SKalle Valo 		range->throughput = 1500 * 1000;
695730db0ca8SKalle Valo 
695830db0ca8SKalle Valo 	range->min_rts = 0;
695930db0ca8SKalle Valo 	range->max_rts = AIRO_DEF_MTU;
696030db0ca8SKalle Valo 	range->min_frag = 256;
696130db0ca8SKalle Valo 	range->max_frag = AIRO_DEF_MTU;
696230db0ca8SKalle Valo 
696330db0ca8SKalle Valo 	if (cap_rid.softCap & cpu_to_le16(2)) {
696430db0ca8SKalle Valo 		// WEP: RC4 40 bits
696530db0ca8SKalle Valo 		range->encoding_size[0] = 5;
696630db0ca8SKalle Valo 		// RC4 ~128 bits
696730db0ca8SKalle Valo 		if (cap_rid.softCap & cpu_to_le16(0x100)) {
696830db0ca8SKalle Valo 			range->encoding_size[1] = 13;
696930db0ca8SKalle Valo 			range->num_encoding_sizes = 2;
697030db0ca8SKalle Valo 		} else
697130db0ca8SKalle Valo 			range->num_encoding_sizes = 1;
697230db0ca8SKalle Valo 		range->max_encoding_tokens =
697330db0ca8SKalle Valo 			cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1;
697430db0ca8SKalle Valo 	} else {
697530db0ca8SKalle Valo 		range->num_encoding_sizes = 0;
697630db0ca8SKalle Valo 		range->max_encoding_tokens = 0;
697730db0ca8SKalle Valo 	}
697830db0ca8SKalle Valo 	range->min_pmp = 0;
697930db0ca8SKalle Valo 	range->max_pmp = 5000000;	/* 5 secs */
698030db0ca8SKalle Valo 	range->min_pmt = 0;
698130db0ca8SKalle Valo 	range->max_pmt = 65535 * 1024;	/* ??? */
698230db0ca8SKalle Valo 	range->pmp_flags = IW_POWER_PERIOD;
698330db0ca8SKalle Valo 	range->pmt_flags = IW_POWER_TIMEOUT;
698430db0ca8SKalle Valo 	range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
698530db0ca8SKalle Valo 
698630db0ca8SKalle Valo 	/* Transmit Power - values are in mW */
698730db0ca8SKalle Valo 	for (i = 0 ; i < 8 ; i++) {
698830db0ca8SKalle Valo 		range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]);
698930db0ca8SKalle Valo 		if (range->txpower[i] == 0)
699030db0ca8SKalle Valo 			break;
699130db0ca8SKalle Valo 	}
699230db0ca8SKalle Valo 	range->num_txpower = i;
699330db0ca8SKalle Valo 	range->txpower_capa = IW_TXPOW_MWATT;
699430db0ca8SKalle Valo 	range->we_version_source = 19;
699530db0ca8SKalle Valo 	range->we_version_compiled = WIRELESS_EXT;
699630db0ca8SKalle Valo 	range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
699730db0ca8SKalle Valo 	range->retry_flags = IW_RETRY_LIMIT;
699830db0ca8SKalle Valo 	range->r_time_flags = IW_RETRY_LIFETIME;
699930db0ca8SKalle Valo 	range->min_retry = 1;
700030db0ca8SKalle Valo 	range->max_retry = 65535;
700130db0ca8SKalle Valo 	range->min_r_time = 1024;
700230db0ca8SKalle Valo 	range->max_r_time = 65535 * 1024;
700330db0ca8SKalle Valo 
700430db0ca8SKalle Valo 	/* Event capability (kernel + driver) */
700530db0ca8SKalle Valo 	range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
700630db0ca8SKalle Valo 				IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
700730db0ca8SKalle Valo 				IW_EVENT_CAPA_MASK(SIOCGIWAP) |
700830db0ca8SKalle Valo 				IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
700930db0ca8SKalle Valo 	range->event_capa[1] = IW_EVENT_CAPA_K_1;
701030db0ca8SKalle Valo 	range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP);
701130db0ca8SKalle Valo 	return 0;
701230db0ca8SKalle Valo }
701330db0ca8SKalle Valo 
701430db0ca8SKalle Valo /*------------------------------------------------------------------*/
701530db0ca8SKalle Valo /*
701630db0ca8SKalle Valo  * Wireless Handler : set Power Management
701730db0ca8SKalle Valo  */
701830db0ca8SKalle Valo static int airo_set_power(struct net_device *dev,
701930db0ca8SKalle Valo 			  struct iw_request_info *info,
702030db0ca8SKalle Valo 			  struct iw_param *vwrq,
702130db0ca8SKalle Valo 			  char *extra)
702230db0ca8SKalle Valo {
702330db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
702430db0ca8SKalle Valo 
702530db0ca8SKalle Valo 	readConfigRid(local, 1);
702630db0ca8SKalle Valo 	if (vwrq->disabled) {
702730db0ca8SKalle Valo 		if (sniffing_mode(local))
702830db0ca8SKalle Valo 			return -EINVAL;
702930db0ca8SKalle Valo 		local->config.powerSaveMode = POWERSAVE_CAM;
703030db0ca8SKalle Valo 		local->config.rmode &= ~RXMODE_MASK;
703130db0ca8SKalle Valo 		local->config.rmode |= RXMODE_BC_MC_ADDR;
703230db0ca8SKalle Valo 		set_bit (FLAG_COMMIT, &local->flags);
703330db0ca8SKalle Valo 		return -EINPROGRESS;		/* Call commit handler */
703430db0ca8SKalle Valo 	}
703530db0ca8SKalle Valo 	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
703630db0ca8SKalle Valo 		local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024);
703730db0ca8SKalle Valo 		local->config.powerSaveMode = POWERSAVE_PSPCAM;
703830db0ca8SKalle Valo 		set_bit (FLAG_COMMIT, &local->flags);
703930db0ca8SKalle Valo 	} else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
704030db0ca8SKalle Valo 		local->config.fastListenInterval =
704130db0ca8SKalle Valo 		local->config.listenInterval =
704230db0ca8SKalle Valo 			cpu_to_le16((vwrq->value + 500) / 1024);
704330db0ca8SKalle Valo 		local->config.powerSaveMode = POWERSAVE_PSPCAM;
704430db0ca8SKalle Valo 		set_bit (FLAG_COMMIT, &local->flags);
704530db0ca8SKalle Valo 	}
704630db0ca8SKalle Valo 	switch (vwrq->flags & IW_POWER_MODE) {
704730db0ca8SKalle Valo 		case IW_POWER_UNICAST_R:
704830db0ca8SKalle Valo 			if (sniffing_mode(local))
704930db0ca8SKalle Valo 				return -EINVAL;
705030db0ca8SKalle Valo 			local->config.rmode &= ~RXMODE_MASK;
705130db0ca8SKalle Valo 			local->config.rmode |= RXMODE_ADDR;
705230db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &local->flags);
705330db0ca8SKalle Valo 			break;
705430db0ca8SKalle Valo 		case IW_POWER_ALL_R:
705530db0ca8SKalle Valo 			if (sniffing_mode(local))
705630db0ca8SKalle Valo 				return -EINVAL;
705730db0ca8SKalle Valo 			local->config.rmode &= ~RXMODE_MASK;
705830db0ca8SKalle Valo 			local->config.rmode |= RXMODE_BC_MC_ADDR;
705930db0ca8SKalle Valo 			set_bit (FLAG_COMMIT, &local->flags);
706030db0ca8SKalle Valo 		case IW_POWER_ON:
706130db0ca8SKalle Valo 			/* This is broken, fixme ;-) */
706230db0ca8SKalle Valo 			break;
706330db0ca8SKalle Valo 		default:
706430db0ca8SKalle Valo 			return -EINVAL;
706530db0ca8SKalle Valo 	}
706630db0ca8SKalle Valo 	// Note : we may want to factor local->need_commit here
706730db0ca8SKalle Valo 	// Note2 : may also want to factor RXMODE_RFMON test
706830db0ca8SKalle Valo 	return -EINPROGRESS;		/* Call commit handler */
706930db0ca8SKalle Valo }
707030db0ca8SKalle Valo 
707130db0ca8SKalle Valo /*------------------------------------------------------------------*/
707230db0ca8SKalle Valo /*
707330db0ca8SKalle Valo  * Wireless Handler : get Power Management
707430db0ca8SKalle Valo  */
707530db0ca8SKalle Valo static int airo_get_power(struct net_device *dev,
707630db0ca8SKalle Valo 			  struct iw_request_info *info,
707730db0ca8SKalle Valo 			  struct iw_param *vwrq,
707830db0ca8SKalle Valo 			  char *extra)
707930db0ca8SKalle Valo {
708030db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
708130db0ca8SKalle Valo 	__le16 mode;
708230db0ca8SKalle Valo 
708330db0ca8SKalle Valo 	readConfigRid(local, 1);
708430db0ca8SKalle Valo 	mode = local->config.powerSaveMode;
708530db0ca8SKalle Valo 	if ((vwrq->disabled = (mode == POWERSAVE_CAM)))
708630db0ca8SKalle Valo 		return 0;
708730db0ca8SKalle Valo 	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
708830db0ca8SKalle Valo 		vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024;
708930db0ca8SKalle Valo 		vwrq->flags = IW_POWER_TIMEOUT;
709030db0ca8SKalle Valo 	} else {
709130db0ca8SKalle Valo 		vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024;
709230db0ca8SKalle Valo 		vwrq->flags = IW_POWER_PERIOD;
709330db0ca8SKalle Valo 	}
709430db0ca8SKalle Valo 	if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR)
709530db0ca8SKalle Valo 		vwrq->flags |= IW_POWER_UNICAST_R;
709630db0ca8SKalle Valo 	else
709730db0ca8SKalle Valo 		vwrq->flags |= IW_POWER_ALL_R;
709830db0ca8SKalle Valo 
709930db0ca8SKalle Valo 	return 0;
710030db0ca8SKalle Valo }
710130db0ca8SKalle Valo 
710230db0ca8SKalle Valo /*------------------------------------------------------------------*/
710330db0ca8SKalle Valo /*
710430db0ca8SKalle Valo  * Wireless Handler : set Sensitivity
710530db0ca8SKalle Valo  */
710630db0ca8SKalle Valo static int airo_set_sens(struct net_device *dev,
710730db0ca8SKalle Valo 			 struct iw_request_info *info,
710830db0ca8SKalle Valo 			 struct iw_param *vwrq,
710930db0ca8SKalle Valo 			 char *extra)
711030db0ca8SKalle Valo {
711130db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
711230db0ca8SKalle Valo 
711330db0ca8SKalle Valo 	readConfigRid(local, 1);
711430db0ca8SKalle Valo 	local->config.rssiThreshold =
711530db0ca8SKalle Valo 		cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value);
711630db0ca8SKalle Valo 	set_bit (FLAG_COMMIT, &local->flags);
711730db0ca8SKalle Valo 
711830db0ca8SKalle Valo 	return -EINPROGRESS;		/* Call commit handler */
711930db0ca8SKalle Valo }
712030db0ca8SKalle Valo 
712130db0ca8SKalle Valo /*------------------------------------------------------------------*/
712230db0ca8SKalle Valo /*
712330db0ca8SKalle Valo  * Wireless Handler : get Sensitivity
712430db0ca8SKalle Valo  */
712530db0ca8SKalle Valo static int airo_get_sens(struct net_device *dev,
712630db0ca8SKalle Valo 			 struct iw_request_info *info,
712730db0ca8SKalle Valo 			 struct iw_param *vwrq,
712830db0ca8SKalle Valo 			 char *extra)
712930db0ca8SKalle Valo {
713030db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
713130db0ca8SKalle Valo 
713230db0ca8SKalle Valo 	readConfigRid(local, 1);
713330db0ca8SKalle Valo 	vwrq->value = le16_to_cpu(local->config.rssiThreshold);
713430db0ca8SKalle Valo 	vwrq->disabled = (vwrq->value == 0);
713530db0ca8SKalle Valo 	vwrq->fixed = 1;
713630db0ca8SKalle Valo 
713730db0ca8SKalle Valo 	return 0;
713830db0ca8SKalle Valo }
713930db0ca8SKalle Valo 
714030db0ca8SKalle Valo /*------------------------------------------------------------------*/
714130db0ca8SKalle Valo /*
714230db0ca8SKalle Valo  * Wireless Handler : get AP List
714330db0ca8SKalle Valo  * Note : this is deprecated in favor of IWSCAN
714430db0ca8SKalle Valo  */
714530db0ca8SKalle Valo static int airo_get_aplist(struct net_device *dev,
714630db0ca8SKalle Valo 			   struct iw_request_info *info,
714730db0ca8SKalle Valo 			   struct iw_point *dwrq,
714830db0ca8SKalle Valo 			   char *extra)
714930db0ca8SKalle Valo {
715030db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
715130db0ca8SKalle Valo 	struct sockaddr *address = (struct sockaddr *) extra;
715230db0ca8SKalle Valo 	struct iw_quality *qual;
715330db0ca8SKalle Valo 	BSSListRid BSSList;
715430db0ca8SKalle Valo 	int i;
715530db0ca8SKalle Valo 	int loseSync = capable(CAP_NET_ADMIN) ? 1: -1;
715630db0ca8SKalle Valo 
71576da2ec56SKees Cook 	qual = kmalloc_array(IW_MAX_AP, sizeof(*qual), GFP_KERNEL);
715830db0ca8SKalle Valo 	if (!qual)
715930db0ca8SKalle Valo 		return -ENOMEM;
716030db0ca8SKalle Valo 
716130db0ca8SKalle Valo 	for (i = 0; i < IW_MAX_AP; i++) {
716230db0ca8SKalle Valo 		u16 dBm;
716330db0ca8SKalle Valo 		if (readBSSListRid(local, loseSync, &BSSList))
716430db0ca8SKalle Valo 			break;
716530db0ca8SKalle Valo 		loseSync = 0;
716630db0ca8SKalle Valo 		memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN);
716730db0ca8SKalle Valo 		address[i].sa_family = ARPHRD_ETHER;
716830db0ca8SKalle Valo 		dBm = le16_to_cpu(BSSList.dBm);
716930db0ca8SKalle Valo 		if (local->rssi) {
717030db0ca8SKalle Valo 			qual[i].level = 0x100 - dBm;
717130db0ca8SKalle Valo 			qual[i].qual = airo_dbm_to_pct(local->rssi, dBm);
717230db0ca8SKalle Valo 			qual[i].updated = IW_QUAL_QUAL_UPDATED
717330db0ca8SKalle Valo 					| IW_QUAL_LEVEL_UPDATED
717430db0ca8SKalle Valo 					| IW_QUAL_DBM;
717530db0ca8SKalle Valo 		} else {
717630db0ca8SKalle Valo 			qual[i].level = (dBm + 321) / 2;
717730db0ca8SKalle Valo 			qual[i].qual = 0;
717830db0ca8SKalle Valo 			qual[i].updated = IW_QUAL_QUAL_INVALID
717930db0ca8SKalle Valo 					| IW_QUAL_LEVEL_UPDATED
718030db0ca8SKalle Valo 					| IW_QUAL_DBM;
718130db0ca8SKalle Valo 		}
718230db0ca8SKalle Valo 		qual[i].noise = local->wstats.qual.noise;
718330db0ca8SKalle Valo 		if (BSSList.index == cpu_to_le16(0xffff))
718430db0ca8SKalle Valo 			break;
718530db0ca8SKalle Valo 	}
718630db0ca8SKalle Valo 	if (!i) {
718730db0ca8SKalle Valo 		StatusRid status_rid;		/* Card status info */
718830db0ca8SKalle Valo 		readStatusRid(local, &status_rid, 1);
718930db0ca8SKalle Valo 		for (i = 0;
719030db0ca8SKalle Valo 		     i < min(IW_MAX_AP, 4) &&
719130db0ca8SKalle Valo 			     (status_rid.bssid[i][0]
719230db0ca8SKalle Valo 			      & status_rid.bssid[i][1]
719330db0ca8SKalle Valo 			      & status_rid.bssid[i][2]
719430db0ca8SKalle Valo 			      & status_rid.bssid[i][3]
719530db0ca8SKalle Valo 			      & status_rid.bssid[i][4]
719630db0ca8SKalle Valo 			      & status_rid.bssid[i][5])!=0xff &&
719730db0ca8SKalle Valo 			     (status_rid.bssid[i][0]
719830db0ca8SKalle Valo 			      | status_rid.bssid[i][1]
719930db0ca8SKalle Valo 			      | status_rid.bssid[i][2]
720030db0ca8SKalle Valo 			      | status_rid.bssid[i][3]
720130db0ca8SKalle Valo 			      | status_rid.bssid[i][4]
720230db0ca8SKalle Valo 			      | status_rid.bssid[i][5]);
720330db0ca8SKalle Valo 		     i++) {
720430db0ca8SKalle Valo 			memcpy(address[i].sa_data,
720530db0ca8SKalle Valo 			       status_rid.bssid[i], ETH_ALEN);
720630db0ca8SKalle Valo 			address[i].sa_family = ARPHRD_ETHER;
720730db0ca8SKalle Valo 		}
720830db0ca8SKalle Valo 	} else {
720930db0ca8SKalle Valo 		dwrq->flags = 1; /* Should be define'd */
721030db0ca8SKalle Valo 		memcpy(extra + sizeof(struct sockaddr) * i, qual,
721130db0ca8SKalle Valo 		       sizeof(struct iw_quality) * i);
721230db0ca8SKalle Valo 	}
721330db0ca8SKalle Valo 	dwrq->length = i;
721430db0ca8SKalle Valo 
721530db0ca8SKalle Valo 	kfree(qual);
721630db0ca8SKalle Valo 	return 0;
721730db0ca8SKalle Valo }
721830db0ca8SKalle Valo 
721930db0ca8SKalle Valo /*------------------------------------------------------------------*/
722030db0ca8SKalle Valo /*
722130db0ca8SKalle Valo  * Wireless Handler : Initiate Scan
722230db0ca8SKalle Valo  */
722330db0ca8SKalle Valo static int airo_set_scan(struct net_device *dev,
722430db0ca8SKalle Valo 			 struct iw_request_info *info,
722530db0ca8SKalle Valo 			 struct iw_point *dwrq,
722630db0ca8SKalle Valo 			 char *extra)
722730db0ca8SKalle Valo {
722830db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
722930db0ca8SKalle Valo 	Cmd cmd;
723030db0ca8SKalle Valo 	Resp rsp;
723130db0ca8SKalle Valo 	int wake = 0;
723230db0ca8SKalle Valo 	APListRid APList_rid_empty;
723330db0ca8SKalle Valo 
723430db0ca8SKalle Valo 	/* Note : you may have realised that, as this is a SET operation,
723530db0ca8SKalle Valo 	 * this is privileged and therefore a normal user can't
723630db0ca8SKalle Valo 	 * perform scanning.
723730db0ca8SKalle Valo 	 * This is not an error, while the device perform scanning,
723830db0ca8SKalle Valo 	 * traffic doesn't flow, so it's a perfect DoS...
723930db0ca8SKalle Valo 	 * Jean II */
724030db0ca8SKalle Valo 	if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
724130db0ca8SKalle Valo 
724230db0ca8SKalle Valo 	if (down_interruptible(&ai->sem))
724330db0ca8SKalle Valo 		return -ERESTARTSYS;
724430db0ca8SKalle Valo 
724530db0ca8SKalle Valo 	/* If there's already a scan in progress, don't
724630db0ca8SKalle Valo 	 * trigger another one. */
724730db0ca8SKalle Valo 	if (ai->scan_timeout > 0)
724830db0ca8SKalle Valo 		goto out;
724930db0ca8SKalle Valo 
725030db0ca8SKalle Valo 	/* Clear APList as it affects scan results */
725130db0ca8SKalle Valo 	memset(&APList_rid_empty, 0, sizeof(APList_rid_empty));
725230db0ca8SKalle Valo 	APList_rid_empty.len = cpu_to_le16(sizeof(APList_rid_empty));
725330db0ca8SKalle Valo 	disable_MAC(ai, 2);
725430db0ca8SKalle Valo 	writeAPListRid(ai, &APList_rid_empty, 0);
725530db0ca8SKalle Valo 	enable_MAC(ai, 0);
725630db0ca8SKalle Valo 
725730db0ca8SKalle Valo 	/* Initiate a scan command */
725830db0ca8SKalle Valo 	ai->scan_timeout = RUN_AT(3*HZ);
725930db0ca8SKalle Valo 	memset(&cmd, 0, sizeof(cmd));
726030db0ca8SKalle Valo 	cmd.cmd = CMD_LISTBSS;
726130db0ca8SKalle Valo 	issuecommand(ai, &cmd, &rsp);
726230db0ca8SKalle Valo 	wake = 1;
726330db0ca8SKalle Valo 
726430db0ca8SKalle Valo out:
726530db0ca8SKalle Valo 	up(&ai->sem);
726630db0ca8SKalle Valo 	if (wake)
726730db0ca8SKalle Valo 		wake_up_interruptible(&ai->thr_wait);
726830db0ca8SKalle Valo 	return 0;
726930db0ca8SKalle Valo }
727030db0ca8SKalle Valo 
727130db0ca8SKalle Valo /*------------------------------------------------------------------*/
727230db0ca8SKalle Valo /*
727330db0ca8SKalle Valo  * Translate scan data returned from the card to a card independent
727430db0ca8SKalle Valo  * format that the Wireless Tools will understand - Jean II
727530db0ca8SKalle Valo  */
727630db0ca8SKalle Valo static inline char *airo_translate_scan(struct net_device *dev,
727730db0ca8SKalle Valo 					struct iw_request_info *info,
727830db0ca8SKalle Valo 					char *current_ev,
727930db0ca8SKalle Valo 					char *end_buf,
728030db0ca8SKalle Valo 					BSSListRid *bss)
728130db0ca8SKalle Valo {
728230db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
728330db0ca8SKalle Valo 	struct iw_event		iwe;		/* Temporary buffer */
728430db0ca8SKalle Valo 	__le16			capabilities;
728530db0ca8SKalle Valo 	char *			current_val;	/* For rates */
728630db0ca8SKalle Valo 	int			i;
728730db0ca8SKalle Valo 	char *		buf;
728830db0ca8SKalle Valo 	u16 dBm;
728930db0ca8SKalle Valo 
729030db0ca8SKalle Valo 	/* First entry *MUST* be the AP MAC address */
729130db0ca8SKalle Valo 	iwe.cmd = SIOCGIWAP;
729230db0ca8SKalle Valo 	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
729330db0ca8SKalle Valo 	memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
729430db0ca8SKalle Valo 	current_ev = iwe_stream_add_event(info, current_ev, end_buf,
729530db0ca8SKalle Valo 					  &iwe, IW_EV_ADDR_LEN);
729630db0ca8SKalle Valo 
729730db0ca8SKalle Valo 	/* Other entries will be displayed in the order we give them */
729830db0ca8SKalle Valo 
729930db0ca8SKalle Valo 	/* Add the ESSID */
730030db0ca8SKalle Valo 	iwe.u.data.length = bss->ssidLen;
730130db0ca8SKalle Valo 	if (iwe.u.data.length > 32)
730230db0ca8SKalle Valo 		iwe.u.data.length = 32;
730330db0ca8SKalle Valo 	iwe.cmd = SIOCGIWESSID;
730430db0ca8SKalle Valo 	iwe.u.data.flags = 1;
730530db0ca8SKalle Valo 	current_ev = iwe_stream_add_point(info, current_ev, end_buf,
730630db0ca8SKalle Valo 					  &iwe, bss->ssid);
730730db0ca8SKalle Valo 
730830db0ca8SKalle Valo 	/* Add mode */
730930db0ca8SKalle Valo 	iwe.cmd = SIOCGIWMODE;
731030db0ca8SKalle Valo 	capabilities = bss->cap;
731130db0ca8SKalle Valo 	if (capabilities & (CAP_ESS | CAP_IBSS)) {
731230db0ca8SKalle Valo 		if (capabilities & CAP_ESS)
731330db0ca8SKalle Valo 			iwe.u.mode = IW_MODE_MASTER;
731430db0ca8SKalle Valo 		else
731530db0ca8SKalle Valo 			iwe.u.mode = IW_MODE_ADHOC;
731630db0ca8SKalle Valo 		current_ev = iwe_stream_add_event(info, current_ev, end_buf,
731730db0ca8SKalle Valo 						  &iwe, IW_EV_UINT_LEN);
731830db0ca8SKalle Valo 	}
731930db0ca8SKalle Valo 
732030db0ca8SKalle Valo 	/* Add frequency */
732130db0ca8SKalle Valo 	iwe.cmd = SIOCGIWFREQ;
732230db0ca8SKalle Valo 	iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
732330db0ca8SKalle Valo 	iwe.u.freq.m = 100000 *
732457fbcce3SJohannes Berg 	      ieee80211_channel_to_frequency(iwe.u.freq.m, NL80211_BAND_2GHZ);
732530db0ca8SKalle Valo 	iwe.u.freq.e = 1;
732630db0ca8SKalle Valo 	current_ev = iwe_stream_add_event(info, current_ev, end_buf,
732730db0ca8SKalle Valo 					  &iwe, IW_EV_FREQ_LEN);
732830db0ca8SKalle Valo 
732930db0ca8SKalle Valo 	dBm = le16_to_cpu(bss->dBm);
733030db0ca8SKalle Valo 
733130db0ca8SKalle Valo 	/* Add quality statistics */
733230db0ca8SKalle Valo 	iwe.cmd = IWEVQUAL;
733330db0ca8SKalle Valo 	if (ai->rssi) {
733430db0ca8SKalle Valo 		iwe.u.qual.level = 0x100 - dBm;
733530db0ca8SKalle Valo 		iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm);
733630db0ca8SKalle Valo 		iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
733730db0ca8SKalle Valo 				| IW_QUAL_LEVEL_UPDATED
733830db0ca8SKalle Valo 				| IW_QUAL_DBM;
733930db0ca8SKalle Valo 	} else {
734030db0ca8SKalle Valo 		iwe.u.qual.level = (dBm + 321) / 2;
734130db0ca8SKalle Valo 		iwe.u.qual.qual = 0;
734230db0ca8SKalle Valo 		iwe.u.qual.updated = IW_QUAL_QUAL_INVALID
734330db0ca8SKalle Valo 				| IW_QUAL_LEVEL_UPDATED
734430db0ca8SKalle Valo 				| IW_QUAL_DBM;
734530db0ca8SKalle Valo 	}
734630db0ca8SKalle Valo 	iwe.u.qual.noise = ai->wstats.qual.noise;
734730db0ca8SKalle Valo 	current_ev = iwe_stream_add_event(info, current_ev, end_buf,
734830db0ca8SKalle Valo 					  &iwe, IW_EV_QUAL_LEN);
734930db0ca8SKalle Valo 
735030db0ca8SKalle Valo 	/* Add encryption capability */
735130db0ca8SKalle Valo 	iwe.cmd = SIOCGIWENCODE;
735230db0ca8SKalle Valo 	if (capabilities & CAP_PRIVACY)
735330db0ca8SKalle Valo 		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
735430db0ca8SKalle Valo 	else
735530db0ca8SKalle Valo 		iwe.u.data.flags = IW_ENCODE_DISABLED;
735630db0ca8SKalle Valo 	iwe.u.data.length = 0;
735730db0ca8SKalle Valo 	current_ev = iwe_stream_add_point(info, current_ev, end_buf,
735830db0ca8SKalle Valo 					  &iwe, bss->ssid);
735930db0ca8SKalle Valo 
736030db0ca8SKalle Valo 	/* Rate : stuffing multiple values in a single event require a bit
736130db0ca8SKalle Valo 	 * more of magic - Jean II */
736230db0ca8SKalle Valo 	current_val = current_ev + iwe_stream_lcp_len(info);
736330db0ca8SKalle Valo 
736430db0ca8SKalle Valo 	iwe.cmd = SIOCGIWRATE;
736530db0ca8SKalle Valo 	/* Those two flags are ignored... */
736630db0ca8SKalle Valo 	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
736730db0ca8SKalle Valo 	/* Max 8 values */
736830db0ca8SKalle Valo 	for (i = 0 ; i < 8 ; i++) {
736930db0ca8SKalle Valo 		/* NULL terminated */
737030db0ca8SKalle Valo 		if (bss->rates[i] == 0)
737130db0ca8SKalle Valo 			break;
737230db0ca8SKalle Valo 		/* Bit rate given in 500 kb/s units (+ 0x80) */
737330db0ca8SKalle Valo 		iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000);
737430db0ca8SKalle Valo 		/* Add new value to event */
737530db0ca8SKalle Valo 		current_val = iwe_stream_add_value(info, current_ev,
737630db0ca8SKalle Valo 						   current_val, end_buf,
737730db0ca8SKalle Valo 						   &iwe, IW_EV_PARAM_LEN);
737830db0ca8SKalle Valo 	}
737930db0ca8SKalle Valo 	/* Check if we added any event */
738030db0ca8SKalle Valo 	if ((current_val - current_ev) > iwe_stream_lcp_len(info))
738130db0ca8SKalle Valo 		current_ev = current_val;
738230db0ca8SKalle Valo 
738330db0ca8SKalle Valo 	/* Beacon interval */
738430db0ca8SKalle Valo 	buf = kmalloc(30, GFP_KERNEL);
738530db0ca8SKalle Valo 	if (buf) {
738630db0ca8SKalle Valo 		iwe.cmd = IWEVCUSTOM;
738730db0ca8SKalle Valo 		sprintf(buf, "bcn_int=%d", bss->beaconInterval);
738830db0ca8SKalle Valo 		iwe.u.data.length = strlen(buf);
738930db0ca8SKalle Valo 		current_ev = iwe_stream_add_point(info, current_ev, end_buf,
739030db0ca8SKalle Valo 						  &iwe, buf);
739130db0ca8SKalle Valo 		kfree(buf);
739230db0ca8SKalle Valo 	}
739330db0ca8SKalle Valo 
739430db0ca8SKalle Valo 	/* Put WPA/RSN Information Elements into the event stream */
739530db0ca8SKalle Valo 	if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
739630db0ca8SKalle Valo 		unsigned int num_null_ies = 0;
739730db0ca8SKalle Valo 		u16 length = sizeof (bss->extra.iep);
739830db0ca8SKalle Valo 		u8 *ie = (void *)&bss->extra.iep;
739930db0ca8SKalle Valo 
740030db0ca8SKalle Valo 		while ((length >= 2) && (num_null_ies < 2)) {
740130db0ca8SKalle Valo 			if (2 + ie[1] > length) {
740230db0ca8SKalle Valo 				/* Invalid element, don't continue parsing IE */
740330db0ca8SKalle Valo 				break;
740430db0ca8SKalle Valo 			}
740530db0ca8SKalle Valo 
740630db0ca8SKalle Valo 			switch (ie[0]) {
740730db0ca8SKalle Valo 			case WLAN_EID_SSID:
740830db0ca8SKalle Valo 				/* Two zero-length SSID elements
740930db0ca8SKalle Valo 				 * mean we're done parsing elements */
741030db0ca8SKalle Valo 				if (!ie[1])
741130db0ca8SKalle Valo 					num_null_ies++;
741230db0ca8SKalle Valo 				break;
741330db0ca8SKalle Valo 
741430db0ca8SKalle Valo 			case WLAN_EID_VENDOR_SPECIFIC:
741530db0ca8SKalle Valo 				if (ie[1] >= 4 &&
741630db0ca8SKalle Valo 				    ie[2] == 0x00 &&
741730db0ca8SKalle Valo 				    ie[3] == 0x50 &&
741830db0ca8SKalle Valo 				    ie[4] == 0xf2 &&
741930db0ca8SKalle Valo 				    ie[5] == 0x01) {
742030db0ca8SKalle Valo 					iwe.cmd = IWEVGENIE;
742130db0ca8SKalle Valo 					/* 64 is an arbitrary cut-off */
742230db0ca8SKalle Valo 					iwe.u.data.length = min(ie[1] + 2,
742330db0ca8SKalle Valo 								64);
742430db0ca8SKalle Valo 					current_ev = iwe_stream_add_point(
742530db0ca8SKalle Valo 							info, current_ev,
742630db0ca8SKalle Valo 							end_buf, &iwe, ie);
742730db0ca8SKalle Valo 				}
742830db0ca8SKalle Valo 				break;
742930db0ca8SKalle Valo 
743030db0ca8SKalle Valo 			case WLAN_EID_RSN:
743130db0ca8SKalle Valo 				iwe.cmd = IWEVGENIE;
743230db0ca8SKalle Valo 				/* 64 is an arbitrary cut-off */
743330db0ca8SKalle Valo 				iwe.u.data.length = min(ie[1] + 2, 64);
743430db0ca8SKalle Valo 				current_ev = iwe_stream_add_point(
743530db0ca8SKalle Valo 					info, current_ev, end_buf,
743630db0ca8SKalle Valo 					&iwe, ie);
743730db0ca8SKalle Valo 				break;
743830db0ca8SKalle Valo 
743930db0ca8SKalle Valo 			default:
744030db0ca8SKalle Valo 				break;
744130db0ca8SKalle Valo 			}
744230db0ca8SKalle Valo 
744330db0ca8SKalle Valo 			length -= 2 + ie[1];
744430db0ca8SKalle Valo 			ie += 2 + ie[1];
744530db0ca8SKalle Valo 		}
744630db0ca8SKalle Valo 	}
744730db0ca8SKalle Valo 	return current_ev;
744830db0ca8SKalle Valo }
744930db0ca8SKalle Valo 
745030db0ca8SKalle Valo /*------------------------------------------------------------------*/
745130db0ca8SKalle Valo /*
745230db0ca8SKalle Valo  * Wireless Handler : Read Scan Results
745330db0ca8SKalle Valo  */
745430db0ca8SKalle Valo static int airo_get_scan(struct net_device *dev,
745530db0ca8SKalle Valo 			 struct iw_request_info *info,
745630db0ca8SKalle Valo 			 struct iw_point *dwrq,
745730db0ca8SKalle Valo 			 char *extra)
745830db0ca8SKalle Valo {
745930db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
746030db0ca8SKalle Valo 	BSSListElement *net;
746130db0ca8SKalle Valo 	int err = 0;
746230db0ca8SKalle Valo 	char *current_ev = extra;
746330db0ca8SKalle Valo 
746430db0ca8SKalle Valo 	/* If a scan is in-progress, return -EAGAIN */
746530db0ca8SKalle Valo 	if (ai->scan_timeout > 0)
746630db0ca8SKalle Valo 		return -EAGAIN;
746730db0ca8SKalle Valo 
746830db0ca8SKalle Valo 	if (down_interruptible(&ai->sem))
746930db0ca8SKalle Valo 		return -EAGAIN;
747030db0ca8SKalle Valo 
747130db0ca8SKalle Valo 	list_for_each_entry (net, &ai->network_list, list) {
747230db0ca8SKalle Valo 		/* Translate to WE format this entry */
747330db0ca8SKalle Valo 		current_ev = airo_translate_scan(dev, info, current_ev,
747430db0ca8SKalle Valo 						 extra + dwrq->length,
747530db0ca8SKalle Valo 						 &net->bss);
747630db0ca8SKalle Valo 
747730db0ca8SKalle Valo 		/* Check if there is space for one more entry */
747830db0ca8SKalle Valo 		if ((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
747930db0ca8SKalle Valo 			/* Ask user space to try again with a bigger buffer */
748030db0ca8SKalle Valo 			err = -E2BIG;
748130db0ca8SKalle Valo 			goto out;
748230db0ca8SKalle Valo 		}
748330db0ca8SKalle Valo 	}
748430db0ca8SKalle Valo 
748530db0ca8SKalle Valo 	/* Length of data */
748630db0ca8SKalle Valo 	dwrq->length = (current_ev - extra);
748730db0ca8SKalle Valo 	dwrq->flags = 0;	/* todo */
748830db0ca8SKalle Valo 
748930db0ca8SKalle Valo out:
749030db0ca8SKalle Valo 	up(&ai->sem);
749130db0ca8SKalle Valo 	return err;
749230db0ca8SKalle Valo }
749330db0ca8SKalle Valo 
749430db0ca8SKalle Valo /*------------------------------------------------------------------*/
749530db0ca8SKalle Valo /*
749630db0ca8SKalle Valo  * Commit handler : called after a bunch of SET operations
749730db0ca8SKalle Valo  */
749830db0ca8SKalle Valo static int airo_config_commit(struct net_device *dev,
749930db0ca8SKalle Valo 			      struct iw_request_info *info,	/* NULL */
750030db0ca8SKalle Valo 			      void *zwrq,			/* NULL */
750130db0ca8SKalle Valo 			      char *extra)			/* NULL */
750230db0ca8SKalle Valo {
750330db0ca8SKalle Valo 	struct airo_info *local = dev->ml_priv;
750430db0ca8SKalle Valo 
750530db0ca8SKalle Valo 	if (!test_bit (FLAG_COMMIT, &local->flags))
750630db0ca8SKalle Valo 		return 0;
750730db0ca8SKalle Valo 
750830db0ca8SKalle Valo 	/* Some of the "SET" function may have modified some of the
750930db0ca8SKalle Valo 	 * parameters. It's now time to commit them in the card */
751030db0ca8SKalle Valo 	disable_MAC(local, 1);
751130db0ca8SKalle Valo 	if (test_bit (FLAG_RESET, &local->flags)) {
751230db0ca8SKalle Valo 		SsidRid SSID_rid;
751330db0ca8SKalle Valo 
751430db0ca8SKalle Valo 		readSsidRid(local, &SSID_rid);
751530db0ca8SKalle Valo 		if (test_bit(FLAG_MPI,&local->flags))
751630db0ca8SKalle Valo 			setup_card(local, dev->dev_addr, 1);
751730db0ca8SKalle Valo 		else
751830db0ca8SKalle Valo 			reset_airo_card(dev);
751930db0ca8SKalle Valo 		disable_MAC(local, 1);
752030db0ca8SKalle Valo 		writeSsidRid(local, &SSID_rid, 1);
752130db0ca8SKalle Valo 		writeAPListRid(local, &local->APList, 1);
752230db0ca8SKalle Valo 	}
752330db0ca8SKalle Valo 	if (down_interruptible(&local->sem))
752430db0ca8SKalle Valo 		return -ERESTARTSYS;
752530db0ca8SKalle Valo 	writeConfigRid(local, 0);
752630db0ca8SKalle Valo 	enable_MAC(local, 0);
752730db0ca8SKalle Valo 	if (test_bit (FLAG_RESET, &local->flags))
752830db0ca8SKalle Valo 		airo_set_promisc(local);
752930db0ca8SKalle Valo 	else
753030db0ca8SKalle Valo 		up(&local->sem);
753130db0ca8SKalle Valo 
753230db0ca8SKalle Valo 	return 0;
753330db0ca8SKalle Valo }
753430db0ca8SKalle Valo 
753530db0ca8SKalle Valo /*------------------------------------------------------------------*/
753630db0ca8SKalle Valo /*
753730db0ca8SKalle Valo  * Structures to export the Wireless Handlers
753830db0ca8SKalle Valo  */
753930db0ca8SKalle Valo 
754030db0ca8SKalle Valo static const struct iw_priv_args airo_private_args[] = {
754130db0ca8SKalle Valo /*{ cmd,         set_args,                            get_args, name } */
754230db0ca8SKalle Valo   { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
754330db0ca8SKalle Valo     IW_PRIV_TYPE_BYTE | 2047, "airoioctl" },
754430db0ca8SKalle Valo   { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl),
754530db0ca8SKalle Valo     IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" },
754630db0ca8SKalle Valo };
754730db0ca8SKalle Valo 
754830db0ca8SKalle Valo static const iw_handler		airo_handler[] =
754930db0ca8SKalle Valo {
755030db0ca8SKalle Valo 	(iw_handler) airo_config_commit,	/* SIOCSIWCOMMIT */
755130db0ca8SKalle Valo 	(iw_handler) airo_get_name,		/* SIOCGIWNAME */
755230db0ca8SKalle Valo 	(iw_handler) NULL,			/* SIOCSIWNWID */
755330db0ca8SKalle Valo 	(iw_handler) NULL,			/* SIOCGIWNWID */
755430db0ca8SKalle Valo 	(iw_handler) airo_set_freq,		/* SIOCSIWFREQ */
755530db0ca8SKalle Valo 	(iw_handler) airo_get_freq,		/* SIOCGIWFREQ */
755630db0ca8SKalle Valo 	(iw_handler) airo_set_mode,		/* SIOCSIWMODE */
755730db0ca8SKalle Valo 	(iw_handler) airo_get_mode,		/* SIOCGIWMODE */
755830db0ca8SKalle Valo 	(iw_handler) airo_set_sens,		/* SIOCSIWSENS */
755930db0ca8SKalle Valo 	(iw_handler) airo_get_sens,		/* SIOCGIWSENS */
756030db0ca8SKalle Valo 	(iw_handler) NULL,			/* SIOCSIWRANGE */
756130db0ca8SKalle Valo 	(iw_handler) airo_get_range,		/* SIOCGIWRANGE */
756230db0ca8SKalle Valo 	(iw_handler) NULL,			/* SIOCSIWPRIV */
756330db0ca8SKalle Valo 	(iw_handler) NULL,			/* SIOCGIWPRIV */
756430db0ca8SKalle Valo 	(iw_handler) NULL,			/* SIOCSIWSTATS */
756530db0ca8SKalle Valo 	(iw_handler) NULL,			/* SIOCGIWSTATS */
756630db0ca8SKalle Valo 	iw_handler_set_spy,			/* SIOCSIWSPY */
756730db0ca8SKalle Valo 	iw_handler_get_spy,			/* SIOCGIWSPY */
756830db0ca8SKalle Valo 	iw_handler_set_thrspy,			/* SIOCSIWTHRSPY */
756930db0ca8SKalle Valo 	iw_handler_get_thrspy,			/* SIOCGIWTHRSPY */
757030db0ca8SKalle Valo 	(iw_handler) airo_set_wap,		/* SIOCSIWAP */
757130db0ca8SKalle Valo 	(iw_handler) airo_get_wap,		/* SIOCGIWAP */
757230db0ca8SKalle Valo 	(iw_handler) NULL,			/* -- hole -- */
757330db0ca8SKalle Valo 	(iw_handler) airo_get_aplist,		/* SIOCGIWAPLIST */
757430db0ca8SKalle Valo 	(iw_handler) airo_set_scan,		/* SIOCSIWSCAN */
757530db0ca8SKalle Valo 	(iw_handler) airo_get_scan,		/* SIOCGIWSCAN */
757630db0ca8SKalle Valo 	(iw_handler) airo_set_essid,		/* SIOCSIWESSID */
757730db0ca8SKalle Valo 	(iw_handler) airo_get_essid,		/* SIOCGIWESSID */
757830db0ca8SKalle Valo 	(iw_handler) airo_set_nick,		/* SIOCSIWNICKN */
757930db0ca8SKalle Valo 	(iw_handler) airo_get_nick,		/* SIOCGIWNICKN */
758030db0ca8SKalle Valo 	(iw_handler) NULL,			/* -- hole -- */
758130db0ca8SKalle Valo 	(iw_handler) NULL,			/* -- hole -- */
758230db0ca8SKalle Valo 	(iw_handler) airo_set_rate,		/* SIOCSIWRATE */
758330db0ca8SKalle Valo 	(iw_handler) airo_get_rate,		/* SIOCGIWRATE */
758430db0ca8SKalle Valo 	(iw_handler) airo_set_rts,		/* SIOCSIWRTS */
758530db0ca8SKalle Valo 	(iw_handler) airo_get_rts,		/* SIOCGIWRTS */
758630db0ca8SKalle Valo 	(iw_handler) airo_set_frag,		/* SIOCSIWFRAG */
758730db0ca8SKalle Valo 	(iw_handler) airo_get_frag,		/* SIOCGIWFRAG */
758830db0ca8SKalle Valo 	(iw_handler) airo_set_txpow,		/* SIOCSIWTXPOW */
758930db0ca8SKalle Valo 	(iw_handler) airo_get_txpow,		/* SIOCGIWTXPOW */
759030db0ca8SKalle Valo 	(iw_handler) airo_set_retry,		/* SIOCSIWRETRY */
759130db0ca8SKalle Valo 	(iw_handler) airo_get_retry,		/* SIOCGIWRETRY */
759230db0ca8SKalle Valo 	(iw_handler) airo_set_encode,		/* SIOCSIWENCODE */
759330db0ca8SKalle Valo 	(iw_handler) airo_get_encode,		/* SIOCGIWENCODE */
759430db0ca8SKalle Valo 	(iw_handler) airo_set_power,		/* SIOCSIWPOWER */
759530db0ca8SKalle Valo 	(iw_handler) airo_get_power,		/* SIOCGIWPOWER */
759630db0ca8SKalle Valo 	(iw_handler) NULL,			/* -- hole -- */
759730db0ca8SKalle Valo 	(iw_handler) NULL,			/* -- hole -- */
759830db0ca8SKalle Valo 	(iw_handler) NULL,			/* SIOCSIWGENIE */
759930db0ca8SKalle Valo 	(iw_handler) NULL,			/* SIOCGIWGENIE */
760030db0ca8SKalle Valo 	(iw_handler) airo_set_auth,		/* SIOCSIWAUTH */
760130db0ca8SKalle Valo 	(iw_handler) airo_get_auth,		/* SIOCGIWAUTH */
760230db0ca8SKalle Valo 	(iw_handler) airo_set_encodeext,	/* SIOCSIWENCODEEXT */
760330db0ca8SKalle Valo 	(iw_handler) airo_get_encodeext,	/* SIOCGIWENCODEEXT */
760430db0ca8SKalle Valo 	(iw_handler) NULL,			/* SIOCSIWPMKSA */
760530db0ca8SKalle Valo };
760630db0ca8SKalle Valo 
760730db0ca8SKalle Valo /* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here.
760830db0ca8SKalle Valo  * We want to force the use of the ioctl code, because those can't be
760930db0ca8SKalle Valo  * won't work the iw_handler code (because they simultaneously read
761030db0ca8SKalle Valo  * and write data and iw_handler can't do that).
761130db0ca8SKalle Valo  * Note that it's perfectly legal to read/write on a single ioctl command,
761230db0ca8SKalle Valo  * you just can't use iwpriv and need to force it via the ioctl handler.
761330db0ca8SKalle Valo  * Jean II */
761430db0ca8SKalle Valo static const iw_handler		airo_private_handler[] =
761530db0ca8SKalle Valo {
761630db0ca8SKalle Valo 	NULL,				/* SIOCIWFIRSTPRIV */
761730db0ca8SKalle Valo };
761830db0ca8SKalle Valo 
761930db0ca8SKalle Valo static const struct iw_handler_def	airo_handler_def =
762030db0ca8SKalle Valo {
762130db0ca8SKalle Valo 	.num_standard	= ARRAY_SIZE(airo_handler),
762230db0ca8SKalle Valo 	.num_private	= ARRAY_SIZE(airo_private_handler),
762330db0ca8SKalle Valo 	.num_private_args = ARRAY_SIZE(airo_private_args),
762430db0ca8SKalle Valo 	.standard	= airo_handler,
762530db0ca8SKalle Valo 	.private	= airo_private_handler,
762630db0ca8SKalle Valo 	.private_args	= airo_private_args,
762730db0ca8SKalle Valo 	.get_wireless_stats = airo_get_wireless_stats,
762830db0ca8SKalle Valo };
762930db0ca8SKalle Valo 
763030db0ca8SKalle Valo /*
763130db0ca8SKalle Valo  * This defines the configuration part of the Wireless Extensions
763230db0ca8SKalle Valo  * Note : irq and spinlock protection will occur in the subroutines
763330db0ca8SKalle Valo  *
763430db0ca8SKalle Valo  * TODO :
763530db0ca8SKalle Valo  *	o Check input value more carefully and fill correct values in range
763630db0ca8SKalle Valo  *	o Test and shakeout the bugs (if any)
763730db0ca8SKalle Valo  *
763830db0ca8SKalle Valo  * Jean II
763930db0ca8SKalle Valo  *
764030db0ca8SKalle Valo  * Javier Achirica did a great job of merging code from the unnamed CISCO
764130db0ca8SKalle Valo  * developer that added support for flashing the card.
764230db0ca8SKalle Valo  */
764330db0ca8SKalle Valo static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
764430db0ca8SKalle Valo {
764530db0ca8SKalle Valo 	int rc = 0;
764630db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
764730db0ca8SKalle Valo 
764830db0ca8SKalle Valo 	if (ai->power.event)
764930db0ca8SKalle Valo 		return 0;
765030db0ca8SKalle Valo 
765130db0ca8SKalle Valo 	switch (cmd) {
765230db0ca8SKalle Valo #ifdef CISCO_EXT
765330db0ca8SKalle Valo 	case AIROIDIFC:
765430db0ca8SKalle Valo #ifdef AIROOLDIDIFC
765530db0ca8SKalle Valo 	case AIROOLDIDIFC:
765630db0ca8SKalle Valo #endif
765730db0ca8SKalle Valo 	{
765830db0ca8SKalle Valo 		int val = AIROMAGIC;
765930db0ca8SKalle Valo 		aironet_ioctl com;
766030db0ca8SKalle Valo 		if (copy_from_user(&com, rq->ifr_data, sizeof(com)))
766130db0ca8SKalle Valo 			rc = -EFAULT;
766230db0ca8SKalle Valo 		else if (copy_to_user(com.data, (char *)&val, sizeof(val)))
766330db0ca8SKalle Valo 			rc = -EFAULT;
766430db0ca8SKalle Valo 	}
766530db0ca8SKalle Valo 	break;
766630db0ca8SKalle Valo 
766730db0ca8SKalle Valo 	case AIROIOCTL:
766830db0ca8SKalle Valo #ifdef AIROOLDIOCTL
766930db0ca8SKalle Valo 	case AIROOLDIOCTL:
767030db0ca8SKalle Valo #endif
767130db0ca8SKalle Valo 		/* Get the command struct and hand it off for evaluation by
767230db0ca8SKalle Valo 		 * the proper subfunction
767330db0ca8SKalle Valo 		 */
767430db0ca8SKalle Valo 	{
767530db0ca8SKalle Valo 		aironet_ioctl com;
767630db0ca8SKalle Valo 		if (copy_from_user(&com, rq->ifr_data, sizeof(com))) {
767730db0ca8SKalle Valo 			rc = -EFAULT;
767830db0ca8SKalle Valo 			break;
767930db0ca8SKalle Valo 		}
768030db0ca8SKalle Valo 
768130db0ca8SKalle Valo 		/* Separate R/W functions bracket legality here
768230db0ca8SKalle Valo 		 */
768330db0ca8SKalle Valo 		if (com.command == AIRORSWVERSION) {
768430db0ca8SKalle Valo 			if (copy_to_user(com.data, swversion, sizeof(swversion)))
768530db0ca8SKalle Valo 				rc = -EFAULT;
768630db0ca8SKalle Valo 			else
768730db0ca8SKalle Valo 				rc = 0;
768830db0ca8SKalle Valo 		}
768930db0ca8SKalle Valo 		else if (com.command <= AIRORRID)
769030db0ca8SKalle Valo 			rc = readrids(dev,&com);
769130db0ca8SKalle Valo 		else if (com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2))
769230db0ca8SKalle Valo 			rc = writerids(dev,&com);
769330db0ca8SKalle Valo 		else if (com.command >= AIROFLSHRST && com.command <= AIRORESTART)
769430db0ca8SKalle Valo 			rc = flashcard(dev,&com);
769530db0ca8SKalle Valo 		else
769630db0ca8SKalle Valo 			rc = -EINVAL;      /* Bad command in ioctl */
769730db0ca8SKalle Valo 	}
769830db0ca8SKalle Valo 	break;
769930db0ca8SKalle Valo #endif /* CISCO_EXT */
770030db0ca8SKalle Valo 
770130db0ca8SKalle Valo 	// All other calls are currently unsupported
770230db0ca8SKalle Valo 	default:
770330db0ca8SKalle Valo 		rc = -EOPNOTSUPP;
770430db0ca8SKalle Valo 	}
770530db0ca8SKalle Valo 	return rc;
770630db0ca8SKalle Valo }
770730db0ca8SKalle Valo 
770830db0ca8SKalle Valo /*
770930db0ca8SKalle Valo  * Get the Wireless stats out of the driver
771030db0ca8SKalle Valo  * Note : irq and spinlock protection will occur in the subroutines
771130db0ca8SKalle Valo  *
771230db0ca8SKalle Valo  * TODO :
771330db0ca8SKalle Valo  *	o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs)
771430db0ca8SKalle Valo  *
771530db0ca8SKalle Valo  * Jean
771630db0ca8SKalle Valo  */
771730db0ca8SKalle Valo static void airo_read_wireless_stats(struct airo_info *local)
771830db0ca8SKalle Valo {
771930db0ca8SKalle Valo 	StatusRid status_rid;
772030db0ca8SKalle Valo 	StatsRid stats_rid;
772130db0ca8SKalle Valo 	CapabilityRid cap_rid;
772230db0ca8SKalle Valo 	__le32 *vals = stats_rid.vals;
772330db0ca8SKalle Valo 
772430db0ca8SKalle Valo 	/* Get stats out of the card */
772524bf941fSSebastian Andrzej Siewior 	if (local->power.event)
772630db0ca8SKalle Valo 		return;
772724bf941fSSebastian Andrzej Siewior 
772830db0ca8SKalle Valo 	readCapabilityRid(local, &cap_rid, 0);
772930db0ca8SKalle Valo 	readStatusRid(local, &status_rid, 0);
773030db0ca8SKalle Valo 	readStatsRid(local, &stats_rid, RID_STATS, 0);
773130db0ca8SKalle Valo 
773230db0ca8SKalle Valo 	/* The status */
773330db0ca8SKalle Valo 	local->wstats.status = le16_to_cpu(status_rid.mode);
773430db0ca8SKalle Valo 
773530db0ca8SKalle Valo 	/* Signal quality and co */
773630db0ca8SKalle Valo 	if (local->rssi) {
773730db0ca8SKalle Valo 		local->wstats.qual.level =
773830db0ca8SKalle Valo 			airo_rssi_to_dbm(local->rssi,
773930db0ca8SKalle Valo 					 le16_to_cpu(status_rid.sigQuality));
774030db0ca8SKalle Valo 		/* normalizedSignalStrength appears to be a percentage */
774130db0ca8SKalle Valo 		local->wstats.qual.qual =
774230db0ca8SKalle Valo 			le16_to_cpu(status_rid.normalizedSignalStrength);
774330db0ca8SKalle Valo 	} else {
774430db0ca8SKalle Valo 		local->wstats.qual.level =
774530db0ca8SKalle Valo 			(le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2;
774630db0ca8SKalle Valo 		local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid);
774730db0ca8SKalle Valo 	}
774830db0ca8SKalle Valo 	if (le16_to_cpu(status_rid.len) >= 124) {
774930db0ca8SKalle Valo 		local->wstats.qual.noise = 0x100 - status_rid.noisedBm;
775030db0ca8SKalle Valo 		local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
775130db0ca8SKalle Valo 	} else {
775230db0ca8SKalle Valo 		local->wstats.qual.noise = 0;
775330db0ca8SKalle Valo 		local->wstats.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID | IW_QUAL_DBM;
775430db0ca8SKalle Valo 	}
775530db0ca8SKalle Valo 
775630db0ca8SKalle Valo 	/* Packets discarded in the wireless adapter due to wireless
775730db0ca8SKalle Valo 	 * specific problems */
775830db0ca8SKalle Valo 	local->wstats.discard.nwid = le32_to_cpu(vals[56]) +
775930db0ca8SKalle Valo 				     le32_to_cpu(vals[57]) +
776030db0ca8SKalle Valo 				     le32_to_cpu(vals[58]); /* SSID Mismatch */
776130db0ca8SKalle Valo 	local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */
776230db0ca8SKalle Valo 	local->wstats.discard.fragment = le32_to_cpu(vals[30]);
776330db0ca8SKalle Valo 	local->wstats.discard.retries = le32_to_cpu(vals[10]);
776430db0ca8SKalle Valo 	local->wstats.discard.misc = le32_to_cpu(vals[1]) +
776530db0ca8SKalle Valo 				     le32_to_cpu(vals[32]);
776630db0ca8SKalle Valo 	local->wstats.miss.beacon = le32_to_cpu(vals[34]);
776730db0ca8SKalle Valo }
776830db0ca8SKalle Valo 
776930db0ca8SKalle Valo static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
777030db0ca8SKalle Valo {
777130db0ca8SKalle Valo 	struct airo_info *local =  dev->ml_priv;
777230db0ca8SKalle Valo 
777324bf941fSSebastian Andrzej Siewior 	if (!down_interruptible(&local->sem)) {
777430db0ca8SKalle Valo 		airo_read_wireless_stats(local);
777524bf941fSSebastian Andrzej Siewior 		up(&local->sem);
777630db0ca8SKalle Valo 	}
777730db0ca8SKalle Valo 	return &local->wstats;
777830db0ca8SKalle Valo }
777930db0ca8SKalle Valo 
778030db0ca8SKalle Valo #ifdef CISCO_EXT
778130db0ca8SKalle Valo /*
778230db0ca8SKalle Valo  * This just translates from driver IOCTL codes to the command codes to
778330db0ca8SKalle Valo  * feed to the radio's host interface. Things can be added/deleted
778430db0ca8SKalle Valo  * as needed.  This represents the READ side of control I/O to
778530db0ca8SKalle Valo  * the card
778630db0ca8SKalle Valo  */
7787ba4d6513SLee Jones static int readrids(struct net_device *dev, aironet_ioctl *comp)
7788ba4d6513SLee Jones {
778930db0ca8SKalle Valo 	unsigned short ridcode;
779030db0ca8SKalle Valo 	unsigned char *iobuf;
779130db0ca8SKalle Valo 	int len;
779230db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
779330db0ca8SKalle Valo 
779430db0ca8SKalle Valo 	if (test_bit(FLAG_FLASHING, &ai->flags))
779530db0ca8SKalle Valo 		return -EIO;
779630db0ca8SKalle Valo 
779730db0ca8SKalle Valo 	switch(comp->command)
779830db0ca8SKalle Valo 	{
779930db0ca8SKalle Valo 	case AIROGCAP:      ridcode = RID_CAPABILITIES; break;
780030db0ca8SKalle Valo 	case AIROGCFG:      ridcode = RID_CONFIG;
780130db0ca8SKalle Valo 		if (test_bit(FLAG_COMMIT, &ai->flags)) {
780230db0ca8SKalle Valo 			disable_MAC (ai, 1);
780330db0ca8SKalle Valo 			writeConfigRid (ai, 1);
780430db0ca8SKalle Valo 			enable_MAC(ai, 1);
780530db0ca8SKalle Valo 		}
780630db0ca8SKalle Valo 		break;
780730db0ca8SKalle Valo 	case AIROGSLIST:    ridcode = RID_SSID;         break;
780830db0ca8SKalle Valo 	case AIROGVLIST:    ridcode = RID_APLIST;       break;
780930db0ca8SKalle Valo 	case AIROGDRVNAM:   ridcode = RID_DRVNAME;      break;
781030db0ca8SKalle Valo 	case AIROGEHTENC:   ridcode = RID_ETHERENCAP;   break;
781178f7a756SMichael Ellerman 	case AIROGWEPKTMP:  ridcode = RID_WEP_TEMP;	break;
781278f7a756SMichael Ellerman 	case AIROGWEPKNV:   ridcode = RID_WEP_PERM;	break;
781330db0ca8SKalle Valo 	case AIROGSTAT:     ridcode = RID_STATUS;       break;
781430db0ca8SKalle Valo 	case AIROGSTATSD32: ridcode = RID_STATSDELTA;   break;
781530db0ca8SKalle Valo 	case AIROGSTATSC32: ridcode = RID_STATS;        break;
781630db0ca8SKalle Valo 	case AIROGMICSTATS:
781730db0ca8SKalle Valo 		if (copy_to_user(comp->data, &ai->micstats,
781830db0ca8SKalle Valo 				 min((int)comp->len, (int)sizeof(ai->micstats))))
781930db0ca8SKalle Valo 			return -EFAULT;
782030db0ca8SKalle Valo 		return 0;
782130db0ca8SKalle Valo 	case AIRORRID:      ridcode = comp->ridnum;     break;
782230db0ca8SKalle Valo 	default:
782330db0ca8SKalle Valo 		return -EINVAL;
782430db0ca8SKalle Valo 	}
782530db0ca8SKalle Valo 
782678f7a756SMichael Ellerman 	if (ridcode == RID_WEP_TEMP || ridcode == RID_WEP_PERM) {
782778f7a756SMichael Ellerman 		/* Only super-user can read WEP keys */
782878f7a756SMichael Ellerman 		if (!capable(CAP_NET_ADMIN))
782978f7a756SMichael Ellerman 			return -EPERM;
783078f7a756SMichael Ellerman 	}
783178f7a756SMichael Ellerman 
7832d6bce213SMichael Ellerman 	if ((iobuf = kzalloc(RIDSIZE, GFP_KERNEL)) == NULL)
783330db0ca8SKalle Valo 		return -ENOMEM;
783430db0ca8SKalle Valo 
783530db0ca8SKalle Valo 	PC4500_readrid(ai, ridcode, iobuf, RIDSIZE, 1);
783630db0ca8SKalle Valo 	/* get the count of bytes in the rid  docs say 1st 2 bytes is it.
783730db0ca8SKalle Valo 	 * then return it to the user
783830db0ca8SKalle Valo 	 * 9/22/2000 Honor user given length
783930db0ca8SKalle Valo 	 */
784030db0ca8SKalle Valo 	len = comp->len;
784130db0ca8SKalle Valo 
784230db0ca8SKalle Valo 	if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) {
784330db0ca8SKalle Valo 		kfree (iobuf);
784430db0ca8SKalle Valo 		return -EFAULT;
784530db0ca8SKalle Valo 	}
784630db0ca8SKalle Valo 	kfree (iobuf);
784730db0ca8SKalle Valo 	return 0;
784830db0ca8SKalle Valo }
784930db0ca8SKalle Valo 
785030db0ca8SKalle Valo /*
785130db0ca8SKalle Valo  * Danger Will Robinson write the rids here
785230db0ca8SKalle Valo  */
785330db0ca8SKalle Valo 
7854ba4d6513SLee Jones static int writerids(struct net_device *dev, aironet_ioctl *comp)
7855ba4d6513SLee Jones {
785630db0ca8SKalle Valo 	struct airo_info *ai = dev->ml_priv;
785730db0ca8SKalle Valo 	int  ridcode;
785830db0ca8SKalle Valo         int  enabled;
785906548fbbSGustavo A. R. Silva 	int (*writer)(struct airo_info *, u16 rid, const void *, int, int);
786030db0ca8SKalle Valo 	unsigned char *iobuf;
786130db0ca8SKalle Valo 
786230db0ca8SKalle Valo 	/* Only super-user can write RIDs */
786330db0ca8SKalle Valo 	if (!capable(CAP_NET_ADMIN))
786430db0ca8SKalle Valo 		return -EPERM;
786530db0ca8SKalle Valo 
786630db0ca8SKalle Valo 	if (test_bit(FLAG_FLASHING, &ai->flags))
786730db0ca8SKalle Valo 		return -EIO;
786830db0ca8SKalle Valo 
786930db0ca8SKalle Valo 	ridcode = 0;
787030db0ca8SKalle Valo 	writer = do_writerid;
787130db0ca8SKalle Valo 
787230db0ca8SKalle Valo 	switch(comp->command)
787330db0ca8SKalle Valo 	{
787430db0ca8SKalle Valo 	case AIROPSIDS:     ridcode = RID_SSID;         break;
787530db0ca8SKalle Valo 	case AIROPCAP:      ridcode = RID_CAPABILITIES; break;
787630db0ca8SKalle Valo 	case AIROPAPLIST:   ridcode = RID_APLIST;       break;
787730db0ca8SKalle Valo 	case AIROPCFG: ai->config.len = 0;
787830db0ca8SKalle Valo 			    clear_bit(FLAG_COMMIT, &ai->flags);
787930db0ca8SKalle Valo 			    ridcode = RID_CONFIG;       break;
788030db0ca8SKalle Valo 	case AIROPWEPKEYNV: ridcode = RID_WEP_PERM;     break;
788130db0ca8SKalle Valo 	case AIROPLEAPUSR:  ridcode = RID_LEAPUSERNAME; break;
788230db0ca8SKalle Valo 	case AIROPLEAPPWD:  ridcode = RID_LEAPPASSWORD; break;
788330db0ca8SKalle Valo 	case AIROPWEPKEY:   ridcode = RID_WEP_TEMP; writer = PC4500_writerid;
788430db0ca8SKalle Valo 		break;
788530db0ca8SKalle Valo 	case AIROPLEAPUSR+1: ridcode = 0xFF2A;          break;
788630db0ca8SKalle Valo 	case AIROPLEAPUSR+2: ridcode = 0xFF2B;          break;
788730db0ca8SKalle Valo 
788830db0ca8SKalle Valo 		/* this is not really a rid but a command given to the card
788930db0ca8SKalle Valo 		 * same with MAC off
789030db0ca8SKalle Valo 		 */
789130db0ca8SKalle Valo 	case AIROPMACON:
789230db0ca8SKalle Valo 		if (enable_MAC(ai, 1) != 0)
789330db0ca8SKalle Valo 			return -EIO;
789430db0ca8SKalle Valo 		return 0;
789530db0ca8SKalle Valo 
789630db0ca8SKalle Valo 		/*
789730db0ca8SKalle Valo 		 * Evidently this code in the airo driver does not get a symbol
789830db0ca8SKalle Valo 		 * as disable_MAC. it's probably so short the compiler does not gen one.
789930db0ca8SKalle Valo 		 */
790030db0ca8SKalle Valo 	case AIROPMACOFF:
790130db0ca8SKalle Valo 		disable_MAC(ai, 1);
790230db0ca8SKalle Valo 		return 0;
790330db0ca8SKalle Valo 
790430db0ca8SKalle Valo 		/* This command merely clears the counts does not actually store any data
790530db0ca8SKalle Valo 		 * only reads rid. But as it changes the cards state, I put it in the
790630db0ca8SKalle Valo 		 * writerid routines.
790730db0ca8SKalle Valo 		 */
790830db0ca8SKalle Valo 	case AIROPSTCLR:
790930db0ca8SKalle Valo 		if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
791030db0ca8SKalle Valo 			return -ENOMEM;
791130db0ca8SKalle Valo 
791230db0ca8SKalle Valo 		PC4500_readrid(ai, RID_STATSDELTACLEAR, iobuf, RIDSIZE, 1);
791330db0ca8SKalle Valo 
791430db0ca8SKalle Valo 		enabled = ai->micstats.enabled;
791530db0ca8SKalle Valo 		memset(&ai->micstats, 0, sizeof(ai->micstats));
791630db0ca8SKalle Valo 		ai->micstats.enabled = enabled;
791730db0ca8SKalle Valo 
791830db0ca8SKalle Valo 		if (copy_to_user(comp->data, iobuf,
791930db0ca8SKalle Valo 				 min((int)comp->len, (int)RIDSIZE))) {
792030db0ca8SKalle Valo 			kfree (iobuf);
792130db0ca8SKalle Valo 			return -EFAULT;
792230db0ca8SKalle Valo 		}
792330db0ca8SKalle Valo 		kfree (iobuf);
792430db0ca8SKalle Valo 		return 0;
792530db0ca8SKalle Valo 
792630db0ca8SKalle Valo 	default:
792730db0ca8SKalle Valo 		return -EOPNOTSUPP;	/* Blarg! */
792830db0ca8SKalle Valo 	}
792930db0ca8SKalle Valo 	if (comp->len > RIDSIZE)
793030db0ca8SKalle Valo 		return -EINVAL;
793130db0ca8SKalle Valo 
793230db0ca8SKalle Valo 	if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL)
793330db0ca8SKalle Valo 		return -ENOMEM;
793430db0ca8SKalle Valo 
793530db0ca8SKalle Valo 	if (copy_from_user(iobuf, comp->data, comp->len)) {
793630db0ca8SKalle Valo 		kfree (iobuf);
793730db0ca8SKalle Valo 		return -EFAULT;
793830db0ca8SKalle Valo 	}
793930db0ca8SKalle Valo 
794030db0ca8SKalle Valo 	if (comp->command == AIROPCFG) {
794130db0ca8SKalle Valo 		ConfigRid *cfg = (ConfigRid *)iobuf;
794230db0ca8SKalle Valo 
794330db0ca8SKalle Valo 		if (test_bit(FLAG_MIC_CAPABLE, &ai->flags))
794430db0ca8SKalle Valo 			cfg->opmode |= MODE_MIC;
794530db0ca8SKalle Valo 
794630db0ca8SKalle Valo 		if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS)
794730db0ca8SKalle Valo 			set_bit (FLAG_ADHOC, &ai->flags);
794830db0ca8SKalle Valo 		else
794930db0ca8SKalle Valo 			clear_bit (FLAG_ADHOC, &ai->flags);
795030db0ca8SKalle Valo 	}
795130db0ca8SKalle Valo 
795230db0ca8SKalle Valo 	if ((*writer)(ai, ridcode, iobuf, comp->len, 1)) {
795330db0ca8SKalle Valo 		kfree (iobuf);
795430db0ca8SKalle Valo 		return -EIO;
795530db0ca8SKalle Valo 	}
795630db0ca8SKalle Valo 	kfree (iobuf);
795730db0ca8SKalle Valo 	return 0;
795830db0ca8SKalle Valo }
795930db0ca8SKalle Valo 
796030db0ca8SKalle Valo /*****************************************************************************
796130db0ca8SKalle Valo  * Ancillary flash / mod functions much black magic lurkes here              *
796230db0ca8SKalle Valo  *****************************************************************************
796330db0ca8SKalle Valo  */
796430db0ca8SKalle Valo 
796530db0ca8SKalle Valo /*
796630db0ca8SKalle Valo  * Flash command switch table
796730db0ca8SKalle Valo  */
796830db0ca8SKalle Valo 
7969ba4d6513SLee Jones static int flashcard(struct net_device *dev, aironet_ioctl *comp)
7970ba4d6513SLee Jones {
797130db0ca8SKalle Valo 	int z;
797230db0ca8SKalle Valo 
797330db0ca8SKalle Valo 	/* Only super-user can modify flash */
797430db0ca8SKalle Valo 	if (!capable(CAP_NET_ADMIN))
797530db0ca8SKalle Valo 		return -EPERM;
797630db0ca8SKalle Valo 
797730db0ca8SKalle Valo 	switch(comp->command)
797830db0ca8SKalle Valo 	{
797930db0ca8SKalle Valo 	case AIROFLSHRST:
798030db0ca8SKalle Valo 		return cmdreset((struct airo_info *)dev->ml_priv);
798130db0ca8SKalle Valo 
798230db0ca8SKalle Valo 	case AIROFLSHSTFL:
798330db0ca8SKalle Valo 		if (!AIRO_FLASH(dev) &&
798430db0ca8SKalle Valo 		    (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL)
798530db0ca8SKalle Valo 			return -ENOMEM;
798630db0ca8SKalle Valo 		return setflashmode((struct airo_info *)dev->ml_priv);
798730db0ca8SKalle Valo 
798830db0ca8SKalle Valo 	case AIROFLSHGCHR: /* Get char from aux */
798930db0ca8SKalle Valo 		if (comp->len != sizeof(int))
799030db0ca8SKalle Valo 			return -EINVAL;
799130db0ca8SKalle Valo 		if (copy_from_user(&z, comp->data, comp->len))
799230db0ca8SKalle Valo 			return -EFAULT;
799330db0ca8SKalle Valo 		return flashgchar((struct airo_info *)dev->ml_priv, z, 8000);
799430db0ca8SKalle Valo 
799530db0ca8SKalle Valo 	case AIROFLSHPCHR: /* Send char to card. */
799630db0ca8SKalle Valo 		if (comp->len != sizeof(int))
799730db0ca8SKalle Valo 			return -EINVAL;
799830db0ca8SKalle Valo 		if (copy_from_user(&z, comp->data, comp->len))
799930db0ca8SKalle Valo 			return -EFAULT;
800030db0ca8SKalle Valo 		return flashpchar((struct airo_info *)dev->ml_priv, z, 8000);
800130db0ca8SKalle Valo 
800230db0ca8SKalle Valo 	case AIROFLPUTBUF: /* Send 32k to card */
800330db0ca8SKalle Valo 		if (!AIRO_FLASH(dev))
800430db0ca8SKalle Valo 			return -ENOMEM;
800530db0ca8SKalle Valo 		if (comp->len > FLASHSIZE)
800630db0ca8SKalle Valo 			return -EINVAL;
800730db0ca8SKalle Valo 		if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len))
800830db0ca8SKalle Valo 			return -EFAULT;
800930db0ca8SKalle Valo 
801030db0ca8SKalle Valo 		flashputbuf((struct airo_info *)dev->ml_priv);
801130db0ca8SKalle Valo 		return 0;
801230db0ca8SKalle Valo 
801330db0ca8SKalle Valo 	case AIRORESTART:
801430db0ca8SKalle Valo 		if (flashrestart((struct airo_info *)dev->ml_priv, dev))
801530db0ca8SKalle Valo 			return -EIO;
801630db0ca8SKalle Valo 		return 0;
801730db0ca8SKalle Valo 	}
801830db0ca8SKalle Valo 	return -EINVAL;
801930db0ca8SKalle Valo }
802030db0ca8SKalle Valo 
802130db0ca8SKalle Valo #define FLASH_COMMAND  0x7e7e
802230db0ca8SKalle Valo 
802330db0ca8SKalle Valo /*
802430db0ca8SKalle Valo  * STEP 1)
802530db0ca8SKalle Valo  * Disable MAC and do soft reset on
802630db0ca8SKalle Valo  * card.
802730db0ca8SKalle Valo  */
802830db0ca8SKalle Valo 
8029ba4d6513SLee Jones static int cmdreset(struct airo_info *ai)
8030ba4d6513SLee Jones {
803130db0ca8SKalle Valo 	disable_MAC(ai, 1);
803230db0ca8SKalle Valo 
803330db0ca8SKalle Valo 	if (!waitbusy (ai)) {
803430db0ca8SKalle Valo 		airo_print_info(ai->dev->name, "Waitbusy hang before RESET");
803530db0ca8SKalle Valo 		return -EBUSY;
803630db0ca8SKalle Valo 	}
803730db0ca8SKalle Valo 
803830db0ca8SKalle Valo 	OUT4500(ai, COMMAND, CMD_SOFTRESET);
803930db0ca8SKalle Valo 
804030db0ca8SKalle Valo 	ssleep(1);			/* WAS 600 12/7/00 */
804130db0ca8SKalle Valo 
804230db0ca8SKalle Valo 	if (!waitbusy (ai)) {
804330db0ca8SKalle Valo 		airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET");
804430db0ca8SKalle Valo 		return -EBUSY;
804530db0ca8SKalle Valo 	}
804630db0ca8SKalle Valo 	return 0;
804730db0ca8SKalle Valo }
804830db0ca8SKalle Valo 
804930db0ca8SKalle Valo /* STEP 2)
805030db0ca8SKalle Valo  * Put the card in legendary flash
805130db0ca8SKalle Valo  * mode
805230db0ca8SKalle Valo  */
805330db0ca8SKalle Valo 
8054ba4d6513SLee Jones static int setflashmode (struct airo_info *ai)
8055ba4d6513SLee Jones {
805630db0ca8SKalle Valo 	set_bit (FLAG_FLASHING, &ai->flags);
805730db0ca8SKalle Valo 
805830db0ca8SKalle Valo 	OUT4500(ai, SWS0, FLASH_COMMAND);
805930db0ca8SKalle Valo 	OUT4500(ai, SWS1, FLASH_COMMAND);
806030db0ca8SKalle Valo 	if (probe) {
806130db0ca8SKalle Valo 		OUT4500(ai, SWS0, FLASH_COMMAND);
806230db0ca8SKalle Valo 		OUT4500(ai, COMMAND, 0x10);
806330db0ca8SKalle Valo 	} else {
806430db0ca8SKalle Valo 		OUT4500(ai, SWS2, FLASH_COMMAND);
806530db0ca8SKalle Valo 		OUT4500(ai, SWS3, FLASH_COMMAND);
806630db0ca8SKalle Valo 		OUT4500(ai, COMMAND, 0);
806730db0ca8SKalle Valo 	}
806830db0ca8SKalle Valo 	msleep(500);		/* 500ms delay */
806930db0ca8SKalle Valo 
807030db0ca8SKalle Valo 	if (!waitbusy(ai)) {
807130db0ca8SKalle Valo 		clear_bit (FLAG_FLASHING, &ai->flags);
807230db0ca8SKalle Valo 		airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode");
807330db0ca8SKalle Valo 		return -EIO;
807430db0ca8SKalle Valo 	}
807530db0ca8SKalle Valo 	return 0;
807630db0ca8SKalle Valo }
807730db0ca8SKalle Valo 
807830db0ca8SKalle Valo /* Put character to SWS0 wait for dwelltime
807930db0ca8SKalle Valo  * x 50us for  echo .
808030db0ca8SKalle Valo  */
808130db0ca8SKalle Valo 
8082ba4d6513SLee Jones static int flashpchar(struct airo_info *ai, int byte, int dwelltime)
8083ba4d6513SLee Jones {
808430db0ca8SKalle Valo 	int echo;
808530db0ca8SKalle Valo 	int waittime;
808630db0ca8SKalle Valo 
808730db0ca8SKalle Valo 	byte |= 0x8000;
808830db0ca8SKalle Valo 
808930db0ca8SKalle Valo 	if (dwelltime == 0)
809030db0ca8SKalle Valo 		dwelltime = 200;
809130db0ca8SKalle Valo 
809230db0ca8SKalle Valo 	waittime = dwelltime;
809330db0ca8SKalle Valo 
809430db0ca8SKalle Valo 	/* Wait for busy bit d15 to go false indicating buffer empty */
809530db0ca8SKalle Valo 	while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) {
809630db0ca8SKalle Valo 		udelay (50);
809730db0ca8SKalle Valo 		waittime -= 50;
809830db0ca8SKalle Valo 	}
809930db0ca8SKalle Valo 
810030db0ca8SKalle Valo 	/* timeout for busy clear wait */
810130db0ca8SKalle Valo 	if (waittime <= 0) {
810230db0ca8SKalle Valo 		airo_print_info(ai->dev->name, "flash putchar busywait timeout!");
810330db0ca8SKalle Valo 		return -EBUSY;
810430db0ca8SKalle Valo 	}
810530db0ca8SKalle Valo 
810630db0ca8SKalle Valo 	/* Port is clear now write byte and wait for it to echo back */
810730db0ca8SKalle Valo 	do {
810830db0ca8SKalle Valo 		OUT4500(ai, SWS0, byte);
810930db0ca8SKalle Valo 		udelay(50);
811030db0ca8SKalle Valo 		dwelltime -= 50;
811130db0ca8SKalle Valo 		echo = IN4500(ai, SWS1);
811230db0ca8SKalle Valo 	} while (dwelltime >= 0 && echo != byte);
811330db0ca8SKalle Valo 
811430db0ca8SKalle Valo 	OUT4500(ai, SWS1, 0);
811530db0ca8SKalle Valo 
811630db0ca8SKalle Valo 	return (echo == byte) ? 0 : -EIO;
811730db0ca8SKalle Valo }
811830db0ca8SKalle Valo 
811930db0ca8SKalle Valo /*
812030db0ca8SKalle Valo  * Get a character from the card matching matchbyte
812130db0ca8SKalle Valo  * Step 3)
812230db0ca8SKalle Valo  */
8123ba4d6513SLee Jones static int flashgchar(struct airo_info *ai, int matchbyte, int dwelltime)
8124ba4d6513SLee Jones {
812530db0ca8SKalle Valo 	int           rchar;
812630db0ca8SKalle Valo 	unsigned char rbyte = 0;
812730db0ca8SKalle Valo 
812830db0ca8SKalle Valo 	do {
812930db0ca8SKalle Valo 		rchar = IN4500(ai, SWS1);
813030db0ca8SKalle Valo 
813130db0ca8SKalle Valo 		if (dwelltime && !(0x8000 & rchar)) {
813230db0ca8SKalle Valo 			dwelltime -= 10;
813330db0ca8SKalle Valo 			mdelay(10);
813430db0ca8SKalle Valo 			continue;
813530db0ca8SKalle Valo 		}
813630db0ca8SKalle Valo 		rbyte = 0xff & rchar;
813730db0ca8SKalle Valo 
813830db0ca8SKalle Valo 		if ((rbyte == matchbyte) && (0x8000 & rchar)) {
813930db0ca8SKalle Valo 			OUT4500(ai, SWS1, 0);
814030db0ca8SKalle Valo 			return 0;
814130db0ca8SKalle Valo 		}
814230db0ca8SKalle Valo 		if (rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar)
814330db0ca8SKalle Valo 			break;
814430db0ca8SKalle Valo 		OUT4500(ai, SWS1, 0);
814530db0ca8SKalle Valo 
814630db0ca8SKalle Valo 	} while (dwelltime > 0);
814730db0ca8SKalle Valo 	return -EIO;
814830db0ca8SKalle Valo }
814930db0ca8SKalle Valo 
815030db0ca8SKalle Valo /*
815130db0ca8SKalle Valo  * Transfer 32k of firmware data from user buffer to our buffer and
815230db0ca8SKalle Valo  * send to the card
815330db0ca8SKalle Valo  */
815430db0ca8SKalle Valo 
8155ba4d6513SLee Jones static int flashputbuf(struct airo_info *ai)
8156ba4d6513SLee Jones {
815730db0ca8SKalle Valo 	int            nwords;
815830db0ca8SKalle Valo 
815930db0ca8SKalle Valo 	/* Write stuff */
816030db0ca8SKalle Valo 	if (test_bit(FLAG_MPI,&ai->flags))
816130db0ca8SKalle Valo 		memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE);
816230db0ca8SKalle Valo 	else {
816330db0ca8SKalle Valo 		OUT4500(ai, AUXPAGE, 0x100);
816430db0ca8SKalle Valo 		OUT4500(ai, AUXOFF, 0);
816530db0ca8SKalle Valo 
816630db0ca8SKalle Valo 		for (nwords = 0; nwords != FLASHSIZE / 2; nwords++) {
816730db0ca8SKalle Valo 			OUT4500(ai, AUXDATA, ai->flash[nwords] & 0xffff);
816830db0ca8SKalle Valo 		}
816930db0ca8SKalle Valo 	}
817030db0ca8SKalle Valo 	OUT4500(ai, SWS0, 0x8000);
817130db0ca8SKalle Valo 
817230db0ca8SKalle Valo 	return 0;
817330db0ca8SKalle Valo }
817430db0ca8SKalle Valo 
817530db0ca8SKalle Valo /*
817630db0ca8SKalle Valo  *
817730db0ca8SKalle Valo  */
8178ba4d6513SLee Jones static int flashrestart(struct airo_info *ai, struct net_device *dev)
8179ba4d6513SLee Jones {
818030db0ca8SKalle Valo 	int    i, status;
818130db0ca8SKalle Valo 
818230db0ca8SKalle Valo 	ssleep(1);			/* Added 12/7/00 */
818330db0ca8SKalle Valo 	clear_bit (FLAG_FLASHING, &ai->flags);
818430db0ca8SKalle Valo 	if (test_bit(FLAG_MPI, &ai->flags)) {
818530db0ca8SKalle Valo 		status = mpi_init_descriptors(ai);
818630db0ca8SKalle Valo 		if (status != SUCCESS)
818730db0ca8SKalle Valo 			return status;
818830db0ca8SKalle Valo 	}
818930db0ca8SKalle Valo 	status = setup_card(ai, dev->dev_addr, 1);
819030db0ca8SKalle Valo 
819130db0ca8SKalle Valo 	if (!test_bit(FLAG_MPI,&ai->flags))
819230db0ca8SKalle Valo 		for (i = 0; i < MAX_FIDS; i++) {
819330db0ca8SKalle Valo 			ai->fids[i] = transmit_allocate
819430db0ca8SKalle Valo 				(ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2);
819530db0ca8SKalle Valo 		}
819630db0ca8SKalle Valo 
819730db0ca8SKalle Valo 	ssleep(1);			/* Added 12/7/00 */
819830db0ca8SKalle Valo 	return status;
819930db0ca8SKalle Valo }
820030db0ca8SKalle Valo #endif /* CISCO_EXT */
820130db0ca8SKalle Valo 
820230db0ca8SKalle Valo /*
820330db0ca8SKalle Valo     This program is free software; you can redistribute it and/or
820430db0ca8SKalle Valo     modify it under the terms of the GNU General Public License
820530db0ca8SKalle Valo     as published by the Free Software Foundation; either version 2
820630db0ca8SKalle Valo     of the License, or (at your option) any later version.
820730db0ca8SKalle Valo 
820830db0ca8SKalle Valo     This program is distributed in the hope that it will be useful,
820930db0ca8SKalle Valo     but WITHOUT ANY WARRANTY; without even the implied warranty of
821030db0ca8SKalle Valo     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
821130db0ca8SKalle Valo     GNU General Public License for more details.
821230db0ca8SKalle Valo 
821330db0ca8SKalle Valo     In addition:
821430db0ca8SKalle Valo 
821530db0ca8SKalle Valo     Redistribution and use in source and binary forms, with or without
821630db0ca8SKalle Valo     modification, are permitted provided that the following conditions
821730db0ca8SKalle Valo     are met:
821830db0ca8SKalle Valo 
821930db0ca8SKalle Valo     1. Redistributions of source code must retain the above copyright
822030db0ca8SKalle Valo        notice, this list of conditions and the following disclaimer.
822130db0ca8SKalle Valo     2. Redistributions in binary form must reproduce the above copyright
822230db0ca8SKalle Valo        notice, this list of conditions and the following disclaimer in the
822330db0ca8SKalle Valo        documentation and/or other materials provided with the distribution.
822430db0ca8SKalle Valo     3. The name of the author may not be used to endorse or promote
822530db0ca8SKalle Valo        products derived from this software without specific prior written
822630db0ca8SKalle Valo        permission.
822730db0ca8SKalle Valo 
822830db0ca8SKalle Valo     THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
822930db0ca8SKalle Valo     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
823030db0ca8SKalle Valo     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
823130db0ca8SKalle Valo     ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
823230db0ca8SKalle Valo     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
823330db0ca8SKalle Valo     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
823430db0ca8SKalle Valo     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
823530db0ca8SKalle Valo     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
823630db0ca8SKalle Valo     STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
823730db0ca8SKalle Valo     IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
823830db0ca8SKalle Valo     POSSIBILITY OF SUCH DAMAGE.
823930db0ca8SKalle Valo */
824030db0ca8SKalle Valo 
824130db0ca8SKalle Valo module_init(airo_init_module);
824230db0ca8SKalle Valo module_exit(airo_cleanup_module);
8243