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