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