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