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