162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci  Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci  Contact Information:
862306a36Sopenharmony_ci  Intel Linux Wireless <ilw@linux.intel.com>
962306a36Sopenharmony_ci  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci  Portions of this file are based on the sample_* files provided by Wireless
1262306a36Sopenharmony_ci  Extensions 0.26 package and copyright (c) 1997-2003 Jean Tourrilhes
1362306a36Sopenharmony_ci  <jt@hpl.hp.com>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci  Portions of this file are based on the Host AP project,
1662306a36Sopenharmony_ci  Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
1762306a36Sopenharmony_ci    <j@w1.fi>
1862306a36Sopenharmony_ci  Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci  Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and
2162306a36Sopenharmony_ci  ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c
2262306a36Sopenharmony_ci  available in the 2.4.25 kernel sources, and are copyright (c) Alan Cox
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci******************************************************************************/
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci Initial driver on which this is based was developed by Janusz Gorycki,
2862306a36Sopenharmony_ci Maciej Urbaniak, and Maciej Sosnowski.
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci Promiscuous mode support added by Jacek Wysoczynski and Maciej Urbaniak.
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciTheory of Operation
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciTx - Commands and Data
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ciFirmware and host share a circular queue of Transmit Buffer Descriptors (TBDs)
3762306a36Sopenharmony_ciEach TBD contains a pointer to the physical (dma_addr_t) address of data being
3862306a36Sopenharmony_cisent to the firmware as well as the length of the data.
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ciThe host writes to the TBD queue at the WRITE index.  The WRITE index points
4162306a36Sopenharmony_cito the _next_ packet to be written and is advanced when after the TBD has been
4262306a36Sopenharmony_cifilled.
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ciThe firmware pulls from the TBD queue at the READ index.  The READ index points
4562306a36Sopenharmony_cito the currently being read entry, and is advanced once the firmware is
4662306a36Sopenharmony_cidone with a packet.
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciWhen data is sent to the firmware, the first TBD is used to indicate to the
4962306a36Sopenharmony_cifirmware if a Command or Data is being sent.  If it is Command, all of the
5062306a36Sopenharmony_cicommand information is contained within the physical address referred to by the
5162306a36Sopenharmony_ciTBD.  If it is Data, the first TBD indicates the type of data packet, number
5262306a36Sopenharmony_ciof fragments, etc.  The next TBD then refers to the actual packet location.
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciThe Tx flow cycle is as follows:
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci1) ipw2100_tx() is called by kernel with SKB to transmit
5762306a36Sopenharmony_ci2) Packet is move from the tx_free_list and appended to the transmit pending
5862306a36Sopenharmony_ci   list (tx_pend_list)
5962306a36Sopenharmony_ci3) work is scheduled to move pending packets into the shared circular queue.
6062306a36Sopenharmony_ci4) when placing packet in the circular queue, the incoming SKB is DMA mapped
6162306a36Sopenharmony_ci   to a physical address.  That address is entered into a TBD.  Two TBDs are
6262306a36Sopenharmony_ci   filled out.  The first indicating a data packet, the second referring to the
6362306a36Sopenharmony_ci   actual payload data.
6462306a36Sopenharmony_ci5) the packet is removed from tx_pend_list and placed on the end of the
6562306a36Sopenharmony_ci   firmware pending list (fw_pend_list)
6662306a36Sopenharmony_ci6) firmware is notified that the WRITE index has
6762306a36Sopenharmony_ci7) Once the firmware has processed the TBD, INTA is triggered.
6862306a36Sopenharmony_ci8) For each Tx interrupt received from the firmware, the READ index is checked
6962306a36Sopenharmony_ci   to see which TBDs are done being processed.
7062306a36Sopenharmony_ci9) For each TBD that has been processed, the ISR pulls the oldest packet
7162306a36Sopenharmony_ci   from the fw_pend_list.
7262306a36Sopenharmony_ci10)The packet structure contained in the fw_pend_list is then used
7362306a36Sopenharmony_ci   to unmap the DMA address and to free the SKB originally passed to the driver
7462306a36Sopenharmony_ci   from the kernel.
7562306a36Sopenharmony_ci11)The packet structure is placed onto the tx_free_list
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciThe above steps are the same for commands, only the msg_free_list/msg_pend_list
7862306a36Sopenharmony_ciare used instead of tx_free_list/tx_pend_list
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci...
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ciCritical Sections / Locking :
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ciThere are two locks utilized.  The first is the low level lock (priv->low_lock)
8562306a36Sopenharmony_cithat protects the following:
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci- Access to the Tx/Rx queue lists via priv->low_lock. The lists are as follows:
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci  tx_free_list : Holds pre-allocated Tx buffers.
9062306a36Sopenharmony_ci    TAIL modified in __ipw2100_tx_process()
9162306a36Sopenharmony_ci    HEAD modified in ipw2100_tx()
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci  tx_pend_list : Holds used Tx buffers waiting to go into the TBD ring
9462306a36Sopenharmony_ci    TAIL modified ipw2100_tx()
9562306a36Sopenharmony_ci    HEAD modified by ipw2100_tx_send_data()
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci  msg_free_list : Holds pre-allocated Msg (Command) buffers
9862306a36Sopenharmony_ci    TAIL modified in __ipw2100_tx_process()
9962306a36Sopenharmony_ci    HEAD modified in ipw2100_hw_send_command()
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci  msg_pend_list : Holds used Msg buffers waiting to go into the TBD ring
10262306a36Sopenharmony_ci    TAIL modified in ipw2100_hw_send_command()
10362306a36Sopenharmony_ci    HEAD modified in ipw2100_tx_send_commands()
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci  The flow of data on the TX side is as follows:
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci  MSG_FREE_LIST + COMMAND => MSG_PEND_LIST => TBD => MSG_FREE_LIST
10862306a36Sopenharmony_ci  TX_FREE_LIST + DATA => TX_PEND_LIST => TBD => TX_FREE_LIST
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci  The methods that work on the TBD ring are protected via priv->low_lock.
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci- The internal data state of the device itself
11362306a36Sopenharmony_ci- Access to the firmware read/write indexes for the BD queues
11462306a36Sopenharmony_ci  and associated logic
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ciAll external entry functions are locked with the priv->action_lock to ensure
11762306a36Sopenharmony_cithat only one external action is invoked at a time.
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci*/
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#include <linux/compiler.h>
12362306a36Sopenharmony_ci#include <linux/errno.h>
12462306a36Sopenharmony_ci#include <linux/if_arp.h>
12562306a36Sopenharmony_ci#include <linux/in6.h>
12662306a36Sopenharmony_ci#include <linux/in.h>
12762306a36Sopenharmony_ci#include <linux/ip.h>
12862306a36Sopenharmony_ci#include <linux/kernel.h>
12962306a36Sopenharmony_ci#include <linux/kmod.h>
13062306a36Sopenharmony_ci#include <linux/module.h>
13162306a36Sopenharmony_ci#include <linux/netdevice.h>
13262306a36Sopenharmony_ci#include <linux/ethtool.h>
13362306a36Sopenharmony_ci#include <linux/pci.h>
13462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
13562306a36Sopenharmony_ci#include <linux/proc_fs.h>
13662306a36Sopenharmony_ci#include <linux/skbuff.h>
13762306a36Sopenharmony_ci#include <linux/uaccess.h>
13862306a36Sopenharmony_ci#include <asm/io.h>
13962306a36Sopenharmony_ci#include <linux/fs.h>
14062306a36Sopenharmony_ci#include <linux/mm.h>
14162306a36Sopenharmony_ci#include <linux/slab.h>
14262306a36Sopenharmony_ci#include <linux/unistd.h>
14362306a36Sopenharmony_ci#include <linux/stringify.h>
14462306a36Sopenharmony_ci#include <linux/tcp.h>
14562306a36Sopenharmony_ci#include <linux/types.h>
14662306a36Sopenharmony_ci#include <linux/time.h>
14762306a36Sopenharmony_ci#include <linux/firmware.h>
14862306a36Sopenharmony_ci#include <linux/acpi.h>
14962306a36Sopenharmony_ci#include <linux/ctype.h>
15062306a36Sopenharmony_ci#include <linux/pm_qos.h>
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci#include <net/lib80211.h>
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci#include "ipw2100.h"
15562306a36Sopenharmony_ci#include "ipw.h"
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci#define IPW2100_VERSION "git-1.2.2"
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#define DRV_NAME	"ipw2100"
16062306a36Sopenharmony_ci#define DRV_VERSION	IPW2100_VERSION
16162306a36Sopenharmony_ci#define DRV_DESCRIPTION	"Intel(R) PRO/Wireless 2100 Network Driver"
16262306a36Sopenharmony_ci#define DRV_COPYRIGHT	"Copyright(c) 2003-2006 Intel Corporation"
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic struct pm_qos_request ipw2100_pm_qos_req;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/* Debugging stuff */
16762306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
16862306a36Sopenharmony_ci#define IPW2100_RX_DEBUG	/* Reception debugging */
16962306a36Sopenharmony_ci#endif
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION);
17262306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
17362306a36Sopenharmony_ciMODULE_AUTHOR(DRV_COPYRIGHT);
17462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int debug = 0;
17762306a36Sopenharmony_cistatic int network_mode = 0;
17862306a36Sopenharmony_cistatic int channel = 0;
17962306a36Sopenharmony_cistatic int associate = 0;
18062306a36Sopenharmony_cistatic int disable = 0;
18162306a36Sopenharmony_ci#ifdef CONFIG_PM
18262306a36Sopenharmony_cistatic struct ipw2100_fw ipw2100_firmware;
18362306a36Sopenharmony_ci#endif
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci#include <linux/moduleparam.h>
18662306a36Sopenharmony_cimodule_param(debug, int, 0444);
18762306a36Sopenharmony_cimodule_param_named(mode, network_mode, int, 0444);
18862306a36Sopenharmony_cimodule_param(channel, int, 0444);
18962306a36Sopenharmony_cimodule_param(associate, int, 0444);
19062306a36Sopenharmony_cimodule_param(disable, int, 0444);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "debug level");
19362306a36Sopenharmony_ciMODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
19462306a36Sopenharmony_ciMODULE_PARM_DESC(channel, "channel");
19562306a36Sopenharmony_ciMODULE_PARM_DESC(associate, "auto associate when scanning (default off)");
19662306a36Sopenharmony_ciMODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic u32 ipw2100_debug_level = IPW_DL_NONE;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
20162306a36Sopenharmony_ci#define IPW_DEBUG(level, message...) \
20262306a36Sopenharmony_cido { \
20362306a36Sopenharmony_ci	if (ipw2100_debug_level & (level)) { \
20462306a36Sopenharmony_ci		printk(KERN_DEBUG "ipw2100: %s ", __func__); \
20562306a36Sopenharmony_ci		printk(message); \
20662306a36Sopenharmony_ci	} \
20762306a36Sopenharmony_ci} while (0)
20862306a36Sopenharmony_ci#else
20962306a36Sopenharmony_ci#define IPW_DEBUG(level, message...) do {} while (0)
21062306a36Sopenharmony_ci#endif				/* CONFIG_IPW2100_DEBUG */
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
21362306a36Sopenharmony_cistatic const char *command_types[] = {
21462306a36Sopenharmony_ci	"undefined",
21562306a36Sopenharmony_ci	"unused",		/* HOST_ATTENTION */
21662306a36Sopenharmony_ci	"HOST_COMPLETE",
21762306a36Sopenharmony_ci	"unused",		/* SLEEP */
21862306a36Sopenharmony_ci	"unused",		/* HOST_POWER_DOWN */
21962306a36Sopenharmony_ci	"unused",
22062306a36Sopenharmony_ci	"SYSTEM_CONFIG",
22162306a36Sopenharmony_ci	"unused",		/* SET_IMR */
22262306a36Sopenharmony_ci	"SSID",
22362306a36Sopenharmony_ci	"MANDATORY_BSSID",
22462306a36Sopenharmony_ci	"AUTHENTICATION_TYPE",
22562306a36Sopenharmony_ci	"ADAPTER_ADDRESS",
22662306a36Sopenharmony_ci	"PORT_TYPE",
22762306a36Sopenharmony_ci	"INTERNATIONAL_MODE",
22862306a36Sopenharmony_ci	"CHANNEL",
22962306a36Sopenharmony_ci	"RTS_THRESHOLD",
23062306a36Sopenharmony_ci	"FRAG_THRESHOLD",
23162306a36Sopenharmony_ci	"POWER_MODE",
23262306a36Sopenharmony_ci	"TX_RATES",
23362306a36Sopenharmony_ci	"BASIC_TX_RATES",
23462306a36Sopenharmony_ci	"WEP_KEY_INFO",
23562306a36Sopenharmony_ci	"unused",
23662306a36Sopenharmony_ci	"unused",
23762306a36Sopenharmony_ci	"unused",
23862306a36Sopenharmony_ci	"unused",
23962306a36Sopenharmony_ci	"WEP_KEY_INDEX",
24062306a36Sopenharmony_ci	"WEP_FLAGS",
24162306a36Sopenharmony_ci	"ADD_MULTICAST",
24262306a36Sopenharmony_ci	"CLEAR_ALL_MULTICAST",
24362306a36Sopenharmony_ci	"BEACON_INTERVAL",
24462306a36Sopenharmony_ci	"ATIM_WINDOW",
24562306a36Sopenharmony_ci	"CLEAR_STATISTICS",
24662306a36Sopenharmony_ci	"undefined",
24762306a36Sopenharmony_ci	"undefined",
24862306a36Sopenharmony_ci	"undefined",
24962306a36Sopenharmony_ci	"undefined",
25062306a36Sopenharmony_ci	"TX_POWER_INDEX",
25162306a36Sopenharmony_ci	"undefined",
25262306a36Sopenharmony_ci	"undefined",
25362306a36Sopenharmony_ci	"undefined",
25462306a36Sopenharmony_ci	"undefined",
25562306a36Sopenharmony_ci	"undefined",
25662306a36Sopenharmony_ci	"undefined",
25762306a36Sopenharmony_ci	"BROADCAST_SCAN",
25862306a36Sopenharmony_ci	"CARD_DISABLE",
25962306a36Sopenharmony_ci	"PREFERRED_BSSID",
26062306a36Sopenharmony_ci	"SET_SCAN_OPTIONS",
26162306a36Sopenharmony_ci	"SCAN_DWELL_TIME",
26262306a36Sopenharmony_ci	"SWEEP_TABLE",
26362306a36Sopenharmony_ci	"AP_OR_STATION_TABLE",
26462306a36Sopenharmony_ci	"GROUP_ORDINALS",
26562306a36Sopenharmony_ci	"SHORT_RETRY_LIMIT",
26662306a36Sopenharmony_ci	"LONG_RETRY_LIMIT",
26762306a36Sopenharmony_ci	"unused",		/* SAVE_CALIBRATION */
26862306a36Sopenharmony_ci	"unused",		/* RESTORE_CALIBRATION */
26962306a36Sopenharmony_ci	"undefined",
27062306a36Sopenharmony_ci	"undefined",
27162306a36Sopenharmony_ci	"undefined",
27262306a36Sopenharmony_ci	"HOST_PRE_POWER_DOWN",
27362306a36Sopenharmony_ci	"unused",		/* HOST_INTERRUPT_COALESCING */
27462306a36Sopenharmony_ci	"undefined",
27562306a36Sopenharmony_ci	"CARD_DISABLE_PHY_OFF",
27662306a36Sopenharmony_ci	"MSDU_TX_RATES",
27762306a36Sopenharmony_ci	"undefined",
27862306a36Sopenharmony_ci	"SET_STATION_STAT_BITS",
27962306a36Sopenharmony_ci	"CLEAR_STATIONS_STAT_BITS",
28062306a36Sopenharmony_ci	"LEAP_ROGUE_MODE",
28162306a36Sopenharmony_ci	"SET_SECURITY_INFORMATION",
28262306a36Sopenharmony_ci	"DISASSOCIATION_BSSID",
28362306a36Sopenharmony_ci	"SET_WPA_ASS_IE"
28462306a36Sopenharmony_ci};
28562306a36Sopenharmony_ci#endif
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic const long ipw2100_frequencies[] = {
28862306a36Sopenharmony_ci	2412, 2417, 2422, 2427,
28962306a36Sopenharmony_ci	2432, 2437, 2442, 2447,
29062306a36Sopenharmony_ci	2452, 2457, 2462, 2467,
29162306a36Sopenharmony_ci	2472, 2484
29262306a36Sopenharmony_ci};
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci#define FREQ_COUNT	ARRAY_SIZE(ipw2100_frequencies)
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic struct ieee80211_rate ipw2100_bg_rates[] = {
29762306a36Sopenharmony_ci	{ .bitrate = 10 },
29862306a36Sopenharmony_ci	{ .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
29962306a36Sopenharmony_ci	{ .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
30062306a36Sopenharmony_ci	{ .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
30162306a36Sopenharmony_ci};
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci#define RATE_COUNT ARRAY_SIZE(ipw2100_bg_rates)
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/* Pre-decl until we get the code solid and then we can clean it up */
30662306a36Sopenharmony_cistatic void ipw2100_tx_send_commands(struct ipw2100_priv *priv);
30762306a36Sopenharmony_cistatic void ipw2100_tx_send_data(struct ipw2100_priv *priv);
30862306a36Sopenharmony_cistatic int ipw2100_adapter_setup(struct ipw2100_priv *priv);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic void ipw2100_queues_initialize(struct ipw2100_priv *priv);
31162306a36Sopenharmony_cistatic void ipw2100_queues_free(struct ipw2100_priv *priv);
31262306a36Sopenharmony_cistatic int ipw2100_queues_allocate(struct ipw2100_priv *priv);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic int ipw2100_fw_download(struct ipw2100_priv *priv,
31562306a36Sopenharmony_ci			       struct ipw2100_fw *fw);
31662306a36Sopenharmony_cistatic int ipw2100_get_firmware(struct ipw2100_priv *priv,
31762306a36Sopenharmony_ci				struct ipw2100_fw *fw);
31862306a36Sopenharmony_cistatic int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
31962306a36Sopenharmony_ci				 size_t max);
32062306a36Sopenharmony_cistatic int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
32162306a36Sopenharmony_ci				    size_t max);
32262306a36Sopenharmony_cistatic void ipw2100_release_firmware(struct ipw2100_priv *priv,
32362306a36Sopenharmony_ci				     struct ipw2100_fw *fw);
32462306a36Sopenharmony_cistatic int ipw2100_ucode_download(struct ipw2100_priv *priv,
32562306a36Sopenharmony_ci				  struct ipw2100_fw *fw);
32662306a36Sopenharmony_cistatic void ipw2100_wx_event_work(struct work_struct *work);
32762306a36Sopenharmony_cistatic struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev);
32862306a36Sopenharmony_cistatic const struct iw_handler_def ipw2100_wx_handler_def;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic inline void read_register(struct net_device *dev, u32 reg, u32 * val)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	*val = ioread32(priv->ioaddr + reg);
33562306a36Sopenharmony_ci	IPW_DEBUG_IO("r: 0x%08X => 0x%08X\n", reg, *val);
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic inline void write_register(struct net_device *dev, u32 reg, u32 val)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	iowrite32(val, priv->ioaddr + reg);
34362306a36Sopenharmony_ci	IPW_DEBUG_IO("w: 0x%08X <= 0x%08X\n", reg, val);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic inline void read_register_word(struct net_device *dev, u32 reg,
34762306a36Sopenharmony_ci				      u16 * val)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	*val = ioread16(priv->ioaddr + reg);
35262306a36Sopenharmony_ci	IPW_DEBUG_IO("r: 0x%08X => %04X\n", reg, *val);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic inline void read_register_byte(struct net_device *dev, u32 reg, u8 * val)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	*val = ioread8(priv->ioaddr + reg);
36062306a36Sopenharmony_ci	IPW_DEBUG_IO("r: 0x%08X => %02X\n", reg, *val);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic inline void write_register_word(struct net_device *dev, u32 reg, u16 val)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	iowrite16(val, priv->ioaddr + reg);
36862306a36Sopenharmony_ci	IPW_DEBUG_IO("w: 0x%08X <= %04X\n", reg, val);
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic inline void write_register_byte(struct net_device *dev, u32 reg, u8 val)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	iowrite8(val, priv->ioaddr + reg);
37662306a36Sopenharmony_ci	IPW_DEBUG_IO("w: 0x%08X =< %02X\n", reg, val);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic inline void read_nic_dword(struct net_device *dev, u32 addr, u32 * val)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
38262306a36Sopenharmony_ci		       addr & IPW_REG_INDIRECT_ADDR_MASK);
38362306a36Sopenharmony_ci	read_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic inline void write_nic_dword(struct net_device *dev, u32 addr, u32 val)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
38962306a36Sopenharmony_ci		       addr & IPW_REG_INDIRECT_ADDR_MASK);
39062306a36Sopenharmony_ci	write_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic inline void read_nic_word(struct net_device *dev, u32 addr, u16 * val)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
39662306a36Sopenharmony_ci		       addr & IPW_REG_INDIRECT_ADDR_MASK);
39762306a36Sopenharmony_ci	read_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic inline void write_nic_word(struct net_device *dev, u32 addr, u16 val)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
40362306a36Sopenharmony_ci		       addr & IPW_REG_INDIRECT_ADDR_MASK);
40462306a36Sopenharmony_ci	write_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic inline void read_nic_byte(struct net_device *dev, u32 addr, u8 * val)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
41062306a36Sopenharmony_ci		       addr & IPW_REG_INDIRECT_ADDR_MASK);
41162306a36Sopenharmony_ci	read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic inline void write_nic_byte(struct net_device *dev, u32 addr, u8 val)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
41762306a36Sopenharmony_ci		       addr & IPW_REG_INDIRECT_ADDR_MASK);
41862306a36Sopenharmony_ci	write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void write_nic_memory(struct net_device *dev, u32 addr, u32 len,
42262306a36Sopenharmony_ci				    const u8 * buf)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	u32 aligned_addr;
42562306a36Sopenharmony_ci	u32 aligned_len;
42662306a36Sopenharmony_ci	u32 dif_len;
42762306a36Sopenharmony_ci	u32 i;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* read first nibble byte by byte */
43062306a36Sopenharmony_ci	aligned_addr = addr & (~0x3);
43162306a36Sopenharmony_ci	dif_len = addr - aligned_addr;
43262306a36Sopenharmony_ci	if (dif_len) {
43362306a36Sopenharmony_ci		/* Start reading at aligned_addr + dif_len */
43462306a36Sopenharmony_ci		write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
43562306a36Sopenharmony_ci			       aligned_addr);
43662306a36Sopenharmony_ci		for (i = dif_len; i < 4; i++, buf++)
43762306a36Sopenharmony_ci			write_register_byte(dev,
43862306a36Sopenharmony_ci					    IPW_REG_INDIRECT_ACCESS_DATA + i,
43962306a36Sopenharmony_ci					    *buf);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		len -= dif_len;
44262306a36Sopenharmony_ci		aligned_addr += 4;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* read DWs through autoincrement registers */
44662306a36Sopenharmony_ci	write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr);
44762306a36Sopenharmony_ci	aligned_len = len & (~0x3);
44862306a36Sopenharmony_ci	for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
44962306a36Sopenharmony_ci		write_register(dev, IPW_REG_AUTOINCREMENT_DATA, *(u32 *) buf);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* copy the last nibble */
45262306a36Sopenharmony_ci	dif_len = len - aligned_len;
45362306a36Sopenharmony_ci	write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
45462306a36Sopenharmony_ci	for (i = 0; i < dif_len; i++, buf++)
45562306a36Sopenharmony_ci		write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i,
45662306a36Sopenharmony_ci				    *buf);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic void read_nic_memory(struct net_device *dev, u32 addr, u32 len,
46062306a36Sopenharmony_ci				   u8 * buf)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	u32 aligned_addr;
46362306a36Sopenharmony_ci	u32 aligned_len;
46462306a36Sopenharmony_ci	u32 dif_len;
46562306a36Sopenharmony_ci	u32 i;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* read first nibble byte by byte */
46862306a36Sopenharmony_ci	aligned_addr = addr & (~0x3);
46962306a36Sopenharmony_ci	dif_len = addr - aligned_addr;
47062306a36Sopenharmony_ci	if (dif_len) {
47162306a36Sopenharmony_ci		/* Start reading at aligned_addr + dif_len */
47262306a36Sopenharmony_ci		write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
47362306a36Sopenharmony_ci			       aligned_addr);
47462306a36Sopenharmony_ci		for (i = dif_len; i < 4; i++, buf++)
47562306a36Sopenharmony_ci			read_register_byte(dev,
47662306a36Sopenharmony_ci					   IPW_REG_INDIRECT_ACCESS_DATA + i,
47762306a36Sopenharmony_ci					   buf);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		len -= dif_len;
48062306a36Sopenharmony_ci		aligned_addr += 4;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* read DWs through autoincrement registers */
48462306a36Sopenharmony_ci	write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr);
48562306a36Sopenharmony_ci	aligned_len = len & (~0x3);
48662306a36Sopenharmony_ci	for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
48762306a36Sopenharmony_ci		read_register(dev, IPW_REG_AUTOINCREMENT_DATA, (u32 *) buf);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* copy the last nibble */
49062306a36Sopenharmony_ci	dif_len = len - aligned_len;
49162306a36Sopenharmony_ci	write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
49262306a36Sopenharmony_ci	for (i = 0; i < dif_len; i++, buf++)
49362306a36Sopenharmony_ci		read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, buf);
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic bool ipw2100_hw_is_adapter_in_system(struct net_device *dev)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	u32 dbg;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	read_register(dev, IPW_REG_DOA_DEBUG_AREA_START, &dbg);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return dbg == IPW_DATA_DOA_DEBUG_VALUE;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord,
50662306a36Sopenharmony_ci			       void *val, u32 * len)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	struct ipw2100_ordinals *ordinals = &priv->ordinals;
50962306a36Sopenharmony_ci	u32 addr;
51062306a36Sopenharmony_ci	u32 field_info;
51162306a36Sopenharmony_ci	u16 field_len;
51262306a36Sopenharmony_ci	u16 field_count;
51362306a36Sopenharmony_ci	u32 total_length;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (ordinals->table1_addr == 0) {
51662306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME ": attempt to use fw ordinals "
51762306a36Sopenharmony_ci		       "before they have been loaded.\n");
51862306a36Sopenharmony_ci		return -EINVAL;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
52262306a36Sopenharmony_ci		if (*len < IPW_ORD_TAB_1_ENTRY_SIZE) {
52362306a36Sopenharmony_ci			*len = IPW_ORD_TAB_1_ENTRY_SIZE;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci			printk(KERN_WARNING DRV_NAME
52662306a36Sopenharmony_ci			       ": ordinal buffer length too small, need %zd\n",
52762306a36Sopenharmony_ci			       IPW_ORD_TAB_1_ENTRY_SIZE);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci			return -EINVAL;
53062306a36Sopenharmony_ci		}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		read_nic_dword(priv->net_dev,
53362306a36Sopenharmony_ci			       ordinals->table1_addr + (ord << 2), &addr);
53462306a36Sopenharmony_ci		read_nic_dword(priv->net_dev, addr, val);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		*len = IPW_ORD_TAB_1_ENTRY_SIZE;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		return 0;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) {
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci		ord -= IPW_START_ORD_TAB_2;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		/* get the address of statistic */
54662306a36Sopenharmony_ci		read_nic_dword(priv->net_dev,
54762306a36Sopenharmony_ci			       ordinals->table2_addr + (ord << 3), &addr);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		/* get the second DW of statistics ;
55062306a36Sopenharmony_ci		 * two 16-bit words - first is length, second is count */
55162306a36Sopenharmony_ci		read_nic_dword(priv->net_dev,
55262306a36Sopenharmony_ci			       ordinals->table2_addr + (ord << 3) + sizeof(u32),
55362306a36Sopenharmony_ci			       &field_info);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		/* get each entry length */
55662306a36Sopenharmony_ci		field_len = *((u16 *) & field_info);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		/* get number of entries */
55962306a36Sopenharmony_ci		field_count = *(((u16 *) & field_info) + 1);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci		/* abort if no enough memory */
56262306a36Sopenharmony_ci		total_length = field_len * field_count;
56362306a36Sopenharmony_ci		if (total_length > *len) {
56462306a36Sopenharmony_ci			*len = total_length;
56562306a36Sopenharmony_ci			return -EINVAL;
56662306a36Sopenharmony_ci		}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		*len = total_length;
56962306a36Sopenharmony_ci		if (!total_length)
57062306a36Sopenharmony_ci			return 0;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci		/* read the ordinal data from the SRAM */
57362306a36Sopenharmony_ci		read_nic_memory(priv->net_dev, addr, total_length, val);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		return 0;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	printk(KERN_WARNING DRV_NAME ": ordinal %d neither in table 1 nor "
57962306a36Sopenharmony_ci	       "in table 2\n", ord);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	return -EINVAL;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic int ipw2100_set_ordinal(struct ipw2100_priv *priv, u32 ord, u32 * val,
58562306a36Sopenharmony_ci			       u32 * len)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct ipw2100_ordinals *ordinals = &priv->ordinals;
58862306a36Sopenharmony_ci	u32 addr;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
59162306a36Sopenharmony_ci		if (*len != IPW_ORD_TAB_1_ENTRY_SIZE) {
59262306a36Sopenharmony_ci			*len = IPW_ORD_TAB_1_ENTRY_SIZE;
59362306a36Sopenharmony_ci			IPW_DEBUG_INFO("wrong size\n");
59462306a36Sopenharmony_ci			return -EINVAL;
59562306a36Sopenharmony_ci		}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		read_nic_dword(priv->net_dev,
59862306a36Sopenharmony_ci			       ordinals->table1_addr + (ord << 2), &addr);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		write_nic_dword(priv->net_dev, addr, *val);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		*len = IPW_ORD_TAB_1_ENTRY_SIZE;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		return 0;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	IPW_DEBUG_INFO("wrong table\n");
60862306a36Sopenharmony_ci	if (IS_ORDINAL_TABLE_TWO(ordinals, ord))
60962306a36Sopenharmony_ci		return -EINVAL;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	return -EINVAL;
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic char *snprint_line(char *buf, size_t count,
61562306a36Sopenharmony_ci			  const u8 * data, u32 len, u32 ofs)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	int out, i, j, l;
61862306a36Sopenharmony_ci	char c;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	out = scnprintf(buf, count, "%08X", ofs);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	for (l = 0, i = 0; i < 2; i++) {
62362306a36Sopenharmony_ci		out += scnprintf(buf + out, count - out, " ");
62462306a36Sopenharmony_ci		for (j = 0; j < 8 && l < len; j++, l++)
62562306a36Sopenharmony_ci			out += scnprintf(buf + out, count - out, "%02X ",
62662306a36Sopenharmony_ci					data[(i * 8 + j)]);
62762306a36Sopenharmony_ci		for (; j < 8; j++)
62862306a36Sopenharmony_ci			out += scnprintf(buf + out, count - out, "   ");
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	out += scnprintf(buf + out, count - out, " ");
63262306a36Sopenharmony_ci	for (l = 0, i = 0; i < 2; i++) {
63362306a36Sopenharmony_ci		out += scnprintf(buf + out, count - out, " ");
63462306a36Sopenharmony_ci		for (j = 0; j < 8 && l < len; j++, l++) {
63562306a36Sopenharmony_ci			c = data[(i * 8 + j)];
63662306a36Sopenharmony_ci			if (!isascii(c) || !isprint(c))
63762306a36Sopenharmony_ci				c = '.';
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci			out += scnprintf(buf + out, count - out, "%c", c);
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci		for (; j < 8; j++)
64362306a36Sopenharmony_ci			out += scnprintf(buf + out, count - out, " ");
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	return buf;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic void printk_buf(int level, const u8 * data, u32 len)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	char line[81];
65262306a36Sopenharmony_ci	u32 ofs = 0;
65362306a36Sopenharmony_ci	if (!(ipw2100_debug_level & level))
65462306a36Sopenharmony_ci		return;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	while (len) {
65762306a36Sopenharmony_ci		printk(KERN_DEBUG "%s\n",
65862306a36Sopenharmony_ci		       snprint_line(line, sizeof(line), &data[ofs],
65962306a36Sopenharmony_ci				    min(len, 16U), ofs));
66062306a36Sopenharmony_ci		ofs += 16;
66162306a36Sopenharmony_ci		len -= min(len, 16U);
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci#define MAX_RESET_BACKOFF 10
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cistatic void schedule_reset(struct ipw2100_priv *priv)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	time64_t now = ktime_get_boottime_seconds();
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/* If we haven't received a reset request within the backoff period,
67262306a36Sopenharmony_ci	 * then we can reset the backoff interval so this reset occurs
67362306a36Sopenharmony_ci	 * immediately */
67462306a36Sopenharmony_ci	if (priv->reset_backoff &&
67562306a36Sopenharmony_ci	    (now - priv->last_reset > priv->reset_backoff))
67662306a36Sopenharmony_ci		priv->reset_backoff = 0;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	priv->last_reset = now;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (!(priv->status & STATUS_RESET_PENDING)) {
68162306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: Scheduling firmware restart (%llds).\n",
68262306a36Sopenharmony_ci			       priv->net_dev->name, priv->reset_backoff);
68362306a36Sopenharmony_ci		netif_carrier_off(priv->net_dev);
68462306a36Sopenharmony_ci		netif_stop_queue(priv->net_dev);
68562306a36Sopenharmony_ci		priv->status |= STATUS_RESET_PENDING;
68662306a36Sopenharmony_ci		if (priv->reset_backoff)
68762306a36Sopenharmony_ci			schedule_delayed_work(&priv->reset_work,
68862306a36Sopenharmony_ci					      priv->reset_backoff * HZ);
68962306a36Sopenharmony_ci		else
69062306a36Sopenharmony_ci			schedule_delayed_work(&priv->reset_work, 0);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		if (priv->reset_backoff < MAX_RESET_BACKOFF)
69362306a36Sopenharmony_ci			priv->reset_backoff++;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci		wake_up_interruptible(&priv->wait_command_queue);
69662306a36Sopenharmony_ci	} else
69762306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: Firmware restart already in progress.\n",
69862306a36Sopenharmony_ci			       priv->net_dev->name);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci#define HOST_COMPLETE_TIMEOUT (2 * HZ)
70362306a36Sopenharmony_cistatic int ipw2100_hw_send_command(struct ipw2100_priv *priv,
70462306a36Sopenharmony_ci				   struct host_command *cmd)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct list_head *element;
70762306a36Sopenharmony_ci	struct ipw2100_tx_packet *packet;
70862306a36Sopenharmony_ci	unsigned long flags;
70962306a36Sopenharmony_ci	int err = 0;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n",
71262306a36Sopenharmony_ci		     command_types[cmd->host_command], cmd->host_command,
71362306a36Sopenharmony_ci		     cmd->host_command_length);
71462306a36Sopenharmony_ci	printk_buf(IPW_DL_HC, (u8 *) cmd->host_command_parameters,
71562306a36Sopenharmony_ci		   cmd->host_command_length);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	spin_lock_irqsave(&priv->low_lock, flags);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (priv->fatal_error) {
72062306a36Sopenharmony_ci		IPW_DEBUG_INFO
72162306a36Sopenharmony_ci		    ("Attempt to send command while hardware in fatal error condition.\n");
72262306a36Sopenharmony_ci		err = -EIO;
72362306a36Sopenharmony_ci		goto fail_unlock;
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (!(priv->status & STATUS_RUNNING)) {
72762306a36Sopenharmony_ci		IPW_DEBUG_INFO
72862306a36Sopenharmony_ci		    ("Attempt to send command while hardware is not running.\n");
72962306a36Sopenharmony_ci		err = -EIO;
73062306a36Sopenharmony_ci		goto fail_unlock;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (priv->status & STATUS_CMD_ACTIVE) {
73462306a36Sopenharmony_ci		IPW_DEBUG_INFO
73562306a36Sopenharmony_ci		    ("Attempt to send command while another command is pending.\n");
73662306a36Sopenharmony_ci		err = -EBUSY;
73762306a36Sopenharmony_ci		goto fail_unlock;
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	if (list_empty(&priv->msg_free_list)) {
74162306a36Sopenharmony_ci		IPW_DEBUG_INFO("no available msg buffers\n");
74262306a36Sopenharmony_ci		goto fail_unlock;
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	priv->status |= STATUS_CMD_ACTIVE;
74662306a36Sopenharmony_ci	priv->messages_sent++;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	element = priv->msg_free_list.next;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	packet = list_entry(element, struct ipw2100_tx_packet, list);
75162306a36Sopenharmony_ci	packet->jiffy_start = jiffies;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	/* initialize the firmware command packet */
75462306a36Sopenharmony_ci	packet->info.c_struct.cmd->host_command_reg = cmd->host_command;
75562306a36Sopenharmony_ci	packet->info.c_struct.cmd->host_command_reg1 = cmd->host_command1;
75662306a36Sopenharmony_ci	packet->info.c_struct.cmd->host_command_len_reg =
75762306a36Sopenharmony_ci	    cmd->host_command_length;
75862306a36Sopenharmony_ci	packet->info.c_struct.cmd->sequence = cmd->host_command_sequence;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	memcpy(packet->info.c_struct.cmd->host_command_params_reg,
76162306a36Sopenharmony_ci	       cmd->host_command_parameters,
76262306a36Sopenharmony_ci	       sizeof(packet->info.c_struct.cmd->host_command_params_reg));
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	list_del(element);
76562306a36Sopenharmony_ci	DEC_STAT(&priv->msg_free_stat);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	list_add_tail(element, &priv->msg_pend_list);
76862306a36Sopenharmony_ci	INC_STAT(&priv->msg_pend_stat);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	ipw2100_tx_send_commands(priv);
77162306a36Sopenharmony_ci	ipw2100_tx_send_data(priv);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/*
77662306a36Sopenharmony_ci	 * We must wait for this command to complete before another
77762306a36Sopenharmony_ci	 * command can be sent...  but if we wait more than 3 seconds
77862306a36Sopenharmony_ci	 * then there is a problem.
77962306a36Sopenharmony_ci	 */
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	err =
78262306a36Sopenharmony_ci	    wait_event_interruptible_timeout(priv->wait_command_queue,
78362306a36Sopenharmony_ci					     !(priv->
78462306a36Sopenharmony_ci					       status & STATUS_CMD_ACTIVE),
78562306a36Sopenharmony_ci					     HOST_COMPLETE_TIMEOUT);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (err == 0) {
78862306a36Sopenharmony_ci		IPW_DEBUG_INFO("Command completion failed out after %dms.\n",
78962306a36Sopenharmony_ci			       1000 * (HOST_COMPLETE_TIMEOUT / HZ));
79062306a36Sopenharmony_ci		priv->fatal_error = IPW2100_ERR_MSG_TIMEOUT;
79162306a36Sopenharmony_ci		priv->status &= ~STATUS_CMD_ACTIVE;
79262306a36Sopenharmony_ci		schedule_reset(priv);
79362306a36Sopenharmony_ci		return -EIO;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (priv->fatal_error) {
79762306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME ": %s: firmware fatal error\n",
79862306a36Sopenharmony_ci		       priv->net_dev->name);
79962306a36Sopenharmony_ci		return -EIO;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	/* !!!!! HACK TEST !!!!!
80362306a36Sopenharmony_ci	 * When lots of debug trace statements are enabled, the driver
80462306a36Sopenharmony_ci	 * doesn't seem to have as many firmware restart cycles...
80562306a36Sopenharmony_ci	 *
80662306a36Sopenharmony_ci	 * As a test, we're sticking in a 1/100s delay here */
80762306a36Sopenharmony_ci	schedule_timeout_uninterruptible(msecs_to_jiffies(10));
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	return 0;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci      fail_unlock:
81262306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	return err;
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci/*
81862306a36Sopenharmony_ci * Verify the values and data access of the hardware
81962306a36Sopenharmony_ci * No locks needed or used.  No functions called.
82062306a36Sopenharmony_ci */
82162306a36Sopenharmony_cistatic int ipw2100_verify(struct ipw2100_priv *priv)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	u32 data1, data2;
82462306a36Sopenharmony_ci	u32 address;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	u32 val1 = 0x76543210;
82762306a36Sopenharmony_ci	u32 val2 = 0xFEDCBA98;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	/* Domain 0 check - all values should be DOA_DEBUG */
83062306a36Sopenharmony_ci	for (address = IPW_REG_DOA_DEBUG_AREA_START;
83162306a36Sopenharmony_ci	     address < IPW_REG_DOA_DEBUG_AREA_END; address += sizeof(u32)) {
83262306a36Sopenharmony_ci		read_register(priv->net_dev, address, &data1);
83362306a36Sopenharmony_ci		if (data1 != IPW_DATA_DOA_DEBUG_VALUE)
83462306a36Sopenharmony_ci			return -EIO;
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	/* Domain 1 check - use arbitrary read/write compare  */
83862306a36Sopenharmony_ci	for (address = 0; address < 5; address++) {
83962306a36Sopenharmony_ci		/* The memory area is not used now */
84062306a36Sopenharmony_ci		write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
84162306a36Sopenharmony_ci			       val1);
84262306a36Sopenharmony_ci		write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
84362306a36Sopenharmony_ci			       val2);
84462306a36Sopenharmony_ci		read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
84562306a36Sopenharmony_ci			      &data1);
84662306a36Sopenharmony_ci		read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
84762306a36Sopenharmony_ci			      &data2);
84862306a36Sopenharmony_ci		if (val1 == data1 && val2 == data2)
84962306a36Sopenharmony_ci			return 0;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	return -EIO;
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci/*
85662306a36Sopenharmony_ci *
85762306a36Sopenharmony_ci * Loop until the CARD_DISABLED bit is the same value as the
85862306a36Sopenharmony_ci * supplied parameter
85962306a36Sopenharmony_ci *
86062306a36Sopenharmony_ci * TODO: See if it would be more efficient to do a wait/wake
86162306a36Sopenharmony_ci *       cycle and have the completion event trigger the wakeup
86262306a36Sopenharmony_ci *
86362306a36Sopenharmony_ci */
86462306a36Sopenharmony_ci#define IPW_CARD_DISABLE_COMPLETE_WAIT		    100	// 100 milli
86562306a36Sopenharmony_cistatic int ipw2100_wait_for_card_state(struct ipw2100_priv *priv, int state)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	int i;
86862306a36Sopenharmony_ci	u32 card_state;
86962306a36Sopenharmony_ci	u32 len = sizeof(card_state);
87062306a36Sopenharmony_ci	int err;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	for (i = 0; i <= IPW_CARD_DISABLE_COMPLETE_WAIT * 1000; i += 50) {
87362306a36Sopenharmony_ci		err = ipw2100_get_ordinal(priv, IPW_ORD_CARD_DISABLED,
87462306a36Sopenharmony_ci					  &card_state, &len);
87562306a36Sopenharmony_ci		if (err) {
87662306a36Sopenharmony_ci			IPW_DEBUG_INFO("Query of CARD_DISABLED ordinal "
87762306a36Sopenharmony_ci				       "failed.\n");
87862306a36Sopenharmony_ci			return 0;
87962306a36Sopenharmony_ci		}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		/* We'll break out if either the HW state says it is
88262306a36Sopenharmony_ci		 * in the state we want, or if HOST_COMPLETE command
88362306a36Sopenharmony_ci		 * finishes */
88462306a36Sopenharmony_ci		if ((card_state == state) ||
88562306a36Sopenharmony_ci		    ((priv->status & STATUS_ENABLED) ?
88662306a36Sopenharmony_ci		     IPW_HW_STATE_ENABLED : IPW_HW_STATE_DISABLED) == state) {
88762306a36Sopenharmony_ci			if (state == IPW_HW_STATE_ENABLED)
88862306a36Sopenharmony_ci				priv->status |= STATUS_ENABLED;
88962306a36Sopenharmony_ci			else
89062306a36Sopenharmony_ci				priv->status &= ~STATUS_ENABLED;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci			return 0;
89362306a36Sopenharmony_ci		}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci		udelay(50);
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	IPW_DEBUG_INFO("ipw2100_wait_for_card_state to %s state timed out\n",
89962306a36Sopenharmony_ci		       state ? "DISABLED" : "ENABLED");
90062306a36Sopenharmony_ci	return -EIO;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci/*********************************************************************
90462306a36Sopenharmony_ci    Procedure   :   sw_reset_and_clock
90562306a36Sopenharmony_ci    Purpose     :   Asserts s/w reset, asserts clock initialization
90662306a36Sopenharmony_ci                    and waits for clock stabilization
90762306a36Sopenharmony_ci ********************************************************************/
90862306a36Sopenharmony_cistatic int sw_reset_and_clock(struct ipw2100_priv *priv)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	int i;
91162306a36Sopenharmony_ci	u32 r;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	// assert s/w reset
91462306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_RESET_REG,
91562306a36Sopenharmony_ci		       IPW_AUX_HOST_RESET_REG_SW_RESET);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	// wait for clock stabilization
91862306a36Sopenharmony_ci	for (i = 0; i < 1000; i++) {
91962306a36Sopenharmony_ci		udelay(IPW_WAIT_RESET_ARC_COMPLETE_DELAY);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci		// check clock ready bit
92262306a36Sopenharmony_ci		read_register(priv->net_dev, IPW_REG_RESET_REG, &r);
92362306a36Sopenharmony_ci		if (r & IPW_AUX_HOST_RESET_REG_PRINCETON_RESET)
92462306a36Sopenharmony_ci			break;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	if (i == 1000)
92862306a36Sopenharmony_ci		return -EIO;	// TODO: better error value
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	/* set "initialization complete" bit to move adapter to
93162306a36Sopenharmony_ci	 * D0 state */
93262306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_GP_CNTRL,
93362306a36Sopenharmony_ci		       IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	/* wait for clock stabilization */
93662306a36Sopenharmony_ci	for (i = 0; i < 10000; i++) {
93762306a36Sopenharmony_ci		udelay(IPW_WAIT_CLOCK_STABILIZATION_DELAY * 4);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci		/* check clock ready bit */
94062306a36Sopenharmony_ci		read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
94162306a36Sopenharmony_ci		if (r & IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY)
94262306a36Sopenharmony_ci			break;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (i == 10000)
94662306a36Sopenharmony_ci		return -EIO;	/* TODO: better error value */
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	/* set D0 standby bit */
94962306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
95062306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_GP_CNTRL,
95162306a36Sopenharmony_ci		       r | IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	return 0;
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci/*********************************************************************
95762306a36Sopenharmony_ci    Procedure   :   ipw2100_download_firmware
95862306a36Sopenharmony_ci    Purpose     :   Initiaze adapter after power on.
95962306a36Sopenharmony_ci                    The sequence is:
96062306a36Sopenharmony_ci                    1. assert s/w reset first!
96162306a36Sopenharmony_ci                    2. awake clocks & wait for clock stabilization
96262306a36Sopenharmony_ci                    3. hold ARC (don't ask me why...)
96362306a36Sopenharmony_ci                    4. load Dino ucode and reset/clock init again
96462306a36Sopenharmony_ci                    5. zero-out shared mem
96562306a36Sopenharmony_ci                    6. download f/w
96662306a36Sopenharmony_ci *******************************************************************/
96762306a36Sopenharmony_cistatic int ipw2100_download_firmware(struct ipw2100_priv *priv)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	u32 address;
97062306a36Sopenharmony_ci	int err;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci#ifndef CONFIG_PM
97362306a36Sopenharmony_ci	/* Fetch the firmware and microcode */
97462306a36Sopenharmony_ci	struct ipw2100_fw ipw2100_firmware;
97562306a36Sopenharmony_ci#endif
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (priv->fatal_error) {
97862306a36Sopenharmony_ci		IPW_DEBUG_ERROR("%s: ipw2100_download_firmware called after "
97962306a36Sopenharmony_ci				"fatal error %d.  Interface must be brought down.\n",
98062306a36Sopenharmony_ci				priv->net_dev->name, priv->fatal_error);
98162306a36Sopenharmony_ci		return -EINVAL;
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci#ifdef CONFIG_PM
98462306a36Sopenharmony_ci	if (!ipw2100_firmware.version) {
98562306a36Sopenharmony_ci		err = ipw2100_get_firmware(priv, &ipw2100_firmware);
98662306a36Sopenharmony_ci		if (err) {
98762306a36Sopenharmony_ci			IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
98862306a36Sopenharmony_ci					priv->net_dev->name, err);
98962306a36Sopenharmony_ci			priv->fatal_error = IPW2100_ERR_FW_LOAD;
99062306a36Sopenharmony_ci			goto fail;
99162306a36Sopenharmony_ci		}
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci#else
99462306a36Sopenharmony_ci	err = ipw2100_get_firmware(priv, &ipw2100_firmware);
99562306a36Sopenharmony_ci	if (err) {
99662306a36Sopenharmony_ci		IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
99762306a36Sopenharmony_ci				priv->net_dev->name, err);
99862306a36Sopenharmony_ci		priv->fatal_error = IPW2100_ERR_FW_LOAD;
99962306a36Sopenharmony_ci		goto fail;
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci#endif
100262306a36Sopenharmony_ci	priv->firmware_version = ipw2100_firmware.version;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/* s/w reset and clock stabilization */
100562306a36Sopenharmony_ci	err = sw_reset_and_clock(priv);
100662306a36Sopenharmony_ci	if (err) {
100762306a36Sopenharmony_ci		IPW_DEBUG_ERROR("%s: sw_reset_and_clock failed: %d\n",
100862306a36Sopenharmony_ci				priv->net_dev->name, err);
100962306a36Sopenharmony_ci		goto fail;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	err = ipw2100_verify(priv);
101362306a36Sopenharmony_ci	if (err) {
101462306a36Sopenharmony_ci		IPW_DEBUG_ERROR("%s: ipw2100_verify failed: %d\n",
101562306a36Sopenharmony_ci				priv->net_dev->name, err);
101662306a36Sopenharmony_ci		goto fail;
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	/* Hold ARC */
102062306a36Sopenharmony_ci	write_nic_dword(priv->net_dev,
102162306a36Sopenharmony_ci			IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x80000000);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* allow ARC to run */
102462306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	/* load microcode */
102762306a36Sopenharmony_ci	err = ipw2100_ucode_download(priv, &ipw2100_firmware);
102862306a36Sopenharmony_ci	if (err) {
102962306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME ": %s: Error loading microcode: %d\n",
103062306a36Sopenharmony_ci		       priv->net_dev->name, err);
103162306a36Sopenharmony_ci		goto fail;
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	/* release ARC */
103562306a36Sopenharmony_ci	write_nic_dword(priv->net_dev,
103662306a36Sopenharmony_ci			IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x00000000);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	/* s/w reset and clock stabilization (again!!!) */
103962306a36Sopenharmony_ci	err = sw_reset_and_clock(priv);
104062306a36Sopenharmony_ci	if (err) {
104162306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME
104262306a36Sopenharmony_ci		       ": %s: sw_reset_and_clock failed: %d\n",
104362306a36Sopenharmony_ci		       priv->net_dev->name, err);
104462306a36Sopenharmony_ci		goto fail;
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	/* load f/w */
104862306a36Sopenharmony_ci	err = ipw2100_fw_download(priv, &ipw2100_firmware);
104962306a36Sopenharmony_ci	if (err) {
105062306a36Sopenharmony_ci		IPW_DEBUG_ERROR("%s: Error loading firmware: %d\n",
105162306a36Sopenharmony_ci				priv->net_dev->name, err);
105262306a36Sopenharmony_ci		goto fail;
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci#ifndef CONFIG_PM
105562306a36Sopenharmony_ci	/*
105662306a36Sopenharmony_ci	 * When the .resume method of the driver is called, the other
105762306a36Sopenharmony_ci	 * part of the system, i.e. the ide driver could still stay in
105862306a36Sopenharmony_ci	 * the suspend stage. This prevents us from loading the firmware
105962306a36Sopenharmony_ci	 * from the disk.  --YZ
106062306a36Sopenharmony_ci	 */
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	/* free any storage allocated for firmware image */
106362306a36Sopenharmony_ci	ipw2100_release_firmware(priv, &ipw2100_firmware);
106462306a36Sopenharmony_ci#endif
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/* zero out Domain 1 area indirectly (Si requirement) */
106762306a36Sopenharmony_ci	for (address = IPW_HOST_FW_SHARED_AREA0;
106862306a36Sopenharmony_ci	     address < IPW_HOST_FW_SHARED_AREA0_END; address += 4)
106962306a36Sopenharmony_ci		write_nic_dword(priv->net_dev, address, 0);
107062306a36Sopenharmony_ci	for (address = IPW_HOST_FW_SHARED_AREA1;
107162306a36Sopenharmony_ci	     address < IPW_HOST_FW_SHARED_AREA1_END; address += 4)
107262306a36Sopenharmony_ci		write_nic_dword(priv->net_dev, address, 0);
107362306a36Sopenharmony_ci	for (address = IPW_HOST_FW_SHARED_AREA2;
107462306a36Sopenharmony_ci	     address < IPW_HOST_FW_SHARED_AREA2_END; address += 4)
107562306a36Sopenharmony_ci		write_nic_dword(priv->net_dev, address, 0);
107662306a36Sopenharmony_ci	for (address = IPW_HOST_FW_SHARED_AREA3;
107762306a36Sopenharmony_ci	     address < IPW_HOST_FW_SHARED_AREA3_END; address += 4)
107862306a36Sopenharmony_ci		write_nic_dword(priv->net_dev, address, 0);
107962306a36Sopenharmony_ci	for (address = IPW_HOST_FW_INTERRUPT_AREA;
108062306a36Sopenharmony_ci	     address < IPW_HOST_FW_INTERRUPT_AREA_END; address += 4)
108162306a36Sopenharmony_ci		write_nic_dword(priv->net_dev, address, 0);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	return 0;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci      fail:
108662306a36Sopenharmony_ci	ipw2100_release_firmware(priv, &ipw2100_firmware);
108762306a36Sopenharmony_ci	return err;
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_cistatic inline void ipw2100_enable_interrupts(struct ipw2100_priv *priv)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	if (priv->status & STATUS_INT_ENABLED)
109362306a36Sopenharmony_ci		return;
109462306a36Sopenharmony_ci	priv->status |= STATUS_INT_ENABLED;
109562306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_INTA_MASK, IPW_INTERRUPT_MASK);
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic inline void ipw2100_disable_interrupts(struct ipw2100_priv *priv)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	if (!(priv->status & STATUS_INT_ENABLED))
110162306a36Sopenharmony_ci		return;
110262306a36Sopenharmony_ci	priv->status &= ~STATUS_INT_ENABLED;
110362306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_INTA_MASK, 0x0);
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cistatic void ipw2100_initialize_ordinals(struct ipw2100_priv *priv)
110762306a36Sopenharmony_ci{
110862306a36Sopenharmony_ci	struct ipw2100_ordinals *ord = &priv->ordinals;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1,
111362306a36Sopenharmony_ci		      &ord->table1_addr);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2,
111662306a36Sopenharmony_ci		      &ord->table2_addr);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	read_nic_dword(priv->net_dev, ord->table1_addr, &ord->table1_size);
111962306a36Sopenharmony_ci	read_nic_dword(priv->net_dev, ord->table2_addr, &ord->table2_size);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	ord->table2_size &= 0x0000FFFF;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	IPW_DEBUG_INFO("table 1 size: %d\n", ord->table1_size);
112462306a36Sopenharmony_ci	IPW_DEBUG_INFO("table 2 size: %d\n", ord->table2_size);
112562306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic inline void ipw2100_hw_set_gpio(struct ipw2100_priv *priv)
112962306a36Sopenharmony_ci{
113062306a36Sopenharmony_ci	u32 reg = 0;
113162306a36Sopenharmony_ci	/*
113262306a36Sopenharmony_ci	 * Set GPIO 3 writable by FW; GPIO 1 writable
113362306a36Sopenharmony_ci	 * by driver and enable clock
113462306a36Sopenharmony_ci	 */
113562306a36Sopenharmony_ci	reg = (IPW_BIT_GPIO_GPIO3_MASK | IPW_BIT_GPIO_GPIO1_ENABLE |
113662306a36Sopenharmony_ci	       IPW_BIT_GPIO_LED_OFF);
113762306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_GPIO, reg);
113862306a36Sopenharmony_ci}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_cistatic int rf_kill_active(struct ipw2100_priv *priv)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci#define MAX_RF_KILL_CHECKS 5
114362306a36Sopenharmony_ci#define RF_KILL_CHECK_DELAY 40
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	unsigned short value = 0;
114662306a36Sopenharmony_ci	u32 reg = 0;
114762306a36Sopenharmony_ci	int i;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	if (!(priv->hw_features & HW_FEATURE_RFKILL)) {
115062306a36Sopenharmony_ci		wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false);
115162306a36Sopenharmony_ci		priv->status &= ~STATUS_RF_KILL_HW;
115262306a36Sopenharmony_ci		return 0;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	for (i = 0; i < MAX_RF_KILL_CHECKS; i++) {
115662306a36Sopenharmony_ci		udelay(RF_KILL_CHECK_DELAY);
115762306a36Sopenharmony_ci		read_register(priv->net_dev, IPW_REG_GPIO, &reg);
115862306a36Sopenharmony_ci		value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1);
115962306a36Sopenharmony_ci	}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	if (value == 0) {
116262306a36Sopenharmony_ci		wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
116362306a36Sopenharmony_ci		priv->status |= STATUS_RF_KILL_HW;
116462306a36Sopenharmony_ci	} else {
116562306a36Sopenharmony_ci		wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false);
116662306a36Sopenharmony_ci		priv->status &= ~STATUS_RF_KILL_HW;
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	return (value == 0);
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic int ipw2100_get_hw_features(struct ipw2100_priv *priv)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	u32 addr, len;
117562306a36Sopenharmony_ci	u32 val;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	/*
117862306a36Sopenharmony_ci	 * EEPROM_SRAM_DB_START_ADDRESS using ordinal in ordinal table 1
117962306a36Sopenharmony_ci	 */
118062306a36Sopenharmony_ci	len = sizeof(addr);
118162306a36Sopenharmony_ci	if (ipw2100_get_ordinal
118262306a36Sopenharmony_ci	    (priv, IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, &addr, &len)) {
118362306a36Sopenharmony_ci		IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
118462306a36Sopenharmony_ci			       __LINE__);
118562306a36Sopenharmony_ci		return -EIO;
118662306a36Sopenharmony_ci	}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	IPW_DEBUG_INFO("EEPROM address: %08X\n", addr);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	/*
119162306a36Sopenharmony_ci	 * EEPROM version is the byte at offset 0xfd in firmware
119262306a36Sopenharmony_ci	 * We read 4 bytes, then shift out the byte we actually want */
119362306a36Sopenharmony_ci	read_nic_dword(priv->net_dev, addr + 0xFC, &val);
119462306a36Sopenharmony_ci	priv->eeprom_version = (val >> 24) & 0xFF;
119562306a36Sopenharmony_ci	IPW_DEBUG_INFO("EEPROM version: %d\n", priv->eeprom_version);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	/*
119862306a36Sopenharmony_ci	 *  HW RF Kill enable is bit 0 in byte at offset 0x21 in firmware
119962306a36Sopenharmony_ci	 *
120062306a36Sopenharmony_ci	 *  notice that the EEPROM bit is reverse polarity, i.e.
120162306a36Sopenharmony_ci	 *     bit = 0  signifies HW RF kill switch is supported
120262306a36Sopenharmony_ci	 *     bit = 1  signifies HW RF kill switch is NOT supported
120362306a36Sopenharmony_ci	 */
120462306a36Sopenharmony_ci	read_nic_dword(priv->net_dev, addr + 0x20, &val);
120562306a36Sopenharmony_ci	if (!((val >> 24) & 0x01))
120662306a36Sopenharmony_ci		priv->hw_features |= HW_FEATURE_RFKILL;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	IPW_DEBUG_INFO("HW RF Kill: %ssupported.\n",
120962306a36Sopenharmony_ci		       (priv->hw_features & HW_FEATURE_RFKILL) ? "" : "not ");
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	return 0;
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci/*
121562306a36Sopenharmony_ci * Start firmware execution after power on and initialization
121662306a36Sopenharmony_ci * The sequence is:
121762306a36Sopenharmony_ci *  1. Release ARC
121862306a36Sopenharmony_ci *  2. Wait for f/w initialization completes;
121962306a36Sopenharmony_ci */
122062306a36Sopenharmony_cistatic int ipw2100_start_adapter(struct ipw2100_priv *priv)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	int i;
122362306a36Sopenharmony_ci	u32 inta, inta_mask, gpio;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	if (priv->status & STATUS_RUNNING)
122862306a36Sopenharmony_ci		return 0;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	/*
123162306a36Sopenharmony_ci	 * Initialize the hw - drive adapter to DO state by setting
123262306a36Sopenharmony_ci	 * init_done bit. Wait for clk_ready bit and Download
123362306a36Sopenharmony_ci	 * fw & dino ucode
123462306a36Sopenharmony_ci	 */
123562306a36Sopenharmony_ci	if (ipw2100_download_firmware(priv)) {
123662306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME
123762306a36Sopenharmony_ci		       ": %s: Failed to power on the adapter.\n",
123862306a36Sopenharmony_ci		       priv->net_dev->name);
123962306a36Sopenharmony_ci		return -EIO;
124062306a36Sopenharmony_ci	}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	/* Clear the Tx, Rx and Msg queues and the r/w indexes
124362306a36Sopenharmony_ci	 * in the firmware RBD and TBD ring queue */
124462306a36Sopenharmony_ci	ipw2100_queues_initialize(priv);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	ipw2100_hw_set_gpio(priv);
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	/* TODO -- Look at disabling interrupts here to make sure none
124962306a36Sopenharmony_ci	 * get fired during FW initialization */
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	/* Release ARC - clear reset bit */
125262306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	/* wait for f/w initialization complete */
125562306a36Sopenharmony_ci	IPW_DEBUG_FW("Waiting for f/w initialization to complete...\n");
125662306a36Sopenharmony_ci	i = 5000;
125762306a36Sopenharmony_ci	do {
125862306a36Sopenharmony_ci		schedule_timeout_uninterruptible(msecs_to_jiffies(40));
125962306a36Sopenharmony_ci		/* Todo... wait for sync command ... */
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci		read_register(priv->net_dev, IPW_REG_INTA, &inta);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci		/* check "init done" bit */
126462306a36Sopenharmony_ci		if (inta & IPW2100_INTA_FW_INIT_DONE) {
126562306a36Sopenharmony_ci			/* reset "init done" bit */
126662306a36Sopenharmony_ci			write_register(priv->net_dev, IPW_REG_INTA,
126762306a36Sopenharmony_ci				       IPW2100_INTA_FW_INIT_DONE);
126862306a36Sopenharmony_ci			break;
126962306a36Sopenharmony_ci		}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci		/* check error conditions : we check these after the firmware
127262306a36Sopenharmony_ci		 * check so that if there is an error, the interrupt handler
127362306a36Sopenharmony_ci		 * will see it and the adapter will be reset */
127462306a36Sopenharmony_ci		if (inta &
127562306a36Sopenharmony_ci		    (IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR)) {
127662306a36Sopenharmony_ci			/* clear error conditions */
127762306a36Sopenharmony_ci			write_register(priv->net_dev, IPW_REG_INTA,
127862306a36Sopenharmony_ci				       IPW2100_INTA_FATAL_ERROR |
127962306a36Sopenharmony_ci				       IPW2100_INTA_PARITY_ERROR);
128062306a36Sopenharmony_ci		}
128162306a36Sopenharmony_ci	} while (--i);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	/* Clear out any pending INTAs since we aren't supposed to have
128462306a36Sopenharmony_ci	 * interrupts enabled at this point... */
128562306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_REG_INTA, &inta);
128662306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
128762306a36Sopenharmony_ci	inta &= IPW_INTERRUPT_MASK;
128862306a36Sopenharmony_ci	/* Clear out any pending interrupts */
128962306a36Sopenharmony_ci	if (inta & inta_mask)
129062306a36Sopenharmony_ci		write_register(priv->net_dev, IPW_REG_INTA, inta);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	IPW_DEBUG_FW("f/w initialization complete: %s\n",
129362306a36Sopenharmony_ci		     i ? "SUCCESS" : "FAILED");
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	if (!i) {
129662306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
129762306a36Sopenharmony_ci		       ": %s: Firmware did not initialize.\n",
129862306a36Sopenharmony_ci		       priv->net_dev->name);
129962306a36Sopenharmony_ci		return -EIO;
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	/* allow firmware to write to GPIO1 & GPIO3 */
130362306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_REG_GPIO, &gpio);
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	gpio |= (IPW_BIT_GPIO_GPIO1_MASK | IPW_BIT_GPIO_GPIO3_MASK);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_GPIO, gpio);
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	/* Ready to receive commands */
131062306a36Sopenharmony_ci	priv->status |= STATUS_RUNNING;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	/* The adapter has been reset; we are not associated */
131362306a36Sopenharmony_ci	priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	return 0;
131862306a36Sopenharmony_ci}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_cistatic inline void ipw2100_reset_fatalerror(struct ipw2100_priv *priv)
132162306a36Sopenharmony_ci{
132262306a36Sopenharmony_ci	if (!priv->fatal_error)
132362306a36Sopenharmony_ci		return;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	priv->fatal_errors[priv->fatal_index++] = priv->fatal_error;
132662306a36Sopenharmony_ci	priv->fatal_index %= IPW2100_ERROR_QUEUE;
132762306a36Sopenharmony_ci	priv->fatal_error = 0;
132862306a36Sopenharmony_ci}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci/* NOTE: Our interrupt is disabled when this method is called */
133162306a36Sopenharmony_cistatic int ipw2100_power_cycle_adapter(struct ipw2100_priv *priv)
133262306a36Sopenharmony_ci{
133362306a36Sopenharmony_ci	u32 reg;
133462306a36Sopenharmony_ci	int i;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	IPW_DEBUG_INFO("Power cycling the hardware.\n");
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	ipw2100_hw_set_gpio(priv);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	/* Step 1. Stop Master Assert */
134162306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_RESET_REG,
134262306a36Sopenharmony_ci		       IPW_AUX_HOST_RESET_REG_STOP_MASTER);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	/* Step 2. Wait for stop Master Assert
134562306a36Sopenharmony_ci	 *         (not more than 50us, otherwise ret error */
134662306a36Sopenharmony_ci	i = 5;
134762306a36Sopenharmony_ci	do {
134862306a36Sopenharmony_ci		udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
134962306a36Sopenharmony_ci		read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci		if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
135262306a36Sopenharmony_ci			break;
135362306a36Sopenharmony_ci	} while (--i);
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	priv->status &= ~STATUS_RESET_PENDING;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	if (!i) {
135862306a36Sopenharmony_ci		IPW_DEBUG_INFO
135962306a36Sopenharmony_ci		    ("exit - waited too long for master assert stop\n");
136062306a36Sopenharmony_ci		return -EIO;
136162306a36Sopenharmony_ci	}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_RESET_REG,
136462306a36Sopenharmony_ci		       IPW_AUX_HOST_RESET_REG_SW_RESET);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	/* Reset any fatal_error conditions */
136762306a36Sopenharmony_ci	ipw2100_reset_fatalerror(priv);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	/* At this point, the adapter is now stopped and disabled */
137062306a36Sopenharmony_ci	priv->status &= ~(STATUS_RUNNING | STATUS_ASSOCIATING |
137162306a36Sopenharmony_ci			  STATUS_ASSOCIATED | STATUS_ENABLED);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	return 0;
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci/*
137762306a36Sopenharmony_ci * Send the CARD_DISABLE_PHY_OFF command to the card to disable it
137862306a36Sopenharmony_ci *
137962306a36Sopenharmony_ci * After disabling, if the card was associated, a STATUS_ASSN_LOST will be sent.
138062306a36Sopenharmony_ci *
138162306a36Sopenharmony_ci * STATUS_CARD_DISABLE_NOTIFICATION will be sent regardless of
138262306a36Sopenharmony_ci * if STATUS_ASSN_LOST is sent.
138362306a36Sopenharmony_ci */
138462306a36Sopenharmony_cistatic int ipw2100_hw_phy_off(struct ipw2100_priv *priv)
138562306a36Sopenharmony_ci{
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci#define HW_PHY_OFF_LOOP_DELAY (msecs_to_jiffies(50))
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	struct host_command cmd = {
139062306a36Sopenharmony_ci		.host_command = CARD_DISABLE_PHY_OFF,
139162306a36Sopenharmony_ci		.host_command_sequence = 0,
139262306a36Sopenharmony_ci		.host_command_length = 0,
139362306a36Sopenharmony_ci	};
139462306a36Sopenharmony_ci	int err, i;
139562306a36Sopenharmony_ci	u32 val1, val2;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	IPW_DEBUG_HC("CARD_DISABLE_PHY_OFF\n");
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	/* Turn off the radio */
140062306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
140162306a36Sopenharmony_ci	if (err)
140262306a36Sopenharmony_ci		return err;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	for (i = 0; i < 2500; i++) {
140562306a36Sopenharmony_ci		read_nic_dword(priv->net_dev, IPW2100_CONTROL_REG, &val1);
140662306a36Sopenharmony_ci		read_nic_dword(priv->net_dev, IPW2100_COMMAND, &val2);
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci		if ((val1 & IPW2100_CONTROL_PHY_OFF) &&
140962306a36Sopenharmony_ci		    (val2 & IPW2100_COMMAND_PHY_OFF))
141062306a36Sopenharmony_ci			return 0;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci		schedule_timeout_uninterruptible(HW_PHY_OFF_LOOP_DELAY);
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	return -EIO;
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic int ipw2100_enable_adapter(struct ipw2100_priv *priv)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	struct host_command cmd = {
142162306a36Sopenharmony_ci		.host_command = HOST_COMPLETE,
142262306a36Sopenharmony_ci		.host_command_sequence = 0,
142362306a36Sopenharmony_ci		.host_command_length = 0
142462306a36Sopenharmony_ci	};
142562306a36Sopenharmony_ci	int err = 0;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	IPW_DEBUG_HC("HOST_COMPLETE\n");
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	if (priv->status & STATUS_ENABLED)
143062306a36Sopenharmony_ci		return 0;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	mutex_lock(&priv->adapter_mutex);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	if (rf_kill_active(priv)) {
143562306a36Sopenharmony_ci		IPW_DEBUG_HC("Command aborted due to RF kill active.\n");
143662306a36Sopenharmony_ci		goto fail_up;
143762306a36Sopenharmony_ci	}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
144062306a36Sopenharmony_ci	if (err) {
144162306a36Sopenharmony_ci		IPW_DEBUG_INFO("Failed to send HOST_COMPLETE command\n");
144262306a36Sopenharmony_ci		goto fail_up;
144362306a36Sopenharmony_ci	}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_ENABLED);
144662306a36Sopenharmony_ci	if (err) {
144762306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: card not responding to init command.\n",
144862306a36Sopenharmony_ci			       priv->net_dev->name);
144962306a36Sopenharmony_ci		goto fail_up;
145062306a36Sopenharmony_ci	}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	if (priv->stop_hang_check) {
145362306a36Sopenharmony_ci		priv->stop_hang_check = 0;
145462306a36Sopenharmony_ci		schedule_delayed_work(&priv->hang_check, HZ / 2);
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci      fail_up:
145862306a36Sopenharmony_ci	mutex_unlock(&priv->adapter_mutex);
145962306a36Sopenharmony_ci	return err;
146062306a36Sopenharmony_ci}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_cistatic int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv)
146362306a36Sopenharmony_ci{
146462306a36Sopenharmony_ci#define HW_POWER_DOWN_DELAY (msecs_to_jiffies(100))
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	struct host_command cmd = {
146762306a36Sopenharmony_ci		.host_command = HOST_PRE_POWER_DOWN,
146862306a36Sopenharmony_ci		.host_command_sequence = 0,
146962306a36Sopenharmony_ci		.host_command_length = 0,
147062306a36Sopenharmony_ci	};
147162306a36Sopenharmony_ci	int err, i;
147262306a36Sopenharmony_ci	u32 reg;
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	if (!(priv->status & STATUS_RUNNING))
147562306a36Sopenharmony_ci		return 0;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	priv->status |= STATUS_STOPPING;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	/* We can only shut down the card if the firmware is operational.  So,
148062306a36Sopenharmony_ci	 * if we haven't reset since a fatal_error, then we can not send the
148162306a36Sopenharmony_ci	 * shutdown commands. */
148262306a36Sopenharmony_ci	if (!priv->fatal_error) {
148362306a36Sopenharmony_ci		/* First, make sure the adapter is enabled so that the PHY_OFF
148462306a36Sopenharmony_ci		 * command can shut it down */
148562306a36Sopenharmony_ci		ipw2100_enable_adapter(priv);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		err = ipw2100_hw_phy_off(priv);
148862306a36Sopenharmony_ci		if (err)
148962306a36Sopenharmony_ci			printk(KERN_WARNING DRV_NAME
149062306a36Sopenharmony_ci			       ": Error disabling radio %d\n", err);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci		/*
149362306a36Sopenharmony_ci		 * If in D0-standby mode going directly to D3 may cause a
149462306a36Sopenharmony_ci		 * PCI bus violation.  Therefore we must change out of the D0
149562306a36Sopenharmony_ci		 * state.
149662306a36Sopenharmony_ci		 *
149762306a36Sopenharmony_ci		 * Sending the PREPARE_FOR_POWER_DOWN will restrict the
149862306a36Sopenharmony_ci		 * hardware from going into standby mode and will transition
149962306a36Sopenharmony_ci		 * out of D0-standby if it is already in that state.
150062306a36Sopenharmony_ci		 *
150162306a36Sopenharmony_ci		 * STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the
150262306a36Sopenharmony_ci		 * driver upon completion.  Once received, the driver can
150362306a36Sopenharmony_ci		 * proceed to the D3 state.
150462306a36Sopenharmony_ci		 *
150562306a36Sopenharmony_ci		 * Prepare for power down command to fw.  This command would
150662306a36Sopenharmony_ci		 * take HW out of D0-standby and prepare it for D3 state.
150762306a36Sopenharmony_ci		 *
150862306a36Sopenharmony_ci		 * Currently FW does not support event notification for this
150962306a36Sopenharmony_ci		 * event. Therefore, skip waiting for it.  Just wait a fixed
151062306a36Sopenharmony_ci		 * 100ms
151162306a36Sopenharmony_ci		 */
151262306a36Sopenharmony_ci		IPW_DEBUG_HC("HOST_PRE_POWER_DOWN\n");
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci		err = ipw2100_hw_send_command(priv, &cmd);
151562306a36Sopenharmony_ci		if (err)
151662306a36Sopenharmony_ci			printk(KERN_WARNING DRV_NAME ": "
151762306a36Sopenharmony_ci			       "%s: Power down command failed: Error %d\n",
151862306a36Sopenharmony_ci			       priv->net_dev->name, err);
151962306a36Sopenharmony_ci		else
152062306a36Sopenharmony_ci			schedule_timeout_uninterruptible(HW_POWER_DOWN_DELAY);
152162306a36Sopenharmony_ci	}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	priv->status &= ~STATUS_ENABLED;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	/*
152662306a36Sopenharmony_ci	 * Set GPIO 3 writable by FW; GPIO 1 writable
152762306a36Sopenharmony_ci	 * by driver and enable clock
152862306a36Sopenharmony_ci	 */
152962306a36Sopenharmony_ci	ipw2100_hw_set_gpio(priv);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	/*
153262306a36Sopenharmony_ci	 * Power down adapter.  Sequence:
153362306a36Sopenharmony_ci	 * 1. Stop master assert (RESET_REG[9]=1)
153462306a36Sopenharmony_ci	 * 2. Wait for stop master (RESET_REG[8]==1)
153562306a36Sopenharmony_ci	 * 3. S/w reset assert (RESET_REG[7] = 1)
153662306a36Sopenharmony_ci	 */
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	/* Stop master assert */
153962306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_RESET_REG,
154062306a36Sopenharmony_ci		       IPW_AUX_HOST_RESET_REG_STOP_MASTER);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	/* wait stop master not more than 50 usec.
154362306a36Sopenharmony_ci	 * Otherwise return error. */
154462306a36Sopenharmony_ci	for (i = 5; i > 0; i--) {
154562306a36Sopenharmony_ci		udelay(10);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci		/* Check master stop bit */
154862306a36Sopenharmony_ci		read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci		if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
155162306a36Sopenharmony_ci			break;
155262306a36Sopenharmony_ci	}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	if (i == 0)
155562306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
155662306a36Sopenharmony_ci		       ": %s: Could now power down adapter.\n",
155762306a36Sopenharmony_ci		       priv->net_dev->name);
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	/* assert s/w reset */
156062306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_RESET_REG,
156162306a36Sopenharmony_ci		       IPW_AUX_HOST_RESET_REG_SW_RESET);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	priv->status &= ~(STATUS_RUNNING | STATUS_STOPPING);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	return 0;
156662306a36Sopenharmony_ci}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_cistatic int ipw2100_disable_adapter(struct ipw2100_priv *priv)
156962306a36Sopenharmony_ci{
157062306a36Sopenharmony_ci	struct host_command cmd = {
157162306a36Sopenharmony_ci		.host_command = CARD_DISABLE,
157262306a36Sopenharmony_ci		.host_command_sequence = 0,
157362306a36Sopenharmony_ci		.host_command_length = 0
157462306a36Sopenharmony_ci	};
157562306a36Sopenharmony_ci	int err = 0;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	IPW_DEBUG_HC("CARD_DISABLE\n");
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	if (!(priv->status & STATUS_ENABLED))
158062306a36Sopenharmony_ci		return 0;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	/* Make sure we clear the associated state */
158362306a36Sopenharmony_ci	priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	if (!priv->stop_hang_check) {
158662306a36Sopenharmony_ci		priv->stop_hang_check = 1;
158762306a36Sopenharmony_ci		cancel_delayed_work(&priv->hang_check);
158862306a36Sopenharmony_ci	}
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	mutex_lock(&priv->adapter_mutex);
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
159362306a36Sopenharmony_ci	if (err) {
159462306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
159562306a36Sopenharmony_ci		       ": exit - failed to send CARD_DISABLE command\n");
159662306a36Sopenharmony_ci		goto fail_up;
159762306a36Sopenharmony_ci	}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_DISABLED);
160062306a36Sopenharmony_ci	if (err) {
160162306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
160262306a36Sopenharmony_ci		       ": exit - card failed to change to DISABLED\n");
160362306a36Sopenharmony_ci		goto fail_up;
160462306a36Sopenharmony_ci	}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	IPW_DEBUG_INFO("TODO: implement scan state machine\n");
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci      fail_up:
160962306a36Sopenharmony_ci	mutex_unlock(&priv->adapter_mutex);
161062306a36Sopenharmony_ci	return err;
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_cistatic int ipw2100_set_scan_options(struct ipw2100_priv *priv)
161462306a36Sopenharmony_ci{
161562306a36Sopenharmony_ci	struct host_command cmd = {
161662306a36Sopenharmony_ci		.host_command = SET_SCAN_OPTIONS,
161762306a36Sopenharmony_ci		.host_command_sequence = 0,
161862306a36Sopenharmony_ci		.host_command_length = 8
161962306a36Sopenharmony_ci	};
162062306a36Sopenharmony_ci	int err;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	IPW_DEBUG_SCAN("setting scan options\n");
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	cmd.host_command_parameters[0] = 0;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	if (!(priv->config & CFG_ASSOCIATE))
162962306a36Sopenharmony_ci		cmd.host_command_parameters[0] |= IPW_SCAN_NOASSOCIATE;
163062306a36Sopenharmony_ci	if ((priv->ieee->sec.flags & SEC_ENABLED) && priv->ieee->sec.enabled)
163162306a36Sopenharmony_ci		cmd.host_command_parameters[0] |= IPW_SCAN_MIXED_CELL;
163262306a36Sopenharmony_ci	if (priv->config & CFG_PASSIVE_SCAN)
163362306a36Sopenharmony_ci		cmd.host_command_parameters[0] |= IPW_SCAN_PASSIVE;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	cmd.host_command_parameters[1] = priv->channel_mask;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	IPW_DEBUG_HC("SET_SCAN_OPTIONS 0x%04X\n",
164062306a36Sopenharmony_ci		     cmd.host_command_parameters[0]);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	return err;
164362306a36Sopenharmony_ci}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_cistatic int ipw2100_start_scan(struct ipw2100_priv *priv)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	struct host_command cmd = {
164862306a36Sopenharmony_ci		.host_command = BROADCAST_SCAN,
164962306a36Sopenharmony_ci		.host_command_sequence = 0,
165062306a36Sopenharmony_ci		.host_command_length = 4
165162306a36Sopenharmony_ci	};
165262306a36Sopenharmony_ci	int err;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	IPW_DEBUG_HC("START_SCAN\n");
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	cmd.host_command_parameters[0] = 0;
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	/* No scanning if in monitor mode */
165962306a36Sopenharmony_ci	if (priv->ieee->iw_mode == IW_MODE_MONITOR)
166062306a36Sopenharmony_ci		return 1;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	if (priv->status & STATUS_SCANNING) {
166362306a36Sopenharmony_ci		IPW_DEBUG_SCAN("Scan requested while already in scan...\n");
166462306a36Sopenharmony_ci		return 0;
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	/* Not clearing here; doing so makes iwlist always return nothing...
167062306a36Sopenharmony_ci	 *
167162306a36Sopenharmony_ci	 * We should modify the table logic to use aging tables vs. clearing
167262306a36Sopenharmony_ci	 * the table on each scan start.
167362306a36Sopenharmony_ci	 */
167462306a36Sopenharmony_ci	IPW_DEBUG_SCAN("starting scan\n");
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	priv->status |= STATUS_SCANNING;
167762306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
167862306a36Sopenharmony_ci	if (err)
167962306a36Sopenharmony_ci		priv->status &= ~STATUS_SCANNING;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	return err;
168462306a36Sopenharmony_ci}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_cistatic const struct libipw_geo ipw_geos[] = {
168762306a36Sopenharmony_ci	{			/* Restricted */
168862306a36Sopenharmony_ci	 "---",
168962306a36Sopenharmony_ci	 .bg_channels = 14,
169062306a36Sopenharmony_ci	 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
169162306a36Sopenharmony_ci		{2427, 4}, {2432, 5}, {2437, 6},
169262306a36Sopenharmony_ci		{2442, 7}, {2447, 8}, {2452, 9},
169362306a36Sopenharmony_ci		{2457, 10}, {2462, 11}, {2467, 12},
169462306a36Sopenharmony_ci		{2472, 13}, {2484, 14}},
169562306a36Sopenharmony_ci	 },
169662306a36Sopenharmony_ci};
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_cistatic int ipw2100_up(struct ipw2100_priv *priv, int deferred)
169962306a36Sopenharmony_ci{
170062306a36Sopenharmony_ci	unsigned long flags;
170162306a36Sopenharmony_ci	int err = 0;
170262306a36Sopenharmony_ci	u32 lock;
170362306a36Sopenharmony_ci	u32 ord_len = sizeof(lock);
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	/* Age scan list entries found before suspend */
170662306a36Sopenharmony_ci	if (priv->suspend_time) {
170762306a36Sopenharmony_ci		libipw_networks_age(priv->ieee, priv->suspend_time);
170862306a36Sopenharmony_ci		priv->suspend_time = 0;
170962306a36Sopenharmony_ci	}
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	/* Quiet if manually disabled. */
171262306a36Sopenharmony_ci	if (priv->status & STATUS_RF_KILL_SW) {
171362306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: Radio is disabled by Manual Disable "
171462306a36Sopenharmony_ci			       "switch\n", priv->net_dev->name);
171562306a36Sopenharmony_ci		return 0;
171662306a36Sopenharmony_ci	}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	/* the ipw2100 hardware really doesn't want power management delays
171962306a36Sopenharmony_ci	 * longer than 175usec
172062306a36Sopenharmony_ci	 */
172162306a36Sopenharmony_ci	cpu_latency_qos_update_request(&ipw2100_pm_qos_req, 175);
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	/* If the interrupt is enabled, turn it off... */
172462306a36Sopenharmony_ci	spin_lock_irqsave(&priv->low_lock, flags);
172562306a36Sopenharmony_ci	ipw2100_disable_interrupts(priv);
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	/* Reset any fatal_error conditions */
172862306a36Sopenharmony_ci	ipw2100_reset_fatalerror(priv);
172962306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	if (priv->status & STATUS_POWERED ||
173262306a36Sopenharmony_ci	    (priv->status & STATUS_RESET_PENDING)) {
173362306a36Sopenharmony_ci		/* Power cycle the card ... */
173462306a36Sopenharmony_ci		err = ipw2100_power_cycle_adapter(priv);
173562306a36Sopenharmony_ci		if (err) {
173662306a36Sopenharmony_ci			printk(KERN_WARNING DRV_NAME
173762306a36Sopenharmony_ci			       ": %s: Could not cycle adapter.\n",
173862306a36Sopenharmony_ci			       priv->net_dev->name);
173962306a36Sopenharmony_ci			goto exit;
174062306a36Sopenharmony_ci		}
174162306a36Sopenharmony_ci	} else
174262306a36Sopenharmony_ci		priv->status |= STATUS_POWERED;
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	/* Load the firmware, start the clocks, etc. */
174562306a36Sopenharmony_ci	err = ipw2100_start_adapter(priv);
174662306a36Sopenharmony_ci	if (err) {
174762306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME
174862306a36Sopenharmony_ci		       ": %s: Failed to start the firmware.\n",
174962306a36Sopenharmony_ci		       priv->net_dev->name);
175062306a36Sopenharmony_ci		goto exit;
175162306a36Sopenharmony_ci	}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	ipw2100_initialize_ordinals(priv);
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	/* Determine capabilities of this particular HW configuration */
175662306a36Sopenharmony_ci	err = ipw2100_get_hw_features(priv);
175762306a36Sopenharmony_ci	if (err) {
175862306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME
175962306a36Sopenharmony_ci		       ": %s: Failed to determine HW features.\n",
176062306a36Sopenharmony_ci		       priv->net_dev->name);
176162306a36Sopenharmony_ci		goto exit;
176262306a36Sopenharmony_ci	}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	/* Initialize the geo */
176562306a36Sopenharmony_ci	libipw_set_geo(priv->ieee, &ipw_geos[0]);
176662306a36Sopenharmony_ci	priv->ieee->freq_band = LIBIPW_24GHZ_BAND;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	lock = LOCK_NONE;
176962306a36Sopenharmony_ci	err = ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len);
177062306a36Sopenharmony_ci	if (err) {
177162306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME
177262306a36Sopenharmony_ci		       ": %s: Failed to clear ordinal lock.\n",
177362306a36Sopenharmony_ci		       priv->net_dev->name);
177462306a36Sopenharmony_ci		goto exit;
177562306a36Sopenharmony_ci	}
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	priv->status &= ~STATUS_SCANNING;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	if (rf_kill_active(priv)) {
178062306a36Sopenharmony_ci		printk(KERN_INFO "%s: Radio is disabled by RF switch.\n",
178162306a36Sopenharmony_ci		       priv->net_dev->name);
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci		if (priv->stop_rf_kill) {
178462306a36Sopenharmony_ci			priv->stop_rf_kill = 0;
178562306a36Sopenharmony_ci			schedule_delayed_work(&priv->rf_kill,
178662306a36Sopenharmony_ci					      round_jiffies_relative(HZ));
178762306a36Sopenharmony_ci		}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci		deferred = 1;
179062306a36Sopenharmony_ci	}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	/* Turn on the interrupt so that commands can be processed */
179362306a36Sopenharmony_ci	ipw2100_enable_interrupts(priv);
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	/* Send all of the commands that must be sent prior to
179662306a36Sopenharmony_ci	 * HOST_COMPLETE */
179762306a36Sopenharmony_ci	err = ipw2100_adapter_setup(priv);
179862306a36Sopenharmony_ci	if (err) {
179962306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME ": %s: Failed to start the card.\n",
180062306a36Sopenharmony_ci		       priv->net_dev->name);
180162306a36Sopenharmony_ci		goto exit;
180262306a36Sopenharmony_ci	}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	if (!deferred) {
180562306a36Sopenharmony_ci		/* Enable the adapter - sends HOST_COMPLETE */
180662306a36Sopenharmony_ci		err = ipw2100_enable_adapter(priv);
180762306a36Sopenharmony_ci		if (err) {
180862306a36Sopenharmony_ci			printk(KERN_ERR DRV_NAME ": "
180962306a36Sopenharmony_ci			       "%s: failed in call to enable adapter.\n",
181062306a36Sopenharmony_ci			       priv->net_dev->name);
181162306a36Sopenharmony_ci			ipw2100_hw_stop_adapter(priv);
181262306a36Sopenharmony_ci			goto exit;
181362306a36Sopenharmony_ci		}
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci		/* Start a scan . . . */
181662306a36Sopenharmony_ci		ipw2100_set_scan_options(priv);
181762306a36Sopenharmony_ci		ipw2100_start_scan(priv);
181862306a36Sopenharmony_ci	}
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci      exit:
182162306a36Sopenharmony_ci	return err;
182262306a36Sopenharmony_ci}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_cistatic void ipw2100_down(struct ipw2100_priv *priv)
182562306a36Sopenharmony_ci{
182662306a36Sopenharmony_ci	unsigned long flags;
182762306a36Sopenharmony_ci	union iwreq_data wrqu = {
182862306a36Sopenharmony_ci		.ap_addr = {
182962306a36Sopenharmony_ci			    .sa_family = ARPHRD_ETHER}
183062306a36Sopenharmony_ci	};
183162306a36Sopenharmony_ci	int associated = priv->status & STATUS_ASSOCIATED;
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	/* Kill the RF switch timer */
183462306a36Sopenharmony_ci	if (!priv->stop_rf_kill) {
183562306a36Sopenharmony_ci		priv->stop_rf_kill = 1;
183662306a36Sopenharmony_ci		cancel_delayed_work(&priv->rf_kill);
183762306a36Sopenharmony_ci	}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	/* Kill the firmware hang check timer */
184062306a36Sopenharmony_ci	if (!priv->stop_hang_check) {
184162306a36Sopenharmony_ci		priv->stop_hang_check = 1;
184262306a36Sopenharmony_ci		cancel_delayed_work(&priv->hang_check);
184362306a36Sopenharmony_ci	}
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	/* Kill any pending resets */
184662306a36Sopenharmony_ci	if (priv->status & STATUS_RESET_PENDING)
184762306a36Sopenharmony_ci		cancel_delayed_work(&priv->reset_work);
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	/* Make sure the interrupt is on so that FW commands will be
185062306a36Sopenharmony_ci	 * processed correctly */
185162306a36Sopenharmony_ci	spin_lock_irqsave(&priv->low_lock, flags);
185262306a36Sopenharmony_ci	ipw2100_enable_interrupts(priv);
185362306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	if (ipw2100_hw_stop_adapter(priv))
185662306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME ": %s: Error stopping adapter.\n",
185762306a36Sopenharmony_ci		       priv->net_dev->name);
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	/* Do not disable the interrupt until _after_ we disable
186062306a36Sopenharmony_ci	 * the adaptor.  Otherwise the CARD_DISABLE command will never
186162306a36Sopenharmony_ci	 * be ack'd by the firmware */
186262306a36Sopenharmony_ci	spin_lock_irqsave(&priv->low_lock, flags);
186362306a36Sopenharmony_ci	ipw2100_disable_interrupts(priv);
186462306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	cpu_latency_qos_update_request(&ipw2100_pm_qos_req,
186762306a36Sopenharmony_ci				       PM_QOS_DEFAULT_VALUE);
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	/* We have to signal any supplicant if we are disassociating */
187062306a36Sopenharmony_ci	if (associated)
187162306a36Sopenharmony_ci		wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
187462306a36Sopenharmony_ci	netif_carrier_off(priv->net_dev);
187562306a36Sopenharmony_ci	netif_stop_queue(priv->net_dev);
187662306a36Sopenharmony_ci}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_cistatic int ipw2100_wdev_init(struct net_device *dev)
187962306a36Sopenharmony_ci{
188062306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
188162306a36Sopenharmony_ci	const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
188262306a36Sopenharmony_ci	struct wireless_dev *wdev = &priv->ieee->wdev;
188362306a36Sopenharmony_ci	int i;
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN);
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	/* fill-out priv->ieee->bg_band */
188862306a36Sopenharmony_ci	if (geo->bg_channels) {
188962306a36Sopenharmony_ci		struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci		bg_band->band = NL80211_BAND_2GHZ;
189262306a36Sopenharmony_ci		bg_band->n_channels = geo->bg_channels;
189362306a36Sopenharmony_ci		bg_band->channels = kcalloc(geo->bg_channels,
189462306a36Sopenharmony_ci					    sizeof(struct ieee80211_channel),
189562306a36Sopenharmony_ci					    GFP_KERNEL);
189662306a36Sopenharmony_ci		if (!bg_band->channels) {
189762306a36Sopenharmony_ci			ipw2100_down(priv);
189862306a36Sopenharmony_ci			return -ENOMEM;
189962306a36Sopenharmony_ci		}
190062306a36Sopenharmony_ci		/* translate geo->bg to bg_band.channels */
190162306a36Sopenharmony_ci		for (i = 0; i < geo->bg_channels; i++) {
190262306a36Sopenharmony_ci			bg_band->channels[i].band = NL80211_BAND_2GHZ;
190362306a36Sopenharmony_ci			bg_band->channels[i].center_freq = geo->bg[i].freq;
190462306a36Sopenharmony_ci			bg_band->channels[i].hw_value = geo->bg[i].channel;
190562306a36Sopenharmony_ci			bg_band->channels[i].max_power = geo->bg[i].max_power;
190662306a36Sopenharmony_ci			if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY)
190762306a36Sopenharmony_ci				bg_band->channels[i].flags |=
190862306a36Sopenharmony_ci					IEEE80211_CHAN_NO_IR;
190962306a36Sopenharmony_ci			if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS)
191062306a36Sopenharmony_ci				bg_band->channels[i].flags |=
191162306a36Sopenharmony_ci					IEEE80211_CHAN_NO_IR;
191262306a36Sopenharmony_ci			if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT)
191362306a36Sopenharmony_ci				bg_band->channels[i].flags |=
191462306a36Sopenharmony_ci					IEEE80211_CHAN_RADAR;
191562306a36Sopenharmony_ci			/* No equivalent for LIBIPW_CH_80211H_RULES,
191662306a36Sopenharmony_ci			   LIBIPW_CH_UNIFORM_SPREADING, or
191762306a36Sopenharmony_ci			   LIBIPW_CH_B_ONLY... */
191862306a36Sopenharmony_ci		}
191962306a36Sopenharmony_ci		/* point at bitrate info */
192062306a36Sopenharmony_ci		bg_band->bitrates = ipw2100_bg_rates;
192162306a36Sopenharmony_ci		bg_band->n_bitrates = RATE_COUNT;
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci		wdev->wiphy->bands[NL80211_BAND_2GHZ] = bg_band;
192462306a36Sopenharmony_ci	}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	wdev->wiphy->cipher_suites = ipw_cipher_suites;
192762306a36Sopenharmony_ci	wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites);
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev);
193062306a36Sopenharmony_ci	if (wiphy_register(wdev->wiphy))
193162306a36Sopenharmony_ci		return -EIO;
193262306a36Sopenharmony_ci	return 0;
193362306a36Sopenharmony_ci}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_cistatic void ipw2100_reset_adapter(struct work_struct *work)
193662306a36Sopenharmony_ci{
193762306a36Sopenharmony_ci	struct ipw2100_priv *priv =
193862306a36Sopenharmony_ci		container_of(work, struct ipw2100_priv, reset_work.work);
193962306a36Sopenharmony_ci	unsigned long flags;
194062306a36Sopenharmony_ci	union iwreq_data wrqu = {
194162306a36Sopenharmony_ci		.ap_addr = {
194262306a36Sopenharmony_ci			    .sa_family = ARPHRD_ETHER}
194362306a36Sopenharmony_ci	};
194462306a36Sopenharmony_ci	int associated = priv->status & STATUS_ASSOCIATED;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	spin_lock_irqsave(&priv->low_lock, flags);
194762306a36Sopenharmony_ci	IPW_DEBUG_INFO(": %s: Restarting adapter.\n", priv->net_dev->name);
194862306a36Sopenharmony_ci	priv->resets++;
194962306a36Sopenharmony_ci	priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
195062306a36Sopenharmony_ci	priv->status |= STATUS_SECURITY_UPDATED;
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	/* Force a power cycle even if interface hasn't been opened
195362306a36Sopenharmony_ci	 * yet */
195462306a36Sopenharmony_ci	cancel_delayed_work(&priv->reset_work);
195562306a36Sopenharmony_ci	priv->status |= STATUS_RESET_PENDING;
195662306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
195962306a36Sopenharmony_ci	/* stop timed checks so that they don't interfere with reset */
196062306a36Sopenharmony_ci	priv->stop_hang_check = 1;
196162306a36Sopenharmony_ci	cancel_delayed_work(&priv->hang_check);
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	/* We have to signal any supplicant if we are disassociating */
196462306a36Sopenharmony_ci	if (associated)
196562306a36Sopenharmony_ci		wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	ipw2100_up(priv, 0);
196862306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci}
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_cistatic void isr_indicate_associated(struct ipw2100_priv *priv, u32 status)
197362306a36Sopenharmony_ci{
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci#define MAC_ASSOCIATION_READ_DELAY (HZ)
197662306a36Sopenharmony_ci	int ret;
197762306a36Sopenharmony_ci	unsigned int len, essid_len;
197862306a36Sopenharmony_ci	char essid[IW_ESSID_MAX_SIZE];
197962306a36Sopenharmony_ci	u32 txrate;
198062306a36Sopenharmony_ci	u32 chan;
198162306a36Sopenharmony_ci	char *txratename;
198262306a36Sopenharmony_ci	u8 bssid[ETH_ALEN];
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	/*
198562306a36Sopenharmony_ci	 * TBD: BSSID is usually 00:00:00:00:00:00 here and not
198662306a36Sopenharmony_ci	 *      an actual MAC of the AP. Seems like FW sets this
198762306a36Sopenharmony_ci	 *      address too late. Read it later and expose through
198862306a36Sopenharmony_ci	 *      /proc or schedule a later task to query and update
198962306a36Sopenharmony_ci	 */
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	essid_len = IW_ESSID_MAX_SIZE;
199262306a36Sopenharmony_ci	ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID,
199362306a36Sopenharmony_ci				  essid, &essid_len);
199462306a36Sopenharmony_ci	if (ret) {
199562306a36Sopenharmony_ci		IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
199662306a36Sopenharmony_ci			       __LINE__);
199762306a36Sopenharmony_ci		return;
199862306a36Sopenharmony_ci	}
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	len = sizeof(u32);
200162306a36Sopenharmony_ci	ret = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &txrate, &len);
200262306a36Sopenharmony_ci	if (ret) {
200362306a36Sopenharmony_ci		IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
200462306a36Sopenharmony_ci			       __LINE__);
200562306a36Sopenharmony_ci		return;
200662306a36Sopenharmony_ci	}
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	len = sizeof(u32);
200962306a36Sopenharmony_ci	ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &len);
201062306a36Sopenharmony_ci	if (ret) {
201162306a36Sopenharmony_ci		IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
201262306a36Sopenharmony_ci			       __LINE__);
201362306a36Sopenharmony_ci		return;
201462306a36Sopenharmony_ci	}
201562306a36Sopenharmony_ci	len = ETH_ALEN;
201662306a36Sopenharmony_ci	ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, bssid,
201762306a36Sopenharmony_ci				  &len);
201862306a36Sopenharmony_ci	if (ret) {
201962306a36Sopenharmony_ci		IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
202062306a36Sopenharmony_ci			       __LINE__);
202162306a36Sopenharmony_ci		return;
202262306a36Sopenharmony_ci	}
202362306a36Sopenharmony_ci	memcpy(priv->ieee->bssid, bssid, ETH_ALEN);
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	switch (txrate) {
202662306a36Sopenharmony_ci	case TX_RATE_1_MBIT:
202762306a36Sopenharmony_ci		txratename = "1Mbps";
202862306a36Sopenharmony_ci		break;
202962306a36Sopenharmony_ci	case TX_RATE_2_MBIT:
203062306a36Sopenharmony_ci		txratename = "2Mbsp";
203162306a36Sopenharmony_ci		break;
203262306a36Sopenharmony_ci	case TX_RATE_5_5_MBIT:
203362306a36Sopenharmony_ci		txratename = "5.5Mbps";
203462306a36Sopenharmony_ci		break;
203562306a36Sopenharmony_ci	case TX_RATE_11_MBIT:
203662306a36Sopenharmony_ci		txratename = "11Mbps";
203762306a36Sopenharmony_ci		break;
203862306a36Sopenharmony_ci	default:
203962306a36Sopenharmony_ci		IPW_DEBUG_INFO("Unknown rate: %d\n", txrate);
204062306a36Sopenharmony_ci		txratename = "unknown rate";
204162306a36Sopenharmony_ci		break;
204262306a36Sopenharmony_ci	}
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci	IPW_DEBUG_INFO("%s: Associated with '%*pE' at %s, channel %d (BSSID=%pM)\n",
204562306a36Sopenharmony_ci		       priv->net_dev->name, essid_len, essid,
204662306a36Sopenharmony_ci		       txratename, chan, bssid);
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	/* now we copy read ssid into dev */
204962306a36Sopenharmony_ci	if (!(priv->config & CFG_STATIC_ESSID)) {
205062306a36Sopenharmony_ci		priv->essid_len = min((u8) essid_len, (u8) IW_ESSID_MAX_SIZE);
205162306a36Sopenharmony_ci		memcpy(priv->essid, essid, priv->essid_len);
205262306a36Sopenharmony_ci	}
205362306a36Sopenharmony_ci	priv->channel = chan;
205462306a36Sopenharmony_ci	memcpy(priv->bssid, bssid, ETH_ALEN);
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	priv->status |= STATUS_ASSOCIATING;
205762306a36Sopenharmony_ci	priv->connect_start = ktime_get_boottime_seconds();
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	schedule_delayed_work(&priv->wx_event_work, HZ / 10);
206062306a36Sopenharmony_ci}
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_cistatic int ipw2100_set_essid(struct ipw2100_priv *priv, char *essid,
206362306a36Sopenharmony_ci			     int length, int batch_mode)
206462306a36Sopenharmony_ci{
206562306a36Sopenharmony_ci	int ssid_len = min(length, IW_ESSID_MAX_SIZE);
206662306a36Sopenharmony_ci	struct host_command cmd = {
206762306a36Sopenharmony_ci		.host_command = SSID,
206862306a36Sopenharmony_ci		.host_command_sequence = 0,
206962306a36Sopenharmony_ci		.host_command_length = ssid_len
207062306a36Sopenharmony_ci	};
207162306a36Sopenharmony_ci	int err;
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	IPW_DEBUG_HC("SSID: '%*pE'\n", ssid_len, essid);
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	if (ssid_len)
207662306a36Sopenharmony_ci		memcpy(cmd.host_command_parameters, essid, ssid_len);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	if (!batch_mode) {
207962306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
208062306a36Sopenharmony_ci		if (err)
208162306a36Sopenharmony_ci			return err;
208262306a36Sopenharmony_ci	}
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	/* Bug in FW currently doesn't honor bit 0 in SET_SCAN_OPTIONS to
208562306a36Sopenharmony_ci	 * disable auto association -- so we cheat by setting a bogus SSID */
208662306a36Sopenharmony_ci	if (!ssid_len && !(priv->config & CFG_ASSOCIATE)) {
208762306a36Sopenharmony_ci		int i;
208862306a36Sopenharmony_ci		u8 *bogus = (u8 *) cmd.host_command_parameters;
208962306a36Sopenharmony_ci		for (i = 0; i < IW_ESSID_MAX_SIZE; i++)
209062306a36Sopenharmony_ci			bogus[i] = 0x18 + i;
209162306a36Sopenharmony_ci		cmd.host_command_length = IW_ESSID_MAX_SIZE;
209262306a36Sopenharmony_ci	}
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	/* NOTE:  We always send the SSID command even if the provided ESSID is
209562306a36Sopenharmony_ci	 * the same as what we currently think is set. */
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
209862306a36Sopenharmony_ci	if (!err) {
209962306a36Sopenharmony_ci		memset(priv->essid + ssid_len, 0, IW_ESSID_MAX_SIZE - ssid_len);
210062306a36Sopenharmony_ci		memcpy(priv->essid, essid, ssid_len);
210162306a36Sopenharmony_ci		priv->essid_len = ssid_len;
210262306a36Sopenharmony_ci	}
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	if (!batch_mode) {
210562306a36Sopenharmony_ci		if (ipw2100_enable_adapter(priv))
210662306a36Sopenharmony_ci			err = -EIO;
210762306a36Sopenharmony_ci	}
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	return err;
211062306a36Sopenharmony_ci}
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_cistatic void isr_indicate_association_lost(struct ipw2100_priv *priv, u32 status)
211362306a36Sopenharmony_ci{
211462306a36Sopenharmony_ci	IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
211562306a36Sopenharmony_ci		  "disassociated: '%*pE' %pM\n", priv->essid_len, priv->essid,
211662306a36Sopenharmony_ci		  priv->bssid);
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	if (priv->status & STATUS_STOPPING) {
212162306a36Sopenharmony_ci		IPW_DEBUG_INFO("Card is stopping itself, discard ASSN_LOST.\n");
212262306a36Sopenharmony_ci		return;
212362306a36Sopenharmony_ci	}
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	eth_zero_addr(priv->bssid);
212662306a36Sopenharmony_ci	eth_zero_addr(priv->ieee->bssid);
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	netif_carrier_off(priv->net_dev);
212962306a36Sopenharmony_ci	netif_stop_queue(priv->net_dev);
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	if (!(priv->status & STATUS_RUNNING))
213262306a36Sopenharmony_ci		return;
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	if (priv->status & STATUS_SECURITY_UPDATED)
213562306a36Sopenharmony_ci		schedule_delayed_work(&priv->security_work, 0);
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	schedule_delayed_work(&priv->wx_event_work, 0);
213862306a36Sopenharmony_ci}
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_cistatic void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status)
214162306a36Sopenharmony_ci{
214262306a36Sopenharmony_ci	IPW_DEBUG_INFO("%s: RF Kill state changed to radio OFF.\n",
214362306a36Sopenharmony_ci		       priv->net_dev->name);
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	/* RF_KILL is now enabled (else we wouldn't be here) */
214662306a36Sopenharmony_ci	wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
214762306a36Sopenharmony_ci	priv->status |= STATUS_RF_KILL_HW;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	/* Make sure the RF Kill check timer is running */
215062306a36Sopenharmony_ci	priv->stop_rf_kill = 0;
215162306a36Sopenharmony_ci	mod_delayed_work(system_wq, &priv->rf_kill, round_jiffies_relative(HZ));
215262306a36Sopenharmony_ci}
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_cistatic void ipw2100_scan_event(struct work_struct *work)
215562306a36Sopenharmony_ci{
215662306a36Sopenharmony_ci	struct ipw2100_priv *priv = container_of(work, struct ipw2100_priv,
215762306a36Sopenharmony_ci						 scan_event.work);
215862306a36Sopenharmony_ci	union iwreq_data wrqu;
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	wrqu.data.length = 0;
216162306a36Sopenharmony_ci	wrqu.data.flags = 0;
216262306a36Sopenharmony_ci	wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
216362306a36Sopenharmony_ci}
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_cistatic void isr_scan_complete(struct ipw2100_priv *priv, u32 status)
216662306a36Sopenharmony_ci{
216762306a36Sopenharmony_ci	IPW_DEBUG_SCAN("scan complete\n");
216862306a36Sopenharmony_ci	/* Age the scan results... */
216962306a36Sopenharmony_ci	priv->ieee->scans++;
217062306a36Sopenharmony_ci	priv->status &= ~STATUS_SCANNING;
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	/* Only userspace-requested scan completion events go out immediately */
217362306a36Sopenharmony_ci	if (!priv->user_requested_scan) {
217462306a36Sopenharmony_ci		schedule_delayed_work(&priv->scan_event,
217562306a36Sopenharmony_ci				      round_jiffies_relative(msecs_to_jiffies(4000)));
217662306a36Sopenharmony_ci	} else {
217762306a36Sopenharmony_ci		priv->user_requested_scan = 0;
217862306a36Sopenharmony_ci		mod_delayed_work(system_wq, &priv->scan_event, 0);
217962306a36Sopenharmony_ci	}
218062306a36Sopenharmony_ci}
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
218362306a36Sopenharmony_ci#define IPW2100_HANDLER(v, f) { v, f, # v }
218462306a36Sopenharmony_cistruct ipw2100_status_indicator {
218562306a36Sopenharmony_ci	int status;
218662306a36Sopenharmony_ci	void (*cb) (struct ipw2100_priv * priv, u32 status);
218762306a36Sopenharmony_ci	char *name;
218862306a36Sopenharmony_ci};
218962306a36Sopenharmony_ci#else
219062306a36Sopenharmony_ci#define IPW2100_HANDLER(v, f) { v, f }
219162306a36Sopenharmony_cistruct ipw2100_status_indicator {
219262306a36Sopenharmony_ci	int status;
219362306a36Sopenharmony_ci	void (*cb) (struct ipw2100_priv * priv, u32 status);
219462306a36Sopenharmony_ci};
219562306a36Sopenharmony_ci#endif				/* CONFIG_IPW2100_DEBUG */
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_cistatic void isr_indicate_scanning(struct ipw2100_priv *priv, u32 status)
219862306a36Sopenharmony_ci{
219962306a36Sopenharmony_ci	IPW_DEBUG_SCAN("Scanning...\n");
220062306a36Sopenharmony_ci	priv->status |= STATUS_SCANNING;
220162306a36Sopenharmony_ci}
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_cistatic const struct ipw2100_status_indicator status_handlers[] = {
220462306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_INITIALIZED, NULL),
220562306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_COUNTRY_FOUND, NULL),
220662306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_ASSOCIATED, isr_indicate_associated),
220762306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_ASSN_LOST, isr_indicate_association_lost),
220862306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_ASSN_CHANGED, NULL),
220962306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_SCAN_COMPLETE, isr_scan_complete),
221062306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_ENTERED_PSP, NULL),
221162306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_LEFT_PSP, NULL),
221262306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_RF_KILL, isr_indicate_rf_kill),
221362306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_DISABLED, NULL),
221462306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_POWER_DOWN, NULL),
221562306a36Sopenharmony_ci	IPW2100_HANDLER(IPW_STATE_SCANNING, isr_indicate_scanning),
221662306a36Sopenharmony_ci	IPW2100_HANDLER(-1, NULL)
221762306a36Sopenharmony_ci};
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_cistatic void isr_status_change(struct ipw2100_priv *priv, int status)
222062306a36Sopenharmony_ci{
222162306a36Sopenharmony_ci	int i;
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	if (status == IPW_STATE_SCANNING &&
222462306a36Sopenharmony_ci	    priv->status & STATUS_ASSOCIATED &&
222562306a36Sopenharmony_ci	    !(priv->status & STATUS_SCANNING)) {
222662306a36Sopenharmony_ci		IPW_DEBUG_INFO("Scan detected while associated, with "
222762306a36Sopenharmony_ci			       "no scan request.  Restarting firmware.\n");
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci		/* Wake up any sleeping jobs */
223062306a36Sopenharmony_ci		schedule_reset(priv);
223162306a36Sopenharmony_ci	}
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	for (i = 0; status_handlers[i].status != -1; i++) {
223462306a36Sopenharmony_ci		if (status == status_handlers[i].status) {
223562306a36Sopenharmony_ci			IPW_DEBUG_NOTIF("Status change: %s\n",
223662306a36Sopenharmony_ci					status_handlers[i].name);
223762306a36Sopenharmony_ci			if (status_handlers[i].cb)
223862306a36Sopenharmony_ci				status_handlers[i].cb(priv, status);
223962306a36Sopenharmony_ci			priv->wstats.status = status;
224062306a36Sopenharmony_ci			return;
224162306a36Sopenharmony_ci		}
224262306a36Sopenharmony_ci	}
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	IPW_DEBUG_NOTIF("unknown status received: %04x\n", status);
224562306a36Sopenharmony_ci}
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_cistatic void isr_rx_complete_command(struct ipw2100_priv *priv,
224862306a36Sopenharmony_ci				    struct ipw2100_cmd_header *cmd)
224962306a36Sopenharmony_ci{
225062306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
225162306a36Sopenharmony_ci	if (cmd->host_command_reg < ARRAY_SIZE(command_types)) {
225262306a36Sopenharmony_ci		IPW_DEBUG_HC("Command completed '%s (%d)'\n",
225362306a36Sopenharmony_ci			     command_types[cmd->host_command_reg],
225462306a36Sopenharmony_ci			     cmd->host_command_reg);
225562306a36Sopenharmony_ci	}
225662306a36Sopenharmony_ci#endif
225762306a36Sopenharmony_ci	if (cmd->host_command_reg == HOST_COMPLETE)
225862306a36Sopenharmony_ci		priv->status |= STATUS_ENABLED;
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	if (cmd->host_command_reg == CARD_DISABLE)
226162306a36Sopenharmony_ci		priv->status &= ~STATUS_ENABLED;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	priv->status &= ~STATUS_CMD_ACTIVE;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	wake_up_interruptible(&priv->wait_command_queue);
226662306a36Sopenharmony_ci}
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
226962306a36Sopenharmony_cistatic const char *frame_types[] = {
227062306a36Sopenharmony_ci	"COMMAND_STATUS_VAL",
227162306a36Sopenharmony_ci	"STATUS_CHANGE_VAL",
227262306a36Sopenharmony_ci	"P80211_DATA_VAL",
227362306a36Sopenharmony_ci	"P8023_DATA_VAL",
227462306a36Sopenharmony_ci	"HOST_NOTIFICATION_VAL"
227562306a36Sopenharmony_ci};
227662306a36Sopenharmony_ci#endif
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_cistatic int ipw2100_alloc_skb(struct ipw2100_priv *priv,
227962306a36Sopenharmony_ci				    struct ipw2100_rx_packet *packet)
228062306a36Sopenharmony_ci{
228162306a36Sopenharmony_ci	packet->skb = dev_alloc_skb(sizeof(struct ipw2100_rx));
228262306a36Sopenharmony_ci	if (!packet->skb)
228362306a36Sopenharmony_ci		return -ENOMEM;
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci	packet->rxp = (struct ipw2100_rx *)packet->skb->data;
228662306a36Sopenharmony_ci	packet->dma_addr = dma_map_single(&priv->pci_dev->dev,
228762306a36Sopenharmony_ci					  packet->skb->data,
228862306a36Sopenharmony_ci					  sizeof(struct ipw2100_rx),
228962306a36Sopenharmony_ci					  DMA_FROM_DEVICE);
229062306a36Sopenharmony_ci	if (dma_mapping_error(&priv->pci_dev->dev, packet->dma_addr)) {
229162306a36Sopenharmony_ci		dev_kfree_skb(packet->skb);
229262306a36Sopenharmony_ci		return -ENOMEM;
229362306a36Sopenharmony_ci	}
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci	return 0;
229662306a36Sopenharmony_ci}
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci#define SEARCH_ERROR   0xffffffff
229962306a36Sopenharmony_ci#define SEARCH_FAIL    0xfffffffe
230062306a36Sopenharmony_ci#define SEARCH_SUCCESS 0xfffffff0
230162306a36Sopenharmony_ci#define SEARCH_DISCARD 0
230262306a36Sopenharmony_ci#define SEARCH_SNAPSHOT 1
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci#define SNAPSHOT_ADDR(ofs) (priv->snapshot[((ofs) >> 12) & 0xff] + ((ofs) & 0xfff))
230562306a36Sopenharmony_cistatic void ipw2100_snapshot_free(struct ipw2100_priv *priv)
230662306a36Sopenharmony_ci{
230762306a36Sopenharmony_ci	int i;
230862306a36Sopenharmony_ci	if (!priv->snapshot[0])
230962306a36Sopenharmony_ci		return;
231062306a36Sopenharmony_ci	for (i = 0; i < 0x30; i++)
231162306a36Sopenharmony_ci		kfree(priv->snapshot[i]);
231262306a36Sopenharmony_ci	priv->snapshot[0] = NULL;
231362306a36Sopenharmony_ci}
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci#ifdef IPW2100_DEBUG_C3
231662306a36Sopenharmony_cistatic int ipw2100_snapshot_alloc(struct ipw2100_priv *priv)
231762306a36Sopenharmony_ci{
231862306a36Sopenharmony_ci	int i;
231962306a36Sopenharmony_ci	if (priv->snapshot[0])
232062306a36Sopenharmony_ci		return 1;
232162306a36Sopenharmony_ci	for (i = 0; i < 0x30; i++) {
232262306a36Sopenharmony_ci		priv->snapshot[i] = kmalloc(0x1000, GFP_ATOMIC);
232362306a36Sopenharmony_ci		if (!priv->snapshot[i]) {
232462306a36Sopenharmony_ci			IPW_DEBUG_INFO("%s: Error allocating snapshot "
232562306a36Sopenharmony_ci				       "buffer %d\n", priv->net_dev->name, i);
232662306a36Sopenharmony_ci			while (i > 0)
232762306a36Sopenharmony_ci				kfree(priv->snapshot[--i]);
232862306a36Sopenharmony_ci			priv->snapshot[0] = NULL;
232962306a36Sopenharmony_ci			return 0;
233062306a36Sopenharmony_ci		}
233162306a36Sopenharmony_ci	}
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_ci	return 1;
233462306a36Sopenharmony_ci}
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_cistatic u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 * in_buf,
233762306a36Sopenharmony_ci				    size_t len, int mode)
233862306a36Sopenharmony_ci{
233962306a36Sopenharmony_ci	u32 i, j;
234062306a36Sopenharmony_ci	u32 tmp;
234162306a36Sopenharmony_ci	u8 *s, *d;
234262306a36Sopenharmony_ci	u32 ret;
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci	s = in_buf;
234562306a36Sopenharmony_ci	if (mode == SEARCH_SNAPSHOT) {
234662306a36Sopenharmony_ci		if (!ipw2100_snapshot_alloc(priv))
234762306a36Sopenharmony_ci			mode = SEARCH_DISCARD;
234862306a36Sopenharmony_ci	}
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	for (ret = SEARCH_FAIL, i = 0; i < 0x30000; i += 4) {
235162306a36Sopenharmony_ci		read_nic_dword(priv->net_dev, i, &tmp);
235262306a36Sopenharmony_ci		if (mode == SEARCH_SNAPSHOT)
235362306a36Sopenharmony_ci			*(u32 *) SNAPSHOT_ADDR(i) = tmp;
235462306a36Sopenharmony_ci		if (ret == SEARCH_FAIL) {
235562306a36Sopenharmony_ci			d = (u8 *) & tmp;
235662306a36Sopenharmony_ci			for (j = 0; j < 4; j++) {
235762306a36Sopenharmony_ci				if (*s != *d) {
235862306a36Sopenharmony_ci					s = in_buf;
235962306a36Sopenharmony_ci					continue;
236062306a36Sopenharmony_ci				}
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci				s++;
236362306a36Sopenharmony_ci				d++;
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci				if ((s - in_buf) == len)
236662306a36Sopenharmony_ci					ret = (i + j) - len + 1;
236762306a36Sopenharmony_ci			}
236862306a36Sopenharmony_ci		} else if (mode == SEARCH_DISCARD)
236962306a36Sopenharmony_ci			return ret;
237062306a36Sopenharmony_ci	}
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	return ret;
237362306a36Sopenharmony_ci}
237462306a36Sopenharmony_ci#endif
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci/*
237762306a36Sopenharmony_ci *
237862306a36Sopenharmony_ci * 0) Disconnect the SKB from the firmware (just unmap)
237962306a36Sopenharmony_ci * 1) Pack the ETH header into the SKB
238062306a36Sopenharmony_ci * 2) Pass the SKB to the network stack
238162306a36Sopenharmony_ci *
238262306a36Sopenharmony_ci * When packet is provided by the firmware, it contains the following:
238362306a36Sopenharmony_ci *
238462306a36Sopenharmony_ci * .  libipw_hdr
238562306a36Sopenharmony_ci * .  libipw_snap_hdr
238662306a36Sopenharmony_ci *
238762306a36Sopenharmony_ci * The size of the constructed ethernet
238862306a36Sopenharmony_ci *
238962306a36Sopenharmony_ci */
239062306a36Sopenharmony_ci#ifdef IPW2100_RX_DEBUG
239162306a36Sopenharmony_cistatic u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH];
239262306a36Sopenharmony_ci#endif
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_cistatic void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i)
239562306a36Sopenharmony_ci{
239662306a36Sopenharmony_ci#ifdef IPW2100_DEBUG_C3
239762306a36Sopenharmony_ci	struct ipw2100_status *status = &priv->status_queue.drv[i];
239862306a36Sopenharmony_ci	u32 match, reg;
239962306a36Sopenharmony_ci	int j;
240062306a36Sopenharmony_ci#endif
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	IPW_DEBUG_INFO(": PCI latency error detected at 0x%04zX.\n",
240362306a36Sopenharmony_ci		       i * sizeof(struct ipw2100_status));
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci#ifdef IPW2100_DEBUG_C3
240662306a36Sopenharmony_ci	/* Halt the firmware so we can get a good image */
240762306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_REG_RESET_REG,
240862306a36Sopenharmony_ci		       IPW_AUX_HOST_RESET_REG_STOP_MASTER);
240962306a36Sopenharmony_ci	j = 5;
241062306a36Sopenharmony_ci	do {
241162306a36Sopenharmony_ci		udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
241262306a36Sopenharmony_ci		read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci		if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
241562306a36Sopenharmony_ci			break;
241662306a36Sopenharmony_ci	} while (j--);
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	match = ipw2100_match_buf(priv, (u8 *) status,
241962306a36Sopenharmony_ci				  sizeof(struct ipw2100_status),
242062306a36Sopenharmony_ci				  SEARCH_SNAPSHOT);
242162306a36Sopenharmony_ci	if (match < SEARCH_SUCCESS)
242262306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: DMA status match in Firmware at "
242362306a36Sopenharmony_ci			       "offset 0x%06X, length %d:\n",
242462306a36Sopenharmony_ci			       priv->net_dev->name, match,
242562306a36Sopenharmony_ci			       sizeof(struct ipw2100_status));
242662306a36Sopenharmony_ci	else
242762306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: No DMA status match in "
242862306a36Sopenharmony_ci			       "Firmware.\n", priv->net_dev->name);
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	printk_buf((u8 *) priv->status_queue.drv,
243162306a36Sopenharmony_ci		   sizeof(struct ipw2100_status) * RX_QUEUE_LENGTH);
243262306a36Sopenharmony_ci#endif
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	priv->fatal_error = IPW2100_ERR_C3_CORRUPTION;
243562306a36Sopenharmony_ci	priv->net_dev->stats.rx_errors++;
243662306a36Sopenharmony_ci	schedule_reset(priv);
243762306a36Sopenharmony_ci}
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_cistatic void isr_rx(struct ipw2100_priv *priv, int i,
244062306a36Sopenharmony_ci			  struct libipw_rx_stats *stats)
244162306a36Sopenharmony_ci{
244262306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
244362306a36Sopenharmony_ci	struct ipw2100_status *status = &priv->status_queue.drv[i];
244462306a36Sopenharmony_ci	struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	IPW_DEBUG_RX("Handler...\n");
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	if (unlikely(status->frame_size > skb_tailroom(packet->skb))) {
244962306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
245062306a36Sopenharmony_ci			       "  Dropping.\n",
245162306a36Sopenharmony_ci			       dev->name,
245262306a36Sopenharmony_ci			       status->frame_size, skb_tailroom(packet->skb));
245362306a36Sopenharmony_ci		dev->stats.rx_errors++;
245462306a36Sopenharmony_ci		return;
245562306a36Sopenharmony_ci	}
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci	if (unlikely(!netif_running(dev))) {
245862306a36Sopenharmony_ci		dev->stats.rx_errors++;
245962306a36Sopenharmony_ci		priv->wstats.discard.misc++;
246062306a36Sopenharmony_ci		IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
246162306a36Sopenharmony_ci		return;
246262306a36Sopenharmony_ci	}
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci	if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR &&
246562306a36Sopenharmony_ci		     !(priv->status & STATUS_ASSOCIATED))) {
246662306a36Sopenharmony_ci		IPW_DEBUG_DROP("Dropping packet while not associated.\n");
246762306a36Sopenharmony_ci		priv->wstats.discard.misc++;
246862306a36Sopenharmony_ci		return;
246962306a36Sopenharmony_ci	}
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	dma_unmap_single(&priv->pci_dev->dev, packet->dma_addr,
247262306a36Sopenharmony_ci			 sizeof(struct ipw2100_rx), DMA_FROM_DEVICE);
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	skb_put(packet->skb, status->frame_size);
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci#ifdef IPW2100_RX_DEBUG
247762306a36Sopenharmony_ci	/* Make a copy of the frame so we can dump it to the logs if
247862306a36Sopenharmony_ci	 * libipw_rx fails */
247962306a36Sopenharmony_ci	skb_copy_from_linear_data(packet->skb, packet_data,
248062306a36Sopenharmony_ci				  min_t(u32, status->frame_size,
248162306a36Sopenharmony_ci					     IPW_RX_NIC_BUFFER_LENGTH));
248262306a36Sopenharmony_ci#endif
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ci	if (!libipw_rx(priv->ieee, packet->skb, stats)) {
248562306a36Sopenharmony_ci#ifdef IPW2100_RX_DEBUG
248662306a36Sopenharmony_ci		IPW_DEBUG_DROP("%s: Non consumed packet:\n",
248762306a36Sopenharmony_ci			       dev->name);
248862306a36Sopenharmony_ci		printk_buf(IPW_DL_DROP, packet_data, status->frame_size);
248962306a36Sopenharmony_ci#endif
249062306a36Sopenharmony_ci		dev->stats.rx_errors++;
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci		/* libipw_rx failed, so it didn't free the SKB */
249362306a36Sopenharmony_ci		dev_kfree_skb_any(packet->skb);
249462306a36Sopenharmony_ci		packet->skb = NULL;
249562306a36Sopenharmony_ci	}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci	/* We need to allocate a new SKB and attach it to the RDB. */
249862306a36Sopenharmony_ci	if (unlikely(ipw2100_alloc_skb(priv, packet))) {
249962306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME ": "
250062306a36Sopenharmony_ci		       "%s: Unable to allocate SKB onto RBD ring - disabling "
250162306a36Sopenharmony_ci		       "adapter.\n", dev->name);
250262306a36Sopenharmony_ci		/* TODO: schedule adapter shutdown */
250362306a36Sopenharmony_ci		IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
250462306a36Sopenharmony_ci	}
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	/* Update the RDB entry */
250762306a36Sopenharmony_ci	priv->rx_queue.drv[i].host_addr = packet->dma_addr;
250862306a36Sopenharmony_ci}
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_cistatic void isr_rx_monitor(struct ipw2100_priv *priv, int i,
251362306a36Sopenharmony_ci		   struct libipw_rx_stats *stats)
251462306a36Sopenharmony_ci{
251562306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
251662306a36Sopenharmony_ci	struct ipw2100_status *status = &priv->status_queue.drv[i];
251762306a36Sopenharmony_ci	struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	/* Magic struct that slots into the radiotap header -- no reason
252062306a36Sopenharmony_ci	 * to build this manually element by element, we can write it much
252162306a36Sopenharmony_ci	 * more efficiently than we can parse it. ORDER MATTERS HERE */
252262306a36Sopenharmony_ci	struct ipw_rt_hdr {
252362306a36Sopenharmony_ci		struct ieee80211_radiotap_header rt_hdr;
252462306a36Sopenharmony_ci		s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
252562306a36Sopenharmony_ci	} *ipw_rt;
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	IPW_DEBUG_RX("Handler...\n");
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	if (unlikely(status->frame_size > skb_tailroom(packet->skb) -
253062306a36Sopenharmony_ci				sizeof(struct ipw_rt_hdr))) {
253162306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
253262306a36Sopenharmony_ci			       "  Dropping.\n",
253362306a36Sopenharmony_ci			       dev->name,
253462306a36Sopenharmony_ci			       status->frame_size,
253562306a36Sopenharmony_ci			       skb_tailroom(packet->skb));
253662306a36Sopenharmony_ci		dev->stats.rx_errors++;
253762306a36Sopenharmony_ci		return;
253862306a36Sopenharmony_ci	}
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci	if (unlikely(!netif_running(dev))) {
254162306a36Sopenharmony_ci		dev->stats.rx_errors++;
254262306a36Sopenharmony_ci		priv->wstats.discard.misc++;
254362306a36Sopenharmony_ci		IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
254462306a36Sopenharmony_ci		return;
254562306a36Sopenharmony_ci	}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci	if (unlikely(priv->config & CFG_CRC_CHECK &&
254862306a36Sopenharmony_ci		     status->flags & IPW_STATUS_FLAG_CRC_ERROR)) {
254962306a36Sopenharmony_ci		IPW_DEBUG_RX("CRC error in packet.  Dropping.\n");
255062306a36Sopenharmony_ci		dev->stats.rx_errors++;
255162306a36Sopenharmony_ci		return;
255262306a36Sopenharmony_ci	}
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	dma_unmap_single(&priv->pci_dev->dev, packet->dma_addr,
255562306a36Sopenharmony_ci			 sizeof(struct ipw2100_rx), DMA_FROM_DEVICE);
255662306a36Sopenharmony_ci	memmove(packet->skb->data + sizeof(struct ipw_rt_hdr),
255762306a36Sopenharmony_ci		packet->skb->data, status->frame_size);
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	ipw_rt = (struct ipw_rt_hdr *) packet->skb->data;
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
256262306a36Sopenharmony_ci	ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
256362306a36Sopenharmony_ci	ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total hdr+data */
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	ipw_rt->rt_hdr.it_present = cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci	ipw_rt->rt_dbmsignal = status->rssi + IPW2100_RSSI_TO_DBM;
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_ci	skb_put(packet->skb, status->frame_size + sizeof(struct ipw_rt_hdr));
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	if (!libipw_rx(priv->ieee, packet->skb, stats)) {
257262306a36Sopenharmony_ci		dev->stats.rx_errors++;
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci		/* libipw_rx failed, so it didn't free the SKB */
257562306a36Sopenharmony_ci		dev_kfree_skb_any(packet->skb);
257662306a36Sopenharmony_ci		packet->skb = NULL;
257762306a36Sopenharmony_ci	}
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_ci	/* We need to allocate a new SKB and attach it to the RDB. */
258062306a36Sopenharmony_ci	if (unlikely(ipw2100_alloc_skb(priv, packet))) {
258162306a36Sopenharmony_ci		IPW_DEBUG_WARNING(
258262306a36Sopenharmony_ci			"%s: Unable to allocate SKB onto RBD ring - disabling "
258362306a36Sopenharmony_ci			"adapter.\n", dev->name);
258462306a36Sopenharmony_ci		/* TODO: schedule adapter shutdown */
258562306a36Sopenharmony_ci		IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
258662306a36Sopenharmony_ci	}
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	/* Update the RDB entry */
258962306a36Sopenharmony_ci	priv->rx_queue.drv[i].host_addr = packet->dma_addr;
259062306a36Sopenharmony_ci}
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci#endif
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_cistatic int ipw2100_corruption_check(struct ipw2100_priv *priv, int i)
259562306a36Sopenharmony_ci{
259662306a36Sopenharmony_ci	struct ipw2100_status *status = &priv->status_queue.drv[i];
259762306a36Sopenharmony_ci	struct ipw2100_rx *u = priv->rx_buffers[i].rxp;
259862306a36Sopenharmony_ci	u16 frame_type = status->status_fields & STATUS_TYPE_MASK;
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	switch (frame_type) {
260162306a36Sopenharmony_ci	case COMMAND_STATUS_VAL:
260262306a36Sopenharmony_ci		return (status->frame_size != sizeof(u->rx_data.command));
260362306a36Sopenharmony_ci	case STATUS_CHANGE_VAL:
260462306a36Sopenharmony_ci		return (status->frame_size != sizeof(u->rx_data.status));
260562306a36Sopenharmony_ci	case HOST_NOTIFICATION_VAL:
260662306a36Sopenharmony_ci		return (status->frame_size < sizeof(u->rx_data.notification));
260762306a36Sopenharmony_ci	case P80211_DATA_VAL:
260862306a36Sopenharmony_ci	case P8023_DATA_VAL:
260962306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
261062306a36Sopenharmony_ci		return 0;
261162306a36Sopenharmony_ci#else
261262306a36Sopenharmony_ci		switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
261362306a36Sopenharmony_ci		case IEEE80211_FTYPE_MGMT:
261462306a36Sopenharmony_ci		case IEEE80211_FTYPE_CTL:
261562306a36Sopenharmony_ci			return 0;
261662306a36Sopenharmony_ci		case IEEE80211_FTYPE_DATA:
261762306a36Sopenharmony_ci			return (status->frame_size >
261862306a36Sopenharmony_ci				IPW_MAX_802_11_PAYLOAD_LENGTH);
261962306a36Sopenharmony_ci		}
262062306a36Sopenharmony_ci#endif
262162306a36Sopenharmony_ci	}
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci	return 1;
262462306a36Sopenharmony_ci}
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci/*
262762306a36Sopenharmony_ci * ipw2100 interrupts are disabled at this point, and the ISR
262862306a36Sopenharmony_ci * is the only code that calls this method.  So, we do not need
262962306a36Sopenharmony_ci * to play with any locks.
263062306a36Sopenharmony_ci *
263162306a36Sopenharmony_ci * RX Queue works as follows:
263262306a36Sopenharmony_ci *
263362306a36Sopenharmony_ci * Read index - firmware places packet in entry identified by the
263462306a36Sopenharmony_ci *              Read index and advances Read index.  In this manner,
263562306a36Sopenharmony_ci *              Read index will always point to the next packet to
263662306a36Sopenharmony_ci *              be filled--but not yet valid.
263762306a36Sopenharmony_ci *
263862306a36Sopenharmony_ci * Write index - driver fills this entry with an unused RBD entry.
263962306a36Sopenharmony_ci *               This entry has not filled by the firmware yet.
264062306a36Sopenharmony_ci *
264162306a36Sopenharmony_ci * In between the W and R indexes are the RBDs that have been received
264262306a36Sopenharmony_ci * but not yet processed.
264362306a36Sopenharmony_ci *
264462306a36Sopenharmony_ci * The process of handling packets will start at WRITE + 1 and advance
264562306a36Sopenharmony_ci * until it reaches the READ index.
264662306a36Sopenharmony_ci *
264762306a36Sopenharmony_ci * The WRITE index is cached in the variable 'priv->rx_queue.next'.
264862306a36Sopenharmony_ci *
264962306a36Sopenharmony_ci */
265062306a36Sopenharmony_cistatic void __ipw2100_rx_process(struct ipw2100_priv *priv)
265162306a36Sopenharmony_ci{
265262306a36Sopenharmony_ci	struct ipw2100_bd_queue *rxq = &priv->rx_queue;
265362306a36Sopenharmony_ci	struct ipw2100_status_queue *sq = &priv->status_queue;
265462306a36Sopenharmony_ci	struct ipw2100_rx_packet *packet;
265562306a36Sopenharmony_ci	u16 frame_type;
265662306a36Sopenharmony_ci	u32 r, w, i, s;
265762306a36Sopenharmony_ci	struct ipw2100_rx *u;
265862306a36Sopenharmony_ci	struct libipw_rx_stats stats = {
265962306a36Sopenharmony_ci		.mac_time = jiffies,
266062306a36Sopenharmony_ci	};
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_READ_INDEX, &r);
266362306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, &w);
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci	if (r >= rxq->entries) {
266662306a36Sopenharmony_ci		IPW_DEBUG_RX("exit - bad read index\n");
266762306a36Sopenharmony_ci		return;
266862306a36Sopenharmony_ci	}
266962306a36Sopenharmony_ci
267062306a36Sopenharmony_ci	i = (rxq->next + 1) % rxq->entries;
267162306a36Sopenharmony_ci	s = i;
267262306a36Sopenharmony_ci	while (i != r) {
267362306a36Sopenharmony_ci		/* IPW_DEBUG_RX("r = %d : w = %d : processing = %d\n",
267462306a36Sopenharmony_ci		   r, rxq->next, i); */
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci		packet = &priv->rx_buffers[i];
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci		/* Sync the DMA for the RX buffer so CPU is sure to get
267962306a36Sopenharmony_ci		 * the correct values */
268062306a36Sopenharmony_ci		dma_sync_single_for_cpu(&priv->pci_dev->dev, packet->dma_addr,
268162306a36Sopenharmony_ci					sizeof(struct ipw2100_rx),
268262306a36Sopenharmony_ci					DMA_FROM_DEVICE);
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci		if (unlikely(ipw2100_corruption_check(priv, i))) {
268562306a36Sopenharmony_ci			ipw2100_corruption_detected(priv, i);
268662306a36Sopenharmony_ci			goto increment;
268762306a36Sopenharmony_ci		}
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci		u = packet->rxp;
269062306a36Sopenharmony_ci		frame_type = sq->drv[i].status_fields & STATUS_TYPE_MASK;
269162306a36Sopenharmony_ci		stats.rssi = sq->drv[i].rssi + IPW2100_RSSI_TO_DBM;
269262306a36Sopenharmony_ci		stats.len = sq->drv[i].frame_size;
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_ci		stats.mask = 0;
269562306a36Sopenharmony_ci		if (stats.rssi != 0)
269662306a36Sopenharmony_ci			stats.mask |= LIBIPW_STATMASK_RSSI;
269762306a36Sopenharmony_ci		stats.freq = LIBIPW_24GHZ_BAND;
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_ci		IPW_DEBUG_RX("%s: '%s' frame type received (%d).\n",
270062306a36Sopenharmony_ci			     priv->net_dev->name, frame_types[frame_type],
270162306a36Sopenharmony_ci			     stats.len);
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci		switch (frame_type) {
270462306a36Sopenharmony_ci		case COMMAND_STATUS_VAL:
270562306a36Sopenharmony_ci			/* Reset Rx watchdog */
270662306a36Sopenharmony_ci			isr_rx_complete_command(priv, &u->rx_data.command);
270762306a36Sopenharmony_ci			break;
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci		case STATUS_CHANGE_VAL:
271062306a36Sopenharmony_ci			isr_status_change(priv, u->rx_data.status);
271162306a36Sopenharmony_ci			break;
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci		case P80211_DATA_VAL:
271462306a36Sopenharmony_ci		case P8023_DATA_VAL:
271562306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
271662306a36Sopenharmony_ci			if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
271762306a36Sopenharmony_ci				isr_rx_monitor(priv, i, &stats);
271862306a36Sopenharmony_ci				break;
271962306a36Sopenharmony_ci			}
272062306a36Sopenharmony_ci#endif
272162306a36Sopenharmony_ci			if (stats.len < sizeof(struct libipw_hdr_3addr))
272262306a36Sopenharmony_ci				break;
272362306a36Sopenharmony_ci			switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
272462306a36Sopenharmony_ci			case IEEE80211_FTYPE_MGMT:
272562306a36Sopenharmony_ci				libipw_rx_mgt(priv->ieee,
272662306a36Sopenharmony_ci						 &u->rx_data.header, &stats);
272762306a36Sopenharmony_ci				break;
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_ci			case IEEE80211_FTYPE_CTL:
273062306a36Sopenharmony_ci				break;
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci			case IEEE80211_FTYPE_DATA:
273362306a36Sopenharmony_ci				isr_rx(priv, i, &stats);
273462306a36Sopenharmony_ci				break;
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci			}
273762306a36Sopenharmony_ci			break;
273862306a36Sopenharmony_ci		}
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	      increment:
274162306a36Sopenharmony_ci		/* clear status field associated with this RBD */
274262306a36Sopenharmony_ci		rxq->drv[i].status.info.field = 0;
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci		i = (i + 1) % rxq->entries;
274562306a36Sopenharmony_ci	}
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_ci	if (i != s) {
274862306a36Sopenharmony_ci		/* backtrack one entry, wrapping to end if at 0 */
274962306a36Sopenharmony_ci		rxq->next = (i ? i : rxq->entries) - 1;
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci		write_register(priv->net_dev,
275262306a36Sopenharmony_ci			       IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, rxq->next);
275362306a36Sopenharmony_ci	}
275462306a36Sopenharmony_ci}
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci/*
275762306a36Sopenharmony_ci * __ipw2100_tx_process
275862306a36Sopenharmony_ci *
275962306a36Sopenharmony_ci * This routine will determine whether the next packet on
276062306a36Sopenharmony_ci * the fw_pend_list has been processed by the firmware yet.
276162306a36Sopenharmony_ci *
276262306a36Sopenharmony_ci * If not, then it does nothing and returns.
276362306a36Sopenharmony_ci *
276462306a36Sopenharmony_ci * If so, then it removes the item from the fw_pend_list, frees
276562306a36Sopenharmony_ci * any associated storage, and places the item back on the
276662306a36Sopenharmony_ci * free list of its source (either msg_free_list or tx_free_list)
276762306a36Sopenharmony_ci *
276862306a36Sopenharmony_ci * TX Queue works as follows:
276962306a36Sopenharmony_ci *
277062306a36Sopenharmony_ci * Read index - points to the next TBD that the firmware will
277162306a36Sopenharmony_ci *              process.  The firmware will read the data, and once
277262306a36Sopenharmony_ci *              done processing, it will advance the Read index.
277362306a36Sopenharmony_ci *
277462306a36Sopenharmony_ci * Write index - driver fills this entry with an constructed TBD
277562306a36Sopenharmony_ci *               entry.  The Write index is not advanced until the
277662306a36Sopenharmony_ci *               packet has been configured.
277762306a36Sopenharmony_ci *
277862306a36Sopenharmony_ci * In between the W and R indexes are the TBDs that have NOT been
277962306a36Sopenharmony_ci * processed.  Lagging behind the R index are packets that have
278062306a36Sopenharmony_ci * been processed but have not been freed by the driver.
278162306a36Sopenharmony_ci *
278262306a36Sopenharmony_ci * In order to free old storage, an internal index will be maintained
278362306a36Sopenharmony_ci * that points to the next packet to be freed.  When all used
278462306a36Sopenharmony_ci * packets have been freed, the oldest index will be the same as the
278562306a36Sopenharmony_ci * firmware's read index.
278662306a36Sopenharmony_ci *
278762306a36Sopenharmony_ci * The OLDEST index is cached in the variable 'priv->tx_queue.oldest'
278862306a36Sopenharmony_ci *
278962306a36Sopenharmony_ci * Because the TBD structure can not contain arbitrary data, the
279062306a36Sopenharmony_ci * driver must keep an internal queue of cached allocations such that
279162306a36Sopenharmony_ci * it can put that data back into the tx_free_list and msg_free_list
279262306a36Sopenharmony_ci * for use by future command and data packets.
279362306a36Sopenharmony_ci *
279462306a36Sopenharmony_ci */
279562306a36Sopenharmony_cistatic int __ipw2100_tx_process(struct ipw2100_priv *priv)
279662306a36Sopenharmony_ci{
279762306a36Sopenharmony_ci	struct ipw2100_bd_queue *txq = &priv->tx_queue;
279862306a36Sopenharmony_ci	struct ipw2100_bd *tbd;
279962306a36Sopenharmony_ci	struct list_head *element;
280062306a36Sopenharmony_ci	struct ipw2100_tx_packet *packet;
280162306a36Sopenharmony_ci	int descriptors_used;
280262306a36Sopenharmony_ci	int e, i;
280362306a36Sopenharmony_ci	u32 r, w, frag_num = 0;
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	if (list_empty(&priv->fw_pend_list))
280662306a36Sopenharmony_ci		return 0;
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	element = priv->fw_pend_list.next;
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	packet = list_entry(element, struct ipw2100_tx_packet, list);
281162306a36Sopenharmony_ci	tbd = &txq->drv[packet->index];
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci	/* Determine how many TBD entries must be finished... */
281462306a36Sopenharmony_ci	switch (packet->type) {
281562306a36Sopenharmony_ci	case COMMAND:
281662306a36Sopenharmony_ci		/* COMMAND uses only one slot; don't advance */
281762306a36Sopenharmony_ci		descriptors_used = 1;
281862306a36Sopenharmony_ci		e = txq->oldest;
281962306a36Sopenharmony_ci		break;
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci	case DATA:
282262306a36Sopenharmony_ci		/* DATA uses two slots; advance and loop position. */
282362306a36Sopenharmony_ci		descriptors_used = tbd->num_fragments;
282462306a36Sopenharmony_ci		frag_num = tbd->num_fragments - 1;
282562306a36Sopenharmony_ci		e = txq->oldest + frag_num;
282662306a36Sopenharmony_ci		e %= txq->entries;
282762306a36Sopenharmony_ci		break;
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	default:
283062306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME ": %s: Bad fw_pend_list entry!\n",
283162306a36Sopenharmony_ci		       priv->net_dev->name);
283262306a36Sopenharmony_ci		return 0;
283362306a36Sopenharmony_ci	}
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci	/* if the last TBD is not done by NIC yet, then packet is
283662306a36Sopenharmony_ci	 * not ready to be released.
283762306a36Sopenharmony_ci	 *
283862306a36Sopenharmony_ci	 */
283962306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
284062306a36Sopenharmony_ci		      &r);
284162306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
284262306a36Sopenharmony_ci		      &w);
284362306a36Sopenharmony_ci	if (w != txq->next)
284462306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME ": %s: write index mismatch\n",
284562306a36Sopenharmony_ci		       priv->net_dev->name);
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	/*
284862306a36Sopenharmony_ci	 * txq->next is the index of the last packet written txq->oldest is
284962306a36Sopenharmony_ci	 * the index of the r is the index of the next packet to be read by
285062306a36Sopenharmony_ci	 * firmware
285162306a36Sopenharmony_ci	 */
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci	/*
285462306a36Sopenharmony_ci	 * Quick graphic to help you visualize the following
285562306a36Sopenharmony_ci	 * if / else statement
285662306a36Sopenharmony_ci	 *
285762306a36Sopenharmony_ci	 * ===>|                     s---->|===============
285862306a36Sopenharmony_ci	 *                               e>|
285962306a36Sopenharmony_ci	 * | a | b | c | d | e | f | g | h | i | j | k | l
286062306a36Sopenharmony_ci	 *       r---->|
286162306a36Sopenharmony_ci	 *               w
286262306a36Sopenharmony_ci	 *
286362306a36Sopenharmony_ci	 * w - updated by driver
286462306a36Sopenharmony_ci	 * r - updated by firmware
286562306a36Sopenharmony_ci	 * s - start of oldest BD entry (txq->oldest)
286662306a36Sopenharmony_ci	 * e - end of oldest BD entry
286762306a36Sopenharmony_ci	 *
286862306a36Sopenharmony_ci	 */
286962306a36Sopenharmony_ci	if (!((r <= w && (e < r || e >= w)) || (e < r && e >= w))) {
287062306a36Sopenharmony_ci		IPW_DEBUG_TX("exit - no processed packets ready to release.\n");
287162306a36Sopenharmony_ci		return 0;
287262306a36Sopenharmony_ci	}
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci	list_del(element);
287562306a36Sopenharmony_ci	DEC_STAT(&priv->fw_pend_stat);
287662306a36Sopenharmony_ci
287762306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
287862306a36Sopenharmony_ci	{
287962306a36Sopenharmony_ci		i = txq->oldest;
288062306a36Sopenharmony_ci		IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i,
288162306a36Sopenharmony_ci			     &txq->drv[i],
288262306a36Sopenharmony_ci			     (u32) (txq->nic + i * sizeof(struct ipw2100_bd)),
288362306a36Sopenharmony_ci			     txq->drv[i].host_addr, txq->drv[i].buf_length);
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci		if (packet->type == DATA) {
288662306a36Sopenharmony_ci			i = (i + 1) % txq->entries;
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_ci			IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i,
288962306a36Sopenharmony_ci				     &txq->drv[i],
289062306a36Sopenharmony_ci				     (u32) (txq->nic + i *
289162306a36Sopenharmony_ci					    sizeof(struct ipw2100_bd)),
289262306a36Sopenharmony_ci				     (u32) txq->drv[i].host_addr,
289362306a36Sopenharmony_ci				     txq->drv[i].buf_length);
289462306a36Sopenharmony_ci		}
289562306a36Sopenharmony_ci	}
289662306a36Sopenharmony_ci#endif
289762306a36Sopenharmony_ci
289862306a36Sopenharmony_ci	switch (packet->type) {
289962306a36Sopenharmony_ci	case DATA:
290062306a36Sopenharmony_ci		if (txq->drv[txq->oldest].status.info.fields.txType != 0)
290162306a36Sopenharmony_ci			printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch.  "
290262306a36Sopenharmony_ci			       "Expecting DATA TBD but pulled "
290362306a36Sopenharmony_ci			       "something else: ids %d=%d.\n",
290462306a36Sopenharmony_ci			       priv->net_dev->name, txq->oldest, packet->index);
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci		/* DATA packet; we have to unmap and free the SKB */
290762306a36Sopenharmony_ci		for (i = 0; i < frag_num; i++) {
290862306a36Sopenharmony_ci			tbd = &txq->drv[(packet->index + 1 + i) % txq->entries];
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci			IPW_DEBUG_TX("TX%d P=%08x L=%d\n",
291162306a36Sopenharmony_ci				     (packet->index + 1 + i) % txq->entries,
291262306a36Sopenharmony_ci				     tbd->host_addr, tbd->buf_length);
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci			dma_unmap_single(&priv->pci_dev->dev, tbd->host_addr,
291562306a36Sopenharmony_ci					 tbd->buf_length, DMA_TO_DEVICE);
291662306a36Sopenharmony_ci		}
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_ci		libipw_txb_free(packet->info.d_struct.txb);
291962306a36Sopenharmony_ci		packet->info.d_struct.txb = NULL;
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci		list_add_tail(element, &priv->tx_free_list);
292262306a36Sopenharmony_ci		INC_STAT(&priv->tx_free_stat);
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci		/* We have a free slot in the Tx queue, so wake up the
292562306a36Sopenharmony_ci		 * transmit layer if it is stopped. */
292662306a36Sopenharmony_ci		if (priv->status & STATUS_ASSOCIATED)
292762306a36Sopenharmony_ci			netif_wake_queue(priv->net_dev);
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_ci		/* A packet was processed by the hardware, so update the
293062306a36Sopenharmony_ci		 * watchdog */
293162306a36Sopenharmony_ci		netif_trans_update(priv->net_dev);
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci		break;
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_ci	case COMMAND:
293662306a36Sopenharmony_ci		if (txq->drv[txq->oldest].status.info.fields.txType != 1)
293762306a36Sopenharmony_ci			printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch.  "
293862306a36Sopenharmony_ci			       "Expecting COMMAND TBD but pulled "
293962306a36Sopenharmony_ci			       "something else: ids %d=%d.\n",
294062306a36Sopenharmony_ci			       priv->net_dev->name, txq->oldest, packet->index);
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
294362306a36Sopenharmony_ci		if (packet->info.c_struct.cmd->host_command_reg <
294462306a36Sopenharmony_ci		    ARRAY_SIZE(command_types))
294562306a36Sopenharmony_ci			IPW_DEBUG_TX("Command '%s (%d)' processed: %d.\n",
294662306a36Sopenharmony_ci				     command_types[packet->info.c_struct.cmd->
294762306a36Sopenharmony_ci						   host_command_reg],
294862306a36Sopenharmony_ci				     packet->info.c_struct.cmd->
294962306a36Sopenharmony_ci				     host_command_reg,
295062306a36Sopenharmony_ci				     packet->info.c_struct.cmd->cmd_status_reg);
295162306a36Sopenharmony_ci#endif
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci		list_add_tail(element, &priv->msg_free_list);
295462306a36Sopenharmony_ci		INC_STAT(&priv->msg_free_stat);
295562306a36Sopenharmony_ci		break;
295662306a36Sopenharmony_ci	}
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	/* advance oldest used TBD pointer to start of next entry */
295962306a36Sopenharmony_ci	txq->oldest = (e + 1) % txq->entries;
296062306a36Sopenharmony_ci	/* increase available TBDs number */
296162306a36Sopenharmony_ci	txq->available += descriptors_used;
296262306a36Sopenharmony_ci	SET_STAT(&priv->txq_stat, txq->available);
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	IPW_DEBUG_TX("packet latency (send to process)  %ld jiffies\n",
296562306a36Sopenharmony_ci		     jiffies - packet->jiffy_start);
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	return (!list_empty(&priv->fw_pend_list));
296862306a36Sopenharmony_ci}
296962306a36Sopenharmony_ci
297062306a36Sopenharmony_cistatic inline void __ipw2100_tx_complete(struct ipw2100_priv *priv)
297162306a36Sopenharmony_ci{
297262306a36Sopenharmony_ci	int i = 0;
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci	while (__ipw2100_tx_process(priv) && i < 200)
297562306a36Sopenharmony_ci		i++;
297662306a36Sopenharmony_ci
297762306a36Sopenharmony_ci	if (i == 200) {
297862306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME ": "
297962306a36Sopenharmony_ci		       "%s: Driver is running slow (%d iters).\n",
298062306a36Sopenharmony_ci		       priv->net_dev->name, i);
298162306a36Sopenharmony_ci	}
298262306a36Sopenharmony_ci}
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_cistatic void ipw2100_tx_send_commands(struct ipw2100_priv *priv)
298562306a36Sopenharmony_ci{
298662306a36Sopenharmony_ci	struct list_head *element;
298762306a36Sopenharmony_ci	struct ipw2100_tx_packet *packet;
298862306a36Sopenharmony_ci	struct ipw2100_bd_queue *txq = &priv->tx_queue;
298962306a36Sopenharmony_ci	struct ipw2100_bd *tbd;
299062306a36Sopenharmony_ci	int next = txq->next;
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci	while (!list_empty(&priv->msg_pend_list)) {
299362306a36Sopenharmony_ci		/* if there isn't enough space in TBD queue, then
299462306a36Sopenharmony_ci		 * don't stuff a new one in.
299562306a36Sopenharmony_ci		 * NOTE: 3 are needed as a command will take one,
299662306a36Sopenharmony_ci		 *       and there is a minimum of 2 that must be
299762306a36Sopenharmony_ci		 *       maintained between the r and w indexes
299862306a36Sopenharmony_ci		 */
299962306a36Sopenharmony_ci		if (txq->available <= 3) {
300062306a36Sopenharmony_ci			IPW_DEBUG_TX("no room in tx_queue\n");
300162306a36Sopenharmony_ci			break;
300262306a36Sopenharmony_ci		}
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci		element = priv->msg_pend_list.next;
300562306a36Sopenharmony_ci		list_del(element);
300662306a36Sopenharmony_ci		DEC_STAT(&priv->msg_pend_stat);
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci		packet = list_entry(element, struct ipw2100_tx_packet, list);
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_ci		IPW_DEBUG_TX("using TBD at virt=%p, phys=%04X\n",
301162306a36Sopenharmony_ci			     &txq->drv[txq->next],
301262306a36Sopenharmony_ci			     (u32) (txq->nic + txq->next *
301362306a36Sopenharmony_ci				      sizeof(struct ipw2100_bd)));
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci		packet->index = txq->next;
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci		tbd = &txq->drv[txq->next];
301862306a36Sopenharmony_ci
301962306a36Sopenharmony_ci		/* initialize TBD */
302062306a36Sopenharmony_ci		tbd->host_addr = packet->info.c_struct.cmd_phys;
302162306a36Sopenharmony_ci		tbd->buf_length = sizeof(struct ipw2100_cmd_header);
302262306a36Sopenharmony_ci		/* not marking number of fragments causes problems
302362306a36Sopenharmony_ci		 * with f/w debug version */
302462306a36Sopenharmony_ci		tbd->num_fragments = 1;
302562306a36Sopenharmony_ci		tbd->status.info.field =
302662306a36Sopenharmony_ci		    IPW_BD_STATUS_TX_FRAME_COMMAND |
302762306a36Sopenharmony_ci		    IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci		/* update TBD queue counters */
303062306a36Sopenharmony_ci		txq->next++;
303162306a36Sopenharmony_ci		txq->next %= txq->entries;
303262306a36Sopenharmony_ci		txq->available--;
303362306a36Sopenharmony_ci		DEC_STAT(&priv->txq_stat);
303462306a36Sopenharmony_ci
303562306a36Sopenharmony_ci		list_add_tail(element, &priv->fw_pend_list);
303662306a36Sopenharmony_ci		INC_STAT(&priv->fw_pend_stat);
303762306a36Sopenharmony_ci	}
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ci	if (txq->next != next) {
304062306a36Sopenharmony_ci		/* kick off the DMA by notifying firmware the
304162306a36Sopenharmony_ci		 * write index has moved; make sure TBD stores are sync'd */
304262306a36Sopenharmony_ci		wmb();
304362306a36Sopenharmony_ci		write_register(priv->net_dev,
304462306a36Sopenharmony_ci			       IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
304562306a36Sopenharmony_ci			       txq->next);
304662306a36Sopenharmony_ci	}
304762306a36Sopenharmony_ci}
304862306a36Sopenharmony_ci
304962306a36Sopenharmony_ci/*
305062306a36Sopenharmony_ci * ipw2100_tx_send_data
305162306a36Sopenharmony_ci *
305262306a36Sopenharmony_ci */
305362306a36Sopenharmony_cistatic void ipw2100_tx_send_data(struct ipw2100_priv *priv)
305462306a36Sopenharmony_ci{
305562306a36Sopenharmony_ci	struct list_head *element;
305662306a36Sopenharmony_ci	struct ipw2100_tx_packet *packet;
305762306a36Sopenharmony_ci	struct ipw2100_bd_queue *txq = &priv->tx_queue;
305862306a36Sopenharmony_ci	struct ipw2100_bd *tbd;
305962306a36Sopenharmony_ci	int next = txq->next;
306062306a36Sopenharmony_ci	int i = 0;
306162306a36Sopenharmony_ci	struct ipw2100_data_header *ipw_hdr;
306262306a36Sopenharmony_ci	struct libipw_hdr_3addr *hdr;
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	while (!list_empty(&priv->tx_pend_list)) {
306562306a36Sopenharmony_ci		/* if there isn't enough space in TBD queue, then
306662306a36Sopenharmony_ci		 * don't stuff a new one in.
306762306a36Sopenharmony_ci		 * NOTE: 4 are needed as a data will take two,
306862306a36Sopenharmony_ci		 *       and there is a minimum of 2 that must be
306962306a36Sopenharmony_ci		 *       maintained between the r and w indexes
307062306a36Sopenharmony_ci		 */
307162306a36Sopenharmony_ci		element = priv->tx_pend_list.next;
307262306a36Sopenharmony_ci		packet = list_entry(element, struct ipw2100_tx_packet, list);
307362306a36Sopenharmony_ci
307462306a36Sopenharmony_ci		if (unlikely(1 + packet->info.d_struct.txb->nr_frags >
307562306a36Sopenharmony_ci			     IPW_MAX_BDS)) {
307662306a36Sopenharmony_ci			/* TODO: Support merging buffers if more than
307762306a36Sopenharmony_ci			 * IPW_MAX_BDS are used */
307862306a36Sopenharmony_ci			IPW_DEBUG_INFO("%s: Maximum BD threshold exceeded.  "
307962306a36Sopenharmony_ci				       "Increase fragmentation level.\n",
308062306a36Sopenharmony_ci				       priv->net_dev->name);
308162306a36Sopenharmony_ci		}
308262306a36Sopenharmony_ci
308362306a36Sopenharmony_ci		if (txq->available <= 3 + packet->info.d_struct.txb->nr_frags) {
308462306a36Sopenharmony_ci			IPW_DEBUG_TX("no room in tx_queue\n");
308562306a36Sopenharmony_ci			break;
308662306a36Sopenharmony_ci		}
308762306a36Sopenharmony_ci
308862306a36Sopenharmony_ci		list_del(element);
308962306a36Sopenharmony_ci		DEC_STAT(&priv->tx_pend_stat);
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci		tbd = &txq->drv[txq->next];
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci		packet->index = txq->next;
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci		ipw_hdr = packet->info.d_struct.data;
309662306a36Sopenharmony_ci		hdr = (struct libipw_hdr_3addr *)packet->info.d_struct.txb->
309762306a36Sopenharmony_ci		    fragments[0]->data;
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_ci		if (priv->ieee->iw_mode == IW_MODE_INFRA) {
310062306a36Sopenharmony_ci			/* To DS: Addr1 = BSSID, Addr2 = SA,
310162306a36Sopenharmony_ci			   Addr3 = DA */
310262306a36Sopenharmony_ci			memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
310362306a36Sopenharmony_ci			memcpy(ipw_hdr->dst_addr, hdr->addr3, ETH_ALEN);
310462306a36Sopenharmony_ci		} else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
310562306a36Sopenharmony_ci			/* not From/To DS: Addr1 = DA, Addr2 = SA,
310662306a36Sopenharmony_ci			   Addr3 = BSSID */
310762306a36Sopenharmony_ci			memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
310862306a36Sopenharmony_ci			memcpy(ipw_hdr->dst_addr, hdr->addr1, ETH_ALEN);
310962306a36Sopenharmony_ci		}
311062306a36Sopenharmony_ci
311162306a36Sopenharmony_ci		ipw_hdr->host_command_reg = SEND;
311262306a36Sopenharmony_ci		ipw_hdr->host_command_reg1 = 0;
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci		/* For now we only support host based encryption */
311562306a36Sopenharmony_ci		ipw_hdr->needs_encryption = 0;
311662306a36Sopenharmony_ci		ipw_hdr->encrypted = packet->info.d_struct.txb->encrypted;
311762306a36Sopenharmony_ci		if (packet->info.d_struct.txb->nr_frags > 1)
311862306a36Sopenharmony_ci			ipw_hdr->fragment_size =
311962306a36Sopenharmony_ci			    packet->info.d_struct.txb->frag_size -
312062306a36Sopenharmony_ci			    LIBIPW_3ADDR_LEN;
312162306a36Sopenharmony_ci		else
312262306a36Sopenharmony_ci			ipw_hdr->fragment_size = 0;
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_ci		tbd->host_addr = packet->info.d_struct.data_phys;
312562306a36Sopenharmony_ci		tbd->buf_length = sizeof(struct ipw2100_data_header);
312662306a36Sopenharmony_ci		tbd->num_fragments = 1 + packet->info.d_struct.txb->nr_frags;
312762306a36Sopenharmony_ci		tbd->status.info.field =
312862306a36Sopenharmony_ci		    IPW_BD_STATUS_TX_FRAME_802_3 |
312962306a36Sopenharmony_ci		    IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
313062306a36Sopenharmony_ci		txq->next++;
313162306a36Sopenharmony_ci		txq->next %= txq->entries;
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci		IPW_DEBUG_TX("data header tbd TX%d P=%08x L=%d\n",
313462306a36Sopenharmony_ci			     packet->index, tbd->host_addr, tbd->buf_length);
313562306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
313662306a36Sopenharmony_ci		if (packet->info.d_struct.txb->nr_frags > 1)
313762306a36Sopenharmony_ci			IPW_DEBUG_FRAG("fragment Tx: %d frames\n",
313862306a36Sopenharmony_ci				       packet->info.d_struct.txb->nr_frags);
313962306a36Sopenharmony_ci#endif
314062306a36Sopenharmony_ci
314162306a36Sopenharmony_ci		for (i = 0; i < packet->info.d_struct.txb->nr_frags; i++) {
314262306a36Sopenharmony_ci			tbd = &txq->drv[txq->next];
314362306a36Sopenharmony_ci			if (i == packet->info.d_struct.txb->nr_frags - 1)
314462306a36Sopenharmony_ci				tbd->status.info.field =
314562306a36Sopenharmony_ci				    IPW_BD_STATUS_TX_FRAME_802_3 |
314662306a36Sopenharmony_ci				    IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
314762306a36Sopenharmony_ci			else
314862306a36Sopenharmony_ci				tbd->status.info.field =
314962306a36Sopenharmony_ci				    IPW_BD_STATUS_TX_FRAME_802_3 |
315062306a36Sopenharmony_ci				    IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci			tbd->buf_length = packet->info.d_struct.txb->
315362306a36Sopenharmony_ci			    fragments[i]->len - LIBIPW_3ADDR_LEN;
315462306a36Sopenharmony_ci
315562306a36Sopenharmony_ci			tbd->host_addr = dma_map_single(&priv->pci_dev->dev,
315662306a36Sopenharmony_ci							packet->info.d_struct.
315762306a36Sopenharmony_ci							txb->fragments[i]->data +
315862306a36Sopenharmony_ci							LIBIPW_3ADDR_LEN,
315962306a36Sopenharmony_ci							tbd->buf_length,
316062306a36Sopenharmony_ci							DMA_TO_DEVICE);
316162306a36Sopenharmony_ci			if (dma_mapping_error(&priv->pci_dev->dev, tbd->host_addr)) {
316262306a36Sopenharmony_ci				IPW_DEBUG_TX("dma mapping error\n");
316362306a36Sopenharmony_ci				break;
316462306a36Sopenharmony_ci			}
316562306a36Sopenharmony_ci
316662306a36Sopenharmony_ci			IPW_DEBUG_TX("data frag tbd TX%d P=%08x L=%d\n",
316762306a36Sopenharmony_ci				     txq->next, tbd->host_addr,
316862306a36Sopenharmony_ci				     tbd->buf_length);
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci			dma_sync_single_for_device(&priv->pci_dev->dev,
317162306a36Sopenharmony_ci						   tbd->host_addr,
317262306a36Sopenharmony_ci						   tbd->buf_length,
317362306a36Sopenharmony_ci						   DMA_TO_DEVICE);
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci			txq->next++;
317662306a36Sopenharmony_ci			txq->next %= txq->entries;
317762306a36Sopenharmony_ci		}
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_ci		txq->available -= 1 + packet->info.d_struct.txb->nr_frags;
318062306a36Sopenharmony_ci		SET_STAT(&priv->txq_stat, txq->available);
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ci		list_add_tail(element, &priv->fw_pend_list);
318362306a36Sopenharmony_ci		INC_STAT(&priv->fw_pend_stat);
318462306a36Sopenharmony_ci	}
318562306a36Sopenharmony_ci
318662306a36Sopenharmony_ci	if (txq->next != next) {
318762306a36Sopenharmony_ci		/* kick off the DMA by notifying firmware the
318862306a36Sopenharmony_ci		 * write index has moved; make sure TBD stores are sync'd */
318962306a36Sopenharmony_ci		write_register(priv->net_dev,
319062306a36Sopenharmony_ci			       IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
319162306a36Sopenharmony_ci			       txq->next);
319262306a36Sopenharmony_ci	}
319362306a36Sopenharmony_ci}
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_cistatic void ipw2100_irq_tasklet(struct tasklet_struct *t)
319662306a36Sopenharmony_ci{
319762306a36Sopenharmony_ci	struct ipw2100_priv *priv = from_tasklet(priv, t, irq_tasklet);
319862306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
319962306a36Sopenharmony_ci	unsigned long flags;
320062306a36Sopenharmony_ci	u32 inta, tmp;
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci	spin_lock_irqsave(&priv->low_lock, flags);
320362306a36Sopenharmony_ci	ipw2100_disable_interrupts(priv);
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci	read_register(dev, IPW_REG_INTA, &inta);
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ci	IPW_DEBUG_ISR("enter - INTA: 0x%08lX\n",
320862306a36Sopenharmony_ci		      (unsigned long)inta & IPW_INTERRUPT_MASK);
320962306a36Sopenharmony_ci
321062306a36Sopenharmony_ci	priv->in_isr++;
321162306a36Sopenharmony_ci	priv->interrupts++;
321262306a36Sopenharmony_ci
321362306a36Sopenharmony_ci	/* We do not loop and keep polling for more interrupts as this
321462306a36Sopenharmony_ci	 * is frowned upon and doesn't play nicely with other potentially
321562306a36Sopenharmony_ci	 * chained IRQs */
321662306a36Sopenharmony_ci	IPW_DEBUG_ISR("INTA: 0x%08lX\n",
321762306a36Sopenharmony_ci		      (unsigned long)inta & IPW_INTERRUPT_MASK);
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci	if (inta & IPW2100_INTA_FATAL_ERROR) {
322062306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
322162306a36Sopenharmony_ci		       ": Fatal interrupt. Scheduling firmware restart.\n");
322262306a36Sopenharmony_ci		priv->inta_other++;
322362306a36Sopenharmony_ci		write_register(dev, IPW_REG_INTA, IPW2100_INTA_FATAL_ERROR);
322462306a36Sopenharmony_ci
322562306a36Sopenharmony_ci		read_nic_dword(dev, IPW_NIC_FATAL_ERROR, &priv->fatal_error);
322662306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: Fatal error value: 0x%08X\n",
322762306a36Sopenharmony_ci			       priv->net_dev->name, priv->fatal_error);
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_ci		read_nic_dword(dev, IPW_ERROR_ADDR(priv->fatal_error), &tmp);
323062306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: Fatal error address value: 0x%08X\n",
323162306a36Sopenharmony_ci			       priv->net_dev->name, tmp);
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci		/* Wake up any sleeping jobs */
323462306a36Sopenharmony_ci		schedule_reset(priv);
323562306a36Sopenharmony_ci	}
323662306a36Sopenharmony_ci
323762306a36Sopenharmony_ci	if (inta & IPW2100_INTA_PARITY_ERROR) {
323862306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME
323962306a36Sopenharmony_ci		       ": ***** PARITY ERROR INTERRUPT !!!!\n");
324062306a36Sopenharmony_ci		priv->inta_other++;
324162306a36Sopenharmony_ci		write_register(dev, IPW_REG_INTA, IPW2100_INTA_PARITY_ERROR);
324262306a36Sopenharmony_ci	}
324362306a36Sopenharmony_ci
324462306a36Sopenharmony_ci	if (inta & IPW2100_INTA_RX_TRANSFER) {
324562306a36Sopenharmony_ci		IPW_DEBUG_ISR("RX interrupt\n");
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci		priv->rx_interrupts++;
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_ci		write_register(dev, IPW_REG_INTA, IPW2100_INTA_RX_TRANSFER);
325062306a36Sopenharmony_ci
325162306a36Sopenharmony_ci		__ipw2100_rx_process(priv);
325262306a36Sopenharmony_ci		__ipw2100_tx_complete(priv);
325362306a36Sopenharmony_ci	}
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci	if (inta & IPW2100_INTA_TX_TRANSFER) {
325662306a36Sopenharmony_ci		IPW_DEBUG_ISR("TX interrupt\n");
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci		priv->tx_interrupts++;
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci		write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_TRANSFER);
326162306a36Sopenharmony_ci
326262306a36Sopenharmony_ci		__ipw2100_tx_complete(priv);
326362306a36Sopenharmony_ci		ipw2100_tx_send_commands(priv);
326462306a36Sopenharmony_ci		ipw2100_tx_send_data(priv);
326562306a36Sopenharmony_ci	}
326662306a36Sopenharmony_ci
326762306a36Sopenharmony_ci	if (inta & IPW2100_INTA_TX_COMPLETE) {
326862306a36Sopenharmony_ci		IPW_DEBUG_ISR("TX complete\n");
326962306a36Sopenharmony_ci		priv->inta_other++;
327062306a36Sopenharmony_ci		write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_COMPLETE);
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci		__ipw2100_tx_complete(priv);
327362306a36Sopenharmony_ci	}
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_ci	if (inta & IPW2100_INTA_EVENT_INTERRUPT) {
327662306a36Sopenharmony_ci		/* ipw2100_handle_event(dev); */
327762306a36Sopenharmony_ci		priv->inta_other++;
327862306a36Sopenharmony_ci		write_register(dev, IPW_REG_INTA, IPW2100_INTA_EVENT_INTERRUPT);
327962306a36Sopenharmony_ci	}
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ci	if (inta & IPW2100_INTA_FW_INIT_DONE) {
328262306a36Sopenharmony_ci		IPW_DEBUG_ISR("FW init done interrupt\n");
328362306a36Sopenharmony_ci		priv->inta_other++;
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_ci		read_register(dev, IPW_REG_INTA, &tmp);
328662306a36Sopenharmony_ci		if (tmp & (IPW2100_INTA_FATAL_ERROR |
328762306a36Sopenharmony_ci			   IPW2100_INTA_PARITY_ERROR)) {
328862306a36Sopenharmony_ci			write_register(dev, IPW_REG_INTA,
328962306a36Sopenharmony_ci				       IPW2100_INTA_FATAL_ERROR |
329062306a36Sopenharmony_ci				       IPW2100_INTA_PARITY_ERROR);
329162306a36Sopenharmony_ci		}
329262306a36Sopenharmony_ci
329362306a36Sopenharmony_ci		write_register(dev, IPW_REG_INTA, IPW2100_INTA_FW_INIT_DONE);
329462306a36Sopenharmony_ci	}
329562306a36Sopenharmony_ci
329662306a36Sopenharmony_ci	if (inta & IPW2100_INTA_STATUS_CHANGE) {
329762306a36Sopenharmony_ci		IPW_DEBUG_ISR("Status change interrupt\n");
329862306a36Sopenharmony_ci		priv->inta_other++;
329962306a36Sopenharmony_ci		write_register(dev, IPW_REG_INTA, IPW2100_INTA_STATUS_CHANGE);
330062306a36Sopenharmony_ci	}
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci	if (inta & IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE) {
330362306a36Sopenharmony_ci		IPW_DEBUG_ISR("slave host mode interrupt\n");
330462306a36Sopenharmony_ci		priv->inta_other++;
330562306a36Sopenharmony_ci		write_register(dev, IPW_REG_INTA,
330662306a36Sopenharmony_ci			       IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE);
330762306a36Sopenharmony_ci	}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci	priv->in_isr--;
331062306a36Sopenharmony_ci	ipw2100_enable_interrupts(priv);
331162306a36Sopenharmony_ci
331262306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
331362306a36Sopenharmony_ci
331462306a36Sopenharmony_ci	IPW_DEBUG_ISR("exit\n");
331562306a36Sopenharmony_ci}
331662306a36Sopenharmony_ci
331762306a36Sopenharmony_cistatic irqreturn_t ipw2100_interrupt(int irq, void *data)
331862306a36Sopenharmony_ci{
331962306a36Sopenharmony_ci	struct ipw2100_priv *priv = data;
332062306a36Sopenharmony_ci	u32 inta, inta_mask;
332162306a36Sopenharmony_ci
332262306a36Sopenharmony_ci	if (!data)
332362306a36Sopenharmony_ci		return IRQ_NONE;
332462306a36Sopenharmony_ci
332562306a36Sopenharmony_ci	spin_lock(&priv->low_lock);
332662306a36Sopenharmony_ci
332762306a36Sopenharmony_ci	/* We check to see if we should be ignoring interrupts before
332862306a36Sopenharmony_ci	 * we touch the hardware.  During ucode load if we try and handle
332962306a36Sopenharmony_ci	 * an interrupt we can cause keyboard problems as well as cause
333062306a36Sopenharmony_ci	 * the ucode to fail to initialize */
333162306a36Sopenharmony_ci	if (!(priv->status & STATUS_INT_ENABLED)) {
333262306a36Sopenharmony_ci		/* Shared IRQ */
333362306a36Sopenharmony_ci		goto none;
333462306a36Sopenharmony_ci	}
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
333762306a36Sopenharmony_ci	read_register(priv->net_dev, IPW_REG_INTA, &inta);
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci	if (inta == 0xFFFFFFFF) {
334062306a36Sopenharmony_ci		/* Hardware disappeared */
334162306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME ": IRQ INTA == 0xFFFFFFFF\n");
334262306a36Sopenharmony_ci		goto none;
334362306a36Sopenharmony_ci	}
334462306a36Sopenharmony_ci
334562306a36Sopenharmony_ci	inta &= IPW_INTERRUPT_MASK;
334662306a36Sopenharmony_ci
334762306a36Sopenharmony_ci	if (!(inta & inta_mask)) {
334862306a36Sopenharmony_ci		/* Shared interrupt */
334962306a36Sopenharmony_ci		goto none;
335062306a36Sopenharmony_ci	}
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci	/* We disable the hardware interrupt here just to prevent unneeded
335362306a36Sopenharmony_ci	 * calls to be made.  We disable this again within the actual
335462306a36Sopenharmony_ci	 * work tasklet, so if another part of the code re-enables the
335562306a36Sopenharmony_ci	 * interrupt, that is fine */
335662306a36Sopenharmony_ci	ipw2100_disable_interrupts(priv);
335762306a36Sopenharmony_ci
335862306a36Sopenharmony_ci	tasklet_schedule(&priv->irq_tasklet);
335962306a36Sopenharmony_ci	spin_unlock(&priv->low_lock);
336062306a36Sopenharmony_ci
336162306a36Sopenharmony_ci	return IRQ_HANDLED;
336262306a36Sopenharmony_ci      none:
336362306a36Sopenharmony_ci	spin_unlock(&priv->low_lock);
336462306a36Sopenharmony_ci	return IRQ_NONE;
336562306a36Sopenharmony_ci}
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_cistatic netdev_tx_t ipw2100_tx(struct libipw_txb *txb,
336862306a36Sopenharmony_ci			      struct net_device *dev, int pri)
336962306a36Sopenharmony_ci{
337062306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
337162306a36Sopenharmony_ci	struct list_head *element;
337262306a36Sopenharmony_ci	struct ipw2100_tx_packet *packet;
337362306a36Sopenharmony_ci	unsigned long flags;
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_ci	spin_lock_irqsave(&priv->low_lock, flags);
337662306a36Sopenharmony_ci
337762306a36Sopenharmony_ci	if (!(priv->status & STATUS_ASSOCIATED)) {
337862306a36Sopenharmony_ci		IPW_DEBUG_INFO("Can not transmit when not connected.\n");
337962306a36Sopenharmony_ci		priv->net_dev->stats.tx_carrier_errors++;
338062306a36Sopenharmony_ci		netif_stop_queue(dev);
338162306a36Sopenharmony_ci		goto fail_unlock;
338262306a36Sopenharmony_ci	}
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci	if (list_empty(&priv->tx_free_list))
338562306a36Sopenharmony_ci		goto fail_unlock;
338662306a36Sopenharmony_ci
338762306a36Sopenharmony_ci	element = priv->tx_free_list.next;
338862306a36Sopenharmony_ci	packet = list_entry(element, struct ipw2100_tx_packet, list);
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_ci	packet->info.d_struct.txb = txb;
339162306a36Sopenharmony_ci
339262306a36Sopenharmony_ci	IPW_DEBUG_TX("Sending fragment (%d bytes):\n", txb->fragments[0]->len);
339362306a36Sopenharmony_ci	printk_buf(IPW_DL_TX, txb->fragments[0]->data, txb->fragments[0]->len);
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci	packet->jiffy_start = jiffies;
339662306a36Sopenharmony_ci
339762306a36Sopenharmony_ci	list_del(element);
339862306a36Sopenharmony_ci	DEC_STAT(&priv->tx_free_stat);
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ci	list_add_tail(element, &priv->tx_pend_list);
340162306a36Sopenharmony_ci	INC_STAT(&priv->tx_pend_stat);
340262306a36Sopenharmony_ci
340362306a36Sopenharmony_ci	ipw2100_tx_send_data(priv);
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
340662306a36Sopenharmony_ci	return NETDEV_TX_OK;
340762306a36Sopenharmony_ci
340862306a36Sopenharmony_cifail_unlock:
340962306a36Sopenharmony_ci	netif_stop_queue(dev);
341062306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
341162306a36Sopenharmony_ci	return NETDEV_TX_BUSY;
341262306a36Sopenharmony_ci}
341362306a36Sopenharmony_ci
341462306a36Sopenharmony_cistatic int ipw2100_msg_allocate(struct ipw2100_priv *priv)
341562306a36Sopenharmony_ci{
341662306a36Sopenharmony_ci	int i, j, err = -EINVAL;
341762306a36Sopenharmony_ci	void *v;
341862306a36Sopenharmony_ci	dma_addr_t p;
341962306a36Sopenharmony_ci
342062306a36Sopenharmony_ci	priv->msg_buffers =
342162306a36Sopenharmony_ci	    kmalloc_array(IPW_COMMAND_POOL_SIZE,
342262306a36Sopenharmony_ci			  sizeof(struct ipw2100_tx_packet),
342362306a36Sopenharmony_ci			  GFP_KERNEL);
342462306a36Sopenharmony_ci	if (!priv->msg_buffers)
342562306a36Sopenharmony_ci		return -ENOMEM;
342662306a36Sopenharmony_ci
342762306a36Sopenharmony_ci	for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
342862306a36Sopenharmony_ci		v = dma_alloc_coherent(&priv->pci_dev->dev,
342962306a36Sopenharmony_ci				       sizeof(struct ipw2100_cmd_header), &p,
343062306a36Sopenharmony_ci				       GFP_KERNEL);
343162306a36Sopenharmony_ci		if (!v) {
343262306a36Sopenharmony_ci			printk(KERN_ERR DRV_NAME ": "
343362306a36Sopenharmony_ci			       "%s: PCI alloc failed for msg "
343462306a36Sopenharmony_ci			       "buffers.\n", priv->net_dev->name);
343562306a36Sopenharmony_ci			err = -ENOMEM;
343662306a36Sopenharmony_ci			break;
343762306a36Sopenharmony_ci		}
343862306a36Sopenharmony_ci
343962306a36Sopenharmony_ci		priv->msg_buffers[i].type = COMMAND;
344062306a36Sopenharmony_ci		priv->msg_buffers[i].info.c_struct.cmd =
344162306a36Sopenharmony_ci		    (struct ipw2100_cmd_header *)v;
344262306a36Sopenharmony_ci		priv->msg_buffers[i].info.c_struct.cmd_phys = p;
344362306a36Sopenharmony_ci	}
344462306a36Sopenharmony_ci
344562306a36Sopenharmony_ci	if (i == IPW_COMMAND_POOL_SIZE)
344662306a36Sopenharmony_ci		return 0;
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_ci	for (j = 0; j < i; j++) {
344962306a36Sopenharmony_ci		dma_free_coherent(&priv->pci_dev->dev,
345062306a36Sopenharmony_ci				  sizeof(struct ipw2100_cmd_header),
345162306a36Sopenharmony_ci				  priv->msg_buffers[j].info.c_struct.cmd,
345262306a36Sopenharmony_ci				  priv->msg_buffers[j].info.c_struct.cmd_phys);
345362306a36Sopenharmony_ci	}
345462306a36Sopenharmony_ci
345562306a36Sopenharmony_ci	kfree(priv->msg_buffers);
345662306a36Sopenharmony_ci	priv->msg_buffers = NULL;
345762306a36Sopenharmony_ci
345862306a36Sopenharmony_ci	return err;
345962306a36Sopenharmony_ci}
346062306a36Sopenharmony_ci
346162306a36Sopenharmony_cistatic int ipw2100_msg_initialize(struct ipw2100_priv *priv)
346262306a36Sopenharmony_ci{
346362306a36Sopenharmony_ci	int i;
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->msg_free_list);
346662306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->msg_pend_list);
346762306a36Sopenharmony_ci
346862306a36Sopenharmony_ci	for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++)
346962306a36Sopenharmony_ci		list_add_tail(&priv->msg_buffers[i].list, &priv->msg_free_list);
347062306a36Sopenharmony_ci	SET_STAT(&priv->msg_free_stat, i);
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci	return 0;
347362306a36Sopenharmony_ci}
347462306a36Sopenharmony_ci
347562306a36Sopenharmony_cistatic void ipw2100_msg_free(struct ipw2100_priv *priv)
347662306a36Sopenharmony_ci{
347762306a36Sopenharmony_ci	int i;
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci	if (!priv->msg_buffers)
348062306a36Sopenharmony_ci		return;
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci	for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
348362306a36Sopenharmony_ci		dma_free_coherent(&priv->pci_dev->dev,
348462306a36Sopenharmony_ci				  sizeof(struct ipw2100_cmd_header),
348562306a36Sopenharmony_ci				  priv->msg_buffers[i].info.c_struct.cmd,
348662306a36Sopenharmony_ci				  priv->msg_buffers[i].info.c_struct.cmd_phys);
348762306a36Sopenharmony_ci	}
348862306a36Sopenharmony_ci
348962306a36Sopenharmony_ci	kfree(priv->msg_buffers);
349062306a36Sopenharmony_ci	priv->msg_buffers = NULL;
349162306a36Sopenharmony_ci}
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_cistatic ssize_t pci_show(struct device *d, struct device_attribute *attr,
349462306a36Sopenharmony_ci			char *buf)
349562306a36Sopenharmony_ci{
349662306a36Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(d);
349762306a36Sopenharmony_ci	char *out = buf;
349862306a36Sopenharmony_ci	int i, j;
349962306a36Sopenharmony_ci	u32 val;
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci	for (i = 0; i < 16; i++) {
350262306a36Sopenharmony_ci		out += sprintf(out, "[%08X] ", i * 16);
350362306a36Sopenharmony_ci		for (j = 0; j < 16; j += 4) {
350462306a36Sopenharmony_ci			pci_read_config_dword(pci_dev, i * 16 + j, &val);
350562306a36Sopenharmony_ci			out += sprintf(out, "%08X ", val);
350662306a36Sopenharmony_ci		}
350762306a36Sopenharmony_ci		out += sprintf(out, "\n");
350862306a36Sopenharmony_ci	}
350962306a36Sopenharmony_ci
351062306a36Sopenharmony_ci	return out - buf;
351162306a36Sopenharmony_ci}
351262306a36Sopenharmony_ci
351362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(pci);
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_cistatic ssize_t cfg_show(struct device *d, struct device_attribute *attr,
351662306a36Sopenharmony_ci			char *buf)
351762306a36Sopenharmony_ci{
351862306a36Sopenharmony_ci	struct ipw2100_priv *p = dev_get_drvdata(d);
351962306a36Sopenharmony_ci	return sprintf(buf, "0x%08x\n", (int)p->config);
352062306a36Sopenharmony_ci}
352162306a36Sopenharmony_ci
352262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(cfg);
352362306a36Sopenharmony_ci
352462306a36Sopenharmony_cistatic ssize_t status_show(struct device *d, struct device_attribute *attr,
352562306a36Sopenharmony_ci			   char *buf)
352662306a36Sopenharmony_ci{
352762306a36Sopenharmony_ci	struct ipw2100_priv *p = dev_get_drvdata(d);
352862306a36Sopenharmony_ci	return sprintf(buf, "0x%08x\n", (int)p->status);
352962306a36Sopenharmony_ci}
353062306a36Sopenharmony_ci
353162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(status);
353262306a36Sopenharmony_ci
353362306a36Sopenharmony_cistatic ssize_t capability_show(struct device *d, struct device_attribute *attr,
353462306a36Sopenharmony_ci			       char *buf)
353562306a36Sopenharmony_ci{
353662306a36Sopenharmony_ci	struct ipw2100_priv *p = dev_get_drvdata(d);
353762306a36Sopenharmony_ci	return sprintf(buf, "0x%08x\n", (int)p->capability);
353862306a36Sopenharmony_ci}
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(capability);
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci#define IPW2100_REG(x) { IPW_ ##x, #x }
354362306a36Sopenharmony_cistatic const struct {
354462306a36Sopenharmony_ci	u32 addr;
354562306a36Sopenharmony_ci	const char *name;
354662306a36Sopenharmony_ci} hw_data[] = {
354762306a36Sopenharmony_ciIPW2100_REG(REG_GP_CNTRL),
354862306a36Sopenharmony_ci	    IPW2100_REG(REG_GPIO),
354962306a36Sopenharmony_ci	    IPW2100_REG(REG_INTA),
355062306a36Sopenharmony_ci	    IPW2100_REG(REG_INTA_MASK), IPW2100_REG(REG_RESET_REG),};
355162306a36Sopenharmony_ci#define IPW2100_NIC(x, s) { x, #x, s }
355262306a36Sopenharmony_cistatic const struct {
355362306a36Sopenharmony_ci	u32 addr;
355462306a36Sopenharmony_ci	const char *name;
355562306a36Sopenharmony_ci	size_t size;
355662306a36Sopenharmony_ci} nic_data[] = {
355762306a36Sopenharmony_ciIPW2100_NIC(IPW2100_CONTROL_REG, 2),
355862306a36Sopenharmony_ci	    IPW2100_NIC(0x210014, 1), IPW2100_NIC(0x210000, 1),};
355962306a36Sopenharmony_ci#define IPW2100_ORD(x, d) { IPW_ORD_ ##x, #x, d }
356062306a36Sopenharmony_cistatic const struct {
356162306a36Sopenharmony_ci	u8 index;
356262306a36Sopenharmony_ci	const char *name;
356362306a36Sopenharmony_ci	const char *desc;
356462306a36Sopenharmony_ci} ord_data[] = {
356562306a36Sopenharmony_ciIPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"),
356662306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_HOST_COMPLETE,
356762306a36Sopenharmony_ci				"successful Host Tx's (MSDU)"),
356862306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_DIR_DATA,
356962306a36Sopenharmony_ci				"successful Directed Tx's (MSDU)"),
357062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_DIR_DATA1,
357162306a36Sopenharmony_ci				"successful Directed Tx's (MSDU) @ 1MB"),
357262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_DIR_DATA2,
357362306a36Sopenharmony_ci				"successful Directed Tx's (MSDU) @ 2MB"),
357462306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_DIR_DATA5_5,
357562306a36Sopenharmony_ci				"successful Directed Tx's (MSDU) @ 5_5MB"),
357662306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_DIR_DATA11,
357762306a36Sopenharmony_ci				"successful Directed Tx's (MSDU) @ 11MB"),
357862306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_NODIR_DATA1,
357962306a36Sopenharmony_ci				"successful Non_Directed Tx's (MSDU) @ 1MB"),
358062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_NODIR_DATA2,
358162306a36Sopenharmony_ci				"successful Non_Directed Tx's (MSDU) @ 2MB"),
358262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_NODIR_DATA5_5,
358362306a36Sopenharmony_ci				"successful Non_Directed Tx's (MSDU) @ 5.5MB"),
358462306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_NODIR_DATA11,
358562306a36Sopenharmony_ci				"successful Non_Directed Tx's (MSDU) @ 11MB"),
358662306a36Sopenharmony_ci	    IPW2100_ORD(STAT_NULL_DATA, "successful NULL data Tx's"),
358762306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_RTS, "successful Tx RTS"),
358862306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_CTS, "successful Tx CTS"),
358962306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_ACK, "successful Tx ACK"),
359062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_ASSN, "successful Association Tx's"),
359162306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_ASSN_RESP,
359262306a36Sopenharmony_ci				"successful Association response Tx's"),
359362306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_REASSN,
359462306a36Sopenharmony_ci				"successful Reassociation Tx's"),
359562306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_REASSN_RESP,
359662306a36Sopenharmony_ci				"successful Reassociation response Tx's"),
359762306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_PROBE,
359862306a36Sopenharmony_ci				"probes successfully transmitted"),
359962306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_PROBE_RESP,
360062306a36Sopenharmony_ci				"probe responses successfully transmitted"),
360162306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_BEACON, "tx beacon"),
360262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_ATIM, "Tx ATIM"),
360362306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_DISASSN,
360462306a36Sopenharmony_ci				"successful Disassociation TX"),
360562306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_AUTH, "successful Authentication Tx"),
360662306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_DEAUTH,
360762306a36Sopenharmony_ci				"successful Deauthentication TX"),
360862306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_TOTAL_BYTES,
360962306a36Sopenharmony_ci				"Total successful Tx data bytes"),
361062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_RETRIES, "Tx retries"),
361162306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_RETRY1, "Tx retries at 1MBPS"),
361262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_RETRY2, "Tx retries at 2MBPS"),
361362306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_RETRY5_5, "Tx retries at 5.5MBPS"),
361462306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_RETRY11, "Tx retries at 11MBPS"),
361562306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_FAILURES, "Tx Failures"),
361662306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_MAX_TRIES_IN_HOP,
361762306a36Sopenharmony_ci				"times max tries in a hop failed"),
361862306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_DISASSN_FAIL,
361962306a36Sopenharmony_ci				"times disassociation failed"),
362062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_ERR_CTS, "missed/bad CTS frames"),
362162306a36Sopenharmony_ci	    IPW2100_ORD(STAT_TX_ERR_ACK, "tx err due to acks"),
362262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_HOST, "packets passed to host"),
362362306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DIR_DATA, "directed packets"),
362462306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DIR_DATA1, "directed packets at 1MB"),
362562306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DIR_DATA2, "directed packets at 2MB"),
362662306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DIR_DATA5_5,
362762306a36Sopenharmony_ci				"directed packets at 5.5MB"),
362862306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DIR_DATA11, "directed packets at 11MB"),
362962306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_NODIR_DATA, "nondirected packets"),
363062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_NODIR_DATA1,
363162306a36Sopenharmony_ci				"nondirected packets at 1MB"),
363262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_NODIR_DATA2,
363362306a36Sopenharmony_ci				"nondirected packets at 2MB"),
363462306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_NODIR_DATA5_5,
363562306a36Sopenharmony_ci				"nondirected packets at 5.5MB"),
363662306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_NODIR_DATA11,
363762306a36Sopenharmony_ci				"nondirected packets at 11MB"),
363862306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_NULL_DATA, "null data rx's"),
363962306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_RTS, "Rx RTS"), IPW2100_ORD(STAT_RX_CTS,
364062306a36Sopenharmony_ci								    "Rx CTS"),
364162306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ACK, "Rx ACK"),
364262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_CFEND, "Rx CF End"),
364362306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_CFEND_ACK, "Rx CF End + CF Ack"),
364462306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ASSN, "Association Rx's"),
364562306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ASSN_RESP, "Association response Rx's"),
364662306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_REASSN, "Reassociation Rx's"),
364762306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_REASSN_RESP,
364862306a36Sopenharmony_ci				"Reassociation response Rx's"),
364962306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_PROBE, "probe Rx's"),
365062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_PROBE_RESP, "probe response Rx's"),
365162306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_BEACON, "Rx beacon"),
365262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ATIM, "Rx ATIM"),
365362306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DISASSN, "disassociation Rx"),
365462306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_AUTH, "authentication Rx"),
365562306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DEAUTH, "deauthentication Rx"),
365662306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_TOTAL_BYTES,
365762306a36Sopenharmony_ci				"Total rx data bytes received"),
365862306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ERR_CRC, "packets with Rx CRC error"),
365962306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ERR_CRC1, "Rx CRC errors at 1MB"),
366062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ERR_CRC2, "Rx CRC errors at 2MB"),
366162306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ERR_CRC5_5, "Rx CRC errors at 5.5MB"),
366262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ERR_CRC11, "Rx CRC errors at 11MB"),
366362306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DUPLICATE1,
366462306a36Sopenharmony_ci				"duplicate rx packets at 1MB"),
366562306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DUPLICATE2,
366662306a36Sopenharmony_ci				"duplicate rx packets at 2MB"),
366762306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DUPLICATE5_5,
366862306a36Sopenharmony_ci				"duplicate rx packets at 5.5MB"),
366962306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DUPLICATE11,
367062306a36Sopenharmony_ci				"duplicate rx packets at 11MB"),
367162306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_DUPLICATE, "duplicate rx packets"),
367262306a36Sopenharmony_ci	    IPW2100_ORD(PERS_DB_LOCK, "locking fw permanent  db"),
367362306a36Sopenharmony_ci	    IPW2100_ORD(PERS_DB_SIZE, "size of fw permanent  db"),
367462306a36Sopenharmony_ci	    IPW2100_ORD(PERS_DB_ADDR, "address of fw permanent  db"),
367562306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_INVALID_PROTOCOL,
367662306a36Sopenharmony_ci				"rx frames with invalid protocol"),
367762306a36Sopenharmony_ci	    IPW2100_ORD(SYS_BOOT_TIME, "Boot time"),
367862306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_NO_BUFFER,
367962306a36Sopenharmony_ci				"rx frames rejected due to no buffer"),
368062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_MISSING_FRAG,
368162306a36Sopenharmony_ci				"rx frames dropped due to missing fragment"),
368262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ORPHAN_FRAG,
368362306a36Sopenharmony_ci				"rx frames dropped due to non-sequential fragment"),
368462306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ORPHAN_FRAME,
368562306a36Sopenharmony_ci				"rx frames dropped due to unmatched 1st frame"),
368662306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_FRAG_AGEOUT,
368762306a36Sopenharmony_ci				"rx frames dropped due to uncompleted frame"),
368862306a36Sopenharmony_ci	    IPW2100_ORD(STAT_RX_ICV_ERRORS,
368962306a36Sopenharmony_ci				"ICV errors during decryption"),
369062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_PSP_SUSPENSION, "times adapter suspended"),
369162306a36Sopenharmony_ci	    IPW2100_ORD(STAT_PSP_BCN_TIMEOUT, "beacon timeout"),
369262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_PSP_POLL_TIMEOUT,
369362306a36Sopenharmony_ci				"poll response timeouts"),
369462306a36Sopenharmony_ci	    IPW2100_ORD(STAT_PSP_NONDIR_TIMEOUT,
369562306a36Sopenharmony_ci				"timeouts waiting for last {broad,multi}cast pkt"),
369662306a36Sopenharmony_ci	    IPW2100_ORD(STAT_PSP_RX_DTIMS, "PSP DTIMs received"),
369762306a36Sopenharmony_ci	    IPW2100_ORD(STAT_PSP_RX_TIMS, "PSP TIMs received"),
369862306a36Sopenharmony_ci	    IPW2100_ORD(STAT_PSP_STATION_ID, "PSP Station ID"),
369962306a36Sopenharmony_ci	    IPW2100_ORD(LAST_ASSN_TIME, "RTC time of last association"),
370062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_PERCENT_MISSED_BCNS,
370162306a36Sopenharmony_ci				"current calculation of % missed beacons"),
370262306a36Sopenharmony_ci	    IPW2100_ORD(STAT_PERCENT_RETRIES,
370362306a36Sopenharmony_ci				"current calculation of % missed tx retries"),
370462306a36Sopenharmony_ci	    IPW2100_ORD(ASSOCIATED_AP_PTR,
370562306a36Sopenharmony_ci				"0 if not associated, else pointer to AP table entry"),
370662306a36Sopenharmony_ci	    IPW2100_ORD(AVAILABLE_AP_CNT,
370762306a36Sopenharmony_ci				"AP's described in the AP table"),
370862306a36Sopenharmony_ci	    IPW2100_ORD(AP_LIST_PTR, "Ptr to list of available APs"),
370962306a36Sopenharmony_ci	    IPW2100_ORD(STAT_AP_ASSNS, "associations"),
371062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_ASSN_FAIL, "association failures"),
371162306a36Sopenharmony_ci	    IPW2100_ORD(STAT_ASSN_RESP_FAIL,
371262306a36Sopenharmony_ci				"failures due to response fail"),
371362306a36Sopenharmony_ci	    IPW2100_ORD(STAT_FULL_SCANS, "full scans"),
371462306a36Sopenharmony_ci	    IPW2100_ORD(CARD_DISABLED, "Card Disabled"),
371562306a36Sopenharmony_ci	    IPW2100_ORD(STAT_ROAM_INHIBIT,
371662306a36Sopenharmony_ci				"times roaming was inhibited due to activity"),
371762306a36Sopenharmony_ci	    IPW2100_ORD(RSSI_AT_ASSN,
371862306a36Sopenharmony_ci				"RSSI of associated AP at time of association"),
371962306a36Sopenharmony_ci	    IPW2100_ORD(STAT_ASSN_CAUSE1,
372062306a36Sopenharmony_ci				"reassociation: no probe response or TX on hop"),
372162306a36Sopenharmony_ci	    IPW2100_ORD(STAT_ASSN_CAUSE2,
372262306a36Sopenharmony_ci				"reassociation: poor tx/rx quality"),
372362306a36Sopenharmony_ci	    IPW2100_ORD(STAT_ASSN_CAUSE3,
372462306a36Sopenharmony_ci				"reassociation: tx/rx quality (excessive AP load"),
372562306a36Sopenharmony_ci	    IPW2100_ORD(STAT_ASSN_CAUSE4,
372662306a36Sopenharmony_ci				"reassociation: AP RSSI level"),
372762306a36Sopenharmony_ci	    IPW2100_ORD(STAT_ASSN_CAUSE5,
372862306a36Sopenharmony_ci				"reassociations due to load leveling"),
372962306a36Sopenharmony_ci	    IPW2100_ORD(STAT_AUTH_FAIL, "times authentication failed"),
373062306a36Sopenharmony_ci	    IPW2100_ORD(STAT_AUTH_RESP_FAIL,
373162306a36Sopenharmony_ci				"times authentication response failed"),
373262306a36Sopenharmony_ci	    IPW2100_ORD(STATION_TABLE_CNT,
373362306a36Sopenharmony_ci				"entries in association table"),
373462306a36Sopenharmony_ci	    IPW2100_ORD(RSSI_AVG_CURR, "Current avg RSSI"),
373562306a36Sopenharmony_ci	    IPW2100_ORD(POWER_MGMT_MODE, "Power mode - 0=CAM, 1=PSP"),
373662306a36Sopenharmony_ci	    IPW2100_ORD(COUNTRY_CODE,
373762306a36Sopenharmony_ci				"IEEE country code as recv'd from beacon"),
373862306a36Sopenharmony_ci	    IPW2100_ORD(COUNTRY_CHANNELS,
373962306a36Sopenharmony_ci				"channels supported by country"),
374062306a36Sopenharmony_ci	    IPW2100_ORD(RESET_CNT, "adapter resets (warm)"),
374162306a36Sopenharmony_ci	    IPW2100_ORD(BEACON_INTERVAL, "Beacon interval"),
374262306a36Sopenharmony_ci	    IPW2100_ORD(ANTENNA_DIVERSITY,
374362306a36Sopenharmony_ci				"TRUE if antenna diversity is disabled"),
374462306a36Sopenharmony_ci	    IPW2100_ORD(DTIM_PERIOD, "beacon intervals between DTIMs"),
374562306a36Sopenharmony_ci	    IPW2100_ORD(OUR_FREQ,
374662306a36Sopenharmony_ci				"current radio freq lower digits - channel ID"),
374762306a36Sopenharmony_ci	    IPW2100_ORD(RTC_TIME, "current RTC time"),
374862306a36Sopenharmony_ci	    IPW2100_ORD(PORT_TYPE, "operating mode"),
374962306a36Sopenharmony_ci	    IPW2100_ORD(CURRENT_TX_RATE, "current tx rate"),
375062306a36Sopenharmony_ci	    IPW2100_ORD(SUPPORTED_RATES, "supported tx rates"),
375162306a36Sopenharmony_ci	    IPW2100_ORD(ATIM_WINDOW, "current ATIM Window"),
375262306a36Sopenharmony_ci	    IPW2100_ORD(BASIC_RATES, "basic tx rates"),
375362306a36Sopenharmony_ci	    IPW2100_ORD(NIC_HIGHEST_RATE, "NIC highest tx rate"),
375462306a36Sopenharmony_ci	    IPW2100_ORD(AP_HIGHEST_RATE, "AP highest tx rate"),
375562306a36Sopenharmony_ci	    IPW2100_ORD(CAPABILITIES,
375662306a36Sopenharmony_ci				"Management frame capability field"),
375762306a36Sopenharmony_ci	    IPW2100_ORD(AUTH_TYPE, "Type of authentication"),
375862306a36Sopenharmony_ci	    IPW2100_ORD(RADIO_TYPE, "Adapter card platform type"),
375962306a36Sopenharmony_ci	    IPW2100_ORD(RTS_THRESHOLD,
376062306a36Sopenharmony_ci				"Min packet length for RTS handshaking"),
376162306a36Sopenharmony_ci	    IPW2100_ORD(INT_MODE, "International mode"),
376262306a36Sopenharmony_ci	    IPW2100_ORD(FRAGMENTATION_THRESHOLD,
376362306a36Sopenharmony_ci				"protocol frag threshold"),
376462306a36Sopenharmony_ci	    IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_START_ADDRESS,
376562306a36Sopenharmony_ci				"EEPROM offset in SRAM"),
376662306a36Sopenharmony_ci	    IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_SIZE,
376762306a36Sopenharmony_ci				"EEPROM size in SRAM"),
376862306a36Sopenharmony_ci	    IPW2100_ORD(EEPROM_SKU_CAPABILITY, "EEPROM SKU Capability"),
376962306a36Sopenharmony_ci	    IPW2100_ORD(EEPROM_IBSS_11B_CHANNELS,
377062306a36Sopenharmony_ci				"EEPROM IBSS 11b channel set"),
377162306a36Sopenharmony_ci	    IPW2100_ORD(MAC_VERSION, "MAC Version"),
377262306a36Sopenharmony_ci	    IPW2100_ORD(MAC_REVISION, "MAC Revision"),
377362306a36Sopenharmony_ci	    IPW2100_ORD(RADIO_VERSION, "Radio Version"),
377462306a36Sopenharmony_ci	    IPW2100_ORD(NIC_MANF_DATE_TIME, "MANF Date/Time STAMP"),
377562306a36Sopenharmony_ci	    IPW2100_ORD(UCODE_VERSION, "Ucode Version"),};
377662306a36Sopenharmony_ci
377762306a36Sopenharmony_cistatic ssize_t registers_show(struct device *d, struct device_attribute *attr,
377862306a36Sopenharmony_ci			      char *buf)
377962306a36Sopenharmony_ci{
378062306a36Sopenharmony_ci	int i;
378162306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
378262306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
378362306a36Sopenharmony_ci	char *out = buf;
378462306a36Sopenharmony_ci	u32 val = 0;
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci	out += sprintf(out, "%30s [Address ] : Hex\n", "Register");
378762306a36Sopenharmony_ci
378862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(hw_data); i++) {
378962306a36Sopenharmony_ci		read_register(dev, hw_data[i].addr, &val);
379062306a36Sopenharmony_ci		out += sprintf(out, "%30s [%08X] : %08X\n",
379162306a36Sopenharmony_ci			       hw_data[i].name, hw_data[i].addr, val);
379262306a36Sopenharmony_ci	}
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_ci	return out - buf;
379562306a36Sopenharmony_ci}
379662306a36Sopenharmony_ci
379762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(registers);
379862306a36Sopenharmony_ci
379962306a36Sopenharmony_cistatic ssize_t hardware_show(struct device *d, struct device_attribute *attr,
380062306a36Sopenharmony_ci			     char *buf)
380162306a36Sopenharmony_ci{
380262306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
380362306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
380462306a36Sopenharmony_ci	char *out = buf;
380562306a36Sopenharmony_ci	int i;
380662306a36Sopenharmony_ci
380762306a36Sopenharmony_ci	out += sprintf(out, "%30s [Address ] : Hex\n", "NIC entry");
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(nic_data); i++) {
381062306a36Sopenharmony_ci		u8 tmp8;
381162306a36Sopenharmony_ci		u16 tmp16;
381262306a36Sopenharmony_ci		u32 tmp32;
381362306a36Sopenharmony_ci
381462306a36Sopenharmony_ci		switch (nic_data[i].size) {
381562306a36Sopenharmony_ci		case 1:
381662306a36Sopenharmony_ci			read_nic_byte(dev, nic_data[i].addr, &tmp8);
381762306a36Sopenharmony_ci			out += sprintf(out, "%30s [%08X] : %02X\n",
381862306a36Sopenharmony_ci				       nic_data[i].name, nic_data[i].addr,
381962306a36Sopenharmony_ci				       tmp8);
382062306a36Sopenharmony_ci			break;
382162306a36Sopenharmony_ci		case 2:
382262306a36Sopenharmony_ci			read_nic_word(dev, nic_data[i].addr, &tmp16);
382362306a36Sopenharmony_ci			out += sprintf(out, "%30s [%08X] : %04X\n",
382462306a36Sopenharmony_ci				       nic_data[i].name, nic_data[i].addr,
382562306a36Sopenharmony_ci				       tmp16);
382662306a36Sopenharmony_ci			break;
382762306a36Sopenharmony_ci		case 4:
382862306a36Sopenharmony_ci			read_nic_dword(dev, nic_data[i].addr, &tmp32);
382962306a36Sopenharmony_ci			out += sprintf(out, "%30s [%08X] : %08X\n",
383062306a36Sopenharmony_ci				       nic_data[i].name, nic_data[i].addr,
383162306a36Sopenharmony_ci				       tmp32);
383262306a36Sopenharmony_ci			break;
383362306a36Sopenharmony_ci		}
383462306a36Sopenharmony_ci	}
383562306a36Sopenharmony_ci	return out - buf;
383662306a36Sopenharmony_ci}
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(hardware);
383962306a36Sopenharmony_ci
384062306a36Sopenharmony_cistatic ssize_t memory_show(struct device *d, struct device_attribute *attr,
384162306a36Sopenharmony_ci			   char *buf)
384262306a36Sopenharmony_ci{
384362306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
384462306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
384562306a36Sopenharmony_ci	static unsigned long loop = 0;
384662306a36Sopenharmony_ci	int len = 0;
384762306a36Sopenharmony_ci	u32 buffer[4];
384862306a36Sopenharmony_ci	int i;
384962306a36Sopenharmony_ci	char line[81];
385062306a36Sopenharmony_ci
385162306a36Sopenharmony_ci	if (loop >= 0x30000)
385262306a36Sopenharmony_ci		loop = 0;
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci	/* sysfs provides us PAGE_SIZE buffer */
385562306a36Sopenharmony_ci	while (len < PAGE_SIZE - 128 && loop < 0x30000) {
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_ci		if (priv->snapshot[0])
385862306a36Sopenharmony_ci			for (i = 0; i < 4; i++)
385962306a36Sopenharmony_ci				buffer[i] =
386062306a36Sopenharmony_ci				    *(u32 *) SNAPSHOT_ADDR(loop + i * 4);
386162306a36Sopenharmony_ci		else
386262306a36Sopenharmony_ci			for (i = 0; i < 4; i++)
386362306a36Sopenharmony_ci				read_nic_dword(dev, loop + i * 4, &buffer[i]);
386462306a36Sopenharmony_ci
386562306a36Sopenharmony_ci		if (priv->dump_raw)
386662306a36Sopenharmony_ci			len += sprintf(buf + len,
386762306a36Sopenharmony_ci				       "%c%c%c%c"
386862306a36Sopenharmony_ci				       "%c%c%c%c"
386962306a36Sopenharmony_ci				       "%c%c%c%c"
387062306a36Sopenharmony_ci				       "%c%c%c%c",
387162306a36Sopenharmony_ci				       ((u8 *) buffer)[0x0],
387262306a36Sopenharmony_ci				       ((u8 *) buffer)[0x1],
387362306a36Sopenharmony_ci				       ((u8 *) buffer)[0x2],
387462306a36Sopenharmony_ci				       ((u8 *) buffer)[0x3],
387562306a36Sopenharmony_ci				       ((u8 *) buffer)[0x4],
387662306a36Sopenharmony_ci				       ((u8 *) buffer)[0x5],
387762306a36Sopenharmony_ci				       ((u8 *) buffer)[0x6],
387862306a36Sopenharmony_ci				       ((u8 *) buffer)[0x7],
387962306a36Sopenharmony_ci				       ((u8 *) buffer)[0x8],
388062306a36Sopenharmony_ci				       ((u8 *) buffer)[0x9],
388162306a36Sopenharmony_ci				       ((u8 *) buffer)[0xa],
388262306a36Sopenharmony_ci				       ((u8 *) buffer)[0xb],
388362306a36Sopenharmony_ci				       ((u8 *) buffer)[0xc],
388462306a36Sopenharmony_ci				       ((u8 *) buffer)[0xd],
388562306a36Sopenharmony_ci				       ((u8 *) buffer)[0xe],
388662306a36Sopenharmony_ci				       ((u8 *) buffer)[0xf]);
388762306a36Sopenharmony_ci		else
388862306a36Sopenharmony_ci			len += sprintf(buf + len, "%s\n",
388962306a36Sopenharmony_ci				       snprint_line(line, sizeof(line),
389062306a36Sopenharmony_ci						    (u8 *) buffer, 16, loop));
389162306a36Sopenharmony_ci		loop += 16;
389262306a36Sopenharmony_ci	}
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	return len;
389562306a36Sopenharmony_ci}
389662306a36Sopenharmony_ci
389762306a36Sopenharmony_cistatic ssize_t memory_store(struct device *d, struct device_attribute *attr,
389862306a36Sopenharmony_ci			    const char *buf, size_t count)
389962306a36Sopenharmony_ci{
390062306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
390162306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
390262306a36Sopenharmony_ci	const char *p = buf;
390362306a36Sopenharmony_ci
390462306a36Sopenharmony_ci	(void)dev;		/* kill unused-var warning for debug-only code */
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci	if (count < 1)
390762306a36Sopenharmony_ci		return count;
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci	if (p[0] == '1' ||
391062306a36Sopenharmony_ci	    (count >= 2 && tolower(p[0]) == 'o' && tolower(p[1]) == 'n')) {
391162306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: Setting memory dump to RAW mode.\n",
391262306a36Sopenharmony_ci			       dev->name);
391362306a36Sopenharmony_ci		priv->dump_raw = 1;
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ci	} else if (p[0] == '0' || (count >= 2 && tolower(p[0]) == 'o' &&
391662306a36Sopenharmony_ci				   tolower(p[1]) == 'f')) {
391762306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: Setting memory dump to HEX mode.\n",
391862306a36Sopenharmony_ci			       dev->name);
391962306a36Sopenharmony_ci		priv->dump_raw = 0;
392062306a36Sopenharmony_ci
392162306a36Sopenharmony_ci	} else if (tolower(p[0]) == 'r') {
392262306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: Resetting firmware snapshot.\n", dev->name);
392362306a36Sopenharmony_ci		ipw2100_snapshot_free(priv);
392462306a36Sopenharmony_ci
392562306a36Sopenharmony_ci	} else
392662306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: Usage: 0|on = HEX, 1|off = RAW, "
392762306a36Sopenharmony_ci			       "reset = clear memory snapshot\n", dev->name);
392862306a36Sopenharmony_ci
392962306a36Sopenharmony_ci	return count;
393062306a36Sopenharmony_ci}
393162306a36Sopenharmony_ci
393262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(memory);
393362306a36Sopenharmony_ci
393462306a36Sopenharmony_cistatic ssize_t ordinals_show(struct device *d, struct device_attribute *attr,
393562306a36Sopenharmony_ci			     char *buf)
393662306a36Sopenharmony_ci{
393762306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
393862306a36Sopenharmony_ci	u32 val = 0;
393962306a36Sopenharmony_ci	int len = 0;
394062306a36Sopenharmony_ci	u32 val_len;
394162306a36Sopenharmony_ci	static int loop = 0;
394262306a36Sopenharmony_ci
394362306a36Sopenharmony_ci	if (priv->status & STATUS_RF_KILL_MASK)
394462306a36Sopenharmony_ci		return 0;
394562306a36Sopenharmony_ci
394662306a36Sopenharmony_ci	if (loop >= ARRAY_SIZE(ord_data))
394762306a36Sopenharmony_ci		loop = 0;
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci	/* sysfs provides us PAGE_SIZE buffer */
395062306a36Sopenharmony_ci	while (len < PAGE_SIZE - 128 && loop < ARRAY_SIZE(ord_data)) {
395162306a36Sopenharmony_ci		val_len = sizeof(u32);
395262306a36Sopenharmony_ci
395362306a36Sopenharmony_ci		if (ipw2100_get_ordinal(priv, ord_data[loop].index, &val,
395462306a36Sopenharmony_ci					&val_len))
395562306a36Sopenharmony_ci			len += sprintf(buf + len, "[0x%02X] = ERROR    %s\n",
395662306a36Sopenharmony_ci				       ord_data[loop].index,
395762306a36Sopenharmony_ci				       ord_data[loop].desc);
395862306a36Sopenharmony_ci		else
395962306a36Sopenharmony_ci			len += sprintf(buf + len, "[0x%02X] = 0x%08X %s\n",
396062306a36Sopenharmony_ci				       ord_data[loop].index, val,
396162306a36Sopenharmony_ci				       ord_data[loop].desc);
396262306a36Sopenharmony_ci		loop++;
396362306a36Sopenharmony_ci	}
396462306a36Sopenharmony_ci
396562306a36Sopenharmony_ci	return len;
396662306a36Sopenharmony_ci}
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(ordinals);
396962306a36Sopenharmony_ci
397062306a36Sopenharmony_cistatic ssize_t stats_show(struct device *d, struct device_attribute *attr,
397162306a36Sopenharmony_ci			  char *buf)
397262306a36Sopenharmony_ci{
397362306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
397462306a36Sopenharmony_ci	char *out = buf;
397562306a36Sopenharmony_ci
397662306a36Sopenharmony_ci	out += sprintf(out, "interrupts: %d {tx: %d, rx: %d, other: %d}\n",
397762306a36Sopenharmony_ci		       priv->interrupts, priv->tx_interrupts,
397862306a36Sopenharmony_ci		       priv->rx_interrupts, priv->inta_other);
397962306a36Sopenharmony_ci	out += sprintf(out, "firmware resets: %d\n", priv->resets);
398062306a36Sopenharmony_ci	out += sprintf(out, "firmware hangs: %d\n", priv->hangs);
398162306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
398262306a36Sopenharmony_ci	out += sprintf(out, "packet mismatch image: %s\n",
398362306a36Sopenharmony_ci		       priv->snapshot[0] ? "YES" : "NO");
398462306a36Sopenharmony_ci#endif
398562306a36Sopenharmony_ci
398662306a36Sopenharmony_ci	return out - buf;
398762306a36Sopenharmony_ci}
398862306a36Sopenharmony_ci
398962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(stats);
399062306a36Sopenharmony_ci
399162306a36Sopenharmony_cistatic int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode)
399262306a36Sopenharmony_ci{
399362306a36Sopenharmony_ci	int err;
399462306a36Sopenharmony_ci
399562306a36Sopenharmony_ci	if (mode == priv->ieee->iw_mode)
399662306a36Sopenharmony_ci		return 0;
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci	err = ipw2100_disable_adapter(priv);
399962306a36Sopenharmony_ci	if (err) {
400062306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
400162306a36Sopenharmony_ci		       priv->net_dev->name, err);
400262306a36Sopenharmony_ci		return err;
400362306a36Sopenharmony_ci	}
400462306a36Sopenharmony_ci
400562306a36Sopenharmony_ci	switch (mode) {
400662306a36Sopenharmony_ci	case IW_MODE_INFRA:
400762306a36Sopenharmony_ci		priv->net_dev->type = ARPHRD_ETHER;
400862306a36Sopenharmony_ci		break;
400962306a36Sopenharmony_ci	case IW_MODE_ADHOC:
401062306a36Sopenharmony_ci		priv->net_dev->type = ARPHRD_ETHER;
401162306a36Sopenharmony_ci		break;
401262306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
401362306a36Sopenharmony_ci	case IW_MODE_MONITOR:
401462306a36Sopenharmony_ci		priv->last_mode = priv->ieee->iw_mode;
401562306a36Sopenharmony_ci		priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
401662306a36Sopenharmony_ci		break;
401762306a36Sopenharmony_ci#endif				/* CONFIG_IPW2100_MONITOR */
401862306a36Sopenharmony_ci	}
401962306a36Sopenharmony_ci
402062306a36Sopenharmony_ci	priv->ieee->iw_mode = mode;
402162306a36Sopenharmony_ci
402262306a36Sopenharmony_ci#ifdef CONFIG_PM
402362306a36Sopenharmony_ci	/* Indicate ipw2100_download_firmware download firmware
402462306a36Sopenharmony_ci	 * from disk instead of memory. */
402562306a36Sopenharmony_ci	ipw2100_firmware.version = 0;
402662306a36Sopenharmony_ci#endif
402762306a36Sopenharmony_ci
402862306a36Sopenharmony_ci	printk(KERN_INFO "%s: Resetting on mode change.\n", priv->net_dev->name);
402962306a36Sopenharmony_ci	priv->reset_backoff = 0;
403062306a36Sopenharmony_ci	schedule_reset(priv);
403162306a36Sopenharmony_ci
403262306a36Sopenharmony_ci	return 0;
403362306a36Sopenharmony_ci}
403462306a36Sopenharmony_ci
403562306a36Sopenharmony_cistatic ssize_t internals_show(struct device *d, struct device_attribute *attr,
403662306a36Sopenharmony_ci			      char *buf)
403762306a36Sopenharmony_ci{
403862306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
403962306a36Sopenharmony_ci	int len = 0;
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci#define DUMP_VAR(x,y) len += sprintf(buf + len, # x ": %" y "\n", priv-> x)
404262306a36Sopenharmony_ci
404362306a36Sopenharmony_ci	if (priv->status & STATUS_ASSOCIATED)
404462306a36Sopenharmony_ci		len += sprintf(buf + len, "connected: %llu\n",
404562306a36Sopenharmony_ci			       ktime_get_boottime_seconds() - priv->connect_start);
404662306a36Sopenharmony_ci	else
404762306a36Sopenharmony_ci		len += sprintf(buf + len, "not connected\n");
404862306a36Sopenharmony_ci
404962306a36Sopenharmony_ci	DUMP_VAR(ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx], "p");
405062306a36Sopenharmony_ci	DUMP_VAR(status, "08lx");
405162306a36Sopenharmony_ci	DUMP_VAR(config, "08lx");
405262306a36Sopenharmony_ci	DUMP_VAR(capability, "08lx");
405362306a36Sopenharmony_ci
405462306a36Sopenharmony_ci	len +=
405562306a36Sopenharmony_ci	    sprintf(buf + len, "last_rtc: %lu\n",
405662306a36Sopenharmony_ci		    (unsigned long)priv->last_rtc);
405762306a36Sopenharmony_ci
405862306a36Sopenharmony_ci	DUMP_VAR(fatal_error, "d");
405962306a36Sopenharmony_ci	DUMP_VAR(stop_hang_check, "d");
406062306a36Sopenharmony_ci	DUMP_VAR(stop_rf_kill, "d");
406162306a36Sopenharmony_ci	DUMP_VAR(messages_sent, "d");
406262306a36Sopenharmony_ci
406362306a36Sopenharmony_ci	DUMP_VAR(tx_pend_stat.value, "d");
406462306a36Sopenharmony_ci	DUMP_VAR(tx_pend_stat.hi, "d");
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_ci	DUMP_VAR(tx_free_stat.value, "d");
406762306a36Sopenharmony_ci	DUMP_VAR(tx_free_stat.lo, "d");
406862306a36Sopenharmony_ci
406962306a36Sopenharmony_ci	DUMP_VAR(msg_free_stat.value, "d");
407062306a36Sopenharmony_ci	DUMP_VAR(msg_free_stat.lo, "d");
407162306a36Sopenharmony_ci
407262306a36Sopenharmony_ci	DUMP_VAR(msg_pend_stat.value, "d");
407362306a36Sopenharmony_ci	DUMP_VAR(msg_pend_stat.hi, "d");
407462306a36Sopenharmony_ci
407562306a36Sopenharmony_ci	DUMP_VAR(fw_pend_stat.value, "d");
407662306a36Sopenharmony_ci	DUMP_VAR(fw_pend_stat.hi, "d");
407762306a36Sopenharmony_ci
407862306a36Sopenharmony_ci	DUMP_VAR(txq_stat.value, "d");
407962306a36Sopenharmony_ci	DUMP_VAR(txq_stat.lo, "d");
408062306a36Sopenharmony_ci
408162306a36Sopenharmony_ci	DUMP_VAR(ieee->scans, "d");
408262306a36Sopenharmony_ci	DUMP_VAR(reset_backoff, "lld");
408362306a36Sopenharmony_ci
408462306a36Sopenharmony_ci	return len;
408562306a36Sopenharmony_ci}
408662306a36Sopenharmony_ci
408762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(internals);
408862306a36Sopenharmony_ci
408962306a36Sopenharmony_cistatic ssize_t bssinfo_show(struct device *d, struct device_attribute *attr,
409062306a36Sopenharmony_ci			    char *buf)
409162306a36Sopenharmony_ci{
409262306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
409362306a36Sopenharmony_ci	char essid[IW_ESSID_MAX_SIZE + 1];
409462306a36Sopenharmony_ci	u8 bssid[ETH_ALEN];
409562306a36Sopenharmony_ci	u32 chan = 0;
409662306a36Sopenharmony_ci	char *out = buf;
409762306a36Sopenharmony_ci	unsigned int length;
409862306a36Sopenharmony_ci	int ret;
409962306a36Sopenharmony_ci
410062306a36Sopenharmony_ci	if (priv->status & STATUS_RF_KILL_MASK)
410162306a36Sopenharmony_ci		return 0;
410262306a36Sopenharmony_ci
410362306a36Sopenharmony_ci	memset(essid, 0, sizeof(essid));
410462306a36Sopenharmony_ci	memset(bssid, 0, sizeof(bssid));
410562306a36Sopenharmony_ci
410662306a36Sopenharmony_ci	length = IW_ESSID_MAX_SIZE;
410762306a36Sopenharmony_ci	ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, essid, &length);
410862306a36Sopenharmony_ci	if (ret)
410962306a36Sopenharmony_ci		IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
411062306a36Sopenharmony_ci			       __LINE__);
411162306a36Sopenharmony_ci
411262306a36Sopenharmony_ci	length = sizeof(bssid);
411362306a36Sopenharmony_ci	ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
411462306a36Sopenharmony_ci				  bssid, &length);
411562306a36Sopenharmony_ci	if (ret)
411662306a36Sopenharmony_ci		IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
411762306a36Sopenharmony_ci			       __LINE__);
411862306a36Sopenharmony_ci
411962306a36Sopenharmony_ci	length = sizeof(u32);
412062306a36Sopenharmony_ci	ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &length);
412162306a36Sopenharmony_ci	if (ret)
412262306a36Sopenharmony_ci		IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
412362306a36Sopenharmony_ci			       __LINE__);
412462306a36Sopenharmony_ci
412562306a36Sopenharmony_ci	out += sprintf(out, "ESSID: %s\n", essid);
412662306a36Sopenharmony_ci	out += sprintf(out, "BSSID:   %pM\n", bssid);
412762306a36Sopenharmony_ci	out += sprintf(out, "Channel: %d\n", chan);
412862306a36Sopenharmony_ci
412962306a36Sopenharmony_ci	return out - buf;
413062306a36Sopenharmony_ci}
413162306a36Sopenharmony_ci
413262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(bssinfo);
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
413562306a36Sopenharmony_cistatic ssize_t debug_level_show(struct device_driver *d, char *buf)
413662306a36Sopenharmony_ci{
413762306a36Sopenharmony_ci	return sprintf(buf, "0x%08X\n", ipw2100_debug_level);
413862306a36Sopenharmony_ci}
413962306a36Sopenharmony_ci
414062306a36Sopenharmony_cistatic ssize_t debug_level_store(struct device_driver *d,
414162306a36Sopenharmony_ci				 const char *buf, size_t count)
414262306a36Sopenharmony_ci{
414362306a36Sopenharmony_ci	u32 val;
414462306a36Sopenharmony_ci	int ret;
414562306a36Sopenharmony_ci
414662306a36Sopenharmony_ci	ret = kstrtou32(buf, 0, &val);
414762306a36Sopenharmony_ci	if (ret)
414862306a36Sopenharmony_ci		IPW_DEBUG_INFO(": %s is not in hex or decimal form.\n", buf);
414962306a36Sopenharmony_ci	else
415062306a36Sopenharmony_ci		ipw2100_debug_level = val;
415162306a36Sopenharmony_ci
415262306a36Sopenharmony_ci	return strnlen(buf, count);
415362306a36Sopenharmony_ci}
415462306a36Sopenharmony_cistatic DRIVER_ATTR_RW(debug_level);
415562306a36Sopenharmony_ci#endif				/* CONFIG_IPW2100_DEBUG */
415662306a36Sopenharmony_ci
415762306a36Sopenharmony_cistatic ssize_t fatal_error_show(struct device *d,
415862306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
415962306a36Sopenharmony_ci{
416062306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
416162306a36Sopenharmony_ci	char *out = buf;
416262306a36Sopenharmony_ci	int i;
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_ci	if (priv->fatal_error)
416562306a36Sopenharmony_ci		out += sprintf(out, "0x%08X\n", priv->fatal_error);
416662306a36Sopenharmony_ci	else
416762306a36Sopenharmony_ci		out += sprintf(out, "0\n");
416862306a36Sopenharmony_ci
416962306a36Sopenharmony_ci	for (i = 1; i <= IPW2100_ERROR_QUEUE; i++) {
417062306a36Sopenharmony_ci		if (!priv->fatal_errors[(priv->fatal_index - i) %
417162306a36Sopenharmony_ci					IPW2100_ERROR_QUEUE])
417262306a36Sopenharmony_ci			continue;
417362306a36Sopenharmony_ci
417462306a36Sopenharmony_ci		out += sprintf(out, "%d. 0x%08X\n", i,
417562306a36Sopenharmony_ci			       priv->fatal_errors[(priv->fatal_index - i) %
417662306a36Sopenharmony_ci						  IPW2100_ERROR_QUEUE]);
417762306a36Sopenharmony_ci	}
417862306a36Sopenharmony_ci
417962306a36Sopenharmony_ci	return out - buf;
418062306a36Sopenharmony_ci}
418162306a36Sopenharmony_ci
418262306a36Sopenharmony_cistatic ssize_t fatal_error_store(struct device *d,
418362306a36Sopenharmony_ci				 struct device_attribute *attr, const char *buf,
418462306a36Sopenharmony_ci				 size_t count)
418562306a36Sopenharmony_ci{
418662306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
418762306a36Sopenharmony_ci	schedule_reset(priv);
418862306a36Sopenharmony_ci	return count;
418962306a36Sopenharmony_ci}
419062306a36Sopenharmony_ci
419162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(fatal_error);
419262306a36Sopenharmony_ci
419362306a36Sopenharmony_cistatic ssize_t scan_age_show(struct device *d, struct device_attribute *attr,
419462306a36Sopenharmony_ci			     char *buf)
419562306a36Sopenharmony_ci{
419662306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
419762306a36Sopenharmony_ci	return sprintf(buf, "%d\n", priv->ieee->scan_age);
419862306a36Sopenharmony_ci}
419962306a36Sopenharmony_ci
420062306a36Sopenharmony_cistatic ssize_t scan_age_store(struct device *d, struct device_attribute *attr,
420162306a36Sopenharmony_ci			      const char *buf, size_t count)
420262306a36Sopenharmony_ci{
420362306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
420462306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
420562306a36Sopenharmony_ci	unsigned long val;
420662306a36Sopenharmony_ci	int ret;
420762306a36Sopenharmony_ci
420862306a36Sopenharmony_ci	(void)dev;		/* kill unused-var warning for debug-only code */
420962306a36Sopenharmony_ci
421062306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
421162306a36Sopenharmony_ci
421262306a36Sopenharmony_ci	ret = kstrtoul(buf, 0, &val);
421362306a36Sopenharmony_ci	if (ret) {
421462306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
421562306a36Sopenharmony_ci	} else {
421662306a36Sopenharmony_ci		priv->ieee->scan_age = val;
421762306a36Sopenharmony_ci		IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
421862306a36Sopenharmony_ci	}
421962306a36Sopenharmony_ci
422062306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
422162306a36Sopenharmony_ci	return strnlen(buf, count);
422262306a36Sopenharmony_ci}
422362306a36Sopenharmony_ci
422462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(scan_age);
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_cistatic ssize_t rf_kill_show(struct device *d, struct device_attribute *attr,
422762306a36Sopenharmony_ci			    char *buf)
422862306a36Sopenharmony_ci{
422962306a36Sopenharmony_ci	/* 0 - RF kill not enabled
423062306a36Sopenharmony_ci	   1 - SW based RF kill active (sysfs)
423162306a36Sopenharmony_ci	   2 - HW based RF kill active
423262306a36Sopenharmony_ci	   3 - Both HW and SW baed RF kill active */
423362306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
423462306a36Sopenharmony_ci	int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
423562306a36Sopenharmony_ci	    (rf_kill_active(priv) ? 0x2 : 0x0);
423662306a36Sopenharmony_ci	return sprintf(buf, "%i\n", val);
423762306a36Sopenharmony_ci}
423862306a36Sopenharmony_ci
423962306a36Sopenharmony_cistatic int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio)
424062306a36Sopenharmony_ci{
424162306a36Sopenharmony_ci	if ((disable_radio ? 1 : 0) ==
424262306a36Sopenharmony_ci	    (priv->status & STATUS_RF_KILL_SW ? 1 : 0))
424362306a36Sopenharmony_ci		return 0;
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci	IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO  %s\n",
424662306a36Sopenharmony_ci			  disable_radio ? "OFF" : "ON");
424762306a36Sopenharmony_ci
424862306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
424962306a36Sopenharmony_ci
425062306a36Sopenharmony_ci	if (disable_radio) {
425162306a36Sopenharmony_ci		priv->status |= STATUS_RF_KILL_SW;
425262306a36Sopenharmony_ci		ipw2100_down(priv);
425362306a36Sopenharmony_ci	} else {
425462306a36Sopenharmony_ci		priv->status &= ~STATUS_RF_KILL_SW;
425562306a36Sopenharmony_ci		if (rf_kill_active(priv)) {
425662306a36Sopenharmony_ci			IPW_DEBUG_RF_KILL("Can not turn radio back on - "
425762306a36Sopenharmony_ci					  "disabled by HW switch\n");
425862306a36Sopenharmony_ci			/* Make sure the RF_KILL check timer is running */
425962306a36Sopenharmony_ci			priv->stop_rf_kill = 0;
426062306a36Sopenharmony_ci			mod_delayed_work(system_wq, &priv->rf_kill,
426162306a36Sopenharmony_ci					 round_jiffies_relative(HZ));
426262306a36Sopenharmony_ci		} else
426362306a36Sopenharmony_ci			schedule_reset(priv);
426462306a36Sopenharmony_ci	}
426562306a36Sopenharmony_ci
426662306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
426762306a36Sopenharmony_ci	return 1;
426862306a36Sopenharmony_ci}
426962306a36Sopenharmony_ci
427062306a36Sopenharmony_cistatic ssize_t rf_kill_store(struct device *d, struct device_attribute *attr,
427162306a36Sopenharmony_ci			     const char *buf, size_t count)
427262306a36Sopenharmony_ci{
427362306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(d);
427462306a36Sopenharmony_ci	ipw_radio_kill_sw(priv, buf[0] == '1');
427562306a36Sopenharmony_ci	return count;
427662306a36Sopenharmony_ci}
427762306a36Sopenharmony_ci
427862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(rf_kill);
427962306a36Sopenharmony_ci
428062306a36Sopenharmony_cistatic struct attribute *ipw2100_sysfs_entries[] = {
428162306a36Sopenharmony_ci	&dev_attr_hardware.attr,
428262306a36Sopenharmony_ci	&dev_attr_registers.attr,
428362306a36Sopenharmony_ci	&dev_attr_ordinals.attr,
428462306a36Sopenharmony_ci	&dev_attr_pci.attr,
428562306a36Sopenharmony_ci	&dev_attr_stats.attr,
428662306a36Sopenharmony_ci	&dev_attr_internals.attr,
428762306a36Sopenharmony_ci	&dev_attr_bssinfo.attr,
428862306a36Sopenharmony_ci	&dev_attr_memory.attr,
428962306a36Sopenharmony_ci	&dev_attr_scan_age.attr,
429062306a36Sopenharmony_ci	&dev_attr_fatal_error.attr,
429162306a36Sopenharmony_ci	&dev_attr_rf_kill.attr,
429262306a36Sopenharmony_ci	&dev_attr_cfg.attr,
429362306a36Sopenharmony_ci	&dev_attr_status.attr,
429462306a36Sopenharmony_ci	&dev_attr_capability.attr,
429562306a36Sopenharmony_ci	NULL,
429662306a36Sopenharmony_ci};
429762306a36Sopenharmony_ci
429862306a36Sopenharmony_cistatic const struct attribute_group ipw2100_attribute_group = {
429962306a36Sopenharmony_ci	.attrs = ipw2100_sysfs_entries,
430062306a36Sopenharmony_ci};
430162306a36Sopenharmony_ci
430262306a36Sopenharmony_cistatic int status_queue_allocate(struct ipw2100_priv *priv, int entries)
430362306a36Sopenharmony_ci{
430462306a36Sopenharmony_ci	struct ipw2100_status_queue *q = &priv->status_queue;
430562306a36Sopenharmony_ci
430662306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
430762306a36Sopenharmony_ci
430862306a36Sopenharmony_ci	q->size = entries * sizeof(struct ipw2100_status);
430962306a36Sopenharmony_ci	q->drv = dma_alloc_coherent(&priv->pci_dev->dev, q->size, &q->nic,
431062306a36Sopenharmony_ci				    GFP_KERNEL);
431162306a36Sopenharmony_ci	if (!q->drv) {
431262306a36Sopenharmony_ci		IPW_DEBUG_WARNING("Can not allocate status queue.\n");
431362306a36Sopenharmony_ci		return -ENOMEM;
431462306a36Sopenharmony_ci	}
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
431762306a36Sopenharmony_ci
431862306a36Sopenharmony_ci	return 0;
431962306a36Sopenharmony_ci}
432062306a36Sopenharmony_ci
432162306a36Sopenharmony_cistatic void status_queue_free(struct ipw2100_priv *priv)
432262306a36Sopenharmony_ci{
432362306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
432462306a36Sopenharmony_ci
432562306a36Sopenharmony_ci	if (priv->status_queue.drv) {
432662306a36Sopenharmony_ci		dma_free_coherent(&priv->pci_dev->dev,
432762306a36Sopenharmony_ci				  priv->status_queue.size,
432862306a36Sopenharmony_ci				  priv->status_queue.drv,
432962306a36Sopenharmony_ci				  priv->status_queue.nic);
433062306a36Sopenharmony_ci		priv->status_queue.drv = NULL;
433162306a36Sopenharmony_ci	}
433262306a36Sopenharmony_ci
433362306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
433462306a36Sopenharmony_ci}
433562306a36Sopenharmony_ci
433662306a36Sopenharmony_cistatic int bd_queue_allocate(struct ipw2100_priv *priv,
433762306a36Sopenharmony_ci			     struct ipw2100_bd_queue *q, int entries)
433862306a36Sopenharmony_ci{
433962306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
434062306a36Sopenharmony_ci
434162306a36Sopenharmony_ci	memset(q, 0, sizeof(struct ipw2100_bd_queue));
434262306a36Sopenharmony_ci
434362306a36Sopenharmony_ci	q->entries = entries;
434462306a36Sopenharmony_ci	q->size = entries * sizeof(struct ipw2100_bd);
434562306a36Sopenharmony_ci	q->drv = dma_alloc_coherent(&priv->pci_dev->dev, q->size, &q->nic,
434662306a36Sopenharmony_ci				    GFP_KERNEL);
434762306a36Sopenharmony_ci	if (!q->drv) {
434862306a36Sopenharmony_ci		IPW_DEBUG_INFO
434962306a36Sopenharmony_ci		    ("can't allocate shared memory for buffer descriptors\n");
435062306a36Sopenharmony_ci		return -ENOMEM;
435162306a36Sopenharmony_ci	}
435262306a36Sopenharmony_ci
435362306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
435462306a36Sopenharmony_ci
435562306a36Sopenharmony_ci	return 0;
435662306a36Sopenharmony_ci}
435762306a36Sopenharmony_ci
435862306a36Sopenharmony_cistatic void bd_queue_free(struct ipw2100_priv *priv, struct ipw2100_bd_queue *q)
435962306a36Sopenharmony_ci{
436062306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
436162306a36Sopenharmony_ci
436262306a36Sopenharmony_ci	if (!q)
436362306a36Sopenharmony_ci		return;
436462306a36Sopenharmony_ci
436562306a36Sopenharmony_ci	if (q->drv) {
436662306a36Sopenharmony_ci		dma_free_coherent(&priv->pci_dev->dev, q->size, q->drv,
436762306a36Sopenharmony_ci				  q->nic);
436862306a36Sopenharmony_ci		q->drv = NULL;
436962306a36Sopenharmony_ci	}
437062306a36Sopenharmony_ci
437162306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
437262306a36Sopenharmony_ci}
437362306a36Sopenharmony_ci
437462306a36Sopenharmony_cistatic void bd_queue_initialize(struct ipw2100_priv *priv,
437562306a36Sopenharmony_ci				struct ipw2100_bd_queue *q, u32 base, u32 size,
437662306a36Sopenharmony_ci				u32 r, u32 w)
437762306a36Sopenharmony_ci{
437862306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
437962306a36Sopenharmony_ci
438062306a36Sopenharmony_ci	IPW_DEBUG_INFO("initializing bd queue at virt=%p, phys=%08x\n", q->drv,
438162306a36Sopenharmony_ci		       (u32) q->nic);
438262306a36Sopenharmony_ci
438362306a36Sopenharmony_ci	write_register(priv->net_dev, base, q->nic);
438462306a36Sopenharmony_ci	write_register(priv->net_dev, size, q->entries);
438562306a36Sopenharmony_ci	write_register(priv->net_dev, r, q->oldest);
438662306a36Sopenharmony_ci	write_register(priv->net_dev, w, q->next);
438762306a36Sopenharmony_ci
438862306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
438962306a36Sopenharmony_ci}
439062306a36Sopenharmony_ci
439162306a36Sopenharmony_cistatic void ipw2100_kill_works(struct ipw2100_priv *priv)
439262306a36Sopenharmony_ci{
439362306a36Sopenharmony_ci	priv->stop_rf_kill = 1;
439462306a36Sopenharmony_ci	priv->stop_hang_check = 1;
439562306a36Sopenharmony_ci	cancel_delayed_work_sync(&priv->reset_work);
439662306a36Sopenharmony_ci	cancel_delayed_work_sync(&priv->security_work);
439762306a36Sopenharmony_ci	cancel_delayed_work_sync(&priv->wx_event_work);
439862306a36Sopenharmony_ci	cancel_delayed_work_sync(&priv->hang_check);
439962306a36Sopenharmony_ci	cancel_delayed_work_sync(&priv->rf_kill);
440062306a36Sopenharmony_ci	cancel_delayed_work_sync(&priv->scan_event);
440162306a36Sopenharmony_ci}
440262306a36Sopenharmony_ci
440362306a36Sopenharmony_cistatic int ipw2100_tx_allocate(struct ipw2100_priv *priv)
440462306a36Sopenharmony_ci{
440562306a36Sopenharmony_ci	int i, j, err;
440662306a36Sopenharmony_ci	void *v;
440762306a36Sopenharmony_ci	dma_addr_t p;
440862306a36Sopenharmony_ci
440962306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
441062306a36Sopenharmony_ci
441162306a36Sopenharmony_ci	err = bd_queue_allocate(priv, &priv->tx_queue, TX_QUEUE_LENGTH);
441262306a36Sopenharmony_ci	if (err) {
441362306a36Sopenharmony_ci		IPW_DEBUG_ERROR("%s: failed bd_queue_allocate\n",
441462306a36Sopenharmony_ci				priv->net_dev->name);
441562306a36Sopenharmony_ci		return err;
441662306a36Sopenharmony_ci	}
441762306a36Sopenharmony_ci
441862306a36Sopenharmony_ci	priv->tx_buffers = kmalloc_array(TX_PENDED_QUEUE_LENGTH,
441962306a36Sopenharmony_ci					 sizeof(struct ipw2100_tx_packet),
442062306a36Sopenharmony_ci					 GFP_KERNEL);
442162306a36Sopenharmony_ci	if (!priv->tx_buffers) {
442262306a36Sopenharmony_ci		bd_queue_free(priv, &priv->tx_queue);
442362306a36Sopenharmony_ci		return -ENOMEM;
442462306a36Sopenharmony_ci	}
442562306a36Sopenharmony_ci
442662306a36Sopenharmony_ci	for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
442762306a36Sopenharmony_ci		v = dma_alloc_coherent(&priv->pci_dev->dev,
442862306a36Sopenharmony_ci				       sizeof(struct ipw2100_data_header), &p,
442962306a36Sopenharmony_ci				       GFP_KERNEL);
443062306a36Sopenharmony_ci		if (!v) {
443162306a36Sopenharmony_ci			printk(KERN_ERR DRV_NAME
443262306a36Sopenharmony_ci			       ": %s: PCI alloc failed for tx " "buffers.\n",
443362306a36Sopenharmony_ci			       priv->net_dev->name);
443462306a36Sopenharmony_ci			err = -ENOMEM;
443562306a36Sopenharmony_ci			break;
443662306a36Sopenharmony_ci		}
443762306a36Sopenharmony_ci
443862306a36Sopenharmony_ci		priv->tx_buffers[i].type = DATA;
443962306a36Sopenharmony_ci		priv->tx_buffers[i].info.d_struct.data =
444062306a36Sopenharmony_ci		    (struct ipw2100_data_header *)v;
444162306a36Sopenharmony_ci		priv->tx_buffers[i].info.d_struct.data_phys = p;
444262306a36Sopenharmony_ci		priv->tx_buffers[i].info.d_struct.txb = NULL;
444362306a36Sopenharmony_ci	}
444462306a36Sopenharmony_ci
444562306a36Sopenharmony_ci	if (i == TX_PENDED_QUEUE_LENGTH)
444662306a36Sopenharmony_ci		return 0;
444762306a36Sopenharmony_ci
444862306a36Sopenharmony_ci	for (j = 0; j < i; j++) {
444962306a36Sopenharmony_ci		dma_free_coherent(&priv->pci_dev->dev,
445062306a36Sopenharmony_ci				  sizeof(struct ipw2100_data_header),
445162306a36Sopenharmony_ci				  priv->tx_buffers[j].info.d_struct.data,
445262306a36Sopenharmony_ci				  priv->tx_buffers[j].info.d_struct.data_phys);
445362306a36Sopenharmony_ci	}
445462306a36Sopenharmony_ci
445562306a36Sopenharmony_ci	kfree(priv->tx_buffers);
445662306a36Sopenharmony_ci	priv->tx_buffers = NULL;
445762306a36Sopenharmony_ci
445862306a36Sopenharmony_ci	return err;
445962306a36Sopenharmony_ci}
446062306a36Sopenharmony_ci
446162306a36Sopenharmony_cistatic void ipw2100_tx_initialize(struct ipw2100_priv *priv)
446262306a36Sopenharmony_ci{
446362306a36Sopenharmony_ci	int i;
446462306a36Sopenharmony_ci
446562306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
446662306a36Sopenharmony_ci
446762306a36Sopenharmony_ci	/*
446862306a36Sopenharmony_ci	 * reinitialize packet info lists
446962306a36Sopenharmony_ci	 */
447062306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->fw_pend_list);
447162306a36Sopenharmony_ci	INIT_STAT(&priv->fw_pend_stat);
447262306a36Sopenharmony_ci
447362306a36Sopenharmony_ci	/*
447462306a36Sopenharmony_ci	 * reinitialize lists
447562306a36Sopenharmony_ci	 */
447662306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->tx_pend_list);
447762306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->tx_free_list);
447862306a36Sopenharmony_ci	INIT_STAT(&priv->tx_pend_stat);
447962306a36Sopenharmony_ci	INIT_STAT(&priv->tx_free_stat);
448062306a36Sopenharmony_ci
448162306a36Sopenharmony_ci	for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
448262306a36Sopenharmony_ci		/* We simply drop any SKBs that have been queued for
448362306a36Sopenharmony_ci		 * transmit */
448462306a36Sopenharmony_ci		if (priv->tx_buffers[i].info.d_struct.txb) {
448562306a36Sopenharmony_ci			libipw_txb_free(priv->tx_buffers[i].info.d_struct.
448662306a36Sopenharmony_ci					   txb);
448762306a36Sopenharmony_ci			priv->tx_buffers[i].info.d_struct.txb = NULL;
448862306a36Sopenharmony_ci		}
448962306a36Sopenharmony_ci
449062306a36Sopenharmony_ci		list_add_tail(&priv->tx_buffers[i].list, &priv->tx_free_list);
449162306a36Sopenharmony_ci	}
449262306a36Sopenharmony_ci
449362306a36Sopenharmony_ci	SET_STAT(&priv->tx_free_stat, i);
449462306a36Sopenharmony_ci
449562306a36Sopenharmony_ci	priv->tx_queue.oldest = 0;
449662306a36Sopenharmony_ci	priv->tx_queue.available = priv->tx_queue.entries;
449762306a36Sopenharmony_ci	priv->tx_queue.next = 0;
449862306a36Sopenharmony_ci	INIT_STAT(&priv->txq_stat);
449962306a36Sopenharmony_ci	SET_STAT(&priv->txq_stat, priv->tx_queue.available);
450062306a36Sopenharmony_ci
450162306a36Sopenharmony_ci	bd_queue_initialize(priv, &priv->tx_queue,
450262306a36Sopenharmony_ci			    IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE,
450362306a36Sopenharmony_ci			    IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE,
450462306a36Sopenharmony_ci			    IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
450562306a36Sopenharmony_ci			    IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX);
450662306a36Sopenharmony_ci
450762306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
450862306a36Sopenharmony_ci
450962306a36Sopenharmony_ci}
451062306a36Sopenharmony_ci
451162306a36Sopenharmony_cistatic void ipw2100_tx_free(struct ipw2100_priv *priv)
451262306a36Sopenharmony_ci{
451362306a36Sopenharmony_ci	int i;
451462306a36Sopenharmony_ci
451562306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
451662306a36Sopenharmony_ci
451762306a36Sopenharmony_ci	bd_queue_free(priv, &priv->tx_queue);
451862306a36Sopenharmony_ci
451962306a36Sopenharmony_ci	if (!priv->tx_buffers)
452062306a36Sopenharmony_ci		return;
452162306a36Sopenharmony_ci
452262306a36Sopenharmony_ci	for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
452362306a36Sopenharmony_ci		if (priv->tx_buffers[i].info.d_struct.txb) {
452462306a36Sopenharmony_ci			libipw_txb_free(priv->tx_buffers[i].info.d_struct.
452562306a36Sopenharmony_ci					   txb);
452662306a36Sopenharmony_ci			priv->tx_buffers[i].info.d_struct.txb = NULL;
452762306a36Sopenharmony_ci		}
452862306a36Sopenharmony_ci		if (priv->tx_buffers[i].info.d_struct.data)
452962306a36Sopenharmony_ci			dma_free_coherent(&priv->pci_dev->dev,
453062306a36Sopenharmony_ci					  sizeof(struct ipw2100_data_header),
453162306a36Sopenharmony_ci					  priv->tx_buffers[i].info.d_struct.data,
453262306a36Sopenharmony_ci					  priv->tx_buffers[i].info.d_struct.data_phys);
453362306a36Sopenharmony_ci	}
453462306a36Sopenharmony_ci
453562306a36Sopenharmony_ci	kfree(priv->tx_buffers);
453662306a36Sopenharmony_ci	priv->tx_buffers = NULL;
453762306a36Sopenharmony_ci
453862306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
453962306a36Sopenharmony_ci}
454062306a36Sopenharmony_ci
454162306a36Sopenharmony_cistatic int ipw2100_rx_allocate(struct ipw2100_priv *priv)
454262306a36Sopenharmony_ci{
454362306a36Sopenharmony_ci	int i, j, err = -EINVAL;
454462306a36Sopenharmony_ci
454562306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
454662306a36Sopenharmony_ci
454762306a36Sopenharmony_ci	err = bd_queue_allocate(priv, &priv->rx_queue, RX_QUEUE_LENGTH);
454862306a36Sopenharmony_ci	if (err) {
454962306a36Sopenharmony_ci		IPW_DEBUG_INFO("failed bd_queue_allocate\n");
455062306a36Sopenharmony_ci		return err;
455162306a36Sopenharmony_ci	}
455262306a36Sopenharmony_ci
455362306a36Sopenharmony_ci	err = status_queue_allocate(priv, RX_QUEUE_LENGTH);
455462306a36Sopenharmony_ci	if (err) {
455562306a36Sopenharmony_ci		IPW_DEBUG_INFO("failed status_queue_allocate\n");
455662306a36Sopenharmony_ci		bd_queue_free(priv, &priv->rx_queue);
455762306a36Sopenharmony_ci		return err;
455862306a36Sopenharmony_ci	}
455962306a36Sopenharmony_ci
456062306a36Sopenharmony_ci	/*
456162306a36Sopenharmony_ci	 * allocate packets
456262306a36Sopenharmony_ci	 */
456362306a36Sopenharmony_ci	priv->rx_buffers = kmalloc_array(RX_QUEUE_LENGTH,
456462306a36Sopenharmony_ci					 sizeof(struct ipw2100_rx_packet),
456562306a36Sopenharmony_ci					 GFP_KERNEL);
456662306a36Sopenharmony_ci	if (!priv->rx_buffers) {
456762306a36Sopenharmony_ci		IPW_DEBUG_INFO("can't allocate rx packet buffer table\n");
456862306a36Sopenharmony_ci
456962306a36Sopenharmony_ci		bd_queue_free(priv, &priv->rx_queue);
457062306a36Sopenharmony_ci
457162306a36Sopenharmony_ci		status_queue_free(priv);
457262306a36Sopenharmony_ci
457362306a36Sopenharmony_ci		return -ENOMEM;
457462306a36Sopenharmony_ci	}
457562306a36Sopenharmony_ci
457662306a36Sopenharmony_ci	for (i = 0; i < RX_QUEUE_LENGTH; i++) {
457762306a36Sopenharmony_ci		struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
457862306a36Sopenharmony_ci
457962306a36Sopenharmony_ci		err = ipw2100_alloc_skb(priv, packet);
458062306a36Sopenharmony_ci		if (unlikely(err)) {
458162306a36Sopenharmony_ci			err = -ENOMEM;
458262306a36Sopenharmony_ci			break;
458362306a36Sopenharmony_ci		}
458462306a36Sopenharmony_ci
458562306a36Sopenharmony_ci		/* The BD holds the cache aligned address */
458662306a36Sopenharmony_ci		priv->rx_queue.drv[i].host_addr = packet->dma_addr;
458762306a36Sopenharmony_ci		priv->rx_queue.drv[i].buf_length = IPW_RX_NIC_BUFFER_LENGTH;
458862306a36Sopenharmony_ci		priv->status_queue.drv[i].status_fields = 0;
458962306a36Sopenharmony_ci	}
459062306a36Sopenharmony_ci
459162306a36Sopenharmony_ci	if (i == RX_QUEUE_LENGTH)
459262306a36Sopenharmony_ci		return 0;
459362306a36Sopenharmony_ci
459462306a36Sopenharmony_ci	for (j = 0; j < i; j++) {
459562306a36Sopenharmony_ci		dma_unmap_single(&priv->pci_dev->dev,
459662306a36Sopenharmony_ci				 priv->rx_buffers[j].dma_addr,
459762306a36Sopenharmony_ci				 sizeof(struct ipw2100_rx_packet),
459862306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
459962306a36Sopenharmony_ci		dev_kfree_skb(priv->rx_buffers[j].skb);
460062306a36Sopenharmony_ci	}
460162306a36Sopenharmony_ci
460262306a36Sopenharmony_ci	kfree(priv->rx_buffers);
460362306a36Sopenharmony_ci	priv->rx_buffers = NULL;
460462306a36Sopenharmony_ci
460562306a36Sopenharmony_ci	bd_queue_free(priv, &priv->rx_queue);
460662306a36Sopenharmony_ci
460762306a36Sopenharmony_ci	status_queue_free(priv);
460862306a36Sopenharmony_ci
460962306a36Sopenharmony_ci	return err;
461062306a36Sopenharmony_ci}
461162306a36Sopenharmony_ci
461262306a36Sopenharmony_cistatic void ipw2100_rx_initialize(struct ipw2100_priv *priv)
461362306a36Sopenharmony_ci{
461462306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
461562306a36Sopenharmony_ci
461662306a36Sopenharmony_ci	priv->rx_queue.oldest = 0;
461762306a36Sopenharmony_ci	priv->rx_queue.available = priv->rx_queue.entries - 1;
461862306a36Sopenharmony_ci	priv->rx_queue.next = priv->rx_queue.entries - 1;
461962306a36Sopenharmony_ci
462062306a36Sopenharmony_ci	INIT_STAT(&priv->rxq_stat);
462162306a36Sopenharmony_ci	SET_STAT(&priv->rxq_stat, priv->rx_queue.available);
462262306a36Sopenharmony_ci
462362306a36Sopenharmony_ci	bd_queue_initialize(priv, &priv->rx_queue,
462462306a36Sopenharmony_ci			    IPW_MEM_HOST_SHARED_RX_BD_BASE,
462562306a36Sopenharmony_ci			    IPW_MEM_HOST_SHARED_RX_BD_SIZE,
462662306a36Sopenharmony_ci			    IPW_MEM_HOST_SHARED_RX_READ_INDEX,
462762306a36Sopenharmony_ci			    IPW_MEM_HOST_SHARED_RX_WRITE_INDEX);
462862306a36Sopenharmony_ci
462962306a36Sopenharmony_ci	/* set up the status queue */
463062306a36Sopenharmony_ci	write_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_STATUS_BASE,
463162306a36Sopenharmony_ci		       priv->status_queue.nic);
463262306a36Sopenharmony_ci
463362306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
463462306a36Sopenharmony_ci}
463562306a36Sopenharmony_ci
463662306a36Sopenharmony_cistatic void ipw2100_rx_free(struct ipw2100_priv *priv)
463762306a36Sopenharmony_ci{
463862306a36Sopenharmony_ci	int i;
463962306a36Sopenharmony_ci
464062306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
464162306a36Sopenharmony_ci
464262306a36Sopenharmony_ci	bd_queue_free(priv, &priv->rx_queue);
464362306a36Sopenharmony_ci	status_queue_free(priv);
464462306a36Sopenharmony_ci
464562306a36Sopenharmony_ci	if (!priv->rx_buffers)
464662306a36Sopenharmony_ci		return;
464762306a36Sopenharmony_ci
464862306a36Sopenharmony_ci	for (i = 0; i < RX_QUEUE_LENGTH; i++) {
464962306a36Sopenharmony_ci		if (priv->rx_buffers[i].rxp) {
465062306a36Sopenharmony_ci			dma_unmap_single(&priv->pci_dev->dev,
465162306a36Sopenharmony_ci					 priv->rx_buffers[i].dma_addr,
465262306a36Sopenharmony_ci					 sizeof(struct ipw2100_rx),
465362306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
465462306a36Sopenharmony_ci			dev_kfree_skb(priv->rx_buffers[i].skb);
465562306a36Sopenharmony_ci		}
465662306a36Sopenharmony_ci	}
465762306a36Sopenharmony_ci
465862306a36Sopenharmony_ci	kfree(priv->rx_buffers);
465962306a36Sopenharmony_ci	priv->rx_buffers = NULL;
466062306a36Sopenharmony_ci
466162306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
466262306a36Sopenharmony_ci}
466362306a36Sopenharmony_ci
466462306a36Sopenharmony_cistatic int ipw2100_read_mac_address(struct ipw2100_priv *priv)
466562306a36Sopenharmony_ci{
466662306a36Sopenharmony_ci	u32 length = ETH_ALEN;
466762306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
466862306a36Sopenharmony_ci
466962306a36Sopenharmony_ci	int err;
467062306a36Sopenharmony_ci
467162306a36Sopenharmony_ci	err = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ADAPTER_MAC, addr, &length);
467262306a36Sopenharmony_ci	if (err) {
467362306a36Sopenharmony_ci		IPW_DEBUG_INFO("MAC address read failed\n");
467462306a36Sopenharmony_ci		return -EIO;
467562306a36Sopenharmony_ci	}
467662306a36Sopenharmony_ci
467762306a36Sopenharmony_ci	eth_hw_addr_set(priv->net_dev, addr);
467862306a36Sopenharmony_ci	IPW_DEBUG_INFO("card MAC is %pM\n", priv->net_dev->dev_addr);
467962306a36Sopenharmony_ci
468062306a36Sopenharmony_ci	return 0;
468162306a36Sopenharmony_ci}
468262306a36Sopenharmony_ci
468362306a36Sopenharmony_ci/********************************************************************
468462306a36Sopenharmony_ci *
468562306a36Sopenharmony_ci * Firmware Commands
468662306a36Sopenharmony_ci *
468762306a36Sopenharmony_ci ********************************************************************/
468862306a36Sopenharmony_ci
468962306a36Sopenharmony_cistatic int ipw2100_set_mac_address(struct ipw2100_priv *priv, int batch_mode)
469062306a36Sopenharmony_ci{
469162306a36Sopenharmony_ci	struct host_command cmd = {
469262306a36Sopenharmony_ci		.host_command = ADAPTER_ADDRESS,
469362306a36Sopenharmony_ci		.host_command_sequence = 0,
469462306a36Sopenharmony_ci		.host_command_length = ETH_ALEN
469562306a36Sopenharmony_ci	};
469662306a36Sopenharmony_ci	int err;
469762306a36Sopenharmony_ci
469862306a36Sopenharmony_ci	IPW_DEBUG_HC("SET_MAC_ADDRESS\n");
469962306a36Sopenharmony_ci
470062306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
470162306a36Sopenharmony_ci
470262306a36Sopenharmony_ci	if (priv->config & CFG_CUSTOM_MAC) {
470362306a36Sopenharmony_ci		memcpy(cmd.host_command_parameters, priv->mac_addr, ETH_ALEN);
470462306a36Sopenharmony_ci		eth_hw_addr_set(priv->net_dev, priv->mac_addr);
470562306a36Sopenharmony_ci	} else
470662306a36Sopenharmony_ci		memcpy(cmd.host_command_parameters, priv->net_dev->dev_addr,
470762306a36Sopenharmony_ci		       ETH_ALEN);
470862306a36Sopenharmony_ci
470962306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
471062306a36Sopenharmony_ci
471162306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
471262306a36Sopenharmony_ci	return err;
471362306a36Sopenharmony_ci}
471462306a36Sopenharmony_ci
471562306a36Sopenharmony_cistatic int ipw2100_set_port_type(struct ipw2100_priv *priv, u32 port_type,
471662306a36Sopenharmony_ci				 int batch_mode)
471762306a36Sopenharmony_ci{
471862306a36Sopenharmony_ci	struct host_command cmd = {
471962306a36Sopenharmony_ci		.host_command = PORT_TYPE,
472062306a36Sopenharmony_ci		.host_command_sequence = 0,
472162306a36Sopenharmony_ci		.host_command_length = sizeof(u32)
472262306a36Sopenharmony_ci	};
472362306a36Sopenharmony_ci	int err;
472462306a36Sopenharmony_ci
472562306a36Sopenharmony_ci	switch (port_type) {
472662306a36Sopenharmony_ci	case IW_MODE_INFRA:
472762306a36Sopenharmony_ci		cmd.host_command_parameters[0] = IPW_BSS;
472862306a36Sopenharmony_ci		break;
472962306a36Sopenharmony_ci	case IW_MODE_ADHOC:
473062306a36Sopenharmony_ci		cmd.host_command_parameters[0] = IPW_IBSS;
473162306a36Sopenharmony_ci		break;
473262306a36Sopenharmony_ci	}
473362306a36Sopenharmony_ci
473462306a36Sopenharmony_ci	IPW_DEBUG_HC("PORT_TYPE: %s\n",
473562306a36Sopenharmony_ci		     port_type == IPW_IBSS ? "Ad-Hoc" : "Managed");
473662306a36Sopenharmony_ci
473762306a36Sopenharmony_ci	if (!batch_mode) {
473862306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
473962306a36Sopenharmony_ci		if (err) {
474062306a36Sopenharmony_ci			printk(KERN_ERR DRV_NAME
474162306a36Sopenharmony_ci			       ": %s: Could not disable adapter %d\n",
474262306a36Sopenharmony_ci			       priv->net_dev->name, err);
474362306a36Sopenharmony_ci			return err;
474462306a36Sopenharmony_ci		}
474562306a36Sopenharmony_ci	}
474662306a36Sopenharmony_ci
474762306a36Sopenharmony_ci	/* send cmd to firmware */
474862306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
474962306a36Sopenharmony_ci
475062306a36Sopenharmony_ci	if (!batch_mode)
475162306a36Sopenharmony_ci		ipw2100_enable_adapter(priv);
475262306a36Sopenharmony_ci
475362306a36Sopenharmony_ci	return err;
475462306a36Sopenharmony_ci}
475562306a36Sopenharmony_ci
475662306a36Sopenharmony_cistatic int ipw2100_set_channel(struct ipw2100_priv *priv, u32 channel,
475762306a36Sopenharmony_ci			       int batch_mode)
475862306a36Sopenharmony_ci{
475962306a36Sopenharmony_ci	struct host_command cmd = {
476062306a36Sopenharmony_ci		.host_command = CHANNEL,
476162306a36Sopenharmony_ci		.host_command_sequence = 0,
476262306a36Sopenharmony_ci		.host_command_length = sizeof(u32)
476362306a36Sopenharmony_ci	};
476462306a36Sopenharmony_ci	int err;
476562306a36Sopenharmony_ci
476662306a36Sopenharmony_ci	cmd.host_command_parameters[0] = channel;
476762306a36Sopenharmony_ci
476862306a36Sopenharmony_ci	IPW_DEBUG_HC("CHANNEL: %d\n", channel);
476962306a36Sopenharmony_ci
477062306a36Sopenharmony_ci	/* If BSS then we don't support channel selection */
477162306a36Sopenharmony_ci	if (priv->ieee->iw_mode == IW_MODE_INFRA)
477262306a36Sopenharmony_ci		return 0;
477362306a36Sopenharmony_ci
477462306a36Sopenharmony_ci	if ((channel != 0) &&
477562306a36Sopenharmony_ci	    ((channel < REG_MIN_CHANNEL) || (channel > REG_MAX_CHANNEL)))
477662306a36Sopenharmony_ci		return -EINVAL;
477762306a36Sopenharmony_ci
477862306a36Sopenharmony_ci	if (!batch_mode) {
477962306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
478062306a36Sopenharmony_ci		if (err)
478162306a36Sopenharmony_ci			return err;
478262306a36Sopenharmony_ci	}
478362306a36Sopenharmony_ci
478462306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
478562306a36Sopenharmony_ci	if (err) {
478662306a36Sopenharmony_ci		IPW_DEBUG_INFO("Failed to set channel to %d", channel);
478762306a36Sopenharmony_ci		return err;
478862306a36Sopenharmony_ci	}
478962306a36Sopenharmony_ci
479062306a36Sopenharmony_ci	if (channel)
479162306a36Sopenharmony_ci		priv->config |= CFG_STATIC_CHANNEL;
479262306a36Sopenharmony_ci	else
479362306a36Sopenharmony_ci		priv->config &= ~CFG_STATIC_CHANNEL;
479462306a36Sopenharmony_ci
479562306a36Sopenharmony_ci	priv->channel = channel;
479662306a36Sopenharmony_ci
479762306a36Sopenharmony_ci	if (!batch_mode) {
479862306a36Sopenharmony_ci		err = ipw2100_enable_adapter(priv);
479962306a36Sopenharmony_ci		if (err)
480062306a36Sopenharmony_ci			return err;
480162306a36Sopenharmony_ci	}
480262306a36Sopenharmony_ci
480362306a36Sopenharmony_ci	return 0;
480462306a36Sopenharmony_ci}
480562306a36Sopenharmony_ci
480662306a36Sopenharmony_cistatic int ipw2100_system_config(struct ipw2100_priv *priv, int batch_mode)
480762306a36Sopenharmony_ci{
480862306a36Sopenharmony_ci	struct host_command cmd = {
480962306a36Sopenharmony_ci		.host_command = SYSTEM_CONFIG,
481062306a36Sopenharmony_ci		.host_command_sequence = 0,
481162306a36Sopenharmony_ci		.host_command_length = 12,
481262306a36Sopenharmony_ci	};
481362306a36Sopenharmony_ci	u32 ibss_mask, len = sizeof(u32);
481462306a36Sopenharmony_ci	int err;
481562306a36Sopenharmony_ci
481662306a36Sopenharmony_ci	/* Set system configuration */
481762306a36Sopenharmony_ci
481862306a36Sopenharmony_ci	if (!batch_mode) {
481962306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
482062306a36Sopenharmony_ci		if (err)
482162306a36Sopenharmony_ci			return err;
482262306a36Sopenharmony_ci	}
482362306a36Sopenharmony_ci
482462306a36Sopenharmony_ci	if (priv->ieee->iw_mode == IW_MODE_ADHOC)
482562306a36Sopenharmony_ci		cmd.host_command_parameters[0] |= IPW_CFG_IBSS_AUTO_START;
482662306a36Sopenharmony_ci
482762306a36Sopenharmony_ci	cmd.host_command_parameters[0] |= IPW_CFG_IBSS_MASK |
482862306a36Sopenharmony_ci	    IPW_CFG_BSS_MASK | IPW_CFG_802_1x_ENABLE;
482962306a36Sopenharmony_ci
483062306a36Sopenharmony_ci	if (!(priv->config & CFG_LONG_PREAMBLE))
483162306a36Sopenharmony_ci		cmd.host_command_parameters[0] |= IPW_CFG_PREAMBLE_AUTO;
483262306a36Sopenharmony_ci
483362306a36Sopenharmony_ci	err = ipw2100_get_ordinal(priv,
483462306a36Sopenharmony_ci				  IPW_ORD_EEPROM_IBSS_11B_CHANNELS,
483562306a36Sopenharmony_ci				  &ibss_mask, &len);
483662306a36Sopenharmony_ci	if (err)
483762306a36Sopenharmony_ci		ibss_mask = IPW_IBSS_11B_DEFAULT_MASK;
483862306a36Sopenharmony_ci
483962306a36Sopenharmony_ci	cmd.host_command_parameters[1] = REG_CHANNEL_MASK;
484062306a36Sopenharmony_ci	cmd.host_command_parameters[2] = REG_CHANNEL_MASK & ibss_mask;
484162306a36Sopenharmony_ci
484262306a36Sopenharmony_ci	/* 11b only */
484362306a36Sopenharmony_ci	/*cmd.host_command_parameters[0] |= DIVERSITY_ANTENNA_A; */
484462306a36Sopenharmony_ci
484562306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
484662306a36Sopenharmony_ci	if (err)
484762306a36Sopenharmony_ci		return err;
484862306a36Sopenharmony_ci
484962306a36Sopenharmony_ci/* If IPv6 is configured in the kernel then we don't want to filter out all
485062306a36Sopenharmony_ci * of the multicast packets as IPv6 needs some. */
485162306a36Sopenharmony_ci#if !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE)
485262306a36Sopenharmony_ci	cmd.host_command = ADD_MULTICAST;
485362306a36Sopenharmony_ci	cmd.host_command_sequence = 0;
485462306a36Sopenharmony_ci	cmd.host_command_length = 0;
485562306a36Sopenharmony_ci
485662306a36Sopenharmony_ci	ipw2100_hw_send_command(priv, &cmd);
485762306a36Sopenharmony_ci#endif
485862306a36Sopenharmony_ci	if (!batch_mode) {
485962306a36Sopenharmony_ci		err = ipw2100_enable_adapter(priv);
486062306a36Sopenharmony_ci		if (err)
486162306a36Sopenharmony_ci			return err;
486262306a36Sopenharmony_ci	}
486362306a36Sopenharmony_ci
486462306a36Sopenharmony_ci	return 0;
486562306a36Sopenharmony_ci}
486662306a36Sopenharmony_ci
486762306a36Sopenharmony_cistatic int ipw2100_set_tx_rates(struct ipw2100_priv *priv, u32 rate,
486862306a36Sopenharmony_ci				int batch_mode)
486962306a36Sopenharmony_ci{
487062306a36Sopenharmony_ci	struct host_command cmd = {
487162306a36Sopenharmony_ci		.host_command = BASIC_TX_RATES,
487262306a36Sopenharmony_ci		.host_command_sequence = 0,
487362306a36Sopenharmony_ci		.host_command_length = 4
487462306a36Sopenharmony_ci	};
487562306a36Sopenharmony_ci	int err;
487662306a36Sopenharmony_ci
487762306a36Sopenharmony_ci	cmd.host_command_parameters[0] = rate & TX_RATE_MASK;
487862306a36Sopenharmony_ci
487962306a36Sopenharmony_ci	if (!batch_mode) {
488062306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
488162306a36Sopenharmony_ci		if (err)
488262306a36Sopenharmony_ci			return err;
488362306a36Sopenharmony_ci	}
488462306a36Sopenharmony_ci
488562306a36Sopenharmony_ci	/* Set BASIC TX Rate first */
488662306a36Sopenharmony_ci	ipw2100_hw_send_command(priv, &cmd);
488762306a36Sopenharmony_ci
488862306a36Sopenharmony_ci	/* Set TX Rate */
488962306a36Sopenharmony_ci	cmd.host_command = TX_RATES;
489062306a36Sopenharmony_ci	ipw2100_hw_send_command(priv, &cmd);
489162306a36Sopenharmony_ci
489262306a36Sopenharmony_ci	/* Set MSDU TX Rate */
489362306a36Sopenharmony_ci	cmd.host_command = MSDU_TX_RATES;
489462306a36Sopenharmony_ci	ipw2100_hw_send_command(priv, &cmd);
489562306a36Sopenharmony_ci
489662306a36Sopenharmony_ci	if (!batch_mode) {
489762306a36Sopenharmony_ci		err = ipw2100_enable_adapter(priv);
489862306a36Sopenharmony_ci		if (err)
489962306a36Sopenharmony_ci			return err;
490062306a36Sopenharmony_ci	}
490162306a36Sopenharmony_ci
490262306a36Sopenharmony_ci	priv->tx_rates = rate;
490362306a36Sopenharmony_ci
490462306a36Sopenharmony_ci	return 0;
490562306a36Sopenharmony_ci}
490662306a36Sopenharmony_ci
490762306a36Sopenharmony_cistatic int ipw2100_set_power_mode(struct ipw2100_priv *priv, int power_level)
490862306a36Sopenharmony_ci{
490962306a36Sopenharmony_ci	struct host_command cmd = {
491062306a36Sopenharmony_ci		.host_command = POWER_MODE,
491162306a36Sopenharmony_ci		.host_command_sequence = 0,
491262306a36Sopenharmony_ci		.host_command_length = 4
491362306a36Sopenharmony_ci	};
491462306a36Sopenharmony_ci	int err;
491562306a36Sopenharmony_ci
491662306a36Sopenharmony_ci	cmd.host_command_parameters[0] = power_level;
491762306a36Sopenharmony_ci
491862306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
491962306a36Sopenharmony_ci	if (err)
492062306a36Sopenharmony_ci		return err;
492162306a36Sopenharmony_ci
492262306a36Sopenharmony_ci	if (power_level == IPW_POWER_MODE_CAM)
492362306a36Sopenharmony_ci		priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
492462306a36Sopenharmony_ci	else
492562306a36Sopenharmony_ci		priv->power_mode = IPW_POWER_ENABLED | power_level;
492662306a36Sopenharmony_ci
492762306a36Sopenharmony_ci#ifdef IPW2100_TX_POWER
492862306a36Sopenharmony_ci	if (priv->port_type == IBSS && priv->adhoc_power != DFTL_IBSS_TX_POWER) {
492962306a36Sopenharmony_ci		/* Set beacon interval */
493062306a36Sopenharmony_ci		cmd.host_command = TX_POWER_INDEX;
493162306a36Sopenharmony_ci		cmd.host_command_parameters[0] = (u32) priv->adhoc_power;
493262306a36Sopenharmony_ci
493362306a36Sopenharmony_ci		err = ipw2100_hw_send_command(priv, &cmd);
493462306a36Sopenharmony_ci		if (err)
493562306a36Sopenharmony_ci			return err;
493662306a36Sopenharmony_ci	}
493762306a36Sopenharmony_ci#endif
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_ci	return 0;
494062306a36Sopenharmony_ci}
494162306a36Sopenharmony_ci
494262306a36Sopenharmony_cistatic int ipw2100_set_rts_threshold(struct ipw2100_priv *priv, u32 threshold)
494362306a36Sopenharmony_ci{
494462306a36Sopenharmony_ci	struct host_command cmd = {
494562306a36Sopenharmony_ci		.host_command = RTS_THRESHOLD,
494662306a36Sopenharmony_ci		.host_command_sequence = 0,
494762306a36Sopenharmony_ci		.host_command_length = 4
494862306a36Sopenharmony_ci	};
494962306a36Sopenharmony_ci	int err;
495062306a36Sopenharmony_ci
495162306a36Sopenharmony_ci	if (threshold & RTS_DISABLED)
495262306a36Sopenharmony_ci		cmd.host_command_parameters[0] = MAX_RTS_THRESHOLD;
495362306a36Sopenharmony_ci	else
495462306a36Sopenharmony_ci		cmd.host_command_parameters[0] = threshold & ~RTS_DISABLED;
495562306a36Sopenharmony_ci
495662306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
495762306a36Sopenharmony_ci	if (err)
495862306a36Sopenharmony_ci		return err;
495962306a36Sopenharmony_ci
496062306a36Sopenharmony_ci	priv->rts_threshold = threshold;
496162306a36Sopenharmony_ci
496262306a36Sopenharmony_ci	return 0;
496362306a36Sopenharmony_ci}
496462306a36Sopenharmony_ci
496562306a36Sopenharmony_ci#if 0
496662306a36Sopenharmony_ciint ipw2100_set_fragmentation_threshold(struct ipw2100_priv *priv,
496762306a36Sopenharmony_ci					u32 threshold, int batch_mode)
496862306a36Sopenharmony_ci{
496962306a36Sopenharmony_ci	struct host_command cmd = {
497062306a36Sopenharmony_ci		.host_command = FRAG_THRESHOLD,
497162306a36Sopenharmony_ci		.host_command_sequence = 0,
497262306a36Sopenharmony_ci		.host_command_length = 4,
497362306a36Sopenharmony_ci		.host_command_parameters[0] = 0,
497462306a36Sopenharmony_ci	};
497562306a36Sopenharmony_ci	int err;
497662306a36Sopenharmony_ci
497762306a36Sopenharmony_ci	if (!batch_mode) {
497862306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
497962306a36Sopenharmony_ci		if (err)
498062306a36Sopenharmony_ci			return err;
498162306a36Sopenharmony_ci	}
498262306a36Sopenharmony_ci
498362306a36Sopenharmony_ci	if (threshold == 0)
498462306a36Sopenharmony_ci		threshold = DEFAULT_FRAG_THRESHOLD;
498562306a36Sopenharmony_ci	else {
498662306a36Sopenharmony_ci		threshold = max(threshold, MIN_FRAG_THRESHOLD);
498762306a36Sopenharmony_ci		threshold = min(threshold, MAX_FRAG_THRESHOLD);
498862306a36Sopenharmony_ci	}
498962306a36Sopenharmony_ci
499062306a36Sopenharmony_ci	cmd.host_command_parameters[0] = threshold;
499162306a36Sopenharmony_ci
499262306a36Sopenharmony_ci	IPW_DEBUG_HC("FRAG_THRESHOLD: %u\n", threshold);
499362306a36Sopenharmony_ci
499462306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
499562306a36Sopenharmony_ci
499662306a36Sopenharmony_ci	if (!batch_mode)
499762306a36Sopenharmony_ci		ipw2100_enable_adapter(priv);
499862306a36Sopenharmony_ci
499962306a36Sopenharmony_ci	if (!err)
500062306a36Sopenharmony_ci		priv->frag_threshold = threshold;
500162306a36Sopenharmony_ci
500262306a36Sopenharmony_ci	return err;
500362306a36Sopenharmony_ci}
500462306a36Sopenharmony_ci#endif
500562306a36Sopenharmony_ci
500662306a36Sopenharmony_cistatic int ipw2100_set_short_retry(struct ipw2100_priv *priv, u32 retry)
500762306a36Sopenharmony_ci{
500862306a36Sopenharmony_ci	struct host_command cmd = {
500962306a36Sopenharmony_ci		.host_command = SHORT_RETRY_LIMIT,
501062306a36Sopenharmony_ci		.host_command_sequence = 0,
501162306a36Sopenharmony_ci		.host_command_length = 4
501262306a36Sopenharmony_ci	};
501362306a36Sopenharmony_ci	int err;
501462306a36Sopenharmony_ci
501562306a36Sopenharmony_ci	cmd.host_command_parameters[0] = retry;
501662306a36Sopenharmony_ci
501762306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
501862306a36Sopenharmony_ci	if (err)
501962306a36Sopenharmony_ci		return err;
502062306a36Sopenharmony_ci
502162306a36Sopenharmony_ci	priv->short_retry_limit = retry;
502262306a36Sopenharmony_ci
502362306a36Sopenharmony_ci	return 0;
502462306a36Sopenharmony_ci}
502562306a36Sopenharmony_ci
502662306a36Sopenharmony_cistatic int ipw2100_set_long_retry(struct ipw2100_priv *priv, u32 retry)
502762306a36Sopenharmony_ci{
502862306a36Sopenharmony_ci	struct host_command cmd = {
502962306a36Sopenharmony_ci		.host_command = LONG_RETRY_LIMIT,
503062306a36Sopenharmony_ci		.host_command_sequence = 0,
503162306a36Sopenharmony_ci		.host_command_length = 4
503262306a36Sopenharmony_ci	};
503362306a36Sopenharmony_ci	int err;
503462306a36Sopenharmony_ci
503562306a36Sopenharmony_ci	cmd.host_command_parameters[0] = retry;
503662306a36Sopenharmony_ci
503762306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
503862306a36Sopenharmony_ci	if (err)
503962306a36Sopenharmony_ci		return err;
504062306a36Sopenharmony_ci
504162306a36Sopenharmony_ci	priv->long_retry_limit = retry;
504262306a36Sopenharmony_ci
504362306a36Sopenharmony_ci	return 0;
504462306a36Sopenharmony_ci}
504562306a36Sopenharmony_ci
504662306a36Sopenharmony_cistatic int ipw2100_set_mandatory_bssid(struct ipw2100_priv *priv, u8 * bssid,
504762306a36Sopenharmony_ci				       int batch_mode)
504862306a36Sopenharmony_ci{
504962306a36Sopenharmony_ci	struct host_command cmd = {
505062306a36Sopenharmony_ci		.host_command = MANDATORY_BSSID,
505162306a36Sopenharmony_ci		.host_command_sequence = 0,
505262306a36Sopenharmony_ci		.host_command_length = (bssid == NULL) ? 0 : ETH_ALEN
505362306a36Sopenharmony_ci	};
505462306a36Sopenharmony_ci	int err;
505562306a36Sopenharmony_ci
505662306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
505762306a36Sopenharmony_ci	if (bssid != NULL)
505862306a36Sopenharmony_ci		IPW_DEBUG_HC("MANDATORY_BSSID: %pM\n", bssid);
505962306a36Sopenharmony_ci	else
506062306a36Sopenharmony_ci		IPW_DEBUG_HC("MANDATORY_BSSID: <clear>\n");
506162306a36Sopenharmony_ci#endif
506262306a36Sopenharmony_ci	/* if BSSID is empty then we disable mandatory bssid mode */
506362306a36Sopenharmony_ci	if (bssid != NULL)
506462306a36Sopenharmony_ci		memcpy(cmd.host_command_parameters, bssid, ETH_ALEN);
506562306a36Sopenharmony_ci
506662306a36Sopenharmony_ci	if (!batch_mode) {
506762306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
506862306a36Sopenharmony_ci		if (err)
506962306a36Sopenharmony_ci			return err;
507062306a36Sopenharmony_ci	}
507162306a36Sopenharmony_ci
507262306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
507362306a36Sopenharmony_ci
507462306a36Sopenharmony_ci	if (!batch_mode)
507562306a36Sopenharmony_ci		ipw2100_enable_adapter(priv);
507662306a36Sopenharmony_ci
507762306a36Sopenharmony_ci	return err;
507862306a36Sopenharmony_ci}
507962306a36Sopenharmony_ci
508062306a36Sopenharmony_cistatic int ipw2100_disassociate_bssid(struct ipw2100_priv *priv)
508162306a36Sopenharmony_ci{
508262306a36Sopenharmony_ci	struct host_command cmd = {
508362306a36Sopenharmony_ci		.host_command = DISASSOCIATION_BSSID,
508462306a36Sopenharmony_ci		.host_command_sequence = 0,
508562306a36Sopenharmony_ci		.host_command_length = ETH_ALEN
508662306a36Sopenharmony_ci	};
508762306a36Sopenharmony_ci	int err;
508862306a36Sopenharmony_ci
508962306a36Sopenharmony_ci	IPW_DEBUG_HC("DISASSOCIATION_BSSID\n");
509062306a36Sopenharmony_ci
509162306a36Sopenharmony_ci	/* The Firmware currently ignores the BSSID and just disassociates from
509262306a36Sopenharmony_ci	 * the currently associated AP -- but in the off chance that a future
509362306a36Sopenharmony_ci	 * firmware does use the BSSID provided here, we go ahead and try and
509462306a36Sopenharmony_ci	 * set it to the currently associated AP's BSSID */
509562306a36Sopenharmony_ci	memcpy(cmd.host_command_parameters, priv->bssid, ETH_ALEN);
509662306a36Sopenharmony_ci
509762306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
509862306a36Sopenharmony_ci
509962306a36Sopenharmony_ci	return err;
510062306a36Sopenharmony_ci}
510162306a36Sopenharmony_ci
510262306a36Sopenharmony_cistatic int ipw2100_set_wpa_ie(struct ipw2100_priv *,
510362306a36Sopenharmony_ci			      struct ipw2100_wpa_assoc_frame *, int)
510462306a36Sopenharmony_ci    __attribute__ ((unused));
510562306a36Sopenharmony_ci
510662306a36Sopenharmony_cistatic int ipw2100_set_wpa_ie(struct ipw2100_priv *priv,
510762306a36Sopenharmony_ci			      struct ipw2100_wpa_assoc_frame *wpa_frame,
510862306a36Sopenharmony_ci			      int batch_mode)
510962306a36Sopenharmony_ci{
511062306a36Sopenharmony_ci	struct host_command cmd = {
511162306a36Sopenharmony_ci		.host_command = SET_WPA_IE,
511262306a36Sopenharmony_ci		.host_command_sequence = 0,
511362306a36Sopenharmony_ci		.host_command_length = sizeof(struct ipw2100_wpa_assoc_frame),
511462306a36Sopenharmony_ci	};
511562306a36Sopenharmony_ci	int err;
511662306a36Sopenharmony_ci
511762306a36Sopenharmony_ci	IPW_DEBUG_HC("SET_WPA_IE\n");
511862306a36Sopenharmony_ci
511962306a36Sopenharmony_ci	if (!batch_mode) {
512062306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
512162306a36Sopenharmony_ci		if (err)
512262306a36Sopenharmony_ci			return err;
512362306a36Sopenharmony_ci	}
512462306a36Sopenharmony_ci
512562306a36Sopenharmony_ci	memcpy(cmd.host_command_parameters, wpa_frame,
512662306a36Sopenharmony_ci	       sizeof(struct ipw2100_wpa_assoc_frame));
512762306a36Sopenharmony_ci
512862306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
512962306a36Sopenharmony_ci
513062306a36Sopenharmony_ci	if (!batch_mode) {
513162306a36Sopenharmony_ci		if (ipw2100_enable_adapter(priv))
513262306a36Sopenharmony_ci			err = -EIO;
513362306a36Sopenharmony_ci	}
513462306a36Sopenharmony_ci
513562306a36Sopenharmony_ci	return err;
513662306a36Sopenharmony_ci}
513762306a36Sopenharmony_ci
513862306a36Sopenharmony_cistruct security_info_params {
513962306a36Sopenharmony_ci	u32 allowed_ciphers;
514062306a36Sopenharmony_ci	u16 version;
514162306a36Sopenharmony_ci	u8 auth_mode;
514262306a36Sopenharmony_ci	u8 replay_counters_number;
514362306a36Sopenharmony_ci	u8 unicast_using_group;
514462306a36Sopenharmony_ci} __packed;
514562306a36Sopenharmony_ci
514662306a36Sopenharmony_cistatic int ipw2100_set_security_information(struct ipw2100_priv *priv,
514762306a36Sopenharmony_ci					    int auth_mode,
514862306a36Sopenharmony_ci					    int security_level,
514962306a36Sopenharmony_ci					    int unicast_using_group,
515062306a36Sopenharmony_ci					    int batch_mode)
515162306a36Sopenharmony_ci{
515262306a36Sopenharmony_ci	struct host_command cmd = {
515362306a36Sopenharmony_ci		.host_command = SET_SECURITY_INFORMATION,
515462306a36Sopenharmony_ci		.host_command_sequence = 0,
515562306a36Sopenharmony_ci		.host_command_length = sizeof(struct security_info_params)
515662306a36Sopenharmony_ci	};
515762306a36Sopenharmony_ci	struct security_info_params *security =
515862306a36Sopenharmony_ci	    (struct security_info_params *)&cmd.host_command_parameters;
515962306a36Sopenharmony_ci	int err;
516062306a36Sopenharmony_ci	memset(security, 0, sizeof(*security));
516162306a36Sopenharmony_ci
516262306a36Sopenharmony_ci	/* If shared key AP authentication is turned on, then we need to
516362306a36Sopenharmony_ci	 * configure the firmware to try and use it.
516462306a36Sopenharmony_ci	 *
516562306a36Sopenharmony_ci	 * Actual data encryption/decryption is handled by the host. */
516662306a36Sopenharmony_ci	security->auth_mode = auth_mode;
516762306a36Sopenharmony_ci	security->unicast_using_group = unicast_using_group;
516862306a36Sopenharmony_ci
516962306a36Sopenharmony_ci	switch (security_level) {
517062306a36Sopenharmony_ci	default:
517162306a36Sopenharmony_ci	case SEC_LEVEL_0:
517262306a36Sopenharmony_ci		security->allowed_ciphers = IPW_NONE_CIPHER;
517362306a36Sopenharmony_ci		break;
517462306a36Sopenharmony_ci	case SEC_LEVEL_1:
517562306a36Sopenharmony_ci		security->allowed_ciphers = IPW_WEP40_CIPHER |
517662306a36Sopenharmony_ci		    IPW_WEP104_CIPHER;
517762306a36Sopenharmony_ci		break;
517862306a36Sopenharmony_ci	case SEC_LEVEL_2:
517962306a36Sopenharmony_ci		security->allowed_ciphers = IPW_WEP40_CIPHER |
518062306a36Sopenharmony_ci		    IPW_WEP104_CIPHER | IPW_TKIP_CIPHER;
518162306a36Sopenharmony_ci		break;
518262306a36Sopenharmony_ci	case SEC_LEVEL_2_CKIP:
518362306a36Sopenharmony_ci		security->allowed_ciphers = IPW_WEP40_CIPHER |
518462306a36Sopenharmony_ci		    IPW_WEP104_CIPHER | IPW_CKIP_CIPHER;
518562306a36Sopenharmony_ci		break;
518662306a36Sopenharmony_ci	case SEC_LEVEL_3:
518762306a36Sopenharmony_ci		security->allowed_ciphers = IPW_WEP40_CIPHER |
518862306a36Sopenharmony_ci		    IPW_WEP104_CIPHER | IPW_TKIP_CIPHER | IPW_CCMP_CIPHER;
518962306a36Sopenharmony_ci		break;
519062306a36Sopenharmony_ci	}
519162306a36Sopenharmony_ci
519262306a36Sopenharmony_ci	IPW_DEBUG_HC
519362306a36Sopenharmony_ci	    ("SET_SECURITY_INFORMATION: auth:%d cipher:0x%02X (level %d)\n",
519462306a36Sopenharmony_ci	     security->auth_mode, security->allowed_ciphers, security_level);
519562306a36Sopenharmony_ci
519662306a36Sopenharmony_ci	security->replay_counters_number = 0;
519762306a36Sopenharmony_ci
519862306a36Sopenharmony_ci	if (!batch_mode) {
519962306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
520062306a36Sopenharmony_ci		if (err)
520162306a36Sopenharmony_ci			return err;
520262306a36Sopenharmony_ci	}
520362306a36Sopenharmony_ci
520462306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
520562306a36Sopenharmony_ci
520662306a36Sopenharmony_ci	if (!batch_mode)
520762306a36Sopenharmony_ci		ipw2100_enable_adapter(priv);
520862306a36Sopenharmony_ci
520962306a36Sopenharmony_ci	return err;
521062306a36Sopenharmony_ci}
521162306a36Sopenharmony_ci
521262306a36Sopenharmony_cistatic int ipw2100_set_tx_power(struct ipw2100_priv *priv, u32 tx_power)
521362306a36Sopenharmony_ci{
521462306a36Sopenharmony_ci	struct host_command cmd = {
521562306a36Sopenharmony_ci		.host_command = TX_POWER_INDEX,
521662306a36Sopenharmony_ci		.host_command_sequence = 0,
521762306a36Sopenharmony_ci		.host_command_length = 4
521862306a36Sopenharmony_ci	};
521962306a36Sopenharmony_ci	int err = 0;
522062306a36Sopenharmony_ci	u32 tmp = tx_power;
522162306a36Sopenharmony_ci
522262306a36Sopenharmony_ci	if (tx_power != IPW_TX_POWER_DEFAULT)
522362306a36Sopenharmony_ci		tmp = (tx_power - IPW_TX_POWER_MIN_DBM) * 16 /
522462306a36Sopenharmony_ci		      (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM);
522562306a36Sopenharmony_ci
522662306a36Sopenharmony_ci	cmd.host_command_parameters[0] = tmp;
522762306a36Sopenharmony_ci
522862306a36Sopenharmony_ci	if (priv->ieee->iw_mode == IW_MODE_ADHOC)
522962306a36Sopenharmony_ci		err = ipw2100_hw_send_command(priv, &cmd);
523062306a36Sopenharmony_ci	if (!err)
523162306a36Sopenharmony_ci		priv->tx_power = tx_power;
523262306a36Sopenharmony_ci
523362306a36Sopenharmony_ci	return 0;
523462306a36Sopenharmony_ci}
523562306a36Sopenharmony_ci
523662306a36Sopenharmony_cistatic int ipw2100_set_ibss_beacon_interval(struct ipw2100_priv *priv,
523762306a36Sopenharmony_ci					    u32 interval, int batch_mode)
523862306a36Sopenharmony_ci{
523962306a36Sopenharmony_ci	struct host_command cmd = {
524062306a36Sopenharmony_ci		.host_command = BEACON_INTERVAL,
524162306a36Sopenharmony_ci		.host_command_sequence = 0,
524262306a36Sopenharmony_ci		.host_command_length = 4
524362306a36Sopenharmony_ci	};
524462306a36Sopenharmony_ci	int err;
524562306a36Sopenharmony_ci
524662306a36Sopenharmony_ci	cmd.host_command_parameters[0] = interval;
524762306a36Sopenharmony_ci
524862306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
524962306a36Sopenharmony_ci
525062306a36Sopenharmony_ci	if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
525162306a36Sopenharmony_ci		if (!batch_mode) {
525262306a36Sopenharmony_ci			err = ipw2100_disable_adapter(priv);
525362306a36Sopenharmony_ci			if (err)
525462306a36Sopenharmony_ci				return err;
525562306a36Sopenharmony_ci		}
525662306a36Sopenharmony_ci
525762306a36Sopenharmony_ci		ipw2100_hw_send_command(priv, &cmd);
525862306a36Sopenharmony_ci
525962306a36Sopenharmony_ci		if (!batch_mode) {
526062306a36Sopenharmony_ci			err = ipw2100_enable_adapter(priv);
526162306a36Sopenharmony_ci			if (err)
526262306a36Sopenharmony_ci				return err;
526362306a36Sopenharmony_ci		}
526462306a36Sopenharmony_ci	}
526562306a36Sopenharmony_ci
526662306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
526762306a36Sopenharmony_ci
526862306a36Sopenharmony_ci	return 0;
526962306a36Sopenharmony_ci}
527062306a36Sopenharmony_ci
527162306a36Sopenharmony_cistatic void ipw2100_queues_initialize(struct ipw2100_priv *priv)
527262306a36Sopenharmony_ci{
527362306a36Sopenharmony_ci	ipw2100_tx_initialize(priv);
527462306a36Sopenharmony_ci	ipw2100_rx_initialize(priv);
527562306a36Sopenharmony_ci	ipw2100_msg_initialize(priv);
527662306a36Sopenharmony_ci}
527762306a36Sopenharmony_ci
527862306a36Sopenharmony_cistatic void ipw2100_queues_free(struct ipw2100_priv *priv)
527962306a36Sopenharmony_ci{
528062306a36Sopenharmony_ci	ipw2100_tx_free(priv);
528162306a36Sopenharmony_ci	ipw2100_rx_free(priv);
528262306a36Sopenharmony_ci	ipw2100_msg_free(priv);
528362306a36Sopenharmony_ci}
528462306a36Sopenharmony_ci
528562306a36Sopenharmony_cistatic int ipw2100_queues_allocate(struct ipw2100_priv *priv)
528662306a36Sopenharmony_ci{
528762306a36Sopenharmony_ci	if (ipw2100_tx_allocate(priv) ||
528862306a36Sopenharmony_ci	    ipw2100_rx_allocate(priv) || ipw2100_msg_allocate(priv))
528962306a36Sopenharmony_ci		goto fail;
529062306a36Sopenharmony_ci
529162306a36Sopenharmony_ci	return 0;
529262306a36Sopenharmony_ci
529362306a36Sopenharmony_ci      fail:
529462306a36Sopenharmony_ci	ipw2100_tx_free(priv);
529562306a36Sopenharmony_ci	ipw2100_rx_free(priv);
529662306a36Sopenharmony_ci	ipw2100_msg_free(priv);
529762306a36Sopenharmony_ci	return -ENOMEM;
529862306a36Sopenharmony_ci}
529962306a36Sopenharmony_ci
530062306a36Sopenharmony_ci#define IPW_PRIVACY_CAPABLE 0x0008
530162306a36Sopenharmony_ci
530262306a36Sopenharmony_cistatic int ipw2100_set_wep_flags(struct ipw2100_priv *priv, u32 flags,
530362306a36Sopenharmony_ci				 int batch_mode)
530462306a36Sopenharmony_ci{
530562306a36Sopenharmony_ci	struct host_command cmd = {
530662306a36Sopenharmony_ci		.host_command = WEP_FLAGS,
530762306a36Sopenharmony_ci		.host_command_sequence = 0,
530862306a36Sopenharmony_ci		.host_command_length = 4
530962306a36Sopenharmony_ci	};
531062306a36Sopenharmony_ci	int err;
531162306a36Sopenharmony_ci
531262306a36Sopenharmony_ci	cmd.host_command_parameters[0] = flags;
531362306a36Sopenharmony_ci
531462306a36Sopenharmony_ci	IPW_DEBUG_HC("WEP_FLAGS: flags = 0x%08X\n", flags);
531562306a36Sopenharmony_ci
531662306a36Sopenharmony_ci	if (!batch_mode) {
531762306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
531862306a36Sopenharmony_ci		if (err) {
531962306a36Sopenharmony_ci			printk(KERN_ERR DRV_NAME
532062306a36Sopenharmony_ci			       ": %s: Could not disable adapter %d\n",
532162306a36Sopenharmony_ci			       priv->net_dev->name, err);
532262306a36Sopenharmony_ci			return err;
532362306a36Sopenharmony_ci		}
532462306a36Sopenharmony_ci	}
532562306a36Sopenharmony_ci
532662306a36Sopenharmony_ci	/* send cmd to firmware */
532762306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
532862306a36Sopenharmony_ci
532962306a36Sopenharmony_ci	if (!batch_mode)
533062306a36Sopenharmony_ci		ipw2100_enable_adapter(priv);
533162306a36Sopenharmony_ci
533262306a36Sopenharmony_ci	return err;
533362306a36Sopenharmony_ci}
533462306a36Sopenharmony_ci
533562306a36Sopenharmony_cistruct ipw2100_wep_key {
533662306a36Sopenharmony_ci	u8 idx;
533762306a36Sopenharmony_ci	u8 len;
533862306a36Sopenharmony_ci	u8 key[13];
533962306a36Sopenharmony_ci};
534062306a36Sopenharmony_ci
534162306a36Sopenharmony_ci/* Macros to ease up priting WEP keys */
534262306a36Sopenharmony_ci#define WEP_FMT_64  "%02X%02X%02X%02X-%02X"
534362306a36Sopenharmony_ci#define WEP_FMT_128 "%02X%02X%02X%02X-%02X%02X%02X%02X-%02X%02X%02X"
534462306a36Sopenharmony_ci#define WEP_STR_64(x) x[0],x[1],x[2],x[3],x[4]
534562306a36Sopenharmony_ci#define WEP_STR_128(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10]
534662306a36Sopenharmony_ci
534762306a36Sopenharmony_ci/**
534862306a36Sopenharmony_ci * ipw2100_set_key() - Set a the wep key
534962306a36Sopenharmony_ci *
535062306a36Sopenharmony_ci * @priv: struct to work on
535162306a36Sopenharmony_ci * @idx: index of the key we want to set
535262306a36Sopenharmony_ci * @key: ptr to the key data to set
535362306a36Sopenharmony_ci * @len: length of the buffer at @key
535462306a36Sopenharmony_ci * @batch_mode: FIXME perform the operation in batch mode, not
535562306a36Sopenharmony_ci *              disabling the device.
535662306a36Sopenharmony_ci *
535762306a36Sopenharmony_ci * @returns 0 if OK, < 0 errno code on error.
535862306a36Sopenharmony_ci *
535962306a36Sopenharmony_ci * Fill out a command structure with the new wep key, length an
536062306a36Sopenharmony_ci * index and send it down the wire.
536162306a36Sopenharmony_ci */
536262306a36Sopenharmony_cistatic int ipw2100_set_key(struct ipw2100_priv *priv,
536362306a36Sopenharmony_ci			   int idx, char *key, int len, int batch_mode)
536462306a36Sopenharmony_ci{
536562306a36Sopenharmony_ci	int keylen = len ? (len <= 5 ? 5 : 13) : 0;
536662306a36Sopenharmony_ci	struct host_command cmd = {
536762306a36Sopenharmony_ci		.host_command = WEP_KEY_INFO,
536862306a36Sopenharmony_ci		.host_command_sequence = 0,
536962306a36Sopenharmony_ci		.host_command_length = sizeof(struct ipw2100_wep_key),
537062306a36Sopenharmony_ci	};
537162306a36Sopenharmony_ci	struct ipw2100_wep_key *wep_key = (void *)cmd.host_command_parameters;
537262306a36Sopenharmony_ci	int err;
537362306a36Sopenharmony_ci
537462306a36Sopenharmony_ci	IPW_DEBUG_HC("WEP_KEY_INFO: index = %d, len = %d/%d\n",
537562306a36Sopenharmony_ci		     idx, keylen, len);
537662306a36Sopenharmony_ci
537762306a36Sopenharmony_ci	/* NOTE: We don't check cached values in case the firmware was reset
537862306a36Sopenharmony_ci	 * or some other problem is occurring.  If the user is setting the key,
537962306a36Sopenharmony_ci	 * then we push the change */
538062306a36Sopenharmony_ci
538162306a36Sopenharmony_ci	wep_key->idx = idx;
538262306a36Sopenharmony_ci	wep_key->len = keylen;
538362306a36Sopenharmony_ci
538462306a36Sopenharmony_ci	if (keylen) {
538562306a36Sopenharmony_ci		memcpy(wep_key->key, key, len);
538662306a36Sopenharmony_ci		memset(wep_key->key + len, 0, keylen - len);
538762306a36Sopenharmony_ci	}
538862306a36Sopenharmony_ci
538962306a36Sopenharmony_ci	/* Will be optimized out on debug not being configured in */
539062306a36Sopenharmony_ci	if (keylen == 0)
539162306a36Sopenharmony_ci		IPW_DEBUG_WEP("%s: Clearing key %d\n",
539262306a36Sopenharmony_ci			      priv->net_dev->name, wep_key->idx);
539362306a36Sopenharmony_ci	else if (keylen == 5)
539462306a36Sopenharmony_ci		IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_64 "\n",
539562306a36Sopenharmony_ci			      priv->net_dev->name, wep_key->idx, wep_key->len,
539662306a36Sopenharmony_ci			      WEP_STR_64(wep_key->key));
539762306a36Sopenharmony_ci	else
539862306a36Sopenharmony_ci		IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_128
539962306a36Sopenharmony_ci			      "\n",
540062306a36Sopenharmony_ci			      priv->net_dev->name, wep_key->idx, wep_key->len,
540162306a36Sopenharmony_ci			      WEP_STR_128(wep_key->key));
540262306a36Sopenharmony_ci
540362306a36Sopenharmony_ci	if (!batch_mode) {
540462306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
540562306a36Sopenharmony_ci		/* FIXME: IPG: shouldn't this prink be in _disable_adapter()? */
540662306a36Sopenharmony_ci		if (err) {
540762306a36Sopenharmony_ci			printk(KERN_ERR DRV_NAME
540862306a36Sopenharmony_ci			       ": %s: Could not disable adapter %d\n",
540962306a36Sopenharmony_ci			       priv->net_dev->name, err);
541062306a36Sopenharmony_ci			return err;
541162306a36Sopenharmony_ci		}
541262306a36Sopenharmony_ci	}
541362306a36Sopenharmony_ci
541462306a36Sopenharmony_ci	/* send cmd to firmware */
541562306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
541662306a36Sopenharmony_ci
541762306a36Sopenharmony_ci	if (!batch_mode) {
541862306a36Sopenharmony_ci		int err2 = ipw2100_enable_adapter(priv);
541962306a36Sopenharmony_ci		if (err == 0)
542062306a36Sopenharmony_ci			err = err2;
542162306a36Sopenharmony_ci	}
542262306a36Sopenharmony_ci	return err;
542362306a36Sopenharmony_ci}
542462306a36Sopenharmony_ci
542562306a36Sopenharmony_cistatic int ipw2100_set_key_index(struct ipw2100_priv *priv,
542662306a36Sopenharmony_ci				 int idx, int batch_mode)
542762306a36Sopenharmony_ci{
542862306a36Sopenharmony_ci	struct host_command cmd = {
542962306a36Sopenharmony_ci		.host_command = WEP_KEY_INDEX,
543062306a36Sopenharmony_ci		.host_command_sequence = 0,
543162306a36Sopenharmony_ci		.host_command_length = 4,
543262306a36Sopenharmony_ci		.host_command_parameters = {idx},
543362306a36Sopenharmony_ci	};
543462306a36Sopenharmony_ci	int err;
543562306a36Sopenharmony_ci
543662306a36Sopenharmony_ci	IPW_DEBUG_HC("WEP_KEY_INDEX: index = %d\n", idx);
543762306a36Sopenharmony_ci
543862306a36Sopenharmony_ci	if (idx < 0 || idx > 3)
543962306a36Sopenharmony_ci		return -EINVAL;
544062306a36Sopenharmony_ci
544162306a36Sopenharmony_ci	if (!batch_mode) {
544262306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
544362306a36Sopenharmony_ci		if (err) {
544462306a36Sopenharmony_ci			printk(KERN_ERR DRV_NAME
544562306a36Sopenharmony_ci			       ": %s: Could not disable adapter %d\n",
544662306a36Sopenharmony_ci			       priv->net_dev->name, err);
544762306a36Sopenharmony_ci			return err;
544862306a36Sopenharmony_ci		}
544962306a36Sopenharmony_ci	}
545062306a36Sopenharmony_ci
545162306a36Sopenharmony_ci	/* send cmd to firmware */
545262306a36Sopenharmony_ci	err = ipw2100_hw_send_command(priv, &cmd);
545362306a36Sopenharmony_ci
545462306a36Sopenharmony_ci	if (!batch_mode)
545562306a36Sopenharmony_ci		ipw2100_enable_adapter(priv);
545662306a36Sopenharmony_ci
545762306a36Sopenharmony_ci	return err;
545862306a36Sopenharmony_ci}
545962306a36Sopenharmony_ci
546062306a36Sopenharmony_cistatic int ipw2100_configure_security(struct ipw2100_priv *priv, int batch_mode)
546162306a36Sopenharmony_ci{
546262306a36Sopenharmony_ci	int i, err, auth_mode, sec_level, use_group;
546362306a36Sopenharmony_ci
546462306a36Sopenharmony_ci	if (!(priv->status & STATUS_RUNNING))
546562306a36Sopenharmony_ci		return 0;
546662306a36Sopenharmony_ci
546762306a36Sopenharmony_ci	if (!batch_mode) {
546862306a36Sopenharmony_ci		err = ipw2100_disable_adapter(priv);
546962306a36Sopenharmony_ci		if (err)
547062306a36Sopenharmony_ci			return err;
547162306a36Sopenharmony_ci	}
547262306a36Sopenharmony_ci
547362306a36Sopenharmony_ci	if (!priv->ieee->sec.enabled) {
547462306a36Sopenharmony_ci		err =
547562306a36Sopenharmony_ci		    ipw2100_set_security_information(priv, IPW_AUTH_OPEN,
547662306a36Sopenharmony_ci						     SEC_LEVEL_0, 0, 1);
547762306a36Sopenharmony_ci	} else {
547862306a36Sopenharmony_ci		auth_mode = IPW_AUTH_OPEN;
547962306a36Sopenharmony_ci		if (priv->ieee->sec.flags & SEC_AUTH_MODE) {
548062306a36Sopenharmony_ci			if (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)
548162306a36Sopenharmony_ci				auth_mode = IPW_AUTH_SHARED;
548262306a36Sopenharmony_ci			else if (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP)
548362306a36Sopenharmony_ci				auth_mode = IPW_AUTH_LEAP_CISCO_ID;
548462306a36Sopenharmony_ci		}
548562306a36Sopenharmony_ci
548662306a36Sopenharmony_ci		sec_level = SEC_LEVEL_0;
548762306a36Sopenharmony_ci		if (priv->ieee->sec.flags & SEC_LEVEL)
548862306a36Sopenharmony_ci			sec_level = priv->ieee->sec.level;
548962306a36Sopenharmony_ci
549062306a36Sopenharmony_ci		use_group = 0;
549162306a36Sopenharmony_ci		if (priv->ieee->sec.flags & SEC_UNICAST_GROUP)
549262306a36Sopenharmony_ci			use_group = priv->ieee->sec.unicast_uses_group;
549362306a36Sopenharmony_ci
549462306a36Sopenharmony_ci		err =
549562306a36Sopenharmony_ci		    ipw2100_set_security_information(priv, auth_mode, sec_level,
549662306a36Sopenharmony_ci						     use_group, 1);
549762306a36Sopenharmony_ci	}
549862306a36Sopenharmony_ci
549962306a36Sopenharmony_ci	if (err)
550062306a36Sopenharmony_ci		goto exit;
550162306a36Sopenharmony_ci
550262306a36Sopenharmony_ci	if (priv->ieee->sec.enabled) {
550362306a36Sopenharmony_ci		for (i = 0; i < 4; i++) {
550462306a36Sopenharmony_ci			if (!(priv->ieee->sec.flags & (1 << i))) {
550562306a36Sopenharmony_ci				memset(priv->ieee->sec.keys[i], 0, WEP_KEY_LEN);
550662306a36Sopenharmony_ci				priv->ieee->sec.key_sizes[i] = 0;
550762306a36Sopenharmony_ci			} else {
550862306a36Sopenharmony_ci				err = ipw2100_set_key(priv, i,
550962306a36Sopenharmony_ci						      priv->ieee->sec.keys[i],
551062306a36Sopenharmony_ci						      priv->ieee->sec.
551162306a36Sopenharmony_ci						      key_sizes[i], 1);
551262306a36Sopenharmony_ci				if (err)
551362306a36Sopenharmony_ci					goto exit;
551462306a36Sopenharmony_ci			}
551562306a36Sopenharmony_ci		}
551662306a36Sopenharmony_ci
551762306a36Sopenharmony_ci		ipw2100_set_key_index(priv, priv->ieee->crypt_info.tx_keyidx, 1);
551862306a36Sopenharmony_ci	}
551962306a36Sopenharmony_ci
552062306a36Sopenharmony_ci	/* Always enable privacy so the Host can filter WEP packets if
552162306a36Sopenharmony_ci	 * encrypted data is sent up */
552262306a36Sopenharmony_ci	err =
552362306a36Sopenharmony_ci	    ipw2100_set_wep_flags(priv,
552462306a36Sopenharmony_ci				  priv->ieee->sec.
552562306a36Sopenharmony_ci				  enabled ? IPW_PRIVACY_CAPABLE : 0, 1);
552662306a36Sopenharmony_ci	if (err)
552762306a36Sopenharmony_ci		goto exit;
552862306a36Sopenharmony_ci
552962306a36Sopenharmony_ci	priv->status &= ~STATUS_SECURITY_UPDATED;
553062306a36Sopenharmony_ci
553162306a36Sopenharmony_ci      exit:
553262306a36Sopenharmony_ci	if (!batch_mode)
553362306a36Sopenharmony_ci		ipw2100_enable_adapter(priv);
553462306a36Sopenharmony_ci
553562306a36Sopenharmony_ci	return err;
553662306a36Sopenharmony_ci}
553762306a36Sopenharmony_ci
553862306a36Sopenharmony_cistatic void ipw2100_security_work(struct work_struct *work)
553962306a36Sopenharmony_ci{
554062306a36Sopenharmony_ci	struct ipw2100_priv *priv =
554162306a36Sopenharmony_ci		container_of(work, struct ipw2100_priv, security_work.work);
554262306a36Sopenharmony_ci
554362306a36Sopenharmony_ci	/* If we happen to have reconnected before we get a chance to
554462306a36Sopenharmony_ci	 * process this, then update the security settings--which causes
554562306a36Sopenharmony_ci	 * a disassociation to occur */
554662306a36Sopenharmony_ci	if (!(priv->status & STATUS_ASSOCIATED) &&
554762306a36Sopenharmony_ci	    priv->status & STATUS_SECURITY_UPDATED)
554862306a36Sopenharmony_ci		ipw2100_configure_security(priv, 0);
554962306a36Sopenharmony_ci}
555062306a36Sopenharmony_ci
555162306a36Sopenharmony_cistatic void shim__set_security(struct net_device *dev,
555262306a36Sopenharmony_ci			       struct libipw_security *sec)
555362306a36Sopenharmony_ci{
555462306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
555562306a36Sopenharmony_ci	int i;
555662306a36Sopenharmony_ci
555762306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
555862306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED))
555962306a36Sopenharmony_ci		goto done;
556062306a36Sopenharmony_ci
556162306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
556262306a36Sopenharmony_ci		if (sec->flags & (1 << i)) {
556362306a36Sopenharmony_ci			priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
556462306a36Sopenharmony_ci			if (sec->key_sizes[i] == 0)
556562306a36Sopenharmony_ci				priv->ieee->sec.flags &= ~(1 << i);
556662306a36Sopenharmony_ci			else
556762306a36Sopenharmony_ci				memcpy(priv->ieee->sec.keys[i], sec->keys[i],
556862306a36Sopenharmony_ci				       sec->key_sizes[i]);
556962306a36Sopenharmony_ci			if (sec->level == SEC_LEVEL_1) {
557062306a36Sopenharmony_ci				priv->ieee->sec.flags |= (1 << i);
557162306a36Sopenharmony_ci				priv->status |= STATUS_SECURITY_UPDATED;
557262306a36Sopenharmony_ci			} else
557362306a36Sopenharmony_ci				priv->ieee->sec.flags &= ~(1 << i);
557462306a36Sopenharmony_ci		}
557562306a36Sopenharmony_ci	}
557662306a36Sopenharmony_ci
557762306a36Sopenharmony_ci	if ((sec->flags & SEC_ACTIVE_KEY) &&
557862306a36Sopenharmony_ci	    priv->ieee->sec.active_key != sec->active_key) {
557962306a36Sopenharmony_ci		priv->ieee->sec.active_key = sec->active_key;
558062306a36Sopenharmony_ci		priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
558162306a36Sopenharmony_ci		priv->status |= STATUS_SECURITY_UPDATED;
558262306a36Sopenharmony_ci	}
558362306a36Sopenharmony_ci
558462306a36Sopenharmony_ci	if ((sec->flags & SEC_AUTH_MODE) &&
558562306a36Sopenharmony_ci	    (priv->ieee->sec.auth_mode != sec->auth_mode)) {
558662306a36Sopenharmony_ci		priv->ieee->sec.auth_mode = sec->auth_mode;
558762306a36Sopenharmony_ci		priv->ieee->sec.flags |= SEC_AUTH_MODE;
558862306a36Sopenharmony_ci		priv->status |= STATUS_SECURITY_UPDATED;
558962306a36Sopenharmony_ci	}
559062306a36Sopenharmony_ci
559162306a36Sopenharmony_ci	if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
559262306a36Sopenharmony_ci		priv->ieee->sec.flags |= SEC_ENABLED;
559362306a36Sopenharmony_ci		priv->ieee->sec.enabled = sec->enabled;
559462306a36Sopenharmony_ci		priv->status |= STATUS_SECURITY_UPDATED;
559562306a36Sopenharmony_ci	}
559662306a36Sopenharmony_ci
559762306a36Sopenharmony_ci	if (sec->flags & SEC_ENCRYPT)
559862306a36Sopenharmony_ci		priv->ieee->sec.encrypt = sec->encrypt;
559962306a36Sopenharmony_ci
560062306a36Sopenharmony_ci	if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
560162306a36Sopenharmony_ci		priv->ieee->sec.level = sec->level;
560262306a36Sopenharmony_ci		priv->ieee->sec.flags |= SEC_LEVEL;
560362306a36Sopenharmony_ci		priv->status |= STATUS_SECURITY_UPDATED;
560462306a36Sopenharmony_ci	}
560562306a36Sopenharmony_ci
560662306a36Sopenharmony_ci	IPW_DEBUG_WEP("Security flags: %c %c%c%c%c %c%c%c%c\n",
560762306a36Sopenharmony_ci		      priv->ieee->sec.flags & (1 << 8) ? '1' : '0',
560862306a36Sopenharmony_ci		      priv->ieee->sec.flags & (1 << 7) ? '1' : '0',
560962306a36Sopenharmony_ci		      priv->ieee->sec.flags & (1 << 6) ? '1' : '0',
561062306a36Sopenharmony_ci		      priv->ieee->sec.flags & (1 << 5) ? '1' : '0',
561162306a36Sopenharmony_ci		      priv->ieee->sec.flags & (1 << 4) ? '1' : '0',
561262306a36Sopenharmony_ci		      priv->ieee->sec.flags & (1 << 3) ? '1' : '0',
561362306a36Sopenharmony_ci		      priv->ieee->sec.flags & (1 << 2) ? '1' : '0',
561462306a36Sopenharmony_ci		      priv->ieee->sec.flags & (1 << 1) ? '1' : '0',
561562306a36Sopenharmony_ci		      priv->ieee->sec.flags & (1 << 0) ? '1' : '0');
561662306a36Sopenharmony_ci
561762306a36Sopenharmony_ci/* As a temporary work around to enable WPA until we figure out why
561862306a36Sopenharmony_ci * wpa_supplicant toggles the security capability of the driver, which
561962306a36Sopenharmony_ci * forces a disassociation with force_update...
562062306a36Sopenharmony_ci *
562162306a36Sopenharmony_ci *	if (force_update || !(priv->status & STATUS_ASSOCIATED))*/
562262306a36Sopenharmony_ci	if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
562362306a36Sopenharmony_ci		ipw2100_configure_security(priv, 0);
562462306a36Sopenharmony_ci      done:
562562306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
562662306a36Sopenharmony_ci}
562762306a36Sopenharmony_ci
562862306a36Sopenharmony_cistatic int ipw2100_adapter_setup(struct ipw2100_priv *priv)
562962306a36Sopenharmony_ci{
563062306a36Sopenharmony_ci	int err;
563162306a36Sopenharmony_ci	int batch_mode = 1;
563262306a36Sopenharmony_ci	u8 *bssid;
563362306a36Sopenharmony_ci
563462306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
563562306a36Sopenharmony_ci
563662306a36Sopenharmony_ci	err = ipw2100_disable_adapter(priv);
563762306a36Sopenharmony_ci	if (err)
563862306a36Sopenharmony_ci		return err;
563962306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
564062306a36Sopenharmony_ci	if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
564162306a36Sopenharmony_ci		err = ipw2100_set_channel(priv, priv->channel, batch_mode);
564262306a36Sopenharmony_ci		if (err)
564362306a36Sopenharmony_ci			return err;
564462306a36Sopenharmony_ci
564562306a36Sopenharmony_ci		IPW_DEBUG_INFO("exit\n");
564662306a36Sopenharmony_ci
564762306a36Sopenharmony_ci		return 0;
564862306a36Sopenharmony_ci	}
564962306a36Sopenharmony_ci#endif				/* CONFIG_IPW2100_MONITOR */
565062306a36Sopenharmony_ci
565162306a36Sopenharmony_ci	err = ipw2100_read_mac_address(priv);
565262306a36Sopenharmony_ci	if (err)
565362306a36Sopenharmony_ci		return -EIO;
565462306a36Sopenharmony_ci
565562306a36Sopenharmony_ci	err = ipw2100_set_mac_address(priv, batch_mode);
565662306a36Sopenharmony_ci	if (err)
565762306a36Sopenharmony_ci		return err;
565862306a36Sopenharmony_ci
565962306a36Sopenharmony_ci	err = ipw2100_set_port_type(priv, priv->ieee->iw_mode, batch_mode);
566062306a36Sopenharmony_ci	if (err)
566162306a36Sopenharmony_ci		return err;
566262306a36Sopenharmony_ci
566362306a36Sopenharmony_ci	if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
566462306a36Sopenharmony_ci		err = ipw2100_set_channel(priv, priv->channel, batch_mode);
566562306a36Sopenharmony_ci		if (err)
566662306a36Sopenharmony_ci			return err;
566762306a36Sopenharmony_ci	}
566862306a36Sopenharmony_ci
566962306a36Sopenharmony_ci	err = ipw2100_system_config(priv, batch_mode);
567062306a36Sopenharmony_ci	if (err)
567162306a36Sopenharmony_ci		return err;
567262306a36Sopenharmony_ci
567362306a36Sopenharmony_ci	err = ipw2100_set_tx_rates(priv, priv->tx_rates, batch_mode);
567462306a36Sopenharmony_ci	if (err)
567562306a36Sopenharmony_ci		return err;
567662306a36Sopenharmony_ci
567762306a36Sopenharmony_ci	/* Default to power mode OFF */
567862306a36Sopenharmony_ci	err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
567962306a36Sopenharmony_ci	if (err)
568062306a36Sopenharmony_ci		return err;
568162306a36Sopenharmony_ci
568262306a36Sopenharmony_ci	err = ipw2100_set_rts_threshold(priv, priv->rts_threshold);
568362306a36Sopenharmony_ci	if (err)
568462306a36Sopenharmony_ci		return err;
568562306a36Sopenharmony_ci
568662306a36Sopenharmony_ci	if (priv->config & CFG_STATIC_BSSID)
568762306a36Sopenharmony_ci		bssid = priv->bssid;
568862306a36Sopenharmony_ci	else
568962306a36Sopenharmony_ci		bssid = NULL;
569062306a36Sopenharmony_ci	err = ipw2100_set_mandatory_bssid(priv, bssid, batch_mode);
569162306a36Sopenharmony_ci	if (err)
569262306a36Sopenharmony_ci		return err;
569362306a36Sopenharmony_ci
569462306a36Sopenharmony_ci	if (priv->config & CFG_STATIC_ESSID)
569562306a36Sopenharmony_ci		err = ipw2100_set_essid(priv, priv->essid, priv->essid_len,
569662306a36Sopenharmony_ci					batch_mode);
569762306a36Sopenharmony_ci	else
569862306a36Sopenharmony_ci		err = ipw2100_set_essid(priv, NULL, 0, batch_mode);
569962306a36Sopenharmony_ci	if (err)
570062306a36Sopenharmony_ci		return err;
570162306a36Sopenharmony_ci
570262306a36Sopenharmony_ci	err = ipw2100_configure_security(priv, batch_mode);
570362306a36Sopenharmony_ci	if (err)
570462306a36Sopenharmony_ci		return err;
570562306a36Sopenharmony_ci
570662306a36Sopenharmony_ci	if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
570762306a36Sopenharmony_ci		err =
570862306a36Sopenharmony_ci		    ipw2100_set_ibss_beacon_interval(priv,
570962306a36Sopenharmony_ci						     priv->beacon_interval,
571062306a36Sopenharmony_ci						     batch_mode);
571162306a36Sopenharmony_ci		if (err)
571262306a36Sopenharmony_ci			return err;
571362306a36Sopenharmony_ci
571462306a36Sopenharmony_ci		err = ipw2100_set_tx_power(priv, priv->tx_power);
571562306a36Sopenharmony_ci		if (err)
571662306a36Sopenharmony_ci			return err;
571762306a36Sopenharmony_ci	}
571862306a36Sopenharmony_ci
571962306a36Sopenharmony_ci	/*
572062306a36Sopenharmony_ci	   err = ipw2100_set_fragmentation_threshold(
572162306a36Sopenharmony_ci	   priv, priv->frag_threshold, batch_mode);
572262306a36Sopenharmony_ci	   if (err)
572362306a36Sopenharmony_ci	   return err;
572462306a36Sopenharmony_ci	 */
572562306a36Sopenharmony_ci
572662306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
572762306a36Sopenharmony_ci
572862306a36Sopenharmony_ci	return 0;
572962306a36Sopenharmony_ci}
573062306a36Sopenharmony_ci
573162306a36Sopenharmony_ci/*************************************************************************
573262306a36Sopenharmony_ci *
573362306a36Sopenharmony_ci * EXTERNALLY CALLED METHODS
573462306a36Sopenharmony_ci *
573562306a36Sopenharmony_ci *************************************************************************/
573662306a36Sopenharmony_ci
573762306a36Sopenharmony_ci/* This method is called by the network layer -- not to be confused with
573862306a36Sopenharmony_ci * ipw2100_set_mac_address() declared above called by this driver (and this
573962306a36Sopenharmony_ci * method as well) to talk to the firmware */
574062306a36Sopenharmony_cistatic int ipw2100_set_address(struct net_device *dev, void *p)
574162306a36Sopenharmony_ci{
574262306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
574362306a36Sopenharmony_ci	struct sockaddr *addr = p;
574462306a36Sopenharmony_ci	int err = 0;
574562306a36Sopenharmony_ci
574662306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
574762306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
574862306a36Sopenharmony_ci
574962306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
575062306a36Sopenharmony_ci
575162306a36Sopenharmony_ci	priv->config |= CFG_CUSTOM_MAC;
575262306a36Sopenharmony_ci	memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
575362306a36Sopenharmony_ci
575462306a36Sopenharmony_ci	err = ipw2100_set_mac_address(priv, 0);
575562306a36Sopenharmony_ci	if (err)
575662306a36Sopenharmony_ci		goto done;
575762306a36Sopenharmony_ci
575862306a36Sopenharmony_ci	priv->reset_backoff = 0;
575962306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
576062306a36Sopenharmony_ci	ipw2100_reset_adapter(&priv->reset_work.work);
576162306a36Sopenharmony_ci	return 0;
576262306a36Sopenharmony_ci
576362306a36Sopenharmony_ci      done:
576462306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
576562306a36Sopenharmony_ci	return err;
576662306a36Sopenharmony_ci}
576762306a36Sopenharmony_ci
576862306a36Sopenharmony_cistatic int ipw2100_open(struct net_device *dev)
576962306a36Sopenharmony_ci{
577062306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
577162306a36Sopenharmony_ci	unsigned long flags;
577262306a36Sopenharmony_ci	IPW_DEBUG_INFO("dev->open\n");
577362306a36Sopenharmony_ci
577462306a36Sopenharmony_ci	spin_lock_irqsave(&priv->low_lock, flags);
577562306a36Sopenharmony_ci	if (priv->status & STATUS_ASSOCIATED) {
577662306a36Sopenharmony_ci		netif_carrier_on(dev);
577762306a36Sopenharmony_ci		netif_start_queue(dev);
577862306a36Sopenharmony_ci	}
577962306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
578062306a36Sopenharmony_ci
578162306a36Sopenharmony_ci	return 0;
578262306a36Sopenharmony_ci}
578362306a36Sopenharmony_ci
578462306a36Sopenharmony_cistatic int ipw2100_close(struct net_device *dev)
578562306a36Sopenharmony_ci{
578662306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
578762306a36Sopenharmony_ci	unsigned long flags;
578862306a36Sopenharmony_ci	struct list_head *element;
578962306a36Sopenharmony_ci	struct ipw2100_tx_packet *packet;
579062306a36Sopenharmony_ci
579162306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
579262306a36Sopenharmony_ci
579362306a36Sopenharmony_ci	spin_lock_irqsave(&priv->low_lock, flags);
579462306a36Sopenharmony_ci
579562306a36Sopenharmony_ci	if (priv->status & STATUS_ASSOCIATED)
579662306a36Sopenharmony_ci		netif_carrier_off(dev);
579762306a36Sopenharmony_ci	netif_stop_queue(dev);
579862306a36Sopenharmony_ci
579962306a36Sopenharmony_ci	/* Flush the TX queue ... */
580062306a36Sopenharmony_ci	while (!list_empty(&priv->tx_pend_list)) {
580162306a36Sopenharmony_ci		element = priv->tx_pend_list.next;
580262306a36Sopenharmony_ci		packet = list_entry(element, struct ipw2100_tx_packet, list);
580362306a36Sopenharmony_ci
580462306a36Sopenharmony_ci		list_del(element);
580562306a36Sopenharmony_ci		DEC_STAT(&priv->tx_pend_stat);
580662306a36Sopenharmony_ci
580762306a36Sopenharmony_ci		libipw_txb_free(packet->info.d_struct.txb);
580862306a36Sopenharmony_ci		packet->info.d_struct.txb = NULL;
580962306a36Sopenharmony_ci
581062306a36Sopenharmony_ci		list_add_tail(element, &priv->tx_free_list);
581162306a36Sopenharmony_ci		INC_STAT(&priv->tx_free_stat);
581262306a36Sopenharmony_ci	}
581362306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
581462306a36Sopenharmony_ci
581562306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
581662306a36Sopenharmony_ci
581762306a36Sopenharmony_ci	return 0;
581862306a36Sopenharmony_ci}
581962306a36Sopenharmony_ci
582062306a36Sopenharmony_ci/*
582162306a36Sopenharmony_ci * TODO:  Fix this function... its just wrong
582262306a36Sopenharmony_ci */
582362306a36Sopenharmony_cistatic void ipw2100_tx_timeout(struct net_device *dev, unsigned int txqueue)
582462306a36Sopenharmony_ci{
582562306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
582662306a36Sopenharmony_ci
582762306a36Sopenharmony_ci	dev->stats.tx_errors++;
582862306a36Sopenharmony_ci
582962306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
583062306a36Sopenharmony_ci	if (priv->ieee->iw_mode == IW_MODE_MONITOR)
583162306a36Sopenharmony_ci		return;
583262306a36Sopenharmony_ci#endif
583362306a36Sopenharmony_ci
583462306a36Sopenharmony_ci	IPW_DEBUG_INFO("%s: TX timed out.  Scheduling firmware restart.\n",
583562306a36Sopenharmony_ci		       dev->name);
583662306a36Sopenharmony_ci	schedule_reset(priv);
583762306a36Sopenharmony_ci}
583862306a36Sopenharmony_ci
583962306a36Sopenharmony_cistatic int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value)
584062306a36Sopenharmony_ci{
584162306a36Sopenharmony_ci	/* This is called when wpa_supplicant loads and closes the driver
584262306a36Sopenharmony_ci	 * interface. */
584362306a36Sopenharmony_ci	priv->ieee->wpa_enabled = value;
584462306a36Sopenharmony_ci	return 0;
584562306a36Sopenharmony_ci}
584662306a36Sopenharmony_ci
584762306a36Sopenharmony_cistatic int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value)
584862306a36Sopenharmony_ci{
584962306a36Sopenharmony_ci
585062306a36Sopenharmony_ci	struct libipw_device *ieee = priv->ieee;
585162306a36Sopenharmony_ci	struct libipw_security sec = {
585262306a36Sopenharmony_ci		.flags = SEC_AUTH_MODE,
585362306a36Sopenharmony_ci	};
585462306a36Sopenharmony_ci	int ret = 0;
585562306a36Sopenharmony_ci
585662306a36Sopenharmony_ci	if (value & IW_AUTH_ALG_SHARED_KEY) {
585762306a36Sopenharmony_ci		sec.auth_mode = WLAN_AUTH_SHARED_KEY;
585862306a36Sopenharmony_ci		ieee->open_wep = 0;
585962306a36Sopenharmony_ci	} else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
586062306a36Sopenharmony_ci		sec.auth_mode = WLAN_AUTH_OPEN;
586162306a36Sopenharmony_ci		ieee->open_wep = 1;
586262306a36Sopenharmony_ci	} else if (value & IW_AUTH_ALG_LEAP) {
586362306a36Sopenharmony_ci		sec.auth_mode = WLAN_AUTH_LEAP;
586462306a36Sopenharmony_ci		ieee->open_wep = 1;
586562306a36Sopenharmony_ci	} else
586662306a36Sopenharmony_ci		return -EINVAL;
586762306a36Sopenharmony_ci
586862306a36Sopenharmony_ci	if (ieee->set_security)
586962306a36Sopenharmony_ci		ieee->set_security(ieee->dev, &sec);
587062306a36Sopenharmony_ci	else
587162306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
587262306a36Sopenharmony_ci
587362306a36Sopenharmony_ci	return ret;
587462306a36Sopenharmony_ci}
587562306a36Sopenharmony_ci
587662306a36Sopenharmony_cistatic void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv,
587762306a36Sopenharmony_ci				    char *wpa_ie, int wpa_ie_len)
587862306a36Sopenharmony_ci{
587962306a36Sopenharmony_ci
588062306a36Sopenharmony_ci	struct ipw2100_wpa_assoc_frame frame;
588162306a36Sopenharmony_ci
588262306a36Sopenharmony_ci	frame.fixed_ie_mask = 0;
588362306a36Sopenharmony_ci
588462306a36Sopenharmony_ci	/* copy WPA IE */
588562306a36Sopenharmony_ci	memcpy(frame.var_ie, wpa_ie, wpa_ie_len);
588662306a36Sopenharmony_ci	frame.var_ie_len = wpa_ie_len;
588762306a36Sopenharmony_ci
588862306a36Sopenharmony_ci	/* make sure WPA is enabled */
588962306a36Sopenharmony_ci	ipw2100_wpa_enable(priv, 1);
589062306a36Sopenharmony_ci	ipw2100_set_wpa_ie(priv, &frame, 0);
589162306a36Sopenharmony_ci}
589262306a36Sopenharmony_ci
589362306a36Sopenharmony_cistatic void ipw_ethtool_get_drvinfo(struct net_device *dev,
589462306a36Sopenharmony_ci				    struct ethtool_drvinfo *info)
589562306a36Sopenharmony_ci{
589662306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
589762306a36Sopenharmony_ci	char fw_ver[64], ucode_ver[64];
589862306a36Sopenharmony_ci
589962306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
590062306a36Sopenharmony_ci	strscpy(info->version, DRV_VERSION, sizeof(info->version));
590162306a36Sopenharmony_ci
590262306a36Sopenharmony_ci	ipw2100_get_fwversion(priv, fw_ver, sizeof(fw_ver));
590362306a36Sopenharmony_ci	ipw2100_get_ucodeversion(priv, ucode_ver, sizeof(ucode_ver));
590462306a36Sopenharmony_ci
590562306a36Sopenharmony_ci	snprintf(info->fw_version, sizeof(info->fw_version), "%s:%d:%s",
590662306a36Sopenharmony_ci		 fw_ver, priv->eeprom_version, ucode_ver);
590762306a36Sopenharmony_ci
590862306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(priv->pci_dev),
590962306a36Sopenharmony_ci		sizeof(info->bus_info));
591062306a36Sopenharmony_ci}
591162306a36Sopenharmony_ci
591262306a36Sopenharmony_cistatic u32 ipw2100_ethtool_get_link(struct net_device *dev)
591362306a36Sopenharmony_ci{
591462306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
591562306a36Sopenharmony_ci	return (priv->status & STATUS_ASSOCIATED) ? 1 : 0;
591662306a36Sopenharmony_ci}
591762306a36Sopenharmony_ci
591862306a36Sopenharmony_cistatic const struct ethtool_ops ipw2100_ethtool_ops = {
591962306a36Sopenharmony_ci	.get_link = ipw2100_ethtool_get_link,
592062306a36Sopenharmony_ci	.get_drvinfo = ipw_ethtool_get_drvinfo,
592162306a36Sopenharmony_ci};
592262306a36Sopenharmony_ci
592362306a36Sopenharmony_cistatic void ipw2100_hang_check(struct work_struct *work)
592462306a36Sopenharmony_ci{
592562306a36Sopenharmony_ci	struct ipw2100_priv *priv =
592662306a36Sopenharmony_ci		container_of(work, struct ipw2100_priv, hang_check.work);
592762306a36Sopenharmony_ci	unsigned long flags;
592862306a36Sopenharmony_ci	u32 rtc = 0xa5a5a5a5;
592962306a36Sopenharmony_ci	u32 len = sizeof(rtc);
593062306a36Sopenharmony_ci	int restart = 0;
593162306a36Sopenharmony_ci
593262306a36Sopenharmony_ci	spin_lock_irqsave(&priv->low_lock, flags);
593362306a36Sopenharmony_ci
593462306a36Sopenharmony_ci	if (priv->fatal_error != 0) {
593562306a36Sopenharmony_ci		/* If fatal_error is set then we need to restart */
593662306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: Hardware fatal error detected.\n",
593762306a36Sopenharmony_ci			       priv->net_dev->name);
593862306a36Sopenharmony_ci
593962306a36Sopenharmony_ci		restart = 1;
594062306a36Sopenharmony_ci	} else if (ipw2100_get_ordinal(priv, IPW_ORD_RTC_TIME, &rtc, &len) ||
594162306a36Sopenharmony_ci		   (rtc == priv->last_rtc)) {
594262306a36Sopenharmony_ci		/* Check if firmware is hung */
594362306a36Sopenharmony_ci		IPW_DEBUG_INFO("%s: Firmware RTC stalled.\n",
594462306a36Sopenharmony_ci			       priv->net_dev->name);
594562306a36Sopenharmony_ci
594662306a36Sopenharmony_ci		restart = 1;
594762306a36Sopenharmony_ci	}
594862306a36Sopenharmony_ci
594962306a36Sopenharmony_ci	if (restart) {
595062306a36Sopenharmony_ci		/* Kill timer */
595162306a36Sopenharmony_ci		priv->stop_hang_check = 1;
595262306a36Sopenharmony_ci		priv->hangs++;
595362306a36Sopenharmony_ci
595462306a36Sopenharmony_ci		/* Restart the NIC */
595562306a36Sopenharmony_ci		schedule_reset(priv);
595662306a36Sopenharmony_ci	}
595762306a36Sopenharmony_ci
595862306a36Sopenharmony_ci	priv->last_rtc = rtc;
595962306a36Sopenharmony_ci
596062306a36Sopenharmony_ci	if (!priv->stop_hang_check)
596162306a36Sopenharmony_ci		schedule_delayed_work(&priv->hang_check, HZ / 2);
596262306a36Sopenharmony_ci
596362306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
596462306a36Sopenharmony_ci}
596562306a36Sopenharmony_ci
596662306a36Sopenharmony_cistatic void ipw2100_rf_kill(struct work_struct *work)
596762306a36Sopenharmony_ci{
596862306a36Sopenharmony_ci	struct ipw2100_priv *priv =
596962306a36Sopenharmony_ci		container_of(work, struct ipw2100_priv, rf_kill.work);
597062306a36Sopenharmony_ci	unsigned long flags;
597162306a36Sopenharmony_ci
597262306a36Sopenharmony_ci	spin_lock_irqsave(&priv->low_lock, flags);
597362306a36Sopenharmony_ci
597462306a36Sopenharmony_ci	if (rf_kill_active(priv)) {
597562306a36Sopenharmony_ci		IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
597662306a36Sopenharmony_ci		if (!priv->stop_rf_kill)
597762306a36Sopenharmony_ci			schedule_delayed_work(&priv->rf_kill,
597862306a36Sopenharmony_ci					      round_jiffies_relative(HZ));
597962306a36Sopenharmony_ci		goto exit_unlock;
598062306a36Sopenharmony_ci	}
598162306a36Sopenharmony_ci
598262306a36Sopenharmony_ci	/* RF Kill is now disabled, so bring the device back up */
598362306a36Sopenharmony_ci
598462306a36Sopenharmony_ci	if (!(priv->status & STATUS_RF_KILL_MASK)) {
598562306a36Sopenharmony_ci		IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
598662306a36Sopenharmony_ci				  "device\n");
598762306a36Sopenharmony_ci		schedule_reset(priv);
598862306a36Sopenharmony_ci	} else
598962306a36Sopenharmony_ci		IPW_DEBUG_RF_KILL("HW RF Kill deactivated.  SW RF Kill still "
599062306a36Sopenharmony_ci				  "enabled\n");
599162306a36Sopenharmony_ci
599262306a36Sopenharmony_ci      exit_unlock:
599362306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->low_lock, flags);
599462306a36Sopenharmony_ci}
599562306a36Sopenharmony_ci
599662306a36Sopenharmony_cistatic void ipw2100_irq_tasklet(struct tasklet_struct *t);
599762306a36Sopenharmony_ci
599862306a36Sopenharmony_cistatic const struct net_device_ops ipw2100_netdev_ops = {
599962306a36Sopenharmony_ci	.ndo_open		= ipw2100_open,
600062306a36Sopenharmony_ci	.ndo_stop		= ipw2100_close,
600162306a36Sopenharmony_ci	.ndo_start_xmit		= libipw_xmit,
600262306a36Sopenharmony_ci	.ndo_tx_timeout		= ipw2100_tx_timeout,
600362306a36Sopenharmony_ci	.ndo_set_mac_address	= ipw2100_set_address,
600462306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
600562306a36Sopenharmony_ci};
600662306a36Sopenharmony_ci
600762306a36Sopenharmony_ci/* Look into using netdev destructor to shutdown libipw? */
600862306a36Sopenharmony_ci
600962306a36Sopenharmony_cistatic struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev,
601062306a36Sopenharmony_ci					       void __iomem * ioaddr)
601162306a36Sopenharmony_ci{
601262306a36Sopenharmony_ci	struct ipw2100_priv *priv;
601362306a36Sopenharmony_ci	struct net_device *dev;
601462306a36Sopenharmony_ci
601562306a36Sopenharmony_ci	dev = alloc_libipw(sizeof(struct ipw2100_priv), 0);
601662306a36Sopenharmony_ci	if (!dev)
601762306a36Sopenharmony_ci		return NULL;
601862306a36Sopenharmony_ci	priv = libipw_priv(dev);
601962306a36Sopenharmony_ci	priv->ieee = netdev_priv(dev);
602062306a36Sopenharmony_ci	priv->pci_dev = pci_dev;
602162306a36Sopenharmony_ci	priv->net_dev = dev;
602262306a36Sopenharmony_ci	priv->ioaddr = ioaddr;
602362306a36Sopenharmony_ci
602462306a36Sopenharmony_ci	priv->ieee->hard_start_xmit = ipw2100_tx;
602562306a36Sopenharmony_ci	priv->ieee->set_security = shim__set_security;
602662306a36Sopenharmony_ci
602762306a36Sopenharmony_ci	priv->ieee->perfect_rssi = -20;
602862306a36Sopenharmony_ci	priv->ieee->worst_rssi = -85;
602962306a36Sopenharmony_ci
603062306a36Sopenharmony_ci	dev->netdev_ops = &ipw2100_netdev_ops;
603162306a36Sopenharmony_ci	dev->ethtool_ops = &ipw2100_ethtool_ops;
603262306a36Sopenharmony_ci	dev->wireless_handlers = &ipw2100_wx_handler_def;
603362306a36Sopenharmony_ci	priv->wireless_data.libipw = priv->ieee;
603462306a36Sopenharmony_ci	dev->wireless_data = &priv->wireless_data;
603562306a36Sopenharmony_ci	dev->watchdog_timeo = 3 * HZ;
603662306a36Sopenharmony_ci	dev->irq = 0;
603762306a36Sopenharmony_ci	dev->min_mtu = 68;
603862306a36Sopenharmony_ci	dev->max_mtu = LIBIPW_DATA_LEN;
603962306a36Sopenharmony_ci
604062306a36Sopenharmony_ci	/* NOTE: We don't use the wireless_handlers hook
604162306a36Sopenharmony_ci	 * in dev as the system will start throwing WX requests
604262306a36Sopenharmony_ci	 * to us before we're actually initialized and it just
604362306a36Sopenharmony_ci	 * ends up causing problems.  So, we just handle
604462306a36Sopenharmony_ci	 * the WX extensions through the ipw2100_ioctl interface */
604562306a36Sopenharmony_ci
604662306a36Sopenharmony_ci	/* memset() puts everything to 0, so we only have explicitly set
604762306a36Sopenharmony_ci	 * those values that need to be something else */
604862306a36Sopenharmony_ci
604962306a36Sopenharmony_ci	/* If power management is turned on, default to AUTO mode */
605062306a36Sopenharmony_ci	priv->power_mode = IPW_POWER_AUTO;
605162306a36Sopenharmony_ci
605262306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
605362306a36Sopenharmony_ci	priv->config |= CFG_CRC_CHECK;
605462306a36Sopenharmony_ci#endif
605562306a36Sopenharmony_ci	priv->ieee->wpa_enabled = 0;
605662306a36Sopenharmony_ci	priv->ieee->drop_unencrypted = 0;
605762306a36Sopenharmony_ci	priv->ieee->privacy_invoked = 0;
605862306a36Sopenharmony_ci	priv->ieee->ieee802_1x = 1;
605962306a36Sopenharmony_ci
606062306a36Sopenharmony_ci	/* Set module parameters */
606162306a36Sopenharmony_ci	switch (network_mode) {
606262306a36Sopenharmony_ci	case 1:
606362306a36Sopenharmony_ci		priv->ieee->iw_mode = IW_MODE_ADHOC;
606462306a36Sopenharmony_ci		break;
606562306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
606662306a36Sopenharmony_ci	case 2:
606762306a36Sopenharmony_ci		priv->ieee->iw_mode = IW_MODE_MONITOR;
606862306a36Sopenharmony_ci		break;
606962306a36Sopenharmony_ci#endif
607062306a36Sopenharmony_ci	default:
607162306a36Sopenharmony_ci	case 0:
607262306a36Sopenharmony_ci		priv->ieee->iw_mode = IW_MODE_INFRA;
607362306a36Sopenharmony_ci		break;
607462306a36Sopenharmony_ci	}
607562306a36Sopenharmony_ci
607662306a36Sopenharmony_ci	if (disable == 1)
607762306a36Sopenharmony_ci		priv->status |= STATUS_RF_KILL_SW;
607862306a36Sopenharmony_ci
607962306a36Sopenharmony_ci	if (channel != 0 &&
608062306a36Sopenharmony_ci	    ((channel >= REG_MIN_CHANNEL) && (channel <= REG_MAX_CHANNEL))) {
608162306a36Sopenharmony_ci		priv->config |= CFG_STATIC_CHANNEL;
608262306a36Sopenharmony_ci		priv->channel = channel;
608362306a36Sopenharmony_ci	}
608462306a36Sopenharmony_ci
608562306a36Sopenharmony_ci	if (associate)
608662306a36Sopenharmony_ci		priv->config |= CFG_ASSOCIATE;
608762306a36Sopenharmony_ci
608862306a36Sopenharmony_ci	priv->beacon_interval = DEFAULT_BEACON_INTERVAL;
608962306a36Sopenharmony_ci	priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
609062306a36Sopenharmony_ci	priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
609162306a36Sopenharmony_ci	priv->rts_threshold = DEFAULT_RTS_THRESHOLD | RTS_DISABLED;
609262306a36Sopenharmony_ci	priv->frag_threshold = DEFAULT_FTS | FRAG_DISABLED;
609362306a36Sopenharmony_ci	priv->tx_power = IPW_TX_POWER_DEFAULT;
609462306a36Sopenharmony_ci	priv->tx_rates = DEFAULT_TX_RATES;
609562306a36Sopenharmony_ci
609662306a36Sopenharmony_ci	strcpy(priv->nick, "ipw2100");
609762306a36Sopenharmony_ci
609862306a36Sopenharmony_ci	spin_lock_init(&priv->low_lock);
609962306a36Sopenharmony_ci	mutex_init(&priv->action_mutex);
610062306a36Sopenharmony_ci	mutex_init(&priv->adapter_mutex);
610162306a36Sopenharmony_ci
610262306a36Sopenharmony_ci	init_waitqueue_head(&priv->wait_command_queue);
610362306a36Sopenharmony_ci
610462306a36Sopenharmony_ci	netif_carrier_off(dev);
610562306a36Sopenharmony_ci
610662306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->msg_free_list);
610762306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->msg_pend_list);
610862306a36Sopenharmony_ci	INIT_STAT(&priv->msg_free_stat);
610962306a36Sopenharmony_ci	INIT_STAT(&priv->msg_pend_stat);
611062306a36Sopenharmony_ci
611162306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->tx_free_list);
611262306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->tx_pend_list);
611362306a36Sopenharmony_ci	INIT_STAT(&priv->tx_free_stat);
611462306a36Sopenharmony_ci	INIT_STAT(&priv->tx_pend_stat);
611562306a36Sopenharmony_ci
611662306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->fw_pend_list);
611762306a36Sopenharmony_ci	INIT_STAT(&priv->fw_pend_stat);
611862306a36Sopenharmony_ci
611962306a36Sopenharmony_ci	INIT_DELAYED_WORK(&priv->reset_work, ipw2100_reset_adapter);
612062306a36Sopenharmony_ci	INIT_DELAYED_WORK(&priv->security_work, ipw2100_security_work);
612162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&priv->wx_event_work, ipw2100_wx_event_work);
612262306a36Sopenharmony_ci	INIT_DELAYED_WORK(&priv->hang_check, ipw2100_hang_check);
612362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&priv->rf_kill, ipw2100_rf_kill);
612462306a36Sopenharmony_ci	INIT_DELAYED_WORK(&priv->scan_event, ipw2100_scan_event);
612562306a36Sopenharmony_ci
612662306a36Sopenharmony_ci	tasklet_setup(&priv->irq_tasklet, ipw2100_irq_tasklet);
612762306a36Sopenharmony_ci
612862306a36Sopenharmony_ci	/* NOTE:  We do not start the deferred work for status checks yet */
612962306a36Sopenharmony_ci	priv->stop_rf_kill = 1;
613062306a36Sopenharmony_ci	priv->stop_hang_check = 1;
613162306a36Sopenharmony_ci
613262306a36Sopenharmony_ci	return dev;
613362306a36Sopenharmony_ci}
613462306a36Sopenharmony_ci
613562306a36Sopenharmony_cistatic int ipw2100_pci_init_one(struct pci_dev *pci_dev,
613662306a36Sopenharmony_ci				const struct pci_device_id *ent)
613762306a36Sopenharmony_ci{
613862306a36Sopenharmony_ci	void __iomem *ioaddr;
613962306a36Sopenharmony_ci	struct net_device *dev = NULL;
614062306a36Sopenharmony_ci	struct ipw2100_priv *priv = NULL;
614162306a36Sopenharmony_ci	int err = 0;
614262306a36Sopenharmony_ci	int registered = 0;
614362306a36Sopenharmony_ci	u32 val;
614462306a36Sopenharmony_ci
614562306a36Sopenharmony_ci	IPW_DEBUG_INFO("enter\n");
614662306a36Sopenharmony_ci
614762306a36Sopenharmony_ci	if (!(pci_resource_flags(pci_dev, 0) & IORESOURCE_MEM)) {
614862306a36Sopenharmony_ci		IPW_DEBUG_INFO("weird - resource type is not memory\n");
614962306a36Sopenharmony_ci		err = -ENODEV;
615062306a36Sopenharmony_ci		goto out;
615162306a36Sopenharmony_ci	}
615262306a36Sopenharmony_ci
615362306a36Sopenharmony_ci	ioaddr = pci_iomap(pci_dev, 0, 0);
615462306a36Sopenharmony_ci	if (!ioaddr) {
615562306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
615662306a36Sopenharmony_ci		       "Error calling ioremap.\n");
615762306a36Sopenharmony_ci		err = -EIO;
615862306a36Sopenharmony_ci		goto fail;
615962306a36Sopenharmony_ci	}
616062306a36Sopenharmony_ci
616162306a36Sopenharmony_ci	/* allocate and initialize our net_device */
616262306a36Sopenharmony_ci	dev = ipw2100_alloc_device(pci_dev, ioaddr);
616362306a36Sopenharmony_ci	if (!dev) {
616462306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
616562306a36Sopenharmony_ci		       "Error calling ipw2100_alloc_device.\n");
616662306a36Sopenharmony_ci		err = -ENOMEM;
616762306a36Sopenharmony_ci		goto fail;
616862306a36Sopenharmony_ci	}
616962306a36Sopenharmony_ci
617062306a36Sopenharmony_ci	/* set up PCI mappings for device */
617162306a36Sopenharmony_ci	err = pci_enable_device(pci_dev);
617262306a36Sopenharmony_ci	if (err) {
617362306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
617462306a36Sopenharmony_ci		       "Error calling pci_enable_device.\n");
617562306a36Sopenharmony_ci		return err;
617662306a36Sopenharmony_ci	}
617762306a36Sopenharmony_ci
617862306a36Sopenharmony_ci	priv = libipw_priv(dev);
617962306a36Sopenharmony_ci
618062306a36Sopenharmony_ci	pci_set_master(pci_dev);
618162306a36Sopenharmony_ci	pci_set_drvdata(pci_dev, priv);
618262306a36Sopenharmony_ci
618362306a36Sopenharmony_ci	err = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32));
618462306a36Sopenharmony_ci	if (err) {
618562306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
618662306a36Sopenharmony_ci		       "Error calling pci_set_dma_mask.\n");
618762306a36Sopenharmony_ci		pci_disable_device(pci_dev);
618862306a36Sopenharmony_ci		return err;
618962306a36Sopenharmony_ci	}
619062306a36Sopenharmony_ci
619162306a36Sopenharmony_ci	err = pci_request_regions(pci_dev, DRV_NAME);
619262306a36Sopenharmony_ci	if (err) {
619362306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
619462306a36Sopenharmony_ci		       "Error calling pci_request_regions.\n");
619562306a36Sopenharmony_ci		pci_disable_device(pci_dev);
619662306a36Sopenharmony_ci		return err;
619762306a36Sopenharmony_ci	}
619862306a36Sopenharmony_ci
619962306a36Sopenharmony_ci	/* We disable the RETRY_TIMEOUT register (0x41) to keep
620062306a36Sopenharmony_ci	 * PCI Tx retries from interfering with C3 CPU state */
620162306a36Sopenharmony_ci	pci_read_config_dword(pci_dev, 0x40, &val);
620262306a36Sopenharmony_ci	if ((val & 0x0000ff00) != 0)
620362306a36Sopenharmony_ci		pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
620462306a36Sopenharmony_ci
620562306a36Sopenharmony_ci	if (!ipw2100_hw_is_adapter_in_system(dev)) {
620662306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
620762306a36Sopenharmony_ci		       "Device not found via register read.\n");
620862306a36Sopenharmony_ci		err = -ENODEV;
620962306a36Sopenharmony_ci		goto fail;
621062306a36Sopenharmony_ci	}
621162306a36Sopenharmony_ci
621262306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pci_dev->dev);
621362306a36Sopenharmony_ci
621462306a36Sopenharmony_ci	/* Force interrupts to be shut off on the device */
621562306a36Sopenharmony_ci	priv->status |= STATUS_INT_ENABLED;
621662306a36Sopenharmony_ci	ipw2100_disable_interrupts(priv);
621762306a36Sopenharmony_ci
621862306a36Sopenharmony_ci	/* Allocate and initialize the Tx/Rx queues and lists */
621962306a36Sopenharmony_ci	if (ipw2100_queues_allocate(priv)) {
622062306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
622162306a36Sopenharmony_ci		       "Error calling ipw2100_queues_allocate.\n");
622262306a36Sopenharmony_ci		err = -ENOMEM;
622362306a36Sopenharmony_ci		goto fail;
622462306a36Sopenharmony_ci	}
622562306a36Sopenharmony_ci	ipw2100_queues_initialize(priv);
622662306a36Sopenharmony_ci
622762306a36Sopenharmony_ci	err = request_irq(pci_dev->irq,
622862306a36Sopenharmony_ci			  ipw2100_interrupt, IRQF_SHARED, dev->name, priv);
622962306a36Sopenharmony_ci	if (err) {
623062306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
623162306a36Sopenharmony_ci		       "Error calling request_irq: %d.\n", pci_dev->irq);
623262306a36Sopenharmony_ci		goto fail;
623362306a36Sopenharmony_ci	}
623462306a36Sopenharmony_ci	dev->irq = pci_dev->irq;
623562306a36Sopenharmony_ci
623662306a36Sopenharmony_ci	IPW_DEBUG_INFO("Attempting to register device...\n");
623762306a36Sopenharmony_ci
623862306a36Sopenharmony_ci	printk(KERN_INFO DRV_NAME
623962306a36Sopenharmony_ci	       ": Detected Intel PRO/Wireless 2100 Network Connection\n");
624062306a36Sopenharmony_ci
624162306a36Sopenharmony_ci	err = ipw2100_up(priv, 1);
624262306a36Sopenharmony_ci	if (err)
624362306a36Sopenharmony_ci		goto fail;
624462306a36Sopenharmony_ci
624562306a36Sopenharmony_ci	err = ipw2100_wdev_init(dev);
624662306a36Sopenharmony_ci	if (err)
624762306a36Sopenharmony_ci		goto fail;
624862306a36Sopenharmony_ci	registered = 1;
624962306a36Sopenharmony_ci
625062306a36Sopenharmony_ci	/* Bring up the interface.  Pre 0.46, after we registered the
625162306a36Sopenharmony_ci	 * network device we would call ipw2100_up.  This introduced a race
625262306a36Sopenharmony_ci	 * condition with newer hotplug configurations (network was coming
625362306a36Sopenharmony_ci	 * up and making calls before the device was initialized).
625462306a36Sopenharmony_ci	 */
625562306a36Sopenharmony_ci	err = register_netdev(dev);
625662306a36Sopenharmony_ci	if (err) {
625762306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME
625862306a36Sopenharmony_ci		       "Error calling register_netdev.\n");
625962306a36Sopenharmony_ci		goto fail;
626062306a36Sopenharmony_ci	}
626162306a36Sopenharmony_ci	registered = 2;
626262306a36Sopenharmony_ci
626362306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
626462306a36Sopenharmony_ci
626562306a36Sopenharmony_ci	IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev));
626662306a36Sopenharmony_ci
626762306a36Sopenharmony_ci	/* perform this after register_netdev so that dev->name is set */
626862306a36Sopenharmony_ci	err = sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
626962306a36Sopenharmony_ci	if (err)
627062306a36Sopenharmony_ci		goto fail_unlock;
627162306a36Sopenharmony_ci
627262306a36Sopenharmony_ci	/* If the RF Kill switch is disabled, go ahead and complete the
627362306a36Sopenharmony_ci	 * startup sequence */
627462306a36Sopenharmony_ci	if (!(priv->status & STATUS_RF_KILL_MASK)) {
627562306a36Sopenharmony_ci		/* Enable the adapter - sends HOST_COMPLETE */
627662306a36Sopenharmony_ci		if (ipw2100_enable_adapter(priv)) {
627762306a36Sopenharmony_ci			printk(KERN_WARNING DRV_NAME
627862306a36Sopenharmony_ci			       ": %s: failed in call to enable adapter.\n",
627962306a36Sopenharmony_ci			       priv->net_dev->name);
628062306a36Sopenharmony_ci			ipw2100_hw_stop_adapter(priv);
628162306a36Sopenharmony_ci			err = -EIO;
628262306a36Sopenharmony_ci			goto fail_unlock;
628362306a36Sopenharmony_ci		}
628462306a36Sopenharmony_ci
628562306a36Sopenharmony_ci		/* Start a scan . . . */
628662306a36Sopenharmony_ci		ipw2100_set_scan_options(priv);
628762306a36Sopenharmony_ci		ipw2100_start_scan(priv);
628862306a36Sopenharmony_ci	}
628962306a36Sopenharmony_ci
629062306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
629162306a36Sopenharmony_ci
629262306a36Sopenharmony_ci	priv->status |= STATUS_INITIALIZED;
629362306a36Sopenharmony_ci
629462306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
629562306a36Sopenharmony_ciout:
629662306a36Sopenharmony_ci	return err;
629762306a36Sopenharmony_ci
629862306a36Sopenharmony_ci      fail_unlock:
629962306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
630062306a36Sopenharmony_ci      fail:
630162306a36Sopenharmony_ci	if (dev) {
630262306a36Sopenharmony_ci		if (registered >= 2)
630362306a36Sopenharmony_ci			unregister_netdev(dev);
630462306a36Sopenharmony_ci
630562306a36Sopenharmony_ci		if (registered) {
630662306a36Sopenharmony_ci			wiphy_unregister(priv->ieee->wdev.wiphy);
630762306a36Sopenharmony_ci			kfree(priv->ieee->bg_band.channels);
630862306a36Sopenharmony_ci		}
630962306a36Sopenharmony_ci
631062306a36Sopenharmony_ci		ipw2100_hw_stop_adapter(priv);
631162306a36Sopenharmony_ci
631262306a36Sopenharmony_ci		ipw2100_disable_interrupts(priv);
631362306a36Sopenharmony_ci
631462306a36Sopenharmony_ci		if (dev->irq)
631562306a36Sopenharmony_ci			free_irq(dev->irq, priv);
631662306a36Sopenharmony_ci
631762306a36Sopenharmony_ci		ipw2100_kill_works(priv);
631862306a36Sopenharmony_ci
631962306a36Sopenharmony_ci		/* These are safe to call even if they weren't allocated */
632062306a36Sopenharmony_ci		ipw2100_queues_free(priv);
632162306a36Sopenharmony_ci		sysfs_remove_group(&pci_dev->dev.kobj,
632262306a36Sopenharmony_ci				   &ipw2100_attribute_group);
632362306a36Sopenharmony_ci
632462306a36Sopenharmony_ci		free_libipw(dev, 0);
632562306a36Sopenharmony_ci	}
632662306a36Sopenharmony_ci
632762306a36Sopenharmony_ci	pci_iounmap(pci_dev, ioaddr);
632862306a36Sopenharmony_ci
632962306a36Sopenharmony_ci	pci_release_regions(pci_dev);
633062306a36Sopenharmony_ci	pci_disable_device(pci_dev);
633162306a36Sopenharmony_ci	goto out;
633262306a36Sopenharmony_ci}
633362306a36Sopenharmony_ci
633462306a36Sopenharmony_cistatic void ipw2100_pci_remove_one(struct pci_dev *pci_dev)
633562306a36Sopenharmony_ci{
633662306a36Sopenharmony_ci	struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
633762306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
633862306a36Sopenharmony_ci
633962306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
634062306a36Sopenharmony_ci
634162306a36Sopenharmony_ci	priv->status &= ~STATUS_INITIALIZED;
634262306a36Sopenharmony_ci
634362306a36Sopenharmony_ci	sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
634462306a36Sopenharmony_ci
634562306a36Sopenharmony_ci#ifdef CONFIG_PM
634662306a36Sopenharmony_ci	if (ipw2100_firmware.version)
634762306a36Sopenharmony_ci		ipw2100_release_firmware(priv, &ipw2100_firmware);
634862306a36Sopenharmony_ci#endif
634962306a36Sopenharmony_ci	/* Take down the hardware */
635062306a36Sopenharmony_ci	ipw2100_down(priv);
635162306a36Sopenharmony_ci
635262306a36Sopenharmony_ci	/* Release the mutex so that the network subsystem can
635362306a36Sopenharmony_ci	 * complete any needed calls into the driver... */
635462306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
635562306a36Sopenharmony_ci
635662306a36Sopenharmony_ci	/* Unregister the device first - this results in close()
635762306a36Sopenharmony_ci	 * being called if the device is open.  If we free storage
635862306a36Sopenharmony_ci	 * first, then close() will crash.
635962306a36Sopenharmony_ci	 * FIXME: remove the comment above. */
636062306a36Sopenharmony_ci	unregister_netdev(dev);
636162306a36Sopenharmony_ci
636262306a36Sopenharmony_ci	ipw2100_kill_works(priv);
636362306a36Sopenharmony_ci
636462306a36Sopenharmony_ci	ipw2100_queues_free(priv);
636562306a36Sopenharmony_ci
636662306a36Sopenharmony_ci	/* Free potential debugging firmware snapshot */
636762306a36Sopenharmony_ci	ipw2100_snapshot_free(priv);
636862306a36Sopenharmony_ci
636962306a36Sopenharmony_ci	free_irq(dev->irq, priv);
637062306a36Sopenharmony_ci
637162306a36Sopenharmony_ci	pci_iounmap(pci_dev, priv->ioaddr);
637262306a36Sopenharmony_ci
637362306a36Sopenharmony_ci	/* wiphy_unregister needs to be here, before free_libipw */
637462306a36Sopenharmony_ci	wiphy_unregister(priv->ieee->wdev.wiphy);
637562306a36Sopenharmony_ci	kfree(priv->ieee->bg_band.channels);
637662306a36Sopenharmony_ci	free_libipw(dev, 0);
637762306a36Sopenharmony_ci
637862306a36Sopenharmony_ci	pci_release_regions(pci_dev);
637962306a36Sopenharmony_ci	pci_disable_device(pci_dev);
638062306a36Sopenharmony_ci
638162306a36Sopenharmony_ci	IPW_DEBUG_INFO("exit\n");
638262306a36Sopenharmony_ci}
638362306a36Sopenharmony_ci
638462306a36Sopenharmony_cistatic int __maybe_unused ipw2100_suspend(struct device *dev_d)
638562306a36Sopenharmony_ci{
638662306a36Sopenharmony_ci	struct ipw2100_priv *priv = dev_get_drvdata(dev_d);
638762306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
638862306a36Sopenharmony_ci
638962306a36Sopenharmony_ci	IPW_DEBUG_INFO("%s: Going into suspend...\n", dev->name);
639062306a36Sopenharmony_ci
639162306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
639262306a36Sopenharmony_ci	if (priv->status & STATUS_INITIALIZED) {
639362306a36Sopenharmony_ci		/* Take down the device; powers it off, etc. */
639462306a36Sopenharmony_ci		ipw2100_down(priv);
639562306a36Sopenharmony_ci	}
639662306a36Sopenharmony_ci
639762306a36Sopenharmony_ci	/* Remove the PRESENT state of the device */
639862306a36Sopenharmony_ci	netif_device_detach(dev);
639962306a36Sopenharmony_ci
640062306a36Sopenharmony_ci	priv->suspend_at = ktime_get_boottime_seconds();
640162306a36Sopenharmony_ci
640262306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
640362306a36Sopenharmony_ci
640462306a36Sopenharmony_ci	return 0;
640562306a36Sopenharmony_ci}
640662306a36Sopenharmony_ci
640762306a36Sopenharmony_cistatic int __maybe_unused ipw2100_resume(struct device *dev_d)
640862306a36Sopenharmony_ci{
640962306a36Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(dev_d);
641062306a36Sopenharmony_ci	struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
641162306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
641262306a36Sopenharmony_ci	u32 val;
641362306a36Sopenharmony_ci
641462306a36Sopenharmony_ci	if (IPW2100_PM_DISABLED)
641562306a36Sopenharmony_ci		return 0;
641662306a36Sopenharmony_ci
641762306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
641862306a36Sopenharmony_ci
641962306a36Sopenharmony_ci	IPW_DEBUG_INFO("%s: Coming out of suspend...\n", dev->name);
642062306a36Sopenharmony_ci
642162306a36Sopenharmony_ci	/*
642262306a36Sopenharmony_ci	 * Suspend/Resume resets the PCI configuration space, so we have to
642362306a36Sopenharmony_ci	 * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
642462306a36Sopenharmony_ci	 * from interfering with C3 CPU state. pci_restore_state won't help
642562306a36Sopenharmony_ci	 * here since it only restores the first 64 bytes pci config header.
642662306a36Sopenharmony_ci	 */
642762306a36Sopenharmony_ci	pci_read_config_dword(pci_dev, 0x40, &val);
642862306a36Sopenharmony_ci	if ((val & 0x0000ff00) != 0)
642962306a36Sopenharmony_ci		pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
643062306a36Sopenharmony_ci
643162306a36Sopenharmony_ci	/* Set the device back into the PRESENT state; this will also wake
643262306a36Sopenharmony_ci	 * the queue of needed */
643362306a36Sopenharmony_ci	netif_device_attach(dev);
643462306a36Sopenharmony_ci
643562306a36Sopenharmony_ci	priv->suspend_time = ktime_get_boottime_seconds() - priv->suspend_at;
643662306a36Sopenharmony_ci
643762306a36Sopenharmony_ci	/* Bring the device back up */
643862306a36Sopenharmony_ci	if (!(priv->status & STATUS_RF_KILL_SW))
643962306a36Sopenharmony_ci		ipw2100_up(priv, 0);
644062306a36Sopenharmony_ci
644162306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
644262306a36Sopenharmony_ci
644362306a36Sopenharmony_ci	return 0;
644462306a36Sopenharmony_ci}
644562306a36Sopenharmony_ci
644662306a36Sopenharmony_cistatic void ipw2100_shutdown(struct pci_dev *pci_dev)
644762306a36Sopenharmony_ci{
644862306a36Sopenharmony_ci	struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
644962306a36Sopenharmony_ci
645062306a36Sopenharmony_ci	/* Take down the device; powers it off, etc. */
645162306a36Sopenharmony_ci	ipw2100_down(priv);
645262306a36Sopenharmony_ci
645362306a36Sopenharmony_ci	pci_disable_device(pci_dev);
645462306a36Sopenharmony_ci}
645562306a36Sopenharmony_ci
645662306a36Sopenharmony_ci#define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x }
645762306a36Sopenharmony_ci
645862306a36Sopenharmony_cistatic const struct pci_device_id ipw2100_pci_id_table[] = {
645962306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2520),	/* IN 2100A mPCI 3A */
646062306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2521),	/* IN 2100A mPCI 3B */
646162306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2524),	/* IN 2100A mPCI 3B */
646262306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2525),	/* IN 2100A mPCI 3B */
646362306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2526),	/* IN 2100A mPCI Gen A3 */
646462306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2522),	/* IN 2100 mPCI 3B */
646562306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2523),	/* IN 2100 mPCI 3A */
646662306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2527),	/* IN 2100 mPCI 3B */
646762306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2528),	/* IN 2100 mPCI 3B */
646862306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2529),	/* IN 2100 mPCI 3B */
646962306a36Sopenharmony_ci	IPW2100_DEV_ID(0x252B),	/* IN 2100 mPCI 3A */
647062306a36Sopenharmony_ci	IPW2100_DEV_ID(0x252C),	/* IN 2100 mPCI 3A */
647162306a36Sopenharmony_ci	IPW2100_DEV_ID(0x252D),	/* IN 2100 mPCI 3A */
647262306a36Sopenharmony_ci
647362306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2550),	/* IB 2100A mPCI 3B */
647462306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2551),	/* IB 2100 mPCI 3B */
647562306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2553),	/* IB 2100 mPCI 3B */
647662306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2554),	/* IB 2100 mPCI 3B */
647762306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2555),	/* IB 2100 mPCI 3B */
647862306a36Sopenharmony_ci
647962306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2560),	/* DE 2100A mPCI 3A */
648062306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2562),	/* DE 2100A mPCI 3A */
648162306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2563),	/* DE 2100A mPCI 3A */
648262306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2561),	/* DE 2100 mPCI 3A */
648362306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2565),	/* DE 2100 mPCI 3A */
648462306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2566),	/* DE 2100 mPCI 3A */
648562306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2567),	/* DE 2100 mPCI 3A */
648662306a36Sopenharmony_ci
648762306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2570),	/* GA 2100 mPCI 3B */
648862306a36Sopenharmony_ci
648962306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2580),	/* TO 2100A mPCI 3B */
649062306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2582),	/* TO 2100A mPCI 3B */
649162306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2583),	/* TO 2100A mPCI 3B */
649262306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2581),	/* TO 2100 mPCI 3B */
649362306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2585),	/* TO 2100 mPCI 3B */
649462306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2586),	/* TO 2100 mPCI 3B */
649562306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2587),	/* TO 2100 mPCI 3B */
649662306a36Sopenharmony_ci
649762306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2590),	/* SO 2100A mPCI 3B */
649862306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2592),	/* SO 2100A mPCI 3B */
649962306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2591),	/* SO 2100 mPCI 3B */
650062306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2593),	/* SO 2100 mPCI 3B */
650162306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2596),	/* SO 2100 mPCI 3B */
650262306a36Sopenharmony_ci	IPW2100_DEV_ID(0x2598),	/* SO 2100 mPCI 3B */
650362306a36Sopenharmony_ci
650462306a36Sopenharmony_ci	IPW2100_DEV_ID(0x25A0),	/* HP 2100 mPCI 3B */
650562306a36Sopenharmony_ci	{0,},
650662306a36Sopenharmony_ci};
650762306a36Sopenharmony_ci
650862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ipw2100_pci_id_table);
650962306a36Sopenharmony_ci
651062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ipw2100_pm_ops, ipw2100_suspend, ipw2100_resume);
651162306a36Sopenharmony_ci
651262306a36Sopenharmony_cistatic struct pci_driver ipw2100_pci_driver = {
651362306a36Sopenharmony_ci	.name = DRV_NAME,
651462306a36Sopenharmony_ci	.id_table = ipw2100_pci_id_table,
651562306a36Sopenharmony_ci	.probe = ipw2100_pci_init_one,
651662306a36Sopenharmony_ci	.remove = ipw2100_pci_remove_one,
651762306a36Sopenharmony_ci	.driver.pm = &ipw2100_pm_ops,
651862306a36Sopenharmony_ci	.shutdown = ipw2100_shutdown,
651962306a36Sopenharmony_ci};
652062306a36Sopenharmony_ci
652162306a36Sopenharmony_ci/*
652262306a36Sopenharmony_ci * Initialize the ipw2100 driver/module
652362306a36Sopenharmony_ci *
652462306a36Sopenharmony_ci * @returns 0 if ok, < 0 errno node con error.
652562306a36Sopenharmony_ci *
652662306a36Sopenharmony_ci * Note: we cannot init the /proc stuff until the PCI driver is there,
652762306a36Sopenharmony_ci * or we risk an unlikely race condition on someone accessing
652862306a36Sopenharmony_ci * uninitialized data in the PCI dev struct through /proc.
652962306a36Sopenharmony_ci */
653062306a36Sopenharmony_cistatic int __init ipw2100_init(void)
653162306a36Sopenharmony_ci{
653262306a36Sopenharmony_ci	int ret;
653362306a36Sopenharmony_ci
653462306a36Sopenharmony_ci	printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
653562306a36Sopenharmony_ci	printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT);
653662306a36Sopenharmony_ci
653762306a36Sopenharmony_ci	cpu_latency_qos_add_request(&ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE);
653862306a36Sopenharmony_ci
653962306a36Sopenharmony_ci	ret = pci_register_driver(&ipw2100_pci_driver);
654062306a36Sopenharmony_ci	if (ret)
654162306a36Sopenharmony_ci		goto out;
654262306a36Sopenharmony_ci
654362306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
654462306a36Sopenharmony_ci	ipw2100_debug_level = debug;
654562306a36Sopenharmony_ci	ret = driver_create_file(&ipw2100_pci_driver.driver,
654662306a36Sopenharmony_ci				 &driver_attr_debug_level);
654762306a36Sopenharmony_ci#endif
654862306a36Sopenharmony_ci
654962306a36Sopenharmony_ciout:
655062306a36Sopenharmony_ci	return ret;
655162306a36Sopenharmony_ci}
655262306a36Sopenharmony_ci
655362306a36Sopenharmony_ci/*
655462306a36Sopenharmony_ci * Cleanup ipw2100 driver registration
655562306a36Sopenharmony_ci */
655662306a36Sopenharmony_cistatic void __exit ipw2100_exit(void)
655762306a36Sopenharmony_ci{
655862306a36Sopenharmony_ci	/* FIXME: IPG: check that we have no instances of the devices open */
655962306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
656062306a36Sopenharmony_ci	driver_remove_file(&ipw2100_pci_driver.driver,
656162306a36Sopenharmony_ci			   &driver_attr_debug_level);
656262306a36Sopenharmony_ci#endif
656362306a36Sopenharmony_ci	pci_unregister_driver(&ipw2100_pci_driver);
656462306a36Sopenharmony_ci	cpu_latency_qos_remove_request(&ipw2100_pm_qos_req);
656562306a36Sopenharmony_ci}
656662306a36Sopenharmony_ci
656762306a36Sopenharmony_cimodule_init(ipw2100_init);
656862306a36Sopenharmony_cimodule_exit(ipw2100_exit);
656962306a36Sopenharmony_ci
657062306a36Sopenharmony_cistatic int ipw2100_wx_get_name(struct net_device *dev,
657162306a36Sopenharmony_ci			       struct iw_request_info *info,
657262306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
657362306a36Sopenharmony_ci{
657462306a36Sopenharmony_ci	/*
657562306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
657662306a36Sopenharmony_ci	 */
657762306a36Sopenharmony_ci
657862306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
657962306a36Sopenharmony_ci	if (!(priv->status & STATUS_ASSOCIATED))
658062306a36Sopenharmony_ci		strcpy(wrqu->name, "unassociated");
658162306a36Sopenharmony_ci	else
658262306a36Sopenharmony_ci		snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b");
658362306a36Sopenharmony_ci
658462306a36Sopenharmony_ci	IPW_DEBUG_WX("Name: %s\n", wrqu->name);
658562306a36Sopenharmony_ci	return 0;
658662306a36Sopenharmony_ci}
658762306a36Sopenharmony_ci
658862306a36Sopenharmony_cistatic int ipw2100_wx_set_freq(struct net_device *dev,
658962306a36Sopenharmony_ci			       struct iw_request_info *info,
659062306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
659162306a36Sopenharmony_ci{
659262306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
659362306a36Sopenharmony_ci	struct iw_freq *fwrq = &wrqu->freq;
659462306a36Sopenharmony_ci	int err = 0;
659562306a36Sopenharmony_ci
659662306a36Sopenharmony_ci	if (priv->ieee->iw_mode == IW_MODE_INFRA)
659762306a36Sopenharmony_ci		return -EOPNOTSUPP;
659862306a36Sopenharmony_ci
659962306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
660062306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
660162306a36Sopenharmony_ci		err = -EIO;
660262306a36Sopenharmony_ci		goto done;
660362306a36Sopenharmony_ci	}
660462306a36Sopenharmony_ci
660562306a36Sopenharmony_ci	/* if setting by freq convert to channel */
660662306a36Sopenharmony_ci	if (fwrq->e == 1) {
660762306a36Sopenharmony_ci		if ((fwrq->m >= (int)2.412e8 && fwrq->m <= (int)2.487e8)) {
660862306a36Sopenharmony_ci			int f = fwrq->m / 100000;
660962306a36Sopenharmony_ci			int c = 0;
661062306a36Sopenharmony_ci
661162306a36Sopenharmony_ci			while ((c < REG_MAX_CHANNEL) &&
661262306a36Sopenharmony_ci			       (f != ipw2100_frequencies[c]))
661362306a36Sopenharmony_ci				c++;
661462306a36Sopenharmony_ci
661562306a36Sopenharmony_ci			/* hack to fall through */
661662306a36Sopenharmony_ci			fwrq->e = 0;
661762306a36Sopenharmony_ci			fwrq->m = c + 1;
661862306a36Sopenharmony_ci		}
661962306a36Sopenharmony_ci	}
662062306a36Sopenharmony_ci
662162306a36Sopenharmony_ci	if (fwrq->e > 0 || fwrq->m > 1000) {
662262306a36Sopenharmony_ci		err = -EOPNOTSUPP;
662362306a36Sopenharmony_ci		goto done;
662462306a36Sopenharmony_ci	} else {		/* Set the channel */
662562306a36Sopenharmony_ci		IPW_DEBUG_WX("SET Freq/Channel -> %d\n", fwrq->m);
662662306a36Sopenharmony_ci		err = ipw2100_set_channel(priv, fwrq->m, 0);
662762306a36Sopenharmony_ci	}
662862306a36Sopenharmony_ci
662962306a36Sopenharmony_ci      done:
663062306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
663162306a36Sopenharmony_ci	return err;
663262306a36Sopenharmony_ci}
663362306a36Sopenharmony_ci
663462306a36Sopenharmony_cistatic int ipw2100_wx_get_freq(struct net_device *dev,
663562306a36Sopenharmony_ci			       struct iw_request_info *info,
663662306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
663762306a36Sopenharmony_ci{
663862306a36Sopenharmony_ci	/*
663962306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
664062306a36Sopenharmony_ci	 */
664162306a36Sopenharmony_ci
664262306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
664362306a36Sopenharmony_ci
664462306a36Sopenharmony_ci	wrqu->freq.e = 0;
664562306a36Sopenharmony_ci
664662306a36Sopenharmony_ci	/* If we are associated, trying to associate, or have a statically
664762306a36Sopenharmony_ci	 * configured CHANNEL then return that; otherwise return ANY */
664862306a36Sopenharmony_ci	if (priv->config & CFG_STATIC_CHANNEL ||
664962306a36Sopenharmony_ci	    priv->status & STATUS_ASSOCIATED)
665062306a36Sopenharmony_ci		wrqu->freq.m = priv->channel;
665162306a36Sopenharmony_ci	else
665262306a36Sopenharmony_ci		wrqu->freq.m = 0;
665362306a36Sopenharmony_ci
665462306a36Sopenharmony_ci	IPW_DEBUG_WX("GET Freq/Channel -> %d\n", priv->channel);
665562306a36Sopenharmony_ci	return 0;
665662306a36Sopenharmony_ci
665762306a36Sopenharmony_ci}
665862306a36Sopenharmony_ci
665962306a36Sopenharmony_cistatic int ipw2100_wx_set_mode(struct net_device *dev,
666062306a36Sopenharmony_ci			       struct iw_request_info *info,
666162306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
666262306a36Sopenharmony_ci{
666362306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
666462306a36Sopenharmony_ci	int err = 0;
666562306a36Sopenharmony_ci
666662306a36Sopenharmony_ci	IPW_DEBUG_WX("SET Mode -> %d\n", wrqu->mode);
666762306a36Sopenharmony_ci
666862306a36Sopenharmony_ci	if (wrqu->mode == priv->ieee->iw_mode)
666962306a36Sopenharmony_ci		return 0;
667062306a36Sopenharmony_ci
667162306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
667262306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
667362306a36Sopenharmony_ci		err = -EIO;
667462306a36Sopenharmony_ci		goto done;
667562306a36Sopenharmony_ci	}
667662306a36Sopenharmony_ci
667762306a36Sopenharmony_ci	switch (wrqu->mode) {
667862306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
667962306a36Sopenharmony_ci	case IW_MODE_MONITOR:
668062306a36Sopenharmony_ci		err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
668162306a36Sopenharmony_ci		break;
668262306a36Sopenharmony_ci#endif				/* CONFIG_IPW2100_MONITOR */
668362306a36Sopenharmony_ci	case IW_MODE_ADHOC:
668462306a36Sopenharmony_ci		err = ipw2100_switch_mode(priv, IW_MODE_ADHOC);
668562306a36Sopenharmony_ci		break;
668662306a36Sopenharmony_ci	case IW_MODE_INFRA:
668762306a36Sopenharmony_ci	case IW_MODE_AUTO:
668862306a36Sopenharmony_ci	default:
668962306a36Sopenharmony_ci		err = ipw2100_switch_mode(priv, IW_MODE_INFRA);
669062306a36Sopenharmony_ci		break;
669162306a36Sopenharmony_ci	}
669262306a36Sopenharmony_ci
669362306a36Sopenharmony_ci      done:
669462306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
669562306a36Sopenharmony_ci	return err;
669662306a36Sopenharmony_ci}
669762306a36Sopenharmony_ci
669862306a36Sopenharmony_cistatic int ipw2100_wx_get_mode(struct net_device *dev,
669962306a36Sopenharmony_ci			       struct iw_request_info *info,
670062306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
670162306a36Sopenharmony_ci{
670262306a36Sopenharmony_ci	/*
670362306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
670462306a36Sopenharmony_ci	 */
670562306a36Sopenharmony_ci
670662306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
670762306a36Sopenharmony_ci
670862306a36Sopenharmony_ci	wrqu->mode = priv->ieee->iw_mode;
670962306a36Sopenharmony_ci	IPW_DEBUG_WX("GET Mode -> %d\n", wrqu->mode);
671062306a36Sopenharmony_ci
671162306a36Sopenharmony_ci	return 0;
671262306a36Sopenharmony_ci}
671362306a36Sopenharmony_ci
671462306a36Sopenharmony_ci#define POWER_MODES 5
671562306a36Sopenharmony_ci
671662306a36Sopenharmony_ci/* Values are in microsecond */
671762306a36Sopenharmony_cistatic const s32 timeout_duration[POWER_MODES] = {
671862306a36Sopenharmony_ci	350000,
671962306a36Sopenharmony_ci	250000,
672062306a36Sopenharmony_ci	75000,
672162306a36Sopenharmony_ci	37000,
672262306a36Sopenharmony_ci	25000,
672362306a36Sopenharmony_ci};
672462306a36Sopenharmony_ci
672562306a36Sopenharmony_cistatic const s32 period_duration[POWER_MODES] = {
672662306a36Sopenharmony_ci	400000,
672762306a36Sopenharmony_ci	700000,
672862306a36Sopenharmony_ci	1000000,
672962306a36Sopenharmony_ci	1000000,
673062306a36Sopenharmony_ci	1000000
673162306a36Sopenharmony_ci};
673262306a36Sopenharmony_ci
673362306a36Sopenharmony_cistatic int ipw2100_wx_get_range(struct net_device *dev,
673462306a36Sopenharmony_ci				struct iw_request_info *info,
673562306a36Sopenharmony_ci				union iwreq_data *wrqu, char *extra)
673662306a36Sopenharmony_ci{
673762306a36Sopenharmony_ci	/*
673862306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
673962306a36Sopenharmony_ci	 */
674062306a36Sopenharmony_ci
674162306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
674262306a36Sopenharmony_ci	struct iw_range *range = (struct iw_range *)extra;
674362306a36Sopenharmony_ci	u16 val;
674462306a36Sopenharmony_ci	int i, level;
674562306a36Sopenharmony_ci
674662306a36Sopenharmony_ci	wrqu->data.length = sizeof(*range);
674762306a36Sopenharmony_ci	memset(range, 0, sizeof(*range));
674862306a36Sopenharmony_ci
674962306a36Sopenharmony_ci	/* Let's try to keep this struct in the same order as in
675062306a36Sopenharmony_ci	 * linux/include/wireless.h
675162306a36Sopenharmony_ci	 */
675262306a36Sopenharmony_ci
675362306a36Sopenharmony_ci	/* TODO: See what values we can set, and remove the ones we can't
675462306a36Sopenharmony_ci	 * set, or fill them with some default data.
675562306a36Sopenharmony_ci	 */
675662306a36Sopenharmony_ci
675762306a36Sopenharmony_ci	/* ~5 Mb/s real (802.11b) */
675862306a36Sopenharmony_ci	range->throughput = 5 * 1000 * 1000;
675962306a36Sopenharmony_ci
676062306a36Sopenharmony_ci//      range->sensitivity;     /* signal level threshold range */
676162306a36Sopenharmony_ci
676262306a36Sopenharmony_ci	range->max_qual.qual = 100;
676362306a36Sopenharmony_ci	/* TODO: Find real max RSSI and stick here */
676462306a36Sopenharmony_ci	range->max_qual.level = 0;
676562306a36Sopenharmony_ci	range->max_qual.noise = 0;
676662306a36Sopenharmony_ci	range->max_qual.updated = 7;	/* Updated all three */
676762306a36Sopenharmony_ci
676862306a36Sopenharmony_ci	range->avg_qual.qual = 70;	/* > 8% missed beacons is 'bad' */
676962306a36Sopenharmony_ci	/* TODO: Find real 'good' to 'bad' threshold value for RSSI */
677062306a36Sopenharmony_ci	range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM;
677162306a36Sopenharmony_ci	range->avg_qual.noise = 0;
677262306a36Sopenharmony_ci	range->avg_qual.updated = 7;	/* Updated all three */
677362306a36Sopenharmony_ci
677462306a36Sopenharmony_ci	range->num_bitrates = RATE_COUNT;
677562306a36Sopenharmony_ci
677662306a36Sopenharmony_ci	for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) {
677762306a36Sopenharmony_ci		range->bitrate[i] = ipw2100_bg_rates[i].bitrate * 100 * 1000;
677862306a36Sopenharmony_ci	}
677962306a36Sopenharmony_ci
678062306a36Sopenharmony_ci	range->min_rts = MIN_RTS_THRESHOLD;
678162306a36Sopenharmony_ci	range->max_rts = MAX_RTS_THRESHOLD;
678262306a36Sopenharmony_ci	range->min_frag = MIN_FRAG_THRESHOLD;
678362306a36Sopenharmony_ci	range->max_frag = MAX_FRAG_THRESHOLD;
678462306a36Sopenharmony_ci
678562306a36Sopenharmony_ci	range->min_pmp = period_duration[0];	/* Minimal PM period */
678662306a36Sopenharmony_ci	range->max_pmp = period_duration[POWER_MODES - 1];	/* Maximal PM period */
678762306a36Sopenharmony_ci	range->min_pmt = timeout_duration[POWER_MODES - 1];	/* Minimal PM timeout */
678862306a36Sopenharmony_ci	range->max_pmt = timeout_duration[0];	/* Maximal PM timeout */
678962306a36Sopenharmony_ci
679062306a36Sopenharmony_ci	/* How to decode max/min PM period */
679162306a36Sopenharmony_ci	range->pmp_flags = IW_POWER_PERIOD;
679262306a36Sopenharmony_ci	/* How to decode max/min PM period */
679362306a36Sopenharmony_ci	range->pmt_flags = IW_POWER_TIMEOUT;
679462306a36Sopenharmony_ci	/* What PM options are supported */
679562306a36Sopenharmony_ci	range->pm_capa = IW_POWER_TIMEOUT | IW_POWER_PERIOD;
679662306a36Sopenharmony_ci
679762306a36Sopenharmony_ci	range->encoding_size[0] = 5;
679862306a36Sopenharmony_ci	range->encoding_size[1] = 13;	/* Different token sizes */
679962306a36Sopenharmony_ci	range->num_encoding_sizes = 2;	/* Number of entry in the list */
680062306a36Sopenharmony_ci	range->max_encoding_tokens = WEP_KEYS;	/* Max number of tokens */
680162306a36Sopenharmony_ci//      range->encoding_login_index;            /* token index for login token */
680262306a36Sopenharmony_ci
680362306a36Sopenharmony_ci	if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
680462306a36Sopenharmony_ci		range->txpower_capa = IW_TXPOW_DBM;
680562306a36Sopenharmony_ci		range->num_txpower = IW_MAX_TXPOWER;
680662306a36Sopenharmony_ci		for (i = 0, level = (IPW_TX_POWER_MAX_DBM * 16);
680762306a36Sopenharmony_ci		     i < IW_MAX_TXPOWER;
680862306a36Sopenharmony_ci		     i++, level -=
680962306a36Sopenharmony_ci		     ((IPW_TX_POWER_MAX_DBM -
681062306a36Sopenharmony_ci		       IPW_TX_POWER_MIN_DBM) * 16) / (IW_MAX_TXPOWER - 1))
681162306a36Sopenharmony_ci			range->txpower[i] = level / 16;
681262306a36Sopenharmony_ci	} else {
681362306a36Sopenharmony_ci		range->txpower_capa = 0;
681462306a36Sopenharmony_ci		range->num_txpower = 0;
681562306a36Sopenharmony_ci	}
681662306a36Sopenharmony_ci
681762306a36Sopenharmony_ci	/* Set the Wireless Extension versions */
681862306a36Sopenharmony_ci	range->we_version_compiled = WIRELESS_EXT;
681962306a36Sopenharmony_ci	range->we_version_source = 18;
682062306a36Sopenharmony_ci
682162306a36Sopenharmony_ci//      range->retry_capa;      /* What retry options are supported */
682262306a36Sopenharmony_ci//      range->retry_flags;     /* How to decode max/min retry limit */
682362306a36Sopenharmony_ci//      range->r_time_flags;    /* How to decode max/min retry life */
682462306a36Sopenharmony_ci//      range->min_retry;       /* Minimal number of retries */
682562306a36Sopenharmony_ci//      range->max_retry;       /* Maximal number of retries */
682662306a36Sopenharmony_ci//      range->min_r_time;      /* Minimal retry lifetime */
682762306a36Sopenharmony_ci//      range->max_r_time;      /* Maximal retry lifetime */
682862306a36Sopenharmony_ci
682962306a36Sopenharmony_ci	range->num_channels = FREQ_COUNT;
683062306a36Sopenharmony_ci
683162306a36Sopenharmony_ci	val = 0;
683262306a36Sopenharmony_ci	for (i = 0; i < FREQ_COUNT; i++) {
683362306a36Sopenharmony_ci		// TODO: Include only legal frequencies for some countries
683462306a36Sopenharmony_ci//              if (local->channel_mask & (1 << i)) {
683562306a36Sopenharmony_ci		range->freq[val].i = i + 1;
683662306a36Sopenharmony_ci		range->freq[val].m = ipw2100_frequencies[i] * 100000;
683762306a36Sopenharmony_ci		range->freq[val].e = 1;
683862306a36Sopenharmony_ci		val++;
683962306a36Sopenharmony_ci//              }
684062306a36Sopenharmony_ci		if (val == IW_MAX_FREQUENCIES)
684162306a36Sopenharmony_ci			break;
684262306a36Sopenharmony_ci	}
684362306a36Sopenharmony_ci	range->num_frequency = val;
684462306a36Sopenharmony_ci
684562306a36Sopenharmony_ci	/* Event capability (kernel + driver) */
684662306a36Sopenharmony_ci	range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
684762306a36Sopenharmony_ci				IW_EVENT_CAPA_MASK(SIOCGIWAP));
684862306a36Sopenharmony_ci	range->event_capa[1] = IW_EVENT_CAPA_K_1;
684962306a36Sopenharmony_ci
685062306a36Sopenharmony_ci	range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
685162306a36Sopenharmony_ci		IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
685262306a36Sopenharmony_ci
685362306a36Sopenharmony_ci	IPW_DEBUG_WX("GET Range\n");
685462306a36Sopenharmony_ci
685562306a36Sopenharmony_ci	return 0;
685662306a36Sopenharmony_ci}
685762306a36Sopenharmony_ci
685862306a36Sopenharmony_cistatic int ipw2100_wx_set_wap(struct net_device *dev,
685962306a36Sopenharmony_ci			      struct iw_request_info *info,
686062306a36Sopenharmony_ci			      union iwreq_data *wrqu, char *extra)
686162306a36Sopenharmony_ci{
686262306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
686362306a36Sopenharmony_ci	int err = 0;
686462306a36Sopenharmony_ci
686562306a36Sopenharmony_ci	// sanity checks
686662306a36Sopenharmony_ci	if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
686762306a36Sopenharmony_ci		return -EINVAL;
686862306a36Sopenharmony_ci
686962306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
687062306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
687162306a36Sopenharmony_ci		err = -EIO;
687262306a36Sopenharmony_ci		goto done;
687362306a36Sopenharmony_ci	}
687462306a36Sopenharmony_ci
687562306a36Sopenharmony_ci	if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) ||
687662306a36Sopenharmony_ci	    is_zero_ether_addr(wrqu->ap_addr.sa_data)) {
687762306a36Sopenharmony_ci		/* we disable mandatory BSSID association */
687862306a36Sopenharmony_ci		IPW_DEBUG_WX("exit - disable mandatory BSSID\n");
687962306a36Sopenharmony_ci		priv->config &= ~CFG_STATIC_BSSID;
688062306a36Sopenharmony_ci		err = ipw2100_set_mandatory_bssid(priv, NULL, 0);
688162306a36Sopenharmony_ci		goto done;
688262306a36Sopenharmony_ci	}
688362306a36Sopenharmony_ci
688462306a36Sopenharmony_ci	priv->config |= CFG_STATIC_BSSID;
688562306a36Sopenharmony_ci	memcpy(priv->mandatory_bssid_mac, wrqu->ap_addr.sa_data, ETH_ALEN);
688662306a36Sopenharmony_ci
688762306a36Sopenharmony_ci	err = ipw2100_set_mandatory_bssid(priv, wrqu->ap_addr.sa_data, 0);
688862306a36Sopenharmony_ci
688962306a36Sopenharmony_ci	IPW_DEBUG_WX("SET BSSID -> %pM\n", wrqu->ap_addr.sa_data);
689062306a36Sopenharmony_ci
689162306a36Sopenharmony_ci      done:
689262306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
689362306a36Sopenharmony_ci	return err;
689462306a36Sopenharmony_ci}
689562306a36Sopenharmony_ci
689662306a36Sopenharmony_cistatic int ipw2100_wx_get_wap(struct net_device *dev,
689762306a36Sopenharmony_ci			      struct iw_request_info *info,
689862306a36Sopenharmony_ci			      union iwreq_data *wrqu, char *extra)
689962306a36Sopenharmony_ci{
690062306a36Sopenharmony_ci	/*
690162306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
690262306a36Sopenharmony_ci	 */
690362306a36Sopenharmony_ci
690462306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
690562306a36Sopenharmony_ci
690662306a36Sopenharmony_ci	/* If we are associated, trying to associate, or have a statically
690762306a36Sopenharmony_ci	 * configured BSSID then return that; otherwise return ANY */
690862306a36Sopenharmony_ci	if (priv->config & CFG_STATIC_BSSID || priv->status & STATUS_ASSOCIATED) {
690962306a36Sopenharmony_ci		wrqu->ap_addr.sa_family = ARPHRD_ETHER;
691062306a36Sopenharmony_ci		memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
691162306a36Sopenharmony_ci	} else
691262306a36Sopenharmony_ci		eth_zero_addr(wrqu->ap_addr.sa_data);
691362306a36Sopenharmony_ci
691462306a36Sopenharmony_ci	IPW_DEBUG_WX("Getting WAP BSSID: %pM\n", wrqu->ap_addr.sa_data);
691562306a36Sopenharmony_ci	return 0;
691662306a36Sopenharmony_ci}
691762306a36Sopenharmony_ci
691862306a36Sopenharmony_cistatic int ipw2100_wx_set_essid(struct net_device *dev,
691962306a36Sopenharmony_ci				struct iw_request_info *info,
692062306a36Sopenharmony_ci				union iwreq_data *wrqu, char *extra)
692162306a36Sopenharmony_ci{
692262306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
692362306a36Sopenharmony_ci	char *essid = "";	/* ANY */
692462306a36Sopenharmony_ci	int length = 0;
692562306a36Sopenharmony_ci	int err = 0;
692662306a36Sopenharmony_ci
692762306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
692862306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
692962306a36Sopenharmony_ci		err = -EIO;
693062306a36Sopenharmony_ci		goto done;
693162306a36Sopenharmony_ci	}
693262306a36Sopenharmony_ci
693362306a36Sopenharmony_ci	if (wrqu->essid.flags && wrqu->essid.length) {
693462306a36Sopenharmony_ci		length = wrqu->essid.length;
693562306a36Sopenharmony_ci		essid = extra;
693662306a36Sopenharmony_ci	}
693762306a36Sopenharmony_ci
693862306a36Sopenharmony_ci	if (length == 0) {
693962306a36Sopenharmony_ci		IPW_DEBUG_WX("Setting ESSID to ANY\n");
694062306a36Sopenharmony_ci		priv->config &= ~CFG_STATIC_ESSID;
694162306a36Sopenharmony_ci		err = ipw2100_set_essid(priv, NULL, 0, 0);
694262306a36Sopenharmony_ci		goto done;
694362306a36Sopenharmony_ci	}
694462306a36Sopenharmony_ci
694562306a36Sopenharmony_ci	length = min(length, IW_ESSID_MAX_SIZE);
694662306a36Sopenharmony_ci
694762306a36Sopenharmony_ci	priv->config |= CFG_STATIC_ESSID;
694862306a36Sopenharmony_ci
694962306a36Sopenharmony_ci	if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
695062306a36Sopenharmony_ci		IPW_DEBUG_WX("ESSID set to current ESSID.\n");
695162306a36Sopenharmony_ci		err = 0;
695262306a36Sopenharmony_ci		goto done;
695362306a36Sopenharmony_ci	}
695462306a36Sopenharmony_ci
695562306a36Sopenharmony_ci	IPW_DEBUG_WX("Setting ESSID: '%*pE' (%d)\n", length, essid, length);
695662306a36Sopenharmony_ci
695762306a36Sopenharmony_ci	priv->essid_len = length;
695862306a36Sopenharmony_ci	memcpy(priv->essid, essid, priv->essid_len);
695962306a36Sopenharmony_ci
696062306a36Sopenharmony_ci	err = ipw2100_set_essid(priv, essid, length, 0);
696162306a36Sopenharmony_ci
696262306a36Sopenharmony_ci      done:
696362306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
696462306a36Sopenharmony_ci	return err;
696562306a36Sopenharmony_ci}
696662306a36Sopenharmony_ci
696762306a36Sopenharmony_cistatic int ipw2100_wx_get_essid(struct net_device *dev,
696862306a36Sopenharmony_ci				struct iw_request_info *info,
696962306a36Sopenharmony_ci				union iwreq_data *wrqu, char *extra)
697062306a36Sopenharmony_ci{
697162306a36Sopenharmony_ci	/*
697262306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
697362306a36Sopenharmony_ci	 */
697462306a36Sopenharmony_ci
697562306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
697662306a36Sopenharmony_ci
697762306a36Sopenharmony_ci	/* If we are associated, trying to associate, or have a statically
697862306a36Sopenharmony_ci	 * configured ESSID then return that; otherwise return ANY */
697962306a36Sopenharmony_ci	if (priv->config & CFG_STATIC_ESSID || priv->status & STATUS_ASSOCIATED) {
698062306a36Sopenharmony_ci		IPW_DEBUG_WX("Getting essid: '%*pE'\n",
698162306a36Sopenharmony_ci			     priv->essid_len, priv->essid);
698262306a36Sopenharmony_ci		memcpy(extra, priv->essid, priv->essid_len);
698362306a36Sopenharmony_ci		wrqu->essid.length = priv->essid_len;
698462306a36Sopenharmony_ci		wrqu->essid.flags = 1;	/* active */
698562306a36Sopenharmony_ci	} else {
698662306a36Sopenharmony_ci		IPW_DEBUG_WX("Getting essid: ANY\n");
698762306a36Sopenharmony_ci		wrqu->essid.length = 0;
698862306a36Sopenharmony_ci		wrqu->essid.flags = 0;	/* active */
698962306a36Sopenharmony_ci	}
699062306a36Sopenharmony_ci
699162306a36Sopenharmony_ci	return 0;
699262306a36Sopenharmony_ci}
699362306a36Sopenharmony_ci
699462306a36Sopenharmony_cistatic int ipw2100_wx_set_nick(struct net_device *dev,
699562306a36Sopenharmony_ci			       struct iw_request_info *info,
699662306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
699762306a36Sopenharmony_ci{
699862306a36Sopenharmony_ci	/*
699962306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
700062306a36Sopenharmony_ci	 */
700162306a36Sopenharmony_ci
700262306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
700362306a36Sopenharmony_ci
700462306a36Sopenharmony_ci	if (wrqu->data.length > IW_ESSID_MAX_SIZE)
700562306a36Sopenharmony_ci		return -E2BIG;
700662306a36Sopenharmony_ci
700762306a36Sopenharmony_ci	wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick));
700862306a36Sopenharmony_ci	memset(priv->nick, 0, sizeof(priv->nick));
700962306a36Sopenharmony_ci	memcpy(priv->nick, extra, wrqu->data.length);
701062306a36Sopenharmony_ci
701162306a36Sopenharmony_ci	IPW_DEBUG_WX("SET Nickname -> %s\n", priv->nick);
701262306a36Sopenharmony_ci
701362306a36Sopenharmony_ci	return 0;
701462306a36Sopenharmony_ci}
701562306a36Sopenharmony_ci
701662306a36Sopenharmony_cistatic int ipw2100_wx_get_nick(struct net_device *dev,
701762306a36Sopenharmony_ci			       struct iw_request_info *info,
701862306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
701962306a36Sopenharmony_ci{
702062306a36Sopenharmony_ci	/*
702162306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
702262306a36Sopenharmony_ci	 */
702362306a36Sopenharmony_ci
702462306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
702562306a36Sopenharmony_ci
702662306a36Sopenharmony_ci	wrqu->data.length = strlen(priv->nick);
702762306a36Sopenharmony_ci	memcpy(extra, priv->nick, wrqu->data.length);
702862306a36Sopenharmony_ci	wrqu->data.flags = 1;	/* active */
702962306a36Sopenharmony_ci
703062306a36Sopenharmony_ci	IPW_DEBUG_WX("GET Nickname -> %s\n", extra);
703162306a36Sopenharmony_ci
703262306a36Sopenharmony_ci	return 0;
703362306a36Sopenharmony_ci}
703462306a36Sopenharmony_ci
703562306a36Sopenharmony_cistatic int ipw2100_wx_set_rate(struct net_device *dev,
703662306a36Sopenharmony_ci			       struct iw_request_info *info,
703762306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
703862306a36Sopenharmony_ci{
703962306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
704062306a36Sopenharmony_ci	u32 target_rate = wrqu->bitrate.value;
704162306a36Sopenharmony_ci	u32 rate;
704262306a36Sopenharmony_ci	int err = 0;
704362306a36Sopenharmony_ci
704462306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
704562306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
704662306a36Sopenharmony_ci		err = -EIO;
704762306a36Sopenharmony_ci		goto done;
704862306a36Sopenharmony_ci	}
704962306a36Sopenharmony_ci
705062306a36Sopenharmony_ci	rate = 0;
705162306a36Sopenharmony_ci
705262306a36Sopenharmony_ci	if (target_rate == 1000000 ||
705362306a36Sopenharmony_ci	    (!wrqu->bitrate.fixed && target_rate > 1000000))
705462306a36Sopenharmony_ci		rate |= TX_RATE_1_MBIT;
705562306a36Sopenharmony_ci	if (target_rate == 2000000 ||
705662306a36Sopenharmony_ci	    (!wrqu->bitrate.fixed && target_rate > 2000000))
705762306a36Sopenharmony_ci		rate |= TX_RATE_2_MBIT;
705862306a36Sopenharmony_ci	if (target_rate == 5500000 ||
705962306a36Sopenharmony_ci	    (!wrqu->bitrate.fixed && target_rate > 5500000))
706062306a36Sopenharmony_ci		rate |= TX_RATE_5_5_MBIT;
706162306a36Sopenharmony_ci	if (target_rate == 11000000 ||
706262306a36Sopenharmony_ci	    (!wrqu->bitrate.fixed && target_rate > 11000000))
706362306a36Sopenharmony_ci		rate |= TX_RATE_11_MBIT;
706462306a36Sopenharmony_ci	if (rate == 0)
706562306a36Sopenharmony_ci		rate = DEFAULT_TX_RATES;
706662306a36Sopenharmony_ci
706762306a36Sopenharmony_ci	err = ipw2100_set_tx_rates(priv, rate, 0);
706862306a36Sopenharmony_ci
706962306a36Sopenharmony_ci	IPW_DEBUG_WX("SET Rate -> %04X\n", rate);
707062306a36Sopenharmony_ci      done:
707162306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
707262306a36Sopenharmony_ci	return err;
707362306a36Sopenharmony_ci}
707462306a36Sopenharmony_ci
707562306a36Sopenharmony_cistatic int ipw2100_wx_get_rate(struct net_device *dev,
707662306a36Sopenharmony_ci			       struct iw_request_info *info,
707762306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
707862306a36Sopenharmony_ci{
707962306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
708062306a36Sopenharmony_ci	int val;
708162306a36Sopenharmony_ci	unsigned int len = sizeof(val);
708262306a36Sopenharmony_ci	int err = 0;
708362306a36Sopenharmony_ci
708462306a36Sopenharmony_ci	if (!(priv->status & STATUS_ENABLED) ||
708562306a36Sopenharmony_ci	    priv->status & STATUS_RF_KILL_MASK ||
708662306a36Sopenharmony_ci	    !(priv->status & STATUS_ASSOCIATED)) {
708762306a36Sopenharmony_ci		wrqu->bitrate.value = 0;
708862306a36Sopenharmony_ci		return 0;
708962306a36Sopenharmony_ci	}
709062306a36Sopenharmony_ci
709162306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
709262306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
709362306a36Sopenharmony_ci		err = -EIO;
709462306a36Sopenharmony_ci		goto done;
709562306a36Sopenharmony_ci	}
709662306a36Sopenharmony_ci
709762306a36Sopenharmony_ci	err = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &val, &len);
709862306a36Sopenharmony_ci	if (err) {
709962306a36Sopenharmony_ci		IPW_DEBUG_WX("failed querying ordinals.\n");
710062306a36Sopenharmony_ci		goto done;
710162306a36Sopenharmony_ci	}
710262306a36Sopenharmony_ci
710362306a36Sopenharmony_ci	switch (val & TX_RATE_MASK) {
710462306a36Sopenharmony_ci	case TX_RATE_1_MBIT:
710562306a36Sopenharmony_ci		wrqu->bitrate.value = 1000000;
710662306a36Sopenharmony_ci		break;
710762306a36Sopenharmony_ci	case TX_RATE_2_MBIT:
710862306a36Sopenharmony_ci		wrqu->bitrate.value = 2000000;
710962306a36Sopenharmony_ci		break;
711062306a36Sopenharmony_ci	case TX_RATE_5_5_MBIT:
711162306a36Sopenharmony_ci		wrqu->bitrate.value = 5500000;
711262306a36Sopenharmony_ci		break;
711362306a36Sopenharmony_ci	case TX_RATE_11_MBIT:
711462306a36Sopenharmony_ci		wrqu->bitrate.value = 11000000;
711562306a36Sopenharmony_ci		break;
711662306a36Sopenharmony_ci	default:
711762306a36Sopenharmony_ci		wrqu->bitrate.value = 0;
711862306a36Sopenharmony_ci	}
711962306a36Sopenharmony_ci
712062306a36Sopenharmony_ci	IPW_DEBUG_WX("GET Rate -> %d\n", wrqu->bitrate.value);
712162306a36Sopenharmony_ci
712262306a36Sopenharmony_ci      done:
712362306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
712462306a36Sopenharmony_ci	return err;
712562306a36Sopenharmony_ci}
712662306a36Sopenharmony_ci
712762306a36Sopenharmony_cistatic int ipw2100_wx_set_rts(struct net_device *dev,
712862306a36Sopenharmony_ci			      struct iw_request_info *info,
712962306a36Sopenharmony_ci			      union iwreq_data *wrqu, char *extra)
713062306a36Sopenharmony_ci{
713162306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
713262306a36Sopenharmony_ci	int value, err;
713362306a36Sopenharmony_ci
713462306a36Sopenharmony_ci	/* Auto RTS not yet supported */
713562306a36Sopenharmony_ci	if (wrqu->rts.fixed == 0)
713662306a36Sopenharmony_ci		return -EINVAL;
713762306a36Sopenharmony_ci
713862306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
713962306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
714062306a36Sopenharmony_ci		err = -EIO;
714162306a36Sopenharmony_ci		goto done;
714262306a36Sopenharmony_ci	}
714362306a36Sopenharmony_ci
714462306a36Sopenharmony_ci	if (wrqu->rts.disabled)
714562306a36Sopenharmony_ci		value = priv->rts_threshold | RTS_DISABLED;
714662306a36Sopenharmony_ci	else {
714762306a36Sopenharmony_ci		if (wrqu->rts.value < 1 || wrqu->rts.value > 2304) {
714862306a36Sopenharmony_ci			err = -EINVAL;
714962306a36Sopenharmony_ci			goto done;
715062306a36Sopenharmony_ci		}
715162306a36Sopenharmony_ci		value = wrqu->rts.value;
715262306a36Sopenharmony_ci	}
715362306a36Sopenharmony_ci
715462306a36Sopenharmony_ci	err = ipw2100_set_rts_threshold(priv, value);
715562306a36Sopenharmony_ci
715662306a36Sopenharmony_ci	IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X\n", value);
715762306a36Sopenharmony_ci      done:
715862306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
715962306a36Sopenharmony_ci	return err;
716062306a36Sopenharmony_ci}
716162306a36Sopenharmony_ci
716262306a36Sopenharmony_cistatic int ipw2100_wx_get_rts(struct net_device *dev,
716362306a36Sopenharmony_ci			      struct iw_request_info *info,
716462306a36Sopenharmony_ci			      union iwreq_data *wrqu, char *extra)
716562306a36Sopenharmony_ci{
716662306a36Sopenharmony_ci	/*
716762306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
716862306a36Sopenharmony_ci	 */
716962306a36Sopenharmony_ci
717062306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
717162306a36Sopenharmony_ci
717262306a36Sopenharmony_ci	wrqu->rts.value = priv->rts_threshold & ~RTS_DISABLED;
717362306a36Sopenharmony_ci	wrqu->rts.fixed = 1;	/* no auto select */
717462306a36Sopenharmony_ci
717562306a36Sopenharmony_ci	/* If RTS is set to the default value, then it is disabled */
717662306a36Sopenharmony_ci	wrqu->rts.disabled = (priv->rts_threshold & RTS_DISABLED) ? 1 : 0;
717762306a36Sopenharmony_ci
717862306a36Sopenharmony_ci	IPW_DEBUG_WX("GET RTS Threshold -> 0x%08X\n", wrqu->rts.value);
717962306a36Sopenharmony_ci
718062306a36Sopenharmony_ci	return 0;
718162306a36Sopenharmony_ci}
718262306a36Sopenharmony_ci
718362306a36Sopenharmony_cistatic int ipw2100_wx_set_txpow(struct net_device *dev,
718462306a36Sopenharmony_ci				struct iw_request_info *info,
718562306a36Sopenharmony_ci				union iwreq_data *wrqu, char *extra)
718662306a36Sopenharmony_ci{
718762306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
718862306a36Sopenharmony_ci	int err = 0, value;
718962306a36Sopenharmony_ci
719062306a36Sopenharmony_ci	if (ipw_radio_kill_sw(priv, wrqu->txpower.disabled))
719162306a36Sopenharmony_ci		return -EINPROGRESS;
719262306a36Sopenharmony_ci
719362306a36Sopenharmony_ci	if (priv->ieee->iw_mode != IW_MODE_ADHOC)
719462306a36Sopenharmony_ci		return 0;
719562306a36Sopenharmony_ci
719662306a36Sopenharmony_ci	if ((wrqu->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
719762306a36Sopenharmony_ci		return -EINVAL;
719862306a36Sopenharmony_ci
719962306a36Sopenharmony_ci	if (wrqu->txpower.fixed == 0)
720062306a36Sopenharmony_ci		value = IPW_TX_POWER_DEFAULT;
720162306a36Sopenharmony_ci	else {
720262306a36Sopenharmony_ci		if (wrqu->txpower.value < IPW_TX_POWER_MIN_DBM ||
720362306a36Sopenharmony_ci		    wrqu->txpower.value > IPW_TX_POWER_MAX_DBM)
720462306a36Sopenharmony_ci			return -EINVAL;
720562306a36Sopenharmony_ci
720662306a36Sopenharmony_ci		value = wrqu->txpower.value;
720762306a36Sopenharmony_ci	}
720862306a36Sopenharmony_ci
720962306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
721062306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
721162306a36Sopenharmony_ci		err = -EIO;
721262306a36Sopenharmony_ci		goto done;
721362306a36Sopenharmony_ci	}
721462306a36Sopenharmony_ci
721562306a36Sopenharmony_ci	err = ipw2100_set_tx_power(priv, value);
721662306a36Sopenharmony_ci
721762306a36Sopenharmony_ci	IPW_DEBUG_WX("SET TX Power -> %d\n", value);
721862306a36Sopenharmony_ci
721962306a36Sopenharmony_ci      done:
722062306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
722162306a36Sopenharmony_ci	return err;
722262306a36Sopenharmony_ci}
722362306a36Sopenharmony_ci
722462306a36Sopenharmony_cistatic int ipw2100_wx_get_txpow(struct net_device *dev,
722562306a36Sopenharmony_ci				struct iw_request_info *info,
722662306a36Sopenharmony_ci				union iwreq_data *wrqu, char *extra)
722762306a36Sopenharmony_ci{
722862306a36Sopenharmony_ci	/*
722962306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
723062306a36Sopenharmony_ci	 */
723162306a36Sopenharmony_ci
723262306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
723362306a36Sopenharmony_ci
723462306a36Sopenharmony_ci	wrqu->txpower.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
723562306a36Sopenharmony_ci
723662306a36Sopenharmony_ci	if (priv->tx_power == IPW_TX_POWER_DEFAULT) {
723762306a36Sopenharmony_ci		wrqu->txpower.fixed = 0;
723862306a36Sopenharmony_ci		wrqu->txpower.value = IPW_TX_POWER_MAX_DBM;
723962306a36Sopenharmony_ci	} else {
724062306a36Sopenharmony_ci		wrqu->txpower.fixed = 1;
724162306a36Sopenharmony_ci		wrqu->txpower.value = priv->tx_power;
724262306a36Sopenharmony_ci	}
724362306a36Sopenharmony_ci
724462306a36Sopenharmony_ci	wrqu->txpower.flags = IW_TXPOW_DBM;
724562306a36Sopenharmony_ci
724662306a36Sopenharmony_ci	IPW_DEBUG_WX("GET TX Power -> %d\n", wrqu->txpower.value);
724762306a36Sopenharmony_ci
724862306a36Sopenharmony_ci	return 0;
724962306a36Sopenharmony_ci}
725062306a36Sopenharmony_ci
725162306a36Sopenharmony_cistatic int ipw2100_wx_set_frag(struct net_device *dev,
725262306a36Sopenharmony_ci			       struct iw_request_info *info,
725362306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
725462306a36Sopenharmony_ci{
725562306a36Sopenharmony_ci	/*
725662306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
725762306a36Sopenharmony_ci	 */
725862306a36Sopenharmony_ci
725962306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
726062306a36Sopenharmony_ci
726162306a36Sopenharmony_ci	if (!wrqu->frag.fixed)
726262306a36Sopenharmony_ci		return -EINVAL;
726362306a36Sopenharmony_ci
726462306a36Sopenharmony_ci	if (wrqu->frag.disabled) {
726562306a36Sopenharmony_ci		priv->frag_threshold |= FRAG_DISABLED;
726662306a36Sopenharmony_ci		priv->ieee->fts = DEFAULT_FTS;
726762306a36Sopenharmony_ci	} else {
726862306a36Sopenharmony_ci		if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
726962306a36Sopenharmony_ci		    wrqu->frag.value > MAX_FRAG_THRESHOLD)
727062306a36Sopenharmony_ci			return -EINVAL;
727162306a36Sopenharmony_ci
727262306a36Sopenharmony_ci		priv->ieee->fts = wrqu->frag.value & ~0x1;
727362306a36Sopenharmony_ci		priv->frag_threshold = priv->ieee->fts;
727462306a36Sopenharmony_ci	}
727562306a36Sopenharmony_ci
727662306a36Sopenharmony_ci	IPW_DEBUG_WX("SET Frag Threshold -> %d\n", priv->ieee->fts);
727762306a36Sopenharmony_ci
727862306a36Sopenharmony_ci	return 0;
727962306a36Sopenharmony_ci}
728062306a36Sopenharmony_ci
728162306a36Sopenharmony_cistatic int ipw2100_wx_get_frag(struct net_device *dev,
728262306a36Sopenharmony_ci			       struct iw_request_info *info,
728362306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
728462306a36Sopenharmony_ci{
728562306a36Sopenharmony_ci	/*
728662306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
728762306a36Sopenharmony_ci	 */
728862306a36Sopenharmony_ci
728962306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
729062306a36Sopenharmony_ci	wrqu->frag.value = priv->frag_threshold & ~FRAG_DISABLED;
729162306a36Sopenharmony_ci	wrqu->frag.fixed = 0;	/* no auto select */
729262306a36Sopenharmony_ci	wrqu->frag.disabled = (priv->frag_threshold & FRAG_DISABLED) ? 1 : 0;
729362306a36Sopenharmony_ci
729462306a36Sopenharmony_ci	IPW_DEBUG_WX("GET Frag Threshold -> %d\n", wrqu->frag.value);
729562306a36Sopenharmony_ci
729662306a36Sopenharmony_ci	return 0;
729762306a36Sopenharmony_ci}
729862306a36Sopenharmony_ci
729962306a36Sopenharmony_cistatic int ipw2100_wx_set_retry(struct net_device *dev,
730062306a36Sopenharmony_ci				struct iw_request_info *info,
730162306a36Sopenharmony_ci				union iwreq_data *wrqu, char *extra)
730262306a36Sopenharmony_ci{
730362306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
730462306a36Sopenharmony_ci	int err = 0;
730562306a36Sopenharmony_ci
730662306a36Sopenharmony_ci	if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
730762306a36Sopenharmony_ci		return -EINVAL;
730862306a36Sopenharmony_ci
730962306a36Sopenharmony_ci	if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
731062306a36Sopenharmony_ci		return 0;
731162306a36Sopenharmony_ci
731262306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
731362306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
731462306a36Sopenharmony_ci		err = -EIO;
731562306a36Sopenharmony_ci		goto done;
731662306a36Sopenharmony_ci	}
731762306a36Sopenharmony_ci
731862306a36Sopenharmony_ci	if (wrqu->retry.flags & IW_RETRY_SHORT) {
731962306a36Sopenharmony_ci		err = ipw2100_set_short_retry(priv, wrqu->retry.value);
732062306a36Sopenharmony_ci		IPW_DEBUG_WX("SET Short Retry Limit -> %d\n",
732162306a36Sopenharmony_ci			     wrqu->retry.value);
732262306a36Sopenharmony_ci		goto done;
732362306a36Sopenharmony_ci	}
732462306a36Sopenharmony_ci
732562306a36Sopenharmony_ci	if (wrqu->retry.flags & IW_RETRY_LONG) {
732662306a36Sopenharmony_ci		err = ipw2100_set_long_retry(priv, wrqu->retry.value);
732762306a36Sopenharmony_ci		IPW_DEBUG_WX("SET Long Retry Limit -> %d\n",
732862306a36Sopenharmony_ci			     wrqu->retry.value);
732962306a36Sopenharmony_ci		goto done;
733062306a36Sopenharmony_ci	}
733162306a36Sopenharmony_ci
733262306a36Sopenharmony_ci	err = ipw2100_set_short_retry(priv, wrqu->retry.value);
733362306a36Sopenharmony_ci	if (!err)
733462306a36Sopenharmony_ci		err = ipw2100_set_long_retry(priv, wrqu->retry.value);
733562306a36Sopenharmony_ci
733662306a36Sopenharmony_ci	IPW_DEBUG_WX("SET Both Retry Limits -> %d\n", wrqu->retry.value);
733762306a36Sopenharmony_ci
733862306a36Sopenharmony_ci      done:
733962306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
734062306a36Sopenharmony_ci	return err;
734162306a36Sopenharmony_ci}
734262306a36Sopenharmony_ci
734362306a36Sopenharmony_cistatic int ipw2100_wx_get_retry(struct net_device *dev,
734462306a36Sopenharmony_ci				struct iw_request_info *info,
734562306a36Sopenharmony_ci				union iwreq_data *wrqu, char *extra)
734662306a36Sopenharmony_ci{
734762306a36Sopenharmony_ci	/*
734862306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
734962306a36Sopenharmony_ci	 */
735062306a36Sopenharmony_ci
735162306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
735262306a36Sopenharmony_ci
735362306a36Sopenharmony_ci	wrqu->retry.disabled = 0;	/* can't be disabled */
735462306a36Sopenharmony_ci
735562306a36Sopenharmony_ci	if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
735662306a36Sopenharmony_ci		return -EINVAL;
735762306a36Sopenharmony_ci
735862306a36Sopenharmony_ci	if (wrqu->retry.flags & IW_RETRY_LONG) {
735962306a36Sopenharmony_ci		wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
736062306a36Sopenharmony_ci		wrqu->retry.value = priv->long_retry_limit;
736162306a36Sopenharmony_ci	} else {
736262306a36Sopenharmony_ci		wrqu->retry.flags =
736362306a36Sopenharmony_ci		    (priv->short_retry_limit !=
736462306a36Sopenharmony_ci		     priv->long_retry_limit) ?
736562306a36Sopenharmony_ci		    IW_RETRY_LIMIT | IW_RETRY_SHORT : IW_RETRY_LIMIT;
736662306a36Sopenharmony_ci
736762306a36Sopenharmony_ci		wrqu->retry.value = priv->short_retry_limit;
736862306a36Sopenharmony_ci	}
736962306a36Sopenharmony_ci
737062306a36Sopenharmony_ci	IPW_DEBUG_WX("GET Retry -> %d\n", wrqu->retry.value);
737162306a36Sopenharmony_ci
737262306a36Sopenharmony_ci	return 0;
737362306a36Sopenharmony_ci}
737462306a36Sopenharmony_ci
737562306a36Sopenharmony_cistatic int ipw2100_wx_set_scan(struct net_device *dev,
737662306a36Sopenharmony_ci			       struct iw_request_info *info,
737762306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
737862306a36Sopenharmony_ci{
737962306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
738062306a36Sopenharmony_ci	int err = 0;
738162306a36Sopenharmony_ci
738262306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
738362306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
738462306a36Sopenharmony_ci		err = -EIO;
738562306a36Sopenharmony_ci		goto done;
738662306a36Sopenharmony_ci	}
738762306a36Sopenharmony_ci
738862306a36Sopenharmony_ci	IPW_DEBUG_WX("Initiating scan...\n");
738962306a36Sopenharmony_ci
739062306a36Sopenharmony_ci	priv->user_requested_scan = 1;
739162306a36Sopenharmony_ci	if (ipw2100_set_scan_options(priv) || ipw2100_start_scan(priv)) {
739262306a36Sopenharmony_ci		IPW_DEBUG_WX("Start scan failed.\n");
739362306a36Sopenharmony_ci
739462306a36Sopenharmony_ci		/* TODO: Mark a scan as pending so when hardware initialized
739562306a36Sopenharmony_ci		 *       a scan starts */
739662306a36Sopenharmony_ci	}
739762306a36Sopenharmony_ci
739862306a36Sopenharmony_ci      done:
739962306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
740062306a36Sopenharmony_ci	return err;
740162306a36Sopenharmony_ci}
740262306a36Sopenharmony_ci
740362306a36Sopenharmony_cistatic int ipw2100_wx_get_scan(struct net_device *dev,
740462306a36Sopenharmony_ci			       struct iw_request_info *info,
740562306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
740662306a36Sopenharmony_ci{
740762306a36Sopenharmony_ci	/*
740862306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
740962306a36Sopenharmony_ci	 */
741062306a36Sopenharmony_ci
741162306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
741262306a36Sopenharmony_ci	return libipw_wx_get_scan(priv->ieee, info, wrqu, extra);
741362306a36Sopenharmony_ci}
741462306a36Sopenharmony_ci
741562306a36Sopenharmony_ci/*
741662306a36Sopenharmony_ci * Implementation based on code in hostap-driver v0.1.3 hostap_ioctl.c
741762306a36Sopenharmony_ci */
741862306a36Sopenharmony_cistatic int ipw2100_wx_set_encode(struct net_device *dev,
741962306a36Sopenharmony_ci				 struct iw_request_info *info,
742062306a36Sopenharmony_ci				 union iwreq_data *wrqu, char *key)
742162306a36Sopenharmony_ci{
742262306a36Sopenharmony_ci	/*
742362306a36Sopenharmony_ci	 * No check of STATUS_INITIALIZED required
742462306a36Sopenharmony_ci	 */
742562306a36Sopenharmony_ci
742662306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
742762306a36Sopenharmony_ci	return libipw_wx_set_encode(priv->ieee, info, wrqu, key);
742862306a36Sopenharmony_ci}
742962306a36Sopenharmony_ci
743062306a36Sopenharmony_cistatic int ipw2100_wx_get_encode(struct net_device *dev,
743162306a36Sopenharmony_ci				 struct iw_request_info *info,
743262306a36Sopenharmony_ci				 union iwreq_data *wrqu, char *key)
743362306a36Sopenharmony_ci{
743462306a36Sopenharmony_ci	/*
743562306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
743662306a36Sopenharmony_ci	 */
743762306a36Sopenharmony_ci
743862306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
743962306a36Sopenharmony_ci	return libipw_wx_get_encode(priv->ieee, info, wrqu, key);
744062306a36Sopenharmony_ci}
744162306a36Sopenharmony_ci
744262306a36Sopenharmony_cistatic int ipw2100_wx_set_power(struct net_device *dev,
744362306a36Sopenharmony_ci				struct iw_request_info *info,
744462306a36Sopenharmony_ci				union iwreq_data *wrqu, char *extra)
744562306a36Sopenharmony_ci{
744662306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
744762306a36Sopenharmony_ci	int err = 0;
744862306a36Sopenharmony_ci
744962306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
745062306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
745162306a36Sopenharmony_ci		err = -EIO;
745262306a36Sopenharmony_ci		goto done;
745362306a36Sopenharmony_ci	}
745462306a36Sopenharmony_ci
745562306a36Sopenharmony_ci	if (wrqu->power.disabled) {
745662306a36Sopenharmony_ci		priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
745762306a36Sopenharmony_ci		err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
745862306a36Sopenharmony_ci		IPW_DEBUG_WX("SET Power Management Mode -> off\n");
745962306a36Sopenharmony_ci		goto done;
746062306a36Sopenharmony_ci	}
746162306a36Sopenharmony_ci
746262306a36Sopenharmony_ci	switch (wrqu->power.flags & IW_POWER_MODE) {
746362306a36Sopenharmony_ci	case IW_POWER_ON:	/* If not specified */
746462306a36Sopenharmony_ci	case IW_POWER_MODE:	/* If set all mask */
746562306a36Sopenharmony_ci	case IW_POWER_ALL_R:	/* If explicitly state all */
746662306a36Sopenharmony_ci		break;
746762306a36Sopenharmony_ci	default:		/* Otherwise we don't support it */
746862306a36Sopenharmony_ci		IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
746962306a36Sopenharmony_ci			     wrqu->power.flags);
747062306a36Sopenharmony_ci		err = -EOPNOTSUPP;
747162306a36Sopenharmony_ci		goto done;
747262306a36Sopenharmony_ci	}
747362306a36Sopenharmony_ci
747462306a36Sopenharmony_ci	/* If the user hasn't specified a power management mode yet, default
747562306a36Sopenharmony_ci	 * to BATTERY */
747662306a36Sopenharmony_ci	priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
747762306a36Sopenharmony_ci	err = ipw2100_set_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
747862306a36Sopenharmony_ci
747962306a36Sopenharmony_ci	IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
748062306a36Sopenharmony_ci
748162306a36Sopenharmony_ci      done:
748262306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
748362306a36Sopenharmony_ci	return err;
748462306a36Sopenharmony_ci
748562306a36Sopenharmony_ci}
748662306a36Sopenharmony_ci
748762306a36Sopenharmony_cistatic int ipw2100_wx_get_power(struct net_device *dev,
748862306a36Sopenharmony_ci				struct iw_request_info *info,
748962306a36Sopenharmony_ci				union iwreq_data *wrqu, char *extra)
749062306a36Sopenharmony_ci{
749162306a36Sopenharmony_ci	/*
749262306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
749362306a36Sopenharmony_ci	 */
749462306a36Sopenharmony_ci
749562306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
749662306a36Sopenharmony_ci
749762306a36Sopenharmony_ci	if (!(priv->power_mode & IPW_POWER_ENABLED))
749862306a36Sopenharmony_ci		wrqu->power.disabled = 1;
749962306a36Sopenharmony_ci	else {
750062306a36Sopenharmony_ci		wrqu->power.disabled = 0;
750162306a36Sopenharmony_ci		wrqu->power.flags = 0;
750262306a36Sopenharmony_ci	}
750362306a36Sopenharmony_ci
750462306a36Sopenharmony_ci	IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
750562306a36Sopenharmony_ci
750662306a36Sopenharmony_ci	return 0;
750762306a36Sopenharmony_ci}
750862306a36Sopenharmony_ci
750962306a36Sopenharmony_ci/*
751062306a36Sopenharmony_ci * WE-18 WPA support
751162306a36Sopenharmony_ci */
751262306a36Sopenharmony_ci
751362306a36Sopenharmony_ci/* SIOCSIWGENIE */
751462306a36Sopenharmony_cistatic int ipw2100_wx_set_genie(struct net_device *dev,
751562306a36Sopenharmony_ci				struct iw_request_info *info,
751662306a36Sopenharmony_ci				union iwreq_data *wrqu, char *extra)
751762306a36Sopenharmony_ci{
751862306a36Sopenharmony_ci
751962306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
752062306a36Sopenharmony_ci	struct libipw_device *ieee = priv->ieee;
752162306a36Sopenharmony_ci	u8 *buf;
752262306a36Sopenharmony_ci
752362306a36Sopenharmony_ci	if (!ieee->wpa_enabled)
752462306a36Sopenharmony_ci		return -EOPNOTSUPP;
752562306a36Sopenharmony_ci
752662306a36Sopenharmony_ci	if (wrqu->data.length > MAX_WPA_IE_LEN ||
752762306a36Sopenharmony_ci	    (wrqu->data.length && extra == NULL))
752862306a36Sopenharmony_ci		return -EINVAL;
752962306a36Sopenharmony_ci
753062306a36Sopenharmony_ci	if (wrqu->data.length) {
753162306a36Sopenharmony_ci		buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL);
753262306a36Sopenharmony_ci		if (buf == NULL)
753362306a36Sopenharmony_ci			return -ENOMEM;
753462306a36Sopenharmony_ci
753562306a36Sopenharmony_ci		kfree(ieee->wpa_ie);
753662306a36Sopenharmony_ci		ieee->wpa_ie = buf;
753762306a36Sopenharmony_ci		ieee->wpa_ie_len = wrqu->data.length;
753862306a36Sopenharmony_ci	} else {
753962306a36Sopenharmony_ci		kfree(ieee->wpa_ie);
754062306a36Sopenharmony_ci		ieee->wpa_ie = NULL;
754162306a36Sopenharmony_ci		ieee->wpa_ie_len = 0;
754262306a36Sopenharmony_ci	}
754362306a36Sopenharmony_ci
754462306a36Sopenharmony_ci	ipw2100_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
754562306a36Sopenharmony_ci
754662306a36Sopenharmony_ci	return 0;
754762306a36Sopenharmony_ci}
754862306a36Sopenharmony_ci
754962306a36Sopenharmony_ci/* SIOCGIWGENIE */
755062306a36Sopenharmony_cistatic int ipw2100_wx_get_genie(struct net_device *dev,
755162306a36Sopenharmony_ci				struct iw_request_info *info,
755262306a36Sopenharmony_ci				union iwreq_data *wrqu, char *extra)
755362306a36Sopenharmony_ci{
755462306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
755562306a36Sopenharmony_ci	struct libipw_device *ieee = priv->ieee;
755662306a36Sopenharmony_ci
755762306a36Sopenharmony_ci	if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
755862306a36Sopenharmony_ci		wrqu->data.length = 0;
755962306a36Sopenharmony_ci		return 0;
756062306a36Sopenharmony_ci	}
756162306a36Sopenharmony_ci
756262306a36Sopenharmony_ci	if (wrqu->data.length < ieee->wpa_ie_len)
756362306a36Sopenharmony_ci		return -E2BIG;
756462306a36Sopenharmony_ci
756562306a36Sopenharmony_ci	wrqu->data.length = ieee->wpa_ie_len;
756662306a36Sopenharmony_ci	memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
756762306a36Sopenharmony_ci
756862306a36Sopenharmony_ci	return 0;
756962306a36Sopenharmony_ci}
757062306a36Sopenharmony_ci
757162306a36Sopenharmony_ci/* SIOCSIWAUTH */
757262306a36Sopenharmony_cistatic int ipw2100_wx_set_auth(struct net_device *dev,
757362306a36Sopenharmony_ci			       struct iw_request_info *info,
757462306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
757562306a36Sopenharmony_ci{
757662306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
757762306a36Sopenharmony_ci	struct libipw_device *ieee = priv->ieee;
757862306a36Sopenharmony_ci	struct iw_param *param = &wrqu->param;
757962306a36Sopenharmony_ci	struct lib80211_crypt_data *crypt;
758062306a36Sopenharmony_ci	unsigned long flags;
758162306a36Sopenharmony_ci	int ret = 0;
758262306a36Sopenharmony_ci
758362306a36Sopenharmony_ci	switch (param->flags & IW_AUTH_INDEX) {
758462306a36Sopenharmony_ci	case IW_AUTH_WPA_VERSION:
758562306a36Sopenharmony_ci	case IW_AUTH_CIPHER_PAIRWISE:
758662306a36Sopenharmony_ci	case IW_AUTH_CIPHER_GROUP:
758762306a36Sopenharmony_ci	case IW_AUTH_KEY_MGMT:
758862306a36Sopenharmony_ci		/*
758962306a36Sopenharmony_ci		 * ipw2200 does not use these parameters
759062306a36Sopenharmony_ci		 */
759162306a36Sopenharmony_ci		break;
759262306a36Sopenharmony_ci
759362306a36Sopenharmony_ci	case IW_AUTH_TKIP_COUNTERMEASURES:
759462306a36Sopenharmony_ci		crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
759562306a36Sopenharmony_ci		if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
759662306a36Sopenharmony_ci			break;
759762306a36Sopenharmony_ci
759862306a36Sopenharmony_ci		flags = crypt->ops->get_flags(crypt->priv);
759962306a36Sopenharmony_ci
760062306a36Sopenharmony_ci		if (param->value)
760162306a36Sopenharmony_ci			flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
760262306a36Sopenharmony_ci		else
760362306a36Sopenharmony_ci			flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
760462306a36Sopenharmony_ci
760562306a36Sopenharmony_ci		crypt->ops->set_flags(flags, crypt->priv);
760662306a36Sopenharmony_ci
760762306a36Sopenharmony_ci		break;
760862306a36Sopenharmony_ci
760962306a36Sopenharmony_ci	case IW_AUTH_DROP_UNENCRYPTED:{
761062306a36Sopenharmony_ci			/* HACK:
761162306a36Sopenharmony_ci			 *
761262306a36Sopenharmony_ci			 * wpa_supplicant calls set_wpa_enabled when the driver
761362306a36Sopenharmony_ci			 * is loaded and unloaded, regardless of if WPA is being
761462306a36Sopenharmony_ci			 * used.  No other calls are made which can be used to
761562306a36Sopenharmony_ci			 * determine if encryption will be used or not prior to
761662306a36Sopenharmony_ci			 * association being expected.  If encryption is not being
761762306a36Sopenharmony_ci			 * used, drop_unencrypted is set to false, else true -- we
761862306a36Sopenharmony_ci			 * can use this to determine if the CAP_PRIVACY_ON bit should
761962306a36Sopenharmony_ci			 * be set.
762062306a36Sopenharmony_ci			 */
762162306a36Sopenharmony_ci			struct libipw_security sec = {
762262306a36Sopenharmony_ci				.flags = SEC_ENABLED,
762362306a36Sopenharmony_ci				.enabled = param->value,
762462306a36Sopenharmony_ci			};
762562306a36Sopenharmony_ci			priv->ieee->drop_unencrypted = param->value;
762662306a36Sopenharmony_ci			/* We only change SEC_LEVEL for open mode. Others
762762306a36Sopenharmony_ci			 * are set by ipw_wpa_set_encryption.
762862306a36Sopenharmony_ci			 */
762962306a36Sopenharmony_ci			if (!param->value) {
763062306a36Sopenharmony_ci				sec.flags |= SEC_LEVEL;
763162306a36Sopenharmony_ci				sec.level = SEC_LEVEL_0;
763262306a36Sopenharmony_ci			} else {
763362306a36Sopenharmony_ci				sec.flags |= SEC_LEVEL;
763462306a36Sopenharmony_ci				sec.level = SEC_LEVEL_1;
763562306a36Sopenharmony_ci			}
763662306a36Sopenharmony_ci			if (priv->ieee->set_security)
763762306a36Sopenharmony_ci				priv->ieee->set_security(priv->ieee->dev, &sec);
763862306a36Sopenharmony_ci			break;
763962306a36Sopenharmony_ci		}
764062306a36Sopenharmony_ci
764162306a36Sopenharmony_ci	case IW_AUTH_80211_AUTH_ALG:
764262306a36Sopenharmony_ci		ret = ipw2100_wpa_set_auth_algs(priv, param->value);
764362306a36Sopenharmony_ci		break;
764462306a36Sopenharmony_ci
764562306a36Sopenharmony_ci	case IW_AUTH_WPA_ENABLED:
764662306a36Sopenharmony_ci		ret = ipw2100_wpa_enable(priv, param->value);
764762306a36Sopenharmony_ci		break;
764862306a36Sopenharmony_ci
764962306a36Sopenharmony_ci	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
765062306a36Sopenharmony_ci		ieee->ieee802_1x = param->value;
765162306a36Sopenharmony_ci		break;
765262306a36Sopenharmony_ci
765362306a36Sopenharmony_ci		//case IW_AUTH_ROAMING_CONTROL:
765462306a36Sopenharmony_ci	case IW_AUTH_PRIVACY_INVOKED:
765562306a36Sopenharmony_ci		ieee->privacy_invoked = param->value;
765662306a36Sopenharmony_ci		break;
765762306a36Sopenharmony_ci
765862306a36Sopenharmony_ci	default:
765962306a36Sopenharmony_ci		return -EOPNOTSUPP;
766062306a36Sopenharmony_ci	}
766162306a36Sopenharmony_ci	return ret;
766262306a36Sopenharmony_ci}
766362306a36Sopenharmony_ci
766462306a36Sopenharmony_ci/* SIOCGIWAUTH */
766562306a36Sopenharmony_cistatic int ipw2100_wx_get_auth(struct net_device *dev,
766662306a36Sopenharmony_ci			       struct iw_request_info *info,
766762306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
766862306a36Sopenharmony_ci{
766962306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
767062306a36Sopenharmony_ci	struct libipw_device *ieee = priv->ieee;
767162306a36Sopenharmony_ci	struct lib80211_crypt_data *crypt;
767262306a36Sopenharmony_ci	struct iw_param *param = &wrqu->param;
767362306a36Sopenharmony_ci
767462306a36Sopenharmony_ci	switch (param->flags & IW_AUTH_INDEX) {
767562306a36Sopenharmony_ci	case IW_AUTH_WPA_VERSION:
767662306a36Sopenharmony_ci	case IW_AUTH_CIPHER_PAIRWISE:
767762306a36Sopenharmony_ci	case IW_AUTH_CIPHER_GROUP:
767862306a36Sopenharmony_ci	case IW_AUTH_KEY_MGMT:
767962306a36Sopenharmony_ci		/*
768062306a36Sopenharmony_ci		 * wpa_supplicant will control these internally
768162306a36Sopenharmony_ci		 */
768262306a36Sopenharmony_ci		break;
768362306a36Sopenharmony_ci
768462306a36Sopenharmony_ci	case IW_AUTH_TKIP_COUNTERMEASURES:
768562306a36Sopenharmony_ci		crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
768662306a36Sopenharmony_ci		if (!crypt || !crypt->ops->get_flags) {
768762306a36Sopenharmony_ci			IPW_DEBUG_WARNING("Can't get TKIP countermeasures: "
768862306a36Sopenharmony_ci					  "crypt not set!\n");
768962306a36Sopenharmony_ci			break;
769062306a36Sopenharmony_ci		}
769162306a36Sopenharmony_ci
769262306a36Sopenharmony_ci		param->value = (crypt->ops->get_flags(crypt->priv) &
769362306a36Sopenharmony_ci				IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
769462306a36Sopenharmony_ci
769562306a36Sopenharmony_ci		break;
769662306a36Sopenharmony_ci
769762306a36Sopenharmony_ci	case IW_AUTH_DROP_UNENCRYPTED:
769862306a36Sopenharmony_ci		param->value = ieee->drop_unencrypted;
769962306a36Sopenharmony_ci		break;
770062306a36Sopenharmony_ci
770162306a36Sopenharmony_ci	case IW_AUTH_80211_AUTH_ALG:
770262306a36Sopenharmony_ci		param->value = priv->ieee->sec.auth_mode;
770362306a36Sopenharmony_ci		break;
770462306a36Sopenharmony_ci
770562306a36Sopenharmony_ci	case IW_AUTH_WPA_ENABLED:
770662306a36Sopenharmony_ci		param->value = ieee->wpa_enabled;
770762306a36Sopenharmony_ci		break;
770862306a36Sopenharmony_ci
770962306a36Sopenharmony_ci	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
771062306a36Sopenharmony_ci		param->value = ieee->ieee802_1x;
771162306a36Sopenharmony_ci		break;
771262306a36Sopenharmony_ci
771362306a36Sopenharmony_ci	case IW_AUTH_ROAMING_CONTROL:
771462306a36Sopenharmony_ci	case IW_AUTH_PRIVACY_INVOKED:
771562306a36Sopenharmony_ci		param->value = ieee->privacy_invoked;
771662306a36Sopenharmony_ci		break;
771762306a36Sopenharmony_ci
771862306a36Sopenharmony_ci	default:
771962306a36Sopenharmony_ci		return -EOPNOTSUPP;
772062306a36Sopenharmony_ci	}
772162306a36Sopenharmony_ci	return 0;
772262306a36Sopenharmony_ci}
772362306a36Sopenharmony_ci
772462306a36Sopenharmony_ci/* SIOCSIWENCODEEXT */
772562306a36Sopenharmony_cistatic int ipw2100_wx_set_encodeext(struct net_device *dev,
772662306a36Sopenharmony_ci				    struct iw_request_info *info,
772762306a36Sopenharmony_ci				    union iwreq_data *wrqu, char *extra)
772862306a36Sopenharmony_ci{
772962306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
773062306a36Sopenharmony_ci	return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra);
773162306a36Sopenharmony_ci}
773262306a36Sopenharmony_ci
773362306a36Sopenharmony_ci/* SIOCGIWENCODEEXT */
773462306a36Sopenharmony_cistatic int ipw2100_wx_get_encodeext(struct net_device *dev,
773562306a36Sopenharmony_ci				    struct iw_request_info *info,
773662306a36Sopenharmony_ci				    union iwreq_data *wrqu, char *extra)
773762306a36Sopenharmony_ci{
773862306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
773962306a36Sopenharmony_ci	return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra);
774062306a36Sopenharmony_ci}
774162306a36Sopenharmony_ci
774262306a36Sopenharmony_ci/* SIOCSIWMLME */
774362306a36Sopenharmony_cistatic int ipw2100_wx_set_mlme(struct net_device *dev,
774462306a36Sopenharmony_ci			       struct iw_request_info *info,
774562306a36Sopenharmony_ci			       union iwreq_data *wrqu, char *extra)
774662306a36Sopenharmony_ci{
774762306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
774862306a36Sopenharmony_ci	struct iw_mlme *mlme = (struct iw_mlme *)extra;
774962306a36Sopenharmony_ci
775062306a36Sopenharmony_ci	switch (mlme->cmd) {
775162306a36Sopenharmony_ci	case IW_MLME_DEAUTH:
775262306a36Sopenharmony_ci		// silently ignore
775362306a36Sopenharmony_ci		break;
775462306a36Sopenharmony_ci
775562306a36Sopenharmony_ci	case IW_MLME_DISASSOC:
775662306a36Sopenharmony_ci		ipw2100_disassociate_bssid(priv);
775762306a36Sopenharmony_ci		break;
775862306a36Sopenharmony_ci
775962306a36Sopenharmony_ci	default:
776062306a36Sopenharmony_ci		return -EOPNOTSUPP;
776162306a36Sopenharmony_ci	}
776262306a36Sopenharmony_ci	return 0;
776362306a36Sopenharmony_ci}
776462306a36Sopenharmony_ci
776562306a36Sopenharmony_ci/*
776662306a36Sopenharmony_ci *
776762306a36Sopenharmony_ci * IWPRIV handlers
776862306a36Sopenharmony_ci *
776962306a36Sopenharmony_ci */
777062306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
777162306a36Sopenharmony_cistatic int ipw2100_wx_set_promisc(struct net_device *dev,
777262306a36Sopenharmony_ci				  struct iw_request_info *info,
777362306a36Sopenharmony_ci				  union iwreq_data *wrqu, char *extra)
777462306a36Sopenharmony_ci{
777562306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
777662306a36Sopenharmony_ci	int *parms = (int *)extra;
777762306a36Sopenharmony_ci	int enable = (parms[0] > 0);
777862306a36Sopenharmony_ci	int err = 0;
777962306a36Sopenharmony_ci
778062306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
778162306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
778262306a36Sopenharmony_ci		err = -EIO;
778362306a36Sopenharmony_ci		goto done;
778462306a36Sopenharmony_ci	}
778562306a36Sopenharmony_ci
778662306a36Sopenharmony_ci	if (enable) {
778762306a36Sopenharmony_ci		if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
778862306a36Sopenharmony_ci			err = ipw2100_set_channel(priv, parms[1], 0);
778962306a36Sopenharmony_ci			goto done;
779062306a36Sopenharmony_ci		}
779162306a36Sopenharmony_ci		priv->channel = parms[1];
779262306a36Sopenharmony_ci		err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
779362306a36Sopenharmony_ci	} else {
779462306a36Sopenharmony_ci		if (priv->ieee->iw_mode == IW_MODE_MONITOR)
779562306a36Sopenharmony_ci			err = ipw2100_switch_mode(priv, priv->last_mode);
779662306a36Sopenharmony_ci	}
779762306a36Sopenharmony_ci      done:
779862306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
779962306a36Sopenharmony_ci	return err;
780062306a36Sopenharmony_ci}
780162306a36Sopenharmony_ci
780262306a36Sopenharmony_cistatic int ipw2100_wx_reset(struct net_device *dev,
780362306a36Sopenharmony_ci			    struct iw_request_info *info,
780462306a36Sopenharmony_ci			    union iwreq_data *wrqu, char *extra)
780562306a36Sopenharmony_ci{
780662306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
780762306a36Sopenharmony_ci	if (priv->status & STATUS_INITIALIZED)
780862306a36Sopenharmony_ci		schedule_reset(priv);
780962306a36Sopenharmony_ci	return 0;
781062306a36Sopenharmony_ci}
781162306a36Sopenharmony_ci
781262306a36Sopenharmony_ci#endif
781362306a36Sopenharmony_ci
781462306a36Sopenharmony_cistatic int ipw2100_wx_set_powermode(struct net_device *dev,
781562306a36Sopenharmony_ci				    struct iw_request_info *info,
781662306a36Sopenharmony_ci				    union iwreq_data *wrqu, char *extra)
781762306a36Sopenharmony_ci{
781862306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
781962306a36Sopenharmony_ci	int err = 0, mode = *(int *)extra;
782062306a36Sopenharmony_ci
782162306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
782262306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
782362306a36Sopenharmony_ci		err = -EIO;
782462306a36Sopenharmony_ci		goto done;
782562306a36Sopenharmony_ci	}
782662306a36Sopenharmony_ci
782762306a36Sopenharmony_ci	if ((mode < 0) || (mode > POWER_MODES))
782862306a36Sopenharmony_ci		mode = IPW_POWER_AUTO;
782962306a36Sopenharmony_ci
783062306a36Sopenharmony_ci	if (IPW_POWER_LEVEL(priv->power_mode) != mode)
783162306a36Sopenharmony_ci		err = ipw2100_set_power_mode(priv, mode);
783262306a36Sopenharmony_ci      done:
783362306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
783462306a36Sopenharmony_ci	return err;
783562306a36Sopenharmony_ci}
783662306a36Sopenharmony_ci
783762306a36Sopenharmony_ci#define MAX_POWER_STRING 80
783862306a36Sopenharmony_cistatic int ipw2100_wx_get_powermode(struct net_device *dev,
783962306a36Sopenharmony_ci				    struct iw_request_info *info,
784062306a36Sopenharmony_ci				    union iwreq_data *wrqu, char *extra)
784162306a36Sopenharmony_ci{
784262306a36Sopenharmony_ci	/*
784362306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
784462306a36Sopenharmony_ci	 */
784562306a36Sopenharmony_ci
784662306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
784762306a36Sopenharmony_ci	int level = IPW_POWER_LEVEL(priv->power_mode);
784862306a36Sopenharmony_ci	s32 timeout, period;
784962306a36Sopenharmony_ci
785062306a36Sopenharmony_ci	if (!(priv->power_mode & IPW_POWER_ENABLED)) {
785162306a36Sopenharmony_ci		snprintf(extra, MAX_POWER_STRING,
785262306a36Sopenharmony_ci			 "Power save level: %d (Off)", level);
785362306a36Sopenharmony_ci	} else {
785462306a36Sopenharmony_ci		switch (level) {
785562306a36Sopenharmony_ci		case IPW_POWER_MODE_CAM:
785662306a36Sopenharmony_ci			snprintf(extra, MAX_POWER_STRING,
785762306a36Sopenharmony_ci				 "Power save level: %d (None)", level);
785862306a36Sopenharmony_ci			break;
785962306a36Sopenharmony_ci		case IPW_POWER_AUTO:
786062306a36Sopenharmony_ci			snprintf(extra, MAX_POWER_STRING,
786162306a36Sopenharmony_ci				 "Power save level: %d (Auto)", level);
786262306a36Sopenharmony_ci			break;
786362306a36Sopenharmony_ci		default:
786462306a36Sopenharmony_ci			timeout = timeout_duration[level - 1] / 1000;
786562306a36Sopenharmony_ci			period = period_duration[level - 1] / 1000;
786662306a36Sopenharmony_ci			snprintf(extra, MAX_POWER_STRING,
786762306a36Sopenharmony_ci				 "Power save level: %d "
786862306a36Sopenharmony_ci				 "(Timeout %dms, Period %dms)",
786962306a36Sopenharmony_ci				 level, timeout, period);
787062306a36Sopenharmony_ci		}
787162306a36Sopenharmony_ci	}
787262306a36Sopenharmony_ci
787362306a36Sopenharmony_ci	wrqu->data.length = strlen(extra) + 1;
787462306a36Sopenharmony_ci
787562306a36Sopenharmony_ci	return 0;
787662306a36Sopenharmony_ci}
787762306a36Sopenharmony_ci
787862306a36Sopenharmony_cistatic int ipw2100_wx_set_preamble(struct net_device *dev,
787962306a36Sopenharmony_ci				   struct iw_request_info *info,
788062306a36Sopenharmony_ci				   union iwreq_data *wrqu, char *extra)
788162306a36Sopenharmony_ci{
788262306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
788362306a36Sopenharmony_ci	int err, mode = *(int *)extra;
788462306a36Sopenharmony_ci
788562306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
788662306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
788762306a36Sopenharmony_ci		err = -EIO;
788862306a36Sopenharmony_ci		goto done;
788962306a36Sopenharmony_ci	}
789062306a36Sopenharmony_ci
789162306a36Sopenharmony_ci	if (mode == 1)
789262306a36Sopenharmony_ci		priv->config |= CFG_LONG_PREAMBLE;
789362306a36Sopenharmony_ci	else if (mode == 0)
789462306a36Sopenharmony_ci		priv->config &= ~CFG_LONG_PREAMBLE;
789562306a36Sopenharmony_ci	else {
789662306a36Sopenharmony_ci		err = -EINVAL;
789762306a36Sopenharmony_ci		goto done;
789862306a36Sopenharmony_ci	}
789962306a36Sopenharmony_ci
790062306a36Sopenharmony_ci	err = ipw2100_system_config(priv, 0);
790162306a36Sopenharmony_ci
790262306a36Sopenharmony_ci      done:
790362306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
790462306a36Sopenharmony_ci	return err;
790562306a36Sopenharmony_ci}
790662306a36Sopenharmony_ci
790762306a36Sopenharmony_cistatic int ipw2100_wx_get_preamble(struct net_device *dev,
790862306a36Sopenharmony_ci				   struct iw_request_info *info,
790962306a36Sopenharmony_ci				   union iwreq_data *wrqu, char *extra)
791062306a36Sopenharmony_ci{
791162306a36Sopenharmony_ci	/*
791262306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
791362306a36Sopenharmony_ci	 */
791462306a36Sopenharmony_ci
791562306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
791662306a36Sopenharmony_ci
791762306a36Sopenharmony_ci	if (priv->config & CFG_LONG_PREAMBLE)
791862306a36Sopenharmony_ci		snprintf(wrqu->name, IFNAMSIZ, "long (1)");
791962306a36Sopenharmony_ci	else
792062306a36Sopenharmony_ci		snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
792162306a36Sopenharmony_ci
792262306a36Sopenharmony_ci	return 0;
792362306a36Sopenharmony_ci}
792462306a36Sopenharmony_ci
792562306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
792662306a36Sopenharmony_cistatic int ipw2100_wx_set_crc_check(struct net_device *dev,
792762306a36Sopenharmony_ci				    struct iw_request_info *info,
792862306a36Sopenharmony_ci				    union iwreq_data *wrqu, char *extra)
792962306a36Sopenharmony_ci{
793062306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
793162306a36Sopenharmony_ci	int err, mode = *(int *)extra;
793262306a36Sopenharmony_ci
793362306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
793462306a36Sopenharmony_ci	if (!(priv->status & STATUS_INITIALIZED)) {
793562306a36Sopenharmony_ci		err = -EIO;
793662306a36Sopenharmony_ci		goto done;
793762306a36Sopenharmony_ci	}
793862306a36Sopenharmony_ci
793962306a36Sopenharmony_ci	if (mode == 1)
794062306a36Sopenharmony_ci		priv->config |= CFG_CRC_CHECK;
794162306a36Sopenharmony_ci	else if (mode == 0)
794262306a36Sopenharmony_ci		priv->config &= ~CFG_CRC_CHECK;
794362306a36Sopenharmony_ci	else {
794462306a36Sopenharmony_ci		err = -EINVAL;
794562306a36Sopenharmony_ci		goto done;
794662306a36Sopenharmony_ci	}
794762306a36Sopenharmony_ci	err = 0;
794862306a36Sopenharmony_ci
794962306a36Sopenharmony_ci      done:
795062306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
795162306a36Sopenharmony_ci	return err;
795262306a36Sopenharmony_ci}
795362306a36Sopenharmony_ci
795462306a36Sopenharmony_cistatic int ipw2100_wx_get_crc_check(struct net_device *dev,
795562306a36Sopenharmony_ci				    struct iw_request_info *info,
795662306a36Sopenharmony_ci				    union iwreq_data *wrqu, char *extra)
795762306a36Sopenharmony_ci{
795862306a36Sopenharmony_ci	/*
795962306a36Sopenharmony_ci	 * This can be called at any time.  No action lock required
796062306a36Sopenharmony_ci	 */
796162306a36Sopenharmony_ci
796262306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
796362306a36Sopenharmony_ci
796462306a36Sopenharmony_ci	if (priv->config & CFG_CRC_CHECK)
796562306a36Sopenharmony_ci		snprintf(wrqu->name, IFNAMSIZ, "CRC checked (1)");
796662306a36Sopenharmony_ci	else
796762306a36Sopenharmony_ci		snprintf(wrqu->name, IFNAMSIZ, "CRC ignored (0)");
796862306a36Sopenharmony_ci
796962306a36Sopenharmony_ci	return 0;
797062306a36Sopenharmony_ci}
797162306a36Sopenharmony_ci#endif				/* CONFIG_IPW2100_MONITOR */
797262306a36Sopenharmony_ci
797362306a36Sopenharmony_cistatic iw_handler ipw2100_wx_handlers[] = {
797462306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWNAME, ipw2100_wx_get_name),
797562306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWFREQ, ipw2100_wx_set_freq),
797662306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWFREQ, ipw2100_wx_get_freq),
797762306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWMODE, ipw2100_wx_set_mode),
797862306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWMODE, ipw2100_wx_get_mode),
797962306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWRANGE, ipw2100_wx_get_range),
798062306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWAP, ipw2100_wx_set_wap),
798162306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWAP, ipw2100_wx_get_wap),
798262306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWMLME, ipw2100_wx_set_mlme),
798362306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWSCAN, ipw2100_wx_set_scan),
798462306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWSCAN, ipw2100_wx_get_scan),
798562306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWESSID, ipw2100_wx_set_essid),
798662306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWESSID, ipw2100_wx_get_essid),
798762306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWNICKN, ipw2100_wx_set_nick),
798862306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWNICKN, ipw2100_wx_get_nick),
798962306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWRATE, ipw2100_wx_set_rate),
799062306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWRATE, ipw2100_wx_get_rate),
799162306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWRTS, ipw2100_wx_set_rts),
799262306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWRTS, ipw2100_wx_get_rts),
799362306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWFRAG, ipw2100_wx_set_frag),
799462306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWFRAG, ipw2100_wx_get_frag),
799562306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWTXPOW, ipw2100_wx_set_txpow),
799662306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWTXPOW, ipw2100_wx_get_txpow),
799762306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWRETRY, ipw2100_wx_set_retry),
799862306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWRETRY, ipw2100_wx_get_retry),
799962306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWENCODE, ipw2100_wx_set_encode),
800062306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWENCODE, ipw2100_wx_get_encode),
800162306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWPOWER, ipw2100_wx_set_power),
800262306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWPOWER, ipw2100_wx_get_power),
800362306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWGENIE, ipw2100_wx_set_genie),
800462306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWGENIE, ipw2100_wx_get_genie),
800562306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWAUTH, ipw2100_wx_set_auth),
800662306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWAUTH, ipw2100_wx_get_auth),
800762306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWENCODEEXT, ipw2100_wx_set_encodeext),
800862306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWENCODEEXT, ipw2100_wx_get_encodeext),
800962306a36Sopenharmony_ci};
801062306a36Sopenharmony_ci
801162306a36Sopenharmony_ci#define IPW2100_PRIV_SET_MONITOR	SIOCIWFIRSTPRIV
801262306a36Sopenharmony_ci#define IPW2100_PRIV_RESET		SIOCIWFIRSTPRIV+1
801362306a36Sopenharmony_ci#define IPW2100_PRIV_SET_POWER		SIOCIWFIRSTPRIV+2
801462306a36Sopenharmony_ci#define IPW2100_PRIV_GET_POWER		SIOCIWFIRSTPRIV+3
801562306a36Sopenharmony_ci#define IPW2100_PRIV_SET_LONGPREAMBLE	SIOCIWFIRSTPRIV+4
801662306a36Sopenharmony_ci#define IPW2100_PRIV_GET_LONGPREAMBLE	SIOCIWFIRSTPRIV+5
801762306a36Sopenharmony_ci#define IPW2100_PRIV_SET_CRC_CHECK	SIOCIWFIRSTPRIV+6
801862306a36Sopenharmony_ci#define IPW2100_PRIV_GET_CRC_CHECK	SIOCIWFIRSTPRIV+7
801962306a36Sopenharmony_ci
802062306a36Sopenharmony_cistatic const struct iw_priv_args ipw2100_private_args[] = {
802162306a36Sopenharmony_ci
802262306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
802362306a36Sopenharmony_ci	{
802462306a36Sopenharmony_ci	 IPW2100_PRIV_SET_MONITOR,
802562306a36Sopenharmony_ci	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
802662306a36Sopenharmony_ci	{
802762306a36Sopenharmony_ci	 IPW2100_PRIV_RESET,
802862306a36Sopenharmony_ci	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
802962306a36Sopenharmony_ci#endif				/* CONFIG_IPW2100_MONITOR */
803062306a36Sopenharmony_ci
803162306a36Sopenharmony_ci	{
803262306a36Sopenharmony_ci	 IPW2100_PRIV_SET_POWER,
803362306a36Sopenharmony_ci	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_power"},
803462306a36Sopenharmony_ci	{
803562306a36Sopenharmony_ci	 IPW2100_PRIV_GET_POWER,
803662306a36Sopenharmony_ci	 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_POWER_STRING,
803762306a36Sopenharmony_ci	 "get_power"},
803862306a36Sopenharmony_ci	{
803962306a36Sopenharmony_ci	 IPW2100_PRIV_SET_LONGPREAMBLE,
804062306a36Sopenharmony_ci	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"},
804162306a36Sopenharmony_ci	{
804262306a36Sopenharmony_ci	 IPW2100_PRIV_GET_LONGPREAMBLE,
804362306a36Sopenharmony_ci	 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_preamble"},
804462306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
804562306a36Sopenharmony_ci	{
804662306a36Sopenharmony_ci	 IPW2100_PRIV_SET_CRC_CHECK,
804762306a36Sopenharmony_ci	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_crc_check"},
804862306a36Sopenharmony_ci	{
804962306a36Sopenharmony_ci	 IPW2100_PRIV_GET_CRC_CHECK,
805062306a36Sopenharmony_ci	 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_crc_check"},
805162306a36Sopenharmony_ci#endif				/* CONFIG_IPW2100_MONITOR */
805262306a36Sopenharmony_ci};
805362306a36Sopenharmony_ci
805462306a36Sopenharmony_cistatic iw_handler ipw2100_private_handler[] = {
805562306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
805662306a36Sopenharmony_ci	ipw2100_wx_set_promisc,
805762306a36Sopenharmony_ci	ipw2100_wx_reset,
805862306a36Sopenharmony_ci#else				/* CONFIG_IPW2100_MONITOR */
805962306a36Sopenharmony_ci	NULL,
806062306a36Sopenharmony_ci	NULL,
806162306a36Sopenharmony_ci#endif				/* CONFIG_IPW2100_MONITOR */
806262306a36Sopenharmony_ci	ipw2100_wx_set_powermode,
806362306a36Sopenharmony_ci	ipw2100_wx_get_powermode,
806462306a36Sopenharmony_ci	ipw2100_wx_set_preamble,
806562306a36Sopenharmony_ci	ipw2100_wx_get_preamble,
806662306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
806762306a36Sopenharmony_ci	ipw2100_wx_set_crc_check,
806862306a36Sopenharmony_ci	ipw2100_wx_get_crc_check,
806962306a36Sopenharmony_ci#else				/* CONFIG_IPW2100_MONITOR */
807062306a36Sopenharmony_ci	NULL,
807162306a36Sopenharmony_ci	NULL,
807262306a36Sopenharmony_ci#endif				/* CONFIG_IPW2100_MONITOR */
807362306a36Sopenharmony_ci};
807462306a36Sopenharmony_ci
807562306a36Sopenharmony_ci/*
807662306a36Sopenharmony_ci * Get wireless statistics.
807762306a36Sopenharmony_ci * Called by /proc/net/wireless
807862306a36Sopenharmony_ci * Also called by SIOCGIWSTATS
807962306a36Sopenharmony_ci */
808062306a36Sopenharmony_cistatic struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev)
808162306a36Sopenharmony_ci{
808262306a36Sopenharmony_ci	enum {
808362306a36Sopenharmony_ci		POOR = 30,
808462306a36Sopenharmony_ci		FAIR = 60,
808562306a36Sopenharmony_ci		GOOD = 80,
808662306a36Sopenharmony_ci		VERY_GOOD = 90,
808762306a36Sopenharmony_ci		EXCELLENT = 95,
808862306a36Sopenharmony_ci		PERFECT = 100
808962306a36Sopenharmony_ci	};
809062306a36Sopenharmony_ci	int rssi_qual;
809162306a36Sopenharmony_ci	int tx_qual;
809262306a36Sopenharmony_ci	int beacon_qual;
809362306a36Sopenharmony_ci	int quality;
809462306a36Sopenharmony_ci
809562306a36Sopenharmony_ci	struct ipw2100_priv *priv = libipw_priv(dev);
809662306a36Sopenharmony_ci	struct iw_statistics *wstats;
809762306a36Sopenharmony_ci	u32 rssi, tx_retries, missed_beacons, tx_failures;
809862306a36Sopenharmony_ci	u32 ord_len = sizeof(u32);
809962306a36Sopenharmony_ci
810062306a36Sopenharmony_ci	if (!priv)
810162306a36Sopenharmony_ci		return (struct iw_statistics *)NULL;
810262306a36Sopenharmony_ci
810362306a36Sopenharmony_ci	wstats = &priv->wstats;
810462306a36Sopenharmony_ci
810562306a36Sopenharmony_ci	/* if hw is disabled, then ipw2100_get_ordinal() can't be called.
810662306a36Sopenharmony_ci	 * ipw2100_wx_wireless_stats seems to be called before fw is
810762306a36Sopenharmony_ci	 * initialized.  STATUS_ASSOCIATED will only be set if the hw is up
810862306a36Sopenharmony_ci	 * and associated; if not associcated, the values are all meaningless
810962306a36Sopenharmony_ci	 * anyway, so set them all to NULL and INVALID */
811062306a36Sopenharmony_ci	if (!(priv->status & STATUS_ASSOCIATED)) {
811162306a36Sopenharmony_ci		wstats->miss.beacon = 0;
811262306a36Sopenharmony_ci		wstats->discard.retries = 0;
811362306a36Sopenharmony_ci		wstats->qual.qual = 0;
811462306a36Sopenharmony_ci		wstats->qual.level = 0;
811562306a36Sopenharmony_ci		wstats->qual.noise = 0;
811662306a36Sopenharmony_ci		wstats->qual.updated = 7;
811762306a36Sopenharmony_ci		wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
811862306a36Sopenharmony_ci		    IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
811962306a36Sopenharmony_ci		return wstats;
812062306a36Sopenharmony_ci	}
812162306a36Sopenharmony_ci
812262306a36Sopenharmony_ci	if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_MISSED_BCNS,
812362306a36Sopenharmony_ci				&missed_beacons, &ord_len))
812462306a36Sopenharmony_ci		goto fail_get_ordinal;
812562306a36Sopenharmony_ci
812662306a36Sopenharmony_ci	/* If we don't have a connection the quality and level is 0 */
812762306a36Sopenharmony_ci	if (!(priv->status & STATUS_ASSOCIATED)) {
812862306a36Sopenharmony_ci		wstats->qual.qual = 0;
812962306a36Sopenharmony_ci		wstats->qual.level = 0;
813062306a36Sopenharmony_ci	} else {
813162306a36Sopenharmony_ci		if (ipw2100_get_ordinal(priv, IPW_ORD_RSSI_AVG_CURR,
813262306a36Sopenharmony_ci					&rssi, &ord_len))
813362306a36Sopenharmony_ci			goto fail_get_ordinal;
813462306a36Sopenharmony_ci		wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
813562306a36Sopenharmony_ci		if (rssi < 10)
813662306a36Sopenharmony_ci			rssi_qual = rssi * POOR / 10;
813762306a36Sopenharmony_ci		else if (rssi < 15)
813862306a36Sopenharmony_ci			rssi_qual = (rssi - 10) * (FAIR - POOR) / 5 + POOR;
813962306a36Sopenharmony_ci		else if (rssi < 20)
814062306a36Sopenharmony_ci			rssi_qual = (rssi - 15) * (GOOD - FAIR) / 5 + FAIR;
814162306a36Sopenharmony_ci		else if (rssi < 30)
814262306a36Sopenharmony_ci			rssi_qual = (rssi - 20) * (VERY_GOOD - GOOD) /
814362306a36Sopenharmony_ci			    10 + GOOD;
814462306a36Sopenharmony_ci		else
814562306a36Sopenharmony_ci			rssi_qual = (rssi - 30) * (PERFECT - VERY_GOOD) /
814662306a36Sopenharmony_ci			    10 + VERY_GOOD;
814762306a36Sopenharmony_ci
814862306a36Sopenharmony_ci		if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_RETRIES,
814962306a36Sopenharmony_ci					&tx_retries, &ord_len))
815062306a36Sopenharmony_ci			goto fail_get_ordinal;
815162306a36Sopenharmony_ci
815262306a36Sopenharmony_ci		if (tx_retries > 75)
815362306a36Sopenharmony_ci			tx_qual = (90 - tx_retries) * POOR / 15;
815462306a36Sopenharmony_ci		else if (tx_retries > 70)
815562306a36Sopenharmony_ci			tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
815662306a36Sopenharmony_ci		else if (tx_retries > 65)
815762306a36Sopenharmony_ci			tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
815862306a36Sopenharmony_ci		else if (tx_retries > 50)
815962306a36Sopenharmony_ci			tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
816062306a36Sopenharmony_ci			    15 + GOOD;
816162306a36Sopenharmony_ci		else
816262306a36Sopenharmony_ci			tx_qual = (50 - tx_retries) *
816362306a36Sopenharmony_ci			    (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
816462306a36Sopenharmony_ci
816562306a36Sopenharmony_ci		if (missed_beacons > 50)
816662306a36Sopenharmony_ci			beacon_qual = (60 - missed_beacons) * POOR / 10;
816762306a36Sopenharmony_ci		else if (missed_beacons > 40)
816862306a36Sopenharmony_ci			beacon_qual = (50 - missed_beacons) * (FAIR - POOR) /
816962306a36Sopenharmony_ci			    10 + POOR;
817062306a36Sopenharmony_ci		else if (missed_beacons > 32)
817162306a36Sopenharmony_ci			beacon_qual = (40 - missed_beacons) * (GOOD - FAIR) /
817262306a36Sopenharmony_ci			    18 + FAIR;
817362306a36Sopenharmony_ci		else if (missed_beacons > 20)
817462306a36Sopenharmony_ci			beacon_qual = (32 - missed_beacons) *
817562306a36Sopenharmony_ci			    (VERY_GOOD - GOOD) / 20 + GOOD;
817662306a36Sopenharmony_ci		else
817762306a36Sopenharmony_ci			beacon_qual = (20 - missed_beacons) *
817862306a36Sopenharmony_ci			    (PERFECT - VERY_GOOD) / 20 + VERY_GOOD;
817962306a36Sopenharmony_ci
818062306a36Sopenharmony_ci		quality = min(tx_qual, rssi_qual);
818162306a36Sopenharmony_ci		quality = min(beacon_qual, quality);
818262306a36Sopenharmony_ci
818362306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_DEBUG
818462306a36Sopenharmony_ci		if (beacon_qual == quality)
818562306a36Sopenharmony_ci			IPW_DEBUG_WX("Quality clamped by Missed Beacons\n");
818662306a36Sopenharmony_ci		else if (tx_qual == quality)
818762306a36Sopenharmony_ci			IPW_DEBUG_WX("Quality clamped by Tx Retries\n");
818862306a36Sopenharmony_ci		else if (quality != 100)
818962306a36Sopenharmony_ci			IPW_DEBUG_WX("Quality clamped by Signal Strength\n");
819062306a36Sopenharmony_ci		else
819162306a36Sopenharmony_ci			IPW_DEBUG_WX("Quality not clamped.\n");
819262306a36Sopenharmony_ci#endif
819362306a36Sopenharmony_ci
819462306a36Sopenharmony_ci		wstats->qual.qual = quality;
819562306a36Sopenharmony_ci		wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
819662306a36Sopenharmony_ci	}
819762306a36Sopenharmony_ci
819862306a36Sopenharmony_ci	wstats->qual.noise = 0;
819962306a36Sopenharmony_ci	wstats->qual.updated = 7;
820062306a36Sopenharmony_ci	wstats->qual.updated |= IW_QUAL_NOISE_INVALID;
820162306a36Sopenharmony_ci
820262306a36Sopenharmony_ci	/* FIXME: this is percent and not a # */
820362306a36Sopenharmony_ci	wstats->miss.beacon = missed_beacons;
820462306a36Sopenharmony_ci
820562306a36Sopenharmony_ci	if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURES,
820662306a36Sopenharmony_ci				&tx_failures, &ord_len))
820762306a36Sopenharmony_ci		goto fail_get_ordinal;
820862306a36Sopenharmony_ci	wstats->discard.retries = tx_failures;
820962306a36Sopenharmony_ci
821062306a36Sopenharmony_ci	return wstats;
821162306a36Sopenharmony_ci
821262306a36Sopenharmony_ci      fail_get_ordinal:
821362306a36Sopenharmony_ci	IPW_DEBUG_WX("failed querying ordinals.\n");
821462306a36Sopenharmony_ci
821562306a36Sopenharmony_ci	return (struct iw_statistics *)NULL;
821662306a36Sopenharmony_ci}
821762306a36Sopenharmony_ci
821862306a36Sopenharmony_cistatic const struct iw_handler_def ipw2100_wx_handler_def = {
821962306a36Sopenharmony_ci	.standard = ipw2100_wx_handlers,
822062306a36Sopenharmony_ci	.num_standard = ARRAY_SIZE(ipw2100_wx_handlers),
822162306a36Sopenharmony_ci	.num_private = ARRAY_SIZE(ipw2100_private_handler),
822262306a36Sopenharmony_ci	.num_private_args = ARRAY_SIZE(ipw2100_private_args),
822362306a36Sopenharmony_ci	.private = (iw_handler *) ipw2100_private_handler,
822462306a36Sopenharmony_ci	.private_args = (struct iw_priv_args *)ipw2100_private_args,
822562306a36Sopenharmony_ci	.get_wireless_stats = ipw2100_wx_wireless_stats,
822662306a36Sopenharmony_ci};
822762306a36Sopenharmony_ci
822862306a36Sopenharmony_cistatic void ipw2100_wx_event_work(struct work_struct *work)
822962306a36Sopenharmony_ci{
823062306a36Sopenharmony_ci	struct ipw2100_priv *priv =
823162306a36Sopenharmony_ci		container_of(work, struct ipw2100_priv, wx_event_work.work);
823262306a36Sopenharmony_ci	union iwreq_data wrqu;
823362306a36Sopenharmony_ci	unsigned int len = ETH_ALEN;
823462306a36Sopenharmony_ci
823562306a36Sopenharmony_ci	if (priv->status & STATUS_STOPPING)
823662306a36Sopenharmony_ci		return;
823762306a36Sopenharmony_ci
823862306a36Sopenharmony_ci	mutex_lock(&priv->action_mutex);
823962306a36Sopenharmony_ci
824062306a36Sopenharmony_ci	IPW_DEBUG_WX("enter\n");
824162306a36Sopenharmony_ci
824262306a36Sopenharmony_ci	mutex_unlock(&priv->action_mutex);
824362306a36Sopenharmony_ci
824462306a36Sopenharmony_ci	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
824562306a36Sopenharmony_ci
824662306a36Sopenharmony_ci	/* Fetch BSSID from the hardware */
824762306a36Sopenharmony_ci	if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) ||
824862306a36Sopenharmony_ci	    priv->status & STATUS_RF_KILL_MASK ||
824962306a36Sopenharmony_ci	    ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
825062306a36Sopenharmony_ci				&priv->bssid, &len)) {
825162306a36Sopenharmony_ci		eth_zero_addr(wrqu.ap_addr.sa_data);
825262306a36Sopenharmony_ci	} else {
825362306a36Sopenharmony_ci		/* We now have the BSSID, so can finish setting to the full
825462306a36Sopenharmony_ci		 * associated state */
825562306a36Sopenharmony_ci		memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
825662306a36Sopenharmony_ci		memcpy(priv->ieee->bssid, priv->bssid, ETH_ALEN);
825762306a36Sopenharmony_ci		priv->status &= ~STATUS_ASSOCIATING;
825862306a36Sopenharmony_ci		priv->status |= STATUS_ASSOCIATED;
825962306a36Sopenharmony_ci		netif_carrier_on(priv->net_dev);
826062306a36Sopenharmony_ci		netif_wake_queue(priv->net_dev);
826162306a36Sopenharmony_ci	}
826262306a36Sopenharmony_ci
826362306a36Sopenharmony_ci	if (!(priv->status & STATUS_ASSOCIATED)) {
826462306a36Sopenharmony_ci		IPW_DEBUG_WX("Configuring ESSID\n");
826562306a36Sopenharmony_ci		mutex_lock(&priv->action_mutex);
826662306a36Sopenharmony_ci		/* This is a disassociation event, so kick the firmware to
826762306a36Sopenharmony_ci		 * look for another AP */
826862306a36Sopenharmony_ci		if (priv->config & CFG_STATIC_ESSID)
826962306a36Sopenharmony_ci			ipw2100_set_essid(priv, priv->essid, priv->essid_len,
827062306a36Sopenharmony_ci					  0);
827162306a36Sopenharmony_ci		else
827262306a36Sopenharmony_ci			ipw2100_set_essid(priv, NULL, 0, 0);
827362306a36Sopenharmony_ci		mutex_unlock(&priv->action_mutex);
827462306a36Sopenharmony_ci	}
827562306a36Sopenharmony_ci
827662306a36Sopenharmony_ci	wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
827762306a36Sopenharmony_ci}
827862306a36Sopenharmony_ci
827962306a36Sopenharmony_ci#define IPW2100_FW_MAJOR_VERSION 1
828062306a36Sopenharmony_ci#define IPW2100_FW_MINOR_VERSION 3
828162306a36Sopenharmony_ci
828262306a36Sopenharmony_ci#define IPW2100_FW_MINOR(x) ((x & 0xff) >> 8)
828362306a36Sopenharmony_ci#define IPW2100_FW_MAJOR(x) (x & 0xff)
828462306a36Sopenharmony_ci
828562306a36Sopenharmony_ci#define IPW2100_FW_VERSION ((IPW2100_FW_MINOR_VERSION << 8) | \
828662306a36Sopenharmony_ci                             IPW2100_FW_MAJOR_VERSION)
828762306a36Sopenharmony_ci
828862306a36Sopenharmony_ci#define IPW2100_FW_PREFIX "ipw2100-" __stringify(IPW2100_FW_MAJOR_VERSION) \
828962306a36Sopenharmony_ci"." __stringify(IPW2100_FW_MINOR_VERSION)
829062306a36Sopenharmony_ci
829162306a36Sopenharmony_ci#define IPW2100_FW_NAME(x) IPW2100_FW_PREFIX "" x ".fw"
829262306a36Sopenharmony_ci
829362306a36Sopenharmony_ci/*
829462306a36Sopenharmony_ci
829562306a36Sopenharmony_ciBINARY FIRMWARE HEADER FORMAT
829662306a36Sopenharmony_ci
829762306a36Sopenharmony_cioffset      length   desc
829862306a36Sopenharmony_ci0           2        version
829962306a36Sopenharmony_ci2           2        mode == 0:BSS,1:IBSS,2:MONITOR
830062306a36Sopenharmony_ci4           4        fw_len
830162306a36Sopenharmony_ci8           4        uc_len
830262306a36Sopenharmony_ciC           fw_len   firmware data
830362306a36Sopenharmony_ci12 + fw_len uc_len   microcode data
830462306a36Sopenharmony_ci
830562306a36Sopenharmony_ci*/
830662306a36Sopenharmony_ci
830762306a36Sopenharmony_cistruct ipw2100_fw_header {
830862306a36Sopenharmony_ci	short version;
830962306a36Sopenharmony_ci	short mode;
831062306a36Sopenharmony_ci	unsigned int fw_size;
831162306a36Sopenharmony_ci	unsigned int uc_size;
831262306a36Sopenharmony_ci} __packed;
831362306a36Sopenharmony_ci
831462306a36Sopenharmony_cistatic int ipw2100_mod_firmware_load(struct ipw2100_fw *fw)
831562306a36Sopenharmony_ci{
831662306a36Sopenharmony_ci	struct ipw2100_fw_header *h =
831762306a36Sopenharmony_ci	    (struct ipw2100_fw_header *)fw->fw_entry->data;
831862306a36Sopenharmony_ci
831962306a36Sopenharmony_ci	if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
832062306a36Sopenharmony_ci		printk(KERN_WARNING DRV_NAME ": Firmware image not compatible "
832162306a36Sopenharmony_ci		       "(detected version id of %u). "
832262306a36Sopenharmony_ci		       "See Documentation/networking/device_drivers/wifi/intel/ipw2100.rst\n",
832362306a36Sopenharmony_ci		       h->version);
832462306a36Sopenharmony_ci		return 1;
832562306a36Sopenharmony_ci	}
832662306a36Sopenharmony_ci
832762306a36Sopenharmony_ci	fw->version = h->version;
832862306a36Sopenharmony_ci	fw->fw.data = fw->fw_entry->data + sizeof(struct ipw2100_fw_header);
832962306a36Sopenharmony_ci	fw->fw.size = h->fw_size;
833062306a36Sopenharmony_ci	fw->uc.data = fw->fw.data + h->fw_size;
833162306a36Sopenharmony_ci	fw->uc.size = h->uc_size;
833262306a36Sopenharmony_ci
833362306a36Sopenharmony_ci	return 0;
833462306a36Sopenharmony_ci}
833562306a36Sopenharmony_ci
833662306a36Sopenharmony_cistatic int ipw2100_get_firmware(struct ipw2100_priv *priv,
833762306a36Sopenharmony_ci				struct ipw2100_fw *fw)
833862306a36Sopenharmony_ci{
833962306a36Sopenharmony_ci	char *fw_name;
834062306a36Sopenharmony_ci	int rc;
834162306a36Sopenharmony_ci
834262306a36Sopenharmony_ci	IPW_DEBUG_INFO("%s: Using hotplug firmware load.\n",
834362306a36Sopenharmony_ci		       priv->net_dev->name);
834462306a36Sopenharmony_ci
834562306a36Sopenharmony_ci	switch (priv->ieee->iw_mode) {
834662306a36Sopenharmony_ci	case IW_MODE_ADHOC:
834762306a36Sopenharmony_ci		fw_name = IPW2100_FW_NAME("-i");
834862306a36Sopenharmony_ci		break;
834962306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
835062306a36Sopenharmony_ci	case IW_MODE_MONITOR:
835162306a36Sopenharmony_ci		fw_name = IPW2100_FW_NAME("-p");
835262306a36Sopenharmony_ci		break;
835362306a36Sopenharmony_ci#endif
835462306a36Sopenharmony_ci	case IW_MODE_INFRA:
835562306a36Sopenharmony_ci	default:
835662306a36Sopenharmony_ci		fw_name = IPW2100_FW_NAME("");
835762306a36Sopenharmony_ci		break;
835862306a36Sopenharmony_ci	}
835962306a36Sopenharmony_ci
836062306a36Sopenharmony_ci	rc = request_firmware(&fw->fw_entry, fw_name, &priv->pci_dev->dev);
836162306a36Sopenharmony_ci
836262306a36Sopenharmony_ci	if (rc < 0) {
836362306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME ": "
836462306a36Sopenharmony_ci		       "%s: Firmware '%s' not available or load failed.\n",
836562306a36Sopenharmony_ci		       priv->net_dev->name, fw_name);
836662306a36Sopenharmony_ci		return rc;
836762306a36Sopenharmony_ci	}
836862306a36Sopenharmony_ci	IPW_DEBUG_INFO("firmware data %p size %zd\n", fw->fw_entry->data,
836962306a36Sopenharmony_ci		       fw->fw_entry->size);
837062306a36Sopenharmony_ci
837162306a36Sopenharmony_ci	ipw2100_mod_firmware_load(fw);
837262306a36Sopenharmony_ci
837362306a36Sopenharmony_ci	return 0;
837462306a36Sopenharmony_ci}
837562306a36Sopenharmony_ci
837662306a36Sopenharmony_ciMODULE_FIRMWARE(IPW2100_FW_NAME("-i"));
837762306a36Sopenharmony_ci#ifdef CONFIG_IPW2100_MONITOR
837862306a36Sopenharmony_ciMODULE_FIRMWARE(IPW2100_FW_NAME("-p"));
837962306a36Sopenharmony_ci#endif
838062306a36Sopenharmony_ciMODULE_FIRMWARE(IPW2100_FW_NAME(""));
838162306a36Sopenharmony_ci
838262306a36Sopenharmony_cistatic void ipw2100_release_firmware(struct ipw2100_priv *priv,
838362306a36Sopenharmony_ci				     struct ipw2100_fw *fw)
838462306a36Sopenharmony_ci{
838562306a36Sopenharmony_ci	fw->version = 0;
838662306a36Sopenharmony_ci	release_firmware(fw->fw_entry);
838762306a36Sopenharmony_ci	fw->fw_entry = NULL;
838862306a36Sopenharmony_ci}
838962306a36Sopenharmony_ci
839062306a36Sopenharmony_cistatic int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
839162306a36Sopenharmony_ci				 size_t max)
839262306a36Sopenharmony_ci{
839362306a36Sopenharmony_ci	char ver[MAX_FW_VERSION_LEN];
839462306a36Sopenharmony_ci	u32 len = MAX_FW_VERSION_LEN;
839562306a36Sopenharmony_ci	u32 tmp;
839662306a36Sopenharmony_ci	int i;
839762306a36Sopenharmony_ci	/* firmware version is an ascii string (max len of 14) */
839862306a36Sopenharmony_ci	if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_FW_VER_NUM, ver, &len))
839962306a36Sopenharmony_ci		return -EIO;
840062306a36Sopenharmony_ci	tmp = max;
840162306a36Sopenharmony_ci	if (len >= max)
840262306a36Sopenharmony_ci		len = max - 1;
840362306a36Sopenharmony_ci	for (i = 0; i < len; i++)
840462306a36Sopenharmony_ci		buf[i] = ver[i];
840562306a36Sopenharmony_ci	buf[i] = '\0';
840662306a36Sopenharmony_ci	return tmp;
840762306a36Sopenharmony_ci}
840862306a36Sopenharmony_ci
840962306a36Sopenharmony_cistatic int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
841062306a36Sopenharmony_ci				    size_t max)
841162306a36Sopenharmony_ci{
841262306a36Sopenharmony_ci	u32 ver;
841362306a36Sopenharmony_ci	u32 len = sizeof(ver);
841462306a36Sopenharmony_ci	/* microcode version is a 32 bit integer */
841562306a36Sopenharmony_ci	if (ipw2100_get_ordinal(priv, IPW_ORD_UCODE_VERSION, &ver, &len))
841662306a36Sopenharmony_ci		return -EIO;
841762306a36Sopenharmony_ci	return snprintf(buf, max, "%08X", ver);
841862306a36Sopenharmony_ci}
841962306a36Sopenharmony_ci
842062306a36Sopenharmony_ci/*
842162306a36Sopenharmony_ci * On exit, the firmware will have been freed from the fw list
842262306a36Sopenharmony_ci */
842362306a36Sopenharmony_cistatic int ipw2100_fw_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw)
842462306a36Sopenharmony_ci{
842562306a36Sopenharmony_ci	/* firmware is constructed of N contiguous entries, each entry is
842662306a36Sopenharmony_ci	 * structured as:
842762306a36Sopenharmony_ci	 *
842862306a36Sopenharmony_ci	 * offset    sie         desc
842962306a36Sopenharmony_ci	 * 0         4           address to write to
843062306a36Sopenharmony_ci	 * 4         2           length of data run
843162306a36Sopenharmony_ci	 * 6         length      data
843262306a36Sopenharmony_ci	 */
843362306a36Sopenharmony_ci	unsigned int addr;
843462306a36Sopenharmony_ci	unsigned short len;
843562306a36Sopenharmony_ci
843662306a36Sopenharmony_ci	const unsigned char *firmware_data = fw->fw.data;
843762306a36Sopenharmony_ci	unsigned int firmware_data_left = fw->fw.size;
843862306a36Sopenharmony_ci
843962306a36Sopenharmony_ci	while (firmware_data_left > 0) {
844062306a36Sopenharmony_ci		addr = *(u32 *) (firmware_data);
844162306a36Sopenharmony_ci		firmware_data += 4;
844262306a36Sopenharmony_ci		firmware_data_left -= 4;
844362306a36Sopenharmony_ci
844462306a36Sopenharmony_ci		len = *(u16 *) (firmware_data);
844562306a36Sopenharmony_ci		firmware_data += 2;
844662306a36Sopenharmony_ci		firmware_data_left -= 2;
844762306a36Sopenharmony_ci
844862306a36Sopenharmony_ci		if (len > 32) {
844962306a36Sopenharmony_ci			printk(KERN_ERR DRV_NAME ": "
845062306a36Sopenharmony_ci			       "Invalid firmware run-length of %d bytes\n",
845162306a36Sopenharmony_ci			       len);
845262306a36Sopenharmony_ci			return -EINVAL;
845362306a36Sopenharmony_ci		}
845462306a36Sopenharmony_ci
845562306a36Sopenharmony_ci		write_nic_memory(priv->net_dev, addr, len, firmware_data);
845662306a36Sopenharmony_ci		firmware_data += len;
845762306a36Sopenharmony_ci		firmware_data_left -= len;
845862306a36Sopenharmony_ci	}
845962306a36Sopenharmony_ci
846062306a36Sopenharmony_ci	return 0;
846162306a36Sopenharmony_ci}
846262306a36Sopenharmony_ci
846362306a36Sopenharmony_cistruct symbol_alive_response {
846462306a36Sopenharmony_ci	u8 cmd_id;
846562306a36Sopenharmony_ci	u8 seq_num;
846662306a36Sopenharmony_ci	u8 ucode_rev;
846762306a36Sopenharmony_ci	u8 eeprom_valid;
846862306a36Sopenharmony_ci	u16 valid_flags;
846962306a36Sopenharmony_ci	u8 IEEE_addr[6];
847062306a36Sopenharmony_ci	u16 flags;
847162306a36Sopenharmony_ci	u16 pcb_rev;
847262306a36Sopenharmony_ci	u16 clock_settle_time;	// 1us LSB
847362306a36Sopenharmony_ci	u16 powerup_settle_time;	// 1us LSB
847462306a36Sopenharmony_ci	u16 hop_settle_time;	// 1us LSB
847562306a36Sopenharmony_ci	u8 date[3];		// month, day, year
847662306a36Sopenharmony_ci	u8 time[2];		// hours, minutes
847762306a36Sopenharmony_ci	u8 ucode_valid;
847862306a36Sopenharmony_ci};
847962306a36Sopenharmony_ci
848062306a36Sopenharmony_cistatic int ipw2100_ucode_download(struct ipw2100_priv *priv,
848162306a36Sopenharmony_ci				  struct ipw2100_fw *fw)
848262306a36Sopenharmony_ci{
848362306a36Sopenharmony_ci	struct net_device *dev = priv->net_dev;
848462306a36Sopenharmony_ci	const unsigned char *microcode_data = fw->uc.data;
848562306a36Sopenharmony_ci	unsigned int microcode_data_left = fw->uc.size;
848662306a36Sopenharmony_ci	void __iomem *reg = priv->ioaddr;
848762306a36Sopenharmony_ci
848862306a36Sopenharmony_ci	struct symbol_alive_response response;
848962306a36Sopenharmony_ci	int i, j;
849062306a36Sopenharmony_ci	u8 data;
849162306a36Sopenharmony_ci
849262306a36Sopenharmony_ci	/* Symbol control */
849362306a36Sopenharmony_ci	write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
849462306a36Sopenharmony_ci	readl(reg);
849562306a36Sopenharmony_ci	write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
849662306a36Sopenharmony_ci	readl(reg);
849762306a36Sopenharmony_ci
849862306a36Sopenharmony_ci	/* HW config */
849962306a36Sopenharmony_ci	write_nic_byte(dev, 0x210014, 0x72);	/* fifo width =16 */
850062306a36Sopenharmony_ci	readl(reg);
850162306a36Sopenharmony_ci	write_nic_byte(dev, 0x210014, 0x72);	/* fifo width =16 */
850262306a36Sopenharmony_ci	readl(reg);
850362306a36Sopenharmony_ci
850462306a36Sopenharmony_ci	/* EN_CS_ACCESS bit to reset control store pointer */
850562306a36Sopenharmony_ci	write_nic_byte(dev, 0x210000, 0x40);
850662306a36Sopenharmony_ci	readl(reg);
850762306a36Sopenharmony_ci	write_nic_byte(dev, 0x210000, 0x0);
850862306a36Sopenharmony_ci	readl(reg);
850962306a36Sopenharmony_ci	write_nic_byte(dev, 0x210000, 0x40);
851062306a36Sopenharmony_ci	readl(reg);
851162306a36Sopenharmony_ci
851262306a36Sopenharmony_ci	/* copy microcode from buffer into Symbol */
851362306a36Sopenharmony_ci
851462306a36Sopenharmony_ci	while (microcode_data_left > 0) {
851562306a36Sopenharmony_ci		write_nic_byte(dev, 0x210010, *microcode_data++);
851662306a36Sopenharmony_ci		write_nic_byte(dev, 0x210010, *microcode_data++);
851762306a36Sopenharmony_ci		microcode_data_left -= 2;
851862306a36Sopenharmony_ci	}
851962306a36Sopenharmony_ci
852062306a36Sopenharmony_ci	/* EN_CS_ACCESS bit to reset the control store pointer */
852162306a36Sopenharmony_ci	write_nic_byte(dev, 0x210000, 0x0);
852262306a36Sopenharmony_ci	readl(reg);
852362306a36Sopenharmony_ci
852462306a36Sopenharmony_ci	/* Enable System (Reg 0)
852562306a36Sopenharmony_ci	 * first enable causes garbage in RX FIFO */
852662306a36Sopenharmony_ci	write_nic_byte(dev, 0x210000, 0x0);
852762306a36Sopenharmony_ci	readl(reg);
852862306a36Sopenharmony_ci	write_nic_byte(dev, 0x210000, 0x80);
852962306a36Sopenharmony_ci	readl(reg);
853062306a36Sopenharmony_ci
853162306a36Sopenharmony_ci	/* Reset External Baseband Reg */
853262306a36Sopenharmony_ci	write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
853362306a36Sopenharmony_ci	readl(reg);
853462306a36Sopenharmony_ci	write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
853562306a36Sopenharmony_ci	readl(reg);
853662306a36Sopenharmony_ci
853762306a36Sopenharmony_ci	/* HW Config (Reg 5) */
853862306a36Sopenharmony_ci	write_nic_byte(dev, 0x210014, 0x72);	// fifo width =16
853962306a36Sopenharmony_ci	readl(reg);
854062306a36Sopenharmony_ci	write_nic_byte(dev, 0x210014, 0x72);	// fifo width =16
854162306a36Sopenharmony_ci	readl(reg);
854262306a36Sopenharmony_ci
854362306a36Sopenharmony_ci	/* Enable System (Reg 0)
854462306a36Sopenharmony_ci	 * second enable should be OK */
854562306a36Sopenharmony_ci	write_nic_byte(dev, 0x210000, 0x00);	// clear enable system
854662306a36Sopenharmony_ci	readl(reg);
854762306a36Sopenharmony_ci	write_nic_byte(dev, 0x210000, 0x80);	// set enable system
854862306a36Sopenharmony_ci
854962306a36Sopenharmony_ci	/* check Symbol is enabled - upped this from 5 as it wasn't always
855062306a36Sopenharmony_ci	 * catching the update */
855162306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
855262306a36Sopenharmony_ci		udelay(10);
855362306a36Sopenharmony_ci
855462306a36Sopenharmony_ci		/* check Dino is enabled bit */
855562306a36Sopenharmony_ci		read_nic_byte(dev, 0x210000, &data);
855662306a36Sopenharmony_ci		if (data & 0x1)
855762306a36Sopenharmony_ci			break;
855862306a36Sopenharmony_ci	}
855962306a36Sopenharmony_ci
856062306a36Sopenharmony_ci	if (i == 10) {
856162306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME ": %s: Error initializing Symbol\n",
856262306a36Sopenharmony_ci		       dev->name);
856362306a36Sopenharmony_ci		return -EIO;
856462306a36Sopenharmony_ci	}
856562306a36Sopenharmony_ci
856662306a36Sopenharmony_ci	/* Get Symbol alive response */
856762306a36Sopenharmony_ci	for (i = 0; i < 30; i++) {
856862306a36Sopenharmony_ci		/* Read alive response structure */
856962306a36Sopenharmony_ci		for (j = 0;
857062306a36Sopenharmony_ci		     j < (sizeof(struct symbol_alive_response) >> 1); j++)
857162306a36Sopenharmony_ci			read_nic_word(dev, 0x210004, ((u16 *) & response) + j);
857262306a36Sopenharmony_ci
857362306a36Sopenharmony_ci		if ((response.cmd_id == 1) && (response.ucode_valid == 0x1))
857462306a36Sopenharmony_ci			break;
857562306a36Sopenharmony_ci		udelay(10);
857662306a36Sopenharmony_ci	}
857762306a36Sopenharmony_ci
857862306a36Sopenharmony_ci	if (i == 30) {
857962306a36Sopenharmony_ci		printk(KERN_ERR DRV_NAME
858062306a36Sopenharmony_ci		       ": %s: No response from Symbol - hw not alive\n",
858162306a36Sopenharmony_ci		       dev->name);
858262306a36Sopenharmony_ci		printk_buf(IPW_DL_ERROR, (u8 *) & response, sizeof(response));
858362306a36Sopenharmony_ci		return -EIO;
858462306a36Sopenharmony_ci	}
858562306a36Sopenharmony_ci
858662306a36Sopenharmony_ci	return 0;
858762306a36Sopenharmony_ci}
8588