162306a36Sopenharmony_ci/*====================================================================== 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci Aironet driver for 4500 and 4800 series cards 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci This code is released under both the GPL version 2 and BSD licenses. 662306a36Sopenharmony_ci Either license may be used. The respective licenses are found at 762306a36Sopenharmony_ci the end of this file. 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci This code was developed by Benjamin Reed <breed@users.sourceforge.net> 1062306a36Sopenharmony_ci including portions of which come from the Aironet PC4500 1162306a36Sopenharmony_ci Developer's Reference Manual and used with permission. Copyright 1262306a36Sopenharmony_ci (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use 1362306a36Sopenharmony_ci code in the Developer's manual was granted for this driver by 1462306a36Sopenharmony_ci Aironet. Major code contributions were received from Javier Achirica 1562306a36Sopenharmony_ci <achirica@users.sourceforge.net> and Jean Tourrilhes <jt@hpl.hp.com>. 1662306a36Sopenharmony_ci Code was also integrated from the Cisco Aironet driver for Linux. 1762306a36Sopenharmony_ci Support for MPI350 cards was added by Fabrice Bellet 1862306a36Sopenharmony_ci <fabrice@bellet.info>. 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci======================================================================*/ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/err.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/kernel.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/proc_fs.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/sched.h> 3062306a36Sopenharmony_ci#include <linux/ptrace.h> 3162306a36Sopenharmony_ci#include <linux/slab.h> 3262306a36Sopenharmony_ci#include <linux/string.h> 3362306a36Sopenharmony_ci#include <linux/timer.h> 3462306a36Sopenharmony_ci#include <linux/interrupt.h> 3562306a36Sopenharmony_ci#include <linux/in.h> 3662306a36Sopenharmony_ci#include <linux/bitops.h> 3762306a36Sopenharmony_ci#include <linux/scatterlist.h> 3862306a36Sopenharmony_ci#include <linux/crypto.h> 3962306a36Sopenharmony_ci#include <linux/io.h> 4062306a36Sopenharmony_ci#include <asm/unaligned.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include <linux/netdevice.h> 4362306a36Sopenharmony_ci#include <linux/etherdevice.h> 4462306a36Sopenharmony_ci#include <linux/skbuff.h> 4562306a36Sopenharmony_ci#include <linux/if_arp.h> 4662306a36Sopenharmony_ci#include <linux/ioport.h> 4762306a36Sopenharmony_ci#include <linux/pci.h> 4862306a36Sopenharmony_ci#include <linux/uaccess.h> 4962306a36Sopenharmony_ci#include <linux/kthread.h> 5062306a36Sopenharmony_ci#include <linux/freezer.h> 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#include <crypto/aes.h> 5362306a36Sopenharmony_ci#include <crypto/skcipher.h> 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#include <net/cfg80211.h> 5662306a36Sopenharmony_ci#include <net/iw_handler.h> 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#include "airo.h" 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define DRV_NAME "airo" 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#ifdef CONFIG_PCI 6362306a36Sopenharmony_cistatic const struct pci_device_id card_ids[] = { 6462306a36Sopenharmony_ci { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, }, 6562306a36Sopenharmony_ci { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID }, 6662306a36Sopenharmony_ci { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, }, 6762306a36Sopenharmony_ci { 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, }, 6862306a36Sopenharmony_ci { 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, }, 6962306a36Sopenharmony_ci { 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, }, 7062306a36Sopenharmony_ci { 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, }, 7162306a36Sopenharmony_ci { 0, } 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, card_ids); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int airo_pci_probe(struct pci_dev *, const struct pci_device_id *); 7662306a36Sopenharmony_cistatic void airo_pci_remove(struct pci_dev *); 7762306a36Sopenharmony_cistatic int __maybe_unused airo_pci_suspend(struct device *dev); 7862306a36Sopenharmony_cistatic int __maybe_unused airo_pci_resume(struct device *dev); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(airo_pci_pm_ops, 8162306a36Sopenharmony_ci airo_pci_suspend, 8262306a36Sopenharmony_ci airo_pci_resume); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic struct pci_driver airo_driver = { 8562306a36Sopenharmony_ci .name = DRV_NAME, 8662306a36Sopenharmony_ci .id_table = card_ids, 8762306a36Sopenharmony_ci .probe = airo_pci_probe, 8862306a36Sopenharmony_ci .remove = airo_pci_remove, 8962306a36Sopenharmony_ci .driver.pm = &airo_pci_pm_ops, 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci#endif /* CONFIG_PCI */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* Include Wireless Extension definition and check version - Jean II */ 9462306a36Sopenharmony_ci#include <linux/wireless.h> 9562306a36Sopenharmony_ci#define WIRELESS_SPY /* enable iwspy support */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define CISCO_EXT /* enable Cisco extensions */ 9862306a36Sopenharmony_ci#ifdef CISCO_EXT 9962306a36Sopenharmony_ci#include <linux/delay.h> 10062306a36Sopenharmony_ci#endif 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* Hack to do some power saving */ 10362306a36Sopenharmony_ci#define POWER_ON_DOWN 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* As you can see this list is HUGH! 10662306a36Sopenharmony_ci I really don't know what a lot of these counts are about, but they 10762306a36Sopenharmony_ci are all here for completeness. If the IGNLABEL macro is put in 10862306a36Sopenharmony_ci infront of the label, that statistic will not be included in the list 10962306a36Sopenharmony_ci of statistics in the /proc filesystem */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define IGNLABEL(comment) NULL 11262306a36Sopenharmony_cistatic const char *statsLabels[] = { 11362306a36Sopenharmony_ci "RxOverrun", 11462306a36Sopenharmony_ci IGNLABEL("RxPlcpCrcErr"), 11562306a36Sopenharmony_ci IGNLABEL("RxPlcpFormatErr"), 11662306a36Sopenharmony_ci IGNLABEL("RxPlcpLengthErr"), 11762306a36Sopenharmony_ci "RxMacCrcErr", 11862306a36Sopenharmony_ci "RxMacCrcOk", 11962306a36Sopenharmony_ci "RxWepErr", 12062306a36Sopenharmony_ci "RxWepOk", 12162306a36Sopenharmony_ci "RetryLong", 12262306a36Sopenharmony_ci "RetryShort", 12362306a36Sopenharmony_ci "MaxRetries", 12462306a36Sopenharmony_ci "NoAck", 12562306a36Sopenharmony_ci "NoCts", 12662306a36Sopenharmony_ci "RxAck", 12762306a36Sopenharmony_ci "RxCts", 12862306a36Sopenharmony_ci "TxAck", 12962306a36Sopenharmony_ci "TxRts", 13062306a36Sopenharmony_ci "TxCts", 13162306a36Sopenharmony_ci "TxMc", 13262306a36Sopenharmony_ci "TxBc", 13362306a36Sopenharmony_ci "TxUcFrags", 13462306a36Sopenharmony_ci "TxUcPackets", 13562306a36Sopenharmony_ci "TxBeacon", 13662306a36Sopenharmony_ci "RxBeacon", 13762306a36Sopenharmony_ci "TxSinColl", 13862306a36Sopenharmony_ci "TxMulColl", 13962306a36Sopenharmony_ci "DefersNo", 14062306a36Sopenharmony_ci "DefersProt", 14162306a36Sopenharmony_ci "DefersEngy", 14262306a36Sopenharmony_ci "DupFram", 14362306a36Sopenharmony_ci "RxFragDisc", 14462306a36Sopenharmony_ci "TxAged", 14562306a36Sopenharmony_ci "RxAged", 14662306a36Sopenharmony_ci "LostSync-MaxRetry", 14762306a36Sopenharmony_ci "LostSync-MissedBeacons", 14862306a36Sopenharmony_ci "LostSync-ArlExceeded", 14962306a36Sopenharmony_ci "LostSync-Deauth", 15062306a36Sopenharmony_ci "LostSync-Disassoced", 15162306a36Sopenharmony_ci "LostSync-TsfTiming", 15262306a36Sopenharmony_ci "HostTxMc", 15362306a36Sopenharmony_ci "HostTxBc", 15462306a36Sopenharmony_ci "HostTxUc", 15562306a36Sopenharmony_ci "HostTxFail", 15662306a36Sopenharmony_ci "HostRxMc", 15762306a36Sopenharmony_ci "HostRxBc", 15862306a36Sopenharmony_ci "HostRxUc", 15962306a36Sopenharmony_ci "HostRxDiscard", 16062306a36Sopenharmony_ci IGNLABEL("HmacTxMc"), 16162306a36Sopenharmony_ci IGNLABEL("HmacTxBc"), 16262306a36Sopenharmony_ci IGNLABEL("HmacTxUc"), 16362306a36Sopenharmony_ci IGNLABEL("HmacTxFail"), 16462306a36Sopenharmony_ci IGNLABEL("HmacRxMc"), 16562306a36Sopenharmony_ci IGNLABEL("HmacRxBc"), 16662306a36Sopenharmony_ci IGNLABEL("HmacRxUc"), 16762306a36Sopenharmony_ci IGNLABEL("HmacRxDiscard"), 16862306a36Sopenharmony_ci IGNLABEL("HmacRxAccepted"), 16962306a36Sopenharmony_ci "SsidMismatch", 17062306a36Sopenharmony_ci "ApMismatch", 17162306a36Sopenharmony_ci "RatesMismatch", 17262306a36Sopenharmony_ci "AuthReject", 17362306a36Sopenharmony_ci "AuthTimeout", 17462306a36Sopenharmony_ci "AssocReject", 17562306a36Sopenharmony_ci "AssocTimeout", 17662306a36Sopenharmony_ci IGNLABEL("ReasonOutsideTable"), 17762306a36Sopenharmony_ci IGNLABEL("ReasonStatus1"), 17862306a36Sopenharmony_ci IGNLABEL("ReasonStatus2"), 17962306a36Sopenharmony_ci IGNLABEL("ReasonStatus3"), 18062306a36Sopenharmony_ci IGNLABEL("ReasonStatus4"), 18162306a36Sopenharmony_ci IGNLABEL("ReasonStatus5"), 18262306a36Sopenharmony_ci IGNLABEL("ReasonStatus6"), 18362306a36Sopenharmony_ci IGNLABEL("ReasonStatus7"), 18462306a36Sopenharmony_ci IGNLABEL("ReasonStatus8"), 18562306a36Sopenharmony_ci IGNLABEL("ReasonStatus9"), 18662306a36Sopenharmony_ci IGNLABEL("ReasonStatus10"), 18762306a36Sopenharmony_ci IGNLABEL("ReasonStatus11"), 18862306a36Sopenharmony_ci IGNLABEL("ReasonStatus12"), 18962306a36Sopenharmony_ci IGNLABEL("ReasonStatus13"), 19062306a36Sopenharmony_ci IGNLABEL("ReasonStatus14"), 19162306a36Sopenharmony_ci IGNLABEL("ReasonStatus15"), 19262306a36Sopenharmony_ci IGNLABEL("ReasonStatus16"), 19362306a36Sopenharmony_ci IGNLABEL("ReasonStatus17"), 19462306a36Sopenharmony_ci IGNLABEL("ReasonStatus18"), 19562306a36Sopenharmony_ci IGNLABEL("ReasonStatus19"), 19662306a36Sopenharmony_ci "RxMan", 19762306a36Sopenharmony_ci "TxMan", 19862306a36Sopenharmony_ci "RxRefresh", 19962306a36Sopenharmony_ci "TxRefresh", 20062306a36Sopenharmony_ci "RxPoll", 20162306a36Sopenharmony_ci "TxPoll", 20262306a36Sopenharmony_ci "HostRetries", 20362306a36Sopenharmony_ci "LostSync-HostReq", 20462306a36Sopenharmony_ci "HostTxBytes", 20562306a36Sopenharmony_ci "HostRxBytes", 20662306a36Sopenharmony_ci "ElapsedUsec", 20762306a36Sopenharmony_ci "ElapsedSec", 20862306a36Sopenharmony_ci "LostSyncBetterAP", 20962306a36Sopenharmony_ci "PrivacyMismatch", 21062306a36Sopenharmony_ci "Jammed", 21162306a36Sopenharmony_ci "DiscRxNotWepped", 21262306a36Sopenharmony_ci "PhyEleMismatch", 21362306a36Sopenharmony_ci (char*)-1 }; 21462306a36Sopenharmony_ci#ifndef RUN_AT 21562306a36Sopenharmony_ci#define RUN_AT(x) (jiffies+(x)) 21662306a36Sopenharmony_ci#endif 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* These variables are for insmod, since it seems that the rates 22062306a36Sopenharmony_ci can only be set in setup_card. Rates should be a comma separated 22162306a36Sopenharmony_ci (no spaces) list of rates (up to 8). */ 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int rates[8]; 22462306a36Sopenharmony_cistatic char *ssids[3]; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int io[4]; 22762306a36Sopenharmony_cistatic int irq[4]; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic 23062306a36Sopenharmony_ciint maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at. 23162306a36Sopenharmony_ci 0 means no limit. For old cards this was 4 */ 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */ 23462306a36Sopenharmony_cistatic int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read 23562306a36Sopenharmony_ci the bap, needed on some older cards and buses. */ 23662306a36Sopenharmony_cistatic int adhoc; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int probe = 1; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic kuid_t proc_kuid; 24162306a36Sopenharmony_cistatic int proc_uid /* = 0 */; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic kgid_t proc_kgid; 24462306a36Sopenharmony_cistatic int proc_gid /* = 0 */; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int airo_perm = 0555; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int proc_perm = 0644; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciMODULE_AUTHOR("Benjamin Reed"); 25162306a36Sopenharmony_ciMODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet cards. " 25262306a36Sopenharmony_ci "Direct support for ISA/PCI/MPI cards and support for PCMCIA when used with airo_cs."); 25362306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 25462306a36Sopenharmony_cimodule_param_hw_array(io, int, ioport, NULL, 0); 25562306a36Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0); 25662306a36Sopenharmony_cimodule_param_array(rates, int, NULL, 0); 25762306a36Sopenharmony_cimodule_param_array(ssids, charp, NULL, 0); 25862306a36Sopenharmony_cimodule_param(auto_wep, int, 0); 25962306a36Sopenharmony_ciMODULE_PARM_DESC(auto_wep, 26062306a36Sopenharmony_ci "If non-zero, the driver will keep looping through the authentication options until an association is made. " 26162306a36Sopenharmony_ci "The value of auto_wep is number of the wep keys to check. " 26262306a36Sopenharmony_ci "A value of 2 will try using the key at index 0 and index 1."); 26362306a36Sopenharmony_cimodule_param(aux_bap, int, 0); 26462306a36Sopenharmony_ciMODULE_PARM_DESC(aux_bap, 26562306a36Sopenharmony_ci "If non-zero, the driver will switch into a mode that seems to work better for older cards with some older buses. " 26662306a36Sopenharmony_ci "Before switching it checks that the switch is needed."); 26762306a36Sopenharmony_cimodule_param(maxencrypt, int, 0); 26862306a36Sopenharmony_ciMODULE_PARM_DESC(maxencrypt, 26962306a36Sopenharmony_ci "The maximum speed that the card can do encryption. " 27062306a36Sopenharmony_ci "Units are in 512kbs. " 27162306a36Sopenharmony_ci "Zero (default) means there is no limit. " 27262306a36Sopenharmony_ci "Older cards used to be limited to 2mbs (4)."); 27362306a36Sopenharmony_cimodule_param(adhoc, int, 0); 27462306a36Sopenharmony_ciMODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode."); 27562306a36Sopenharmony_cimodule_param(probe, int, 0); 27662306a36Sopenharmony_ciMODULE_PARM_DESC(probe, "If zero, the driver won't start the card."); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cimodule_param(proc_uid, int, 0); 27962306a36Sopenharmony_ciMODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to."); 28062306a36Sopenharmony_cimodule_param(proc_gid, int, 0); 28162306a36Sopenharmony_ciMODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to."); 28262306a36Sopenharmony_cimodule_param(airo_perm, int, 0); 28362306a36Sopenharmony_ciMODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet."); 28462306a36Sopenharmony_cimodule_param(proc_perm, int, 0); 28562306a36Sopenharmony_ciMODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc"); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/* This is a kind of sloppy hack to get this information to OUT4500 and 28862306a36Sopenharmony_ci IN4500. I would be extremely interested in the situation where this 28962306a36Sopenharmony_ci doesn't work though!!! */ 29062306a36Sopenharmony_cistatic int do8bitIO /* = 0 */; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/* Return codes */ 29362306a36Sopenharmony_ci#define SUCCESS 0 29462306a36Sopenharmony_ci#define ERROR -1 29562306a36Sopenharmony_ci#define NO_PACKET -2 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* Commands */ 29862306a36Sopenharmony_ci#define NOP2 0x0000 29962306a36Sopenharmony_ci#define MAC_ENABLE 0x0001 30062306a36Sopenharmony_ci#define MAC_DISABLE 0x0002 30162306a36Sopenharmony_ci#define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */ 30262306a36Sopenharmony_ci#define CMD_SOFTRESET 0x0004 30362306a36Sopenharmony_ci#define HOSTSLEEP 0x0005 30462306a36Sopenharmony_ci#define CMD_MAGIC_PKT 0x0006 30562306a36Sopenharmony_ci#define CMD_SETWAKEMASK 0x0007 30662306a36Sopenharmony_ci#define CMD_READCFG 0x0008 30762306a36Sopenharmony_ci#define CMD_SETMODE 0x0009 30862306a36Sopenharmony_ci#define CMD_ALLOCATETX 0x000a 30962306a36Sopenharmony_ci#define CMD_TRANSMIT 0x000b 31062306a36Sopenharmony_ci#define CMD_DEALLOCATETX 0x000c 31162306a36Sopenharmony_ci#define NOP 0x0010 31262306a36Sopenharmony_ci#define CMD_WORKAROUND 0x0011 31362306a36Sopenharmony_ci#define CMD_ALLOCATEAUX 0x0020 31462306a36Sopenharmony_ci#define CMD_ACCESS 0x0021 31562306a36Sopenharmony_ci#define CMD_PCIBAP 0x0022 31662306a36Sopenharmony_ci#define CMD_PCIAUX 0x0023 31762306a36Sopenharmony_ci#define CMD_ALLOCBUF 0x0028 31862306a36Sopenharmony_ci#define CMD_GETTLV 0x0029 31962306a36Sopenharmony_ci#define CMD_PUTTLV 0x002a 32062306a36Sopenharmony_ci#define CMD_DELTLV 0x002b 32162306a36Sopenharmony_ci#define CMD_FINDNEXTTLV 0x002c 32262306a36Sopenharmony_ci#define CMD_PSPNODES 0x0030 32362306a36Sopenharmony_ci#define CMD_SETCW 0x0031 32462306a36Sopenharmony_ci#define CMD_SETPCF 0x0032 32562306a36Sopenharmony_ci#define CMD_SETPHYREG 0x003e 32662306a36Sopenharmony_ci#define CMD_TXTEST 0x003f 32762306a36Sopenharmony_ci#define MAC_ENABLETX 0x0101 32862306a36Sopenharmony_ci#define CMD_LISTBSS 0x0103 32962306a36Sopenharmony_ci#define CMD_SAVECFG 0x0108 33062306a36Sopenharmony_ci#define CMD_ENABLEAUX 0x0111 33162306a36Sopenharmony_ci#define CMD_WRITERID 0x0121 33262306a36Sopenharmony_ci#define CMD_USEPSPNODES 0x0130 33362306a36Sopenharmony_ci#define MAC_ENABLERX 0x0201 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/* Command errors */ 33662306a36Sopenharmony_ci#define ERROR_QUALIF 0x00 33762306a36Sopenharmony_ci#define ERROR_ILLCMD 0x01 33862306a36Sopenharmony_ci#define ERROR_ILLFMT 0x02 33962306a36Sopenharmony_ci#define ERROR_INVFID 0x03 34062306a36Sopenharmony_ci#define ERROR_INVRID 0x04 34162306a36Sopenharmony_ci#define ERROR_LARGE 0x05 34262306a36Sopenharmony_ci#define ERROR_NDISABL 0x06 34362306a36Sopenharmony_ci#define ERROR_ALLOCBSY 0x07 34462306a36Sopenharmony_ci#define ERROR_NORD 0x0B 34562306a36Sopenharmony_ci#define ERROR_NOWR 0x0C 34662306a36Sopenharmony_ci#define ERROR_INVFIDTX 0x0D 34762306a36Sopenharmony_ci#define ERROR_TESTACT 0x0E 34862306a36Sopenharmony_ci#define ERROR_TAGNFND 0x12 34962306a36Sopenharmony_ci#define ERROR_DECODE 0x20 35062306a36Sopenharmony_ci#define ERROR_DESCUNAV 0x21 35162306a36Sopenharmony_ci#define ERROR_BADLEN 0x22 35262306a36Sopenharmony_ci#define ERROR_MODE 0x80 35362306a36Sopenharmony_ci#define ERROR_HOP 0x81 35462306a36Sopenharmony_ci#define ERROR_BINTER 0x82 35562306a36Sopenharmony_ci#define ERROR_RXMODE 0x83 35662306a36Sopenharmony_ci#define ERROR_MACADDR 0x84 35762306a36Sopenharmony_ci#define ERROR_RATES 0x85 35862306a36Sopenharmony_ci#define ERROR_ORDER 0x86 35962306a36Sopenharmony_ci#define ERROR_SCAN 0x87 36062306a36Sopenharmony_ci#define ERROR_AUTH 0x88 36162306a36Sopenharmony_ci#define ERROR_PSMODE 0x89 36262306a36Sopenharmony_ci#define ERROR_RTYPE 0x8A 36362306a36Sopenharmony_ci#define ERROR_DIVER 0x8B 36462306a36Sopenharmony_ci#define ERROR_SSID 0x8C 36562306a36Sopenharmony_ci#define ERROR_APLIST 0x8D 36662306a36Sopenharmony_ci#define ERROR_AUTOWAKE 0x8E 36762306a36Sopenharmony_ci#define ERROR_LEAP 0x8F 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/* Registers */ 37062306a36Sopenharmony_ci#define COMMAND 0x00 37162306a36Sopenharmony_ci#define PARAM0 0x02 37262306a36Sopenharmony_ci#define PARAM1 0x04 37362306a36Sopenharmony_ci#define PARAM2 0x06 37462306a36Sopenharmony_ci#define STATUS 0x08 37562306a36Sopenharmony_ci#define RESP0 0x0a 37662306a36Sopenharmony_ci#define RESP1 0x0c 37762306a36Sopenharmony_ci#define RESP2 0x0e 37862306a36Sopenharmony_ci#define LINKSTAT 0x10 37962306a36Sopenharmony_ci#define SELECT0 0x18 38062306a36Sopenharmony_ci#define OFFSET0 0x1c 38162306a36Sopenharmony_ci#define RXFID 0x20 38262306a36Sopenharmony_ci#define TXALLOCFID 0x22 38362306a36Sopenharmony_ci#define TXCOMPLFID 0x24 38462306a36Sopenharmony_ci#define DATA0 0x36 38562306a36Sopenharmony_ci#define EVSTAT 0x30 38662306a36Sopenharmony_ci#define EVINTEN 0x32 38762306a36Sopenharmony_ci#define EVACK 0x34 38862306a36Sopenharmony_ci#define SWS0 0x28 38962306a36Sopenharmony_ci#define SWS1 0x2a 39062306a36Sopenharmony_ci#define SWS2 0x2c 39162306a36Sopenharmony_ci#define SWS3 0x2e 39262306a36Sopenharmony_ci#define AUXPAGE 0x3A 39362306a36Sopenharmony_ci#define AUXOFF 0x3C 39462306a36Sopenharmony_ci#define AUXDATA 0x3E 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci#define FID_TX 1 39762306a36Sopenharmony_ci#define FID_RX 2 39862306a36Sopenharmony_ci/* Offset into aux memory for descriptors */ 39962306a36Sopenharmony_ci#define AUX_OFFSET 0x800 40062306a36Sopenharmony_ci/* Size of allocated packets */ 40162306a36Sopenharmony_ci#define PKTSIZE 1840 40262306a36Sopenharmony_ci#define RIDSIZE 2048 40362306a36Sopenharmony_ci/* Size of the transmit queue */ 40462306a36Sopenharmony_ci#define MAXTXQ 64 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/* BAP selectors */ 40762306a36Sopenharmony_ci#define BAP0 0 /* Used for receiving packets */ 40862306a36Sopenharmony_ci#define BAP1 2 /* Used for xmiting packets and working with RIDS */ 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* Flags */ 41162306a36Sopenharmony_ci#define COMMAND_BUSY 0x8000 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci#define BAP_BUSY 0x8000 41462306a36Sopenharmony_ci#define BAP_ERR 0x4000 41562306a36Sopenharmony_ci#define BAP_DONE 0x2000 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci#define PROMISC 0xffff 41862306a36Sopenharmony_ci#define NOPROMISC 0x0000 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci#define EV_CMD 0x10 42162306a36Sopenharmony_ci#define EV_CLEARCOMMANDBUSY 0x4000 42262306a36Sopenharmony_ci#define EV_RX 0x01 42362306a36Sopenharmony_ci#define EV_TX 0x02 42462306a36Sopenharmony_ci#define EV_TXEXC 0x04 42562306a36Sopenharmony_ci#define EV_ALLOC 0x08 42662306a36Sopenharmony_ci#define EV_LINK 0x80 42762306a36Sopenharmony_ci#define EV_AWAKE 0x100 42862306a36Sopenharmony_ci#define EV_TXCPY 0x400 42962306a36Sopenharmony_ci#define EV_UNKNOWN 0x800 43062306a36Sopenharmony_ci#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */ 43162306a36Sopenharmony_ci#define EV_AWAKEN 0x2000 43262306a36Sopenharmony_ci#define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC) 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci#ifdef CHECK_UNKNOWN_INTS 43562306a36Sopenharmony_ci#define IGNORE_INTS (EV_CMD | EV_UNKNOWN) 43662306a36Sopenharmony_ci#else 43762306a36Sopenharmony_ci#define IGNORE_INTS (~STATUS_INTS) 43862306a36Sopenharmony_ci#endif 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/* RID TYPES */ 44162306a36Sopenharmony_ci#define RID_RW 0x20 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci/* The RIDs */ 44462306a36Sopenharmony_ci#define RID_CAPABILITIES 0xFF00 44562306a36Sopenharmony_ci#define RID_APINFO 0xFF01 44662306a36Sopenharmony_ci#define RID_RADIOINFO 0xFF02 44762306a36Sopenharmony_ci#define RID_UNKNOWN3 0xFF03 44862306a36Sopenharmony_ci#define RID_RSSI 0xFF04 44962306a36Sopenharmony_ci#define RID_CONFIG 0xFF10 45062306a36Sopenharmony_ci#define RID_SSID 0xFF11 45162306a36Sopenharmony_ci#define RID_APLIST 0xFF12 45262306a36Sopenharmony_ci#define RID_DRVNAME 0xFF13 45362306a36Sopenharmony_ci#define RID_ETHERENCAP 0xFF14 45462306a36Sopenharmony_ci#define RID_WEP_TEMP 0xFF15 45562306a36Sopenharmony_ci#define RID_WEP_PERM 0xFF16 45662306a36Sopenharmony_ci#define RID_MODULATION 0xFF17 45762306a36Sopenharmony_ci#define RID_OPTIONS 0xFF18 45862306a36Sopenharmony_ci#define RID_ACTUALCONFIG 0xFF20 /*readonly*/ 45962306a36Sopenharmony_ci#define RID_FACTORYCONFIG 0xFF21 46062306a36Sopenharmony_ci#define RID_UNKNOWN22 0xFF22 46162306a36Sopenharmony_ci#define RID_LEAPUSERNAME 0xFF23 46262306a36Sopenharmony_ci#define RID_LEAPPASSWORD 0xFF24 46362306a36Sopenharmony_ci#define RID_STATUS 0xFF50 46462306a36Sopenharmony_ci#define RID_BEACON_HST 0xFF51 46562306a36Sopenharmony_ci#define RID_BUSY_HST 0xFF52 46662306a36Sopenharmony_ci#define RID_RETRIES_HST 0xFF53 46762306a36Sopenharmony_ci#define RID_UNKNOWN54 0xFF54 46862306a36Sopenharmony_ci#define RID_UNKNOWN55 0xFF55 46962306a36Sopenharmony_ci#define RID_UNKNOWN56 0xFF56 47062306a36Sopenharmony_ci#define RID_MIC 0xFF57 47162306a36Sopenharmony_ci#define RID_STATS16 0xFF60 47262306a36Sopenharmony_ci#define RID_STATS16DELTA 0xFF61 47362306a36Sopenharmony_ci#define RID_STATS16DELTACLEAR 0xFF62 47462306a36Sopenharmony_ci#define RID_STATS 0xFF68 47562306a36Sopenharmony_ci#define RID_STATSDELTA 0xFF69 47662306a36Sopenharmony_ci#define RID_STATSDELTACLEAR 0xFF6A 47762306a36Sopenharmony_ci#define RID_ECHOTEST_RID 0xFF70 47862306a36Sopenharmony_ci#define RID_ECHOTEST_RESULTS 0xFF71 47962306a36Sopenharmony_ci#define RID_BSSLISTFIRST 0xFF72 48062306a36Sopenharmony_ci#define RID_BSSLISTNEXT 0xFF73 48162306a36Sopenharmony_ci#define RID_WPA_BSSLISTFIRST 0xFF74 48262306a36Sopenharmony_ci#define RID_WPA_BSSLISTNEXT 0xFF75 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_citypedef struct { 48562306a36Sopenharmony_ci u16 cmd; 48662306a36Sopenharmony_ci u16 parm0; 48762306a36Sopenharmony_ci u16 parm1; 48862306a36Sopenharmony_ci u16 parm2; 48962306a36Sopenharmony_ci} Cmd; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_citypedef struct { 49262306a36Sopenharmony_ci u16 status; 49362306a36Sopenharmony_ci u16 rsp0; 49462306a36Sopenharmony_ci u16 rsp1; 49562306a36Sopenharmony_ci u16 rsp2; 49662306a36Sopenharmony_ci} Resp; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* 49962306a36Sopenharmony_ci * Rids and endian-ness: The Rids will always be in cpu endian, since 50062306a36Sopenharmony_ci * this all the patches from the big-endian guys end up doing that. 50162306a36Sopenharmony_ci * so all rid access should use the read/writeXXXRid routines. 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/* This structure came from an email sent to me from an engineer at 50562306a36Sopenharmony_ci aironet for inclusion into this driver */ 50662306a36Sopenharmony_citypedef struct WepKeyRid WepKeyRid; 50762306a36Sopenharmony_cistruct WepKeyRid { 50862306a36Sopenharmony_ci __le16 len; 50962306a36Sopenharmony_ci __le16 kindex; 51062306a36Sopenharmony_ci u8 mac[ETH_ALEN]; 51162306a36Sopenharmony_ci __le16 klen; 51262306a36Sopenharmony_ci u8 key[16]; 51362306a36Sopenharmony_ci} __packed; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci/* These structures are from the Aironet's PC4500 Developers Manual */ 51662306a36Sopenharmony_citypedef struct Ssid Ssid; 51762306a36Sopenharmony_cistruct Ssid { 51862306a36Sopenharmony_ci __le16 len; 51962306a36Sopenharmony_ci u8 ssid[32]; 52062306a36Sopenharmony_ci} __packed; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_citypedef struct SsidRid SsidRid; 52362306a36Sopenharmony_cistruct SsidRid { 52462306a36Sopenharmony_ci __le16 len; 52562306a36Sopenharmony_ci Ssid ssids[3]; 52662306a36Sopenharmony_ci} __packed; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_citypedef struct ModulationRid ModulationRid; 52962306a36Sopenharmony_cistruct ModulationRid { 53062306a36Sopenharmony_ci __le16 len; 53162306a36Sopenharmony_ci __le16 modulation; 53262306a36Sopenharmony_ci#define MOD_DEFAULT cpu_to_le16(0) 53362306a36Sopenharmony_ci#define MOD_CCK cpu_to_le16(1) 53462306a36Sopenharmony_ci#define MOD_MOK cpu_to_le16(2) 53562306a36Sopenharmony_ci} __packed; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_citypedef struct ConfigRid ConfigRid; 53862306a36Sopenharmony_cistruct ConfigRid { 53962306a36Sopenharmony_ci __le16 len; /* sizeof(ConfigRid) */ 54062306a36Sopenharmony_ci __le16 opmode; /* operating mode */ 54162306a36Sopenharmony_ci#define MODE_STA_IBSS cpu_to_le16(0) 54262306a36Sopenharmony_ci#define MODE_STA_ESS cpu_to_le16(1) 54362306a36Sopenharmony_ci#define MODE_AP cpu_to_le16(2) 54462306a36Sopenharmony_ci#define MODE_AP_RPTR cpu_to_le16(3) 54562306a36Sopenharmony_ci#define MODE_CFG_MASK cpu_to_le16(0xff) 54662306a36Sopenharmony_ci#define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */ 54762306a36Sopenharmony_ci#define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */ 54862306a36Sopenharmony_ci#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extensions */ 54962306a36Sopenharmony_ci#define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */ 55062306a36Sopenharmony_ci#define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */ 55162306a36Sopenharmony_ci#define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */ 55262306a36Sopenharmony_ci#define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */ 55362306a36Sopenharmony_ci#define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */ 55462306a36Sopenharmony_ci#define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */ 55562306a36Sopenharmony_ci __le16 rmode; /* receive mode */ 55662306a36Sopenharmony_ci#define RXMODE_BC_MC_ADDR cpu_to_le16(0) 55762306a36Sopenharmony_ci#define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */ 55862306a36Sopenharmony_ci#define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */ 55962306a36Sopenharmony_ci#define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */ 56062306a36Sopenharmony_ci#define RXMODE_RFMON_ANYBSS cpu_to_le16(4) 56162306a36Sopenharmony_ci#define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */ 56262306a36Sopenharmony_ci#define RXMODE_MASK cpu_to_le16(255) 56362306a36Sopenharmony_ci#define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */ 56462306a36Sopenharmony_ci#define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER) 56562306a36Sopenharmony_ci#define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */ 56662306a36Sopenharmony_ci __le16 fragThresh; 56762306a36Sopenharmony_ci __le16 rtsThres; 56862306a36Sopenharmony_ci u8 macAddr[ETH_ALEN]; 56962306a36Sopenharmony_ci u8 rates[8]; 57062306a36Sopenharmony_ci __le16 shortRetryLimit; 57162306a36Sopenharmony_ci __le16 longRetryLimit; 57262306a36Sopenharmony_ci __le16 txLifetime; /* in kusec */ 57362306a36Sopenharmony_ci __le16 rxLifetime; /* in kusec */ 57462306a36Sopenharmony_ci __le16 stationary; 57562306a36Sopenharmony_ci __le16 ordering; 57662306a36Sopenharmony_ci __le16 u16deviceType; /* for overriding device type */ 57762306a36Sopenharmony_ci __le16 cfpRate; 57862306a36Sopenharmony_ci __le16 cfpDuration; 57962306a36Sopenharmony_ci __le16 _reserved1[3]; 58062306a36Sopenharmony_ci /*---------- Scanning/Associating ----------*/ 58162306a36Sopenharmony_ci __le16 scanMode; 58262306a36Sopenharmony_ci#define SCANMODE_ACTIVE cpu_to_le16(0) 58362306a36Sopenharmony_ci#define SCANMODE_PASSIVE cpu_to_le16(1) 58462306a36Sopenharmony_ci#define SCANMODE_AIROSCAN cpu_to_le16(2) 58562306a36Sopenharmony_ci __le16 probeDelay; /* in kusec */ 58662306a36Sopenharmony_ci __le16 probeEnergyTimeout; /* in kusec */ 58762306a36Sopenharmony_ci __le16 probeResponseTimeout; 58862306a36Sopenharmony_ci __le16 beaconListenTimeout; 58962306a36Sopenharmony_ci __le16 joinNetTimeout; 59062306a36Sopenharmony_ci __le16 authTimeout; 59162306a36Sopenharmony_ci __le16 authType; 59262306a36Sopenharmony_ci#define AUTH_OPEN cpu_to_le16(0x1) 59362306a36Sopenharmony_ci#define AUTH_ENCRYPT cpu_to_le16(0x101) 59462306a36Sopenharmony_ci#define AUTH_SHAREDKEY cpu_to_le16(0x102) 59562306a36Sopenharmony_ci#define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200) 59662306a36Sopenharmony_ci __le16 associationTimeout; 59762306a36Sopenharmony_ci __le16 specifiedApTimeout; 59862306a36Sopenharmony_ci __le16 offlineScanInterval; 59962306a36Sopenharmony_ci __le16 offlineScanDuration; 60062306a36Sopenharmony_ci __le16 linkLossDelay; 60162306a36Sopenharmony_ci __le16 maxBeaconLostTime; 60262306a36Sopenharmony_ci __le16 refreshInterval; 60362306a36Sopenharmony_ci#define DISABLE_REFRESH cpu_to_le16(0xFFFF) 60462306a36Sopenharmony_ci __le16 _reserved1a[1]; 60562306a36Sopenharmony_ci /*---------- Power save operation ----------*/ 60662306a36Sopenharmony_ci __le16 powerSaveMode; 60762306a36Sopenharmony_ci#define POWERSAVE_CAM cpu_to_le16(0) 60862306a36Sopenharmony_ci#define POWERSAVE_PSP cpu_to_le16(1) 60962306a36Sopenharmony_ci#define POWERSAVE_PSPCAM cpu_to_le16(2) 61062306a36Sopenharmony_ci __le16 sleepForDtims; 61162306a36Sopenharmony_ci __le16 listenInterval; 61262306a36Sopenharmony_ci __le16 fastListenInterval; 61362306a36Sopenharmony_ci __le16 listenDecay; 61462306a36Sopenharmony_ci __le16 fastListenDelay; 61562306a36Sopenharmony_ci __le16 _reserved2[2]; 61662306a36Sopenharmony_ci /*---------- Ap/Ibss config items ----------*/ 61762306a36Sopenharmony_ci __le16 beaconPeriod; 61862306a36Sopenharmony_ci __le16 atimDuration; 61962306a36Sopenharmony_ci __le16 hopPeriod; 62062306a36Sopenharmony_ci __le16 channelSet; 62162306a36Sopenharmony_ci __le16 channel; 62262306a36Sopenharmony_ci __le16 dtimPeriod; 62362306a36Sopenharmony_ci __le16 bridgeDistance; 62462306a36Sopenharmony_ci __le16 radioID; 62562306a36Sopenharmony_ci /*---------- Radio configuration ----------*/ 62662306a36Sopenharmony_ci __le16 radioType; 62762306a36Sopenharmony_ci#define RADIOTYPE_DEFAULT cpu_to_le16(0) 62862306a36Sopenharmony_ci#define RADIOTYPE_802_11 cpu_to_le16(1) 62962306a36Sopenharmony_ci#define RADIOTYPE_LEGACY cpu_to_le16(2) 63062306a36Sopenharmony_ci u8 rxDiversity; 63162306a36Sopenharmony_ci u8 txDiversity; 63262306a36Sopenharmony_ci __le16 txPower; 63362306a36Sopenharmony_ci#define TXPOWER_DEFAULT 0 63462306a36Sopenharmony_ci __le16 rssiThreshold; 63562306a36Sopenharmony_ci#define RSSI_DEFAULT 0 63662306a36Sopenharmony_ci __le16 modulation; 63762306a36Sopenharmony_ci#define PREAMBLE_AUTO cpu_to_le16(0) 63862306a36Sopenharmony_ci#define PREAMBLE_LONG cpu_to_le16(1) 63962306a36Sopenharmony_ci#define PREAMBLE_SHORT cpu_to_le16(2) 64062306a36Sopenharmony_ci __le16 preamble; 64162306a36Sopenharmony_ci __le16 homeProduct; 64262306a36Sopenharmony_ci __le16 radioSpecific; 64362306a36Sopenharmony_ci /*---------- Aironet Extensions ----------*/ 64462306a36Sopenharmony_ci u8 nodeName[16]; 64562306a36Sopenharmony_ci __le16 arlThreshold; 64662306a36Sopenharmony_ci __le16 arlDecay; 64762306a36Sopenharmony_ci __le16 arlDelay; 64862306a36Sopenharmony_ci __le16 _reserved4[1]; 64962306a36Sopenharmony_ci /*---------- Aironet Extensions ----------*/ 65062306a36Sopenharmony_ci u8 magicAction; 65162306a36Sopenharmony_ci#define MAGIC_ACTION_STSCHG 1 65262306a36Sopenharmony_ci#define MAGIC_ACTION_RESUME 2 65362306a36Sopenharmony_ci#define MAGIC_IGNORE_MCAST (1<<8) 65462306a36Sopenharmony_ci#define MAGIC_IGNORE_BCAST (1<<9) 65562306a36Sopenharmony_ci#define MAGIC_SWITCH_TO_PSP (0<<10) 65662306a36Sopenharmony_ci#define MAGIC_STAY_IN_CAM (1<<10) 65762306a36Sopenharmony_ci u8 magicControl; 65862306a36Sopenharmony_ci __le16 autoWake; 65962306a36Sopenharmony_ci} __packed; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_citypedef struct StatusRid StatusRid; 66262306a36Sopenharmony_cistruct StatusRid { 66362306a36Sopenharmony_ci __le16 len; 66462306a36Sopenharmony_ci u8 mac[ETH_ALEN]; 66562306a36Sopenharmony_ci __le16 mode; 66662306a36Sopenharmony_ci __le16 errorCode; 66762306a36Sopenharmony_ci __le16 sigQuality; 66862306a36Sopenharmony_ci __le16 SSIDlen; 66962306a36Sopenharmony_ci char SSID[32]; 67062306a36Sopenharmony_ci char apName[16]; 67162306a36Sopenharmony_ci u8 bssid[4][ETH_ALEN]; 67262306a36Sopenharmony_ci __le16 beaconPeriod; 67362306a36Sopenharmony_ci __le16 dimPeriod; 67462306a36Sopenharmony_ci __le16 atimDuration; 67562306a36Sopenharmony_ci __le16 hopPeriod; 67662306a36Sopenharmony_ci __le16 channelSet; 67762306a36Sopenharmony_ci __le16 channel; 67862306a36Sopenharmony_ci __le16 hopsToBackbone; 67962306a36Sopenharmony_ci __le16 apTotalLoad; 68062306a36Sopenharmony_ci __le16 generatedLoad; 68162306a36Sopenharmony_ci __le16 accumulatedArl; 68262306a36Sopenharmony_ci __le16 signalQuality; 68362306a36Sopenharmony_ci __le16 currentXmitRate; 68462306a36Sopenharmony_ci __le16 apDevExtensions; 68562306a36Sopenharmony_ci __le16 normalizedSignalStrength; 68662306a36Sopenharmony_ci __le16 shortPreamble; 68762306a36Sopenharmony_ci u8 apIP[4]; 68862306a36Sopenharmony_ci u8 noisePercent; /* Noise percent in last second */ 68962306a36Sopenharmony_ci u8 noisedBm; /* Noise dBm in last second */ 69062306a36Sopenharmony_ci u8 noiseAvePercent; /* Noise percent in last minute */ 69162306a36Sopenharmony_ci u8 noiseAvedBm; /* Noise dBm in last minute */ 69262306a36Sopenharmony_ci u8 noiseMaxPercent; /* Highest noise percent in last minute */ 69362306a36Sopenharmony_ci u8 noiseMaxdBm; /* Highest noise dbm in last minute */ 69462306a36Sopenharmony_ci __le16 load; 69562306a36Sopenharmony_ci u8 carrier[4]; 69662306a36Sopenharmony_ci __le16 assocStatus; 69762306a36Sopenharmony_ci#define STAT_NOPACKETS 0 69862306a36Sopenharmony_ci#define STAT_NOCARRIERSET 10 69962306a36Sopenharmony_ci#define STAT_GOTCARRIERSET 11 70062306a36Sopenharmony_ci#define STAT_WRONGSSID 20 70162306a36Sopenharmony_ci#define STAT_BADCHANNEL 25 70262306a36Sopenharmony_ci#define STAT_BADBITRATES 30 70362306a36Sopenharmony_ci#define STAT_BADPRIVACY 35 70462306a36Sopenharmony_ci#define STAT_APFOUND 40 70562306a36Sopenharmony_ci#define STAT_APREJECTED 50 70662306a36Sopenharmony_ci#define STAT_AUTHENTICATING 60 70762306a36Sopenharmony_ci#define STAT_DEAUTHENTICATED 61 70862306a36Sopenharmony_ci#define STAT_AUTHTIMEOUT 62 70962306a36Sopenharmony_ci#define STAT_ASSOCIATING 70 71062306a36Sopenharmony_ci#define STAT_DEASSOCIATED 71 71162306a36Sopenharmony_ci#define STAT_ASSOCTIMEOUT 72 71262306a36Sopenharmony_ci#define STAT_NOTAIROAP 73 71362306a36Sopenharmony_ci#define STAT_ASSOCIATED 80 71462306a36Sopenharmony_ci#define STAT_LEAPING 90 71562306a36Sopenharmony_ci#define STAT_LEAPFAILED 91 71662306a36Sopenharmony_ci#define STAT_LEAPTIMEDOUT 92 71762306a36Sopenharmony_ci#define STAT_LEAPCOMPLETE 93 71862306a36Sopenharmony_ci} __packed; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_citypedef struct StatsRid StatsRid; 72162306a36Sopenharmony_cistruct StatsRid { 72262306a36Sopenharmony_ci __le16 len; 72362306a36Sopenharmony_ci __le16 spacer; 72462306a36Sopenharmony_ci __le32 vals[100]; 72562306a36Sopenharmony_ci} __packed; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_citypedef struct APListRid APListRid; 72862306a36Sopenharmony_cistruct APListRid { 72962306a36Sopenharmony_ci __le16 len; 73062306a36Sopenharmony_ci u8 ap[4][ETH_ALEN]; 73162306a36Sopenharmony_ci} __packed; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_citypedef struct CapabilityRid CapabilityRid; 73462306a36Sopenharmony_cistruct CapabilityRid { 73562306a36Sopenharmony_ci __le16 len; 73662306a36Sopenharmony_ci char oui[3]; 73762306a36Sopenharmony_ci char zero; 73862306a36Sopenharmony_ci __le16 prodNum; 73962306a36Sopenharmony_ci char manName[32]; 74062306a36Sopenharmony_ci char prodName[16]; 74162306a36Sopenharmony_ci char prodVer[8]; 74262306a36Sopenharmony_ci char factoryAddr[ETH_ALEN]; 74362306a36Sopenharmony_ci char aironetAddr[ETH_ALEN]; 74462306a36Sopenharmony_ci __le16 radioType; 74562306a36Sopenharmony_ci __le16 country; 74662306a36Sopenharmony_ci char callid[ETH_ALEN]; 74762306a36Sopenharmony_ci char supportedRates[8]; 74862306a36Sopenharmony_ci char rxDiversity; 74962306a36Sopenharmony_ci char txDiversity; 75062306a36Sopenharmony_ci __le16 txPowerLevels[8]; 75162306a36Sopenharmony_ci __le16 hardVer; 75262306a36Sopenharmony_ci __le16 hardCap; 75362306a36Sopenharmony_ci __le16 tempRange; 75462306a36Sopenharmony_ci __le16 softVer; 75562306a36Sopenharmony_ci __le16 softSubVer; 75662306a36Sopenharmony_ci __le16 interfaceVer; 75762306a36Sopenharmony_ci __le16 softCap; 75862306a36Sopenharmony_ci __le16 bootBlockVer; 75962306a36Sopenharmony_ci __le16 requiredHard; 76062306a36Sopenharmony_ci __le16 extSoftCap; 76162306a36Sopenharmony_ci} __packed; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci/* Only present on firmware >= 5.30.17 */ 76462306a36Sopenharmony_citypedef struct BSSListRidExtra BSSListRidExtra; 76562306a36Sopenharmony_cistruct BSSListRidExtra { 76662306a36Sopenharmony_ci __le16 unknown[4]; 76762306a36Sopenharmony_ci u8 fixed[12]; /* WLAN management frame */ 76862306a36Sopenharmony_ci u8 iep[624]; 76962306a36Sopenharmony_ci} __packed; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_citypedef struct BSSListRid BSSListRid; 77262306a36Sopenharmony_cistruct BSSListRid { 77362306a36Sopenharmony_ci __le16 len; 77462306a36Sopenharmony_ci __le16 index; /* First is 0 and 0xffff means end of list */ 77562306a36Sopenharmony_ci#define RADIO_FH 1 /* Frequency hopping radio type */ 77662306a36Sopenharmony_ci#define RADIO_DS 2 /* Direct sequence radio type */ 77762306a36Sopenharmony_ci#define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */ 77862306a36Sopenharmony_ci __le16 radioType; 77962306a36Sopenharmony_ci u8 bssid[ETH_ALEN]; /* Mac address of the BSS */ 78062306a36Sopenharmony_ci u8 zero; 78162306a36Sopenharmony_ci u8 ssidLen; 78262306a36Sopenharmony_ci u8 ssid[32]; 78362306a36Sopenharmony_ci __le16 dBm; 78462306a36Sopenharmony_ci#define CAP_ESS cpu_to_le16(1<<0) 78562306a36Sopenharmony_ci#define CAP_IBSS cpu_to_le16(1<<1) 78662306a36Sopenharmony_ci#define CAP_PRIVACY cpu_to_le16(1<<4) 78762306a36Sopenharmony_ci#define CAP_SHORTHDR cpu_to_le16(1<<5) 78862306a36Sopenharmony_ci __le16 cap; 78962306a36Sopenharmony_ci __le16 beaconInterval; 79062306a36Sopenharmony_ci u8 rates[8]; /* Same as rates for config rid */ 79162306a36Sopenharmony_ci struct { /* For frequency hopping only */ 79262306a36Sopenharmony_ci __le16 dwell; 79362306a36Sopenharmony_ci u8 hopSet; 79462306a36Sopenharmony_ci u8 hopPattern; 79562306a36Sopenharmony_ci u8 hopIndex; 79662306a36Sopenharmony_ci u8 fill; 79762306a36Sopenharmony_ci } fh; 79862306a36Sopenharmony_ci __le16 dsChannel; 79962306a36Sopenharmony_ci __le16 atimWindow; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Only present on firmware >= 5.30.17 */ 80262306a36Sopenharmony_ci BSSListRidExtra extra; 80362306a36Sopenharmony_ci} __packed; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_citypedef struct { 80662306a36Sopenharmony_ci BSSListRid bss; 80762306a36Sopenharmony_ci struct list_head list; 80862306a36Sopenharmony_ci} BSSListElement; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_citypedef struct tdsRssiEntry tdsRssiEntry; 81162306a36Sopenharmony_cistruct tdsRssiEntry { 81262306a36Sopenharmony_ci u8 rssipct; 81362306a36Sopenharmony_ci u8 rssidBm; 81462306a36Sopenharmony_ci} __packed; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_citypedef struct tdsRssiRid tdsRssiRid; 81762306a36Sopenharmony_cistruct tdsRssiRid { 81862306a36Sopenharmony_ci u16 len; 81962306a36Sopenharmony_ci tdsRssiEntry x[256]; 82062306a36Sopenharmony_ci} __packed; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_citypedef struct MICRid MICRid; 82362306a36Sopenharmony_cistruct MICRid { 82462306a36Sopenharmony_ci __le16 len; 82562306a36Sopenharmony_ci __le16 state; 82662306a36Sopenharmony_ci __le16 multicastValid; 82762306a36Sopenharmony_ci u8 multicast[16]; 82862306a36Sopenharmony_ci __le16 unicastValid; 82962306a36Sopenharmony_ci u8 unicast[16]; 83062306a36Sopenharmony_ci} __packed; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_citypedef struct MICBuffer MICBuffer; 83362306a36Sopenharmony_cistruct MICBuffer { 83462306a36Sopenharmony_ci __be16 typelen; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci union { 83762306a36Sopenharmony_ci u8 snap[8]; 83862306a36Sopenharmony_ci struct { 83962306a36Sopenharmony_ci u8 dsap; 84062306a36Sopenharmony_ci u8 ssap; 84162306a36Sopenharmony_ci u8 control; 84262306a36Sopenharmony_ci u8 orgcode[3]; 84362306a36Sopenharmony_ci u8 fieldtype[2]; 84462306a36Sopenharmony_ci } llc; 84562306a36Sopenharmony_ci } u; 84662306a36Sopenharmony_ci __be32 mic; 84762306a36Sopenharmony_ci __be32 seq; 84862306a36Sopenharmony_ci} __packed; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_citypedef struct { 85162306a36Sopenharmony_ci u8 da[ETH_ALEN]; 85262306a36Sopenharmony_ci u8 sa[ETH_ALEN]; 85362306a36Sopenharmony_ci} etherHead; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci#define TXCTL_TXOK (1<<1) /* report if tx is ok */ 85662306a36Sopenharmony_ci#define TXCTL_TXEX (1<<2) /* report if tx fails */ 85762306a36Sopenharmony_ci#define TXCTL_802_3 (0<<3) /* 802.3 packet */ 85862306a36Sopenharmony_ci#define TXCTL_802_11 (1<<3) /* 802.11 mac packet */ 85962306a36Sopenharmony_ci#define TXCTL_ETHERNET (0<<4) /* payload has ethertype */ 86062306a36Sopenharmony_ci#define TXCTL_LLC (1<<4) /* payload is llc */ 86162306a36Sopenharmony_ci#define TXCTL_RELEASE (0<<5) /* release after completion */ 86262306a36Sopenharmony_ci#define TXCTL_NORELEASE (1<<5) /* on completion returns to host */ 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci#define BUSY_FID 0x10000 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci#ifdef CISCO_EXT 86762306a36Sopenharmony_ci#define AIROMAGIC 0xa55a 86862306a36Sopenharmony_ci/* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */ 86962306a36Sopenharmony_ci#ifdef SIOCIWFIRSTPRIV 87062306a36Sopenharmony_ci#ifdef SIOCDEVPRIVATE 87162306a36Sopenharmony_ci#define AIROOLDIOCTL SIOCDEVPRIVATE 87262306a36Sopenharmony_ci#define AIROOLDIDIFC AIROOLDIOCTL + 1 87362306a36Sopenharmony_ci#endif /* SIOCDEVPRIVATE */ 87462306a36Sopenharmony_ci#else /* SIOCIWFIRSTPRIV */ 87562306a36Sopenharmony_ci#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE 87662306a36Sopenharmony_ci#endif /* SIOCIWFIRSTPRIV */ 87762306a36Sopenharmony_ci/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably 87862306a36Sopenharmony_ci * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root 87962306a36Sopenharmony_ci * only and don't return the modified struct ifreq to the application which 88062306a36Sopenharmony_ci * is usually a problem. - Jean II */ 88162306a36Sopenharmony_ci#define AIROIOCTL SIOCIWFIRSTPRIV 88262306a36Sopenharmony_ci#define AIROIDIFC AIROIOCTL + 1 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci/* Ioctl constants to be used in airo_ioctl.command */ 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci#define AIROGCAP 0 // Capability rid 88762306a36Sopenharmony_ci#define AIROGCFG 1 // USED A LOT 88862306a36Sopenharmony_ci#define AIROGSLIST 2 // System ID list 88962306a36Sopenharmony_ci#define AIROGVLIST 3 // List of specified AP's 89062306a36Sopenharmony_ci#define AIROGDRVNAM 4 // NOTUSED 89162306a36Sopenharmony_ci#define AIROGEHTENC 5 // NOTUSED 89262306a36Sopenharmony_ci#define AIROGWEPKTMP 6 89362306a36Sopenharmony_ci#define AIROGWEPKNV 7 89462306a36Sopenharmony_ci#define AIROGSTAT 8 89562306a36Sopenharmony_ci#define AIROGSTATSC32 9 89662306a36Sopenharmony_ci#define AIROGSTATSD32 10 89762306a36Sopenharmony_ci#define AIROGMICRID 11 89862306a36Sopenharmony_ci#define AIROGMICSTATS 12 89962306a36Sopenharmony_ci#define AIROGFLAGS 13 90062306a36Sopenharmony_ci#define AIROGID 14 90162306a36Sopenharmony_ci#define AIRORRID 15 90262306a36Sopenharmony_ci#define AIRORSWVERSION 17 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci/* Leave gap of 40 commands after AIROGSTATSD32 for future */ 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci#define AIROPCAP AIROGSTATSD32 + 40 90762306a36Sopenharmony_ci#define AIROPVLIST AIROPCAP + 1 90862306a36Sopenharmony_ci#define AIROPSLIST AIROPVLIST + 1 90962306a36Sopenharmony_ci#define AIROPCFG AIROPSLIST + 1 91062306a36Sopenharmony_ci#define AIROPSIDS AIROPCFG + 1 91162306a36Sopenharmony_ci#define AIROPAPLIST AIROPSIDS + 1 91262306a36Sopenharmony_ci#define AIROPMACON AIROPAPLIST + 1 /* Enable mac */ 91362306a36Sopenharmony_ci#define AIROPMACOFF AIROPMACON + 1 /* Disable mac */ 91462306a36Sopenharmony_ci#define AIROPSTCLR AIROPMACOFF + 1 91562306a36Sopenharmony_ci#define AIROPWEPKEY AIROPSTCLR + 1 91662306a36Sopenharmony_ci#define AIROPWEPKEYNV AIROPWEPKEY + 1 91762306a36Sopenharmony_ci#define AIROPLEAPPWD AIROPWEPKEYNV + 1 91862306a36Sopenharmony_ci#define AIROPLEAPUSR AIROPLEAPPWD + 1 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci/* Flash codes */ 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci#define AIROFLSHRST AIROPWEPKEYNV + 40 92362306a36Sopenharmony_ci#define AIROFLSHGCHR AIROFLSHRST + 1 92462306a36Sopenharmony_ci#define AIROFLSHSTFL AIROFLSHGCHR + 1 92562306a36Sopenharmony_ci#define AIROFLSHPCHR AIROFLSHSTFL + 1 92662306a36Sopenharmony_ci#define AIROFLPUTBUF AIROFLSHPCHR + 1 92762306a36Sopenharmony_ci#define AIRORESTART AIROFLPUTBUF + 1 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci#define FLASHSIZE 32768 93062306a36Sopenharmony_ci#define AUXMEMSIZE (256 * 1024) 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_citypedef struct aironet_ioctl { 93362306a36Sopenharmony_ci unsigned short command; // What to do 93462306a36Sopenharmony_ci unsigned short len; // Len of data 93562306a36Sopenharmony_ci unsigned short ridnum; // rid number 93662306a36Sopenharmony_ci unsigned char __user *data; // d-data 93762306a36Sopenharmony_ci} aironet_ioctl; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic const char swversion[] = "2.1"; 94062306a36Sopenharmony_ci#endif /* CISCO_EXT */ 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci#define NUM_MODULES 2 94362306a36Sopenharmony_ci#define MIC_MSGLEN_MAX 2400 94462306a36Sopenharmony_ci#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX 94562306a36Sopenharmony_ci#define AIRO_DEF_MTU 2312 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_citypedef struct { 94862306a36Sopenharmony_ci u32 size; // size 94962306a36Sopenharmony_ci u8 enabled; // MIC enabled or not 95062306a36Sopenharmony_ci u32 rxSuccess; // successful packets received 95162306a36Sopenharmony_ci u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison 95262306a36Sopenharmony_ci u32 rxNotMICed; // pkts dropped due to not being MIC'd 95362306a36Sopenharmony_ci u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed 95462306a36Sopenharmony_ci u32 rxWrongSequence; // pkts dropped due to sequence number violation 95562306a36Sopenharmony_ci u32 reserve[32]; 95662306a36Sopenharmony_ci} mic_statistics; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_citypedef struct { 95962306a36Sopenharmony_ci __be32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2]; 96062306a36Sopenharmony_ci u64 accum; // accumulated mic, reduced to u32 in final() 96162306a36Sopenharmony_ci int position; // current position (byte offset) in message 96262306a36Sopenharmony_ci union { 96362306a36Sopenharmony_ci u8 d8[4]; 96462306a36Sopenharmony_ci __be32 d32; 96562306a36Sopenharmony_ci } part; // saves partial message word across update() calls 96662306a36Sopenharmony_ci} emmh32_context; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_citypedef struct { 96962306a36Sopenharmony_ci emmh32_context seed; // Context - the seed 97062306a36Sopenharmony_ci u32 rx; // Received sequence number 97162306a36Sopenharmony_ci u32 tx; // Tx sequence number 97262306a36Sopenharmony_ci u32 window; // Start of window 97362306a36Sopenharmony_ci u8 valid; // Flag to say if context is valid or not 97462306a36Sopenharmony_ci u8 key[16]; 97562306a36Sopenharmony_ci} miccntx; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_citypedef struct { 97862306a36Sopenharmony_ci miccntx mCtx; // Multicast context 97962306a36Sopenharmony_ci miccntx uCtx; // Unicast context 98062306a36Sopenharmony_ci} mic_module; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_citypedef struct { 98362306a36Sopenharmony_ci unsigned int rid: 16; 98462306a36Sopenharmony_ci unsigned int len: 15; 98562306a36Sopenharmony_ci unsigned int valid: 1; 98662306a36Sopenharmony_ci dma_addr_t host_addr; 98762306a36Sopenharmony_ci} Rid; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_citypedef struct { 99062306a36Sopenharmony_ci unsigned int offset: 15; 99162306a36Sopenharmony_ci unsigned int eoc: 1; 99262306a36Sopenharmony_ci unsigned int len: 15; 99362306a36Sopenharmony_ci unsigned int valid: 1; 99462306a36Sopenharmony_ci dma_addr_t host_addr; 99562306a36Sopenharmony_ci} TxFid; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistruct rx_hdr { 99862306a36Sopenharmony_ci __le16 status, len; 99962306a36Sopenharmony_ci u8 rssi[2]; 100062306a36Sopenharmony_ci u8 rate; 100162306a36Sopenharmony_ci u8 freq; 100262306a36Sopenharmony_ci __le16 tmp[4]; 100362306a36Sopenharmony_ci} __packed; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_citypedef struct { 100662306a36Sopenharmony_ci unsigned int ctl: 15; 100762306a36Sopenharmony_ci unsigned int rdy: 1; 100862306a36Sopenharmony_ci unsigned int len: 15; 100962306a36Sopenharmony_ci unsigned int valid: 1; 101062306a36Sopenharmony_ci dma_addr_t host_addr; 101162306a36Sopenharmony_ci} RxFid; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci/* 101462306a36Sopenharmony_ci * Host receive descriptor 101562306a36Sopenharmony_ci */ 101662306a36Sopenharmony_citypedef struct { 101762306a36Sopenharmony_ci unsigned char __iomem *card_ram_off; /* offset into card memory of the 101862306a36Sopenharmony_ci desc */ 101962306a36Sopenharmony_ci RxFid rx_desc; /* card receive descriptor */ 102062306a36Sopenharmony_ci char *virtual_host_addr; /* virtual address of host receive 102162306a36Sopenharmony_ci buffer */ 102262306a36Sopenharmony_ci int pending; 102362306a36Sopenharmony_ci} HostRxDesc; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci/* 102662306a36Sopenharmony_ci * Host transmit descriptor 102762306a36Sopenharmony_ci */ 102862306a36Sopenharmony_citypedef struct { 102962306a36Sopenharmony_ci unsigned char __iomem *card_ram_off; /* offset into card memory of the 103062306a36Sopenharmony_ci desc */ 103162306a36Sopenharmony_ci TxFid tx_desc; /* card transmit descriptor */ 103262306a36Sopenharmony_ci char *virtual_host_addr; /* virtual address of host receive 103362306a36Sopenharmony_ci buffer */ 103462306a36Sopenharmony_ci int pending; 103562306a36Sopenharmony_ci} HostTxDesc; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci/* 103862306a36Sopenharmony_ci * Host RID descriptor 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_citypedef struct { 104162306a36Sopenharmony_ci unsigned char __iomem *card_ram_off; /* offset into card memory of the 104262306a36Sopenharmony_ci descriptor */ 104362306a36Sopenharmony_ci Rid rid_desc; /* card RID descriptor */ 104462306a36Sopenharmony_ci char *virtual_host_addr; /* virtual address of host receive 104562306a36Sopenharmony_ci buffer */ 104662306a36Sopenharmony_ci} HostRidDesc; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_citypedef struct { 104962306a36Sopenharmony_ci u16 sw0; 105062306a36Sopenharmony_ci u16 sw1; 105162306a36Sopenharmony_ci u16 status; 105262306a36Sopenharmony_ci u16 len; 105362306a36Sopenharmony_ci#define HOST_SET (1 << 0) 105462306a36Sopenharmony_ci#define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */ 105562306a36Sopenharmony_ci#define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */ 105662306a36Sopenharmony_ci#define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */ 105762306a36Sopenharmony_ci#define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */ 105862306a36Sopenharmony_ci#define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */ 105962306a36Sopenharmony_ci#define HOST_CLR_AID (1 << 7) /* clear AID failure */ 106062306a36Sopenharmony_ci#define HOST_RTS (1 << 9) /* Force RTS use */ 106162306a36Sopenharmony_ci#define HOST_SHORT (1 << 10) /* Do short preamble */ 106262306a36Sopenharmony_ci u16 ctl; 106362306a36Sopenharmony_ci u16 aid; 106462306a36Sopenharmony_ci u16 retries; 106562306a36Sopenharmony_ci u16 fill; 106662306a36Sopenharmony_ci} TxCtlHdr; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_citypedef struct { 106962306a36Sopenharmony_ci u16 ctl; 107062306a36Sopenharmony_ci u16 duration; 107162306a36Sopenharmony_ci char addr1[6]; 107262306a36Sopenharmony_ci char addr2[6]; 107362306a36Sopenharmony_ci char addr3[6]; 107462306a36Sopenharmony_ci u16 seq; 107562306a36Sopenharmony_ci char addr4[6]; 107662306a36Sopenharmony_ci} WifiHdr; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_citypedef struct { 108062306a36Sopenharmony_ci TxCtlHdr ctlhdr; 108162306a36Sopenharmony_ci u16 fill1; 108262306a36Sopenharmony_ci u16 fill2; 108362306a36Sopenharmony_ci WifiHdr wifihdr; 108462306a36Sopenharmony_ci u16 gaplen; 108562306a36Sopenharmony_ci u16 status; 108662306a36Sopenharmony_ci} WifiCtlHdr; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic WifiCtlHdr wifictlhdr8023 = { 108962306a36Sopenharmony_ci .ctlhdr = { 109062306a36Sopenharmony_ci .ctl = HOST_DONT_RLSE, 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci}; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci// A few details needed for WEP (Wireless Equivalent Privacy) 109562306a36Sopenharmony_ci#define MAX_KEY_SIZE 13 // 128 (?) bits 109662306a36Sopenharmony_ci#define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP 109762306a36Sopenharmony_citypedef struct wep_key_t { 109862306a36Sopenharmony_ci u16 len; 109962306a36Sopenharmony_ci u8 key[16]; /* 40-bit and 104-bit keys */ 110062306a36Sopenharmony_ci} wep_key_t; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci/* List of Wireless Handlers (new API) */ 110362306a36Sopenharmony_cistatic const struct iw_handler_def airo_handler_def; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)"; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistruct airo_info; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic int get_dec_u16(char *buffer, int *start, int limit); 111062306a36Sopenharmony_cistatic void OUT4500(struct airo_info *, u16 reg, u16 value); 111162306a36Sopenharmony_cistatic unsigned short IN4500(struct airo_info *, u16 reg); 111262306a36Sopenharmony_cistatic u16 setup_card(struct airo_info*, struct net_device *dev, int lock); 111362306a36Sopenharmony_cistatic int enable_MAC(struct airo_info *ai, int lock); 111462306a36Sopenharmony_cistatic void disable_MAC(struct airo_info *ai, int lock); 111562306a36Sopenharmony_cistatic void enable_interrupts(struct airo_info*); 111662306a36Sopenharmony_cistatic void disable_interrupts(struct airo_info*); 111762306a36Sopenharmony_cistatic u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp, 111862306a36Sopenharmony_ci bool may_sleep); 111962306a36Sopenharmony_cistatic int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap); 112062306a36Sopenharmony_cistatic int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen, 112162306a36Sopenharmony_ci int whichbap); 112262306a36Sopenharmony_cistatic int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen, 112362306a36Sopenharmony_ci int whichbap); 112462306a36Sopenharmony_cistatic int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen, 112562306a36Sopenharmony_ci int whichbap); 112662306a36Sopenharmony_cistatic int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd); 112762306a36Sopenharmony_cistatic int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock); 112862306a36Sopenharmony_cistatic int PC4500_writerid(struct airo_info*, u16 rid, const void 112962306a36Sopenharmony_ci *pBuf, int len, int lock); 113062306a36Sopenharmony_cistatic int do_writerid(struct airo_info*, u16 rid, const void *rid_data, 113162306a36Sopenharmony_ci int len, int dummy); 113262306a36Sopenharmony_cistatic u16 transmit_allocate(struct airo_info*, int lenPayload, int raw); 113362306a36Sopenharmony_cistatic int transmit_802_3_packet(struct airo_info*, int len, char *pPacket, 113462306a36Sopenharmony_ci bool may_sleep); 113562306a36Sopenharmony_cistatic int transmit_802_11_packet(struct airo_info*, int len, char *pPacket, 113662306a36Sopenharmony_ci bool may_sleep); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_cistatic int mpi_send_packet(struct net_device *dev); 113962306a36Sopenharmony_cistatic void mpi_unmap_card(struct pci_dev *pci); 114062306a36Sopenharmony_cistatic void mpi_receive_802_3(struct airo_info *ai); 114162306a36Sopenharmony_cistatic void mpi_receive_802_11(struct airo_info *ai); 114262306a36Sopenharmony_cistatic int waitbusy(struct airo_info *ai); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_cistatic irqreturn_t airo_interrupt(int irq, void* dev_id); 114562306a36Sopenharmony_cistatic int airo_thread(void *data); 114662306a36Sopenharmony_cistatic void timer_func(struct net_device *dev); 114762306a36Sopenharmony_cistatic int airo_siocdevprivate(struct net_device *dev, struct ifreq *rq, void __user *, int cmd); 114862306a36Sopenharmony_cistatic struct iw_statistics *airo_get_wireless_stats(struct net_device *dev); 114962306a36Sopenharmony_ci#ifdef CISCO_EXT 115062306a36Sopenharmony_cistatic int readrids(struct net_device *dev, aironet_ioctl *comp); 115162306a36Sopenharmony_cistatic int writerids(struct net_device *dev, aironet_ioctl *comp); 115262306a36Sopenharmony_cistatic int flashcard(struct net_device *dev, aironet_ioctl *comp); 115362306a36Sopenharmony_ci#endif /* CISCO_EXT */ 115462306a36Sopenharmony_cistatic void micinit(struct airo_info *ai); 115562306a36Sopenharmony_cistatic int micsetup(struct airo_info *ai); 115662306a36Sopenharmony_cistatic int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len); 115762306a36Sopenharmony_cistatic int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic u8 airo_rssi_to_dbm(tdsRssiEntry *rssi_rid, u8 rssi); 116062306a36Sopenharmony_cistatic u8 airo_dbm_to_pct(tdsRssiEntry *rssi_rid, u8 dbm); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cistatic void airo_networks_free(struct airo_info *ai); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistruct airo_info { 116562306a36Sopenharmony_ci struct net_device *dev; 116662306a36Sopenharmony_ci struct list_head dev_list; 116762306a36Sopenharmony_ci /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we 116862306a36Sopenharmony_ci use the high bit to mark whether it is in use. */ 116962306a36Sopenharmony_ci#define MAX_FIDS 6 117062306a36Sopenharmony_ci#define MPI_MAX_FIDS 1 117162306a36Sopenharmony_ci u32 fids[MAX_FIDS]; 117262306a36Sopenharmony_ci ConfigRid config; 117362306a36Sopenharmony_ci char keyindex; // Used with auto wep 117462306a36Sopenharmony_ci char defindex; // Used with auto wep 117562306a36Sopenharmony_ci struct proc_dir_entry *proc_entry; 117662306a36Sopenharmony_ci spinlock_t aux_lock; 117762306a36Sopenharmony_ci#define FLAG_RADIO_OFF 0 /* User disabling of MAC */ 117862306a36Sopenharmony_ci#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */ 117962306a36Sopenharmony_ci#define FLAG_RADIO_MASK 0x03 118062306a36Sopenharmony_ci#define FLAG_ENABLED 2 118162306a36Sopenharmony_ci#define FLAG_ADHOC 3 /* Needed by MIC */ 118262306a36Sopenharmony_ci#define FLAG_MIC_CAPABLE 4 118362306a36Sopenharmony_ci#define FLAG_UPDATE_MULTI 5 118462306a36Sopenharmony_ci#define FLAG_UPDATE_UNI 6 118562306a36Sopenharmony_ci#define FLAG_802_11 7 118662306a36Sopenharmony_ci#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */ 118762306a36Sopenharmony_ci#define FLAG_PENDING_XMIT 9 118862306a36Sopenharmony_ci#define FLAG_PENDING_XMIT11 10 118962306a36Sopenharmony_ci#define FLAG_MPI 11 119062306a36Sopenharmony_ci#define FLAG_REGISTERED 12 119162306a36Sopenharmony_ci#define FLAG_COMMIT 13 119262306a36Sopenharmony_ci#define FLAG_RESET 14 119362306a36Sopenharmony_ci#define FLAG_FLASHING 15 119462306a36Sopenharmony_ci#define FLAG_WPA_CAPABLE 16 119562306a36Sopenharmony_ci unsigned long flags; 119662306a36Sopenharmony_ci#define JOB_DIE 0 119762306a36Sopenharmony_ci#define JOB_XMIT 1 119862306a36Sopenharmony_ci#define JOB_XMIT11 2 119962306a36Sopenharmony_ci#define JOB_STATS 3 120062306a36Sopenharmony_ci#define JOB_PROMISC 4 120162306a36Sopenharmony_ci#define JOB_MIC 5 120262306a36Sopenharmony_ci#define JOB_EVENT 6 120362306a36Sopenharmony_ci#define JOB_AUTOWEP 7 120462306a36Sopenharmony_ci#define JOB_SCAN_RESULTS 9 120562306a36Sopenharmony_ci unsigned long jobs; 120662306a36Sopenharmony_ci int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen, 120762306a36Sopenharmony_ci int whichbap); 120862306a36Sopenharmony_ci unsigned short *flash; 120962306a36Sopenharmony_ci tdsRssiEntry *rssi; 121062306a36Sopenharmony_ci struct task_struct *list_bss_task; 121162306a36Sopenharmony_ci struct task_struct *airo_thread_task; 121262306a36Sopenharmony_ci struct semaphore sem; 121362306a36Sopenharmony_ci wait_queue_head_t thr_wait; 121462306a36Sopenharmony_ci unsigned long expires; 121562306a36Sopenharmony_ci struct { 121662306a36Sopenharmony_ci struct sk_buff *skb; 121762306a36Sopenharmony_ci int fid; 121862306a36Sopenharmony_ci } xmit, xmit11; 121962306a36Sopenharmony_ci struct net_device *wifidev; 122062306a36Sopenharmony_ci struct iw_statistics wstats; // wireless stats 122162306a36Sopenharmony_ci unsigned long scan_timeout; /* Time scan should be read */ 122262306a36Sopenharmony_ci struct iw_spy_data spy_data; 122362306a36Sopenharmony_ci struct iw_public_data wireless_data; 122462306a36Sopenharmony_ci /* MIC stuff */ 122562306a36Sopenharmony_ci struct crypto_sync_skcipher *tfm; 122662306a36Sopenharmony_ci mic_module mod[2]; 122762306a36Sopenharmony_ci mic_statistics micstats; 122862306a36Sopenharmony_ci HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors 122962306a36Sopenharmony_ci HostTxDesc txfids[MPI_MAX_FIDS]; 123062306a36Sopenharmony_ci HostRidDesc config_desc; 123162306a36Sopenharmony_ci unsigned long ridbus; // phys addr of config_desc 123262306a36Sopenharmony_ci struct sk_buff_head txq;// tx queue used by mpi350 code 123362306a36Sopenharmony_ci struct pci_dev *pci; 123462306a36Sopenharmony_ci unsigned char __iomem *pcimem; 123562306a36Sopenharmony_ci unsigned char __iomem *pciaux; 123662306a36Sopenharmony_ci unsigned char *shared; 123762306a36Sopenharmony_ci dma_addr_t shared_dma; 123862306a36Sopenharmony_ci pm_message_t power; 123962306a36Sopenharmony_ci SsidRid *SSID; 124062306a36Sopenharmony_ci APListRid APList; 124162306a36Sopenharmony_ci#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE 124262306a36Sopenharmony_ci char proc_name[IFNAMSIZ]; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci int wep_capable; 124562306a36Sopenharmony_ci int max_wep_idx; 124662306a36Sopenharmony_ci int last_auth; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci /* WPA-related stuff */ 124962306a36Sopenharmony_ci unsigned int bssListFirst; 125062306a36Sopenharmony_ci unsigned int bssListNext; 125162306a36Sopenharmony_ci unsigned int bssListRidLen; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci struct list_head network_list; 125462306a36Sopenharmony_ci struct list_head network_free_list; 125562306a36Sopenharmony_ci BSSListElement *networks; 125662306a36Sopenharmony_ci}; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen, 125962306a36Sopenharmony_ci int whichbap) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci return ai->bap_read(ai, pu16Dst, bytelen, whichbap); 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_cistatic int setup_proc_entry(struct net_device *dev, 126562306a36Sopenharmony_ci struct airo_info *apriv); 126662306a36Sopenharmony_cistatic int takedown_proc_entry(struct net_device *dev, 126762306a36Sopenharmony_ci struct airo_info *apriv); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic int cmdreset(struct airo_info *ai); 127062306a36Sopenharmony_cistatic int setflashmode(struct airo_info *ai); 127162306a36Sopenharmony_cistatic int flashgchar(struct airo_info *ai, int matchbyte, int dwelltime); 127262306a36Sopenharmony_cistatic int flashputbuf(struct airo_info *ai); 127362306a36Sopenharmony_cistatic int flashrestart(struct airo_info *ai, struct net_device *dev); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci#define airo_print(type, name, fmt, args...) \ 127662306a36Sopenharmony_ci printk(type DRV_NAME "(%s): " fmt "\n", name, ##args) 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci#define airo_print_info(name, fmt, args...) \ 127962306a36Sopenharmony_ci airo_print(KERN_INFO, name, fmt, ##args) 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci#define airo_print_dbg(name, fmt, args...) \ 128262306a36Sopenharmony_ci airo_print(KERN_DEBUG, name, fmt, ##args) 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci#define airo_print_warn(name, fmt, args...) \ 128562306a36Sopenharmony_ci airo_print(KERN_WARNING, name, fmt, ##args) 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci#define airo_print_err(name, fmt, args...) \ 128862306a36Sopenharmony_ci airo_print(KERN_ERR, name, fmt, ##args) 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash) 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci/*********************************************************************** 129362306a36Sopenharmony_ci * MIC ROUTINES * 129462306a36Sopenharmony_ci *********************************************************************** 129562306a36Sopenharmony_ci */ 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic int RxSeqValid(struct airo_info *ai, miccntx *context, int mcast, u32 micSeq); 129862306a36Sopenharmony_cistatic void MoveWindow(miccntx *context, u32 micSeq); 129962306a36Sopenharmony_cistatic void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, 130062306a36Sopenharmony_ci struct crypto_sync_skcipher *tfm); 130162306a36Sopenharmony_cistatic void emmh32_init(emmh32_context *context); 130262306a36Sopenharmony_cistatic void emmh32_update(emmh32_context *context, u8 *pOctets, int len); 130362306a36Sopenharmony_cistatic void emmh32_final(emmh32_context *context, u8 digest[4]); 130462306a36Sopenharmony_cistatic int flashpchar(struct airo_info *ai, int byte, int dwelltime); 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_cistatic void age_mic_context(miccntx *cur, miccntx *old, u8 *key, int key_len, 130762306a36Sopenharmony_ci struct crypto_sync_skcipher *tfm) 130862306a36Sopenharmony_ci{ 130962306a36Sopenharmony_ci /* If the current MIC context is valid and its key is the same as 131062306a36Sopenharmony_ci * the MIC register, there's nothing to do. 131162306a36Sopenharmony_ci */ 131262306a36Sopenharmony_ci if (cur->valid && (memcmp(cur->key, key, key_len) == 0)) 131362306a36Sopenharmony_ci return; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci /* Age current mic Context */ 131662306a36Sopenharmony_ci memcpy(old, cur, sizeof(*cur)); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci /* Initialize new context */ 131962306a36Sopenharmony_ci memcpy(cur->key, key, key_len); 132062306a36Sopenharmony_ci cur->window = 33; /* Window always points to the middle */ 132162306a36Sopenharmony_ci cur->rx = 0; /* Rx Sequence numbers */ 132262306a36Sopenharmony_ci cur->tx = 0; /* Tx sequence numbers */ 132362306a36Sopenharmony_ci cur->valid = 1; /* Key is now valid */ 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* Give key to mic seed */ 132662306a36Sopenharmony_ci emmh32_setseed(&cur->seed, key, key_len, tfm); 132762306a36Sopenharmony_ci} 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci/* micinit - Initialize mic seed */ 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_cistatic void micinit(struct airo_info *ai) 133262306a36Sopenharmony_ci{ 133362306a36Sopenharmony_ci MICRid mic_rid; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci clear_bit(JOB_MIC, &ai->jobs); 133662306a36Sopenharmony_ci PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0); 133762306a36Sopenharmony_ci up(&ai->sem); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci ai->micstats.enabled = (le16_to_cpu(mic_rid.state) & 0x00FF) ? 1 : 0; 134062306a36Sopenharmony_ci if (!ai->micstats.enabled) { 134162306a36Sopenharmony_ci /* So next time we have a valid key and mic is enabled, we will 134262306a36Sopenharmony_ci * update the sequence number if the key is the same as before. 134362306a36Sopenharmony_ci */ 134462306a36Sopenharmony_ci ai->mod[0].uCtx.valid = 0; 134562306a36Sopenharmony_ci ai->mod[0].mCtx.valid = 0; 134662306a36Sopenharmony_ci return; 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (mic_rid.multicastValid) { 135062306a36Sopenharmony_ci age_mic_context(&ai->mod[0].mCtx, &ai->mod[1].mCtx, 135162306a36Sopenharmony_ci mic_rid.multicast, sizeof(mic_rid.multicast), 135262306a36Sopenharmony_ci ai->tfm); 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (mic_rid.unicastValid) { 135662306a36Sopenharmony_ci age_mic_context(&ai->mod[0].uCtx, &ai->mod[1].uCtx, 135762306a36Sopenharmony_ci mic_rid.unicast, sizeof(mic_rid.unicast), 135862306a36Sopenharmony_ci ai->tfm); 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci} 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci/* micsetup - Get ready for business */ 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_cistatic int micsetup(struct airo_info *ai) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci int i; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci if (ai->tfm == NULL) 136962306a36Sopenharmony_ci ai->tfm = crypto_alloc_sync_skcipher("ctr(aes)", 0, 0); 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci if (IS_ERR(ai->tfm)) { 137262306a36Sopenharmony_ci airo_print_err(ai->dev->name, "failed to load transform for AES"); 137362306a36Sopenharmony_ci ai->tfm = NULL; 137462306a36Sopenharmony_ci return ERROR; 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci for (i = 0; i < NUM_MODULES; i++) { 137862306a36Sopenharmony_ci memset(&ai->mod[i].mCtx, 0, sizeof(miccntx)); 137962306a36Sopenharmony_ci memset(&ai->mod[i].uCtx, 0, sizeof(miccntx)); 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci return SUCCESS; 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic const u8 micsnap[] = {0xAA, 0xAA, 0x03, 0x00, 0x40, 0x96, 0x00, 0x02}; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci/*=========================================================================== 138762306a36Sopenharmony_ci * Description: Mic a packet 138862306a36Sopenharmony_ci * 138962306a36Sopenharmony_ci * Inputs: etherHead * pointer to an 802.3 frame 139062306a36Sopenharmony_ci * 139162306a36Sopenharmony_ci * Returns: BOOLEAN if successful, otherwise false. 139262306a36Sopenharmony_ci * PacketTxLen will be updated with the mic'd packets size. 139362306a36Sopenharmony_ci * 139462306a36Sopenharmony_ci * Caveats: It is assumed that the frame buffer will already 139562306a36Sopenharmony_ci * be big enough to hold the largets mic message possible. 139662306a36Sopenharmony_ci * (No memory allocation is done here). 139762306a36Sopenharmony_ci * 139862306a36Sopenharmony_ci * Author: sbraneky (10/15/01) 139962306a36Sopenharmony_ci * Merciless hacks by rwilcher (1/14/02) 140062306a36Sopenharmony_ci */ 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_cistatic int encapsulate(struct airo_info *ai, etherHead *frame, MICBuffer *mic, int payLen) 140362306a36Sopenharmony_ci{ 140462306a36Sopenharmony_ci miccntx *context; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci // Determine correct context 140762306a36Sopenharmony_ci // If not adhoc, always use unicast key 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1)) 141062306a36Sopenharmony_ci context = &ai->mod[0].mCtx; 141162306a36Sopenharmony_ci else 141262306a36Sopenharmony_ci context = &ai->mod[0].uCtx; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci if (!context->valid) 141562306a36Sopenharmony_ci return ERROR; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci mic->typelen = htons(payLen + 16); //Length of Mic'd packet 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci // Add Tx sequence 142262306a36Sopenharmony_ci mic->seq = htonl(context->tx); 142362306a36Sopenharmony_ci context->tx += 2; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci emmh32_init(&context->seed); // Mic the packet 142662306a36Sopenharmony_ci emmh32_update(&context->seed, frame->da, ETH_ALEN * 2); // DA, SA 142762306a36Sopenharmony_ci emmh32_update(&context->seed, (u8*)&mic->typelen, 10); // Type/Length and Snap 142862306a36Sopenharmony_ci emmh32_update(&context->seed, (u8*)&mic->seq, sizeof(mic->seq)); //SEQ 142962306a36Sopenharmony_ci emmh32_update(&context->seed, (u8*)(frame + 1), payLen); //payload 143062306a36Sopenharmony_ci emmh32_final(&context->seed, (u8*)&mic->mic); 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci /* New Type/length ?????????? */ 143362306a36Sopenharmony_ci mic->typelen = 0; //Let NIC know it could be an oversized packet 143462306a36Sopenharmony_ci return SUCCESS; 143562306a36Sopenharmony_ci} 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_citypedef enum { 143862306a36Sopenharmony_ci NONE, 143962306a36Sopenharmony_ci NOMIC, 144062306a36Sopenharmony_ci NOMICPLUMMED, 144162306a36Sopenharmony_ci SEQUENCE, 144262306a36Sopenharmony_ci INCORRECTMIC, 144362306a36Sopenharmony_ci} mic_error; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci/*=========================================================================== 144662306a36Sopenharmony_ci * Description: Decapsulates a MIC'd packet and returns the 802.3 packet 144762306a36Sopenharmony_ci * (removes the MIC stuff) if packet is a valid packet. 144862306a36Sopenharmony_ci * 144962306a36Sopenharmony_ci * Inputs: etherHead pointer to the 802.3 packet 145062306a36Sopenharmony_ci * 145162306a36Sopenharmony_ci * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE 145262306a36Sopenharmony_ci * 145362306a36Sopenharmony_ci * Author: sbraneky (10/15/01) 145462306a36Sopenharmony_ci * Merciless hacks by rwilcher (1/14/02) 145562306a36Sopenharmony_ci *--------------------------------------------------------------------------- 145662306a36Sopenharmony_ci */ 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_cistatic int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen) 145962306a36Sopenharmony_ci{ 146062306a36Sopenharmony_ci int i; 146162306a36Sopenharmony_ci u32 micSEQ; 146262306a36Sopenharmony_ci miccntx *context; 146362306a36Sopenharmony_ci u8 digest[4]; 146462306a36Sopenharmony_ci mic_error micError = NONE; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci // Check if the packet is a Mic'd packet 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (!ai->micstats.enabled) { 146962306a36Sopenharmony_ci //No Mic set or Mic OFF but we received a MIC'd packet. 147062306a36Sopenharmony_ci if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) { 147162306a36Sopenharmony_ci ai->micstats.rxMICPlummed++; 147262306a36Sopenharmony_ci return ERROR; 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci return SUCCESS; 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci if (ntohs(mic->typelen) == 0x888E) 147862306a36Sopenharmony_ci return SUCCESS; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) { 148162306a36Sopenharmony_ci // Mic enabled but packet isn't Mic'd 148262306a36Sopenharmony_ci ai->micstats.rxMICPlummed++; 148362306a36Sopenharmony_ci return ERROR; 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci micSEQ = ntohl(mic->seq); //store SEQ as CPU order 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci //At this point we a have a mic'd packet and mic is enabled 148962306a36Sopenharmony_ci //Now do the mic error checking. 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci //Receive seq must be odd 149262306a36Sopenharmony_ci if ((micSEQ & 1) == 0) { 149362306a36Sopenharmony_ci ai->micstats.rxWrongSequence++; 149462306a36Sopenharmony_ci return ERROR; 149562306a36Sopenharmony_ci } 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci for (i = 0; i < NUM_MODULES; i++) { 149862306a36Sopenharmony_ci int mcast = eth->da[0] & 1; 149962306a36Sopenharmony_ci //Determine proper context 150062306a36Sopenharmony_ci context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci //Make sure context is valid 150362306a36Sopenharmony_ci if (!context->valid) { 150462306a36Sopenharmony_ci if (i == 0) 150562306a36Sopenharmony_ci micError = NOMICPLUMMED; 150662306a36Sopenharmony_ci continue; 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci //DeMic it 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci if (!mic->typelen) 151162306a36Sopenharmony_ci mic->typelen = htons(payLen + sizeof(MICBuffer) - 2); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci emmh32_init(&context->seed); 151462306a36Sopenharmony_ci emmh32_update(&context->seed, eth->da, ETH_ALEN*2); 151562306a36Sopenharmony_ci emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap)); 151662306a36Sopenharmony_ci emmh32_update(&context->seed, (u8 *)&mic->seq, sizeof(mic->seq)); 151762306a36Sopenharmony_ci emmh32_update(&context->seed, (u8 *)(eth + 1), payLen); 151862306a36Sopenharmony_ci //Calculate MIC 151962306a36Sopenharmony_ci emmh32_final(&context->seed, digest); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match 152262306a36Sopenharmony_ci //Invalid Mic 152362306a36Sopenharmony_ci if (i == 0) 152462306a36Sopenharmony_ci micError = INCORRECTMIC; 152562306a36Sopenharmony_ci continue; 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci //Check Sequence number if mics pass 152962306a36Sopenharmony_ci if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) { 153062306a36Sopenharmony_ci ai->micstats.rxSuccess++; 153162306a36Sopenharmony_ci return SUCCESS; 153262306a36Sopenharmony_ci } 153362306a36Sopenharmony_ci if (i == 0) 153462306a36Sopenharmony_ci micError = SEQUENCE; 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci // Update statistics 153862306a36Sopenharmony_ci switch (micError) { 153962306a36Sopenharmony_ci case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break; 154062306a36Sopenharmony_ci case SEQUENCE: ai->micstats.rxWrongSequence++; break; 154162306a36Sopenharmony_ci case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break; 154262306a36Sopenharmony_ci case NONE: break; 154362306a36Sopenharmony_ci case NOMIC: break; 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci return ERROR; 154662306a36Sopenharmony_ci} 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci/*=========================================================================== 154962306a36Sopenharmony_ci * Description: Checks the Rx Seq number to make sure it is valid 155062306a36Sopenharmony_ci * and hasn't already been received 155162306a36Sopenharmony_ci * 155262306a36Sopenharmony_ci * Inputs: miccntx - mic context to check seq against 155362306a36Sopenharmony_ci * micSeq - the Mic seq number 155462306a36Sopenharmony_ci * 155562306a36Sopenharmony_ci * Returns: TRUE if valid otherwise FALSE. 155662306a36Sopenharmony_ci * 155762306a36Sopenharmony_ci * Author: sbraneky (10/15/01) 155862306a36Sopenharmony_ci * Merciless hacks by rwilcher (1/14/02) 155962306a36Sopenharmony_ci *--------------------------------------------------------------------------- 156062306a36Sopenharmony_ci */ 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_cistatic int RxSeqValid(struct airo_info *ai, miccntx *context, int mcast, u32 micSeq) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci u32 seq, index; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci //Allow for the ap being rebooted - if it is then use the next 156762306a36Sopenharmony_ci //sequence number of the current sequence number - might go backwards 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci if (mcast) { 157062306a36Sopenharmony_ci if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) { 157162306a36Sopenharmony_ci clear_bit (FLAG_UPDATE_MULTI, &ai->flags); 157262306a36Sopenharmony_ci context->window = (micSeq > 33) ? micSeq : 33; 157362306a36Sopenharmony_ci context->rx = 0; // Reset rx 157462306a36Sopenharmony_ci } 157562306a36Sopenharmony_ci } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) { 157662306a36Sopenharmony_ci clear_bit (FLAG_UPDATE_UNI, &ai->flags); 157762306a36Sopenharmony_ci context->window = (micSeq > 33) ? micSeq : 33; // Move window 157862306a36Sopenharmony_ci context->rx = 0; // Reset rx 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci //Make sequence number relative to START of window 158262306a36Sopenharmony_ci seq = micSeq - (context->window - 33); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci //Too old of a SEQ number to check. 158562306a36Sopenharmony_ci if ((s32)seq < 0) 158662306a36Sopenharmony_ci return ERROR; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci if (seq > 64) { 158962306a36Sopenharmony_ci //Window is infinite forward 159062306a36Sopenharmony_ci MoveWindow(context, micSeq); 159162306a36Sopenharmony_ci return SUCCESS; 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci // We are in the window. Now check the context rx bit to see if it was already sent 159562306a36Sopenharmony_ci seq >>= 1; //divide by 2 because we only have odd numbers 159662306a36Sopenharmony_ci index = 1 << seq; //Get an index number 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci if (!(context->rx & index)) { 159962306a36Sopenharmony_ci //micSEQ falls inside the window. 160062306a36Sopenharmony_ci //Add seqence number to the list of received numbers. 160162306a36Sopenharmony_ci context->rx |= index; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci MoveWindow(context, micSeq); 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci return SUCCESS; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci return ERROR; 160862306a36Sopenharmony_ci} 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_cistatic void MoveWindow(miccntx *context, u32 micSeq) 161162306a36Sopenharmony_ci{ 161262306a36Sopenharmony_ci u32 shift; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci //Move window if seq greater than the middle of the window 161562306a36Sopenharmony_ci if (micSeq > context->window) { 161662306a36Sopenharmony_ci shift = (micSeq - context->window) >> 1; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci //Shift out old 161962306a36Sopenharmony_ci if (shift < 32) 162062306a36Sopenharmony_ci context->rx >>= shift; 162162306a36Sopenharmony_ci else 162262306a36Sopenharmony_ci context->rx = 0; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci context->window = micSeq; //Move window 162562306a36Sopenharmony_ci } 162662306a36Sopenharmony_ci} 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci/*==============================================*/ 162962306a36Sopenharmony_ci/*========== EMMH ROUTINES ====================*/ 163062306a36Sopenharmony_ci/*==============================================*/ 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci/* mic accumulate */ 163362306a36Sopenharmony_ci#define MIC_ACCUM(val) \ 163462306a36Sopenharmony_ci context->accum += (u64)(val) * be32_to_cpu(context->coeff[coeff_position++]); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci/* expand the key to fill the MMH coefficient array */ 163762306a36Sopenharmony_cistatic void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, 163862306a36Sopenharmony_ci struct crypto_sync_skcipher *tfm) 163962306a36Sopenharmony_ci{ 164062306a36Sopenharmony_ci /* take the keying material, expand if necessary, truncate at 16-bytes */ 164162306a36Sopenharmony_ci /* run through AES counter mode to generate context->coeff[] */ 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm); 164462306a36Sopenharmony_ci struct scatterlist sg; 164562306a36Sopenharmony_ci u8 iv[AES_BLOCK_SIZE] = {}; 164662306a36Sopenharmony_ci int ret; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci crypto_sync_skcipher_setkey(tfm, pkey, 16); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci memset(context->coeff, 0, sizeof(context->coeff)); 165162306a36Sopenharmony_ci sg_init_one(&sg, context->coeff, sizeof(context->coeff)); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, tfm); 165462306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 165562306a36Sopenharmony_ci skcipher_request_set_crypt(req, &sg, &sg, sizeof(context->coeff), iv); 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci ret = crypto_skcipher_encrypt(req); 165862306a36Sopenharmony_ci WARN_ON_ONCE(ret); 165962306a36Sopenharmony_ci} 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci/* prepare for calculation of a new mic */ 166262306a36Sopenharmony_cistatic void emmh32_init(emmh32_context *context) 166362306a36Sopenharmony_ci{ 166462306a36Sopenharmony_ci /* prepare for new mic calculation */ 166562306a36Sopenharmony_ci context->accum = 0; 166662306a36Sopenharmony_ci context->position = 0; 166762306a36Sopenharmony_ci} 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci/* add some bytes to the mic calculation */ 167062306a36Sopenharmony_cistatic void emmh32_update(emmh32_context *context, u8 *pOctets, int len) 167162306a36Sopenharmony_ci{ 167262306a36Sopenharmony_ci int coeff_position, byte_position; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci if (len == 0) return; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci coeff_position = context->position >> 2; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci /* deal with partial 32-bit word left over from last update */ 167962306a36Sopenharmony_ci byte_position = context->position & 3; 168062306a36Sopenharmony_ci if (byte_position) { 168162306a36Sopenharmony_ci /* have a partial word in part to deal with */ 168262306a36Sopenharmony_ci do { 168362306a36Sopenharmony_ci if (len == 0) return; 168462306a36Sopenharmony_ci context->part.d8[byte_position++] = *pOctets++; 168562306a36Sopenharmony_ci context->position++; 168662306a36Sopenharmony_ci len--; 168762306a36Sopenharmony_ci } while (byte_position < 4); 168862306a36Sopenharmony_ci MIC_ACCUM(ntohl(context->part.d32)); 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci /* deal with full 32-bit words */ 169262306a36Sopenharmony_ci while (len >= 4) { 169362306a36Sopenharmony_ci MIC_ACCUM(ntohl(*(__be32 *)pOctets)); 169462306a36Sopenharmony_ci context->position += 4; 169562306a36Sopenharmony_ci pOctets += 4; 169662306a36Sopenharmony_ci len -= 4; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci /* deal with partial 32-bit word that will be left over from this update */ 170062306a36Sopenharmony_ci byte_position = 0; 170162306a36Sopenharmony_ci while (len > 0) { 170262306a36Sopenharmony_ci context->part.d8[byte_position++] = *pOctets++; 170362306a36Sopenharmony_ci context->position++; 170462306a36Sopenharmony_ci len--; 170562306a36Sopenharmony_ci } 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci/* mask used to zero empty bytes for final partial word */ 170962306a36Sopenharmony_cistatic u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L }; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci/* calculate the mic */ 171262306a36Sopenharmony_cistatic void emmh32_final(emmh32_context *context, u8 digest[4]) 171362306a36Sopenharmony_ci{ 171462306a36Sopenharmony_ci int coeff_position, byte_position; 171562306a36Sopenharmony_ci u32 val; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci u64 sum, utmp; 171862306a36Sopenharmony_ci s64 stmp; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci coeff_position = context->position >> 2; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci /* deal with partial 32-bit word left over from last update */ 172362306a36Sopenharmony_ci byte_position = context->position & 3; 172462306a36Sopenharmony_ci if (byte_position) { 172562306a36Sopenharmony_ci /* have a partial word in part to deal with */ 172662306a36Sopenharmony_ci val = ntohl(context->part.d32); 172762306a36Sopenharmony_ci MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */ 172862306a36Sopenharmony_ci } 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci /* reduce the accumulated u64 to a 32-bit MIC */ 173162306a36Sopenharmony_ci sum = context->accum; 173262306a36Sopenharmony_ci stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15); 173362306a36Sopenharmony_ci utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15); 173462306a36Sopenharmony_ci sum = utmp & 0xffffffffLL; 173562306a36Sopenharmony_ci if (utmp > 0x10000000fLL) 173662306a36Sopenharmony_ci sum -= 15; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci val = (u32)sum; 173962306a36Sopenharmony_ci digest[0] = (val>>24) & 0xFF; 174062306a36Sopenharmony_ci digest[1] = (val>>16) & 0xFF; 174162306a36Sopenharmony_ci digest[2] = (val>>8) & 0xFF; 174262306a36Sopenharmony_ci digest[3] = val & 0xFF; 174362306a36Sopenharmony_ci} 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_cistatic int readBSSListRid(struct airo_info *ai, int first, 174662306a36Sopenharmony_ci BSSListRid *list) 174762306a36Sopenharmony_ci{ 174862306a36Sopenharmony_ci Cmd cmd; 174962306a36Sopenharmony_ci Resp rsp; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci if (first == 1) { 175262306a36Sopenharmony_ci if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN; 175362306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 175462306a36Sopenharmony_ci cmd.cmd = CMD_LISTBSS; 175562306a36Sopenharmony_ci if (down_interruptible(&ai->sem)) 175662306a36Sopenharmony_ci return -ERESTARTSYS; 175762306a36Sopenharmony_ci ai->list_bss_task = current; 175862306a36Sopenharmony_ci issuecommand(ai, &cmd, &rsp, true); 175962306a36Sopenharmony_ci up(&ai->sem); 176062306a36Sopenharmony_ci /* Let the command take effect */ 176162306a36Sopenharmony_ci schedule_timeout_uninterruptible(3 * HZ); 176262306a36Sopenharmony_ci ai->list_bss_task = NULL; 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext, 176562306a36Sopenharmony_ci list, ai->bssListRidLen, 1); 176662306a36Sopenharmony_ci} 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_cistatic int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock) 176962306a36Sopenharmony_ci{ 177062306a36Sopenharmony_ci return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM, 177162306a36Sopenharmony_ci wkr, sizeof(*wkr), lock); 177262306a36Sopenharmony_ci} 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_cistatic int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock) 177562306a36Sopenharmony_ci{ 177662306a36Sopenharmony_ci int rc; 177762306a36Sopenharmony_ci rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock); 177862306a36Sopenharmony_ci if (rc!=SUCCESS) 177962306a36Sopenharmony_ci airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc); 178062306a36Sopenharmony_ci if (perm) { 178162306a36Sopenharmony_ci rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock); 178262306a36Sopenharmony_ci if (rc!=SUCCESS) 178362306a36Sopenharmony_ci airo_print_err(ai->dev->name, "WEP_PERM set %x", rc); 178462306a36Sopenharmony_ci } 178562306a36Sopenharmony_ci return rc; 178662306a36Sopenharmony_ci} 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_cistatic int readSsidRid(struct airo_info*ai, SsidRid *ssidr) 178962306a36Sopenharmony_ci{ 179062306a36Sopenharmony_ci return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1); 179162306a36Sopenharmony_ci} 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_cistatic int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock) 179462306a36Sopenharmony_ci{ 179562306a36Sopenharmony_ci return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock); 179662306a36Sopenharmony_ci} 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_cistatic int readConfigRid(struct airo_info *ai, int lock) 179962306a36Sopenharmony_ci{ 180062306a36Sopenharmony_ci int rc; 180162306a36Sopenharmony_ci ConfigRid cfg; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (ai->config.len) 180462306a36Sopenharmony_ci return SUCCESS; 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock); 180762306a36Sopenharmony_ci if (rc != SUCCESS) 180862306a36Sopenharmony_ci return rc; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci ai->config = cfg; 181162306a36Sopenharmony_ci return SUCCESS; 181262306a36Sopenharmony_ci} 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_cistatic inline void checkThrottle(struct airo_info *ai) 181562306a36Sopenharmony_ci{ 181662306a36Sopenharmony_ci int i; 181762306a36Sopenharmony_ci/* Old hardware had a limit on encryption speed */ 181862306a36Sopenharmony_ci if (ai->config.authType != AUTH_OPEN && maxencrypt) { 181962306a36Sopenharmony_ci for (i = 0; i<8; i++) { 182062306a36Sopenharmony_ci if (ai->config.rates[i] > maxencrypt) { 182162306a36Sopenharmony_ci ai->config.rates[i] = 0; 182262306a36Sopenharmony_ci } 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci } 182562306a36Sopenharmony_ci} 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_cistatic int writeConfigRid(struct airo_info *ai, int lock) 182862306a36Sopenharmony_ci{ 182962306a36Sopenharmony_ci ConfigRid cfgr; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci if (!test_bit (FLAG_COMMIT, &ai->flags)) 183262306a36Sopenharmony_ci return SUCCESS; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci clear_bit (FLAG_COMMIT, &ai->flags); 183562306a36Sopenharmony_ci clear_bit (FLAG_RESET, &ai->flags); 183662306a36Sopenharmony_ci checkThrottle(ai); 183762306a36Sopenharmony_ci cfgr = ai->config; 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS) 184062306a36Sopenharmony_ci set_bit(FLAG_ADHOC, &ai->flags); 184162306a36Sopenharmony_ci else 184262306a36Sopenharmony_ci clear_bit(FLAG_ADHOC, &ai->flags); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci return PC4500_writerid(ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock); 184562306a36Sopenharmony_ci} 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_cistatic int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock) 184862306a36Sopenharmony_ci{ 184962306a36Sopenharmony_ci return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock); 185062306a36Sopenharmony_ci} 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_cistatic int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock) 185362306a36Sopenharmony_ci{ 185462306a36Sopenharmony_ci return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock); 185562306a36Sopenharmony_ci} 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_cistatic int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock) 185862306a36Sopenharmony_ci{ 185962306a36Sopenharmony_ci return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock); 186062306a36Sopenharmony_ci} 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_cistatic int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock) 186362306a36Sopenharmony_ci{ 186462306a36Sopenharmony_ci return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock); 186562306a36Sopenharmony_ci} 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_cistatic void try_auto_wep(struct airo_info *ai) 186862306a36Sopenharmony_ci{ 186962306a36Sopenharmony_ci if (auto_wep && !test_bit(FLAG_RADIO_DOWN, &ai->flags)) { 187062306a36Sopenharmony_ci ai->expires = RUN_AT(3*HZ); 187162306a36Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 187262306a36Sopenharmony_ci } 187362306a36Sopenharmony_ci} 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_cistatic int airo_open(struct net_device *dev) 187662306a36Sopenharmony_ci{ 187762306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 187862306a36Sopenharmony_ci int rc = 0; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci if (test_bit(FLAG_FLASHING, &ai->flags)) 188162306a36Sopenharmony_ci return -EIO; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci /* Make sure the card is configured. 188462306a36Sopenharmony_ci * Wireless Extensions may postpone config changes until the card 188562306a36Sopenharmony_ci * is open (to pipeline changes and speed-up card setup). If 188662306a36Sopenharmony_ci * those changes are not yet committed, do it now - Jean II */ 188762306a36Sopenharmony_ci if (test_bit(FLAG_COMMIT, &ai->flags)) { 188862306a36Sopenharmony_ci disable_MAC(ai, 1); 188962306a36Sopenharmony_ci writeConfigRid(ai, 1); 189062306a36Sopenharmony_ci } 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci if (ai->wifidev != dev) { 189362306a36Sopenharmony_ci clear_bit(JOB_DIE, &ai->jobs); 189462306a36Sopenharmony_ci ai->airo_thread_task = kthread_run(airo_thread, dev, "%s", 189562306a36Sopenharmony_ci dev->name); 189662306a36Sopenharmony_ci if (IS_ERR(ai->airo_thread_task)) 189762306a36Sopenharmony_ci return (int)PTR_ERR(ai->airo_thread_task); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED, 190062306a36Sopenharmony_ci dev->name, dev); 190162306a36Sopenharmony_ci if (rc) { 190262306a36Sopenharmony_ci airo_print_err(dev->name, 190362306a36Sopenharmony_ci "register interrupt %d failed, rc %d", 190462306a36Sopenharmony_ci dev->irq, rc); 190562306a36Sopenharmony_ci set_bit(JOB_DIE, &ai->jobs); 190662306a36Sopenharmony_ci kthread_stop(ai->airo_thread_task); 190762306a36Sopenharmony_ci return rc; 190862306a36Sopenharmony_ci } 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci /* Power on the MAC controller (which may have been disabled) */ 191162306a36Sopenharmony_ci clear_bit(FLAG_RADIO_DOWN, &ai->flags); 191262306a36Sopenharmony_ci enable_interrupts(ai); 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci try_auto_wep(ai); 191562306a36Sopenharmony_ci } 191662306a36Sopenharmony_ci enable_MAC(ai, 1); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci netif_start_queue(dev); 191962306a36Sopenharmony_ci return 0; 192062306a36Sopenharmony_ci} 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_cistatic netdev_tx_t mpi_start_xmit(struct sk_buff *skb, 192362306a36Sopenharmony_ci struct net_device *dev) 192462306a36Sopenharmony_ci{ 192562306a36Sopenharmony_ci int npacks, pending; 192662306a36Sopenharmony_ci unsigned long flags; 192762306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci if (!skb) { 193062306a36Sopenharmony_ci airo_print_err(dev->name, "%s: skb == NULL!",__func__); 193162306a36Sopenharmony_ci return NETDEV_TX_OK; 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) { 193462306a36Sopenharmony_ci dev->stats.tx_dropped++; 193562306a36Sopenharmony_ci return NETDEV_TX_OK; 193662306a36Sopenharmony_ci } 193762306a36Sopenharmony_ci npacks = skb_queue_len (&ai->txq); 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci if (npacks >= MAXTXQ - 1) { 194062306a36Sopenharmony_ci netif_stop_queue (dev); 194162306a36Sopenharmony_ci if (npacks > MAXTXQ) { 194262306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 194362306a36Sopenharmony_ci return NETDEV_TX_BUSY; 194462306a36Sopenharmony_ci } 194562306a36Sopenharmony_ci skb_queue_tail (&ai->txq, skb); 194662306a36Sopenharmony_ci return NETDEV_TX_OK; 194762306a36Sopenharmony_ci } 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci spin_lock_irqsave(&ai->aux_lock, flags); 195062306a36Sopenharmony_ci skb_queue_tail (&ai->txq, skb); 195162306a36Sopenharmony_ci pending = test_bit(FLAG_PENDING_XMIT, &ai->flags); 195262306a36Sopenharmony_ci spin_unlock_irqrestore(&ai->aux_lock, flags); 195362306a36Sopenharmony_ci netif_wake_queue (dev); 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci if (pending == 0) { 195662306a36Sopenharmony_ci set_bit(FLAG_PENDING_XMIT, &ai->flags); 195762306a36Sopenharmony_ci mpi_send_packet (dev); 195862306a36Sopenharmony_ci } 195962306a36Sopenharmony_ci return NETDEV_TX_OK; 196062306a36Sopenharmony_ci} 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci/* 196362306a36Sopenharmony_ci * @mpi_send_packet 196462306a36Sopenharmony_ci * 196562306a36Sopenharmony_ci * Attempt to transmit a packet. Can be called from interrupt 196662306a36Sopenharmony_ci * or transmit . return number of packets we tried to send 196762306a36Sopenharmony_ci */ 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_cistatic int mpi_send_packet (struct net_device *dev) 197062306a36Sopenharmony_ci{ 197162306a36Sopenharmony_ci struct sk_buff *skb; 197262306a36Sopenharmony_ci unsigned char *buffer; 197362306a36Sopenharmony_ci s16 len; 197462306a36Sopenharmony_ci __le16 *payloadLen; 197562306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 197662306a36Sopenharmony_ci u8 *sendbuf; 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci /* get a packet to send */ 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci if ((skb = skb_dequeue(&ai->txq)) == NULL) { 198162306a36Sopenharmony_ci airo_print_err(dev->name, 198262306a36Sopenharmony_ci "%s: Dequeue'd zero in send_packet()", 198362306a36Sopenharmony_ci __func__); 198462306a36Sopenharmony_ci return 0; 198562306a36Sopenharmony_ci } 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci /* check min length*/ 198862306a36Sopenharmony_ci len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; 198962306a36Sopenharmony_ci buffer = skb->data; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci ai->txfids[0].tx_desc.offset = 0; 199262306a36Sopenharmony_ci ai->txfids[0].tx_desc.valid = 1; 199362306a36Sopenharmony_ci ai->txfids[0].tx_desc.eoc = 1; 199462306a36Sopenharmony_ci ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr); 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci/* 199762306a36Sopenharmony_ci * Magic, the cards firmware needs a length count (2 bytes) in the host buffer 199862306a36Sopenharmony_ci * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen 199962306a36Sopenharmony_ci * is immediately after it. ------------------------------------------------ 200062306a36Sopenharmony_ci * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA| 200162306a36Sopenharmony_ci * ------------------------------------------------ 200262306a36Sopenharmony_ci */ 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci memcpy(ai->txfids[0].virtual_host_addr, 200562306a36Sopenharmony_ci (char *)&wifictlhdr8023, sizeof(wifictlhdr8023)); 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr + 200862306a36Sopenharmony_ci sizeof(wifictlhdr8023)); 200962306a36Sopenharmony_ci sendbuf = ai->txfids[0].virtual_host_addr + 201062306a36Sopenharmony_ci sizeof(wifictlhdr8023) + 2 ; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci /* 201362306a36Sopenharmony_ci * Firmware automatically puts 802 header on so 201462306a36Sopenharmony_ci * we don't need to account for it in the length 201562306a36Sopenharmony_ci */ 201662306a36Sopenharmony_ci if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled && 201762306a36Sopenharmony_ci (ntohs(((__be16 *)buffer)[6]) != 0x888E)) { 201862306a36Sopenharmony_ci MICBuffer pMic; 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS) 202162306a36Sopenharmony_ci return ERROR; 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic)); 202462306a36Sopenharmony_ci ai->txfids[0].tx_desc.len += sizeof(pMic); 202562306a36Sopenharmony_ci /* copy data into airo dma buffer */ 202662306a36Sopenharmony_ci memcpy (sendbuf, buffer, sizeof(etherHead)); 202762306a36Sopenharmony_ci buffer += sizeof(etherHead); 202862306a36Sopenharmony_ci sendbuf += sizeof(etherHead); 202962306a36Sopenharmony_ci memcpy (sendbuf, &pMic, sizeof(pMic)); 203062306a36Sopenharmony_ci sendbuf += sizeof(pMic); 203162306a36Sopenharmony_ci memcpy (sendbuf, buffer, len - sizeof(etherHead)); 203262306a36Sopenharmony_ci } else { 203362306a36Sopenharmony_ci *payloadLen = cpu_to_le16(len - sizeof(etherHead)); 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci netif_trans_update(dev); 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci /* copy data into airo dma buffer */ 203862306a36Sopenharmony_ci memcpy(sendbuf, buffer, len); 203962306a36Sopenharmony_ci } 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci memcpy_toio(ai->txfids[0].card_ram_off, 204262306a36Sopenharmony_ci &ai->txfids[0].tx_desc, sizeof(TxFid)); 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci OUT4500(ai, EVACK, 8); 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 204762306a36Sopenharmony_ci return 1; 204862306a36Sopenharmony_ci} 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_cistatic void get_tx_error(struct airo_info *ai, s32 fid) 205162306a36Sopenharmony_ci{ 205262306a36Sopenharmony_ci __le16 status; 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci if (fid < 0) 205562306a36Sopenharmony_ci status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status; 205662306a36Sopenharmony_ci else { 205762306a36Sopenharmony_ci if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS) 205862306a36Sopenharmony_ci return; 205962306a36Sopenharmony_ci bap_read(ai, &status, 2, BAP0); 206062306a36Sopenharmony_ci } 206162306a36Sopenharmony_ci if (le16_to_cpu(status) & 2) /* Too many retries */ 206262306a36Sopenharmony_ci ai->dev->stats.tx_aborted_errors++; 206362306a36Sopenharmony_ci if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */ 206462306a36Sopenharmony_ci ai->dev->stats.tx_heartbeat_errors++; 206562306a36Sopenharmony_ci if (le16_to_cpu(status) & 8) /* Aid fail */ 206662306a36Sopenharmony_ci { } 206762306a36Sopenharmony_ci if (le16_to_cpu(status) & 0x10) /* MAC disabled */ 206862306a36Sopenharmony_ci ai->dev->stats.tx_carrier_errors++; 206962306a36Sopenharmony_ci if (le16_to_cpu(status) & 0x20) /* Association lost */ 207062306a36Sopenharmony_ci { } 207162306a36Sopenharmony_ci /* We produce a TXDROP event only for retry or lifetime 207262306a36Sopenharmony_ci * exceeded, because that's the only status that really mean 207362306a36Sopenharmony_ci * that this particular node went away. 207462306a36Sopenharmony_ci * Other errors means that *we* screwed up. - Jean II */ 207562306a36Sopenharmony_ci if ((le16_to_cpu(status) & 2) || 207662306a36Sopenharmony_ci (le16_to_cpu(status) & 4)) { 207762306a36Sopenharmony_ci union iwreq_data wrqu; 207862306a36Sopenharmony_ci char junk[0x18]; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci /* Faster to skip over useless data than to do 208162306a36Sopenharmony_ci * another bap_setup(). We are at offset 0x6 and 208262306a36Sopenharmony_ci * need to go to 0x18 and read 6 bytes - Jean II */ 208362306a36Sopenharmony_ci bap_read(ai, (__le16 *) junk, 0x18, BAP0); 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci /* Copy 802.11 dest address. 208662306a36Sopenharmony_ci * We use the 802.11 header because the frame may 208762306a36Sopenharmony_ci * not be 802.3 or may be mangled... 208862306a36Sopenharmony_ci * In Ad-Hoc mode, it will be the node address. 208962306a36Sopenharmony_ci * In managed mode, it will be most likely the AP addr 209062306a36Sopenharmony_ci * User space will figure out how to convert it to 209162306a36Sopenharmony_ci * whatever it needs (IP address or else). 209262306a36Sopenharmony_ci * - Jean II */ 209362306a36Sopenharmony_ci memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN); 209462306a36Sopenharmony_ci wrqu.addr.sa_family = ARPHRD_ETHER; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci /* Send event to user space */ 209762306a36Sopenharmony_ci wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL); 209862306a36Sopenharmony_ci } 209962306a36Sopenharmony_ci} 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_cistatic void airo_end_xmit(struct net_device *dev, bool may_sleep) 210262306a36Sopenharmony_ci{ 210362306a36Sopenharmony_ci u16 status; 210462306a36Sopenharmony_ci int i; 210562306a36Sopenharmony_ci struct airo_info *priv = dev->ml_priv; 210662306a36Sopenharmony_ci struct sk_buff *skb = priv->xmit.skb; 210762306a36Sopenharmony_ci int fid = priv->xmit.fid; 210862306a36Sopenharmony_ci u32 *fids = priv->fids; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci clear_bit(JOB_XMIT, &priv->jobs); 211162306a36Sopenharmony_ci clear_bit(FLAG_PENDING_XMIT, &priv->flags); 211262306a36Sopenharmony_ci status = transmit_802_3_packet(priv, fids[fid], skb->data, may_sleep); 211362306a36Sopenharmony_ci up(&priv->sem); 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci i = 0; 211662306a36Sopenharmony_ci if (status == SUCCESS) { 211762306a36Sopenharmony_ci netif_trans_update(dev); 211862306a36Sopenharmony_ci for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++); 211962306a36Sopenharmony_ci } else { 212062306a36Sopenharmony_ci priv->fids[fid] &= 0xffff; 212162306a36Sopenharmony_ci dev->stats.tx_window_errors++; 212262306a36Sopenharmony_ci } 212362306a36Sopenharmony_ci if (i < MAX_FIDS / 2) 212462306a36Sopenharmony_ci netif_wake_queue(dev); 212562306a36Sopenharmony_ci dev_kfree_skb(skb); 212662306a36Sopenharmony_ci} 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_cistatic netdev_tx_t airo_start_xmit(struct sk_buff *skb, 212962306a36Sopenharmony_ci struct net_device *dev) 213062306a36Sopenharmony_ci{ 213162306a36Sopenharmony_ci s16 len; 213262306a36Sopenharmony_ci int i, j; 213362306a36Sopenharmony_ci struct airo_info *priv = dev->ml_priv; 213462306a36Sopenharmony_ci u32 *fids = priv->fids; 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci if (skb == NULL) { 213762306a36Sopenharmony_ci airo_print_err(dev->name, "%s: skb == NULL!", __func__); 213862306a36Sopenharmony_ci return NETDEV_TX_OK; 213962306a36Sopenharmony_ci } 214062306a36Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) { 214162306a36Sopenharmony_ci dev->stats.tx_dropped++; 214262306a36Sopenharmony_ci return NETDEV_TX_OK; 214362306a36Sopenharmony_ci } 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci /* Find a vacant FID */ 214662306a36Sopenharmony_ci for (i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++); 214762306a36Sopenharmony_ci for (j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++); 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci if (j >= MAX_FIDS / 2) { 215062306a36Sopenharmony_ci netif_stop_queue(dev); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci if (i == MAX_FIDS / 2) { 215362306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 215462306a36Sopenharmony_ci return NETDEV_TX_BUSY; 215562306a36Sopenharmony_ci } 215662306a36Sopenharmony_ci } 215762306a36Sopenharmony_ci /* check min length*/ 215862306a36Sopenharmony_ci len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; 215962306a36Sopenharmony_ci /* Mark fid as used & save length for later */ 216062306a36Sopenharmony_ci fids[i] |= (len << 16); 216162306a36Sopenharmony_ci priv->xmit.skb = skb; 216262306a36Sopenharmony_ci priv->xmit.fid = i; 216362306a36Sopenharmony_ci if (down_trylock(&priv->sem) != 0) { 216462306a36Sopenharmony_ci set_bit(FLAG_PENDING_XMIT, &priv->flags); 216562306a36Sopenharmony_ci netif_stop_queue(dev); 216662306a36Sopenharmony_ci set_bit(JOB_XMIT, &priv->jobs); 216762306a36Sopenharmony_ci wake_up_interruptible(&priv->thr_wait); 216862306a36Sopenharmony_ci } else 216962306a36Sopenharmony_ci airo_end_xmit(dev, false); 217062306a36Sopenharmony_ci return NETDEV_TX_OK; 217162306a36Sopenharmony_ci} 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_cistatic void airo_end_xmit11(struct net_device *dev, bool may_sleep) 217462306a36Sopenharmony_ci{ 217562306a36Sopenharmony_ci u16 status; 217662306a36Sopenharmony_ci int i; 217762306a36Sopenharmony_ci struct airo_info *priv = dev->ml_priv; 217862306a36Sopenharmony_ci struct sk_buff *skb = priv->xmit11.skb; 217962306a36Sopenharmony_ci int fid = priv->xmit11.fid; 218062306a36Sopenharmony_ci u32 *fids = priv->fids; 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci clear_bit(JOB_XMIT11, &priv->jobs); 218362306a36Sopenharmony_ci clear_bit(FLAG_PENDING_XMIT11, &priv->flags); 218462306a36Sopenharmony_ci status = transmit_802_11_packet(priv, fids[fid], skb->data, may_sleep); 218562306a36Sopenharmony_ci up(&priv->sem); 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci i = MAX_FIDS / 2; 218862306a36Sopenharmony_ci if (status == SUCCESS) { 218962306a36Sopenharmony_ci netif_trans_update(dev); 219062306a36Sopenharmony_ci for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++); 219162306a36Sopenharmony_ci } else { 219262306a36Sopenharmony_ci priv->fids[fid] &= 0xffff; 219362306a36Sopenharmony_ci dev->stats.tx_window_errors++; 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci if (i < MAX_FIDS) 219662306a36Sopenharmony_ci netif_wake_queue(dev); 219762306a36Sopenharmony_ci dev_kfree_skb(skb); 219862306a36Sopenharmony_ci} 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_cistatic netdev_tx_t airo_start_xmit11(struct sk_buff *skb, 220162306a36Sopenharmony_ci struct net_device *dev) 220262306a36Sopenharmony_ci{ 220362306a36Sopenharmony_ci s16 len; 220462306a36Sopenharmony_ci int i, j; 220562306a36Sopenharmony_ci struct airo_info *priv = dev->ml_priv; 220662306a36Sopenharmony_ci u32 *fids = priv->fids; 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci if (test_bit(FLAG_MPI, &priv->flags)) { 220962306a36Sopenharmony_ci /* Not implemented yet for MPI350 */ 221062306a36Sopenharmony_ci netif_stop_queue(dev); 221162306a36Sopenharmony_ci dev_kfree_skb_any(skb); 221262306a36Sopenharmony_ci return NETDEV_TX_OK; 221362306a36Sopenharmony_ci } 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci if (skb == NULL) { 221662306a36Sopenharmony_ci airo_print_err(dev->name, "%s: skb == NULL!", __func__); 221762306a36Sopenharmony_ci return NETDEV_TX_OK; 221862306a36Sopenharmony_ci } 221962306a36Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) { 222062306a36Sopenharmony_ci dev->stats.tx_dropped++; 222162306a36Sopenharmony_ci return NETDEV_TX_OK; 222262306a36Sopenharmony_ci } 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci /* Find a vacant FID */ 222562306a36Sopenharmony_ci for (i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++); 222662306a36Sopenharmony_ci for (j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++); 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci if (j >= MAX_FIDS) { 222962306a36Sopenharmony_ci netif_stop_queue(dev); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci if (i == MAX_FIDS) { 223262306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 223362306a36Sopenharmony_ci return NETDEV_TX_BUSY; 223462306a36Sopenharmony_ci } 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci /* check min length*/ 223762306a36Sopenharmony_ci len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; 223862306a36Sopenharmony_ci /* Mark fid as used & save length for later */ 223962306a36Sopenharmony_ci fids[i] |= (len << 16); 224062306a36Sopenharmony_ci priv->xmit11.skb = skb; 224162306a36Sopenharmony_ci priv->xmit11.fid = i; 224262306a36Sopenharmony_ci if (down_trylock(&priv->sem) != 0) { 224362306a36Sopenharmony_ci set_bit(FLAG_PENDING_XMIT11, &priv->flags); 224462306a36Sopenharmony_ci netif_stop_queue(dev); 224562306a36Sopenharmony_ci set_bit(JOB_XMIT11, &priv->jobs); 224662306a36Sopenharmony_ci wake_up_interruptible(&priv->thr_wait); 224762306a36Sopenharmony_ci } else 224862306a36Sopenharmony_ci airo_end_xmit11(dev, false); 224962306a36Sopenharmony_ci return NETDEV_TX_OK; 225062306a36Sopenharmony_ci} 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_cistatic void airo_read_stats(struct net_device *dev) 225362306a36Sopenharmony_ci{ 225462306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 225562306a36Sopenharmony_ci StatsRid stats_rid; 225662306a36Sopenharmony_ci __le32 *vals = stats_rid.vals; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci clear_bit(JOB_STATS, &ai->jobs); 225962306a36Sopenharmony_ci if (ai->power.event) { 226062306a36Sopenharmony_ci up(&ai->sem); 226162306a36Sopenharmony_ci return; 226262306a36Sopenharmony_ci } 226362306a36Sopenharmony_ci readStatsRid(ai, &stats_rid, RID_STATS, 0); 226462306a36Sopenharmony_ci up(&ai->sem); 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) + 226762306a36Sopenharmony_ci le32_to_cpu(vals[45]); 226862306a36Sopenharmony_ci dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) + 226962306a36Sopenharmony_ci le32_to_cpu(vals[41]); 227062306a36Sopenharmony_ci dev->stats.rx_bytes = le32_to_cpu(vals[92]); 227162306a36Sopenharmony_ci dev->stats.tx_bytes = le32_to_cpu(vals[91]); 227262306a36Sopenharmony_ci dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) + 227362306a36Sopenharmony_ci le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]); 227462306a36Sopenharmony_ci dev->stats.tx_errors = le32_to_cpu(vals[42]) + 227562306a36Sopenharmony_ci dev->stats.tx_fifo_errors; 227662306a36Sopenharmony_ci dev->stats.multicast = le32_to_cpu(vals[43]); 227762306a36Sopenharmony_ci dev->stats.collisions = le32_to_cpu(vals[89]); 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci /* detailed rx_errors: */ 228062306a36Sopenharmony_ci dev->stats.rx_length_errors = le32_to_cpu(vals[3]); 228162306a36Sopenharmony_ci dev->stats.rx_crc_errors = le32_to_cpu(vals[4]); 228262306a36Sopenharmony_ci dev->stats.rx_frame_errors = le32_to_cpu(vals[2]); 228362306a36Sopenharmony_ci dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]); 228462306a36Sopenharmony_ci} 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_cistatic struct net_device_stats *airo_get_stats(struct net_device *dev) 228762306a36Sopenharmony_ci{ 228862306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci if (!test_bit(JOB_STATS, &local->jobs)) { 229162306a36Sopenharmony_ci set_bit(JOB_STATS, &local->jobs); 229262306a36Sopenharmony_ci wake_up_interruptible(&local->thr_wait); 229362306a36Sopenharmony_ci } 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci return &dev->stats; 229662306a36Sopenharmony_ci} 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_cistatic void airo_set_promisc(struct airo_info *ai, bool may_sleep) 229962306a36Sopenharmony_ci{ 230062306a36Sopenharmony_ci Cmd cmd; 230162306a36Sopenharmony_ci Resp rsp; 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 230462306a36Sopenharmony_ci cmd.cmd = CMD_SETMODE; 230562306a36Sopenharmony_ci clear_bit(JOB_PROMISC, &ai->jobs); 230662306a36Sopenharmony_ci cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC; 230762306a36Sopenharmony_ci issuecommand(ai, &cmd, &rsp, may_sleep); 230862306a36Sopenharmony_ci up(&ai->sem); 230962306a36Sopenharmony_ci} 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_cistatic void airo_set_multicast_list(struct net_device *dev) 231262306a36Sopenharmony_ci{ 231362306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci if ((dev->flags ^ ai->flags) & IFF_PROMISC) { 231662306a36Sopenharmony_ci change_bit(FLAG_PROMISC, &ai->flags); 231762306a36Sopenharmony_ci if (down_trylock(&ai->sem) != 0) { 231862306a36Sopenharmony_ci set_bit(JOB_PROMISC, &ai->jobs); 231962306a36Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 232062306a36Sopenharmony_ci } else 232162306a36Sopenharmony_ci airo_set_promisc(ai, false); 232262306a36Sopenharmony_ci } 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci if ((dev->flags&IFF_ALLMULTI) || !netdev_mc_empty(dev)) { 232562306a36Sopenharmony_ci /* Turn on multicast. (Should be already setup...) */ 232662306a36Sopenharmony_ci } 232762306a36Sopenharmony_ci} 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_cistatic int airo_set_mac_address(struct net_device *dev, void *p) 233062306a36Sopenharmony_ci{ 233162306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 233262306a36Sopenharmony_ci struct sockaddr *addr = p; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci readConfigRid(ai, 1); 233562306a36Sopenharmony_ci memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len); 233662306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 233762306a36Sopenharmony_ci disable_MAC(ai, 1); 233862306a36Sopenharmony_ci writeConfigRid (ai, 1); 233962306a36Sopenharmony_ci enable_MAC(ai, 1); 234062306a36Sopenharmony_ci dev_addr_set(ai->dev, addr->sa_data); 234162306a36Sopenharmony_ci if (ai->wifidev) 234262306a36Sopenharmony_ci dev_addr_set(ai->wifidev, addr->sa_data); 234362306a36Sopenharmony_ci return 0; 234462306a36Sopenharmony_ci} 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_cistatic LIST_HEAD(airo_devices); 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_cistatic void add_airo_dev(struct airo_info *ai) 234962306a36Sopenharmony_ci{ 235062306a36Sopenharmony_ci /* Upper layers already keep track of PCI devices, 235162306a36Sopenharmony_ci * so we only need to remember our non-PCI cards. */ 235262306a36Sopenharmony_ci if (!ai->pci) 235362306a36Sopenharmony_ci list_add_tail(&ai->dev_list, &airo_devices); 235462306a36Sopenharmony_ci} 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_cistatic void del_airo_dev(struct airo_info *ai) 235762306a36Sopenharmony_ci{ 235862306a36Sopenharmony_ci if (!ai->pci) 235962306a36Sopenharmony_ci list_del(&ai->dev_list); 236062306a36Sopenharmony_ci} 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_cistatic int airo_close(struct net_device *dev) 236362306a36Sopenharmony_ci{ 236462306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci netif_stop_queue(dev); 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci if (ai->wifidev != dev) { 236962306a36Sopenharmony_ci#ifdef POWER_ON_DOWN 237062306a36Sopenharmony_ci /* Shut power to the card. The idea is that the user can save 237162306a36Sopenharmony_ci * power when he doesn't need the card with "ifconfig down". 237262306a36Sopenharmony_ci * That's the method that is most friendly towards the network 237362306a36Sopenharmony_ci * stack (i.e. the network stack won't try to broadcast 237462306a36Sopenharmony_ci * anything on the interface and routes are gone. Jean II */ 237562306a36Sopenharmony_ci set_bit(FLAG_RADIO_DOWN, &ai->flags); 237662306a36Sopenharmony_ci disable_MAC(ai, 1); 237762306a36Sopenharmony_ci#endif 237862306a36Sopenharmony_ci disable_interrupts(ai); 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci free_irq(dev->irq, dev); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci set_bit(JOB_DIE, &ai->jobs); 238362306a36Sopenharmony_ci kthread_stop(ai->airo_thread_task); 238462306a36Sopenharmony_ci } 238562306a36Sopenharmony_ci return 0; 238662306a36Sopenharmony_ci} 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_civoid stop_airo_card(struct net_device *dev, int freeres) 238962306a36Sopenharmony_ci{ 239062306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci set_bit(FLAG_RADIO_DOWN, &ai->flags); 239362306a36Sopenharmony_ci disable_MAC(ai, 1); 239462306a36Sopenharmony_ci disable_interrupts(ai); 239562306a36Sopenharmony_ci takedown_proc_entry(dev, ai); 239662306a36Sopenharmony_ci if (test_bit(FLAG_REGISTERED, &ai->flags)) { 239762306a36Sopenharmony_ci unregister_netdev(dev); 239862306a36Sopenharmony_ci if (ai->wifidev) { 239962306a36Sopenharmony_ci unregister_netdev(ai->wifidev); 240062306a36Sopenharmony_ci free_netdev(ai->wifidev); 240162306a36Sopenharmony_ci ai->wifidev = NULL; 240262306a36Sopenharmony_ci } 240362306a36Sopenharmony_ci clear_bit(FLAG_REGISTERED, &ai->flags); 240462306a36Sopenharmony_ci } 240562306a36Sopenharmony_ci /* 240662306a36Sopenharmony_ci * Clean out tx queue 240762306a36Sopenharmony_ci */ 240862306a36Sopenharmony_ci if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) { 240962306a36Sopenharmony_ci struct sk_buff *skb = NULL; 241062306a36Sopenharmony_ci for (;(skb = skb_dequeue(&ai->txq));) 241162306a36Sopenharmony_ci dev_kfree_skb(skb); 241262306a36Sopenharmony_ci } 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci airo_networks_free (ai); 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci kfree(ai->flash); 241762306a36Sopenharmony_ci kfree(ai->rssi); 241862306a36Sopenharmony_ci kfree(ai->SSID); 241962306a36Sopenharmony_ci if (freeres) { 242062306a36Sopenharmony_ci /* PCMCIA frees this stuff, so only for PCI and ISA */ 242162306a36Sopenharmony_ci release_region(dev->base_addr, 64); 242262306a36Sopenharmony_ci if (test_bit(FLAG_MPI, &ai->flags)) { 242362306a36Sopenharmony_ci if (ai->pci) 242462306a36Sopenharmony_ci mpi_unmap_card(ai->pci); 242562306a36Sopenharmony_ci if (ai->pcimem) 242662306a36Sopenharmony_ci iounmap(ai->pcimem); 242762306a36Sopenharmony_ci if (ai->pciaux) 242862306a36Sopenharmony_ci iounmap(ai->pciaux); 242962306a36Sopenharmony_ci dma_free_coherent(&ai->pci->dev, PCI_SHARED_LEN, 243062306a36Sopenharmony_ci ai->shared, ai->shared_dma); 243162306a36Sopenharmony_ci } 243262306a36Sopenharmony_ci } 243362306a36Sopenharmony_ci crypto_free_sync_skcipher(ai->tfm); 243462306a36Sopenharmony_ci del_airo_dev(ai); 243562306a36Sopenharmony_ci free_netdev(dev); 243662306a36Sopenharmony_ci} 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ciEXPORT_SYMBOL(stop_airo_card); 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_cistatic int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr) 244162306a36Sopenharmony_ci{ 244262306a36Sopenharmony_ci memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); 244362306a36Sopenharmony_ci return ETH_ALEN; 244462306a36Sopenharmony_ci} 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_cistatic void mpi_unmap_card(struct pci_dev *pci) 244762306a36Sopenharmony_ci{ 244862306a36Sopenharmony_ci unsigned long mem_start = pci_resource_start(pci, 1); 244962306a36Sopenharmony_ci unsigned long mem_len = pci_resource_len(pci, 1); 245062306a36Sopenharmony_ci unsigned long aux_start = pci_resource_start(pci, 2); 245162306a36Sopenharmony_ci unsigned long aux_len = AUXMEMSIZE; 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci release_mem_region(aux_start, aux_len); 245462306a36Sopenharmony_ci release_mem_region(mem_start, mem_len); 245562306a36Sopenharmony_ci} 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ci/************************************************************* 245862306a36Sopenharmony_ci * This routine assumes that descriptors have been setup . 245962306a36Sopenharmony_ci * Run at insmod time or after reset when the descriptors 246062306a36Sopenharmony_ci * have been initialized . Returns 0 if all is well nz 246162306a36Sopenharmony_ci * otherwise . Does not allocate memory but sets up card 246262306a36Sopenharmony_ci * using previously allocated descriptors. 246362306a36Sopenharmony_ci */ 246462306a36Sopenharmony_cistatic int mpi_init_descriptors (struct airo_info *ai) 246562306a36Sopenharmony_ci{ 246662306a36Sopenharmony_ci Cmd cmd; 246762306a36Sopenharmony_ci Resp rsp; 246862306a36Sopenharmony_ci int i; 246962306a36Sopenharmony_ci int rc = SUCCESS; 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci /* Alloc card RX descriptors */ 247262306a36Sopenharmony_ci netif_stop_queue(ai->dev); 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 247562306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci cmd.cmd = CMD_ALLOCATEAUX; 247862306a36Sopenharmony_ci cmd.parm0 = FID_RX; 247962306a36Sopenharmony_ci cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux); 248062306a36Sopenharmony_ci cmd.parm2 = MPI_MAX_FIDS; 248162306a36Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp, true); 248262306a36Sopenharmony_ci if (rc != SUCCESS) { 248362306a36Sopenharmony_ci airo_print_err(ai->dev->name, "Couldn't allocate RX FID"); 248462306a36Sopenharmony_ci return rc; 248562306a36Sopenharmony_ci } 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci for (i = 0; i<MPI_MAX_FIDS; i++) { 248862306a36Sopenharmony_ci memcpy_toio(ai->rxfids[i].card_ram_off, 248962306a36Sopenharmony_ci &ai->rxfids[i].rx_desc, sizeof(RxFid)); 249062306a36Sopenharmony_ci } 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci /* Alloc card TX descriptors */ 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 249562306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci cmd.cmd = CMD_ALLOCATEAUX; 249862306a36Sopenharmony_ci cmd.parm0 = FID_TX; 249962306a36Sopenharmony_ci cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux); 250062306a36Sopenharmony_ci cmd.parm2 = MPI_MAX_FIDS; 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci for (i = 0; i<MPI_MAX_FIDS; i++) { 250362306a36Sopenharmony_ci ai->txfids[i].tx_desc.valid = 1; 250462306a36Sopenharmony_ci memcpy_toio(ai->txfids[i].card_ram_off, 250562306a36Sopenharmony_ci &ai->txfids[i].tx_desc, sizeof(TxFid)); 250662306a36Sopenharmony_ci } 250762306a36Sopenharmony_ci ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */ 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp, true); 251062306a36Sopenharmony_ci if (rc != SUCCESS) { 251162306a36Sopenharmony_ci airo_print_err(ai->dev->name, "Couldn't allocate TX FID"); 251262306a36Sopenharmony_ci return rc; 251362306a36Sopenharmony_ci } 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci /* Alloc card Rid descriptor */ 251662306a36Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 251762306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci cmd.cmd = CMD_ALLOCATEAUX; 252062306a36Sopenharmony_ci cmd.parm0 = RID_RW; 252162306a36Sopenharmony_ci cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux); 252262306a36Sopenharmony_ci cmd.parm2 = 1; /* Magic number... */ 252362306a36Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp, true); 252462306a36Sopenharmony_ci if (rc != SUCCESS) { 252562306a36Sopenharmony_ci airo_print_err(ai->dev->name, "Couldn't allocate RID"); 252662306a36Sopenharmony_ci return rc; 252762306a36Sopenharmony_ci } 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci memcpy_toio(ai->config_desc.card_ram_off, 253062306a36Sopenharmony_ci &ai->config_desc.rid_desc, sizeof(Rid)); 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci return rc; 253362306a36Sopenharmony_ci} 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci/* 253662306a36Sopenharmony_ci * We are setting up three things here: 253762306a36Sopenharmony_ci * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid. 253862306a36Sopenharmony_ci * 2) Map PCI memory for issuing commands. 253962306a36Sopenharmony_ci * 3) Allocate memory (shared) to send and receive ethernet frames. 254062306a36Sopenharmony_ci */ 254162306a36Sopenharmony_cistatic int mpi_map_card(struct airo_info *ai, struct pci_dev *pci) 254262306a36Sopenharmony_ci{ 254362306a36Sopenharmony_ci unsigned long mem_start, mem_len, aux_start, aux_len; 254462306a36Sopenharmony_ci int rc = -1; 254562306a36Sopenharmony_ci int i; 254662306a36Sopenharmony_ci dma_addr_t busaddroff; 254762306a36Sopenharmony_ci unsigned char *vpackoff; 254862306a36Sopenharmony_ci unsigned char __iomem *pciaddroff; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci mem_start = pci_resource_start(pci, 1); 255162306a36Sopenharmony_ci mem_len = pci_resource_len(pci, 1); 255262306a36Sopenharmony_ci aux_start = pci_resource_start(pci, 2); 255362306a36Sopenharmony_ci aux_len = AUXMEMSIZE; 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_ci if (!request_mem_region(mem_start, mem_len, DRV_NAME)) { 255662306a36Sopenharmony_ci airo_print_err("", "Couldn't get region %x[%x]", 255762306a36Sopenharmony_ci (int)mem_start, (int)mem_len); 255862306a36Sopenharmony_ci goto out; 255962306a36Sopenharmony_ci } 256062306a36Sopenharmony_ci if (!request_mem_region(aux_start, aux_len, DRV_NAME)) { 256162306a36Sopenharmony_ci airo_print_err("", "Couldn't get region %x[%x]", 256262306a36Sopenharmony_ci (int)aux_start, (int)aux_len); 256362306a36Sopenharmony_ci goto free_region1; 256462306a36Sopenharmony_ci } 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci ai->pcimem = ioremap(mem_start, mem_len); 256762306a36Sopenharmony_ci if (!ai->pcimem) { 256862306a36Sopenharmony_ci airo_print_err("", "Couldn't map region %x[%x]", 256962306a36Sopenharmony_ci (int)mem_start, (int)mem_len); 257062306a36Sopenharmony_ci goto free_region2; 257162306a36Sopenharmony_ci } 257262306a36Sopenharmony_ci ai->pciaux = ioremap(aux_start, aux_len); 257362306a36Sopenharmony_ci if (!ai->pciaux) { 257462306a36Sopenharmony_ci airo_print_err("", "Couldn't map region %x[%x]", 257562306a36Sopenharmony_ci (int)aux_start, (int)aux_len); 257662306a36Sopenharmony_ci goto free_memmap; 257762306a36Sopenharmony_ci } 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ci /* Reserve PKTSIZE for each fid and 2K for the Rids */ 258062306a36Sopenharmony_ci ai->shared = dma_alloc_coherent(&pci->dev, PCI_SHARED_LEN, 258162306a36Sopenharmony_ci &ai->shared_dma, GFP_KERNEL); 258262306a36Sopenharmony_ci if (!ai->shared) { 258362306a36Sopenharmony_ci airo_print_err("", "Couldn't alloc_coherent %d", 258462306a36Sopenharmony_ci PCI_SHARED_LEN); 258562306a36Sopenharmony_ci goto free_auxmap; 258662306a36Sopenharmony_ci } 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci /* 258962306a36Sopenharmony_ci * Setup descriptor RX, TX, CONFIG 259062306a36Sopenharmony_ci */ 259162306a36Sopenharmony_ci busaddroff = ai->shared_dma; 259262306a36Sopenharmony_ci pciaddroff = ai->pciaux + AUX_OFFSET; 259362306a36Sopenharmony_ci vpackoff = ai->shared; 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci /* RX descriptor setup */ 259662306a36Sopenharmony_ci for (i = 0; i < MPI_MAX_FIDS; i++) { 259762306a36Sopenharmony_ci ai->rxfids[i].pending = 0; 259862306a36Sopenharmony_ci ai->rxfids[i].card_ram_off = pciaddroff; 259962306a36Sopenharmony_ci ai->rxfids[i].virtual_host_addr = vpackoff; 260062306a36Sopenharmony_ci ai->rxfids[i].rx_desc.host_addr = busaddroff; 260162306a36Sopenharmony_ci ai->rxfids[i].rx_desc.valid = 1; 260262306a36Sopenharmony_ci ai->rxfids[i].rx_desc.len = PKTSIZE; 260362306a36Sopenharmony_ci ai->rxfids[i].rx_desc.rdy = 0; 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci pciaddroff += sizeof(RxFid); 260662306a36Sopenharmony_ci busaddroff += PKTSIZE; 260762306a36Sopenharmony_ci vpackoff += PKTSIZE; 260862306a36Sopenharmony_ci } 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci /* TX descriptor setup */ 261162306a36Sopenharmony_ci for (i = 0; i < MPI_MAX_FIDS; i++) { 261262306a36Sopenharmony_ci ai->txfids[i].card_ram_off = pciaddroff; 261362306a36Sopenharmony_ci ai->txfids[i].virtual_host_addr = vpackoff; 261462306a36Sopenharmony_ci ai->txfids[i].tx_desc.valid = 1; 261562306a36Sopenharmony_ci ai->txfids[i].tx_desc.host_addr = busaddroff; 261662306a36Sopenharmony_ci memcpy(ai->txfids[i].virtual_host_addr, 261762306a36Sopenharmony_ci &wifictlhdr8023, sizeof(wifictlhdr8023)); 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci pciaddroff += sizeof(TxFid); 262062306a36Sopenharmony_ci busaddroff += PKTSIZE; 262162306a36Sopenharmony_ci vpackoff += PKTSIZE; 262262306a36Sopenharmony_ci } 262362306a36Sopenharmony_ci ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */ 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci /* Rid descriptor setup */ 262662306a36Sopenharmony_ci ai->config_desc.card_ram_off = pciaddroff; 262762306a36Sopenharmony_ci ai->config_desc.virtual_host_addr = vpackoff; 262862306a36Sopenharmony_ci ai->config_desc.rid_desc.host_addr = busaddroff; 262962306a36Sopenharmony_ci ai->ridbus = busaddroff; 263062306a36Sopenharmony_ci ai->config_desc.rid_desc.rid = 0; 263162306a36Sopenharmony_ci ai->config_desc.rid_desc.len = RIDSIZE; 263262306a36Sopenharmony_ci ai->config_desc.rid_desc.valid = 1; 263362306a36Sopenharmony_ci pciaddroff += sizeof(Rid); 263462306a36Sopenharmony_ci busaddroff += RIDSIZE; 263562306a36Sopenharmony_ci vpackoff += RIDSIZE; 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci /* Tell card about descriptors */ 263862306a36Sopenharmony_ci if (mpi_init_descriptors (ai) != SUCCESS) 263962306a36Sopenharmony_ci goto free_shared; 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci return 0; 264262306a36Sopenharmony_ci free_shared: 264362306a36Sopenharmony_ci dma_free_coherent(&pci->dev, PCI_SHARED_LEN, ai->shared, 264462306a36Sopenharmony_ci ai->shared_dma); 264562306a36Sopenharmony_ci free_auxmap: 264662306a36Sopenharmony_ci iounmap(ai->pciaux); 264762306a36Sopenharmony_ci free_memmap: 264862306a36Sopenharmony_ci iounmap(ai->pcimem); 264962306a36Sopenharmony_ci free_region2: 265062306a36Sopenharmony_ci release_mem_region(aux_start, aux_len); 265162306a36Sopenharmony_ci free_region1: 265262306a36Sopenharmony_ci release_mem_region(mem_start, mem_len); 265362306a36Sopenharmony_ci out: 265462306a36Sopenharmony_ci return rc; 265562306a36Sopenharmony_ci} 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_cistatic const struct header_ops airo_header_ops = { 265862306a36Sopenharmony_ci .parse = wll_header_parse, 265962306a36Sopenharmony_ci}; 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_cistatic const struct net_device_ops airo11_netdev_ops = { 266262306a36Sopenharmony_ci .ndo_open = airo_open, 266362306a36Sopenharmony_ci .ndo_stop = airo_close, 266462306a36Sopenharmony_ci .ndo_start_xmit = airo_start_xmit11, 266562306a36Sopenharmony_ci .ndo_get_stats = airo_get_stats, 266662306a36Sopenharmony_ci .ndo_set_mac_address = airo_set_mac_address, 266762306a36Sopenharmony_ci .ndo_siocdevprivate = airo_siocdevprivate, 266862306a36Sopenharmony_ci}; 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_cistatic void wifi_setup(struct net_device *dev) 267162306a36Sopenharmony_ci{ 267262306a36Sopenharmony_ci dev->netdev_ops = &airo11_netdev_ops; 267362306a36Sopenharmony_ci dev->header_ops = &airo_header_ops; 267462306a36Sopenharmony_ci dev->wireless_handlers = &airo_handler_def; 267562306a36Sopenharmony_ci 267662306a36Sopenharmony_ci dev->type = ARPHRD_IEEE80211; 267762306a36Sopenharmony_ci dev->hard_header_len = ETH_HLEN; 267862306a36Sopenharmony_ci dev->mtu = AIRO_DEF_MTU; 267962306a36Sopenharmony_ci dev->min_mtu = 68; 268062306a36Sopenharmony_ci dev->max_mtu = MIC_MSGLEN_MAX; 268162306a36Sopenharmony_ci dev->addr_len = ETH_ALEN; 268262306a36Sopenharmony_ci dev->tx_queue_len = 100; 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci eth_broadcast_addr(dev->broadcast); 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_ci dev->flags = IFF_BROADCAST|IFF_MULTICAST; 268762306a36Sopenharmony_ci} 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_cistatic struct net_device *init_wifidev(struct airo_info *ai, 269062306a36Sopenharmony_ci struct net_device *ethdev) 269162306a36Sopenharmony_ci{ 269262306a36Sopenharmony_ci int err; 269362306a36Sopenharmony_ci struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN, 269462306a36Sopenharmony_ci wifi_setup); 269562306a36Sopenharmony_ci if (!dev) 269662306a36Sopenharmony_ci return NULL; 269762306a36Sopenharmony_ci dev->ml_priv = ethdev->ml_priv; 269862306a36Sopenharmony_ci dev->irq = ethdev->irq; 269962306a36Sopenharmony_ci dev->base_addr = ethdev->base_addr; 270062306a36Sopenharmony_ci dev->wireless_data = ethdev->wireless_data; 270162306a36Sopenharmony_ci SET_NETDEV_DEV(dev, ethdev->dev.parent); 270262306a36Sopenharmony_ci eth_hw_addr_inherit(dev, ethdev); 270362306a36Sopenharmony_ci err = register_netdev(dev); 270462306a36Sopenharmony_ci if (err<0) { 270562306a36Sopenharmony_ci free_netdev(dev); 270662306a36Sopenharmony_ci return NULL; 270762306a36Sopenharmony_ci } 270862306a36Sopenharmony_ci return dev; 270962306a36Sopenharmony_ci} 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_cistatic int reset_card(struct net_device *dev, int lock) 271262306a36Sopenharmony_ci{ 271362306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ci if (lock && down_interruptible(&ai->sem)) 271662306a36Sopenharmony_ci return -1; 271762306a36Sopenharmony_ci waitbusy (ai); 271862306a36Sopenharmony_ci OUT4500(ai, COMMAND, CMD_SOFTRESET); 271962306a36Sopenharmony_ci msleep(200); 272062306a36Sopenharmony_ci waitbusy (ai); 272162306a36Sopenharmony_ci msleep(200); 272262306a36Sopenharmony_ci if (lock) 272362306a36Sopenharmony_ci up(&ai->sem); 272462306a36Sopenharmony_ci return 0; 272562306a36Sopenharmony_ci} 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci#define AIRO_MAX_NETWORK_COUNT 64 272862306a36Sopenharmony_cistatic int airo_networks_allocate(struct airo_info *ai) 272962306a36Sopenharmony_ci{ 273062306a36Sopenharmony_ci if (ai->networks) 273162306a36Sopenharmony_ci return 0; 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_ci ai->networks = kcalloc(AIRO_MAX_NETWORK_COUNT, sizeof(BSSListElement), 273462306a36Sopenharmony_ci GFP_KERNEL); 273562306a36Sopenharmony_ci if (!ai->networks) { 273662306a36Sopenharmony_ci airo_print_warn("", "Out of memory allocating beacons"); 273762306a36Sopenharmony_ci return -ENOMEM; 273862306a36Sopenharmony_ci } 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci return 0; 274162306a36Sopenharmony_ci} 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_cistatic void airo_networks_free(struct airo_info *ai) 274462306a36Sopenharmony_ci{ 274562306a36Sopenharmony_ci kfree(ai->networks); 274662306a36Sopenharmony_ci ai->networks = NULL; 274762306a36Sopenharmony_ci} 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_cistatic void airo_networks_initialize(struct airo_info *ai) 275062306a36Sopenharmony_ci{ 275162306a36Sopenharmony_ci int i; 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci INIT_LIST_HEAD(&ai->network_free_list); 275462306a36Sopenharmony_ci INIT_LIST_HEAD(&ai->network_list); 275562306a36Sopenharmony_ci for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++) 275662306a36Sopenharmony_ci list_add_tail(&ai->networks[i].list, 275762306a36Sopenharmony_ci &ai->network_free_list); 275862306a36Sopenharmony_ci} 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_cistatic const struct net_device_ops airo_netdev_ops = { 276162306a36Sopenharmony_ci .ndo_open = airo_open, 276262306a36Sopenharmony_ci .ndo_stop = airo_close, 276362306a36Sopenharmony_ci .ndo_start_xmit = airo_start_xmit, 276462306a36Sopenharmony_ci .ndo_get_stats = airo_get_stats, 276562306a36Sopenharmony_ci .ndo_set_rx_mode = airo_set_multicast_list, 276662306a36Sopenharmony_ci .ndo_set_mac_address = airo_set_mac_address, 276762306a36Sopenharmony_ci .ndo_siocdevprivate = airo_siocdevprivate, 276862306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 276962306a36Sopenharmony_ci}; 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_cistatic const struct net_device_ops mpi_netdev_ops = { 277262306a36Sopenharmony_ci .ndo_open = airo_open, 277362306a36Sopenharmony_ci .ndo_stop = airo_close, 277462306a36Sopenharmony_ci .ndo_start_xmit = mpi_start_xmit, 277562306a36Sopenharmony_ci .ndo_get_stats = airo_get_stats, 277662306a36Sopenharmony_ci .ndo_set_rx_mode = airo_set_multicast_list, 277762306a36Sopenharmony_ci .ndo_set_mac_address = airo_set_mac_address, 277862306a36Sopenharmony_ci .ndo_siocdevprivate = airo_siocdevprivate, 277962306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 278062306a36Sopenharmony_ci}; 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_cistatic struct net_device *_init_airo_card(unsigned short irq, int port, 278462306a36Sopenharmony_ci int is_pcmcia, struct pci_dev *pci, 278562306a36Sopenharmony_ci struct device *dmdev) 278662306a36Sopenharmony_ci{ 278762306a36Sopenharmony_ci struct net_device *dev; 278862306a36Sopenharmony_ci struct airo_info *ai; 278962306a36Sopenharmony_ci int i, rc; 279062306a36Sopenharmony_ci CapabilityRid cap_rid; 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci /* Create the network device object. */ 279362306a36Sopenharmony_ci dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup); 279462306a36Sopenharmony_ci if (!dev) { 279562306a36Sopenharmony_ci airo_print_err("", "Couldn't alloc_etherdev"); 279662306a36Sopenharmony_ci return NULL; 279762306a36Sopenharmony_ci } 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci ai = dev->ml_priv = netdev_priv(dev); 280062306a36Sopenharmony_ci ai->wifidev = NULL; 280162306a36Sopenharmony_ci ai->flags = 1 << FLAG_RADIO_DOWN; 280262306a36Sopenharmony_ci ai->jobs = 0; 280362306a36Sopenharmony_ci ai->dev = dev; 280462306a36Sopenharmony_ci if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) { 280562306a36Sopenharmony_ci airo_print_dbg("", "Found an MPI350 card"); 280662306a36Sopenharmony_ci set_bit(FLAG_MPI, &ai->flags); 280762306a36Sopenharmony_ci } 280862306a36Sopenharmony_ci spin_lock_init(&ai->aux_lock); 280962306a36Sopenharmony_ci sema_init(&ai->sem, 1); 281062306a36Sopenharmony_ci ai->config.len = 0; 281162306a36Sopenharmony_ci ai->pci = pci; 281262306a36Sopenharmony_ci init_waitqueue_head (&ai->thr_wait); 281362306a36Sopenharmony_ci ai->tfm = NULL; 281462306a36Sopenharmony_ci add_airo_dev(ai); 281562306a36Sopenharmony_ci ai->APList.len = cpu_to_le16(sizeof(struct APListRid)); 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci if (airo_networks_allocate (ai)) 281862306a36Sopenharmony_ci goto err_out_free; 281962306a36Sopenharmony_ci airo_networks_initialize (ai); 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ci skb_queue_head_init (&ai->txq); 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci /* The Airo-specific entries in the device structure. */ 282462306a36Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) 282562306a36Sopenharmony_ci dev->netdev_ops = &mpi_netdev_ops; 282662306a36Sopenharmony_ci else 282762306a36Sopenharmony_ci dev->netdev_ops = &airo_netdev_ops; 282862306a36Sopenharmony_ci dev->wireless_handlers = &airo_handler_def; 282962306a36Sopenharmony_ci ai->wireless_data.spy_data = &ai->spy_data; 283062306a36Sopenharmony_ci dev->wireless_data = &ai->wireless_data; 283162306a36Sopenharmony_ci dev->irq = irq; 283262306a36Sopenharmony_ci dev->base_addr = port; 283362306a36Sopenharmony_ci dev->priv_flags &= ~IFF_TX_SKB_SHARING; 283462306a36Sopenharmony_ci dev->max_mtu = MIC_MSGLEN_MAX; 283562306a36Sopenharmony_ci 283662306a36Sopenharmony_ci SET_NETDEV_DEV(dev, dmdev); 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_ci reset_card (dev, 1); 283962306a36Sopenharmony_ci msleep(400); 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci if (!is_pcmcia) { 284262306a36Sopenharmony_ci if (!request_region(dev->base_addr, 64, DRV_NAME)) { 284362306a36Sopenharmony_ci rc = -EBUSY; 284462306a36Sopenharmony_ci airo_print_err(dev->name, "Couldn't request region"); 284562306a36Sopenharmony_ci goto err_out_nets; 284662306a36Sopenharmony_ci } 284762306a36Sopenharmony_ci } 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) { 285062306a36Sopenharmony_ci if (mpi_map_card(ai, pci)) { 285162306a36Sopenharmony_ci airo_print_err("", "Could not map memory"); 285262306a36Sopenharmony_ci goto err_out_res; 285362306a36Sopenharmony_ci } 285462306a36Sopenharmony_ci } 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci if (probe) { 285762306a36Sopenharmony_ci if (setup_card(ai, dev, 1) != SUCCESS) { 285862306a36Sopenharmony_ci airo_print_err(dev->name, "MAC could not be enabled"); 285962306a36Sopenharmony_ci rc = -EIO; 286062306a36Sopenharmony_ci goto err_out_map; 286162306a36Sopenharmony_ci } 286262306a36Sopenharmony_ci } else if (!test_bit(FLAG_MPI,&ai->flags)) { 286362306a36Sopenharmony_ci ai->bap_read = fast_bap_read; 286462306a36Sopenharmony_ci set_bit(FLAG_FLASHING, &ai->flags); 286562306a36Sopenharmony_ci } 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_ci strcpy(dev->name, "eth%d"); 286862306a36Sopenharmony_ci rc = register_netdev(dev); 286962306a36Sopenharmony_ci if (rc) { 287062306a36Sopenharmony_ci airo_print_err(dev->name, "Couldn't register_netdev"); 287162306a36Sopenharmony_ci goto err_out_map; 287262306a36Sopenharmony_ci } 287362306a36Sopenharmony_ci ai->wifidev = init_wifidev(ai, dev); 287462306a36Sopenharmony_ci if (!ai->wifidev) 287562306a36Sopenharmony_ci goto err_out_reg; 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_ci rc = readCapabilityRid(ai, &cap_rid, 1); 287862306a36Sopenharmony_ci if (rc != SUCCESS) { 287962306a36Sopenharmony_ci rc = -EIO; 288062306a36Sopenharmony_ci goto err_out_wifi; 288162306a36Sopenharmony_ci } 288262306a36Sopenharmony_ci /* WEP capability discovery */ 288362306a36Sopenharmony_ci ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0; 288462306a36Sopenharmony_ci ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0; 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_ci airo_print_info(dev->name, "Firmware version %x.%x.%02d", 288762306a36Sopenharmony_ci ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF), 288862306a36Sopenharmony_ci (le16_to_cpu(cap_rid.softVer) & 0xFF), 288962306a36Sopenharmony_ci le16_to_cpu(cap_rid.softSubVer)); 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci /* Test for WPA support */ 289262306a36Sopenharmony_ci /* Only firmware versions 5.30.17 or better can do WPA */ 289362306a36Sopenharmony_ci if (le16_to_cpu(cap_rid.softVer) > 0x530 289462306a36Sopenharmony_ci || (le16_to_cpu(cap_rid.softVer) == 0x530 289562306a36Sopenharmony_ci && le16_to_cpu(cap_rid.softSubVer) >= 17)) { 289662306a36Sopenharmony_ci airo_print_info(ai->dev->name, "WPA supported."); 289762306a36Sopenharmony_ci 289862306a36Sopenharmony_ci set_bit(FLAG_WPA_CAPABLE, &ai->flags); 289962306a36Sopenharmony_ci ai->bssListFirst = RID_WPA_BSSLISTFIRST; 290062306a36Sopenharmony_ci ai->bssListNext = RID_WPA_BSSLISTNEXT; 290162306a36Sopenharmony_ci ai->bssListRidLen = sizeof(BSSListRid); 290262306a36Sopenharmony_ci } else { 290362306a36Sopenharmony_ci airo_print_info(ai->dev->name, "WPA unsupported with firmware " 290462306a36Sopenharmony_ci "versions older than 5.30.17."); 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_ci ai->bssListFirst = RID_BSSLISTFIRST; 290762306a36Sopenharmony_ci ai->bssListNext = RID_BSSLISTNEXT; 290862306a36Sopenharmony_ci ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra); 290962306a36Sopenharmony_ci } 291062306a36Sopenharmony_ci 291162306a36Sopenharmony_ci set_bit(FLAG_REGISTERED,&ai->flags); 291262306a36Sopenharmony_ci airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr); 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci /* Allocate the transmit buffers */ 291562306a36Sopenharmony_ci if (probe && !test_bit(FLAG_MPI,&ai->flags)) 291662306a36Sopenharmony_ci for (i = 0; i < MAX_FIDS; i++) 291762306a36Sopenharmony_ci ai->fids[i] = transmit_allocate(ai, AIRO_DEF_MTU, i>=MAX_FIDS/2); 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci if (setup_proc_entry(dev, dev->ml_priv) < 0) 292062306a36Sopenharmony_ci goto err_out_wifi; 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci return dev; 292362306a36Sopenharmony_ci 292462306a36Sopenharmony_cierr_out_wifi: 292562306a36Sopenharmony_ci unregister_netdev(ai->wifidev); 292662306a36Sopenharmony_ci free_netdev(ai->wifidev); 292762306a36Sopenharmony_cierr_out_reg: 292862306a36Sopenharmony_ci unregister_netdev(dev); 292962306a36Sopenharmony_cierr_out_map: 293062306a36Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags) && pci) { 293162306a36Sopenharmony_ci dma_free_coherent(&pci->dev, PCI_SHARED_LEN, ai->shared, 293262306a36Sopenharmony_ci ai->shared_dma); 293362306a36Sopenharmony_ci iounmap(ai->pciaux); 293462306a36Sopenharmony_ci iounmap(ai->pcimem); 293562306a36Sopenharmony_ci mpi_unmap_card(ai->pci); 293662306a36Sopenharmony_ci } 293762306a36Sopenharmony_cierr_out_res: 293862306a36Sopenharmony_ci if (!is_pcmcia) 293962306a36Sopenharmony_ci release_region(dev->base_addr, 64); 294062306a36Sopenharmony_cierr_out_nets: 294162306a36Sopenharmony_ci airo_networks_free(ai); 294262306a36Sopenharmony_cierr_out_free: 294362306a36Sopenharmony_ci del_airo_dev(ai); 294462306a36Sopenharmony_ci free_netdev(dev); 294562306a36Sopenharmony_ci return NULL; 294662306a36Sopenharmony_ci} 294762306a36Sopenharmony_ci 294862306a36Sopenharmony_cistruct net_device *init_airo_card(unsigned short irq, int port, int is_pcmcia, 294962306a36Sopenharmony_ci struct device *dmdev) 295062306a36Sopenharmony_ci{ 295162306a36Sopenharmony_ci return _init_airo_card (irq, port, is_pcmcia, NULL, dmdev); 295262306a36Sopenharmony_ci} 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ciEXPORT_SYMBOL(init_airo_card); 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_cistatic int waitbusy (struct airo_info *ai) 295762306a36Sopenharmony_ci{ 295862306a36Sopenharmony_ci int delay = 0; 295962306a36Sopenharmony_ci while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) { 296062306a36Sopenharmony_ci udelay (10); 296162306a36Sopenharmony_ci if ((++delay % 20) == 0) 296262306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); 296362306a36Sopenharmony_ci } 296462306a36Sopenharmony_ci return delay < 10000; 296562306a36Sopenharmony_ci} 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ciint reset_airo_card(struct net_device *dev) 296862306a36Sopenharmony_ci{ 296962306a36Sopenharmony_ci int i; 297062306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci if (reset_card (dev, 1)) 297362306a36Sopenharmony_ci return -1; 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_ci if (setup_card(ai, dev, 1) != SUCCESS) { 297662306a36Sopenharmony_ci airo_print_err(dev->name, "MAC could not be enabled"); 297762306a36Sopenharmony_ci return -1; 297862306a36Sopenharmony_ci } 297962306a36Sopenharmony_ci airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr); 298062306a36Sopenharmony_ci /* Allocate the transmit buffers if needed */ 298162306a36Sopenharmony_ci if (!test_bit(FLAG_MPI,&ai->flags)) 298262306a36Sopenharmony_ci for (i = 0; i < MAX_FIDS; i++) 298362306a36Sopenharmony_ci ai->fids[i] = transmit_allocate (ai, AIRO_DEF_MTU, i>=MAX_FIDS/2); 298462306a36Sopenharmony_ci 298562306a36Sopenharmony_ci enable_interrupts(ai); 298662306a36Sopenharmony_ci netif_wake_queue(dev); 298762306a36Sopenharmony_ci return 0; 298862306a36Sopenharmony_ci} 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_ciEXPORT_SYMBOL(reset_airo_card); 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_cistatic void airo_send_event(struct net_device *dev) 299362306a36Sopenharmony_ci{ 299462306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 299562306a36Sopenharmony_ci union iwreq_data wrqu; 299662306a36Sopenharmony_ci StatusRid status_rid; 299762306a36Sopenharmony_ci 299862306a36Sopenharmony_ci clear_bit(JOB_EVENT, &ai->jobs); 299962306a36Sopenharmony_ci PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0); 300062306a36Sopenharmony_ci up(&ai->sem); 300162306a36Sopenharmony_ci wrqu.data.length = 0; 300262306a36Sopenharmony_ci wrqu.data.flags = 0; 300362306a36Sopenharmony_ci memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN); 300462306a36Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci /* Send event to user space */ 300762306a36Sopenharmony_ci wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 300862306a36Sopenharmony_ci} 300962306a36Sopenharmony_ci 301062306a36Sopenharmony_cistatic void airo_process_scan_results (struct airo_info *ai) 301162306a36Sopenharmony_ci{ 301262306a36Sopenharmony_ci union iwreq_data wrqu; 301362306a36Sopenharmony_ci BSSListRid bss; 301462306a36Sopenharmony_ci int rc; 301562306a36Sopenharmony_ci BSSListElement * loop_net; 301662306a36Sopenharmony_ci BSSListElement * tmp_net; 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci /* Blow away current list of scan results */ 301962306a36Sopenharmony_ci list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) { 302062306a36Sopenharmony_ci list_move_tail (&loop_net->list, &ai->network_free_list); 302162306a36Sopenharmony_ci /* Don't blow away ->list, just BSS data */ 302262306a36Sopenharmony_ci memset (loop_net, 0, sizeof (loop_net->bss)); 302362306a36Sopenharmony_ci } 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ci /* Try to read the first entry of the scan result */ 302662306a36Sopenharmony_ci rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0); 302762306a36Sopenharmony_ci if ((rc) || (bss.index == cpu_to_le16(0xffff))) { 302862306a36Sopenharmony_ci /* No scan results */ 302962306a36Sopenharmony_ci goto out; 303062306a36Sopenharmony_ci } 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_ci /* Read and parse all entries */ 303362306a36Sopenharmony_ci tmp_net = NULL; 303462306a36Sopenharmony_ci while ((!rc) && (bss.index != cpu_to_le16(0xffff))) { 303562306a36Sopenharmony_ci /* Grab a network off the free list */ 303662306a36Sopenharmony_ci if (!list_empty(&ai->network_free_list)) { 303762306a36Sopenharmony_ci tmp_net = list_entry(ai->network_free_list.next, 303862306a36Sopenharmony_ci BSSListElement, list); 303962306a36Sopenharmony_ci list_del(ai->network_free_list.next); 304062306a36Sopenharmony_ci } 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci if (tmp_net != NULL) { 304362306a36Sopenharmony_ci memcpy(tmp_net, &bss, sizeof(tmp_net->bss)); 304462306a36Sopenharmony_ci list_add_tail(&tmp_net->list, &ai->network_list); 304562306a36Sopenharmony_ci tmp_net = NULL; 304662306a36Sopenharmony_ci } 304762306a36Sopenharmony_ci 304862306a36Sopenharmony_ci /* Read next entry */ 304962306a36Sopenharmony_ci rc = PC4500_readrid(ai, ai->bssListNext, 305062306a36Sopenharmony_ci &bss, ai->bssListRidLen, 0); 305162306a36Sopenharmony_ci } 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ciout: 305462306a36Sopenharmony_ci /* write APList back (we cleared it in airo_set_scan) */ 305562306a36Sopenharmony_ci disable_MAC(ai, 2); 305662306a36Sopenharmony_ci writeAPListRid(ai, &ai->APList, 0); 305762306a36Sopenharmony_ci enable_MAC(ai, 0); 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci ai->scan_timeout = 0; 306062306a36Sopenharmony_ci clear_bit(JOB_SCAN_RESULTS, &ai->jobs); 306162306a36Sopenharmony_ci up(&ai->sem); 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci /* Send an empty event to user space. 306462306a36Sopenharmony_ci * We don't send the received data on 306562306a36Sopenharmony_ci * the event because it would require 306662306a36Sopenharmony_ci * us to do complex transcoding, and 306762306a36Sopenharmony_ci * we want to minimise the work done in 306862306a36Sopenharmony_ci * the irq handler. Use a request to 306962306a36Sopenharmony_ci * extract the data - Jean II */ 307062306a36Sopenharmony_ci wrqu.data.length = 0; 307162306a36Sopenharmony_ci wrqu.data.flags = 0; 307262306a36Sopenharmony_ci wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL); 307362306a36Sopenharmony_ci} 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_cistatic int airo_thread(void *data) 307662306a36Sopenharmony_ci{ 307762306a36Sopenharmony_ci struct net_device *dev = data; 307862306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 307962306a36Sopenharmony_ci int locked; 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci set_freezable(); 308262306a36Sopenharmony_ci while (1) { 308362306a36Sopenharmony_ci /* make swsusp happy with our thread */ 308462306a36Sopenharmony_ci try_to_freeze(); 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci if (test_bit(JOB_DIE, &ai->jobs)) 308762306a36Sopenharmony_ci break; 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci if (ai->jobs) { 309062306a36Sopenharmony_ci locked = down_interruptible(&ai->sem); 309162306a36Sopenharmony_ci } else { 309262306a36Sopenharmony_ci wait_queue_entry_t wait; 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_ci init_waitqueue_entry(&wait, current); 309562306a36Sopenharmony_ci add_wait_queue(&ai->thr_wait, &wait); 309662306a36Sopenharmony_ci for (;;) { 309762306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 309862306a36Sopenharmony_ci if (ai->jobs) 309962306a36Sopenharmony_ci break; 310062306a36Sopenharmony_ci if (ai->expires || ai->scan_timeout) { 310162306a36Sopenharmony_ci if (ai->scan_timeout && 310262306a36Sopenharmony_ci time_after_eq(jiffies, ai->scan_timeout)) { 310362306a36Sopenharmony_ci set_bit(JOB_SCAN_RESULTS, &ai->jobs); 310462306a36Sopenharmony_ci break; 310562306a36Sopenharmony_ci } else if (ai->expires && 310662306a36Sopenharmony_ci time_after_eq(jiffies, ai->expires)) { 310762306a36Sopenharmony_ci set_bit(JOB_AUTOWEP, &ai->jobs); 310862306a36Sopenharmony_ci break; 310962306a36Sopenharmony_ci } 311062306a36Sopenharmony_ci if (!kthread_should_stop() && 311162306a36Sopenharmony_ci !freezing(current)) { 311262306a36Sopenharmony_ci unsigned long wake_at; 311362306a36Sopenharmony_ci if (!ai->expires || !ai->scan_timeout) { 311462306a36Sopenharmony_ci wake_at = max(ai->expires, 311562306a36Sopenharmony_ci ai->scan_timeout); 311662306a36Sopenharmony_ci } else { 311762306a36Sopenharmony_ci wake_at = min(ai->expires, 311862306a36Sopenharmony_ci ai->scan_timeout); 311962306a36Sopenharmony_ci } 312062306a36Sopenharmony_ci schedule_timeout(wake_at - jiffies); 312162306a36Sopenharmony_ci continue; 312262306a36Sopenharmony_ci } 312362306a36Sopenharmony_ci } else if (!kthread_should_stop() && 312462306a36Sopenharmony_ci !freezing(current)) { 312562306a36Sopenharmony_ci schedule(); 312662306a36Sopenharmony_ci continue; 312762306a36Sopenharmony_ci } 312862306a36Sopenharmony_ci break; 312962306a36Sopenharmony_ci } 313062306a36Sopenharmony_ci __set_current_state(TASK_RUNNING); 313162306a36Sopenharmony_ci remove_wait_queue(&ai->thr_wait, &wait); 313262306a36Sopenharmony_ci locked = 1; 313362306a36Sopenharmony_ci } 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci if (locked) 313662306a36Sopenharmony_ci continue; 313762306a36Sopenharmony_ci 313862306a36Sopenharmony_ci if (test_bit(JOB_DIE, &ai->jobs)) { 313962306a36Sopenharmony_ci up(&ai->sem); 314062306a36Sopenharmony_ci break; 314162306a36Sopenharmony_ci } 314262306a36Sopenharmony_ci 314362306a36Sopenharmony_ci if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) { 314462306a36Sopenharmony_ci up(&ai->sem); 314562306a36Sopenharmony_ci continue; 314662306a36Sopenharmony_ci } 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci if (test_bit(JOB_XMIT, &ai->jobs)) 314962306a36Sopenharmony_ci airo_end_xmit(dev, true); 315062306a36Sopenharmony_ci else if (test_bit(JOB_XMIT11, &ai->jobs)) 315162306a36Sopenharmony_ci airo_end_xmit11(dev, true); 315262306a36Sopenharmony_ci else if (test_bit(JOB_STATS, &ai->jobs)) 315362306a36Sopenharmony_ci airo_read_stats(dev); 315462306a36Sopenharmony_ci else if (test_bit(JOB_PROMISC, &ai->jobs)) 315562306a36Sopenharmony_ci airo_set_promisc(ai, true); 315662306a36Sopenharmony_ci else if (test_bit(JOB_MIC, &ai->jobs)) 315762306a36Sopenharmony_ci micinit(ai); 315862306a36Sopenharmony_ci else if (test_bit(JOB_EVENT, &ai->jobs)) 315962306a36Sopenharmony_ci airo_send_event(dev); 316062306a36Sopenharmony_ci else if (test_bit(JOB_AUTOWEP, &ai->jobs)) 316162306a36Sopenharmony_ci timer_func(dev); 316262306a36Sopenharmony_ci else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs)) 316362306a36Sopenharmony_ci airo_process_scan_results(ai); 316462306a36Sopenharmony_ci else /* Shouldn't get here, but we make sure to unlock */ 316562306a36Sopenharmony_ci up(&ai->sem); 316662306a36Sopenharmony_ci } 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ci return 0; 316962306a36Sopenharmony_ci} 317062306a36Sopenharmony_ci 317162306a36Sopenharmony_cistatic int header_len(__le16 ctl) 317262306a36Sopenharmony_ci{ 317362306a36Sopenharmony_ci u16 fc = le16_to_cpu(ctl); 317462306a36Sopenharmony_ci switch (fc & 0xc) { 317562306a36Sopenharmony_ci case 4: 317662306a36Sopenharmony_ci if ((fc & 0xe0) == 0xc0) 317762306a36Sopenharmony_ci return 10; /* one-address control packet */ 317862306a36Sopenharmony_ci return 16; /* two-address control packet */ 317962306a36Sopenharmony_ci case 8: 318062306a36Sopenharmony_ci if ((fc & 0x300) == 0x300) 318162306a36Sopenharmony_ci return 30; /* WDS packet */ 318262306a36Sopenharmony_ci } 318362306a36Sopenharmony_ci return 24; 318462306a36Sopenharmony_ci} 318562306a36Sopenharmony_ci 318662306a36Sopenharmony_cistatic void airo_handle_cisco_mic(struct airo_info *ai) 318762306a36Sopenharmony_ci{ 318862306a36Sopenharmony_ci if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) { 318962306a36Sopenharmony_ci set_bit(JOB_MIC, &ai->jobs); 319062306a36Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 319162306a36Sopenharmony_ci } 319262306a36Sopenharmony_ci} 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_ci/* Airo Status codes */ 319562306a36Sopenharmony_ci#define STAT_NOBEACON 0x8000 /* Loss of sync - missed beacons */ 319662306a36Sopenharmony_ci#define STAT_MAXRETRIES 0x8001 /* Loss of sync - max retries */ 319762306a36Sopenharmony_ci#define STAT_MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/ 319862306a36Sopenharmony_ci#define STAT_FORCELOSS 0x8003 /* Loss of sync - host request */ 319962306a36Sopenharmony_ci#define STAT_TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */ 320062306a36Sopenharmony_ci#define STAT_DEAUTH 0x8100 /* low byte is 802.11 reason code */ 320162306a36Sopenharmony_ci#define STAT_DISASSOC 0x8200 /* low byte is 802.11 reason code */ 320262306a36Sopenharmony_ci#define STAT_ASSOC_FAIL 0x8400 /* low byte is 802.11 reason code */ 320362306a36Sopenharmony_ci#define STAT_AUTH_FAIL 0x0300 /* low byte is 802.11 reason code */ 320462306a36Sopenharmony_ci#define STAT_ASSOC 0x0400 /* Associated */ 320562306a36Sopenharmony_ci#define STAT_REASSOC 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */ 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_cistatic void airo_print_status(const char *devname, u16 status) 320862306a36Sopenharmony_ci{ 320962306a36Sopenharmony_ci u8 reason = status & 0xFF; 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci switch (status & 0xFF00) { 321262306a36Sopenharmony_ci case STAT_NOBEACON: 321362306a36Sopenharmony_ci switch (status) { 321462306a36Sopenharmony_ci case STAT_NOBEACON: 321562306a36Sopenharmony_ci airo_print_dbg(devname, "link lost (missed beacons)"); 321662306a36Sopenharmony_ci break; 321762306a36Sopenharmony_ci case STAT_MAXRETRIES: 321862306a36Sopenharmony_ci case STAT_MAXARL: 321962306a36Sopenharmony_ci airo_print_dbg(devname, "link lost (max retries)"); 322062306a36Sopenharmony_ci break; 322162306a36Sopenharmony_ci case STAT_FORCELOSS: 322262306a36Sopenharmony_ci airo_print_dbg(devname, "link lost (local choice)"); 322362306a36Sopenharmony_ci break; 322462306a36Sopenharmony_ci case STAT_TSFSYNC: 322562306a36Sopenharmony_ci airo_print_dbg(devname, "link lost (TSF sync lost)"); 322662306a36Sopenharmony_ci break; 322762306a36Sopenharmony_ci default: 322862306a36Sopenharmony_ci airo_print_dbg(devname, "unknown status %x\n", status); 322962306a36Sopenharmony_ci break; 323062306a36Sopenharmony_ci } 323162306a36Sopenharmony_ci break; 323262306a36Sopenharmony_ci case STAT_DEAUTH: 323362306a36Sopenharmony_ci airo_print_dbg(devname, "deauthenticated (reason: %d)", reason); 323462306a36Sopenharmony_ci break; 323562306a36Sopenharmony_ci case STAT_DISASSOC: 323662306a36Sopenharmony_ci airo_print_dbg(devname, "disassociated (reason: %d)", reason); 323762306a36Sopenharmony_ci break; 323862306a36Sopenharmony_ci case STAT_ASSOC_FAIL: 323962306a36Sopenharmony_ci airo_print_dbg(devname, "association failed (reason: %d)", 324062306a36Sopenharmony_ci reason); 324162306a36Sopenharmony_ci break; 324262306a36Sopenharmony_ci case STAT_AUTH_FAIL: 324362306a36Sopenharmony_ci airo_print_dbg(devname, "authentication failed (reason: %d)", 324462306a36Sopenharmony_ci reason); 324562306a36Sopenharmony_ci break; 324662306a36Sopenharmony_ci case STAT_ASSOC: 324762306a36Sopenharmony_ci case STAT_REASSOC: 324862306a36Sopenharmony_ci break; 324962306a36Sopenharmony_ci default: 325062306a36Sopenharmony_ci airo_print_dbg(devname, "unknown status %x\n", status); 325162306a36Sopenharmony_ci break; 325262306a36Sopenharmony_ci } 325362306a36Sopenharmony_ci} 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_cistatic void airo_handle_link(struct airo_info *ai) 325662306a36Sopenharmony_ci{ 325762306a36Sopenharmony_ci union iwreq_data wrqu; 325862306a36Sopenharmony_ci int scan_forceloss = 0; 325962306a36Sopenharmony_ci u16 status; 326062306a36Sopenharmony_ci 326162306a36Sopenharmony_ci /* Get new status and acknowledge the link change */ 326262306a36Sopenharmony_ci status = le16_to_cpu(IN4500(ai, LINKSTAT)); 326362306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_LINK); 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0)) 326662306a36Sopenharmony_ci scan_forceloss = 1; 326762306a36Sopenharmony_ci 326862306a36Sopenharmony_ci airo_print_status(ai->dev->name, status); 326962306a36Sopenharmony_ci 327062306a36Sopenharmony_ci if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) { 327162306a36Sopenharmony_ci if (auto_wep) 327262306a36Sopenharmony_ci ai->expires = 0; 327362306a36Sopenharmony_ci if (ai->list_bss_task) 327462306a36Sopenharmony_ci wake_up_process(ai->list_bss_task); 327562306a36Sopenharmony_ci set_bit(FLAG_UPDATE_UNI, &ai->flags); 327662306a36Sopenharmony_ci set_bit(FLAG_UPDATE_MULTI, &ai->flags); 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_ci set_bit(JOB_EVENT, &ai->jobs); 327962306a36Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci netif_carrier_on(ai->dev); 328262306a36Sopenharmony_ci } else if (!scan_forceloss) { 328362306a36Sopenharmony_ci if (auto_wep && !ai->expires) { 328462306a36Sopenharmony_ci ai->expires = RUN_AT(3*HZ); 328562306a36Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 328662306a36Sopenharmony_ci } 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci /* Send event to user space */ 328962306a36Sopenharmony_ci eth_zero_addr(wrqu.ap_addr.sa_data); 329062306a36Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 329162306a36Sopenharmony_ci wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL); 329262306a36Sopenharmony_ci netif_carrier_off(ai->dev); 329362306a36Sopenharmony_ci } else { 329462306a36Sopenharmony_ci netif_carrier_off(ai->dev); 329562306a36Sopenharmony_ci } 329662306a36Sopenharmony_ci} 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_cistatic void airo_handle_rx(struct airo_info *ai) 329962306a36Sopenharmony_ci{ 330062306a36Sopenharmony_ci struct sk_buff *skb = NULL; 330162306a36Sopenharmony_ci __le16 fc, v, *buffer, tmpbuf[4]; 330262306a36Sopenharmony_ci u16 len, hdrlen = 0, gap, fid; 330362306a36Sopenharmony_ci struct rx_hdr hdr; 330462306a36Sopenharmony_ci int success = 0; 330562306a36Sopenharmony_ci 330662306a36Sopenharmony_ci if (test_bit(FLAG_MPI, &ai->flags)) { 330762306a36Sopenharmony_ci if (test_bit(FLAG_802_11, &ai->flags)) 330862306a36Sopenharmony_ci mpi_receive_802_11(ai); 330962306a36Sopenharmony_ci else 331062306a36Sopenharmony_ci mpi_receive_802_3(ai); 331162306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_RX); 331262306a36Sopenharmony_ci return; 331362306a36Sopenharmony_ci } 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_ci fid = IN4500(ai, RXFID); 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci /* Get the packet length */ 331862306a36Sopenharmony_ci if (test_bit(FLAG_802_11, &ai->flags)) { 331962306a36Sopenharmony_ci bap_setup (ai, fid, 4, BAP0); 332062306a36Sopenharmony_ci bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0); 332162306a36Sopenharmony_ci /* Bad CRC. Ignore packet */ 332262306a36Sopenharmony_ci if (le16_to_cpu(hdr.status) & 2) 332362306a36Sopenharmony_ci hdr.len = 0; 332462306a36Sopenharmony_ci if (ai->wifidev == NULL) 332562306a36Sopenharmony_ci hdr.len = 0; 332662306a36Sopenharmony_ci } else { 332762306a36Sopenharmony_ci bap_setup(ai, fid, 0x36, BAP0); 332862306a36Sopenharmony_ci bap_read(ai, &hdr.len, 2, BAP0); 332962306a36Sopenharmony_ci } 333062306a36Sopenharmony_ci len = le16_to_cpu(hdr.len); 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci if (len > AIRO_DEF_MTU) { 333362306a36Sopenharmony_ci airo_print_err(ai->dev->name, "Bad size %d", len); 333462306a36Sopenharmony_ci goto done; 333562306a36Sopenharmony_ci } 333662306a36Sopenharmony_ci if (len == 0) 333762306a36Sopenharmony_ci goto done; 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci if (test_bit(FLAG_802_11, &ai->flags)) { 334062306a36Sopenharmony_ci bap_read(ai, &fc, sizeof (fc), BAP0); 334162306a36Sopenharmony_ci hdrlen = header_len(fc); 334262306a36Sopenharmony_ci } else 334362306a36Sopenharmony_ci hdrlen = ETH_ALEN * 2; 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci skb = dev_alloc_skb(len + hdrlen + 2 + 2); 334662306a36Sopenharmony_ci if (!skb) { 334762306a36Sopenharmony_ci ai->dev->stats.rx_dropped++; 334862306a36Sopenharmony_ci goto done; 334962306a36Sopenharmony_ci } 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci skb_reserve(skb, 2); /* This way the IP header is aligned */ 335262306a36Sopenharmony_ci buffer = skb_put(skb, len + hdrlen); 335362306a36Sopenharmony_ci if (test_bit(FLAG_802_11, &ai->flags)) { 335462306a36Sopenharmony_ci buffer[0] = fc; 335562306a36Sopenharmony_ci bap_read(ai, buffer + 1, hdrlen - 2, BAP0); 335662306a36Sopenharmony_ci if (hdrlen == 24) 335762306a36Sopenharmony_ci bap_read(ai, tmpbuf, 6, BAP0); 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci bap_read(ai, &v, sizeof(v), BAP0); 336062306a36Sopenharmony_ci gap = le16_to_cpu(v); 336162306a36Sopenharmony_ci if (gap) { 336262306a36Sopenharmony_ci if (gap <= 8) { 336362306a36Sopenharmony_ci bap_read(ai, tmpbuf, gap, BAP0); 336462306a36Sopenharmony_ci } else { 336562306a36Sopenharmony_ci airo_print_err(ai->dev->name, "gaplen too " 336662306a36Sopenharmony_ci "big. Problems will follow..."); 336762306a36Sopenharmony_ci } 336862306a36Sopenharmony_ci } 336962306a36Sopenharmony_ci bap_read(ai, buffer + hdrlen/2, len, BAP0); 337062306a36Sopenharmony_ci } else { 337162306a36Sopenharmony_ci MICBuffer micbuf; 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci bap_read(ai, buffer, ETH_ALEN * 2, BAP0); 337462306a36Sopenharmony_ci if (ai->micstats.enabled) { 337562306a36Sopenharmony_ci bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0); 337662306a36Sopenharmony_ci if (ntohs(micbuf.typelen) > 0x05DC) 337762306a36Sopenharmony_ci bap_setup(ai, fid, 0x44, BAP0); 337862306a36Sopenharmony_ci else { 337962306a36Sopenharmony_ci if (len <= sizeof (micbuf)) { 338062306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 338162306a36Sopenharmony_ci goto done; 338262306a36Sopenharmony_ci } 338362306a36Sopenharmony_ci 338462306a36Sopenharmony_ci len -= sizeof(micbuf); 338562306a36Sopenharmony_ci skb_trim(skb, len + hdrlen); 338662306a36Sopenharmony_ci } 338762306a36Sopenharmony_ci } 338862306a36Sopenharmony_ci 338962306a36Sopenharmony_ci bap_read(ai, buffer + ETH_ALEN, len, BAP0); 339062306a36Sopenharmony_ci if (decapsulate(ai, &micbuf, (etherHead*) buffer, len)) 339162306a36Sopenharmony_ci dev_kfree_skb_irq (skb); 339262306a36Sopenharmony_ci else 339362306a36Sopenharmony_ci success = 1; 339462306a36Sopenharmony_ci } 339562306a36Sopenharmony_ci 339662306a36Sopenharmony_ci#ifdef WIRELESS_SPY 339762306a36Sopenharmony_ci if (success && (ai->spy_data.spy_number > 0)) { 339862306a36Sopenharmony_ci char *sa; 339962306a36Sopenharmony_ci struct iw_quality wstats; 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci /* Prepare spy data : addr + qual */ 340262306a36Sopenharmony_ci if (!test_bit(FLAG_802_11, &ai->flags)) { 340362306a36Sopenharmony_ci sa = (char *) buffer + 6; 340462306a36Sopenharmony_ci bap_setup(ai, fid, 8, BAP0); 340562306a36Sopenharmony_ci bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0); 340662306a36Sopenharmony_ci } else 340762306a36Sopenharmony_ci sa = (char *) buffer + 10; 340862306a36Sopenharmony_ci wstats.qual = hdr.rssi[0]; 340962306a36Sopenharmony_ci if (ai->rssi) 341062306a36Sopenharmony_ci wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm; 341162306a36Sopenharmony_ci else 341262306a36Sopenharmony_ci wstats.level = (hdr.rssi[1] + 321) / 2; 341362306a36Sopenharmony_ci wstats.noise = ai->wstats.qual.noise; 341462306a36Sopenharmony_ci wstats.updated = IW_QUAL_LEVEL_UPDATED 341562306a36Sopenharmony_ci | IW_QUAL_QUAL_UPDATED 341662306a36Sopenharmony_ci | IW_QUAL_DBM; 341762306a36Sopenharmony_ci /* Update spy records */ 341862306a36Sopenharmony_ci wireless_spy_update(ai->dev, sa, &wstats); 341962306a36Sopenharmony_ci } 342062306a36Sopenharmony_ci#endif /* WIRELESS_SPY */ 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_cidone: 342362306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_RX); 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci if (success) { 342662306a36Sopenharmony_ci if (test_bit(FLAG_802_11, &ai->flags)) { 342762306a36Sopenharmony_ci skb_reset_mac_header(skb); 342862306a36Sopenharmony_ci skb->pkt_type = PACKET_OTHERHOST; 342962306a36Sopenharmony_ci skb->dev = ai->wifidev; 343062306a36Sopenharmony_ci skb->protocol = htons(ETH_P_802_2); 343162306a36Sopenharmony_ci } else 343262306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, ai->dev); 343362306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_ci netif_rx(skb); 343662306a36Sopenharmony_ci } 343762306a36Sopenharmony_ci} 343862306a36Sopenharmony_ci 343962306a36Sopenharmony_cistatic void airo_handle_tx(struct airo_info *ai, u16 status) 344062306a36Sopenharmony_ci{ 344162306a36Sopenharmony_ci int i, index = -1; 344262306a36Sopenharmony_ci u16 fid; 344362306a36Sopenharmony_ci 344462306a36Sopenharmony_ci if (test_bit(FLAG_MPI, &ai->flags)) { 344562306a36Sopenharmony_ci unsigned long flags; 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ci if (status & EV_TXEXC) 344862306a36Sopenharmony_ci get_tx_error(ai, -1); 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_ci spin_lock_irqsave(&ai->aux_lock, flags); 345162306a36Sopenharmony_ci if (!skb_queue_empty(&ai->txq)) { 345262306a36Sopenharmony_ci spin_unlock_irqrestore(&ai->aux_lock, flags); 345362306a36Sopenharmony_ci mpi_send_packet(ai->dev); 345462306a36Sopenharmony_ci } else { 345562306a36Sopenharmony_ci clear_bit(FLAG_PENDING_XMIT, &ai->flags); 345662306a36Sopenharmony_ci spin_unlock_irqrestore(&ai->aux_lock, flags); 345762306a36Sopenharmony_ci netif_wake_queue(ai->dev); 345862306a36Sopenharmony_ci } 345962306a36Sopenharmony_ci OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC)); 346062306a36Sopenharmony_ci return; 346162306a36Sopenharmony_ci } 346262306a36Sopenharmony_ci 346362306a36Sopenharmony_ci fid = IN4500(ai, TXCOMPLFID); 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci for (i = 0; i < MAX_FIDS; i++) { 346662306a36Sopenharmony_ci if ((ai->fids[i] & 0xffff) == fid) 346762306a36Sopenharmony_ci index = i; 346862306a36Sopenharmony_ci } 346962306a36Sopenharmony_ci 347062306a36Sopenharmony_ci if (index != -1) { 347162306a36Sopenharmony_ci if (status & EV_TXEXC) 347262306a36Sopenharmony_ci get_tx_error(ai, index); 347362306a36Sopenharmony_ci 347462306a36Sopenharmony_ci OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC)); 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci /* Set up to be used again */ 347762306a36Sopenharmony_ci ai->fids[index] &= 0xffff; 347862306a36Sopenharmony_ci if (index < MAX_FIDS / 2) { 347962306a36Sopenharmony_ci if (!test_bit(FLAG_PENDING_XMIT, &ai->flags)) 348062306a36Sopenharmony_ci netif_wake_queue(ai->dev); 348162306a36Sopenharmony_ci } else { 348262306a36Sopenharmony_ci if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags)) 348362306a36Sopenharmony_ci netif_wake_queue(ai->wifidev); 348462306a36Sopenharmony_ci } 348562306a36Sopenharmony_ci } else { 348662306a36Sopenharmony_ci OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC)); 348762306a36Sopenharmony_ci airo_print_err(ai->dev->name, "Unallocated FID was used to xmit"); 348862306a36Sopenharmony_ci } 348962306a36Sopenharmony_ci} 349062306a36Sopenharmony_ci 349162306a36Sopenharmony_cistatic irqreturn_t airo_interrupt(int irq, void *dev_id) 349262306a36Sopenharmony_ci{ 349362306a36Sopenharmony_ci struct net_device *dev = dev_id; 349462306a36Sopenharmony_ci u16 status, savedInterrupts = 0; 349562306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 349662306a36Sopenharmony_ci int handled = 0; 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_ci if (!netif_device_present(dev)) 349962306a36Sopenharmony_ci return IRQ_NONE; 350062306a36Sopenharmony_ci 350162306a36Sopenharmony_ci for (;;) { 350262306a36Sopenharmony_ci status = IN4500(ai, EVSTAT); 350362306a36Sopenharmony_ci if (!(status & STATUS_INTS) || (status == 0xffff)) 350462306a36Sopenharmony_ci break; 350562306a36Sopenharmony_ci 350662306a36Sopenharmony_ci handled = 1; 350762306a36Sopenharmony_ci 350862306a36Sopenharmony_ci if (status & EV_AWAKE) { 350962306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_AWAKE); 351062306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_AWAKE); 351162306a36Sopenharmony_ci } 351262306a36Sopenharmony_ci 351362306a36Sopenharmony_ci if (!savedInterrupts) { 351462306a36Sopenharmony_ci savedInterrupts = IN4500(ai, EVINTEN); 351562306a36Sopenharmony_ci OUT4500(ai, EVINTEN, 0); 351662306a36Sopenharmony_ci } 351762306a36Sopenharmony_ci 351862306a36Sopenharmony_ci if (status & EV_MIC) { 351962306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_MIC); 352062306a36Sopenharmony_ci airo_handle_cisco_mic(ai); 352162306a36Sopenharmony_ci } 352262306a36Sopenharmony_ci 352362306a36Sopenharmony_ci if (status & EV_LINK) { 352462306a36Sopenharmony_ci /* Link status changed */ 352562306a36Sopenharmony_ci airo_handle_link(ai); 352662306a36Sopenharmony_ci } 352762306a36Sopenharmony_ci 352862306a36Sopenharmony_ci /* Check to see if there is something to receive */ 352962306a36Sopenharmony_ci if (status & EV_RX) 353062306a36Sopenharmony_ci airo_handle_rx(ai); 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_ci /* Check to see if a packet has been transmitted */ 353362306a36Sopenharmony_ci if (status & (EV_TX | EV_TXCPY | EV_TXEXC)) 353462306a36Sopenharmony_ci airo_handle_tx(ai, status); 353562306a36Sopenharmony_ci 353662306a36Sopenharmony_ci if (status & ~STATUS_INTS & ~IGNORE_INTS) { 353762306a36Sopenharmony_ci airo_print_warn(ai->dev->name, "Got weird status %x", 353862306a36Sopenharmony_ci status & ~STATUS_INTS & ~IGNORE_INTS); 353962306a36Sopenharmony_ci } 354062306a36Sopenharmony_ci } 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_ci if (savedInterrupts) 354362306a36Sopenharmony_ci OUT4500(ai, EVINTEN, savedInterrupts); 354462306a36Sopenharmony_ci 354562306a36Sopenharmony_ci return IRQ_RETVAL(handled); 354662306a36Sopenharmony_ci} 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci/* 354962306a36Sopenharmony_ci * Routines to talk to the card 355062306a36Sopenharmony_ci */ 355162306a36Sopenharmony_ci 355262306a36Sopenharmony_ci/* 355362306a36Sopenharmony_ci * This was originally written for the 4500, hence the name 355462306a36Sopenharmony_ci * NOTE: If use with 8bit mode and SMP bad things will happen! 355562306a36Sopenharmony_ci * Why would some one do 8 bit IO in an SMP machine?!? 355662306a36Sopenharmony_ci */ 355762306a36Sopenharmony_cistatic void OUT4500(struct airo_info *ai, u16 reg, u16 val) 355862306a36Sopenharmony_ci{ 355962306a36Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) 356062306a36Sopenharmony_ci reg <<= 1; 356162306a36Sopenharmony_ci if (!do8bitIO) 356262306a36Sopenharmony_ci outw(val, ai->dev->base_addr + reg); 356362306a36Sopenharmony_ci else { 356462306a36Sopenharmony_ci outb(val & 0xff, ai->dev->base_addr + reg); 356562306a36Sopenharmony_ci outb(val >> 8, ai->dev->base_addr + reg + 1); 356662306a36Sopenharmony_ci } 356762306a36Sopenharmony_ci} 356862306a36Sopenharmony_ci 356962306a36Sopenharmony_cistatic u16 IN4500(struct airo_info *ai, u16 reg) 357062306a36Sopenharmony_ci{ 357162306a36Sopenharmony_ci unsigned short rc; 357262306a36Sopenharmony_ci 357362306a36Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) 357462306a36Sopenharmony_ci reg <<= 1; 357562306a36Sopenharmony_ci if (!do8bitIO) 357662306a36Sopenharmony_ci rc = inw(ai->dev->base_addr + reg); 357762306a36Sopenharmony_ci else { 357862306a36Sopenharmony_ci rc = inb(ai->dev->base_addr + reg); 357962306a36Sopenharmony_ci rc += ((int)inb(ai->dev->base_addr + reg + 1)) << 8; 358062306a36Sopenharmony_ci } 358162306a36Sopenharmony_ci return rc; 358262306a36Sopenharmony_ci} 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_cistatic int enable_MAC(struct airo_info *ai, int lock) 358562306a36Sopenharmony_ci{ 358662306a36Sopenharmony_ci int rc; 358762306a36Sopenharmony_ci Cmd cmd; 358862306a36Sopenharmony_ci Resp rsp; 358962306a36Sopenharmony_ci 359062306a36Sopenharmony_ci /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions 359162306a36Sopenharmony_ci * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down" 359262306a36Sopenharmony_ci * Note : we could try to use !netif_running(dev) in enable_MAC() 359362306a36Sopenharmony_ci * instead of this flag, but I don't trust it *within* the 359462306a36Sopenharmony_ci * open/close functions, and testing both flags together is 359562306a36Sopenharmony_ci * "cheaper" - Jean II */ 359662306a36Sopenharmony_ci if (ai->flags & FLAG_RADIO_MASK) return SUCCESS; 359762306a36Sopenharmony_ci 359862306a36Sopenharmony_ci if (lock && down_interruptible(&ai->sem)) 359962306a36Sopenharmony_ci return -ERESTARTSYS; 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ci if (!test_bit(FLAG_ENABLED, &ai->flags)) { 360262306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 360362306a36Sopenharmony_ci cmd.cmd = MAC_ENABLE; 360462306a36Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp, true); 360562306a36Sopenharmony_ci if (rc == SUCCESS) 360662306a36Sopenharmony_ci set_bit(FLAG_ENABLED, &ai->flags); 360762306a36Sopenharmony_ci } else 360862306a36Sopenharmony_ci rc = SUCCESS; 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_ci if (lock) 361162306a36Sopenharmony_ci up(&ai->sem); 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci if (rc) 361462306a36Sopenharmony_ci airo_print_err(ai->dev->name, "Cannot enable MAC"); 361562306a36Sopenharmony_ci else if ((rsp.status & 0xFF00) != 0) { 361662306a36Sopenharmony_ci airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, " 361762306a36Sopenharmony_ci "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2); 361862306a36Sopenharmony_ci rc = ERROR; 361962306a36Sopenharmony_ci } 362062306a36Sopenharmony_ci return rc; 362162306a36Sopenharmony_ci} 362262306a36Sopenharmony_ci 362362306a36Sopenharmony_cistatic void disable_MAC(struct airo_info *ai, int lock) 362462306a36Sopenharmony_ci{ 362562306a36Sopenharmony_ci Cmd cmd; 362662306a36Sopenharmony_ci Resp rsp; 362762306a36Sopenharmony_ci 362862306a36Sopenharmony_ci if (lock == 1 && down_interruptible(&ai->sem)) 362962306a36Sopenharmony_ci return; 363062306a36Sopenharmony_ci 363162306a36Sopenharmony_ci if (test_bit(FLAG_ENABLED, &ai->flags)) { 363262306a36Sopenharmony_ci if (lock != 2) /* lock == 2 means don't disable carrier */ 363362306a36Sopenharmony_ci netif_carrier_off(ai->dev); 363462306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 363562306a36Sopenharmony_ci cmd.cmd = MAC_DISABLE; // disable in case already enabled 363662306a36Sopenharmony_ci issuecommand(ai, &cmd, &rsp, true); 363762306a36Sopenharmony_ci clear_bit(FLAG_ENABLED, &ai->flags); 363862306a36Sopenharmony_ci } 363962306a36Sopenharmony_ci if (lock == 1) 364062306a36Sopenharmony_ci up(&ai->sem); 364162306a36Sopenharmony_ci} 364262306a36Sopenharmony_ci 364362306a36Sopenharmony_cistatic void enable_interrupts(struct airo_info *ai) 364462306a36Sopenharmony_ci{ 364562306a36Sopenharmony_ci /* Enable the interrupts */ 364662306a36Sopenharmony_ci OUT4500(ai, EVINTEN, STATUS_INTS); 364762306a36Sopenharmony_ci} 364862306a36Sopenharmony_ci 364962306a36Sopenharmony_cistatic void disable_interrupts(struct airo_info *ai) 365062306a36Sopenharmony_ci{ 365162306a36Sopenharmony_ci OUT4500(ai, EVINTEN, 0); 365262306a36Sopenharmony_ci} 365362306a36Sopenharmony_ci 365462306a36Sopenharmony_cistatic void mpi_receive_802_3(struct airo_info *ai) 365562306a36Sopenharmony_ci{ 365662306a36Sopenharmony_ci RxFid rxd; 365762306a36Sopenharmony_ci int len = 0; 365862306a36Sopenharmony_ci struct sk_buff *skb; 365962306a36Sopenharmony_ci char *buffer; 366062306a36Sopenharmony_ci int off = 0; 366162306a36Sopenharmony_ci MICBuffer micbuf; 366262306a36Sopenharmony_ci 366362306a36Sopenharmony_ci memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd)); 366462306a36Sopenharmony_ci /* Make sure we got something */ 366562306a36Sopenharmony_ci if (rxd.rdy && rxd.valid == 0) { 366662306a36Sopenharmony_ci len = rxd.len + 12; 366762306a36Sopenharmony_ci if (len < 12 || len > 2048) 366862306a36Sopenharmony_ci goto badrx; 366962306a36Sopenharmony_ci 367062306a36Sopenharmony_ci skb = dev_alloc_skb(len); 367162306a36Sopenharmony_ci if (!skb) { 367262306a36Sopenharmony_ci ai->dev->stats.rx_dropped++; 367362306a36Sopenharmony_ci goto badrx; 367462306a36Sopenharmony_ci } 367562306a36Sopenharmony_ci buffer = skb_put(skb, len); 367662306a36Sopenharmony_ci memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2); 367762306a36Sopenharmony_ci if (ai->micstats.enabled) { 367862306a36Sopenharmony_ci memcpy(&micbuf, 367962306a36Sopenharmony_ci ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2, 368062306a36Sopenharmony_ci sizeof(micbuf)); 368162306a36Sopenharmony_ci if (ntohs(micbuf.typelen) <= 0x05DC) { 368262306a36Sopenharmony_ci if (len <= sizeof(micbuf) + ETH_ALEN * 2) 368362306a36Sopenharmony_ci goto badmic; 368462306a36Sopenharmony_ci 368562306a36Sopenharmony_ci off = sizeof(micbuf); 368662306a36Sopenharmony_ci skb_trim (skb, len - off); 368762306a36Sopenharmony_ci } 368862306a36Sopenharmony_ci } 368962306a36Sopenharmony_ci memcpy(buffer + ETH_ALEN * 2, 369062306a36Sopenharmony_ci ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off, 369162306a36Sopenharmony_ci len - ETH_ALEN * 2 - off); 369262306a36Sopenharmony_ci if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) { 369362306a36Sopenharmony_cibadmic: 369462306a36Sopenharmony_ci dev_kfree_skb_irq (skb); 369562306a36Sopenharmony_ci goto badrx; 369662306a36Sopenharmony_ci } 369762306a36Sopenharmony_ci#ifdef WIRELESS_SPY 369862306a36Sopenharmony_ci if (ai->spy_data.spy_number > 0) { 369962306a36Sopenharmony_ci char *sa; 370062306a36Sopenharmony_ci struct iw_quality wstats; 370162306a36Sopenharmony_ci /* Prepare spy data : addr + qual */ 370262306a36Sopenharmony_ci sa = buffer + ETH_ALEN; 370362306a36Sopenharmony_ci wstats.qual = 0; /* XXX Where do I get that info from ??? */ 370462306a36Sopenharmony_ci wstats.level = 0; 370562306a36Sopenharmony_ci wstats.updated = 0; 370662306a36Sopenharmony_ci /* Update spy records */ 370762306a36Sopenharmony_ci wireless_spy_update(ai->dev, sa, &wstats); 370862306a36Sopenharmony_ci } 370962306a36Sopenharmony_ci#endif /* WIRELESS_SPY */ 371062306a36Sopenharmony_ci 371162306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 371262306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, ai->dev); 371362306a36Sopenharmony_ci netif_rx(skb); 371462306a36Sopenharmony_ci } 371562306a36Sopenharmony_cibadrx: 371662306a36Sopenharmony_ci if (rxd.valid == 0) { 371762306a36Sopenharmony_ci rxd.valid = 1; 371862306a36Sopenharmony_ci rxd.rdy = 0; 371962306a36Sopenharmony_ci rxd.len = PKTSIZE; 372062306a36Sopenharmony_ci memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd)); 372162306a36Sopenharmony_ci } 372262306a36Sopenharmony_ci} 372362306a36Sopenharmony_ci 372462306a36Sopenharmony_cistatic void mpi_receive_802_11(struct airo_info *ai) 372562306a36Sopenharmony_ci{ 372662306a36Sopenharmony_ci RxFid rxd; 372762306a36Sopenharmony_ci struct sk_buff *skb = NULL; 372862306a36Sopenharmony_ci u16 len, hdrlen = 0; 372962306a36Sopenharmony_ci __le16 fc; 373062306a36Sopenharmony_ci struct rx_hdr hdr; 373162306a36Sopenharmony_ci u16 gap; 373262306a36Sopenharmony_ci u16 *buffer; 373362306a36Sopenharmony_ci char *ptr = ai->rxfids[0].virtual_host_addr + 4; 373462306a36Sopenharmony_ci 373562306a36Sopenharmony_ci memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd)); 373662306a36Sopenharmony_ci memcpy ((char *)&hdr, ptr, sizeof(hdr)); 373762306a36Sopenharmony_ci ptr += sizeof(hdr); 373862306a36Sopenharmony_ci /* Bad CRC. Ignore packet */ 373962306a36Sopenharmony_ci if (le16_to_cpu(hdr.status) & 2) 374062306a36Sopenharmony_ci hdr.len = 0; 374162306a36Sopenharmony_ci if (ai->wifidev == NULL) 374262306a36Sopenharmony_ci hdr.len = 0; 374362306a36Sopenharmony_ci len = le16_to_cpu(hdr.len); 374462306a36Sopenharmony_ci if (len > AIRO_DEF_MTU) { 374562306a36Sopenharmony_ci airo_print_err(ai->dev->name, "Bad size %d", len); 374662306a36Sopenharmony_ci goto badrx; 374762306a36Sopenharmony_ci } 374862306a36Sopenharmony_ci if (len == 0) 374962306a36Sopenharmony_ci goto badrx; 375062306a36Sopenharmony_ci 375162306a36Sopenharmony_ci fc = get_unaligned((__le16 *)ptr); 375262306a36Sopenharmony_ci hdrlen = header_len(fc); 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_ci skb = dev_alloc_skb(len + hdrlen + 2); 375562306a36Sopenharmony_ci if (!skb) { 375662306a36Sopenharmony_ci ai->dev->stats.rx_dropped++; 375762306a36Sopenharmony_ci goto badrx; 375862306a36Sopenharmony_ci } 375962306a36Sopenharmony_ci buffer = skb_put(skb, len + hdrlen); 376062306a36Sopenharmony_ci memcpy ((char *)buffer, ptr, hdrlen); 376162306a36Sopenharmony_ci ptr += hdrlen; 376262306a36Sopenharmony_ci if (hdrlen == 24) 376362306a36Sopenharmony_ci ptr += 6; 376462306a36Sopenharmony_ci gap = get_unaligned_le16(ptr); 376562306a36Sopenharmony_ci ptr += sizeof(__le16); 376662306a36Sopenharmony_ci if (gap) { 376762306a36Sopenharmony_ci if (gap <= 8) 376862306a36Sopenharmony_ci ptr += gap; 376962306a36Sopenharmony_ci else 377062306a36Sopenharmony_ci airo_print_err(ai->dev->name, 377162306a36Sopenharmony_ci "gaplen too big. Problems will follow..."); 377262306a36Sopenharmony_ci } 377362306a36Sopenharmony_ci memcpy ((char *)buffer + hdrlen, ptr, len); 377462306a36Sopenharmony_ci ptr += len; 377562306a36Sopenharmony_ci#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ 377662306a36Sopenharmony_ci if (ai->spy_data.spy_number > 0) { 377762306a36Sopenharmony_ci char *sa; 377862306a36Sopenharmony_ci struct iw_quality wstats; 377962306a36Sopenharmony_ci /* Prepare spy data : addr + qual */ 378062306a36Sopenharmony_ci sa = (char*)buffer + 10; 378162306a36Sopenharmony_ci wstats.qual = hdr.rssi[0]; 378262306a36Sopenharmony_ci if (ai->rssi) 378362306a36Sopenharmony_ci wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm; 378462306a36Sopenharmony_ci else 378562306a36Sopenharmony_ci wstats.level = (hdr.rssi[1] + 321) / 2; 378662306a36Sopenharmony_ci wstats.noise = ai->wstats.qual.noise; 378762306a36Sopenharmony_ci wstats.updated = IW_QUAL_QUAL_UPDATED 378862306a36Sopenharmony_ci | IW_QUAL_LEVEL_UPDATED 378962306a36Sopenharmony_ci | IW_QUAL_DBM; 379062306a36Sopenharmony_ci /* Update spy records */ 379162306a36Sopenharmony_ci wireless_spy_update(ai->dev, sa, &wstats); 379262306a36Sopenharmony_ci } 379362306a36Sopenharmony_ci#endif /* IW_WIRELESS_SPY */ 379462306a36Sopenharmony_ci skb_reset_mac_header(skb); 379562306a36Sopenharmony_ci skb->pkt_type = PACKET_OTHERHOST; 379662306a36Sopenharmony_ci skb->dev = ai->wifidev; 379762306a36Sopenharmony_ci skb->protocol = htons(ETH_P_802_2); 379862306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 379962306a36Sopenharmony_ci netif_rx(skb); 380062306a36Sopenharmony_ci 380162306a36Sopenharmony_cibadrx: 380262306a36Sopenharmony_ci if (rxd.valid == 0) { 380362306a36Sopenharmony_ci rxd.valid = 1; 380462306a36Sopenharmony_ci rxd.rdy = 0; 380562306a36Sopenharmony_ci rxd.len = PKTSIZE; 380662306a36Sopenharmony_ci memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd)); 380762306a36Sopenharmony_ci } 380862306a36Sopenharmony_ci} 380962306a36Sopenharmony_ci 381062306a36Sopenharmony_cistatic inline void set_auth_type(struct airo_info *local, int auth_type) 381162306a36Sopenharmony_ci{ 381262306a36Sopenharmony_ci local->config.authType = auth_type; 381362306a36Sopenharmony_ci /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT). 381462306a36Sopenharmony_ci * Used by airo_set_auth() 381562306a36Sopenharmony_ci */ 381662306a36Sopenharmony_ci if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT) 381762306a36Sopenharmony_ci local->last_auth = auth_type; 381862306a36Sopenharmony_ci} 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_cistatic int noinline_for_stack airo_readconfig(struct airo_info *ai, 382162306a36Sopenharmony_ci struct net_device *dev, int lock) 382262306a36Sopenharmony_ci{ 382362306a36Sopenharmony_ci int i, status; 382462306a36Sopenharmony_ci /* large variables, so don't inline this function, 382562306a36Sopenharmony_ci * maybe change to kmalloc 382662306a36Sopenharmony_ci */ 382762306a36Sopenharmony_ci tdsRssiRid rssi_rid; 382862306a36Sopenharmony_ci CapabilityRid cap_rid; 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_ci kfree(ai->SSID); 383162306a36Sopenharmony_ci ai->SSID = NULL; 383262306a36Sopenharmony_ci // general configuration (read/modify/write) 383362306a36Sopenharmony_ci status = readConfigRid(ai, lock); 383462306a36Sopenharmony_ci if (status != SUCCESS) return ERROR; 383562306a36Sopenharmony_ci 383662306a36Sopenharmony_ci status = readCapabilityRid(ai, &cap_rid, lock); 383762306a36Sopenharmony_ci if (status != SUCCESS) return ERROR; 383862306a36Sopenharmony_ci 383962306a36Sopenharmony_ci status = PC4500_readrid(ai, RID_RSSI, &rssi_rid, sizeof(rssi_rid), lock); 384062306a36Sopenharmony_ci if (status == SUCCESS) { 384162306a36Sopenharmony_ci if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL) 384262306a36Sopenharmony_ci memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */ 384362306a36Sopenharmony_ci } 384462306a36Sopenharmony_ci else { 384562306a36Sopenharmony_ci kfree(ai->rssi); 384662306a36Sopenharmony_ci ai->rssi = NULL; 384762306a36Sopenharmony_ci if (cap_rid.softCap & cpu_to_le16(8)) 384862306a36Sopenharmony_ci ai->config.rmode |= RXMODE_NORMALIZED_RSSI; 384962306a36Sopenharmony_ci else 385062306a36Sopenharmony_ci airo_print_warn(ai->dev->name, "unknown received signal " 385162306a36Sopenharmony_ci "level scale"); 385262306a36Sopenharmony_ci } 385362306a36Sopenharmony_ci ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS; 385462306a36Sopenharmony_ci set_auth_type(ai, AUTH_OPEN); 385562306a36Sopenharmony_ci ai->config.modulation = MOD_CCK; 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_ci if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) && 385862306a36Sopenharmony_ci (cap_rid.extSoftCap & cpu_to_le16(1)) && 385962306a36Sopenharmony_ci micsetup(ai) == SUCCESS) { 386062306a36Sopenharmony_ci ai->config.opmode |= MODE_MIC; 386162306a36Sopenharmony_ci set_bit(FLAG_MIC_CAPABLE, &ai->flags); 386262306a36Sopenharmony_ci } 386362306a36Sopenharmony_ci 386462306a36Sopenharmony_ci /* Save off the MAC */ 386562306a36Sopenharmony_ci eth_hw_addr_set(dev, ai->config.macAddr); 386662306a36Sopenharmony_ci 386762306a36Sopenharmony_ci /* Check to see if there are any insmod configured 386862306a36Sopenharmony_ci rates to add */ 386962306a36Sopenharmony_ci if (rates[0]) { 387062306a36Sopenharmony_ci memset(ai->config.rates, 0, sizeof(ai->config.rates)); 387162306a36Sopenharmony_ci for (i = 0; i < 8 && rates[i]; i++) { 387262306a36Sopenharmony_ci ai->config.rates[i] = rates[i]; 387362306a36Sopenharmony_ci } 387462306a36Sopenharmony_ci } 387562306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 387662306a36Sopenharmony_ci 387762306a36Sopenharmony_ci return SUCCESS; 387862306a36Sopenharmony_ci} 387962306a36Sopenharmony_ci 388062306a36Sopenharmony_ci 388162306a36Sopenharmony_cistatic u16 setup_card(struct airo_info *ai, struct net_device *dev, int lock) 388262306a36Sopenharmony_ci{ 388362306a36Sopenharmony_ci Cmd cmd; 388462306a36Sopenharmony_ci Resp rsp; 388562306a36Sopenharmony_ci int status; 388662306a36Sopenharmony_ci SsidRid mySsid; 388762306a36Sopenharmony_ci __le16 lastindex; 388862306a36Sopenharmony_ci WepKeyRid wkr; 388962306a36Sopenharmony_ci int rc; 389062306a36Sopenharmony_ci 389162306a36Sopenharmony_ci memset(&mySsid, 0, sizeof(mySsid)); 389262306a36Sopenharmony_ci kfree (ai->flash); 389362306a36Sopenharmony_ci ai->flash = NULL; 389462306a36Sopenharmony_ci 389562306a36Sopenharmony_ci /* The NOP is the first step in getting the card going */ 389662306a36Sopenharmony_ci cmd.cmd = NOP; 389762306a36Sopenharmony_ci cmd.parm0 = cmd.parm1 = cmd.parm2 = 0; 389862306a36Sopenharmony_ci if (lock && down_interruptible(&ai->sem)) 389962306a36Sopenharmony_ci return ERROR; 390062306a36Sopenharmony_ci if (issuecommand(ai, &cmd, &rsp, true) != SUCCESS) { 390162306a36Sopenharmony_ci if (lock) 390262306a36Sopenharmony_ci up(&ai->sem); 390362306a36Sopenharmony_ci return ERROR; 390462306a36Sopenharmony_ci } 390562306a36Sopenharmony_ci disable_MAC(ai, 0); 390662306a36Sopenharmony_ci 390762306a36Sopenharmony_ci // Let's figure out if we need to use the AUX port 390862306a36Sopenharmony_ci if (!test_bit(FLAG_MPI,&ai->flags)) { 390962306a36Sopenharmony_ci cmd.cmd = CMD_ENABLEAUX; 391062306a36Sopenharmony_ci if (issuecommand(ai, &cmd, &rsp, true) != SUCCESS) { 391162306a36Sopenharmony_ci if (lock) 391262306a36Sopenharmony_ci up(&ai->sem); 391362306a36Sopenharmony_ci airo_print_err(ai->dev->name, "Error checking for AUX port"); 391462306a36Sopenharmony_ci return ERROR; 391562306a36Sopenharmony_ci } 391662306a36Sopenharmony_ci if (!aux_bap || rsp.status & 0xff00) { 391762306a36Sopenharmony_ci ai->bap_read = fast_bap_read; 391862306a36Sopenharmony_ci airo_print_dbg(ai->dev->name, "Doing fast bap_reads"); 391962306a36Sopenharmony_ci } else { 392062306a36Sopenharmony_ci ai->bap_read = aux_bap_read; 392162306a36Sopenharmony_ci airo_print_dbg(ai->dev->name, "Doing AUX bap_reads"); 392262306a36Sopenharmony_ci } 392362306a36Sopenharmony_ci } 392462306a36Sopenharmony_ci if (lock) 392562306a36Sopenharmony_ci up(&ai->sem); 392662306a36Sopenharmony_ci if (ai->config.len == 0) { 392762306a36Sopenharmony_ci status = airo_readconfig(ai, dev, lock); 392862306a36Sopenharmony_ci if (status != SUCCESS) 392962306a36Sopenharmony_ci return ERROR; 393062306a36Sopenharmony_ci } 393162306a36Sopenharmony_ci 393262306a36Sopenharmony_ci /* Setup the SSIDs if present */ 393362306a36Sopenharmony_ci if (ssids[0]) { 393462306a36Sopenharmony_ci int i; 393562306a36Sopenharmony_ci for (i = 0; i < 3 && ssids[i]; i++) { 393662306a36Sopenharmony_ci size_t len = strlen(ssids[i]); 393762306a36Sopenharmony_ci if (len > 32) 393862306a36Sopenharmony_ci len = 32; 393962306a36Sopenharmony_ci mySsid.ssids[i].len = cpu_to_le16(len); 394062306a36Sopenharmony_ci memcpy(mySsid.ssids[i].ssid, ssids[i], len); 394162306a36Sopenharmony_ci } 394262306a36Sopenharmony_ci mySsid.len = cpu_to_le16(sizeof(mySsid)); 394362306a36Sopenharmony_ci } 394462306a36Sopenharmony_ci 394562306a36Sopenharmony_ci status = writeConfigRid(ai, lock); 394662306a36Sopenharmony_ci if (status != SUCCESS) return ERROR; 394762306a36Sopenharmony_ci 394862306a36Sopenharmony_ci /* Set up the SSID list */ 394962306a36Sopenharmony_ci if (ssids[0]) { 395062306a36Sopenharmony_ci status = writeSsidRid(ai, &mySsid, lock); 395162306a36Sopenharmony_ci if (status != SUCCESS) return ERROR; 395262306a36Sopenharmony_ci } 395362306a36Sopenharmony_ci 395462306a36Sopenharmony_ci status = enable_MAC(ai, lock); 395562306a36Sopenharmony_ci if (status != SUCCESS) 395662306a36Sopenharmony_ci return ERROR; 395762306a36Sopenharmony_ci 395862306a36Sopenharmony_ci /* Grab the initial wep key, we gotta save it for auto_wep */ 395962306a36Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 1, lock); 396062306a36Sopenharmony_ci if (rc == SUCCESS) do { 396162306a36Sopenharmony_ci lastindex = wkr.kindex; 396262306a36Sopenharmony_ci if (wkr.kindex == cpu_to_le16(0xffff)) { 396362306a36Sopenharmony_ci ai->defindex = wkr.mac[0]; 396462306a36Sopenharmony_ci } 396562306a36Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 0, lock); 396662306a36Sopenharmony_ci } while (lastindex != wkr.kindex); 396762306a36Sopenharmony_ci 396862306a36Sopenharmony_ci try_auto_wep(ai); 396962306a36Sopenharmony_ci 397062306a36Sopenharmony_ci return SUCCESS; 397162306a36Sopenharmony_ci} 397262306a36Sopenharmony_ci 397362306a36Sopenharmony_cistatic u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp, 397462306a36Sopenharmony_ci bool may_sleep) 397562306a36Sopenharmony_ci{ 397662306a36Sopenharmony_ci // Im really paranoid about letting it run forever! 397762306a36Sopenharmony_ci int max_tries = 600000; 397862306a36Sopenharmony_ci 397962306a36Sopenharmony_ci if (IN4500(ai, EVSTAT) & EV_CMD) 398062306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_CMD); 398162306a36Sopenharmony_ci 398262306a36Sopenharmony_ci OUT4500(ai, PARAM0, pCmd->parm0); 398362306a36Sopenharmony_ci OUT4500(ai, PARAM1, pCmd->parm1); 398462306a36Sopenharmony_ci OUT4500(ai, PARAM2, pCmd->parm2); 398562306a36Sopenharmony_ci OUT4500(ai, COMMAND, pCmd->cmd); 398662306a36Sopenharmony_ci 398762306a36Sopenharmony_ci while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) { 398862306a36Sopenharmony_ci if ((IN4500(ai, COMMAND)) == pCmd->cmd) 398962306a36Sopenharmony_ci // PC4500 didn't notice command, try again 399062306a36Sopenharmony_ci OUT4500(ai, COMMAND, pCmd->cmd); 399162306a36Sopenharmony_ci if (may_sleep && (max_tries & 255) == 0) 399262306a36Sopenharmony_ci cond_resched(); 399362306a36Sopenharmony_ci } 399462306a36Sopenharmony_ci 399562306a36Sopenharmony_ci if (max_tries == -1) { 399662306a36Sopenharmony_ci airo_print_err(ai->dev->name, 399762306a36Sopenharmony_ci "Max tries exceeded when issuing command"); 399862306a36Sopenharmony_ci if (IN4500(ai, COMMAND) & COMMAND_BUSY) 399962306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); 400062306a36Sopenharmony_ci return ERROR; 400162306a36Sopenharmony_ci } 400262306a36Sopenharmony_ci 400362306a36Sopenharmony_ci // command completed 400462306a36Sopenharmony_ci pRsp->status = IN4500(ai, STATUS); 400562306a36Sopenharmony_ci pRsp->rsp0 = IN4500(ai, RESP0); 400662306a36Sopenharmony_ci pRsp->rsp1 = IN4500(ai, RESP1); 400762306a36Sopenharmony_ci pRsp->rsp2 = IN4500(ai, RESP2); 400862306a36Sopenharmony_ci if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET) 400962306a36Sopenharmony_ci airo_print_err(ai->dev->name, 401062306a36Sopenharmony_ci "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x", 401162306a36Sopenharmony_ci pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1, 401262306a36Sopenharmony_ci pRsp->rsp2); 401362306a36Sopenharmony_ci 401462306a36Sopenharmony_ci // clear stuck command busy if necessary 401562306a36Sopenharmony_ci if (IN4500(ai, COMMAND) & COMMAND_BUSY) { 401662306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); 401762306a36Sopenharmony_ci } 401862306a36Sopenharmony_ci // acknowledge processing the status/response 401962306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_CMD); 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_ci return SUCCESS; 402262306a36Sopenharmony_ci} 402362306a36Sopenharmony_ci 402462306a36Sopenharmony_ci/* Sets up the bap to start exchange data. whichbap should 402562306a36Sopenharmony_ci * be one of the BAP0 or BAP1 defines. Locks should be held before 402662306a36Sopenharmony_ci * calling! */ 402762306a36Sopenharmony_cistatic int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap) 402862306a36Sopenharmony_ci{ 402962306a36Sopenharmony_ci int timeout = 50; 403062306a36Sopenharmony_ci int max_tries = 3; 403162306a36Sopenharmony_ci 403262306a36Sopenharmony_ci OUT4500(ai, SELECT0+whichbap, rid); 403362306a36Sopenharmony_ci OUT4500(ai, OFFSET0+whichbap, offset); 403462306a36Sopenharmony_ci while (1) { 403562306a36Sopenharmony_ci int status = IN4500(ai, OFFSET0+whichbap); 403662306a36Sopenharmony_ci if (status & BAP_BUSY) { 403762306a36Sopenharmony_ci /* This isn't really a timeout, but its kinda 403862306a36Sopenharmony_ci close */ 403962306a36Sopenharmony_ci if (timeout--) { 404062306a36Sopenharmony_ci continue; 404162306a36Sopenharmony_ci } 404262306a36Sopenharmony_ci } else if (status & BAP_ERR) { 404362306a36Sopenharmony_ci /* invalid rid or offset */ 404462306a36Sopenharmony_ci airo_print_err(ai->dev->name, "BAP error %x %d", 404562306a36Sopenharmony_ci status, whichbap); 404662306a36Sopenharmony_ci return ERROR; 404762306a36Sopenharmony_ci } else if (status & BAP_DONE) { // success 404862306a36Sopenharmony_ci return SUCCESS; 404962306a36Sopenharmony_ci } 405062306a36Sopenharmony_ci if (!(max_tries--)) { 405162306a36Sopenharmony_ci airo_print_err(ai->dev->name, 405262306a36Sopenharmony_ci "BAP setup error too many retries\n"); 405362306a36Sopenharmony_ci return ERROR; 405462306a36Sopenharmony_ci } 405562306a36Sopenharmony_ci // -- PC4500 missed it, try again 405662306a36Sopenharmony_ci OUT4500(ai, SELECT0+whichbap, rid); 405762306a36Sopenharmony_ci OUT4500(ai, OFFSET0+whichbap, offset); 405862306a36Sopenharmony_ci timeout = 50; 405962306a36Sopenharmony_ci } 406062306a36Sopenharmony_ci} 406162306a36Sopenharmony_ci 406262306a36Sopenharmony_ci/* should only be called by aux_bap_read. This aux function and the 406362306a36Sopenharmony_ci following use concepts not documented in the developers guide. I 406462306a36Sopenharmony_ci got them from a patch given to my by Aironet */ 406562306a36Sopenharmony_cistatic u16 aux_setup(struct airo_info *ai, u16 page, 406662306a36Sopenharmony_ci u16 offset, u16 *len) 406762306a36Sopenharmony_ci{ 406862306a36Sopenharmony_ci u16 next; 406962306a36Sopenharmony_ci 407062306a36Sopenharmony_ci OUT4500(ai, AUXPAGE, page); 407162306a36Sopenharmony_ci OUT4500(ai, AUXOFF, 0); 407262306a36Sopenharmony_ci next = IN4500(ai, AUXDATA); 407362306a36Sopenharmony_ci *len = IN4500(ai, AUXDATA)&0xff; 407462306a36Sopenharmony_ci if (offset != 4) OUT4500(ai, AUXOFF, offset); 407562306a36Sopenharmony_ci return next; 407662306a36Sopenharmony_ci} 407762306a36Sopenharmony_ci 407862306a36Sopenharmony_ci/* requires call to bap_setup() first */ 407962306a36Sopenharmony_cistatic int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst, 408062306a36Sopenharmony_ci int bytelen, int whichbap) 408162306a36Sopenharmony_ci{ 408262306a36Sopenharmony_ci u16 len; 408362306a36Sopenharmony_ci u16 page; 408462306a36Sopenharmony_ci u16 offset; 408562306a36Sopenharmony_ci u16 next; 408662306a36Sopenharmony_ci int words; 408762306a36Sopenharmony_ci int i; 408862306a36Sopenharmony_ci unsigned long flags; 408962306a36Sopenharmony_ci 409062306a36Sopenharmony_ci spin_lock_irqsave(&ai->aux_lock, flags); 409162306a36Sopenharmony_ci page = IN4500(ai, SWS0+whichbap); 409262306a36Sopenharmony_ci offset = IN4500(ai, SWS2+whichbap); 409362306a36Sopenharmony_ci next = aux_setup(ai, page, offset, &len); 409462306a36Sopenharmony_ci words = (bytelen+1)>>1; 409562306a36Sopenharmony_ci 409662306a36Sopenharmony_ci for (i = 0; i<words;) { 409762306a36Sopenharmony_ci int count; 409862306a36Sopenharmony_ci count = (len>>1) < (words-i) ? (len>>1) : (words-i); 409962306a36Sopenharmony_ci if (!do8bitIO) 410062306a36Sopenharmony_ci insw(ai->dev->base_addr+DATA0+whichbap, 410162306a36Sopenharmony_ci pu16Dst+i, count); 410262306a36Sopenharmony_ci else 410362306a36Sopenharmony_ci insb(ai->dev->base_addr+DATA0+whichbap, 410462306a36Sopenharmony_ci pu16Dst+i, count << 1); 410562306a36Sopenharmony_ci i += count; 410662306a36Sopenharmony_ci if (i<words) { 410762306a36Sopenharmony_ci next = aux_setup(ai, next, 4, &len); 410862306a36Sopenharmony_ci } 410962306a36Sopenharmony_ci } 411062306a36Sopenharmony_ci spin_unlock_irqrestore(&ai->aux_lock, flags); 411162306a36Sopenharmony_ci return SUCCESS; 411262306a36Sopenharmony_ci} 411362306a36Sopenharmony_ci 411462306a36Sopenharmony_ci 411562306a36Sopenharmony_ci/* requires call to bap_setup() first */ 411662306a36Sopenharmony_cistatic int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst, 411762306a36Sopenharmony_ci int bytelen, int whichbap) 411862306a36Sopenharmony_ci{ 411962306a36Sopenharmony_ci bytelen = (bytelen + 1) & (~1); // round up to even value 412062306a36Sopenharmony_ci if (!do8bitIO) 412162306a36Sopenharmony_ci insw(ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1); 412262306a36Sopenharmony_ci else 412362306a36Sopenharmony_ci insb(ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen); 412462306a36Sopenharmony_ci return SUCCESS; 412562306a36Sopenharmony_ci} 412662306a36Sopenharmony_ci 412762306a36Sopenharmony_ci/* requires call to bap_setup() first */ 412862306a36Sopenharmony_cistatic int bap_write(struct airo_info *ai, const __le16 *pu16Src, 412962306a36Sopenharmony_ci int bytelen, int whichbap) 413062306a36Sopenharmony_ci{ 413162306a36Sopenharmony_ci bytelen = (bytelen + 1) & (~1); // round up to even value 413262306a36Sopenharmony_ci if (!do8bitIO) 413362306a36Sopenharmony_ci outsw(ai->dev->base_addr+DATA0+whichbap, 413462306a36Sopenharmony_ci pu16Src, bytelen>>1); 413562306a36Sopenharmony_ci else 413662306a36Sopenharmony_ci outsb(ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen); 413762306a36Sopenharmony_ci return SUCCESS; 413862306a36Sopenharmony_ci} 413962306a36Sopenharmony_ci 414062306a36Sopenharmony_cistatic int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd) 414162306a36Sopenharmony_ci{ 414262306a36Sopenharmony_ci Cmd cmd; /* for issuing commands */ 414362306a36Sopenharmony_ci Resp rsp; /* response from commands */ 414462306a36Sopenharmony_ci u16 status; 414562306a36Sopenharmony_ci 414662306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 414762306a36Sopenharmony_ci cmd.cmd = accmd; 414862306a36Sopenharmony_ci cmd.parm0 = rid; 414962306a36Sopenharmony_ci status = issuecommand(ai, &cmd, &rsp, true); 415062306a36Sopenharmony_ci if (status != 0) return status; 415162306a36Sopenharmony_ci if ((rsp.status & 0x7F00) != 0) { 415262306a36Sopenharmony_ci return (accmd << 8) + (rsp.rsp0 & 0xFF); 415362306a36Sopenharmony_ci } 415462306a36Sopenharmony_ci return 0; 415562306a36Sopenharmony_ci} 415662306a36Sopenharmony_ci 415762306a36Sopenharmony_ci/* Note, that we are using BAP1 which is also used by transmit, so 415862306a36Sopenharmony_ci * we must get a lock. */ 415962306a36Sopenharmony_cistatic int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock) 416062306a36Sopenharmony_ci{ 416162306a36Sopenharmony_ci u16 status; 416262306a36Sopenharmony_ci int rc = SUCCESS; 416362306a36Sopenharmony_ci 416462306a36Sopenharmony_ci if (lock) { 416562306a36Sopenharmony_ci if (down_interruptible(&ai->sem)) 416662306a36Sopenharmony_ci return ERROR; 416762306a36Sopenharmony_ci } 416862306a36Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) { 416962306a36Sopenharmony_ci Cmd cmd; 417062306a36Sopenharmony_ci Resp rsp; 417162306a36Sopenharmony_ci 417262306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 417362306a36Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 417462306a36Sopenharmony_ci ai->config_desc.rid_desc.valid = 1; 417562306a36Sopenharmony_ci ai->config_desc.rid_desc.len = RIDSIZE; 417662306a36Sopenharmony_ci ai->config_desc.rid_desc.rid = 0; 417762306a36Sopenharmony_ci ai->config_desc.rid_desc.host_addr = ai->ridbus; 417862306a36Sopenharmony_ci 417962306a36Sopenharmony_ci cmd.cmd = CMD_ACCESS; 418062306a36Sopenharmony_ci cmd.parm0 = rid; 418162306a36Sopenharmony_ci 418262306a36Sopenharmony_ci memcpy_toio(ai->config_desc.card_ram_off, 418362306a36Sopenharmony_ci &ai->config_desc.rid_desc, sizeof(Rid)); 418462306a36Sopenharmony_ci 418562306a36Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp, true); 418662306a36Sopenharmony_ci 418762306a36Sopenharmony_ci if (rsp.status & 0x7f00) 418862306a36Sopenharmony_ci rc = rsp.rsp0; 418962306a36Sopenharmony_ci if (!rc) 419062306a36Sopenharmony_ci memcpy(pBuf, ai->config_desc.virtual_host_addr, len); 419162306a36Sopenharmony_ci goto done; 419262306a36Sopenharmony_ci } else { 419362306a36Sopenharmony_ci if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) { 419462306a36Sopenharmony_ci rc = status; 419562306a36Sopenharmony_ci goto done; 419662306a36Sopenharmony_ci } 419762306a36Sopenharmony_ci if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) { 419862306a36Sopenharmony_ci rc = ERROR; 419962306a36Sopenharmony_ci goto done; 420062306a36Sopenharmony_ci } 420162306a36Sopenharmony_ci // read the rid length field 420262306a36Sopenharmony_ci bap_read(ai, pBuf, 2, BAP1); 420362306a36Sopenharmony_ci // length for remaining part of rid 420462306a36Sopenharmony_ci len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2; 420562306a36Sopenharmony_ci 420662306a36Sopenharmony_ci if (len <= 2) { 420762306a36Sopenharmony_ci airo_print_err(ai->dev->name, 420862306a36Sopenharmony_ci "Rid %x has a length of %d which is too short", 420962306a36Sopenharmony_ci (int)rid, (int)len); 421062306a36Sopenharmony_ci rc = ERROR; 421162306a36Sopenharmony_ci goto done; 421262306a36Sopenharmony_ci } 421362306a36Sopenharmony_ci // read remainder of the rid 421462306a36Sopenharmony_ci rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1); 421562306a36Sopenharmony_ci } 421662306a36Sopenharmony_cidone: 421762306a36Sopenharmony_ci if (lock) 421862306a36Sopenharmony_ci up(&ai->sem); 421962306a36Sopenharmony_ci return rc; 422062306a36Sopenharmony_ci} 422162306a36Sopenharmony_ci 422262306a36Sopenharmony_ci/* Note, that we are using BAP1 which is also used by transmit, so 422362306a36Sopenharmony_ci * make sure this isn't called when a transmit is happening */ 422462306a36Sopenharmony_cistatic int PC4500_writerid(struct airo_info *ai, u16 rid, 422562306a36Sopenharmony_ci const void *pBuf, int len, int lock) 422662306a36Sopenharmony_ci{ 422762306a36Sopenharmony_ci u16 status; 422862306a36Sopenharmony_ci int rc = SUCCESS; 422962306a36Sopenharmony_ci 423062306a36Sopenharmony_ci *(__le16*)pBuf = cpu_to_le16((u16)len); 423162306a36Sopenharmony_ci 423262306a36Sopenharmony_ci if (lock) { 423362306a36Sopenharmony_ci if (down_interruptible(&ai->sem)) 423462306a36Sopenharmony_ci return ERROR; 423562306a36Sopenharmony_ci } 423662306a36Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) { 423762306a36Sopenharmony_ci Cmd cmd; 423862306a36Sopenharmony_ci Resp rsp; 423962306a36Sopenharmony_ci 424062306a36Sopenharmony_ci if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid)) 424162306a36Sopenharmony_ci airo_print_err(ai->dev->name, 424262306a36Sopenharmony_ci "%s: MAC should be disabled (rid=%04x)", 424362306a36Sopenharmony_ci __func__, rid); 424462306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 424562306a36Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 424662306a36Sopenharmony_ci 424762306a36Sopenharmony_ci ai->config_desc.rid_desc.valid = 1; 424862306a36Sopenharmony_ci ai->config_desc.rid_desc.len = *((u16 *)pBuf); 424962306a36Sopenharmony_ci ai->config_desc.rid_desc.rid = 0; 425062306a36Sopenharmony_ci 425162306a36Sopenharmony_ci cmd.cmd = CMD_WRITERID; 425262306a36Sopenharmony_ci cmd.parm0 = rid; 425362306a36Sopenharmony_ci 425462306a36Sopenharmony_ci memcpy_toio(ai->config_desc.card_ram_off, 425562306a36Sopenharmony_ci &ai->config_desc.rid_desc, sizeof(Rid)); 425662306a36Sopenharmony_ci 425762306a36Sopenharmony_ci if (len < 4 || len > 2047) { 425862306a36Sopenharmony_ci airo_print_err(ai->dev->name, "%s: len=%d", __func__, len); 425962306a36Sopenharmony_ci rc = -1; 426062306a36Sopenharmony_ci } else { 426162306a36Sopenharmony_ci memcpy(ai->config_desc.virtual_host_addr, 426262306a36Sopenharmony_ci pBuf, len); 426362306a36Sopenharmony_ci 426462306a36Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp, true); 426562306a36Sopenharmony_ci if ((rc & 0xff00) != 0) { 426662306a36Sopenharmony_ci airo_print_err(ai->dev->name, "%s: Write rid Error %d", 426762306a36Sopenharmony_ci __func__, rc); 426862306a36Sopenharmony_ci airo_print_err(ai->dev->name, "%s: Cmd=%04x", 426962306a36Sopenharmony_ci __func__, cmd.cmd); 427062306a36Sopenharmony_ci } 427162306a36Sopenharmony_ci 427262306a36Sopenharmony_ci if ((rsp.status & 0x7f00)) 427362306a36Sopenharmony_ci rc = rsp.rsp0; 427462306a36Sopenharmony_ci } 427562306a36Sopenharmony_ci } else { 427662306a36Sopenharmony_ci // --- first access so that we can write the rid data 427762306a36Sopenharmony_ci if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) { 427862306a36Sopenharmony_ci rc = status; 427962306a36Sopenharmony_ci goto done; 428062306a36Sopenharmony_ci } 428162306a36Sopenharmony_ci // --- now write the rid data 428262306a36Sopenharmony_ci if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) { 428362306a36Sopenharmony_ci rc = ERROR; 428462306a36Sopenharmony_ci goto done; 428562306a36Sopenharmony_ci } 428662306a36Sopenharmony_ci bap_write(ai, pBuf, len, BAP1); 428762306a36Sopenharmony_ci // ---now commit the rid data 428862306a36Sopenharmony_ci rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS); 428962306a36Sopenharmony_ci } 429062306a36Sopenharmony_cidone: 429162306a36Sopenharmony_ci if (lock) 429262306a36Sopenharmony_ci up(&ai->sem); 429362306a36Sopenharmony_ci return rc; 429462306a36Sopenharmony_ci} 429562306a36Sopenharmony_ci 429662306a36Sopenharmony_ci/* Allocates a FID to be used for transmitting packets. We only use 429762306a36Sopenharmony_ci one for now. */ 429862306a36Sopenharmony_cistatic u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw) 429962306a36Sopenharmony_ci{ 430062306a36Sopenharmony_ci unsigned int loop = 3000; 430162306a36Sopenharmony_ci Cmd cmd; 430262306a36Sopenharmony_ci Resp rsp; 430362306a36Sopenharmony_ci u16 txFid; 430462306a36Sopenharmony_ci __le16 txControl; 430562306a36Sopenharmony_ci 430662306a36Sopenharmony_ci cmd.cmd = CMD_ALLOCATETX; 430762306a36Sopenharmony_ci cmd.parm0 = lenPayload; 430862306a36Sopenharmony_ci if (down_interruptible(&ai->sem)) 430962306a36Sopenharmony_ci return ERROR; 431062306a36Sopenharmony_ci if (issuecommand(ai, &cmd, &rsp, true) != SUCCESS) { 431162306a36Sopenharmony_ci txFid = ERROR; 431262306a36Sopenharmony_ci goto done; 431362306a36Sopenharmony_ci } 431462306a36Sopenharmony_ci if ((rsp.status & 0xFF00) != 0) { 431562306a36Sopenharmony_ci txFid = ERROR; 431662306a36Sopenharmony_ci goto done; 431762306a36Sopenharmony_ci } 431862306a36Sopenharmony_ci /* wait for the allocate event/indication 431962306a36Sopenharmony_ci * It makes me kind of nervous that this can just sit here and spin, 432062306a36Sopenharmony_ci * but in practice it only loops like four times. */ 432162306a36Sopenharmony_ci while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop); 432262306a36Sopenharmony_ci if (!loop) { 432362306a36Sopenharmony_ci txFid = ERROR; 432462306a36Sopenharmony_ci goto done; 432562306a36Sopenharmony_ci } 432662306a36Sopenharmony_ci 432762306a36Sopenharmony_ci // get the allocated fid and acknowledge 432862306a36Sopenharmony_ci txFid = IN4500(ai, TXALLOCFID); 432962306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_ALLOC); 433062306a36Sopenharmony_ci 433162306a36Sopenharmony_ci /* The CARD is pretty cool since it converts the ethernet packet 433262306a36Sopenharmony_ci * into 802.11. Also note that we don't release the FID since we 433362306a36Sopenharmony_ci * will be using the same one over and over again. */ 433462306a36Sopenharmony_ci /* We only have to setup the control once since we are not 433562306a36Sopenharmony_ci * releasing the fid. */ 433662306a36Sopenharmony_ci if (raw) 433762306a36Sopenharmony_ci txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11 433862306a36Sopenharmony_ci | TXCTL_ETHERNET | TXCTL_NORELEASE); 433962306a36Sopenharmony_ci else 434062306a36Sopenharmony_ci txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3 434162306a36Sopenharmony_ci | TXCTL_ETHERNET | TXCTL_NORELEASE); 434262306a36Sopenharmony_ci if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS) 434362306a36Sopenharmony_ci txFid = ERROR; 434462306a36Sopenharmony_ci else 434562306a36Sopenharmony_ci bap_write(ai, &txControl, sizeof(txControl), BAP1); 434662306a36Sopenharmony_ci 434762306a36Sopenharmony_cidone: 434862306a36Sopenharmony_ci up(&ai->sem); 434962306a36Sopenharmony_ci 435062306a36Sopenharmony_ci return txFid; 435162306a36Sopenharmony_ci} 435262306a36Sopenharmony_ci 435362306a36Sopenharmony_ci/* In general BAP1 is dedicated to transmiting packets. However, 435462306a36Sopenharmony_ci since we need a BAP when accessing RIDs, we also use BAP1 for that. 435562306a36Sopenharmony_ci Make sure the BAP1 spinlock is held when this is called. */ 435662306a36Sopenharmony_cistatic int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket, 435762306a36Sopenharmony_ci bool may_sleep) 435862306a36Sopenharmony_ci{ 435962306a36Sopenharmony_ci __le16 payloadLen; 436062306a36Sopenharmony_ci Cmd cmd; 436162306a36Sopenharmony_ci Resp rsp; 436262306a36Sopenharmony_ci int miclen = 0; 436362306a36Sopenharmony_ci u16 txFid = len; 436462306a36Sopenharmony_ci MICBuffer pMic; 436562306a36Sopenharmony_ci 436662306a36Sopenharmony_ci len >>= 16; 436762306a36Sopenharmony_ci 436862306a36Sopenharmony_ci if (len <= ETH_ALEN * 2) { 436962306a36Sopenharmony_ci airo_print_warn(ai->dev->name, "Short packet %d", len); 437062306a36Sopenharmony_ci return ERROR; 437162306a36Sopenharmony_ci } 437262306a36Sopenharmony_ci len -= ETH_ALEN * 2; 437362306a36Sopenharmony_ci 437462306a36Sopenharmony_ci if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled && 437562306a36Sopenharmony_ci (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) { 437662306a36Sopenharmony_ci if (encapsulate(ai, (etherHead *)pPacket,&pMic, len) != SUCCESS) 437762306a36Sopenharmony_ci return ERROR; 437862306a36Sopenharmony_ci miclen = sizeof(pMic); 437962306a36Sopenharmony_ci } 438062306a36Sopenharmony_ci // packet is destination[6], source[6], payload[len-12] 438162306a36Sopenharmony_ci // write the payload length and dst/src/payload 438262306a36Sopenharmony_ci if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR; 438362306a36Sopenharmony_ci /* The hardware addresses aren't counted as part of the payload, so 438462306a36Sopenharmony_ci * we have to subtract the 12 bytes for the addresses off */ 438562306a36Sopenharmony_ci payloadLen = cpu_to_le16(len + miclen); 438662306a36Sopenharmony_ci bap_write(ai, &payloadLen, sizeof(payloadLen), BAP1); 438762306a36Sopenharmony_ci bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1); 438862306a36Sopenharmony_ci if (miclen) 438962306a36Sopenharmony_ci bap_write(ai, (__le16*)&pMic, miclen, BAP1); 439062306a36Sopenharmony_ci bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1); 439162306a36Sopenharmony_ci // issue the transmit command 439262306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 439362306a36Sopenharmony_ci cmd.cmd = CMD_TRANSMIT; 439462306a36Sopenharmony_ci cmd.parm0 = txFid; 439562306a36Sopenharmony_ci if (issuecommand(ai, &cmd, &rsp, may_sleep) != SUCCESS) 439662306a36Sopenharmony_ci return ERROR; 439762306a36Sopenharmony_ci if ((rsp.status & 0xFF00) != 0) return ERROR; 439862306a36Sopenharmony_ci return SUCCESS; 439962306a36Sopenharmony_ci} 440062306a36Sopenharmony_ci 440162306a36Sopenharmony_cistatic int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket, 440262306a36Sopenharmony_ci bool may_sleep) 440362306a36Sopenharmony_ci{ 440462306a36Sopenharmony_ci __le16 fc, payloadLen; 440562306a36Sopenharmony_ci Cmd cmd; 440662306a36Sopenharmony_ci Resp rsp; 440762306a36Sopenharmony_ci int hdrlen; 440862306a36Sopenharmony_ci static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6}; 440962306a36Sopenharmony_ci /* padding of header to full size + le16 gaplen (6) + gaplen bytes */ 441062306a36Sopenharmony_ci u16 txFid = len; 441162306a36Sopenharmony_ci len >>= 16; 441262306a36Sopenharmony_ci 441362306a36Sopenharmony_ci fc = *(__le16*)pPacket; 441462306a36Sopenharmony_ci hdrlen = header_len(fc); 441562306a36Sopenharmony_ci 441662306a36Sopenharmony_ci if (len < hdrlen) { 441762306a36Sopenharmony_ci airo_print_warn(ai->dev->name, "Short packet %d", len); 441862306a36Sopenharmony_ci return ERROR; 441962306a36Sopenharmony_ci } 442062306a36Sopenharmony_ci 442162306a36Sopenharmony_ci /* packet is 802.11 header + payload 442262306a36Sopenharmony_ci * write the payload length and dst/src/payload */ 442362306a36Sopenharmony_ci if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR; 442462306a36Sopenharmony_ci /* The 802.11 header aren't counted as part of the payload, so 442562306a36Sopenharmony_ci * we have to subtract the header bytes off */ 442662306a36Sopenharmony_ci payloadLen = cpu_to_le16(len-hdrlen); 442762306a36Sopenharmony_ci bap_write(ai, &payloadLen, sizeof(payloadLen), BAP1); 442862306a36Sopenharmony_ci if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR; 442962306a36Sopenharmony_ci bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1); 443062306a36Sopenharmony_ci bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1); 443162306a36Sopenharmony_ci 443262306a36Sopenharmony_ci bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1); 443362306a36Sopenharmony_ci // issue the transmit command 443462306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 443562306a36Sopenharmony_ci cmd.cmd = CMD_TRANSMIT; 443662306a36Sopenharmony_ci cmd.parm0 = txFid; 443762306a36Sopenharmony_ci if (issuecommand(ai, &cmd, &rsp, may_sleep) != SUCCESS) 443862306a36Sopenharmony_ci return ERROR; 443962306a36Sopenharmony_ci if ((rsp.status & 0xFF00) != 0) return ERROR; 444062306a36Sopenharmony_ci return SUCCESS; 444162306a36Sopenharmony_ci} 444262306a36Sopenharmony_ci 444362306a36Sopenharmony_ci/* 444462306a36Sopenharmony_ci * This is the proc_fs routines. It is a bit messier than I would 444562306a36Sopenharmony_ci * like! Feel free to clean it up! 444662306a36Sopenharmony_ci */ 444762306a36Sopenharmony_ci 444862306a36Sopenharmony_cistatic ssize_t proc_read(struct file *file, 444962306a36Sopenharmony_ci char __user *buffer, 445062306a36Sopenharmony_ci size_t len, 445162306a36Sopenharmony_ci loff_t *offset); 445262306a36Sopenharmony_ci 445362306a36Sopenharmony_cistatic ssize_t proc_write(struct file *file, 445462306a36Sopenharmony_ci const char __user *buffer, 445562306a36Sopenharmony_ci size_t len, 445662306a36Sopenharmony_ci loff_t *offset); 445762306a36Sopenharmony_cistatic int proc_close(struct inode *inode, struct file *file); 445862306a36Sopenharmony_ci 445962306a36Sopenharmony_cistatic int proc_stats_open(struct inode *inode, struct file *file); 446062306a36Sopenharmony_cistatic int proc_statsdelta_open(struct inode *inode, struct file *file); 446162306a36Sopenharmony_cistatic int proc_status_open(struct inode *inode, struct file *file); 446262306a36Sopenharmony_cistatic int proc_SSID_open(struct inode *inode, struct file *file); 446362306a36Sopenharmony_cistatic int proc_APList_open(struct inode *inode, struct file *file); 446462306a36Sopenharmony_cistatic int proc_BSSList_open(struct inode *inode, struct file *file); 446562306a36Sopenharmony_cistatic int proc_config_open(struct inode *inode, struct file *file); 446662306a36Sopenharmony_cistatic int proc_wepkey_open(struct inode *inode, struct file *file); 446762306a36Sopenharmony_ci 446862306a36Sopenharmony_cistatic const struct proc_ops proc_statsdelta_ops = { 446962306a36Sopenharmony_ci .proc_read = proc_read, 447062306a36Sopenharmony_ci .proc_open = proc_statsdelta_open, 447162306a36Sopenharmony_ci .proc_release = proc_close, 447262306a36Sopenharmony_ci .proc_lseek = default_llseek, 447362306a36Sopenharmony_ci}; 447462306a36Sopenharmony_ci 447562306a36Sopenharmony_cistatic const struct proc_ops proc_stats_ops = { 447662306a36Sopenharmony_ci .proc_read = proc_read, 447762306a36Sopenharmony_ci .proc_open = proc_stats_open, 447862306a36Sopenharmony_ci .proc_release = proc_close, 447962306a36Sopenharmony_ci .proc_lseek = default_llseek, 448062306a36Sopenharmony_ci}; 448162306a36Sopenharmony_ci 448262306a36Sopenharmony_cistatic const struct proc_ops proc_status_ops = { 448362306a36Sopenharmony_ci .proc_read = proc_read, 448462306a36Sopenharmony_ci .proc_open = proc_status_open, 448562306a36Sopenharmony_ci .proc_release = proc_close, 448662306a36Sopenharmony_ci .proc_lseek = default_llseek, 448762306a36Sopenharmony_ci}; 448862306a36Sopenharmony_ci 448962306a36Sopenharmony_cistatic const struct proc_ops proc_SSID_ops = { 449062306a36Sopenharmony_ci .proc_read = proc_read, 449162306a36Sopenharmony_ci .proc_write = proc_write, 449262306a36Sopenharmony_ci .proc_open = proc_SSID_open, 449362306a36Sopenharmony_ci .proc_release = proc_close, 449462306a36Sopenharmony_ci .proc_lseek = default_llseek, 449562306a36Sopenharmony_ci}; 449662306a36Sopenharmony_ci 449762306a36Sopenharmony_cistatic const struct proc_ops proc_BSSList_ops = { 449862306a36Sopenharmony_ci .proc_read = proc_read, 449962306a36Sopenharmony_ci .proc_write = proc_write, 450062306a36Sopenharmony_ci .proc_open = proc_BSSList_open, 450162306a36Sopenharmony_ci .proc_release = proc_close, 450262306a36Sopenharmony_ci .proc_lseek = default_llseek, 450362306a36Sopenharmony_ci}; 450462306a36Sopenharmony_ci 450562306a36Sopenharmony_cistatic const struct proc_ops proc_APList_ops = { 450662306a36Sopenharmony_ci .proc_read = proc_read, 450762306a36Sopenharmony_ci .proc_write = proc_write, 450862306a36Sopenharmony_ci .proc_open = proc_APList_open, 450962306a36Sopenharmony_ci .proc_release = proc_close, 451062306a36Sopenharmony_ci .proc_lseek = default_llseek, 451162306a36Sopenharmony_ci}; 451262306a36Sopenharmony_ci 451362306a36Sopenharmony_cistatic const struct proc_ops proc_config_ops = { 451462306a36Sopenharmony_ci .proc_read = proc_read, 451562306a36Sopenharmony_ci .proc_write = proc_write, 451662306a36Sopenharmony_ci .proc_open = proc_config_open, 451762306a36Sopenharmony_ci .proc_release = proc_close, 451862306a36Sopenharmony_ci .proc_lseek = default_llseek, 451962306a36Sopenharmony_ci}; 452062306a36Sopenharmony_ci 452162306a36Sopenharmony_cistatic const struct proc_ops proc_wepkey_ops = { 452262306a36Sopenharmony_ci .proc_read = proc_read, 452362306a36Sopenharmony_ci .proc_write = proc_write, 452462306a36Sopenharmony_ci .proc_open = proc_wepkey_open, 452562306a36Sopenharmony_ci .proc_release = proc_close, 452662306a36Sopenharmony_ci .proc_lseek = default_llseek, 452762306a36Sopenharmony_ci}; 452862306a36Sopenharmony_ci 452962306a36Sopenharmony_cistatic struct proc_dir_entry *airo_entry; 453062306a36Sopenharmony_ci 453162306a36Sopenharmony_cistruct proc_data { 453262306a36Sopenharmony_ci int release_buffer; 453362306a36Sopenharmony_ci int readlen; 453462306a36Sopenharmony_ci char *rbuffer; 453562306a36Sopenharmony_ci int writelen; 453662306a36Sopenharmony_ci int maxwritelen; 453762306a36Sopenharmony_ci char *wbuffer; 453862306a36Sopenharmony_ci void (*on_close) (struct inode *, struct file *); 453962306a36Sopenharmony_ci}; 454062306a36Sopenharmony_ci 454162306a36Sopenharmony_cistatic int setup_proc_entry(struct net_device *dev, 454262306a36Sopenharmony_ci struct airo_info *apriv) 454362306a36Sopenharmony_ci{ 454462306a36Sopenharmony_ci struct proc_dir_entry *entry; 454562306a36Sopenharmony_ci 454662306a36Sopenharmony_ci /* First setup the device directory */ 454762306a36Sopenharmony_ci strcpy(apriv->proc_name, dev->name); 454862306a36Sopenharmony_ci apriv->proc_entry = proc_mkdir_mode(apriv->proc_name, airo_perm, 454962306a36Sopenharmony_ci airo_entry); 455062306a36Sopenharmony_ci if (!apriv->proc_entry) 455162306a36Sopenharmony_ci return -ENOMEM; 455262306a36Sopenharmony_ci proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid); 455362306a36Sopenharmony_ci 455462306a36Sopenharmony_ci /* Setup the StatsDelta */ 455562306a36Sopenharmony_ci entry = proc_create_data("StatsDelta", 0444 & proc_perm, 455662306a36Sopenharmony_ci apriv->proc_entry, &proc_statsdelta_ops, dev); 455762306a36Sopenharmony_ci if (!entry) 455862306a36Sopenharmony_ci goto fail; 455962306a36Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 456062306a36Sopenharmony_ci 456162306a36Sopenharmony_ci /* Setup the Stats */ 456262306a36Sopenharmony_ci entry = proc_create_data("Stats", 0444 & proc_perm, 456362306a36Sopenharmony_ci apriv->proc_entry, &proc_stats_ops, dev); 456462306a36Sopenharmony_ci if (!entry) 456562306a36Sopenharmony_ci goto fail; 456662306a36Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 456762306a36Sopenharmony_ci 456862306a36Sopenharmony_ci /* Setup the Status */ 456962306a36Sopenharmony_ci entry = proc_create_data("Status", 0444 & proc_perm, 457062306a36Sopenharmony_ci apriv->proc_entry, &proc_status_ops, dev); 457162306a36Sopenharmony_ci if (!entry) 457262306a36Sopenharmony_ci goto fail; 457362306a36Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 457462306a36Sopenharmony_ci 457562306a36Sopenharmony_ci /* Setup the Config */ 457662306a36Sopenharmony_ci entry = proc_create_data("Config", proc_perm, 457762306a36Sopenharmony_ci apriv->proc_entry, &proc_config_ops, dev); 457862306a36Sopenharmony_ci if (!entry) 457962306a36Sopenharmony_ci goto fail; 458062306a36Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 458162306a36Sopenharmony_ci 458262306a36Sopenharmony_ci /* Setup the SSID */ 458362306a36Sopenharmony_ci entry = proc_create_data("SSID", proc_perm, 458462306a36Sopenharmony_ci apriv->proc_entry, &proc_SSID_ops, dev); 458562306a36Sopenharmony_ci if (!entry) 458662306a36Sopenharmony_ci goto fail; 458762306a36Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 458862306a36Sopenharmony_ci 458962306a36Sopenharmony_ci /* Setup the APList */ 459062306a36Sopenharmony_ci entry = proc_create_data("APList", proc_perm, 459162306a36Sopenharmony_ci apriv->proc_entry, &proc_APList_ops, dev); 459262306a36Sopenharmony_ci if (!entry) 459362306a36Sopenharmony_ci goto fail; 459462306a36Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 459562306a36Sopenharmony_ci 459662306a36Sopenharmony_ci /* Setup the BSSList */ 459762306a36Sopenharmony_ci entry = proc_create_data("BSSList", proc_perm, 459862306a36Sopenharmony_ci apriv->proc_entry, &proc_BSSList_ops, dev); 459962306a36Sopenharmony_ci if (!entry) 460062306a36Sopenharmony_ci goto fail; 460162306a36Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 460262306a36Sopenharmony_ci 460362306a36Sopenharmony_ci /* Setup the WepKey */ 460462306a36Sopenharmony_ci entry = proc_create_data("WepKey", proc_perm, 460562306a36Sopenharmony_ci apriv->proc_entry, &proc_wepkey_ops, dev); 460662306a36Sopenharmony_ci if (!entry) 460762306a36Sopenharmony_ci goto fail; 460862306a36Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 460962306a36Sopenharmony_ci return 0; 461062306a36Sopenharmony_ci 461162306a36Sopenharmony_cifail: 461262306a36Sopenharmony_ci remove_proc_subtree(apriv->proc_name, airo_entry); 461362306a36Sopenharmony_ci return -ENOMEM; 461462306a36Sopenharmony_ci} 461562306a36Sopenharmony_ci 461662306a36Sopenharmony_cistatic int takedown_proc_entry(struct net_device *dev, 461762306a36Sopenharmony_ci struct airo_info *apriv) 461862306a36Sopenharmony_ci{ 461962306a36Sopenharmony_ci remove_proc_subtree(apriv->proc_name, airo_entry); 462062306a36Sopenharmony_ci return 0; 462162306a36Sopenharmony_ci} 462262306a36Sopenharmony_ci 462362306a36Sopenharmony_ci/* 462462306a36Sopenharmony_ci * What we want from the proc_fs is to be able to efficiently read 462562306a36Sopenharmony_ci * and write the configuration. To do this, we want to read the 462662306a36Sopenharmony_ci * configuration when the file is opened and write it when the file is 462762306a36Sopenharmony_ci * closed. So basically we allocate a read buffer at open and fill it 462862306a36Sopenharmony_ci * with data, and allocate a write buffer and read it at close. 462962306a36Sopenharmony_ci */ 463062306a36Sopenharmony_ci 463162306a36Sopenharmony_ci/* 463262306a36Sopenharmony_ci * The read routine is generic, it relies on the preallocated rbuffer 463362306a36Sopenharmony_ci * to supply the data. 463462306a36Sopenharmony_ci */ 463562306a36Sopenharmony_cistatic ssize_t proc_read(struct file *file, 463662306a36Sopenharmony_ci char __user *buffer, 463762306a36Sopenharmony_ci size_t len, 463862306a36Sopenharmony_ci loff_t *offset) 463962306a36Sopenharmony_ci{ 464062306a36Sopenharmony_ci struct proc_data *priv = file->private_data; 464162306a36Sopenharmony_ci 464262306a36Sopenharmony_ci if (!priv->rbuffer) 464362306a36Sopenharmony_ci return -EINVAL; 464462306a36Sopenharmony_ci 464562306a36Sopenharmony_ci return simple_read_from_buffer(buffer, len, offset, priv->rbuffer, 464662306a36Sopenharmony_ci priv->readlen); 464762306a36Sopenharmony_ci} 464862306a36Sopenharmony_ci 464962306a36Sopenharmony_ci/* 465062306a36Sopenharmony_ci * The write routine is generic, it fills in a preallocated rbuffer 465162306a36Sopenharmony_ci * to supply the data. 465262306a36Sopenharmony_ci */ 465362306a36Sopenharmony_cistatic ssize_t proc_write(struct file *file, 465462306a36Sopenharmony_ci const char __user *buffer, 465562306a36Sopenharmony_ci size_t len, 465662306a36Sopenharmony_ci loff_t *offset) 465762306a36Sopenharmony_ci{ 465862306a36Sopenharmony_ci ssize_t ret; 465962306a36Sopenharmony_ci struct proc_data *priv = file->private_data; 466062306a36Sopenharmony_ci 466162306a36Sopenharmony_ci if (!priv->wbuffer) 466262306a36Sopenharmony_ci return -EINVAL; 466362306a36Sopenharmony_ci 466462306a36Sopenharmony_ci ret = simple_write_to_buffer(priv->wbuffer, priv->maxwritelen, offset, 466562306a36Sopenharmony_ci buffer, len); 466662306a36Sopenharmony_ci if (ret > 0) 466762306a36Sopenharmony_ci priv->writelen = max_t(int, priv->writelen, *offset); 466862306a36Sopenharmony_ci 466962306a36Sopenharmony_ci return ret; 467062306a36Sopenharmony_ci} 467162306a36Sopenharmony_ci 467262306a36Sopenharmony_cistatic int proc_status_open(struct inode *inode, struct file *file) 467362306a36Sopenharmony_ci{ 467462306a36Sopenharmony_ci struct proc_data *data; 467562306a36Sopenharmony_ci struct net_device *dev = pde_data(inode); 467662306a36Sopenharmony_ci struct airo_info *apriv = dev->ml_priv; 467762306a36Sopenharmony_ci CapabilityRid cap_rid; 467862306a36Sopenharmony_ci StatusRid status_rid; 467962306a36Sopenharmony_ci u16 mode; 468062306a36Sopenharmony_ci int i; 468162306a36Sopenharmony_ci 468262306a36Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 468362306a36Sopenharmony_ci return -ENOMEM; 468462306a36Sopenharmony_ci data = file->private_data; 468562306a36Sopenharmony_ci if ((data->rbuffer = kmalloc(2048, GFP_KERNEL)) == NULL) { 468662306a36Sopenharmony_ci kfree (file->private_data); 468762306a36Sopenharmony_ci return -ENOMEM; 468862306a36Sopenharmony_ci } 468962306a36Sopenharmony_ci 469062306a36Sopenharmony_ci readStatusRid(apriv, &status_rid, 1); 469162306a36Sopenharmony_ci readCapabilityRid(apriv, &cap_rid, 1); 469262306a36Sopenharmony_ci 469362306a36Sopenharmony_ci mode = le16_to_cpu(status_rid.mode); 469462306a36Sopenharmony_ci 469562306a36Sopenharmony_ci i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n", 469662306a36Sopenharmony_ci mode & 1 ? "CFG ": "", 469762306a36Sopenharmony_ci mode & 2 ? "ACT ": "", 469862306a36Sopenharmony_ci mode & 0x10 ? "SYN ": "", 469962306a36Sopenharmony_ci mode & 0x20 ? "LNK ": "", 470062306a36Sopenharmony_ci mode & 0x40 ? "LEAP ": "", 470162306a36Sopenharmony_ci mode & 0x80 ? "PRIV ": "", 470262306a36Sopenharmony_ci mode & 0x100 ? "KEY ": "", 470362306a36Sopenharmony_ci mode & 0x200 ? "WEP ": "", 470462306a36Sopenharmony_ci mode & 0x8000 ? "ERR ": ""); 470562306a36Sopenharmony_ci sprintf(data->rbuffer+i, "Mode: %x\n" 470662306a36Sopenharmony_ci "Signal Strength: %d\n" 470762306a36Sopenharmony_ci "Signal Quality: %d\n" 470862306a36Sopenharmony_ci "SSID: %-.*s\n" 470962306a36Sopenharmony_ci "AP: %-.16s\n" 471062306a36Sopenharmony_ci "Freq: %d\n" 471162306a36Sopenharmony_ci "BitRate: %dmbs\n" 471262306a36Sopenharmony_ci "Driver Version: %s\n" 471362306a36Sopenharmony_ci "Device: %s\nManufacturer: %s\nFirmware Version: %s\n" 471462306a36Sopenharmony_ci "Radio type: %x\nCountry: %x\nHardware Version: %x\n" 471562306a36Sopenharmony_ci "Software Version: %x\nSoftware Subversion: %x\n" 471662306a36Sopenharmony_ci "Boot block version: %x\n", 471762306a36Sopenharmony_ci le16_to_cpu(status_rid.mode), 471862306a36Sopenharmony_ci le16_to_cpu(status_rid.normalizedSignalStrength), 471962306a36Sopenharmony_ci le16_to_cpu(status_rid.signalQuality), 472062306a36Sopenharmony_ci le16_to_cpu(status_rid.SSIDlen), 472162306a36Sopenharmony_ci status_rid.SSID, 472262306a36Sopenharmony_ci status_rid.apName, 472362306a36Sopenharmony_ci le16_to_cpu(status_rid.channel), 472462306a36Sopenharmony_ci le16_to_cpu(status_rid.currentXmitRate) / 2, 472562306a36Sopenharmony_ci version, 472662306a36Sopenharmony_ci cap_rid.prodName, 472762306a36Sopenharmony_ci cap_rid.manName, 472862306a36Sopenharmony_ci cap_rid.prodVer, 472962306a36Sopenharmony_ci le16_to_cpu(cap_rid.radioType), 473062306a36Sopenharmony_ci le16_to_cpu(cap_rid.country), 473162306a36Sopenharmony_ci le16_to_cpu(cap_rid.hardVer), 473262306a36Sopenharmony_ci le16_to_cpu(cap_rid.softVer), 473362306a36Sopenharmony_ci le16_to_cpu(cap_rid.softSubVer), 473462306a36Sopenharmony_ci le16_to_cpu(cap_rid.bootBlockVer)); 473562306a36Sopenharmony_ci data->readlen = strlen(data->rbuffer); 473662306a36Sopenharmony_ci return 0; 473762306a36Sopenharmony_ci} 473862306a36Sopenharmony_ci 473962306a36Sopenharmony_cistatic int proc_stats_rid_open(struct inode*, struct file*, u16); 474062306a36Sopenharmony_cistatic int proc_statsdelta_open(struct inode *inode, 474162306a36Sopenharmony_ci struct file *file) 474262306a36Sopenharmony_ci{ 474362306a36Sopenharmony_ci if (file->f_mode&FMODE_WRITE) { 474462306a36Sopenharmony_ci return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR); 474562306a36Sopenharmony_ci } 474662306a36Sopenharmony_ci return proc_stats_rid_open(inode, file, RID_STATSDELTA); 474762306a36Sopenharmony_ci} 474862306a36Sopenharmony_ci 474962306a36Sopenharmony_cistatic int proc_stats_open(struct inode *inode, struct file *file) 475062306a36Sopenharmony_ci{ 475162306a36Sopenharmony_ci return proc_stats_rid_open(inode, file, RID_STATS); 475262306a36Sopenharmony_ci} 475362306a36Sopenharmony_ci 475462306a36Sopenharmony_cistatic int proc_stats_rid_open(struct inode *inode, 475562306a36Sopenharmony_ci struct file *file, 475662306a36Sopenharmony_ci u16 rid) 475762306a36Sopenharmony_ci{ 475862306a36Sopenharmony_ci struct proc_data *data; 475962306a36Sopenharmony_ci struct net_device *dev = pde_data(inode); 476062306a36Sopenharmony_ci struct airo_info *apriv = dev->ml_priv; 476162306a36Sopenharmony_ci StatsRid stats; 476262306a36Sopenharmony_ci int i, j; 476362306a36Sopenharmony_ci __le32 *vals = stats.vals; 476462306a36Sopenharmony_ci int len; 476562306a36Sopenharmony_ci 476662306a36Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 476762306a36Sopenharmony_ci return -ENOMEM; 476862306a36Sopenharmony_ci data = file->private_data; 476962306a36Sopenharmony_ci if ((data->rbuffer = kmalloc(4096, GFP_KERNEL)) == NULL) { 477062306a36Sopenharmony_ci kfree (file->private_data); 477162306a36Sopenharmony_ci return -ENOMEM; 477262306a36Sopenharmony_ci } 477362306a36Sopenharmony_ci 477462306a36Sopenharmony_ci readStatsRid(apriv, &stats, rid, 1); 477562306a36Sopenharmony_ci len = le16_to_cpu(stats.len); 477662306a36Sopenharmony_ci 477762306a36Sopenharmony_ci j = 0; 477862306a36Sopenharmony_ci for (i = 0; statsLabels[i]!=(char *)-1 && i*4<len; i++) { 477962306a36Sopenharmony_ci if (!statsLabels[i]) continue; 478062306a36Sopenharmony_ci if (j+strlen(statsLabels[i])+16>4096) { 478162306a36Sopenharmony_ci airo_print_warn(apriv->dev->name, 478262306a36Sopenharmony_ci "Potentially disastrous buffer overflow averted!"); 478362306a36Sopenharmony_ci break; 478462306a36Sopenharmony_ci } 478562306a36Sopenharmony_ci j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i], 478662306a36Sopenharmony_ci le32_to_cpu(vals[i])); 478762306a36Sopenharmony_ci } 478862306a36Sopenharmony_ci if (i*4 >= len) { 478962306a36Sopenharmony_ci airo_print_warn(apriv->dev->name, "Got a short rid"); 479062306a36Sopenharmony_ci } 479162306a36Sopenharmony_ci data->readlen = j; 479262306a36Sopenharmony_ci return 0; 479362306a36Sopenharmony_ci} 479462306a36Sopenharmony_ci 479562306a36Sopenharmony_cistatic int get_dec_u16(char *buffer, int *start, int limit) 479662306a36Sopenharmony_ci{ 479762306a36Sopenharmony_ci u16 value; 479862306a36Sopenharmony_ci int valid = 0; 479962306a36Sopenharmony_ci for (value = 0; *start < limit && buffer[*start] >= '0' && 480062306a36Sopenharmony_ci buffer[*start] <= '9'; (*start)++) { 480162306a36Sopenharmony_ci valid = 1; 480262306a36Sopenharmony_ci value *= 10; 480362306a36Sopenharmony_ci value += buffer[*start] - '0'; 480462306a36Sopenharmony_ci } 480562306a36Sopenharmony_ci if (!valid) return -1; 480662306a36Sopenharmony_ci return value; 480762306a36Sopenharmony_ci} 480862306a36Sopenharmony_ci 480962306a36Sopenharmony_cistatic int airo_config_commit(struct net_device *dev, 481062306a36Sopenharmony_ci struct iw_request_info *info, 481162306a36Sopenharmony_ci union iwreq_data *wrqu, 481262306a36Sopenharmony_ci char *extra); 481362306a36Sopenharmony_ci 481462306a36Sopenharmony_cistatic inline int sniffing_mode(struct airo_info *ai) 481562306a36Sopenharmony_ci{ 481662306a36Sopenharmony_ci return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >= 481762306a36Sopenharmony_ci le16_to_cpu(RXMODE_RFMON); 481862306a36Sopenharmony_ci} 481962306a36Sopenharmony_ci 482062306a36Sopenharmony_cistatic void proc_config_on_close(struct inode *inode, struct file *file) 482162306a36Sopenharmony_ci{ 482262306a36Sopenharmony_ci struct proc_data *data = file->private_data; 482362306a36Sopenharmony_ci struct net_device *dev = pde_data(inode); 482462306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 482562306a36Sopenharmony_ci char *line; 482662306a36Sopenharmony_ci 482762306a36Sopenharmony_ci if (!data->writelen) return; 482862306a36Sopenharmony_ci 482962306a36Sopenharmony_ci readConfigRid(ai, 1); 483062306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 483162306a36Sopenharmony_ci 483262306a36Sopenharmony_ci line = data->wbuffer; 483362306a36Sopenharmony_ci while (line[0]) { 483462306a36Sopenharmony_ci/*** Mode processing */ 483562306a36Sopenharmony_ci if (!strncmp(line, "Mode: ", 6)) { 483662306a36Sopenharmony_ci line += 6; 483762306a36Sopenharmony_ci if (sniffing_mode(ai)) 483862306a36Sopenharmony_ci set_bit (FLAG_RESET, &ai->flags); 483962306a36Sopenharmony_ci ai->config.rmode &= ~RXMODE_FULL_MASK; 484062306a36Sopenharmony_ci clear_bit (FLAG_802_11, &ai->flags); 484162306a36Sopenharmony_ci ai->config.opmode &= ~MODE_CFG_MASK; 484262306a36Sopenharmony_ci ai->config.scanMode = SCANMODE_ACTIVE; 484362306a36Sopenharmony_ci if (line[0] == 'a') { 484462306a36Sopenharmony_ci ai->config.opmode |= MODE_STA_IBSS; 484562306a36Sopenharmony_ci } else { 484662306a36Sopenharmony_ci ai->config.opmode |= MODE_STA_ESS; 484762306a36Sopenharmony_ci if (line[0] == 'r') { 484862306a36Sopenharmony_ci ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER; 484962306a36Sopenharmony_ci ai->config.scanMode = SCANMODE_PASSIVE; 485062306a36Sopenharmony_ci set_bit (FLAG_802_11, &ai->flags); 485162306a36Sopenharmony_ci } else if (line[0] == 'y') { 485262306a36Sopenharmony_ci ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER; 485362306a36Sopenharmony_ci ai->config.scanMode = SCANMODE_PASSIVE; 485462306a36Sopenharmony_ci set_bit (FLAG_802_11, &ai->flags); 485562306a36Sopenharmony_ci } else if (line[0] == 'l') 485662306a36Sopenharmony_ci ai->config.rmode |= RXMODE_LANMON; 485762306a36Sopenharmony_ci } 485862306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 485962306a36Sopenharmony_ci } 486062306a36Sopenharmony_ci 486162306a36Sopenharmony_ci/*** Radio status */ 486262306a36Sopenharmony_ci else if (!strncmp(line,"Radio: ", 7)) { 486362306a36Sopenharmony_ci line += 7; 486462306a36Sopenharmony_ci if (!strncmp(line,"off", 3)) { 486562306a36Sopenharmony_ci set_bit (FLAG_RADIO_OFF, &ai->flags); 486662306a36Sopenharmony_ci } else { 486762306a36Sopenharmony_ci clear_bit (FLAG_RADIO_OFF, &ai->flags); 486862306a36Sopenharmony_ci } 486962306a36Sopenharmony_ci } 487062306a36Sopenharmony_ci/*** NodeName processing */ 487162306a36Sopenharmony_ci else if (!strncmp(line, "NodeName: ", 10)) { 487262306a36Sopenharmony_ci int j; 487362306a36Sopenharmony_ci 487462306a36Sopenharmony_ci line += 10; 487562306a36Sopenharmony_ci memset(ai->config.nodeName, 0, 16); 487662306a36Sopenharmony_ci/* Do the name, assume a space between the mode and node name */ 487762306a36Sopenharmony_ci for (j = 0; j < 16 && line[j] != '\n'; j++) { 487862306a36Sopenharmony_ci ai->config.nodeName[j] = line[j]; 487962306a36Sopenharmony_ci } 488062306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 488162306a36Sopenharmony_ci } 488262306a36Sopenharmony_ci 488362306a36Sopenharmony_ci/*** PowerMode processing */ 488462306a36Sopenharmony_ci else if (!strncmp(line, "PowerMode: ", 11)) { 488562306a36Sopenharmony_ci line += 11; 488662306a36Sopenharmony_ci if (!strncmp(line, "PSPCAM", 6)) { 488762306a36Sopenharmony_ci ai->config.powerSaveMode = POWERSAVE_PSPCAM; 488862306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 488962306a36Sopenharmony_ci } else if (!strncmp(line, "PSP", 3)) { 489062306a36Sopenharmony_ci ai->config.powerSaveMode = POWERSAVE_PSP; 489162306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 489262306a36Sopenharmony_ci } else { 489362306a36Sopenharmony_ci ai->config.powerSaveMode = POWERSAVE_CAM; 489462306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 489562306a36Sopenharmony_ci } 489662306a36Sopenharmony_ci } else if (!strncmp(line, "DataRates: ", 11)) { 489762306a36Sopenharmony_ci int v, i = 0, k = 0; /* i is index into line, 489862306a36Sopenharmony_ci k is index to rates */ 489962306a36Sopenharmony_ci 490062306a36Sopenharmony_ci line += 11; 490162306a36Sopenharmony_ci while ((v = get_dec_u16(line, &i, 3))!=-1) { 490262306a36Sopenharmony_ci ai->config.rates[k++] = (u8)v; 490362306a36Sopenharmony_ci line += i + 1; 490462306a36Sopenharmony_ci i = 0; 490562306a36Sopenharmony_ci } 490662306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 490762306a36Sopenharmony_ci } else if (!strncmp(line, "Channel: ", 9)) { 490862306a36Sopenharmony_ci int v, i = 0; 490962306a36Sopenharmony_ci line += 9; 491062306a36Sopenharmony_ci v = get_dec_u16(line, &i, i+3); 491162306a36Sopenharmony_ci if (v != -1) { 491262306a36Sopenharmony_ci ai->config.channelSet = cpu_to_le16(v); 491362306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 491462306a36Sopenharmony_ci } 491562306a36Sopenharmony_ci } else if (!strncmp(line, "XmitPower: ", 11)) { 491662306a36Sopenharmony_ci int v, i = 0; 491762306a36Sopenharmony_ci line += 11; 491862306a36Sopenharmony_ci v = get_dec_u16(line, &i, i+3); 491962306a36Sopenharmony_ci if (v != -1) { 492062306a36Sopenharmony_ci ai->config.txPower = cpu_to_le16(v); 492162306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 492262306a36Sopenharmony_ci } 492362306a36Sopenharmony_ci } else if (!strncmp(line, "WEP: ", 5)) { 492462306a36Sopenharmony_ci line += 5; 492562306a36Sopenharmony_ci switch(line[0]) { 492662306a36Sopenharmony_ci case 's': 492762306a36Sopenharmony_ci set_auth_type(ai, AUTH_SHAREDKEY); 492862306a36Sopenharmony_ci break; 492962306a36Sopenharmony_ci case 'e': 493062306a36Sopenharmony_ci set_auth_type(ai, AUTH_ENCRYPT); 493162306a36Sopenharmony_ci break; 493262306a36Sopenharmony_ci default: 493362306a36Sopenharmony_ci set_auth_type(ai, AUTH_OPEN); 493462306a36Sopenharmony_ci break; 493562306a36Sopenharmony_ci } 493662306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 493762306a36Sopenharmony_ci } else if (!strncmp(line, "LongRetryLimit: ", 16)) { 493862306a36Sopenharmony_ci int v, i = 0; 493962306a36Sopenharmony_ci 494062306a36Sopenharmony_ci line += 16; 494162306a36Sopenharmony_ci v = get_dec_u16(line, &i, 3); 494262306a36Sopenharmony_ci v = (v<0) ? 0 : ((v>255) ? 255 : v); 494362306a36Sopenharmony_ci ai->config.longRetryLimit = cpu_to_le16(v); 494462306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 494562306a36Sopenharmony_ci } else if (!strncmp(line, "ShortRetryLimit: ", 17)) { 494662306a36Sopenharmony_ci int v, i = 0; 494762306a36Sopenharmony_ci 494862306a36Sopenharmony_ci line += 17; 494962306a36Sopenharmony_ci v = get_dec_u16(line, &i, 3); 495062306a36Sopenharmony_ci v = (v<0) ? 0 : ((v>255) ? 255 : v); 495162306a36Sopenharmony_ci ai->config.shortRetryLimit = cpu_to_le16(v); 495262306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 495362306a36Sopenharmony_ci } else if (!strncmp(line, "RTSThreshold: ", 14)) { 495462306a36Sopenharmony_ci int v, i = 0; 495562306a36Sopenharmony_ci 495662306a36Sopenharmony_ci line += 14; 495762306a36Sopenharmony_ci v = get_dec_u16(line, &i, 4); 495862306a36Sopenharmony_ci v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v); 495962306a36Sopenharmony_ci ai->config.rtsThres = cpu_to_le16(v); 496062306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 496162306a36Sopenharmony_ci } else if (!strncmp(line, "TXMSDULifetime: ", 16)) { 496262306a36Sopenharmony_ci int v, i = 0; 496362306a36Sopenharmony_ci 496462306a36Sopenharmony_ci line += 16; 496562306a36Sopenharmony_ci v = get_dec_u16(line, &i, 5); 496662306a36Sopenharmony_ci v = (v<0) ? 0 : v; 496762306a36Sopenharmony_ci ai->config.txLifetime = cpu_to_le16(v); 496862306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 496962306a36Sopenharmony_ci } else if (!strncmp(line, "RXMSDULifetime: ", 16)) { 497062306a36Sopenharmony_ci int v, i = 0; 497162306a36Sopenharmony_ci 497262306a36Sopenharmony_ci line += 16; 497362306a36Sopenharmony_ci v = get_dec_u16(line, &i, 5); 497462306a36Sopenharmony_ci v = (v<0) ? 0 : v; 497562306a36Sopenharmony_ci ai->config.rxLifetime = cpu_to_le16(v); 497662306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 497762306a36Sopenharmony_ci } else if (!strncmp(line, "TXDiversity: ", 13)) { 497862306a36Sopenharmony_ci ai->config.txDiversity = 497962306a36Sopenharmony_ci (line[13]=='l') ? 1 : 498062306a36Sopenharmony_ci ((line[13]=='r')? 2: 3); 498162306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 498262306a36Sopenharmony_ci } else if (!strncmp(line, "RXDiversity: ", 13)) { 498362306a36Sopenharmony_ci ai->config.rxDiversity = 498462306a36Sopenharmony_ci (line[13]=='l') ? 1 : 498562306a36Sopenharmony_ci ((line[13]=='r')? 2: 3); 498662306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 498762306a36Sopenharmony_ci } else if (!strncmp(line, "FragThreshold: ", 15)) { 498862306a36Sopenharmony_ci int v, i = 0; 498962306a36Sopenharmony_ci 499062306a36Sopenharmony_ci line += 15; 499162306a36Sopenharmony_ci v = get_dec_u16(line, &i, 4); 499262306a36Sopenharmony_ci v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v); 499362306a36Sopenharmony_ci v = v & 0xfffe; /* Make sure its even */ 499462306a36Sopenharmony_ci ai->config.fragThresh = cpu_to_le16(v); 499562306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 499662306a36Sopenharmony_ci } else if (!strncmp(line, "Modulation: ", 12)) { 499762306a36Sopenharmony_ci line += 12; 499862306a36Sopenharmony_ci switch(*line) { 499962306a36Sopenharmony_ci case 'd': ai->config.modulation = MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break; 500062306a36Sopenharmony_ci case 'c': ai->config.modulation = MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break; 500162306a36Sopenharmony_ci case 'm': ai->config.modulation = MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break; 500262306a36Sopenharmony_ci default: airo_print_warn(ai->dev->name, "Unknown modulation"); 500362306a36Sopenharmony_ci } 500462306a36Sopenharmony_ci } else if (!strncmp(line, "Preamble: ", 10)) { 500562306a36Sopenharmony_ci line += 10; 500662306a36Sopenharmony_ci switch(*line) { 500762306a36Sopenharmony_ci case 'a': ai->config.preamble = PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break; 500862306a36Sopenharmony_ci case 'l': ai->config.preamble = PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break; 500962306a36Sopenharmony_ci case 's': ai->config.preamble = PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break; 501062306a36Sopenharmony_ci default: airo_print_warn(ai->dev->name, "Unknown preamble"); 501162306a36Sopenharmony_ci } 501262306a36Sopenharmony_ci } else { 501362306a36Sopenharmony_ci airo_print_warn(ai->dev->name, "Couldn't figure out %s", line); 501462306a36Sopenharmony_ci } 501562306a36Sopenharmony_ci while (line[0] && line[0] != '\n') line++; 501662306a36Sopenharmony_ci if (line[0]) line++; 501762306a36Sopenharmony_ci } 501862306a36Sopenharmony_ci airo_config_commit(dev, NULL, NULL, NULL); 501962306a36Sopenharmony_ci} 502062306a36Sopenharmony_ci 502162306a36Sopenharmony_cistatic const char *get_rmode(__le16 mode) 502262306a36Sopenharmony_ci{ 502362306a36Sopenharmony_ci switch(mode & RXMODE_MASK) { 502462306a36Sopenharmony_ci case RXMODE_RFMON: return "rfmon"; 502562306a36Sopenharmony_ci case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon"; 502662306a36Sopenharmony_ci case RXMODE_LANMON: return "lanmon"; 502762306a36Sopenharmony_ci } 502862306a36Sopenharmony_ci return "ESS"; 502962306a36Sopenharmony_ci} 503062306a36Sopenharmony_ci 503162306a36Sopenharmony_cistatic int proc_config_open(struct inode *inode, struct file *file) 503262306a36Sopenharmony_ci{ 503362306a36Sopenharmony_ci struct proc_data *data; 503462306a36Sopenharmony_ci struct net_device *dev = pde_data(inode); 503562306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 503662306a36Sopenharmony_ci int i; 503762306a36Sopenharmony_ci __le16 mode; 503862306a36Sopenharmony_ci 503962306a36Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 504062306a36Sopenharmony_ci return -ENOMEM; 504162306a36Sopenharmony_ci data = file->private_data; 504262306a36Sopenharmony_ci if ((data->rbuffer = kmalloc(2048, GFP_KERNEL)) == NULL) { 504362306a36Sopenharmony_ci kfree (file->private_data); 504462306a36Sopenharmony_ci return -ENOMEM; 504562306a36Sopenharmony_ci } 504662306a36Sopenharmony_ci if ((data->wbuffer = kzalloc(2048, GFP_KERNEL)) == NULL) { 504762306a36Sopenharmony_ci kfree (data->rbuffer); 504862306a36Sopenharmony_ci kfree (file->private_data); 504962306a36Sopenharmony_ci return -ENOMEM; 505062306a36Sopenharmony_ci } 505162306a36Sopenharmony_ci data->maxwritelen = 2048; 505262306a36Sopenharmony_ci data->on_close = proc_config_on_close; 505362306a36Sopenharmony_ci 505462306a36Sopenharmony_ci readConfigRid(ai, 1); 505562306a36Sopenharmony_ci 505662306a36Sopenharmony_ci mode = ai->config.opmode & MODE_CFG_MASK; 505762306a36Sopenharmony_ci i = sprintf(data->rbuffer, 505862306a36Sopenharmony_ci "Mode: %s\n" 505962306a36Sopenharmony_ci "Radio: %s\n" 506062306a36Sopenharmony_ci "NodeName: %-16s\n" 506162306a36Sopenharmony_ci "PowerMode: %s\n" 506262306a36Sopenharmony_ci "DataRates: %d %d %d %d %d %d %d %d\n" 506362306a36Sopenharmony_ci "Channel: %d\n" 506462306a36Sopenharmony_ci "XmitPower: %d\n", 506562306a36Sopenharmony_ci mode == MODE_STA_IBSS ? "adhoc" : 506662306a36Sopenharmony_ci mode == MODE_STA_ESS ? get_rmode(ai->config.rmode): 506762306a36Sopenharmony_ci mode == MODE_AP ? "AP" : 506862306a36Sopenharmony_ci mode == MODE_AP_RPTR ? "AP RPTR" : "Error", 506962306a36Sopenharmony_ci test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on", 507062306a36Sopenharmony_ci ai->config.nodeName, 507162306a36Sopenharmony_ci ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" : 507262306a36Sopenharmony_ci ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" : 507362306a36Sopenharmony_ci ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" : 507462306a36Sopenharmony_ci "Error", 507562306a36Sopenharmony_ci (int)ai->config.rates[0], 507662306a36Sopenharmony_ci (int)ai->config.rates[1], 507762306a36Sopenharmony_ci (int)ai->config.rates[2], 507862306a36Sopenharmony_ci (int)ai->config.rates[3], 507962306a36Sopenharmony_ci (int)ai->config.rates[4], 508062306a36Sopenharmony_ci (int)ai->config.rates[5], 508162306a36Sopenharmony_ci (int)ai->config.rates[6], 508262306a36Sopenharmony_ci (int)ai->config.rates[7], 508362306a36Sopenharmony_ci le16_to_cpu(ai->config.channelSet), 508462306a36Sopenharmony_ci le16_to_cpu(ai->config.txPower) 508562306a36Sopenharmony_ci ); 508662306a36Sopenharmony_ci sprintf(data->rbuffer + i, 508762306a36Sopenharmony_ci "LongRetryLimit: %d\n" 508862306a36Sopenharmony_ci "ShortRetryLimit: %d\n" 508962306a36Sopenharmony_ci "RTSThreshold: %d\n" 509062306a36Sopenharmony_ci "TXMSDULifetime: %d\n" 509162306a36Sopenharmony_ci "RXMSDULifetime: %d\n" 509262306a36Sopenharmony_ci "TXDiversity: %s\n" 509362306a36Sopenharmony_ci "RXDiversity: %s\n" 509462306a36Sopenharmony_ci "FragThreshold: %d\n" 509562306a36Sopenharmony_ci "WEP: %s\n" 509662306a36Sopenharmony_ci "Modulation: %s\n" 509762306a36Sopenharmony_ci "Preamble: %s\n", 509862306a36Sopenharmony_ci le16_to_cpu(ai->config.longRetryLimit), 509962306a36Sopenharmony_ci le16_to_cpu(ai->config.shortRetryLimit), 510062306a36Sopenharmony_ci le16_to_cpu(ai->config.rtsThres), 510162306a36Sopenharmony_ci le16_to_cpu(ai->config.txLifetime), 510262306a36Sopenharmony_ci le16_to_cpu(ai->config.rxLifetime), 510362306a36Sopenharmony_ci ai->config.txDiversity == 1 ? "left" : 510462306a36Sopenharmony_ci ai->config.txDiversity == 2 ? "right" : "both", 510562306a36Sopenharmony_ci ai->config.rxDiversity == 1 ? "left" : 510662306a36Sopenharmony_ci ai->config.rxDiversity == 2 ? "right" : "both", 510762306a36Sopenharmony_ci le16_to_cpu(ai->config.fragThresh), 510862306a36Sopenharmony_ci ai->config.authType == AUTH_ENCRYPT ? "encrypt" : 510962306a36Sopenharmony_ci ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open", 511062306a36Sopenharmony_ci ai->config.modulation == MOD_DEFAULT ? "default" : 511162306a36Sopenharmony_ci ai->config.modulation == MOD_CCK ? "cck" : 511262306a36Sopenharmony_ci ai->config.modulation == MOD_MOK ? "mok" : "error", 511362306a36Sopenharmony_ci ai->config.preamble == PREAMBLE_AUTO ? "auto" : 511462306a36Sopenharmony_ci ai->config.preamble == PREAMBLE_LONG ? "long" : 511562306a36Sopenharmony_ci ai->config.preamble == PREAMBLE_SHORT ? "short" : "error" 511662306a36Sopenharmony_ci ); 511762306a36Sopenharmony_ci data->readlen = strlen(data->rbuffer); 511862306a36Sopenharmony_ci return 0; 511962306a36Sopenharmony_ci} 512062306a36Sopenharmony_ci 512162306a36Sopenharmony_cistatic void proc_SSID_on_close(struct inode *inode, struct file *file) 512262306a36Sopenharmony_ci{ 512362306a36Sopenharmony_ci struct proc_data *data = file->private_data; 512462306a36Sopenharmony_ci struct net_device *dev = pde_data(inode); 512562306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 512662306a36Sopenharmony_ci SsidRid SSID_rid; 512762306a36Sopenharmony_ci int i; 512862306a36Sopenharmony_ci char *p = data->wbuffer; 512962306a36Sopenharmony_ci char *end = p + data->writelen; 513062306a36Sopenharmony_ci 513162306a36Sopenharmony_ci if (!data->writelen) 513262306a36Sopenharmony_ci return; 513362306a36Sopenharmony_ci 513462306a36Sopenharmony_ci *end = '\n'; /* sentinel; we have space for it */ 513562306a36Sopenharmony_ci 513662306a36Sopenharmony_ci memset(&SSID_rid, 0, sizeof(SSID_rid)); 513762306a36Sopenharmony_ci 513862306a36Sopenharmony_ci for (i = 0; i < 3 && p < end; i++) { 513962306a36Sopenharmony_ci int j = 0; 514062306a36Sopenharmony_ci /* copy up to 32 characters from this line */ 514162306a36Sopenharmony_ci while (*p != '\n' && j < 32) 514262306a36Sopenharmony_ci SSID_rid.ssids[i].ssid[j++] = *p++; 514362306a36Sopenharmony_ci if (j == 0) 514462306a36Sopenharmony_ci break; 514562306a36Sopenharmony_ci SSID_rid.ssids[i].len = cpu_to_le16(j); 514662306a36Sopenharmony_ci /* skip to the beginning of the next line */ 514762306a36Sopenharmony_ci while (*p++ != '\n') 514862306a36Sopenharmony_ci ; 514962306a36Sopenharmony_ci } 515062306a36Sopenharmony_ci if (i) 515162306a36Sopenharmony_ci SSID_rid.len = cpu_to_le16(sizeof(SSID_rid)); 515262306a36Sopenharmony_ci disable_MAC(ai, 1); 515362306a36Sopenharmony_ci writeSsidRid(ai, &SSID_rid, 1); 515462306a36Sopenharmony_ci enable_MAC(ai, 1); 515562306a36Sopenharmony_ci} 515662306a36Sopenharmony_ci 515762306a36Sopenharmony_cistatic void proc_APList_on_close(struct inode *inode, struct file *file) 515862306a36Sopenharmony_ci{ 515962306a36Sopenharmony_ci struct proc_data *data = file->private_data; 516062306a36Sopenharmony_ci struct net_device *dev = pde_data(inode); 516162306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 516262306a36Sopenharmony_ci APListRid *APList_rid = &ai->APList; 516362306a36Sopenharmony_ci int i; 516462306a36Sopenharmony_ci 516562306a36Sopenharmony_ci if (!data->writelen) return; 516662306a36Sopenharmony_ci 516762306a36Sopenharmony_ci memset(APList_rid, 0, sizeof(*APList_rid)); 516862306a36Sopenharmony_ci APList_rid->len = cpu_to_le16(sizeof(*APList_rid)); 516962306a36Sopenharmony_ci 517062306a36Sopenharmony_ci for (i = 0; i < 4 && data->writelen >= (i + 1) * 6 * 3; i++) 517162306a36Sopenharmony_ci mac_pton(data->wbuffer + i * 6 * 3, APList_rid->ap[i]); 517262306a36Sopenharmony_ci 517362306a36Sopenharmony_ci disable_MAC(ai, 1); 517462306a36Sopenharmony_ci writeAPListRid(ai, APList_rid, 1); 517562306a36Sopenharmony_ci enable_MAC(ai, 1); 517662306a36Sopenharmony_ci} 517762306a36Sopenharmony_ci 517862306a36Sopenharmony_ci/* This function wraps PC4500_writerid with a MAC disable */ 517962306a36Sopenharmony_cistatic int do_writerid(struct airo_info *ai, u16 rid, const void *rid_data, 518062306a36Sopenharmony_ci int len, int dummy) 518162306a36Sopenharmony_ci{ 518262306a36Sopenharmony_ci int rc; 518362306a36Sopenharmony_ci 518462306a36Sopenharmony_ci disable_MAC(ai, 1); 518562306a36Sopenharmony_ci rc = PC4500_writerid(ai, rid, rid_data, len, 1); 518662306a36Sopenharmony_ci enable_MAC(ai, 1); 518762306a36Sopenharmony_ci return rc; 518862306a36Sopenharmony_ci} 518962306a36Sopenharmony_ci 519062306a36Sopenharmony_ci/* Returns the WEP key at the specified index, or -1 if that key does 519162306a36Sopenharmony_ci * not exist. The buffer is assumed to be at least 16 bytes in length. 519262306a36Sopenharmony_ci */ 519362306a36Sopenharmony_cistatic int get_wep_key(struct airo_info *ai, u16 index, char *buf, u16 buflen) 519462306a36Sopenharmony_ci{ 519562306a36Sopenharmony_ci WepKeyRid wkr; 519662306a36Sopenharmony_ci int rc; 519762306a36Sopenharmony_ci __le16 lastindex; 519862306a36Sopenharmony_ci 519962306a36Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 1, 1); 520062306a36Sopenharmony_ci if (rc != SUCCESS) 520162306a36Sopenharmony_ci return -1; 520262306a36Sopenharmony_ci do { 520362306a36Sopenharmony_ci lastindex = wkr.kindex; 520462306a36Sopenharmony_ci if (le16_to_cpu(wkr.kindex) == index) { 520562306a36Sopenharmony_ci int klen = min_t(int, buflen, le16_to_cpu(wkr.klen)); 520662306a36Sopenharmony_ci memcpy(buf, wkr.key, klen); 520762306a36Sopenharmony_ci return klen; 520862306a36Sopenharmony_ci } 520962306a36Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 0, 1); 521062306a36Sopenharmony_ci if (rc != SUCCESS) 521162306a36Sopenharmony_ci return -1; 521262306a36Sopenharmony_ci } while (lastindex != wkr.kindex); 521362306a36Sopenharmony_ci return -1; 521462306a36Sopenharmony_ci} 521562306a36Sopenharmony_ci 521662306a36Sopenharmony_cistatic int get_wep_tx_idx(struct airo_info *ai) 521762306a36Sopenharmony_ci{ 521862306a36Sopenharmony_ci WepKeyRid wkr; 521962306a36Sopenharmony_ci int rc; 522062306a36Sopenharmony_ci __le16 lastindex; 522162306a36Sopenharmony_ci 522262306a36Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 1, 1); 522362306a36Sopenharmony_ci if (rc != SUCCESS) 522462306a36Sopenharmony_ci return -1; 522562306a36Sopenharmony_ci do { 522662306a36Sopenharmony_ci lastindex = wkr.kindex; 522762306a36Sopenharmony_ci if (wkr.kindex == cpu_to_le16(0xffff)) 522862306a36Sopenharmony_ci return wkr.mac[0]; 522962306a36Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 0, 1); 523062306a36Sopenharmony_ci if (rc != SUCCESS) 523162306a36Sopenharmony_ci return -1; 523262306a36Sopenharmony_ci } while (lastindex != wkr.kindex); 523362306a36Sopenharmony_ci return -1; 523462306a36Sopenharmony_ci} 523562306a36Sopenharmony_ci 523662306a36Sopenharmony_cistatic int set_wep_key(struct airo_info *ai, u16 index, const u8 *key, 523762306a36Sopenharmony_ci u16 keylen, int perm, int lock) 523862306a36Sopenharmony_ci{ 523962306a36Sopenharmony_ci static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 }; 524062306a36Sopenharmony_ci WepKeyRid wkr; 524162306a36Sopenharmony_ci int rc; 524262306a36Sopenharmony_ci 524362306a36Sopenharmony_ci if (WARN_ON(keylen == 0)) 524462306a36Sopenharmony_ci return -1; 524562306a36Sopenharmony_ci 524662306a36Sopenharmony_ci memset(&wkr, 0, sizeof(wkr)); 524762306a36Sopenharmony_ci wkr.len = cpu_to_le16(sizeof(wkr)); 524862306a36Sopenharmony_ci wkr.kindex = cpu_to_le16(index); 524962306a36Sopenharmony_ci wkr.klen = cpu_to_le16(keylen); 525062306a36Sopenharmony_ci memcpy(wkr.key, key, keylen); 525162306a36Sopenharmony_ci memcpy(wkr.mac, macaddr, ETH_ALEN); 525262306a36Sopenharmony_ci 525362306a36Sopenharmony_ci if (perm) disable_MAC(ai, lock); 525462306a36Sopenharmony_ci rc = writeWepKeyRid(ai, &wkr, perm, lock); 525562306a36Sopenharmony_ci if (perm) enable_MAC(ai, lock); 525662306a36Sopenharmony_ci return rc; 525762306a36Sopenharmony_ci} 525862306a36Sopenharmony_ci 525962306a36Sopenharmony_cistatic int set_wep_tx_idx(struct airo_info *ai, u16 index, int perm, int lock) 526062306a36Sopenharmony_ci{ 526162306a36Sopenharmony_ci WepKeyRid wkr; 526262306a36Sopenharmony_ci int rc; 526362306a36Sopenharmony_ci 526462306a36Sopenharmony_ci memset(&wkr, 0, sizeof(wkr)); 526562306a36Sopenharmony_ci wkr.len = cpu_to_le16(sizeof(wkr)); 526662306a36Sopenharmony_ci wkr.kindex = cpu_to_le16(0xffff); 526762306a36Sopenharmony_ci wkr.mac[0] = (char)index; 526862306a36Sopenharmony_ci 526962306a36Sopenharmony_ci if (perm) { 527062306a36Sopenharmony_ci ai->defindex = (char)index; 527162306a36Sopenharmony_ci disable_MAC(ai, lock); 527262306a36Sopenharmony_ci } 527362306a36Sopenharmony_ci 527462306a36Sopenharmony_ci rc = writeWepKeyRid(ai, &wkr, perm, lock); 527562306a36Sopenharmony_ci 527662306a36Sopenharmony_ci if (perm) 527762306a36Sopenharmony_ci enable_MAC(ai, lock); 527862306a36Sopenharmony_ci return rc; 527962306a36Sopenharmony_ci} 528062306a36Sopenharmony_ci 528162306a36Sopenharmony_cistatic void proc_wepkey_on_close(struct inode *inode, struct file *file) 528262306a36Sopenharmony_ci{ 528362306a36Sopenharmony_ci struct proc_data *data; 528462306a36Sopenharmony_ci struct net_device *dev = pde_data(inode); 528562306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 528662306a36Sopenharmony_ci int i, rc; 528762306a36Sopenharmony_ci u8 key[16]; 528862306a36Sopenharmony_ci u16 index = 0; 528962306a36Sopenharmony_ci int j = 0; 529062306a36Sopenharmony_ci 529162306a36Sopenharmony_ci memset(key, 0, sizeof(key)); 529262306a36Sopenharmony_ci 529362306a36Sopenharmony_ci data = file->private_data; 529462306a36Sopenharmony_ci if (!data->writelen) return; 529562306a36Sopenharmony_ci 529662306a36Sopenharmony_ci if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' && 529762306a36Sopenharmony_ci (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) { 529862306a36Sopenharmony_ci index = data->wbuffer[0] - '0'; 529962306a36Sopenharmony_ci if (data->wbuffer[1] == '\n') { 530062306a36Sopenharmony_ci rc = set_wep_tx_idx(ai, index, 1, 1); 530162306a36Sopenharmony_ci if (rc < 0) { 530262306a36Sopenharmony_ci airo_print_err(ai->dev->name, "failed to set " 530362306a36Sopenharmony_ci "WEP transmit index to %d: %d.", 530462306a36Sopenharmony_ci index, rc); 530562306a36Sopenharmony_ci } 530662306a36Sopenharmony_ci return; 530762306a36Sopenharmony_ci } 530862306a36Sopenharmony_ci j = 2; 530962306a36Sopenharmony_ci } else { 531062306a36Sopenharmony_ci airo_print_err(ai->dev->name, "WepKey passed invalid key index"); 531162306a36Sopenharmony_ci return; 531262306a36Sopenharmony_ci } 531362306a36Sopenharmony_ci 531462306a36Sopenharmony_ci for (i = 0; i < 16*3 && data->wbuffer[i+j]; i++) { 531562306a36Sopenharmony_ci int val; 531662306a36Sopenharmony_ci 531762306a36Sopenharmony_ci if (i % 3 == 2) 531862306a36Sopenharmony_ci continue; 531962306a36Sopenharmony_ci 532062306a36Sopenharmony_ci val = hex_to_bin(data->wbuffer[i+j]); 532162306a36Sopenharmony_ci if (val < 0) { 532262306a36Sopenharmony_ci airo_print_err(ai->dev->name, "WebKey passed invalid key hex"); 532362306a36Sopenharmony_ci return; 532462306a36Sopenharmony_ci } 532562306a36Sopenharmony_ci switch(i%3) { 532662306a36Sopenharmony_ci case 0: 532762306a36Sopenharmony_ci key[i/3] = (u8)val << 4; 532862306a36Sopenharmony_ci break; 532962306a36Sopenharmony_ci case 1: 533062306a36Sopenharmony_ci key[i/3] |= (u8)val; 533162306a36Sopenharmony_ci break; 533262306a36Sopenharmony_ci } 533362306a36Sopenharmony_ci } 533462306a36Sopenharmony_ci 533562306a36Sopenharmony_ci rc = set_wep_key(ai, index, key, i/3, 1, 1); 533662306a36Sopenharmony_ci if (rc < 0) { 533762306a36Sopenharmony_ci airo_print_err(ai->dev->name, "failed to set WEP key at index " 533862306a36Sopenharmony_ci "%d: %d.", index, rc); 533962306a36Sopenharmony_ci } 534062306a36Sopenharmony_ci} 534162306a36Sopenharmony_ci 534262306a36Sopenharmony_cistatic int proc_wepkey_open(struct inode *inode, struct file *file) 534362306a36Sopenharmony_ci{ 534462306a36Sopenharmony_ci struct proc_data *data; 534562306a36Sopenharmony_ci struct net_device *dev = pde_data(inode); 534662306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 534762306a36Sopenharmony_ci char *ptr; 534862306a36Sopenharmony_ci WepKeyRid wkr; 534962306a36Sopenharmony_ci __le16 lastindex; 535062306a36Sopenharmony_ci int j = 0; 535162306a36Sopenharmony_ci int rc; 535262306a36Sopenharmony_ci 535362306a36Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 535462306a36Sopenharmony_ci return -ENOMEM; 535562306a36Sopenharmony_ci memset(&wkr, 0, sizeof(wkr)); 535662306a36Sopenharmony_ci data = file->private_data; 535762306a36Sopenharmony_ci if ((data->rbuffer = kzalloc(180, GFP_KERNEL)) == NULL) { 535862306a36Sopenharmony_ci kfree (file->private_data); 535962306a36Sopenharmony_ci return -ENOMEM; 536062306a36Sopenharmony_ci } 536162306a36Sopenharmony_ci data->writelen = 0; 536262306a36Sopenharmony_ci data->maxwritelen = 80; 536362306a36Sopenharmony_ci if ((data->wbuffer = kzalloc(80, GFP_KERNEL)) == NULL) { 536462306a36Sopenharmony_ci kfree (data->rbuffer); 536562306a36Sopenharmony_ci kfree (file->private_data); 536662306a36Sopenharmony_ci return -ENOMEM; 536762306a36Sopenharmony_ci } 536862306a36Sopenharmony_ci data->on_close = proc_wepkey_on_close; 536962306a36Sopenharmony_ci 537062306a36Sopenharmony_ci ptr = data->rbuffer; 537162306a36Sopenharmony_ci strcpy(ptr, "No wep keys\n"); 537262306a36Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 1, 1); 537362306a36Sopenharmony_ci if (rc == SUCCESS) do { 537462306a36Sopenharmony_ci lastindex = wkr.kindex; 537562306a36Sopenharmony_ci if (wkr.kindex == cpu_to_le16(0xffff)) { 537662306a36Sopenharmony_ci j += sprintf(ptr+j, "Tx key = %d\n", 537762306a36Sopenharmony_ci (int)wkr.mac[0]); 537862306a36Sopenharmony_ci } else { 537962306a36Sopenharmony_ci j += sprintf(ptr+j, "Key %d set with length = %d\n", 538062306a36Sopenharmony_ci le16_to_cpu(wkr.kindex), 538162306a36Sopenharmony_ci le16_to_cpu(wkr.klen)); 538262306a36Sopenharmony_ci } 538362306a36Sopenharmony_ci readWepKeyRid(ai, &wkr, 0, 1); 538462306a36Sopenharmony_ci } while ((lastindex != wkr.kindex) && (j < 180-30)); 538562306a36Sopenharmony_ci 538662306a36Sopenharmony_ci data->readlen = strlen(data->rbuffer); 538762306a36Sopenharmony_ci return 0; 538862306a36Sopenharmony_ci} 538962306a36Sopenharmony_ci 539062306a36Sopenharmony_cistatic int proc_SSID_open(struct inode *inode, struct file *file) 539162306a36Sopenharmony_ci{ 539262306a36Sopenharmony_ci struct proc_data *data; 539362306a36Sopenharmony_ci struct net_device *dev = pde_data(inode); 539462306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 539562306a36Sopenharmony_ci int i; 539662306a36Sopenharmony_ci char *ptr; 539762306a36Sopenharmony_ci SsidRid SSID_rid; 539862306a36Sopenharmony_ci 539962306a36Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 540062306a36Sopenharmony_ci return -ENOMEM; 540162306a36Sopenharmony_ci data = file->private_data; 540262306a36Sopenharmony_ci if ((data->rbuffer = kmalloc(104, GFP_KERNEL)) == NULL) { 540362306a36Sopenharmony_ci kfree (file->private_data); 540462306a36Sopenharmony_ci return -ENOMEM; 540562306a36Sopenharmony_ci } 540662306a36Sopenharmony_ci data->writelen = 0; 540762306a36Sopenharmony_ci data->maxwritelen = 33*3; 540862306a36Sopenharmony_ci /* allocate maxwritelen + 1; we'll want a sentinel */ 540962306a36Sopenharmony_ci if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) { 541062306a36Sopenharmony_ci kfree (data->rbuffer); 541162306a36Sopenharmony_ci kfree (file->private_data); 541262306a36Sopenharmony_ci return -ENOMEM; 541362306a36Sopenharmony_ci } 541462306a36Sopenharmony_ci data->on_close = proc_SSID_on_close; 541562306a36Sopenharmony_ci 541662306a36Sopenharmony_ci readSsidRid(ai, &SSID_rid); 541762306a36Sopenharmony_ci ptr = data->rbuffer; 541862306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 541962306a36Sopenharmony_ci int j; 542062306a36Sopenharmony_ci size_t len = le16_to_cpu(SSID_rid.ssids[i].len); 542162306a36Sopenharmony_ci if (!len) 542262306a36Sopenharmony_ci break; 542362306a36Sopenharmony_ci if (len > 32) 542462306a36Sopenharmony_ci len = 32; 542562306a36Sopenharmony_ci for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++) 542662306a36Sopenharmony_ci *ptr++ = SSID_rid.ssids[i].ssid[j]; 542762306a36Sopenharmony_ci *ptr++ = '\n'; 542862306a36Sopenharmony_ci } 542962306a36Sopenharmony_ci *ptr = '\0'; 543062306a36Sopenharmony_ci data->readlen = strlen(data->rbuffer); 543162306a36Sopenharmony_ci return 0; 543262306a36Sopenharmony_ci} 543362306a36Sopenharmony_ci 543462306a36Sopenharmony_cistatic int proc_APList_open(struct inode *inode, struct file *file) 543562306a36Sopenharmony_ci{ 543662306a36Sopenharmony_ci struct proc_data *data; 543762306a36Sopenharmony_ci struct net_device *dev = pde_data(inode); 543862306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 543962306a36Sopenharmony_ci int i; 544062306a36Sopenharmony_ci char *ptr; 544162306a36Sopenharmony_ci APListRid *APList_rid = &ai->APList; 544262306a36Sopenharmony_ci 544362306a36Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 544462306a36Sopenharmony_ci return -ENOMEM; 544562306a36Sopenharmony_ci data = file->private_data; 544662306a36Sopenharmony_ci if ((data->rbuffer = kmalloc(104, GFP_KERNEL)) == NULL) { 544762306a36Sopenharmony_ci kfree (file->private_data); 544862306a36Sopenharmony_ci return -ENOMEM; 544962306a36Sopenharmony_ci } 545062306a36Sopenharmony_ci data->writelen = 0; 545162306a36Sopenharmony_ci data->maxwritelen = 4*6*3; 545262306a36Sopenharmony_ci if ((data->wbuffer = kzalloc(data->maxwritelen, GFP_KERNEL)) == NULL) { 545362306a36Sopenharmony_ci kfree (data->rbuffer); 545462306a36Sopenharmony_ci kfree (file->private_data); 545562306a36Sopenharmony_ci return -ENOMEM; 545662306a36Sopenharmony_ci } 545762306a36Sopenharmony_ci data->on_close = proc_APList_on_close; 545862306a36Sopenharmony_ci 545962306a36Sopenharmony_ci ptr = data->rbuffer; 546062306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 546162306a36Sopenharmony_ci// We end when we find a zero MAC 546262306a36Sopenharmony_ci if (!*(int*)APList_rid->ap[i] && 546362306a36Sopenharmony_ci !*(int*)&APList_rid->ap[i][2]) break; 546462306a36Sopenharmony_ci ptr += sprintf(ptr, "%pM\n", APList_rid->ap[i]); 546562306a36Sopenharmony_ci } 546662306a36Sopenharmony_ci if (i==0) ptr += sprintf(ptr, "Not using specific APs\n"); 546762306a36Sopenharmony_ci 546862306a36Sopenharmony_ci *ptr = '\0'; 546962306a36Sopenharmony_ci data->readlen = strlen(data->rbuffer); 547062306a36Sopenharmony_ci return 0; 547162306a36Sopenharmony_ci} 547262306a36Sopenharmony_ci 547362306a36Sopenharmony_cistatic int proc_BSSList_open(struct inode *inode, struct file *file) 547462306a36Sopenharmony_ci{ 547562306a36Sopenharmony_ci struct proc_data *data; 547662306a36Sopenharmony_ci struct net_device *dev = pde_data(inode); 547762306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 547862306a36Sopenharmony_ci char *ptr; 547962306a36Sopenharmony_ci BSSListRid BSSList_rid; 548062306a36Sopenharmony_ci int rc; 548162306a36Sopenharmony_ci /* If doLoseSync is not 1, we won't do a Lose Sync */ 548262306a36Sopenharmony_ci int doLoseSync = -1; 548362306a36Sopenharmony_ci 548462306a36Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 548562306a36Sopenharmony_ci return -ENOMEM; 548662306a36Sopenharmony_ci data = file->private_data; 548762306a36Sopenharmony_ci if ((data->rbuffer = kmalloc(1024, GFP_KERNEL)) == NULL) { 548862306a36Sopenharmony_ci kfree (file->private_data); 548962306a36Sopenharmony_ci return -ENOMEM; 549062306a36Sopenharmony_ci } 549162306a36Sopenharmony_ci data->writelen = 0; 549262306a36Sopenharmony_ci data->maxwritelen = 0; 549362306a36Sopenharmony_ci data->wbuffer = NULL; 549462306a36Sopenharmony_ci data->on_close = NULL; 549562306a36Sopenharmony_ci 549662306a36Sopenharmony_ci if (file->f_mode & FMODE_WRITE) { 549762306a36Sopenharmony_ci if (!(file->f_mode & FMODE_READ)) { 549862306a36Sopenharmony_ci Cmd cmd; 549962306a36Sopenharmony_ci Resp rsp; 550062306a36Sopenharmony_ci 550162306a36Sopenharmony_ci if (ai->flags & FLAG_RADIO_MASK) { 550262306a36Sopenharmony_ci kfree(data->rbuffer); 550362306a36Sopenharmony_ci kfree(file->private_data); 550462306a36Sopenharmony_ci return -ENETDOWN; 550562306a36Sopenharmony_ci } 550662306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 550762306a36Sopenharmony_ci cmd.cmd = CMD_LISTBSS; 550862306a36Sopenharmony_ci if (down_interruptible(&ai->sem)) { 550962306a36Sopenharmony_ci kfree(data->rbuffer); 551062306a36Sopenharmony_ci kfree(file->private_data); 551162306a36Sopenharmony_ci return -ERESTARTSYS; 551262306a36Sopenharmony_ci } 551362306a36Sopenharmony_ci issuecommand(ai, &cmd, &rsp, true); 551462306a36Sopenharmony_ci up(&ai->sem); 551562306a36Sopenharmony_ci data->readlen = 0; 551662306a36Sopenharmony_ci return 0; 551762306a36Sopenharmony_ci } 551862306a36Sopenharmony_ci doLoseSync = 1; 551962306a36Sopenharmony_ci } 552062306a36Sopenharmony_ci ptr = data->rbuffer; 552162306a36Sopenharmony_ci /* There is a race condition here if there are concurrent opens. 552262306a36Sopenharmony_ci Since it is a rare condition, we'll just live with it, otherwise 552362306a36Sopenharmony_ci we have to add a spin lock... */ 552462306a36Sopenharmony_ci rc = readBSSListRid(ai, doLoseSync, &BSSList_rid); 552562306a36Sopenharmony_ci while (rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) { 552662306a36Sopenharmony_ci ptr += sprintf(ptr, "%pM %.*s rssi = %d", 552762306a36Sopenharmony_ci BSSList_rid.bssid, 552862306a36Sopenharmony_ci (int)BSSList_rid.ssidLen, 552962306a36Sopenharmony_ci BSSList_rid.ssid, 553062306a36Sopenharmony_ci le16_to_cpu(BSSList_rid.dBm)); 553162306a36Sopenharmony_ci ptr += sprintf(ptr, " channel = %d %s %s %s %s\n", 553262306a36Sopenharmony_ci le16_to_cpu(BSSList_rid.dsChannel), 553362306a36Sopenharmony_ci BSSList_rid.cap & CAP_ESS ? "ESS" : "", 553462306a36Sopenharmony_ci BSSList_rid.cap & CAP_IBSS ? "adhoc" : "", 553562306a36Sopenharmony_ci BSSList_rid.cap & CAP_PRIVACY ? "wep" : "", 553662306a36Sopenharmony_ci BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : ""); 553762306a36Sopenharmony_ci rc = readBSSListRid(ai, 0, &BSSList_rid); 553862306a36Sopenharmony_ci } 553962306a36Sopenharmony_ci *ptr = '\0'; 554062306a36Sopenharmony_ci data->readlen = strlen(data->rbuffer); 554162306a36Sopenharmony_ci return 0; 554262306a36Sopenharmony_ci} 554362306a36Sopenharmony_ci 554462306a36Sopenharmony_cistatic int proc_close(struct inode *inode, struct file *file) 554562306a36Sopenharmony_ci{ 554662306a36Sopenharmony_ci struct proc_data *data = file->private_data; 554762306a36Sopenharmony_ci 554862306a36Sopenharmony_ci if (data->on_close != NULL) 554962306a36Sopenharmony_ci data->on_close(inode, file); 555062306a36Sopenharmony_ci kfree(data->rbuffer); 555162306a36Sopenharmony_ci kfree(data->wbuffer); 555262306a36Sopenharmony_ci kfree(data); 555362306a36Sopenharmony_ci return 0; 555462306a36Sopenharmony_ci} 555562306a36Sopenharmony_ci 555662306a36Sopenharmony_ci/* Since the card doesn't automatically switch to the right WEP mode, 555762306a36Sopenharmony_ci we will make it do it. If the card isn't associated, every secs we 555862306a36Sopenharmony_ci will switch WEP modes to see if that will help. If the card is 555962306a36Sopenharmony_ci associated we will check every minute to see if anything has 556062306a36Sopenharmony_ci changed. */ 556162306a36Sopenharmony_cistatic void timer_func(struct net_device *dev) 556262306a36Sopenharmony_ci{ 556362306a36Sopenharmony_ci struct airo_info *apriv = dev->ml_priv; 556462306a36Sopenharmony_ci 556562306a36Sopenharmony_ci/* We don't have a link so try changing the authtype */ 556662306a36Sopenharmony_ci readConfigRid(apriv, 0); 556762306a36Sopenharmony_ci disable_MAC(apriv, 0); 556862306a36Sopenharmony_ci switch(apriv->config.authType) { 556962306a36Sopenharmony_ci case AUTH_ENCRYPT: 557062306a36Sopenharmony_ci/* So drop to OPEN */ 557162306a36Sopenharmony_ci apriv->config.authType = AUTH_OPEN; 557262306a36Sopenharmony_ci break; 557362306a36Sopenharmony_ci case AUTH_SHAREDKEY: 557462306a36Sopenharmony_ci if (apriv->keyindex < auto_wep) { 557562306a36Sopenharmony_ci set_wep_tx_idx(apriv, apriv->keyindex, 0, 0); 557662306a36Sopenharmony_ci apriv->config.authType = AUTH_SHAREDKEY; 557762306a36Sopenharmony_ci apriv->keyindex++; 557862306a36Sopenharmony_ci } else { 557962306a36Sopenharmony_ci /* Drop to ENCRYPT */ 558062306a36Sopenharmony_ci apriv->keyindex = 0; 558162306a36Sopenharmony_ci set_wep_tx_idx(apriv, apriv->defindex, 0, 0); 558262306a36Sopenharmony_ci apriv->config.authType = AUTH_ENCRYPT; 558362306a36Sopenharmony_ci } 558462306a36Sopenharmony_ci break; 558562306a36Sopenharmony_ci default: /* We'll escalate to SHAREDKEY */ 558662306a36Sopenharmony_ci apriv->config.authType = AUTH_SHAREDKEY; 558762306a36Sopenharmony_ci } 558862306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &apriv->flags); 558962306a36Sopenharmony_ci writeConfigRid(apriv, 0); 559062306a36Sopenharmony_ci enable_MAC(apriv, 0); 559162306a36Sopenharmony_ci up(&apriv->sem); 559262306a36Sopenharmony_ci 559362306a36Sopenharmony_ci/* Schedule check to see if the change worked */ 559462306a36Sopenharmony_ci clear_bit(JOB_AUTOWEP, &apriv->jobs); 559562306a36Sopenharmony_ci apriv->expires = RUN_AT(HZ*3); 559662306a36Sopenharmony_ci} 559762306a36Sopenharmony_ci 559862306a36Sopenharmony_ci#ifdef CONFIG_PCI 559962306a36Sopenharmony_cistatic int airo_pci_probe(struct pci_dev *pdev, 560062306a36Sopenharmony_ci const struct pci_device_id *pent) 560162306a36Sopenharmony_ci{ 560262306a36Sopenharmony_ci struct net_device *dev; 560362306a36Sopenharmony_ci 560462306a36Sopenharmony_ci if (pci_enable_device(pdev)) 560562306a36Sopenharmony_ci return -ENODEV; 560662306a36Sopenharmony_ci pci_set_master(pdev); 560762306a36Sopenharmony_ci 560862306a36Sopenharmony_ci if (pdev->device == 0x5000 || pdev->device == 0xa504) 560962306a36Sopenharmony_ci dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev); 561062306a36Sopenharmony_ci else 561162306a36Sopenharmony_ci dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev); 561262306a36Sopenharmony_ci if (!dev) { 561362306a36Sopenharmony_ci pci_disable_device(pdev); 561462306a36Sopenharmony_ci return -ENODEV; 561562306a36Sopenharmony_ci } 561662306a36Sopenharmony_ci 561762306a36Sopenharmony_ci pci_set_drvdata(pdev, dev); 561862306a36Sopenharmony_ci return 0; 561962306a36Sopenharmony_ci} 562062306a36Sopenharmony_ci 562162306a36Sopenharmony_cistatic void airo_pci_remove(struct pci_dev *pdev) 562262306a36Sopenharmony_ci{ 562362306a36Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 562462306a36Sopenharmony_ci 562562306a36Sopenharmony_ci airo_print_info(dev->name, "Unregistering..."); 562662306a36Sopenharmony_ci stop_airo_card(dev, 1); 562762306a36Sopenharmony_ci pci_disable_device(pdev); 562862306a36Sopenharmony_ci} 562962306a36Sopenharmony_ci 563062306a36Sopenharmony_cistatic int __maybe_unused airo_pci_suspend(struct device *dev_d) 563162306a36Sopenharmony_ci{ 563262306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 563362306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 563462306a36Sopenharmony_ci Cmd cmd; 563562306a36Sopenharmony_ci Resp rsp; 563662306a36Sopenharmony_ci 563762306a36Sopenharmony_ci if (!ai->SSID) 563862306a36Sopenharmony_ci ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL); 563962306a36Sopenharmony_ci if (!ai->SSID) 564062306a36Sopenharmony_ci return -ENOMEM; 564162306a36Sopenharmony_ci readSsidRid(ai, ai->SSID); 564262306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 564362306a36Sopenharmony_ci /* the lock will be released at the end of the resume callback */ 564462306a36Sopenharmony_ci if (down_interruptible(&ai->sem)) 564562306a36Sopenharmony_ci return -EAGAIN; 564662306a36Sopenharmony_ci disable_MAC(ai, 0); 564762306a36Sopenharmony_ci netif_device_detach(dev); 564862306a36Sopenharmony_ci ai->power = PMSG_SUSPEND; 564962306a36Sopenharmony_ci cmd.cmd = HOSTSLEEP; 565062306a36Sopenharmony_ci issuecommand(ai, &cmd, &rsp, true); 565162306a36Sopenharmony_ci 565262306a36Sopenharmony_ci device_wakeup_enable(dev_d); 565362306a36Sopenharmony_ci return 0; 565462306a36Sopenharmony_ci} 565562306a36Sopenharmony_ci 565662306a36Sopenharmony_cistatic int __maybe_unused airo_pci_resume(struct device *dev_d) 565762306a36Sopenharmony_ci{ 565862306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 565962306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 566062306a36Sopenharmony_ci pci_power_t prev_state = to_pci_dev(dev_d)->current_state; 566162306a36Sopenharmony_ci 566262306a36Sopenharmony_ci device_wakeup_disable(dev_d); 566362306a36Sopenharmony_ci 566462306a36Sopenharmony_ci if (prev_state != PCI_D1) { 566562306a36Sopenharmony_ci reset_card(dev, 0); 566662306a36Sopenharmony_ci mpi_init_descriptors(ai); 566762306a36Sopenharmony_ci setup_card(ai, dev, 0); 566862306a36Sopenharmony_ci clear_bit(FLAG_RADIO_OFF, &ai->flags); 566962306a36Sopenharmony_ci clear_bit(FLAG_PENDING_XMIT, &ai->flags); 567062306a36Sopenharmony_ci } else { 567162306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_AWAKEN); 567262306a36Sopenharmony_ci OUT4500(ai, EVACK, EV_AWAKEN); 567362306a36Sopenharmony_ci msleep(100); 567462306a36Sopenharmony_ci } 567562306a36Sopenharmony_ci 567662306a36Sopenharmony_ci set_bit(FLAG_COMMIT, &ai->flags); 567762306a36Sopenharmony_ci disable_MAC(ai, 0); 567862306a36Sopenharmony_ci msleep(200); 567962306a36Sopenharmony_ci if (ai->SSID) { 568062306a36Sopenharmony_ci writeSsidRid(ai, ai->SSID, 0); 568162306a36Sopenharmony_ci kfree(ai->SSID); 568262306a36Sopenharmony_ci ai->SSID = NULL; 568362306a36Sopenharmony_ci } 568462306a36Sopenharmony_ci writeAPListRid(ai, &ai->APList, 0); 568562306a36Sopenharmony_ci writeConfigRid(ai, 0); 568662306a36Sopenharmony_ci enable_MAC(ai, 0); 568762306a36Sopenharmony_ci ai->power = PMSG_ON; 568862306a36Sopenharmony_ci netif_device_attach(dev); 568962306a36Sopenharmony_ci netif_wake_queue(dev); 569062306a36Sopenharmony_ci enable_interrupts(ai); 569162306a36Sopenharmony_ci up(&ai->sem); 569262306a36Sopenharmony_ci return 0; 569362306a36Sopenharmony_ci} 569462306a36Sopenharmony_ci#endif 569562306a36Sopenharmony_ci 569662306a36Sopenharmony_cistatic int __init airo_init_module(void) 569762306a36Sopenharmony_ci{ 569862306a36Sopenharmony_ci int i; 569962306a36Sopenharmony_ci 570062306a36Sopenharmony_ci proc_kuid = make_kuid(&init_user_ns, proc_uid); 570162306a36Sopenharmony_ci proc_kgid = make_kgid(&init_user_ns, proc_gid); 570262306a36Sopenharmony_ci if (!uid_valid(proc_kuid) || !gid_valid(proc_kgid)) 570362306a36Sopenharmony_ci return -EINVAL; 570462306a36Sopenharmony_ci 570562306a36Sopenharmony_ci airo_entry = proc_mkdir_mode("driver/aironet", airo_perm, NULL); 570662306a36Sopenharmony_ci 570762306a36Sopenharmony_ci if (airo_entry) 570862306a36Sopenharmony_ci proc_set_user(airo_entry, proc_kuid, proc_kgid); 570962306a36Sopenharmony_ci 571062306a36Sopenharmony_ci for (i = 0; i < 4 && io[i] && irq[i]; i++) { 571162306a36Sopenharmony_ci airo_print_info("", "Trying to configure ISA adapter at irq=%d " 571262306a36Sopenharmony_ci "io = 0x%x", irq[i], io[i]); 571362306a36Sopenharmony_ci if (init_airo_card(irq[i], io[i], 0, NULL)) { 571462306a36Sopenharmony_ci /* do nothing */ ; 571562306a36Sopenharmony_ci } 571662306a36Sopenharmony_ci } 571762306a36Sopenharmony_ci 571862306a36Sopenharmony_ci#ifdef CONFIG_PCI 571962306a36Sopenharmony_ci airo_print_info("", "Probing for PCI adapters"); 572062306a36Sopenharmony_ci i = pci_register_driver(&airo_driver); 572162306a36Sopenharmony_ci airo_print_info("", "Finished probing for PCI adapters"); 572262306a36Sopenharmony_ci 572362306a36Sopenharmony_ci if (i) { 572462306a36Sopenharmony_ci remove_proc_entry("driver/aironet", NULL); 572562306a36Sopenharmony_ci return i; 572662306a36Sopenharmony_ci } 572762306a36Sopenharmony_ci#endif 572862306a36Sopenharmony_ci 572962306a36Sopenharmony_ci /* Always exit with success, as we are a library module 573062306a36Sopenharmony_ci * as well as a driver module 573162306a36Sopenharmony_ci */ 573262306a36Sopenharmony_ci return 0; 573362306a36Sopenharmony_ci} 573462306a36Sopenharmony_ci 573562306a36Sopenharmony_cistatic void __exit airo_cleanup_module(void) 573662306a36Sopenharmony_ci{ 573762306a36Sopenharmony_ci struct airo_info *ai; 573862306a36Sopenharmony_ci while (!list_empty(&airo_devices)) { 573962306a36Sopenharmony_ci ai = list_entry(airo_devices.next, struct airo_info, dev_list); 574062306a36Sopenharmony_ci airo_print_info(ai->dev->name, "Unregistering..."); 574162306a36Sopenharmony_ci stop_airo_card(ai->dev, 1); 574262306a36Sopenharmony_ci } 574362306a36Sopenharmony_ci#ifdef CONFIG_PCI 574462306a36Sopenharmony_ci pci_unregister_driver(&airo_driver); 574562306a36Sopenharmony_ci#endif 574662306a36Sopenharmony_ci remove_proc_entry("driver/aironet", NULL); 574762306a36Sopenharmony_ci} 574862306a36Sopenharmony_ci 574962306a36Sopenharmony_ci/* 575062306a36Sopenharmony_ci * Initial Wireless Extension code for Aironet driver by : 575162306a36Sopenharmony_ci * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00 575262306a36Sopenharmony_ci * Conversion to new driver API by : 575362306a36Sopenharmony_ci * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02 575462306a36Sopenharmony_ci * Javier also did a good amount of work here, adding some new extensions 575562306a36Sopenharmony_ci * and fixing my code. Let's just say that without him this code just 575662306a36Sopenharmony_ci * would not work at all... - Jean II 575762306a36Sopenharmony_ci */ 575862306a36Sopenharmony_ci 575962306a36Sopenharmony_cistatic u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi) 576062306a36Sopenharmony_ci{ 576162306a36Sopenharmony_ci if (!rssi_rid) 576262306a36Sopenharmony_ci return 0; 576362306a36Sopenharmony_ci 576462306a36Sopenharmony_ci return (0x100 - rssi_rid[rssi].rssidBm); 576562306a36Sopenharmony_ci} 576662306a36Sopenharmony_ci 576762306a36Sopenharmony_cistatic u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm) 576862306a36Sopenharmony_ci{ 576962306a36Sopenharmony_ci int i; 577062306a36Sopenharmony_ci 577162306a36Sopenharmony_ci if (!rssi_rid) 577262306a36Sopenharmony_ci return 0; 577362306a36Sopenharmony_ci 577462306a36Sopenharmony_ci for (i = 0; i < 256; i++) 577562306a36Sopenharmony_ci if (rssi_rid[i].rssidBm == dbm) 577662306a36Sopenharmony_ci return rssi_rid[i].rssipct; 577762306a36Sopenharmony_ci 577862306a36Sopenharmony_ci return 0; 577962306a36Sopenharmony_ci} 578062306a36Sopenharmony_ci 578162306a36Sopenharmony_ci 578262306a36Sopenharmony_cistatic int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid) 578362306a36Sopenharmony_ci{ 578462306a36Sopenharmony_ci int quality = 0; 578562306a36Sopenharmony_ci u16 sq; 578662306a36Sopenharmony_ci 578762306a36Sopenharmony_ci if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f)) 578862306a36Sopenharmony_ci return 0; 578962306a36Sopenharmony_ci 579062306a36Sopenharmony_ci if (!(cap_rid->hardCap & cpu_to_le16(8))) 579162306a36Sopenharmony_ci return 0; 579262306a36Sopenharmony_ci 579362306a36Sopenharmony_ci sq = le16_to_cpu(status_rid->signalQuality); 579462306a36Sopenharmony_ci if (memcmp(cap_rid->prodName, "350", 3)) 579562306a36Sopenharmony_ci if (sq > 0x20) 579662306a36Sopenharmony_ci quality = 0; 579762306a36Sopenharmony_ci else 579862306a36Sopenharmony_ci quality = 0x20 - sq; 579962306a36Sopenharmony_ci else 580062306a36Sopenharmony_ci if (sq > 0xb0) 580162306a36Sopenharmony_ci quality = 0; 580262306a36Sopenharmony_ci else if (sq < 0x10) 580362306a36Sopenharmony_ci quality = 0xa0; 580462306a36Sopenharmony_ci else 580562306a36Sopenharmony_ci quality = 0xb0 - sq; 580662306a36Sopenharmony_ci return quality; 580762306a36Sopenharmony_ci} 580862306a36Sopenharmony_ci 580962306a36Sopenharmony_ci#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0) 581062306a36Sopenharmony_ci#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50) 581162306a36Sopenharmony_ci 581262306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 581362306a36Sopenharmony_ci/* 581462306a36Sopenharmony_ci * Wireless Handler : get protocol name 581562306a36Sopenharmony_ci */ 581662306a36Sopenharmony_cistatic int airo_get_name(struct net_device *dev, 581762306a36Sopenharmony_ci struct iw_request_info *info, 581862306a36Sopenharmony_ci union iwreq_data *cwrq, 581962306a36Sopenharmony_ci char *extra) 582062306a36Sopenharmony_ci{ 582162306a36Sopenharmony_ci strcpy(cwrq->name, "IEEE 802.11-DS"); 582262306a36Sopenharmony_ci return 0; 582362306a36Sopenharmony_ci} 582462306a36Sopenharmony_ci 582562306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 582662306a36Sopenharmony_ci/* 582762306a36Sopenharmony_ci * Wireless Handler : set frequency 582862306a36Sopenharmony_ci */ 582962306a36Sopenharmony_cistatic int airo_set_freq(struct net_device *dev, 583062306a36Sopenharmony_ci struct iw_request_info *info, 583162306a36Sopenharmony_ci union iwreq_data *wrqu, 583262306a36Sopenharmony_ci char *extra) 583362306a36Sopenharmony_ci{ 583462306a36Sopenharmony_ci struct iw_freq *fwrq = &wrqu->freq; 583562306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 583662306a36Sopenharmony_ci int rc = -EINPROGRESS; /* Call commit handler */ 583762306a36Sopenharmony_ci 583862306a36Sopenharmony_ci /* If setting by frequency, convert to a channel */ 583962306a36Sopenharmony_ci if (fwrq->e == 1) { 584062306a36Sopenharmony_ci int f = fwrq->m / 100000; 584162306a36Sopenharmony_ci 584262306a36Sopenharmony_ci /* Hack to fall through... */ 584362306a36Sopenharmony_ci fwrq->e = 0; 584462306a36Sopenharmony_ci fwrq->m = ieee80211_frequency_to_channel(f); 584562306a36Sopenharmony_ci } 584662306a36Sopenharmony_ci /* Setting by channel number */ 584762306a36Sopenharmony_ci if (fwrq->m < 0 || fwrq->m > 1000 || fwrq->e > 0) 584862306a36Sopenharmony_ci rc = -EOPNOTSUPP; 584962306a36Sopenharmony_ci else { 585062306a36Sopenharmony_ci int channel = fwrq->m; 585162306a36Sopenharmony_ci /* We should do a better check than that, 585262306a36Sopenharmony_ci * based on the card capability !!! */ 585362306a36Sopenharmony_ci if ((channel < 1) || (channel > 14)) { 585462306a36Sopenharmony_ci airo_print_dbg(dev->name, "New channel value of %d is invalid!", 585562306a36Sopenharmony_ci fwrq->m); 585662306a36Sopenharmony_ci rc = -EINVAL; 585762306a36Sopenharmony_ci } else { 585862306a36Sopenharmony_ci readConfigRid(local, 1); 585962306a36Sopenharmony_ci /* Yes ! We can set it !!! */ 586062306a36Sopenharmony_ci local->config.channelSet = cpu_to_le16(channel); 586162306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 586262306a36Sopenharmony_ci } 586362306a36Sopenharmony_ci } 586462306a36Sopenharmony_ci return rc; 586562306a36Sopenharmony_ci} 586662306a36Sopenharmony_ci 586762306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 586862306a36Sopenharmony_ci/* 586962306a36Sopenharmony_ci * Wireless Handler : get frequency 587062306a36Sopenharmony_ci */ 587162306a36Sopenharmony_cistatic int airo_get_freq(struct net_device *dev, 587262306a36Sopenharmony_ci struct iw_request_info *info, 587362306a36Sopenharmony_ci union iwreq_data *wrqu, 587462306a36Sopenharmony_ci char *extra) 587562306a36Sopenharmony_ci{ 587662306a36Sopenharmony_ci struct iw_freq *fwrq = &wrqu->freq; 587762306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 587862306a36Sopenharmony_ci StatusRid status_rid; /* Card status info */ 587962306a36Sopenharmony_ci int ch; 588062306a36Sopenharmony_ci 588162306a36Sopenharmony_ci readConfigRid(local, 1); 588262306a36Sopenharmony_ci if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS) 588362306a36Sopenharmony_ci status_rid.channel = local->config.channelSet; 588462306a36Sopenharmony_ci else 588562306a36Sopenharmony_ci readStatusRid(local, &status_rid, 1); 588662306a36Sopenharmony_ci 588762306a36Sopenharmony_ci ch = le16_to_cpu(status_rid.channel); 588862306a36Sopenharmony_ci if ((ch > 0) && (ch < 15)) { 588962306a36Sopenharmony_ci fwrq->m = 100000 * 589062306a36Sopenharmony_ci ieee80211_channel_to_frequency(ch, NL80211_BAND_2GHZ); 589162306a36Sopenharmony_ci fwrq->e = 1; 589262306a36Sopenharmony_ci } else { 589362306a36Sopenharmony_ci fwrq->m = ch; 589462306a36Sopenharmony_ci fwrq->e = 0; 589562306a36Sopenharmony_ci } 589662306a36Sopenharmony_ci 589762306a36Sopenharmony_ci return 0; 589862306a36Sopenharmony_ci} 589962306a36Sopenharmony_ci 590062306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 590162306a36Sopenharmony_ci/* 590262306a36Sopenharmony_ci * Wireless Handler : set ESSID 590362306a36Sopenharmony_ci */ 590462306a36Sopenharmony_cistatic int airo_set_essid(struct net_device *dev, 590562306a36Sopenharmony_ci struct iw_request_info *info, 590662306a36Sopenharmony_ci union iwreq_data *wrqu, 590762306a36Sopenharmony_ci char *extra) 590862306a36Sopenharmony_ci{ 590962306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->essid; 591062306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 591162306a36Sopenharmony_ci SsidRid SSID_rid; /* SSIDs */ 591262306a36Sopenharmony_ci 591362306a36Sopenharmony_ci /* Reload the list of current SSID */ 591462306a36Sopenharmony_ci readSsidRid(local, &SSID_rid); 591562306a36Sopenharmony_ci 591662306a36Sopenharmony_ci /* Check if we asked for `any' */ 591762306a36Sopenharmony_ci if (dwrq->flags == 0) { 591862306a36Sopenharmony_ci /* Just send an empty SSID list */ 591962306a36Sopenharmony_ci memset(&SSID_rid, 0, sizeof(SSID_rid)); 592062306a36Sopenharmony_ci } else { 592162306a36Sopenharmony_ci unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 592262306a36Sopenharmony_ci 592362306a36Sopenharmony_ci /* Check the size of the string */ 592462306a36Sopenharmony_ci if (dwrq->length > IW_ESSID_MAX_SIZE) 592562306a36Sopenharmony_ci return -E2BIG ; 592662306a36Sopenharmony_ci 592762306a36Sopenharmony_ci /* Check if index is valid */ 592862306a36Sopenharmony_ci if (index >= ARRAY_SIZE(SSID_rid.ssids)) 592962306a36Sopenharmony_ci return -EINVAL; 593062306a36Sopenharmony_ci 593162306a36Sopenharmony_ci /* Set the SSID */ 593262306a36Sopenharmony_ci memset(SSID_rid.ssids[index].ssid, 0, 593362306a36Sopenharmony_ci sizeof(SSID_rid.ssids[index].ssid)); 593462306a36Sopenharmony_ci memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length); 593562306a36Sopenharmony_ci SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length); 593662306a36Sopenharmony_ci } 593762306a36Sopenharmony_ci SSID_rid.len = cpu_to_le16(sizeof(SSID_rid)); 593862306a36Sopenharmony_ci /* Write it to the card */ 593962306a36Sopenharmony_ci disable_MAC(local, 1); 594062306a36Sopenharmony_ci writeSsidRid(local, &SSID_rid, 1); 594162306a36Sopenharmony_ci enable_MAC(local, 1); 594262306a36Sopenharmony_ci 594362306a36Sopenharmony_ci return 0; 594462306a36Sopenharmony_ci} 594562306a36Sopenharmony_ci 594662306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 594762306a36Sopenharmony_ci/* 594862306a36Sopenharmony_ci * Wireless Handler : get ESSID 594962306a36Sopenharmony_ci */ 595062306a36Sopenharmony_cistatic int airo_get_essid(struct net_device *dev, 595162306a36Sopenharmony_ci struct iw_request_info *info, 595262306a36Sopenharmony_ci union iwreq_data *wrqu, 595362306a36Sopenharmony_ci char *extra) 595462306a36Sopenharmony_ci{ 595562306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->essid; 595662306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 595762306a36Sopenharmony_ci StatusRid status_rid; /* Card status info */ 595862306a36Sopenharmony_ci 595962306a36Sopenharmony_ci readStatusRid(local, &status_rid, 1); 596062306a36Sopenharmony_ci 596162306a36Sopenharmony_ci /* Note : if dwrq->flags != 0, we should 596262306a36Sopenharmony_ci * get the relevant SSID from the SSID list... */ 596362306a36Sopenharmony_ci 596462306a36Sopenharmony_ci /* Get the current SSID */ 596562306a36Sopenharmony_ci memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen)); 596662306a36Sopenharmony_ci /* If none, we may want to get the one that was set */ 596762306a36Sopenharmony_ci 596862306a36Sopenharmony_ci /* Push it out ! */ 596962306a36Sopenharmony_ci dwrq->length = le16_to_cpu(status_rid.SSIDlen); 597062306a36Sopenharmony_ci dwrq->flags = 1; /* active */ 597162306a36Sopenharmony_ci 597262306a36Sopenharmony_ci return 0; 597362306a36Sopenharmony_ci} 597462306a36Sopenharmony_ci 597562306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 597662306a36Sopenharmony_ci/* 597762306a36Sopenharmony_ci * Wireless Handler : set AP address 597862306a36Sopenharmony_ci */ 597962306a36Sopenharmony_cistatic int airo_set_wap(struct net_device *dev, 598062306a36Sopenharmony_ci struct iw_request_info *info, 598162306a36Sopenharmony_ci union iwreq_data *wrqu, 598262306a36Sopenharmony_ci char *extra) 598362306a36Sopenharmony_ci{ 598462306a36Sopenharmony_ci struct sockaddr *awrq = &wrqu->ap_addr; 598562306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 598662306a36Sopenharmony_ci Cmd cmd; 598762306a36Sopenharmony_ci Resp rsp; 598862306a36Sopenharmony_ci APListRid *APList_rid = &local->APList; 598962306a36Sopenharmony_ci 599062306a36Sopenharmony_ci if (awrq->sa_family != ARPHRD_ETHER) 599162306a36Sopenharmony_ci return -EINVAL; 599262306a36Sopenharmony_ci else if (is_broadcast_ether_addr(awrq->sa_data) || 599362306a36Sopenharmony_ci is_zero_ether_addr(awrq->sa_data)) { 599462306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 599562306a36Sopenharmony_ci cmd.cmd = CMD_LOSE_SYNC; 599662306a36Sopenharmony_ci if (down_interruptible(&local->sem)) 599762306a36Sopenharmony_ci return -ERESTARTSYS; 599862306a36Sopenharmony_ci issuecommand(local, &cmd, &rsp, true); 599962306a36Sopenharmony_ci up(&local->sem); 600062306a36Sopenharmony_ci } else { 600162306a36Sopenharmony_ci memset(APList_rid, 0, sizeof(*APList_rid)); 600262306a36Sopenharmony_ci APList_rid->len = cpu_to_le16(sizeof(*APList_rid)); 600362306a36Sopenharmony_ci memcpy(APList_rid->ap[0], awrq->sa_data, ETH_ALEN); 600462306a36Sopenharmony_ci disable_MAC(local, 1); 600562306a36Sopenharmony_ci writeAPListRid(local, APList_rid, 1); 600662306a36Sopenharmony_ci enable_MAC(local, 1); 600762306a36Sopenharmony_ci } 600862306a36Sopenharmony_ci return 0; 600962306a36Sopenharmony_ci} 601062306a36Sopenharmony_ci 601162306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 601262306a36Sopenharmony_ci/* 601362306a36Sopenharmony_ci * Wireless Handler : get AP address 601462306a36Sopenharmony_ci */ 601562306a36Sopenharmony_cistatic int airo_get_wap(struct net_device *dev, 601662306a36Sopenharmony_ci struct iw_request_info *info, 601762306a36Sopenharmony_ci union iwreq_data *wrqu, 601862306a36Sopenharmony_ci char *extra) 601962306a36Sopenharmony_ci{ 602062306a36Sopenharmony_ci struct sockaddr *awrq = &wrqu->ap_addr; 602162306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 602262306a36Sopenharmony_ci StatusRid status_rid; /* Card status info */ 602362306a36Sopenharmony_ci 602462306a36Sopenharmony_ci readStatusRid(local, &status_rid, 1); 602562306a36Sopenharmony_ci 602662306a36Sopenharmony_ci /* Tentative. This seems to work, wow, I'm lucky !!! */ 602762306a36Sopenharmony_ci memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN); 602862306a36Sopenharmony_ci awrq->sa_family = ARPHRD_ETHER; 602962306a36Sopenharmony_ci 603062306a36Sopenharmony_ci return 0; 603162306a36Sopenharmony_ci} 603262306a36Sopenharmony_ci 603362306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 603462306a36Sopenharmony_ci/* 603562306a36Sopenharmony_ci * Wireless Handler : set Nickname 603662306a36Sopenharmony_ci */ 603762306a36Sopenharmony_cistatic int airo_set_nick(struct net_device *dev, 603862306a36Sopenharmony_ci struct iw_request_info *info, 603962306a36Sopenharmony_ci union iwreq_data *wrqu, 604062306a36Sopenharmony_ci char *extra) 604162306a36Sopenharmony_ci{ 604262306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->data; 604362306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 604462306a36Sopenharmony_ci 604562306a36Sopenharmony_ci /* Check the size of the string */ 604662306a36Sopenharmony_ci if (dwrq->length > 16) { 604762306a36Sopenharmony_ci return -E2BIG; 604862306a36Sopenharmony_ci } 604962306a36Sopenharmony_ci readConfigRid(local, 1); 605062306a36Sopenharmony_ci memset(local->config.nodeName, 0, sizeof(local->config.nodeName)); 605162306a36Sopenharmony_ci memcpy(local->config.nodeName, extra, dwrq->length); 605262306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 605362306a36Sopenharmony_ci 605462306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 605562306a36Sopenharmony_ci} 605662306a36Sopenharmony_ci 605762306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 605862306a36Sopenharmony_ci/* 605962306a36Sopenharmony_ci * Wireless Handler : get Nickname 606062306a36Sopenharmony_ci */ 606162306a36Sopenharmony_cistatic int airo_get_nick(struct net_device *dev, 606262306a36Sopenharmony_ci struct iw_request_info *info, 606362306a36Sopenharmony_ci union iwreq_data *wrqu, 606462306a36Sopenharmony_ci char *extra) 606562306a36Sopenharmony_ci{ 606662306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->data; 606762306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 606862306a36Sopenharmony_ci 606962306a36Sopenharmony_ci readConfigRid(local, 1); 607062306a36Sopenharmony_ci strncpy(extra, local->config.nodeName, 16); 607162306a36Sopenharmony_ci extra[16] = '\0'; 607262306a36Sopenharmony_ci dwrq->length = strlen(extra); 607362306a36Sopenharmony_ci 607462306a36Sopenharmony_ci return 0; 607562306a36Sopenharmony_ci} 607662306a36Sopenharmony_ci 607762306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 607862306a36Sopenharmony_ci/* 607962306a36Sopenharmony_ci * Wireless Handler : set Bit-Rate 608062306a36Sopenharmony_ci */ 608162306a36Sopenharmony_cistatic int airo_set_rate(struct net_device *dev, 608262306a36Sopenharmony_ci struct iw_request_info *info, 608362306a36Sopenharmony_ci union iwreq_data *wrqu, 608462306a36Sopenharmony_ci char *extra) 608562306a36Sopenharmony_ci{ 608662306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->bitrate; 608762306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 608862306a36Sopenharmony_ci CapabilityRid cap_rid; /* Card capability info */ 608962306a36Sopenharmony_ci u8 brate = 0; 609062306a36Sopenharmony_ci int i; 609162306a36Sopenharmony_ci 609262306a36Sopenharmony_ci /* First : get a valid bit rate value */ 609362306a36Sopenharmony_ci readCapabilityRid(local, &cap_rid, 1); 609462306a36Sopenharmony_ci 609562306a36Sopenharmony_ci /* Which type of value ? */ 609662306a36Sopenharmony_ci if ((vwrq->value < 8) && (vwrq->value >= 0)) { 609762306a36Sopenharmony_ci /* Setting by rate index */ 609862306a36Sopenharmony_ci /* Find value in the magic rate table */ 609962306a36Sopenharmony_ci brate = cap_rid.supportedRates[vwrq->value]; 610062306a36Sopenharmony_ci } else { 610162306a36Sopenharmony_ci /* Setting by frequency value */ 610262306a36Sopenharmony_ci u8 normvalue = (u8) (vwrq->value/500000); 610362306a36Sopenharmony_ci 610462306a36Sopenharmony_ci /* Check if rate is valid */ 610562306a36Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 610662306a36Sopenharmony_ci if (normvalue == cap_rid.supportedRates[i]) { 610762306a36Sopenharmony_ci brate = normvalue; 610862306a36Sopenharmony_ci break; 610962306a36Sopenharmony_ci } 611062306a36Sopenharmony_ci } 611162306a36Sopenharmony_ci } 611262306a36Sopenharmony_ci /* -1 designed the max rate (mostly auto mode) */ 611362306a36Sopenharmony_ci if (vwrq->value == -1) { 611462306a36Sopenharmony_ci /* Get the highest available rate */ 611562306a36Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 611662306a36Sopenharmony_ci if (cap_rid.supportedRates[i] == 0) 611762306a36Sopenharmony_ci break; 611862306a36Sopenharmony_ci } 611962306a36Sopenharmony_ci if (i != 0) 612062306a36Sopenharmony_ci brate = cap_rid.supportedRates[i - 1]; 612162306a36Sopenharmony_ci } 612262306a36Sopenharmony_ci /* Check that it is valid */ 612362306a36Sopenharmony_ci if (brate == 0) { 612462306a36Sopenharmony_ci return -EINVAL; 612562306a36Sopenharmony_ci } 612662306a36Sopenharmony_ci 612762306a36Sopenharmony_ci readConfigRid(local, 1); 612862306a36Sopenharmony_ci /* Now, check if we want a fixed or auto value */ 612962306a36Sopenharmony_ci if (vwrq->fixed == 0) { 613062306a36Sopenharmony_ci /* Fill all the rates up to this max rate */ 613162306a36Sopenharmony_ci memset(local->config.rates, 0, 8); 613262306a36Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 613362306a36Sopenharmony_ci local->config.rates[i] = cap_rid.supportedRates[i]; 613462306a36Sopenharmony_ci if (local->config.rates[i] == brate) 613562306a36Sopenharmony_ci break; 613662306a36Sopenharmony_ci } 613762306a36Sopenharmony_ci } else { 613862306a36Sopenharmony_ci /* Fixed mode */ 613962306a36Sopenharmony_ci /* One rate, fixed */ 614062306a36Sopenharmony_ci memset(local->config.rates, 0, 8); 614162306a36Sopenharmony_ci local->config.rates[0] = brate; 614262306a36Sopenharmony_ci } 614362306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 614462306a36Sopenharmony_ci 614562306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 614662306a36Sopenharmony_ci} 614762306a36Sopenharmony_ci 614862306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 614962306a36Sopenharmony_ci/* 615062306a36Sopenharmony_ci * Wireless Handler : get Bit-Rate 615162306a36Sopenharmony_ci */ 615262306a36Sopenharmony_cistatic int airo_get_rate(struct net_device *dev, 615362306a36Sopenharmony_ci struct iw_request_info *info, 615462306a36Sopenharmony_ci union iwreq_data *wrqu, 615562306a36Sopenharmony_ci char *extra) 615662306a36Sopenharmony_ci{ 615762306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->bitrate; 615862306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 615962306a36Sopenharmony_ci StatusRid status_rid; /* Card status info */ 616062306a36Sopenharmony_ci int ret; 616162306a36Sopenharmony_ci 616262306a36Sopenharmony_ci ret = readStatusRid(local, &status_rid, 1); 616362306a36Sopenharmony_ci if (ret) 616462306a36Sopenharmony_ci return -EBUSY; 616562306a36Sopenharmony_ci 616662306a36Sopenharmony_ci vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000; 616762306a36Sopenharmony_ci /* If more than one rate, set auto */ 616862306a36Sopenharmony_ci readConfigRid(local, 1); 616962306a36Sopenharmony_ci vwrq->fixed = (local->config.rates[1] == 0); 617062306a36Sopenharmony_ci 617162306a36Sopenharmony_ci return 0; 617262306a36Sopenharmony_ci} 617362306a36Sopenharmony_ci 617462306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 617562306a36Sopenharmony_ci/* 617662306a36Sopenharmony_ci * Wireless Handler : set RTS threshold 617762306a36Sopenharmony_ci */ 617862306a36Sopenharmony_cistatic int airo_set_rts(struct net_device *dev, 617962306a36Sopenharmony_ci struct iw_request_info *info, 618062306a36Sopenharmony_ci union iwreq_data *wrqu, 618162306a36Sopenharmony_ci char *extra) 618262306a36Sopenharmony_ci{ 618362306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->rts; 618462306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 618562306a36Sopenharmony_ci int rthr = vwrq->value; 618662306a36Sopenharmony_ci 618762306a36Sopenharmony_ci if (vwrq->disabled) 618862306a36Sopenharmony_ci rthr = AIRO_DEF_MTU; 618962306a36Sopenharmony_ci if ((rthr < 0) || (rthr > AIRO_DEF_MTU)) { 619062306a36Sopenharmony_ci return -EINVAL; 619162306a36Sopenharmony_ci } 619262306a36Sopenharmony_ci readConfigRid(local, 1); 619362306a36Sopenharmony_ci local->config.rtsThres = cpu_to_le16(rthr); 619462306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 619562306a36Sopenharmony_ci 619662306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 619762306a36Sopenharmony_ci} 619862306a36Sopenharmony_ci 619962306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 620062306a36Sopenharmony_ci/* 620162306a36Sopenharmony_ci * Wireless Handler : get RTS threshold 620262306a36Sopenharmony_ci */ 620362306a36Sopenharmony_cistatic int airo_get_rts(struct net_device *dev, 620462306a36Sopenharmony_ci struct iw_request_info *info, 620562306a36Sopenharmony_ci union iwreq_data *wrqu, 620662306a36Sopenharmony_ci char *extra) 620762306a36Sopenharmony_ci{ 620862306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->rts; 620962306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 621062306a36Sopenharmony_ci 621162306a36Sopenharmony_ci readConfigRid(local, 1); 621262306a36Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.rtsThres); 621362306a36Sopenharmony_ci vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU); 621462306a36Sopenharmony_ci vwrq->fixed = 1; 621562306a36Sopenharmony_ci 621662306a36Sopenharmony_ci return 0; 621762306a36Sopenharmony_ci} 621862306a36Sopenharmony_ci 621962306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 622062306a36Sopenharmony_ci/* 622162306a36Sopenharmony_ci * Wireless Handler : set Fragmentation threshold 622262306a36Sopenharmony_ci */ 622362306a36Sopenharmony_cistatic int airo_set_frag(struct net_device *dev, 622462306a36Sopenharmony_ci struct iw_request_info *info, 622562306a36Sopenharmony_ci union iwreq_data *wrqu, char *extra) 622662306a36Sopenharmony_ci{ 622762306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->frag; 622862306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 622962306a36Sopenharmony_ci int fthr = vwrq->value; 623062306a36Sopenharmony_ci 623162306a36Sopenharmony_ci if (vwrq->disabled) 623262306a36Sopenharmony_ci fthr = AIRO_DEF_MTU; 623362306a36Sopenharmony_ci if ((fthr < 256) || (fthr > AIRO_DEF_MTU)) { 623462306a36Sopenharmony_ci return -EINVAL; 623562306a36Sopenharmony_ci } 623662306a36Sopenharmony_ci fthr &= ~0x1; /* Get an even value - is it really needed ??? */ 623762306a36Sopenharmony_ci readConfigRid(local, 1); 623862306a36Sopenharmony_ci local->config.fragThresh = cpu_to_le16(fthr); 623962306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 624062306a36Sopenharmony_ci 624162306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 624262306a36Sopenharmony_ci} 624362306a36Sopenharmony_ci 624462306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 624562306a36Sopenharmony_ci/* 624662306a36Sopenharmony_ci * Wireless Handler : get Fragmentation threshold 624762306a36Sopenharmony_ci */ 624862306a36Sopenharmony_cistatic int airo_get_frag(struct net_device *dev, 624962306a36Sopenharmony_ci struct iw_request_info *info, 625062306a36Sopenharmony_ci union iwreq_data *wrqu, 625162306a36Sopenharmony_ci char *extra) 625262306a36Sopenharmony_ci{ 625362306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->frag; 625462306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 625562306a36Sopenharmony_ci 625662306a36Sopenharmony_ci readConfigRid(local, 1); 625762306a36Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.fragThresh); 625862306a36Sopenharmony_ci vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU); 625962306a36Sopenharmony_ci vwrq->fixed = 1; 626062306a36Sopenharmony_ci 626162306a36Sopenharmony_ci return 0; 626262306a36Sopenharmony_ci} 626362306a36Sopenharmony_ci 626462306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 626562306a36Sopenharmony_ci/* 626662306a36Sopenharmony_ci * Wireless Handler : set Mode of Operation 626762306a36Sopenharmony_ci */ 626862306a36Sopenharmony_cistatic int airo_set_mode(struct net_device *dev, 626962306a36Sopenharmony_ci struct iw_request_info *info, 627062306a36Sopenharmony_ci union iwreq_data *uwrq, 627162306a36Sopenharmony_ci char *extra) 627262306a36Sopenharmony_ci{ 627362306a36Sopenharmony_ci __u32 mode = uwrq->mode; 627462306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 627562306a36Sopenharmony_ci int reset = 0; 627662306a36Sopenharmony_ci 627762306a36Sopenharmony_ci readConfigRid(local, 1); 627862306a36Sopenharmony_ci if (sniffing_mode(local)) 627962306a36Sopenharmony_ci reset = 1; 628062306a36Sopenharmony_ci 628162306a36Sopenharmony_ci switch (mode) { 628262306a36Sopenharmony_ci case IW_MODE_ADHOC: 628362306a36Sopenharmony_ci local->config.opmode &= ~MODE_CFG_MASK; 628462306a36Sopenharmony_ci local->config.opmode |= MODE_STA_IBSS; 628562306a36Sopenharmony_ci local->config.rmode &= ~RXMODE_FULL_MASK; 628662306a36Sopenharmony_ci local->config.scanMode = SCANMODE_ACTIVE; 628762306a36Sopenharmony_ci clear_bit (FLAG_802_11, &local->flags); 628862306a36Sopenharmony_ci break; 628962306a36Sopenharmony_ci case IW_MODE_INFRA: 629062306a36Sopenharmony_ci local->config.opmode &= ~MODE_CFG_MASK; 629162306a36Sopenharmony_ci local->config.opmode |= MODE_STA_ESS; 629262306a36Sopenharmony_ci local->config.rmode &= ~RXMODE_FULL_MASK; 629362306a36Sopenharmony_ci local->config.scanMode = SCANMODE_ACTIVE; 629462306a36Sopenharmony_ci clear_bit (FLAG_802_11, &local->flags); 629562306a36Sopenharmony_ci break; 629662306a36Sopenharmony_ci case IW_MODE_MASTER: 629762306a36Sopenharmony_ci local->config.opmode &= ~MODE_CFG_MASK; 629862306a36Sopenharmony_ci local->config.opmode |= MODE_AP; 629962306a36Sopenharmony_ci local->config.rmode &= ~RXMODE_FULL_MASK; 630062306a36Sopenharmony_ci local->config.scanMode = SCANMODE_ACTIVE; 630162306a36Sopenharmony_ci clear_bit (FLAG_802_11, &local->flags); 630262306a36Sopenharmony_ci break; 630362306a36Sopenharmony_ci case IW_MODE_REPEAT: 630462306a36Sopenharmony_ci local->config.opmode &= ~MODE_CFG_MASK; 630562306a36Sopenharmony_ci local->config.opmode |= MODE_AP_RPTR; 630662306a36Sopenharmony_ci local->config.rmode &= ~RXMODE_FULL_MASK; 630762306a36Sopenharmony_ci local->config.scanMode = SCANMODE_ACTIVE; 630862306a36Sopenharmony_ci clear_bit (FLAG_802_11, &local->flags); 630962306a36Sopenharmony_ci break; 631062306a36Sopenharmony_ci case IW_MODE_MONITOR: 631162306a36Sopenharmony_ci local->config.opmode &= ~MODE_CFG_MASK; 631262306a36Sopenharmony_ci local->config.opmode |= MODE_STA_ESS; 631362306a36Sopenharmony_ci local->config.rmode &= ~RXMODE_FULL_MASK; 631462306a36Sopenharmony_ci local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER; 631562306a36Sopenharmony_ci local->config.scanMode = SCANMODE_PASSIVE; 631662306a36Sopenharmony_ci set_bit (FLAG_802_11, &local->flags); 631762306a36Sopenharmony_ci break; 631862306a36Sopenharmony_ci default: 631962306a36Sopenharmony_ci return -EINVAL; 632062306a36Sopenharmony_ci } 632162306a36Sopenharmony_ci if (reset) 632262306a36Sopenharmony_ci set_bit (FLAG_RESET, &local->flags); 632362306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 632462306a36Sopenharmony_ci 632562306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 632662306a36Sopenharmony_ci} 632762306a36Sopenharmony_ci 632862306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 632962306a36Sopenharmony_ci/* 633062306a36Sopenharmony_ci * Wireless Handler : get Mode of Operation 633162306a36Sopenharmony_ci */ 633262306a36Sopenharmony_cistatic int airo_get_mode(struct net_device *dev, 633362306a36Sopenharmony_ci struct iw_request_info *info, 633462306a36Sopenharmony_ci union iwreq_data *uwrq, 633562306a36Sopenharmony_ci char *extra) 633662306a36Sopenharmony_ci{ 633762306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 633862306a36Sopenharmony_ci 633962306a36Sopenharmony_ci readConfigRid(local, 1); 634062306a36Sopenharmony_ci /* If not managed, assume it's ad-hoc */ 634162306a36Sopenharmony_ci switch (local->config.opmode & MODE_CFG_MASK) { 634262306a36Sopenharmony_ci case MODE_STA_ESS: 634362306a36Sopenharmony_ci uwrq->mode = IW_MODE_INFRA; 634462306a36Sopenharmony_ci break; 634562306a36Sopenharmony_ci case MODE_AP: 634662306a36Sopenharmony_ci uwrq->mode = IW_MODE_MASTER; 634762306a36Sopenharmony_ci break; 634862306a36Sopenharmony_ci case MODE_AP_RPTR: 634962306a36Sopenharmony_ci uwrq->mode = IW_MODE_REPEAT; 635062306a36Sopenharmony_ci break; 635162306a36Sopenharmony_ci default: 635262306a36Sopenharmony_ci uwrq->mode = IW_MODE_ADHOC; 635362306a36Sopenharmony_ci } 635462306a36Sopenharmony_ci 635562306a36Sopenharmony_ci return 0; 635662306a36Sopenharmony_ci} 635762306a36Sopenharmony_ci 635862306a36Sopenharmony_cistatic inline int valid_index(struct airo_info *ai, int index) 635962306a36Sopenharmony_ci{ 636062306a36Sopenharmony_ci return (index >= 0) && (index <= ai->max_wep_idx); 636162306a36Sopenharmony_ci} 636262306a36Sopenharmony_ci 636362306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 636462306a36Sopenharmony_ci/* 636562306a36Sopenharmony_ci * Wireless Handler : set Encryption Key 636662306a36Sopenharmony_ci */ 636762306a36Sopenharmony_cistatic int airo_set_encode(struct net_device *dev, 636862306a36Sopenharmony_ci struct iw_request_info *info, 636962306a36Sopenharmony_ci union iwreq_data *wrqu, 637062306a36Sopenharmony_ci char *extra) 637162306a36Sopenharmony_ci{ 637262306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->encoding; 637362306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 637462306a36Sopenharmony_ci int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1); 637562306a36Sopenharmony_ci __le16 currentAuthType = local->config.authType; 637662306a36Sopenharmony_ci int rc = 0; 637762306a36Sopenharmony_ci 637862306a36Sopenharmony_ci if (!local->wep_capable) 637962306a36Sopenharmony_ci return -EOPNOTSUPP; 638062306a36Sopenharmony_ci 638162306a36Sopenharmony_ci readConfigRid(local, 1); 638262306a36Sopenharmony_ci 638362306a36Sopenharmony_ci /* Basic checking: do we have a key to set ? 638462306a36Sopenharmony_ci * Note : with the new API, it's impossible to get a NULL pointer. 638562306a36Sopenharmony_ci * Therefore, we need to check a key size == 0 instead. 638662306a36Sopenharmony_ci * New version of iwconfig properly set the IW_ENCODE_NOKEY flag 638762306a36Sopenharmony_ci * when no key is present (only change flags), but older versions 638862306a36Sopenharmony_ci * don't do it. - Jean II */ 638962306a36Sopenharmony_ci if (dwrq->length > 0) { 639062306a36Sopenharmony_ci wep_key_t key; 639162306a36Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 639262306a36Sopenharmony_ci int current_index; 639362306a36Sopenharmony_ci 639462306a36Sopenharmony_ci /* Check the size of the key */ 639562306a36Sopenharmony_ci if (dwrq->length > MAX_KEY_SIZE) { 639662306a36Sopenharmony_ci return -EINVAL; 639762306a36Sopenharmony_ci } 639862306a36Sopenharmony_ci 639962306a36Sopenharmony_ci current_index = get_wep_tx_idx(local); 640062306a36Sopenharmony_ci if (current_index < 0) 640162306a36Sopenharmony_ci current_index = 0; 640262306a36Sopenharmony_ci 640362306a36Sopenharmony_ci /* Check the index (none -> use current) */ 640462306a36Sopenharmony_ci if (!valid_index(local, index)) 640562306a36Sopenharmony_ci index = current_index; 640662306a36Sopenharmony_ci 640762306a36Sopenharmony_ci /* Set the length */ 640862306a36Sopenharmony_ci if (dwrq->length > MIN_KEY_SIZE) 640962306a36Sopenharmony_ci key.len = MAX_KEY_SIZE; 641062306a36Sopenharmony_ci else 641162306a36Sopenharmony_ci key.len = MIN_KEY_SIZE; 641262306a36Sopenharmony_ci /* Check if the key is not marked as invalid */ 641362306a36Sopenharmony_ci if (!(dwrq->flags & IW_ENCODE_NOKEY)) { 641462306a36Sopenharmony_ci /* Cleanup */ 641562306a36Sopenharmony_ci memset(key.key, 0, MAX_KEY_SIZE); 641662306a36Sopenharmony_ci /* Copy the key in the driver */ 641762306a36Sopenharmony_ci memcpy(key.key, extra, dwrq->length); 641862306a36Sopenharmony_ci /* Send the key to the card */ 641962306a36Sopenharmony_ci rc = set_wep_key(local, index, key.key, key.len, perm, 1); 642062306a36Sopenharmony_ci if (rc < 0) { 642162306a36Sopenharmony_ci airo_print_err(local->dev->name, "failed to set" 642262306a36Sopenharmony_ci " WEP key at index %d: %d.", 642362306a36Sopenharmony_ci index, rc); 642462306a36Sopenharmony_ci return rc; 642562306a36Sopenharmony_ci } 642662306a36Sopenharmony_ci } 642762306a36Sopenharmony_ci /* WE specify that if a valid key is set, encryption 642862306a36Sopenharmony_ci * should be enabled (user may turn it off later) 642962306a36Sopenharmony_ci * This is also how "iwconfig ethX key on" works */ 643062306a36Sopenharmony_ci if ((index == current_index) && (key.len > 0) && 643162306a36Sopenharmony_ci (local->config.authType == AUTH_OPEN)) 643262306a36Sopenharmony_ci set_auth_type(local, AUTH_ENCRYPT); 643362306a36Sopenharmony_ci } else { 643462306a36Sopenharmony_ci /* Do we want to just set the transmit key index ? */ 643562306a36Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 643662306a36Sopenharmony_ci if (valid_index(local, index)) { 643762306a36Sopenharmony_ci rc = set_wep_tx_idx(local, index, perm, 1); 643862306a36Sopenharmony_ci if (rc < 0) { 643962306a36Sopenharmony_ci airo_print_err(local->dev->name, "failed to set" 644062306a36Sopenharmony_ci " WEP transmit index to %d: %d.", 644162306a36Sopenharmony_ci index, rc); 644262306a36Sopenharmony_ci return rc; 644362306a36Sopenharmony_ci } 644462306a36Sopenharmony_ci } else { 644562306a36Sopenharmony_ci /* Don't complain if only change the mode */ 644662306a36Sopenharmony_ci if (!(dwrq->flags & IW_ENCODE_MODE)) 644762306a36Sopenharmony_ci return -EINVAL; 644862306a36Sopenharmony_ci } 644962306a36Sopenharmony_ci } 645062306a36Sopenharmony_ci /* Read the flags */ 645162306a36Sopenharmony_ci if (dwrq->flags & IW_ENCODE_DISABLED) 645262306a36Sopenharmony_ci set_auth_type(local, AUTH_OPEN); /* disable encryption */ 645362306a36Sopenharmony_ci if (dwrq->flags & IW_ENCODE_RESTRICTED) 645462306a36Sopenharmony_ci set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ 645562306a36Sopenharmony_ci if (dwrq->flags & IW_ENCODE_OPEN) 645662306a36Sopenharmony_ci set_auth_type(local, AUTH_ENCRYPT); /* Only Wep */ 645762306a36Sopenharmony_ci /* Commit the changes to flags if needed */ 645862306a36Sopenharmony_ci if (local->config.authType != currentAuthType) 645962306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 646062306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 646162306a36Sopenharmony_ci} 646262306a36Sopenharmony_ci 646362306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 646462306a36Sopenharmony_ci/* 646562306a36Sopenharmony_ci * Wireless Handler : get Encryption Key 646662306a36Sopenharmony_ci */ 646762306a36Sopenharmony_cistatic int airo_get_encode(struct net_device *dev, 646862306a36Sopenharmony_ci struct iw_request_info *info, 646962306a36Sopenharmony_ci union iwreq_data *wrqu, 647062306a36Sopenharmony_ci char *extra) 647162306a36Sopenharmony_ci{ 647262306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->encoding; 647362306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 647462306a36Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 647562306a36Sopenharmony_ci int wep_key_len; 647662306a36Sopenharmony_ci u8 buf[16]; 647762306a36Sopenharmony_ci 647862306a36Sopenharmony_ci if (!local->wep_capable) 647962306a36Sopenharmony_ci return -EOPNOTSUPP; 648062306a36Sopenharmony_ci 648162306a36Sopenharmony_ci readConfigRid(local, 1); 648262306a36Sopenharmony_ci 648362306a36Sopenharmony_ci /* Check encryption mode */ 648462306a36Sopenharmony_ci switch(local->config.authType) { 648562306a36Sopenharmony_ci case AUTH_ENCRYPT: 648662306a36Sopenharmony_ci dwrq->flags = IW_ENCODE_OPEN; 648762306a36Sopenharmony_ci break; 648862306a36Sopenharmony_ci case AUTH_SHAREDKEY: 648962306a36Sopenharmony_ci dwrq->flags = IW_ENCODE_RESTRICTED; 649062306a36Sopenharmony_ci break; 649162306a36Sopenharmony_ci default: 649262306a36Sopenharmony_ci case AUTH_OPEN: 649362306a36Sopenharmony_ci dwrq->flags = IW_ENCODE_DISABLED; 649462306a36Sopenharmony_ci break; 649562306a36Sopenharmony_ci } 649662306a36Sopenharmony_ci /* We can't return the key, so set the proper flag and return zero */ 649762306a36Sopenharmony_ci dwrq->flags |= IW_ENCODE_NOKEY; 649862306a36Sopenharmony_ci memset(extra, 0, 16); 649962306a36Sopenharmony_ci 650062306a36Sopenharmony_ci /* Which key do we want ? -1 -> tx index */ 650162306a36Sopenharmony_ci if (!valid_index(local, index)) { 650262306a36Sopenharmony_ci index = get_wep_tx_idx(local); 650362306a36Sopenharmony_ci if (index < 0) 650462306a36Sopenharmony_ci index = 0; 650562306a36Sopenharmony_ci } 650662306a36Sopenharmony_ci dwrq->flags |= index + 1; 650762306a36Sopenharmony_ci 650862306a36Sopenharmony_ci /* Copy the key to the user buffer */ 650962306a36Sopenharmony_ci wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf)); 651062306a36Sopenharmony_ci if (wep_key_len < 0) { 651162306a36Sopenharmony_ci dwrq->length = 0; 651262306a36Sopenharmony_ci } else { 651362306a36Sopenharmony_ci dwrq->length = wep_key_len; 651462306a36Sopenharmony_ci memcpy(extra, buf, dwrq->length); 651562306a36Sopenharmony_ci } 651662306a36Sopenharmony_ci 651762306a36Sopenharmony_ci return 0; 651862306a36Sopenharmony_ci} 651962306a36Sopenharmony_ci 652062306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 652162306a36Sopenharmony_ci/* 652262306a36Sopenharmony_ci * Wireless Handler : set extended Encryption parameters 652362306a36Sopenharmony_ci */ 652462306a36Sopenharmony_cistatic int airo_set_encodeext(struct net_device *dev, 652562306a36Sopenharmony_ci struct iw_request_info *info, 652662306a36Sopenharmony_ci union iwreq_data *wrqu, 652762306a36Sopenharmony_ci char *extra) 652862306a36Sopenharmony_ci{ 652962306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 653062306a36Sopenharmony_ci struct iw_point *encoding = &wrqu->encoding; 653162306a36Sopenharmony_ci struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 653262306a36Sopenharmony_ci int perm = (encoding->flags & IW_ENCODE_TEMP ? 0 : 1); 653362306a36Sopenharmony_ci __le16 currentAuthType = local->config.authType; 653462306a36Sopenharmony_ci int idx, key_len, alg = ext->alg, set_key = 1, rc; 653562306a36Sopenharmony_ci wep_key_t key; 653662306a36Sopenharmony_ci 653762306a36Sopenharmony_ci if (!local->wep_capable) 653862306a36Sopenharmony_ci return -EOPNOTSUPP; 653962306a36Sopenharmony_ci 654062306a36Sopenharmony_ci readConfigRid(local, 1); 654162306a36Sopenharmony_ci 654262306a36Sopenharmony_ci /* Determine and validate the key index */ 654362306a36Sopenharmony_ci idx = encoding->flags & IW_ENCODE_INDEX; 654462306a36Sopenharmony_ci if (idx) { 654562306a36Sopenharmony_ci if (!valid_index(local, idx - 1)) 654662306a36Sopenharmony_ci return -EINVAL; 654762306a36Sopenharmony_ci idx--; 654862306a36Sopenharmony_ci } else { 654962306a36Sopenharmony_ci idx = get_wep_tx_idx(local); 655062306a36Sopenharmony_ci if (idx < 0) 655162306a36Sopenharmony_ci idx = 0; 655262306a36Sopenharmony_ci } 655362306a36Sopenharmony_ci 655462306a36Sopenharmony_ci if (encoding->flags & IW_ENCODE_DISABLED) 655562306a36Sopenharmony_ci alg = IW_ENCODE_ALG_NONE; 655662306a36Sopenharmony_ci 655762306a36Sopenharmony_ci if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { 655862306a36Sopenharmony_ci /* Only set transmit key index here, actual 655962306a36Sopenharmony_ci * key is set below if needed. 656062306a36Sopenharmony_ci */ 656162306a36Sopenharmony_ci rc = set_wep_tx_idx(local, idx, perm, 1); 656262306a36Sopenharmony_ci if (rc < 0) { 656362306a36Sopenharmony_ci airo_print_err(local->dev->name, "failed to set " 656462306a36Sopenharmony_ci "WEP transmit index to %d: %d.", 656562306a36Sopenharmony_ci idx, rc); 656662306a36Sopenharmony_ci return rc; 656762306a36Sopenharmony_ci } 656862306a36Sopenharmony_ci set_key = ext->key_len > 0 ? 1 : 0; 656962306a36Sopenharmony_ci } 657062306a36Sopenharmony_ci 657162306a36Sopenharmony_ci if (set_key) { 657262306a36Sopenharmony_ci /* Set the requested key first */ 657362306a36Sopenharmony_ci memset(key.key, 0, MAX_KEY_SIZE); 657462306a36Sopenharmony_ci switch (alg) { 657562306a36Sopenharmony_ci case IW_ENCODE_ALG_NONE: 657662306a36Sopenharmony_ci key.len = 0; 657762306a36Sopenharmony_ci break; 657862306a36Sopenharmony_ci case IW_ENCODE_ALG_WEP: 657962306a36Sopenharmony_ci if (ext->key_len > MIN_KEY_SIZE) { 658062306a36Sopenharmony_ci key.len = MAX_KEY_SIZE; 658162306a36Sopenharmony_ci } else if (ext->key_len > 0) { 658262306a36Sopenharmony_ci key.len = MIN_KEY_SIZE; 658362306a36Sopenharmony_ci } else { 658462306a36Sopenharmony_ci return -EINVAL; 658562306a36Sopenharmony_ci } 658662306a36Sopenharmony_ci key_len = min (ext->key_len, key.len); 658762306a36Sopenharmony_ci memcpy(key.key, ext->key, key_len); 658862306a36Sopenharmony_ci break; 658962306a36Sopenharmony_ci default: 659062306a36Sopenharmony_ci return -EINVAL; 659162306a36Sopenharmony_ci } 659262306a36Sopenharmony_ci if (key.len == 0) { 659362306a36Sopenharmony_ci rc = set_wep_tx_idx(local, idx, perm, 1); 659462306a36Sopenharmony_ci if (rc < 0) { 659562306a36Sopenharmony_ci airo_print_err(local->dev->name, 659662306a36Sopenharmony_ci "failed to set WEP transmit index to %d: %d.", 659762306a36Sopenharmony_ci idx, rc); 659862306a36Sopenharmony_ci return rc; 659962306a36Sopenharmony_ci } 660062306a36Sopenharmony_ci } else { 660162306a36Sopenharmony_ci rc = set_wep_key(local, idx, key.key, key.len, perm, 1); 660262306a36Sopenharmony_ci if (rc < 0) { 660362306a36Sopenharmony_ci airo_print_err(local->dev->name, 660462306a36Sopenharmony_ci "failed to set WEP key at index %d: %d.", 660562306a36Sopenharmony_ci idx, rc); 660662306a36Sopenharmony_ci return rc; 660762306a36Sopenharmony_ci } 660862306a36Sopenharmony_ci } 660962306a36Sopenharmony_ci } 661062306a36Sopenharmony_ci 661162306a36Sopenharmony_ci /* Read the flags */ 661262306a36Sopenharmony_ci if (encoding->flags & IW_ENCODE_DISABLED) 661362306a36Sopenharmony_ci set_auth_type(local, AUTH_OPEN); /* disable encryption */ 661462306a36Sopenharmony_ci if (encoding->flags & IW_ENCODE_RESTRICTED) 661562306a36Sopenharmony_ci set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ 661662306a36Sopenharmony_ci if (encoding->flags & IW_ENCODE_OPEN) 661762306a36Sopenharmony_ci set_auth_type(local, AUTH_ENCRYPT); 661862306a36Sopenharmony_ci /* Commit the changes to flags if needed */ 661962306a36Sopenharmony_ci if (local->config.authType != currentAuthType) 662062306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 662162306a36Sopenharmony_ci 662262306a36Sopenharmony_ci return -EINPROGRESS; 662362306a36Sopenharmony_ci} 662462306a36Sopenharmony_ci 662562306a36Sopenharmony_ci 662662306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 662762306a36Sopenharmony_ci/* 662862306a36Sopenharmony_ci * Wireless Handler : get extended Encryption parameters 662962306a36Sopenharmony_ci */ 663062306a36Sopenharmony_cistatic int airo_get_encodeext(struct net_device *dev, 663162306a36Sopenharmony_ci struct iw_request_info *info, 663262306a36Sopenharmony_ci union iwreq_data *wrqu, 663362306a36Sopenharmony_ci char *extra) 663462306a36Sopenharmony_ci{ 663562306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 663662306a36Sopenharmony_ci struct iw_point *encoding = &wrqu->encoding; 663762306a36Sopenharmony_ci struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 663862306a36Sopenharmony_ci int idx, max_key_len, wep_key_len; 663962306a36Sopenharmony_ci u8 buf[16]; 664062306a36Sopenharmony_ci 664162306a36Sopenharmony_ci if (!local->wep_capable) 664262306a36Sopenharmony_ci return -EOPNOTSUPP; 664362306a36Sopenharmony_ci 664462306a36Sopenharmony_ci readConfigRid(local, 1); 664562306a36Sopenharmony_ci 664662306a36Sopenharmony_ci max_key_len = encoding->length - sizeof(*ext); 664762306a36Sopenharmony_ci if (max_key_len < 0) 664862306a36Sopenharmony_ci return -EINVAL; 664962306a36Sopenharmony_ci 665062306a36Sopenharmony_ci idx = encoding->flags & IW_ENCODE_INDEX; 665162306a36Sopenharmony_ci if (idx) { 665262306a36Sopenharmony_ci if (!valid_index(local, idx - 1)) 665362306a36Sopenharmony_ci return -EINVAL; 665462306a36Sopenharmony_ci idx--; 665562306a36Sopenharmony_ci } else { 665662306a36Sopenharmony_ci idx = get_wep_tx_idx(local); 665762306a36Sopenharmony_ci if (idx < 0) 665862306a36Sopenharmony_ci idx = 0; 665962306a36Sopenharmony_ci } 666062306a36Sopenharmony_ci 666162306a36Sopenharmony_ci encoding->flags = idx + 1; 666262306a36Sopenharmony_ci memset(ext, 0, sizeof(*ext)); 666362306a36Sopenharmony_ci 666462306a36Sopenharmony_ci /* Check encryption mode */ 666562306a36Sopenharmony_ci switch(local->config.authType) { 666662306a36Sopenharmony_ci case AUTH_ENCRYPT: 666762306a36Sopenharmony_ci encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED; 666862306a36Sopenharmony_ci break; 666962306a36Sopenharmony_ci case AUTH_SHAREDKEY: 667062306a36Sopenharmony_ci encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED; 667162306a36Sopenharmony_ci break; 667262306a36Sopenharmony_ci default: 667362306a36Sopenharmony_ci case AUTH_OPEN: 667462306a36Sopenharmony_ci encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED; 667562306a36Sopenharmony_ci break; 667662306a36Sopenharmony_ci } 667762306a36Sopenharmony_ci /* We can't return the key, so set the proper flag and return zero */ 667862306a36Sopenharmony_ci encoding->flags |= IW_ENCODE_NOKEY; 667962306a36Sopenharmony_ci memset(extra, 0, 16); 668062306a36Sopenharmony_ci 668162306a36Sopenharmony_ci /* Copy the key to the user buffer */ 668262306a36Sopenharmony_ci wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf)); 668362306a36Sopenharmony_ci if (wep_key_len < 0) { 668462306a36Sopenharmony_ci ext->key_len = 0; 668562306a36Sopenharmony_ci } else { 668662306a36Sopenharmony_ci ext->key_len = wep_key_len; 668762306a36Sopenharmony_ci memcpy(extra, buf, ext->key_len); 668862306a36Sopenharmony_ci } 668962306a36Sopenharmony_ci 669062306a36Sopenharmony_ci return 0; 669162306a36Sopenharmony_ci} 669262306a36Sopenharmony_ci 669362306a36Sopenharmony_ci 669462306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 669562306a36Sopenharmony_ci/* 669662306a36Sopenharmony_ci * Wireless Handler : set extended authentication parameters 669762306a36Sopenharmony_ci */ 669862306a36Sopenharmony_cistatic int airo_set_auth(struct net_device *dev, 669962306a36Sopenharmony_ci struct iw_request_info *info, 670062306a36Sopenharmony_ci union iwreq_data *wrqu, char *extra) 670162306a36Sopenharmony_ci{ 670262306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 670362306a36Sopenharmony_ci struct iw_param *param = &wrqu->param; 670462306a36Sopenharmony_ci __le16 currentAuthType = local->config.authType; 670562306a36Sopenharmony_ci 670662306a36Sopenharmony_ci switch (param->flags & IW_AUTH_INDEX) { 670762306a36Sopenharmony_ci case IW_AUTH_WPA_VERSION: 670862306a36Sopenharmony_ci case IW_AUTH_CIPHER_PAIRWISE: 670962306a36Sopenharmony_ci case IW_AUTH_CIPHER_GROUP: 671062306a36Sopenharmony_ci case IW_AUTH_KEY_MGMT: 671162306a36Sopenharmony_ci case IW_AUTH_RX_UNENCRYPTED_EAPOL: 671262306a36Sopenharmony_ci case IW_AUTH_PRIVACY_INVOKED: 671362306a36Sopenharmony_ci /* 671462306a36Sopenharmony_ci * airo does not use these parameters 671562306a36Sopenharmony_ci */ 671662306a36Sopenharmony_ci break; 671762306a36Sopenharmony_ci 671862306a36Sopenharmony_ci case IW_AUTH_DROP_UNENCRYPTED: 671962306a36Sopenharmony_ci if (param->value) { 672062306a36Sopenharmony_ci /* Only change auth type if unencrypted */ 672162306a36Sopenharmony_ci if (currentAuthType == AUTH_OPEN) 672262306a36Sopenharmony_ci set_auth_type(local, AUTH_ENCRYPT); 672362306a36Sopenharmony_ci } else { 672462306a36Sopenharmony_ci set_auth_type(local, AUTH_OPEN); 672562306a36Sopenharmony_ci } 672662306a36Sopenharmony_ci 672762306a36Sopenharmony_ci /* Commit the changes to flags if needed */ 672862306a36Sopenharmony_ci if (local->config.authType != currentAuthType) 672962306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 673062306a36Sopenharmony_ci break; 673162306a36Sopenharmony_ci 673262306a36Sopenharmony_ci case IW_AUTH_80211_AUTH_ALG: { 673362306a36Sopenharmony_ci if (param->value & IW_AUTH_ALG_SHARED_KEY) { 673462306a36Sopenharmony_ci set_auth_type(local, AUTH_SHAREDKEY); 673562306a36Sopenharmony_ci } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { 673662306a36Sopenharmony_ci /* We don't know here if WEP open system or 673762306a36Sopenharmony_ci * unencrypted mode was requested - so use the 673862306a36Sopenharmony_ci * last mode (of these two) used last time 673962306a36Sopenharmony_ci */ 674062306a36Sopenharmony_ci set_auth_type(local, local->last_auth); 674162306a36Sopenharmony_ci } else 674262306a36Sopenharmony_ci return -EINVAL; 674362306a36Sopenharmony_ci 674462306a36Sopenharmony_ci /* Commit the changes to flags if needed */ 674562306a36Sopenharmony_ci if (local->config.authType != currentAuthType) 674662306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 674762306a36Sopenharmony_ci break; 674862306a36Sopenharmony_ci } 674962306a36Sopenharmony_ci 675062306a36Sopenharmony_ci case IW_AUTH_WPA_ENABLED: 675162306a36Sopenharmony_ci /* Silently accept disable of WPA */ 675262306a36Sopenharmony_ci if (param->value > 0) 675362306a36Sopenharmony_ci return -EOPNOTSUPP; 675462306a36Sopenharmony_ci break; 675562306a36Sopenharmony_ci 675662306a36Sopenharmony_ci default: 675762306a36Sopenharmony_ci return -EOPNOTSUPP; 675862306a36Sopenharmony_ci } 675962306a36Sopenharmony_ci return -EINPROGRESS; 676062306a36Sopenharmony_ci} 676162306a36Sopenharmony_ci 676262306a36Sopenharmony_ci 676362306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 676462306a36Sopenharmony_ci/* 676562306a36Sopenharmony_ci * Wireless Handler : get extended authentication parameters 676662306a36Sopenharmony_ci */ 676762306a36Sopenharmony_cistatic int airo_get_auth(struct net_device *dev, 676862306a36Sopenharmony_ci struct iw_request_info *info, 676962306a36Sopenharmony_ci union iwreq_data *wrqu, char *extra) 677062306a36Sopenharmony_ci{ 677162306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 677262306a36Sopenharmony_ci struct iw_param *param = &wrqu->param; 677362306a36Sopenharmony_ci __le16 currentAuthType = local->config.authType; 677462306a36Sopenharmony_ci 677562306a36Sopenharmony_ci switch (param->flags & IW_AUTH_INDEX) { 677662306a36Sopenharmony_ci case IW_AUTH_DROP_UNENCRYPTED: 677762306a36Sopenharmony_ci switch (currentAuthType) { 677862306a36Sopenharmony_ci case AUTH_SHAREDKEY: 677962306a36Sopenharmony_ci case AUTH_ENCRYPT: 678062306a36Sopenharmony_ci param->value = 1; 678162306a36Sopenharmony_ci break; 678262306a36Sopenharmony_ci default: 678362306a36Sopenharmony_ci param->value = 0; 678462306a36Sopenharmony_ci break; 678562306a36Sopenharmony_ci } 678662306a36Sopenharmony_ci break; 678762306a36Sopenharmony_ci 678862306a36Sopenharmony_ci case IW_AUTH_80211_AUTH_ALG: 678962306a36Sopenharmony_ci switch (currentAuthType) { 679062306a36Sopenharmony_ci case AUTH_SHAREDKEY: 679162306a36Sopenharmony_ci param->value = IW_AUTH_ALG_SHARED_KEY; 679262306a36Sopenharmony_ci break; 679362306a36Sopenharmony_ci case AUTH_ENCRYPT: 679462306a36Sopenharmony_ci default: 679562306a36Sopenharmony_ci param->value = IW_AUTH_ALG_OPEN_SYSTEM; 679662306a36Sopenharmony_ci break; 679762306a36Sopenharmony_ci } 679862306a36Sopenharmony_ci break; 679962306a36Sopenharmony_ci 680062306a36Sopenharmony_ci case IW_AUTH_WPA_ENABLED: 680162306a36Sopenharmony_ci param->value = 0; 680262306a36Sopenharmony_ci break; 680362306a36Sopenharmony_ci 680462306a36Sopenharmony_ci default: 680562306a36Sopenharmony_ci return -EOPNOTSUPP; 680662306a36Sopenharmony_ci } 680762306a36Sopenharmony_ci return 0; 680862306a36Sopenharmony_ci} 680962306a36Sopenharmony_ci 681062306a36Sopenharmony_ci 681162306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 681262306a36Sopenharmony_ci/* 681362306a36Sopenharmony_ci * Wireless Handler : set Tx-Power 681462306a36Sopenharmony_ci */ 681562306a36Sopenharmony_cistatic int airo_set_txpow(struct net_device *dev, 681662306a36Sopenharmony_ci struct iw_request_info *info, 681762306a36Sopenharmony_ci union iwreq_data *wrqu, 681862306a36Sopenharmony_ci char *extra) 681962306a36Sopenharmony_ci{ 682062306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->txpower; 682162306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 682262306a36Sopenharmony_ci CapabilityRid cap_rid; /* Card capability info */ 682362306a36Sopenharmony_ci int i; 682462306a36Sopenharmony_ci int rc = -EINVAL; 682562306a36Sopenharmony_ci __le16 v = cpu_to_le16(vwrq->value); 682662306a36Sopenharmony_ci 682762306a36Sopenharmony_ci readCapabilityRid(local, &cap_rid, 1); 682862306a36Sopenharmony_ci 682962306a36Sopenharmony_ci if (vwrq->disabled) { 683062306a36Sopenharmony_ci set_bit (FLAG_RADIO_OFF, &local->flags); 683162306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 683262306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 683362306a36Sopenharmony_ci } 683462306a36Sopenharmony_ci if (vwrq->flags != IW_TXPOW_MWATT) { 683562306a36Sopenharmony_ci return -EINVAL; 683662306a36Sopenharmony_ci } 683762306a36Sopenharmony_ci clear_bit (FLAG_RADIO_OFF, &local->flags); 683862306a36Sopenharmony_ci for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++) 683962306a36Sopenharmony_ci if (v == cap_rid.txPowerLevels[i]) { 684062306a36Sopenharmony_ci readConfigRid(local, 1); 684162306a36Sopenharmony_ci local->config.txPower = v; 684262306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 684362306a36Sopenharmony_ci rc = -EINPROGRESS; /* Call commit handler */ 684462306a36Sopenharmony_ci break; 684562306a36Sopenharmony_ci } 684662306a36Sopenharmony_ci return rc; 684762306a36Sopenharmony_ci} 684862306a36Sopenharmony_ci 684962306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 685062306a36Sopenharmony_ci/* 685162306a36Sopenharmony_ci * Wireless Handler : get Tx-Power 685262306a36Sopenharmony_ci */ 685362306a36Sopenharmony_cistatic int airo_get_txpow(struct net_device *dev, 685462306a36Sopenharmony_ci struct iw_request_info *info, 685562306a36Sopenharmony_ci union iwreq_data *wrqu, 685662306a36Sopenharmony_ci char *extra) 685762306a36Sopenharmony_ci{ 685862306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->txpower; 685962306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 686062306a36Sopenharmony_ci 686162306a36Sopenharmony_ci readConfigRid(local, 1); 686262306a36Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.txPower); 686362306a36Sopenharmony_ci vwrq->fixed = 1; /* No power control */ 686462306a36Sopenharmony_ci vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags); 686562306a36Sopenharmony_ci vwrq->flags = IW_TXPOW_MWATT; 686662306a36Sopenharmony_ci 686762306a36Sopenharmony_ci return 0; 686862306a36Sopenharmony_ci} 686962306a36Sopenharmony_ci 687062306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 687162306a36Sopenharmony_ci/* 687262306a36Sopenharmony_ci * Wireless Handler : set Retry limits 687362306a36Sopenharmony_ci */ 687462306a36Sopenharmony_cistatic int airo_set_retry(struct net_device *dev, 687562306a36Sopenharmony_ci struct iw_request_info *info, 687662306a36Sopenharmony_ci union iwreq_data *wrqu, 687762306a36Sopenharmony_ci char *extra) 687862306a36Sopenharmony_ci{ 687962306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->retry; 688062306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 688162306a36Sopenharmony_ci int rc = -EINVAL; 688262306a36Sopenharmony_ci 688362306a36Sopenharmony_ci if (vwrq->disabled) { 688462306a36Sopenharmony_ci return -EINVAL; 688562306a36Sopenharmony_ci } 688662306a36Sopenharmony_ci readConfigRid(local, 1); 688762306a36Sopenharmony_ci if (vwrq->flags & IW_RETRY_LIMIT) { 688862306a36Sopenharmony_ci __le16 v = cpu_to_le16(vwrq->value); 688962306a36Sopenharmony_ci if (vwrq->flags & IW_RETRY_LONG) 689062306a36Sopenharmony_ci local->config.longRetryLimit = v; 689162306a36Sopenharmony_ci else if (vwrq->flags & IW_RETRY_SHORT) 689262306a36Sopenharmony_ci local->config.shortRetryLimit = v; 689362306a36Sopenharmony_ci else { 689462306a36Sopenharmony_ci /* No modifier : set both */ 689562306a36Sopenharmony_ci local->config.longRetryLimit = v; 689662306a36Sopenharmony_ci local->config.shortRetryLimit = v; 689762306a36Sopenharmony_ci } 689862306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 689962306a36Sopenharmony_ci rc = -EINPROGRESS; /* Call commit handler */ 690062306a36Sopenharmony_ci } 690162306a36Sopenharmony_ci if (vwrq->flags & IW_RETRY_LIFETIME) { 690262306a36Sopenharmony_ci local->config.txLifetime = cpu_to_le16(vwrq->value / 1024); 690362306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 690462306a36Sopenharmony_ci rc = -EINPROGRESS; /* Call commit handler */ 690562306a36Sopenharmony_ci } 690662306a36Sopenharmony_ci return rc; 690762306a36Sopenharmony_ci} 690862306a36Sopenharmony_ci 690962306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 691062306a36Sopenharmony_ci/* 691162306a36Sopenharmony_ci * Wireless Handler : get Retry limits 691262306a36Sopenharmony_ci */ 691362306a36Sopenharmony_cistatic int airo_get_retry(struct net_device *dev, 691462306a36Sopenharmony_ci struct iw_request_info *info, 691562306a36Sopenharmony_ci union iwreq_data *wrqu, 691662306a36Sopenharmony_ci char *extra) 691762306a36Sopenharmony_ci{ 691862306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->retry; 691962306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 692062306a36Sopenharmony_ci 692162306a36Sopenharmony_ci vwrq->disabled = 0; /* Can't be disabled */ 692262306a36Sopenharmony_ci 692362306a36Sopenharmony_ci readConfigRid(local, 1); 692462306a36Sopenharmony_ci /* Note : by default, display the min retry number */ 692562306a36Sopenharmony_ci if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { 692662306a36Sopenharmony_ci vwrq->flags = IW_RETRY_LIFETIME; 692762306a36Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024; 692862306a36Sopenharmony_ci } else if ((vwrq->flags & IW_RETRY_LONG)) { 692962306a36Sopenharmony_ci vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; 693062306a36Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.longRetryLimit); 693162306a36Sopenharmony_ci } else { 693262306a36Sopenharmony_ci vwrq->flags = IW_RETRY_LIMIT; 693362306a36Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.shortRetryLimit); 693462306a36Sopenharmony_ci if (local->config.shortRetryLimit != local->config.longRetryLimit) 693562306a36Sopenharmony_ci vwrq->flags |= IW_RETRY_SHORT; 693662306a36Sopenharmony_ci } 693762306a36Sopenharmony_ci 693862306a36Sopenharmony_ci return 0; 693962306a36Sopenharmony_ci} 694062306a36Sopenharmony_ci 694162306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 694262306a36Sopenharmony_ci/* 694362306a36Sopenharmony_ci * Wireless Handler : get range info 694462306a36Sopenharmony_ci */ 694562306a36Sopenharmony_cistatic int airo_get_range(struct net_device *dev, 694662306a36Sopenharmony_ci struct iw_request_info *info, 694762306a36Sopenharmony_ci union iwreq_data *wrqu, 694862306a36Sopenharmony_ci char *extra) 694962306a36Sopenharmony_ci{ 695062306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->data; 695162306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 695262306a36Sopenharmony_ci struct iw_range *range = (struct iw_range *) extra; 695362306a36Sopenharmony_ci CapabilityRid cap_rid; /* Card capability info */ 695462306a36Sopenharmony_ci int i; 695562306a36Sopenharmony_ci int k; 695662306a36Sopenharmony_ci 695762306a36Sopenharmony_ci readCapabilityRid(local, &cap_rid, 1); 695862306a36Sopenharmony_ci 695962306a36Sopenharmony_ci dwrq->length = sizeof(struct iw_range); 696062306a36Sopenharmony_ci memset(range, 0, sizeof(*range)); 696162306a36Sopenharmony_ci range->min_nwid = 0x0000; 696262306a36Sopenharmony_ci range->max_nwid = 0x0000; 696362306a36Sopenharmony_ci range->num_channels = 14; 696462306a36Sopenharmony_ci /* Should be based on cap_rid.country to give only 696562306a36Sopenharmony_ci * what the current card support */ 696662306a36Sopenharmony_ci k = 0; 696762306a36Sopenharmony_ci for (i = 0; i < 14; i++) { 696862306a36Sopenharmony_ci range->freq[k].i = i + 1; /* List index */ 696962306a36Sopenharmony_ci range->freq[k].m = 100000 * 697062306a36Sopenharmony_ci ieee80211_channel_to_frequency(i + 1, NL80211_BAND_2GHZ); 697162306a36Sopenharmony_ci range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */ 697262306a36Sopenharmony_ci } 697362306a36Sopenharmony_ci range->num_frequency = k; 697462306a36Sopenharmony_ci 697562306a36Sopenharmony_ci range->sensitivity = 65535; 697662306a36Sopenharmony_ci 697762306a36Sopenharmony_ci /* Hum... Should put the right values there */ 697862306a36Sopenharmony_ci if (local->rssi) 697962306a36Sopenharmony_ci range->max_qual.qual = 100; /* % */ 698062306a36Sopenharmony_ci else 698162306a36Sopenharmony_ci range->max_qual.qual = airo_get_max_quality(&cap_rid); 698262306a36Sopenharmony_ci range->max_qual.level = 0x100 - 120; /* -120 dBm */ 698362306a36Sopenharmony_ci range->max_qual.noise = 0x100 - 120; /* -120 dBm */ 698462306a36Sopenharmony_ci 698562306a36Sopenharmony_ci /* Experimental measurements - boundary 11/5.5 Mb/s */ 698662306a36Sopenharmony_ci /* Note : with or without the (local->rssi), results 698762306a36Sopenharmony_ci * are somewhat different. - Jean II */ 698862306a36Sopenharmony_ci if (local->rssi) { 698962306a36Sopenharmony_ci range->avg_qual.qual = 50; /* % */ 699062306a36Sopenharmony_ci range->avg_qual.level = 0x100 - 70; /* -70 dBm */ 699162306a36Sopenharmony_ci } else { 699262306a36Sopenharmony_ci range->avg_qual.qual = airo_get_avg_quality(&cap_rid); 699362306a36Sopenharmony_ci range->avg_qual.level = 0x100 - 80; /* -80 dBm */ 699462306a36Sopenharmony_ci } 699562306a36Sopenharmony_ci range->avg_qual.noise = 0x100 - 85; /* -85 dBm */ 699662306a36Sopenharmony_ci 699762306a36Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 699862306a36Sopenharmony_ci range->bitrate[i] = cap_rid.supportedRates[i] * 500000; 699962306a36Sopenharmony_ci if (range->bitrate[i] == 0) 700062306a36Sopenharmony_ci break; 700162306a36Sopenharmony_ci } 700262306a36Sopenharmony_ci range->num_bitrates = i; 700362306a36Sopenharmony_ci 700462306a36Sopenharmony_ci /* Set an indication of the max TCP throughput 700562306a36Sopenharmony_ci * in bit/s that we can expect using this interface. 700662306a36Sopenharmony_ci * May be use for QoS stuff... Jean II */ 700762306a36Sopenharmony_ci if (i > 2) 700862306a36Sopenharmony_ci range->throughput = 5000 * 1000; 700962306a36Sopenharmony_ci else 701062306a36Sopenharmony_ci range->throughput = 1500 * 1000; 701162306a36Sopenharmony_ci 701262306a36Sopenharmony_ci range->min_rts = 0; 701362306a36Sopenharmony_ci range->max_rts = AIRO_DEF_MTU; 701462306a36Sopenharmony_ci range->min_frag = 256; 701562306a36Sopenharmony_ci range->max_frag = AIRO_DEF_MTU; 701662306a36Sopenharmony_ci 701762306a36Sopenharmony_ci if (cap_rid.softCap & cpu_to_le16(2)) { 701862306a36Sopenharmony_ci // WEP: RC4 40 bits 701962306a36Sopenharmony_ci range->encoding_size[0] = 5; 702062306a36Sopenharmony_ci // RC4 ~128 bits 702162306a36Sopenharmony_ci if (cap_rid.softCap & cpu_to_le16(0x100)) { 702262306a36Sopenharmony_ci range->encoding_size[1] = 13; 702362306a36Sopenharmony_ci range->num_encoding_sizes = 2; 702462306a36Sopenharmony_ci } else 702562306a36Sopenharmony_ci range->num_encoding_sizes = 1; 702662306a36Sopenharmony_ci range->max_encoding_tokens = 702762306a36Sopenharmony_ci cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1; 702862306a36Sopenharmony_ci } else { 702962306a36Sopenharmony_ci range->num_encoding_sizes = 0; 703062306a36Sopenharmony_ci range->max_encoding_tokens = 0; 703162306a36Sopenharmony_ci } 703262306a36Sopenharmony_ci range->min_pmp = 0; 703362306a36Sopenharmony_ci range->max_pmp = 5000000; /* 5 secs */ 703462306a36Sopenharmony_ci range->min_pmt = 0; 703562306a36Sopenharmony_ci range->max_pmt = 65535 * 1024; /* ??? */ 703662306a36Sopenharmony_ci range->pmp_flags = IW_POWER_PERIOD; 703762306a36Sopenharmony_ci range->pmt_flags = IW_POWER_TIMEOUT; 703862306a36Sopenharmony_ci range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; 703962306a36Sopenharmony_ci 704062306a36Sopenharmony_ci /* Transmit Power - values are in mW */ 704162306a36Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 704262306a36Sopenharmony_ci range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]); 704362306a36Sopenharmony_ci if (range->txpower[i] == 0) 704462306a36Sopenharmony_ci break; 704562306a36Sopenharmony_ci } 704662306a36Sopenharmony_ci range->num_txpower = i; 704762306a36Sopenharmony_ci range->txpower_capa = IW_TXPOW_MWATT; 704862306a36Sopenharmony_ci range->we_version_source = 19; 704962306a36Sopenharmony_ci range->we_version_compiled = WIRELESS_EXT; 705062306a36Sopenharmony_ci range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; 705162306a36Sopenharmony_ci range->retry_flags = IW_RETRY_LIMIT; 705262306a36Sopenharmony_ci range->r_time_flags = IW_RETRY_LIFETIME; 705362306a36Sopenharmony_ci range->min_retry = 1; 705462306a36Sopenharmony_ci range->max_retry = 65535; 705562306a36Sopenharmony_ci range->min_r_time = 1024; 705662306a36Sopenharmony_ci range->max_r_time = 65535 * 1024; 705762306a36Sopenharmony_ci 705862306a36Sopenharmony_ci /* Event capability (kernel + driver) */ 705962306a36Sopenharmony_ci range->event_capa[0] = (IW_EVENT_CAPA_K_0 | 706062306a36Sopenharmony_ci IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | 706162306a36Sopenharmony_ci IW_EVENT_CAPA_MASK(SIOCGIWAP) | 706262306a36Sopenharmony_ci IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); 706362306a36Sopenharmony_ci range->event_capa[1] = IW_EVENT_CAPA_K_1; 706462306a36Sopenharmony_ci range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP); 706562306a36Sopenharmony_ci return 0; 706662306a36Sopenharmony_ci} 706762306a36Sopenharmony_ci 706862306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 706962306a36Sopenharmony_ci/* 707062306a36Sopenharmony_ci * Wireless Handler : set Power Management 707162306a36Sopenharmony_ci */ 707262306a36Sopenharmony_cistatic int airo_set_power(struct net_device *dev, 707362306a36Sopenharmony_ci struct iw_request_info *info, 707462306a36Sopenharmony_ci union iwreq_data *wrqu, char *extra) 707562306a36Sopenharmony_ci{ 707662306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->power; 707762306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 707862306a36Sopenharmony_ci 707962306a36Sopenharmony_ci readConfigRid(local, 1); 708062306a36Sopenharmony_ci if (vwrq->disabled) { 708162306a36Sopenharmony_ci if (sniffing_mode(local)) 708262306a36Sopenharmony_ci return -EINVAL; 708362306a36Sopenharmony_ci local->config.powerSaveMode = POWERSAVE_CAM; 708462306a36Sopenharmony_ci local->config.rmode &= ~RXMODE_MASK; 708562306a36Sopenharmony_ci local->config.rmode |= RXMODE_BC_MC_ADDR; 708662306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 708762306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 708862306a36Sopenharmony_ci } 708962306a36Sopenharmony_ci if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { 709062306a36Sopenharmony_ci local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024); 709162306a36Sopenharmony_ci local->config.powerSaveMode = POWERSAVE_PSPCAM; 709262306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 709362306a36Sopenharmony_ci } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { 709462306a36Sopenharmony_ci local->config.fastListenInterval = 709562306a36Sopenharmony_ci local->config.listenInterval = 709662306a36Sopenharmony_ci cpu_to_le16((vwrq->value + 500) / 1024); 709762306a36Sopenharmony_ci local->config.powerSaveMode = POWERSAVE_PSPCAM; 709862306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 709962306a36Sopenharmony_ci } 710062306a36Sopenharmony_ci switch (vwrq->flags & IW_POWER_MODE) { 710162306a36Sopenharmony_ci case IW_POWER_UNICAST_R: 710262306a36Sopenharmony_ci if (sniffing_mode(local)) 710362306a36Sopenharmony_ci return -EINVAL; 710462306a36Sopenharmony_ci local->config.rmode &= ~RXMODE_MASK; 710562306a36Sopenharmony_ci local->config.rmode |= RXMODE_ADDR; 710662306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 710762306a36Sopenharmony_ci break; 710862306a36Sopenharmony_ci case IW_POWER_ALL_R: 710962306a36Sopenharmony_ci if (sniffing_mode(local)) 711062306a36Sopenharmony_ci return -EINVAL; 711162306a36Sopenharmony_ci local->config.rmode &= ~RXMODE_MASK; 711262306a36Sopenharmony_ci local->config.rmode |= RXMODE_BC_MC_ADDR; 711362306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 711462306a36Sopenharmony_ci break; 711562306a36Sopenharmony_ci case IW_POWER_ON: 711662306a36Sopenharmony_ci /* This is broken, fixme ;-) */ 711762306a36Sopenharmony_ci break; 711862306a36Sopenharmony_ci default: 711962306a36Sopenharmony_ci return -EINVAL; 712062306a36Sopenharmony_ci } 712162306a36Sopenharmony_ci // Note : we may want to factor local->need_commit here 712262306a36Sopenharmony_ci // Note2 : may also want to factor RXMODE_RFMON test 712362306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 712462306a36Sopenharmony_ci} 712562306a36Sopenharmony_ci 712662306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 712762306a36Sopenharmony_ci/* 712862306a36Sopenharmony_ci * Wireless Handler : get Power Management 712962306a36Sopenharmony_ci */ 713062306a36Sopenharmony_cistatic int airo_get_power(struct net_device *dev, 713162306a36Sopenharmony_ci struct iw_request_info *info, 713262306a36Sopenharmony_ci union iwreq_data *wrqu, 713362306a36Sopenharmony_ci char *extra) 713462306a36Sopenharmony_ci{ 713562306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->power; 713662306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 713762306a36Sopenharmony_ci __le16 mode; 713862306a36Sopenharmony_ci 713962306a36Sopenharmony_ci readConfigRid(local, 1); 714062306a36Sopenharmony_ci mode = local->config.powerSaveMode; 714162306a36Sopenharmony_ci if ((vwrq->disabled = (mode == POWERSAVE_CAM))) 714262306a36Sopenharmony_ci return 0; 714362306a36Sopenharmony_ci if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { 714462306a36Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024; 714562306a36Sopenharmony_ci vwrq->flags = IW_POWER_TIMEOUT; 714662306a36Sopenharmony_ci } else { 714762306a36Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024; 714862306a36Sopenharmony_ci vwrq->flags = IW_POWER_PERIOD; 714962306a36Sopenharmony_ci } 715062306a36Sopenharmony_ci if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR) 715162306a36Sopenharmony_ci vwrq->flags |= IW_POWER_UNICAST_R; 715262306a36Sopenharmony_ci else 715362306a36Sopenharmony_ci vwrq->flags |= IW_POWER_ALL_R; 715462306a36Sopenharmony_ci 715562306a36Sopenharmony_ci return 0; 715662306a36Sopenharmony_ci} 715762306a36Sopenharmony_ci 715862306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 715962306a36Sopenharmony_ci/* 716062306a36Sopenharmony_ci * Wireless Handler : set Sensitivity 716162306a36Sopenharmony_ci */ 716262306a36Sopenharmony_cistatic int airo_set_sens(struct net_device *dev, 716362306a36Sopenharmony_ci struct iw_request_info *info, 716462306a36Sopenharmony_ci union iwreq_data *wrqu, 716562306a36Sopenharmony_ci char *extra) 716662306a36Sopenharmony_ci{ 716762306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->sens; 716862306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 716962306a36Sopenharmony_ci 717062306a36Sopenharmony_ci readConfigRid(local, 1); 717162306a36Sopenharmony_ci local->config.rssiThreshold = 717262306a36Sopenharmony_ci cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value); 717362306a36Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 717462306a36Sopenharmony_ci 717562306a36Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 717662306a36Sopenharmony_ci} 717762306a36Sopenharmony_ci 717862306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 717962306a36Sopenharmony_ci/* 718062306a36Sopenharmony_ci * Wireless Handler : get Sensitivity 718162306a36Sopenharmony_ci */ 718262306a36Sopenharmony_cistatic int airo_get_sens(struct net_device *dev, 718362306a36Sopenharmony_ci struct iw_request_info *info, 718462306a36Sopenharmony_ci union iwreq_data *wrqu, 718562306a36Sopenharmony_ci char *extra) 718662306a36Sopenharmony_ci{ 718762306a36Sopenharmony_ci struct iw_param *vwrq = &wrqu->sens; 718862306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 718962306a36Sopenharmony_ci 719062306a36Sopenharmony_ci readConfigRid(local, 1); 719162306a36Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.rssiThreshold); 719262306a36Sopenharmony_ci vwrq->disabled = (vwrq->value == 0); 719362306a36Sopenharmony_ci vwrq->fixed = 1; 719462306a36Sopenharmony_ci 719562306a36Sopenharmony_ci return 0; 719662306a36Sopenharmony_ci} 719762306a36Sopenharmony_ci 719862306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 719962306a36Sopenharmony_ci/* 720062306a36Sopenharmony_ci * Wireless Handler : get AP List 720162306a36Sopenharmony_ci * Note : this is deprecated in favor of IWSCAN 720262306a36Sopenharmony_ci */ 720362306a36Sopenharmony_cistatic int airo_get_aplist(struct net_device *dev, 720462306a36Sopenharmony_ci struct iw_request_info *info, 720562306a36Sopenharmony_ci union iwreq_data *wrqu, 720662306a36Sopenharmony_ci char *extra) 720762306a36Sopenharmony_ci{ 720862306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->data; 720962306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 721062306a36Sopenharmony_ci struct sockaddr *address = (struct sockaddr *) extra; 721162306a36Sopenharmony_ci struct iw_quality *qual; 721262306a36Sopenharmony_ci BSSListRid BSSList; 721362306a36Sopenharmony_ci int i; 721462306a36Sopenharmony_ci int loseSync = capable(CAP_NET_ADMIN) ? 1: -1; 721562306a36Sopenharmony_ci 721662306a36Sopenharmony_ci qual = kmalloc_array(IW_MAX_AP, sizeof(*qual), GFP_KERNEL); 721762306a36Sopenharmony_ci if (!qual) 721862306a36Sopenharmony_ci return -ENOMEM; 721962306a36Sopenharmony_ci 722062306a36Sopenharmony_ci for (i = 0; i < IW_MAX_AP; i++) { 722162306a36Sopenharmony_ci u16 dBm; 722262306a36Sopenharmony_ci if (readBSSListRid(local, loseSync, &BSSList)) 722362306a36Sopenharmony_ci break; 722462306a36Sopenharmony_ci loseSync = 0; 722562306a36Sopenharmony_ci memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN); 722662306a36Sopenharmony_ci address[i].sa_family = ARPHRD_ETHER; 722762306a36Sopenharmony_ci dBm = le16_to_cpu(BSSList.dBm); 722862306a36Sopenharmony_ci if (local->rssi) { 722962306a36Sopenharmony_ci qual[i].level = 0x100 - dBm; 723062306a36Sopenharmony_ci qual[i].qual = airo_dbm_to_pct(local->rssi, dBm); 723162306a36Sopenharmony_ci qual[i].updated = IW_QUAL_QUAL_UPDATED 723262306a36Sopenharmony_ci | IW_QUAL_LEVEL_UPDATED 723362306a36Sopenharmony_ci | IW_QUAL_DBM; 723462306a36Sopenharmony_ci } else { 723562306a36Sopenharmony_ci qual[i].level = (dBm + 321) / 2; 723662306a36Sopenharmony_ci qual[i].qual = 0; 723762306a36Sopenharmony_ci qual[i].updated = IW_QUAL_QUAL_INVALID 723862306a36Sopenharmony_ci | IW_QUAL_LEVEL_UPDATED 723962306a36Sopenharmony_ci | IW_QUAL_DBM; 724062306a36Sopenharmony_ci } 724162306a36Sopenharmony_ci qual[i].noise = local->wstats.qual.noise; 724262306a36Sopenharmony_ci if (BSSList.index == cpu_to_le16(0xffff)) 724362306a36Sopenharmony_ci break; 724462306a36Sopenharmony_ci } 724562306a36Sopenharmony_ci if (!i) { 724662306a36Sopenharmony_ci StatusRid status_rid; /* Card status info */ 724762306a36Sopenharmony_ci readStatusRid(local, &status_rid, 1); 724862306a36Sopenharmony_ci for (i = 0; 724962306a36Sopenharmony_ci i < min(IW_MAX_AP, 4) && 725062306a36Sopenharmony_ci (status_rid.bssid[i][0] 725162306a36Sopenharmony_ci & status_rid.bssid[i][1] 725262306a36Sopenharmony_ci & status_rid.bssid[i][2] 725362306a36Sopenharmony_ci & status_rid.bssid[i][3] 725462306a36Sopenharmony_ci & status_rid.bssid[i][4] 725562306a36Sopenharmony_ci & status_rid.bssid[i][5])!=0xff && 725662306a36Sopenharmony_ci (status_rid.bssid[i][0] 725762306a36Sopenharmony_ci | status_rid.bssid[i][1] 725862306a36Sopenharmony_ci | status_rid.bssid[i][2] 725962306a36Sopenharmony_ci | status_rid.bssid[i][3] 726062306a36Sopenharmony_ci | status_rid.bssid[i][4] 726162306a36Sopenharmony_ci | status_rid.bssid[i][5]); 726262306a36Sopenharmony_ci i++) { 726362306a36Sopenharmony_ci memcpy(address[i].sa_data, 726462306a36Sopenharmony_ci status_rid.bssid[i], ETH_ALEN); 726562306a36Sopenharmony_ci address[i].sa_family = ARPHRD_ETHER; 726662306a36Sopenharmony_ci } 726762306a36Sopenharmony_ci } else { 726862306a36Sopenharmony_ci dwrq->flags = 1; /* Should be define'd */ 726962306a36Sopenharmony_ci memcpy(extra + sizeof(struct sockaddr) * i, qual, 727062306a36Sopenharmony_ci sizeof(struct iw_quality) * i); 727162306a36Sopenharmony_ci } 727262306a36Sopenharmony_ci dwrq->length = i; 727362306a36Sopenharmony_ci 727462306a36Sopenharmony_ci kfree(qual); 727562306a36Sopenharmony_ci return 0; 727662306a36Sopenharmony_ci} 727762306a36Sopenharmony_ci 727862306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 727962306a36Sopenharmony_ci/* 728062306a36Sopenharmony_ci * Wireless Handler : Initiate Scan 728162306a36Sopenharmony_ci */ 728262306a36Sopenharmony_cistatic int airo_set_scan(struct net_device *dev, 728362306a36Sopenharmony_ci struct iw_request_info *info, 728462306a36Sopenharmony_ci union iwreq_data *wrqu, 728562306a36Sopenharmony_ci char *extra) 728662306a36Sopenharmony_ci{ 728762306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 728862306a36Sopenharmony_ci Cmd cmd; 728962306a36Sopenharmony_ci Resp rsp; 729062306a36Sopenharmony_ci int wake = 0; 729162306a36Sopenharmony_ci APListRid APList_rid_empty; 729262306a36Sopenharmony_ci 729362306a36Sopenharmony_ci /* Note : you may have realised that, as this is a SET operation, 729462306a36Sopenharmony_ci * this is privileged and therefore a normal user can't 729562306a36Sopenharmony_ci * perform scanning. 729662306a36Sopenharmony_ci * This is not an error, while the device perform scanning, 729762306a36Sopenharmony_ci * traffic doesn't flow, so it's a perfect DoS... 729862306a36Sopenharmony_ci * Jean II */ 729962306a36Sopenharmony_ci if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN; 730062306a36Sopenharmony_ci 730162306a36Sopenharmony_ci if (down_interruptible(&ai->sem)) 730262306a36Sopenharmony_ci return -ERESTARTSYS; 730362306a36Sopenharmony_ci 730462306a36Sopenharmony_ci /* If there's already a scan in progress, don't 730562306a36Sopenharmony_ci * trigger another one. */ 730662306a36Sopenharmony_ci if (ai->scan_timeout > 0) 730762306a36Sopenharmony_ci goto out; 730862306a36Sopenharmony_ci 730962306a36Sopenharmony_ci /* Clear APList as it affects scan results */ 731062306a36Sopenharmony_ci memset(&APList_rid_empty, 0, sizeof(APList_rid_empty)); 731162306a36Sopenharmony_ci APList_rid_empty.len = cpu_to_le16(sizeof(APList_rid_empty)); 731262306a36Sopenharmony_ci disable_MAC(ai, 2); 731362306a36Sopenharmony_ci writeAPListRid(ai, &APList_rid_empty, 0); 731462306a36Sopenharmony_ci enable_MAC(ai, 0); 731562306a36Sopenharmony_ci 731662306a36Sopenharmony_ci /* Initiate a scan command */ 731762306a36Sopenharmony_ci ai->scan_timeout = RUN_AT(3*HZ); 731862306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 731962306a36Sopenharmony_ci cmd.cmd = CMD_LISTBSS; 732062306a36Sopenharmony_ci issuecommand(ai, &cmd, &rsp, true); 732162306a36Sopenharmony_ci wake = 1; 732262306a36Sopenharmony_ci 732362306a36Sopenharmony_ciout: 732462306a36Sopenharmony_ci up(&ai->sem); 732562306a36Sopenharmony_ci if (wake) 732662306a36Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 732762306a36Sopenharmony_ci return 0; 732862306a36Sopenharmony_ci} 732962306a36Sopenharmony_ci 733062306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 733162306a36Sopenharmony_ci/* 733262306a36Sopenharmony_ci * Translate scan data returned from the card to a card independent 733362306a36Sopenharmony_ci * format that the Wireless Tools will understand - Jean II 733462306a36Sopenharmony_ci */ 733562306a36Sopenharmony_cistatic inline char *airo_translate_scan(struct net_device *dev, 733662306a36Sopenharmony_ci struct iw_request_info *info, 733762306a36Sopenharmony_ci char *current_ev, 733862306a36Sopenharmony_ci char *end_buf, 733962306a36Sopenharmony_ci BSSListRid *bss) 734062306a36Sopenharmony_ci{ 734162306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 734262306a36Sopenharmony_ci struct iw_event iwe; /* Temporary buffer */ 734362306a36Sopenharmony_ci __le16 capabilities; 734462306a36Sopenharmony_ci char * current_val; /* For rates */ 734562306a36Sopenharmony_ci int i; 734662306a36Sopenharmony_ci char * buf; 734762306a36Sopenharmony_ci u16 dBm; 734862306a36Sopenharmony_ci 734962306a36Sopenharmony_ci /* First entry *MUST* be the AP MAC address */ 735062306a36Sopenharmony_ci iwe.cmd = SIOCGIWAP; 735162306a36Sopenharmony_ci iwe.u.ap_addr.sa_family = ARPHRD_ETHER; 735262306a36Sopenharmony_ci memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); 735362306a36Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, end_buf, 735462306a36Sopenharmony_ci &iwe, IW_EV_ADDR_LEN); 735562306a36Sopenharmony_ci 735662306a36Sopenharmony_ci /* Other entries will be displayed in the order we give them */ 735762306a36Sopenharmony_ci 735862306a36Sopenharmony_ci /* Add the ESSID */ 735962306a36Sopenharmony_ci iwe.u.data.length = bss->ssidLen; 736062306a36Sopenharmony_ci if (iwe.u.data.length > 32) 736162306a36Sopenharmony_ci iwe.u.data.length = 32; 736262306a36Sopenharmony_ci iwe.cmd = SIOCGIWESSID; 736362306a36Sopenharmony_ci iwe.u.data.flags = 1; 736462306a36Sopenharmony_ci current_ev = iwe_stream_add_point(info, current_ev, end_buf, 736562306a36Sopenharmony_ci &iwe, bss->ssid); 736662306a36Sopenharmony_ci 736762306a36Sopenharmony_ci /* Add mode */ 736862306a36Sopenharmony_ci iwe.cmd = SIOCGIWMODE; 736962306a36Sopenharmony_ci capabilities = bss->cap; 737062306a36Sopenharmony_ci if (capabilities & (CAP_ESS | CAP_IBSS)) { 737162306a36Sopenharmony_ci if (capabilities & CAP_ESS) 737262306a36Sopenharmony_ci iwe.u.mode = IW_MODE_MASTER; 737362306a36Sopenharmony_ci else 737462306a36Sopenharmony_ci iwe.u.mode = IW_MODE_ADHOC; 737562306a36Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, end_buf, 737662306a36Sopenharmony_ci &iwe, IW_EV_UINT_LEN); 737762306a36Sopenharmony_ci } 737862306a36Sopenharmony_ci 737962306a36Sopenharmony_ci /* Add frequency */ 738062306a36Sopenharmony_ci iwe.cmd = SIOCGIWFREQ; 738162306a36Sopenharmony_ci iwe.u.freq.m = le16_to_cpu(bss->dsChannel); 738262306a36Sopenharmony_ci iwe.u.freq.m = 100000 * 738362306a36Sopenharmony_ci ieee80211_channel_to_frequency(iwe.u.freq.m, NL80211_BAND_2GHZ); 738462306a36Sopenharmony_ci iwe.u.freq.e = 1; 738562306a36Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, end_buf, 738662306a36Sopenharmony_ci &iwe, IW_EV_FREQ_LEN); 738762306a36Sopenharmony_ci 738862306a36Sopenharmony_ci dBm = le16_to_cpu(bss->dBm); 738962306a36Sopenharmony_ci 739062306a36Sopenharmony_ci /* Add quality statistics */ 739162306a36Sopenharmony_ci iwe.cmd = IWEVQUAL; 739262306a36Sopenharmony_ci if (ai->rssi) { 739362306a36Sopenharmony_ci iwe.u.qual.level = 0x100 - dBm; 739462306a36Sopenharmony_ci iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm); 739562306a36Sopenharmony_ci iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED 739662306a36Sopenharmony_ci | IW_QUAL_LEVEL_UPDATED 739762306a36Sopenharmony_ci | IW_QUAL_DBM; 739862306a36Sopenharmony_ci } else { 739962306a36Sopenharmony_ci iwe.u.qual.level = (dBm + 321) / 2; 740062306a36Sopenharmony_ci iwe.u.qual.qual = 0; 740162306a36Sopenharmony_ci iwe.u.qual.updated = IW_QUAL_QUAL_INVALID 740262306a36Sopenharmony_ci | IW_QUAL_LEVEL_UPDATED 740362306a36Sopenharmony_ci | IW_QUAL_DBM; 740462306a36Sopenharmony_ci } 740562306a36Sopenharmony_ci iwe.u.qual.noise = ai->wstats.qual.noise; 740662306a36Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, end_buf, 740762306a36Sopenharmony_ci &iwe, IW_EV_QUAL_LEN); 740862306a36Sopenharmony_ci 740962306a36Sopenharmony_ci /* Add encryption capability */ 741062306a36Sopenharmony_ci iwe.cmd = SIOCGIWENCODE; 741162306a36Sopenharmony_ci if (capabilities & CAP_PRIVACY) 741262306a36Sopenharmony_ci iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; 741362306a36Sopenharmony_ci else 741462306a36Sopenharmony_ci iwe.u.data.flags = IW_ENCODE_DISABLED; 741562306a36Sopenharmony_ci iwe.u.data.length = 0; 741662306a36Sopenharmony_ci current_ev = iwe_stream_add_point(info, current_ev, end_buf, 741762306a36Sopenharmony_ci &iwe, bss->ssid); 741862306a36Sopenharmony_ci 741962306a36Sopenharmony_ci /* Rate : stuffing multiple values in a single event require a bit 742062306a36Sopenharmony_ci * more of magic - Jean II */ 742162306a36Sopenharmony_ci current_val = current_ev + iwe_stream_lcp_len(info); 742262306a36Sopenharmony_ci 742362306a36Sopenharmony_ci iwe.cmd = SIOCGIWRATE; 742462306a36Sopenharmony_ci /* Those two flags are ignored... */ 742562306a36Sopenharmony_ci iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; 742662306a36Sopenharmony_ci /* Max 8 values */ 742762306a36Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 742862306a36Sopenharmony_ci /* NULL terminated */ 742962306a36Sopenharmony_ci if (bss->rates[i] == 0) 743062306a36Sopenharmony_ci break; 743162306a36Sopenharmony_ci /* Bit rate given in 500 kb/s units (+ 0x80) */ 743262306a36Sopenharmony_ci iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000); 743362306a36Sopenharmony_ci /* Add new value to event */ 743462306a36Sopenharmony_ci current_val = iwe_stream_add_value(info, current_ev, 743562306a36Sopenharmony_ci current_val, end_buf, 743662306a36Sopenharmony_ci &iwe, IW_EV_PARAM_LEN); 743762306a36Sopenharmony_ci } 743862306a36Sopenharmony_ci /* Check if we added any event */ 743962306a36Sopenharmony_ci if ((current_val - current_ev) > iwe_stream_lcp_len(info)) 744062306a36Sopenharmony_ci current_ev = current_val; 744162306a36Sopenharmony_ci 744262306a36Sopenharmony_ci /* Beacon interval */ 744362306a36Sopenharmony_ci buf = kmalloc(30, GFP_KERNEL); 744462306a36Sopenharmony_ci if (buf) { 744562306a36Sopenharmony_ci iwe.cmd = IWEVCUSTOM; 744662306a36Sopenharmony_ci sprintf(buf, "bcn_int=%d", bss->beaconInterval); 744762306a36Sopenharmony_ci iwe.u.data.length = strlen(buf); 744862306a36Sopenharmony_ci current_ev = iwe_stream_add_point(info, current_ev, end_buf, 744962306a36Sopenharmony_ci &iwe, buf); 745062306a36Sopenharmony_ci kfree(buf); 745162306a36Sopenharmony_ci } 745262306a36Sopenharmony_ci 745362306a36Sopenharmony_ci /* Put WPA/RSN Information Elements into the event stream */ 745462306a36Sopenharmony_ci if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) { 745562306a36Sopenharmony_ci unsigned int num_null_ies = 0; 745662306a36Sopenharmony_ci u16 length = sizeof (bss->extra.iep); 745762306a36Sopenharmony_ci u8 *ie = (void *)&bss->extra.iep; 745862306a36Sopenharmony_ci 745962306a36Sopenharmony_ci while ((length >= 2) && (num_null_ies < 2)) { 746062306a36Sopenharmony_ci if (2 + ie[1] > length) { 746162306a36Sopenharmony_ci /* Invalid element, don't continue parsing IE */ 746262306a36Sopenharmony_ci break; 746362306a36Sopenharmony_ci } 746462306a36Sopenharmony_ci 746562306a36Sopenharmony_ci switch (ie[0]) { 746662306a36Sopenharmony_ci case WLAN_EID_SSID: 746762306a36Sopenharmony_ci /* Two zero-length SSID elements 746862306a36Sopenharmony_ci * mean we're done parsing elements */ 746962306a36Sopenharmony_ci if (!ie[1]) 747062306a36Sopenharmony_ci num_null_ies++; 747162306a36Sopenharmony_ci break; 747262306a36Sopenharmony_ci 747362306a36Sopenharmony_ci case WLAN_EID_VENDOR_SPECIFIC: 747462306a36Sopenharmony_ci if (ie[1] >= 4 && 747562306a36Sopenharmony_ci ie[2] == 0x00 && 747662306a36Sopenharmony_ci ie[3] == 0x50 && 747762306a36Sopenharmony_ci ie[4] == 0xf2 && 747862306a36Sopenharmony_ci ie[5] == 0x01) { 747962306a36Sopenharmony_ci iwe.cmd = IWEVGENIE; 748062306a36Sopenharmony_ci /* 64 is an arbitrary cut-off */ 748162306a36Sopenharmony_ci iwe.u.data.length = min(ie[1] + 2, 748262306a36Sopenharmony_ci 64); 748362306a36Sopenharmony_ci current_ev = iwe_stream_add_point( 748462306a36Sopenharmony_ci info, current_ev, 748562306a36Sopenharmony_ci end_buf, &iwe, ie); 748662306a36Sopenharmony_ci } 748762306a36Sopenharmony_ci break; 748862306a36Sopenharmony_ci 748962306a36Sopenharmony_ci case WLAN_EID_RSN: 749062306a36Sopenharmony_ci iwe.cmd = IWEVGENIE; 749162306a36Sopenharmony_ci /* 64 is an arbitrary cut-off */ 749262306a36Sopenharmony_ci iwe.u.data.length = min(ie[1] + 2, 64); 749362306a36Sopenharmony_ci current_ev = iwe_stream_add_point( 749462306a36Sopenharmony_ci info, current_ev, end_buf, 749562306a36Sopenharmony_ci &iwe, ie); 749662306a36Sopenharmony_ci break; 749762306a36Sopenharmony_ci 749862306a36Sopenharmony_ci default: 749962306a36Sopenharmony_ci break; 750062306a36Sopenharmony_ci } 750162306a36Sopenharmony_ci 750262306a36Sopenharmony_ci length -= 2 + ie[1]; 750362306a36Sopenharmony_ci ie += 2 + ie[1]; 750462306a36Sopenharmony_ci } 750562306a36Sopenharmony_ci } 750662306a36Sopenharmony_ci return current_ev; 750762306a36Sopenharmony_ci} 750862306a36Sopenharmony_ci 750962306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 751062306a36Sopenharmony_ci/* 751162306a36Sopenharmony_ci * Wireless Handler : Read Scan Results 751262306a36Sopenharmony_ci */ 751362306a36Sopenharmony_cistatic int airo_get_scan(struct net_device *dev, 751462306a36Sopenharmony_ci struct iw_request_info *info, 751562306a36Sopenharmony_ci union iwreq_data *wrqu, 751662306a36Sopenharmony_ci char *extra) 751762306a36Sopenharmony_ci{ 751862306a36Sopenharmony_ci struct iw_point *dwrq = &wrqu->data; 751962306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 752062306a36Sopenharmony_ci BSSListElement *net; 752162306a36Sopenharmony_ci int err = 0; 752262306a36Sopenharmony_ci char *current_ev = extra; 752362306a36Sopenharmony_ci 752462306a36Sopenharmony_ci /* If a scan is in-progress, return -EAGAIN */ 752562306a36Sopenharmony_ci if (ai->scan_timeout > 0) 752662306a36Sopenharmony_ci return -EAGAIN; 752762306a36Sopenharmony_ci 752862306a36Sopenharmony_ci if (down_interruptible(&ai->sem)) 752962306a36Sopenharmony_ci return -EAGAIN; 753062306a36Sopenharmony_ci 753162306a36Sopenharmony_ci list_for_each_entry (net, &ai->network_list, list) { 753262306a36Sopenharmony_ci /* Translate to WE format this entry */ 753362306a36Sopenharmony_ci current_ev = airo_translate_scan(dev, info, current_ev, 753462306a36Sopenharmony_ci extra + dwrq->length, 753562306a36Sopenharmony_ci &net->bss); 753662306a36Sopenharmony_ci 753762306a36Sopenharmony_ci /* Check if there is space for one more entry */ 753862306a36Sopenharmony_ci if ((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) { 753962306a36Sopenharmony_ci /* Ask user space to try again with a bigger buffer */ 754062306a36Sopenharmony_ci err = -E2BIG; 754162306a36Sopenharmony_ci goto out; 754262306a36Sopenharmony_ci } 754362306a36Sopenharmony_ci } 754462306a36Sopenharmony_ci 754562306a36Sopenharmony_ci /* Length of data */ 754662306a36Sopenharmony_ci dwrq->length = (current_ev - extra); 754762306a36Sopenharmony_ci dwrq->flags = 0; /* todo */ 754862306a36Sopenharmony_ci 754962306a36Sopenharmony_ciout: 755062306a36Sopenharmony_ci up(&ai->sem); 755162306a36Sopenharmony_ci return err; 755262306a36Sopenharmony_ci} 755362306a36Sopenharmony_ci 755462306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 755562306a36Sopenharmony_ci/* 755662306a36Sopenharmony_ci * Commit handler : called after a bunch of SET operations 755762306a36Sopenharmony_ci */ 755862306a36Sopenharmony_cistatic int airo_config_commit(struct net_device *dev, 755962306a36Sopenharmony_ci struct iw_request_info *info, /* NULL */ 756062306a36Sopenharmony_ci union iwreq_data *wrqu, /* NULL */ 756162306a36Sopenharmony_ci char *extra) /* NULL */ 756262306a36Sopenharmony_ci{ 756362306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 756462306a36Sopenharmony_ci 756562306a36Sopenharmony_ci if (!test_bit (FLAG_COMMIT, &local->flags)) 756662306a36Sopenharmony_ci return 0; 756762306a36Sopenharmony_ci 756862306a36Sopenharmony_ci /* Some of the "SET" function may have modified some of the 756962306a36Sopenharmony_ci * parameters. It's now time to commit them in the card */ 757062306a36Sopenharmony_ci disable_MAC(local, 1); 757162306a36Sopenharmony_ci if (test_bit (FLAG_RESET, &local->flags)) { 757262306a36Sopenharmony_ci SsidRid SSID_rid; 757362306a36Sopenharmony_ci 757462306a36Sopenharmony_ci readSsidRid(local, &SSID_rid); 757562306a36Sopenharmony_ci if (test_bit(FLAG_MPI,&local->flags)) 757662306a36Sopenharmony_ci setup_card(local, dev, 1); 757762306a36Sopenharmony_ci else 757862306a36Sopenharmony_ci reset_airo_card(dev); 757962306a36Sopenharmony_ci disable_MAC(local, 1); 758062306a36Sopenharmony_ci writeSsidRid(local, &SSID_rid, 1); 758162306a36Sopenharmony_ci writeAPListRid(local, &local->APList, 1); 758262306a36Sopenharmony_ci } 758362306a36Sopenharmony_ci if (down_interruptible(&local->sem)) 758462306a36Sopenharmony_ci return -ERESTARTSYS; 758562306a36Sopenharmony_ci writeConfigRid(local, 0); 758662306a36Sopenharmony_ci enable_MAC(local, 0); 758762306a36Sopenharmony_ci if (test_bit (FLAG_RESET, &local->flags)) 758862306a36Sopenharmony_ci airo_set_promisc(local, true); 758962306a36Sopenharmony_ci else 759062306a36Sopenharmony_ci up(&local->sem); 759162306a36Sopenharmony_ci 759262306a36Sopenharmony_ci return 0; 759362306a36Sopenharmony_ci} 759462306a36Sopenharmony_ci 759562306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 759662306a36Sopenharmony_ci/* 759762306a36Sopenharmony_ci * Structures to export the Wireless Handlers 759862306a36Sopenharmony_ci */ 759962306a36Sopenharmony_ci 760062306a36Sopenharmony_cistatic const struct iw_priv_args airo_private_args[] = { 760162306a36Sopenharmony_ci/*{ cmd, set_args, get_args, name } */ 760262306a36Sopenharmony_ci { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), 760362306a36Sopenharmony_ci IW_PRIV_TYPE_BYTE | 2047, "airoioctl" }, 760462306a36Sopenharmony_ci { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), 760562306a36Sopenharmony_ci IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" }, 760662306a36Sopenharmony_ci}; 760762306a36Sopenharmony_ci 760862306a36Sopenharmony_cistatic const iw_handler airo_handler[] = 760962306a36Sopenharmony_ci{ 761062306a36Sopenharmony_ci IW_HANDLER(SIOCSIWCOMMIT, airo_config_commit), 761162306a36Sopenharmony_ci IW_HANDLER(SIOCGIWNAME, airo_get_name), 761262306a36Sopenharmony_ci IW_HANDLER(SIOCSIWFREQ, airo_set_freq), 761362306a36Sopenharmony_ci IW_HANDLER(SIOCGIWFREQ, airo_get_freq), 761462306a36Sopenharmony_ci IW_HANDLER(SIOCSIWMODE, airo_set_mode), 761562306a36Sopenharmony_ci IW_HANDLER(SIOCGIWMODE, airo_get_mode), 761662306a36Sopenharmony_ci IW_HANDLER(SIOCSIWSENS, airo_set_sens), 761762306a36Sopenharmony_ci IW_HANDLER(SIOCGIWSENS, airo_get_sens), 761862306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRANGE, airo_get_range), 761962306a36Sopenharmony_ci IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), 762062306a36Sopenharmony_ci IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), 762162306a36Sopenharmony_ci IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), 762262306a36Sopenharmony_ci IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), 762362306a36Sopenharmony_ci IW_HANDLER(SIOCSIWAP, airo_set_wap), 762462306a36Sopenharmony_ci IW_HANDLER(SIOCGIWAP, airo_get_wap), 762562306a36Sopenharmony_ci IW_HANDLER(SIOCGIWAPLIST, airo_get_aplist), 762662306a36Sopenharmony_ci IW_HANDLER(SIOCSIWSCAN, airo_set_scan), 762762306a36Sopenharmony_ci IW_HANDLER(SIOCGIWSCAN, airo_get_scan), 762862306a36Sopenharmony_ci IW_HANDLER(SIOCSIWESSID, airo_set_essid), 762962306a36Sopenharmony_ci IW_HANDLER(SIOCGIWESSID, airo_get_essid), 763062306a36Sopenharmony_ci IW_HANDLER(SIOCSIWNICKN, airo_set_nick), 763162306a36Sopenharmony_ci IW_HANDLER(SIOCGIWNICKN, airo_get_nick), 763262306a36Sopenharmony_ci IW_HANDLER(SIOCSIWRATE, airo_set_rate), 763362306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRATE, airo_get_rate), 763462306a36Sopenharmony_ci IW_HANDLER(SIOCSIWRTS, airo_set_rts), 763562306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRTS, airo_get_rts), 763662306a36Sopenharmony_ci IW_HANDLER(SIOCSIWFRAG, airo_set_frag), 763762306a36Sopenharmony_ci IW_HANDLER(SIOCGIWFRAG, airo_get_frag), 763862306a36Sopenharmony_ci IW_HANDLER(SIOCSIWTXPOW, airo_set_txpow), 763962306a36Sopenharmony_ci IW_HANDLER(SIOCGIWTXPOW, airo_get_txpow), 764062306a36Sopenharmony_ci IW_HANDLER(SIOCSIWRETRY, airo_set_retry), 764162306a36Sopenharmony_ci IW_HANDLER(SIOCGIWRETRY, airo_get_retry), 764262306a36Sopenharmony_ci IW_HANDLER(SIOCSIWENCODE, airo_set_encode), 764362306a36Sopenharmony_ci IW_HANDLER(SIOCGIWENCODE, airo_get_encode), 764462306a36Sopenharmony_ci IW_HANDLER(SIOCSIWPOWER, airo_set_power), 764562306a36Sopenharmony_ci IW_HANDLER(SIOCGIWPOWER, airo_get_power), 764662306a36Sopenharmony_ci IW_HANDLER(SIOCSIWAUTH, airo_set_auth), 764762306a36Sopenharmony_ci IW_HANDLER(SIOCGIWAUTH, airo_get_auth), 764862306a36Sopenharmony_ci IW_HANDLER(SIOCSIWENCODEEXT, airo_set_encodeext), 764962306a36Sopenharmony_ci IW_HANDLER(SIOCGIWENCODEEXT, airo_get_encodeext), 765062306a36Sopenharmony_ci}; 765162306a36Sopenharmony_ci 765262306a36Sopenharmony_ci/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here. 765362306a36Sopenharmony_ci * We want to force the use of the ioctl code, because those can't be 765462306a36Sopenharmony_ci * won't work the iw_handler code (because they simultaneously read 765562306a36Sopenharmony_ci * and write data and iw_handler can't do that). 765662306a36Sopenharmony_ci * Note that it's perfectly legal to read/write on a single ioctl command, 765762306a36Sopenharmony_ci * you just can't use iwpriv and need to force it via the ioctl handler. 765862306a36Sopenharmony_ci * Jean II */ 765962306a36Sopenharmony_cistatic const iw_handler airo_private_handler[] = 766062306a36Sopenharmony_ci{ 766162306a36Sopenharmony_ci NULL, /* SIOCIWFIRSTPRIV */ 766262306a36Sopenharmony_ci}; 766362306a36Sopenharmony_ci 766462306a36Sopenharmony_cistatic const struct iw_handler_def airo_handler_def = 766562306a36Sopenharmony_ci{ 766662306a36Sopenharmony_ci .num_standard = ARRAY_SIZE(airo_handler), 766762306a36Sopenharmony_ci .num_private = ARRAY_SIZE(airo_private_handler), 766862306a36Sopenharmony_ci .num_private_args = ARRAY_SIZE(airo_private_args), 766962306a36Sopenharmony_ci .standard = airo_handler, 767062306a36Sopenharmony_ci .private = airo_private_handler, 767162306a36Sopenharmony_ci .private_args = airo_private_args, 767262306a36Sopenharmony_ci .get_wireless_stats = airo_get_wireless_stats, 767362306a36Sopenharmony_ci}; 767462306a36Sopenharmony_ci 767562306a36Sopenharmony_ci/* 767662306a36Sopenharmony_ci * This defines the configuration part of the Wireless Extensions 767762306a36Sopenharmony_ci * Note : irq and spinlock protection will occur in the subroutines 767862306a36Sopenharmony_ci * 767962306a36Sopenharmony_ci * TODO : 768062306a36Sopenharmony_ci * o Check input value more carefully and fill correct values in range 768162306a36Sopenharmony_ci * o Test and shakeout the bugs (if any) 768262306a36Sopenharmony_ci * 768362306a36Sopenharmony_ci * Jean II 768462306a36Sopenharmony_ci * 768562306a36Sopenharmony_ci * Javier Achirica did a great job of merging code from the unnamed CISCO 768662306a36Sopenharmony_ci * developer that added support for flashing the card. 768762306a36Sopenharmony_ci */ 768862306a36Sopenharmony_cistatic int airo_siocdevprivate(struct net_device *dev, struct ifreq *rq, 768962306a36Sopenharmony_ci void __user *data, int cmd) 769062306a36Sopenharmony_ci{ 769162306a36Sopenharmony_ci int rc = 0; 769262306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 769362306a36Sopenharmony_ci 769462306a36Sopenharmony_ci if (ai->power.event) 769562306a36Sopenharmony_ci return 0; 769662306a36Sopenharmony_ci 769762306a36Sopenharmony_ci switch (cmd) { 769862306a36Sopenharmony_ci#ifdef CISCO_EXT 769962306a36Sopenharmony_ci case AIROIDIFC: 770062306a36Sopenharmony_ci#ifdef AIROOLDIDIFC 770162306a36Sopenharmony_ci case AIROOLDIDIFC: 770262306a36Sopenharmony_ci#endif 770362306a36Sopenharmony_ci { 770462306a36Sopenharmony_ci int val = AIROMAGIC; 770562306a36Sopenharmony_ci aironet_ioctl com; 770662306a36Sopenharmony_ci if (copy_from_user(&com, data, sizeof(com))) 770762306a36Sopenharmony_ci rc = -EFAULT; 770862306a36Sopenharmony_ci else if (copy_to_user(com.data, (char *)&val, sizeof(val))) 770962306a36Sopenharmony_ci rc = -EFAULT; 771062306a36Sopenharmony_ci } 771162306a36Sopenharmony_ci break; 771262306a36Sopenharmony_ci 771362306a36Sopenharmony_ci case AIROIOCTL: 771462306a36Sopenharmony_ci#ifdef AIROOLDIOCTL 771562306a36Sopenharmony_ci case AIROOLDIOCTL: 771662306a36Sopenharmony_ci#endif 771762306a36Sopenharmony_ci /* Get the command struct and hand it off for evaluation by 771862306a36Sopenharmony_ci * the proper subfunction 771962306a36Sopenharmony_ci */ 772062306a36Sopenharmony_ci { 772162306a36Sopenharmony_ci aironet_ioctl com; 772262306a36Sopenharmony_ci if (copy_from_user(&com, data, sizeof(com))) { 772362306a36Sopenharmony_ci rc = -EFAULT; 772462306a36Sopenharmony_ci break; 772562306a36Sopenharmony_ci } 772662306a36Sopenharmony_ci 772762306a36Sopenharmony_ci /* Separate R/W functions bracket legality here 772862306a36Sopenharmony_ci */ 772962306a36Sopenharmony_ci if (com.command == AIRORSWVERSION) { 773062306a36Sopenharmony_ci if (copy_to_user(com.data, swversion, sizeof(swversion))) 773162306a36Sopenharmony_ci rc = -EFAULT; 773262306a36Sopenharmony_ci else 773362306a36Sopenharmony_ci rc = 0; 773462306a36Sopenharmony_ci } 773562306a36Sopenharmony_ci else if (com.command <= AIRORRID) 773662306a36Sopenharmony_ci rc = readrids(dev,&com); 773762306a36Sopenharmony_ci else if (com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2)) 773862306a36Sopenharmony_ci rc = writerids(dev,&com); 773962306a36Sopenharmony_ci else if (com.command >= AIROFLSHRST && com.command <= AIRORESTART) 774062306a36Sopenharmony_ci rc = flashcard(dev,&com); 774162306a36Sopenharmony_ci else 774262306a36Sopenharmony_ci rc = -EINVAL; /* Bad command in ioctl */ 774362306a36Sopenharmony_ci } 774462306a36Sopenharmony_ci break; 774562306a36Sopenharmony_ci#endif /* CISCO_EXT */ 774662306a36Sopenharmony_ci 774762306a36Sopenharmony_ci // All other calls are currently unsupported 774862306a36Sopenharmony_ci default: 774962306a36Sopenharmony_ci rc = -EOPNOTSUPP; 775062306a36Sopenharmony_ci } 775162306a36Sopenharmony_ci return rc; 775262306a36Sopenharmony_ci} 775362306a36Sopenharmony_ci 775462306a36Sopenharmony_ci/* 775562306a36Sopenharmony_ci * Get the Wireless stats out of the driver 775662306a36Sopenharmony_ci * Note : irq and spinlock protection will occur in the subroutines 775762306a36Sopenharmony_ci * 775862306a36Sopenharmony_ci * TODO : 775962306a36Sopenharmony_ci * o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs) 776062306a36Sopenharmony_ci * 776162306a36Sopenharmony_ci * Jean 776262306a36Sopenharmony_ci */ 776362306a36Sopenharmony_cistatic void airo_read_wireless_stats(struct airo_info *local) 776462306a36Sopenharmony_ci{ 776562306a36Sopenharmony_ci StatusRid status_rid; 776662306a36Sopenharmony_ci StatsRid stats_rid; 776762306a36Sopenharmony_ci CapabilityRid cap_rid; 776862306a36Sopenharmony_ci __le32 *vals = stats_rid.vals; 776962306a36Sopenharmony_ci 777062306a36Sopenharmony_ci /* Get stats out of the card */ 777162306a36Sopenharmony_ci if (local->power.event) 777262306a36Sopenharmony_ci return; 777362306a36Sopenharmony_ci 777462306a36Sopenharmony_ci readCapabilityRid(local, &cap_rid, 0); 777562306a36Sopenharmony_ci readStatusRid(local, &status_rid, 0); 777662306a36Sopenharmony_ci readStatsRid(local, &stats_rid, RID_STATS, 0); 777762306a36Sopenharmony_ci 777862306a36Sopenharmony_ci /* The status */ 777962306a36Sopenharmony_ci local->wstats.status = le16_to_cpu(status_rid.mode); 778062306a36Sopenharmony_ci 778162306a36Sopenharmony_ci /* Signal quality and co */ 778262306a36Sopenharmony_ci if (local->rssi) { 778362306a36Sopenharmony_ci local->wstats.qual.level = 778462306a36Sopenharmony_ci airo_rssi_to_dbm(local->rssi, 778562306a36Sopenharmony_ci le16_to_cpu(status_rid.sigQuality)); 778662306a36Sopenharmony_ci /* normalizedSignalStrength appears to be a percentage */ 778762306a36Sopenharmony_ci local->wstats.qual.qual = 778862306a36Sopenharmony_ci le16_to_cpu(status_rid.normalizedSignalStrength); 778962306a36Sopenharmony_ci } else { 779062306a36Sopenharmony_ci local->wstats.qual.level = 779162306a36Sopenharmony_ci (le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2; 779262306a36Sopenharmony_ci local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid); 779362306a36Sopenharmony_ci } 779462306a36Sopenharmony_ci if (le16_to_cpu(status_rid.len) >= 124) { 779562306a36Sopenharmony_ci local->wstats.qual.noise = 0x100 - status_rid.noisedBm; 779662306a36Sopenharmony_ci local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; 779762306a36Sopenharmony_ci } else { 779862306a36Sopenharmony_ci local->wstats.qual.noise = 0; 779962306a36Sopenharmony_ci local->wstats.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID | IW_QUAL_DBM; 780062306a36Sopenharmony_ci } 780162306a36Sopenharmony_ci 780262306a36Sopenharmony_ci /* Packets discarded in the wireless adapter due to wireless 780362306a36Sopenharmony_ci * specific problems */ 780462306a36Sopenharmony_ci local->wstats.discard.nwid = le32_to_cpu(vals[56]) + 780562306a36Sopenharmony_ci le32_to_cpu(vals[57]) + 780662306a36Sopenharmony_ci le32_to_cpu(vals[58]); /* SSID Mismatch */ 780762306a36Sopenharmony_ci local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */ 780862306a36Sopenharmony_ci local->wstats.discard.fragment = le32_to_cpu(vals[30]); 780962306a36Sopenharmony_ci local->wstats.discard.retries = le32_to_cpu(vals[10]); 781062306a36Sopenharmony_ci local->wstats.discard.misc = le32_to_cpu(vals[1]) + 781162306a36Sopenharmony_ci le32_to_cpu(vals[32]); 781262306a36Sopenharmony_ci local->wstats.miss.beacon = le32_to_cpu(vals[34]); 781362306a36Sopenharmony_ci} 781462306a36Sopenharmony_ci 781562306a36Sopenharmony_cistatic struct iw_statistics *airo_get_wireless_stats(struct net_device *dev) 781662306a36Sopenharmony_ci{ 781762306a36Sopenharmony_ci struct airo_info *local = dev->ml_priv; 781862306a36Sopenharmony_ci 781962306a36Sopenharmony_ci if (!down_interruptible(&local->sem)) { 782062306a36Sopenharmony_ci airo_read_wireless_stats(local); 782162306a36Sopenharmony_ci up(&local->sem); 782262306a36Sopenharmony_ci } 782362306a36Sopenharmony_ci return &local->wstats; 782462306a36Sopenharmony_ci} 782562306a36Sopenharmony_ci 782662306a36Sopenharmony_ci#ifdef CISCO_EXT 782762306a36Sopenharmony_ci/* 782862306a36Sopenharmony_ci * This just translates from driver IOCTL codes to the command codes to 782962306a36Sopenharmony_ci * feed to the radio's host interface. Things can be added/deleted 783062306a36Sopenharmony_ci * as needed. This represents the READ side of control I/O to 783162306a36Sopenharmony_ci * the card 783262306a36Sopenharmony_ci */ 783362306a36Sopenharmony_cistatic int readrids(struct net_device *dev, aironet_ioctl *comp) 783462306a36Sopenharmony_ci{ 783562306a36Sopenharmony_ci unsigned short ridcode; 783662306a36Sopenharmony_ci unsigned char *iobuf; 783762306a36Sopenharmony_ci int len; 783862306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 783962306a36Sopenharmony_ci 784062306a36Sopenharmony_ci if (test_bit(FLAG_FLASHING, &ai->flags)) 784162306a36Sopenharmony_ci return -EIO; 784262306a36Sopenharmony_ci 784362306a36Sopenharmony_ci switch(comp->command) 784462306a36Sopenharmony_ci { 784562306a36Sopenharmony_ci case AIROGCAP: ridcode = RID_CAPABILITIES; break; 784662306a36Sopenharmony_ci case AIROGCFG: ridcode = RID_CONFIG; 784762306a36Sopenharmony_ci if (test_bit(FLAG_COMMIT, &ai->flags)) { 784862306a36Sopenharmony_ci disable_MAC (ai, 1); 784962306a36Sopenharmony_ci writeConfigRid (ai, 1); 785062306a36Sopenharmony_ci enable_MAC(ai, 1); 785162306a36Sopenharmony_ci } 785262306a36Sopenharmony_ci break; 785362306a36Sopenharmony_ci case AIROGSLIST: ridcode = RID_SSID; break; 785462306a36Sopenharmony_ci case AIROGVLIST: ridcode = RID_APLIST; break; 785562306a36Sopenharmony_ci case AIROGDRVNAM: ridcode = RID_DRVNAME; break; 785662306a36Sopenharmony_ci case AIROGEHTENC: ridcode = RID_ETHERENCAP; break; 785762306a36Sopenharmony_ci case AIROGWEPKTMP: ridcode = RID_WEP_TEMP; break; 785862306a36Sopenharmony_ci case AIROGWEPKNV: ridcode = RID_WEP_PERM; break; 785962306a36Sopenharmony_ci case AIROGSTAT: ridcode = RID_STATUS; break; 786062306a36Sopenharmony_ci case AIROGSTATSD32: ridcode = RID_STATSDELTA; break; 786162306a36Sopenharmony_ci case AIROGSTATSC32: ridcode = RID_STATS; break; 786262306a36Sopenharmony_ci case AIROGMICSTATS: 786362306a36Sopenharmony_ci if (copy_to_user(comp->data, &ai->micstats, 786462306a36Sopenharmony_ci min((int)comp->len, (int)sizeof(ai->micstats)))) 786562306a36Sopenharmony_ci return -EFAULT; 786662306a36Sopenharmony_ci return 0; 786762306a36Sopenharmony_ci case AIRORRID: ridcode = comp->ridnum; break; 786862306a36Sopenharmony_ci default: 786962306a36Sopenharmony_ci return -EINVAL; 787062306a36Sopenharmony_ci } 787162306a36Sopenharmony_ci 787262306a36Sopenharmony_ci if (ridcode == RID_WEP_TEMP || ridcode == RID_WEP_PERM) { 787362306a36Sopenharmony_ci /* Only super-user can read WEP keys */ 787462306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 787562306a36Sopenharmony_ci return -EPERM; 787662306a36Sopenharmony_ci } 787762306a36Sopenharmony_ci 787862306a36Sopenharmony_ci if ((iobuf = kzalloc(RIDSIZE, GFP_KERNEL)) == NULL) 787962306a36Sopenharmony_ci return -ENOMEM; 788062306a36Sopenharmony_ci 788162306a36Sopenharmony_ci PC4500_readrid(ai, ridcode, iobuf, RIDSIZE, 1); 788262306a36Sopenharmony_ci /* get the count of bytes in the rid docs say 1st 2 bytes is it. 788362306a36Sopenharmony_ci * then return it to the user 788462306a36Sopenharmony_ci * 9/22/2000 Honor user given length 788562306a36Sopenharmony_ci */ 788662306a36Sopenharmony_ci len = comp->len; 788762306a36Sopenharmony_ci 788862306a36Sopenharmony_ci if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) { 788962306a36Sopenharmony_ci kfree (iobuf); 789062306a36Sopenharmony_ci return -EFAULT; 789162306a36Sopenharmony_ci } 789262306a36Sopenharmony_ci kfree (iobuf); 789362306a36Sopenharmony_ci return 0; 789462306a36Sopenharmony_ci} 789562306a36Sopenharmony_ci 789662306a36Sopenharmony_ci/* 789762306a36Sopenharmony_ci * Danger Will Robinson write the rids here 789862306a36Sopenharmony_ci */ 789962306a36Sopenharmony_ci 790062306a36Sopenharmony_cistatic int writerids(struct net_device *dev, aironet_ioctl *comp) 790162306a36Sopenharmony_ci{ 790262306a36Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 790362306a36Sopenharmony_ci int ridcode; 790462306a36Sopenharmony_ci int enabled; 790562306a36Sopenharmony_ci int (*writer)(struct airo_info *, u16 rid, const void *, int, int); 790662306a36Sopenharmony_ci unsigned char *iobuf; 790762306a36Sopenharmony_ci 790862306a36Sopenharmony_ci /* Only super-user can write RIDs */ 790962306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 791062306a36Sopenharmony_ci return -EPERM; 791162306a36Sopenharmony_ci 791262306a36Sopenharmony_ci if (test_bit(FLAG_FLASHING, &ai->flags)) 791362306a36Sopenharmony_ci return -EIO; 791462306a36Sopenharmony_ci 791562306a36Sopenharmony_ci ridcode = 0; 791662306a36Sopenharmony_ci writer = do_writerid; 791762306a36Sopenharmony_ci 791862306a36Sopenharmony_ci switch(comp->command) 791962306a36Sopenharmony_ci { 792062306a36Sopenharmony_ci case AIROPSIDS: ridcode = RID_SSID; break; 792162306a36Sopenharmony_ci case AIROPCAP: ridcode = RID_CAPABILITIES; break; 792262306a36Sopenharmony_ci case AIROPAPLIST: ridcode = RID_APLIST; break; 792362306a36Sopenharmony_ci case AIROPCFG: ai->config.len = 0; 792462306a36Sopenharmony_ci clear_bit(FLAG_COMMIT, &ai->flags); 792562306a36Sopenharmony_ci ridcode = RID_CONFIG; break; 792662306a36Sopenharmony_ci case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break; 792762306a36Sopenharmony_ci case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break; 792862306a36Sopenharmony_ci case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break; 792962306a36Sopenharmony_ci case AIROPWEPKEY: ridcode = RID_WEP_TEMP; writer = PC4500_writerid; 793062306a36Sopenharmony_ci break; 793162306a36Sopenharmony_ci case AIROPLEAPUSR+1: ridcode = 0xFF2A; break; 793262306a36Sopenharmony_ci case AIROPLEAPUSR+2: ridcode = 0xFF2B; break; 793362306a36Sopenharmony_ci 793462306a36Sopenharmony_ci /* this is not really a rid but a command given to the card 793562306a36Sopenharmony_ci * same with MAC off 793662306a36Sopenharmony_ci */ 793762306a36Sopenharmony_ci case AIROPMACON: 793862306a36Sopenharmony_ci if (enable_MAC(ai, 1) != 0) 793962306a36Sopenharmony_ci return -EIO; 794062306a36Sopenharmony_ci return 0; 794162306a36Sopenharmony_ci 794262306a36Sopenharmony_ci /* 794362306a36Sopenharmony_ci * Evidently this code in the airo driver does not get a symbol 794462306a36Sopenharmony_ci * as disable_MAC. it's probably so short the compiler does not gen one. 794562306a36Sopenharmony_ci */ 794662306a36Sopenharmony_ci case AIROPMACOFF: 794762306a36Sopenharmony_ci disable_MAC(ai, 1); 794862306a36Sopenharmony_ci return 0; 794962306a36Sopenharmony_ci 795062306a36Sopenharmony_ci /* This command merely clears the counts does not actually store any data 795162306a36Sopenharmony_ci * only reads rid. But as it changes the cards state, I put it in the 795262306a36Sopenharmony_ci * writerid routines. 795362306a36Sopenharmony_ci */ 795462306a36Sopenharmony_ci case AIROPSTCLR: 795562306a36Sopenharmony_ci if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL) 795662306a36Sopenharmony_ci return -ENOMEM; 795762306a36Sopenharmony_ci 795862306a36Sopenharmony_ci PC4500_readrid(ai, RID_STATSDELTACLEAR, iobuf, RIDSIZE, 1); 795962306a36Sopenharmony_ci 796062306a36Sopenharmony_ci enabled = ai->micstats.enabled; 796162306a36Sopenharmony_ci memset(&ai->micstats, 0, sizeof(ai->micstats)); 796262306a36Sopenharmony_ci ai->micstats.enabled = enabled; 796362306a36Sopenharmony_ci 796462306a36Sopenharmony_ci if (copy_to_user(comp->data, iobuf, 796562306a36Sopenharmony_ci min((int)comp->len, (int)RIDSIZE))) { 796662306a36Sopenharmony_ci kfree (iobuf); 796762306a36Sopenharmony_ci return -EFAULT; 796862306a36Sopenharmony_ci } 796962306a36Sopenharmony_ci kfree (iobuf); 797062306a36Sopenharmony_ci return 0; 797162306a36Sopenharmony_ci 797262306a36Sopenharmony_ci default: 797362306a36Sopenharmony_ci return -EOPNOTSUPP; /* Blarg! */ 797462306a36Sopenharmony_ci } 797562306a36Sopenharmony_ci if (comp->len > RIDSIZE) 797662306a36Sopenharmony_ci return -EINVAL; 797762306a36Sopenharmony_ci 797862306a36Sopenharmony_ci if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL) 797962306a36Sopenharmony_ci return -ENOMEM; 798062306a36Sopenharmony_ci 798162306a36Sopenharmony_ci if (copy_from_user(iobuf, comp->data, comp->len)) { 798262306a36Sopenharmony_ci kfree (iobuf); 798362306a36Sopenharmony_ci return -EFAULT; 798462306a36Sopenharmony_ci } 798562306a36Sopenharmony_ci 798662306a36Sopenharmony_ci if (comp->command == AIROPCFG) { 798762306a36Sopenharmony_ci ConfigRid *cfg = (ConfigRid *)iobuf; 798862306a36Sopenharmony_ci 798962306a36Sopenharmony_ci if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) 799062306a36Sopenharmony_ci cfg->opmode |= MODE_MIC; 799162306a36Sopenharmony_ci 799262306a36Sopenharmony_ci if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS) 799362306a36Sopenharmony_ci set_bit (FLAG_ADHOC, &ai->flags); 799462306a36Sopenharmony_ci else 799562306a36Sopenharmony_ci clear_bit (FLAG_ADHOC, &ai->flags); 799662306a36Sopenharmony_ci } 799762306a36Sopenharmony_ci 799862306a36Sopenharmony_ci if ((*writer)(ai, ridcode, iobuf, comp->len, 1)) { 799962306a36Sopenharmony_ci kfree (iobuf); 800062306a36Sopenharmony_ci return -EIO; 800162306a36Sopenharmony_ci } 800262306a36Sopenharmony_ci kfree (iobuf); 800362306a36Sopenharmony_ci return 0; 800462306a36Sopenharmony_ci} 800562306a36Sopenharmony_ci 800662306a36Sopenharmony_ci/***************************************************************************** 800762306a36Sopenharmony_ci * Ancillary flash / mod functions much black magic lurkes here * 800862306a36Sopenharmony_ci ***************************************************************************** 800962306a36Sopenharmony_ci */ 801062306a36Sopenharmony_ci 801162306a36Sopenharmony_ci/* 801262306a36Sopenharmony_ci * Flash command switch table 801362306a36Sopenharmony_ci */ 801462306a36Sopenharmony_ci 801562306a36Sopenharmony_cistatic int flashcard(struct net_device *dev, aironet_ioctl *comp) 801662306a36Sopenharmony_ci{ 801762306a36Sopenharmony_ci int z; 801862306a36Sopenharmony_ci 801962306a36Sopenharmony_ci /* Only super-user can modify flash */ 802062306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 802162306a36Sopenharmony_ci return -EPERM; 802262306a36Sopenharmony_ci 802362306a36Sopenharmony_ci switch(comp->command) 802462306a36Sopenharmony_ci { 802562306a36Sopenharmony_ci case AIROFLSHRST: 802662306a36Sopenharmony_ci return cmdreset((struct airo_info *)dev->ml_priv); 802762306a36Sopenharmony_ci 802862306a36Sopenharmony_ci case AIROFLSHSTFL: 802962306a36Sopenharmony_ci if (!AIRO_FLASH(dev) && 803062306a36Sopenharmony_ci (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL) 803162306a36Sopenharmony_ci return -ENOMEM; 803262306a36Sopenharmony_ci return setflashmode((struct airo_info *)dev->ml_priv); 803362306a36Sopenharmony_ci 803462306a36Sopenharmony_ci case AIROFLSHGCHR: /* Get char from aux */ 803562306a36Sopenharmony_ci if (comp->len != sizeof(int)) 803662306a36Sopenharmony_ci return -EINVAL; 803762306a36Sopenharmony_ci if (copy_from_user(&z, comp->data, comp->len)) 803862306a36Sopenharmony_ci return -EFAULT; 803962306a36Sopenharmony_ci return flashgchar((struct airo_info *)dev->ml_priv, z, 8000); 804062306a36Sopenharmony_ci 804162306a36Sopenharmony_ci case AIROFLSHPCHR: /* Send char to card. */ 804262306a36Sopenharmony_ci if (comp->len != sizeof(int)) 804362306a36Sopenharmony_ci return -EINVAL; 804462306a36Sopenharmony_ci if (copy_from_user(&z, comp->data, comp->len)) 804562306a36Sopenharmony_ci return -EFAULT; 804662306a36Sopenharmony_ci return flashpchar((struct airo_info *)dev->ml_priv, z, 8000); 804762306a36Sopenharmony_ci 804862306a36Sopenharmony_ci case AIROFLPUTBUF: /* Send 32k to card */ 804962306a36Sopenharmony_ci if (!AIRO_FLASH(dev)) 805062306a36Sopenharmony_ci return -ENOMEM; 805162306a36Sopenharmony_ci if (comp->len > FLASHSIZE) 805262306a36Sopenharmony_ci return -EINVAL; 805362306a36Sopenharmony_ci if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len)) 805462306a36Sopenharmony_ci return -EFAULT; 805562306a36Sopenharmony_ci 805662306a36Sopenharmony_ci flashputbuf((struct airo_info *)dev->ml_priv); 805762306a36Sopenharmony_ci return 0; 805862306a36Sopenharmony_ci 805962306a36Sopenharmony_ci case AIRORESTART: 806062306a36Sopenharmony_ci if (flashrestart((struct airo_info *)dev->ml_priv, dev)) 806162306a36Sopenharmony_ci return -EIO; 806262306a36Sopenharmony_ci return 0; 806362306a36Sopenharmony_ci } 806462306a36Sopenharmony_ci return -EINVAL; 806562306a36Sopenharmony_ci} 806662306a36Sopenharmony_ci 806762306a36Sopenharmony_ci#define FLASH_COMMAND 0x7e7e 806862306a36Sopenharmony_ci 806962306a36Sopenharmony_ci/* 807062306a36Sopenharmony_ci * STEP 1) 807162306a36Sopenharmony_ci * Disable MAC and do soft reset on 807262306a36Sopenharmony_ci * card. 807362306a36Sopenharmony_ci */ 807462306a36Sopenharmony_ci 807562306a36Sopenharmony_cistatic int cmdreset(struct airo_info *ai) 807662306a36Sopenharmony_ci{ 807762306a36Sopenharmony_ci disable_MAC(ai, 1); 807862306a36Sopenharmony_ci 807962306a36Sopenharmony_ci if (!waitbusy (ai)) { 808062306a36Sopenharmony_ci airo_print_info(ai->dev->name, "Waitbusy hang before RESET"); 808162306a36Sopenharmony_ci return -EBUSY; 808262306a36Sopenharmony_ci } 808362306a36Sopenharmony_ci 808462306a36Sopenharmony_ci OUT4500(ai, COMMAND, CMD_SOFTRESET); 808562306a36Sopenharmony_ci 808662306a36Sopenharmony_ci ssleep(1); /* WAS 600 12/7/00 */ 808762306a36Sopenharmony_ci 808862306a36Sopenharmony_ci if (!waitbusy (ai)) { 808962306a36Sopenharmony_ci airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET"); 809062306a36Sopenharmony_ci return -EBUSY; 809162306a36Sopenharmony_ci } 809262306a36Sopenharmony_ci return 0; 809362306a36Sopenharmony_ci} 809462306a36Sopenharmony_ci 809562306a36Sopenharmony_ci/* STEP 2) 809662306a36Sopenharmony_ci * Put the card in legendary flash 809762306a36Sopenharmony_ci * mode 809862306a36Sopenharmony_ci */ 809962306a36Sopenharmony_ci 810062306a36Sopenharmony_cistatic int setflashmode (struct airo_info *ai) 810162306a36Sopenharmony_ci{ 810262306a36Sopenharmony_ci set_bit (FLAG_FLASHING, &ai->flags); 810362306a36Sopenharmony_ci 810462306a36Sopenharmony_ci OUT4500(ai, SWS0, FLASH_COMMAND); 810562306a36Sopenharmony_ci OUT4500(ai, SWS1, FLASH_COMMAND); 810662306a36Sopenharmony_ci if (probe) { 810762306a36Sopenharmony_ci OUT4500(ai, SWS0, FLASH_COMMAND); 810862306a36Sopenharmony_ci OUT4500(ai, COMMAND, 0x10); 810962306a36Sopenharmony_ci } else { 811062306a36Sopenharmony_ci OUT4500(ai, SWS2, FLASH_COMMAND); 811162306a36Sopenharmony_ci OUT4500(ai, SWS3, FLASH_COMMAND); 811262306a36Sopenharmony_ci OUT4500(ai, COMMAND, 0); 811362306a36Sopenharmony_ci } 811462306a36Sopenharmony_ci msleep(500); /* 500ms delay */ 811562306a36Sopenharmony_ci 811662306a36Sopenharmony_ci if (!waitbusy(ai)) { 811762306a36Sopenharmony_ci clear_bit (FLAG_FLASHING, &ai->flags); 811862306a36Sopenharmony_ci airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode"); 811962306a36Sopenharmony_ci return -EIO; 812062306a36Sopenharmony_ci } 812162306a36Sopenharmony_ci return 0; 812262306a36Sopenharmony_ci} 812362306a36Sopenharmony_ci 812462306a36Sopenharmony_ci/* Put character to SWS0 wait for dwelltime 812562306a36Sopenharmony_ci * x 50us for echo . 812662306a36Sopenharmony_ci */ 812762306a36Sopenharmony_ci 812862306a36Sopenharmony_cistatic int flashpchar(struct airo_info *ai, int byte, int dwelltime) 812962306a36Sopenharmony_ci{ 813062306a36Sopenharmony_ci int echo; 813162306a36Sopenharmony_ci int waittime; 813262306a36Sopenharmony_ci 813362306a36Sopenharmony_ci byte |= 0x8000; 813462306a36Sopenharmony_ci 813562306a36Sopenharmony_ci if (dwelltime == 0) 813662306a36Sopenharmony_ci dwelltime = 200; 813762306a36Sopenharmony_ci 813862306a36Sopenharmony_ci waittime = dwelltime; 813962306a36Sopenharmony_ci 814062306a36Sopenharmony_ci /* Wait for busy bit d15 to go false indicating buffer empty */ 814162306a36Sopenharmony_ci while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) { 814262306a36Sopenharmony_ci udelay (50); 814362306a36Sopenharmony_ci waittime -= 50; 814462306a36Sopenharmony_ci } 814562306a36Sopenharmony_ci 814662306a36Sopenharmony_ci /* timeout for busy clear wait */ 814762306a36Sopenharmony_ci if (waittime <= 0) { 814862306a36Sopenharmony_ci airo_print_info(ai->dev->name, "flash putchar busywait timeout!"); 814962306a36Sopenharmony_ci return -EBUSY; 815062306a36Sopenharmony_ci } 815162306a36Sopenharmony_ci 815262306a36Sopenharmony_ci /* Port is clear now write byte and wait for it to echo back */ 815362306a36Sopenharmony_ci do { 815462306a36Sopenharmony_ci OUT4500(ai, SWS0, byte); 815562306a36Sopenharmony_ci udelay(50); 815662306a36Sopenharmony_ci dwelltime -= 50; 815762306a36Sopenharmony_ci echo = IN4500(ai, SWS1); 815862306a36Sopenharmony_ci } while (dwelltime >= 0 && echo != byte); 815962306a36Sopenharmony_ci 816062306a36Sopenharmony_ci OUT4500(ai, SWS1, 0); 816162306a36Sopenharmony_ci 816262306a36Sopenharmony_ci return (echo == byte) ? 0 : -EIO; 816362306a36Sopenharmony_ci} 816462306a36Sopenharmony_ci 816562306a36Sopenharmony_ci/* 816662306a36Sopenharmony_ci * Get a character from the card matching matchbyte 816762306a36Sopenharmony_ci * Step 3) 816862306a36Sopenharmony_ci */ 816962306a36Sopenharmony_cistatic int flashgchar(struct airo_info *ai, int matchbyte, int dwelltime) 817062306a36Sopenharmony_ci{ 817162306a36Sopenharmony_ci int rchar; 817262306a36Sopenharmony_ci unsigned char rbyte = 0; 817362306a36Sopenharmony_ci 817462306a36Sopenharmony_ci do { 817562306a36Sopenharmony_ci rchar = IN4500(ai, SWS1); 817662306a36Sopenharmony_ci 817762306a36Sopenharmony_ci if (dwelltime && !(0x8000 & rchar)) { 817862306a36Sopenharmony_ci dwelltime -= 10; 817962306a36Sopenharmony_ci mdelay(10); 818062306a36Sopenharmony_ci continue; 818162306a36Sopenharmony_ci } 818262306a36Sopenharmony_ci rbyte = 0xff & rchar; 818362306a36Sopenharmony_ci 818462306a36Sopenharmony_ci if ((rbyte == matchbyte) && (0x8000 & rchar)) { 818562306a36Sopenharmony_ci OUT4500(ai, SWS1, 0); 818662306a36Sopenharmony_ci return 0; 818762306a36Sopenharmony_ci } 818862306a36Sopenharmony_ci if (rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar) 818962306a36Sopenharmony_ci break; 819062306a36Sopenharmony_ci OUT4500(ai, SWS1, 0); 819162306a36Sopenharmony_ci 819262306a36Sopenharmony_ci } while (dwelltime > 0); 819362306a36Sopenharmony_ci return -EIO; 819462306a36Sopenharmony_ci} 819562306a36Sopenharmony_ci 819662306a36Sopenharmony_ci/* 819762306a36Sopenharmony_ci * Transfer 32k of firmware data from user buffer to our buffer and 819862306a36Sopenharmony_ci * send to the card 819962306a36Sopenharmony_ci */ 820062306a36Sopenharmony_ci 820162306a36Sopenharmony_cistatic int flashputbuf(struct airo_info *ai) 820262306a36Sopenharmony_ci{ 820362306a36Sopenharmony_ci int nwords; 820462306a36Sopenharmony_ci 820562306a36Sopenharmony_ci /* Write stuff */ 820662306a36Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) 820762306a36Sopenharmony_ci memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE); 820862306a36Sopenharmony_ci else { 820962306a36Sopenharmony_ci OUT4500(ai, AUXPAGE, 0x100); 821062306a36Sopenharmony_ci OUT4500(ai, AUXOFF, 0); 821162306a36Sopenharmony_ci 821262306a36Sopenharmony_ci for (nwords = 0; nwords != FLASHSIZE / 2; nwords++) { 821362306a36Sopenharmony_ci OUT4500(ai, AUXDATA, ai->flash[nwords] & 0xffff); 821462306a36Sopenharmony_ci } 821562306a36Sopenharmony_ci } 821662306a36Sopenharmony_ci OUT4500(ai, SWS0, 0x8000); 821762306a36Sopenharmony_ci 821862306a36Sopenharmony_ci return 0; 821962306a36Sopenharmony_ci} 822062306a36Sopenharmony_ci 822162306a36Sopenharmony_ci/* 822262306a36Sopenharmony_ci * 822362306a36Sopenharmony_ci */ 822462306a36Sopenharmony_cistatic int flashrestart(struct airo_info *ai, struct net_device *dev) 822562306a36Sopenharmony_ci{ 822662306a36Sopenharmony_ci int i, status; 822762306a36Sopenharmony_ci 822862306a36Sopenharmony_ci ssleep(1); /* Added 12/7/00 */ 822962306a36Sopenharmony_ci clear_bit (FLAG_FLASHING, &ai->flags); 823062306a36Sopenharmony_ci if (test_bit(FLAG_MPI, &ai->flags)) { 823162306a36Sopenharmony_ci status = mpi_init_descriptors(ai); 823262306a36Sopenharmony_ci if (status != SUCCESS) 823362306a36Sopenharmony_ci return status; 823462306a36Sopenharmony_ci } 823562306a36Sopenharmony_ci status = setup_card(ai, dev, 1); 823662306a36Sopenharmony_ci 823762306a36Sopenharmony_ci if (!test_bit(FLAG_MPI,&ai->flags)) 823862306a36Sopenharmony_ci for (i = 0; i < MAX_FIDS; i++) { 823962306a36Sopenharmony_ci ai->fids[i] = transmit_allocate 824062306a36Sopenharmony_ci (ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2); 824162306a36Sopenharmony_ci } 824262306a36Sopenharmony_ci 824362306a36Sopenharmony_ci ssleep(1); /* Added 12/7/00 */ 824462306a36Sopenharmony_ci return status; 824562306a36Sopenharmony_ci} 824662306a36Sopenharmony_ci#endif /* CISCO_EXT */ 824762306a36Sopenharmony_ci 824862306a36Sopenharmony_ci/* 824962306a36Sopenharmony_ci This program is free software; you can redistribute it and/or 825062306a36Sopenharmony_ci modify it under the terms of the GNU General Public License 825162306a36Sopenharmony_ci as published by the Free Software Foundation; either version 2 825262306a36Sopenharmony_ci of the License, or (at your option) any later version. 825362306a36Sopenharmony_ci 825462306a36Sopenharmony_ci This program is distributed in the hope that it will be useful, 825562306a36Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 825662306a36Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 825762306a36Sopenharmony_ci GNU General Public License for more details. 825862306a36Sopenharmony_ci 825962306a36Sopenharmony_ci In addition: 826062306a36Sopenharmony_ci 826162306a36Sopenharmony_ci Redistribution and use in source and binary forms, with or without 826262306a36Sopenharmony_ci modification, are permitted provided that the following conditions 826362306a36Sopenharmony_ci are met: 826462306a36Sopenharmony_ci 826562306a36Sopenharmony_ci 1. Redistributions of source code must retain the above copyright 826662306a36Sopenharmony_ci notice, this list of conditions and the following disclaimer. 826762306a36Sopenharmony_ci 2. Redistributions in binary form must reproduce the above copyright 826862306a36Sopenharmony_ci notice, this list of conditions and the following disclaimer in the 826962306a36Sopenharmony_ci documentation and/or other materials provided with the distribution. 827062306a36Sopenharmony_ci 3. The name of the author may not be used to endorse or promote 827162306a36Sopenharmony_ci products derived from this software without specific prior written 827262306a36Sopenharmony_ci permission. 827362306a36Sopenharmony_ci 827462306a36Sopenharmony_ci THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 827562306a36Sopenharmony_ci IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 827662306a36Sopenharmony_ci WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 827762306a36Sopenharmony_ci ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 827862306a36Sopenharmony_ci INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 827962306a36Sopenharmony_ci (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 828062306a36Sopenharmony_ci SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 828162306a36Sopenharmony_ci HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 828262306a36Sopenharmony_ci STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 828362306a36Sopenharmony_ci IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 828462306a36Sopenharmony_ci POSSIBILITY OF SUCH DAMAGE. 828562306a36Sopenharmony_ci*/ 828662306a36Sopenharmony_ci 828762306a36Sopenharmony_cimodule_init(airo_init_module); 828862306a36Sopenharmony_cimodule_exit(airo_cleanup_module); 8289