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