18c2ecf20Sopenharmony_ci/*====================================================================== 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci Aironet driver for 4500 and 4800 series cards 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci This code is released under both the GPL version 2 and BSD licenses. 68c2ecf20Sopenharmony_ci Either license may be used. The respective licenses are found at 78c2ecf20Sopenharmony_ci the end of this file. 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci This code was developed by Benjamin Reed <breed@users.sourceforge.net> 108c2ecf20Sopenharmony_ci including portions of which come from the Aironet PC4500 118c2ecf20Sopenharmony_ci Developer's Reference Manual and used with permission. Copyright 128c2ecf20Sopenharmony_ci (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use 138c2ecf20Sopenharmony_ci code in the Developer's manual was granted for this driver by 148c2ecf20Sopenharmony_ci Aironet. Major code contributions were received from Javier Achirica 158c2ecf20Sopenharmony_ci <achirica@users.sourceforge.net> and Jean Tourrilhes <jt@hpl.hp.com>. 168c2ecf20Sopenharmony_ci Code was also integrated from the Cisco Aironet driver for Linux. 178c2ecf20Sopenharmony_ci Support for MPI350 cards was added by Fabrice Bellet 188c2ecf20Sopenharmony_ci <fabrice@bellet.info>. 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci======================================================================*/ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/err.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/kernel.h> 268c2ecf20Sopenharmony_ci#include <linux/module.h> 278c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/sched.h> 308c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 318c2ecf20Sopenharmony_ci#include <linux/slab.h> 328c2ecf20Sopenharmony_ci#include <linux/string.h> 338c2ecf20Sopenharmony_ci#include <linux/timer.h> 348c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 358c2ecf20Sopenharmony_ci#include <linux/in.h> 368c2ecf20Sopenharmony_ci#include <linux/bitops.h> 378c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 388c2ecf20Sopenharmony_ci#include <linux/crypto.h> 398c2ecf20Sopenharmony_ci#include <linux/io.h> 408c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 438c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 448c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 458c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 468c2ecf20Sopenharmony_ci#include <linux/ioport.h> 478c2ecf20Sopenharmony_ci#include <linux/pci.h> 488c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 498c2ecf20Sopenharmony_ci#include <linux/kthread.h> 508c2ecf20Sopenharmony_ci#include <linux/freezer.h> 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#include <crypto/aes.h> 538c2ecf20Sopenharmony_ci#include <crypto/skcipher.h> 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#include <net/cfg80211.h> 568c2ecf20Sopenharmony_ci#include <net/iw_handler.h> 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#include "airo.h" 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define DRV_NAME "airo" 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 638c2ecf20Sopenharmony_cistatic const struct pci_device_id card_ids[] = { 648c2ecf20Sopenharmony_ci { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, }, 658c2ecf20Sopenharmony_ci { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID }, 668c2ecf20Sopenharmony_ci { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, }, 678c2ecf20Sopenharmony_ci { 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, }, 688c2ecf20Sopenharmony_ci { 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, }, 698c2ecf20Sopenharmony_ci { 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, }, 708c2ecf20Sopenharmony_ci { 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, }, 718c2ecf20Sopenharmony_ci { 0, } 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, card_ids); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int airo_pci_probe(struct pci_dev *, const struct pci_device_id *); 768c2ecf20Sopenharmony_cistatic void airo_pci_remove(struct pci_dev *); 778c2ecf20Sopenharmony_cistatic int __maybe_unused airo_pci_suspend(struct device *dev); 788c2ecf20Sopenharmony_cistatic int __maybe_unused airo_pci_resume(struct device *dev); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(airo_pci_pm_ops, 818c2ecf20Sopenharmony_ci airo_pci_suspend, 828c2ecf20Sopenharmony_ci airo_pci_resume); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic struct pci_driver airo_driver = { 858c2ecf20Sopenharmony_ci .name = DRV_NAME, 868c2ecf20Sopenharmony_ci .id_table = card_ids, 878c2ecf20Sopenharmony_ci .probe = airo_pci_probe, 888c2ecf20Sopenharmony_ci .remove = airo_pci_remove, 898c2ecf20Sopenharmony_ci .driver.pm = &airo_pci_pm_ops, 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* Include Wireless Extension definition and check version - Jean II */ 948c2ecf20Sopenharmony_ci#include <linux/wireless.h> 958c2ecf20Sopenharmony_ci#define WIRELESS_SPY /* enable iwspy support */ 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define CISCO_EXT /* enable Cisco extensions */ 988c2ecf20Sopenharmony_ci#ifdef CISCO_EXT 998c2ecf20Sopenharmony_ci#include <linux/delay.h> 1008c2ecf20Sopenharmony_ci#endif 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* Hack to do some power saving */ 1038c2ecf20Sopenharmony_ci#define POWER_ON_DOWN 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* As you can see this list is HUGH! 1068c2ecf20Sopenharmony_ci I really don't know what a lot of these counts are about, but they 1078c2ecf20Sopenharmony_ci are all here for completeness. If the IGNLABEL macro is put in 1088c2ecf20Sopenharmony_ci infront of the label, that statistic will not be included in the list 1098c2ecf20Sopenharmony_ci of statistics in the /proc filesystem */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define IGNLABEL(comment) NULL 1128c2ecf20Sopenharmony_cistatic const char *statsLabels[] = { 1138c2ecf20Sopenharmony_ci "RxOverrun", 1148c2ecf20Sopenharmony_ci IGNLABEL("RxPlcpCrcErr"), 1158c2ecf20Sopenharmony_ci IGNLABEL("RxPlcpFormatErr"), 1168c2ecf20Sopenharmony_ci IGNLABEL("RxPlcpLengthErr"), 1178c2ecf20Sopenharmony_ci "RxMacCrcErr", 1188c2ecf20Sopenharmony_ci "RxMacCrcOk", 1198c2ecf20Sopenharmony_ci "RxWepErr", 1208c2ecf20Sopenharmony_ci "RxWepOk", 1218c2ecf20Sopenharmony_ci "RetryLong", 1228c2ecf20Sopenharmony_ci "RetryShort", 1238c2ecf20Sopenharmony_ci "MaxRetries", 1248c2ecf20Sopenharmony_ci "NoAck", 1258c2ecf20Sopenharmony_ci "NoCts", 1268c2ecf20Sopenharmony_ci "RxAck", 1278c2ecf20Sopenharmony_ci "RxCts", 1288c2ecf20Sopenharmony_ci "TxAck", 1298c2ecf20Sopenharmony_ci "TxRts", 1308c2ecf20Sopenharmony_ci "TxCts", 1318c2ecf20Sopenharmony_ci "TxMc", 1328c2ecf20Sopenharmony_ci "TxBc", 1338c2ecf20Sopenharmony_ci "TxUcFrags", 1348c2ecf20Sopenharmony_ci "TxUcPackets", 1358c2ecf20Sopenharmony_ci "TxBeacon", 1368c2ecf20Sopenharmony_ci "RxBeacon", 1378c2ecf20Sopenharmony_ci "TxSinColl", 1388c2ecf20Sopenharmony_ci "TxMulColl", 1398c2ecf20Sopenharmony_ci "DefersNo", 1408c2ecf20Sopenharmony_ci "DefersProt", 1418c2ecf20Sopenharmony_ci "DefersEngy", 1428c2ecf20Sopenharmony_ci "DupFram", 1438c2ecf20Sopenharmony_ci "RxFragDisc", 1448c2ecf20Sopenharmony_ci "TxAged", 1458c2ecf20Sopenharmony_ci "RxAged", 1468c2ecf20Sopenharmony_ci "LostSync-MaxRetry", 1478c2ecf20Sopenharmony_ci "LostSync-MissedBeacons", 1488c2ecf20Sopenharmony_ci "LostSync-ArlExceeded", 1498c2ecf20Sopenharmony_ci "LostSync-Deauth", 1508c2ecf20Sopenharmony_ci "LostSync-Disassoced", 1518c2ecf20Sopenharmony_ci "LostSync-TsfTiming", 1528c2ecf20Sopenharmony_ci "HostTxMc", 1538c2ecf20Sopenharmony_ci "HostTxBc", 1548c2ecf20Sopenharmony_ci "HostTxUc", 1558c2ecf20Sopenharmony_ci "HostTxFail", 1568c2ecf20Sopenharmony_ci "HostRxMc", 1578c2ecf20Sopenharmony_ci "HostRxBc", 1588c2ecf20Sopenharmony_ci "HostRxUc", 1598c2ecf20Sopenharmony_ci "HostRxDiscard", 1608c2ecf20Sopenharmony_ci IGNLABEL("HmacTxMc"), 1618c2ecf20Sopenharmony_ci IGNLABEL("HmacTxBc"), 1628c2ecf20Sopenharmony_ci IGNLABEL("HmacTxUc"), 1638c2ecf20Sopenharmony_ci IGNLABEL("HmacTxFail"), 1648c2ecf20Sopenharmony_ci IGNLABEL("HmacRxMc"), 1658c2ecf20Sopenharmony_ci IGNLABEL("HmacRxBc"), 1668c2ecf20Sopenharmony_ci IGNLABEL("HmacRxUc"), 1678c2ecf20Sopenharmony_ci IGNLABEL("HmacRxDiscard"), 1688c2ecf20Sopenharmony_ci IGNLABEL("HmacRxAccepted"), 1698c2ecf20Sopenharmony_ci "SsidMismatch", 1708c2ecf20Sopenharmony_ci "ApMismatch", 1718c2ecf20Sopenharmony_ci "RatesMismatch", 1728c2ecf20Sopenharmony_ci "AuthReject", 1738c2ecf20Sopenharmony_ci "AuthTimeout", 1748c2ecf20Sopenharmony_ci "AssocReject", 1758c2ecf20Sopenharmony_ci "AssocTimeout", 1768c2ecf20Sopenharmony_ci IGNLABEL("ReasonOutsideTable"), 1778c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus1"), 1788c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus2"), 1798c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus3"), 1808c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus4"), 1818c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus5"), 1828c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus6"), 1838c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus7"), 1848c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus8"), 1858c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus9"), 1868c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus10"), 1878c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus11"), 1888c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus12"), 1898c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus13"), 1908c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus14"), 1918c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus15"), 1928c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus16"), 1938c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus17"), 1948c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus18"), 1958c2ecf20Sopenharmony_ci IGNLABEL("ReasonStatus19"), 1968c2ecf20Sopenharmony_ci "RxMan", 1978c2ecf20Sopenharmony_ci "TxMan", 1988c2ecf20Sopenharmony_ci "RxRefresh", 1998c2ecf20Sopenharmony_ci "TxRefresh", 2008c2ecf20Sopenharmony_ci "RxPoll", 2018c2ecf20Sopenharmony_ci "TxPoll", 2028c2ecf20Sopenharmony_ci "HostRetries", 2038c2ecf20Sopenharmony_ci "LostSync-HostReq", 2048c2ecf20Sopenharmony_ci "HostTxBytes", 2058c2ecf20Sopenharmony_ci "HostRxBytes", 2068c2ecf20Sopenharmony_ci "ElapsedUsec", 2078c2ecf20Sopenharmony_ci "ElapsedSec", 2088c2ecf20Sopenharmony_ci "LostSyncBetterAP", 2098c2ecf20Sopenharmony_ci "PrivacyMismatch", 2108c2ecf20Sopenharmony_ci "Jammed", 2118c2ecf20Sopenharmony_ci "DiscRxNotWepped", 2128c2ecf20Sopenharmony_ci "PhyEleMismatch", 2138c2ecf20Sopenharmony_ci (char*)-1 }; 2148c2ecf20Sopenharmony_ci#ifndef RUN_AT 2158c2ecf20Sopenharmony_ci#define RUN_AT(x) (jiffies+(x)) 2168c2ecf20Sopenharmony_ci#endif 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* These variables are for insmod, since it seems that the rates 2208c2ecf20Sopenharmony_ci can only be set in setup_card. Rates should be a comma separated 2218c2ecf20Sopenharmony_ci (no spaces) list of rates (up to 8). */ 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic int rates[8]; 2248c2ecf20Sopenharmony_cistatic char *ssids[3]; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int io[4]; 2278c2ecf20Sopenharmony_cistatic int irq[4]; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic 2308c2ecf20Sopenharmony_ciint maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at. 2318c2ecf20Sopenharmony_ci 0 means no limit. For old cards this was 4 */ 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */ 2348c2ecf20Sopenharmony_cistatic int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read 2358c2ecf20Sopenharmony_ci the bap, needed on some older cards and buses. */ 2368c2ecf20Sopenharmony_cistatic int adhoc; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int probe = 1; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic kuid_t proc_kuid; 2418c2ecf20Sopenharmony_cistatic int proc_uid /* = 0 */; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic kgid_t proc_kgid; 2448c2ecf20Sopenharmony_cistatic int proc_gid /* = 0 */; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int airo_perm = 0555; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int proc_perm = 0644; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ciMODULE_AUTHOR("Benjamin Reed"); 2518c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet cards. " 2528c2ecf20Sopenharmony_ci "Direct support for ISA/PCI/MPI cards and support for PCMCIA when used with airo_cs."); 2538c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 2548c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350"); 2558c2ecf20Sopenharmony_cimodule_param_hw_array(io, int, ioport, NULL, 0); 2568c2ecf20Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0); 2578c2ecf20Sopenharmony_cimodule_param_array(rates, int, NULL, 0); 2588c2ecf20Sopenharmony_cimodule_param_array(ssids, charp, NULL, 0); 2598c2ecf20Sopenharmony_cimodule_param(auto_wep, int, 0); 2608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(auto_wep, 2618c2ecf20Sopenharmony_ci "If non-zero, the driver will keep looping through the authentication options until an association is made. " 2628c2ecf20Sopenharmony_ci "The value of auto_wep is number of the wep keys to check. " 2638c2ecf20Sopenharmony_ci "A value of 2 will try using the key at index 0 and index 1."); 2648c2ecf20Sopenharmony_cimodule_param(aux_bap, int, 0); 2658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(aux_bap, 2668c2ecf20Sopenharmony_ci "If non-zero, the driver will switch into a mode that seems to work better for older cards with some older buses. " 2678c2ecf20Sopenharmony_ci "Before switching it checks that the switch is needed."); 2688c2ecf20Sopenharmony_cimodule_param(maxencrypt, int, 0); 2698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(maxencrypt, 2708c2ecf20Sopenharmony_ci "The maximum speed that the card can do encryption. " 2718c2ecf20Sopenharmony_ci "Units are in 512kbs. " 2728c2ecf20Sopenharmony_ci "Zero (default) means there is no limit. " 2738c2ecf20Sopenharmony_ci "Older cards used to be limited to 2mbs (4)."); 2748c2ecf20Sopenharmony_cimodule_param(adhoc, int, 0); 2758c2ecf20Sopenharmony_ciMODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode."); 2768c2ecf20Sopenharmony_cimodule_param(probe, int, 0); 2778c2ecf20Sopenharmony_ciMODULE_PARM_DESC(probe, "If zero, the driver won't start the card."); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cimodule_param(proc_uid, int, 0); 2808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to."); 2818c2ecf20Sopenharmony_cimodule_param(proc_gid, int, 0); 2828c2ecf20Sopenharmony_ciMODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to."); 2838c2ecf20Sopenharmony_cimodule_param(airo_perm, int, 0); 2848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet."); 2858c2ecf20Sopenharmony_cimodule_param(proc_perm, int, 0); 2868c2ecf20Sopenharmony_ciMODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc"); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/* This is a kind of sloppy hack to get this information to OUT4500 and 2898c2ecf20Sopenharmony_ci IN4500. I would be extremely interested in the situation where this 2908c2ecf20Sopenharmony_ci doesn't work though!!! */ 2918c2ecf20Sopenharmony_cistatic int do8bitIO /* = 0 */; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* Return codes */ 2948c2ecf20Sopenharmony_ci#define SUCCESS 0 2958c2ecf20Sopenharmony_ci#define ERROR -1 2968c2ecf20Sopenharmony_ci#define NO_PACKET -2 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/* Commands */ 2998c2ecf20Sopenharmony_ci#define NOP2 0x0000 3008c2ecf20Sopenharmony_ci#define MAC_ENABLE 0x0001 3018c2ecf20Sopenharmony_ci#define MAC_DISABLE 0x0002 3028c2ecf20Sopenharmony_ci#define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */ 3038c2ecf20Sopenharmony_ci#define CMD_SOFTRESET 0x0004 3048c2ecf20Sopenharmony_ci#define HOSTSLEEP 0x0005 3058c2ecf20Sopenharmony_ci#define CMD_MAGIC_PKT 0x0006 3068c2ecf20Sopenharmony_ci#define CMD_SETWAKEMASK 0x0007 3078c2ecf20Sopenharmony_ci#define CMD_READCFG 0x0008 3088c2ecf20Sopenharmony_ci#define CMD_SETMODE 0x0009 3098c2ecf20Sopenharmony_ci#define CMD_ALLOCATETX 0x000a 3108c2ecf20Sopenharmony_ci#define CMD_TRANSMIT 0x000b 3118c2ecf20Sopenharmony_ci#define CMD_DEALLOCATETX 0x000c 3128c2ecf20Sopenharmony_ci#define NOP 0x0010 3138c2ecf20Sopenharmony_ci#define CMD_WORKAROUND 0x0011 3148c2ecf20Sopenharmony_ci#define CMD_ALLOCATEAUX 0x0020 3158c2ecf20Sopenharmony_ci#define CMD_ACCESS 0x0021 3168c2ecf20Sopenharmony_ci#define CMD_PCIBAP 0x0022 3178c2ecf20Sopenharmony_ci#define CMD_PCIAUX 0x0023 3188c2ecf20Sopenharmony_ci#define CMD_ALLOCBUF 0x0028 3198c2ecf20Sopenharmony_ci#define CMD_GETTLV 0x0029 3208c2ecf20Sopenharmony_ci#define CMD_PUTTLV 0x002a 3218c2ecf20Sopenharmony_ci#define CMD_DELTLV 0x002b 3228c2ecf20Sopenharmony_ci#define CMD_FINDNEXTTLV 0x002c 3238c2ecf20Sopenharmony_ci#define CMD_PSPNODES 0x0030 3248c2ecf20Sopenharmony_ci#define CMD_SETCW 0x0031 3258c2ecf20Sopenharmony_ci#define CMD_SETPCF 0x0032 3268c2ecf20Sopenharmony_ci#define CMD_SETPHYREG 0x003e 3278c2ecf20Sopenharmony_ci#define CMD_TXTEST 0x003f 3288c2ecf20Sopenharmony_ci#define MAC_ENABLETX 0x0101 3298c2ecf20Sopenharmony_ci#define CMD_LISTBSS 0x0103 3308c2ecf20Sopenharmony_ci#define CMD_SAVECFG 0x0108 3318c2ecf20Sopenharmony_ci#define CMD_ENABLEAUX 0x0111 3328c2ecf20Sopenharmony_ci#define CMD_WRITERID 0x0121 3338c2ecf20Sopenharmony_ci#define CMD_USEPSPNODES 0x0130 3348c2ecf20Sopenharmony_ci#define MAC_ENABLERX 0x0201 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* Command errors */ 3378c2ecf20Sopenharmony_ci#define ERROR_QUALIF 0x00 3388c2ecf20Sopenharmony_ci#define ERROR_ILLCMD 0x01 3398c2ecf20Sopenharmony_ci#define ERROR_ILLFMT 0x02 3408c2ecf20Sopenharmony_ci#define ERROR_INVFID 0x03 3418c2ecf20Sopenharmony_ci#define ERROR_INVRID 0x04 3428c2ecf20Sopenharmony_ci#define ERROR_LARGE 0x05 3438c2ecf20Sopenharmony_ci#define ERROR_NDISABL 0x06 3448c2ecf20Sopenharmony_ci#define ERROR_ALLOCBSY 0x07 3458c2ecf20Sopenharmony_ci#define ERROR_NORD 0x0B 3468c2ecf20Sopenharmony_ci#define ERROR_NOWR 0x0C 3478c2ecf20Sopenharmony_ci#define ERROR_INVFIDTX 0x0D 3488c2ecf20Sopenharmony_ci#define ERROR_TESTACT 0x0E 3498c2ecf20Sopenharmony_ci#define ERROR_TAGNFND 0x12 3508c2ecf20Sopenharmony_ci#define ERROR_DECODE 0x20 3518c2ecf20Sopenharmony_ci#define ERROR_DESCUNAV 0x21 3528c2ecf20Sopenharmony_ci#define ERROR_BADLEN 0x22 3538c2ecf20Sopenharmony_ci#define ERROR_MODE 0x80 3548c2ecf20Sopenharmony_ci#define ERROR_HOP 0x81 3558c2ecf20Sopenharmony_ci#define ERROR_BINTER 0x82 3568c2ecf20Sopenharmony_ci#define ERROR_RXMODE 0x83 3578c2ecf20Sopenharmony_ci#define ERROR_MACADDR 0x84 3588c2ecf20Sopenharmony_ci#define ERROR_RATES 0x85 3598c2ecf20Sopenharmony_ci#define ERROR_ORDER 0x86 3608c2ecf20Sopenharmony_ci#define ERROR_SCAN 0x87 3618c2ecf20Sopenharmony_ci#define ERROR_AUTH 0x88 3628c2ecf20Sopenharmony_ci#define ERROR_PSMODE 0x89 3638c2ecf20Sopenharmony_ci#define ERROR_RTYPE 0x8A 3648c2ecf20Sopenharmony_ci#define ERROR_DIVER 0x8B 3658c2ecf20Sopenharmony_ci#define ERROR_SSID 0x8C 3668c2ecf20Sopenharmony_ci#define ERROR_APLIST 0x8D 3678c2ecf20Sopenharmony_ci#define ERROR_AUTOWAKE 0x8E 3688c2ecf20Sopenharmony_ci#define ERROR_LEAP 0x8F 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/* Registers */ 3718c2ecf20Sopenharmony_ci#define COMMAND 0x00 3728c2ecf20Sopenharmony_ci#define PARAM0 0x02 3738c2ecf20Sopenharmony_ci#define PARAM1 0x04 3748c2ecf20Sopenharmony_ci#define PARAM2 0x06 3758c2ecf20Sopenharmony_ci#define STATUS 0x08 3768c2ecf20Sopenharmony_ci#define RESP0 0x0a 3778c2ecf20Sopenharmony_ci#define RESP1 0x0c 3788c2ecf20Sopenharmony_ci#define RESP2 0x0e 3798c2ecf20Sopenharmony_ci#define LINKSTAT 0x10 3808c2ecf20Sopenharmony_ci#define SELECT0 0x18 3818c2ecf20Sopenharmony_ci#define OFFSET0 0x1c 3828c2ecf20Sopenharmony_ci#define RXFID 0x20 3838c2ecf20Sopenharmony_ci#define TXALLOCFID 0x22 3848c2ecf20Sopenharmony_ci#define TXCOMPLFID 0x24 3858c2ecf20Sopenharmony_ci#define DATA0 0x36 3868c2ecf20Sopenharmony_ci#define EVSTAT 0x30 3878c2ecf20Sopenharmony_ci#define EVINTEN 0x32 3888c2ecf20Sopenharmony_ci#define EVACK 0x34 3898c2ecf20Sopenharmony_ci#define SWS0 0x28 3908c2ecf20Sopenharmony_ci#define SWS1 0x2a 3918c2ecf20Sopenharmony_ci#define SWS2 0x2c 3928c2ecf20Sopenharmony_ci#define SWS3 0x2e 3938c2ecf20Sopenharmony_ci#define AUXPAGE 0x3A 3948c2ecf20Sopenharmony_ci#define AUXOFF 0x3C 3958c2ecf20Sopenharmony_ci#define AUXDATA 0x3E 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci#define FID_TX 1 3988c2ecf20Sopenharmony_ci#define FID_RX 2 3998c2ecf20Sopenharmony_ci/* Offset into aux memory for descriptors */ 4008c2ecf20Sopenharmony_ci#define AUX_OFFSET 0x800 4018c2ecf20Sopenharmony_ci/* Size of allocated packets */ 4028c2ecf20Sopenharmony_ci#define PKTSIZE 1840 4038c2ecf20Sopenharmony_ci#define RIDSIZE 2048 4048c2ecf20Sopenharmony_ci/* Size of the transmit queue */ 4058c2ecf20Sopenharmony_ci#define MAXTXQ 64 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/* BAP selectors */ 4088c2ecf20Sopenharmony_ci#define BAP0 0 /* Used for receiving packets */ 4098c2ecf20Sopenharmony_ci#define BAP1 2 /* Used for xmiting packets and working with RIDS */ 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/* Flags */ 4128c2ecf20Sopenharmony_ci#define COMMAND_BUSY 0x8000 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci#define BAP_BUSY 0x8000 4158c2ecf20Sopenharmony_ci#define BAP_ERR 0x4000 4168c2ecf20Sopenharmony_ci#define BAP_DONE 0x2000 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci#define PROMISC 0xffff 4198c2ecf20Sopenharmony_ci#define NOPROMISC 0x0000 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci#define EV_CMD 0x10 4228c2ecf20Sopenharmony_ci#define EV_CLEARCOMMANDBUSY 0x4000 4238c2ecf20Sopenharmony_ci#define EV_RX 0x01 4248c2ecf20Sopenharmony_ci#define EV_TX 0x02 4258c2ecf20Sopenharmony_ci#define EV_TXEXC 0x04 4268c2ecf20Sopenharmony_ci#define EV_ALLOC 0x08 4278c2ecf20Sopenharmony_ci#define EV_LINK 0x80 4288c2ecf20Sopenharmony_ci#define EV_AWAKE 0x100 4298c2ecf20Sopenharmony_ci#define EV_TXCPY 0x400 4308c2ecf20Sopenharmony_ci#define EV_UNKNOWN 0x800 4318c2ecf20Sopenharmony_ci#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */ 4328c2ecf20Sopenharmony_ci#define EV_AWAKEN 0x2000 4338c2ecf20Sopenharmony_ci#define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC) 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci#ifdef CHECK_UNKNOWN_INTS 4368c2ecf20Sopenharmony_ci#define IGNORE_INTS (EV_CMD | EV_UNKNOWN) 4378c2ecf20Sopenharmony_ci#else 4388c2ecf20Sopenharmony_ci#define IGNORE_INTS (~STATUS_INTS) 4398c2ecf20Sopenharmony_ci#endif 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* RID TYPES */ 4428c2ecf20Sopenharmony_ci#define RID_RW 0x20 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci/* The RIDs */ 4458c2ecf20Sopenharmony_ci#define RID_CAPABILITIES 0xFF00 4468c2ecf20Sopenharmony_ci#define RID_APINFO 0xFF01 4478c2ecf20Sopenharmony_ci#define RID_RADIOINFO 0xFF02 4488c2ecf20Sopenharmony_ci#define RID_UNKNOWN3 0xFF03 4498c2ecf20Sopenharmony_ci#define RID_RSSI 0xFF04 4508c2ecf20Sopenharmony_ci#define RID_CONFIG 0xFF10 4518c2ecf20Sopenharmony_ci#define RID_SSID 0xFF11 4528c2ecf20Sopenharmony_ci#define RID_APLIST 0xFF12 4538c2ecf20Sopenharmony_ci#define RID_DRVNAME 0xFF13 4548c2ecf20Sopenharmony_ci#define RID_ETHERENCAP 0xFF14 4558c2ecf20Sopenharmony_ci#define RID_WEP_TEMP 0xFF15 4568c2ecf20Sopenharmony_ci#define RID_WEP_PERM 0xFF16 4578c2ecf20Sopenharmony_ci#define RID_MODULATION 0xFF17 4588c2ecf20Sopenharmony_ci#define RID_OPTIONS 0xFF18 4598c2ecf20Sopenharmony_ci#define RID_ACTUALCONFIG 0xFF20 /*readonly*/ 4608c2ecf20Sopenharmony_ci#define RID_FACTORYCONFIG 0xFF21 4618c2ecf20Sopenharmony_ci#define RID_UNKNOWN22 0xFF22 4628c2ecf20Sopenharmony_ci#define RID_LEAPUSERNAME 0xFF23 4638c2ecf20Sopenharmony_ci#define RID_LEAPPASSWORD 0xFF24 4648c2ecf20Sopenharmony_ci#define RID_STATUS 0xFF50 4658c2ecf20Sopenharmony_ci#define RID_BEACON_HST 0xFF51 4668c2ecf20Sopenharmony_ci#define RID_BUSY_HST 0xFF52 4678c2ecf20Sopenharmony_ci#define RID_RETRIES_HST 0xFF53 4688c2ecf20Sopenharmony_ci#define RID_UNKNOWN54 0xFF54 4698c2ecf20Sopenharmony_ci#define RID_UNKNOWN55 0xFF55 4708c2ecf20Sopenharmony_ci#define RID_UNKNOWN56 0xFF56 4718c2ecf20Sopenharmony_ci#define RID_MIC 0xFF57 4728c2ecf20Sopenharmony_ci#define RID_STATS16 0xFF60 4738c2ecf20Sopenharmony_ci#define RID_STATS16DELTA 0xFF61 4748c2ecf20Sopenharmony_ci#define RID_STATS16DELTACLEAR 0xFF62 4758c2ecf20Sopenharmony_ci#define RID_STATS 0xFF68 4768c2ecf20Sopenharmony_ci#define RID_STATSDELTA 0xFF69 4778c2ecf20Sopenharmony_ci#define RID_STATSDELTACLEAR 0xFF6A 4788c2ecf20Sopenharmony_ci#define RID_ECHOTEST_RID 0xFF70 4798c2ecf20Sopenharmony_ci#define RID_ECHOTEST_RESULTS 0xFF71 4808c2ecf20Sopenharmony_ci#define RID_BSSLISTFIRST 0xFF72 4818c2ecf20Sopenharmony_ci#define RID_BSSLISTNEXT 0xFF73 4828c2ecf20Sopenharmony_ci#define RID_WPA_BSSLISTFIRST 0xFF74 4838c2ecf20Sopenharmony_ci#define RID_WPA_BSSLISTNEXT 0xFF75 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_citypedef struct { 4868c2ecf20Sopenharmony_ci u16 cmd; 4878c2ecf20Sopenharmony_ci u16 parm0; 4888c2ecf20Sopenharmony_ci u16 parm1; 4898c2ecf20Sopenharmony_ci u16 parm2; 4908c2ecf20Sopenharmony_ci} Cmd; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_citypedef struct { 4938c2ecf20Sopenharmony_ci u16 status; 4948c2ecf20Sopenharmony_ci u16 rsp0; 4958c2ecf20Sopenharmony_ci u16 rsp1; 4968c2ecf20Sopenharmony_ci u16 rsp2; 4978c2ecf20Sopenharmony_ci} Resp; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci/* 5008c2ecf20Sopenharmony_ci * Rids and endian-ness: The Rids will always be in cpu endian, since 5018c2ecf20Sopenharmony_ci * this all the patches from the big-endian guys end up doing that. 5028c2ecf20Sopenharmony_ci * so all rid access should use the read/writeXXXRid routines. 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/* This structure came from an email sent to me from an engineer at 5068c2ecf20Sopenharmony_ci aironet for inclusion into this driver */ 5078c2ecf20Sopenharmony_citypedef struct WepKeyRid WepKeyRid; 5088c2ecf20Sopenharmony_cistruct WepKeyRid { 5098c2ecf20Sopenharmony_ci __le16 len; 5108c2ecf20Sopenharmony_ci __le16 kindex; 5118c2ecf20Sopenharmony_ci u8 mac[ETH_ALEN]; 5128c2ecf20Sopenharmony_ci __le16 klen; 5138c2ecf20Sopenharmony_ci u8 key[16]; 5148c2ecf20Sopenharmony_ci} __packed; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci/* These structures are from the Aironet's PC4500 Developers Manual */ 5178c2ecf20Sopenharmony_citypedef struct Ssid Ssid; 5188c2ecf20Sopenharmony_cistruct Ssid { 5198c2ecf20Sopenharmony_ci __le16 len; 5208c2ecf20Sopenharmony_ci u8 ssid[32]; 5218c2ecf20Sopenharmony_ci} __packed; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_citypedef struct SsidRid SsidRid; 5248c2ecf20Sopenharmony_cistruct SsidRid { 5258c2ecf20Sopenharmony_ci __le16 len; 5268c2ecf20Sopenharmony_ci Ssid ssids[3]; 5278c2ecf20Sopenharmony_ci} __packed; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_citypedef struct ModulationRid ModulationRid; 5308c2ecf20Sopenharmony_cistruct ModulationRid { 5318c2ecf20Sopenharmony_ci __le16 len; 5328c2ecf20Sopenharmony_ci __le16 modulation; 5338c2ecf20Sopenharmony_ci#define MOD_DEFAULT cpu_to_le16(0) 5348c2ecf20Sopenharmony_ci#define MOD_CCK cpu_to_le16(1) 5358c2ecf20Sopenharmony_ci#define MOD_MOK cpu_to_le16(2) 5368c2ecf20Sopenharmony_ci} __packed; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_citypedef struct ConfigRid ConfigRid; 5398c2ecf20Sopenharmony_cistruct ConfigRid { 5408c2ecf20Sopenharmony_ci __le16 len; /* sizeof(ConfigRid) */ 5418c2ecf20Sopenharmony_ci __le16 opmode; /* operating mode */ 5428c2ecf20Sopenharmony_ci#define MODE_STA_IBSS cpu_to_le16(0) 5438c2ecf20Sopenharmony_ci#define MODE_STA_ESS cpu_to_le16(1) 5448c2ecf20Sopenharmony_ci#define MODE_AP cpu_to_le16(2) 5458c2ecf20Sopenharmony_ci#define MODE_AP_RPTR cpu_to_le16(3) 5468c2ecf20Sopenharmony_ci#define MODE_CFG_MASK cpu_to_le16(0xff) 5478c2ecf20Sopenharmony_ci#define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */ 5488c2ecf20Sopenharmony_ci#define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */ 5498c2ecf20Sopenharmony_ci#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extenstions */ 5508c2ecf20Sopenharmony_ci#define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */ 5518c2ecf20Sopenharmony_ci#define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */ 5528c2ecf20Sopenharmony_ci#define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */ 5538c2ecf20Sopenharmony_ci#define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */ 5548c2ecf20Sopenharmony_ci#define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */ 5558c2ecf20Sopenharmony_ci#define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */ 5568c2ecf20Sopenharmony_ci __le16 rmode; /* receive mode */ 5578c2ecf20Sopenharmony_ci#define RXMODE_BC_MC_ADDR cpu_to_le16(0) 5588c2ecf20Sopenharmony_ci#define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */ 5598c2ecf20Sopenharmony_ci#define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */ 5608c2ecf20Sopenharmony_ci#define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */ 5618c2ecf20Sopenharmony_ci#define RXMODE_RFMON_ANYBSS cpu_to_le16(4) 5628c2ecf20Sopenharmony_ci#define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */ 5638c2ecf20Sopenharmony_ci#define RXMODE_MASK cpu_to_le16(255) 5648c2ecf20Sopenharmony_ci#define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */ 5658c2ecf20Sopenharmony_ci#define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER) 5668c2ecf20Sopenharmony_ci#define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */ 5678c2ecf20Sopenharmony_ci __le16 fragThresh; 5688c2ecf20Sopenharmony_ci __le16 rtsThres; 5698c2ecf20Sopenharmony_ci u8 macAddr[ETH_ALEN]; 5708c2ecf20Sopenharmony_ci u8 rates[8]; 5718c2ecf20Sopenharmony_ci __le16 shortRetryLimit; 5728c2ecf20Sopenharmony_ci __le16 longRetryLimit; 5738c2ecf20Sopenharmony_ci __le16 txLifetime; /* in kusec */ 5748c2ecf20Sopenharmony_ci __le16 rxLifetime; /* in kusec */ 5758c2ecf20Sopenharmony_ci __le16 stationary; 5768c2ecf20Sopenharmony_ci __le16 ordering; 5778c2ecf20Sopenharmony_ci __le16 u16deviceType; /* for overriding device type */ 5788c2ecf20Sopenharmony_ci __le16 cfpRate; 5798c2ecf20Sopenharmony_ci __le16 cfpDuration; 5808c2ecf20Sopenharmony_ci __le16 _reserved1[3]; 5818c2ecf20Sopenharmony_ci /*---------- Scanning/Associating ----------*/ 5828c2ecf20Sopenharmony_ci __le16 scanMode; 5838c2ecf20Sopenharmony_ci#define SCANMODE_ACTIVE cpu_to_le16(0) 5848c2ecf20Sopenharmony_ci#define SCANMODE_PASSIVE cpu_to_le16(1) 5858c2ecf20Sopenharmony_ci#define SCANMODE_AIROSCAN cpu_to_le16(2) 5868c2ecf20Sopenharmony_ci __le16 probeDelay; /* in kusec */ 5878c2ecf20Sopenharmony_ci __le16 probeEnergyTimeout; /* in kusec */ 5888c2ecf20Sopenharmony_ci __le16 probeResponseTimeout; 5898c2ecf20Sopenharmony_ci __le16 beaconListenTimeout; 5908c2ecf20Sopenharmony_ci __le16 joinNetTimeout; 5918c2ecf20Sopenharmony_ci __le16 authTimeout; 5928c2ecf20Sopenharmony_ci __le16 authType; 5938c2ecf20Sopenharmony_ci#define AUTH_OPEN cpu_to_le16(0x1) 5948c2ecf20Sopenharmony_ci#define AUTH_ENCRYPT cpu_to_le16(0x101) 5958c2ecf20Sopenharmony_ci#define AUTH_SHAREDKEY cpu_to_le16(0x102) 5968c2ecf20Sopenharmony_ci#define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200) 5978c2ecf20Sopenharmony_ci __le16 associationTimeout; 5988c2ecf20Sopenharmony_ci __le16 specifiedApTimeout; 5998c2ecf20Sopenharmony_ci __le16 offlineScanInterval; 6008c2ecf20Sopenharmony_ci __le16 offlineScanDuration; 6018c2ecf20Sopenharmony_ci __le16 linkLossDelay; 6028c2ecf20Sopenharmony_ci __le16 maxBeaconLostTime; 6038c2ecf20Sopenharmony_ci __le16 refreshInterval; 6048c2ecf20Sopenharmony_ci#define DISABLE_REFRESH cpu_to_le16(0xFFFF) 6058c2ecf20Sopenharmony_ci __le16 _reserved1a[1]; 6068c2ecf20Sopenharmony_ci /*---------- Power save operation ----------*/ 6078c2ecf20Sopenharmony_ci __le16 powerSaveMode; 6088c2ecf20Sopenharmony_ci#define POWERSAVE_CAM cpu_to_le16(0) 6098c2ecf20Sopenharmony_ci#define POWERSAVE_PSP cpu_to_le16(1) 6108c2ecf20Sopenharmony_ci#define POWERSAVE_PSPCAM cpu_to_le16(2) 6118c2ecf20Sopenharmony_ci __le16 sleepForDtims; 6128c2ecf20Sopenharmony_ci __le16 listenInterval; 6138c2ecf20Sopenharmony_ci __le16 fastListenInterval; 6148c2ecf20Sopenharmony_ci __le16 listenDecay; 6158c2ecf20Sopenharmony_ci __le16 fastListenDelay; 6168c2ecf20Sopenharmony_ci __le16 _reserved2[2]; 6178c2ecf20Sopenharmony_ci /*---------- Ap/Ibss config items ----------*/ 6188c2ecf20Sopenharmony_ci __le16 beaconPeriod; 6198c2ecf20Sopenharmony_ci __le16 atimDuration; 6208c2ecf20Sopenharmony_ci __le16 hopPeriod; 6218c2ecf20Sopenharmony_ci __le16 channelSet; 6228c2ecf20Sopenharmony_ci __le16 channel; 6238c2ecf20Sopenharmony_ci __le16 dtimPeriod; 6248c2ecf20Sopenharmony_ci __le16 bridgeDistance; 6258c2ecf20Sopenharmony_ci __le16 radioID; 6268c2ecf20Sopenharmony_ci /*---------- Radio configuration ----------*/ 6278c2ecf20Sopenharmony_ci __le16 radioType; 6288c2ecf20Sopenharmony_ci#define RADIOTYPE_DEFAULT cpu_to_le16(0) 6298c2ecf20Sopenharmony_ci#define RADIOTYPE_802_11 cpu_to_le16(1) 6308c2ecf20Sopenharmony_ci#define RADIOTYPE_LEGACY cpu_to_le16(2) 6318c2ecf20Sopenharmony_ci u8 rxDiversity; 6328c2ecf20Sopenharmony_ci u8 txDiversity; 6338c2ecf20Sopenharmony_ci __le16 txPower; 6348c2ecf20Sopenharmony_ci#define TXPOWER_DEFAULT 0 6358c2ecf20Sopenharmony_ci __le16 rssiThreshold; 6368c2ecf20Sopenharmony_ci#define RSSI_DEFAULT 0 6378c2ecf20Sopenharmony_ci __le16 modulation; 6388c2ecf20Sopenharmony_ci#define PREAMBLE_AUTO cpu_to_le16(0) 6398c2ecf20Sopenharmony_ci#define PREAMBLE_LONG cpu_to_le16(1) 6408c2ecf20Sopenharmony_ci#define PREAMBLE_SHORT cpu_to_le16(2) 6418c2ecf20Sopenharmony_ci __le16 preamble; 6428c2ecf20Sopenharmony_ci __le16 homeProduct; 6438c2ecf20Sopenharmony_ci __le16 radioSpecific; 6448c2ecf20Sopenharmony_ci /*---------- Aironet Extensions ----------*/ 6458c2ecf20Sopenharmony_ci u8 nodeName[16]; 6468c2ecf20Sopenharmony_ci __le16 arlThreshold; 6478c2ecf20Sopenharmony_ci __le16 arlDecay; 6488c2ecf20Sopenharmony_ci __le16 arlDelay; 6498c2ecf20Sopenharmony_ci __le16 _reserved4[1]; 6508c2ecf20Sopenharmony_ci /*---------- Aironet Extensions ----------*/ 6518c2ecf20Sopenharmony_ci u8 magicAction; 6528c2ecf20Sopenharmony_ci#define MAGIC_ACTION_STSCHG 1 6538c2ecf20Sopenharmony_ci#define MAGIC_ACTION_RESUME 2 6548c2ecf20Sopenharmony_ci#define MAGIC_IGNORE_MCAST (1<<8) 6558c2ecf20Sopenharmony_ci#define MAGIC_IGNORE_BCAST (1<<9) 6568c2ecf20Sopenharmony_ci#define MAGIC_SWITCH_TO_PSP (0<<10) 6578c2ecf20Sopenharmony_ci#define MAGIC_STAY_IN_CAM (1<<10) 6588c2ecf20Sopenharmony_ci u8 magicControl; 6598c2ecf20Sopenharmony_ci __le16 autoWake; 6608c2ecf20Sopenharmony_ci} __packed; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_citypedef struct StatusRid StatusRid; 6638c2ecf20Sopenharmony_cistruct StatusRid { 6648c2ecf20Sopenharmony_ci __le16 len; 6658c2ecf20Sopenharmony_ci u8 mac[ETH_ALEN]; 6668c2ecf20Sopenharmony_ci __le16 mode; 6678c2ecf20Sopenharmony_ci __le16 errorCode; 6688c2ecf20Sopenharmony_ci __le16 sigQuality; 6698c2ecf20Sopenharmony_ci __le16 SSIDlen; 6708c2ecf20Sopenharmony_ci char SSID[32]; 6718c2ecf20Sopenharmony_ci char apName[16]; 6728c2ecf20Sopenharmony_ci u8 bssid[4][ETH_ALEN]; 6738c2ecf20Sopenharmony_ci __le16 beaconPeriod; 6748c2ecf20Sopenharmony_ci __le16 dimPeriod; 6758c2ecf20Sopenharmony_ci __le16 atimDuration; 6768c2ecf20Sopenharmony_ci __le16 hopPeriod; 6778c2ecf20Sopenharmony_ci __le16 channelSet; 6788c2ecf20Sopenharmony_ci __le16 channel; 6798c2ecf20Sopenharmony_ci __le16 hopsToBackbone; 6808c2ecf20Sopenharmony_ci __le16 apTotalLoad; 6818c2ecf20Sopenharmony_ci __le16 generatedLoad; 6828c2ecf20Sopenharmony_ci __le16 accumulatedArl; 6838c2ecf20Sopenharmony_ci __le16 signalQuality; 6848c2ecf20Sopenharmony_ci __le16 currentXmitRate; 6858c2ecf20Sopenharmony_ci __le16 apDevExtensions; 6868c2ecf20Sopenharmony_ci __le16 normalizedSignalStrength; 6878c2ecf20Sopenharmony_ci __le16 shortPreamble; 6888c2ecf20Sopenharmony_ci u8 apIP[4]; 6898c2ecf20Sopenharmony_ci u8 noisePercent; /* Noise percent in last second */ 6908c2ecf20Sopenharmony_ci u8 noisedBm; /* Noise dBm in last second */ 6918c2ecf20Sopenharmony_ci u8 noiseAvePercent; /* Noise percent in last minute */ 6928c2ecf20Sopenharmony_ci u8 noiseAvedBm; /* Noise dBm in last minute */ 6938c2ecf20Sopenharmony_ci u8 noiseMaxPercent; /* Highest noise percent in last minute */ 6948c2ecf20Sopenharmony_ci u8 noiseMaxdBm; /* Highest noise dbm in last minute */ 6958c2ecf20Sopenharmony_ci __le16 load; 6968c2ecf20Sopenharmony_ci u8 carrier[4]; 6978c2ecf20Sopenharmony_ci __le16 assocStatus; 6988c2ecf20Sopenharmony_ci#define STAT_NOPACKETS 0 6998c2ecf20Sopenharmony_ci#define STAT_NOCARRIERSET 10 7008c2ecf20Sopenharmony_ci#define STAT_GOTCARRIERSET 11 7018c2ecf20Sopenharmony_ci#define STAT_WRONGSSID 20 7028c2ecf20Sopenharmony_ci#define STAT_BADCHANNEL 25 7038c2ecf20Sopenharmony_ci#define STAT_BADBITRATES 30 7048c2ecf20Sopenharmony_ci#define STAT_BADPRIVACY 35 7058c2ecf20Sopenharmony_ci#define STAT_APFOUND 40 7068c2ecf20Sopenharmony_ci#define STAT_APREJECTED 50 7078c2ecf20Sopenharmony_ci#define STAT_AUTHENTICATING 60 7088c2ecf20Sopenharmony_ci#define STAT_DEAUTHENTICATED 61 7098c2ecf20Sopenharmony_ci#define STAT_AUTHTIMEOUT 62 7108c2ecf20Sopenharmony_ci#define STAT_ASSOCIATING 70 7118c2ecf20Sopenharmony_ci#define STAT_DEASSOCIATED 71 7128c2ecf20Sopenharmony_ci#define STAT_ASSOCTIMEOUT 72 7138c2ecf20Sopenharmony_ci#define STAT_NOTAIROAP 73 7148c2ecf20Sopenharmony_ci#define STAT_ASSOCIATED 80 7158c2ecf20Sopenharmony_ci#define STAT_LEAPING 90 7168c2ecf20Sopenharmony_ci#define STAT_LEAPFAILED 91 7178c2ecf20Sopenharmony_ci#define STAT_LEAPTIMEDOUT 92 7188c2ecf20Sopenharmony_ci#define STAT_LEAPCOMPLETE 93 7198c2ecf20Sopenharmony_ci} __packed; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_citypedef struct StatsRid StatsRid; 7228c2ecf20Sopenharmony_cistruct StatsRid { 7238c2ecf20Sopenharmony_ci __le16 len; 7248c2ecf20Sopenharmony_ci __le16 spacer; 7258c2ecf20Sopenharmony_ci __le32 vals[100]; 7268c2ecf20Sopenharmony_ci} __packed; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_citypedef struct APListRid APListRid; 7298c2ecf20Sopenharmony_cistruct APListRid { 7308c2ecf20Sopenharmony_ci __le16 len; 7318c2ecf20Sopenharmony_ci u8 ap[4][ETH_ALEN]; 7328c2ecf20Sopenharmony_ci} __packed; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_citypedef struct CapabilityRid CapabilityRid; 7358c2ecf20Sopenharmony_cistruct CapabilityRid { 7368c2ecf20Sopenharmony_ci __le16 len; 7378c2ecf20Sopenharmony_ci char oui[3]; 7388c2ecf20Sopenharmony_ci char zero; 7398c2ecf20Sopenharmony_ci __le16 prodNum; 7408c2ecf20Sopenharmony_ci char manName[32]; 7418c2ecf20Sopenharmony_ci char prodName[16]; 7428c2ecf20Sopenharmony_ci char prodVer[8]; 7438c2ecf20Sopenharmony_ci char factoryAddr[ETH_ALEN]; 7448c2ecf20Sopenharmony_ci char aironetAddr[ETH_ALEN]; 7458c2ecf20Sopenharmony_ci __le16 radioType; 7468c2ecf20Sopenharmony_ci __le16 country; 7478c2ecf20Sopenharmony_ci char callid[ETH_ALEN]; 7488c2ecf20Sopenharmony_ci char supportedRates[8]; 7498c2ecf20Sopenharmony_ci char rxDiversity; 7508c2ecf20Sopenharmony_ci char txDiversity; 7518c2ecf20Sopenharmony_ci __le16 txPowerLevels[8]; 7528c2ecf20Sopenharmony_ci __le16 hardVer; 7538c2ecf20Sopenharmony_ci __le16 hardCap; 7548c2ecf20Sopenharmony_ci __le16 tempRange; 7558c2ecf20Sopenharmony_ci __le16 softVer; 7568c2ecf20Sopenharmony_ci __le16 softSubVer; 7578c2ecf20Sopenharmony_ci __le16 interfaceVer; 7588c2ecf20Sopenharmony_ci __le16 softCap; 7598c2ecf20Sopenharmony_ci __le16 bootBlockVer; 7608c2ecf20Sopenharmony_ci __le16 requiredHard; 7618c2ecf20Sopenharmony_ci __le16 extSoftCap; 7628c2ecf20Sopenharmony_ci} __packed; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci/* Only present on firmware >= 5.30.17 */ 7658c2ecf20Sopenharmony_citypedef struct BSSListRidExtra BSSListRidExtra; 7668c2ecf20Sopenharmony_cistruct BSSListRidExtra { 7678c2ecf20Sopenharmony_ci __le16 unknown[4]; 7688c2ecf20Sopenharmony_ci u8 fixed[12]; /* WLAN management frame */ 7698c2ecf20Sopenharmony_ci u8 iep[624]; 7708c2ecf20Sopenharmony_ci} __packed; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_citypedef struct BSSListRid BSSListRid; 7738c2ecf20Sopenharmony_cistruct BSSListRid { 7748c2ecf20Sopenharmony_ci __le16 len; 7758c2ecf20Sopenharmony_ci __le16 index; /* First is 0 and 0xffff means end of list */ 7768c2ecf20Sopenharmony_ci#define RADIO_FH 1 /* Frequency hopping radio type */ 7778c2ecf20Sopenharmony_ci#define RADIO_DS 2 /* Direct sequence radio type */ 7788c2ecf20Sopenharmony_ci#define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */ 7798c2ecf20Sopenharmony_ci __le16 radioType; 7808c2ecf20Sopenharmony_ci u8 bssid[ETH_ALEN]; /* Mac address of the BSS */ 7818c2ecf20Sopenharmony_ci u8 zero; 7828c2ecf20Sopenharmony_ci u8 ssidLen; 7838c2ecf20Sopenharmony_ci u8 ssid[32]; 7848c2ecf20Sopenharmony_ci __le16 dBm; 7858c2ecf20Sopenharmony_ci#define CAP_ESS cpu_to_le16(1<<0) 7868c2ecf20Sopenharmony_ci#define CAP_IBSS cpu_to_le16(1<<1) 7878c2ecf20Sopenharmony_ci#define CAP_PRIVACY cpu_to_le16(1<<4) 7888c2ecf20Sopenharmony_ci#define CAP_SHORTHDR cpu_to_le16(1<<5) 7898c2ecf20Sopenharmony_ci __le16 cap; 7908c2ecf20Sopenharmony_ci __le16 beaconInterval; 7918c2ecf20Sopenharmony_ci u8 rates[8]; /* Same as rates for config rid */ 7928c2ecf20Sopenharmony_ci struct { /* For frequency hopping only */ 7938c2ecf20Sopenharmony_ci __le16 dwell; 7948c2ecf20Sopenharmony_ci u8 hopSet; 7958c2ecf20Sopenharmony_ci u8 hopPattern; 7968c2ecf20Sopenharmony_ci u8 hopIndex; 7978c2ecf20Sopenharmony_ci u8 fill; 7988c2ecf20Sopenharmony_ci } fh; 7998c2ecf20Sopenharmony_ci __le16 dsChannel; 8008c2ecf20Sopenharmony_ci __le16 atimWindow; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* Only present on firmware >= 5.30.17 */ 8038c2ecf20Sopenharmony_ci BSSListRidExtra extra; 8048c2ecf20Sopenharmony_ci} __packed; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_citypedef struct { 8078c2ecf20Sopenharmony_ci BSSListRid bss; 8088c2ecf20Sopenharmony_ci struct list_head list; 8098c2ecf20Sopenharmony_ci} BSSListElement; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_citypedef struct tdsRssiEntry tdsRssiEntry; 8128c2ecf20Sopenharmony_cistruct tdsRssiEntry { 8138c2ecf20Sopenharmony_ci u8 rssipct; 8148c2ecf20Sopenharmony_ci u8 rssidBm; 8158c2ecf20Sopenharmony_ci} __packed; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_citypedef struct tdsRssiRid tdsRssiRid; 8188c2ecf20Sopenharmony_cistruct tdsRssiRid { 8198c2ecf20Sopenharmony_ci u16 len; 8208c2ecf20Sopenharmony_ci tdsRssiEntry x[256]; 8218c2ecf20Sopenharmony_ci} __packed; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_citypedef struct MICRid MICRid; 8248c2ecf20Sopenharmony_cistruct MICRid { 8258c2ecf20Sopenharmony_ci __le16 len; 8268c2ecf20Sopenharmony_ci __le16 state; 8278c2ecf20Sopenharmony_ci __le16 multicastValid; 8288c2ecf20Sopenharmony_ci u8 multicast[16]; 8298c2ecf20Sopenharmony_ci __le16 unicastValid; 8308c2ecf20Sopenharmony_ci u8 unicast[16]; 8318c2ecf20Sopenharmony_ci} __packed; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_citypedef struct MICBuffer MICBuffer; 8348c2ecf20Sopenharmony_cistruct MICBuffer { 8358c2ecf20Sopenharmony_ci __be16 typelen; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci union { 8388c2ecf20Sopenharmony_ci u8 snap[8]; 8398c2ecf20Sopenharmony_ci struct { 8408c2ecf20Sopenharmony_ci u8 dsap; 8418c2ecf20Sopenharmony_ci u8 ssap; 8428c2ecf20Sopenharmony_ci u8 control; 8438c2ecf20Sopenharmony_ci u8 orgcode[3]; 8448c2ecf20Sopenharmony_ci u8 fieldtype[2]; 8458c2ecf20Sopenharmony_ci } llc; 8468c2ecf20Sopenharmony_ci } u; 8478c2ecf20Sopenharmony_ci __be32 mic; 8488c2ecf20Sopenharmony_ci __be32 seq; 8498c2ecf20Sopenharmony_ci} __packed; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_citypedef struct { 8528c2ecf20Sopenharmony_ci u8 da[ETH_ALEN]; 8538c2ecf20Sopenharmony_ci u8 sa[ETH_ALEN]; 8548c2ecf20Sopenharmony_ci} etherHead; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci#define TXCTL_TXOK (1<<1) /* report if tx is ok */ 8578c2ecf20Sopenharmony_ci#define TXCTL_TXEX (1<<2) /* report if tx fails */ 8588c2ecf20Sopenharmony_ci#define TXCTL_802_3 (0<<3) /* 802.3 packet */ 8598c2ecf20Sopenharmony_ci#define TXCTL_802_11 (1<<3) /* 802.11 mac packet */ 8608c2ecf20Sopenharmony_ci#define TXCTL_ETHERNET (0<<4) /* payload has ethertype */ 8618c2ecf20Sopenharmony_ci#define TXCTL_LLC (1<<4) /* payload is llc */ 8628c2ecf20Sopenharmony_ci#define TXCTL_RELEASE (0<<5) /* release after completion */ 8638c2ecf20Sopenharmony_ci#define TXCTL_NORELEASE (1<<5) /* on completion returns to host */ 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci#define BUSY_FID 0x10000 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci#ifdef CISCO_EXT 8688c2ecf20Sopenharmony_ci#define AIROMAGIC 0xa55a 8698c2ecf20Sopenharmony_ci/* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */ 8708c2ecf20Sopenharmony_ci#ifdef SIOCIWFIRSTPRIV 8718c2ecf20Sopenharmony_ci#ifdef SIOCDEVPRIVATE 8728c2ecf20Sopenharmony_ci#define AIROOLDIOCTL SIOCDEVPRIVATE 8738c2ecf20Sopenharmony_ci#define AIROOLDIDIFC AIROOLDIOCTL + 1 8748c2ecf20Sopenharmony_ci#endif /* SIOCDEVPRIVATE */ 8758c2ecf20Sopenharmony_ci#else /* SIOCIWFIRSTPRIV */ 8768c2ecf20Sopenharmony_ci#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE 8778c2ecf20Sopenharmony_ci#endif /* SIOCIWFIRSTPRIV */ 8788c2ecf20Sopenharmony_ci/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably 8798c2ecf20Sopenharmony_ci * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root 8808c2ecf20Sopenharmony_ci * only and don't return the modified struct ifreq to the application which 8818c2ecf20Sopenharmony_ci * is usually a problem. - Jean II */ 8828c2ecf20Sopenharmony_ci#define AIROIOCTL SIOCIWFIRSTPRIV 8838c2ecf20Sopenharmony_ci#define AIROIDIFC AIROIOCTL + 1 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci/* Ioctl constants to be used in airo_ioctl.command */ 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci#define AIROGCAP 0 // Capability rid 8888c2ecf20Sopenharmony_ci#define AIROGCFG 1 // USED A LOT 8898c2ecf20Sopenharmony_ci#define AIROGSLIST 2 // System ID list 8908c2ecf20Sopenharmony_ci#define AIROGVLIST 3 // List of specified AP's 8918c2ecf20Sopenharmony_ci#define AIROGDRVNAM 4 // NOTUSED 8928c2ecf20Sopenharmony_ci#define AIROGEHTENC 5 // NOTUSED 8938c2ecf20Sopenharmony_ci#define AIROGWEPKTMP 6 8948c2ecf20Sopenharmony_ci#define AIROGWEPKNV 7 8958c2ecf20Sopenharmony_ci#define AIROGSTAT 8 8968c2ecf20Sopenharmony_ci#define AIROGSTATSC32 9 8978c2ecf20Sopenharmony_ci#define AIROGSTATSD32 10 8988c2ecf20Sopenharmony_ci#define AIROGMICRID 11 8998c2ecf20Sopenharmony_ci#define AIROGMICSTATS 12 9008c2ecf20Sopenharmony_ci#define AIROGFLAGS 13 9018c2ecf20Sopenharmony_ci#define AIROGID 14 9028c2ecf20Sopenharmony_ci#define AIRORRID 15 9038c2ecf20Sopenharmony_ci#define AIRORSWVERSION 17 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci/* Leave gap of 40 commands after AIROGSTATSD32 for future */ 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci#define AIROPCAP AIROGSTATSD32 + 40 9088c2ecf20Sopenharmony_ci#define AIROPVLIST AIROPCAP + 1 9098c2ecf20Sopenharmony_ci#define AIROPSLIST AIROPVLIST + 1 9108c2ecf20Sopenharmony_ci#define AIROPCFG AIROPSLIST + 1 9118c2ecf20Sopenharmony_ci#define AIROPSIDS AIROPCFG + 1 9128c2ecf20Sopenharmony_ci#define AIROPAPLIST AIROPSIDS + 1 9138c2ecf20Sopenharmony_ci#define AIROPMACON AIROPAPLIST + 1 /* Enable mac */ 9148c2ecf20Sopenharmony_ci#define AIROPMACOFF AIROPMACON + 1 /* Disable mac */ 9158c2ecf20Sopenharmony_ci#define AIROPSTCLR AIROPMACOFF + 1 9168c2ecf20Sopenharmony_ci#define AIROPWEPKEY AIROPSTCLR + 1 9178c2ecf20Sopenharmony_ci#define AIROPWEPKEYNV AIROPWEPKEY + 1 9188c2ecf20Sopenharmony_ci#define AIROPLEAPPWD AIROPWEPKEYNV + 1 9198c2ecf20Sopenharmony_ci#define AIROPLEAPUSR AIROPLEAPPWD + 1 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci/* Flash codes */ 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci#define AIROFLSHRST AIROPWEPKEYNV + 40 9248c2ecf20Sopenharmony_ci#define AIROFLSHGCHR AIROFLSHRST + 1 9258c2ecf20Sopenharmony_ci#define AIROFLSHSTFL AIROFLSHGCHR + 1 9268c2ecf20Sopenharmony_ci#define AIROFLSHPCHR AIROFLSHSTFL + 1 9278c2ecf20Sopenharmony_ci#define AIROFLPUTBUF AIROFLSHPCHR + 1 9288c2ecf20Sopenharmony_ci#define AIRORESTART AIROFLPUTBUF + 1 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci#define FLASHSIZE 32768 9318c2ecf20Sopenharmony_ci#define AUXMEMSIZE (256 * 1024) 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_citypedef struct aironet_ioctl { 9348c2ecf20Sopenharmony_ci unsigned short command; // What to do 9358c2ecf20Sopenharmony_ci unsigned short len; // Len of data 9368c2ecf20Sopenharmony_ci unsigned short ridnum; // rid number 9378c2ecf20Sopenharmony_ci unsigned char __user *data; // d-data 9388c2ecf20Sopenharmony_ci} aironet_ioctl; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic const char swversion[] = "2.1"; 9418c2ecf20Sopenharmony_ci#endif /* CISCO_EXT */ 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci#define NUM_MODULES 2 9448c2ecf20Sopenharmony_ci#define MIC_MSGLEN_MAX 2400 9458c2ecf20Sopenharmony_ci#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX 9468c2ecf20Sopenharmony_ci#define AIRO_DEF_MTU 2312 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_citypedef struct { 9498c2ecf20Sopenharmony_ci u32 size; // size 9508c2ecf20Sopenharmony_ci u8 enabled; // MIC enabled or not 9518c2ecf20Sopenharmony_ci u32 rxSuccess; // successful packets received 9528c2ecf20Sopenharmony_ci u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison 9538c2ecf20Sopenharmony_ci u32 rxNotMICed; // pkts dropped due to not being MIC'd 9548c2ecf20Sopenharmony_ci u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed 9558c2ecf20Sopenharmony_ci u32 rxWrongSequence; // pkts dropped due to sequence number violation 9568c2ecf20Sopenharmony_ci u32 reserve[32]; 9578c2ecf20Sopenharmony_ci} mic_statistics; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_citypedef struct { 9608c2ecf20Sopenharmony_ci __be32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2]; 9618c2ecf20Sopenharmony_ci u64 accum; // accumulated mic, reduced to u32 in final() 9628c2ecf20Sopenharmony_ci int position; // current position (byte offset) in message 9638c2ecf20Sopenharmony_ci union { 9648c2ecf20Sopenharmony_ci u8 d8[4]; 9658c2ecf20Sopenharmony_ci __be32 d32; 9668c2ecf20Sopenharmony_ci } part; // saves partial message word across update() calls 9678c2ecf20Sopenharmony_ci} emmh32_context; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_citypedef struct { 9708c2ecf20Sopenharmony_ci emmh32_context seed; // Context - the seed 9718c2ecf20Sopenharmony_ci u32 rx; // Received sequence number 9728c2ecf20Sopenharmony_ci u32 tx; // Tx sequence number 9738c2ecf20Sopenharmony_ci u32 window; // Start of window 9748c2ecf20Sopenharmony_ci u8 valid; // Flag to say if context is valid or not 9758c2ecf20Sopenharmony_ci u8 key[16]; 9768c2ecf20Sopenharmony_ci} miccntx; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_citypedef struct { 9798c2ecf20Sopenharmony_ci miccntx mCtx; // Multicast context 9808c2ecf20Sopenharmony_ci miccntx uCtx; // Unicast context 9818c2ecf20Sopenharmony_ci} mic_module; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_citypedef struct { 9848c2ecf20Sopenharmony_ci unsigned int rid: 16; 9858c2ecf20Sopenharmony_ci unsigned int len: 15; 9868c2ecf20Sopenharmony_ci unsigned int valid: 1; 9878c2ecf20Sopenharmony_ci dma_addr_t host_addr; 9888c2ecf20Sopenharmony_ci} Rid; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_citypedef struct { 9918c2ecf20Sopenharmony_ci unsigned int offset: 15; 9928c2ecf20Sopenharmony_ci unsigned int eoc: 1; 9938c2ecf20Sopenharmony_ci unsigned int len: 15; 9948c2ecf20Sopenharmony_ci unsigned int valid: 1; 9958c2ecf20Sopenharmony_ci dma_addr_t host_addr; 9968c2ecf20Sopenharmony_ci} TxFid; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistruct rx_hdr { 9998c2ecf20Sopenharmony_ci __le16 status, len; 10008c2ecf20Sopenharmony_ci u8 rssi[2]; 10018c2ecf20Sopenharmony_ci u8 rate; 10028c2ecf20Sopenharmony_ci u8 freq; 10038c2ecf20Sopenharmony_ci __le16 tmp[4]; 10048c2ecf20Sopenharmony_ci} __packed; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_citypedef struct { 10078c2ecf20Sopenharmony_ci unsigned int ctl: 15; 10088c2ecf20Sopenharmony_ci unsigned int rdy: 1; 10098c2ecf20Sopenharmony_ci unsigned int len: 15; 10108c2ecf20Sopenharmony_ci unsigned int valid: 1; 10118c2ecf20Sopenharmony_ci dma_addr_t host_addr; 10128c2ecf20Sopenharmony_ci} RxFid; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci/* 10158c2ecf20Sopenharmony_ci * Host receive descriptor 10168c2ecf20Sopenharmony_ci */ 10178c2ecf20Sopenharmony_citypedef struct { 10188c2ecf20Sopenharmony_ci unsigned char __iomem *card_ram_off; /* offset into card memory of the 10198c2ecf20Sopenharmony_ci desc */ 10208c2ecf20Sopenharmony_ci RxFid rx_desc; /* card receive descriptor */ 10218c2ecf20Sopenharmony_ci char *virtual_host_addr; /* virtual address of host receive 10228c2ecf20Sopenharmony_ci buffer */ 10238c2ecf20Sopenharmony_ci int pending; 10248c2ecf20Sopenharmony_ci} HostRxDesc; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci/* 10278c2ecf20Sopenharmony_ci * Host transmit descriptor 10288c2ecf20Sopenharmony_ci */ 10298c2ecf20Sopenharmony_citypedef struct { 10308c2ecf20Sopenharmony_ci unsigned char __iomem *card_ram_off; /* offset into card memory of the 10318c2ecf20Sopenharmony_ci desc */ 10328c2ecf20Sopenharmony_ci TxFid tx_desc; /* card transmit descriptor */ 10338c2ecf20Sopenharmony_ci char *virtual_host_addr; /* virtual address of host receive 10348c2ecf20Sopenharmony_ci buffer */ 10358c2ecf20Sopenharmony_ci int pending; 10368c2ecf20Sopenharmony_ci} HostTxDesc; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci/* 10398c2ecf20Sopenharmony_ci * Host RID descriptor 10408c2ecf20Sopenharmony_ci */ 10418c2ecf20Sopenharmony_citypedef struct { 10428c2ecf20Sopenharmony_ci unsigned char __iomem *card_ram_off; /* offset into card memory of the 10438c2ecf20Sopenharmony_ci descriptor */ 10448c2ecf20Sopenharmony_ci Rid rid_desc; /* card RID descriptor */ 10458c2ecf20Sopenharmony_ci char *virtual_host_addr; /* virtual address of host receive 10468c2ecf20Sopenharmony_ci buffer */ 10478c2ecf20Sopenharmony_ci} HostRidDesc; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_citypedef struct { 10508c2ecf20Sopenharmony_ci u16 sw0; 10518c2ecf20Sopenharmony_ci u16 sw1; 10528c2ecf20Sopenharmony_ci u16 status; 10538c2ecf20Sopenharmony_ci u16 len; 10548c2ecf20Sopenharmony_ci#define HOST_SET (1 << 0) 10558c2ecf20Sopenharmony_ci#define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */ 10568c2ecf20Sopenharmony_ci#define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */ 10578c2ecf20Sopenharmony_ci#define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */ 10588c2ecf20Sopenharmony_ci#define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */ 10598c2ecf20Sopenharmony_ci#define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */ 10608c2ecf20Sopenharmony_ci#define HOST_CLR_AID (1 << 7) /* clear AID failure */ 10618c2ecf20Sopenharmony_ci#define HOST_RTS (1 << 9) /* Force RTS use */ 10628c2ecf20Sopenharmony_ci#define HOST_SHORT (1 << 10) /* Do short preamble */ 10638c2ecf20Sopenharmony_ci u16 ctl; 10648c2ecf20Sopenharmony_ci u16 aid; 10658c2ecf20Sopenharmony_ci u16 retries; 10668c2ecf20Sopenharmony_ci u16 fill; 10678c2ecf20Sopenharmony_ci} TxCtlHdr; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_citypedef struct { 10708c2ecf20Sopenharmony_ci u16 ctl; 10718c2ecf20Sopenharmony_ci u16 duration; 10728c2ecf20Sopenharmony_ci char addr1[6]; 10738c2ecf20Sopenharmony_ci char addr2[6]; 10748c2ecf20Sopenharmony_ci char addr3[6]; 10758c2ecf20Sopenharmony_ci u16 seq; 10768c2ecf20Sopenharmony_ci char addr4[6]; 10778c2ecf20Sopenharmony_ci} WifiHdr; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_citypedef struct { 10818c2ecf20Sopenharmony_ci TxCtlHdr ctlhdr; 10828c2ecf20Sopenharmony_ci u16 fill1; 10838c2ecf20Sopenharmony_ci u16 fill2; 10848c2ecf20Sopenharmony_ci WifiHdr wifihdr; 10858c2ecf20Sopenharmony_ci u16 gaplen; 10868c2ecf20Sopenharmony_ci u16 status; 10878c2ecf20Sopenharmony_ci} WifiCtlHdr; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistatic WifiCtlHdr wifictlhdr8023 = { 10908c2ecf20Sopenharmony_ci .ctlhdr = { 10918c2ecf20Sopenharmony_ci .ctl = HOST_DONT_RLSE, 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci}; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci// A few details needed for WEP (Wireless Equivalent Privacy) 10968c2ecf20Sopenharmony_ci#define MAX_KEY_SIZE 13 // 128 (?) bits 10978c2ecf20Sopenharmony_ci#define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP 10988c2ecf20Sopenharmony_citypedef struct wep_key_t { 10998c2ecf20Sopenharmony_ci u16 len; 11008c2ecf20Sopenharmony_ci u8 key[16]; /* 40-bit and 104-bit keys */ 11018c2ecf20Sopenharmony_ci} wep_key_t; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci/* List of Wireless Handlers (new API) */ 11048c2ecf20Sopenharmony_cistatic const struct iw_handler_def airo_handler_def; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)"; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistruct airo_info; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cistatic int get_dec_u16(char *buffer, int *start, int limit); 11118c2ecf20Sopenharmony_cistatic void OUT4500(struct airo_info *, u16 reg, u16 value); 11128c2ecf20Sopenharmony_cistatic unsigned short IN4500(struct airo_info *, u16 reg); 11138c2ecf20Sopenharmony_cistatic u16 setup_card(struct airo_info*, u8 *mac, int lock); 11148c2ecf20Sopenharmony_cistatic int enable_MAC(struct airo_info *ai, int lock); 11158c2ecf20Sopenharmony_cistatic void disable_MAC(struct airo_info *ai, int lock); 11168c2ecf20Sopenharmony_cistatic void enable_interrupts(struct airo_info*); 11178c2ecf20Sopenharmony_cistatic void disable_interrupts(struct airo_info*); 11188c2ecf20Sopenharmony_cistatic u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp); 11198c2ecf20Sopenharmony_cistatic int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap); 11208c2ecf20Sopenharmony_cistatic int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen, 11218c2ecf20Sopenharmony_ci int whichbap); 11228c2ecf20Sopenharmony_cistatic int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen, 11238c2ecf20Sopenharmony_ci int whichbap); 11248c2ecf20Sopenharmony_cistatic int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen, 11258c2ecf20Sopenharmony_ci int whichbap); 11268c2ecf20Sopenharmony_cistatic int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd); 11278c2ecf20Sopenharmony_cistatic int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock); 11288c2ecf20Sopenharmony_cistatic int PC4500_writerid(struct airo_info*, u16 rid, const void 11298c2ecf20Sopenharmony_ci *pBuf, int len, int lock); 11308c2ecf20Sopenharmony_cistatic int do_writerid(struct airo_info*, u16 rid, const void *rid_data, 11318c2ecf20Sopenharmony_ci int len, int dummy); 11328c2ecf20Sopenharmony_cistatic u16 transmit_allocate(struct airo_info*, int lenPayload, int raw); 11338c2ecf20Sopenharmony_cistatic int transmit_802_3_packet(struct airo_info*, int len, char *pPacket); 11348c2ecf20Sopenharmony_cistatic int transmit_802_11_packet(struct airo_info*, int len, char *pPacket); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_cistatic int mpi_send_packet(struct net_device *dev); 11378c2ecf20Sopenharmony_cistatic void mpi_unmap_card(struct pci_dev *pci); 11388c2ecf20Sopenharmony_cistatic void mpi_receive_802_3(struct airo_info *ai); 11398c2ecf20Sopenharmony_cistatic void mpi_receive_802_11(struct airo_info *ai); 11408c2ecf20Sopenharmony_cistatic int waitbusy(struct airo_info *ai); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_cistatic irqreturn_t airo_interrupt(int irq, void* dev_id); 11438c2ecf20Sopenharmony_cistatic int airo_thread(void *data); 11448c2ecf20Sopenharmony_cistatic void timer_func(struct net_device *dev); 11458c2ecf20Sopenharmony_cistatic int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); 11468c2ecf20Sopenharmony_cistatic struct iw_statistics *airo_get_wireless_stats(struct net_device *dev); 11478c2ecf20Sopenharmony_cistatic void airo_read_wireless_stats(struct airo_info *local); 11488c2ecf20Sopenharmony_ci#ifdef CISCO_EXT 11498c2ecf20Sopenharmony_cistatic int readrids(struct net_device *dev, aironet_ioctl *comp); 11508c2ecf20Sopenharmony_cistatic int writerids(struct net_device *dev, aironet_ioctl *comp); 11518c2ecf20Sopenharmony_cistatic int flashcard(struct net_device *dev, aironet_ioctl *comp); 11528c2ecf20Sopenharmony_ci#endif /* CISCO_EXT */ 11538c2ecf20Sopenharmony_cistatic void micinit(struct airo_info *ai); 11548c2ecf20Sopenharmony_cistatic int micsetup(struct airo_info *ai); 11558c2ecf20Sopenharmony_cistatic int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len); 11568c2ecf20Sopenharmony_cistatic int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_cistatic u8 airo_rssi_to_dbm(tdsRssiEntry *rssi_rid, u8 rssi); 11598c2ecf20Sopenharmony_cistatic u8 airo_dbm_to_pct(tdsRssiEntry *rssi_rid, u8 dbm); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_cistatic void airo_networks_free(struct airo_info *ai); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistruct airo_info { 11648c2ecf20Sopenharmony_ci struct net_device *dev; 11658c2ecf20Sopenharmony_ci struct list_head dev_list; 11668c2ecf20Sopenharmony_ci /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we 11678c2ecf20Sopenharmony_ci use the high bit to mark whether it is in use. */ 11688c2ecf20Sopenharmony_ci#define MAX_FIDS 6 11698c2ecf20Sopenharmony_ci#define MPI_MAX_FIDS 1 11708c2ecf20Sopenharmony_ci u32 fids[MAX_FIDS]; 11718c2ecf20Sopenharmony_ci ConfigRid config; 11728c2ecf20Sopenharmony_ci char keyindex; // Used with auto wep 11738c2ecf20Sopenharmony_ci char defindex; // Used with auto wep 11748c2ecf20Sopenharmony_ci struct proc_dir_entry *proc_entry; 11758c2ecf20Sopenharmony_ci spinlock_t aux_lock; 11768c2ecf20Sopenharmony_ci#define FLAG_RADIO_OFF 0 /* User disabling of MAC */ 11778c2ecf20Sopenharmony_ci#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */ 11788c2ecf20Sopenharmony_ci#define FLAG_RADIO_MASK 0x03 11798c2ecf20Sopenharmony_ci#define FLAG_ENABLED 2 11808c2ecf20Sopenharmony_ci#define FLAG_ADHOC 3 /* Needed by MIC */ 11818c2ecf20Sopenharmony_ci#define FLAG_MIC_CAPABLE 4 11828c2ecf20Sopenharmony_ci#define FLAG_UPDATE_MULTI 5 11838c2ecf20Sopenharmony_ci#define FLAG_UPDATE_UNI 6 11848c2ecf20Sopenharmony_ci#define FLAG_802_11 7 11858c2ecf20Sopenharmony_ci#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */ 11868c2ecf20Sopenharmony_ci#define FLAG_PENDING_XMIT 9 11878c2ecf20Sopenharmony_ci#define FLAG_PENDING_XMIT11 10 11888c2ecf20Sopenharmony_ci#define FLAG_MPI 11 11898c2ecf20Sopenharmony_ci#define FLAG_REGISTERED 12 11908c2ecf20Sopenharmony_ci#define FLAG_COMMIT 13 11918c2ecf20Sopenharmony_ci#define FLAG_RESET 14 11928c2ecf20Sopenharmony_ci#define FLAG_FLASHING 15 11938c2ecf20Sopenharmony_ci#define FLAG_WPA_CAPABLE 16 11948c2ecf20Sopenharmony_ci unsigned long flags; 11958c2ecf20Sopenharmony_ci#define JOB_DIE 0 11968c2ecf20Sopenharmony_ci#define JOB_XMIT 1 11978c2ecf20Sopenharmony_ci#define JOB_XMIT11 2 11988c2ecf20Sopenharmony_ci#define JOB_STATS 3 11998c2ecf20Sopenharmony_ci#define JOB_PROMISC 4 12008c2ecf20Sopenharmony_ci#define JOB_MIC 5 12018c2ecf20Sopenharmony_ci#define JOB_EVENT 6 12028c2ecf20Sopenharmony_ci#define JOB_AUTOWEP 7 12038c2ecf20Sopenharmony_ci#define JOB_WSTATS 8 12048c2ecf20Sopenharmony_ci#define JOB_SCAN_RESULTS 9 12058c2ecf20Sopenharmony_ci unsigned long jobs; 12068c2ecf20Sopenharmony_ci int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen, 12078c2ecf20Sopenharmony_ci int whichbap); 12088c2ecf20Sopenharmony_ci unsigned short *flash; 12098c2ecf20Sopenharmony_ci tdsRssiEntry *rssi; 12108c2ecf20Sopenharmony_ci struct task_struct *list_bss_task; 12118c2ecf20Sopenharmony_ci struct task_struct *airo_thread_task; 12128c2ecf20Sopenharmony_ci struct semaphore sem; 12138c2ecf20Sopenharmony_ci wait_queue_head_t thr_wait; 12148c2ecf20Sopenharmony_ci unsigned long expires; 12158c2ecf20Sopenharmony_ci struct { 12168c2ecf20Sopenharmony_ci struct sk_buff *skb; 12178c2ecf20Sopenharmony_ci int fid; 12188c2ecf20Sopenharmony_ci } xmit, xmit11; 12198c2ecf20Sopenharmony_ci struct net_device *wifidev; 12208c2ecf20Sopenharmony_ci struct iw_statistics wstats; // wireless stats 12218c2ecf20Sopenharmony_ci unsigned long scan_timeout; /* Time scan should be read */ 12228c2ecf20Sopenharmony_ci struct iw_spy_data spy_data; 12238c2ecf20Sopenharmony_ci struct iw_public_data wireless_data; 12248c2ecf20Sopenharmony_ci /* MIC stuff */ 12258c2ecf20Sopenharmony_ci struct crypto_sync_skcipher *tfm; 12268c2ecf20Sopenharmony_ci mic_module mod[2]; 12278c2ecf20Sopenharmony_ci mic_statistics micstats; 12288c2ecf20Sopenharmony_ci HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors 12298c2ecf20Sopenharmony_ci HostTxDesc txfids[MPI_MAX_FIDS]; 12308c2ecf20Sopenharmony_ci HostRidDesc config_desc; 12318c2ecf20Sopenharmony_ci unsigned long ridbus; // phys addr of config_desc 12328c2ecf20Sopenharmony_ci struct sk_buff_head txq;// tx queue used by mpi350 code 12338c2ecf20Sopenharmony_ci struct pci_dev *pci; 12348c2ecf20Sopenharmony_ci unsigned char __iomem *pcimem; 12358c2ecf20Sopenharmony_ci unsigned char __iomem *pciaux; 12368c2ecf20Sopenharmony_ci unsigned char *shared; 12378c2ecf20Sopenharmony_ci dma_addr_t shared_dma; 12388c2ecf20Sopenharmony_ci pm_message_t power; 12398c2ecf20Sopenharmony_ci SsidRid *SSID; 12408c2ecf20Sopenharmony_ci APListRid APList; 12418c2ecf20Sopenharmony_ci#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE 12428c2ecf20Sopenharmony_ci char proc_name[IFNAMSIZ]; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci int wep_capable; 12458c2ecf20Sopenharmony_ci int max_wep_idx; 12468c2ecf20Sopenharmony_ci int last_auth; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci /* WPA-related stuff */ 12498c2ecf20Sopenharmony_ci unsigned int bssListFirst; 12508c2ecf20Sopenharmony_ci unsigned int bssListNext; 12518c2ecf20Sopenharmony_ci unsigned int bssListRidLen; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci struct list_head network_list; 12548c2ecf20Sopenharmony_ci struct list_head network_free_list; 12558c2ecf20Sopenharmony_ci BSSListElement *networks; 12568c2ecf20Sopenharmony_ci}; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cistatic inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen, 12598c2ecf20Sopenharmony_ci int whichbap) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci return ai->bap_read(ai, pu16Dst, bytelen, whichbap); 12628c2ecf20Sopenharmony_ci} 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_cistatic int setup_proc_entry(struct net_device *dev, 12658c2ecf20Sopenharmony_ci struct airo_info *apriv); 12668c2ecf20Sopenharmony_cistatic int takedown_proc_entry(struct net_device *dev, 12678c2ecf20Sopenharmony_ci struct airo_info *apriv); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cistatic int cmdreset(struct airo_info *ai); 12708c2ecf20Sopenharmony_cistatic int setflashmode(struct airo_info *ai); 12718c2ecf20Sopenharmony_cistatic int flashgchar(struct airo_info *ai, int matchbyte, int dwelltime); 12728c2ecf20Sopenharmony_cistatic int flashputbuf(struct airo_info *ai); 12738c2ecf20Sopenharmony_cistatic int flashrestart(struct airo_info *ai, struct net_device *dev); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci#define airo_print(type, name, fmt, args...) \ 12768c2ecf20Sopenharmony_ci printk(type DRV_NAME "(%s): " fmt "\n", name, ##args) 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci#define airo_print_info(name, fmt, args...) \ 12798c2ecf20Sopenharmony_ci airo_print(KERN_INFO, name, fmt, ##args) 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci#define airo_print_dbg(name, fmt, args...) \ 12828c2ecf20Sopenharmony_ci airo_print(KERN_DEBUG, name, fmt, ##args) 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci#define airo_print_warn(name, fmt, args...) \ 12858c2ecf20Sopenharmony_ci airo_print(KERN_WARNING, name, fmt, ##args) 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci#define airo_print_err(name, fmt, args...) \ 12888c2ecf20Sopenharmony_ci airo_print(KERN_ERR, name, fmt, ##args) 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash) 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci/*********************************************************************** 12938c2ecf20Sopenharmony_ci * MIC ROUTINES * 12948c2ecf20Sopenharmony_ci *********************************************************************** 12958c2ecf20Sopenharmony_ci */ 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic int RxSeqValid(struct airo_info *ai, miccntx *context, int mcast, u32 micSeq); 12988c2ecf20Sopenharmony_cistatic void MoveWindow(miccntx *context, u32 micSeq); 12998c2ecf20Sopenharmony_cistatic void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, 13008c2ecf20Sopenharmony_ci struct crypto_sync_skcipher *tfm); 13018c2ecf20Sopenharmony_cistatic void emmh32_init(emmh32_context *context); 13028c2ecf20Sopenharmony_cistatic void emmh32_update(emmh32_context *context, u8 *pOctets, int len); 13038c2ecf20Sopenharmony_cistatic void emmh32_final(emmh32_context *context, u8 digest[4]); 13048c2ecf20Sopenharmony_cistatic int flashpchar(struct airo_info *ai, int byte, int dwelltime); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_cistatic void age_mic_context(miccntx *cur, miccntx *old, u8 *key, int key_len, 13078c2ecf20Sopenharmony_ci struct crypto_sync_skcipher *tfm) 13088c2ecf20Sopenharmony_ci{ 13098c2ecf20Sopenharmony_ci /* If the current MIC context is valid and its key is the same as 13108c2ecf20Sopenharmony_ci * the MIC register, there's nothing to do. 13118c2ecf20Sopenharmony_ci */ 13128c2ecf20Sopenharmony_ci if (cur->valid && (memcmp(cur->key, key, key_len) == 0)) 13138c2ecf20Sopenharmony_ci return; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci /* Age current mic Context */ 13168c2ecf20Sopenharmony_ci memcpy(old, cur, sizeof(*cur)); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci /* Initialize new context */ 13198c2ecf20Sopenharmony_ci memcpy(cur->key, key, key_len); 13208c2ecf20Sopenharmony_ci cur->window = 33; /* Window always points to the middle */ 13218c2ecf20Sopenharmony_ci cur->rx = 0; /* Rx Sequence numbers */ 13228c2ecf20Sopenharmony_ci cur->tx = 0; /* Tx sequence numbers */ 13238c2ecf20Sopenharmony_ci cur->valid = 1; /* Key is now valid */ 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci /* Give key to mic seed */ 13268c2ecf20Sopenharmony_ci emmh32_setseed(&cur->seed, key, key_len, tfm); 13278c2ecf20Sopenharmony_ci} 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci/* micinit - Initialize mic seed */ 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_cistatic void micinit(struct airo_info *ai) 13328c2ecf20Sopenharmony_ci{ 13338c2ecf20Sopenharmony_ci MICRid mic_rid; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci clear_bit(JOB_MIC, &ai->jobs); 13368c2ecf20Sopenharmony_ci PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0); 13378c2ecf20Sopenharmony_ci up(&ai->sem); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci ai->micstats.enabled = (le16_to_cpu(mic_rid.state) & 0x00FF) ? 1 : 0; 13408c2ecf20Sopenharmony_ci if (!ai->micstats.enabled) { 13418c2ecf20Sopenharmony_ci /* So next time we have a valid key and mic is enabled, we will 13428c2ecf20Sopenharmony_ci * update the sequence number if the key is the same as before. 13438c2ecf20Sopenharmony_ci */ 13448c2ecf20Sopenharmony_ci ai->mod[0].uCtx.valid = 0; 13458c2ecf20Sopenharmony_ci ai->mod[0].mCtx.valid = 0; 13468c2ecf20Sopenharmony_ci return; 13478c2ecf20Sopenharmony_ci } 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci if (mic_rid.multicastValid) { 13508c2ecf20Sopenharmony_ci age_mic_context(&ai->mod[0].mCtx, &ai->mod[1].mCtx, 13518c2ecf20Sopenharmony_ci mic_rid.multicast, sizeof(mic_rid.multicast), 13528c2ecf20Sopenharmony_ci ai->tfm); 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci if (mic_rid.unicastValid) { 13568c2ecf20Sopenharmony_ci age_mic_context(&ai->mod[0].uCtx, &ai->mod[1].uCtx, 13578c2ecf20Sopenharmony_ci mic_rid.unicast, sizeof(mic_rid.unicast), 13588c2ecf20Sopenharmony_ci ai->tfm); 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci} 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci/* micsetup - Get ready for business */ 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_cistatic int micsetup(struct airo_info *ai) 13658c2ecf20Sopenharmony_ci{ 13668c2ecf20Sopenharmony_ci int i; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci if (ai->tfm == NULL) 13698c2ecf20Sopenharmony_ci ai->tfm = crypto_alloc_sync_skcipher("ctr(aes)", 0, 0); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci if (IS_ERR(ai->tfm)) { 13728c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "failed to load transform for AES"); 13738c2ecf20Sopenharmony_ci ai->tfm = NULL; 13748c2ecf20Sopenharmony_ci return ERROR; 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci for (i = 0; i < NUM_MODULES; i++) { 13788c2ecf20Sopenharmony_ci memset(&ai->mod[i].mCtx, 0, sizeof(miccntx)); 13798c2ecf20Sopenharmony_ci memset(&ai->mod[i].uCtx, 0, sizeof(miccntx)); 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci return SUCCESS; 13828c2ecf20Sopenharmony_ci} 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_cistatic const u8 micsnap[] = {0xAA, 0xAA, 0x03, 0x00, 0x40, 0x96, 0x00, 0x02}; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci/*=========================================================================== 13878c2ecf20Sopenharmony_ci * Description: Mic a packet 13888c2ecf20Sopenharmony_ci * 13898c2ecf20Sopenharmony_ci * Inputs: etherHead * pointer to an 802.3 frame 13908c2ecf20Sopenharmony_ci * 13918c2ecf20Sopenharmony_ci * Returns: BOOLEAN if successful, otherwise false. 13928c2ecf20Sopenharmony_ci * PacketTxLen will be updated with the mic'd packets size. 13938c2ecf20Sopenharmony_ci * 13948c2ecf20Sopenharmony_ci * Caveats: It is assumed that the frame buffer will already 13958c2ecf20Sopenharmony_ci * be big enough to hold the largets mic message possible. 13968c2ecf20Sopenharmony_ci * (No memory allocation is done here). 13978c2ecf20Sopenharmony_ci * 13988c2ecf20Sopenharmony_ci * Author: sbraneky (10/15/01) 13998c2ecf20Sopenharmony_ci * Merciless hacks by rwilcher (1/14/02) 14008c2ecf20Sopenharmony_ci */ 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_cistatic int encapsulate(struct airo_info *ai, etherHead *frame, MICBuffer *mic, int payLen) 14038c2ecf20Sopenharmony_ci{ 14048c2ecf20Sopenharmony_ci miccntx *context; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci // Determine correct context 14078c2ecf20Sopenharmony_ci // If not adhoc, always use unicast key 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1)) 14108c2ecf20Sopenharmony_ci context = &ai->mod[0].mCtx; 14118c2ecf20Sopenharmony_ci else 14128c2ecf20Sopenharmony_ci context = &ai->mod[0].uCtx; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci if (!context->valid) 14158c2ecf20Sopenharmony_ci return ERROR; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci mic->typelen = htons(payLen + 16); //Length of Mic'd packet 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci // Add Tx sequence 14228c2ecf20Sopenharmony_ci mic->seq = htonl(context->tx); 14238c2ecf20Sopenharmony_ci context->tx += 2; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci emmh32_init(&context->seed); // Mic the packet 14268c2ecf20Sopenharmony_ci emmh32_update(&context->seed, frame->da, ETH_ALEN * 2); // DA, SA 14278c2ecf20Sopenharmony_ci emmh32_update(&context->seed, (u8*)&mic->typelen, 10); // Type/Length and Snap 14288c2ecf20Sopenharmony_ci emmh32_update(&context->seed, (u8*)&mic->seq, sizeof(mic->seq)); //SEQ 14298c2ecf20Sopenharmony_ci emmh32_update(&context->seed, (u8*)(frame + 1), payLen); //payload 14308c2ecf20Sopenharmony_ci emmh32_final(&context->seed, (u8*)&mic->mic); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci /* New Type/length ?????????? */ 14338c2ecf20Sopenharmony_ci mic->typelen = 0; //Let NIC know it could be an oversized packet 14348c2ecf20Sopenharmony_ci return SUCCESS; 14358c2ecf20Sopenharmony_ci} 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_citypedef enum { 14388c2ecf20Sopenharmony_ci NONE, 14398c2ecf20Sopenharmony_ci NOMIC, 14408c2ecf20Sopenharmony_ci NOMICPLUMMED, 14418c2ecf20Sopenharmony_ci SEQUENCE, 14428c2ecf20Sopenharmony_ci INCORRECTMIC, 14438c2ecf20Sopenharmony_ci} mic_error; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci/*=========================================================================== 14468c2ecf20Sopenharmony_ci * Description: Decapsulates a MIC'd packet and returns the 802.3 packet 14478c2ecf20Sopenharmony_ci * (removes the MIC stuff) if packet is a valid packet. 14488c2ecf20Sopenharmony_ci * 14498c2ecf20Sopenharmony_ci * Inputs: etherHead pointer to the 802.3 packet 14508c2ecf20Sopenharmony_ci * 14518c2ecf20Sopenharmony_ci * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE 14528c2ecf20Sopenharmony_ci * 14538c2ecf20Sopenharmony_ci * Author: sbraneky (10/15/01) 14548c2ecf20Sopenharmony_ci * Merciless hacks by rwilcher (1/14/02) 14558c2ecf20Sopenharmony_ci *--------------------------------------------------------------------------- 14568c2ecf20Sopenharmony_ci */ 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_cistatic int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen) 14598c2ecf20Sopenharmony_ci{ 14608c2ecf20Sopenharmony_ci int i; 14618c2ecf20Sopenharmony_ci u32 micSEQ; 14628c2ecf20Sopenharmony_ci miccntx *context; 14638c2ecf20Sopenharmony_ci u8 digest[4]; 14648c2ecf20Sopenharmony_ci mic_error micError = NONE; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci // Check if the packet is a Mic'd packet 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci if (!ai->micstats.enabled) { 14698c2ecf20Sopenharmony_ci //No Mic set or Mic OFF but we received a MIC'd packet. 14708c2ecf20Sopenharmony_ci if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) { 14718c2ecf20Sopenharmony_ci ai->micstats.rxMICPlummed++; 14728c2ecf20Sopenharmony_ci return ERROR; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci return SUCCESS; 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci if (ntohs(mic->typelen) == 0x888E) 14788c2ecf20Sopenharmony_ci return SUCCESS; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) { 14818c2ecf20Sopenharmony_ci // Mic enabled but packet isn't Mic'd 14828c2ecf20Sopenharmony_ci ai->micstats.rxMICPlummed++; 14838c2ecf20Sopenharmony_ci return ERROR; 14848c2ecf20Sopenharmony_ci } 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci micSEQ = ntohl(mic->seq); //store SEQ as CPU order 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci //At this point we a have a mic'd packet and mic is enabled 14898c2ecf20Sopenharmony_ci //Now do the mic error checking. 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci //Receive seq must be odd 14928c2ecf20Sopenharmony_ci if ((micSEQ & 1) == 0) { 14938c2ecf20Sopenharmony_ci ai->micstats.rxWrongSequence++; 14948c2ecf20Sopenharmony_ci return ERROR; 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci for (i = 0; i < NUM_MODULES; i++) { 14988c2ecf20Sopenharmony_ci int mcast = eth->da[0] & 1; 14998c2ecf20Sopenharmony_ci //Determine proper context 15008c2ecf20Sopenharmony_ci context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci //Make sure context is valid 15038c2ecf20Sopenharmony_ci if (!context->valid) { 15048c2ecf20Sopenharmony_ci if (i == 0) 15058c2ecf20Sopenharmony_ci micError = NOMICPLUMMED; 15068c2ecf20Sopenharmony_ci continue; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci //DeMic it 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci if (!mic->typelen) 15118c2ecf20Sopenharmony_ci mic->typelen = htons(payLen + sizeof(MICBuffer) - 2); 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci emmh32_init(&context->seed); 15148c2ecf20Sopenharmony_ci emmh32_update(&context->seed, eth->da, ETH_ALEN*2); 15158c2ecf20Sopenharmony_ci emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap)); 15168c2ecf20Sopenharmony_ci emmh32_update(&context->seed, (u8 *)&mic->seq, sizeof(mic->seq)); 15178c2ecf20Sopenharmony_ci emmh32_update(&context->seed, (u8 *)(eth + 1), payLen); 15188c2ecf20Sopenharmony_ci //Calculate MIC 15198c2ecf20Sopenharmony_ci emmh32_final(&context->seed, digest); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match 15228c2ecf20Sopenharmony_ci //Invalid Mic 15238c2ecf20Sopenharmony_ci if (i == 0) 15248c2ecf20Sopenharmony_ci micError = INCORRECTMIC; 15258c2ecf20Sopenharmony_ci continue; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci //Check Sequence number if mics pass 15298c2ecf20Sopenharmony_ci if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) { 15308c2ecf20Sopenharmony_ci ai->micstats.rxSuccess++; 15318c2ecf20Sopenharmony_ci return SUCCESS; 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci if (i == 0) 15348c2ecf20Sopenharmony_ci micError = SEQUENCE; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci // Update statistics 15388c2ecf20Sopenharmony_ci switch (micError) { 15398c2ecf20Sopenharmony_ci case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break; 15408c2ecf20Sopenharmony_ci case SEQUENCE: ai->micstats.rxWrongSequence++; break; 15418c2ecf20Sopenharmony_ci case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break; 15428c2ecf20Sopenharmony_ci case NONE: break; 15438c2ecf20Sopenharmony_ci case NOMIC: break; 15448c2ecf20Sopenharmony_ci } 15458c2ecf20Sopenharmony_ci return ERROR; 15468c2ecf20Sopenharmony_ci} 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci/*=========================================================================== 15498c2ecf20Sopenharmony_ci * Description: Checks the Rx Seq number to make sure it is valid 15508c2ecf20Sopenharmony_ci * and hasn't already been received 15518c2ecf20Sopenharmony_ci * 15528c2ecf20Sopenharmony_ci * Inputs: miccntx - mic context to check seq against 15538c2ecf20Sopenharmony_ci * micSeq - the Mic seq number 15548c2ecf20Sopenharmony_ci * 15558c2ecf20Sopenharmony_ci * Returns: TRUE if valid otherwise FALSE. 15568c2ecf20Sopenharmony_ci * 15578c2ecf20Sopenharmony_ci * Author: sbraneky (10/15/01) 15588c2ecf20Sopenharmony_ci * Merciless hacks by rwilcher (1/14/02) 15598c2ecf20Sopenharmony_ci *--------------------------------------------------------------------------- 15608c2ecf20Sopenharmony_ci */ 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_cistatic int RxSeqValid(struct airo_info *ai, miccntx *context, int mcast, u32 micSeq) 15638c2ecf20Sopenharmony_ci{ 15648c2ecf20Sopenharmony_ci u32 seq, index; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci //Allow for the ap being rebooted - if it is then use the next 15678c2ecf20Sopenharmony_ci //sequence number of the current sequence number - might go backwards 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci if (mcast) { 15708c2ecf20Sopenharmony_ci if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) { 15718c2ecf20Sopenharmony_ci clear_bit (FLAG_UPDATE_MULTI, &ai->flags); 15728c2ecf20Sopenharmony_ci context->window = (micSeq > 33) ? micSeq : 33; 15738c2ecf20Sopenharmony_ci context->rx = 0; // Reset rx 15748c2ecf20Sopenharmony_ci } 15758c2ecf20Sopenharmony_ci } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) { 15768c2ecf20Sopenharmony_ci clear_bit (FLAG_UPDATE_UNI, &ai->flags); 15778c2ecf20Sopenharmony_ci context->window = (micSeq > 33) ? micSeq : 33; // Move window 15788c2ecf20Sopenharmony_ci context->rx = 0; // Reset rx 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci //Make sequence number relative to START of window 15828c2ecf20Sopenharmony_ci seq = micSeq - (context->window - 33); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci //Too old of a SEQ number to check. 15858c2ecf20Sopenharmony_ci if ((s32)seq < 0) 15868c2ecf20Sopenharmony_ci return ERROR; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci if (seq > 64) { 15898c2ecf20Sopenharmony_ci //Window is infinite forward 15908c2ecf20Sopenharmony_ci MoveWindow(context, micSeq); 15918c2ecf20Sopenharmony_ci return SUCCESS; 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci // We are in the window. Now check the context rx bit to see if it was already sent 15958c2ecf20Sopenharmony_ci seq >>= 1; //divide by 2 because we only have odd numbers 15968c2ecf20Sopenharmony_ci index = 1 << seq; //Get an index number 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci if (!(context->rx & index)) { 15998c2ecf20Sopenharmony_ci //micSEQ falls inside the window. 16008c2ecf20Sopenharmony_ci //Add seqence number to the list of received numbers. 16018c2ecf20Sopenharmony_ci context->rx |= index; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci MoveWindow(context, micSeq); 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci return SUCCESS; 16068c2ecf20Sopenharmony_ci } 16078c2ecf20Sopenharmony_ci return ERROR; 16088c2ecf20Sopenharmony_ci} 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_cistatic void MoveWindow(miccntx *context, u32 micSeq) 16118c2ecf20Sopenharmony_ci{ 16128c2ecf20Sopenharmony_ci u32 shift; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci //Move window if seq greater than the middle of the window 16158c2ecf20Sopenharmony_ci if (micSeq > context->window) { 16168c2ecf20Sopenharmony_ci shift = (micSeq - context->window) >> 1; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci //Shift out old 16198c2ecf20Sopenharmony_ci if (shift < 32) 16208c2ecf20Sopenharmony_ci context->rx >>= shift; 16218c2ecf20Sopenharmony_ci else 16228c2ecf20Sopenharmony_ci context->rx = 0; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci context->window = micSeq; //Move window 16258c2ecf20Sopenharmony_ci } 16268c2ecf20Sopenharmony_ci} 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci/*==============================================*/ 16298c2ecf20Sopenharmony_ci/*========== EMMH ROUTINES ====================*/ 16308c2ecf20Sopenharmony_ci/*==============================================*/ 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci/* mic accumulate */ 16338c2ecf20Sopenharmony_ci#define MIC_ACCUM(val) \ 16348c2ecf20Sopenharmony_ci context->accum += (u64)(val) * be32_to_cpu(context->coeff[coeff_position++]); 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci/* expand the key to fill the MMH coefficient array */ 16378c2ecf20Sopenharmony_cistatic void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, 16388c2ecf20Sopenharmony_ci struct crypto_sync_skcipher *tfm) 16398c2ecf20Sopenharmony_ci{ 16408c2ecf20Sopenharmony_ci /* take the keying material, expand if necessary, truncate at 16-bytes */ 16418c2ecf20Sopenharmony_ci /* run through AES counter mode to generate context->coeff[] */ 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm); 16448c2ecf20Sopenharmony_ci struct scatterlist sg; 16458c2ecf20Sopenharmony_ci u8 iv[AES_BLOCK_SIZE] = {}; 16468c2ecf20Sopenharmony_ci int ret; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci crypto_sync_skcipher_setkey(tfm, pkey, 16); 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci memset(context->coeff, 0, sizeof(context->coeff)); 16518c2ecf20Sopenharmony_ci sg_init_one(&sg, context->coeff, sizeof(context->coeff)); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci skcipher_request_set_sync_tfm(req, tfm); 16548c2ecf20Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 16558c2ecf20Sopenharmony_ci skcipher_request_set_crypt(req, &sg, &sg, sizeof(context->coeff), iv); 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci ret = crypto_skcipher_encrypt(req); 16588c2ecf20Sopenharmony_ci WARN_ON_ONCE(ret); 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci/* prepare for calculation of a new mic */ 16628c2ecf20Sopenharmony_cistatic void emmh32_init(emmh32_context *context) 16638c2ecf20Sopenharmony_ci{ 16648c2ecf20Sopenharmony_ci /* prepare for new mic calculation */ 16658c2ecf20Sopenharmony_ci context->accum = 0; 16668c2ecf20Sopenharmony_ci context->position = 0; 16678c2ecf20Sopenharmony_ci} 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci/* add some bytes to the mic calculation */ 16708c2ecf20Sopenharmony_cistatic void emmh32_update(emmh32_context *context, u8 *pOctets, int len) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci int coeff_position, byte_position; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci if (len == 0) return; 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci coeff_position = context->position >> 2; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci /* deal with partial 32-bit word left over from last update */ 16798c2ecf20Sopenharmony_ci byte_position = context->position & 3; 16808c2ecf20Sopenharmony_ci if (byte_position) { 16818c2ecf20Sopenharmony_ci /* have a partial word in part to deal with */ 16828c2ecf20Sopenharmony_ci do { 16838c2ecf20Sopenharmony_ci if (len == 0) return; 16848c2ecf20Sopenharmony_ci context->part.d8[byte_position++] = *pOctets++; 16858c2ecf20Sopenharmony_ci context->position++; 16868c2ecf20Sopenharmony_ci len--; 16878c2ecf20Sopenharmony_ci } while (byte_position < 4); 16888c2ecf20Sopenharmony_ci MIC_ACCUM(ntohl(context->part.d32)); 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci /* deal with full 32-bit words */ 16928c2ecf20Sopenharmony_ci while (len >= 4) { 16938c2ecf20Sopenharmony_ci MIC_ACCUM(ntohl(*(__be32 *)pOctets)); 16948c2ecf20Sopenharmony_ci context->position += 4; 16958c2ecf20Sopenharmony_ci pOctets += 4; 16968c2ecf20Sopenharmony_ci len -= 4; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci /* deal with partial 32-bit word that will be left over from this update */ 17008c2ecf20Sopenharmony_ci byte_position = 0; 17018c2ecf20Sopenharmony_ci while (len > 0) { 17028c2ecf20Sopenharmony_ci context->part.d8[byte_position++] = *pOctets++; 17038c2ecf20Sopenharmony_ci context->position++; 17048c2ecf20Sopenharmony_ci len--; 17058c2ecf20Sopenharmony_ci } 17068c2ecf20Sopenharmony_ci} 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci/* mask used to zero empty bytes for final partial word */ 17098c2ecf20Sopenharmony_cistatic u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L }; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci/* calculate the mic */ 17128c2ecf20Sopenharmony_cistatic void emmh32_final(emmh32_context *context, u8 digest[4]) 17138c2ecf20Sopenharmony_ci{ 17148c2ecf20Sopenharmony_ci int coeff_position, byte_position; 17158c2ecf20Sopenharmony_ci u32 val; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci u64 sum, utmp; 17188c2ecf20Sopenharmony_ci s64 stmp; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci coeff_position = context->position >> 2; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci /* deal with partial 32-bit word left over from last update */ 17238c2ecf20Sopenharmony_ci byte_position = context->position & 3; 17248c2ecf20Sopenharmony_ci if (byte_position) { 17258c2ecf20Sopenharmony_ci /* have a partial word in part to deal with */ 17268c2ecf20Sopenharmony_ci val = ntohl(context->part.d32); 17278c2ecf20Sopenharmony_ci MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */ 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci /* reduce the accumulated u64 to a 32-bit MIC */ 17318c2ecf20Sopenharmony_ci sum = context->accum; 17328c2ecf20Sopenharmony_ci stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15); 17338c2ecf20Sopenharmony_ci utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15); 17348c2ecf20Sopenharmony_ci sum = utmp & 0xffffffffLL; 17358c2ecf20Sopenharmony_ci if (utmp > 0x10000000fLL) 17368c2ecf20Sopenharmony_ci sum -= 15; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci val = (u32)sum; 17398c2ecf20Sopenharmony_ci digest[0] = (val>>24) & 0xFF; 17408c2ecf20Sopenharmony_ci digest[1] = (val>>16) & 0xFF; 17418c2ecf20Sopenharmony_ci digest[2] = (val>>8) & 0xFF; 17428c2ecf20Sopenharmony_ci digest[3] = val & 0xFF; 17438c2ecf20Sopenharmony_ci} 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_cistatic int readBSSListRid(struct airo_info *ai, int first, 17468c2ecf20Sopenharmony_ci BSSListRid *list) 17478c2ecf20Sopenharmony_ci{ 17488c2ecf20Sopenharmony_ci Cmd cmd; 17498c2ecf20Sopenharmony_ci Resp rsp; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci if (first == 1) { 17528c2ecf20Sopenharmony_ci if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN; 17538c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 17548c2ecf20Sopenharmony_ci cmd.cmd = CMD_LISTBSS; 17558c2ecf20Sopenharmony_ci if (down_interruptible(&ai->sem)) 17568c2ecf20Sopenharmony_ci return -ERESTARTSYS; 17578c2ecf20Sopenharmony_ci ai->list_bss_task = current; 17588c2ecf20Sopenharmony_ci issuecommand(ai, &cmd, &rsp); 17598c2ecf20Sopenharmony_ci up(&ai->sem); 17608c2ecf20Sopenharmony_ci /* Let the command take effect */ 17618c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(3 * HZ); 17628c2ecf20Sopenharmony_ci ai->list_bss_task = NULL; 17638c2ecf20Sopenharmony_ci } 17648c2ecf20Sopenharmony_ci return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext, 17658c2ecf20Sopenharmony_ci list, ai->bssListRidLen, 1); 17668c2ecf20Sopenharmony_ci} 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_cistatic int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock) 17698c2ecf20Sopenharmony_ci{ 17708c2ecf20Sopenharmony_ci return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM, 17718c2ecf20Sopenharmony_ci wkr, sizeof(*wkr), lock); 17728c2ecf20Sopenharmony_ci} 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_cistatic int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock) 17758c2ecf20Sopenharmony_ci{ 17768c2ecf20Sopenharmony_ci int rc; 17778c2ecf20Sopenharmony_ci rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock); 17788c2ecf20Sopenharmony_ci if (rc!=SUCCESS) 17798c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc); 17808c2ecf20Sopenharmony_ci if (perm) { 17818c2ecf20Sopenharmony_ci rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock); 17828c2ecf20Sopenharmony_ci if (rc!=SUCCESS) 17838c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "WEP_PERM set %x", rc); 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci return rc; 17868c2ecf20Sopenharmony_ci} 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_cistatic int readSsidRid(struct airo_info*ai, SsidRid *ssidr) 17898c2ecf20Sopenharmony_ci{ 17908c2ecf20Sopenharmony_ci return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1); 17918c2ecf20Sopenharmony_ci} 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_cistatic int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock) 17948c2ecf20Sopenharmony_ci{ 17958c2ecf20Sopenharmony_ci return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock); 17968c2ecf20Sopenharmony_ci} 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_cistatic int readConfigRid(struct airo_info *ai, int lock) 17998c2ecf20Sopenharmony_ci{ 18008c2ecf20Sopenharmony_ci int rc; 18018c2ecf20Sopenharmony_ci ConfigRid cfg; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci if (ai->config.len) 18048c2ecf20Sopenharmony_ci return SUCCESS; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock); 18078c2ecf20Sopenharmony_ci if (rc != SUCCESS) 18088c2ecf20Sopenharmony_ci return rc; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci ai->config = cfg; 18118c2ecf20Sopenharmony_ci return SUCCESS; 18128c2ecf20Sopenharmony_ci} 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_cistatic inline void checkThrottle(struct airo_info *ai) 18158c2ecf20Sopenharmony_ci{ 18168c2ecf20Sopenharmony_ci int i; 18178c2ecf20Sopenharmony_ci/* Old hardware had a limit on encryption speed */ 18188c2ecf20Sopenharmony_ci if (ai->config.authType != AUTH_OPEN && maxencrypt) { 18198c2ecf20Sopenharmony_ci for (i = 0; i<8; i++) { 18208c2ecf20Sopenharmony_ci if (ai->config.rates[i] > maxencrypt) { 18218c2ecf20Sopenharmony_ci ai->config.rates[i] = 0; 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci } 18258c2ecf20Sopenharmony_ci} 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_cistatic int writeConfigRid(struct airo_info *ai, int lock) 18288c2ecf20Sopenharmony_ci{ 18298c2ecf20Sopenharmony_ci ConfigRid cfgr; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci if (!test_bit (FLAG_COMMIT, &ai->flags)) 18328c2ecf20Sopenharmony_ci return SUCCESS; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci clear_bit (FLAG_COMMIT, &ai->flags); 18358c2ecf20Sopenharmony_ci clear_bit (FLAG_RESET, &ai->flags); 18368c2ecf20Sopenharmony_ci checkThrottle(ai); 18378c2ecf20Sopenharmony_ci cfgr = ai->config; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS) 18408c2ecf20Sopenharmony_ci set_bit(FLAG_ADHOC, &ai->flags); 18418c2ecf20Sopenharmony_ci else 18428c2ecf20Sopenharmony_ci clear_bit(FLAG_ADHOC, &ai->flags); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci return PC4500_writerid(ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock); 18458c2ecf20Sopenharmony_ci} 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_cistatic int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock) 18488c2ecf20Sopenharmony_ci{ 18498c2ecf20Sopenharmony_ci return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock); 18508c2ecf20Sopenharmony_ci} 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_cistatic int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock) 18538c2ecf20Sopenharmony_ci{ 18548c2ecf20Sopenharmony_ci return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock); 18558c2ecf20Sopenharmony_ci} 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_cistatic int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock) 18588c2ecf20Sopenharmony_ci{ 18598c2ecf20Sopenharmony_ci return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock); 18608c2ecf20Sopenharmony_ci} 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_cistatic int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock) 18638c2ecf20Sopenharmony_ci{ 18648c2ecf20Sopenharmony_ci return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock); 18658c2ecf20Sopenharmony_ci} 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_cistatic void try_auto_wep(struct airo_info *ai) 18688c2ecf20Sopenharmony_ci{ 18698c2ecf20Sopenharmony_ci if (auto_wep && !test_bit(FLAG_RADIO_DOWN, &ai->flags)) { 18708c2ecf20Sopenharmony_ci ai->expires = RUN_AT(3*HZ); 18718c2ecf20Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci} 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_cistatic int airo_open(struct net_device *dev) 18768c2ecf20Sopenharmony_ci{ 18778c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 18788c2ecf20Sopenharmony_ci int rc = 0; 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci if (test_bit(FLAG_FLASHING, &ai->flags)) 18818c2ecf20Sopenharmony_ci return -EIO; 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci /* Make sure the card is configured. 18848c2ecf20Sopenharmony_ci * Wireless Extensions may postpone config changes until the card 18858c2ecf20Sopenharmony_ci * is open (to pipeline changes and speed-up card setup). If 18868c2ecf20Sopenharmony_ci * those changes are not yet committed, do it now - Jean II */ 18878c2ecf20Sopenharmony_ci if (test_bit(FLAG_COMMIT, &ai->flags)) { 18888c2ecf20Sopenharmony_ci disable_MAC(ai, 1); 18898c2ecf20Sopenharmony_ci writeConfigRid(ai, 1); 18908c2ecf20Sopenharmony_ci } 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci if (ai->wifidev != dev) { 18938c2ecf20Sopenharmony_ci clear_bit(JOB_DIE, &ai->jobs); 18948c2ecf20Sopenharmony_ci ai->airo_thread_task = kthread_run(airo_thread, dev, "%s", 18958c2ecf20Sopenharmony_ci dev->name); 18968c2ecf20Sopenharmony_ci if (IS_ERR(ai->airo_thread_task)) 18978c2ecf20Sopenharmony_ci return (int)PTR_ERR(ai->airo_thread_task); 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED, 19008c2ecf20Sopenharmony_ci dev->name, dev); 19018c2ecf20Sopenharmony_ci if (rc) { 19028c2ecf20Sopenharmony_ci airo_print_err(dev->name, 19038c2ecf20Sopenharmony_ci "register interrupt %d failed, rc %d", 19048c2ecf20Sopenharmony_ci dev->irq, rc); 19058c2ecf20Sopenharmony_ci set_bit(JOB_DIE, &ai->jobs); 19068c2ecf20Sopenharmony_ci kthread_stop(ai->airo_thread_task); 19078c2ecf20Sopenharmony_ci return rc; 19088c2ecf20Sopenharmony_ci } 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci /* Power on the MAC controller (which may have been disabled) */ 19118c2ecf20Sopenharmony_ci clear_bit(FLAG_RADIO_DOWN, &ai->flags); 19128c2ecf20Sopenharmony_ci enable_interrupts(ai); 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci try_auto_wep(ai); 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci enable_MAC(ai, 1); 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci netif_start_queue(dev); 19198c2ecf20Sopenharmony_ci return 0; 19208c2ecf20Sopenharmony_ci} 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_cistatic netdev_tx_t mpi_start_xmit(struct sk_buff *skb, 19238c2ecf20Sopenharmony_ci struct net_device *dev) 19248c2ecf20Sopenharmony_ci{ 19258c2ecf20Sopenharmony_ci int npacks, pending; 19268c2ecf20Sopenharmony_ci unsigned long flags; 19278c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci if (!skb) { 19308c2ecf20Sopenharmony_ci airo_print_err(dev->name, "%s: skb == NULL!",__func__); 19318c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 19328c2ecf20Sopenharmony_ci } 19338c2ecf20Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) { 19348c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 19358c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 19368c2ecf20Sopenharmony_ci } 19378c2ecf20Sopenharmony_ci npacks = skb_queue_len (&ai->txq); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci if (npacks >= MAXTXQ - 1) { 19408c2ecf20Sopenharmony_ci netif_stop_queue (dev); 19418c2ecf20Sopenharmony_ci if (npacks > MAXTXQ) { 19428c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 19438c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci skb_queue_tail (&ai->txq, skb); 19468c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci spin_lock_irqsave(&ai->aux_lock, flags); 19508c2ecf20Sopenharmony_ci skb_queue_tail (&ai->txq, skb); 19518c2ecf20Sopenharmony_ci pending = test_bit(FLAG_PENDING_XMIT, &ai->flags); 19528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ai->aux_lock, flags); 19538c2ecf20Sopenharmony_ci netif_wake_queue (dev); 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci if (pending == 0) { 19568c2ecf20Sopenharmony_ci set_bit(FLAG_PENDING_XMIT, &ai->flags); 19578c2ecf20Sopenharmony_ci mpi_send_packet (dev); 19588c2ecf20Sopenharmony_ci } 19598c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 19608c2ecf20Sopenharmony_ci} 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci/* 19638c2ecf20Sopenharmony_ci * @mpi_send_packet 19648c2ecf20Sopenharmony_ci * 19658c2ecf20Sopenharmony_ci * Attempt to transmit a packet. Can be called from interrupt 19668c2ecf20Sopenharmony_ci * or transmit . return number of packets we tried to send 19678c2ecf20Sopenharmony_ci */ 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_cistatic int mpi_send_packet (struct net_device *dev) 19708c2ecf20Sopenharmony_ci{ 19718c2ecf20Sopenharmony_ci struct sk_buff *skb; 19728c2ecf20Sopenharmony_ci unsigned char *buffer; 19738c2ecf20Sopenharmony_ci s16 len; 19748c2ecf20Sopenharmony_ci __le16 *payloadLen; 19758c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 19768c2ecf20Sopenharmony_ci u8 *sendbuf; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* get a packet to send */ 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci if ((skb = skb_dequeue(&ai->txq)) == NULL) { 19818c2ecf20Sopenharmony_ci airo_print_err(dev->name, 19828c2ecf20Sopenharmony_ci "%s: Dequeue'd zero in send_packet()", 19838c2ecf20Sopenharmony_ci __func__); 19848c2ecf20Sopenharmony_ci return 0; 19858c2ecf20Sopenharmony_ci } 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* check min length*/ 19888c2ecf20Sopenharmony_ci len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; 19898c2ecf20Sopenharmony_ci buffer = skb->data; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci ai->txfids[0].tx_desc.offset = 0; 19928c2ecf20Sopenharmony_ci ai->txfids[0].tx_desc.valid = 1; 19938c2ecf20Sopenharmony_ci ai->txfids[0].tx_desc.eoc = 1; 19948c2ecf20Sopenharmony_ci ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr); 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci/* 19978c2ecf20Sopenharmony_ci * Magic, the cards firmware needs a length count (2 bytes) in the host buffer 19988c2ecf20Sopenharmony_ci * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen 19998c2ecf20Sopenharmony_ci * is immediately after it. ------------------------------------------------ 20008c2ecf20Sopenharmony_ci * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA| 20018c2ecf20Sopenharmony_ci * ------------------------------------------------ 20028c2ecf20Sopenharmony_ci */ 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci memcpy(ai->txfids[0].virtual_host_addr, 20058c2ecf20Sopenharmony_ci (char *)&wifictlhdr8023, sizeof(wifictlhdr8023)); 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr + 20088c2ecf20Sopenharmony_ci sizeof(wifictlhdr8023)); 20098c2ecf20Sopenharmony_ci sendbuf = ai->txfids[0].virtual_host_addr + 20108c2ecf20Sopenharmony_ci sizeof(wifictlhdr8023) + 2 ; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci /* 20138c2ecf20Sopenharmony_ci * Firmware automatically puts 802 header on so 20148c2ecf20Sopenharmony_ci * we don't need to account for it in the length 20158c2ecf20Sopenharmony_ci */ 20168c2ecf20Sopenharmony_ci if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled && 20178c2ecf20Sopenharmony_ci (ntohs(((__be16 *)buffer)[6]) != 0x888E)) { 20188c2ecf20Sopenharmony_ci MICBuffer pMic; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS) 20218c2ecf20Sopenharmony_ci return ERROR; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic)); 20248c2ecf20Sopenharmony_ci ai->txfids[0].tx_desc.len += sizeof(pMic); 20258c2ecf20Sopenharmony_ci /* copy data into airo dma buffer */ 20268c2ecf20Sopenharmony_ci memcpy (sendbuf, buffer, sizeof(etherHead)); 20278c2ecf20Sopenharmony_ci buffer += sizeof(etherHead); 20288c2ecf20Sopenharmony_ci sendbuf += sizeof(etherHead); 20298c2ecf20Sopenharmony_ci memcpy (sendbuf, &pMic, sizeof(pMic)); 20308c2ecf20Sopenharmony_ci sendbuf += sizeof(pMic); 20318c2ecf20Sopenharmony_ci memcpy (sendbuf, buffer, len - sizeof(etherHead)); 20328c2ecf20Sopenharmony_ci } else { 20338c2ecf20Sopenharmony_ci *payloadLen = cpu_to_le16(len - sizeof(etherHead)); 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci netif_trans_update(dev); 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci /* copy data into airo dma buffer */ 20388c2ecf20Sopenharmony_ci memcpy(sendbuf, buffer, len); 20398c2ecf20Sopenharmony_ci } 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci memcpy_toio(ai->txfids[0].card_ram_off, 20428c2ecf20Sopenharmony_ci &ai->txfids[0].tx_desc, sizeof(TxFid)); 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, 8); 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 20478c2ecf20Sopenharmony_ci return 1; 20488c2ecf20Sopenharmony_ci} 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_cistatic void get_tx_error(struct airo_info *ai, s32 fid) 20518c2ecf20Sopenharmony_ci{ 20528c2ecf20Sopenharmony_ci __le16 status; 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci if (fid < 0) 20558c2ecf20Sopenharmony_ci status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status; 20568c2ecf20Sopenharmony_ci else { 20578c2ecf20Sopenharmony_ci if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS) 20588c2ecf20Sopenharmony_ci return; 20598c2ecf20Sopenharmony_ci bap_read(ai, &status, 2, BAP0); 20608c2ecf20Sopenharmony_ci } 20618c2ecf20Sopenharmony_ci if (le16_to_cpu(status) & 2) /* Too many retries */ 20628c2ecf20Sopenharmony_ci ai->dev->stats.tx_aborted_errors++; 20638c2ecf20Sopenharmony_ci if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */ 20648c2ecf20Sopenharmony_ci ai->dev->stats.tx_heartbeat_errors++; 20658c2ecf20Sopenharmony_ci if (le16_to_cpu(status) & 8) /* Aid fail */ 20668c2ecf20Sopenharmony_ci { } 20678c2ecf20Sopenharmony_ci if (le16_to_cpu(status) & 0x10) /* MAC disabled */ 20688c2ecf20Sopenharmony_ci ai->dev->stats.tx_carrier_errors++; 20698c2ecf20Sopenharmony_ci if (le16_to_cpu(status) & 0x20) /* Association lost */ 20708c2ecf20Sopenharmony_ci { } 20718c2ecf20Sopenharmony_ci /* We produce a TXDROP event only for retry or lifetime 20728c2ecf20Sopenharmony_ci * exceeded, because that's the only status that really mean 20738c2ecf20Sopenharmony_ci * that this particular node went away. 20748c2ecf20Sopenharmony_ci * Other errors means that *we* screwed up. - Jean II */ 20758c2ecf20Sopenharmony_ci if ((le16_to_cpu(status) & 2) || 20768c2ecf20Sopenharmony_ci (le16_to_cpu(status) & 4)) { 20778c2ecf20Sopenharmony_ci union iwreq_data wrqu; 20788c2ecf20Sopenharmony_ci char junk[0x18]; 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci /* Faster to skip over useless data than to do 20818c2ecf20Sopenharmony_ci * another bap_setup(). We are at offset 0x6 and 20828c2ecf20Sopenharmony_ci * need to go to 0x18 and read 6 bytes - Jean II */ 20838c2ecf20Sopenharmony_ci bap_read(ai, (__le16 *) junk, 0x18, BAP0); 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci /* Copy 802.11 dest address. 20868c2ecf20Sopenharmony_ci * We use the 802.11 header because the frame may 20878c2ecf20Sopenharmony_ci * not be 802.3 or may be mangled... 20888c2ecf20Sopenharmony_ci * In Ad-Hoc mode, it will be the node address. 20898c2ecf20Sopenharmony_ci * In managed mode, it will be most likely the AP addr 20908c2ecf20Sopenharmony_ci * User space will figure out how to convert it to 20918c2ecf20Sopenharmony_ci * whatever it needs (IP address or else). 20928c2ecf20Sopenharmony_ci * - Jean II */ 20938c2ecf20Sopenharmony_ci memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN); 20948c2ecf20Sopenharmony_ci wrqu.addr.sa_family = ARPHRD_ETHER; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci /* Send event to user space */ 20978c2ecf20Sopenharmony_ci wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL); 20988c2ecf20Sopenharmony_ci } 20998c2ecf20Sopenharmony_ci} 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_cistatic void airo_end_xmit(struct net_device *dev) 21028c2ecf20Sopenharmony_ci{ 21038c2ecf20Sopenharmony_ci u16 status; 21048c2ecf20Sopenharmony_ci int i; 21058c2ecf20Sopenharmony_ci struct airo_info *priv = dev->ml_priv; 21068c2ecf20Sopenharmony_ci struct sk_buff *skb = priv->xmit.skb; 21078c2ecf20Sopenharmony_ci int fid = priv->xmit.fid; 21088c2ecf20Sopenharmony_ci u32 *fids = priv->fids; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci clear_bit(JOB_XMIT, &priv->jobs); 21118c2ecf20Sopenharmony_ci clear_bit(FLAG_PENDING_XMIT, &priv->flags); 21128c2ecf20Sopenharmony_ci status = transmit_802_3_packet (priv, fids[fid], skb->data); 21138c2ecf20Sopenharmony_ci up(&priv->sem); 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci i = 0; 21168c2ecf20Sopenharmony_ci if (status == SUCCESS) { 21178c2ecf20Sopenharmony_ci netif_trans_update(dev); 21188c2ecf20Sopenharmony_ci for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++); 21198c2ecf20Sopenharmony_ci } else { 21208c2ecf20Sopenharmony_ci priv->fids[fid] &= 0xffff; 21218c2ecf20Sopenharmony_ci dev->stats.tx_window_errors++; 21228c2ecf20Sopenharmony_ci } 21238c2ecf20Sopenharmony_ci if (i < MAX_FIDS / 2) 21248c2ecf20Sopenharmony_ci netif_wake_queue(dev); 21258c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 21268c2ecf20Sopenharmony_ci} 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_cistatic netdev_tx_t airo_start_xmit(struct sk_buff *skb, 21298c2ecf20Sopenharmony_ci struct net_device *dev) 21308c2ecf20Sopenharmony_ci{ 21318c2ecf20Sopenharmony_ci s16 len; 21328c2ecf20Sopenharmony_ci int i, j; 21338c2ecf20Sopenharmony_ci struct airo_info *priv = dev->ml_priv; 21348c2ecf20Sopenharmony_ci u32 *fids = priv->fids; 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci if (skb == NULL) { 21378c2ecf20Sopenharmony_ci airo_print_err(dev->name, "%s: skb == NULL!", __func__); 21388c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 21398c2ecf20Sopenharmony_ci } 21408c2ecf20Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) { 21418c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 21428c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 21438c2ecf20Sopenharmony_ci } 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci /* Find a vacant FID */ 21468c2ecf20Sopenharmony_ci for (i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++); 21478c2ecf20Sopenharmony_ci for (j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++); 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci if (j >= MAX_FIDS / 2) { 21508c2ecf20Sopenharmony_ci netif_stop_queue(dev); 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci if (i == MAX_FIDS / 2) { 21538c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 21548c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 21558c2ecf20Sopenharmony_ci } 21568c2ecf20Sopenharmony_ci } 21578c2ecf20Sopenharmony_ci /* check min length*/ 21588c2ecf20Sopenharmony_ci len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; 21598c2ecf20Sopenharmony_ci /* Mark fid as used & save length for later */ 21608c2ecf20Sopenharmony_ci fids[i] |= (len << 16); 21618c2ecf20Sopenharmony_ci priv->xmit.skb = skb; 21628c2ecf20Sopenharmony_ci priv->xmit.fid = i; 21638c2ecf20Sopenharmony_ci if (down_trylock(&priv->sem) != 0) { 21648c2ecf20Sopenharmony_ci set_bit(FLAG_PENDING_XMIT, &priv->flags); 21658c2ecf20Sopenharmony_ci netif_stop_queue(dev); 21668c2ecf20Sopenharmony_ci set_bit(JOB_XMIT, &priv->jobs); 21678c2ecf20Sopenharmony_ci wake_up_interruptible(&priv->thr_wait); 21688c2ecf20Sopenharmony_ci } else 21698c2ecf20Sopenharmony_ci airo_end_xmit(dev); 21708c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 21718c2ecf20Sopenharmony_ci} 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_cistatic void airo_end_xmit11(struct net_device *dev) 21748c2ecf20Sopenharmony_ci{ 21758c2ecf20Sopenharmony_ci u16 status; 21768c2ecf20Sopenharmony_ci int i; 21778c2ecf20Sopenharmony_ci struct airo_info *priv = dev->ml_priv; 21788c2ecf20Sopenharmony_ci struct sk_buff *skb = priv->xmit11.skb; 21798c2ecf20Sopenharmony_ci int fid = priv->xmit11.fid; 21808c2ecf20Sopenharmony_ci u32 *fids = priv->fids; 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci clear_bit(JOB_XMIT11, &priv->jobs); 21838c2ecf20Sopenharmony_ci clear_bit(FLAG_PENDING_XMIT11, &priv->flags); 21848c2ecf20Sopenharmony_ci status = transmit_802_11_packet (priv, fids[fid], skb->data); 21858c2ecf20Sopenharmony_ci up(&priv->sem); 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci i = MAX_FIDS / 2; 21888c2ecf20Sopenharmony_ci if (status == SUCCESS) { 21898c2ecf20Sopenharmony_ci netif_trans_update(dev); 21908c2ecf20Sopenharmony_ci for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++); 21918c2ecf20Sopenharmony_ci } else { 21928c2ecf20Sopenharmony_ci priv->fids[fid] &= 0xffff; 21938c2ecf20Sopenharmony_ci dev->stats.tx_window_errors++; 21948c2ecf20Sopenharmony_ci } 21958c2ecf20Sopenharmony_ci if (i < MAX_FIDS) 21968c2ecf20Sopenharmony_ci netif_wake_queue(dev); 21978c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 21988c2ecf20Sopenharmony_ci} 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_cistatic netdev_tx_t airo_start_xmit11(struct sk_buff *skb, 22018c2ecf20Sopenharmony_ci struct net_device *dev) 22028c2ecf20Sopenharmony_ci{ 22038c2ecf20Sopenharmony_ci s16 len; 22048c2ecf20Sopenharmony_ci int i, j; 22058c2ecf20Sopenharmony_ci struct airo_info *priv = dev->ml_priv; 22068c2ecf20Sopenharmony_ci u32 *fids = priv->fids; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI, &priv->flags)) { 22098c2ecf20Sopenharmony_ci /* Not implemented yet for MPI350 */ 22108c2ecf20Sopenharmony_ci netif_stop_queue(dev); 22118c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 22128c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 22138c2ecf20Sopenharmony_ci } 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci if (skb == NULL) { 22168c2ecf20Sopenharmony_ci airo_print_err(dev->name, "%s: skb == NULL!", __func__); 22178c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 22188c2ecf20Sopenharmony_ci } 22198c2ecf20Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) { 22208c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 22218c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 22228c2ecf20Sopenharmony_ci } 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci /* Find a vacant FID */ 22258c2ecf20Sopenharmony_ci for (i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++); 22268c2ecf20Sopenharmony_ci for (j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++); 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci if (j >= MAX_FIDS) { 22298c2ecf20Sopenharmony_ci netif_stop_queue(dev); 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci if (i == MAX_FIDS) { 22328c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 22338c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 22348c2ecf20Sopenharmony_ci } 22358c2ecf20Sopenharmony_ci } 22368c2ecf20Sopenharmony_ci /* check min length*/ 22378c2ecf20Sopenharmony_ci len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; 22388c2ecf20Sopenharmony_ci /* Mark fid as used & save length for later */ 22398c2ecf20Sopenharmony_ci fids[i] |= (len << 16); 22408c2ecf20Sopenharmony_ci priv->xmit11.skb = skb; 22418c2ecf20Sopenharmony_ci priv->xmit11.fid = i; 22428c2ecf20Sopenharmony_ci if (down_trylock(&priv->sem) != 0) { 22438c2ecf20Sopenharmony_ci set_bit(FLAG_PENDING_XMIT11, &priv->flags); 22448c2ecf20Sopenharmony_ci netif_stop_queue(dev); 22458c2ecf20Sopenharmony_ci set_bit(JOB_XMIT11, &priv->jobs); 22468c2ecf20Sopenharmony_ci wake_up_interruptible(&priv->thr_wait); 22478c2ecf20Sopenharmony_ci } else 22488c2ecf20Sopenharmony_ci airo_end_xmit11(dev); 22498c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 22508c2ecf20Sopenharmony_ci} 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_cistatic void airo_read_stats(struct net_device *dev) 22538c2ecf20Sopenharmony_ci{ 22548c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 22558c2ecf20Sopenharmony_ci StatsRid stats_rid; 22568c2ecf20Sopenharmony_ci __le32 *vals = stats_rid.vals; 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci clear_bit(JOB_STATS, &ai->jobs); 22598c2ecf20Sopenharmony_ci if (ai->power.event) { 22608c2ecf20Sopenharmony_ci up(&ai->sem); 22618c2ecf20Sopenharmony_ci return; 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci readStatsRid(ai, &stats_rid, RID_STATS, 0); 22648c2ecf20Sopenharmony_ci up(&ai->sem); 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) + 22678c2ecf20Sopenharmony_ci le32_to_cpu(vals[45]); 22688c2ecf20Sopenharmony_ci dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) + 22698c2ecf20Sopenharmony_ci le32_to_cpu(vals[41]); 22708c2ecf20Sopenharmony_ci dev->stats.rx_bytes = le32_to_cpu(vals[92]); 22718c2ecf20Sopenharmony_ci dev->stats.tx_bytes = le32_to_cpu(vals[91]); 22728c2ecf20Sopenharmony_ci dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) + 22738c2ecf20Sopenharmony_ci le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]); 22748c2ecf20Sopenharmony_ci dev->stats.tx_errors = le32_to_cpu(vals[42]) + 22758c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors; 22768c2ecf20Sopenharmony_ci dev->stats.multicast = le32_to_cpu(vals[43]); 22778c2ecf20Sopenharmony_ci dev->stats.collisions = le32_to_cpu(vals[89]); 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci /* detailed rx_errors: */ 22808c2ecf20Sopenharmony_ci dev->stats.rx_length_errors = le32_to_cpu(vals[3]); 22818c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors = le32_to_cpu(vals[4]); 22828c2ecf20Sopenharmony_ci dev->stats.rx_frame_errors = le32_to_cpu(vals[2]); 22838c2ecf20Sopenharmony_ci dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]); 22848c2ecf20Sopenharmony_ci} 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_cistatic struct net_device_stats *airo_get_stats(struct net_device *dev) 22878c2ecf20Sopenharmony_ci{ 22888c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci if (!test_bit(JOB_STATS, &local->jobs)) { 22918c2ecf20Sopenharmony_ci /* Get stats out of the card if available */ 22928c2ecf20Sopenharmony_ci if (down_trylock(&local->sem) != 0) { 22938c2ecf20Sopenharmony_ci set_bit(JOB_STATS, &local->jobs); 22948c2ecf20Sopenharmony_ci wake_up_interruptible(&local->thr_wait); 22958c2ecf20Sopenharmony_ci } else 22968c2ecf20Sopenharmony_ci airo_read_stats(dev); 22978c2ecf20Sopenharmony_ci } 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci return &dev->stats; 23008c2ecf20Sopenharmony_ci} 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_cistatic void airo_set_promisc(struct airo_info *ai) 23038c2ecf20Sopenharmony_ci{ 23048c2ecf20Sopenharmony_ci Cmd cmd; 23058c2ecf20Sopenharmony_ci Resp rsp; 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 23088c2ecf20Sopenharmony_ci cmd.cmd = CMD_SETMODE; 23098c2ecf20Sopenharmony_ci clear_bit(JOB_PROMISC, &ai->jobs); 23108c2ecf20Sopenharmony_ci cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC; 23118c2ecf20Sopenharmony_ci issuecommand(ai, &cmd, &rsp); 23128c2ecf20Sopenharmony_ci up(&ai->sem); 23138c2ecf20Sopenharmony_ci} 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_cistatic void airo_set_multicast_list(struct net_device *dev) 23168c2ecf20Sopenharmony_ci{ 23178c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci if ((dev->flags ^ ai->flags) & IFF_PROMISC) { 23208c2ecf20Sopenharmony_ci change_bit(FLAG_PROMISC, &ai->flags); 23218c2ecf20Sopenharmony_ci if (down_trylock(&ai->sem) != 0) { 23228c2ecf20Sopenharmony_ci set_bit(JOB_PROMISC, &ai->jobs); 23238c2ecf20Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 23248c2ecf20Sopenharmony_ci } else 23258c2ecf20Sopenharmony_ci airo_set_promisc(ai); 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci if ((dev->flags&IFF_ALLMULTI) || !netdev_mc_empty(dev)) { 23298c2ecf20Sopenharmony_ci /* Turn on multicast. (Should be already setup...) */ 23308c2ecf20Sopenharmony_ci } 23318c2ecf20Sopenharmony_ci} 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_cistatic int airo_set_mac_address(struct net_device *dev, void *p) 23348c2ecf20Sopenharmony_ci{ 23358c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 23368c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci readConfigRid(ai, 1); 23398c2ecf20Sopenharmony_ci memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len); 23408c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 23418c2ecf20Sopenharmony_ci disable_MAC(ai, 1); 23428c2ecf20Sopenharmony_ci writeConfigRid (ai, 1); 23438c2ecf20Sopenharmony_ci enable_MAC(ai, 1); 23448c2ecf20Sopenharmony_ci memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len); 23458c2ecf20Sopenharmony_ci if (ai->wifidev) 23468c2ecf20Sopenharmony_ci memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len); 23478c2ecf20Sopenharmony_ci return 0; 23488c2ecf20Sopenharmony_ci} 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_cistatic LIST_HEAD(airo_devices); 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_cistatic void add_airo_dev(struct airo_info *ai) 23538c2ecf20Sopenharmony_ci{ 23548c2ecf20Sopenharmony_ci /* Upper layers already keep track of PCI devices, 23558c2ecf20Sopenharmony_ci * so we only need to remember our non-PCI cards. */ 23568c2ecf20Sopenharmony_ci if (!ai->pci) 23578c2ecf20Sopenharmony_ci list_add_tail(&ai->dev_list, &airo_devices); 23588c2ecf20Sopenharmony_ci} 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_cistatic void del_airo_dev(struct airo_info *ai) 23618c2ecf20Sopenharmony_ci{ 23628c2ecf20Sopenharmony_ci if (!ai->pci) 23638c2ecf20Sopenharmony_ci list_del(&ai->dev_list); 23648c2ecf20Sopenharmony_ci} 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_cistatic int airo_close(struct net_device *dev) 23678c2ecf20Sopenharmony_ci{ 23688c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci netif_stop_queue(dev); 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci if (ai->wifidev != dev) { 23738c2ecf20Sopenharmony_ci#ifdef POWER_ON_DOWN 23748c2ecf20Sopenharmony_ci /* Shut power to the card. The idea is that the user can save 23758c2ecf20Sopenharmony_ci * power when he doesn't need the card with "ifconfig down". 23768c2ecf20Sopenharmony_ci * That's the method that is most friendly towards the network 23778c2ecf20Sopenharmony_ci * stack (i.e. the network stack won't try to broadcast 23788c2ecf20Sopenharmony_ci * anything on the interface and routes are gone. Jean II */ 23798c2ecf20Sopenharmony_ci set_bit(FLAG_RADIO_DOWN, &ai->flags); 23808c2ecf20Sopenharmony_ci disable_MAC(ai, 1); 23818c2ecf20Sopenharmony_ci#endif 23828c2ecf20Sopenharmony_ci disable_interrupts(ai); 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci set_bit(JOB_DIE, &ai->jobs); 23878c2ecf20Sopenharmony_ci kthread_stop(ai->airo_thread_task); 23888c2ecf20Sopenharmony_ci } 23898c2ecf20Sopenharmony_ci return 0; 23908c2ecf20Sopenharmony_ci} 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_civoid stop_airo_card(struct net_device *dev, int freeres) 23938c2ecf20Sopenharmony_ci{ 23948c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci set_bit(FLAG_RADIO_DOWN, &ai->flags); 23978c2ecf20Sopenharmony_ci disable_MAC(ai, 1); 23988c2ecf20Sopenharmony_ci disable_interrupts(ai); 23998c2ecf20Sopenharmony_ci takedown_proc_entry(dev, ai); 24008c2ecf20Sopenharmony_ci if (test_bit(FLAG_REGISTERED, &ai->flags)) { 24018c2ecf20Sopenharmony_ci unregister_netdev(dev); 24028c2ecf20Sopenharmony_ci if (ai->wifidev) { 24038c2ecf20Sopenharmony_ci unregister_netdev(ai->wifidev); 24048c2ecf20Sopenharmony_ci free_netdev(ai->wifidev); 24058c2ecf20Sopenharmony_ci ai->wifidev = NULL; 24068c2ecf20Sopenharmony_ci } 24078c2ecf20Sopenharmony_ci clear_bit(FLAG_REGISTERED, &ai->flags); 24088c2ecf20Sopenharmony_ci } 24098c2ecf20Sopenharmony_ci /* 24108c2ecf20Sopenharmony_ci * Clean out tx queue 24118c2ecf20Sopenharmony_ci */ 24128c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) { 24138c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 24148c2ecf20Sopenharmony_ci for (;(skb = skb_dequeue(&ai->txq));) 24158c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 24168c2ecf20Sopenharmony_ci } 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ci airo_networks_free (ai); 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci kfree(ai->flash); 24218c2ecf20Sopenharmony_ci kfree(ai->rssi); 24228c2ecf20Sopenharmony_ci kfree(ai->SSID); 24238c2ecf20Sopenharmony_ci if (freeres) { 24248c2ecf20Sopenharmony_ci /* PCMCIA frees this stuff, so only for PCI and ISA */ 24258c2ecf20Sopenharmony_ci release_region(dev->base_addr, 64); 24268c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI, &ai->flags)) { 24278c2ecf20Sopenharmony_ci if (ai->pci) 24288c2ecf20Sopenharmony_ci mpi_unmap_card(ai->pci); 24298c2ecf20Sopenharmony_ci if (ai->pcimem) 24308c2ecf20Sopenharmony_ci iounmap(ai->pcimem); 24318c2ecf20Sopenharmony_ci if (ai->pciaux) 24328c2ecf20Sopenharmony_ci iounmap(ai->pciaux); 24338c2ecf20Sopenharmony_ci dma_free_coherent(&ai->pci->dev, PCI_SHARED_LEN, 24348c2ecf20Sopenharmony_ci ai->shared, ai->shared_dma); 24358c2ecf20Sopenharmony_ci } 24368c2ecf20Sopenharmony_ci } 24378c2ecf20Sopenharmony_ci crypto_free_sync_skcipher(ai->tfm); 24388c2ecf20Sopenharmony_ci del_airo_dev(ai); 24398c2ecf20Sopenharmony_ci free_netdev(dev); 24408c2ecf20Sopenharmony_ci} 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(stop_airo_card); 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_cistatic int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr) 24458c2ecf20Sopenharmony_ci{ 24468c2ecf20Sopenharmony_ci memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); 24478c2ecf20Sopenharmony_ci return ETH_ALEN; 24488c2ecf20Sopenharmony_ci} 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_cistatic void mpi_unmap_card(struct pci_dev *pci) 24518c2ecf20Sopenharmony_ci{ 24528c2ecf20Sopenharmony_ci unsigned long mem_start = pci_resource_start(pci, 1); 24538c2ecf20Sopenharmony_ci unsigned long mem_len = pci_resource_len(pci, 1); 24548c2ecf20Sopenharmony_ci unsigned long aux_start = pci_resource_start(pci, 2); 24558c2ecf20Sopenharmony_ci unsigned long aux_len = AUXMEMSIZE; 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci release_mem_region(aux_start, aux_len); 24588c2ecf20Sopenharmony_ci release_mem_region(mem_start, mem_len); 24598c2ecf20Sopenharmony_ci} 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci/************************************************************* 24628c2ecf20Sopenharmony_ci * This routine assumes that descriptors have been setup . 24638c2ecf20Sopenharmony_ci * Run at insmod time or after reset when the descriptors 24648c2ecf20Sopenharmony_ci * have been initialized . Returns 0 if all is well nz 24658c2ecf20Sopenharmony_ci * otherwise . Does not allocate memory but sets up card 24668c2ecf20Sopenharmony_ci * using previously allocated descriptors. 24678c2ecf20Sopenharmony_ci */ 24688c2ecf20Sopenharmony_cistatic int mpi_init_descriptors (struct airo_info *ai) 24698c2ecf20Sopenharmony_ci{ 24708c2ecf20Sopenharmony_ci Cmd cmd; 24718c2ecf20Sopenharmony_ci Resp rsp; 24728c2ecf20Sopenharmony_ci int i; 24738c2ecf20Sopenharmony_ci int rc = SUCCESS; 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci /* Alloc card RX descriptors */ 24768c2ecf20Sopenharmony_ci netif_stop_queue(ai->dev); 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 24798c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci cmd.cmd = CMD_ALLOCATEAUX; 24828c2ecf20Sopenharmony_ci cmd.parm0 = FID_RX; 24838c2ecf20Sopenharmony_ci cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux); 24848c2ecf20Sopenharmony_ci cmd.parm2 = MPI_MAX_FIDS; 24858c2ecf20Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp); 24868c2ecf20Sopenharmony_ci if (rc != SUCCESS) { 24878c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "Couldn't allocate RX FID"); 24888c2ecf20Sopenharmony_ci return rc; 24898c2ecf20Sopenharmony_ci } 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci for (i = 0; i<MPI_MAX_FIDS; i++) { 24928c2ecf20Sopenharmony_ci memcpy_toio(ai->rxfids[i].card_ram_off, 24938c2ecf20Sopenharmony_ci &ai->rxfids[i].rx_desc, sizeof(RxFid)); 24948c2ecf20Sopenharmony_ci } 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci /* Alloc card TX descriptors */ 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 24998c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci cmd.cmd = CMD_ALLOCATEAUX; 25028c2ecf20Sopenharmony_ci cmd.parm0 = FID_TX; 25038c2ecf20Sopenharmony_ci cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux); 25048c2ecf20Sopenharmony_ci cmd.parm2 = MPI_MAX_FIDS; 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci for (i = 0; i<MPI_MAX_FIDS; i++) { 25078c2ecf20Sopenharmony_ci ai->txfids[i].tx_desc.valid = 1; 25088c2ecf20Sopenharmony_ci memcpy_toio(ai->txfids[i].card_ram_off, 25098c2ecf20Sopenharmony_ci &ai->txfids[i].tx_desc, sizeof(TxFid)); 25108c2ecf20Sopenharmony_ci } 25118c2ecf20Sopenharmony_ci ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */ 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp); 25148c2ecf20Sopenharmony_ci if (rc != SUCCESS) { 25158c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "Couldn't allocate TX FID"); 25168c2ecf20Sopenharmony_ci return rc; 25178c2ecf20Sopenharmony_ci } 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci /* Alloc card Rid descriptor */ 25208c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 25218c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_ci cmd.cmd = CMD_ALLOCATEAUX; 25248c2ecf20Sopenharmony_ci cmd.parm0 = RID_RW; 25258c2ecf20Sopenharmony_ci cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux); 25268c2ecf20Sopenharmony_ci cmd.parm2 = 1; /* Magic number... */ 25278c2ecf20Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp); 25288c2ecf20Sopenharmony_ci if (rc != SUCCESS) { 25298c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "Couldn't allocate RID"); 25308c2ecf20Sopenharmony_ci return rc; 25318c2ecf20Sopenharmony_ci } 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci memcpy_toio(ai->config_desc.card_ram_off, 25348c2ecf20Sopenharmony_ci &ai->config_desc.rid_desc, sizeof(Rid)); 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci return rc; 25378c2ecf20Sopenharmony_ci} 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci/* 25408c2ecf20Sopenharmony_ci * We are setting up three things here: 25418c2ecf20Sopenharmony_ci * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid. 25428c2ecf20Sopenharmony_ci * 2) Map PCI memory for issuing commands. 25438c2ecf20Sopenharmony_ci * 3) Allocate memory (shared) to send and receive ethernet frames. 25448c2ecf20Sopenharmony_ci */ 25458c2ecf20Sopenharmony_cistatic int mpi_map_card(struct airo_info *ai, struct pci_dev *pci) 25468c2ecf20Sopenharmony_ci{ 25478c2ecf20Sopenharmony_ci unsigned long mem_start, mem_len, aux_start, aux_len; 25488c2ecf20Sopenharmony_ci int rc = -1; 25498c2ecf20Sopenharmony_ci int i; 25508c2ecf20Sopenharmony_ci dma_addr_t busaddroff; 25518c2ecf20Sopenharmony_ci unsigned char *vpackoff; 25528c2ecf20Sopenharmony_ci unsigned char __iomem *pciaddroff; 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci mem_start = pci_resource_start(pci, 1); 25558c2ecf20Sopenharmony_ci mem_len = pci_resource_len(pci, 1); 25568c2ecf20Sopenharmony_ci aux_start = pci_resource_start(pci, 2); 25578c2ecf20Sopenharmony_ci aux_len = AUXMEMSIZE; 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci if (!request_mem_region(mem_start, mem_len, DRV_NAME)) { 25608c2ecf20Sopenharmony_ci airo_print_err("", "Couldn't get region %x[%x]", 25618c2ecf20Sopenharmony_ci (int)mem_start, (int)mem_len); 25628c2ecf20Sopenharmony_ci goto out; 25638c2ecf20Sopenharmony_ci } 25648c2ecf20Sopenharmony_ci if (!request_mem_region(aux_start, aux_len, DRV_NAME)) { 25658c2ecf20Sopenharmony_ci airo_print_err("", "Couldn't get region %x[%x]", 25668c2ecf20Sopenharmony_ci (int)aux_start, (int)aux_len); 25678c2ecf20Sopenharmony_ci goto free_region1; 25688c2ecf20Sopenharmony_ci } 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci ai->pcimem = ioremap(mem_start, mem_len); 25718c2ecf20Sopenharmony_ci if (!ai->pcimem) { 25728c2ecf20Sopenharmony_ci airo_print_err("", "Couldn't map region %x[%x]", 25738c2ecf20Sopenharmony_ci (int)mem_start, (int)mem_len); 25748c2ecf20Sopenharmony_ci goto free_region2; 25758c2ecf20Sopenharmony_ci } 25768c2ecf20Sopenharmony_ci ai->pciaux = ioremap(aux_start, aux_len); 25778c2ecf20Sopenharmony_ci if (!ai->pciaux) { 25788c2ecf20Sopenharmony_ci airo_print_err("", "Couldn't map region %x[%x]", 25798c2ecf20Sopenharmony_ci (int)aux_start, (int)aux_len); 25808c2ecf20Sopenharmony_ci goto free_memmap; 25818c2ecf20Sopenharmony_ci } 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ci /* Reserve PKTSIZE for each fid and 2K for the Rids */ 25848c2ecf20Sopenharmony_ci ai->shared = dma_alloc_coherent(&pci->dev, PCI_SHARED_LEN, 25858c2ecf20Sopenharmony_ci &ai->shared_dma, GFP_KERNEL); 25868c2ecf20Sopenharmony_ci if (!ai->shared) { 25878c2ecf20Sopenharmony_ci airo_print_err("", "Couldn't alloc_coherent %d", 25888c2ecf20Sopenharmony_ci PCI_SHARED_LEN); 25898c2ecf20Sopenharmony_ci goto free_auxmap; 25908c2ecf20Sopenharmony_ci } 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci /* 25938c2ecf20Sopenharmony_ci * Setup descriptor RX, TX, CONFIG 25948c2ecf20Sopenharmony_ci */ 25958c2ecf20Sopenharmony_ci busaddroff = ai->shared_dma; 25968c2ecf20Sopenharmony_ci pciaddroff = ai->pciaux + AUX_OFFSET; 25978c2ecf20Sopenharmony_ci vpackoff = ai->shared; 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ci /* RX descriptor setup */ 26008c2ecf20Sopenharmony_ci for (i = 0; i < MPI_MAX_FIDS; i++) { 26018c2ecf20Sopenharmony_ci ai->rxfids[i].pending = 0; 26028c2ecf20Sopenharmony_ci ai->rxfids[i].card_ram_off = pciaddroff; 26038c2ecf20Sopenharmony_ci ai->rxfids[i].virtual_host_addr = vpackoff; 26048c2ecf20Sopenharmony_ci ai->rxfids[i].rx_desc.host_addr = busaddroff; 26058c2ecf20Sopenharmony_ci ai->rxfids[i].rx_desc.valid = 1; 26068c2ecf20Sopenharmony_ci ai->rxfids[i].rx_desc.len = PKTSIZE; 26078c2ecf20Sopenharmony_ci ai->rxfids[i].rx_desc.rdy = 0; 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci pciaddroff += sizeof(RxFid); 26108c2ecf20Sopenharmony_ci busaddroff += PKTSIZE; 26118c2ecf20Sopenharmony_ci vpackoff += PKTSIZE; 26128c2ecf20Sopenharmony_ci } 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci /* TX descriptor setup */ 26158c2ecf20Sopenharmony_ci for (i = 0; i < MPI_MAX_FIDS; i++) { 26168c2ecf20Sopenharmony_ci ai->txfids[i].card_ram_off = pciaddroff; 26178c2ecf20Sopenharmony_ci ai->txfids[i].virtual_host_addr = vpackoff; 26188c2ecf20Sopenharmony_ci ai->txfids[i].tx_desc.valid = 1; 26198c2ecf20Sopenharmony_ci ai->txfids[i].tx_desc.host_addr = busaddroff; 26208c2ecf20Sopenharmony_ci memcpy(ai->txfids[i].virtual_host_addr, 26218c2ecf20Sopenharmony_ci &wifictlhdr8023, sizeof(wifictlhdr8023)); 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci pciaddroff += sizeof(TxFid); 26248c2ecf20Sopenharmony_ci busaddroff += PKTSIZE; 26258c2ecf20Sopenharmony_ci vpackoff += PKTSIZE; 26268c2ecf20Sopenharmony_ci } 26278c2ecf20Sopenharmony_ci ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */ 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci /* Rid descriptor setup */ 26308c2ecf20Sopenharmony_ci ai->config_desc.card_ram_off = pciaddroff; 26318c2ecf20Sopenharmony_ci ai->config_desc.virtual_host_addr = vpackoff; 26328c2ecf20Sopenharmony_ci ai->config_desc.rid_desc.host_addr = busaddroff; 26338c2ecf20Sopenharmony_ci ai->ridbus = busaddroff; 26348c2ecf20Sopenharmony_ci ai->config_desc.rid_desc.rid = 0; 26358c2ecf20Sopenharmony_ci ai->config_desc.rid_desc.len = RIDSIZE; 26368c2ecf20Sopenharmony_ci ai->config_desc.rid_desc.valid = 1; 26378c2ecf20Sopenharmony_ci pciaddroff += sizeof(Rid); 26388c2ecf20Sopenharmony_ci busaddroff += RIDSIZE; 26398c2ecf20Sopenharmony_ci vpackoff += RIDSIZE; 26408c2ecf20Sopenharmony_ci 26418c2ecf20Sopenharmony_ci /* Tell card about descriptors */ 26428c2ecf20Sopenharmony_ci if (mpi_init_descriptors (ai) != SUCCESS) 26438c2ecf20Sopenharmony_ci goto free_shared; 26448c2ecf20Sopenharmony_ci 26458c2ecf20Sopenharmony_ci return 0; 26468c2ecf20Sopenharmony_ci free_shared: 26478c2ecf20Sopenharmony_ci dma_free_coherent(&pci->dev, PCI_SHARED_LEN, ai->shared, 26488c2ecf20Sopenharmony_ci ai->shared_dma); 26498c2ecf20Sopenharmony_ci free_auxmap: 26508c2ecf20Sopenharmony_ci iounmap(ai->pciaux); 26518c2ecf20Sopenharmony_ci free_memmap: 26528c2ecf20Sopenharmony_ci iounmap(ai->pcimem); 26538c2ecf20Sopenharmony_ci free_region2: 26548c2ecf20Sopenharmony_ci release_mem_region(aux_start, aux_len); 26558c2ecf20Sopenharmony_ci free_region1: 26568c2ecf20Sopenharmony_ci release_mem_region(mem_start, mem_len); 26578c2ecf20Sopenharmony_ci out: 26588c2ecf20Sopenharmony_ci return rc; 26598c2ecf20Sopenharmony_ci} 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_cistatic const struct header_ops airo_header_ops = { 26628c2ecf20Sopenharmony_ci .parse = wll_header_parse, 26638c2ecf20Sopenharmony_ci}; 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_cistatic const struct net_device_ops airo11_netdev_ops = { 26668c2ecf20Sopenharmony_ci .ndo_open = airo_open, 26678c2ecf20Sopenharmony_ci .ndo_stop = airo_close, 26688c2ecf20Sopenharmony_ci .ndo_start_xmit = airo_start_xmit11, 26698c2ecf20Sopenharmony_ci .ndo_get_stats = airo_get_stats, 26708c2ecf20Sopenharmony_ci .ndo_set_mac_address = airo_set_mac_address, 26718c2ecf20Sopenharmony_ci .ndo_do_ioctl = airo_ioctl, 26728c2ecf20Sopenharmony_ci}; 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_cistatic void wifi_setup(struct net_device *dev) 26758c2ecf20Sopenharmony_ci{ 26768c2ecf20Sopenharmony_ci dev->netdev_ops = &airo11_netdev_ops; 26778c2ecf20Sopenharmony_ci dev->header_ops = &airo_header_ops; 26788c2ecf20Sopenharmony_ci dev->wireless_handlers = &airo_handler_def; 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci dev->type = ARPHRD_IEEE80211; 26818c2ecf20Sopenharmony_ci dev->hard_header_len = ETH_HLEN; 26828c2ecf20Sopenharmony_ci dev->mtu = AIRO_DEF_MTU; 26838c2ecf20Sopenharmony_ci dev->min_mtu = 68; 26848c2ecf20Sopenharmony_ci dev->max_mtu = MIC_MSGLEN_MAX; 26858c2ecf20Sopenharmony_ci dev->addr_len = ETH_ALEN; 26868c2ecf20Sopenharmony_ci dev->tx_queue_len = 100; 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_ci eth_broadcast_addr(dev->broadcast); 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci dev->flags = IFF_BROADCAST|IFF_MULTICAST; 26918c2ecf20Sopenharmony_ci} 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_cistatic struct net_device *init_wifidev(struct airo_info *ai, 26948c2ecf20Sopenharmony_ci struct net_device *ethdev) 26958c2ecf20Sopenharmony_ci{ 26968c2ecf20Sopenharmony_ci int err; 26978c2ecf20Sopenharmony_ci struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN, 26988c2ecf20Sopenharmony_ci wifi_setup); 26998c2ecf20Sopenharmony_ci if (!dev) 27008c2ecf20Sopenharmony_ci return NULL; 27018c2ecf20Sopenharmony_ci dev->ml_priv = ethdev->ml_priv; 27028c2ecf20Sopenharmony_ci dev->irq = ethdev->irq; 27038c2ecf20Sopenharmony_ci dev->base_addr = ethdev->base_addr; 27048c2ecf20Sopenharmony_ci dev->wireless_data = ethdev->wireless_data; 27058c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, ethdev->dev.parent); 27068c2ecf20Sopenharmony_ci eth_hw_addr_inherit(dev, ethdev); 27078c2ecf20Sopenharmony_ci err = register_netdev(dev); 27088c2ecf20Sopenharmony_ci if (err<0) { 27098c2ecf20Sopenharmony_ci free_netdev(dev); 27108c2ecf20Sopenharmony_ci return NULL; 27118c2ecf20Sopenharmony_ci } 27128c2ecf20Sopenharmony_ci return dev; 27138c2ecf20Sopenharmony_ci} 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_cistatic int reset_card(struct net_device *dev, int lock) 27168c2ecf20Sopenharmony_ci{ 27178c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci if (lock && down_interruptible(&ai->sem)) 27208c2ecf20Sopenharmony_ci return -1; 27218c2ecf20Sopenharmony_ci waitbusy (ai); 27228c2ecf20Sopenharmony_ci OUT4500(ai, COMMAND, CMD_SOFTRESET); 27238c2ecf20Sopenharmony_ci msleep(200); 27248c2ecf20Sopenharmony_ci waitbusy (ai); 27258c2ecf20Sopenharmony_ci msleep(200); 27268c2ecf20Sopenharmony_ci if (lock) 27278c2ecf20Sopenharmony_ci up(&ai->sem); 27288c2ecf20Sopenharmony_ci return 0; 27298c2ecf20Sopenharmony_ci} 27308c2ecf20Sopenharmony_ci 27318c2ecf20Sopenharmony_ci#define AIRO_MAX_NETWORK_COUNT 64 27328c2ecf20Sopenharmony_cistatic int airo_networks_allocate(struct airo_info *ai) 27338c2ecf20Sopenharmony_ci{ 27348c2ecf20Sopenharmony_ci if (ai->networks) 27358c2ecf20Sopenharmony_ci return 0; 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci ai->networks = kcalloc(AIRO_MAX_NETWORK_COUNT, sizeof(BSSListElement), 27388c2ecf20Sopenharmony_ci GFP_KERNEL); 27398c2ecf20Sopenharmony_ci if (!ai->networks) { 27408c2ecf20Sopenharmony_ci airo_print_warn("", "Out of memory allocating beacons"); 27418c2ecf20Sopenharmony_ci return -ENOMEM; 27428c2ecf20Sopenharmony_ci } 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_ci return 0; 27458c2ecf20Sopenharmony_ci} 27468c2ecf20Sopenharmony_ci 27478c2ecf20Sopenharmony_cistatic void airo_networks_free(struct airo_info *ai) 27488c2ecf20Sopenharmony_ci{ 27498c2ecf20Sopenharmony_ci kfree(ai->networks); 27508c2ecf20Sopenharmony_ci ai->networks = NULL; 27518c2ecf20Sopenharmony_ci} 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_cistatic void airo_networks_initialize(struct airo_info *ai) 27548c2ecf20Sopenharmony_ci{ 27558c2ecf20Sopenharmony_ci int i; 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ai->network_free_list); 27588c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ai->network_list); 27598c2ecf20Sopenharmony_ci for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++) 27608c2ecf20Sopenharmony_ci list_add_tail(&ai->networks[i].list, 27618c2ecf20Sopenharmony_ci &ai->network_free_list); 27628c2ecf20Sopenharmony_ci} 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_cistatic const struct net_device_ops airo_netdev_ops = { 27658c2ecf20Sopenharmony_ci .ndo_open = airo_open, 27668c2ecf20Sopenharmony_ci .ndo_stop = airo_close, 27678c2ecf20Sopenharmony_ci .ndo_start_xmit = airo_start_xmit, 27688c2ecf20Sopenharmony_ci .ndo_get_stats = airo_get_stats, 27698c2ecf20Sopenharmony_ci .ndo_set_rx_mode = airo_set_multicast_list, 27708c2ecf20Sopenharmony_ci .ndo_set_mac_address = airo_set_mac_address, 27718c2ecf20Sopenharmony_ci .ndo_do_ioctl = airo_ioctl, 27728c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 27738c2ecf20Sopenharmony_ci}; 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_cistatic const struct net_device_ops mpi_netdev_ops = { 27768c2ecf20Sopenharmony_ci .ndo_open = airo_open, 27778c2ecf20Sopenharmony_ci .ndo_stop = airo_close, 27788c2ecf20Sopenharmony_ci .ndo_start_xmit = mpi_start_xmit, 27798c2ecf20Sopenharmony_ci .ndo_get_stats = airo_get_stats, 27808c2ecf20Sopenharmony_ci .ndo_set_rx_mode = airo_set_multicast_list, 27818c2ecf20Sopenharmony_ci .ndo_set_mac_address = airo_set_mac_address, 27828c2ecf20Sopenharmony_ci .ndo_do_ioctl = airo_ioctl, 27838c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 27848c2ecf20Sopenharmony_ci}; 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_cistatic struct net_device *_init_airo_card(unsigned short irq, int port, 27888c2ecf20Sopenharmony_ci int is_pcmcia, struct pci_dev *pci, 27898c2ecf20Sopenharmony_ci struct device *dmdev) 27908c2ecf20Sopenharmony_ci{ 27918c2ecf20Sopenharmony_ci struct net_device *dev; 27928c2ecf20Sopenharmony_ci struct airo_info *ai; 27938c2ecf20Sopenharmony_ci int i, rc; 27948c2ecf20Sopenharmony_ci CapabilityRid cap_rid; 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_ci /* Create the network device object. */ 27978c2ecf20Sopenharmony_ci dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup); 27988c2ecf20Sopenharmony_ci if (!dev) { 27998c2ecf20Sopenharmony_ci airo_print_err("", "Couldn't alloc_etherdev"); 28008c2ecf20Sopenharmony_ci return NULL; 28018c2ecf20Sopenharmony_ci } 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci ai = dev->ml_priv = netdev_priv(dev); 28048c2ecf20Sopenharmony_ci ai->wifidev = NULL; 28058c2ecf20Sopenharmony_ci ai->flags = 1 << FLAG_RADIO_DOWN; 28068c2ecf20Sopenharmony_ci ai->jobs = 0; 28078c2ecf20Sopenharmony_ci ai->dev = dev; 28088c2ecf20Sopenharmony_ci if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) { 28098c2ecf20Sopenharmony_ci airo_print_dbg("", "Found an MPI350 card"); 28108c2ecf20Sopenharmony_ci set_bit(FLAG_MPI, &ai->flags); 28118c2ecf20Sopenharmony_ci } 28128c2ecf20Sopenharmony_ci spin_lock_init(&ai->aux_lock); 28138c2ecf20Sopenharmony_ci sema_init(&ai->sem, 1); 28148c2ecf20Sopenharmony_ci ai->config.len = 0; 28158c2ecf20Sopenharmony_ci ai->pci = pci; 28168c2ecf20Sopenharmony_ci init_waitqueue_head (&ai->thr_wait); 28178c2ecf20Sopenharmony_ci ai->tfm = NULL; 28188c2ecf20Sopenharmony_ci add_airo_dev(ai); 28198c2ecf20Sopenharmony_ci ai->APList.len = cpu_to_le16(sizeof(struct APListRid)); 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci if (airo_networks_allocate (ai)) 28228c2ecf20Sopenharmony_ci goto err_out_free; 28238c2ecf20Sopenharmony_ci airo_networks_initialize (ai); 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci skb_queue_head_init (&ai->txq); 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_ci /* The Airo-specific entries in the device structure. */ 28288c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) 28298c2ecf20Sopenharmony_ci dev->netdev_ops = &mpi_netdev_ops; 28308c2ecf20Sopenharmony_ci else 28318c2ecf20Sopenharmony_ci dev->netdev_ops = &airo_netdev_ops; 28328c2ecf20Sopenharmony_ci dev->wireless_handlers = &airo_handler_def; 28338c2ecf20Sopenharmony_ci ai->wireless_data.spy_data = &ai->spy_data; 28348c2ecf20Sopenharmony_ci dev->wireless_data = &ai->wireless_data; 28358c2ecf20Sopenharmony_ci dev->irq = irq; 28368c2ecf20Sopenharmony_ci dev->base_addr = port; 28378c2ecf20Sopenharmony_ci dev->priv_flags &= ~IFF_TX_SKB_SHARING; 28388c2ecf20Sopenharmony_ci dev->max_mtu = MIC_MSGLEN_MAX; 28398c2ecf20Sopenharmony_ci 28408c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, dmdev); 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_ci reset_card (dev, 1); 28438c2ecf20Sopenharmony_ci msleep(400); 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci if (!is_pcmcia) { 28468c2ecf20Sopenharmony_ci if (!request_region(dev->base_addr, 64, DRV_NAME)) { 28478c2ecf20Sopenharmony_ci rc = -EBUSY; 28488c2ecf20Sopenharmony_ci airo_print_err(dev->name, "Couldn't request region"); 28498c2ecf20Sopenharmony_ci goto err_out_nets; 28508c2ecf20Sopenharmony_ci } 28518c2ecf20Sopenharmony_ci } 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) { 28548c2ecf20Sopenharmony_ci if (mpi_map_card(ai, pci)) { 28558c2ecf20Sopenharmony_ci airo_print_err("", "Could not map memory"); 28568c2ecf20Sopenharmony_ci goto err_out_res; 28578c2ecf20Sopenharmony_ci } 28588c2ecf20Sopenharmony_ci } 28598c2ecf20Sopenharmony_ci 28608c2ecf20Sopenharmony_ci if (probe) { 28618c2ecf20Sopenharmony_ci if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) { 28628c2ecf20Sopenharmony_ci airo_print_err(dev->name, "MAC could not be enabled"); 28638c2ecf20Sopenharmony_ci rc = -EIO; 28648c2ecf20Sopenharmony_ci goto err_out_map; 28658c2ecf20Sopenharmony_ci } 28668c2ecf20Sopenharmony_ci } else if (!test_bit(FLAG_MPI,&ai->flags)) { 28678c2ecf20Sopenharmony_ci ai->bap_read = fast_bap_read; 28688c2ecf20Sopenharmony_ci set_bit(FLAG_FLASHING, &ai->flags); 28698c2ecf20Sopenharmony_ci } 28708c2ecf20Sopenharmony_ci 28718c2ecf20Sopenharmony_ci strcpy(dev->name, "eth%d"); 28728c2ecf20Sopenharmony_ci rc = register_netdev(dev); 28738c2ecf20Sopenharmony_ci if (rc) { 28748c2ecf20Sopenharmony_ci airo_print_err(dev->name, "Couldn't register_netdev"); 28758c2ecf20Sopenharmony_ci goto err_out_map; 28768c2ecf20Sopenharmony_ci } 28778c2ecf20Sopenharmony_ci ai->wifidev = init_wifidev(ai, dev); 28788c2ecf20Sopenharmony_ci if (!ai->wifidev) 28798c2ecf20Sopenharmony_ci goto err_out_reg; 28808c2ecf20Sopenharmony_ci 28818c2ecf20Sopenharmony_ci rc = readCapabilityRid(ai, &cap_rid, 1); 28828c2ecf20Sopenharmony_ci if (rc != SUCCESS) { 28838c2ecf20Sopenharmony_ci rc = -EIO; 28848c2ecf20Sopenharmony_ci goto err_out_wifi; 28858c2ecf20Sopenharmony_ci } 28868c2ecf20Sopenharmony_ci /* WEP capability discovery */ 28878c2ecf20Sopenharmony_ci ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0; 28888c2ecf20Sopenharmony_ci ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0; 28898c2ecf20Sopenharmony_ci 28908c2ecf20Sopenharmony_ci airo_print_info(dev->name, "Firmware version %x.%x.%02d", 28918c2ecf20Sopenharmony_ci ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF), 28928c2ecf20Sopenharmony_ci (le16_to_cpu(cap_rid.softVer) & 0xFF), 28938c2ecf20Sopenharmony_ci le16_to_cpu(cap_rid.softSubVer)); 28948c2ecf20Sopenharmony_ci 28958c2ecf20Sopenharmony_ci /* Test for WPA support */ 28968c2ecf20Sopenharmony_ci /* Only firmware versions 5.30.17 or better can do WPA */ 28978c2ecf20Sopenharmony_ci if (le16_to_cpu(cap_rid.softVer) > 0x530 28988c2ecf20Sopenharmony_ci || (le16_to_cpu(cap_rid.softVer) == 0x530 28998c2ecf20Sopenharmony_ci && le16_to_cpu(cap_rid.softSubVer) >= 17)) { 29008c2ecf20Sopenharmony_ci airo_print_info(ai->dev->name, "WPA supported."); 29018c2ecf20Sopenharmony_ci 29028c2ecf20Sopenharmony_ci set_bit(FLAG_WPA_CAPABLE, &ai->flags); 29038c2ecf20Sopenharmony_ci ai->bssListFirst = RID_WPA_BSSLISTFIRST; 29048c2ecf20Sopenharmony_ci ai->bssListNext = RID_WPA_BSSLISTNEXT; 29058c2ecf20Sopenharmony_ci ai->bssListRidLen = sizeof(BSSListRid); 29068c2ecf20Sopenharmony_ci } else { 29078c2ecf20Sopenharmony_ci airo_print_info(ai->dev->name, "WPA unsupported with firmware " 29088c2ecf20Sopenharmony_ci "versions older than 5.30.17."); 29098c2ecf20Sopenharmony_ci 29108c2ecf20Sopenharmony_ci ai->bssListFirst = RID_BSSLISTFIRST; 29118c2ecf20Sopenharmony_ci ai->bssListNext = RID_BSSLISTNEXT; 29128c2ecf20Sopenharmony_ci ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra); 29138c2ecf20Sopenharmony_ci } 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_ci set_bit(FLAG_REGISTERED,&ai->flags); 29168c2ecf20Sopenharmony_ci airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr); 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci /* Allocate the transmit buffers */ 29198c2ecf20Sopenharmony_ci if (probe && !test_bit(FLAG_MPI,&ai->flags)) 29208c2ecf20Sopenharmony_ci for (i = 0; i < MAX_FIDS; i++) 29218c2ecf20Sopenharmony_ci ai->fids[i] = transmit_allocate(ai, AIRO_DEF_MTU, i>=MAX_FIDS/2); 29228c2ecf20Sopenharmony_ci 29238c2ecf20Sopenharmony_ci if (setup_proc_entry(dev, dev->ml_priv) < 0) 29248c2ecf20Sopenharmony_ci goto err_out_wifi; 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci return dev; 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_cierr_out_wifi: 29298c2ecf20Sopenharmony_ci unregister_netdev(ai->wifidev); 29308c2ecf20Sopenharmony_ci free_netdev(ai->wifidev); 29318c2ecf20Sopenharmony_cierr_out_reg: 29328c2ecf20Sopenharmony_ci unregister_netdev(dev); 29338c2ecf20Sopenharmony_cierr_out_map: 29348c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags) && pci) { 29358c2ecf20Sopenharmony_ci dma_free_coherent(&pci->dev, PCI_SHARED_LEN, ai->shared, 29368c2ecf20Sopenharmony_ci ai->shared_dma); 29378c2ecf20Sopenharmony_ci iounmap(ai->pciaux); 29388c2ecf20Sopenharmony_ci iounmap(ai->pcimem); 29398c2ecf20Sopenharmony_ci mpi_unmap_card(ai->pci); 29408c2ecf20Sopenharmony_ci } 29418c2ecf20Sopenharmony_cierr_out_res: 29428c2ecf20Sopenharmony_ci if (!is_pcmcia) 29438c2ecf20Sopenharmony_ci release_region(dev->base_addr, 64); 29448c2ecf20Sopenharmony_cierr_out_nets: 29458c2ecf20Sopenharmony_ci airo_networks_free(ai); 29468c2ecf20Sopenharmony_cierr_out_free: 29478c2ecf20Sopenharmony_ci del_airo_dev(ai); 29488c2ecf20Sopenharmony_ci free_netdev(dev); 29498c2ecf20Sopenharmony_ci return NULL; 29508c2ecf20Sopenharmony_ci} 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_cistruct net_device *init_airo_card(unsigned short irq, int port, int is_pcmcia, 29538c2ecf20Sopenharmony_ci struct device *dmdev) 29548c2ecf20Sopenharmony_ci{ 29558c2ecf20Sopenharmony_ci return _init_airo_card (irq, port, is_pcmcia, NULL, dmdev); 29568c2ecf20Sopenharmony_ci} 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(init_airo_card); 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_cistatic int waitbusy (struct airo_info *ai) 29618c2ecf20Sopenharmony_ci{ 29628c2ecf20Sopenharmony_ci int delay = 0; 29638c2ecf20Sopenharmony_ci while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) { 29648c2ecf20Sopenharmony_ci udelay (10); 29658c2ecf20Sopenharmony_ci if ((++delay % 20) == 0) 29668c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); 29678c2ecf20Sopenharmony_ci } 29688c2ecf20Sopenharmony_ci return delay < 10000; 29698c2ecf20Sopenharmony_ci} 29708c2ecf20Sopenharmony_ci 29718c2ecf20Sopenharmony_ciint reset_airo_card(struct net_device *dev) 29728c2ecf20Sopenharmony_ci{ 29738c2ecf20Sopenharmony_ci int i; 29748c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_ci if (reset_card (dev, 1)) 29778c2ecf20Sopenharmony_ci return -1; 29788c2ecf20Sopenharmony_ci 29798c2ecf20Sopenharmony_ci if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) { 29808c2ecf20Sopenharmony_ci airo_print_err(dev->name, "MAC could not be enabled"); 29818c2ecf20Sopenharmony_ci return -1; 29828c2ecf20Sopenharmony_ci } 29838c2ecf20Sopenharmony_ci airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr); 29848c2ecf20Sopenharmony_ci /* Allocate the transmit buffers if needed */ 29858c2ecf20Sopenharmony_ci if (!test_bit(FLAG_MPI,&ai->flags)) 29868c2ecf20Sopenharmony_ci for (i = 0; i < MAX_FIDS; i++) 29878c2ecf20Sopenharmony_ci ai->fids[i] = transmit_allocate (ai, AIRO_DEF_MTU, i>=MAX_FIDS/2); 29888c2ecf20Sopenharmony_ci 29898c2ecf20Sopenharmony_ci enable_interrupts(ai); 29908c2ecf20Sopenharmony_ci netif_wake_queue(dev); 29918c2ecf20Sopenharmony_ci return 0; 29928c2ecf20Sopenharmony_ci} 29938c2ecf20Sopenharmony_ci 29948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(reset_airo_card); 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_cistatic void airo_send_event(struct net_device *dev) 29978c2ecf20Sopenharmony_ci{ 29988c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 29998c2ecf20Sopenharmony_ci union iwreq_data wrqu; 30008c2ecf20Sopenharmony_ci StatusRid status_rid; 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci clear_bit(JOB_EVENT, &ai->jobs); 30038c2ecf20Sopenharmony_ci PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0); 30048c2ecf20Sopenharmony_ci up(&ai->sem); 30058c2ecf20Sopenharmony_ci wrqu.data.length = 0; 30068c2ecf20Sopenharmony_ci wrqu.data.flags = 0; 30078c2ecf20Sopenharmony_ci memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN); 30088c2ecf20Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ci /* Send event to user space */ 30118c2ecf20Sopenharmony_ci wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 30128c2ecf20Sopenharmony_ci} 30138c2ecf20Sopenharmony_ci 30148c2ecf20Sopenharmony_cistatic void airo_process_scan_results (struct airo_info *ai) 30158c2ecf20Sopenharmony_ci{ 30168c2ecf20Sopenharmony_ci union iwreq_data wrqu; 30178c2ecf20Sopenharmony_ci BSSListRid bss; 30188c2ecf20Sopenharmony_ci int rc; 30198c2ecf20Sopenharmony_ci BSSListElement * loop_net; 30208c2ecf20Sopenharmony_ci BSSListElement * tmp_net; 30218c2ecf20Sopenharmony_ci 30228c2ecf20Sopenharmony_ci /* Blow away current list of scan results */ 30238c2ecf20Sopenharmony_ci list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) { 30248c2ecf20Sopenharmony_ci list_move_tail (&loop_net->list, &ai->network_free_list); 30258c2ecf20Sopenharmony_ci /* Don't blow away ->list, just BSS data */ 30268c2ecf20Sopenharmony_ci memset (loop_net, 0, sizeof (loop_net->bss)); 30278c2ecf20Sopenharmony_ci } 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_ci /* Try to read the first entry of the scan result */ 30308c2ecf20Sopenharmony_ci rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0); 30318c2ecf20Sopenharmony_ci if ((rc) || (bss.index == cpu_to_le16(0xffff))) { 30328c2ecf20Sopenharmony_ci /* No scan results */ 30338c2ecf20Sopenharmony_ci goto out; 30348c2ecf20Sopenharmony_ci } 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci /* Read and parse all entries */ 30378c2ecf20Sopenharmony_ci tmp_net = NULL; 30388c2ecf20Sopenharmony_ci while ((!rc) && (bss.index != cpu_to_le16(0xffff))) { 30398c2ecf20Sopenharmony_ci /* Grab a network off the free list */ 30408c2ecf20Sopenharmony_ci if (!list_empty(&ai->network_free_list)) { 30418c2ecf20Sopenharmony_ci tmp_net = list_entry(ai->network_free_list.next, 30428c2ecf20Sopenharmony_ci BSSListElement, list); 30438c2ecf20Sopenharmony_ci list_del(ai->network_free_list.next); 30448c2ecf20Sopenharmony_ci } 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci if (tmp_net != NULL) { 30478c2ecf20Sopenharmony_ci memcpy(tmp_net, &bss, sizeof(tmp_net->bss)); 30488c2ecf20Sopenharmony_ci list_add_tail(&tmp_net->list, &ai->network_list); 30498c2ecf20Sopenharmony_ci tmp_net = NULL; 30508c2ecf20Sopenharmony_ci } 30518c2ecf20Sopenharmony_ci 30528c2ecf20Sopenharmony_ci /* Read next entry */ 30538c2ecf20Sopenharmony_ci rc = PC4500_readrid(ai, ai->bssListNext, 30548c2ecf20Sopenharmony_ci &bss, ai->bssListRidLen, 0); 30558c2ecf20Sopenharmony_ci } 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ciout: 30588c2ecf20Sopenharmony_ci /* write APList back (we cleared it in airo_set_scan) */ 30598c2ecf20Sopenharmony_ci disable_MAC(ai, 2); 30608c2ecf20Sopenharmony_ci writeAPListRid(ai, &ai->APList, 0); 30618c2ecf20Sopenharmony_ci enable_MAC(ai, 0); 30628c2ecf20Sopenharmony_ci 30638c2ecf20Sopenharmony_ci ai->scan_timeout = 0; 30648c2ecf20Sopenharmony_ci clear_bit(JOB_SCAN_RESULTS, &ai->jobs); 30658c2ecf20Sopenharmony_ci up(&ai->sem); 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci /* Send an empty event to user space. 30688c2ecf20Sopenharmony_ci * We don't send the received data on 30698c2ecf20Sopenharmony_ci * the event because it would require 30708c2ecf20Sopenharmony_ci * us to do complex transcoding, and 30718c2ecf20Sopenharmony_ci * we want to minimise the work done in 30728c2ecf20Sopenharmony_ci * the irq handler. Use a request to 30738c2ecf20Sopenharmony_ci * extract the data - Jean II */ 30748c2ecf20Sopenharmony_ci wrqu.data.length = 0; 30758c2ecf20Sopenharmony_ci wrqu.data.flags = 0; 30768c2ecf20Sopenharmony_ci wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL); 30778c2ecf20Sopenharmony_ci} 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_cistatic int airo_thread(void *data) 30808c2ecf20Sopenharmony_ci{ 30818c2ecf20Sopenharmony_ci struct net_device *dev = data; 30828c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 30838c2ecf20Sopenharmony_ci int locked; 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_ci set_freezable(); 30868c2ecf20Sopenharmony_ci while (1) { 30878c2ecf20Sopenharmony_ci /* make swsusp happy with our thread */ 30888c2ecf20Sopenharmony_ci try_to_freeze(); 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_ci if (test_bit(JOB_DIE, &ai->jobs)) 30918c2ecf20Sopenharmony_ci break; 30928c2ecf20Sopenharmony_ci 30938c2ecf20Sopenharmony_ci if (ai->jobs) { 30948c2ecf20Sopenharmony_ci locked = down_interruptible(&ai->sem); 30958c2ecf20Sopenharmony_ci } else { 30968c2ecf20Sopenharmony_ci wait_queue_entry_t wait; 30978c2ecf20Sopenharmony_ci 30988c2ecf20Sopenharmony_ci init_waitqueue_entry(&wait, current); 30998c2ecf20Sopenharmony_ci add_wait_queue(&ai->thr_wait, &wait); 31008c2ecf20Sopenharmony_ci for (;;) { 31018c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 31028c2ecf20Sopenharmony_ci if (ai->jobs) 31038c2ecf20Sopenharmony_ci break; 31048c2ecf20Sopenharmony_ci if (ai->expires || ai->scan_timeout) { 31058c2ecf20Sopenharmony_ci if (ai->scan_timeout && 31068c2ecf20Sopenharmony_ci time_after_eq(jiffies, ai->scan_timeout)) { 31078c2ecf20Sopenharmony_ci set_bit(JOB_SCAN_RESULTS, &ai->jobs); 31088c2ecf20Sopenharmony_ci break; 31098c2ecf20Sopenharmony_ci } else if (ai->expires && 31108c2ecf20Sopenharmony_ci time_after_eq(jiffies, ai->expires)) { 31118c2ecf20Sopenharmony_ci set_bit(JOB_AUTOWEP, &ai->jobs); 31128c2ecf20Sopenharmony_ci break; 31138c2ecf20Sopenharmony_ci } 31148c2ecf20Sopenharmony_ci if (!kthread_should_stop() && 31158c2ecf20Sopenharmony_ci !freezing(current)) { 31168c2ecf20Sopenharmony_ci unsigned long wake_at; 31178c2ecf20Sopenharmony_ci if (!ai->expires || !ai->scan_timeout) { 31188c2ecf20Sopenharmony_ci wake_at = max(ai->expires, 31198c2ecf20Sopenharmony_ci ai->scan_timeout); 31208c2ecf20Sopenharmony_ci } else { 31218c2ecf20Sopenharmony_ci wake_at = min(ai->expires, 31228c2ecf20Sopenharmony_ci ai->scan_timeout); 31238c2ecf20Sopenharmony_ci } 31248c2ecf20Sopenharmony_ci schedule_timeout(wake_at - jiffies); 31258c2ecf20Sopenharmony_ci continue; 31268c2ecf20Sopenharmony_ci } 31278c2ecf20Sopenharmony_ci } else if (!kthread_should_stop() && 31288c2ecf20Sopenharmony_ci !freezing(current)) { 31298c2ecf20Sopenharmony_ci schedule(); 31308c2ecf20Sopenharmony_ci continue; 31318c2ecf20Sopenharmony_ci } 31328c2ecf20Sopenharmony_ci break; 31338c2ecf20Sopenharmony_ci } 31348c2ecf20Sopenharmony_ci __set_current_state(TASK_RUNNING); 31358c2ecf20Sopenharmony_ci remove_wait_queue(&ai->thr_wait, &wait); 31368c2ecf20Sopenharmony_ci locked = 1; 31378c2ecf20Sopenharmony_ci } 31388c2ecf20Sopenharmony_ci 31398c2ecf20Sopenharmony_ci if (locked) 31408c2ecf20Sopenharmony_ci continue; 31418c2ecf20Sopenharmony_ci 31428c2ecf20Sopenharmony_ci if (test_bit(JOB_DIE, &ai->jobs)) { 31438c2ecf20Sopenharmony_ci up(&ai->sem); 31448c2ecf20Sopenharmony_ci break; 31458c2ecf20Sopenharmony_ci } 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_ci if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) { 31488c2ecf20Sopenharmony_ci up(&ai->sem); 31498c2ecf20Sopenharmony_ci continue; 31508c2ecf20Sopenharmony_ci } 31518c2ecf20Sopenharmony_ci 31528c2ecf20Sopenharmony_ci if (test_bit(JOB_XMIT, &ai->jobs)) 31538c2ecf20Sopenharmony_ci airo_end_xmit(dev); 31548c2ecf20Sopenharmony_ci else if (test_bit(JOB_XMIT11, &ai->jobs)) 31558c2ecf20Sopenharmony_ci airo_end_xmit11(dev); 31568c2ecf20Sopenharmony_ci else if (test_bit(JOB_STATS, &ai->jobs)) 31578c2ecf20Sopenharmony_ci airo_read_stats(dev); 31588c2ecf20Sopenharmony_ci else if (test_bit(JOB_WSTATS, &ai->jobs)) 31598c2ecf20Sopenharmony_ci airo_read_wireless_stats(ai); 31608c2ecf20Sopenharmony_ci else if (test_bit(JOB_PROMISC, &ai->jobs)) 31618c2ecf20Sopenharmony_ci airo_set_promisc(ai); 31628c2ecf20Sopenharmony_ci else if (test_bit(JOB_MIC, &ai->jobs)) 31638c2ecf20Sopenharmony_ci micinit(ai); 31648c2ecf20Sopenharmony_ci else if (test_bit(JOB_EVENT, &ai->jobs)) 31658c2ecf20Sopenharmony_ci airo_send_event(dev); 31668c2ecf20Sopenharmony_ci else if (test_bit(JOB_AUTOWEP, &ai->jobs)) 31678c2ecf20Sopenharmony_ci timer_func(dev); 31688c2ecf20Sopenharmony_ci else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs)) 31698c2ecf20Sopenharmony_ci airo_process_scan_results(ai); 31708c2ecf20Sopenharmony_ci else /* Shouldn't get here, but we make sure to unlock */ 31718c2ecf20Sopenharmony_ci up(&ai->sem); 31728c2ecf20Sopenharmony_ci } 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci return 0; 31758c2ecf20Sopenharmony_ci} 31768c2ecf20Sopenharmony_ci 31778c2ecf20Sopenharmony_cistatic int header_len(__le16 ctl) 31788c2ecf20Sopenharmony_ci{ 31798c2ecf20Sopenharmony_ci u16 fc = le16_to_cpu(ctl); 31808c2ecf20Sopenharmony_ci switch (fc & 0xc) { 31818c2ecf20Sopenharmony_ci case 4: 31828c2ecf20Sopenharmony_ci if ((fc & 0xe0) == 0xc0) 31838c2ecf20Sopenharmony_ci return 10; /* one-address control packet */ 31848c2ecf20Sopenharmony_ci return 16; /* two-address control packet */ 31858c2ecf20Sopenharmony_ci case 8: 31868c2ecf20Sopenharmony_ci if ((fc & 0x300) == 0x300) 31878c2ecf20Sopenharmony_ci return 30; /* WDS packet */ 31888c2ecf20Sopenharmony_ci } 31898c2ecf20Sopenharmony_ci return 24; 31908c2ecf20Sopenharmony_ci} 31918c2ecf20Sopenharmony_ci 31928c2ecf20Sopenharmony_cistatic void airo_handle_cisco_mic(struct airo_info *ai) 31938c2ecf20Sopenharmony_ci{ 31948c2ecf20Sopenharmony_ci if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) { 31958c2ecf20Sopenharmony_ci set_bit(JOB_MIC, &ai->jobs); 31968c2ecf20Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 31978c2ecf20Sopenharmony_ci } 31988c2ecf20Sopenharmony_ci} 31998c2ecf20Sopenharmony_ci 32008c2ecf20Sopenharmony_ci/* Airo Status codes */ 32018c2ecf20Sopenharmony_ci#define STAT_NOBEACON 0x8000 /* Loss of sync - missed beacons */ 32028c2ecf20Sopenharmony_ci#define STAT_MAXRETRIES 0x8001 /* Loss of sync - max retries */ 32038c2ecf20Sopenharmony_ci#define STAT_MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/ 32048c2ecf20Sopenharmony_ci#define STAT_FORCELOSS 0x8003 /* Loss of sync - host request */ 32058c2ecf20Sopenharmony_ci#define STAT_TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */ 32068c2ecf20Sopenharmony_ci#define STAT_DEAUTH 0x8100 /* low byte is 802.11 reason code */ 32078c2ecf20Sopenharmony_ci#define STAT_DISASSOC 0x8200 /* low byte is 802.11 reason code */ 32088c2ecf20Sopenharmony_ci#define STAT_ASSOC_FAIL 0x8400 /* low byte is 802.11 reason code */ 32098c2ecf20Sopenharmony_ci#define STAT_AUTH_FAIL 0x0300 /* low byte is 802.11 reason code */ 32108c2ecf20Sopenharmony_ci#define STAT_ASSOC 0x0400 /* Associated */ 32118c2ecf20Sopenharmony_ci#define STAT_REASSOC 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */ 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_cistatic void airo_print_status(const char *devname, u16 status) 32148c2ecf20Sopenharmony_ci{ 32158c2ecf20Sopenharmony_ci u8 reason = status & 0xFF; 32168c2ecf20Sopenharmony_ci 32178c2ecf20Sopenharmony_ci switch (status & 0xFF00) { 32188c2ecf20Sopenharmony_ci case STAT_NOBEACON: 32198c2ecf20Sopenharmony_ci switch (status) { 32208c2ecf20Sopenharmony_ci case STAT_NOBEACON: 32218c2ecf20Sopenharmony_ci airo_print_dbg(devname, "link lost (missed beacons)"); 32228c2ecf20Sopenharmony_ci break; 32238c2ecf20Sopenharmony_ci case STAT_MAXRETRIES: 32248c2ecf20Sopenharmony_ci case STAT_MAXARL: 32258c2ecf20Sopenharmony_ci airo_print_dbg(devname, "link lost (max retries)"); 32268c2ecf20Sopenharmony_ci break; 32278c2ecf20Sopenharmony_ci case STAT_FORCELOSS: 32288c2ecf20Sopenharmony_ci airo_print_dbg(devname, "link lost (local choice)"); 32298c2ecf20Sopenharmony_ci break; 32308c2ecf20Sopenharmony_ci case STAT_TSFSYNC: 32318c2ecf20Sopenharmony_ci airo_print_dbg(devname, "link lost (TSF sync lost)"); 32328c2ecf20Sopenharmony_ci break; 32338c2ecf20Sopenharmony_ci default: 32348c2ecf20Sopenharmony_ci airo_print_dbg(devname, "unknown status %x\n", status); 32358c2ecf20Sopenharmony_ci break; 32368c2ecf20Sopenharmony_ci } 32378c2ecf20Sopenharmony_ci break; 32388c2ecf20Sopenharmony_ci case STAT_DEAUTH: 32398c2ecf20Sopenharmony_ci airo_print_dbg(devname, "deauthenticated (reason: %d)", reason); 32408c2ecf20Sopenharmony_ci break; 32418c2ecf20Sopenharmony_ci case STAT_DISASSOC: 32428c2ecf20Sopenharmony_ci airo_print_dbg(devname, "disassociated (reason: %d)", reason); 32438c2ecf20Sopenharmony_ci break; 32448c2ecf20Sopenharmony_ci case STAT_ASSOC_FAIL: 32458c2ecf20Sopenharmony_ci airo_print_dbg(devname, "association failed (reason: %d)", 32468c2ecf20Sopenharmony_ci reason); 32478c2ecf20Sopenharmony_ci break; 32488c2ecf20Sopenharmony_ci case STAT_AUTH_FAIL: 32498c2ecf20Sopenharmony_ci airo_print_dbg(devname, "authentication failed (reason: %d)", 32508c2ecf20Sopenharmony_ci reason); 32518c2ecf20Sopenharmony_ci break; 32528c2ecf20Sopenharmony_ci case STAT_ASSOC: 32538c2ecf20Sopenharmony_ci case STAT_REASSOC: 32548c2ecf20Sopenharmony_ci break; 32558c2ecf20Sopenharmony_ci default: 32568c2ecf20Sopenharmony_ci airo_print_dbg(devname, "unknown status %x\n", status); 32578c2ecf20Sopenharmony_ci break; 32588c2ecf20Sopenharmony_ci } 32598c2ecf20Sopenharmony_ci} 32608c2ecf20Sopenharmony_ci 32618c2ecf20Sopenharmony_cistatic void airo_handle_link(struct airo_info *ai) 32628c2ecf20Sopenharmony_ci{ 32638c2ecf20Sopenharmony_ci union iwreq_data wrqu; 32648c2ecf20Sopenharmony_ci int scan_forceloss = 0; 32658c2ecf20Sopenharmony_ci u16 status; 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_ci /* Get new status and acknowledge the link change */ 32688c2ecf20Sopenharmony_ci status = le16_to_cpu(IN4500(ai, LINKSTAT)); 32698c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_LINK); 32708c2ecf20Sopenharmony_ci 32718c2ecf20Sopenharmony_ci if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0)) 32728c2ecf20Sopenharmony_ci scan_forceloss = 1; 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_ci airo_print_status(ai->dev->name, status); 32758c2ecf20Sopenharmony_ci 32768c2ecf20Sopenharmony_ci if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) { 32778c2ecf20Sopenharmony_ci if (auto_wep) 32788c2ecf20Sopenharmony_ci ai->expires = 0; 32798c2ecf20Sopenharmony_ci if (ai->list_bss_task) 32808c2ecf20Sopenharmony_ci wake_up_process(ai->list_bss_task); 32818c2ecf20Sopenharmony_ci set_bit(FLAG_UPDATE_UNI, &ai->flags); 32828c2ecf20Sopenharmony_ci set_bit(FLAG_UPDATE_MULTI, &ai->flags); 32838c2ecf20Sopenharmony_ci 32848c2ecf20Sopenharmony_ci if (down_trylock(&ai->sem) != 0) { 32858c2ecf20Sopenharmony_ci set_bit(JOB_EVENT, &ai->jobs); 32868c2ecf20Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 32878c2ecf20Sopenharmony_ci } else 32888c2ecf20Sopenharmony_ci airo_send_event(ai->dev); 32898c2ecf20Sopenharmony_ci netif_carrier_on(ai->dev); 32908c2ecf20Sopenharmony_ci } else if (!scan_forceloss) { 32918c2ecf20Sopenharmony_ci if (auto_wep && !ai->expires) { 32928c2ecf20Sopenharmony_ci ai->expires = RUN_AT(3*HZ); 32938c2ecf20Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 32948c2ecf20Sopenharmony_ci } 32958c2ecf20Sopenharmony_ci 32968c2ecf20Sopenharmony_ci /* Send event to user space */ 32978c2ecf20Sopenharmony_ci eth_zero_addr(wrqu.ap_addr.sa_data); 32988c2ecf20Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 32998c2ecf20Sopenharmony_ci wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL); 33008c2ecf20Sopenharmony_ci netif_carrier_off(ai->dev); 33018c2ecf20Sopenharmony_ci } else { 33028c2ecf20Sopenharmony_ci netif_carrier_off(ai->dev); 33038c2ecf20Sopenharmony_ci } 33048c2ecf20Sopenharmony_ci} 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_cistatic void airo_handle_rx(struct airo_info *ai) 33078c2ecf20Sopenharmony_ci{ 33088c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 33098c2ecf20Sopenharmony_ci __le16 fc, v, *buffer, tmpbuf[4]; 33108c2ecf20Sopenharmony_ci u16 len, hdrlen = 0, gap, fid; 33118c2ecf20Sopenharmony_ci struct rx_hdr hdr; 33128c2ecf20Sopenharmony_ci int success = 0; 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI, &ai->flags)) { 33158c2ecf20Sopenharmony_ci if (test_bit(FLAG_802_11, &ai->flags)) 33168c2ecf20Sopenharmony_ci mpi_receive_802_11(ai); 33178c2ecf20Sopenharmony_ci else 33188c2ecf20Sopenharmony_ci mpi_receive_802_3(ai); 33198c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_RX); 33208c2ecf20Sopenharmony_ci return; 33218c2ecf20Sopenharmony_ci } 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_ci fid = IN4500(ai, RXFID); 33248c2ecf20Sopenharmony_ci 33258c2ecf20Sopenharmony_ci /* Get the packet length */ 33268c2ecf20Sopenharmony_ci if (test_bit(FLAG_802_11, &ai->flags)) { 33278c2ecf20Sopenharmony_ci bap_setup (ai, fid, 4, BAP0); 33288c2ecf20Sopenharmony_ci bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0); 33298c2ecf20Sopenharmony_ci /* Bad CRC. Ignore packet */ 33308c2ecf20Sopenharmony_ci if (le16_to_cpu(hdr.status) & 2) 33318c2ecf20Sopenharmony_ci hdr.len = 0; 33328c2ecf20Sopenharmony_ci if (ai->wifidev == NULL) 33338c2ecf20Sopenharmony_ci hdr.len = 0; 33348c2ecf20Sopenharmony_ci } else { 33358c2ecf20Sopenharmony_ci bap_setup(ai, fid, 0x36, BAP0); 33368c2ecf20Sopenharmony_ci bap_read(ai, &hdr.len, 2, BAP0); 33378c2ecf20Sopenharmony_ci } 33388c2ecf20Sopenharmony_ci len = le16_to_cpu(hdr.len); 33398c2ecf20Sopenharmony_ci 33408c2ecf20Sopenharmony_ci if (len > AIRO_DEF_MTU) { 33418c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "Bad size %d", len); 33428c2ecf20Sopenharmony_ci goto done; 33438c2ecf20Sopenharmony_ci } 33448c2ecf20Sopenharmony_ci if (len == 0) 33458c2ecf20Sopenharmony_ci goto done; 33468c2ecf20Sopenharmony_ci 33478c2ecf20Sopenharmony_ci if (test_bit(FLAG_802_11, &ai->flags)) { 33488c2ecf20Sopenharmony_ci bap_read(ai, &fc, sizeof (fc), BAP0); 33498c2ecf20Sopenharmony_ci hdrlen = header_len(fc); 33508c2ecf20Sopenharmony_ci } else 33518c2ecf20Sopenharmony_ci hdrlen = ETH_ALEN * 2; 33528c2ecf20Sopenharmony_ci 33538c2ecf20Sopenharmony_ci skb = dev_alloc_skb(len + hdrlen + 2 + 2); 33548c2ecf20Sopenharmony_ci if (!skb) { 33558c2ecf20Sopenharmony_ci ai->dev->stats.rx_dropped++; 33568c2ecf20Sopenharmony_ci goto done; 33578c2ecf20Sopenharmony_ci } 33588c2ecf20Sopenharmony_ci 33598c2ecf20Sopenharmony_ci skb_reserve(skb, 2); /* This way the IP header is aligned */ 33608c2ecf20Sopenharmony_ci buffer = skb_put(skb, len + hdrlen); 33618c2ecf20Sopenharmony_ci if (test_bit(FLAG_802_11, &ai->flags)) { 33628c2ecf20Sopenharmony_ci buffer[0] = fc; 33638c2ecf20Sopenharmony_ci bap_read(ai, buffer + 1, hdrlen - 2, BAP0); 33648c2ecf20Sopenharmony_ci if (hdrlen == 24) 33658c2ecf20Sopenharmony_ci bap_read(ai, tmpbuf, 6, BAP0); 33668c2ecf20Sopenharmony_ci 33678c2ecf20Sopenharmony_ci bap_read(ai, &v, sizeof(v), BAP0); 33688c2ecf20Sopenharmony_ci gap = le16_to_cpu(v); 33698c2ecf20Sopenharmony_ci if (gap) { 33708c2ecf20Sopenharmony_ci if (gap <= 8) { 33718c2ecf20Sopenharmony_ci bap_read(ai, tmpbuf, gap, BAP0); 33728c2ecf20Sopenharmony_ci } else { 33738c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "gaplen too " 33748c2ecf20Sopenharmony_ci "big. Problems will follow..."); 33758c2ecf20Sopenharmony_ci } 33768c2ecf20Sopenharmony_ci } 33778c2ecf20Sopenharmony_ci bap_read(ai, buffer + hdrlen/2, len, BAP0); 33788c2ecf20Sopenharmony_ci } else { 33798c2ecf20Sopenharmony_ci MICBuffer micbuf; 33808c2ecf20Sopenharmony_ci 33818c2ecf20Sopenharmony_ci bap_read(ai, buffer, ETH_ALEN * 2, BAP0); 33828c2ecf20Sopenharmony_ci if (ai->micstats.enabled) { 33838c2ecf20Sopenharmony_ci bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0); 33848c2ecf20Sopenharmony_ci if (ntohs(micbuf.typelen) > 0x05DC) 33858c2ecf20Sopenharmony_ci bap_setup(ai, fid, 0x44, BAP0); 33868c2ecf20Sopenharmony_ci else { 33878c2ecf20Sopenharmony_ci if (len <= sizeof (micbuf)) { 33888c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 33898c2ecf20Sopenharmony_ci goto done; 33908c2ecf20Sopenharmony_ci } 33918c2ecf20Sopenharmony_ci 33928c2ecf20Sopenharmony_ci len -= sizeof(micbuf); 33938c2ecf20Sopenharmony_ci skb_trim(skb, len + hdrlen); 33948c2ecf20Sopenharmony_ci } 33958c2ecf20Sopenharmony_ci } 33968c2ecf20Sopenharmony_ci 33978c2ecf20Sopenharmony_ci bap_read(ai, buffer + ETH_ALEN, len, BAP0); 33988c2ecf20Sopenharmony_ci if (decapsulate(ai, &micbuf, (etherHead*) buffer, len)) 33998c2ecf20Sopenharmony_ci dev_kfree_skb_irq (skb); 34008c2ecf20Sopenharmony_ci else 34018c2ecf20Sopenharmony_ci success = 1; 34028c2ecf20Sopenharmony_ci } 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci#ifdef WIRELESS_SPY 34058c2ecf20Sopenharmony_ci if (success && (ai->spy_data.spy_number > 0)) { 34068c2ecf20Sopenharmony_ci char *sa; 34078c2ecf20Sopenharmony_ci struct iw_quality wstats; 34088c2ecf20Sopenharmony_ci 34098c2ecf20Sopenharmony_ci /* Prepare spy data : addr + qual */ 34108c2ecf20Sopenharmony_ci if (!test_bit(FLAG_802_11, &ai->flags)) { 34118c2ecf20Sopenharmony_ci sa = (char *) buffer + 6; 34128c2ecf20Sopenharmony_ci bap_setup(ai, fid, 8, BAP0); 34138c2ecf20Sopenharmony_ci bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0); 34148c2ecf20Sopenharmony_ci } else 34158c2ecf20Sopenharmony_ci sa = (char *) buffer + 10; 34168c2ecf20Sopenharmony_ci wstats.qual = hdr.rssi[0]; 34178c2ecf20Sopenharmony_ci if (ai->rssi) 34188c2ecf20Sopenharmony_ci wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm; 34198c2ecf20Sopenharmony_ci else 34208c2ecf20Sopenharmony_ci wstats.level = (hdr.rssi[1] + 321) / 2; 34218c2ecf20Sopenharmony_ci wstats.noise = ai->wstats.qual.noise; 34228c2ecf20Sopenharmony_ci wstats.updated = IW_QUAL_LEVEL_UPDATED 34238c2ecf20Sopenharmony_ci | IW_QUAL_QUAL_UPDATED 34248c2ecf20Sopenharmony_ci | IW_QUAL_DBM; 34258c2ecf20Sopenharmony_ci /* Update spy records */ 34268c2ecf20Sopenharmony_ci wireless_spy_update(ai->dev, sa, &wstats); 34278c2ecf20Sopenharmony_ci } 34288c2ecf20Sopenharmony_ci#endif /* WIRELESS_SPY */ 34298c2ecf20Sopenharmony_ci 34308c2ecf20Sopenharmony_cidone: 34318c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_RX); 34328c2ecf20Sopenharmony_ci 34338c2ecf20Sopenharmony_ci if (success) { 34348c2ecf20Sopenharmony_ci if (test_bit(FLAG_802_11, &ai->flags)) { 34358c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 34368c2ecf20Sopenharmony_ci skb->pkt_type = PACKET_OTHERHOST; 34378c2ecf20Sopenharmony_ci skb->dev = ai->wifidev; 34388c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_802_2); 34398c2ecf20Sopenharmony_ci } else 34408c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ai->dev); 34418c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 34428c2ecf20Sopenharmony_ci 34438c2ecf20Sopenharmony_ci netif_rx(skb); 34448c2ecf20Sopenharmony_ci } 34458c2ecf20Sopenharmony_ci} 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_cistatic void airo_handle_tx(struct airo_info *ai, u16 status) 34488c2ecf20Sopenharmony_ci{ 34498c2ecf20Sopenharmony_ci int i, index = -1; 34508c2ecf20Sopenharmony_ci u16 fid; 34518c2ecf20Sopenharmony_ci 34528c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI, &ai->flags)) { 34538c2ecf20Sopenharmony_ci unsigned long flags; 34548c2ecf20Sopenharmony_ci 34558c2ecf20Sopenharmony_ci if (status & EV_TXEXC) 34568c2ecf20Sopenharmony_ci get_tx_error(ai, -1); 34578c2ecf20Sopenharmony_ci 34588c2ecf20Sopenharmony_ci spin_lock_irqsave(&ai->aux_lock, flags); 34598c2ecf20Sopenharmony_ci if (!skb_queue_empty(&ai->txq)) { 34608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ai->aux_lock, flags); 34618c2ecf20Sopenharmony_ci mpi_send_packet(ai->dev); 34628c2ecf20Sopenharmony_ci } else { 34638c2ecf20Sopenharmony_ci clear_bit(FLAG_PENDING_XMIT, &ai->flags); 34648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ai->aux_lock, flags); 34658c2ecf20Sopenharmony_ci netif_wake_queue(ai->dev); 34668c2ecf20Sopenharmony_ci } 34678c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC)); 34688c2ecf20Sopenharmony_ci return; 34698c2ecf20Sopenharmony_ci } 34708c2ecf20Sopenharmony_ci 34718c2ecf20Sopenharmony_ci fid = IN4500(ai, TXCOMPLFID); 34728c2ecf20Sopenharmony_ci 34738c2ecf20Sopenharmony_ci for (i = 0; i < MAX_FIDS; i++) { 34748c2ecf20Sopenharmony_ci if ((ai->fids[i] & 0xffff) == fid) 34758c2ecf20Sopenharmony_ci index = i; 34768c2ecf20Sopenharmony_ci } 34778c2ecf20Sopenharmony_ci 34788c2ecf20Sopenharmony_ci if (index != -1) { 34798c2ecf20Sopenharmony_ci if (status & EV_TXEXC) 34808c2ecf20Sopenharmony_ci get_tx_error(ai, index); 34818c2ecf20Sopenharmony_ci 34828c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC)); 34838c2ecf20Sopenharmony_ci 34848c2ecf20Sopenharmony_ci /* Set up to be used again */ 34858c2ecf20Sopenharmony_ci ai->fids[index] &= 0xffff; 34868c2ecf20Sopenharmony_ci if (index < MAX_FIDS / 2) { 34878c2ecf20Sopenharmony_ci if (!test_bit(FLAG_PENDING_XMIT, &ai->flags)) 34888c2ecf20Sopenharmony_ci netif_wake_queue(ai->dev); 34898c2ecf20Sopenharmony_ci } else { 34908c2ecf20Sopenharmony_ci if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags)) 34918c2ecf20Sopenharmony_ci netif_wake_queue(ai->wifidev); 34928c2ecf20Sopenharmony_ci } 34938c2ecf20Sopenharmony_ci } else { 34948c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC)); 34958c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "Unallocated FID was used to xmit"); 34968c2ecf20Sopenharmony_ci } 34978c2ecf20Sopenharmony_ci} 34988c2ecf20Sopenharmony_ci 34998c2ecf20Sopenharmony_cistatic irqreturn_t airo_interrupt(int irq, void *dev_id) 35008c2ecf20Sopenharmony_ci{ 35018c2ecf20Sopenharmony_ci struct net_device *dev = dev_id; 35028c2ecf20Sopenharmony_ci u16 status, savedInterrupts = 0; 35038c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 35048c2ecf20Sopenharmony_ci int handled = 0; 35058c2ecf20Sopenharmony_ci 35068c2ecf20Sopenharmony_ci if (!netif_device_present(dev)) 35078c2ecf20Sopenharmony_ci return IRQ_NONE; 35088c2ecf20Sopenharmony_ci 35098c2ecf20Sopenharmony_ci for (;;) { 35108c2ecf20Sopenharmony_ci status = IN4500(ai, EVSTAT); 35118c2ecf20Sopenharmony_ci if (!(status & STATUS_INTS) || (status == 0xffff)) 35128c2ecf20Sopenharmony_ci break; 35138c2ecf20Sopenharmony_ci 35148c2ecf20Sopenharmony_ci handled = 1; 35158c2ecf20Sopenharmony_ci 35168c2ecf20Sopenharmony_ci if (status & EV_AWAKE) { 35178c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_AWAKE); 35188c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_AWAKE); 35198c2ecf20Sopenharmony_ci } 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci if (!savedInterrupts) { 35228c2ecf20Sopenharmony_ci savedInterrupts = IN4500(ai, EVINTEN); 35238c2ecf20Sopenharmony_ci OUT4500(ai, EVINTEN, 0); 35248c2ecf20Sopenharmony_ci } 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_ci if (status & EV_MIC) { 35278c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_MIC); 35288c2ecf20Sopenharmony_ci airo_handle_cisco_mic(ai); 35298c2ecf20Sopenharmony_ci } 35308c2ecf20Sopenharmony_ci 35318c2ecf20Sopenharmony_ci if (status & EV_LINK) { 35328c2ecf20Sopenharmony_ci /* Link status changed */ 35338c2ecf20Sopenharmony_ci airo_handle_link(ai); 35348c2ecf20Sopenharmony_ci } 35358c2ecf20Sopenharmony_ci 35368c2ecf20Sopenharmony_ci /* Check to see if there is something to receive */ 35378c2ecf20Sopenharmony_ci if (status & EV_RX) 35388c2ecf20Sopenharmony_ci airo_handle_rx(ai); 35398c2ecf20Sopenharmony_ci 35408c2ecf20Sopenharmony_ci /* Check to see if a packet has been transmitted */ 35418c2ecf20Sopenharmony_ci if (status & (EV_TX | EV_TXCPY | EV_TXEXC)) 35428c2ecf20Sopenharmony_ci airo_handle_tx(ai, status); 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_ci if (status & ~STATUS_INTS & ~IGNORE_INTS) { 35458c2ecf20Sopenharmony_ci airo_print_warn(ai->dev->name, "Got weird status %x", 35468c2ecf20Sopenharmony_ci status & ~STATUS_INTS & ~IGNORE_INTS); 35478c2ecf20Sopenharmony_ci } 35488c2ecf20Sopenharmony_ci } 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci if (savedInterrupts) 35518c2ecf20Sopenharmony_ci OUT4500(ai, EVINTEN, savedInterrupts); 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 35548c2ecf20Sopenharmony_ci} 35558c2ecf20Sopenharmony_ci 35568c2ecf20Sopenharmony_ci/* 35578c2ecf20Sopenharmony_ci * Routines to talk to the card 35588c2ecf20Sopenharmony_ci */ 35598c2ecf20Sopenharmony_ci 35608c2ecf20Sopenharmony_ci/* 35618c2ecf20Sopenharmony_ci * This was originally written for the 4500, hence the name 35628c2ecf20Sopenharmony_ci * NOTE: If use with 8bit mode and SMP bad things will happen! 35638c2ecf20Sopenharmony_ci * Why would some one do 8 bit IO in an SMP machine?!? 35648c2ecf20Sopenharmony_ci */ 35658c2ecf20Sopenharmony_cistatic void OUT4500(struct airo_info *ai, u16 reg, u16 val) 35668c2ecf20Sopenharmony_ci{ 35678c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) 35688c2ecf20Sopenharmony_ci reg <<= 1; 35698c2ecf20Sopenharmony_ci if (!do8bitIO) 35708c2ecf20Sopenharmony_ci outw(val, ai->dev->base_addr + reg); 35718c2ecf20Sopenharmony_ci else { 35728c2ecf20Sopenharmony_ci outb(val & 0xff, ai->dev->base_addr + reg); 35738c2ecf20Sopenharmony_ci outb(val >> 8, ai->dev->base_addr + reg + 1); 35748c2ecf20Sopenharmony_ci } 35758c2ecf20Sopenharmony_ci} 35768c2ecf20Sopenharmony_ci 35778c2ecf20Sopenharmony_cistatic u16 IN4500(struct airo_info *ai, u16 reg) 35788c2ecf20Sopenharmony_ci{ 35798c2ecf20Sopenharmony_ci unsigned short rc; 35808c2ecf20Sopenharmony_ci 35818c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) 35828c2ecf20Sopenharmony_ci reg <<= 1; 35838c2ecf20Sopenharmony_ci if (!do8bitIO) 35848c2ecf20Sopenharmony_ci rc = inw(ai->dev->base_addr + reg); 35858c2ecf20Sopenharmony_ci else { 35868c2ecf20Sopenharmony_ci rc = inb(ai->dev->base_addr + reg); 35878c2ecf20Sopenharmony_ci rc += ((int)inb(ai->dev->base_addr + reg + 1)) << 8; 35888c2ecf20Sopenharmony_ci } 35898c2ecf20Sopenharmony_ci return rc; 35908c2ecf20Sopenharmony_ci} 35918c2ecf20Sopenharmony_ci 35928c2ecf20Sopenharmony_cistatic int enable_MAC(struct airo_info *ai, int lock) 35938c2ecf20Sopenharmony_ci{ 35948c2ecf20Sopenharmony_ci int rc; 35958c2ecf20Sopenharmony_ci Cmd cmd; 35968c2ecf20Sopenharmony_ci Resp rsp; 35978c2ecf20Sopenharmony_ci 35988c2ecf20Sopenharmony_ci /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions 35998c2ecf20Sopenharmony_ci * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down" 36008c2ecf20Sopenharmony_ci * Note : we could try to use !netif_running(dev) in enable_MAC() 36018c2ecf20Sopenharmony_ci * instead of this flag, but I don't trust it *within* the 36028c2ecf20Sopenharmony_ci * open/close functions, and testing both flags together is 36038c2ecf20Sopenharmony_ci * "cheaper" - Jean II */ 36048c2ecf20Sopenharmony_ci if (ai->flags & FLAG_RADIO_MASK) return SUCCESS; 36058c2ecf20Sopenharmony_ci 36068c2ecf20Sopenharmony_ci if (lock && down_interruptible(&ai->sem)) 36078c2ecf20Sopenharmony_ci return -ERESTARTSYS; 36088c2ecf20Sopenharmony_ci 36098c2ecf20Sopenharmony_ci if (!test_bit(FLAG_ENABLED, &ai->flags)) { 36108c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 36118c2ecf20Sopenharmony_ci cmd.cmd = MAC_ENABLE; 36128c2ecf20Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp); 36138c2ecf20Sopenharmony_ci if (rc == SUCCESS) 36148c2ecf20Sopenharmony_ci set_bit(FLAG_ENABLED, &ai->flags); 36158c2ecf20Sopenharmony_ci } else 36168c2ecf20Sopenharmony_ci rc = SUCCESS; 36178c2ecf20Sopenharmony_ci 36188c2ecf20Sopenharmony_ci if (lock) 36198c2ecf20Sopenharmony_ci up(&ai->sem); 36208c2ecf20Sopenharmony_ci 36218c2ecf20Sopenharmony_ci if (rc) 36228c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "Cannot enable MAC"); 36238c2ecf20Sopenharmony_ci else if ((rsp.status & 0xFF00) != 0) { 36248c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, " 36258c2ecf20Sopenharmony_ci "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2); 36268c2ecf20Sopenharmony_ci rc = ERROR; 36278c2ecf20Sopenharmony_ci } 36288c2ecf20Sopenharmony_ci return rc; 36298c2ecf20Sopenharmony_ci} 36308c2ecf20Sopenharmony_ci 36318c2ecf20Sopenharmony_cistatic void disable_MAC(struct airo_info *ai, int lock) 36328c2ecf20Sopenharmony_ci{ 36338c2ecf20Sopenharmony_ci Cmd cmd; 36348c2ecf20Sopenharmony_ci Resp rsp; 36358c2ecf20Sopenharmony_ci 36368c2ecf20Sopenharmony_ci if (lock == 1 && down_interruptible(&ai->sem)) 36378c2ecf20Sopenharmony_ci return; 36388c2ecf20Sopenharmony_ci 36398c2ecf20Sopenharmony_ci if (test_bit(FLAG_ENABLED, &ai->flags)) { 36408c2ecf20Sopenharmony_ci if (lock != 2) /* lock == 2 means don't disable carrier */ 36418c2ecf20Sopenharmony_ci netif_carrier_off(ai->dev); 36428c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 36438c2ecf20Sopenharmony_ci cmd.cmd = MAC_DISABLE; // disable in case already enabled 36448c2ecf20Sopenharmony_ci issuecommand(ai, &cmd, &rsp); 36458c2ecf20Sopenharmony_ci clear_bit(FLAG_ENABLED, &ai->flags); 36468c2ecf20Sopenharmony_ci } 36478c2ecf20Sopenharmony_ci if (lock == 1) 36488c2ecf20Sopenharmony_ci up(&ai->sem); 36498c2ecf20Sopenharmony_ci} 36508c2ecf20Sopenharmony_ci 36518c2ecf20Sopenharmony_cistatic void enable_interrupts(struct airo_info *ai) 36528c2ecf20Sopenharmony_ci{ 36538c2ecf20Sopenharmony_ci /* Enable the interrupts */ 36548c2ecf20Sopenharmony_ci OUT4500(ai, EVINTEN, STATUS_INTS); 36558c2ecf20Sopenharmony_ci} 36568c2ecf20Sopenharmony_ci 36578c2ecf20Sopenharmony_cistatic void disable_interrupts(struct airo_info *ai) 36588c2ecf20Sopenharmony_ci{ 36598c2ecf20Sopenharmony_ci OUT4500(ai, EVINTEN, 0); 36608c2ecf20Sopenharmony_ci} 36618c2ecf20Sopenharmony_ci 36628c2ecf20Sopenharmony_cistatic void mpi_receive_802_3(struct airo_info *ai) 36638c2ecf20Sopenharmony_ci{ 36648c2ecf20Sopenharmony_ci RxFid rxd; 36658c2ecf20Sopenharmony_ci int len = 0; 36668c2ecf20Sopenharmony_ci struct sk_buff *skb; 36678c2ecf20Sopenharmony_ci char *buffer; 36688c2ecf20Sopenharmony_ci int off = 0; 36698c2ecf20Sopenharmony_ci MICBuffer micbuf; 36708c2ecf20Sopenharmony_ci 36718c2ecf20Sopenharmony_ci memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd)); 36728c2ecf20Sopenharmony_ci /* Make sure we got something */ 36738c2ecf20Sopenharmony_ci if (rxd.rdy && rxd.valid == 0) { 36748c2ecf20Sopenharmony_ci len = rxd.len + 12; 36758c2ecf20Sopenharmony_ci if (len < 12 || len > 2048) 36768c2ecf20Sopenharmony_ci goto badrx; 36778c2ecf20Sopenharmony_ci 36788c2ecf20Sopenharmony_ci skb = dev_alloc_skb(len); 36798c2ecf20Sopenharmony_ci if (!skb) { 36808c2ecf20Sopenharmony_ci ai->dev->stats.rx_dropped++; 36818c2ecf20Sopenharmony_ci goto badrx; 36828c2ecf20Sopenharmony_ci } 36838c2ecf20Sopenharmony_ci buffer = skb_put(skb, len); 36848c2ecf20Sopenharmony_ci memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2); 36858c2ecf20Sopenharmony_ci if (ai->micstats.enabled) { 36868c2ecf20Sopenharmony_ci memcpy(&micbuf, 36878c2ecf20Sopenharmony_ci ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2, 36888c2ecf20Sopenharmony_ci sizeof(micbuf)); 36898c2ecf20Sopenharmony_ci if (ntohs(micbuf.typelen) <= 0x05DC) { 36908c2ecf20Sopenharmony_ci if (len <= sizeof(micbuf) + ETH_ALEN * 2) 36918c2ecf20Sopenharmony_ci goto badmic; 36928c2ecf20Sopenharmony_ci 36938c2ecf20Sopenharmony_ci off = sizeof(micbuf); 36948c2ecf20Sopenharmony_ci skb_trim (skb, len - off); 36958c2ecf20Sopenharmony_ci } 36968c2ecf20Sopenharmony_ci } 36978c2ecf20Sopenharmony_ci memcpy(buffer + ETH_ALEN * 2, 36988c2ecf20Sopenharmony_ci ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off, 36998c2ecf20Sopenharmony_ci len - ETH_ALEN * 2 - off); 37008c2ecf20Sopenharmony_ci if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) { 37018c2ecf20Sopenharmony_cibadmic: 37028c2ecf20Sopenharmony_ci dev_kfree_skb_irq (skb); 37038c2ecf20Sopenharmony_ci goto badrx; 37048c2ecf20Sopenharmony_ci } 37058c2ecf20Sopenharmony_ci#ifdef WIRELESS_SPY 37068c2ecf20Sopenharmony_ci if (ai->spy_data.spy_number > 0) { 37078c2ecf20Sopenharmony_ci char *sa; 37088c2ecf20Sopenharmony_ci struct iw_quality wstats; 37098c2ecf20Sopenharmony_ci /* Prepare spy data : addr + qual */ 37108c2ecf20Sopenharmony_ci sa = buffer + ETH_ALEN; 37118c2ecf20Sopenharmony_ci wstats.qual = 0; /* XXX Where do I get that info from ??? */ 37128c2ecf20Sopenharmony_ci wstats.level = 0; 37138c2ecf20Sopenharmony_ci wstats.updated = 0; 37148c2ecf20Sopenharmony_ci /* Update spy records */ 37158c2ecf20Sopenharmony_ci wireless_spy_update(ai->dev, sa, &wstats); 37168c2ecf20Sopenharmony_ci } 37178c2ecf20Sopenharmony_ci#endif /* WIRELESS_SPY */ 37188c2ecf20Sopenharmony_ci 37198c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 37208c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ai->dev); 37218c2ecf20Sopenharmony_ci netif_rx(skb); 37228c2ecf20Sopenharmony_ci } 37238c2ecf20Sopenharmony_cibadrx: 37248c2ecf20Sopenharmony_ci if (rxd.valid == 0) { 37258c2ecf20Sopenharmony_ci rxd.valid = 1; 37268c2ecf20Sopenharmony_ci rxd.rdy = 0; 37278c2ecf20Sopenharmony_ci rxd.len = PKTSIZE; 37288c2ecf20Sopenharmony_ci memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd)); 37298c2ecf20Sopenharmony_ci } 37308c2ecf20Sopenharmony_ci} 37318c2ecf20Sopenharmony_ci 37328c2ecf20Sopenharmony_cistatic void mpi_receive_802_11(struct airo_info *ai) 37338c2ecf20Sopenharmony_ci{ 37348c2ecf20Sopenharmony_ci RxFid rxd; 37358c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 37368c2ecf20Sopenharmony_ci u16 len, hdrlen = 0; 37378c2ecf20Sopenharmony_ci __le16 fc; 37388c2ecf20Sopenharmony_ci struct rx_hdr hdr; 37398c2ecf20Sopenharmony_ci u16 gap; 37408c2ecf20Sopenharmony_ci u16 *buffer; 37418c2ecf20Sopenharmony_ci char *ptr = ai->rxfids[0].virtual_host_addr + 4; 37428c2ecf20Sopenharmony_ci 37438c2ecf20Sopenharmony_ci memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd)); 37448c2ecf20Sopenharmony_ci memcpy ((char *)&hdr, ptr, sizeof(hdr)); 37458c2ecf20Sopenharmony_ci ptr += sizeof(hdr); 37468c2ecf20Sopenharmony_ci /* Bad CRC. Ignore packet */ 37478c2ecf20Sopenharmony_ci if (le16_to_cpu(hdr.status) & 2) 37488c2ecf20Sopenharmony_ci hdr.len = 0; 37498c2ecf20Sopenharmony_ci if (ai->wifidev == NULL) 37508c2ecf20Sopenharmony_ci hdr.len = 0; 37518c2ecf20Sopenharmony_ci len = le16_to_cpu(hdr.len); 37528c2ecf20Sopenharmony_ci if (len > AIRO_DEF_MTU) { 37538c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "Bad size %d", len); 37548c2ecf20Sopenharmony_ci goto badrx; 37558c2ecf20Sopenharmony_ci } 37568c2ecf20Sopenharmony_ci if (len == 0) 37578c2ecf20Sopenharmony_ci goto badrx; 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci fc = get_unaligned((__le16 *)ptr); 37608c2ecf20Sopenharmony_ci hdrlen = header_len(fc); 37618c2ecf20Sopenharmony_ci 37628c2ecf20Sopenharmony_ci skb = dev_alloc_skb(len + hdrlen + 2); 37638c2ecf20Sopenharmony_ci if (!skb) { 37648c2ecf20Sopenharmony_ci ai->dev->stats.rx_dropped++; 37658c2ecf20Sopenharmony_ci goto badrx; 37668c2ecf20Sopenharmony_ci } 37678c2ecf20Sopenharmony_ci buffer = skb_put(skb, len + hdrlen); 37688c2ecf20Sopenharmony_ci memcpy ((char *)buffer, ptr, hdrlen); 37698c2ecf20Sopenharmony_ci ptr += hdrlen; 37708c2ecf20Sopenharmony_ci if (hdrlen == 24) 37718c2ecf20Sopenharmony_ci ptr += 6; 37728c2ecf20Sopenharmony_ci gap = get_unaligned_le16(ptr); 37738c2ecf20Sopenharmony_ci ptr += sizeof(__le16); 37748c2ecf20Sopenharmony_ci if (gap) { 37758c2ecf20Sopenharmony_ci if (gap <= 8) 37768c2ecf20Sopenharmony_ci ptr += gap; 37778c2ecf20Sopenharmony_ci else 37788c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, 37798c2ecf20Sopenharmony_ci "gaplen too big. Problems will follow..."); 37808c2ecf20Sopenharmony_ci } 37818c2ecf20Sopenharmony_ci memcpy ((char *)buffer + hdrlen, ptr, len); 37828c2ecf20Sopenharmony_ci ptr += len; 37838c2ecf20Sopenharmony_ci#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ 37848c2ecf20Sopenharmony_ci if (ai->spy_data.spy_number > 0) { 37858c2ecf20Sopenharmony_ci char *sa; 37868c2ecf20Sopenharmony_ci struct iw_quality wstats; 37878c2ecf20Sopenharmony_ci /* Prepare spy data : addr + qual */ 37888c2ecf20Sopenharmony_ci sa = (char*)buffer + 10; 37898c2ecf20Sopenharmony_ci wstats.qual = hdr.rssi[0]; 37908c2ecf20Sopenharmony_ci if (ai->rssi) 37918c2ecf20Sopenharmony_ci wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm; 37928c2ecf20Sopenharmony_ci else 37938c2ecf20Sopenharmony_ci wstats.level = (hdr.rssi[1] + 321) / 2; 37948c2ecf20Sopenharmony_ci wstats.noise = ai->wstats.qual.noise; 37958c2ecf20Sopenharmony_ci wstats.updated = IW_QUAL_QUAL_UPDATED 37968c2ecf20Sopenharmony_ci | IW_QUAL_LEVEL_UPDATED 37978c2ecf20Sopenharmony_ci | IW_QUAL_DBM; 37988c2ecf20Sopenharmony_ci /* Update spy records */ 37998c2ecf20Sopenharmony_ci wireless_spy_update(ai->dev, sa, &wstats); 38008c2ecf20Sopenharmony_ci } 38018c2ecf20Sopenharmony_ci#endif /* IW_WIRELESS_SPY */ 38028c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 38038c2ecf20Sopenharmony_ci skb->pkt_type = PACKET_OTHERHOST; 38048c2ecf20Sopenharmony_ci skb->dev = ai->wifidev; 38058c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_802_2); 38068c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 38078c2ecf20Sopenharmony_ci netif_rx(skb); 38088c2ecf20Sopenharmony_ci 38098c2ecf20Sopenharmony_cibadrx: 38108c2ecf20Sopenharmony_ci if (rxd.valid == 0) { 38118c2ecf20Sopenharmony_ci rxd.valid = 1; 38128c2ecf20Sopenharmony_ci rxd.rdy = 0; 38138c2ecf20Sopenharmony_ci rxd.len = PKTSIZE; 38148c2ecf20Sopenharmony_ci memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd)); 38158c2ecf20Sopenharmony_ci } 38168c2ecf20Sopenharmony_ci} 38178c2ecf20Sopenharmony_ci 38188c2ecf20Sopenharmony_cistatic inline void set_auth_type(struct airo_info *local, int auth_type) 38198c2ecf20Sopenharmony_ci{ 38208c2ecf20Sopenharmony_ci local->config.authType = auth_type; 38218c2ecf20Sopenharmony_ci /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT). 38228c2ecf20Sopenharmony_ci * Used by airo_set_auth() 38238c2ecf20Sopenharmony_ci */ 38248c2ecf20Sopenharmony_ci if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT) 38258c2ecf20Sopenharmony_ci local->last_auth = auth_type; 38268c2ecf20Sopenharmony_ci} 38278c2ecf20Sopenharmony_ci 38288c2ecf20Sopenharmony_cistatic int noinline_for_stack airo_readconfig(struct airo_info *ai, u8 *mac, int lock) 38298c2ecf20Sopenharmony_ci{ 38308c2ecf20Sopenharmony_ci int i, status; 38318c2ecf20Sopenharmony_ci /* large variables, so don't inline this function, 38328c2ecf20Sopenharmony_ci * maybe change to kmalloc 38338c2ecf20Sopenharmony_ci */ 38348c2ecf20Sopenharmony_ci tdsRssiRid rssi_rid; 38358c2ecf20Sopenharmony_ci CapabilityRid cap_rid; 38368c2ecf20Sopenharmony_ci 38378c2ecf20Sopenharmony_ci kfree(ai->SSID); 38388c2ecf20Sopenharmony_ci ai->SSID = NULL; 38398c2ecf20Sopenharmony_ci // general configuration (read/modify/write) 38408c2ecf20Sopenharmony_ci status = readConfigRid(ai, lock); 38418c2ecf20Sopenharmony_ci if (status != SUCCESS) return ERROR; 38428c2ecf20Sopenharmony_ci 38438c2ecf20Sopenharmony_ci status = readCapabilityRid(ai, &cap_rid, lock); 38448c2ecf20Sopenharmony_ci if (status != SUCCESS) return ERROR; 38458c2ecf20Sopenharmony_ci 38468c2ecf20Sopenharmony_ci status = PC4500_readrid(ai, RID_RSSI, &rssi_rid, sizeof(rssi_rid), lock); 38478c2ecf20Sopenharmony_ci if (status == SUCCESS) { 38488c2ecf20Sopenharmony_ci if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL) 38498c2ecf20Sopenharmony_ci memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */ 38508c2ecf20Sopenharmony_ci } 38518c2ecf20Sopenharmony_ci else { 38528c2ecf20Sopenharmony_ci kfree(ai->rssi); 38538c2ecf20Sopenharmony_ci ai->rssi = NULL; 38548c2ecf20Sopenharmony_ci if (cap_rid.softCap & cpu_to_le16(8)) 38558c2ecf20Sopenharmony_ci ai->config.rmode |= RXMODE_NORMALIZED_RSSI; 38568c2ecf20Sopenharmony_ci else 38578c2ecf20Sopenharmony_ci airo_print_warn(ai->dev->name, "unknown received signal " 38588c2ecf20Sopenharmony_ci "level scale"); 38598c2ecf20Sopenharmony_ci } 38608c2ecf20Sopenharmony_ci ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS; 38618c2ecf20Sopenharmony_ci set_auth_type(ai, AUTH_OPEN); 38628c2ecf20Sopenharmony_ci ai->config.modulation = MOD_CCK; 38638c2ecf20Sopenharmony_ci 38648c2ecf20Sopenharmony_ci if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) && 38658c2ecf20Sopenharmony_ci (cap_rid.extSoftCap & cpu_to_le16(1)) && 38668c2ecf20Sopenharmony_ci micsetup(ai) == SUCCESS) { 38678c2ecf20Sopenharmony_ci ai->config.opmode |= MODE_MIC; 38688c2ecf20Sopenharmony_ci set_bit(FLAG_MIC_CAPABLE, &ai->flags); 38698c2ecf20Sopenharmony_ci } 38708c2ecf20Sopenharmony_ci 38718c2ecf20Sopenharmony_ci /* Save off the MAC */ 38728c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) { 38738c2ecf20Sopenharmony_ci mac[i] = ai->config.macAddr[i]; 38748c2ecf20Sopenharmony_ci } 38758c2ecf20Sopenharmony_ci 38768c2ecf20Sopenharmony_ci /* Check to see if there are any insmod configured 38778c2ecf20Sopenharmony_ci rates to add */ 38788c2ecf20Sopenharmony_ci if (rates[0]) { 38798c2ecf20Sopenharmony_ci memset(ai->config.rates, 0, sizeof(ai->config.rates)); 38808c2ecf20Sopenharmony_ci for (i = 0; i < 8 && rates[i]; i++) { 38818c2ecf20Sopenharmony_ci ai->config.rates[i] = rates[i]; 38828c2ecf20Sopenharmony_ci } 38838c2ecf20Sopenharmony_ci } 38848c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_ci return SUCCESS; 38878c2ecf20Sopenharmony_ci} 38888c2ecf20Sopenharmony_ci 38898c2ecf20Sopenharmony_ci 38908c2ecf20Sopenharmony_cistatic u16 setup_card(struct airo_info *ai, u8 *mac, int lock) 38918c2ecf20Sopenharmony_ci{ 38928c2ecf20Sopenharmony_ci Cmd cmd; 38938c2ecf20Sopenharmony_ci Resp rsp; 38948c2ecf20Sopenharmony_ci int status; 38958c2ecf20Sopenharmony_ci SsidRid mySsid; 38968c2ecf20Sopenharmony_ci __le16 lastindex; 38978c2ecf20Sopenharmony_ci WepKeyRid wkr; 38988c2ecf20Sopenharmony_ci int rc; 38998c2ecf20Sopenharmony_ci 39008c2ecf20Sopenharmony_ci memset(&mySsid, 0, sizeof(mySsid)); 39018c2ecf20Sopenharmony_ci kfree (ai->flash); 39028c2ecf20Sopenharmony_ci ai->flash = NULL; 39038c2ecf20Sopenharmony_ci 39048c2ecf20Sopenharmony_ci /* The NOP is the first step in getting the card going */ 39058c2ecf20Sopenharmony_ci cmd.cmd = NOP; 39068c2ecf20Sopenharmony_ci cmd.parm0 = cmd.parm1 = cmd.parm2 = 0; 39078c2ecf20Sopenharmony_ci if (lock && down_interruptible(&ai->sem)) 39088c2ecf20Sopenharmony_ci return ERROR; 39098c2ecf20Sopenharmony_ci if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { 39108c2ecf20Sopenharmony_ci if (lock) 39118c2ecf20Sopenharmony_ci up(&ai->sem); 39128c2ecf20Sopenharmony_ci return ERROR; 39138c2ecf20Sopenharmony_ci } 39148c2ecf20Sopenharmony_ci disable_MAC(ai, 0); 39158c2ecf20Sopenharmony_ci 39168c2ecf20Sopenharmony_ci // Let's figure out if we need to use the AUX port 39178c2ecf20Sopenharmony_ci if (!test_bit(FLAG_MPI,&ai->flags)) { 39188c2ecf20Sopenharmony_ci cmd.cmd = CMD_ENABLEAUX; 39198c2ecf20Sopenharmony_ci if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { 39208c2ecf20Sopenharmony_ci if (lock) 39218c2ecf20Sopenharmony_ci up(&ai->sem); 39228c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "Error checking for AUX port"); 39238c2ecf20Sopenharmony_ci return ERROR; 39248c2ecf20Sopenharmony_ci } 39258c2ecf20Sopenharmony_ci if (!aux_bap || rsp.status & 0xff00) { 39268c2ecf20Sopenharmony_ci ai->bap_read = fast_bap_read; 39278c2ecf20Sopenharmony_ci airo_print_dbg(ai->dev->name, "Doing fast bap_reads"); 39288c2ecf20Sopenharmony_ci } else { 39298c2ecf20Sopenharmony_ci ai->bap_read = aux_bap_read; 39308c2ecf20Sopenharmony_ci airo_print_dbg(ai->dev->name, "Doing AUX bap_reads"); 39318c2ecf20Sopenharmony_ci } 39328c2ecf20Sopenharmony_ci } 39338c2ecf20Sopenharmony_ci if (lock) 39348c2ecf20Sopenharmony_ci up(&ai->sem); 39358c2ecf20Sopenharmony_ci if (ai->config.len == 0) { 39368c2ecf20Sopenharmony_ci status = airo_readconfig(ai, mac, lock); 39378c2ecf20Sopenharmony_ci if (status != SUCCESS) 39388c2ecf20Sopenharmony_ci return ERROR; 39398c2ecf20Sopenharmony_ci } 39408c2ecf20Sopenharmony_ci 39418c2ecf20Sopenharmony_ci /* Setup the SSIDs if present */ 39428c2ecf20Sopenharmony_ci if (ssids[0]) { 39438c2ecf20Sopenharmony_ci int i; 39448c2ecf20Sopenharmony_ci for (i = 0; i < 3 && ssids[i]; i++) { 39458c2ecf20Sopenharmony_ci size_t len = strlen(ssids[i]); 39468c2ecf20Sopenharmony_ci if (len > 32) 39478c2ecf20Sopenharmony_ci len = 32; 39488c2ecf20Sopenharmony_ci mySsid.ssids[i].len = cpu_to_le16(len); 39498c2ecf20Sopenharmony_ci memcpy(mySsid.ssids[i].ssid, ssids[i], len); 39508c2ecf20Sopenharmony_ci } 39518c2ecf20Sopenharmony_ci mySsid.len = cpu_to_le16(sizeof(mySsid)); 39528c2ecf20Sopenharmony_ci } 39538c2ecf20Sopenharmony_ci 39548c2ecf20Sopenharmony_ci status = writeConfigRid(ai, lock); 39558c2ecf20Sopenharmony_ci if (status != SUCCESS) return ERROR; 39568c2ecf20Sopenharmony_ci 39578c2ecf20Sopenharmony_ci /* Set up the SSID list */ 39588c2ecf20Sopenharmony_ci if (ssids[0]) { 39598c2ecf20Sopenharmony_ci status = writeSsidRid(ai, &mySsid, lock); 39608c2ecf20Sopenharmony_ci if (status != SUCCESS) return ERROR; 39618c2ecf20Sopenharmony_ci } 39628c2ecf20Sopenharmony_ci 39638c2ecf20Sopenharmony_ci status = enable_MAC(ai, lock); 39648c2ecf20Sopenharmony_ci if (status != SUCCESS) 39658c2ecf20Sopenharmony_ci return ERROR; 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci /* Grab the initial wep key, we gotta save it for auto_wep */ 39688c2ecf20Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 1, lock); 39698c2ecf20Sopenharmony_ci if (rc == SUCCESS) do { 39708c2ecf20Sopenharmony_ci lastindex = wkr.kindex; 39718c2ecf20Sopenharmony_ci if (wkr.kindex == cpu_to_le16(0xffff)) { 39728c2ecf20Sopenharmony_ci ai->defindex = wkr.mac[0]; 39738c2ecf20Sopenharmony_ci } 39748c2ecf20Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 0, lock); 39758c2ecf20Sopenharmony_ci } while (lastindex != wkr.kindex); 39768c2ecf20Sopenharmony_ci 39778c2ecf20Sopenharmony_ci try_auto_wep(ai); 39788c2ecf20Sopenharmony_ci 39798c2ecf20Sopenharmony_ci return SUCCESS; 39808c2ecf20Sopenharmony_ci} 39818c2ecf20Sopenharmony_ci 39828c2ecf20Sopenharmony_cistatic u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) 39838c2ecf20Sopenharmony_ci{ 39848c2ecf20Sopenharmony_ci // Im really paranoid about letting it run forever! 39858c2ecf20Sopenharmony_ci int max_tries = 600000; 39868c2ecf20Sopenharmony_ci 39878c2ecf20Sopenharmony_ci if (IN4500(ai, EVSTAT) & EV_CMD) 39888c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_CMD); 39898c2ecf20Sopenharmony_ci 39908c2ecf20Sopenharmony_ci OUT4500(ai, PARAM0, pCmd->parm0); 39918c2ecf20Sopenharmony_ci OUT4500(ai, PARAM1, pCmd->parm1); 39928c2ecf20Sopenharmony_ci OUT4500(ai, PARAM2, pCmd->parm2); 39938c2ecf20Sopenharmony_ci OUT4500(ai, COMMAND, pCmd->cmd); 39948c2ecf20Sopenharmony_ci 39958c2ecf20Sopenharmony_ci while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) { 39968c2ecf20Sopenharmony_ci if ((IN4500(ai, COMMAND)) == pCmd->cmd) 39978c2ecf20Sopenharmony_ci // PC4500 didn't notice command, try again 39988c2ecf20Sopenharmony_ci OUT4500(ai, COMMAND, pCmd->cmd); 39998c2ecf20Sopenharmony_ci if (!in_atomic() && (max_tries & 255) == 0) 40008c2ecf20Sopenharmony_ci schedule(); 40018c2ecf20Sopenharmony_ci } 40028c2ecf20Sopenharmony_ci 40038c2ecf20Sopenharmony_ci if (max_tries == -1) { 40048c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, 40058c2ecf20Sopenharmony_ci "Max tries exceeded when issuing command"); 40068c2ecf20Sopenharmony_ci if (IN4500(ai, COMMAND) & COMMAND_BUSY) 40078c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); 40088c2ecf20Sopenharmony_ci return ERROR; 40098c2ecf20Sopenharmony_ci } 40108c2ecf20Sopenharmony_ci 40118c2ecf20Sopenharmony_ci // command completed 40128c2ecf20Sopenharmony_ci pRsp->status = IN4500(ai, STATUS); 40138c2ecf20Sopenharmony_ci pRsp->rsp0 = IN4500(ai, RESP0); 40148c2ecf20Sopenharmony_ci pRsp->rsp1 = IN4500(ai, RESP1); 40158c2ecf20Sopenharmony_ci pRsp->rsp2 = IN4500(ai, RESP2); 40168c2ecf20Sopenharmony_ci if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET) 40178c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, 40188c2ecf20Sopenharmony_ci "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x", 40198c2ecf20Sopenharmony_ci pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1, 40208c2ecf20Sopenharmony_ci pRsp->rsp2); 40218c2ecf20Sopenharmony_ci 40228c2ecf20Sopenharmony_ci // clear stuck command busy if necessary 40238c2ecf20Sopenharmony_ci if (IN4500(ai, COMMAND) & COMMAND_BUSY) { 40248c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); 40258c2ecf20Sopenharmony_ci } 40268c2ecf20Sopenharmony_ci // acknowledge processing the status/response 40278c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_CMD); 40288c2ecf20Sopenharmony_ci 40298c2ecf20Sopenharmony_ci return SUCCESS; 40308c2ecf20Sopenharmony_ci} 40318c2ecf20Sopenharmony_ci 40328c2ecf20Sopenharmony_ci/* Sets up the bap to start exchange data. whichbap should 40338c2ecf20Sopenharmony_ci * be one of the BAP0 or BAP1 defines. Locks should be held before 40348c2ecf20Sopenharmony_ci * calling! */ 40358c2ecf20Sopenharmony_cistatic int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap) 40368c2ecf20Sopenharmony_ci{ 40378c2ecf20Sopenharmony_ci int timeout = 50; 40388c2ecf20Sopenharmony_ci int max_tries = 3; 40398c2ecf20Sopenharmony_ci 40408c2ecf20Sopenharmony_ci OUT4500(ai, SELECT0+whichbap, rid); 40418c2ecf20Sopenharmony_ci OUT4500(ai, OFFSET0+whichbap, offset); 40428c2ecf20Sopenharmony_ci while (1) { 40438c2ecf20Sopenharmony_ci int status = IN4500(ai, OFFSET0+whichbap); 40448c2ecf20Sopenharmony_ci if (status & BAP_BUSY) { 40458c2ecf20Sopenharmony_ci /* This isn't really a timeout, but its kinda 40468c2ecf20Sopenharmony_ci close */ 40478c2ecf20Sopenharmony_ci if (timeout--) { 40488c2ecf20Sopenharmony_ci continue; 40498c2ecf20Sopenharmony_ci } 40508c2ecf20Sopenharmony_ci } else if (status & BAP_ERR) { 40518c2ecf20Sopenharmony_ci /* invalid rid or offset */ 40528c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "BAP error %x %d", 40538c2ecf20Sopenharmony_ci status, whichbap); 40548c2ecf20Sopenharmony_ci return ERROR; 40558c2ecf20Sopenharmony_ci } else if (status & BAP_DONE) { // success 40568c2ecf20Sopenharmony_ci return SUCCESS; 40578c2ecf20Sopenharmony_ci } 40588c2ecf20Sopenharmony_ci if (!(max_tries--)) { 40598c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, 40608c2ecf20Sopenharmony_ci "BAP setup error too many retries\n"); 40618c2ecf20Sopenharmony_ci return ERROR; 40628c2ecf20Sopenharmony_ci } 40638c2ecf20Sopenharmony_ci // -- PC4500 missed it, try again 40648c2ecf20Sopenharmony_ci OUT4500(ai, SELECT0+whichbap, rid); 40658c2ecf20Sopenharmony_ci OUT4500(ai, OFFSET0+whichbap, offset); 40668c2ecf20Sopenharmony_ci timeout = 50; 40678c2ecf20Sopenharmony_ci } 40688c2ecf20Sopenharmony_ci} 40698c2ecf20Sopenharmony_ci 40708c2ecf20Sopenharmony_ci/* should only be called by aux_bap_read. This aux function and the 40718c2ecf20Sopenharmony_ci following use concepts not documented in the developers guide. I 40728c2ecf20Sopenharmony_ci got them from a patch given to my by Aironet */ 40738c2ecf20Sopenharmony_cistatic u16 aux_setup(struct airo_info *ai, u16 page, 40748c2ecf20Sopenharmony_ci u16 offset, u16 *len) 40758c2ecf20Sopenharmony_ci{ 40768c2ecf20Sopenharmony_ci u16 next; 40778c2ecf20Sopenharmony_ci 40788c2ecf20Sopenharmony_ci OUT4500(ai, AUXPAGE, page); 40798c2ecf20Sopenharmony_ci OUT4500(ai, AUXOFF, 0); 40808c2ecf20Sopenharmony_ci next = IN4500(ai, AUXDATA); 40818c2ecf20Sopenharmony_ci *len = IN4500(ai, AUXDATA)&0xff; 40828c2ecf20Sopenharmony_ci if (offset != 4) OUT4500(ai, AUXOFF, offset); 40838c2ecf20Sopenharmony_ci return next; 40848c2ecf20Sopenharmony_ci} 40858c2ecf20Sopenharmony_ci 40868c2ecf20Sopenharmony_ci/* requires call to bap_setup() first */ 40878c2ecf20Sopenharmony_cistatic int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst, 40888c2ecf20Sopenharmony_ci int bytelen, int whichbap) 40898c2ecf20Sopenharmony_ci{ 40908c2ecf20Sopenharmony_ci u16 len; 40918c2ecf20Sopenharmony_ci u16 page; 40928c2ecf20Sopenharmony_ci u16 offset; 40938c2ecf20Sopenharmony_ci u16 next; 40948c2ecf20Sopenharmony_ci int words; 40958c2ecf20Sopenharmony_ci int i; 40968c2ecf20Sopenharmony_ci unsigned long flags; 40978c2ecf20Sopenharmony_ci 40988c2ecf20Sopenharmony_ci spin_lock_irqsave(&ai->aux_lock, flags); 40998c2ecf20Sopenharmony_ci page = IN4500(ai, SWS0+whichbap); 41008c2ecf20Sopenharmony_ci offset = IN4500(ai, SWS2+whichbap); 41018c2ecf20Sopenharmony_ci next = aux_setup(ai, page, offset, &len); 41028c2ecf20Sopenharmony_ci words = (bytelen+1)>>1; 41038c2ecf20Sopenharmony_ci 41048c2ecf20Sopenharmony_ci for (i = 0; i<words;) { 41058c2ecf20Sopenharmony_ci int count; 41068c2ecf20Sopenharmony_ci count = (len>>1) < (words-i) ? (len>>1) : (words-i); 41078c2ecf20Sopenharmony_ci if (!do8bitIO) 41088c2ecf20Sopenharmony_ci insw(ai->dev->base_addr+DATA0+whichbap, 41098c2ecf20Sopenharmony_ci pu16Dst+i, count); 41108c2ecf20Sopenharmony_ci else 41118c2ecf20Sopenharmony_ci insb(ai->dev->base_addr+DATA0+whichbap, 41128c2ecf20Sopenharmony_ci pu16Dst+i, count << 1); 41138c2ecf20Sopenharmony_ci i += count; 41148c2ecf20Sopenharmony_ci if (i<words) { 41158c2ecf20Sopenharmony_ci next = aux_setup(ai, next, 4, &len); 41168c2ecf20Sopenharmony_ci } 41178c2ecf20Sopenharmony_ci } 41188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ai->aux_lock, flags); 41198c2ecf20Sopenharmony_ci return SUCCESS; 41208c2ecf20Sopenharmony_ci} 41218c2ecf20Sopenharmony_ci 41228c2ecf20Sopenharmony_ci 41238c2ecf20Sopenharmony_ci/* requires call to bap_setup() first */ 41248c2ecf20Sopenharmony_cistatic int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst, 41258c2ecf20Sopenharmony_ci int bytelen, int whichbap) 41268c2ecf20Sopenharmony_ci{ 41278c2ecf20Sopenharmony_ci bytelen = (bytelen + 1) & (~1); // round up to even value 41288c2ecf20Sopenharmony_ci if (!do8bitIO) 41298c2ecf20Sopenharmony_ci insw(ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1); 41308c2ecf20Sopenharmony_ci else 41318c2ecf20Sopenharmony_ci insb(ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen); 41328c2ecf20Sopenharmony_ci return SUCCESS; 41338c2ecf20Sopenharmony_ci} 41348c2ecf20Sopenharmony_ci 41358c2ecf20Sopenharmony_ci/* requires call to bap_setup() first */ 41368c2ecf20Sopenharmony_cistatic int bap_write(struct airo_info *ai, const __le16 *pu16Src, 41378c2ecf20Sopenharmony_ci int bytelen, int whichbap) 41388c2ecf20Sopenharmony_ci{ 41398c2ecf20Sopenharmony_ci bytelen = (bytelen + 1) & (~1); // round up to even value 41408c2ecf20Sopenharmony_ci if (!do8bitIO) 41418c2ecf20Sopenharmony_ci outsw(ai->dev->base_addr+DATA0+whichbap, 41428c2ecf20Sopenharmony_ci pu16Src, bytelen>>1); 41438c2ecf20Sopenharmony_ci else 41448c2ecf20Sopenharmony_ci outsb(ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen); 41458c2ecf20Sopenharmony_ci return SUCCESS; 41468c2ecf20Sopenharmony_ci} 41478c2ecf20Sopenharmony_ci 41488c2ecf20Sopenharmony_cistatic int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd) 41498c2ecf20Sopenharmony_ci{ 41508c2ecf20Sopenharmony_ci Cmd cmd; /* for issuing commands */ 41518c2ecf20Sopenharmony_ci Resp rsp; /* response from commands */ 41528c2ecf20Sopenharmony_ci u16 status; 41538c2ecf20Sopenharmony_ci 41548c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 41558c2ecf20Sopenharmony_ci cmd.cmd = accmd; 41568c2ecf20Sopenharmony_ci cmd.parm0 = rid; 41578c2ecf20Sopenharmony_ci status = issuecommand(ai, &cmd, &rsp); 41588c2ecf20Sopenharmony_ci if (status != 0) return status; 41598c2ecf20Sopenharmony_ci if ((rsp.status & 0x7F00) != 0) { 41608c2ecf20Sopenharmony_ci return (accmd << 8) + (rsp.rsp0 & 0xFF); 41618c2ecf20Sopenharmony_ci } 41628c2ecf20Sopenharmony_ci return 0; 41638c2ecf20Sopenharmony_ci} 41648c2ecf20Sopenharmony_ci 41658c2ecf20Sopenharmony_ci/* Note, that we are using BAP1 which is also used by transmit, so 41668c2ecf20Sopenharmony_ci * we must get a lock. */ 41678c2ecf20Sopenharmony_cistatic int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock) 41688c2ecf20Sopenharmony_ci{ 41698c2ecf20Sopenharmony_ci u16 status; 41708c2ecf20Sopenharmony_ci int rc = SUCCESS; 41718c2ecf20Sopenharmony_ci 41728c2ecf20Sopenharmony_ci if (lock) { 41738c2ecf20Sopenharmony_ci if (down_interruptible(&ai->sem)) 41748c2ecf20Sopenharmony_ci return ERROR; 41758c2ecf20Sopenharmony_ci } 41768c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) { 41778c2ecf20Sopenharmony_ci Cmd cmd; 41788c2ecf20Sopenharmony_ci Resp rsp; 41798c2ecf20Sopenharmony_ci 41808c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 41818c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 41828c2ecf20Sopenharmony_ci ai->config_desc.rid_desc.valid = 1; 41838c2ecf20Sopenharmony_ci ai->config_desc.rid_desc.len = RIDSIZE; 41848c2ecf20Sopenharmony_ci ai->config_desc.rid_desc.rid = 0; 41858c2ecf20Sopenharmony_ci ai->config_desc.rid_desc.host_addr = ai->ridbus; 41868c2ecf20Sopenharmony_ci 41878c2ecf20Sopenharmony_ci cmd.cmd = CMD_ACCESS; 41888c2ecf20Sopenharmony_ci cmd.parm0 = rid; 41898c2ecf20Sopenharmony_ci 41908c2ecf20Sopenharmony_ci memcpy_toio(ai->config_desc.card_ram_off, 41918c2ecf20Sopenharmony_ci &ai->config_desc.rid_desc, sizeof(Rid)); 41928c2ecf20Sopenharmony_ci 41938c2ecf20Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp); 41948c2ecf20Sopenharmony_ci 41958c2ecf20Sopenharmony_ci if (rsp.status & 0x7f00) 41968c2ecf20Sopenharmony_ci rc = rsp.rsp0; 41978c2ecf20Sopenharmony_ci if (!rc) 41988c2ecf20Sopenharmony_ci memcpy(pBuf, ai->config_desc.virtual_host_addr, len); 41998c2ecf20Sopenharmony_ci goto done; 42008c2ecf20Sopenharmony_ci } else { 42018c2ecf20Sopenharmony_ci if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) { 42028c2ecf20Sopenharmony_ci rc = status; 42038c2ecf20Sopenharmony_ci goto done; 42048c2ecf20Sopenharmony_ci } 42058c2ecf20Sopenharmony_ci if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) { 42068c2ecf20Sopenharmony_ci rc = ERROR; 42078c2ecf20Sopenharmony_ci goto done; 42088c2ecf20Sopenharmony_ci } 42098c2ecf20Sopenharmony_ci // read the rid length field 42108c2ecf20Sopenharmony_ci bap_read(ai, pBuf, 2, BAP1); 42118c2ecf20Sopenharmony_ci // length for remaining part of rid 42128c2ecf20Sopenharmony_ci len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2; 42138c2ecf20Sopenharmony_ci 42148c2ecf20Sopenharmony_ci if (len <= 2) { 42158c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, 42168c2ecf20Sopenharmony_ci "Rid %x has a length of %d which is too short", 42178c2ecf20Sopenharmony_ci (int)rid, (int)len); 42188c2ecf20Sopenharmony_ci rc = ERROR; 42198c2ecf20Sopenharmony_ci goto done; 42208c2ecf20Sopenharmony_ci } 42218c2ecf20Sopenharmony_ci // read remainder of the rid 42228c2ecf20Sopenharmony_ci rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1); 42238c2ecf20Sopenharmony_ci } 42248c2ecf20Sopenharmony_cidone: 42258c2ecf20Sopenharmony_ci if (lock) 42268c2ecf20Sopenharmony_ci up(&ai->sem); 42278c2ecf20Sopenharmony_ci return rc; 42288c2ecf20Sopenharmony_ci} 42298c2ecf20Sopenharmony_ci 42308c2ecf20Sopenharmony_ci/* Note, that we are using BAP1 which is also used by transmit, so 42318c2ecf20Sopenharmony_ci * make sure this isn't called when a transmit is happening */ 42328c2ecf20Sopenharmony_cistatic int PC4500_writerid(struct airo_info *ai, u16 rid, 42338c2ecf20Sopenharmony_ci const void *pBuf, int len, int lock) 42348c2ecf20Sopenharmony_ci{ 42358c2ecf20Sopenharmony_ci u16 status; 42368c2ecf20Sopenharmony_ci int rc = SUCCESS; 42378c2ecf20Sopenharmony_ci 42388c2ecf20Sopenharmony_ci *(__le16*)pBuf = cpu_to_le16((u16)len); 42398c2ecf20Sopenharmony_ci 42408c2ecf20Sopenharmony_ci if (lock) { 42418c2ecf20Sopenharmony_ci if (down_interruptible(&ai->sem)) 42428c2ecf20Sopenharmony_ci return ERROR; 42438c2ecf20Sopenharmony_ci } 42448c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) { 42458c2ecf20Sopenharmony_ci Cmd cmd; 42468c2ecf20Sopenharmony_ci Resp rsp; 42478c2ecf20Sopenharmony_ci 42488c2ecf20Sopenharmony_ci if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid)) 42498c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, 42508c2ecf20Sopenharmony_ci "%s: MAC should be disabled (rid=%04x)", 42518c2ecf20Sopenharmony_ci __func__, rid); 42528c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 42538c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 42548c2ecf20Sopenharmony_ci 42558c2ecf20Sopenharmony_ci ai->config_desc.rid_desc.valid = 1; 42568c2ecf20Sopenharmony_ci ai->config_desc.rid_desc.len = *((u16 *)pBuf); 42578c2ecf20Sopenharmony_ci ai->config_desc.rid_desc.rid = 0; 42588c2ecf20Sopenharmony_ci 42598c2ecf20Sopenharmony_ci cmd.cmd = CMD_WRITERID; 42608c2ecf20Sopenharmony_ci cmd.parm0 = rid; 42618c2ecf20Sopenharmony_ci 42628c2ecf20Sopenharmony_ci memcpy_toio(ai->config_desc.card_ram_off, 42638c2ecf20Sopenharmony_ci &ai->config_desc.rid_desc, sizeof(Rid)); 42648c2ecf20Sopenharmony_ci 42658c2ecf20Sopenharmony_ci if (len < 4 || len > 2047) { 42668c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "%s: len=%d", __func__, len); 42678c2ecf20Sopenharmony_ci rc = -1; 42688c2ecf20Sopenharmony_ci } else { 42698c2ecf20Sopenharmony_ci memcpy(ai->config_desc.virtual_host_addr, 42708c2ecf20Sopenharmony_ci pBuf, len); 42718c2ecf20Sopenharmony_ci 42728c2ecf20Sopenharmony_ci rc = issuecommand(ai, &cmd, &rsp); 42738c2ecf20Sopenharmony_ci if ((rc & 0xff00) != 0) { 42748c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "%s: Write rid Error %d", 42758c2ecf20Sopenharmony_ci __func__, rc); 42768c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "%s: Cmd=%04x", 42778c2ecf20Sopenharmony_ci __func__, cmd.cmd); 42788c2ecf20Sopenharmony_ci } 42798c2ecf20Sopenharmony_ci 42808c2ecf20Sopenharmony_ci if ((rsp.status & 0x7f00)) 42818c2ecf20Sopenharmony_ci rc = rsp.rsp0; 42828c2ecf20Sopenharmony_ci } 42838c2ecf20Sopenharmony_ci } else { 42848c2ecf20Sopenharmony_ci // --- first access so that we can write the rid data 42858c2ecf20Sopenharmony_ci if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) { 42868c2ecf20Sopenharmony_ci rc = status; 42878c2ecf20Sopenharmony_ci goto done; 42888c2ecf20Sopenharmony_ci } 42898c2ecf20Sopenharmony_ci // --- now write the rid data 42908c2ecf20Sopenharmony_ci if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) { 42918c2ecf20Sopenharmony_ci rc = ERROR; 42928c2ecf20Sopenharmony_ci goto done; 42938c2ecf20Sopenharmony_ci } 42948c2ecf20Sopenharmony_ci bap_write(ai, pBuf, len, BAP1); 42958c2ecf20Sopenharmony_ci // ---now commit the rid data 42968c2ecf20Sopenharmony_ci rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS); 42978c2ecf20Sopenharmony_ci } 42988c2ecf20Sopenharmony_cidone: 42998c2ecf20Sopenharmony_ci if (lock) 43008c2ecf20Sopenharmony_ci up(&ai->sem); 43018c2ecf20Sopenharmony_ci return rc; 43028c2ecf20Sopenharmony_ci} 43038c2ecf20Sopenharmony_ci 43048c2ecf20Sopenharmony_ci/* Allocates a FID to be used for transmitting packets. We only use 43058c2ecf20Sopenharmony_ci one for now. */ 43068c2ecf20Sopenharmony_cistatic u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw) 43078c2ecf20Sopenharmony_ci{ 43088c2ecf20Sopenharmony_ci unsigned int loop = 3000; 43098c2ecf20Sopenharmony_ci Cmd cmd; 43108c2ecf20Sopenharmony_ci Resp rsp; 43118c2ecf20Sopenharmony_ci u16 txFid; 43128c2ecf20Sopenharmony_ci __le16 txControl; 43138c2ecf20Sopenharmony_ci 43148c2ecf20Sopenharmony_ci cmd.cmd = CMD_ALLOCATETX; 43158c2ecf20Sopenharmony_ci cmd.parm0 = lenPayload; 43168c2ecf20Sopenharmony_ci if (down_interruptible(&ai->sem)) 43178c2ecf20Sopenharmony_ci return ERROR; 43188c2ecf20Sopenharmony_ci if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { 43198c2ecf20Sopenharmony_ci txFid = ERROR; 43208c2ecf20Sopenharmony_ci goto done; 43218c2ecf20Sopenharmony_ci } 43228c2ecf20Sopenharmony_ci if ((rsp.status & 0xFF00) != 0) { 43238c2ecf20Sopenharmony_ci txFid = ERROR; 43248c2ecf20Sopenharmony_ci goto done; 43258c2ecf20Sopenharmony_ci } 43268c2ecf20Sopenharmony_ci /* wait for the allocate event/indication 43278c2ecf20Sopenharmony_ci * It makes me kind of nervous that this can just sit here and spin, 43288c2ecf20Sopenharmony_ci * but in practice it only loops like four times. */ 43298c2ecf20Sopenharmony_ci while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop); 43308c2ecf20Sopenharmony_ci if (!loop) { 43318c2ecf20Sopenharmony_ci txFid = ERROR; 43328c2ecf20Sopenharmony_ci goto done; 43338c2ecf20Sopenharmony_ci } 43348c2ecf20Sopenharmony_ci 43358c2ecf20Sopenharmony_ci // get the allocated fid and acknowledge 43368c2ecf20Sopenharmony_ci txFid = IN4500(ai, TXALLOCFID); 43378c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_ALLOC); 43388c2ecf20Sopenharmony_ci 43398c2ecf20Sopenharmony_ci /* The CARD is pretty cool since it converts the ethernet packet 43408c2ecf20Sopenharmony_ci * into 802.11. Also note that we don't release the FID since we 43418c2ecf20Sopenharmony_ci * will be using the same one over and over again. */ 43428c2ecf20Sopenharmony_ci /* We only have to setup the control once since we are not 43438c2ecf20Sopenharmony_ci * releasing the fid. */ 43448c2ecf20Sopenharmony_ci if (raw) 43458c2ecf20Sopenharmony_ci txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11 43468c2ecf20Sopenharmony_ci | TXCTL_ETHERNET | TXCTL_NORELEASE); 43478c2ecf20Sopenharmony_ci else 43488c2ecf20Sopenharmony_ci txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3 43498c2ecf20Sopenharmony_ci | TXCTL_ETHERNET | TXCTL_NORELEASE); 43508c2ecf20Sopenharmony_ci if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS) 43518c2ecf20Sopenharmony_ci txFid = ERROR; 43528c2ecf20Sopenharmony_ci else 43538c2ecf20Sopenharmony_ci bap_write(ai, &txControl, sizeof(txControl), BAP1); 43548c2ecf20Sopenharmony_ci 43558c2ecf20Sopenharmony_cidone: 43568c2ecf20Sopenharmony_ci up(&ai->sem); 43578c2ecf20Sopenharmony_ci 43588c2ecf20Sopenharmony_ci return txFid; 43598c2ecf20Sopenharmony_ci} 43608c2ecf20Sopenharmony_ci 43618c2ecf20Sopenharmony_ci/* In general BAP1 is dedicated to transmiting packets. However, 43628c2ecf20Sopenharmony_ci since we need a BAP when accessing RIDs, we also use BAP1 for that. 43638c2ecf20Sopenharmony_ci Make sure the BAP1 spinlock is held when this is called. */ 43648c2ecf20Sopenharmony_cistatic int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket) 43658c2ecf20Sopenharmony_ci{ 43668c2ecf20Sopenharmony_ci __le16 payloadLen; 43678c2ecf20Sopenharmony_ci Cmd cmd; 43688c2ecf20Sopenharmony_ci Resp rsp; 43698c2ecf20Sopenharmony_ci int miclen = 0; 43708c2ecf20Sopenharmony_ci u16 txFid = len; 43718c2ecf20Sopenharmony_ci MICBuffer pMic; 43728c2ecf20Sopenharmony_ci 43738c2ecf20Sopenharmony_ci len >>= 16; 43748c2ecf20Sopenharmony_ci 43758c2ecf20Sopenharmony_ci if (len <= ETH_ALEN * 2) { 43768c2ecf20Sopenharmony_ci airo_print_warn(ai->dev->name, "Short packet %d", len); 43778c2ecf20Sopenharmony_ci return ERROR; 43788c2ecf20Sopenharmony_ci } 43798c2ecf20Sopenharmony_ci len -= ETH_ALEN * 2; 43808c2ecf20Sopenharmony_ci 43818c2ecf20Sopenharmony_ci if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled && 43828c2ecf20Sopenharmony_ci (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) { 43838c2ecf20Sopenharmony_ci if (encapsulate(ai, (etherHead *)pPacket,&pMic, len) != SUCCESS) 43848c2ecf20Sopenharmony_ci return ERROR; 43858c2ecf20Sopenharmony_ci miclen = sizeof(pMic); 43868c2ecf20Sopenharmony_ci } 43878c2ecf20Sopenharmony_ci // packet is destination[6], source[6], payload[len-12] 43888c2ecf20Sopenharmony_ci // write the payload length and dst/src/payload 43898c2ecf20Sopenharmony_ci if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR; 43908c2ecf20Sopenharmony_ci /* The hardware addresses aren't counted as part of the payload, so 43918c2ecf20Sopenharmony_ci * we have to subtract the 12 bytes for the addresses off */ 43928c2ecf20Sopenharmony_ci payloadLen = cpu_to_le16(len + miclen); 43938c2ecf20Sopenharmony_ci bap_write(ai, &payloadLen, sizeof(payloadLen), BAP1); 43948c2ecf20Sopenharmony_ci bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1); 43958c2ecf20Sopenharmony_ci if (miclen) 43968c2ecf20Sopenharmony_ci bap_write(ai, (__le16*)&pMic, miclen, BAP1); 43978c2ecf20Sopenharmony_ci bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1); 43988c2ecf20Sopenharmony_ci // issue the transmit command 43998c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 44008c2ecf20Sopenharmony_ci cmd.cmd = CMD_TRANSMIT; 44018c2ecf20Sopenharmony_ci cmd.parm0 = txFid; 44028c2ecf20Sopenharmony_ci if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR; 44038c2ecf20Sopenharmony_ci if ((rsp.status & 0xFF00) != 0) return ERROR; 44048c2ecf20Sopenharmony_ci return SUCCESS; 44058c2ecf20Sopenharmony_ci} 44068c2ecf20Sopenharmony_ci 44078c2ecf20Sopenharmony_cistatic int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket) 44088c2ecf20Sopenharmony_ci{ 44098c2ecf20Sopenharmony_ci __le16 fc, payloadLen; 44108c2ecf20Sopenharmony_ci Cmd cmd; 44118c2ecf20Sopenharmony_ci Resp rsp; 44128c2ecf20Sopenharmony_ci int hdrlen; 44138c2ecf20Sopenharmony_ci static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6}; 44148c2ecf20Sopenharmony_ci /* padding of header to full size + le16 gaplen (6) + gaplen bytes */ 44158c2ecf20Sopenharmony_ci u16 txFid = len; 44168c2ecf20Sopenharmony_ci len >>= 16; 44178c2ecf20Sopenharmony_ci 44188c2ecf20Sopenharmony_ci fc = *(__le16*)pPacket; 44198c2ecf20Sopenharmony_ci hdrlen = header_len(fc); 44208c2ecf20Sopenharmony_ci 44218c2ecf20Sopenharmony_ci if (len < hdrlen) { 44228c2ecf20Sopenharmony_ci airo_print_warn(ai->dev->name, "Short packet %d", len); 44238c2ecf20Sopenharmony_ci return ERROR; 44248c2ecf20Sopenharmony_ci } 44258c2ecf20Sopenharmony_ci 44268c2ecf20Sopenharmony_ci /* packet is 802.11 header + payload 44278c2ecf20Sopenharmony_ci * write the payload length and dst/src/payload */ 44288c2ecf20Sopenharmony_ci if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR; 44298c2ecf20Sopenharmony_ci /* The 802.11 header aren't counted as part of the payload, so 44308c2ecf20Sopenharmony_ci * we have to subtract the header bytes off */ 44318c2ecf20Sopenharmony_ci payloadLen = cpu_to_le16(len-hdrlen); 44328c2ecf20Sopenharmony_ci bap_write(ai, &payloadLen, sizeof(payloadLen), BAP1); 44338c2ecf20Sopenharmony_ci if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR; 44348c2ecf20Sopenharmony_ci bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1); 44358c2ecf20Sopenharmony_ci bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1); 44368c2ecf20Sopenharmony_ci 44378c2ecf20Sopenharmony_ci bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1); 44388c2ecf20Sopenharmony_ci // issue the transmit command 44398c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 44408c2ecf20Sopenharmony_ci cmd.cmd = CMD_TRANSMIT; 44418c2ecf20Sopenharmony_ci cmd.parm0 = txFid; 44428c2ecf20Sopenharmony_ci if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR; 44438c2ecf20Sopenharmony_ci if ((rsp.status & 0xFF00) != 0) return ERROR; 44448c2ecf20Sopenharmony_ci return SUCCESS; 44458c2ecf20Sopenharmony_ci} 44468c2ecf20Sopenharmony_ci 44478c2ecf20Sopenharmony_ci/* 44488c2ecf20Sopenharmony_ci * This is the proc_fs routines. It is a bit messier than I would 44498c2ecf20Sopenharmony_ci * like! Feel free to clean it up! 44508c2ecf20Sopenharmony_ci */ 44518c2ecf20Sopenharmony_ci 44528c2ecf20Sopenharmony_cistatic ssize_t proc_read(struct file *file, 44538c2ecf20Sopenharmony_ci char __user *buffer, 44548c2ecf20Sopenharmony_ci size_t len, 44558c2ecf20Sopenharmony_ci loff_t *offset); 44568c2ecf20Sopenharmony_ci 44578c2ecf20Sopenharmony_cistatic ssize_t proc_write(struct file *file, 44588c2ecf20Sopenharmony_ci const char __user *buffer, 44598c2ecf20Sopenharmony_ci size_t len, 44608c2ecf20Sopenharmony_ci loff_t *offset); 44618c2ecf20Sopenharmony_cistatic int proc_close(struct inode *inode, struct file *file); 44628c2ecf20Sopenharmony_ci 44638c2ecf20Sopenharmony_cistatic int proc_stats_open(struct inode *inode, struct file *file); 44648c2ecf20Sopenharmony_cistatic int proc_statsdelta_open(struct inode *inode, struct file *file); 44658c2ecf20Sopenharmony_cistatic int proc_status_open(struct inode *inode, struct file *file); 44668c2ecf20Sopenharmony_cistatic int proc_SSID_open(struct inode *inode, struct file *file); 44678c2ecf20Sopenharmony_cistatic int proc_APList_open(struct inode *inode, struct file *file); 44688c2ecf20Sopenharmony_cistatic int proc_BSSList_open(struct inode *inode, struct file *file); 44698c2ecf20Sopenharmony_cistatic int proc_config_open(struct inode *inode, struct file *file); 44708c2ecf20Sopenharmony_cistatic int proc_wepkey_open(struct inode *inode, struct file *file); 44718c2ecf20Sopenharmony_ci 44728c2ecf20Sopenharmony_cistatic const struct proc_ops proc_statsdelta_ops = { 44738c2ecf20Sopenharmony_ci .proc_read = proc_read, 44748c2ecf20Sopenharmony_ci .proc_open = proc_statsdelta_open, 44758c2ecf20Sopenharmony_ci .proc_release = proc_close, 44768c2ecf20Sopenharmony_ci .proc_lseek = default_llseek, 44778c2ecf20Sopenharmony_ci}; 44788c2ecf20Sopenharmony_ci 44798c2ecf20Sopenharmony_cistatic const struct proc_ops proc_stats_ops = { 44808c2ecf20Sopenharmony_ci .proc_read = proc_read, 44818c2ecf20Sopenharmony_ci .proc_open = proc_stats_open, 44828c2ecf20Sopenharmony_ci .proc_release = proc_close, 44838c2ecf20Sopenharmony_ci .proc_lseek = default_llseek, 44848c2ecf20Sopenharmony_ci}; 44858c2ecf20Sopenharmony_ci 44868c2ecf20Sopenharmony_cistatic const struct proc_ops proc_status_ops = { 44878c2ecf20Sopenharmony_ci .proc_read = proc_read, 44888c2ecf20Sopenharmony_ci .proc_open = proc_status_open, 44898c2ecf20Sopenharmony_ci .proc_release = proc_close, 44908c2ecf20Sopenharmony_ci .proc_lseek = default_llseek, 44918c2ecf20Sopenharmony_ci}; 44928c2ecf20Sopenharmony_ci 44938c2ecf20Sopenharmony_cistatic const struct proc_ops proc_SSID_ops = { 44948c2ecf20Sopenharmony_ci .proc_read = proc_read, 44958c2ecf20Sopenharmony_ci .proc_write = proc_write, 44968c2ecf20Sopenharmony_ci .proc_open = proc_SSID_open, 44978c2ecf20Sopenharmony_ci .proc_release = proc_close, 44988c2ecf20Sopenharmony_ci .proc_lseek = default_llseek, 44998c2ecf20Sopenharmony_ci}; 45008c2ecf20Sopenharmony_ci 45018c2ecf20Sopenharmony_cistatic const struct proc_ops proc_BSSList_ops = { 45028c2ecf20Sopenharmony_ci .proc_read = proc_read, 45038c2ecf20Sopenharmony_ci .proc_write = proc_write, 45048c2ecf20Sopenharmony_ci .proc_open = proc_BSSList_open, 45058c2ecf20Sopenharmony_ci .proc_release = proc_close, 45068c2ecf20Sopenharmony_ci .proc_lseek = default_llseek, 45078c2ecf20Sopenharmony_ci}; 45088c2ecf20Sopenharmony_ci 45098c2ecf20Sopenharmony_cistatic const struct proc_ops proc_APList_ops = { 45108c2ecf20Sopenharmony_ci .proc_read = proc_read, 45118c2ecf20Sopenharmony_ci .proc_write = proc_write, 45128c2ecf20Sopenharmony_ci .proc_open = proc_APList_open, 45138c2ecf20Sopenharmony_ci .proc_release = proc_close, 45148c2ecf20Sopenharmony_ci .proc_lseek = default_llseek, 45158c2ecf20Sopenharmony_ci}; 45168c2ecf20Sopenharmony_ci 45178c2ecf20Sopenharmony_cistatic const struct proc_ops proc_config_ops = { 45188c2ecf20Sopenharmony_ci .proc_read = proc_read, 45198c2ecf20Sopenharmony_ci .proc_write = proc_write, 45208c2ecf20Sopenharmony_ci .proc_open = proc_config_open, 45218c2ecf20Sopenharmony_ci .proc_release = proc_close, 45228c2ecf20Sopenharmony_ci .proc_lseek = default_llseek, 45238c2ecf20Sopenharmony_ci}; 45248c2ecf20Sopenharmony_ci 45258c2ecf20Sopenharmony_cistatic const struct proc_ops proc_wepkey_ops = { 45268c2ecf20Sopenharmony_ci .proc_read = proc_read, 45278c2ecf20Sopenharmony_ci .proc_write = proc_write, 45288c2ecf20Sopenharmony_ci .proc_open = proc_wepkey_open, 45298c2ecf20Sopenharmony_ci .proc_release = proc_close, 45308c2ecf20Sopenharmony_ci .proc_lseek = default_llseek, 45318c2ecf20Sopenharmony_ci}; 45328c2ecf20Sopenharmony_ci 45338c2ecf20Sopenharmony_cistatic struct proc_dir_entry *airo_entry; 45348c2ecf20Sopenharmony_ci 45358c2ecf20Sopenharmony_cistruct proc_data { 45368c2ecf20Sopenharmony_ci int release_buffer; 45378c2ecf20Sopenharmony_ci int readlen; 45388c2ecf20Sopenharmony_ci char *rbuffer; 45398c2ecf20Sopenharmony_ci int writelen; 45408c2ecf20Sopenharmony_ci int maxwritelen; 45418c2ecf20Sopenharmony_ci char *wbuffer; 45428c2ecf20Sopenharmony_ci void (*on_close) (struct inode *, struct file *); 45438c2ecf20Sopenharmony_ci}; 45448c2ecf20Sopenharmony_ci 45458c2ecf20Sopenharmony_cistatic int setup_proc_entry(struct net_device *dev, 45468c2ecf20Sopenharmony_ci struct airo_info *apriv) 45478c2ecf20Sopenharmony_ci{ 45488c2ecf20Sopenharmony_ci struct proc_dir_entry *entry; 45498c2ecf20Sopenharmony_ci 45508c2ecf20Sopenharmony_ci /* First setup the device directory */ 45518c2ecf20Sopenharmony_ci strcpy(apriv->proc_name, dev->name); 45528c2ecf20Sopenharmony_ci apriv->proc_entry = proc_mkdir_mode(apriv->proc_name, airo_perm, 45538c2ecf20Sopenharmony_ci airo_entry); 45548c2ecf20Sopenharmony_ci if (!apriv->proc_entry) 45558c2ecf20Sopenharmony_ci return -ENOMEM; 45568c2ecf20Sopenharmony_ci proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid); 45578c2ecf20Sopenharmony_ci 45588c2ecf20Sopenharmony_ci /* Setup the StatsDelta */ 45598c2ecf20Sopenharmony_ci entry = proc_create_data("StatsDelta", 0444 & proc_perm, 45608c2ecf20Sopenharmony_ci apriv->proc_entry, &proc_statsdelta_ops, dev); 45618c2ecf20Sopenharmony_ci if (!entry) 45628c2ecf20Sopenharmony_ci goto fail; 45638c2ecf20Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 45648c2ecf20Sopenharmony_ci 45658c2ecf20Sopenharmony_ci /* Setup the Stats */ 45668c2ecf20Sopenharmony_ci entry = proc_create_data("Stats", 0444 & proc_perm, 45678c2ecf20Sopenharmony_ci apriv->proc_entry, &proc_stats_ops, dev); 45688c2ecf20Sopenharmony_ci if (!entry) 45698c2ecf20Sopenharmony_ci goto fail; 45708c2ecf20Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 45718c2ecf20Sopenharmony_ci 45728c2ecf20Sopenharmony_ci /* Setup the Status */ 45738c2ecf20Sopenharmony_ci entry = proc_create_data("Status", 0444 & proc_perm, 45748c2ecf20Sopenharmony_ci apriv->proc_entry, &proc_status_ops, dev); 45758c2ecf20Sopenharmony_ci if (!entry) 45768c2ecf20Sopenharmony_ci goto fail; 45778c2ecf20Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 45788c2ecf20Sopenharmony_ci 45798c2ecf20Sopenharmony_ci /* Setup the Config */ 45808c2ecf20Sopenharmony_ci entry = proc_create_data("Config", proc_perm, 45818c2ecf20Sopenharmony_ci apriv->proc_entry, &proc_config_ops, dev); 45828c2ecf20Sopenharmony_ci if (!entry) 45838c2ecf20Sopenharmony_ci goto fail; 45848c2ecf20Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 45858c2ecf20Sopenharmony_ci 45868c2ecf20Sopenharmony_ci /* Setup the SSID */ 45878c2ecf20Sopenharmony_ci entry = proc_create_data("SSID", proc_perm, 45888c2ecf20Sopenharmony_ci apriv->proc_entry, &proc_SSID_ops, dev); 45898c2ecf20Sopenharmony_ci if (!entry) 45908c2ecf20Sopenharmony_ci goto fail; 45918c2ecf20Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 45928c2ecf20Sopenharmony_ci 45938c2ecf20Sopenharmony_ci /* Setup the APList */ 45948c2ecf20Sopenharmony_ci entry = proc_create_data("APList", proc_perm, 45958c2ecf20Sopenharmony_ci apriv->proc_entry, &proc_APList_ops, dev); 45968c2ecf20Sopenharmony_ci if (!entry) 45978c2ecf20Sopenharmony_ci goto fail; 45988c2ecf20Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 45998c2ecf20Sopenharmony_ci 46008c2ecf20Sopenharmony_ci /* Setup the BSSList */ 46018c2ecf20Sopenharmony_ci entry = proc_create_data("BSSList", proc_perm, 46028c2ecf20Sopenharmony_ci apriv->proc_entry, &proc_BSSList_ops, dev); 46038c2ecf20Sopenharmony_ci if (!entry) 46048c2ecf20Sopenharmony_ci goto fail; 46058c2ecf20Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 46068c2ecf20Sopenharmony_ci 46078c2ecf20Sopenharmony_ci /* Setup the WepKey */ 46088c2ecf20Sopenharmony_ci entry = proc_create_data("WepKey", proc_perm, 46098c2ecf20Sopenharmony_ci apriv->proc_entry, &proc_wepkey_ops, dev); 46108c2ecf20Sopenharmony_ci if (!entry) 46118c2ecf20Sopenharmony_ci goto fail; 46128c2ecf20Sopenharmony_ci proc_set_user(entry, proc_kuid, proc_kgid); 46138c2ecf20Sopenharmony_ci return 0; 46148c2ecf20Sopenharmony_ci 46158c2ecf20Sopenharmony_cifail: 46168c2ecf20Sopenharmony_ci remove_proc_subtree(apriv->proc_name, airo_entry); 46178c2ecf20Sopenharmony_ci return -ENOMEM; 46188c2ecf20Sopenharmony_ci} 46198c2ecf20Sopenharmony_ci 46208c2ecf20Sopenharmony_cistatic int takedown_proc_entry(struct net_device *dev, 46218c2ecf20Sopenharmony_ci struct airo_info *apriv) 46228c2ecf20Sopenharmony_ci{ 46238c2ecf20Sopenharmony_ci remove_proc_subtree(apriv->proc_name, airo_entry); 46248c2ecf20Sopenharmony_ci return 0; 46258c2ecf20Sopenharmony_ci} 46268c2ecf20Sopenharmony_ci 46278c2ecf20Sopenharmony_ci/* 46288c2ecf20Sopenharmony_ci * What we want from the proc_fs is to be able to efficiently read 46298c2ecf20Sopenharmony_ci * and write the configuration. To do this, we want to read the 46308c2ecf20Sopenharmony_ci * configuration when the file is opened and write it when the file is 46318c2ecf20Sopenharmony_ci * closed. So basically we allocate a read buffer at open and fill it 46328c2ecf20Sopenharmony_ci * with data, and allocate a write buffer and read it at close. 46338c2ecf20Sopenharmony_ci */ 46348c2ecf20Sopenharmony_ci 46358c2ecf20Sopenharmony_ci/* 46368c2ecf20Sopenharmony_ci * The read routine is generic, it relies on the preallocated rbuffer 46378c2ecf20Sopenharmony_ci * to supply the data. 46388c2ecf20Sopenharmony_ci */ 46398c2ecf20Sopenharmony_cistatic ssize_t proc_read(struct file *file, 46408c2ecf20Sopenharmony_ci char __user *buffer, 46418c2ecf20Sopenharmony_ci size_t len, 46428c2ecf20Sopenharmony_ci loff_t *offset) 46438c2ecf20Sopenharmony_ci{ 46448c2ecf20Sopenharmony_ci struct proc_data *priv = file->private_data; 46458c2ecf20Sopenharmony_ci 46468c2ecf20Sopenharmony_ci if (!priv->rbuffer) 46478c2ecf20Sopenharmony_ci return -EINVAL; 46488c2ecf20Sopenharmony_ci 46498c2ecf20Sopenharmony_ci return simple_read_from_buffer(buffer, len, offset, priv->rbuffer, 46508c2ecf20Sopenharmony_ci priv->readlen); 46518c2ecf20Sopenharmony_ci} 46528c2ecf20Sopenharmony_ci 46538c2ecf20Sopenharmony_ci/* 46548c2ecf20Sopenharmony_ci * The write routine is generic, it fills in a preallocated rbuffer 46558c2ecf20Sopenharmony_ci * to supply the data. 46568c2ecf20Sopenharmony_ci */ 46578c2ecf20Sopenharmony_cistatic ssize_t proc_write(struct file *file, 46588c2ecf20Sopenharmony_ci const char __user *buffer, 46598c2ecf20Sopenharmony_ci size_t len, 46608c2ecf20Sopenharmony_ci loff_t *offset) 46618c2ecf20Sopenharmony_ci{ 46628c2ecf20Sopenharmony_ci ssize_t ret; 46638c2ecf20Sopenharmony_ci struct proc_data *priv = file->private_data; 46648c2ecf20Sopenharmony_ci 46658c2ecf20Sopenharmony_ci if (!priv->wbuffer) 46668c2ecf20Sopenharmony_ci return -EINVAL; 46678c2ecf20Sopenharmony_ci 46688c2ecf20Sopenharmony_ci ret = simple_write_to_buffer(priv->wbuffer, priv->maxwritelen, offset, 46698c2ecf20Sopenharmony_ci buffer, len); 46708c2ecf20Sopenharmony_ci if (ret > 0) 46718c2ecf20Sopenharmony_ci priv->writelen = max_t(int, priv->writelen, *offset); 46728c2ecf20Sopenharmony_ci 46738c2ecf20Sopenharmony_ci return ret; 46748c2ecf20Sopenharmony_ci} 46758c2ecf20Sopenharmony_ci 46768c2ecf20Sopenharmony_cistatic int proc_status_open(struct inode *inode, struct file *file) 46778c2ecf20Sopenharmony_ci{ 46788c2ecf20Sopenharmony_ci struct proc_data *data; 46798c2ecf20Sopenharmony_ci struct net_device *dev = PDE_DATA(inode); 46808c2ecf20Sopenharmony_ci struct airo_info *apriv = dev->ml_priv; 46818c2ecf20Sopenharmony_ci CapabilityRid cap_rid; 46828c2ecf20Sopenharmony_ci StatusRid status_rid; 46838c2ecf20Sopenharmony_ci u16 mode; 46848c2ecf20Sopenharmony_ci int i; 46858c2ecf20Sopenharmony_ci 46868c2ecf20Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 46878c2ecf20Sopenharmony_ci return -ENOMEM; 46888c2ecf20Sopenharmony_ci data = file->private_data; 46898c2ecf20Sopenharmony_ci if ((data->rbuffer = kmalloc(2048, GFP_KERNEL)) == NULL) { 46908c2ecf20Sopenharmony_ci kfree (file->private_data); 46918c2ecf20Sopenharmony_ci return -ENOMEM; 46928c2ecf20Sopenharmony_ci } 46938c2ecf20Sopenharmony_ci 46948c2ecf20Sopenharmony_ci readStatusRid(apriv, &status_rid, 1); 46958c2ecf20Sopenharmony_ci readCapabilityRid(apriv, &cap_rid, 1); 46968c2ecf20Sopenharmony_ci 46978c2ecf20Sopenharmony_ci mode = le16_to_cpu(status_rid.mode); 46988c2ecf20Sopenharmony_ci 46998c2ecf20Sopenharmony_ci i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n", 47008c2ecf20Sopenharmony_ci mode & 1 ? "CFG ": "", 47018c2ecf20Sopenharmony_ci mode & 2 ? "ACT ": "", 47028c2ecf20Sopenharmony_ci mode & 0x10 ? "SYN ": "", 47038c2ecf20Sopenharmony_ci mode & 0x20 ? "LNK ": "", 47048c2ecf20Sopenharmony_ci mode & 0x40 ? "LEAP ": "", 47058c2ecf20Sopenharmony_ci mode & 0x80 ? "PRIV ": "", 47068c2ecf20Sopenharmony_ci mode & 0x100 ? "KEY ": "", 47078c2ecf20Sopenharmony_ci mode & 0x200 ? "WEP ": "", 47088c2ecf20Sopenharmony_ci mode & 0x8000 ? "ERR ": ""); 47098c2ecf20Sopenharmony_ci sprintf(data->rbuffer+i, "Mode: %x\n" 47108c2ecf20Sopenharmony_ci "Signal Strength: %d\n" 47118c2ecf20Sopenharmony_ci "Signal Quality: %d\n" 47128c2ecf20Sopenharmony_ci "SSID: %-.*s\n" 47138c2ecf20Sopenharmony_ci "AP: %-.16s\n" 47148c2ecf20Sopenharmony_ci "Freq: %d\n" 47158c2ecf20Sopenharmony_ci "BitRate: %dmbs\n" 47168c2ecf20Sopenharmony_ci "Driver Version: %s\n" 47178c2ecf20Sopenharmony_ci "Device: %s\nManufacturer: %s\nFirmware Version: %s\n" 47188c2ecf20Sopenharmony_ci "Radio type: %x\nCountry: %x\nHardware Version: %x\n" 47198c2ecf20Sopenharmony_ci "Software Version: %x\nSoftware Subversion: %x\n" 47208c2ecf20Sopenharmony_ci "Boot block version: %x\n", 47218c2ecf20Sopenharmony_ci le16_to_cpu(status_rid.mode), 47228c2ecf20Sopenharmony_ci le16_to_cpu(status_rid.normalizedSignalStrength), 47238c2ecf20Sopenharmony_ci le16_to_cpu(status_rid.signalQuality), 47248c2ecf20Sopenharmony_ci le16_to_cpu(status_rid.SSIDlen), 47258c2ecf20Sopenharmony_ci status_rid.SSID, 47268c2ecf20Sopenharmony_ci status_rid.apName, 47278c2ecf20Sopenharmony_ci le16_to_cpu(status_rid.channel), 47288c2ecf20Sopenharmony_ci le16_to_cpu(status_rid.currentXmitRate) / 2, 47298c2ecf20Sopenharmony_ci version, 47308c2ecf20Sopenharmony_ci cap_rid.prodName, 47318c2ecf20Sopenharmony_ci cap_rid.manName, 47328c2ecf20Sopenharmony_ci cap_rid.prodVer, 47338c2ecf20Sopenharmony_ci le16_to_cpu(cap_rid.radioType), 47348c2ecf20Sopenharmony_ci le16_to_cpu(cap_rid.country), 47358c2ecf20Sopenharmony_ci le16_to_cpu(cap_rid.hardVer), 47368c2ecf20Sopenharmony_ci le16_to_cpu(cap_rid.softVer), 47378c2ecf20Sopenharmony_ci le16_to_cpu(cap_rid.softSubVer), 47388c2ecf20Sopenharmony_ci le16_to_cpu(cap_rid.bootBlockVer)); 47398c2ecf20Sopenharmony_ci data->readlen = strlen(data->rbuffer); 47408c2ecf20Sopenharmony_ci return 0; 47418c2ecf20Sopenharmony_ci} 47428c2ecf20Sopenharmony_ci 47438c2ecf20Sopenharmony_cistatic int proc_stats_rid_open(struct inode*, struct file*, u16); 47448c2ecf20Sopenharmony_cistatic int proc_statsdelta_open(struct inode *inode, 47458c2ecf20Sopenharmony_ci struct file *file) 47468c2ecf20Sopenharmony_ci{ 47478c2ecf20Sopenharmony_ci if (file->f_mode&FMODE_WRITE) { 47488c2ecf20Sopenharmony_ci return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR); 47498c2ecf20Sopenharmony_ci } 47508c2ecf20Sopenharmony_ci return proc_stats_rid_open(inode, file, RID_STATSDELTA); 47518c2ecf20Sopenharmony_ci} 47528c2ecf20Sopenharmony_ci 47538c2ecf20Sopenharmony_cistatic int proc_stats_open(struct inode *inode, struct file *file) 47548c2ecf20Sopenharmony_ci{ 47558c2ecf20Sopenharmony_ci return proc_stats_rid_open(inode, file, RID_STATS); 47568c2ecf20Sopenharmony_ci} 47578c2ecf20Sopenharmony_ci 47588c2ecf20Sopenharmony_cistatic int proc_stats_rid_open(struct inode *inode, 47598c2ecf20Sopenharmony_ci struct file *file, 47608c2ecf20Sopenharmony_ci u16 rid) 47618c2ecf20Sopenharmony_ci{ 47628c2ecf20Sopenharmony_ci struct proc_data *data; 47638c2ecf20Sopenharmony_ci struct net_device *dev = PDE_DATA(inode); 47648c2ecf20Sopenharmony_ci struct airo_info *apriv = dev->ml_priv; 47658c2ecf20Sopenharmony_ci StatsRid stats; 47668c2ecf20Sopenharmony_ci int i, j; 47678c2ecf20Sopenharmony_ci __le32 *vals = stats.vals; 47688c2ecf20Sopenharmony_ci int len; 47698c2ecf20Sopenharmony_ci 47708c2ecf20Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 47718c2ecf20Sopenharmony_ci return -ENOMEM; 47728c2ecf20Sopenharmony_ci data = file->private_data; 47738c2ecf20Sopenharmony_ci if ((data->rbuffer = kmalloc(4096, GFP_KERNEL)) == NULL) { 47748c2ecf20Sopenharmony_ci kfree (file->private_data); 47758c2ecf20Sopenharmony_ci return -ENOMEM; 47768c2ecf20Sopenharmony_ci } 47778c2ecf20Sopenharmony_ci 47788c2ecf20Sopenharmony_ci readStatsRid(apriv, &stats, rid, 1); 47798c2ecf20Sopenharmony_ci len = le16_to_cpu(stats.len); 47808c2ecf20Sopenharmony_ci 47818c2ecf20Sopenharmony_ci j = 0; 47828c2ecf20Sopenharmony_ci for (i = 0; statsLabels[i]!=(char *)-1 && i*4<len; i++) { 47838c2ecf20Sopenharmony_ci if (!statsLabels[i]) continue; 47848c2ecf20Sopenharmony_ci if (j+strlen(statsLabels[i])+16>4096) { 47858c2ecf20Sopenharmony_ci airo_print_warn(apriv->dev->name, 47868c2ecf20Sopenharmony_ci "Potentially disastrous buffer overflow averted!"); 47878c2ecf20Sopenharmony_ci break; 47888c2ecf20Sopenharmony_ci } 47898c2ecf20Sopenharmony_ci j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i], 47908c2ecf20Sopenharmony_ci le32_to_cpu(vals[i])); 47918c2ecf20Sopenharmony_ci } 47928c2ecf20Sopenharmony_ci if (i*4 >= len) { 47938c2ecf20Sopenharmony_ci airo_print_warn(apriv->dev->name, "Got a short rid"); 47948c2ecf20Sopenharmony_ci } 47958c2ecf20Sopenharmony_ci data->readlen = j; 47968c2ecf20Sopenharmony_ci return 0; 47978c2ecf20Sopenharmony_ci} 47988c2ecf20Sopenharmony_ci 47998c2ecf20Sopenharmony_cistatic int get_dec_u16(char *buffer, int *start, int limit) 48008c2ecf20Sopenharmony_ci{ 48018c2ecf20Sopenharmony_ci u16 value; 48028c2ecf20Sopenharmony_ci int valid = 0; 48038c2ecf20Sopenharmony_ci for (value = 0; *start < limit && buffer[*start] >= '0' && 48048c2ecf20Sopenharmony_ci buffer[*start] <= '9'; (*start)++) { 48058c2ecf20Sopenharmony_ci valid = 1; 48068c2ecf20Sopenharmony_ci value *= 10; 48078c2ecf20Sopenharmony_ci value += buffer[*start] - '0'; 48088c2ecf20Sopenharmony_ci } 48098c2ecf20Sopenharmony_ci if (!valid) return -1; 48108c2ecf20Sopenharmony_ci return value; 48118c2ecf20Sopenharmony_ci} 48128c2ecf20Sopenharmony_ci 48138c2ecf20Sopenharmony_cistatic int airo_config_commit(struct net_device *dev, 48148c2ecf20Sopenharmony_ci struct iw_request_info *info, void *zwrq, 48158c2ecf20Sopenharmony_ci char *extra); 48168c2ecf20Sopenharmony_ci 48178c2ecf20Sopenharmony_cistatic inline int sniffing_mode(struct airo_info *ai) 48188c2ecf20Sopenharmony_ci{ 48198c2ecf20Sopenharmony_ci return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >= 48208c2ecf20Sopenharmony_ci le16_to_cpu(RXMODE_RFMON); 48218c2ecf20Sopenharmony_ci} 48228c2ecf20Sopenharmony_ci 48238c2ecf20Sopenharmony_cistatic void proc_config_on_close(struct inode *inode, struct file *file) 48248c2ecf20Sopenharmony_ci{ 48258c2ecf20Sopenharmony_ci struct proc_data *data = file->private_data; 48268c2ecf20Sopenharmony_ci struct net_device *dev = PDE_DATA(inode); 48278c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 48288c2ecf20Sopenharmony_ci char *line; 48298c2ecf20Sopenharmony_ci 48308c2ecf20Sopenharmony_ci if (!data->writelen) return; 48318c2ecf20Sopenharmony_ci 48328c2ecf20Sopenharmony_ci readConfigRid(ai, 1); 48338c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 48348c2ecf20Sopenharmony_ci 48358c2ecf20Sopenharmony_ci line = data->wbuffer; 48368c2ecf20Sopenharmony_ci while (line[0]) { 48378c2ecf20Sopenharmony_ci/*** Mode processing */ 48388c2ecf20Sopenharmony_ci if (!strncmp(line, "Mode: ", 6)) { 48398c2ecf20Sopenharmony_ci line += 6; 48408c2ecf20Sopenharmony_ci if (sniffing_mode(ai)) 48418c2ecf20Sopenharmony_ci set_bit (FLAG_RESET, &ai->flags); 48428c2ecf20Sopenharmony_ci ai->config.rmode &= ~RXMODE_FULL_MASK; 48438c2ecf20Sopenharmony_ci clear_bit (FLAG_802_11, &ai->flags); 48448c2ecf20Sopenharmony_ci ai->config.opmode &= ~MODE_CFG_MASK; 48458c2ecf20Sopenharmony_ci ai->config.scanMode = SCANMODE_ACTIVE; 48468c2ecf20Sopenharmony_ci if (line[0] == 'a') { 48478c2ecf20Sopenharmony_ci ai->config.opmode |= MODE_STA_IBSS; 48488c2ecf20Sopenharmony_ci } else { 48498c2ecf20Sopenharmony_ci ai->config.opmode |= MODE_STA_ESS; 48508c2ecf20Sopenharmony_ci if (line[0] == 'r') { 48518c2ecf20Sopenharmony_ci ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER; 48528c2ecf20Sopenharmony_ci ai->config.scanMode = SCANMODE_PASSIVE; 48538c2ecf20Sopenharmony_ci set_bit (FLAG_802_11, &ai->flags); 48548c2ecf20Sopenharmony_ci } else if (line[0] == 'y') { 48558c2ecf20Sopenharmony_ci ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER; 48568c2ecf20Sopenharmony_ci ai->config.scanMode = SCANMODE_PASSIVE; 48578c2ecf20Sopenharmony_ci set_bit (FLAG_802_11, &ai->flags); 48588c2ecf20Sopenharmony_ci } else if (line[0] == 'l') 48598c2ecf20Sopenharmony_ci ai->config.rmode |= RXMODE_LANMON; 48608c2ecf20Sopenharmony_ci } 48618c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 48628c2ecf20Sopenharmony_ci } 48638c2ecf20Sopenharmony_ci 48648c2ecf20Sopenharmony_ci/*** Radio status */ 48658c2ecf20Sopenharmony_ci else if (!strncmp(line,"Radio: ", 7)) { 48668c2ecf20Sopenharmony_ci line += 7; 48678c2ecf20Sopenharmony_ci if (!strncmp(line,"off", 3)) { 48688c2ecf20Sopenharmony_ci set_bit (FLAG_RADIO_OFF, &ai->flags); 48698c2ecf20Sopenharmony_ci } else { 48708c2ecf20Sopenharmony_ci clear_bit (FLAG_RADIO_OFF, &ai->flags); 48718c2ecf20Sopenharmony_ci } 48728c2ecf20Sopenharmony_ci } 48738c2ecf20Sopenharmony_ci/*** NodeName processing */ 48748c2ecf20Sopenharmony_ci else if (!strncmp(line, "NodeName: ", 10)) { 48758c2ecf20Sopenharmony_ci int j; 48768c2ecf20Sopenharmony_ci 48778c2ecf20Sopenharmony_ci line += 10; 48788c2ecf20Sopenharmony_ci memset(ai->config.nodeName, 0, 16); 48798c2ecf20Sopenharmony_ci/* Do the name, assume a space between the mode and node name */ 48808c2ecf20Sopenharmony_ci for (j = 0; j < 16 && line[j] != '\n'; j++) { 48818c2ecf20Sopenharmony_ci ai->config.nodeName[j] = line[j]; 48828c2ecf20Sopenharmony_ci } 48838c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 48848c2ecf20Sopenharmony_ci } 48858c2ecf20Sopenharmony_ci 48868c2ecf20Sopenharmony_ci/*** PowerMode processing */ 48878c2ecf20Sopenharmony_ci else if (!strncmp(line, "PowerMode: ", 11)) { 48888c2ecf20Sopenharmony_ci line += 11; 48898c2ecf20Sopenharmony_ci if (!strncmp(line, "PSPCAM", 6)) { 48908c2ecf20Sopenharmony_ci ai->config.powerSaveMode = POWERSAVE_PSPCAM; 48918c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 48928c2ecf20Sopenharmony_ci } else if (!strncmp(line, "PSP", 3)) { 48938c2ecf20Sopenharmony_ci ai->config.powerSaveMode = POWERSAVE_PSP; 48948c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 48958c2ecf20Sopenharmony_ci } else { 48968c2ecf20Sopenharmony_ci ai->config.powerSaveMode = POWERSAVE_CAM; 48978c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 48988c2ecf20Sopenharmony_ci } 48998c2ecf20Sopenharmony_ci } else if (!strncmp(line, "DataRates: ", 11)) { 49008c2ecf20Sopenharmony_ci int v, i = 0, k = 0; /* i is index into line, 49018c2ecf20Sopenharmony_ci k is index to rates */ 49028c2ecf20Sopenharmony_ci 49038c2ecf20Sopenharmony_ci line += 11; 49048c2ecf20Sopenharmony_ci while ((v = get_dec_u16(line, &i, 3))!=-1) { 49058c2ecf20Sopenharmony_ci ai->config.rates[k++] = (u8)v; 49068c2ecf20Sopenharmony_ci line += i + 1; 49078c2ecf20Sopenharmony_ci i = 0; 49088c2ecf20Sopenharmony_ci } 49098c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49108c2ecf20Sopenharmony_ci } else if (!strncmp(line, "Channel: ", 9)) { 49118c2ecf20Sopenharmony_ci int v, i = 0; 49128c2ecf20Sopenharmony_ci line += 9; 49138c2ecf20Sopenharmony_ci v = get_dec_u16(line, &i, i+3); 49148c2ecf20Sopenharmony_ci if (v != -1) { 49158c2ecf20Sopenharmony_ci ai->config.channelSet = cpu_to_le16(v); 49168c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49178c2ecf20Sopenharmony_ci } 49188c2ecf20Sopenharmony_ci } else if (!strncmp(line, "XmitPower: ", 11)) { 49198c2ecf20Sopenharmony_ci int v, i = 0; 49208c2ecf20Sopenharmony_ci line += 11; 49218c2ecf20Sopenharmony_ci v = get_dec_u16(line, &i, i+3); 49228c2ecf20Sopenharmony_ci if (v != -1) { 49238c2ecf20Sopenharmony_ci ai->config.txPower = cpu_to_le16(v); 49248c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49258c2ecf20Sopenharmony_ci } 49268c2ecf20Sopenharmony_ci } else if (!strncmp(line, "WEP: ", 5)) { 49278c2ecf20Sopenharmony_ci line += 5; 49288c2ecf20Sopenharmony_ci switch(line[0]) { 49298c2ecf20Sopenharmony_ci case 's': 49308c2ecf20Sopenharmony_ci set_auth_type(ai, AUTH_SHAREDKEY); 49318c2ecf20Sopenharmony_ci break; 49328c2ecf20Sopenharmony_ci case 'e': 49338c2ecf20Sopenharmony_ci set_auth_type(ai, AUTH_ENCRYPT); 49348c2ecf20Sopenharmony_ci break; 49358c2ecf20Sopenharmony_ci default: 49368c2ecf20Sopenharmony_ci set_auth_type(ai, AUTH_OPEN); 49378c2ecf20Sopenharmony_ci break; 49388c2ecf20Sopenharmony_ci } 49398c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49408c2ecf20Sopenharmony_ci } else if (!strncmp(line, "LongRetryLimit: ", 16)) { 49418c2ecf20Sopenharmony_ci int v, i = 0; 49428c2ecf20Sopenharmony_ci 49438c2ecf20Sopenharmony_ci line += 16; 49448c2ecf20Sopenharmony_ci v = get_dec_u16(line, &i, 3); 49458c2ecf20Sopenharmony_ci v = (v<0) ? 0 : ((v>255) ? 255 : v); 49468c2ecf20Sopenharmony_ci ai->config.longRetryLimit = cpu_to_le16(v); 49478c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49488c2ecf20Sopenharmony_ci } else if (!strncmp(line, "ShortRetryLimit: ", 17)) { 49498c2ecf20Sopenharmony_ci int v, i = 0; 49508c2ecf20Sopenharmony_ci 49518c2ecf20Sopenharmony_ci line += 17; 49528c2ecf20Sopenharmony_ci v = get_dec_u16(line, &i, 3); 49538c2ecf20Sopenharmony_ci v = (v<0) ? 0 : ((v>255) ? 255 : v); 49548c2ecf20Sopenharmony_ci ai->config.shortRetryLimit = cpu_to_le16(v); 49558c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49568c2ecf20Sopenharmony_ci } else if (!strncmp(line, "RTSThreshold: ", 14)) { 49578c2ecf20Sopenharmony_ci int v, i = 0; 49588c2ecf20Sopenharmony_ci 49598c2ecf20Sopenharmony_ci line += 14; 49608c2ecf20Sopenharmony_ci v = get_dec_u16(line, &i, 4); 49618c2ecf20Sopenharmony_ci v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v); 49628c2ecf20Sopenharmony_ci ai->config.rtsThres = cpu_to_le16(v); 49638c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49648c2ecf20Sopenharmony_ci } else if (!strncmp(line, "TXMSDULifetime: ", 16)) { 49658c2ecf20Sopenharmony_ci int v, i = 0; 49668c2ecf20Sopenharmony_ci 49678c2ecf20Sopenharmony_ci line += 16; 49688c2ecf20Sopenharmony_ci v = get_dec_u16(line, &i, 5); 49698c2ecf20Sopenharmony_ci v = (v<0) ? 0 : v; 49708c2ecf20Sopenharmony_ci ai->config.txLifetime = cpu_to_le16(v); 49718c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49728c2ecf20Sopenharmony_ci } else if (!strncmp(line, "RXMSDULifetime: ", 16)) { 49738c2ecf20Sopenharmony_ci int v, i = 0; 49748c2ecf20Sopenharmony_ci 49758c2ecf20Sopenharmony_ci line += 16; 49768c2ecf20Sopenharmony_ci v = get_dec_u16(line, &i, 5); 49778c2ecf20Sopenharmony_ci v = (v<0) ? 0 : v; 49788c2ecf20Sopenharmony_ci ai->config.rxLifetime = cpu_to_le16(v); 49798c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49808c2ecf20Sopenharmony_ci } else if (!strncmp(line, "TXDiversity: ", 13)) { 49818c2ecf20Sopenharmony_ci ai->config.txDiversity = 49828c2ecf20Sopenharmony_ci (line[13]=='l') ? 1 : 49838c2ecf20Sopenharmony_ci ((line[13]=='r')? 2: 3); 49848c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49858c2ecf20Sopenharmony_ci } else if (!strncmp(line, "RXDiversity: ", 13)) { 49868c2ecf20Sopenharmony_ci ai->config.rxDiversity = 49878c2ecf20Sopenharmony_ci (line[13]=='l') ? 1 : 49888c2ecf20Sopenharmony_ci ((line[13]=='r')? 2: 3); 49898c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49908c2ecf20Sopenharmony_ci } else if (!strncmp(line, "FragThreshold: ", 15)) { 49918c2ecf20Sopenharmony_ci int v, i = 0; 49928c2ecf20Sopenharmony_ci 49938c2ecf20Sopenharmony_ci line += 15; 49948c2ecf20Sopenharmony_ci v = get_dec_u16(line, &i, 4); 49958c2ecf20Sopenharmony_ci v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v); 49968c2ecf20Sopenharmony_ci v = v & 0xfffe; /* Make sure its even */ 49978c2ecf20Sopenharmony_ci ai->config.fragThresh = cpu_to_le16(v); 49988c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &ai->flags); 49998c2ecf20Sopenharmony_ci } else if (!strncmp(line, "Modulation: ", 12)) { 50008c2ecf20Sopenharmony_ci line += 12; 50018c2ecf20Sopenharmony_ci switch(*line) { 50028c2ecf20Sopenharmony_ci case 'd': ai->config.modulation = MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break; 50038c2ecf20Sopenharmony_ci case 'c': ai->config.modulation = MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break; 50048c2ecf20Sopenharmony_ci case 'm': ai->config.modulation = MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break; 50058c2ecf20Sopenharmony_ci default: airo_print_warn(ai->dev->name, "Unknown modulation"); 50068c2ecf20Sopenharmony_ci } 50078c2ecf20Sopenharmony_ci } else if (!strncmp(line, "Preamble: ", 10)) { 50088c2ecf20Sopenharmony_ci line += 10; 50098c2ecf20Sopenharmony_ci switch(*line) { 50108c2ecf20Sopenharmony_ci case 'a': ai->config.preamble = PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break; 50118c2ecf20Sopenharmony_ci case 'l': ai->config.preamble = PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break; 50128c2ecf20Sopenharmony_ci case 's': ai->config.preamble = PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break; 50138c2ecf20Sopenharmony_ci default: airo_print_warn(ai->dev->name, "Unknown preamble"); 50148c2ecf20Sopenharmony_ci } 50158c2ecf20Sopenharmony_ci } else { 50168c2ecf20Sopenharmony_ci airo_print_warn(ai->dev->name, "Couldn't figure out %s", line); 50178c2ecf20Sopenharmony_ci } 50188c2ecf20Sopenharmony_ci while (line[0] && line[0] != '\n') line++; 50198c2ecf20Sopenharmony_ci if (line[0]) line++; 50208c2ecf20Sopenharmony_ci } 50218c2ecf20Sopenharmony_ci airo_config_commit(dev, NULL, NULL, NULL); 50228c2ecf20Sopenharmony_ci} 50238c2ecf20Sopenharmony_ci 50248c2ecf20Sopenharmony_cistatic const char *get_rmode(__le16 mode) 50258c2ecf20Sopenharmony_ci{ 50268c2ecf20Sopenharmony_ci switch(mode & RXMODE_MASK) { 50278c2ecf20Sopenharmony_ci case RXMODE_RFMON: return "rfmon"; 50288c2ecf20Sopenharmony_ci case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon"; 50298c2ecf20Sopenharmony_ci case RXMODE_LANMON: return "lanmon"; 50308c2ecf20Sopenharmony_ci } 50318c2ecf20Sopenharmony_ci return "ESS"; 50328c2ecf20Sopenharmony_ci} 50338c2ecf20Sopenharmony_ci 50348c2ecf20Sopenharmony_cistatic int proc_config_open(struct inode *inode, struct file *file) 50358c2ecf20Sopenharmony_ci{ 50368c2ecf20Sopenharmony_ci struct proc_data *data; 50378c2ecf20Sopenharmony_ci struct net_device *dev = PDE_DATA(inode); 50388c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 50398c2ecf20Sopenharmony_ci int i; 50408c2ecf20Sopenharmony_ci __le16 mode; 50418c2ecf20Sopenharmony_ci 50428c2ecf20Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 50438c2ecf20Sopenharmony_ci return -ENOMEM; 50448c2ecf20Sopenharmony_ci data = file->private_data; 50458c2ecf20Sopenharmony_ci if ((data->rbuffer = kmalloc(2048, GFP_KERNEL)) == NULL) { 50468c2ecf20Sopenharmony_ci kfree (file->private_data); 50478c2ecf20Sopenharmony_ci return -ENOMEM; 50488c2ecf20Sopenharmony_ci } 50498c2ecf20Sopenharmony_ci if ((data->wbuffer = kzalloc(2048, GFP_KERNEL)) == NULL) { 50508c2ecf20Sopenharmony_ci kfree (data->rbuffer); 50518c2ecf20Sopenharmony_ci kfree (file->private_data); 50528c2ecf20Sopenharmony_ci return -ENOMEM; 50538c2ecf20Sopenharmony_ci } 50548c2ecf20Sopenharmony_ci data->maxwritelen = 2048; 50558c2ecf20Sopenharmony_ci data->on_close = proc_config_on_close; 50568c2ecf20Sopenharmony_ci 50578c2ecf20Sopenharmony_ci readConfigRid(ai, 1); 50588c2ecf20Sopenharmony_ci 50598c2ecf20Sopenharmony_ci mode = ai->config.opmode & MODE_CFG_MASK; 50608c2ecf20Sopenharmony_ci i = sprintf(data->rbuffer, 50618c2ecf20Sopenharmony_ci "Mode: %s\n" 50628c2ecf20Sopenharmony_ci "Radio: %s\n" 50638c2ecf20Sopenharmony_ci "NodeName: %-16s\n" 50648c2ecf20Sopenharmony_ci "PowerMode: %s\n" 50658c2ecf20Sopenharmony_ci "DataRates: %d %d %d %d %d %d %d %d\n" 50668c2ecf20Sopenharmony_ci "Channel: %d\n" 50678c2ecf20Sopenharmony_ci "XmitPower: %d\n", 50688c2ecf20Sopenharmony_ci mode == MODE_STA_IBSS ? "adhoc" : 50698c2ecf20Sopenharmony_ci mode == MODE_STA_ESS ? get_rmode(ai->config.rmode): 50708c2ecf20Sopenharmony_ci mode == MODE_AP ? "AP" : 50718c2ecf20Sopenharmony_ci mode == MODE_AP_RPTR ? "AP RPTR" : "Error", 50728c2ecf20Sopenharmony_ci test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on", 50738c2ecf20Sopenharmony_ci ai->config.nodeName, 50748c2ecf20Sopenharmony_ci ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" : 50758c2ecf20Sopenharmony_ci ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" : 50768c2ecf20Sopenharmony_ci ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" : 50778c2ecf20Sopenharmony_ci "Error", 50788c2ecf20Sopenharmony_ci (int)ai->config.rates[0], 50798c2ecf20Sopenharmony_ci (int)ai->config.rates[1], 50808c2ecf20Sopenharmony_ci (int)ai->config.rates[2], 50818c2ecf20Sopenharmony_ci (int)ai->config.rates[3], 50828c2ecf20Sopenharmony_ci (int)ai->config.rates[4], 50838c2ecf20Sopenharmony_ci (int)ai->config.rates[5], 50848c2ecf20Sopenharmony_ci (int)ai->config.rates[6], 50858c2ecf20Sopenharmony_ci (int)ai->config.rates[7], 50868c2ecf20Sopenharmony_ci le16_to_cpu(ai->config.channelSet), 50878c2ecf20Sopenharmony_ci le16_to_cpu(ai->config.txPower) 50888c2ecf20Sopenharmony_ci ); 50898c2ecf20Sopenharmony_ci sprintf(data->rbuffer + i, 50908c2ecf20Sopenharmony_ci "LongRetryLimit: %d\n" 50918c2ecf20Sopenharmony_ci "ShortRetryLimit: %d\n" 50928c2ecf20Sopenharmony_ci "RTSThreshold: %d\n" 50938c2ecf20Sopenharmony_ci "TXMSDULifetime: %d\n" 50948c2ecf20Sopenharmony_ci "RXMSDULifetime: %d\n" 50958c2ecf20Sopenharmony_ci "TXDiversity: %s\n" 50968c2ecf20Sopenharmony_ci "RXDiversity: %s\n" 50978c2ecf20Sopenharmony_ci "FragThreshold: %d\n" 50988c2ecf20Sopenharmony_ci "WEP: %s\n" 50998c2ecf20Sopenharmony_ci "Modulation: %s\n" 51008c2ecf20Sopenharmony_ci "Preamble: %s\n", 51018c2ecf20Sopenharmony_ci le16_to_cpu(ai->config.longRetryLimit), 51028c2ecf20Sopenharmony_ci le16_to_cpu(ai->config.shortRetryLimit), 51038c2ecf20Sopenharmony_ci le16_to_cpu(ai->config.rtsThres), 51048c2ecf20Sopenharmony_ci le16_to_cpu(ai->config.txLifetime), 51058c2ecf20Sopenharmony_ci le16_to_cpu(ai->config.rxLifetime), 51068c2ecf20Sopenharmony_ci ai->config.txDiversity == 1 ? "left" : 51078c2ecf20Sopenharmony_ci ai->config.txDiversity == 2 ? "right" : "both", 51088c2ecf20Sopenharmony_ci ai->config.rxDiversity == 1 ? "left" : 51098c2ecf20Sopenharmony_ci ai->config.rxDiversity == 2 ? "right" : "both", 51108c2ecf20Sopenharmony_ci le16_to_cpu(ai->config.fragThresh), 51118c2ecf20Sopenharmony_ci ai->config.authType == AUTH_ENCRYPT ? "encrypt" : 51128c2ecf20Sopenharmony_ci ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open", 51138c2ecf20Sopenharmony_ci ai->config.modulation == MOD_DEFAULT ? "default" : 51148c2ecf20Sopenharmony_ci ai->config.modulation == MOD_CCK ? "cck" : 51158c2ecf20Sopenharmony_ci ai->config.modulation == MOD_MOK ? "mok" : "error", 51168c2ecf20Sopenharmony_ci ai->config.preamble == PREAMBLE_AUTO ? "auto" : 51178c2ecf20Sopenharmony_ci ai->config.preamble == PREAMBLE_LONG ? "long" : 51188c2ecf20Sopenharmony_ci ai->config.preamble == PREAMBLE_SHORT ? "short" : "error" 51198c2ecf20Sopenharmony_ci ); 51208c2ecf20Sopenharmony_ci data->readlen = strlen(data->rbuffer); 51218c2ecf20Sopenharmony_ci return 0; 51228c2ecf20Sopenharmony_ci} 51238c2ecf20Sopenharmony_ci 51248c2ecf20Sopenharmony_cistatic void proc_SSID_on_close(struct inode *inode, struct file *file) 51258c2ecf20Sopenharmony_ci{ 51268c2ecf20Sopenharmony_ci struct proc_data *data = file->private_data; 51278c2ecf20Sopenharmony_ci struct net_device *dev = PDE_DATA(inode); 51288c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 51298c2ecf20Sopenharmony_ci SsidRid SSID_rid; 51308c2ecf20Sopenharmony_ci int i; 51318c2ecf20Sopenharmony_ci char *p = data->wbuffer; 51328c2ecf20Sopenharmony_ci char *end = p + data->writelen; 51338c2ecf20Sopenharmony_ci 51348c2ecf20Sopenharmony_ci if (!data->writelen) 51358c2ecf20Sopenharmony_ci return; 51368c2ecf20Sopenharmony_ci 51378c2ecf20Sopenharmony_ci *end = '\n'; /* sentinel; we have space for it */ 51388c2ecf20Sopenharmony_ci 51398c2ecf20Sopenharmony_ci memset(&SSID_rid, 0, sizeof(SSID_rid)); 51408c2ecf20Sopenharmony_ci 51418c2ecf20Sopenharmony_ci for (i = 0; i < 3 && p < end; i++) { 51428c2ecf20Sopenharmony_ci int j = 0; 51438c2ecf20Sopenharmony_ci /* copy up to 32 characters from this line */ 51448c2ecf20Sopenharmony_ci while (*p != '\n' && j < 32) 51458c2ecf20Sopenharmony_ci SSID_rid.ssids[i].ssid[j++] = *p++; 51468c2ecf20Sopenharmony_ci if (j == 0) 51478c2ecf20Sopenharmony_ci break; 51488c2ecf20Sopenharmony_ci SSID_rid.ssids[i].len = cpu_to_le16(j); 51498c2ecf20Sopenharmony_ci /* skip to the beginning of the next line */ 51508c2ecf20Sopenharmony_ci while (*p++ != '\n') 51518c2ecf20Sopenharmony_ci ; 51528c2ecf20Sopenharmony_ci } 51538c2ecf20Sopenharmony_ci if (i) 51548c2ecf20Sopenharmony_ci SSID_rid.len = cpu_to_le16(sizeof(SSID_rid)); 51558c2ecf20Sopenharmony_ci disable_MAC(ai, 1); 51568c2ecf20Sopenharmony_ci writeSsidRid(ai, &SSID_rid, 1); 51578c2ecf20Sopenharmony_ci enable_MAC(ai, 1); 51588c2ecf20Sopenharmony_ci} 51598c2ecf20Sopenharmony_ci 51608c2ecf20Sopenharmony_cistatic void proc_APList_on_close(struct inode *inode, struct file *file) 51618c2ecf20Sopenharmony_ci{ 51628c2ecf20Sopenharmony_ci struct proc_data *data = file->private_data; 51638c2ecf20Sopenharmony_ci struct net_device *dev = PDE_DATA(inode); 51648c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 51658c2ecf20Sopenharmony_ci APListRid *APList_rid = &ai->APList; 51668c2ecf20Sopenharmony_ci int i; 51678c2ecf20Sopenharmony_ci 51688c2ecf20Sopenharmony_ci if (!data->writelen) return; 51698c2ecf20Sopenharmony_ci 51708c2ecf20Sopenharmony_ci memset(APList_rid, 0, sizeof(*APList_rid)); 51718c2ecf20Sopenharmony_ci APList_rid->len = cpu_to_le16(sizeof(*APList_rid)); 51728c2ecf20Sopenharmony_ci 51738c2ecf20Sopenharmony_ci for (i = 0; i < 4 && data->writelen >= (i + 1) * 6 * 3; i++) 51748c2ecf20Sopenharmony_ci mac_pton(data->wbuffer + i * 6 * 3, APList_rid->ap[i]); 51758c2ecf20Sopenharmony_ci 51768c2ecf20Sopenharmony_ci disable_MAC(ai, 1); 51778c2ecf20Sopenharmony_ci writeAPListRid(ai, APList_rid, 1); 51788c2ecf20Sopenharmony_ci enable_MAC(ai, 1); 51798c2ecf20Sopenharmony_ci} 51808c2ecf20Sopenharmony_ci 51818c2ecf20Sopenharmony_ci/* This function wraps PC4500_writerid with a MAC disable */ 51828c2ecf20Sopenharmony_cistatic int do_writerid(struct airo_info *ai, u16 rid, const void *rid_data, 51838c2ecf20Sopenharmony_ci int len, int dummy) 51848c2ecf20Sopenharmony_ci{ 51858c2ecf20Sopenharmony_ci int rc; 51868c2ecf20Sopenharmony_ci 51878c2ecf20Sopenharmony_ci disable_MAC(ai, 1); 51888c2ecf20Sopenharmony_ci rc = PC4500_writerid(ai, rid, rid_data, len, 1); 51898c2ecf20Sopenharmony_ci enable_MAC(ai, 1); 51908c2ecf20Sopenharmony_ci return rc; 51918c2ecf20Sopenharmony_ci} 51928c2ecf20Sopenharmony_ci 51938c2ecf20Sopenharmony_ci/* Returns the WEP key at the specified index, or -1 if that key does 51948c2ecf20Sopenharmony_ci * not exist. The buffer is assumed to be at least 16 bytes in length. 51958c2ecf20Sopenharmony_ci */ 51968c2ecf20Sopenharmony_cistatic int get_wep_key(struct airo_info *ai, u16 index, char *buf, u16 buflen) 51978c2ecf20Sopenharmony_ci{ 51988c2ecf20Sopenharmony_ci WepKeyRid wkr; 51998c2ecf20Sopenharmony_ci int rc; 52008c2ecf20Sopenharmony_ci __le16 lastindex; 52018c2ecf20Sopenharmony_ci 52028c2ecf20Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 1, 1); 52038c2ecf20Sopenharmony_ci if (rc != SUCCESS) 52048c2ecf20Sopenharmony_ci return -1; 52058c2ecf20Sopenharmony_ci do { 52068c2ecf20Sopenharmony_ci lastindex = wkr.kindex; 52078c2ecf20Sopenharmony_ci if (le16_to_cpu(wkr.kindex) == index) { 52088c2ecf20Sopenharmony_ci int klen = min_t(int, buflen, le16_to_cpu(wkr.klen)); 52098c2ecf20Sopenharmony_ci memcpy(buf, wkr.key, klen); 52108c2ecf20Sopenharmony_ci return klen; 52118c2ecf20Sopenharmony_ci } 52128c2ecf20Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 0, 1); 52138c2ecf20Sopenharmony_ci if (rc != SUCCESS) 52148c2ecf20Sopenharmony_ci return -1; 52158c2ecf20Sopenharmony_ci } while (lastindex != wkr.kindex); 52168c2ecf20Sopenharmony_ci return -1; 52178c2ecf20Sopenharmony_ci} 52188c2ecf20Sopenharmony_ci 52198c2ecf20Sopenharmony_cistatic int get_wep_tx_idx(struct airo_info *ai) 52208c2ecf20Sopenharmony_ci{ 52218c2ecf20Sopenharmony_ci WepKeyRid wkr; 52228c2ecf20Sopenharmony_ci int rc; 52238c2ecf20Sopenharmony_ci __le16 lastindex; 52248c2ecf20Sopenharmony_ci 52258c2ecf20Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 1, 1); 52268c2ecf20Sopenharmony_ci if (rc != SUCCESS) 52278c2ecf20Sopenharmony_ci return -1; 52288c2ecf20Sopenharmony_ci do { 52298c2ecf20Sopenharmony_ci lastindex = wkr.kindex; 52308c2ecf20Sopenharmony_ci if (wkr.kindex == cpu_to_le16(0xffff)) 52318c2ecf20Sopenharmony_ci return wkr.mac[0]; 52328c2ecf20Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 0, 1); 52338c2ecf20Sopenharmony_ci if (rc != SUCCESS) 52348c2ecf20Sopenharmony_ci return -1; 52358c2ecf20Sopenharmony_ci } while (lastindex != wkr.kindex); 52368c2ecf20Sopenharmony_ci return -1; 52378c2ecf20Sopenharmony_ci} 52388c2ecf20Sopenharmony_ci 52398c2ecf20Sopenharmony_cistatic int set_wep_key(struct airo_info *ai, u16 index, const u8 *key, 52408c2ecf20Sopenharmony_ci u16 keylen, int perm, int lock) 52418c2ecf20Sopenharmony_ci{ 52428c2ecf20Sopenharmony_ci static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 }; 52438c2ecf20Sopenharmony_ci WepKeyRid wkr; 52448c2ecf20Sopenharmony_ci int rc; 52458c2ecf20Sopenharmony_ci 52468c2ecf20Sopenharmony_ci if (WARN_ON(keylen == 0)) 52478c2ecf20Sopenharmony_ci return -1; 52488c2ecf20Sopenharmony_ci 52498c2ecf20Sopenharmony_ci memset(&wkr, 0, sizeof(wkr)); 52508c2ecf20Sopenharmony_ci wkr.len = cpu_to_le16(sizeof(wkr)); 52518c2ecf20Sopenharmony_ci wkr.kindex = cpu_to_le16(index); 52528c2ecf20Sopenharmony_ci wkr.klen = cpu_to_le16(keylen); 52538c2ecf20Sopenharmony_ci memcpy(wkr.key, key, keylen); 52548c2ecf20Sopenharmony_ci memcpy(wkr.mac, macaddr, ETH_ALEN); 52558c2ecf20Sopenharmony_ci 52568c2ecf20Sopenharmony_ci if (perm) disable_MAC(ai, lock); 52578c2ecf20Sopenharmony_ci rc = writeWepKeyRid(ai, &wkr, perm, lock); 52588c2ecf20Sopenharmony_ci if (perm) enable_MAC(ai, lock); 52598c2ecf20Sopenharmony_ci return rc; 52608c2ecf20Sopenharmony_ci} 52618c2ecf20Sopenharmony_ci 52628c2ecf20Sopenharmony_cistatic int set_wep_tx_idx(struct airo_info *ai, u16 index, int perm, int lock) 52638c2ecf20Sopenharmony_ci{ 52648c2ecf20Sopenharmony_ci WepKeyRid wkr; 52658c2ecf20Sopenharmony_ci int rc; 52668c2ecf20Sopenharmony_ci 52678c2ecf20Sopenharmony_ci memset(&wkr, 0, sizeof(wkr)); 52688c2ecf20Sopenharmony_ci wkr.len = cpu_to_le16(sizeof(wkr)); 52698c2ecf20Sopenharmony_ci wkr.kindex = cpu_to_le16(0xffff); 52708c2ecf20Sopenharmony_ci wkr.mac[0] = (char)index; 52718c2ecf20Sopenharmony_ci 52728c2ecf20Sopenharmony_ci if (perm) { 52738c2ecf20Sopenharmony_ci ai->defindex = (char)index; 52748c2ecf20Sopenharmony_ci disable_MAC(ai, lock); 52758c2ecf20Sopenharmony_ci } 52768c2ecf20Sopenharmony_ci 52778c2ecf20Sopenharmony_ci rc = writeWepKeyRid(ai, &wkr, perm, lock); 52788c2ecf20Sopenharmony_ci 52798c2ecf20Sopenharmony_ci if (perm) 52808c2ecf20Sopenharmony_ci enable_MAC(ai, lock); 52818c2ecf20Sopenharmony_ci return rc; 52828c2ecf20Sopenharmony_ci} 52838c2ecf20Sopenharmony_ci 52848c2ecf20Sopenharmony_cistatic void proc_wepkey_on_close(struct inode *inode, struct file *file) 52858c2ecf20Sopenharmony_ci{ 52868c2ecf20Sopenharmony_ci struct proc_data *data; 52878c2ecf20Sopenharmony_ci struct net_device *dev = PDE_DATA(inode); 52888c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 52898c2ecf20Sopenharmony_ci int i, rc; 52908c2ecf20Sopenharmony_ci u8 key[16]; 52918c2ecf20Sopenharmony_ci u16 index = 0; 52928c2ecf20Sopenharmony_ci int j = 0; 52938c2ecf20Sopenharmony_ci 52948c2ecf20Sopenharmony_ci memset(key, 0, sizeof(key)); 52958c2ecf20Sopenharmony_ci 52968c2ecf20Sopenharmony_ci data = file->private_data; 52978c2ecf20Sopenharmony_ci if (!data->writelen) return; 52988c2ecf20Sopenharmony_ci 52998c2ecf20Sopenharmony_ci if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' && 53008c2ecf20Sopenharmony_ci (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) { 53018c2ecf20Sopenharmony_ci index = data->wbuffer[0] - '0'; 53028c2ecf20Sopenharmony_ci if (data->wbuffer[1] == '\n') { 53038c2ecf20Sopenharmony_ci rc = set_wep_tx_idx(ai, index, 1, 1); 53048c2ecf20Sopenharmony_ci if (rc < 0) { 53058c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "failed to set " 53068c2ecf20Sopenharmony_ci "WEP transmit index to %d: %d.", 53078c2ecf20Sopenharmony_ci index, rc); 53088c2ecf20Sopenharmony_ci } 53098c2ecf20Sopenharmony_ci return; 53108c2ecf20Sopenharmony_ci } 53118c2ecf20Sopenharmony_ci j = 2; 53128c2ecf20Sopenharmony_ci } else { 53138c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "WepKey passed invalid key index"); 53148c2ecf20Sopenharmony_ci return; 53158c2ecf20Sopenharmony_ci } 53168c2ecf20Sopenharmony_ci 53178c2ecf20Sopenharmony_ci for (i = 0; i < 16*3 && data->wbuffer[i+j]; i++) { 53188c2ecf20Sopenharmony_ci int val; 53198c2ecf20Sopenharmony_ci 53208c2ecf20Sopenharmony_ci if (i % 3 == 2) 53218c2ecf20Sopenharmony_ci continue; 53228c2ecf20Sopenharmony_ci 53238c2ecf20Sopenharmony_ci val = hex_to_bin(data->wbuffer[i+j]); 53248c2ecf20Sopenharmony_ci if (val < 0) { 53258c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "WebKey passed invalid key hex"); 53268c2ecf20Sopenharmony_ci return; 53278c2ecf20Sopenharmony_ci } 53288c2ecf20Sopenharmony_ci switch(i%3) { 53298c2ecf20Sopenharmony_ci case 0: 53308c2ecf20Sopenharmony_ci key[i/3] = (u8)val << 4; 53318c2ecf20Sopenharmony_ci break; 53328c2ecf20Sopenharmony_ci case 1: 53338c2ecf20Sopenharmony_ci key[i/3] |= (u8)val; 53348c2ecf20Sopenharmony_ci break; 53358c2ecf20Sopenharmony_ci } 53368c2ecf20Sopenharmony_ci } 53378c2ecf20Sopenharmony_ci 53388c2ecf20Sopenharmony_ci rc = set_wep_key(ai, index, key, i/3, 1, 1); 53398c2ecf20Sopenharmony_ci if (rc < 0) { 53408c2ecf20Sopenharmony_ci airo_print_err(ai->dev->name, "failed to set WEP key at index " 53418c2ecf20Sopenharmony_ci "%d: %d.", index, rc); 53428c2ecf20Sopenharmony_ci } 53438c2ecf20Sopenharmony_ci} 53448c2ecf20Sopenharmony_ci 53458c2ecf20Sopenharmony_cistatic int proc_wepkey_open(struct inode *inode, struct file *file) 53468c2ecf20Sopenharmony_ci{ 53478c2ecf20Sopenharmony_ci struct proc_data *data; 53488c2ecf20Sopenharmony_ci struct net_device *dev = PDE_DATA(inode); 53498c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 53508c2ecf20Sopenharmony_ci char *ptr; 53518c2ecf20Sopenharmony_ci WepKeyRid wkr; 53528c2ecf20Sopenharmony_ci __le16 lastindex; 53538c2ecf20Sopenharmony_ci int j = 0; 53548c2ecf20Sopenharmony_ci int rc; 53558c2ecf20Sopenharmony_ci 53568c2ecf20Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 53578c2ecf20Sopenharmony_ci return -ENOMEM; 53588c2ecf20Sopenharmony_ci memset(&wkr, 0, sizeof(wkr)); 53598c2ecf20Sopenharmony_ci data = file->private_data; 53608c2ecf20Sopenharmony_ci if ((data->rbuffer = kzalloc(180, GFP_KERNEL)) == NULL) { 53618c2ecf20Sopenharmony_ci kfree (file->private_data); 53628c2ecf20Sopenharmony_ci return -ENOMEM; 53638c2ecf20Sopenharmony_ci } 53648c2ecf20Sopenharmony_ci data->writelen = 0; 53658c2ecf20Sopenharmony_ci data->maxwritelen = 80; 53668c2ecf20Sopenharmony_ci if ((data->wbuffer = kzalloc(80, GFP_KERNEL)) == NULL) { 53678c2ecf20Sopenharmony_ci kfree (data->rbuffer); 53688c2ecf20Sopenharmony_ci kfree (file->private_data); 53698c2ecf20Sopenharmony_ci return -ENOMEM; 53708c2ecf20Sopenharmony_ci } 53718c2ecf20Sopenharmony_ci data->on_close = proc_wepkey_on_close; 53728c2ecf20Sopenharmony_ci 53738c2ecf20Sopenharmony_ci ptr = data->rbuffer; 53748c2ecf20Sopenharmony_ci strcpy(ptr, "No wep keys\n"); 53758c2ecf20Sopenharmony_ci rc = readWepKeyRid(ai, &wkr, 1, 1); 53768c2ecf20Sopenharmony_ci if (rc == SUCCESS) do { 53778c2ecf20Sopenharmony_ci lastindex = wkr.kindex; 53788c2ecf20Sopenharmony_ci if (wkr.kindex == cpu_to_le16(0xffff)) { 53798c2ecf20Sopenharmony_ci j += sprintf(ptr+j, "Tx key = %d\n", 53808c2ecf20Sopenharmony_ci (int)wkr.mac[0]); 53818c2ecf20Sopenharmony_ci } else { 53828c2ecf20Sopenharmony_ci j += sprintf(ptr+j, "Key %d set with length = %d\n", 53838c2ecf20Sopenharmony_ci le16_to_cpu(wkr.kindex), 53848c2ecf20Sopenharmony_ci le16_to_cpu(wkr.klen)); 53858c2ecf20Sopenharmony_ci } 53868c2ecf20Sopenharmony_ci readWepKeyRid(ai, &wkr, 0, 1); 53878c2ecf20Sopenharmony_ci } while ((lastindex != wkr.kindex) && (j < 180-30)); 53888c2ecf20Sopenharmony_ci 53898c2ecf20Sopenharmony_ci data->readlen = strlen(data->rbuffer); 53908c2ecf20Sopenharmony_ci return 0; 53918c2ecf20Sopenharmony_ci} 53928c2ecf20Sopenharmony_ci 53938c2ecf20Sopenharmony_cistatic int proc_SSID_open(struct inode *inode, struct file *file) 53948c2ecf20Sopenharmony_ci{ 53958c2ecf20Sopenharmony_ci struct proc_data *data; 53968c2ecf20Sopenharmony_ci struct net_device *dev = PDE_DATA(inode); 53978c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 53988c2ecf20Sopenharmony_ci int i; 53998c2ecf20Sopenharmony_ci char *ptr; 54008c2ecf20Sopenharmony_ci SsidRid SSID_rid; 54018c2ecf20Sopenharmony_ci 54028c2ecf20Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 54038c2ecf20Sopenharmony_ci return -ENOMEM; 54048c2ecf20Sopenharmony_ci data = file->private_data; 54058c2ecf20Sopenharmony_ci if ((data->rbuffer = kmalloc(104, GFP_KERNEL)) == NULL) { 54068c2ecf20Sopenharmony_ci kfree (file->private_data); 54078c2ecf20Sopenharmony_ci return -ENOMEM; 54088c2ecf20Sopenharmony_ci } 54098c2ecf20Sopenharmony_ci data->writelen = 0; 54108c2ecf20Sopenharmony_ci data->maxwritelen = 33*3; 54118c2ecf20Sopenharmony_ci /* allocate maxwritelen + 1; we'll want a sentinel */ 54128c2ecf20Sopenharmony_ci if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) { 54138c2ecf20Sopenharmony_ci kfree (data->rbuffer); 54148c2ecf20Sopenharmony_ci kfree (file->private_data); 54158c2ecf20Sopenharmony_ci return -ENOMEM; 54168c2ecf20Sopenharmony_ci } 54178c2ecf20Sopenharmony_ci data->on_close = proc_SSID_on_close; 54188c2ecf20Sopenharmony_ci 54198c2ecf20Sopenharmony_ci readSsidRid(ai, &SSID_rid); 54208c2ecf20Sopenharmony_ci ptr = data->rbuffer; 54218c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 54228c2ecf20Sopenharmony_ci int j; 54238c2ecf20Sopenharmony_ci size_t len = le16_to_cpu(SSID_rid.ssids[i].len); 54248c2ecf20Sopenharmony_ci if (!len) 54258c2ecf20Sopenharmony_ci break; 54268c2ecf20Sopenharmony_ci if (len > 32) 54278c2ecf20Sopenharmony_ci len = 32; 54288c2ecf20Sopenharmony_ci for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++) 54298c2ecf20Sopenharmony_ci *ptr++ = SSID_rid.ssids[i].ssid[j]; 54308c2ecf20Sopenharmony_ci *ptr++ = '\n'; 54318c2ecf20Sopenharmony_ci } 54328c2ecf20Sopenharmony_ci *ptr = '\0'; 54338c2ecf20Sopenharmony_ci data->readlen = strlen(data->rbuffer); 54348c2ecf20Sopenharmony_ci return 0; 54358c2ecf20Sopenharmony_ci} 54368c2ecf20Sopenharmony_ci 54378c2ecf20Sopenharmony_cistatic int proc_APList_open(struct inode *inode, struct file *file) 54388c2ecf20Sopenharmony_ci{ 54398c2ecf20Sopenharmony_ci struct proc_data *data; 54408c2ecf20Sopenharmony_ci struct net_device *dev = PDE_DATA(inode); 54418c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 54428c2ecf20Sopenharmony_ci int i; 54438c2ecf20Sopenharmony_ci char *ptr; 54448c2ecf20Sopenharmony_ci APListRid *APList_rid = &ai->APList; 54458c2ecf20Sopenharmony_ci 54468c2ecf20Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 54478c2ecf20Sopenharmony_ci return -ENOMEM; 54488c2ecf20Sopenharmony_ci data = file->private_data; 54498c2ecf20Sopenharmony_ci if ((data->rbuffer = kmalloc(104, GFP_KERNEL)) == NULL) { 54508c2ecf20Sopenharmony_ci kfree (file->private_data); 54518c2ecf20Sopenharmony_ci return -ENOMEM; 54528c2ecf20Sopenharmony_ci } 54538c2ecf20Sopenharmony_ci data->writelen = 0; 54548c2ecf20Sopenharmony_ci data->maxwritelen = 4*6*3; 54558c2ecf20Sopenharmony_ci if ((data->wbuffer = kzalloc(data->maxwritelen, GFP_KERNEL)) == NULL) { 54568c2ecf20Sopenharmony_ci kfree (data->rbuffer); 54578c2ecf20Sopenharmony_ci kfree (file->private_data); 54588c2ecf20Sopenharmony_ci return -ENOMEM; 54598c2ecf20Sopenharmony_ci } 54608c2ecf20Sopenharmony_ci data->on_close = proc_APList_on_close; 54618c2ecf20Sopenharmony_ci 54628c2ecf20Sopenharmony_ci ptr = data->rbuffer; 54638c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 54648c2ecf20Sopenharmony_ci// We end when we find a zero MAC 54658c2ecf20Sopenharmony_ci if (!*(int*)APList_rid->ap[i] && 54668c2ecf20Sopenharmony_ci !*(int*)&APList_rid->ap[i][2]) break; 54678c2ecf20Sopenharmony_ci ptr += sprintf(ptr, "%pM\n", APList_rid->ap[i]); 54688c2ecf20Sopenharmony_ci } 54698c2ecf20Sopenharmony_ci if (i==0) ptr += sprintf(ptr, "Not using specific APs\n"); 54708c2ecf20Sopenharmony_ci 54718c2ecf20Sopenharmony_ci *ptr = '\0'; 54728c2ecf20Sopenharmony_ci data->readlen = strlen(data->rbuffer); 54738c2ecf20Sopenharmony_ci return 0; 54748c2ecf20Sopenharmony_ci} 54758c2ecf20Sopenharmony_ci 54768c2ecf20Sopenharmony_cistatic int proc_BSSList_open(struct inode *inode, struct file *file) 54778c2ecf20Sopenharmony_ci{ 54788c2ecf20Sopenharmony_ci struct proc_data *data; 54798c2ecf20Sopenharmony_ci struct net_device *dev = PDE_DATA(inode); 54808c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 54818c2ecf20Sopenharmony_ci char *ptr; 54828c2ecf20Sopenharmony_ci BSSListRid BSSList_rid; 54838c2ecf20Sopenharmony_ci int rc; 54848c2ecf20Sopenharmony_ci /* If doLoseSync is not 1, we won't do a Lose Sync */ 54858c2ecf20Sopenharmony_ci int doLoseSync = -1; 54868c2ecf20Sopenharmony_ci 54878c2ecf20Sopenharmony_ci if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) 54888c2ecf20Sopenharmony_ci return -ENOMEM; 54898c2ecf20Sopenharmony_ci data = file->private_data; 54908c2ecf20Sopenharmony_ci if ((data->rbuffer = kmalloc(1024, GFP_KERNEL)) == NULL) { 54918c2ecf20Sopenharmony_ci kfree (file->private_data); 54928c2ecf20Sopenharmony_ci return -ENOMEM; 54938c2ecf20Sopenharmony_ci } 54948c2ecf20Sopenharmony_ci data->writelen = 0; 54958c2ecf20Sopenharmony_ci data->maxwritelen = 0; 54968c2ecf20Sopenharmony_ci data->wbuffer = NULL; 54978c2ecf20Sopenharmony_ci data->on_close = NULL; 54988c2ecf20Sopenharmony_ci 54998c2ecf20Sopenharmony_ci if (file->f_mode & FMODE_WRITE) { 55008c2ecf20Sopenharmony_ci if (!(file->f_mode & FMODE_READ)) { 55018c2ecf20Sopenharmony_ci Cmd cmd; 55028c2ecf20Sopenharmony_ci Resp rsp; 55038c2ecf20Sopenharmony_ci 55048c2ecf20Sopenharmony_ci if (ai->flags & FLAG_RADIO_MASK) { 55058c2ecf20Sopenharmony_ci kfree(data->rbuffer); 55068c2ecf20Sopenharmony_ci kfree(file->private_data); 55078c2ecf20Sopenharmony_ci return -ENETDOWN; 55088c2ecf20Sopenharmony_ci } 55098c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 55108c2ecf20Sopenharmony_ci cmd.cmd = CMD_LISTBSS; 55118c2ecf20Sopenharmony_ci if (down_interruptible(&ai->sem)) { 55128c2ecf20Sopenharmony_ci kfree(data->rbuffer); 55138c2ecf20Sopenharmony_ci kfree(file->private_data); 55148c2ecf20Sopenharmony_ci return -ERESTARTSYS; 55158c2ecf20Sopenharmony_ci } 55168c2ecf20Sopenharmony_ci issuecommand(ai, &cmd, &rsp); 55178c2ecf20Sopenharmony_ci up(&ai->sem); 55188c2ecf20Sopenharmony_ci data->readlen = 0; 55198c2ecf20Sopenharmony_ci return 0; 55208c2ecf20Sopenharmony_ci } 55218c2ecf20Sopenharmony_ci doLoseSync = 1; 55228c2ecf20Sopenharmony_ci } 55238c2ecf20Sopenharmony_ci ptr = data->rbuffer; 55248c2ecf20Sopenharmony_ci /* There is a race condition here if there are concurrent opens. 55258c2ecf20Sopenharmony_ci Since it is a rare condition, we'll just live with it, otherwise 55268c2ecf20Sopenharmony_ci we have to add a spin lock... */ 55278c2ecf20Sopenharmony_ci rc = readBSSListRid(ai, doLoseSync, &BSSList_rid); 55288c2ecf20Sopenharmony_ci while (rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) { 55298c2ecf20Sopenharmony_ci ptr += sprintf(ptr, "%pM %.*s rssi = %d", 55308c2ecf20Sopenharmony_ci BSSList_rid.bssid, 55318c2ecf20Sopenharmony_ci (int)BSSList_rid.ssidLen, 55328c2ecf20Sopenharmony_ci BSSList_rid.ssid, 55338c2ecf20Sopenharmony_ci le16_to_cpu(BSSList_rid.dBm)); 55348c2ecf20Sopenharmony_ci ptr += sprintf(ptr, " channel = %d %s %s %s %s\n", 55358c2ecf20Sopenharmony_ci le16_to_cpu(BSSList_rid.dsChannel), 55368c2ecf20Sopenharmony_ci BSSList_rid.cap & CAP_ESS ? "ESS" : "", 55378c2ecf20Sopenharmony_ci BSSList_rid.cap & CAP_IBSS ? "adhoc" : "", 55388c2ecf20Sopenharmony_ci BSSList_rid.cap & CAP_PRIVACY ? "wep" : "", 55398c2ecf20Sopenharmony_ci BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : ""); 55408c2ecf20Sopenharmony_ci rc = readBSSListRid(ai, 0, &BSSList_rid); 55418c2ecf20Sopenharmony_ci } 55428c2ecf20Sopenharmony_ci *ptr = '\0'; 55438c2ecf20Sopenharmony_ci data->readlen = strlen(data->rbuffer); 55448c2ecf20Sopenharmony_ci return 0; 55458c2ecf20Sopenharmony_ci} 55468c2ecf20Sopenharmony_ci 55478c2ecf20Sopenharmony_cistatic int proc_close(struct inode *inode, struct file *file) 55488c2ecf20Sopenharmony_ci{ 55498c2ecf20Sopenharmony_ci struct proc_data *data = file->private_data; 55508c2ecf20Sopenharmony_ci 55518c2ecf20Sopenharmony_ci if (data->on_close != NULL) 55528c2ecf20Sopenharmony_ci data->on_close(inode, file); 55538c2ecf20Sopenharmony_ci kfree(data->rbuffer); 55548c2ecf20Sopenharmony_ci kfree(data->wbuffer); 55558c2ecf20Sopenharmony_ci kfree(data); 55568c2ecf20Sopenharmony_ci return 0; 55578c2ecf20Sopenharmony_ci} 55588c2ecf20Sopenharmony_ci 55598c2ecf20Sopenharmony_ci/* Since the card doesn't automatically switch to the right WEP mode, 55608c2ecf20Sopenharmony_ci we will make it do it. If the card isn't associated, every secs we 55618c2ecf20Sopenharmony_ci will switch WEP modes to see if that will help. If the card is 55628c2ecf20Sopenharmony_ci associated we will check every minute to see if anything has 55638c2ecf20Sopenharmony_ci changed. */ 55648c2ecf20Sopenharmony_cistatic void timer_func(struct net_device *dev) 55658c2ecf20Sopenharmony_ci{ 55668c2ecf20Sopenharmony_ci struct airo_info *apriv = dev->ml_priv; 55678c2ecf20Sopenharmony_ci 55688c2ecf20Sopenharmony_ci/* We don't have a link so try changing the authtype */ 55698c2ecf20Sopenharmony_ci readConfigRid(apriv, 0); 55708c2ecf20Sopenharmony_ci disable_MAC(apriv, 0); 55718c2ecf20Sopenharmony_ci switch(apriv->config.authType) { 55728c2ecf20Sopenharmony_ci case AUTH_ENCRYPT: 55738c2ecf20Sopenharmony_ci/* So drop to OPEN */ 55748c2ecf20Sopenharmony_ci apriv->config.authType = AUTH_OPEN; 55758c2ecf20Sopenharmony_ci break; 55768c2ecf20Sopenharmony_ci case AUTH_SHAREDKEY: 55778c2ecf20Sopenharmony_ci if (apriv->keyindex < auto_wep) { 55788c2ecf20Sopenharmony_ci set_wep_tx_idx(apriv, apriv->keyindex, 0, 0); 55798c2ecf20Sopenharmony_ci apriv->config.authType = AUTH_SHAREDKEY; 55808c2ecf20Sopenharmony_ci apriv->keyindex++; 55818c2ecf20Sopenharmony_ci } else { 55828c2ecf20Sopenharmony_ci /* Drop to ENCRYPT */ 55838c2ecf20Sopenharmony_ci apriv->keyindex = 0; 55848c2ecf20Sopenharmony_ci set_wep_tx_idx(apriv, apriv->defindex, 0, 0); 55858c2ecf20Sopenharmony_ci apriv->config.authType = AUTH_ENCRYPT; 55868c2ecf20Sopenharmony_ci } 55878c2ecf20Sopenharmony_ci break; 55888c2ecf20Sopenharmony_ci default: /* We'll escalate to SHAREDKEY */ 55898c2ecf20Sopenharmony_ci apriv->config.authType = AUTH_SHAREDKEY; 55908c2ecf20Sopenharmony_ci } 55918c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &apriv->flags); 55928c2ecf20Sopenharmony_ci writeConfigRid(apriv, 0); 55938c2ecf20Sopenharmony_ci enable_MAC(apriv, 0); 55948c2ecf20Sopenharmony_ci up(&apriv->sem); 55958c2ecf20Sopenharmony_ci 55968c2ecf20Sopenharmony_ci/* Schedule check to see if the change worked */ 55978c2ecf20Sopenharmony_ci clear_bit(JOB_AUTOWEP, &apriv->jobs); 55988c2ecf20Sopenharmony_ci apriv->expires = RUN_AT(HZ*3); 55998c2ecf20Sopenharmony_ci} 56008c2ecf20Sopenharmony_ci 56018c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 56028c2ecf20Sopenharmony_cistatic int airo_pci_probe(struct pci_dev *pdev, 56038c2ecf20Sopenharmony_ci const struct pci_device_id *pent) 56048c2ecf20Sopenharmony_ci{ 56058c2ecf20Sopenharmony_ci struct net_device *dev; 56068c2ecf20Sopenharmony_ci 56078c2ecf20Sopenharmony_ci if (pci_enable_device(pdev)) 56088c2ecf20Sopenharmony_ci return -ENODEV; 56098c2ecf20Sopenharmony_ci pci_set_master(pdev); 56108c2ecf20Sopenharmony_ci 56118c2ecf20Sopenharmony_ci if (pdev->device == 0x5000 || pdev->device == 0xa504) 56128c2ecf20Sopenharmony_ci dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev); 56138c2ecf20Sopenharmony_ci else 56148c2ecf20Sopenharmony_ci dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev); 56158c2ecf20Sopenharmony_ci if (!dev) { 56168c2ecf20Sopenharmony_ci pci_disable_device(pdev); 56178c2ecf20Sopenharmony_ci return -ENODEV; 56188c2ecf20Sopenharmony_ci } 56198c2ecf20Sopenharmony_ci 56208c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 56218c2ecf20Sopenharmony_ci return 0; 56228c2ecf20Sopenharmony_ci} 56238c2ecf20Sopenharmony_ci 56248c2ecf20Sopenharmony_cistatic void airo_pci_remove(struct pci_dev *pdev) 56258c2ecf20Sopenharmony_ci{ 56268c2ecf20Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 56278c2ecf20Sopenharmony_ci 56288c2ecf20Sopenharmony_ci airo_print_info(dev->name, "Unregistering..."); 56298c2ecf20Sopenharmony_ci stop_airo_card(dev, 1); 56308c2ecf20Sopenharmony_ci pci_disable_device(pdev); 56318c2ecf20Sopenharmony_ci} 56328c2ecf20Sopenharmony_ci 56338c2ecf20Sopenharmony_cistatic int __maybe_unused airo_pci_suspend(struct device *dev_d) 56348c2ecf20Sopenharmony_ci{ 56358c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 56368c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 56378c2ecf20Sopenharmony_ci Cmd cmd; 56388c2ecf20Sopenharmony_ci Resp rsp; 56398c2ecf20Sopenharmony_ci 56408c2ecf20Sopenharmony_ci if (!ai->SSID) 56418c2ecf20Sopenharmony_ci ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL); 56428c2ecf20Sopenharmony_ci if (!ai->SSID) 56438c2ecf20Sopenharmony_ci return -ENOMEM; 56448c2ecf20Sopenharmony_ci readSsidRid(ai, ai->SSID); 56458c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 56468c2ecf20Sopenharmony_ci /* the lock will be released at the end of the resume callback */ 56478c2ecf20Sopenharmony_ci if (down_interruptible(&ai->sem)) 56488c2ecf20Sopenharmony_ci return -EAGAIN; 56498c2ecf20Sopenharmony_ci disable_MAC(ai, 0); 56508c2ecf20Sopenharmony_ci netif_device_detach(dev); 56518c2ecf20Sopenharmony_ci ai->power = PMSG_SUSPEND; 56528c2ecf20Sopenharmony_ci cmd.cmd = HOSTSLEEP; 56538c2ecf20Sopenharmony_ci issuecommand(ai, &cmd, &rsp); 56548c2ecf20Sopenharmony_ci 56558c2ecf20Sopenharmony_ci device_wakeup_enable(dev_d); 56568c2ecf20Sopenharmony_ci return 0; 56578c2ecf20Sopenharmony_ci} 56588c2ecf20Sopenharmony_ci 56598c2ecf20Sopenharmony_cistatic int __maybe_unused airo_pci_resume(struct device *dev_d) 56608c2ecf20Sopenharmony_ci{ 56618c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 56628c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 56638c2ecf20Sopenharmony_ci pci_power_t prev_state = to_pci_dev(dev_d)->current_state; 56648c2ecf20Sopenharmony_ci 56658c2ecf20Sopenharmony_ci device_wakeup_disable(dev_d); 56668c2ecf20Sopenharmony_ci 56678c2ecf20Sopenharmony_ci if (prev_state != PCI_D1) { 56688c2ecf20Sopenharmony_ci reset_card(dev, 0); 56698c2ecf20Sopenharmony_ci mpi_init_descriptors(ai); 56708c2ecf20Sopenharmony_ci setup_card(ai, dev->dev_addr, 0); 56718c2ecf20Sopenharmony_ci clear_bit(FLAG_RADIO_OFF, &ai->flags); 56728c2ecf20Sopenharmony_ci clear_bit(FLAG_PENDING_XMIT, &ai->flags); 56738c2ecf20Sopenharmony_ci } else { 56748c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_AWAKEN); 56758c2ecf20Sopenharmony_ci OUT4500(ai, EVACK, EV_AWAKEN); 56768c2ecf20Sopenharmony_ci msleep(100); 56778c2ecf20Sopenharmony_ci } 56788c2ecf20Sopenharmony_ci 56798c2ecf20Sopenharmony_ci set_bit(FLAG_COMMIT, &ai->flags); 56808c2ecf20Sopenharmony_ci disable_MAC(ai, 0); 56818c2ecf20Sopenharmony_ci msleep(200); 56828c2ecf20Sopenharmony_ci if (ai->SSID) { 56838c2ecf20Sopenharmony_ci writeSsidRid(ai, ai->SSID, 0); 56848c2ecf20Sopenharmony_ci kfree(ai->SSID); 56858c2ecf20Sopenharmony_ci ai->SSID = NULL; 56868c2ecf20Sopenharmony_ci } 56878c2ecf20Sopenharmony_ci writeAPListRid(ai, &ai->APList, 0); 56888c2ecf20Sopenharmony_ci writeConfigRid(ai, 0); 56898c2ecf20Sopenharmony_ci enable_MAC(ai, 0); 56908c2ecf20Sopenharmony_ci ai->power = PMSG_ON; 56918c2ecf20Sopenharmony_ci netif_device_attach(dev); 56928c2ecf20Sopenharmony_ci netif_wake_queue(dev); 56938c2ecf20Sopenharmony_ci enable_interrupts(ai); 56948c2ecf20Sopenharmony_ci up(&ai->sem); 56958c2ecf20Sopenharmony_ci return 0; 56968c2ecf20Sopenharmony_ci} 56978c2ecf20Sopenharmony_ci#endif 56988c2ecf20Sopenharmony_ci 56998c2ecf20Sopenharmony_cistatic int __init airo_init_module(void) 57008c2ecf20Sopenharmony_ci{ 57018c2ecf20Sopenharmony_ci int i; 57028c2ecf20Sopenharmony_ci 57038c2ecf20Sopenharmony_ci proc_kuid = make_kuid(&init_user_ns, proc_uid); 57048c2ecf20Sopenharmony_ci proc_kgid = make_kgid(&init_user_ns, proc_gid); 57058c2ecf20Sopenharmony_ci if (!uid_valid(proc_kuid) || !gid_valid(proc_kgid)) 57068c2ecf20Sopenharmony_ci return -EINVAL; 57078c2ecf20Sopenharmony_ci 57088c2ecf20Sopenharmony_ci airo_entry = proc_mkdir_mode("driver/aironet", airo_perm, NULL); 57098c2ecf20Sopenharmony_ci 57108c2ecf20Sopenharmony_ci if (airo_entry) 57118c2ecf20Sopenharmony_ci proc_set_user(airo_entry, proc_kuid, proc_kgid); 57128c2ecf20Sopenharmony_ci 57138c2ecf20Sopenharmony_ci for (i = 0; i < 4 && io[i] && irq[i]; i++) { 57148c2ecf20Sopenharmony_ci airo_print_info("", "Trying to configure ISA adapter at irq=%d " 57158c2ecf20Sopenharmony_ci "io = 0x%x", irq[i], io[i]); 57168c2ecf20Sopenharmony_ci if (init_airo_card(irq[i], io[i], 0, NULL)) { 57178c2ecf20Sopenharmony_ci /* do nothing */ ; 57188c2ecf20Sopenharmony_ci } 57198c2ecf20Sopenharmony_ci } 57208c2ecf20Sopenharmony_ci 57218c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 57228c2ecf20Sopenharmony_ci airo_print_info("", "Probing for PCI adapters"); 57238c2ecf20Sopenharmony_ci i = pci_register_driver(&airo_driver); 57248c2ecf20Sopenharmony_ci airo_print_info("", "Finished probing for PCI adapters"); 57258c2ecf20Sopenharmony_ci 57268c2ecf20Sopenharmony_ci if (i) { 57278c2ecf20Sopenharmony_ci remove_proc_entry("driver/aironet", NULL); 57288c2ecf20Sopenharmony_ci return i; 57298c2ecf20Sopenharmony_ci } 57308c2ecf20Sopenharmony_ci#endif 57318c2ecf20Sopenharmony_ci 57328c2ecf20Sopenharmony_ci /* Always exit with success, as we are a library module 57338c2ecf20Sopenharmony_ci * as well as a driver module 57348c2ecf20Sopenharmony_ci */ 57358c2ecf20Sopenharmony_ci return 0; 57368c2ecf20Sopenharmony_ci} 57378c2ecf20Sopenharmony_ci 57388c2ecf20Sopenharmony_cistatic void __exit airo_cleanup_module(void) 57398c2ecf20Sopenharmony_ci{ 57408c2ecf20Sopenharmony_ci struct airo_info *ai; 57418c2ecf20Sopenharmony_ci while (!list_empty(&airo_devices)) { 57428c2ecf20Sopenharmony_ci ai = list_entry(airo_devices.next, struct airo_info, dev_list); 57438c2ecf20Sopenharmony_ci airo_print_info(ai->dev->name, "Unregistering..."); 57448c2ecf20Sopenharmony_ci stop_airo_card(ai->dev, 1); 57458c2ecf20Sopenharmony_ci } 57468c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 57478c2ecf20Sopenharmony_ci pci_unregister_driver(&airo_driver); 57488c2ecf20Sopenharmony_ci#endif 57498c2ecf20Sopenharmony_ci remove_proc_entry("driver/aironet", NULL); 57508c2ecf20Sopenharmony_ci} 57518c2ecf20Sopenharmony_ci 57528c2ecf20Sopenharmony_ci/* 57538c2ecf20Sopenharmony_ci * Initial Wireless Extension code for Aironet driver by : 57548c2ecf20Sopenharmony_ci * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 17 November 00 57558c2ecf20Sopenharmony_ci * Conversion to new driver API by : 57568c2ecf20Sopenharmony_ci * Jean Tourrilhes <jt@hpl.hp.com> - HPL - 26 March 02 57578c2ecf20Sopenharmony_ci * Javier also did a good amount of work here, adding some new extensions 57588c2ecf20Sopenharmony_ci * and fixing my code. Let's just say that without him this code just 57598c2ecf20Sopenharmony_ci * would not work at all... - Jean II 57608c2ecf20Sopenharmony_ci */ 57618c2ecf20Sopenharmony_ci 57628c2ecf20Sopenharmony_cistatic u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi) 57638c2ecf20Sopenharmony_ci{ 57648c2ecf20Sopenharmony_ci if (!rssi_rid) 57658c2ecf20Sopenharmony_ci return 0; 57668c2ecf20Sopenharmony_ci 57678c2ecf20Sopenharmony_ci return (0x100 - rssi_rid[rssi].rssidBm); 57688c2ecf20Sopenharmony_ci} 57698c2ecf20Sopenharmony_ci 57708c2ecf20Sopenharmony_cistatic u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm) 57718c2ecf20Sopenharmony_ci{ 57728c2ecf20Sopenharmony_ci int i; 57738c2ecf20Sopenharmony_ci 57748c2ecf20Sopenharmony_ci if (!rssi_rid) 57758c2ecf20Sopenharmony_ci return 0; 57768c2ecf20Sopenharmony_ci 57778c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) 57788c2ecf20Sopenharmony_ci if (rssi_rid[i].rssidBm == dbm) 57798c2ecf20Sopenharmony_ci return rssi_rid[i].rssipct; 57808c2ecf20Sopenharmony_ci 57818c2ecf20Sopenharmony_ci return 0; 57828c2ecf20Sopenharmony_ci} 57838c2ecf20Sopenharmony_ci 57848c2ecf20Sopenharmony_ci 57858c2ecf20Sopenharmony_cistatic int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid) 57868c2ecf20Sopenharmony_ci{ 57878c2ecf20Sopenharmony_ci int quality = 0; 57888c2ecf20Sopenharmony_ci u16 sq; 57898c2ecf20Sopenharmony_ci 57908c2ecf20Sopenharmony_ci if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f)) 57918c2ecf20Sopenharmony_ci return 0; 57928c2ecf20Sopenharmony_ci 57938c2ecf20Sopenharmony_ci if (!(cap_rid->hardCap & cpu_to_le16(8))) 57948c2ecf20Sopenharmony_ci return 0; 57958c2ecf20Sopenharmony_ci 57968c2ecf20Sopenharmony_ci sq = le16_to_cpu(status_rid->signalQuality); 57978c2ecf20Sopenharmony_ci if (memcmp(cap_rid->prodName, "350", 3)) 57988c2ecf20Sopenharmony_ci if (sq > 0x20) 57998c2ecf20Sopenharmony_ci quality = 0; 58008c2ecf20Sopenharmony_ci else 58018c2ecf20Sopenharmony_ci quality = 0x20 - sq; 58028c2ecf20Sopenharmony_ci else 58038c2ecf20Sopenharmony_ci if (sq > 0xb0) 58048c2ecf20Sopenharmony_ci quality = 0; 58058c2ecf20Sopenharmony_ci else if (sq < 0x10) 58068c2ecf20Sopenharmony_ci quality = 0xa0; 58078c2ecf20Sopenharmony_ci else 58088c2ecf20Sopenharmony_ci quality = 0xb0 - sq; 58098c2ecf20Sopenharmony_ci return quality; 58108c2ecf20Sopenharmony_ci} 58118c2ecf20Sopenharmony_ci 58128c2ecf20Sopenharmony_ci#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0) 58138c2ecf20Sopenharmony_ci#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50); 58148c2ecf20Sopenharmony_ci 58158c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 58168c2ecf20Sopenharmony_ci/* 58178c2ecf20Sopenharmony_ci * Wireless Handler : get protocol name 58188c2ecf20Sopenharmony_ci */ 58198c2ecf20Sopenharmony_cistatic int airo_get_name(struct net_device *dev, 58208c2ecf20Sopenharmony_ci struct iw_request_info *info, 58218c2ecf20Sopenharmony_ci char *cwrq, 58228c2ecf20Sopenharmony_ci char *extra) 58238c2ecf20Sopenharmony_ci{ 58248c2ecf20Sopenharmony_ci strcpy(cwrq, "IEEE 802.11-DS"); 58258c2ecf20Sopenharmony_ci return 0; 58268c2ecf20Sopenharmony_ci} 58278c2ecf20Sopenharmony_ci 58288c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 58298c2ecf20Sopenharmony_ci/* 58308c2ecf20Sopenharmony_ci * Wireless Handler : set frequency 58318c2ecf20Sopenharmony_ci */ 58328c2ecf20Sopenharmony_cistatic int airo_set_freq(struct net_device *dev, 58338c2ecf20Sopenharmony_ci struct iw_request_info *info, 58348c2ecf20Sopenharmony_ci struct iw_freq *fwrq, 58358c2ecf20Sopenharmony_ci char *extra) 58368c2ecf20Sopenharmony_ci{ 58378c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 58388c2ecf20Sopenharmony_ci int rc = -EINPROGRESS; /* Call commit handler */ 58398c2ecf20Sopenharmony_ci 58408c2ecf20Sopenharmony_ci /* If setting by frequency, convert to a channel */ 58418c2ecf20Sopenharmony_ci if (fwrq->e == 1) { 58428c2ecf20Sopenharmony_ci int f = fwrq->m / 100000; 58438c2ecf20Sopenharmony_ci 58448c2ecf20Sopenharmony_ci /* Hack to fall through... */ 58458c2ecf20Sopenharmony_ci fwrq->e = 0; 58468c2ecf20Sopenharmony_ci fwrq->m = ieee80211_frequency_to_channel(f); 58478c2ecf20Sopenharmony_ci } 58488c2ecf20Sopenharmony_ci /* Setting by channel number */ 58498c2ecf20Sopenharmony_ci if (fwrq->m < 0 || fwrq->m > 1000 || fwrq->e > 0) 58508c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 58518c2ecf20Sopenharmony_ci else { 58528c2ecf20Sopenharmony_ci int channel = fwrq->m; 58538c2ecf20Sopenharmony_ci /* We should do a better check than that, 58548c2ecf20Sopenharmony_ci * based on the card capability !!! */ 58558c2ecf20Sopenharmony_ci if ((channel < 1) || (channel > 14)) { 58568c2ecf20Sopenharmony_ci airo_print_dbg(dev->name, "New channel value of %d is invalid!", 58578c2ecf20Sopenharmony_ci fwrq->m); 58588c2ecf20Sopenharmony_ci rc = -EINVAL; 58598c2ecf20Sopenharmony_ci } else { 58608c2ecf20Sopenharmony_ci readConfigRid(local, 1); 58618c2ecf20Sopenharmony_ci /* Yes ! We can set it !!! */ 58628c2ecf20Sopenharmony_ci local->config.channelSet = cpu_to_le16(channel); 58638c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 58648c2ecf20Sopenharmony_ci } 58658c2ecf20Sopenharmony_ci } 58668c2ecf20Sopenharmony_ci return rc; 58678c2ecf20Sopenharmony_ci} 58688c2ecf20Sopenharmony_ci 58698c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 58708c2ecf20Sopenharmony_ci/* 58718c2ecf20Sopenharmony_ci * Wireless Handler : get frequency 58728c2ecf20Sopenharmony_ci */ 58738c2ecf20Sopenharmony_cistatic int airo_get_freq(struct net_device *dev, 58748c2ecf20Sopenharmony_ci struct iw_request_info *info, 58758c2ecf20Sopenharmony_ci struct iw_freq *fwrq, 58768c2ecf20Sopenharmony_ci char *extra) 58778c2ecf20Sopenharmony_ci{ 58788c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 58798c2ecf20Sopenharmony_ci StatusRid status_rid; /* Card status info */ 58808c2ecf20Sopenharmony_ci int ch; 58818c2ecf20Sopenharmony_ci 58828c2ecf20Sopenharmony_ci readConfigRid(local, 1); 58838c2ecf20Sopenharmony_ci if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS) 58848c2ecf20Sopenharmony_ci status_rid.channel = local->config.channelSet; 58858c2ecf20Sopenharmony_ci else 58868c2ecf20Sopenharmony_ci readStatusRid(local, &status_rid, 1); 58878c2ecf20Sopenharmony_ci 58888c2ecf20Sopenharmony_ci ch = le16_to_cpu(status_rid.channel); 58898c2ecf20Sopenharmony_ci if ((ch > 0) && (ch < 15)) { 58908c2ecf20Sopenharmony_ci fwrq->m = 100000 * 58918c2ecf20Sopenharmony_ci ieee80211_channel_to_frequency(ch, NL80211_BAND_2GHZ); 58928c2ecf20Sopenharmony_ci fwrq->e = 1; 58938c2ecf20Sopenharmony_ci } else { 58948c2ecf20Sopenharmony_ci fwrq->m = ch; 58958c2ecf20Sopenharmony_ci fwrq->e = 0; 58968c2ecf20Sopenharmony_ci } 58978c2ecf20Sopenharmony_ci 58988c2ecf20Sopenharmony_ci return 0; 58998c2ecf20Sopenharmony_ci} 59008c2ecf20Sopenharmony_ci 59018c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 59028c2ecf20Sopenharmony_ci/* 59038c2ecf20Sopenharmony_ci * Wireless Handler : set ESSID 59048c2ecf20Sopenharmony_ci */ 59058c2ecf20Sopenharmony_cistatic int airo_set_essid(struct net_device *dev, 59068c2ecf20Sopenharmony_ci struct iw_request_info *info, 59078c2ecf20Sopenharmony_ci struct iw_point *dwrq, 59088c2ecf20Sopenharmony_ci char *extra) 59098c2ecf20Sopenharmony_ci{ 59108c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 59118c2ecf20Sopenharmony_ci SsidRid SSID_rid; /* SSIDs */ 59128c2ecf20Sopenharmony_ci 59138c2ecf20Sopenharmony_ci /* Reload the list of current SSID */ 59148c2ecf20Sopenharmony_ci readSsidRid(local, &SSID_rid); 59158c2ecf20Sopenharmony_ci 59168c2ecf20Sopenharmony_ci /* Check if we asked for `any' */ 59178c2ecf20Sopenharmony_ci if (dwrq->flags == 0) { 59188c2ecf20Sopenharmony_ci /* Just send an empty SSID list */ 59198c2ecf20Sopenharmony_ci memset(&SSID_rid, 0, sizeof(SSID_rid)); 59208c2ecf20Sopenharmony_ci } else { 59218c2ecf20Sopenharmony_ci unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 59228c2ecf20Sopenharmony_ci 59238c2ecf20Sopenharmony_ci /* Check the size of the string */ 59248c2ecf20Sopenharmony_ci if (dwrq->length > IW_ESSID_MAX_SIZE) 59258c2ecf20Sopenharmony_ci return -E2BIG ; 59268c2ecf20Sopenharmony_ci 59278c2ecf20Sopenharmony_ci /* Check if index is valid */ 59288c2ecf20Sopenharmony_ci if (index >= ARRAY_SIZE(SSID_rid.ssids)) 59298c2ecf20Sopenharmony_ci return -EINVAL; 59308c2ecf20Sopenharmony_ci 59318c2ecf20Sopenharmony_ci /* Set the SSID */ 59328c2ecf20Sopenharmony_ci memset(SSID_rid.ssids[index].ssid, 0, 59338c2ecf20Sopenharmony_ci sizeof(SSID_rid.ssids[index].ssid)); 59348c2ecf20Sopenharmony_ci memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length); 59358c2ecf20Sopenharmony_ci SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length); 59368c2ecf20Sopenharmony_ci } 59378c2ecf20Sopenharmony_ci SSID_rid.len = cpu_to_le16(sizeof(SSID_rid)); 59388c2ecf20Sopenharmony_ci /* Write it to the card */ 59398c2ecf20Sopenharmony_ci disable_MAC(local, 1); 59408c2ecf20Sopenharmony_ci writeSsidRid(local, &SSID_rid, 1); 59418c2ecf20Sopenharmony_ci enable_MAC(local, 1); 59428c2ecf20Sopenharmony_ci 59438c2ecf20Sopenharmony_ci return 0; 59448c2ecf20Sopenharmony_ci} 59458c2ecf20Sopenharmony_ci 59468c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 59478c2ecf20Sopenharmony_ci/* 59488c2ecf20Sopenharmony_ci * Wireless Handler : get ESSID 59498c2ecf20Sopenharmony_ci */ 59508c2ecf20Sopenharmony_cistatic int airo_get_essid(struct net_device *dev, 59518c2ecf20Sopenharmony_ci struct iw_request_info *info, 59528c2ecf20Sopenharmony_ci struct iw_point *dwrq, 59538c2ecf20Sopenharmony_ci char *extra) 59548c2ecf20Sopenharmony_ci{ 59558c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 59568c2ecf20Sopenharmony_ci StatusRid status_rid; /* Card status info */ 59578c2ecf20Sopenharmony_ci 59588c2ecf20Sopenharmony_ci readStatusRid(local, &status_rid, 1); 59598c2ecf20Sopenharmony_ci 59608c2ecf20Sopenharmony_ci /* Note : if dwrq->flags != 0, we should 59618c2ecf20Sopenharmony_ci * get the relevant SSID from the SSID list... */ 59628c2ecf20Sopenharmony_ci 59638c2ecf20Sopenharmony_ci /* Get the current SSID */ 59648c2ecf20Sopenharmony_ci memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen)); 59658c2ecf20Sopenharmony_ci /* If none, we may want to get the one that was set */ 59668c2ecf20Sopenharmony_ci 59678c2ecf20Sopenharmony_ci /* Push it out ! */ 59688c2ecf20Sopenharmony_ci dwrq->length = le16_to_cpu(status_rid.SSIDlen); 59698c2ecf20Sopenharmony_ci dwrq->flags = 1; /* active */ 59708c2ecf20Sopenharmony_ci 59718c2ecf20Sopenharmony_ci return 0; 59728c2ecf20Sopenharmony_ci} 59738c2ecf20Sopenharmony_ci 59748c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 59758c2ecf20Sopenharmony_ci/* 59768c2ecf20Sopenharmony_ci * Wireless Handler : set AP address 59778c2ecf20Sopenharmony_ci */ 59788c2ecf20Sopenharmony_cistatic int airo_set_wap(struct net_device *dev, 59798c2ecf20Sopenharmony_ci struct iw_request_info *info, 59808c2ecf20Sopenharmony_ci struct sockaddr *awrq, 59818c2ecf20Sopenharmony_ci char *extra) 59828c2ecf20Sopenharmony_ci{ 59838c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 59848c2ecf20Sopenharmony_ci Cmd cmd; 59858c2ecf20Sopenharmony_ci Resp rsp; 59868c2ecf20Sopenharmony_ci APListRid *APList_rid = &local->APList; 59878c2ecf20Sopenharmony_ci 59888c2ecf20Sopenharmony_ci if (awrq->sa_family != ARPHRD_ETHER) 59898c2ecf20Sopenharmony_ci return -EINVAL; 59908c2ecf20Sopenharmony_ci else if (is_broadcast_ether_addr(awrq->sa_data) || 59918c2ecf20Sopenharmony_ci is_zero_ether_addr(awrq->sa_data)) { 59928c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 59938c2ecf20Sopenharmony_ci cmd.cmd = CMD_LOSE_SYNC; 59948c2ecf20Sopenharmony_ci if (down_interruptible(&local->sem)) 59958c2ecf20Sopenharmony_ci return -ERESTARTSYS; 59968c2ecf20Sopenharmony_ci issuecommand(local, &cmd, &rsp); 59978c2ecf20Sopenharmony_ci up(&local->sem); 59988c2ecf20Sopenharmony_ci } else { 59998c2ecf20Sopenharmony_ci memset(APList_rid, 0, sizeof(*APList_rid)); 60008c2ecf20Sopenharmony_ci APList_rid->len = cpu_to_le16(sizeof(*APList_rid)); 60018c2ecf20Sopenharmony_ci memcpy(APList_rid->ap[0], awrq->sa_data, ETH_ALEN); 60028c2ecf20Sopenharmony_ci disable_MAC(local, 1); 60038c2ecf20Sopenharmony_ci writeAPListRid(local, APList_rid, 1); 60048c2ecf20Sopenharmony_ci enable_MAC(local, 1); 60058c2ecf20Sopenharmony_ci } 60068c2ecf20Sopenharmony_ci return 0; 60078c2ecf20Sopenharmony_ci} 60088c2ecf20Sopenharmony_ci 60098c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 60108c2ecf20Sopenharmony_ci/* 60118c2ecf20Sopenharmony_ci * Wireless Handler : get AP address 60128c2ecf20Sopenharmony_ci */ 60138c2ecf20Sopenharmony_cistatic int airo_get_wap(struct net_device *dev, 60148c2ecf20Sopenharmony_ci struct iw_request_info *info, 60158c2ecf20Sopenharmony_ci struct sockaddr *awrq, 60168c2ecf20Sopenharmony_ci char *extra) 60178c2ecf20Sopenharmony_ci{ 60188c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 60198c2ecf20Sopenharmony_ci StatusRid status_rid; /* Card status info */ 60208c2ecf20Sopenharmony_ci 60218c2ecf20Sopenharmony_ci readStatusRid(local, &status_rid, 1); 60228c2ecf20Sopenharmony_ci 60238c2ecf20Sopenharmony_ci /* Tentative. This seems to work, wow, I'm lucky !!! */ 60248c2ecf20Sopenharmony_ci memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN); 60258c2ecf20Sopenharmony_ci awrq->sa_family = ARPHRD_ETHER; 60268c2ecf20Sopenharmony_ci 60278c2ecf20Sopenharmony_ci return 0; 60288c2ecf20Sopenharmony_ci} 60298c2ecf20Sopenharmony_ci 60308c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 60318c2ecf20Sopenharmony_ci/* 60328c2ecf20Sopenharmony_ci * Wireless Handler : set Nickname 60338c2ecf20Sopenharmony_ci */ 60348c2ecf20Sopenharmony_cistatic int airo_set_nick(struct net_device *dev, 60358c2ecf20Sopenharmony_ci struct iw_request_info *info, 60368c2ecf20Sopenharmony_ci struct iw_point *dwrq, 60378c2ecf20Sopenharmony_ci char *extra) 60388c2ecf20Sopenharmony_ci{ 60398c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 60408c2ecf20Sopenharmony_ci 60418c2ecf20Sopenharmony_ci /* Check the size of the string */ 60428c2ecf20Sopenharmony_ci if (dwrq->length > 16) { 60438c2ecf20Sopenharmony_ci return -E2BIG; 60448c2ecf20Sopenharmony_ci } 60458c2ecf20Sopenharmony_ci readConfigRid(local, 1); 60468c2ecf20Sopenharmony_ci memset(local->config.nodeName, 0, sizeof(local->config.nodeName)); 60478c2ecf20Sopenharmony_ci memcpy(local->config.nodeName, extra, dwrq->length); 60488c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 60498c2ecf20Sopenharmony_ci 60508c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 60518c2ecf20Sopenharmony_ci} 60528c2ecf20Sopenharmony_ci 60538c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 60548c2ecf20Sopenharmony_ci/* 60558c2ecf20Sopenharmony_ci * Wireless Handler : get Nickname 60568c2ecf20Sopenharmony_ci */ 60578c2ecf20Sopenharmony_cistatic int airo_get_nick(struct net_device *dev, 60588c2ecf20Sopenharmony_ci struct iw_request_info *info, 60598c2ecf20Sopenharmony_ci struct iw_point *dwrq, 60608c2ecf20Sopenharmony_ci char *extra) 60618c2ecf20Sopenharmony_ci{ 60628c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 60638c2ecf20Sopenharmony_ci 60648c2ecf20Sopenharmony_ci readConfigRid(local, 1); 60658c2ecf20Sopenharmony_ci strncpy(extra, local->config.nodeName, 16); 60668c2ecf20Sopenharmony_ci extra[16] = '\0'; 60678c2ecf20Sopenharmony_ci dwrq->length = strlen(extra); 60688c2ecf20Sopenharmony_ci 60698c2ecf20Sopenharmony_ci return 0; 60708c2ecf20Sopenharmony_ci} 60718c2ecf20Sopenharmony_ci 60728c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 60738c2ecf20Sopenharmony_ci/* 60748c2ecf20Sopenharmony_ci * Wireless Handler : set Bit-Rate 60758c2ecf20Sopenharmony_ci */ 60768c2ecf20Sopenharmony_cistatic int airo_set_rate(struct net_device *dev, 60778c2ecf20Sopenharmony_ci struct iw_request_info *info, 60788c2ecf20Sopenharmony_ci struct iw_param *vwrq, 60798c2ecf20Sopenharmony_ci char *extra) 60808c2ecf20Sopenharmony_ci{ 60818c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 60828c2ecf20Sopenharmony_ci CapabilityRid cap_rid; /* Card capability info */ 60838c2ecf20Sopenharmony_ci u8 brate = 0; 60848c2ecf20Sopenharmony_ci int i; 60858c2ecf20Sopenharmony_ci 60868c2ecf20Sopenharmony_ci /* First : get a valid bit rate value */ 60878c2ecf20Sopenharmony_ci readCapabilityRid(local, &cap_rid, 1); 60888c2ecf20Sopenharmony_ci 60898c2ecf20Sopenharmony_ci /* Which type of value ? */ 60908c2ecf20Sopenharmony_ci if ((vwrq->value < 8) && (vwrq->value >= 0)) { 60918c2ecf20Sopenharmony_ci /* Setting by rate index */ 60928c2ecf20Sopenharmony_ci /* Find value in the magic rate table */ 60938c2ecf20Sopenharmony_ci brate = cap_rid.supportedRates[vwrq->value]; 60948c2ecf20Sopenharmony_ci } else { 60958c2ecf20Sopenharmony_ci /* Setting by frequency value */ 60968c2ecf20Sopenharmony_ci u8 normvalue = (u8) (vwrq->value/500000); 60978c2ecf20Sopenharmony_ci 60988c2ecf20Sopenharmony_ci /* Check if rate is valid */ 60998c2ecf20Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 61008c2ecf20Sopenharmony_ci if (normvalue == cap_rid.supportedRates[i]) { 61018c2ecf20Sopenharmony_ci brate = normvalue; 61028c2ecf20Sopenharmony_ci break; 61038c2ecf20Sopenharmony_ci } 61048c2ecf20Sopenharmony_ci } 61058c2ecf20Sopenharmony_ci } 61068c2ecf20Sopenharmony_ci /* -1 designed the max rate (mostly auto mode) */ 61078c2ecf20Sopenharmony_ci if (vwrq->value == -1) { 61088c2ecf20Sopenharmony_ci /* Get the highest available rate */ 61098c2ecf20Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 61108c2ecf20Sopenharmony_ci if (cap_rid.supportedRates[i] == 0) 61118c2ecf20Sopenharmony_ci break; 61128c2ecf20Sopenharmony_ci } 61138c2ecf20Sopenharmony_ci if (i != 0) 61148c2ecf20Sopenharmony_ci brate = cap_rid.supportedRates[i - 1]; 61158c2ecf20Sopenharmony_ci } 61168c2ecf20Sopenharmony_ci /* Check that it is valid */ 61178c2ecf20Sopenharmony_ci if (brate == 0) { 61188c2ecf20Sopenharmony_ci return -EINVAL; 61198c2ecf20Sopenharmony_ci } 61208c2ecf20Sopenharmony_ci 61218c2ecf20Sopenharmony_ci readConfigRid(local, 1); 61228c2ecf20Sopenharmony_ci /* Now, check if we want a fixed or auto value */ 61238c2ecf20Sopenharmony_ci if (vwrq->fixed == 0) { 61248c2ecf20Sopenharmony_ci /* Fill all the rates up to this max rate */ 61258c2ecf20Sopenharmony_ci memset(local->config.rates, 0, 8); 61268c2ecf20Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 61278c2ecf20Sopenharmony_ci local->config.rates[i] = cap_rid.supportedRates[i]; 61288c2ecf20Sopenharmony_ci if (local->config.rates[i] == brate) 61298c2ecf20Sopenharmony_ci break; 61308c2ecf20Sopenharmony_ci } 61318c2ecf20Sopenharmony_ci } else { 61328c2ecf20Sopenharmony_ci /* Fixed mode */ 61338c2ecf20Sopenharmony_ci /* One rate, fixed */ 61348c2ecf20Sopenharmony_ci memset(local->config.rates, 0, 8); 61358c2ecf20Sopenharmony_ci local->config.rates[0] = brate; 61368c2ecf20Sopenharmony_ci } 61378c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 61388c2ecf20Sopenharmony_ci 61398c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 61408c2ecf20Sopenharmony_ci} 61418c2ecf20Sopenharmony_ci 61428c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 61438c2ecf20Sopenharmony_ci/* 61448c2ecf20Sopenharmony_ci * Wireless Handler : get Bit-Rate 61458c2ecf20Sopenharmony_ci */ 61468c2ecf20Sopenharmony_cistatic int airo_get_rate(struct net_device *dev, 61478c2ecf20Sopenharmony_ci struct iw_request_info *info, 61488c2ecf20Sopenharmony_ci struct iw_param *vwrq, 61498c2ecf20Sopenharmony_ci char *extra) 61508c2ecf20Sopenharmony_ci{ 61518c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 61528c2ecf20Sopenharmony_ci StatusRid status_rid; /* Card status info */ 61538c2ecf20Sopenharmony_ci int ret; 61548c2ecf20Sopenharmony_ci 61558c2ecf20Sopenharmony_ci ret = readStatusRid(local, &status_rid, 1); 61568c2ecf20Sopenharmony_ci if (ret) 61578c2ecf20Sopenharmony_ci return -EBUSY; 61588c2ecf20Sopenharmony_ci 61598c2ecf20Sopenharmony_ci vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000; 61608c2ecf20Sopenharmony_ci /* If more than one rate, set auto */ 61618c2ecf20Sopenharmony_ci readConfigRid(local, 1); 61628c2ecf20Sopenharmony_ci vwrq->fixed = (local->config.rates[1] == 0); 61638c2ecf20Sopenharmony_ci 61648c2ecf20Sopenharmony_ci return 0; 61658c2ecf20Sopenharmony_ci} 61668c2ecf20Sopenharmony_ci 61678c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 61688c2ecf20Sopenharmony_ci/* 61698c2ecf20Sopenharmony_ci * Wireless Handler : set RTS threshold 61708c2ecf20Sopenharmony_ci */ 61718c2ecf20Sopenharmony_cistatic int airo_set_rts(struct net_device *dev, 61728c2ecf20Sopenharmony_ci struct iw_request_info *info, 61738c2ecf20Sopenharmony_ci struct iw_param *vwrq, 61748c2ecf20Sopenharmony_ci char *extra) 61758c2ecf20Sopenharmony_ci{ 61768c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 61778c2ecf20Sopenharmony_ci int rthr = vwrq->value; 61788c2ecf20Sopenharmony_ci 61798c2ecf20Sopenharmony_ci if (vwrq->disabled) 61808c2ecf20Sopenharmony_ci rthr = AIRO_DEF_MTU; 61818c2ecf20Sopenharmony_ci if ((rthr < 0) || (rthr > AIRO_DEF_MTU)) { 61828c2ecf20Sopenharmony_ci return -EINVAL; 61838c2ecf20Sopenharmony_ci } 61848c2ecf20Sopenharmony_ci readConfigRid(local, 1); 61858c2ecf20Sopenharmony_ci local->config.rtsThres = cpu_to_le16(rthr); 61868c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 61878c2ecf20Sopenharmony_ci 61888c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 61898c2ecf20Sopenharmony_ci} 61908c2ecf20Sopenharmony_ci 61918c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 61928c2ecf20Sopenharmony_ci/* 61938c2ecf20Sopenharmony_ci * Wireless Handler : get RTS threshold 61948c2ecf20Sopenharmony_ci */ 61958c2ecf20Sopenharmony_cistatic int airo_get_rts(struct net_device *dev, 61968c2ecf20Sopenharmony_ci struct iw_request_info *info, 61978c2ecf20Sopenharmony_ci struct iw_param *vwrq, 61988c2ecf20Sopenharmony_ci char *extra) 61998c2ecf20Sopenharmony_ci{ 62008c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 62018c2ecf20Sopenharmony_ci 62028c2ecf20Sopenharmony_ci readConfigRid(local, 1); 62038c2ecf20Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.rtsThres); 62048c2ecf20Sopenharmony_ci vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU); 62058c2ecf20Sopenharmony_ci vwrq->fixed = 1; 62068c2ecf20Sopenharmony_ci 62078c2ecf20Sopenharmony_ci return 0; 62088c2ecf20Sopenharmony_ci} 62098c2ecf20Sopenharmony_ci 62108c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 62118c2ecf20Sopenharmony_ci/* 62128c2ecf20Sopenharmony_ci * Wireless Handler : set Fragmentation threshold 62138c2ecf20Sopenharmony_ci */ 62148c2ecf20Sopenharmony_cistatic int airo_set_frag(struct net_device *dev, 62158c2ecf20Sopenharmony_ci struct iw_request_info *info, 62168c2ecf20Sopenharmony_ci struct iw_param *vwrq, 62178c2ecf20Sopenharmony_ci char *extra) 62188c2ecf20Sopenharmony_ci{ 62198c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 62208c2ecf20Sopenharmony_ci int fthr = vwrq->value; 62218c2ecf20Sopenharmony_ci 62228c2ecf20Sopenharmony_ci if (vwrq->disabled) 62238c2ecf20Sopenharmony_ci fthr = AIRO_DEF_MTU; 62248c2ecf20Sopenharmony_ci if ((fthr < 256) || (fthr > AIRO_DEF_MTU)) { 62258c2ecf20Sopenharmony_ci return -EINVAL; 62268c2ecf20Sopenharmony_ci } 62278c2ecf20Sopenharmony_ci fthr &= ~0x1; /* Get an even value - is it really needed ??? */ 62288c2ecf20Sopenharmony_ci readConfigRid(local, 1); 62298c2ecf20Sopenharmony_ci local->config.fragThresh = cpu_to_le16(fthr); 62308c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 62318c2ecf20Sopenharmony_ci 62328c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 62338c2ecf20Sopenharmony_ci} 62348c2ecf20Sopenharmony_ci 62358c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 62368c2ecf20Sopenharmony_ci/* 62378c2ecf20Sopenharmony_ci * Wireless Handler : get Fragmentation threshold 62388c2ecf20Sopenharmony_ci */ 62398c2ecf20Sopenharmony_cistatic int airo_get_frag(struct net_device *dev, 62408c2ecf20Sopenharmony_ci struct iw_request_info *info, 62418c2ecf20Sopenharmony_ci struct iw_param *vwrq, 62428c2ecf20Sopenharmony_ci char *extra) 62438c2ecf20Sopenharmony_ci{ 62448c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 62458c2ecf20Sopenharmony_ci 62468c2ecf20Sopenharmony_ci readConfigRid(local, 1); 62478c2ecf20Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.fragThresh); 62488c2ecf20Sopenharmony_ci vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU); 62498c2ecf20Sopenharmony_ci vwrq->fixed = 1; 62508c2ecf20Sopenharmony_ci 62518c2ecf20Sopenharmony_ci return 0; 62528c2ecf20Sopenharmony_ci} 62538c2ecf20Sopenharmony_ci 62548c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 62558c2ecf20Sopenharmony_ci/* 62568c2ecf20Sopenharmony_ci * Wireless Handler : set Mode of Operation 62578c2ecf20Sopenharmony_ci */ 62588c2ecf20Sopenharmony_cistatic int airo_set_mode(struct net_device *dev, 62598c2ecf20Sopenharmony_ci struct iw_request_info *info, 62608c2ecf20Sopenharmony_ci __u32 *uwrq, 62618c2ecf20Sopenharmony_ci char *extra) 62628c2ecf20Sopenharmony_ci{ 62638c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 62648c2ecf20Sopenharmony_ci int reset = 0; 62658c2ecf20Sopenharmony_ci 62668c2ecf20Sopenharmony_ci readConfigRid(local, 1); 62678c2ecf20Sopenharmony_ci if (sniffing_mode(local)) 62688c2ecf20Sopenharmony_ci reset = 1; 62698c2ecf20Sopenharmony_ci 62708c2ecf20Sopenharmony_ci switch(*uwrq) { 62718c2ecf20Sopenharmony_ci case IW_MODE_ADHOC: 62728c2ecf20Sopenharmony_ci local->config.opmode &= ~MODE_CFG_MASK; 62738c2ecf20Sopenharmony_ci local->config.opmode |= MODE_STA_IBSS; 62748c2ecf20Sopenharmony_ci local->config.rmode &= ~RXMODE_FULL_MASK; 62758c2ecf20Sopenharmony_ci local->config.scanMode = SCANMODE_ACTIVE; 62768c2ecf20Sopenharmony_ci clear_bit (FLAG_802_11, &local->flags); 62778c2ecf20Sopenharmony_ci break; 62788c2ecf20Sopenharmony_ci case IW_MODE_INFRA: 62798c2ecf20Sopenharmony_ci local->config.opmode &= ~MODE_CFG_MASK; 62808c2ecf20Sopenharmony_ci local->config.opmode |= MODE_STA_ESS; 62818c2ecf20Sopenharmony_ci local->config.rmode &= ~RXMODE_FULL_MASK; 62828c2ecf20Sopenharmony_ci local->config.scanMode = SCANMODE_ACTIVE; 62838c2ecf20Sopenharmony_ci clear_bit (FLAG_802_11, &local->flags); 62848c2ecf20Sopenharmony_ci break; 62858c2ecf20Sopenharmony_ci case IW_MODE_MASTER: 62868c2ecf20Sopenharmony_ci local->config.opmode &= ~MODE_CFG_MASK; 62878c2ecf20Sopenharmony_ci local->config.opmode |= MODE_AP; 62888c2ecf20Sopenharmony_ci local->config.rmode &= ~RXMODE_FULL_MASK; 62898c2ecf20Sopenharmony_ci local->config.scanMode = SCANMODE_ACTIVE; 62908c2ecf20Sopenharmony_ci clear_bit (FLAG_802_11, &local->flags); 62918c2ecf20Sopenharmony_ci break; 62928c2ecf20Sopenharmony_ci case IW_MODE_REPEAT: 62938c2ecf20Sopenharmony_ci local->config.opmode &= ~MODE_CFG_MASK; 62948c2ecf20Sopenharmony_ci local->config.opmode |= MODE_AP_RPTR; 62958c2ecf20Sopenharmony_ci local->config.rmode &= ~RXMODE_FULL_MASK; 62968c2ecf20Sopenharmony_ci local->config.scanMode = SCANMODE_ACTIVE; 62978c2ecf20Sopenharmony_ci clear_bit (FLAG_802_11, &local->flags); 62988c2ecf20Sopenharmony_ci break; 62998c2ecf20Sopenharmony_ci case IW_MODE_MONITOR: 63008c2ecf20Sopenharmony_ci local->config.opmode &= ~MODE_CFG_MASK; 63018c2ecf20Sopenharmony_ci local->config.opmode |= MODE_STA_ESS; 63028c2ecf20Sopenharmony_ci local->config.rmode &= ~RXMODE_FULL_MASK; 63038c2ecf20Sopenharmony_ci local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER; 63048c2ecf20Sopenharmony_ci local->config.scanMode = SCANMODE_PASSIVE; 63058c2ecf20Sopenharmony_ci set_bit (FLAG_802_11, &local->flags); 63068c2ecf20Sopenharmony_ci break; 63078c2ecf20Sopenharmony_ci default: 63088c2ecf20Sopenharmony_ci return -EINVAL; 63098c2ecf20Sopenharmony_ci } 63108c2ecf20Sopenharmony_ci if (reset) 63118c2ecf20Sopenharmony_ci set_bit (FLAG_RESET, &local->flags); 63128c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 63138c2ecf20Sopenharmony_ci 63148c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 63158c2ecf20Sopenharmony_ci} 63168c2ecf20Sopenharmony_ci 63178c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 63188c2ecf20Sopenharmony_ci/* 63198c2ecf20Sopenharmony_ci * Wireless Handler : get Mode of Operation 63208c2ecf20Sopenharmony_ci */ 63218c2ecf20Sopenharmony_cistatic int airo_get_mode(struct net_device *dev, 63228c2ecf20Sopenharmony_ci struct iw_request_info *info, 63238c2ecf20Sopenharmony_ci __u32 *uwrq, 63248c2ecf20Sopenharmony_ci char *extra) 63258c2ecf20Sopenharmony_ci{ 63268c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 63278c2ecf20Sopenharmony_ci 63288c2ecf20Sopenharmony_ci readConfigRid(local, 1); 63298c2ecf20Sopenharmony_ci /* If not managed, assume it's ad-hoc */ 63308c2ecf20Sopenharmony_ci switch (local->config.opmode & MODE_CFG_MASK) { 63318c2ecf20Sopenharmony_ci case MODE_STA_ESS: 63328c2ecf20Sopenharmony_ci *uwrq = IW_MODE_INFRA; 63338c2ecf20Sopenharmony_ci break; 63348c2ecf20Sopenharmony_ci case MODE_AP: 63358c2ecf20Sopenharmony_ci *uwrq = IW_MODE_MASTER; 63368c2ecf20Sopenharmony_ci break; 63378c2ecf20Sopenharmony_ci case MODE_AP_RPTR: 63388c2ecf20Sopenharmony_ci *uwrq = IW_MODE_REPEAT; 63398c2ecf20Sopenharmony_ci break; 63408c2ecf20Sopenharmony_ci default: 63418c2ecf20Sopenharmony_ci *uwrq = IW_MODE_ADHOC; 63428c2ecf20Sopenharmony_ci } 63438c2ecf20Sopenharmony_ci 63448c2ecf20Sopenharmony_ci return 0; 63458c2ecf20Sopenharmony_ci} 63468c2ecf20Sopenharmony_ci 63478c2ecf20Sopenharmony_cistatic inline int valid_index(struct airo_info *ai, int index) 63488c2ecf20Sopenharmony_ci{ 63498c2ecf20Sopenharmony_ci return (index >= 0) && (index <= ai->max_wep_idx); 63508c2ecf20Sopenharmony_ci} 63518c2ecf20Sopenharmony_ci 63528c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 63538c2ecf20Sopenharmony_ci/* 63548c2ecf20Sopenharmony_ci * Wireless Handler : set Encryption Key 63558c2ecf20Sopenharmony_ci */ 63568c2ecf20Sopenharmony_cistatic int airo_set_encode(struct net_device *dev, 63578c2ecf20Sopenharmony_ci struct iw_request_info *info, 63588c2ecf20Sopenharmony_ci struct iw_point *dwrq, 63598c2ecf20Sopenharmony_ci char *extra) 63608c2ecf20Sopenharmony_ci{ 63618c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 63628c2ecf20Sopenharmony_ci int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1); 63638c2ecf20Sopenharmony_ci __le16 currentAuthType = local->config.authType; 63648c2ecf20Sopenharmony_ci int rc = 0; 63658c2ecf20Sopenharmony_ci 63668c2ecf20Sopenharmony_ci if (!local->wep_capable) 63678c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 63688c2ecf20Sopenharmony_ci 63698c2ecf20Sopenharmony_ci readConfigRid(local, 1); 63708c2ecf20Sopenharmony_ci 63718c2ecf20Sopenharmony_ci /* Basic checking: do we have a key to set ? 63728c2ecf20Sopenharmony_ci * Note : with the new API, it's impossible to get a NULL pointer. 63738c2ecf20Sopenharmony_ci * Therefore, we need to check a key size == 0 instead. 63748c2ecf20Sopenharmony_ci * New version of iwconfig properly set the IW_ENCODE_NOKEY flag 63758c2ecf20Sopenharmony_ci * when no key is present (only change flags), but older versions 63768c2ecf20Sopenharmony_ci * don't do it. - Jean II */ 63778c2ecf20Sopenharmony_ci if (dwrq->length > 0) { 63788c2ecf20Sopenharmony_ci wep_key_t key; 63798c2ecf20Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 63808c2ecf20Sopenharmony_ci int current_index; 63818c2ecf20Sopenharmony_ci 63828c2ecf20Sopenharmony_ci /* Check the size of the key */ 63838c2ecf20Sopenharmony_ci if (dwrq->length > MAX_KEY_SIZE) { 63848c2ecf20Sopenharmony_ci return -EINVAL; 63858c2ecf20Sopenharmony_ci } 63868c2ecf20Sopenharmony_ci 63878c2ecf20Sopenharmony_ci current_index = get_wep_tx_idx(local); 63888c2ecf20Sopenharmony_ci if (current_index < 0) 63898c2ecf20Sopenharmony_ci current_index = 0; 63908c2ecf20Sopenharmony_ci 63918c2ecf20Sopenharmony_ci /* Check the index (none -> use current) */ 63928c2ecf20Sopenharmony_ci if (!valid_index(local, index)) 63938c2ecf20Sopenharmony_ci index = current_index; 63948c2ecf20Sopenharmony_ci 63958c2ecf20Sopenharmony_ci /* Set the length */ 63968c2ecf20Sopenharmony_ci if (dwrq->length > MIN_KEY_SIZE) 63978c2ecf20Sopenharmony_ci key.len = MAX_KEY_SIZE; 63988c2ecf20Sopenharmony_ci else 63998c2ecf20Sopenharmony_ci key.len = MIN_KEY_SIZE; 64008c2ecf20Sopenharmony_ci /* Check if the key is not marked as invalid */ 64018c2ecf20Sopenharmony_ci if (!(dwrq->flags & IW_ENCODE_NOKEY)) { 64028c2ecf20Sopenharmony_ci /* Cleanup */ 64038c2ecf20Sopenharmony_ci memset(key.key, 0, MAX_KEY_SIZE); 64048c2ecf20Sopenharmony_ci /* Copy the key in the driver */ 64058c2ecf20Sopenharmony_ci memcpy(key.key, extra, dwrq->length); 64068c2ecf20Sopenharmony_ci /* Send the key to the card */ 64078c2ecf20Sopenharmony_ci rc = set_wep_key(local, index, key.key, key.len, perm, 1); 64088c2ecf20Sopenharmony_ci if (rc < 0) { 64098c2ecf20Sopenharmony_ci airo_print_err(local->dev->name, "failed to set" 64108c2ecf20Sopenharmony_ci " WEP key at index %d: %d.", 64118c2ecf20Sopenharmony_ci index, rc); 64128c2ecf20Sopenharmony_ci return rc; 64138c2ecf20Sopenharmony_ci } 64148c2ecf20Sopenharmony_ci } 64158c2ecf20Sopenharmony_ci /* WE specify that if a valid key is set, encryption 64168c2ecf20Sopenharmony_ci * should be enabled (user may turn it off later) 64178c2ecf20Sopenharmony_ci * This is also how "iwconfig ethX key on" works */ 64188c2ecf20Sopenharmony_ci if ((index == current_index) && (key.len > 0) && 64198c2ecf20Sopenharmony_ci (local->config.authType == AUTH_OPEN)) 64208c2ecf20Sopenharmony_ci set_auth_type(local, AUTH_ENCRYPT); 64218c2ecf20Sopenharmony_ci } else { 64228c2ecf20Sopenharmony_ci /* Do we want to just set the transmit key index ? */ 64238c2ecf20Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 64248c2ecf20Sopenharmony_ci if (valid_index(local, index)) { 64258c2ecf20Sopenharmony_ci rc = set_wep_tx_idx(local, index, perm, 1); 64268c2ecf20Sopenharmony_ci if (rc < 0) { 64278c2ecf20Sopenharmony_ci airo_print_err(local->dev->name, "failed to set" 64288c2ecf20Sopenharmony_ci " WEP transmit index to %d: %d.", 64298c2ecf20Sopenharmony_ci index, rc); 64308c2ecf20Sopenharmony_ci return rc; 64318c2ecf20Sopenharmony_ci } 64328c2ecf20Sopenharmony_ci } else { 64338c2ecf20Sopenharmony_ci /* Don't complain if only change the mode */ 64348c2ecf20Sopenharmony_ci if (!(dwrq->flags & IW_ENCODE_MODE)) 64358c2ecf20Sopenharmony_ci return -EINVAL; 64368c2ecf20Sopenharmony_ci } 64378c2ecf20Sopenharmony_ci } 64388c2ecf20Sopenharmony_ci /* Read the flags */ 64398c2ecf20Sopenharmony_ci if (dwrq->flags & IW_ENCODE_DISABLED) 64408c2ecf20Sopenharmony_ci set_auth_type(local, AUTH_OPEN); /* disable encryption */ 64418c2ecf20Sopenharmony_ci if (dwrq->flags & IW_ENCODE_RESTRICTED) 64428c2ecf20Sopenharmony_ci set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ 64438c2ecf20Sopenharmony_ci if (dwrq->flags & IW_ENCODE_OPEN) 64448c2ecf20Sopenharmony_ci set_auth_type(local, AUTH_ENCRYPT); /* Only Wep */ 64458c2ecf20Sopenharmony_ci /* Commit the changes to flags if needed */ 64468c2ecf20Sopenharmony_ci if (local->config.authType != currentAuthType) 64478c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 64488c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 64498c2ecf20Sopenharmony_ci} 64508c2ecf20Sopenharmony_ci 64518c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 64528c2ecf20Sopenharmony_ci/* 64538c2ecf20Sopenharmony_ci * Wireless Handler : get Encryption Key 64548c2ecf20Sopenharmony_ci */ 64558c2ecf20Sopenharmony_cistatic int airo_get_encode(struct net_device *dev, 64568c2ecf20Sopenharmony_ci struct iw_request_info *info, 64578c2ecf20Sopenharmony_ci struct iw_point *dwrq, 64588c2ecf20Sopenharmony_ci char *extra) 64598c2ecf20Sopenharmony_ci{ 64608c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 64618c2ecf20Sopenharmony_ci int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; 64628c2ecf20Sopenharmony_ci int wep_key_len; 64638c2ecf20Sopenharmony_ci u8 buf[16]; 64648c2ecf20Sopenharmony_ci 64658c2ecf20Sopenharmony_ci if (!local->wep_capable) 64668c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 64678c2ecf20Sopenharmony_ci 64688c2ecf20Sopenharmony_ci readConfigRid(local, 1); 64698c2ecf20Sopenharmony_ci 64708c2ecf20Sopenharmony_ci /* Check encryption mode */ 64718c2ecf20Sopenharmony_ci switch(local->config.authType) { 64728c2ecf20Sopenharmony_ci case AUTH_ENCRYPT: 64738c2ecf20Sopenharmony_ci dwrq->flags = IW_ENCODE_OPEN; 64748c2ecf20Sopenharmony_ci break; 64758c2ecf20Sopenharmony_ci case AUTH_SHAREDKEY: 64768c2ecf20Sopenharmony_ci dwrq->flags = IW_ENCODE_RESTRICTED; 64778c2ecf20Sopenharmony_ci break; 64788c2ecf20Sopenharmony_ci default: 64798c2ecf20Sopenharmony_ci case AUTH_OPEN: 64808c2ecf20Sopenharmony_ci dwrq->flags = IW_ENCODE_DISABLED; 64818c2ecf20Sopenharmony_ci break; 64828c2ecf20Sopenharmony_ci } 64838c2ecf20Sopenharmony_ci /* We can't return the key, so set the proper flag and return zero */ 64848c2ecf20Sopenharmony_ci dwrq->flags |= IW_ENCODE_NOKEY; 64858c2ecf20Sopenharmony_ci memset(extra, 0, 16); 64868c2ecf20Sopenharmony_ci 64878c2ecf20Sopenharmony_ci /* Which key do we want ? -1 -> tx index */ 64888c2ecf20Sopenharmony_ci if (!valid_index(local, index)) { 64898c2ecf20Sopenharmony_ci index = get_wep_tx_idx(local); 64908c2ecf20Sopenharmony_ci if (index < 0) 64918c2ecf20Sopenharmony_ci index = 0; 64928c2ecf20Sopenharmony_ci } 64938c2ecf20Sopenharmony_ci dwrq->flags |= index + 1; 64948c2ecf20Sopenharmony_ci 64958c2ecf20Sopenharmony_ci /* Copy the key to the user buffer */ 64968c2ecf20Sopenharmony_ci wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf)); 64978c2ecf20Sopenharmony_ci if (wep_key_len < 0) { 64988c2ecf20Sopenharmony_ci dwrq->length = 0; 64998c2ecf20Sopenharmony_ci } else { 65008c2ecf20Sopenharmony_ci dwrq->length = wep_key_len; 65018c2ecf20Sopenharmony_ci memcpy(extra, buf, dwrq->length); 65028c2ecf20Sopenharmony_ci } 65038c2ecf20Sopenharmony_ci 65048c2ecf20Sopenharmony_ci return 0; 65058c2ecf20Sopenharmony_ci} 65068c2ecf20Sopenharmony_ci 65078c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 65088c2ecf20Sopenharmony_ci/* 65098c2ecf20Sopenharmony_ci * Wireless Handler : set extended Encryption parameters 65108c2ecf20Sopenharmony_ci */ 65118c2ecf20Sopenharmony_cistatic int airo_set_encodeext(struct net_device *dev, 65128c2ecf20Sopenharmony_ci struct iw_request_info *info, 65138c2ecf20Sopenharmony_ci union iwreq_data *wrqu, 65148c2ecf20Sopenharmony_ci char *extra) 65158c2ecf20Sopenharmony_ci{ 65168c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 65178c2ecf20Sopenharmony_ci struct iw_point *encoding = &wrqu->encoding; 65188c2ecf20Sopenharmony_ci struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 65198c2ecf20Sopenharmony_ci int perm = (encoding->flags & IW_ENCODE_TEMP ? 0 : 1); 65208c2ecf20Sopenharmony_ci __le16 currentAuthType = local->config.authType; 65218c2ecf20Sopenharmony_ci int idx, key_len, alg = ext->alg, set_key = 1, rc; 65228c2ecf20Sopenharmony_ci wep_key_t key; 65238c2ecf20Sopenharmony_ci 65248c2ecf20Sopenharmony_ci if (!local->wep_capable) 65258c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 65268c2ecf20Sopenharmony_ci 65278c2ecf20Sopenharmony_ci readConfigRid(local, 1); 65288c2ecf20Sopenharmony_ci 65298c2ecf20Sopenharmony_ci /* Determine and validate the key index */ 65308c2ecf20Sopenharmony_ci idx = encoding->flags & IW_ENCODE_INDEX; 65318c2ecf20Sopenharmony_ci if (idx) { 65328c2ecf20Sopenharmony_ci if (!valid_index(local, idx - 1)) 65338c2ecf20Sopenharmony_ci return -EINVAL; 65348c2ecf20Sopenharmony_ci idx--; 65358c2ecf20Sopenharmony_ci } else { 65368c2ecf20Sopenharmony_ci idx = get_wep_tx_idx(local); 65378c2ecf20Sopenharmony_ci if (idx < 0) 65388c2ecf20Sopenharmony_ci idx = 0; 65398c2ecf20Sopenharmony_ci } 65408c2ecf20Sopenharmony_ci 65418c2ecf20Sopenharmony_ci if (encoding->flags & IW_ENCODE_DISABLED) 65428c2ecf20Sopenharmony_ci alg = IW_ENCODE_ALG_NONE; 65438c2ecf20Sopenharmony_ci 65448c2ecf20Sopenharmony_ci if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { 65458c2ecf20Sopenharmony_ci /* Only set transmit key index here, actual 65468c2ecf20Sopenharmony_ci * key is set below if needed. 65478c2ecf20Sopenharmony_ci */ 65488c2ecf20Sopenharmony_ci rc = set_wep_tx_idx(local, idx, perm, 1); 65498c2ecf20Sopenharmony_ci if (rc < 0) { 65508c2ecf20Sopenharmony_ci airo_print_err(local->dev->name, "failed to set " 65518c2ecf20Sopenharmony_ci "WEP transmit index to %d: %d.", 65528c2ecf20Sopenharmony_ci idx, rc); 65538c2ecf20Sopenharmony_ci return rc; 65548c2ecf20Sopenharmony_ci } 65558c2ecf20Sopenharmony_ci set_key = ext->key_len > 0 ? 1 : 0; 65568c2ecf20Sopenharmony_ci } 65578c2ecf20Sopenharmony_ci 65588c2ecf20Sopenharmony_ci if (set_key) { 65598c2ecf20Sopenharmony_ci /* Set the requested key first */ 65608c2ecf20Sopenharmony_ci memset(key.key, 0, MAX_KEY_SIZE); 65618c2ecf20Sopenharmony_ci switch (alg) { 65628c2ecf20Sopenharmony_ci case IW_ENCODE_ALG_NONE: 65638c2ecf20Sopenharmony_ci key.len = 0; 65648c2ecf20Sopenharmony_ci break; 65658c2ecf20Sopenharmony_ci case IW_ENCODE_ALG_WEP: 65668c2ecf20Sopenharmony_ci if (ext->key_len > MIN_KEY_SIZE) { 65678c2ecf20Sopenharmony_ci key.len = MAX_KEY_SIZE; 65688c2ecf20Sopenharmony_ci } else if (ext->key_len > 0) { 65698c2ecf20Sopenharmony_ci key.len = MIN_KEY_SIZE; 65708c2ecf20Sopenharmony_ci } else { 65718c2ecf20Sopenharmony_ci return -EINVAL; 65728c2ecf20Sopenharmony_ci } 65738c2ecf20Sopenharmony_ci key_len = min (ext->key_len, key.len); 65748c2ecf20Sopenharmony_ci memcpy(key.key, ext->key, key_len); 65758c2ecf20Sopenharmony_ci break; 65768c2ecf20Sopenharmony_ci default: 65778c2ecf20Sopenharmony_ci return -EINVAL; 65788c2ecf20Sopenharmony_ci } 65798c2ecf20Sopenharmony_ci if (key.len == 0) { 65808c2ecf20Sopenharmony_ci rc = set_wep_tx_idx(local, idx, perm, 1); 65818c2ecf20Sopenharmony_ci if (rc < 0) { 65828c2ecf20Sopenharmony_ci airo_print_err(local->dev->name, 65838c2ecf20Sopenharmony_ci "failed to set WEP transmit index to %d: %d.", 65848c2ecf20Sopenharmony_ci idx, rc); 65858c2ecf20Sopenharmony_ci return rc; 65868c2ecf20Sopenharmony_ci } 65878c2ecf20Sopenharmony_ci } else { 65888c2ecf20Sopenharmony_ci rc = set_wep_key(local, idx, key.key, key.len, perm, 1); 65898c2ecf20Sopenharmony_ci if (rc < 0) { 65908c2ecf20Sopenharmony_ci airo_print_err(local->dev->name, 65918c2ecf20Sopenharmony_ci "failed to set WEP key at index %d: %d.", 65928c2ecf20Sopenharmony_ci idx, rc); 65938c2ecf20Sopenharmony_ci return rc; 65948c2ecf20Sopenharmony_ci } 65958c2ecf20Sopenharmony_ci } 65968c2ecf20Sopenharmony_ci } 65978c2ecf20Sopenharmony_ci 65988c2ecf20Sopenharmony_ci /* Read the flags */ 65998c2ecf20Sopenharmony_ci if (encoding->flags & IW_ENCODE_DISABLED) 66008c2ecf20Sopenharmony_ci set_auth_type(local, AUTH_OPEN); /* disable encryption */ 66018c2ecf20Sopenharmony_ci if (encoding->flags & IW_ENCODE_RESTRICTED) 66028c2ecf20Sopenharmony_ci set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ 66038c2ecf20Sopenharmony_ci if (encoding->flags & IW_ENCODE_OPEN) 66048c2ecf20Sopenharmony_ci set_auth_type(local, AUTH_ENCRYPT); 66058c2ecf20Sopenharmony_ci /* Commit the changes to flags if needed */ 66068c2ecf20Sopenharmony_ci if (local->config.authType != currentAuthType) 66078c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 66088c2ecf20Sopenharmony_ci 66098c2ecf20Sopenharmony_ci return -EINPROGRESS; 66108c2ecf20Sopenharmony_ci} 66118c2ecf20Sopenharmony_ci 66128c2ecf20Sopenharmony_ci 66138c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 66148c2ecf20Sopenharmony_ci/* 66158c2ecf20Sopenharmony_ci * Wireless Handler : get extended Encryption parameters 66168c2ecf20Sopenharmony_ci */ 66178c2ecf20Sopenharmony_cistatic int airo_get_encodeext(struct net_device *dev, 66188c2ecf20Sopenharmony_ci struct iw_request_info *info, 66198c2ecf20Sopenharmony_ci union iwreq_data *wrqu, 66208c2ecf20Sopenharmony_ci char *extra) 66218c2ecf20Sopenharmony_ci{ 66228c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 66238c2ecf20Sopenharmony_ci struct iw_point *encoding = &wrqu->encoding; 66248c2ecf20Sopenharmony_ci struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 66258c2ecf20Sopenharmony_ci int idx, max_key_len, wep_key_len; 66268c2ecf20Sopenharmony_ci u8 buf[16]; 66278c2ecf20Sopenharmony_ci 66288c2ecf20Sopenharmony_ci if (!local->wep_capable) 66298c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 66308c2ecf20Sopenharmony_ci 66318c2ecf20Sopenharmony_ci readConfigRid(local, 1); 66328c2ecf20Sopenharmony_ci 66338c2ecf20Sopenharmony_ci max_key_len = encoding->length - sizeof(*ext); 66348c2ecf20Sopenharmony_ci if (max_key_len < 0) 66358c2ecf20Sopenharmony_ci return -EINVAL; 66368c2ecf20Sopenharmony_ci 66378c2ecf20Sopenharmony_ci idx = encoding->flags & IW_ENCODE_INDEX; 66388c2ecf20Sopenharmony_ci if (idx) { 66398c2ecf20Sopenharmony_ci if (!valid_index(local, idx - 1)) 66408c2ecf20Sopenharmony_ci return -EINVAL; 66418c2ecf20Sopenharmony_ci idx--; 66428c2ecf20Sopenharmony_ci } else { 66438c2ecf20Sopenharmony_ci idx = get_wep_tx_idx(local); 66448c2ecf20Sopenharmony_ci if (idx < 0) 66458c2ecf20Sopenharmony_ci idx = 0; 66468c2ecf20Sopenharmony_ci } 66478c2ecf20Sopenharmony_ci 66488c2ecf20Sopenharmony_ci encoding->flags = idx + 1; 66498c2ecf20Sopenharmony_ci memset(ext, 0, sizeof(*ext)); 66508c2ecf20Sopenharmony_ci 66518c2ecf20Sopenharmony_ci /* Check encryption mode */ 66528c2ecf20Sopenharmony_ci switch(local->config.authType) { 66538c2ecf20Sopenharmony_ci case AUTH_ENCRYPT: 66548c2ecf20Sopenharmony_ci encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED; 66558c2ecf20Sopenharmony_ci break; 66568c2ecf20Sopenharmony_ci case AUTH_SHAREDKEY: 66578c2ecf20Sopenharmony_ci encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED; 66588c2ecf20Sopenharmony_ci break; 66598c2ecf20Sopenharmony_ci default: 66608c2ecf20Sopenharmony_ci case AUTH_OPEN: 66618c2ecf20Sopenharmony_ci encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED; 66628c2ecf20Sopenharmony_ci break; 66638c2ecf20Sopenharmony_ci } 66648c2ecf20Sopenharmony_ci /* We can't return the key, so set the proper flag and return zero */ 66658c2ecf20Sopenharmony_ci encoding->flags |= IW_ENCODE_NOKEY; 66668c2ecf20Sopenharmony_ci memset(extra, 0, 16); 66678c2ecf20Sopenharmony_ci 66688c2ecf20Sopenharmony_ci /* Copy the key to the user buffer */ 66698c2ecf20Sopenharmony_ci wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf)); 66708c2ecf20Sopenharmony_ci if (wep_key_len < 0) { 66718c2ecf20Sopenharmony_ci ext->key_len = 0; 66728c2ecf20Sopenharmony_ci } else { 66738c2ecf20Sopenharmony_ci ext->key_len = wep_key_len; 66748c2ecf20Sopenharmony_ci memcpy(extra, buf, ext->key_len); 66758c2ecf20Sopenharmony_ci } 66768c2ecf20Sopenharmony_ci 66778c2ecf20Sopenharmony_ci return 0; 66788c2ecf20Sopenharmony_ci} 66798c2ecf20Sopenharmony_ci 66808c2ecf20Sopenharmony_ci 66818c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 66828c2ecf20Sopenharmony_ci/* 66838c2ecf20Sopenharmony_ci * Wireless Handler : set extended authentication parameters 66848c2ecf20Sopenharmony_ci */ 66858c2ecf20Sopenharmony_cistatic int airo_set_auth(struct net_device *dev, 66868c2ecf20Sopenharmony_ci struct iw_request_info *info, 66878c2ecf20Sopenharmony_ci union iwreq_data *wrqu, char *extra) 66888c2ecf20Sopenharmony_ci{ 66898c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 66908c2ecf20Sopenharmony_ci struct iw_param *param = &wrqu->param; 66918c2ecf20Sopenharmony_ci __le16 currentAuthType = local->config.authType; 66928c2ecf20Sopenharmony_ci 66938c2ecf20Sopenharmony_ci switch (param->flags & IW_AUTH_INDEX) { 66948c2ecf20Sopenharmony_ci case IW_AUTH_WPA_VERSION: 66958c2ecf20Sopenharmony_ci case IW_AUTH_CIPHER_PAIRWISE: 66968c2ecf20Sopenharmony_ci case IW_AUTH_CIPHER_GROUP: 66978c2ecf20Sopenharmony_ci case IW_AUTH_KEY_MGMT: 66988c2ecf20Sopenharmony_ci case IW_AUTH_RX_UNENCRYPTED_EAPOL: 66998c2ecf20Sopenharmony_ci case IW_AUTH_PRIVACY_INVOKED: 67008c2ecf20Sopenharmony_ci /* 67018c2ecf20Sopenharmony_ci * airo does not use these parameters 67028c2ecf20Sopenharmony_ci */ 67038c2ecf20Sopenharmony_ci break; 67048c2ecf20Sopenharmony_ci 67058c2ecf20Sopenharmony_ci case IW_AUTH_DROP_UNENCRYPTED: 67068c2ecf20Sopenharmony_ci if (param->value) { 67078c2ecf20Sopenharmony_ci /* Only change auth type if unencrypted */ 67088c2ecf20Sopenharmony_ci if (currentAuthType == AUTH_OPEN) 67098c2ecf20Sopenharmony_ci set_auth_type(local, AUTH_ENCRYPT); 67108c2ecf20Sopenharmony_ci } else { 67118c2ecf20Sopenharmony_ci set_auth_type(local, AUTH_OPEN); 67128c2ecf20Sopenharmony_ci } 67138c2ecf20Sopenharmony_ci 67148c2ecf20Sopenharmony_ci /* Commit the changes to flags if needed */ 67158c2ecf20Sopenharmony_ci if (local->config.authType != currentAuthType) 67168c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 67178c2ecf20Sopenharmony_ci break; 67188c2ecf20Sopenharmony_ci 67198c2ecf20Sopenharmony_ci case IW_AUTH_80211_AUTH_ALG: { 67208c2ecf20Sopenharmony_ci if (param->value & IW_AUTH_ALG_SHARED_KEY) { 67218c2ecf20Sopenharmony_ci set_auth_type(local, AUTH_SHAREDKEY); 67228c2ecf20Sopenharmony_ci } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { 67238c2ecf20Sopenharmony_ci /* We don't know here if WEP open system or 67248c2ecf20Sopenharmony_ci * unencrypted mode was requested - so use the 67258c2ecf20Sopenharmony_ci * last mode (of these two) used last time 67268c2ecf20Sopenharmony_ci */ 67278c2ecf20Sopenharmony_ci set_auth_type(local, local->last_auth); 67288c2ecf20Sopenharmony_ci } else 67298c2ecf20Sopenharmony_ci return -EINVAL; 67308c2ecf20Sopenharmony_ci 67318c2ecf20Sopenharmony_ci /* Commit the changes to flags if needed */ 67328c2ecf20Sopenharmony_ci if (local->config.authType != currentAuthType) 67338c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 67348c2ecf20Sopenharmony_ci break; 67358c2ecf20Sopenharmony_ci } 67368c2ecf20Sopenharmony_ci 67378c2ecf20Sopenharmony_ci case IW_AUTH_WPA_ENABLED: 67388c2ecf20Sopenharmony_ci /* Silently accept disable of WPA */ 67398c2ecf20Sopenharmony_ci if (param->value > 0) 67408c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 67418c2ecf20Sopenharmony_ci break; 67428c2ecf20Sopenharmony_ci 67438c2ecf20Sopenharmony_ci default: 67448c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 67458c2ecf20Sopenharmony_ci } 67468c2ecf20Sopenharmony_ci return -EINPROGRESS; 67478c2ecf20Sopenharmony_ci} 67488c2ecf20Sopenharmony_ci 67498c2ecf20Sopenharmony_ci 67508c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 67518c2ecf20Sopenharmony_ci/* 67528c2ecf20Sopenharmony_ci * Wireless Handler : get extended authentication parameters 67538c2ecf20Sopenharmony_ci */ 67548c2ecf20Sopenharmony_cistatic int airo_get_auth(struct net_device *dev, 67558c2ecf20Sopenharmony_ci struct iw_request_info *info, 67568c2ecf20Sopenharmony_ci union iwreq_data *wrqu, char *extra) 67578c2ecf20Sopenharmony_ci{ 67588c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 67598c2ecf20Sopenharmony_ci struct iw_param *param = &wrqu->param; 67608c2ecf20Sopenharmony_ci __le16 currentAuthType = local->config.authType; 67618c2ecf20Sopenharmony_ci 67628c2ecf20Sopenharmony_ci switch (param->flags & IW_AUTH_INDEX) { 67638c2ecf20Sopenharmony_ci case IW_AUTH_DROP_UNENCRYPTED: 67648c2ecf20Sopenharmony_ci switch (currentAuthType) { 67658c2ecf20Sopenharmony_ci case AUTH_SHAREDKEY: 67668c2ecf20Sopenharmony_ci case AUTH_ENCRYPT: 67678c2ecf20Sopenharmony_ci param->value = 1; 67688c2ecf20Sopenharmony_ci break; 67698c2ecf20Sopenharmony_ci default: 67708c2ecf20Sopenharmony_ci param->value = 0; 67718c2ecf20Sopenharmony_ci break; 67728c2ecf20Sopenharmony_ci } 67738c2ecf20Sopenharmony_ci break; 67748c2ecf20Sopenharmony_ci 67758c2ecf20Sopenharmony_ci case IW_AUTH_80211_AUTH_ALG: 67768c2ecf20Sopenharmony_ci switch (currentAuthType) { 67778c2ecf20Sopenharmony_ci case AUTH_SHAREDKEY: 67788c2ecf20Sopenharmony_ci param->value = IW_AUTH_ALG_SHARED_KEY; 67798c2ecf20Sopenharmony_ci break; 67808c2ecf20Sopenharmony_ci case AUTH_ENCRYPT: 67818c2ecf20Sopenharmony_ci default: 67828c2ecf20Sopenharmony_ci param->value = IW_AUTH_ALG_OPEN_SYSTEM; 67838c2ecf20Sopenharmony_ci break; 67848c2ecf20Sopenharmony_ci } 67858c2ecf20Sopenharmony_ci break; 67868c2ecf20Sopenharmony_ci 67878c2ecf20Sopenharmony_ci case IW_AUTH_WPA_ENABLED: 67888c2ecf20Sopenharmony_ci param->value = 0; 67898c2ecf20Sopenharmony_ci break; 67908c2ecf20Sopenharmony_ci 67918c2ecf20Sopenharmony_ci default: 67928c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 67938c2ecf20Sopenharmony_ci } 67948c2ecf20Sopenharmony_ci return 0; 67958c2ecf20Sopenharmony_ci} 67968c2ecf20Sopenharmony_ci 67978c2ecf20Sopenharmony_ci 67988c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 67998c2ecf20Sopenharmony_ci/* 68008c2ecf20Sopenharmony_ci * Wireless Handler : set Tx-Power 68018c2ecf20Sopenharmony_ci */ 68028c2ecf20Sopenharmony_cistatic int airo_set_txpow(struct net_device *dev, 68038c2ecf20Sopenharmony_ci struct iw_request_info *info, 68048c2ecf20Sopenharmony_ci struct iw_param *vwrq, 68058c2ecf20Sopenharmony_ci char *extra) 68068c2ecf20Sopenharmony_ci{ 68078c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 68088c2ecf20Sopenharmony_ci CapabilityRid cap_rid; /* Card capability info */ 68098c2ecf20Sopenharmony_ci int i; 68108c2ecf20Sopenharmony_ci int rc = -EINVAL; 68118c2ecf20Sopenharmony_ci __le16 v = cpu_to_le16(vwrq->value); 68128c2ecf20Sopenharmony_ci 68138c2ecf20Sopenharmony_ci readCapabilityRid(local, &cap_rid, 1); 68148c2ecf20Sopenharmony_ci 68158c2ecf20Sopenharmony_ci if (vwrq->disabled) { 68168c2ecf20Sopenharmony_ci set_bit (FLAG_RADIO_OFF, &local->flags); 68178c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 68188c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 68198c2ecf20Sopenharmony_ci } 68208c2ecf20Sopenharmony_ci if (vwrq->flags != IW_TXPOW_MWATT) { 68218c2ecf20Sopenharmony_ci return -EINVAL; 68228c2ecf20Sopenharmony_ci } 68238c2ecf20Sopenharmony_ci clear_bit (FLAG_RADIO_OFF, &local->flags); 68248c2ecf20Sopenharmony_ci for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++) 68258c2ecf20Sopenharmony_ci if (v == cap_rid.txPowerLevels[i]) { 68268c2ecf20Sopenharmony_ci readConfigRid(local, 1); 68278c2ecf20Sopenharmony_ci local->config.txPower = v; 68288c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 68298c2ecf20Sopenharmony_ci rc = -EINPROGRESS; /* Call commit handler */ 68308c2ecf20Sopenharmony_ci break; 68318c2ecf20Sopenharmony_ci } 68328c2ecf20Sopenharmony_ci return rc; 68338c2ecf20Sopenharmony_ci} 68348c2ecf20Sopenharmony_ci 68358c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 68368c2ecf20Sopenharmony_ci/* 68378c2ecf20Sopenharmony_ci * Wireless Handler : get Tx-Power 68388c2ecf20Sopenharmony_ci */ 68398c2ecf20Sopenharmony_cistatic int airo_get_txpow(struct net_device *dev, 68408c2ecf20Sopenharmony_ci struct iw_request_info *info, 68418c2ecf20Sopenharmony_ci struct iw_param *vwrq, 68428c2ecf20Sopenharmony_ci char *extra) 68438c2ecf20Sopenharmony_ci{ 68448c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 68458c2ecf20Sopenharmony_ci 68468c2ecf20Sopenharmony_ci readConfigRid(local, 1); 68478c2ecf20Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.txPower); 68488c2ecf20Sopenharmony_ci vwrq->fixed = 1; /* No power control */ 68498c2ecf20Sopenharmony_ci vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags); 68508c2ecf20Sopenharmony_ci vwrq->flags = IW_TXPOW_MWATT; 68518c2ecf20Sopenharmony_ci 68528c2ecf20Sopenharmony_ci return 0; 68538c2ecf20Sopenharmony_ci} 68548c2ecf20Sopenharmony_ci 68558c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 68568c2ecf20Sopenharmony_ci/* 68578c2ecf20Sopenharmony_ci * Wireless Handler : set Retry limits 68588c2ecf20Sopenharmony_ci */ 68598c2ecf20Sopenharmony_cistatic int airo_set_retry(struct net_device *dev, 68608c2ecf20Sopenharmony_ci struct iw_request_info *info, 68618c2ecf20Sopenharmony_ci struct iw_param *vwrq, 68628c2ecf20Sopenharmony_ci char *extra) 68638c2ecf20Sopenharmony_ci{ 68648c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 68658c2ecf20Sopenharmony_ci int rc = -EINVAL; 68668c2ecf20Sopenharmony_ci 68678c2ecf20Sopenharmony_ci if (vwrq->disabled) { 68688c2ecf20Sopenharmony_ci return -EINVAL; 68698c2ecf20Sopenharmony_ci } 68708c2ecf20Sopenharmony_ci readConfigRid(local, 1); 68718c2ecf20Sopenharmony_ci if (vwrq->flags & IW_RETRY_LIMIT) { 68728c2ecf20Sopenharmony_ci __le16 v = cpu_to_le16(vwrq->value); 68738c2ecf20Sopenharmony_ci if (vwrq->flags & IW_RETRY_LONG) 68748c2ecf20Sopenharmony_ci local->config.longRetryLimit = v; 68758c2ecf20Sopenharmony_ci else if (vwrq->flags & IW_RETRY_SHORT) 68768c2ecf20Sopenharmony_ci local->config.shortRetryLimit = v; 68778c2ecf20Sopenharmony_ci else { 68788c2ecf20Sopenharmony_ci /* No modifier : set both */ 68798c2ecf20Sopenharmony_ci local->config.longRetryLimit = v; 68808c2ecf20Sopenharmony_ci local->config.shortRetryLimit = v; 68818c2ecf20Sopenharmony_ci } 68828c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 68838c2ecf20Sopenharmony_ci rc = -EINPROGRESS; /* Call commit handler */ 68848c2ecf20Sopenharmony_ci } 68858c2ecf20Sopenharmony_ci if (vwrq->flags & IW_RETRY_LIFETIME) { 68868c2ecf20Sopenharmony_ci local->config.txLifetime = cpu_to_le16(vwrq->value / 1024); 68878c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 68888c2ecf20Sopenharmony_ci rc = -EINPROGRESS; /* Call commit handler */ 68898c2ecf20Sopenharmony_ci } 68908c2ecf20Sopenharmony_ci return rc; 68918c2ecf20Sopenharmony_ci} 68928c2ecf20Sopenharmony_ci 68938c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 68948c2ecf20Sopenharmony_ci/* 68958c2ecf20Sopenharmony_ci * Wireless Handler : get Retry limits 68968c2ecf20Sopenharmony_ci */ 68978c2ecf20Sopenharmony_cistatic int airo_get_retry(struct net_device *dev, 68988c2ecf20Sopenharmony_ci struct iw_request_info *info, 68998c2ecf20Sopenharmony_ci struct iw_param *vwrq, 69008c2ecf20Sopenharmony_ci char *extra) 69018c2ecf20Sopenharmony_ci{ 69028c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 69038c2ecf20Sopenharmony_ci 69048c2ecf20Sopenharmony_ci vwrq->disabled = 0; /* Can't be disabled */ 69058c2ecf20Sopenharmony_ci 69068c2ecf20Sopenharmony_ci readConfigRid(local, 1); 69078c2ecf20Sopenharmony_ci /* Note : by default, display the min retry number */ 69088c2ecf20Sopenharmony_ci if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { 69098c2ecf20Sopenharmony_ci vwrq->flags = IW_RETRY_LIFETIME; 69108c2ecf20Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024; 69118c2ecf20Sopenharmony_ci } else if ((vwrq->flags & IW_RETRY_LONG)) { 69128c2ecf20Sopenharmony_ci vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; 69138c2ecf20Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.longRetryLimit); 69148c2ecf20Sopenharmony_ci } else { 69158c2ecf20Sopenharmony_ci vwrq->flags = IW_RETRY_LIMIT; 69168c2ecf20Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.shortRetryLimit); 69178c2ecf20Sopenharmony_ci if (local->config.shortRetryLimit != local->config.longRetryLimit) 69188c2ecf20Sopenharmony_ci vwrq->flags |= IW_RETRY_SHORT; 69198c2ecf20Sopenharmony_ci } 69208c2ecf20Sopenharmony_ci 69218c2ecf20Sopenharmony_ci return 0; 69228c2ecf20Sopenharmony_ci} 69238c2ecf20Sopenharmony_ci 69248c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 69258c2ecf20Sopenharmony_ci/* 69268c2ecf20Sopenharmony_ci * Wireless Handler : get range info 69278c2ecf20Sopenharmony_ci */ 69288c2ecf20Sopenharmony_cistatic int airo_get_range(struct net_device *dev, 69298c2ecf20Sopenharmony_ci struct iw_request_info *info, 69308c2ecf20Sopenharmony_ci struct iw_point *dwrq, 69318c2ecf20Sopenharmony_ci char *extra) 69328c2ecf20Sopenharmony_ci{ 69338c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 69348c2ecf20Sopenharmony_ci struct iw_range *range = (struct iw_range *) extra; 69358c2ecf20Sopenharmony_ci CapabilityRid cap_rid; /* Card capability info */ 69368c2ecf20Sopenharmony_ci int i; 69378c2ecf20Sopenharmony_ci int k; 69388c2ecf20Sopenharmony_ci 69398c2ecf20Sopenharmony_ci readCapabilityRid(local, &cap_rid, 1); 69408c2ecf20Sopenharmony_ci 69418c2ecf20Sopenharmony_ci dwrq->length = sizeof(struct iw_range); 69428c2ecf20Sopenharmony_ci memset(range, 0, sizeof(*range)); 69438c2ecf20Sopenharmony_ci range->min_nwid = 0x0000; 69448c2ecf20Sopenharmony_ci range->max_nwid = 0x0000; 69458c2ecf20Sopenharmony_ci range->num_channels = 14; 69468c2ecf20Sopenharmony_ci /* Should be based on cap_rid.country to give only 69478c2ecf20Sopenharmony_ci * what the current card support */ 69488c2ecf20Sopenharmony_ci k = 0; 69498c2ecf20Sopenharmony_ci for (i = 0; i < 14; i++) { 69508c2ecf20Sopenharmony_ci range->freq[k].i = i + 1; /* List index */ 69518c2ecf20Sopenharmony_ci range->freq[k].m = 100000 * 69528c2ecf20Sopenharmony_ci ieee80211_channel_to_frequency(i + 1, NL80211_BAND_2GHZ); 69538c2ecf20Sopenharmony_ci range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */ 69548c2ecf20Sopenharmony_ci } 69558c2ecf20Sopenharmony_ci range->num_frequency = k; 69568c2ecf20Sopenharmony_ci 69578c2ecf20Sopenharmony_ci range->sensitivity = 65535; 69588c2ecf20Sopenharmony_ci 69598c2ecf20Sopenharmony_ci /* Hum... Should put the right values there */ 69608c2ecf20Sopenharmony_ci if (local->rssi) 69618c2ecf20Sopenharmony_ci range->max_qual.qual = 100; /* % */ 69628c2ecf20Sopenharmony_ci else 69638c2ecf20Sopenharmony_ci range->max_qual.qual = airo_get_max_quality(&cap_rid); 69648c2ecf20Sopenharmony_ci range->max_qual.level = 0x100 - 120; /* -120 dBm */ 69658c2ecf20Sopenharmony_ci range->max_qual.noise = 0x100 - 120; /* -120 dBm */ 69668c2ecf20Sopenharmony_ci 69678c2ecf20Sopenharmony_ci /* Experimental measurements - boundary 11/5.5 Mb/s */ 69688c2ecf20Sopenharmony_ci /* Note : with or without the (local->rssi), results 69698c2ecf20Sopenharmony_ci * are somewhat different. - Jean II */ 69708c2ecf20Sopenharmony_ci if (local->rssi) { 69718c2ecf20Sopenharmony_ci range->avg_qual.qual = 50; /* % */ 69728c2ecf20Sopenharmony_ci range->avg_qual.level = 0x100 - 70; /* -70 dBm */ 69738c2ecf20Sopenharmony_ci } else { 69748c2ecf20Sopenharmony_ci range->avg_qual.qual = airo_get_avg_quality(&cap_rid); 69758c2ecf20Sopenharmony_ci range->avg_qual.level = 0x100 - 80; /* -80 dBm */ 69768c2ecf20Sopenharmony_ci } 69778c2ecf20Sopenharmony_ci range->avg_qual.noise = 0x100 - 85; /* -85 dBm */ 69788c2ecf20Sopenharmony_ci 69798c2ecf20Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 69808c2ecf20Sopenharmony_ci range->bitrate[i] = cap_rid.supportedRates[i] * 500000; 69818c2ecf20Sopenharmony_ci if (range->bitrate[i] == 0) 69828c2ecf20Sopenharmony_ci break; 69838c2ecf20Sopenharmony_ci } 69848c2ecf20Sopenharmony_ci range->num_bitrates = i; 69858c2ecf20Sopenharmony_ci 69868c2ecf20Sopenharmony_ci /* Set an indication of the max TCP throughput 69878c2ecf20Sopenharmony_ci * in bit/s that we can expect using this interface. 69888c2ecf20Sopenharmony_ci * May be use for QoS stuff... Jean II */ 69898c2ecf20Sopenharmony_ci if (i > 2) 69908c2ecf20Sopenharmony_ci range->throughput = 5000 * 1000; 69918c2ecf20Sopenharmony_ci else 69928c2ecf20Sopenharmony_ci range->throughput = 1500 * 1000; 69938c2ecf20Sopenharmony_ci 69948c2ecf20Sopenharmony_ci range->min_rts = 0; 69958c2ecf20Sopenharmony_ci range->max_rts = AIRO_DEF_MTU; 69968c2ecf20Sopenharmony_ci range->min_frag = 256; 69978c2ecf20Sopenharmony_ci range->max_frag = AIRO_DEF_MTU; 69988c2ecf20Sopenharmony_ci 69998c2ecf20Sopenharmony_ci if (cap_rid.softCap & cpu_to_le16(2)) { 70008c2ecf20Sopenharmony_ci // WEP: RC4 40 bits 70018c2ecf20Sopenharmony_ci range->encoding_size[0] = 5; 70028c2ecf20Sopenharmony_ci // RC4 ~128 bits 70038c2ecf20Sopenharmony_ci if (cap_rid.softCap & cpu_to_le16(0x100)) { 70048c2ecf20Sopenharmony_ci range->encoding_size[1] = 13; 70058c2ecf20Sopenharmony_ci range->num_encoding_sizes = 2; 70068c2ecf20Sopenharmony_ci } else 70078c2ecf20Sopenharmony_ci range->num_encoding_sizes = 1; 70088c2ecf20Sopenharmony_ci range->max_encoding_tokens = 70098c2ecf20Sopenharmony_ci cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1; 70108c2ecf20Sopenharmony_ci } else { 70118c2ecf20Sopenharmony_ci range->num_encoding_sizes = 0; 70128c2ecf20Sopenharmony_ci range->max_encoding_tokens = 0; 70138c2ecf20Sopenharmony_ci } 70148c2ecf20Sopenharmony_ci range->min_pmp = 0; 70158c2ecf20Sopenharmony_ci range->max_pmp = 5000000; /* 5 secs */ 70168c2ecf20Sopenharmony_ci range->min_pmt = 0; 70178c2ecf20Sopenharmony_ci range->max_pmt = 65535 * 1024; /* ??? */ 70188c2ecf20Sopenharmony_ci range->pmp_flags = IW_POWER_PERIOD; 70198c2ecf20Sopenharmony_ci range->pmt_flags = IW_POWER_TIMEOUT; 70208c2ecf20Sopenharmony_ci range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; 70218c2ecf20Sopenharmony_ci 70228c2ecf20Sopenharmony_ci /* Transmit Power - values are in mW */ 70238c2ecf20Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 70248c2ecf20Sopenharmony_ci range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]); 70258c2ecf20Sopenharmony_ci if (range->txpower[i] == 0) 70268c2ecf20Sopenharmony_ci break; 70278c2ecf20Sopenharmony_ci } 70288c2ecf20Sopenharmony_ci range->num_txpower = i; 70298c2ecf20Sopenharmony_ci range->txpower_capa = IW_TXPOW_MWATT; 70308c2ecf20Sopenharmony_ci range->we_version_source = 19; 70318c2ecf20Sopenharmony_ci range->we_version_compiled = WIRELESS_EXT; 70328c2ecf20Sopenharmony_ci range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; 70338c2ecf20Sopenharmony_ci range->retry_flags = IW_RETRY_LIMIT; 70348c2ecf20Sopenharmony_ci range->r_time_flags = IW_RETRY_LIFETIME; 70358c2ecf20Sopenharmony_ci range->min_retry = 1; 70368c2ecf20Sopenharmony_ci range->max_retry = 65535; 70378c2ecf20Sopenharmony_ci range->min_r_time = 1024; 70388c2ecf20Sopenharmony_ci range->max_r_time = 65535 * 1024; 70398c2ecf20Sopenharmony_ci 70408c2ecf20Sopenharmony_ci /* Event capability (kernel + driver) */ 70418c2ecf20Sopenharmony_ci range->event_capa[0] = (IW_EVENT_CAPA_K_0 | 70428c2ecf20Sopenharmony_ci IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | 70438c2ecf20Sopenharmony_ci IW_EVENT_CAPA_MASK(SIOCGIWAP) | 70448c2ecf20Sopenharmony_ci IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); 70458c2ecf20Sopenharmony_ci range->event_capa[1] = IW_EVENT_CAPA_K_1; 70468c2ecf20Sopenharmony_ci range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP); 70478c2ecf20Sopenharmony_ci return 0; 70488c2ecf20Sopenharmony_ci} 70498c2ecf20Sopenharmony_ci 70508c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 70518c2ecf20Sopenharmony_ci/* 70528c2ecf20Sopenharmony_ci * Wireless Handler : set Power Management 70538c2ecf20Sopenharmony_ci */ 70548c2ecf20Sopenharmony_cistatic int airo_set_power(struct net_device *dev, 70558c2ecf20Sopenharmony_ci struct iw_request_info *info, 70568c2ecf20Sopenharmony_ci struct iw_param *vwrq, 70578c2ecf20Sopenharmony_ci char *extra) 70588c2ecf20Sopenharmony_ci{ 70598c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 70608c2ecf20Sopenharmony_ci 70618c2ecf20Sopenharmony_ci readConfigRid(local, 1); 70628c2ecf20Sopenharmony_ci if (vwrq->disabled) { 70638c2ecf20Sopenharmony_ci if (sniffing_mode(local)) 70648c2ecf20Sopenharmony_ci return -EINVAL; 70658c2ecf20Sopenharmony_ci local->config.powerSaveMode = POWERSAVE_CAM; 70668c2ecf20Sopenharmony_ci local->config.rmode &= ~RXMODE_MASK; 70678c2ecf20Sopenharmony_ci local->config.rmode |= RXMODE_BC_MC_ADDR; 70688c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 70698c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 70708c2ecf20Sopenharmony_ci } 70718c2ecf20Sopenharmony_ci if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { 70728c2ecf20Sopenharmony_ci local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024); 70738c2ecf20Sopenharmony_ci local->config.powerSaveMode = POWERSAVE_PSPCAM; 70748c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 70758c2ecf20Sopenharmony_ci } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { 70768c2ecf20Sopenharmony_ci local->config.fastListenInterval = 70778c2ecf20Sopenharmony_ci local->config.listenInterval = 70788c2ecf20Sopenharmony_ci cpu_to_le16((vwrq->value + 500) / 1024); 70798c2ecf20Sopenharmony_ci local->config.powerSaveMode = POWERSAVE_PSPCAM; 70808c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 70818c2ecf20Sopenharmony_ci } 70828c2ecf20Sopenharmony_ci switch (vwrq->flags & IW_POWER_MODE) { 70838c2ecf20Sopenharmony_ci case IW_POWER_UNICAST_R: 70848c2ecf20Sopenharmony_ci if (sniffing_mode(local)) 70858c2ecf20Sopenharmony_ci return -EINVAL; 70868c2ecf20Sopenharmony_ci local->config.rmode &= ~RXMODE_MASK; 70878c2ecf20Sopenharmony_ci local->config.rmode |= RXMODE_ADDR; 70888c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 70898c2ecf20Sopenharmony_ci break; 70908c2ecf20Sopenharmony_ci case IW_POWER_ALL_R: 70918c2ecf20Sopenharmony_ci if (sniffing_mode(local)) 70928c2ecf20Sopenharmony_ci return -EINVAL; 70938c2ecf20Sopenharmony_ci local->config.rmode &= ~RXMODE_MASK; 70948c2ecf20Sopenharmony_ci local->config.rmode |= RXMODE_BC_MC_ADDR; 70958c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 70968c2ecf20Sopenharmony_ci case IW_POWER_ON: 70978c2ecf20Sopenharmony_ci /* This is broken, fixme ;-) */ 70988c2ecf20Sopenharmony_ci break; 70998c2ecf20Sopenharmony_ci default: 71008c2ecf20Sopenharmony_ci return -EINVAL; 71018c2ecf20Sopenharmony_ci } 71028c2ecf20Sopenharmony_ci // Note : we may want to factor local->need_commit here 71038c2ecf20Sopenharmony_ci // Note2 : may also want to factor RXMODE_RFMON test 71048c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 71058c2ecf20Sopenharmony_ci} 71068c2ecf20Sopenharmony_ci 71078c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 71088c2ecf20Sopenharmony_ci/* 71098c2ecf20Sopenharmony_ci * Wireless Handler : get Power Management 71108c2ecf20Sopenharmony_ci */ 71118c2ecf20Sopenharmony_cistatic int airo_get_power(struct net_device *dev, 71128c2ecf20Sopenharmony_ci struct iw_request_info *info, 71138c2ecf20Sopenharmony_ci struct iw_param *vwrq, 71148c2ecf20Sopenharmony_ci char *extra) 71158c2ecf20Sopenharmony_ci{ 71168c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 71178c2ecf20Sopenharmony_ci __le16 mode; 71188c2ecf20Sopenharmony_ci 71198c2ecf20Sopenharmony_ci readConfigRid(local, 1); 71208c2ecf20Sopenharmony_ci mode = local->config.powerSaveMode; 71218c2ecf20Sopenharmony_ci if ((vwrq->disabled = (mode == POWERSAVE_CAM))) 71228c2ecf20Sopenharmony_ci return 0; 71238c2ecf20Sopenharmony_ci if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { 71248c2ecf20Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024; 71258c2ecf20Sopenharmony_ci vwrq->flags = IW_POWER_TIMEOUT; 71268c2ecf20Sopenharmony_ci } else { 71278c2ecf20Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024; 71288c2ecf20Sopenharmony_ci vwrq->flags = IW_POWER_PERIOD; 71298c2ecf20Sopenharmony_ci } 71308c2ecf20Sopenharmony_ci if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR) 71318c2ecf20Sopenharmony_ci vwrq->flags |= IW_POWER_UNICAST_R; 71328c2ecf20Sopenharmony_ci else 71338c2ecf20Sopenharmony_ci vwrq->flags |= IW_POWER_ALL_R; 71348c2ecf20Sopenharmony_ci 71358c2ecf20Sopenharmony_ci return 0; 71368c2ecf20Sopenharmony_ci} 71378c2ecf20Sopenharmony_ci 71388c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 71398c2ecf20Sopenharmony_ci/* 71408c2ecf20Sopenharmony_ci * Wireless Handler : set Sensitivity 71418c2ecf20Sopenharmony_ci */ 71428c2ecf20Sopenharmony_cistatic int airo_set_sens(struct net_device *dev, 71438c2ecf20Sopenharmony_ci struct iw_request_info *info, 71448c2ecf20Sopenharmony_ci struct iw_param *vwrq, 71458c2ecf20Sopenharmony_ci char *extra) 71468c2ecf20Sopenharmony_ci{ 71478c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 71488c2ecf20Sopenharmony_ci 71498c2ecf20Sopenharmony_ci readConfigRid(local, 1); 71508c2ecf20Sopenharmony_ci local->config.rssiThreshold = 71518c2ecf20Sopenharmony_ci cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value); 71528c2ecf20Sopenharmony_ci set_bit (FLAG_COMMIT, &local->flags); 71538c2ecf20Sopenharmony_ci 71548c2ecf20Sopenharmony_ci return -EINPROGRESS; /* Call commit handler */ 71558c2ecf20Sopenharmony_ci} 71568c2ecf20Sopenharmony_ci 71578c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 71588c2ecf20Sopenharmony_ci/* 71598c2ecf20Sopenharmony_ci * Wireless Handler : get Sensitivity 71608c2ecf20Sopenharmony_ci */ 71618c2ecf20Sopenharmony_cistatic int airo_get_sens(struct net_device *dev, 71628c2ecf20Sopenharmony_ci struct iw_request_info *info, 71638c2ecf20Sopenharmony_ci struct iw_param *vwrq, 71648c2ecf20Sopenharmony_ci char *extra) 71658c2ecf20Sopenharmony_ci{ 71668c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 71678c2ecf20Sopenharmony_ci 71688c2ecf20Sopenharmony_ci readConfigRid(local, 1); 71698c2ecf20Sopenharmony_ci vwrq->value = le16_to_cpu(local->config.rssiThreshold); 71708c2ecf20Sopenharmony_ci vwrq->disabled = (vwrq->value == 0); 71718c2ecf20Sopenharmony_ci vwrq->fixed = 1; 71728c2ecf20Sopenharmony_ci 71738c2ecf20Sopenharmony_ci return 0; 71748c2ecf20Sopenharmony_ci} 71758c2ecf20Sopenharmony_ci 71768c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 71778c2ecf20Sopenharmony_ci/* 71788c2ecf20Sopenharmony_ci * Wireless Handler : get AP List 71798c2ecf20Sopenharmony_ci * Note : this is deprecated in favor of IWSCAN 71808c2ecf20Sopenharmony_ci */ 71818c2ecf20Sopenharmony_cistatic int airo_get_aplist(struct net_device *dev, 71828c2ecf20Sopenharmony_ci struct iw_request_info *info, 71838c2ecf20Sopenharmony_ci struct iw_point *dwrq, 71848c2ecf20Sopenharmony_ci char *extra) 71858c2ecf20Sopenharmony_ci{ 71868c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 71878c2ecf20Sopenharmony_ci struct sockaddr *address = (struct sockaddr *) extra; 71888c2ecf20Sopenharmony_ci struct iw_quality *qual; 71898c2ecf20Sopenharmony_ci BSSListRid BSSList; 71908c2ecf20Sopenharmony_ci int i; 71918c2ecf20Sopenharmony_ci int loseSync = capable(CAP_NET_ADMIN) ? 1: -1; 71928c2ecf20Sopenharmony_ci 71938c2ecf20Sopenharmony_ci qual = kmalloc_array(IW_MAX_AP, sizeof(*qual), GFP_KERNEL); 71948c2ecf20Sopenharmony_ci if (!qual) 71958c2ecf20Sopenharmony_ci return -ENOMEM; 71968c2ecf20Sopenharmony_ci 71978c2ecf20Sopenharmony_ci for (i = 0; i < IW_MAX_AP; i++) { 71988c2ecf20Sopenharmony_ci u16 dBm; 71998c2ecf20Sopenharmony_ci if (readBSSListRid(local, loseSync, &BSSList)) 72008c2ecf20Sopenharmony_ci break; 72018c2ecf20Sopenharmony_ci loseSync = 0; 72028c2ecf20Sopenharmony_ci memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN); 72038c2ecf20Sopenharmony_ci address[i].sa_family = ARPHRD_ETHER; 72048c2ecf20Sopenharmony_ci dBm = le16_to_cpu(BSSList.dBm); 72058c2ecf20Sopenharmony_ci if (local->rssi) { 72068c2ecf20Sopenharmony_ci qual[i].level = 0x100 - dBm; 72078c2ecf20Sopenharmony_ci qual[i].qual = airo_dbm_to_pct(local->rssi, dBm); 72088c2ecf20Sopenharmony_ci qual[i].updated = IW_QUAL_QUAL_UPDATED 72098c2ecf20Sopenharmony_ci | IW_QUAL_LEVEL_UPDATED 72108c2ecf20Sopenharmony_ci | IW_QUAL_DBM; 72118c2ecf20Sopenharmony_ci } else { 72128c2ecf20Sopenharmony_ci qual[i].level = (dBm + 321) / 2; 72138c2ecf20Sopenharmony_ci qual[i].qual = 0; 72148c2ecf20Sopenharmony_ci qual[i].updated = IW_QUAL_QUAL_INVALID 72158c2ecf20Sopenharmony_ci | IW_QUAL_LEVEL_UPDATED 72168c2ecf20Sopenharmony_ci | IW_QUAL_DBM; 72178c2ecf20Sopenharmony_ci } 72188c2ecf20Sopenharmony_ci qual[i].noise = local->wstats.qual.noise; 72198c2ecf20Sopenharmony_ci if (BSSList.index == cpu_to_le16(0xffff)) 72208c2ecf20Sopenharmony_ci break; 72218c2ecf20Sopenharmony_ci } 72228c2ecf20Sopenharmony_ci if (!i) { 72238c2ecf20Sopenharmony_ci StatusRid status_rid; /* Card status info */ 72248c2ecf20Sopenharmony_ci readStatusRid(local, &status_rid, 1); 72258c2ecf20Sopenharmony_ci for (i = 0; 72268c2ecf20Sopenharmony_ci i < min(IW_MAX_AP, 4) && 72278c2ecf20Sopenharmony_ci (status_rid.bssid[i][0] 72288c2ecf20Sopenharmony_ci & status_rid.bssid[i][1] 72298c2ecf20Sopenharmony_ci & status_rid.bssid[i][2] 72308c2ecf20Sopenharmony_ci & status_rid.bssid[i][3] 72318c2ecf20Sopenharmony_ci & status_rid.bssid[i][4] 72328c2ecf20Sopenharmony_ci & status_rid.bssid[i][5])!=0xff && 72338c2ecf20Sopenharmony_ci (status_rid.bssid[i][0] 72348c2ecf20Sopenharmony_ci | status_rid.bssid[i][1] 72358c2ecf20Sopenharmony_ci | status_rid.bssid[i][2] 72368c2ecf20Sopenharmony_ci | status_rid.bssid[i][3] 72378c2ecf20Sopenharmony_ci | status_rid.bssid[i][4] 72388c2ecf20Sopenharmony_ci | status_rid.bssid[i][5]); 72398c2ecf20Sopenharmony_ci i++) { 72408c2ecf20Sopenharmony_ci memcpy(address[i].sa_data, 72418c2ecf20Sopenharmony_ci status_rid.bssid[i], ETH_ALEN); 72428c2ecf20Sopenharmony_ci address[i].sa_family = ARPHRD_ETHER; 72438c2ecf20Sopenharmony_ci } 72448c2ecf20Sopenharmony_ci } else { 72458c2ecf20Sopenharmony_ci dwrq->flags = 1; /* Should be define'd */ 72468c2ecf20Sopenharmony_ci memcpy(extra + sizeof(struct sockaddr) * i, qual, 72478c2ecf20Sopenharmony_ci sizeof(struct iw_quality) * i); 72488c2ecf20Sopenharmony_ci } 72498c2ecf20Sopenharmony_ci dwrq->length = i; 72508c2ecf20Sopenharmony_ci 72518c2ecf20Sopenharmony_ci kfree(qual); 72528c2ecf20Sopenharmony_ci return 0; 72538c2ecf20Sopenharmony_ci} 72548c2ecf20Sopenharmony_ci 72558c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 72568c2ecf20Sopenharmony_ci/* 72578c2ecf20Sopenharmony_ci * Wireless Handler : Initiate Scan 72588c2ecf20Sopenharmony_ci */ 72598c2ecf20Sopenharmony_cistatic int airo_set_scan(struct net_device *dev, 72608c2ecf20Sopenharmony_ci struct iw_request_info *info, 72618c2ecf20Sopenharmony_ci struct iw_point *dwrq, 72628c2ecf20Sopenharmony_ci char *extra) 72638c2ecf20Sopenharmony_ci{ 72648c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 72658c2ecf20Sopenharmony_ci Cmd cmd; 72668c2ecf20Sopenharmony_ci Resp rsp; 72678c2ecf20Sopenharmony_ci int wake = 0; 72688c2ecf20Sopenharmony_ci APListRid APList_rid_empty; 72698c2ecf20Sopenharmony_ci 72708c2ecf20Sopenharmony_ci /* Note : you may have realised that, as this is a SET operation, 72718c2ecf20Sopenharmony_ci * this is privileged and therefore a normal user can't 72728c2ecf20Sopenharmony_ci * perform scanning. 72738c2ecf20Sopenharmony_ci * This is not an error, while the device perform scanning, 72748c2ecf20Sopenharmony_ci * traffic doesn't flow, so it's a perfect DoS... 72758c2ecf20Sopenharmony_ci * Jean II */ 72768c2ecf20Sopenharmony_ci if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN; 72778c2ecf20Sopenharmony_ci 72788c2ecf20Sopenharmony_ci if (down_interruptible(&ai->sem)) 72798c2ecf20Sopenharmony_ci return -ERESTARTSYS; 72808c2ecf20Sopenharmony_ci 72818c2ecf20Sopenharmony_ci /* If there's already a scan in progress, don't 72828c2ecf20Sopenharmony_ci * trigger another one. */ 72838c2ecf20Sopenharmony_ci if (ai->scan_timeout > 0) 72848c2ecf20Sopenharmony_ci goto out; 72858c2ecf20Sopenharmony_ci 72868c2ecf20Sopenharmony_ci /* Clear APList as it affects scan results */ 72878c2ecf20Sopenharmony_ci memset(&APList_rid_empty, 0, sizeof(APList_rid_empty)); 72888c2ecf20Sopenharmony_ci APList_rid_empty.len = cpu_to_le16(sizeof(APList_rid_empty)); 72898c2ecf20Sopenharmony_ci disable_MAC(ai, 2); 72908c2ecf20Sopenharmony_ci writeAPListRid(ai, &APList_rid_empty, 0); 72918c2ecf20Sopenharmony_ci enable_MAC(ai, 0); 72928c2ecf20Sopenharmony_ci 72938c2ecf20Sopenharmony_ci /* Initiate a scan command */ 72948c2ecf20Sopenharmony_ci ai->scan_timeout = RUN_AT(3*HZ); 72958c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 72968c2ecf20Sopenharmony_ci cmd.cmd = CMD_LISTBSS; 72978c2ecf20Sopenharmony_ci issuecommand(ai, &cmd, &rsp); 72988c2ecf20Sopenharmony_ci wake = 1; 72998c2ecf20Sopenharmony_ci 73008c2ecf20Sopenharmony_ciout: 73018c2ecf20Sopenharmony_ci up(&ai->sem); 73028c2ecf20Sopenharmony_ci if (wake) 73038c2ecf20Sopenharmony_ci wake_up_interruptible(&ai->thr_wait); 73048c2ecf20Sopenharmony_ci return 0; 73058c2ecf20Sopenharmony_ci} 73068c2ecf20Sopenharmony_ci 73078c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 73088c2ecf20Sopenharmony_ci/* 73098c2ecf20Sopenharmony_ci * Translate scan data returned from the card to a card independent 73108c2ecf20Sopenharmony_ci * format that the Wireless Tools will understand - Jean II 73118c2ecf20Sopenharmony_ci */ 73128c2ecf20Sopenharmony_cistatic inline char *airo_translate_scan(struct net_device *dev, 73138c2ecf20Sopenharmony_ci struct iw_request_info *info, 73148c2ecf20Sopenharmony_ci char *current_ev, 73158c2ecf20Sopenharmony_ci char *end_buf, 73168c2ecf20Sopenharmony_ci BSSListRid *bss) 73178c2ecf20Sopenharmony_ci{ 73188c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 73198c2ecf20Sopenharmony_ci struct iw_event iwe; /* Temporary buffer */ 73208c2ecf20Sopenharmony_ci __le16 capabilities; 73218c2ecf20Sopenharmony_ci char * current_val; /* For rates */ 73228c2ecf20Sopenharmony_ci int i; 73238c2ecf20Sopenharmony_ci char * buf; 73248c2ecf20Sopenharmony_ci u16 dBm; 73258c2ecf20Sopenharmony_ci 73268c2ecf20Sopenharmony_ci /* First entry *MUST* be the AP MAC address */ 73278c2ecf20Sopenharmony_ci iwe.cmd = SIOCGIWAP; 73288c2ecf20Sopenharmony_ci iwe.u.ap_addr.sa_family = ARPHRD_ETHER; 73298c2ecf20Sopenharmony_ci memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); 73308c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, end_buf, 73318c2ecf20Sopenharmony_ci &iwe, IW_EV_ADDR_LEN); 73328c2ecf20Sopenharmony_ci 73338c2ecf20Sopenharmony_ci /* Other entries will be displayed in the order we give them */ 73348c2ecf20Sopenharmony_ci 73358c2ecf20Sopenharmony_ci /* Add the ESSID */ 73368c2ecf20Sopenharmony_ci iwe.u.data.length = bss->ssidLen; 73378c2ecf20Sopenharmony_ci if (iwe.u.data.length > 32) 73388c2ecf20Sopenharmony_ci iwe.u.data.length = 32; 73398c2ecf20Sopenharmony_ci iwe.cmd = SIOCGIWESSID; 73408c2ecf20Sopenharmony_ci iwe.u.data.flags = 1; 73418c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_point(info, current_ev, end_buf, 73428c2ecf20Sopenharmony_ci &iwe, bss->ssid); 73438c2ecf20Sopenharmony_ci 73448c2ecf20Sopenharmony_ci /* Add mode */ 73458c2ecf20Sopenharmony_ci iwe.cmd = SIOCGIWMODE; 73468c2ecf20Sopenharmony_ci capabilities = bss->cap; 73478c2ecf20Sopenharmony_ci if (capabilities & (CAP_ESS | CAP_IBSS)) { 73488c2ecf20Sopenharmony_ci if (capabilities & CAP_ESS) 73498c2ecf20Sopenharmony_ci iwe.u.mode = IW_MODE_MASTER; 73508c2ecf20Sopenharmony_ci else 73518c2ecf20Sopenharmony_ci iwe.u.mode = IW_MODE_ADHOC; 73528c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, end_buf, 73538c2ecf20Sopenharmony_ci &iwe, IW_EV_UINT_LEN); 73548c2ecf20Sopenharmony_ci } 73558c2ecf20Sopenharmony_ci 73568c2ecf20Sopenharmony_ci /* Add frequency */ 73578c2ecf20Sopenharmony_ci iwe.cmd = SIOCGIWFREQ; 73588c2ecf20Sopenharmony_ci iwe.u.freq.m = le16_to_cpu(bss->dsChannel); 73598c2ecf20Sopenharmony_ci iwe.u.freq.m = 100000 * 73608c2ecf20Sopenharmony_ci ieee80211_channel_to_frequency(iwe.u.freq.m, NL80211_BAND_2GHZ); 73618c2ecf20Sopenharmony_ci iwe.u.freq.e = 1; 73628c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, end_buf, 73638c2ecf20Sopenharmony_ci &iwe, IW_EV_FREQ_LEN); 73648c2ecf20Sopenharmony_ci 73658c2ecf20Sopenharmony_ci dBm = le16_to_cpu(bss->dBm); 73668c2ecf20Sopenharmony_ci 73678c2ecf20Sopenharmony_ci /* Add quality statistics */ 73688c2ecf20Sopenharmony_ci iwe.cmd = IWEVQUAL; 73698c2ecf20Sopenharmony_ci if (ai->rssi) { 73708c2ecf20Sopenharmony_ci iwe.u.qual.level = 0x100 - dBm; 73718c2ecf20Sopenharmony_ci iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm); 73728c2ecf20Sopenharmony_ci iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED 73738c2ecf20Sopenharmony_ci | IW_QUAL_LEVEL_UPDATED 73748c2ecf20Sopenharmony_ci | IW_QUAL_DBM; 73758c2ecf20Sopenharmony_ci } else { 73768c2ecf20Sopenharmony_ci iwe.u.qual.level = (dBm + 321) / 2; 73778c2ecf20Sopenharmony_ci iwe.u.qual.qual = 0; 73788c2ecf20Sopenharmony_ci iwe.u.qual.updated = IW_QUAL_QUAL_INVALID 73798c2ecf20Sopenharmony_ci | IW_QUAL_LEVEL_UPDATED 73808c2ecf20Sopenharmony_ci | IW_QUAL_DBM; 73818c2ecf20Sopenharmony_ci } 73828c2ecf20Sopenharmony_ci iwe.u.qual.noise = ai->wstats.qual.noise; 73838c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_event(info, current_ev, end_buf, 73848c2ecf20Sopenharmony_ci &iwe, IW_EV_QUAL_LEN); 73858c2ecf20Sopenharmony_ci 73868c2ecf20Sopenharmony_ci /* Add encryption capability */ 73878c2ecf20Sopenharmony_ci iwe.cmd = SIOCGIWENCODE; 73888c2ecf20Sopenharmony_ci if (capabilities & CAP_PRIVACY) 73898c2ecf20Sopenharmony_ci iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; 73908c2ecf20Sopenharmony_ci else 73918c2ecf20Sopenharmony_ci iwe.u.data.flags = IW_ENCODE_DISABLED; 73928c2ecf20Sopenharmony_ci iwe.u.data.length = 0; 73938c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_point(info, current_ev, end_buf, 73948c2ecf20Sopenharmony_ci &iwe, bss->ssid); 73958c2ecf20Sopenharmony_ci 73968c2ecf20Sopenharmony_ci /* Rate : stuffing multiple values in a single event require a bit 73978c2ecf20Sopenharmony_ci * more of magic - Jean II */ 73988c2ecf20Sopenharmony_ci current_val = current_ev + iwe_stream_lcp_len(info); 73998c2ecf20Sopenharmony_ci 74008c2ecf20Sopenharmony_ci iwe.cmd = SIOCGIWRATE; 74018c2ecf20Sopenharmony_ci /* Those two flags are ignored... */ 74028c2ecf20Sopenharmony_ci iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; 74038c2ecf20Sopenharmony_ci /* Max 8 values */ 74048c2ecf20Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 74058c2ecf20Sopenharmony_ci /* NULL terminated */ 74068c2ecf20Sopenharmony_ci if (bss->rates[i] == 0) 74078c2ecf20Sopenharmony_ci break; 74088c2ecf20Sopenharmony_ci /* Bit rate given in 500 kb/s units (+ 0x80) */ 74098c2ecf20Sopenharmony_ci iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000); 74108c2ecf20Sopenharmony_ci /* Add new value to event */ 74118c2ecf20Sopenharmony_ci current_val = iwe_stream_add_value(info, current_ev, 74128c2ecf20Sopenharmony_ci current_val, end_buf, 74138c2ecf20Sopenharmony_ci &iwe, IW_EV_PARAM_LEN); 74148c2ecf20Sopenharmony_ci } 74158c2ecf20Sopenharmony_ci /* Check if we added any event */ 74168c2ecf20Sopenharmony_ci if ((current_val - current_ev) > iwe_stream_lcp_len(info)) 74178c2ecf20Sopenharmony_ci current_ev = current_val; 74188c2ecf20Sopenharmony_ci 74198c2ecf20Sopenharmony_ci /* Beacon interval */ 74208c2ecf20Sopenharmony_ci buf = kmalloc(30, GFP_KERNEL); 74218c2ecf20Sopenharmony_ci if (buf) { 74228c2ecf20Sopenharmony_ci iwe.cmd = IWEVCUSTOM; 74238c2ecf20Sopenharmony_ci sprintf(buf, "bcn_int=%d", bss->beaconInterval); 74248c2ecf20Sopenharmony_ci iwe.u.data.length = strlen(buf); 74258c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_point(info, current_ev, end_buf, 74268c2ecf20Sopenharmony_ci &iwe, buf); 74278c2ecf20Sopenharmony_ci kfree(buf); 74288c2ecf20Sopenharmony_ci } 74298c2ecf20Sopenharmony_ci 74308c2ecf20Sopenharmony_ci /* Put WPA/RSN Information Elements into the event stream */ 74318c2ecf20Sopenharmony_ci if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) { 74328c2ecf20Sopenharmony_ci unsigned int num_null_ies = 0; 74338c2ecf20Sopenharmony_ci u16 length = sizeof (bss->extra.iep); 74348c2ecf20Sopenharmony_ci u8 *ie = (void *)&bss->extra.iep; 74358c2ecf20Sopenharmony_ci 74368c2ecf20Sopenharmony_ci while ((length >= 2) && (num_null_ies < 2)) { 74378c2ecf20Sopenharmony_ci if (2 + ie[1] > length) { 74388c2ecf20Sopenharmony_ci /* Invalid element, don't continue parsing IE */ 74398c2ecf20Sopenharmony_ci break; 74408c2ecf20Sopenharmony_ci } 74418c2ecf20Sopenharmony_ci 74428c2ecf20Sopenharmony_ci switch (ie[0]) { 74438c2ecf20Sopenharmony_ci case WLAN_EID_SSID: 74448c2ecf20Sopenharmony_ci /* Two zero-length SSID elements 74458c2ecf20Sopenharmony_ci * mean we're done parsing elements */ 74468c2ecf20Sopenharmony_ci if (!ie[1]) 74478c2ecf20Sopenharmony_ci num_null_ies++; 74488c2ecf20Sopenharmony_ci break; 74498c2ecf20Sopenharmony_ci 74508c2ecf20Sopenharmony_ci case WLAN_EID_VENDOR_SPECIFIC: 74518c2ecf20Sopenharmony_ci if (ie[1] >= 4 && 74528c2ecf20Sopenharmony_ci ie[2] == 0x00 && 74538c2ecf20Sopenharmony_ci ie[3] == 0x50 && 74548c2ecf20Sopenharmony_ci ie[4] == 0xf2 && 74558c2ecf20Sopenharmony_ci ie[5] == 0x01) { 74568c2ecf20Sopenharmony_ci iwe.cmd = IWEVGENIE; 74578c2ecf20Sopenharmony_ci /* 64 is an arbitrary cut-off */ 74588c2ecf20Sopenharmony_ci iwe.u.data.length = min(ie[1] + 2, 74598c2ecf20Sopenharmony_ci 64); 74608c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_point( 74618c2ecf20Sopenharmony_ci info, current_ev, 74628c2ecf20Sopenharmony_ci end_buf, &iwe, ie); 74638c2ecf20Sopenharmony_ci } 74648c2ecf20Sopenharmony_ci break; 74658c2ecf20Sopenharmony_ci 74668c2ecf20Sopenharmony_ci case WLAN_EID_RSN: 74678c2ecf20Sopenharmony_ci iwe.cmd = IWEVGENIE; 74688c2ecf20Sopenharmony_ci /* 64 is an arbitrary cut-off */ 74698c2ecf20Sopenharmony_ci iwe.u.data.length = min(ie[1] + 2, 64); 74708c2ecf20Sopenharmony_ci current_ev = iwe_stream_add_point( 74718c2ecf20Sopenharmony_ci info, current_ev, end_buf, 74728c2ecf20Sopenharmony_ci &iwe, ie); 74738c2ecf20Sopenharmony_ci break; 74748c2ecf20Sopenharmony_ci 74758c2ecf20Sopenharmony_ci default: 74768c2ecf20Sopenharmony_ci break; 74778c2ecf20Sopenharmony_ci } 74788c2ecf20Sopenharmony_ci 74798c2ecf20Sopenharmony_ci length -= 2 + ie[1]; 74808c2ecf20Sopenharmony_ci ie += 2 + ie[1]; 74818c2ecf20Sopenharmony_ci } 74828c2ecf20Sopenharmony_ci } 74838c2ecf20Sopenharmony_ci return current_ev; 74848c2ecf20Sopenharmony_ci} 74858c2ecf20Sopenharmony_ci 74868c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 74878c2ecf20Sopenharmony_ci/* 74888c2ecf20Sopenharmony_ci * Wireless Handler : Read Scan Results 74898c2ecf20Sopenharmony_ci */ 74908c2ecf20Sopenharmony_cistatic int airo_get_scan(struct net_device *dev, 74918c2ecf20Sopenharmony_ci struct iw_request_info *info, 74928c2ecf20Sopenharmony_ci struct iw_point *dwrq, 74938c2ecf20Sopenharmony_ci char *extra) 74948c2ecf20Sopenharmony_ci{ 74958c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 74968c2ecf20Sopenharmony_ci BSSListElement *net; 74978c2ecf20Sopenharmony_ci int err = 0; 74988c2ecf20Sopenharmony_ci char *current_ev = extra; 74998c2ecf20Sopenharmony_ci 75008c2ecf20Sopenharmony_ci /* If a scan is in-progress, return -EAGAIN */ 75018c2ecf20Sopenharmony_ci if (ai->scan_timeout > 0) 75028c2ecf20Sopenharmony_ci return -EAGAIN; 75038c2ecf20Sopenharmony_ci 75048c2ecf20Sopenharmony_ci if (down_interruptible(&ai->sem)) 75058c2ecf20Sopenharmony_ci return -EAGAIN; 75068c2ecf20Sopenharmony_ci 75078c2ecf20Sopenharmony_ci list_for_each_entry (net, &ai->network_list, list) { 75088c2ecf20Sopenharmony_ci /* Translate to WE format this entry */ 75098c2ecf20Sopenharmony_ci current_ev = airo_translate_scan(dev, info, current_ev, 75108c2ecf20Sopenharmony_ci extra + dwrq->length, 75118c2ecf20Sopenharmony_ci &net->bss); 75128c2ecf20Sopenharmony_ci 75138c2ecf20Sopenharmony_ci /* Check if there is space for one more entry */ 75148c2ecf20Sopenharmony_ci if ((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) { 75158c2ecf20Sopenharmony_ci /* Ask user space to try again with a bigger buffer */ 75168c2ecf20Sopenharmony_ci err = -E2BIG; 75178c2ecf20Sopenharmony_ci goto out; 75188c2ecf20Sopenharmony_ci } 75198c2ecf20Sopenharmony_ci } 75208c2ecf20Sopenharmony_ci 75218c2ecf20Sopenharmony_ci /* Length of data */ 75228c2ecf20Sopenharmony_ci dwrq->length = (current_ev - extra); 75238c2ecf20Sopenharmony_ci dwrq->flags = 0; /* todo */ 75248c2ecf20Sopenharmony_ci 75258c2ecf20Sopenharmony_ciout: 75268c2ecf20Sopenharmony_ci up(&ai->sem); 75278c2ecf20Sopenharmony_ci return err; 75288c2ecf20Sopenharmony_ci} 75298c2ecf20Sopenharmony_ci 75308c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 75318c2ecf20Sopenharmony_ci/* 75328c2ecf20Sopenharmony_ci * Commit handler : called after a bunch of SET operations 75338c2ecf20Sopenharmony_ci */ 75348c2ecf20Sopenharmony_cistatic int airo_config_commit(struct net_device *dev, 75358c2ecf20Sopenharmony_ci struct iw_request_info *info, /* NULL */ 75368c2ecf20Sopenharmony_ci void *zwrq, /* NULL */ 75378c2ecf20Sopenharmony_ci char *extra) /* NULL */ 75388c2ecf20Sopenharmony_ci{ 75398c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 75408c2ecf20Sopenharmony_ci 75418c2ecf20Sopenharmony_ci if (!test_bit (FLAG_COMMIT, &local->flags)) 75428c2ecf20Sopenharmony_ci return 0; 75438c2ecf20Sopenharmony_ci 75448c2ecf20Sopenharmony_ci /* Some of the "SET" function may have modified some of the 75458c2ecf20Sopenharmony_ci * parameters. It's now time to commit them in the card */ 75468c2ecf20Sopenharmony_ci disable_MAC(local, 1); 75478c2ecf20Sopenharmony_ci if (test_bit (FLAG_RESET, &local->flags)) { 75488c2ecf20Sopenharmony_ci SsidRid SSID_rid; 75498c2ecf20Sopenharmony_ci 75508c2ecf20Sopenharmony_ci readSsidRid(local, &SSID_rid); 75518c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI,&local->flags)) 75528c2ecf20Sopenharmony_ci setup_card(local, dev->dev_addr, 1); 75538c2ecf20Sopenharmony_ci else 75548c2ecf20Sopenharmony_ci reset_airo_card(dev); 75558c2ecf20Sopenharmony_ci disable_MAC(local, 1); 75568c2ecf20Sopenharmony_ci writeSsidRid(local, &SSID_rid, 1); 75578c2ecf20Sopenharmony_ci writeAPListRid(local, &local->APList, 1); 75588c2ecf20Sopenharmony_ci } 75598c2ecf20Sopenharmony_ci if (down_interruptible(&local->sem)) 75608c2ecf20Sopenharmony_ci return -ERESTARTSYS; 75618c2ecf20Sopenharmony_ci writeConfigRid(local, 0); 75628c2ecf20Sopenharmony_ci enable_MAC(local, 0); 75638c2ecf20Sopenharmony_ci if (test_bit (FLAG_RESET, &local->flags)) 75648c2ecf20Sopenharmony_ci airo_set_promisc(local); 75658c2ecf20Sopenharmony_ci else 75668c2ecf20Sopenharmony_ci up(&local->sem); 75678c2ecf20Sopenharmony_ci 75688c2ecf20Sopenharmony_ci return 0; 75698c2ecf20Sopenharmony_ci} 75708c2ecf20Sopenharmony_ci 75718c2ecf20Sopenharmony_ci/*------------------------------------------------------------------*/ 75728c2ecf20Sopenharmony_ci/* 75738c2ecf20Sopenharmony_ci * Structures to export the Wireless Handlers 75748c2ecf20Sopenharmony_ci */ 75758c2ecf20Sopenharmony_ci 75768c2ecf20Sopenharmony_cistatic const struct iw_priv_args airo_private_args[] = { 75778c2ecf20Sopenharmony_ci/*{ cmd, set_args, get_args, name } */ 75788c2ecf20Sopenharmony_ci { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), 75798c2ecf20Sopenharmony_ci IW_PRIV_TYPE_BYTE | 2047, "airoioctl" }, 75808c2ecf20Sopenharmony_ci { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), 75818c2ecf20Sopenharmony_ci IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" }, 75828c2ecf20Sopenharmony_ci}; 75838c2ecf20Sopenharmony_ci 75848c2ecf20Sopenharmony_cistatic const iw_handler airo_handler[] = 75858c2ecf20Sopenharmony_ci{ 75868c2ecf20Sopenharmony_ci (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */ 75878c2ecf20Sopenharmony_ci (iw_handler) airo_get_name, /* SIOCGIWNAME */ 75888c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWNWID */ 75898c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWNWID */ 75908c2ecf20Sopenharmony_ci (iw_handler) airo_set_freq, /* SIOCSIWFREQ */ 75918c2ecf20Sopenharmony_ci (iw_handler) airo_get_freq, /* SIOCGIWFREQ */ 75928c2ecf20Sopenharmony_ci (iw_handler) airo_set_mode, /* SIOCSIWMODE */ 75938c2ecf20Sopenharmony_ci (iw_handler) airo_get_mode, /* SIOCGIWMODE */ 75948c2ecf20Sopenharmony_ci (iw_handler) airo_set_sens, /* SIOCSIWSENS */ 75958c2ecf20Sopenharmony_ci (iw_handler) airo_get_sens, /* SIOCGIWSENS */ 75968c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWRANGE */ 75978c2ecf20Sopenharmony_ci (iw_handler) airo_get_range, /* SIOCGIWRANGE */ 75988c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWPRIV */ 75998c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWPRIV */ 76008c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWSTATS */ 76018c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWSTATS */ 76028c2ecf20Sopenharmony_ci iw_handler_set_spy, /* SIOCSIWSPY */ 76038c2ecf20Sopenharmony_ci iw_handler_get_spy, /* SIOCGIWSPY */ 76048c2ecf20Sopenharmony_ci iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ 76058c2ecf20Sopenharmony_ci iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ 76068c2ecf20Sopenharmony_ci (iw_handler) airo_set_wap, /* SIOCSIWAP */ 76078c2ecf20Sopenharmony_ci (iw_handler) airo_get_wap, /* SIOCGIWAP */ 76088c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 76098c2ecf20Sopenharmony_ci (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */ 76108c2ecf20Sopenharmony_ci (iw_handler) airo_set_scan, /* SIOCSIWSCAN */ 76118c2ecf20Sopenharmony_ci (iw_handler) airo_get_scan, /* SIOCGIWSCAN */ 76128c2ecf20Sopenharmony_ci (iw_handler) airo_set_essid, /* SIOCSIWESSID */ 76138c2ecf20Sopenharmony_ci (iw_handler) airo_get_essid, /* SIOCGIWESSID */ 76148c2ecf20Sopenharmony_ci (iw_handler) airo_set_nick, /* SIOCSIWNICKN */ 76158c2ecf20Sopenharmony_ci (iw_handler) airo_get_nick, /* SIOCGIWNICKN */ 76168c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 76178c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 76188c2ecf20Sopenharmony_ci (iw_handler) airo_set_rate, /* SIOCSIWRATE */ 76198c2ecf20Sopenharmony_ci (iw_handler) airo_get_rate, /* SIOCGIWRATE */ 76208c2ecf20Sopenharmony_ci (iw_handler) airo_set_rts, /* SIOCSIWRTS */ 76218c2ecf20Sopenharmony_ci (iw_handler) airo_get_rts, /* SIOCGIWRTS */ 76228c2ecf20Sopenharmony_ci (iw_handler) airo_set_frag, /* SIOCSIWFRAG */ 76238c2ecf20Sopenharmony_ci (iw_handler) airo_get_frag, /* SIOCGIWFRAG */ 76248c2ecf20Sopenharmony_ci (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */ 76258c2ecf20Sopenharmony_ci (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */ 76268c2ecf20Sopenharmony_ci (iw_handler) airo_set_retry, /* SIOCSIWRETRY */ 76278c2ecf20Sopenharmony_ci (iw_handler) airo_get_retry, /* SIOCGIWRETRY */ 76288c2ecf20Sopenharmony_ci (iw_handler) airo_set_encode, /* SIOCSIWENCODE */ 76298c2ecf20Sopenharmony_ci (iw_handler) airo_get_encode, /* SIOCGIWENCODE */ 76308c2ecf20Sopenharmony_ci (iw_handler) airo_set_power, /* SIOCSIWPOWER */ 76318c2ecf20Sopenharmony_ci (iw_handler) airo_get_power, /* SIOCGIWPOWER */ 76328c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 76338c2ecf20Sopenharmony_ci (iw_handler) NULL, /* -- hole -- */ 76348c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWGENIE */ 76358c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCGIWGENIE */ 76368c2ecf20Sopenharmony_ci (iw_handler) airo_set_auth, /* SIOCSIWAUTH */ 76378c2ecf20Sopenharmony_ci (iw_handler) airo_get_auth, /* SIOCGIWAUTH */ 76388c2ecf20Sopenharmony_ci (iw_handler) airo_set_encodeext, /* SIOCSIWENCODEEXT */ 76398c2ecf20Sopenharmony_ci (iw_handler) airo_get_encodeext, /* SIOCGIWENCODEEXT */ 76408c2ecf20Sopenharmony_ci (iw_handler) NULL, /* SIOCSIWPMKSA */ 76418c2ecf20Sopenharmony_ci}; 76428c2ecf20Sopenharmony_ci 76438c2ecf20Sopenharmony_ci/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here. 76448c2ecf20Sopenharmony_ci * We want to force the use of the ioctl code, because those can't be 76458c2ecf20Sopenharmony_ci * won't work the iw_handler code (because they simultaneously read 76468c2ecf20Sopenharmony_ci * and write data and iw_handler can't do that). 76478c2ecf20Sopenharmony_ci * Note that it's perfectly legal to read/write on a single ioctl command, 76488c2ecf20Sopenharmony_ci * you just can't use iwpriv and need to force it via the ioctl handler. 76498c2ecf20Sopenharmony_ci * Jean II */ 76508c2ecf20Sopenharmony_cistatic const iw_handler airo_private_handler[] = 76518c2ecf20Sopenharmony_ci{ 76528c2ecf20Sopenharmony_ci NULL, /* SIOCIWFIRSTPRIV */ 76538c2ecf20Sopenharmony_ci}; 76548c2ecf20Sopenharmony_ci 76558c2ecf20Sopenharmony_cistatic const struct iw_handler_def airo_handler_def = 76568c2ecf20Sopenharmony_ci{ 76578c2ecf20Sopenharmony_ci .num_standard = ARRAY_SIZE(airo_handler), 76588c2ecf20Sopenharmony_ci .num_private = ARRAY_SIZE(airo_private_handler), 76598c2ecf20Sopenharmony_ci .num_private_args = ARRAY_SIZE(airo_private_args), 76608c2ecf20Sopenharmony_ci .standard = airo_handler, 76618c2ecf20Sopenharmony_ci .private = airo_private_handler, 76628c2ecf20Sopenharmony_ci .private_args = airo_private_args, 76638c2ecf20Sopenharmony_ci .get_wireless_stats = airo_get_wireless_stats, 76648c2ecf20Sopenharmony_ci}; 76658c2ecf20Sopenharmony_ci 76668c2ecf20Sopenharmony_ci/* 76678c2ecf20Sopenharmony_ci * This defines the configuration part of the Wireless Extensions 76688c2ecf20Sopenharmony_ci * Note : irq and spinlock protection will occur in the subroutines 76698c2ecf20Sopenharmony_ci * 76708c2ecf20Sopenharmony_ci * TODO : 76718c2ecf20Sopenharmony_ci * o Check input value more carefully and fill correct values in range 76728c2ecf20Sopenharmony_ci * o Test and shakeout the bugs (if any) 76738c2ecf20Sopenharmony_ci * 76748c2ecf20Sopenharmony_ci * Jean II 76758c2ecf20Sopenharmony_ci * 76768c2ecf20Sopenharmony_ci * Javier Achirica did a great job of merging code from the unnamed CISCO 76778c2ecf20Sopenharmony_ci * developer that added support for flashing the card. 76788c2ecf20Sopenharmony_ci */ 76798c2ecf20Sopenharmony_cistatic int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 76808c2ecf20Sopenharmony_ci{ 76818c2ecf20Sopenharmony_ci int rc = 0; 76828c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 76838c2ecf20Sopenharmony_ci 76848c2ecf20Sopenharmony_ci if (ai->power.event) 76858c2ecf20Sopenharmony_ci return 0; 76868c2ecf20Sopenharmony_ci 76878c2ecf20Sopenharmony_ci switch (cmd) { 76888c2ecf20Sopenharmony_ci#ifdef CISCO_EXT 76898c2ecf20Sopenharmony_ci case AIROIDIFC: 76908c2ecf20Sopenharmony_ci#ifdef AIROOLDIDIFC 76918c2ecf20Sopenharmony_ci case AIROOLDIDIFC: 76928c2ecf20Sopenharmony_ci#endif 76938c2ecf20Sopenharmony_ci { 76948c2ecf20Sopenharmony_ci int val = AIROMAGIC; 76958c2ecf20Sopenharmony_ci aironet_ioctl com; 76968c2ecf20Sopenharmony_ci if (copy_from_user(&com, rq->ifr_data, sizeof(com))) 76978c2ecf20Sopenharmony_ci rc = -EFAULT; 76988c2ecf20Sopenharmony_ci else if (copy_to_user(com.data, (char *)&val, sizeof(val))) 76998c2ecf20Sopenharmony_ci rc = -EFAULT; 77008c2ecf20Sopenharmony_ci } 77018c2ecf20Sopenharmony_ci break; 77028c2ecf20Sopenharmony_ci 77038c2ecf20Sopenharmony_ci case AIROIOCTL: 77048c2ecf20Sopenharmony_ci#ifdef AIROOLDIOCTL 77058c2ecf20Sopenharmony_ci case AIROOLDIOCTL: 77068c2ecf20Sopenharmony_ci#endif 77078c2ecf20Sopenharmony_ci /* Get the command struct and hand it off for evaluation by 77088c2ecf20Sopenharmony_ci * the proper subfunction 77098c2ecf20Sopenharmony_ci */ 77108c2ecf20Sopenharmony_ci { 77118c2ecf20Sopenharmony_ci aironet_ioctl com; 77128c2ecf20Sopenharmony_ci if (copy_from_user(&com, rq->ifr_data, sizeof(com))) { 77138c2ecf20Sopenharmony_ci rc = -EFAULT; 77148c2ecf20Sopenharmony_ci break; 77158c2ecf20Sopenharmony_ci } 77168c2ecf20Sopenharmony_ci 77178c2ecf20Sopenharmony_ci /* Separate R/W functions bracket legality here 77188c2ecf20Sopenharmony_ci */ 77198c2ecf20Sopenharmony_ci if (com.command == AIRORSWVERSION) { 77208c2ecf20Sopenharmony_ci if (copy_to_user(com.data, swversion, sizeof(swversion))) 77218c2ecf20Sopenharmony_ci rc = -EFAULT; 77228c2ecf20Sopenharmony_ci else 77238c2ecf20Sopenharmony_ci rc = 0; 77248c2ecf20Sopenharmony_ci } 77258c2ecf20Sopenharmony_ci else if (com.command <= AIRORRID) 77268c2ecf20Sopenharmony_ci rc = readrids(dev,&com); 77278c2ecf20Sopenharmony_ci else if (com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2)) 77288c2ecf20Sopenharmony_ci rc = writerids(dev,&com); 77298c2ecf20Sopenharmony_ci else if (com.command >= AIROFLSHRST && com.command <= AIRORESTART) 77308c2ecf20Sopenharmony_ci rc = flashcard(dev,&com); 77318c2ecf20Sopenharmony_ci else 77328c2ecf20Sopenharmony_ci rc = -EINVAL; /* Bad command in ioctl */ 77338c2ecf20Sopenharmony_ci } 77348c2ecf20Sopenharmony_ci break; 77358c2ecf20Sopenharmony_ci#endif /* CISCO_EXT */ 77368c2ecf20Sopenharmony_ci 77378c2ecf20Sopenharmony_ci // All other calls are currently unsupported 77388c2ecf20Sopenharmony_ci default: 77398c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 77408c2ecf20Sopenharmony_ci } 77418c2ecf20Sopenharmony_ci return rc; 77428c2ecf20Sopenharmony_ci} 77438c2ecf20Sopenharmony_ci 77448c2ecf20Sopenharmony_ci/* 77458c2ecf20Sopenharmony_ci * Get the Wireless stats out of the driver 77468c2ecf20Sopenharmony_ci * Note : irq and spinlock protection will occur in the subroutines 77478c2ecf20Sopenharmony_ci * 77488c2ecf20Sopenharmony_ci * TODO : 77498c2ecf20Sopenharmony_ci * o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs) 77508c2ecf20Sopenharmony_ci * 77518c2ecf20Sopenharmony_ci * Jean 77528c2ecf20Sopenharmony_ci */ 77538c2ecf20Sopenharmony_cistatic void airo_read_wireless_stats(struct airo_info *local) 77548c2ecf20Sopenharmony_ci{ 77558c2ecf20Sopenharmony_ci StatusRid status_rid; 77568c2ecf20Sopenharmony_ci StatsRid stats_rid; 77578c2ecf20Sopenharmony_ci CapabilityRid cap_rid; 77588c2ecf20Sopenharmony_ci __le32 *vals = stats_rid.vals; 77598c2ecf20Sopenharmony_ci 77608c2ecf20Sopenharmony_ci /* Get stats out of the card */ 77618c2ecf20Sopenharmony_ci clear_bit(JOB_WSTATS, &local->jobs); 77628c2ecf20Sopenharmony_ci if (local->power.event) { 77638c2ecf20Sopenharmony_ci up(&local->sem); 77648c2ecf20Sopenharmony_ci return; 77658c2ecf20Sopenharmony_ci } 77668c2ecf20Sopenharmony_ci readCapabilityRid(local, &cap_rid, 0); 77678c2ecf20Sopenharmony_ci readStatusRid(local, &status_rid, 0); 77688c2ecf20Sopenharmony_ci readStatsRid(local, &stats_rid, RID_STATS, 0); 77698c2ecf20Sopenharmony_ci up(&local->sem); 77708c2ecf20Sopenharmony_ci 77718c2ecf20Sopenharmony_ci /* The status */ 77728c2ecf20Sopenharmony_ci local->wstats.status = le16_to_cpu(status_rid.mode); 77738c2ecf20Sopenharmony_ci 77748c2ecf20Sopenharmony_ci /* Signal quality and co */ 77758c2ecf20Sopenharmony_ci if (local->rssi) { 77768c2ecf20Sopenharmony_ci local->wstats.qual.level = 77778c2ecf20Sopenharmony_ci airo_rssi_to_dbm(local->rssi, 77788c2ecf20Sopenharmony_ci le16_to_cpu(status_rid.sigQuality)); 77798c2ecf20Sopenharmony_ci /* normalizedSignalStrength appears to be a percentage */ 77808c2ecf20Sopenharmony_ci local->wstats.qual.qual = 77818c2ecf20Sopenharmony_ci le16_to_cpu(status_rid.normalizedSignalStrength); 77828c2ecf20Sopenharmony_ci } else { 77838c2ecf20Sopenharmony_ci local->wstats.qual.level = 77848c2ecf20Sopenharmony_ci (le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2; 77858c2ecf20Sopenharmony_ci local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid); 77868c2ecf20Sopenharmony_ci } 77878c2ecf20Sopenharmony_ci if (le16_to_cpu(status_rid.len) >= 124) { 77888c2ecf20Sopenharmony_ci local->wstats.qual.noise = 0x100 - status_rid.noisedBm; 77898c2ecf20Sopenharmony_ci local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; 77908c2ecf20Sopenharmony_ci } else { 77918c2ecf20Sopenharmony_ci local->wstats.qual.noise = 0; 77928c2ecf20Sopenharmony_ci local->wstats.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID | IW_QUAL_DBM; 77938c2ecf20Sopenharmony_ci } 77948c2ecf20Sopenharmony_ci 77958c2ecf20Sopenharmony_ci /* Packets discarded in the wireless adapter due to wireless 77968c2ecf20Sopenharmony_ci * specific problems */ 77978c2ecf20Sopenharmony_ci local->wstats.discard.nwid = le32_to_cpu(vals[56]) + 77988c2ecf20Sopenharmony_ci le32_to_cpu(vals[57]) + 77998c2ecf20Sopenharmony_ci le32_to_cpu(vals[58]); /* SSID Mismatch */ 78008c2ecf20Sopenharmony_ci local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */ 78018c2ecf20Sopenharmony_ci local->wstats.discard.fragment = le32_to_cpu(vals[30]); 78028c2ecf20Sopenharmony_ci local->wstats.discard.retries = le32_to_cpu(vals[10]); 78038c2ecf20Sopenharmony_ci local->wstats.discard.misc = le32_to_cpu(vals[1]) + 78048c2ecf20Sopenharmony_ci le32_to_cpu(vals[32]); 78058c2ecf20Sopenharmony_ci local->wstats.miss.beacon = le32_to_cpu(vals[34]); 78068c2ecf20Sopenharmony_ci} 78078c2ecf20Sopenharmony_ci 78088c2ecf20Sopenharmony_cistatic struct iw_statistics *airo_get_wireless_stats(struct net_device *dev) 78098c2ecf20Sopenharmony_ci{ 78108c2ecf20Sopenharmony_ci struct airo_info *local = dev->ml_priv; 78118c2ecf20Sopenharmony_ci 78128c2ecf20Sopenharmony_ci if (!test_bit(JOB_WSTATS, &local->jobs)) { 78138c2ecf20Sopenharmony_ci /* Get stats out of the card if available */ 78148c2ecf20Sopenharmony_ci if (down_trylock(&local->sem) != 0) { 78158c2ecf20Sopenharmony_ci set_bit(JOB_WSTATS, &local->jobs); 78168c2ecf20Sopenharmony_ci wake_up_interruptible(&local->thr_wait); 78178c2ecf20Sopenharmony_ci } else 78188c2ecf20Sopenharmony_ci airo_read_wireless_stats(local); 78198c2ecf20Sopenharmony_ci } 78208c2ecf20Sopenharmony_ci 78218c2ecf20Sopenharmony_ci return &local->wstats; 78228c2ecf20Sopenharmony_ci} 78238c2ecf20Sopenharmony_ci 78248c2ecf20Sopenharmony_ci#ifdef CISCO_EXT 78258c2ecf20Sopenharmony_ci/* 78268c2ecf20Sopenharmony_ci * This just translates from driver IOCTL codes to the command codes to 78278c2ecf20Sopenharmony_ci * feed to the radio's host interface. Things can be added/deleted 78288c2ecf20Sopenharmony_ci * as needed. This represents the READ side of control I/O to 78298c2ecf20Sopenharmony_ci * the card 78308c2ecf20Sopenharmony_ci */ 78318c2ecf20Sopenharmony_cistatic int readrids(struct net_device *dev, aironet_ioctl *comp) 78328c2ecf20Sopenharmony_ci{ 78338c2ecf20Sopenharmony_ci unsigned short ridcode; 78348c2ecf20Sopenharmony_ci unsigned char *iobuf; 78358c2ecf20Sopenharmony_ci int len; 78368c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 78378c2ecf20Sopenharmony_ci 78388c2ecf20Sopenharmony_ci if (test_bit(FLAG_FLASHING, &ai->flags)) 78398c2ecf20Sopenharmony_ci return -EIO; 78408c2ecf20Sopenharmony_ci 78418c2ecf20Sopenharmony_ci switch(comp->command) 78428c2ecf20Sopenharmony_ci { 78438c2ecf20Sopenharmony_ci case AIROGCAP: ridcode = RID_CAPABILITIES; break; 78448c2ecf20Sopenharmony_ci case AIROGCFG: ridcode = RID_CONFIG; 78458c2ecf20Sopenharmony_ci if (test_bit(FLAG_COMMIT, &ai->flags)) { 78468c2ecf20Sopenharmony_ci disable_MAC (ai, 1); 78478c2ecf20Sopenharmony_ci writeConfigRid (ai, 1); 78488c2ecf20Sopenharmony_ci enable_MAC(ai, 1); 78498c2ecf20Sopenharmony_ci } 78508c2ecf20Sopenharmony_ci break; 78518c2ecf20Sopenharmony_ci case AIROGSLIST: ridcode = RID_SSID; break; 78528c2ecf20Sopenharmony_ci case AIROGVLIST: ridcode = RID_APLIST; break; 78538c2ecf20Sopenharmony_ci case AIROGDRVNAM: ridcode = RID_DRVNAME; break; 78548c2ecf20Sopenharmony_ci case AIROGEHTENC: ridcode = RID_ETHERENCAP; break; 78558c2ecf20Sopenharmony_ci case AIROGWEPKTMP: ridcode = RID_WEP_TEMP; break; 78568c2ecf20Sopenharmony_ci case AIROGWEPKNV: ridcode = RID_WEP_PERM; break; 78578c2ecf20Sopenharmony_ci case AIROGSTAT: ridcode = RID_STATUS; break; 78588c2ecf20Sopenharmony_ci case AIROGSTATSD32: ridcode = RID_STATSDELTA; break; 78598c2ecf20Sopenharmony_ci case AIROGSTATSC32: ridcode = RID_STATS; break; 78608c2ecf20Sopenharmony_ci case AIROGMICSTATS: 78618c2ecf20Sopenharmony_ci if (copy_to_user(comp->data, &ai->micstats, 78628c2ecf20Sopenharmony_ci min((int)comp->len, (int)sizeof(ai->micstats)))) 78638c2ecf20Sopenharmony_ci return -EFAULT; 78648c2ecf20Sopenharmony_ci return 0; 78658c2ecf20Sopenharmony_ci case AIRORRID: ridcode = comp->ridnum; break; 78668c2ecf20Sopenharmony_ci default: 78678c2ecf20Sopenharmony_ci return -EINVAL; 78688c2ecf20Sopenharmony_ci } 78698c2ecf20Sopenharmony_ci 78708c2ecf20Sopenharmony_ci if (ridcode == RID_WEP_TEMP || ridcode == RID_WEP_PERM) { 78718c2ecf20Sopenharmony_ci /* Only super-user can read WEP keys */ 78728c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 78738c2ecf20Sopenharmony_ci return -EPERM; 78748c2ecf20Sopenharmony_ci } 78758c2ecf20Sopenharmony_ci 78768c2ecf20Sopenharmony_ci if ((iobuf = kzalloc(RIDSIZE, GFP_KERNEL)) == NULL) 78778c2ecf20Sopenharmony_ci return -ENOMEM; 78788c2ecf20Sopenharmony_ci 78798c2ecf20Sopenharmony_ci PC4500_readrid(ai, ridcode, iobuf, RIDSIZE, 1); 78808c2ecf20Sopenharmony_ci /* get the count of bytes in the rid docs say 1st 2 bytes is it. 78818c2ecf20Sopenharmony_ci * then return it to the user 78828c2ecf20Sopenharmony_ci * 9/22/2000 Honor user given length 78838c2ecf20Sopenharmony_ci */ 78848c2ecf20Sopenharmony_ci len = comp->len; 78858c2ecf20Sopenharmony_ci 78868c2ecf20Sopenharmony_ci if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) { 78878c2ecf20Sopenharmony_ci kfree (iobuf); 78888c2ecf20Sopenharmony_ci return -EFAULT; 78898c2ecf20Sopenharmony_ci } 78908c2ecf20Sopenharmony_ci kfree (iobuf); 78918c2ecf20Sopenharmony_ci return 0; 78928c2ecf20Sopenharmony_ci} 78938c2ecf20Sopenharmony_ci 78948c2ecf20Sopenharmony_ci/* 78958c2ecf20Sopenharmony_ci * Danger Will Robinson write the rids here 78968c2ecf20Sopenharmony_ci */ 78978c2ecf20Sopenharmony_ci 78988c2ecf20Sopenharmony_cistatic int writerids(struct net_device *dev, aironet_ioctl *comp) 78998c2ecf20Sopenharmony_ci{ 79008c2ecf20Sopenharmony_ci struct airo_info *ai = dev->ml_priv; 79018c2ecf20Sopenharmony_ci int ridcode; 79028c2ecf20Sopenharmony_ci int enabled; 79038c2ecf20Sopenharmony_ci int (*writer)(struct airo_info *, u16 rid, const void *, int, int); 79048c2ecf20Sopenharmony_ci unsigned char *iobuf; 79058c2ecf20Sopenharmony_ci 79068c2ecf20Sopenharmony_ci /* Only super-user can write RIDs */ 79078c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 79088c2ecf20Sopenharmony_ci return -EPERM; 79098c2ecf20Sopenharmony_ci 79108c2ecf20Sopenharmony_ci if (test_bit(FLAG_FLASHING, &ai->flags)) 79118c2ecf20Sopenharmony_ci return -EIO; 79128c2ecf20Sopenharmony_ci 79138c2ecf20Sopenharmony_ci ridcode = 0; 79148c2ecf20Sopenharmony_ci writer = do_writerid; 79158c2ecf20Sopenharmony_ci 79168c2ecf20Sopenharmony_ci switch(comp->command) 79178c2ecf20Sopenharmony_ci { 79188c2ecf20Sopenharmony_ci case AIROPSIDS: ridcode = RID_SSID; break; 79198c2ecf20Sopenharmony_ci case AIROPCAP: ridcode = RID_CAPABILITIES; break; 79208c2ecf20Sopenharmony_ci case AIROPAPLIST: ridcode = RID_APLIST; break; 79218c2ecf20Sopenharmony_ci case AIROPCFG: ai->config.len = 0; 79228c2ecf20Sopenharmony_ci clear_bit(FLAG_COMMIT, &ai->flags); 79238c2ecf20Sopenharmony_ci ridcode = RID_CONFIG; break; 79248c2ecf20Sopenharmony_ci case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break; 79258c2ecf20Sopenharmony_ci case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break; 79268c2ecf20Sopenharmony_ci case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break; 79278c2ecf20Sopenharmony_ci case AIROPWEPKEY: ridcode = RID_WEP_TEMP; writer = PC4500_writerid; 79288c2ecf20Sopenharmony_ci break; 79298c2ecf20Sopenharmony_ci case AIROPLEAPUSR+1: ridcode = 0xFF2A; break; 79308c2ecf20Sopenharmony_ci case AIROPLEAPUSR+2: ridcode = 0xFF2B; break; 79318c2ecf20Sopenharmony_ci 79328c2ecf20Sopenharmony_ci /* this is not really a rid but a command given to the card 79338c2ecf20Sopenharmony_ci * same with MAC off 79348c2ecf20Sopenharmony_ci */ 79358c2ecf20Sopenharmony_ci case AIROPMACON: 79368c2ecf20Sopenharmony_ci if (enable_MAC(ai, 1) != 0) 79378c2ecf20Sopenharmony_ci return -EIO; 79388c2ecf20Sopenharmony_ci return 0; 79398c2ecf20Sopenharmony_ci 79408c2ecf20Sopenharmony_ci /* 79418c2ecf20Sopenharmony_ci * Evidently this code in the airo driver does not get a symbol 79428c2ecf20Sopenharmony_ci * as disable_MAC. it's probably so short the compiler does not gen one. 79438c2ecf20Sopenharmony_ci */ 79448c2ecf20Sopenharmony_ci case AIROPMACOFF: 79458c2ecf20Sopenharmony_ci disable_MAC(ai, 1); 79468c2ecf20Sopenharmony_ci return 0; 79478c2ecf20Sopenharmony_ci 79488c2ecf20Sopenharmony_ci /* This command merely clears the counts does not actually store any data 79498c2ecf20Sopenharmony_ci * only reads rid. But as it changes the cards state, I put it in the 79508c2ecf20Sopenharmony_ci * writerid routines. 79518c2ecf20Sopenharmony_ci */ 79528c2ecf20Sopenharmony_ci case AIROPSTCLR: 79538c2ecf20Sopenharmony_ci if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL) 79548c2ecf20Sopenharmony_ci return -ENOMEM; 79558c2ecf20Sopenharmony_ci 79568c2ecf20Sopenharmony_ci PC4500_readrid(ai, RID_STATSDELTACLEAR, iobuf, RIDSIZE, 1); 79578c2ecf20Sopenharmony_ci 79588c2ecf20Sopenharmony_ci enabled = ai->micstats.enabled; 79598c2ecf20Sopenharmony_ci memset(&ai->micstats, 0, sizeof(ai->micstats)); 79608c2ecf20Sopenharmony_ci ai->micstats.enabled = enabled; 79618c2ecf20Sopenharmony_ci 79628c2ecf20Sopenharmony_ci if (copy_to_user(comp->data, iobuf, 79638c2ecf20Sopenharmony_ci min((int)comp->len, (int)RIDSIZE))) { 79648c2ecf20Sopenharmony_ci kfree (iobuf); 79658c2ecf20Sopenharmony_ci return -EFAULT; 79668c2ecf20Sopenharmony_ci } 79678c2ecf20Sopenharmony_ci kfree (iobuf); 79688c2ecf20Sopenharmony_ci return 0; 79698c2ecf20Sopenharmony_ci 79708c2ecf20Sopenharmony_ci default: 79718c2ecf20Sopenharmony_ci return -EOPNOTSUPP; /* Blarg! */ 79728c2ecf20Sopenharmony_ci } 79738c2ecf20Sopenharmony_ci if (comp->len > RIDSIZE) 79748c2ecf20Sopenharmony_ci return -EINVAL; 79758c2ecf20Sopenharmony_ci 79768c2ecf20Sopenharmony_ci if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL) 79778c2ecf20Sopenharmony_ci return -ENOMEM; 79788c2ecf20Sopenharmony_ci 79798c2ecf20Sopenharmony_ci if (copy_from_user(iobuf, comp->data, comp->len)) { 79808c2ecf20Sopenharmony_ci kfree (iobuf); 79818c2ecf20Sopenharmony_ci return -EFAULT; 79828c2ecf20Sopenharmony_ci } 79838c2ecf20Sopenharmony_ci 79848c2ecf20Sopenharmony_ci if (comp->command == AIROPCFG) { 79858c2ecf20Sopenharmony_ci ConfigRid *cfg = (ConfigRid *)iobuf; 79868c2ecf20Sopenharmony_ci 79878c2ecf20Sopenharmony_ci if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) 79888c2ecf20Sopenharmony_ci cfg->opmode |= MODE_MIC; 79898c2ecf20Sopenharmony_ci 79908c2ecf20Sopenharmony_ci if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS) 79918c2ecf20Sopenharmony_ci set_bit (FLAG_ADHOC, &ai->flags); 79928c2ecf20Sopenharmony_ci else 79938c2ecf20Sopenharmony_ci clear_bit (FLAG_ADHOC, &ai->flags); 79948c2ecf20Sopenharmony_ci } 79958c2ecf20Sopenharmony_ci 79968c2ecf20Sopenharmony_ci if ((*writer)(ai, ridcode, iobuf, comp->len, 1)) { 79978c2ecf20Sopenharmony_ci kfree (iobuf); 79988c2ecf20Sopenharmony_ci return -EIO; 79998c2ecf20Sopenharmony_ci } 80008c2ecf20Sopenharmony_ci kfree (iobuf); 80018c2ecf20Sopenharmony_ci return 0; 80028c2ecf20Sopenharmony_ci} 80038c2ecf20Sopenharmony_ci 80048c2ecf20Sopenharmony_ci/***************************************************************************** 80058c2ecf20Sopenharmony_ci * Ancillary flash / mod functions much black magic lurkes here * 80068c2ecf20Sopenharmony_ci ***************************************************************************** 80078c2ecf20Sopenharmony_ci */ 80088c2ecf20Sopenharmony_ci 80098c2ecf20Sopenharmony_ci/* 80108c2ecf20Sopenharmony_ci * Flash command switch table 80118c2ecf20Sopenharmony_ci */ 80128c2ecf20Sopenharmony_ci 80138c2ecf20Sopenharmony_cistatic int flashcard(struct net_device *dev, aironet_ioctl *comp) 80148c2ecf20Sopenharmony_ci{ 80158c2ecf20Sopenharmony_ci int z; 80168c2ecf20Sopenharmony_ci 80178c2ecf20Sopenharmony_ci /* Only super-user can modify flash */ 80188c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 80198c2ecf20Sopenharmony_ci return -EPERM; 80208c2ecf20Sopenharmony_ci 80218c2ecf20Sopenharmony_ci switch(comp->command) 80228c2ecf20Sopenharmony_ci { 80238c2ecf20Sopenharmony_ci case AIROFLSHRST: 80248c2ecf20Sopenharmony_ci return cmdreset((struct airo_info *)dev->ml_priv); 80258c2ecf20Sopenharmony_ci 80268c2ecf20Sopenharmony_ci case AIROFLSHSTFL: 80278c2ecf20Sopenharmony_ci if (!AIRO_FLASH(dev) && 80288c2ecf20Sopenharmony_ci (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL) 80298c2ecf20Sopenharmony_ci return -ENOMEM; 80308c2ecf20Sopenharmony_ci return setflashmode((struct airo_info *)dev->ml_priv); 80318c2ecf20Sopenharmony_ci 80328c2ecf20Sopenharmony_ci case AIROFLSHGCHR: /* Get char from aux */ 80338c2ecf20Sopenharmony_ci if (comp->len != sizeof(int)) 80348c2ecf20Sopenharmony_ci return -EINVAL; 80358c2ecf20Sopenharmony_ci if (copy_from_user(&z, comp->data, comp->len)) 80368c2ecf20Sopenharmony_ci return -EFAULT; 80378c2ecf20Sopenharmony_ci return flashgchar((struct airo_info *)dev->ml_priv, z, 8000); 80388c2ecf20Sopenharmony_ci 80398c2ecf20Sopenharmony_ci case AIROFLSHPCHR: /* Send char to card. */ 80408c2ecf20Sopenharmony_ci if (comp->len != sizeof(int)) 80418c2ecf20Sopenharmony_ci return -EINVAL; 80428c2ecf20Sopenharmony_ci if (copy_from_user(&z, comp->data, comp->len)) 80438c2ecf20Sopenharmony_ci return -EFAULT; 80448c2ecf20Sopenharmony_ci return flashpchar((struct airo_info *)dev->ml_priv, z, 8000); 80458c2ecf20Sopenharmony_ci 80468c2ecf20Sopenharmony_ci case AIROFLPUTBUF: /* Send 32k to card */ 80478c2ecf20Sopenharmony_ci if (!AIRO_FLASH(dev)) 80488c2ecf20Sopenharmony_ci return -ENOMEM; 80498c2ecf20Sopenharmony_ci if (comp->len > FLASHSIZE) 80508c2ecf20Sopenharmony_ci return -EINVAL; 80518c2ecf20Sopenharmony_ci if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len)) 80528c2ecf20Sopenharmony_ci return -EFAULT; 80538c2ecf20Sopenharmony_ci 80548c2ecf20Sopenharmony_ci flashputbuf((struct airo_info *)dev->ml_priv); 80558c2ecf20Sopenharmony_ci return 0; 80568c2ecf20Sopenharmony_ci 80578c2ecf20Sopenharmony_ci case AIRORESTART: 80588c2ecf20Sopenharmony_ci if (flashrestart((struct airo_info *)dev->ml_priv, dev)) 80598c2ecf20Sopenharmony_ci return -EIO; 80608c2ecf20Sopenharmony_ci return 0; 80618c2ecf20Sopenharmony_ci } 80628c2ecf20Sopenharmony_ci return -EINVAL; 80638c2ecf20Sopenharmony_ci} 80648c2ecf20Sopenharmony_ci 80658c2ecf20Sopenharmony_ci#define FLASH_COMMAND 0x7e7e 80668c2ecf20Sopenharmony_ci 80678c2ecf20Sopenharmony_ci/* 80688c2ecf20Sopenharmony_ci * STEP 1) 80698c2ecf20Sopenharmony_ci * Disable MAC and do soft reset on 80708c2ecf20Sopenharmony_ci * card. 80718c2ecf20Sopenharmony_ci */ 80728c2ecf20Sopenharmony_ci 80738c2ecf20Sopenharmony_cistatic int cmdreset(struct airo_info *ai) 80748c2ecf20Sopenharmony_ci{ 80758c2ecf20Sopenharmony_ci disable_MAC(ai, 1); 80768c2ecf20Sopenharmony_ci 80778c2ecf20Sopenharmony_ci if (!waitbusy (ai)) { 80788c2ecf20Sopenharmony_ci airo_print_info(ai->dev->name, "Waitbusy hang before RESET"); 80798c2ecf20Sopenharmony_ci return -EBUSY; 80808c2ecf20Sopenharmony_ci } 80818c2ecf20Sopenharmony_ci 80828c2ecf20Sopenharmony_ci OUT4500(ai, COMMAND, CMD_SOFTRESET); 80838c2ecf20Sopenharmony_ci 80848c2ecf20Sopenharmony_ci ssleep(1); /* WAS 600 12/7/00 */ 80858c2ecf20Sopenharmony_ci 80868c2ecf20Sopenharmony_ci if (!waitbusy (ai)) { 80878c2ecf20Sopenharmony_ci airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET"); 80888c2ecf20Sopenharmony_ci return -EBUSY; 80898c2ecf20Sopenharmony_ci } 80908c2ecf20Sopenharmony_ci return 0; 80918c2ecf20Sopenharmony_ci} 80928c2ecf20Sopenharmony_ci 80938c2ecf20Sopenharmony_ci/* STEP 2) 80948c2ecf20Sopenharmony_ci * Put the card in legendary flash 80958c2ecf20Sopenharmony_ci * mode 80968c2ecf20Sopenharmony_ci */ 80978c2ecf20Sopenharmony_ci 80988c2ecf20Sopenharmony_cistatic int setflashmode (struct airo_info *ai) 80998c2ecf20Sopenharmony_ci{ 81008c2ecf20Sopenharmony_ci set_bit (FLAG_FLASHING, &ai->flags); 81018c2ecf20Sopenharmony_ci 81028c2ecf20Sopenharmony_ci OUT4500(ai, SWS0, FLASH_COMMAND); 81038c2ecf20Sopenharmony_ci OUT4500(ai, SWS1, FLASH_COMMAND); 81048c2ecf20Sopenharmony_ci if (probe) { 81058c2ecf20Sopenharmony_ci OUT4500(ai, SWS0, FLASH_COMMAND); 81068c2ecf20Sopenharmony_ci OUT4500(ai, COMMAND, 0x10); 81078c2ecf20Sopenharmony_ci } else { 81088c2ecf20Sopenharmony_ci OUT4500(ai, SWS2, FLASH_COMMAND); 81098c2ecf20Sopenharmony_ci OUT4500(ai, SWS3, FLASH_COMMAND); 81108c2ecf20Sopenharmony_ci OUT4500(ai, COMMAND, 0); 81118c2ecf20Sopenharmony_ci } 81128c2ecf20Sopenharmony_ci msleep(500); /* 500ms delay */ 81138c2ecf20Sopenharmony_ci 81148c2ecf20Sopenharmony_ci if (!waitbusy(ai)) { 81158c2ecf20Sopenharmony_ci clear_bit (FLAG_FLASHING, &ai->flags); 81168c2ecf20Sopenharmony_ci airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode"); 81178c2ecf20Sopenharmony_ci return -EIO; 81188c2ecf20Sopenharmony_ci } 81198c2ecf20Sopenharmony_ci return 0; 81208c2ecf20Sopenharmony_ci} 81218c2ecf20Sopenharmony_ci 81228c2ecf20Sopenharmony_ci/* Put character to SWS0 wait for dwelltime 81238c2ecf20Sopenharmony_ci * x 50us for echo . 81248c2ecf20Sopenharmony_ci */ 81258c2ecf20Sopenharmony_ci 81268c2ecf20Sopenharmony_cistatic int flashpchar(struct airo_info *ai, int byte, int dwelltime) 81278c2ecf20Sopenharmony_ci{ 81288c2ecf20Sopenharmony_ci int echo; 81298c2ecf20Sopenharmony_ci int waittime; 81308c2ecf20Sopenharmony_ci 81318c2ecf20Sopenharmony_ci byte |= 0x8000; 81328c2ecf20Sopenharmony_ci 81338c2ecf20Sopenharmony_ci if (dwelltime == 0) 81348c2ecf20Sopenharmony_ci dwelltime = 200; 81358c2ecf20Sopenharmony_ci 81368c2ecf20Sopenharmony_ci waittime = dwelltime; 81378c2ecf20Sopenharmony_ci 81388c2ecf20Sopenharmony_ci /* Wait for busy bit d15 to go false indicating buffer empty */ 81398c2ecf20Sopenharmony_ci while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) { 81408c2ecf20Sopenharmony_ci udelay (50); 81418c2ecf20Sopenharmony_ci waittime -= 50; 81428c2ecf20Sopenharmony_ci } 81438c2ecf20Sopenharmony_ci 81448c2ecf20Sopenharmony_ci /* timeout for busy clear wait */ 81458c2ecf20Sopenharmony_ci if (waittime <= 0) { 81468c2ecf20Sopenharmony_ci airo_print_info(ai->dev->name, "flash putchar busywait timeout!"); 81478c2ecf20Sopenharmony_ci return -EBUSY; 81488c2ecf20Sopenharmony_ci } 81498c2ecf20Sopenharmony_ci 81508c2ecf20Sopenharmony_ci /* Port is clear now write byte and wait for it to echo back */ 81518c2ecf20Sopenharmony_ci do { 81528c2ecf20Sopenharmony_ci OUT4500(ai, SWS0, byte); 81538c2ecf20Sopenharmony_ci udelay(50); 81548c2ecf20Sopenharmony_ci dwelltime -= 50; 81558c2ecf20Sopenharmony_ci echo = IN4500(ai, SWS1); 81568c2ecf20Sopenharmony_ci } while (dwelltime >= 0 && echo != byte); 81578c2ecf20Sopenharmony_ci 81588c2ecf20Sopenharmony_ci OUT4500(ai, SWS1, 0); 81598c2ecf20Sopenharmony_ci 81608c2ecf20Sopenharmony_ci return (echo == byte) ? 0 : -EIO; 81618c2ecf20Sopenharmony_ci} 81628c2ecf20Sopenharmony_ci 81638c2ecf20Sopenharmony_ci/* 81648c2ecf20Sopenharmony_ci * Get a character from the card matching matchbyte 81658c2ecf20Sopenharmony_ci * Step 3) 81668c2ecf20Sopenharmony_ci */ 81678c2ecf20Sopenharmony_cistatic int flashgchar(struct airo_info *ai, int matchbyte, int dwelltime) 81688c2ecf20Sopenharmony_ci{ 81698c2ecf20Sopenharmony_ci int rchar; 81708c2ecf20Sopenharmony_ci unsigned char rbyte = 0; 81718c2ecf20Sopenharmony_ci 81728c2ecf20Sopenharmony_ci do { 81738c2ecf20Sopenharmony_ci rchar = IN4500(ai, SWS1); 81748c2ecf20Sopenharmony_ci 81758c2ecf20Sopenharmony_ci if (dwelltime && !(0x8000 & rchar)) { 81768c2ecf20Sopenharmony_ci dwelltime -= 10; 81778c2ecf20Sopenharmony_ci mdelay(10); 81788c2ecf20Sopenharmony_ci continue; 81798c2ecf20Sopenharmony_ci } 81808c2ecf20Sopenharmony_ci rbyte = 0xff & rchar; 81818c2ecf20Sopenharmony_ci 81828c2ecf20Sopenharmony_ci if ((rbyte == matchbyte) && (0x8000 & rchar)) { 81838c2ecf20Sopenharmony_ci OUT4500(ai, SWS1, 0); 81848c2ecf20Sopenharmony_ci return 0; 81858c2ecf20Sopenharmony_ci } 81868c2ecf20Sopenharmony_ci if (rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar) 81878c2ecf20Sopenharmony_ci break; 81888c2ecf20Sopenharmony_ci OUT4500(ai, SWS1, 0); 81898c2ecf20Sopenharmony_ci 81908c2ecf20Sopenharmony_ci } while (dwelltime > 0); 81918c2ecf20Sopenharmony_ci return -EIO; 81928c2ecf20Sopenharmony_ci} 81938c2ecf20Sopenharmony_ci 81948c2ecf20Sopenharmony_ci/* 81958c2ecf20Sopenharmony_ci * Transfer 32k of firmware data from user buffer to our buffer and 81968c2ecf20Sopenharmony_ci * send to the card 81978c2ecf20Sopenharmony_ci */ 81988c2ecf20Sopenharmony_ci 81998c2ecf20Sopenharmony_cistatic int flashputbuf(struct airo_info *ai) 82008c2ecf20Sopenharmony_ci{ 82018c2ecf20Sopenharmony_ci int nwords; 82028c2ecf20Sopenharmony_ci 82038c2ecf20Sopenharmony_ci /* Write stuff */ 82048c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI,&ai->flags)) 82058c2ecf20Sopenharmony_ci memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE); 82068c2ecf20Sopenharmony_ci else { 82078c2ecf20Sopenharmony_ci OUT4500(ai, AUXPAGE, 0x100); 82088c2ecf20Sopenharmony_ci OUT4500(ai, AUXOFF, 0); 82098c2ecf20Sopenharmony_ci 82108c2ecf20Sopenharmony_ci for (nwords = 0; nwords != FLASHSIZE / 2; nwords++) { 82118c2ecf20Sopenharmony_ci OUT4500(ai, AUXDATA, ai->flash[nwords] & 0xffff); 82128c2ecf20Sopenharmony_ci } 82138c2ecf20Sopenharmony_ci } 82148c2ecf20Sopenharmony_ci OUT4500(ai, SWS0, 0x8000); 82158c2ecf20Sopenharmony_ci 82168c2ecf20Sopenharmony_ci return 0; 82178c2ecf20Sopenharmony_ci} 82188c2ecf20Sopenharmony_ci 82198c2ecf20Sopenharmony_ci/* 82208c2ecf20Sopenharmony_ci * 82218c2ecf20Sopenharmony_ci */ 82228c2ecf20Sopenharmony_cistatic int flashrestart(struct airo_info *ai, struct net_device *dev) 82238c2ecf20Sopenharmony_ci{ 82248c2ecf20Sopenharmony_ci int i, status; 82258c2ecf20Sopenharmony_ci 82268c2ecf20Sopenharmony_ci ssleep(1); /* Added 12/7/00 */ 82278c2ecf20Sopenharmony_ci clear_bit (FLAG_FLASHING, &ai->flags); 82288c2ecf20Sopenharmony_ci if (test_bit(FLAG_MPI, &ai->flags)) { 82298c2ecf20Sopenharmony_ci status = mpi_init_descriptors(ai); 82308c2ecf20Sopenharmony_ci if (status != SUCCESS) 82318c2ecf20Sopenharmony_ci return status; 82328c2ecf20Sopenharmony_ci } 82338c2ecf20Sopenharmony_ci status = setup_card(ai, dev->dev_addr, 1); 82348c2ecf20Sopenharmony_ci 82358c2ecf20Sopenharmony_ci if (!test_bit(FLAG_MPI,&ai->flags)) 82368c2ecf20Sopenharmony_ci for (i = 0; i < MAX_FIDS; i++) { 82378c2ecf20Sopenharmony_ci ai->fids[i] = transmit_allocate 82388c2ecf20Sopenharmony_ci (ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2); 82398c2ecf20Sopenharmony_ci } 82408c2ecf20Sopenharmony_ci 82418c2ecf20Sopenharmony_ci ssleep(1); /* Added 12/7/00 */ 82428c2ecf20Sopenharmony_ci return status; 82438c2ecf20Sopenharmony_ci} 82448c2ecf20Sopenharmony_ci#endif /* CISCO_EXT */ 82458c2ecf20Sopenharmony_ci 82468c2ecf20Sopenharmony_ci/* 82478c2ecf20Sopenharmony_ci This program is free software; you can redistribute it and/or 82488c2ecf20Sopenharmony_ci modify it under the terms of the GNU General Public License 82498c2ecf20Sopenharmony_ci as published by the Free Software Foundation; either version 2 82508c2ecf20Sopenharmony_ci of the License, or (at your option) any later version. 82518c2ecf20Sopenharmony_ci 82528c2ecf20Sopenharmony_ci This program is distributed in the hope that it will be useful, 82538c2ecf20Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 82548c2ecf20Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 82558c2ecf20Sopenharmony_ci GNU General Public License for more details. 82568c2ecf20Sopenharmony_ci 82578c2ecf20Sopenharmony_ci In addition: 82588c2ecf20Sopenharmony_ci 82598c2ecf20Sopenharmony_ci Redistribution and use in source and binary forms, with or without 82608c2ecf20Sopenharmony_ci modification, are permitted provided that the following conditions 82618c2ecf20Sopenharmony_ci are met: 82628c2ecf20Sopenharmony_ci 82638c2ecf20Sopenharmony_ci 1. Redistributions of source code must retain the above copyright 82648c2ecf20Sopenharmony_ci notice, this list of conditions and the following disclaimer. 82658c2ecf20Sopenharmony_ci 2. Redistributions in binary form must reproduce the above copyright 82668c2ecf20Sopenharmony_ci notice, this list of conditions and the following disclaimer in the 82678c2ecf20Sopenharmony_ci documentation and/or other materials provided with the distribution. 82688c2ecf20Sopenharmony_ci 3. The name of the author may not be used to endorse or promote 82698c2ecf20Sopenharmony_ci products derived from this software without specific prior written 82708c2ecf20Sopenharmony_ci permission. 82718c2ecf20Sopenharmony_ci 82728c2ecf20Sopenharmony_ci THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 82738c2ecf20Sopenharmony_ci IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 82748c2ecf20Sopenharmony_ci WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 82758c2ecf20Sopenharmony_ci ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 82768c2ecf20Sopenharmony_ci INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 82778c2ecf20Sopenharmony_ci (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 82788c2ecf20Sopenharmony_ci SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 82798c2ecf20Sopenharmony_ci HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 82808c2ecf20Sopenharmony_ci STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 82818c2ecf20Sopenharmony_ci IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 82828c2ecf20Sopenharmony_ci POSSIBILITY OF SUCH DAMAGE. 82838c2ecf20Sopenharmony_ci*/ 82848c2ecf20Sopenharmony_ci 82858c2ecf20Sopenharmony_cimodule_init(airo_init_module); 82868c2ecf20Sopenharmony_cimodule_exit(airo_cleanup_module); 8287