18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Host AP (software wireless LAN access point) driver for
48c2ecf20Sopenharmony_ci * Intersil Prism2/2.5/3.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
78c2ecf20Sopenharmony_ci * <j@w1.fi>
88c2ecf20Sopenharmony_ci * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * FIX:
118c2ecf20Sopenharmony_ci * - there is currently no way of associating TX packets to correct wds device
128c2ecf20Sopenharmony_ci *   when TX Exc/OK event occurs, so all tx_packets and some
138c2ecf20Sopenharmony_ci *   tx_errors/tx_dropped are added to the main netdevice; using sw_support
148c2ecf20Sopenharmony_ci *   field in txdesc might be used to fix this (using Alloc event to increment
158c2ecf20Sopenharmony_ci *   tx_packets would need some further info in txfid table)
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Buffer Access Path (BAP) usage:
188c2ecf20Sopenharmony_ci *   Prism2 cards have two separate BAPs for accessing the card memory. These
198c2ecf20Sopenharmony_ci *   should allow concurrent access to two different frames and the driver
208c2ecf20Sopenharmony_ci *   previously used BAP0 for sending data and BAP1 for receiving data.
218c2ecf20Sopenharmony_ci *   However, there seems to be number of issues with concurrent access and at
228c2ecf20Sopenharmony_ci *   least one know hardware bug in using BAP0 and BAP1 concurrently with PCI
238c2ecf20Sopenharmony_ci *   Prism2.5. Therefore, the driver now only uses BAP0 for moving data between
248c2ecf20Sopenharmony_ci *   host and card memories. BAP0 accesses are protected with local->baplock
258c2ecf20Sopenharmony_ci *   (spin_lock_bh) to prevent concurrent use.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <asm/delay.h>
318c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <linux/slab.h>
348c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
358c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
368c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
378c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
388c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
398c2ecf20Sopenharmony_ci#include <linux/delay.h>
408c2ecf20Sopenharmony_ci#include <linux/random.h>
418c2ecf20Sopenharmony_ci#include <linux/wait.h>
428c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
438c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
448c2ecf20Sopenharmony_ci#include <linux/wireless.h>
458c2ecf20Sopenharmony_ci#include <net/iw_handler.h>
468c2ecf20Sopenharmony_ci#include <net/lib80211.h>
478c2ecf20Sopenharmony_ci#include <asm/irq.h>
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#include "hostap_80211.h"
508c2ecf20Sopenharmony_ci#include "hostap.h"
518c2ecf20Sopenharmony_ci#include "hostap_ap.h"
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* #define final_version */
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int mtu = 1500;
578c2ecf20Sopenharmony_cimodule_param(mtu, int, 0444);
588c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mtu, "Maximum transfer unit");
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS };
618c2ecf20Sopenharmony_cimodule_param_array(channel, int, NULL, 0444);
628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(channel, "Initial channel");
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic char essid[33] = "test";
658c2ecf20Sopenharmony_cimodule_param_string(essid, essid, sizeof(essid), 0444);
668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(essid, "Host AP's ESSID");
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int iw_mode[MAX_PARM_DEVICES] = { IW_MODE_MASTER, DEF_INTS };
698c2ecf20Sopenharmony_cimodule_param_array(iw_mode, int, NULL, 0444);
708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(iw_mode, "Initial operation mode");
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int beacon_int[MAX_PARM_DEVICES] = { 100, DEF_INTS };
738c2ecf20Sopenharmony_cimodule_param_array(beacon_int, int, NULL, 0444);
748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(beacon_int, "Beacon interval (1 = 1024 usec)");
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int dtim_period[MAX_PARM_DEVICES] = { 1, DEF_INTS };
778c2ecf20Sopenharmony_cimodule_param_array(dtim_period, int, NULL, 0444);
788c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dtim_period, "DTIM period");
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic char dev_template[16] = "wlan%d";
818c2ecf20Sopenharmony_cimodule_param_string(dev_template, dev_template, sizeof(dev_template), 0444);
828c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dev_template, "Prefix for network device name (default: "
838c2ecf20Sopenharmony_ci		 "wlan%d)");
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#ifdef final_version
868c2ecf20Sopenharmony_ci#define EXTRA_EVENTS_WTERR 0
878c2ecf20Sopenharmony_ci#else
888c2ecf20Sopenharmony_ci/* check WTERR events (Wait Time-out) in development versions */
898c2ecf20Sopenharmony_ci#define EXTRA_EVENTS_WTERR HFA384X_EV_WTERR
908c2ecf20Sopenharmony_ci#endif
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* Events that will be using BAP0 */
938c2ecf20Sopenharmony_ci#define HFA384X_BAP0_EVENTS \
948c2ecf20Sopenharmony_ci	(HFA384X_EV_TXEXC | HFA384X_EV_RX | HFA384X_EV_INFO | HFA384X_EV_TX)
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/* event mask, i.e., events that will result in an interrupt */
978c2ecf20Sopenharmony_ci#define HFA384X_EVENT_MASK \
988c2ecf20Sopenharmony_ci	(HFA384X_BAP0_EVENTS | HFA384X_EV_ALLOC | HFA384X_EV_INFDROP | \
998c2ecf20Sopenharmony_ci	HFA384X_EV_CMD | HFA384X_EV_TICK | \
1008c2ecf20Sopenharmony_ci	EXTRA_EVENTS_WTERR)
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/* Default TX control flags: use 802.11 headers and request interrupt for
1038c2ecf20Sopenharmony_ci * failed transmits. Frames that request ACK callback, will add
1048c2ecf20Sopenharmony_ci * _TX_OK flag and _ALT_RTRY flag may be used to select different retry policy.
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_ci#define HFA384X_TX_CTRL_FLAGS \
1078c2ecf20Sopenharmony_ci	(HFA384X_TX_CTRL_802_11 | HFA384X_TX_CTRL_TX_EX)
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/* ca. 1 usec */
1118c2ecf20Sopenharmony_ci#define HFA384X_CMD_BUSY_TIMEOUT 5000
1128c2ecf20Sopenharmony_ci#define HFA384X_BAP_BUSY_TIMEOUT 50000
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* ca. 10 usec */
1158c2ecf20Sopenharmony_ci#define HFA384X_CMD_COMPL_TIMEOUT 20000
1168c2ecf20Sopenharmony_ci#define HFA384X_DL_COMPL_TIMEOUT 1000000
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/* Wait times for initialization; yield to other processes to avoid busy
1198c2ecf20Sopenharmony_ci * waiting for long time. */
1208c2ecf20Sopenharmony_ci#define HFA384X_INIT_TIMEOUT (HZ / 2) /* 500 ms */
1218c2ecf20Sopenharmony_ci#define HFA384X_ALLOC_COMPL_TIMEOUT (HZ / 20) /* 50 ms */
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic void prism2_hw_reset(struct net_device *dev);
1258c2ecf20Sopenharmony_cistatic void prism2_check_sta_fw_version(local_info_t *local);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci#ifdef PRISM2_DOWNLOAD_SUPPORT
1288c2ecf20Sopenharmony_ci/* hostap_download.c */
1298c2ecf20Sopenharmony_cistatic const struct proc_ops prism2_download_aux_dump_proc_ops;
1308c2ecf20Sopenharmony_cistatic u8 * prism2_read_pda(struct net_device *dev);
1318c2ecf20Sopenharmony_cistatic int prism2_download(local_info_t *local,
1328c2ecf20Sopenharmony_ci			   struct prism2_download_param *param);
1338c2ecf20Sopenharmony_cistatic void prism2_download_free_data(struct prism2_download_data *dl);
1348c2ecf20Sopenharmony_cistatic int prism2_download_volatile(local_info_t *local,
1358c2ecf20Sopenharmony_ci				    struct prism2_download_data *param);
1368c2ecf20Sopenharmony_cistatic int prism2_download_genesis(local_info_t *local,
1378c2ecf20Sopenharmony_ci				   struct prism2_download_data *param);
1388c2ecf20Sopenharmony_cistatic int prism2_get_ram_size(local_info_t *local);
1398c2ecf20Sopenharmony_ci#endif /* PRISM2_DOWNLOAD_SUPPORT */
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#ifndef final_version
1458c2ecf20Sopenharmony_ci/* magic value written to SWSUPPORT0 reg. for detecting whether card is still
1468c2ecf20Sopenharmony_ci * present */
1478c2ecf20Sopenharmony_ci#define HFA384X_MAGIC 0x8A32
1488c2ecf20Sopenharmony_ci#endif
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic void hfa384x_read_regs(struct net_device *dev,
1518c2ecf20Sopenharmony_ci			      struct hfa384x_regs *regs)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	regs->cmd = HFA384X_INW(HFA384X_CMD_OFF);
1548c2ecf20Sopenharmony_ci	regs->evstat = HFA384X_INW(HFA384X_EVSTAT_OFF);
1558c2ecf20Sopenharmony_ci	regs->offset0 = HFA384X_INW(HFA384X_OFFSET0_OFF);
1568c2ecf20Sopenharmony_ci	regs->offset1 = HFA384X_INW(HFA384X_OFFSET1_OFF);
1578c2ecf20Sopenharmony_ci	regs->swsupport0 = HFA384X_INW(HFA384X_SWSUPPORT0_OFF);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci/**
1628c2ecf20Sopenharmony_ci * __hostap_cmd_queue_free - Free Prism2 command queue entry (private)
1638c2ecf20Sopenharmony_ci * @local: pointer to private Host AP driver data
1648c2ecf20Sopenharmony_ci * @entry: Prism2 command queue entry to be freed
1658c2ecf20Sopenharmony_ci * @del_req: request the entry to be removed
1668c2ecf20Sopenharmony_ci *
1678c2ecf20Sopenharmony_ci * Internal helper function for freeing Prism2 command queue entries.
1688c2ecf20Sopenharmony_ci * Caller must have acquired local->cmdlock before calling this function.
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_cistatic inline void __hostap_cmd_queue_free(local_info_t *local,
1718c2ecf20Sopenharmony_ci					   struct hostap_cmd_queue *entry,
1728c2ecf20Sopenharmony_ci					   int del_req)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	if (del_req) {
1758c2ecf20Sopenharmony_ci		entry->del_req = 1;
1768c2ecf20Sopenharmony_ci		if (!list_empty(&entry->list)) {
1778c2ecf20Sopenharmony_ci			list_del_init(&entry->list);
1788c2ecf20Sopenharmony_ci			local->cmd_queue_len--;
1798c2ecf20Sopenharmony_ci		}
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&entry->usecnt) && entry->del_req)
1838c2ecf20Sopenharmony_ci		kfree(entry);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/**
1888c2ecf20Sopenharmony_ci * hostap_cmd_queue_free - Free Prism2 command queue entry
1898c2ecf20Sopenharmony_ci * @local: pointer to private Host AP driver data
1908c2ecf20Sopenharmony_ci * @entry: Prism2 command queue entry to be freed
1918c2ecf20Sopenharmony_ci * @del_req: request the entry to be removed
1928c2ecf20Sopenharmony_ci *
1938c2ecf20Sopenharmony_ci * Free a Prism2 command queue entry.
1948c2ecf20Sopenharmony_ci */
1958c2ecf20Sopenharmony_cistatic inline void hostap_cmd_queue_free(local_info_t *local,
1968c2ecf20Sopenharmony_ci					 struct hostap_cmd_queue *entry,
1978c2ecf20Sopenharmony_ci					 int del_req)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	unsigned long flags;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&local->cmdlock, flags);
2028c2ecf20Sopenharmony_ci	__hostap_cmd_queue_free(local, entry, del_req);
2038c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&local->cmdlock, flags);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/**
2088c2ecf20Sopenharmony_ci * prism2_clear_cmd_queue - Free all pending Prism2 command queue entries
2098c2ecf20Sopenharmony_ci * @local: pointer to private Host AP driver data
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_cistatic void prism2_clear_cmd_queue(local_info_t *local)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct list_head *ptr, *n;
2148c2ecf20Sopenharmony_ci	unsigned long flags;
2158c2ecf20Sopenharmony_ci	struct hostap_cmd_queue *entry;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&local->cmdlock, flags);
2188c2ecf20Sopenharmony_ci	list_for_each_safe(ptr, n, &local->cmd_queue) {
2198c2ecf20Sopenharmony_ci		entry = list_entry(ptr, struct hostap_cmd_queue, list);
2208c2ecf20Sopenharmony_ci		refcount_inc(&entry->usecnt);
2218c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: removed pending cmd_queue entry "
2228c2ecf20Sopenharmony_ci		       "(type=%d, cmd=0x%04x, param0=0x%04x)\n",
2238c2ecf20Sopenharmony_ci		       local->dev->name, entry->type, entry->cmd,
2248c2ecf20Sopenharmony_ci		       entry->param0);
2258c2ecf20Sopenharmony_ci		__hostap_cmd_queue_free(local, entry, 1);
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci	if (local->cmd_queue_len) {
2288c2ecf20Sopenharmony_ci		/* This should not happen; print debug message and clear
2298c2ecf20Sopenharmony_ci		 * queue length. */
2308c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: cmd_queue_len (%d) not zero after "
2318c2ecf20Sopenharmony_ci		       "flush\n", local->dev->name, local->cmd_queue_len);
2328c2ecf20Sopenharmony_ci		local->cmd_queue_len = 0;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&local->cmdlock, flags);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci/**
2398c2ecf20Sopenharmony_ci * hfa384x_cmd_issue - Issue a Prism2 command to the hardware
2408c2ecf20Sopenharmony_ci * @dev: pointer to net_device
2418c2ecf20Sopenharmony_ci * @entry: Prism2 command queue entry to be issued
2428c2ecf20Sopenharmony_ci */
2438c2ecf20Sopenharmony_cistatic int hfa384x_cmd_issue(struct net_device *dev,
2448c2ecf20Sopenharmony_ci				    struct hostap_cmd_queue *entry)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
2478c2ecf20Sopenharmony_ci	local_info_t *local;
2488c2ecf20Sopenharmony_ci	int tries;
2498c2ecf20Sopenharmony_ci	u16 reg;
2508c2ecf20Sopenharmony_ci	unsigned long flags;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
2538c2ecf20Sopenharmony_ci	local = iface->local;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (local->func->card_present && !local->func->card_present(local))
2568c2ecf20Sopenharmony_ci		return -ENODEV;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (entry->issued) {
2598c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: driver bug - re-issuing command @%p\n",
2608c2ecf20Sopenharmony_ci		       dev->name, entry);
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* wait until busy bit is clear; this should always be clear since the
2648c2ecf20Sopenharmony_ci	 * commands are serialized */
2658c2ecf20Sopenharmony_ci	tries = HFA384X_CMD_BUSY_TIMEOUT;
2668c2ecf20Sopenharmony_ci	while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) {
2678c2ecf20Sopenharmony_ci		tries--;
2688c2ecf20Sopenharmony_ci		udelay(1);
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci#ifndef final_version
2718c2ecf20Sopenharmony_ci	if (tries != HFA384X_CMD_BUSY_TIMEOUT) {
2728c2ecf20Sopenharmony_ci		prism2_io_debug_error(dev, 1);
2738c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_cmd_issue: cmd reg was busy "
2748c2ecf20Sopenharmony_ci		       "for %d usec\n", dev->name,
2758c2ecf20Sopenharmony_ci		       HFA384X_CMD_BUSY_TIMEOUT - tries);
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci#endif
2788c2ecf20Sopenharmony_ci	if (tries == 0) {
2798c2ecf20Sopenharmony_ci		reg = HFA384X_INW(HFA384X_CMD_OFF);
2808c2ecf20Sopenharmony_ci		prism2_io_debug_error(dev, 2);
2818c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_cmd_issue - timeout - "
2828c2ecf20Sopenharmony_ci		       "reg=0x%04x\n", dev->name, reg);
2838c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/* write command */
2878c2ecf20Sopenharmony_ci	spin_lock_irqsave(&local->cmdlock, flags);
2888c2ecf20Sopenharmony_ci	HFA384X_OUTW(entry->param0, HFA384X_PARAM0_OFF);
2898c2ecf20Sopenharmony_ci	HFA384X_OUTW(entry->param1, HFA384X_PARAM1_OFF);
2908c2ecf20Sopenharmony_ci	HFA384X_OUTW(entry->cmd, HFA384X_CMD_OFF);
2918c2ecf20Sopenharmony_ci	entry->issued = 1;
2928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&local->cmdlock, flags);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return 0;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci/**
2998c2ecf20Sopenharmony_ci * hfa384x_cmd - Issue a Prism2 command and wait (sleep) for completion
3008c2ecf20Sopenharmony_ci * @dev: pointer to net_device
3018c2ecf20Sopenharmony_ci * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
3028c2ecf20Sopenharmony_ci * @param0: value for Param0 register
3038c2ecf20Sopenharmony_ci * @param1: value for Param1 register (pointer; %NULL if not used)
3048c2ecf20Sopenharmony_ci * @resp0: pointer for Resp0 data or %NULL if Resp0 is not needed
3058c2ecf20Sopenharmony_ci *
3068c2ecf20Sopenharmony_ci * Issue given command (possibly after waiting in command queue) and sleep
3078c2ecf20Sopenharmony_ci * until the command is completed (or timed out or interrupted). This can be
3088c2ecf20Sopenharmony_ci * called only from user process context.
3098c2ecf20Sopenharmony_ci */
3108c2ecf20Sopenharmony_cistatic int hfa384x_cmd(struct net_device *dev, u16 cmd, u16 param0,
3118c2ecf20Sopenharmony_ci		       u16 *param1, u16 *resp0)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
3148c2ecf20Sopenharmony_ci	local_info_t *local;
3158c2ecf20Sopenharmony_ci	int err, res, issue, issued = 0;
3168c2ecf20Sopenharmony_ci	unsigned long flags;
3178c2ecf20Sopenharmony_ci	struct hostap_cmd_queue *entry;
3188c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
3218c2ecf20Sopenharmony_ci	local = iface->local;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN) {
3248c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n",
3258c2ecf20Sopenharmony_ci		       dev->name);
3268c2ecf20Sopenharmony_ci		return -1;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (signal_pending(current))
3308c2ecf20Sopenharmony_ci		return -EINTR;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
3338c2ecf20Sopenharmony_ci	if (entry == NULL)
3348c2ecf20Sopenharmony_ci		return -ENOMEM;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	refcount_set(&entry->usecnt, 1);
3378c2ecf20Sopenharmony_ci	entry->type = CMD_SLEEP;
3388c2ecf20Sopenharmony_ci	entry->cmd = cmd;
3398c2ecf20Sopenharmony_ci	entry->param0 = param0;
3408c2ecf20Sopenharmony_ci	if (param1)
3418c2ecf20Sopenharmony_ci		entry->param1 = *param1;
3428c2ecf20Sopenharmony_ci	init_waitqueue_head(&entry->compl);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* prepare to wait for command completion event, but do not sleep yet
3458c2ecf20Sopenharmony_ci	 */
3468c2ecf20Sopenharmony_ci	add_wait_queue(&entry->compl, &wait);
3478c2ecf20Sopenharmony_ci	set_current_state(TASK_INTERRUPTIBLE);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&local->cmdlock, flags);
3508c2ecf20Sopenharmony_ci	issue = list_empty(&local->cmd_queue);
3518c2ecf20Sopenharmony_ci	if (issue)
3528c2ecf20Sopenharmony_ci		entry->issuing = 1;
3538c2ecf20Sopenharmony_ci	list_add_tail(&entry->list, &local->cmd_queue);
3548c2ecf20Sopenharmony_ci	local->cmd_queue_len++;
3558c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&local->cmdlock, flags);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	err = 0;
3588c2ecf20Sopenharmony_ci	if (!issue)
3598c2ecf20Sopenharmony_ci		goto wait_completion;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (signal_pending(current))
3628c2ecf20Sopenharmony_ci		err = -EINTR;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (!err) {
3658c2ecf20Sopenharmony_ci		if (hfa384x_cmd_issue(dev, entry))
3668c2ecf20Sopenharmony_ci			err = -ETIMEDOUT;
3678c2ecf20Sopenharmony_ci		else
3688c2ecf20Sopenharmony_ci			issued = 1;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci wait_completion:
3728c2ecf20Sopenharmony_ci	if (!err && entry->type != CMD_COMPLETED) {
3738c2ecf20Sopenharmony_ci		/* sleep until command is completed or timed out */
3748c2ecf20Sopenharmony_ci		res = schedule_timeout(2 * HZ);
3758c2ecf20Sopenharmony_ci	} else
3768c2ecf20Sopenharmony_ci		res = -1;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (!err && signal_pending(current))
3798c2ecf20Sopenharmony_ci		err = -EINTR;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (err && issued) {
3828c2ecf20Sopenharmony_ci		/* the command was issued, so a CmdCompl event should occur
3838c2ecf20Sopenharmony_ci		 * soon; however, there's a pending signal and
3848c2ecf20Sopenharmony_ci		 * schedule_timeout() would be interrupted; wait a short period
3858c2ecf20Sopenharmony_ci		 * of time to avoid removing entry from the list before
3868c2ecf20Sopenharmony_ci		 * CmdCompl event */
3878c2ecf20Sopenharmony_ci		udelay(300);
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	set_current_state(TASK_RUNNING);
3918c2ecf20Sopenharmony_ci	remove_wait_queue(&entry->compl, &wait);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	/* If entry->list is still in the list, it must be removed
3948c2ecf20Sopenharmony_ci	 * first and in this case prism2_cmd_ev() does not yet have
3958c2ecf20Sopenharmony_ci	 * local reference to it, and the data can be kfree()'d
3968c2ecf20Sopenharmony_ci	 * here. If the command completion event is still generated,
3978c2ecf20Sopenharmony_ci	 * it will be assigned to next (possibly) pending command, but
3988c2ecf20Sopenharmony_ci	 * the driver will reset the card anyway due to timeout
3998c2ecf20Sopenharmony_ci	 *
4008c2ecf20Sopenharmony_ci	 * If the entry is not in the list prism2_cmd_ev() has a local
4018c2ecf20Sopenharmony_ci	 * reference to it, but keeps cmdlock as long as the data is
4028c2ecf20Sopenharmony_ci	 * needed, so the data can be kfree()'d here. */
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* FIX: if the entry->list is in the list, it has not been completed
4058c2ecf20Sopenharmony_ci	 * yet, so removing it here is somewhat wrong.. this could cause
4068c2ecf20Sopenharmony_ci	 * references to freed memory and next list_del() causing NULL pointer
4078c2ecf20Sopenharmony_ci	 * dereference.. it would probably be better to leave the entry in the
4088c2ecf20Sopenharmony_ci	 * list and the list should be emptied during hw reset */
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&local->cmdlock, flags);
4118c2ecf20Sopenharmony_ci	if (!list_empty(&entry->list)) {
4128c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_cmd: entry still in list? "
4138c2ecf20Sopenharmony_ci		       "(entry=%p, type=%d, res=%d)\n", dev->name, entry,
4148c2ecf20Sopenharmony_ci		       entry->type, res);
4158c2ecf20Sopenharmony_ci		list_del_init(&entry->list);
4168c2ecf20Sopenharmony_ci		local->cmd_queue_len--;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&local->cmdlock, flags);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (err) {
4218c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_cmd: interrupted; err=%d\n",
4228c2ecf20Sopenharmony_ci		       dev->name, err);
4238c2ecf20Sopenharmony_ci		res = err;
4248c2ecf20Sopenharmony_ci		goto done;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (entry->type != CMD_COMPLETED) {
4288c2ecf20Sopenharmony_ci		u16 reg = HFA384X_INW(HFA384X_EVSTAT_OFF);
4298c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_cmd: command was not "
4308c2ecf20Sopenharmony_ci		       "completed (res=%d, entry=%p, type=%d, cmd=0x%04x, "
4318c2ecf20Sopenharmony_ci		       "param0=0x%04x, EVSTAT=%04x INTEN=%04x)\n", dev->name,
4328c2ecf20Sopenharmony_ci		       res, entry, entry->type, entry->cmd, entry->param0, reg,
4338c2ecf20Sopenharmony_ci		       HFA384X_INW(HFA384X_INTEN_OFF));
4348c2ecf20Sopenharmony_ci		if (reg & HFA384X_EV_CMD) {
4358c2ecf20Sopenharmony_ci			/* Command completion event is pending, but the
4368c2ecf20Sopenharmony_ci			 * interrupt was not delivered - probably an issue
4378c2ecf20Sopenharmony_ci			 * with pcmcia-cs configuration. */
4388c2ecf20Sopenharmony_ci			printk(KERN_WARNING "%s: interrupt delivery does not "
4398c2ecf20Sopenharmony_ci			       "seem to work\n", dev->name);
4408c2ecf20Sopenharmony_ci		}
4418c2ecf20Sopenharmony_ci		prism2_io_debug_error(dev, 3);
4428c2ecf20Sopenharmony_ci		res = -ETIMEDOUT;
4438c2ecf20Sopenharmony_ci		goto done;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (resp0 != NULL)
4478c2ecf20Sopenharmony_ci		*resp0 = entry->resp0;
4488c2ecf20Sopenharmony_ci#ifndef final_version
4498c2ecf20Sopenharmony_ci	if (entry->res) {
4508c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x, "
4518c2ecf20Sopenharmony_ci		       "resp0=0x%04x\n",
4528c2ecf20Sopenharmony_ci		       dev->name, cmd, entry->res, entry->resp0);
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci#endif /* final_version */
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	res = entry->res;
4578c2ecf20Sopenharmony_ci done:
4588c2ecf20Sopenharmony_ci	hostap_cmd_queue_free(local, entry, 1);
4598c2ecf20Sopenharmony_ci	return res;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci/**
4648c2ecf20Sopenharmony_ci * hfa384x_cmd_callback - Issue a Prism2 command; callback when completed
4658c2ecf20Sopenharmony_ci * @dev: pointer to net_device
4668c2ecf20Sopenharmony_ci * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
4678c2ecf20Sopenharmony_ci * @param0: value for Param0 register
4688c2ecf20Sopenharmony_ci * @callback: command completion callback function (%NULL = no callback)
4698c2ecf20Sopenharmony_ci * @context: context data to be given to the callback function
4708c2ecf20Sopenharmony_ci *
4718c2ecf20Sopenharmony_ci * Issue given command (possibly after waiting in command queue) and use
4728c2ecf20Sopenharmony_ci * callback function to indicate command completion. This can be called both
4738c2ecf20Sopenharmony_ci * from user and interrupt context. The callback function will be called in
4748c2ecf20Sopenharmony_ci * hardware IRQ context. It can be %NULL, when no function is called when
4758c2ecf20Sopenharmony_ci * command is completed.
4768c2ecf20Sopenharmony_ci */
4778c2ecf20Sopenharmony_cistatic int hfa384x_cmd_callback(struct net_device *dev, u16 cmd, u16 param0,
4788c2ecf20Sopenharmony_ci				void (*callback)(struct net_device *dev,
4798c2ecf20Sopenharmony_ci						 long context, u16 resp0,
4808c2ecf20Sopenharmony_ci						 u16 status),
4818c2ecf20Sopenharmony_ci				long context)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
4848c2ecf20Sopenharmony_ci	local_info_t *local;
4858c2ecf20Sopenharmony_ci	int issue, ret;
4868c2ecf20Sopenharmony_ci	unsigned long flags;
4878c2ecf20Sopenharmony_ci	struct hostap_cmd_queue *entry;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
4908c2ecf20Sopenharmony_ci	local = iface->local;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN + 2) {
4938c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n",
4948c2ecf20Sopenharmony_ci		       dev->name);
4958c2ecf20Sopenharmony_ci		return -1;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
4998c2ecf20Sopenharmony_ci	if (entry == NULL)
5008c2ecf20Sopenharmony_ci		return -ENOMEM;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	refcount_set(&entry->usecnt, 1);
5038c2ecf20Sopenharmony_ci	entry->type = CMD_CALLBACK;
5048c2ecf20Sopenharmony_ci	entry->cmd = cmd;
5058c2ecf20Sopenharmony_ci	entry->param0 = param0;
5068c2ecf20Sopenharmony_ci	entry->callback = callback;
5078c2ecf20Sopenharmony_ci	entry->context = context;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&local->cmdlock, flags);
5108c2ecf20Sopenharmony_ci	issue = list_empty(&local->cmd_queue);
5118c2ecf20Sopenharmony_ci	if (issue)
5128c2ecf20Sopenharmony_ci		entry->issuing = 1;
5138c2ecf20Sopenharmony_ci	list_add_tail(&entry->list, &local->cmd_queue);
5148c2ecf20Sopenharmony_ci	local->cmd_queue_len++;
5158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&local->cmdlock, flags);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (issue && hfa384x_cmd_issue(dev, entry))
5188c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
5198c2ecf20Sopenharmony_ci	else
5208c2ecf20Sopenharmony_ci		ret = 0;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	hostap_cmd_queue_free(local, entry, ret);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	return ret;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci/**
5298c2ecf20Sopenharmony_ci * __hfa384x_cmd_no_wait - Issue a Prism2 command (private)
5308c2ecf20Sopenharmony_ci * @dev: pointer to net_device
5318c2ecf20Sopenharmony_ci * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
5328c2ecf20Sopenharmony_ci * @param0: value for Param0 register
5338c2ecf20Sopenharmony_ci * @io_debug_num: I/O debug error number
5348c2ecf20Sopenharmony_ci *
5358c2ecf20Sopenharmony_ci * Shared helper function for hfa384x_cmd_wait() and hfa384x_cmd_no_wait().
5368c2ecf20Sopenharmony_ci */
5378c2ecf20Sopenharmony_cistatic int __hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, u16 param0,
5388c2ecf20Sopenharmony_ci				 int io_debug_num)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	int tries;
5418c2ecf20Sopenharmony_ci	u16 reg;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/* wait until busy bit is clear; this should always be clear since the
5448c2ecf20Sopenharmony_ci	 * commands are serialized */
5458c2ecf20Sopenharmony_ci	tries = HFA384X_CMD_BUSY_TIMEOUT;
5468c2ecf20Sopenharmony_ci	while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) {
5478c2ecf20Sopenharmony_ci		tries--;
5488c2ecf20Sopenharmony_ci		udelay(1);
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci	if (tries == 0) {
5518c2ecf20Sopenharmony_ci		reg = HFA384X_INW(HFA384X_CMD_OFF);
5528c2ecf20Sopenharmony_ci		prism2_io_debug_error(dev, io_debug_num);
5538c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: __hfa384x_cmd_no_wait(%d) - timeout - "
5548c2ecf20Sopenharmony_ci		       "reg=0x%04x\n", dev->name, io_debug_num, reg);
5558c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	/* write command */
5598c2ecf20Sopenharmony_ci	HFA384X_OUTW(param0, HFA384X_PARAM0_OFF);
5608c2ecf20Sopenharmony_ci	HFA384X_OUTW(cmd, HFA384X_CMD_OFF);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	return 0;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci/**
5678c2ecf20Sopenharmony_ci * hfa384x_cmd_wait - Issue a Prism2 command and busy wait for completion
5688c2ecf20Sopenharmony_ci * @dev: pointer to net_device
5698c2ecf20Sopenharmony_ci * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
5708c2ecf20Sopenharmony_ci * @param0: value for Param0 register
5718c2ecf20Sopenharmony_ci */
5728c2ecf20Sopenharmony_cistatic int hfa384x_cmd_wait(struct net_device *dev, u16 cmd, u16 param0)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	int res, tries;
5758c2ecf20Sopenharmony_ci	u16 reg;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	res = __hfa384x_cmd_no_wait(dev, cmd, param0, 4);
5788c2ecf20Sopenharmony_ci	if (res)
5798c2ecf20Sopenharmony_ci		return res;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci        /* wait for command completion */
5828c2ecf20Sopenharmony_ci	if ((cmd & HFA384X_CMDCODE_MASK) == HFA384X_CMDCODE_DOWNLOAD)
5838c2ecf20Sopenharmony_ci		tries = HFA384X_DL_COMPL_TIMEOUT;
5848c2ecf20Sopenharmony_ci	else
5858c2ecf20Sopenharmony_ci		tries = HFA384X_CMD_COMPL_TIMEOUT;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci        while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) &&
5888c2ecf20Sopenharmony_ci               tries > 0) {
5898c2ecf20Sopenharmony_ci                tries--;
5908c2ecf20Sopenharmony_ci                udelay(10);
5918c2ecf20Sopenharmony_ci        }
5928c2ecf20Sopenharmony_ci        if (tries == 0) {
5938c2ecf20Sopenharmony_ci                reg = HFA384X_INW(HFA384X_EVSTAT_OFF);
5948c2ecf20Sopenharmony_ci		prism2_io_debug_error(dev, 5);
5958c2ecf20Sopenharmony_ci                printk(KERN_DEBUG "%s: hfa384x_cmd_wait - timeout2 - "
5968c2ecf20Sopenharmony_ci		       "reg=0x%04x\n", dev->name, reg);
5978c2ecf20Sopenharmony_ci                return -ETIMEDOUT;
5988c2ecf20Sopenharmony_ci        }
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci        res = (HFA384X_INW(HFA384X_STATUS_OFF) &
6018c2ecf20Sopenharmony_ci               (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | BIT(9) |
6028c2ecf20Sopenharmony_ci                BIT(8))) >> 8;
6038c2ecf20Sopenharmony_ci#ifndef final_version
6048c2ecf20Sopenharmony_ci	if (res) {
6058c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x\n",
6068c2ecf20Sopenharmony_ci		       dev->name, cmd, res);
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci#endif
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	return res;
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci/**
6178c2ecf20Sopenharmony_ci * hfa384x_cmd_no_wait - Issue a Prism2 command; do not wait for completion
6188c2ecf20Sopenharmony_ci * @dev: pointer to net_device
6198c2ecf20Sopenharmony_ci * @cmd: Prism2 command code (HFA384X_CMD_CODE_*)
6208c2ecf20Sopenharmony_ci * @param0: value for Param0 register
6218c2ecf20Sopenharmony_ci */
6228c2ecf20Sopenharmony_cistatic inline int hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd,
6238c2ecf20Sopenharmony_ci				      u16 param0)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	return __hfa384x_cmd_no_wait(dev, cmd, param0, 6);
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci/**
6308c2ecf20Sopenharmony_ci * prism2_cmd_ev - Prism2 command completion event handler
6318c2ecf20Sopenharmony_ci * @dev: pointer to net_device
6328c2ecf20Sopenharmony_ci *
6338c2ecf20Sopenharmony_ci * Interrupt handler for command completion events. Called by the main
6348c2ecf20Sopenharmony_ci * interrupt handler in hardware IRQ context. Read Resp0 and status registers
6358c2ecf20Sopenharmony_ci * from the hardware and ACK the event. Depending on the issued command type
6368c2ecf20Sopenharmony_ci * either wake up the sleeping process that is waiting for command completion
6378c2ecf20Sopenharmony_ci * or call the callback function. Issue the next command, if one is pending.
6388c2ecf20Sopenharmony_ci */
6398c2ecf20Sopenharmony_cistatic void prism2_cmd_ev(struct net_device *dev)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
6428c2ecf20Sopenharmony_ci	local_info_t *local;
6438c2ecf20Sopenharmony_ci	struct hostap_cmd_queue *entry = NULL;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
6468c2ecf20Sopenharmony_ci	local = iface->local;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	spin_lock(&local->cmdlock);
6498c2ecf20Sopenharmony_ci	if (!list_empty(&local->cmd_queue)) {
6508c2ecf20Sopenharmony_ci		entry = list_entry(local->cmd_queue.next,
6518c2ecf20Sopenharmony_ci				   struct hostap_cmd_queue, list);
6528c2ecf20Sopenharmony_ci		refcount_inc(&entry->usecnt);
6538c2ecf20Sopenharmony_ci		list_del_init(&entry->list);
6548c2ecf20Sopenharmony_ci		local->cmd_queue_len--;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		if (!entry->issued) {
6578c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: Command completion event, but "
6588c2ecf20Sopenharmony_ci			       "cmd not issued\n", dev->name);
6598c2ecf20Sopenharmony_ci			__hostap_cmd_queue_free(local, entry, 1);
6608c2ecf20Sopenharmony_ci			entry = NULL;
6618c2ecf20Sopenharmony_ci		}
6628c2ecf20Sopenharmony_ci	}
6638c2ecf20Sopenharmony_ci	spin_unlock(&local->cmdlock);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	if (!entry) {
6668c2ecf20Sopenharmony_ci		HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
6678c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: Command completion event, but no "
6688c2ecf20Sopenharmony_ci		       "pending commands\n", dev->name);
6698c2ecf20Sopenharmony_ci		return;
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	entry->resp0 = HFA384X_INW(HFA384X_RESP0_OFF);
6738c2ecf20Sopenharmony_ci	entry->res = (HFA384X_INW(HFA384X_STATUS_OFF) &
6748c2ecf20Sopenharmony_ci		      (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) |
6758c2ecf20Sopenharmony_ci		       BIT(9) | BIT(8))) >> 8;
6768c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/* TODO: rest of the CmdEv handling could be moved to tasklet */
6798c2ecf20Sopenharmony_ci	if (entry->type == CMD_SLEEP) {
6808c2ecf20Sopenharmony_ci		entry->type = CMD_COMPLETED;
6818c2ecf20Sopenharmony_ci		wake_up_interruptible(&entry->compl);
6828c2ecf20Sopenharmony_ci	} else if (entry->type == CMD_CALLBACK) {
6838c2ecf20Sopenharmony_ci		if (entry->callback)
6848c2ecf20Sopenharmony_ci			entry->callback(dev, entry->context, entry->resp0,
6858c2ecf20Sopenharmony_ci					entry->res);
6868c2ecf20Sopenharmony_ci	} else {
6878c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: Invalid command completion type %d\n",
6888c2ecf20Sopenharmony_ci		       dev->name, entry->type);
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci	hostap_cmd_queue_free(local, entry, 1);
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	/* issue next command, if pending */
6938c2ecf20Sopenharmony_ci	entry = NULL;
6948c2ecf20Sopenharmony_ci	spin_lock(&local->cmdlock);
6958c2ecf20Sopenharmony_ci	if (!list_empty(&local->cmd_queue)) {
6968c2ecf20Sopenharmony_ci		entry = list_entry(local->cmd_queue.next,
6978c2ecf20Sopenharmony_ci				   struct hostap_cmd_queue, list);
6988c2ecf20Sopenharmony_ci		if (entry->issuing) {
6998c2ecf20Sopenharmony_ci			/* hfa384x_cmd() has already started issuing this
7008c2ecf20Sopenharmony_ci			 * command, so do not start here */
7018c2ecf20Sopenharmony_ci			entry = NULL;
7028c2ecf20Sopenharmony_ci		}
7038c2ecf20Sopenharmony_ci		if (entry)
7048c2ecf20Sopenharmony_ci			refcount_inc(&entry->usecnt);
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci	spin_unlock(&local->cmdlock);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	if (entry) {
7098c2ecf20Sopenharmony_ci		/* issue next command; if command issuing fails, remove the
7108c2ecf20Sopenharmony_ci		 * entry from cmd_queue */
7118c2ecf20Sopenharmony_ci		int res = hfa384x_cmd_issue(dev, entry);
7128c2ecf20Sopenharmony_ci		spin_lock(&local->cmdlock);
7138c2ecf20Sopenharmony_ci		__hostap_cmd_queue_free(local, entry, res);
7148c2ecf20Sopenharmony_ci		spin_unlock(&local->cmdlock);
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_cistatic int hfa384x_wait_offset(struct net_device *dev, u16 o_off)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	int tries = HFA384X_BAP_BUSY_TIMEOUT;
7228c2ecf20Sopenharmony_ci	int res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	while (res && tries > 0) {
7258c2ecf20Sopenharmony_ci		tries--;
7268c2ecf20Sopenharmony_ci		udelay(1);
7278c2ecf20Sopenharmony_ci		res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY;
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci	return res;
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci/* Offset must be even */
7348c2ecf20Sopenharmony_cistatic int hfa384x_setup_bap(struct net_device *dev, u16 bap, u16 id,
7358c2ecf20Sopenharmony_ci			     int offset)
7368c2ecf20Sopenharmony_ci{
7378c2ecf20Sopenharmony_ci	u16 o_off, s_off;
7388c2ecf20Sopenharmony_ci	int ret = 0;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	if (offset % 2 || bap > 1)
7418c2ecf20Sopenharmony_ci		return -EINVAL;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	if (bap == BAP1) {
7448c2ecf20Sopenharmony_ci		o_off = HFA384X_OFFSET1_OFF;
7458c2ecf20Sopenharmony_ci		s_off = HFA384X_SELECT1_OFF;
7468c2ecf20Sopenharmony_ci	} else {
7478c2ecf20Sopenharmony_ci		o_off = HFA384X_OFFSET0_OFF;
7488c2ecf20Sopenharmony_ci		s_off = HFA384X_SELECT0_OFF;
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (hfa384x_wait_offset(dev, o_off)) {
7528c2ecf20Sopenharmony_ci		prism2_io_debug_error(dev, 7);
7538c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout before\n",
7548c2ecf20Sopenharmony_ci		       dev->name);
7558c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
7568c2ecf20Sopenharmony_ci		goto out;
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	HFA384X_OUTW(id, s_off);
7608c2ecf20Sopenharmony_ci	HFA384X_OUTW(offset, o_off);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	if (hfa384x_wait_offset(dev, o_off)) {
7638c2ecf20Sopenharmony_ci		prism2_io_debug_error(dev, 8);
7648c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout after\n",
7658c2ecf20Sopenharmony_ci		       dev->name);
7668c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
7678c2ecf20Sopenharmony_ci		goto out;
7688c2ecf20Sopenharmony_ci	}
7698c2ecf20Sopenharmony_ci#ifndef final_version
7708c2ecf20Sopenharmony_ci	if (HFA384X_INW(o_off) & HFA384X_OFFSET_ERR) {
7718c2ecf20Sopenharmony_ci		prism2_io_debug_error(dev, 9);
7728c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_setup_bap - offset error "
7738c2ecf20Sopenharmony_ci		       "(%d,0x04%x,%d); reg=0x%04x\n",
7748c2ecf20Sopenharmony_ci		       dev->name, bap, id, offset, HFA384X_INW(o_off));
7758c2ecf20Sopenharmony_ci		ret = -EINVAL;
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci#endif
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci out:
7808c2ecf20Sopenharmony_ci	return ret;
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len,
7858c2ecf20Sopenharmony_ci			   int exact_len)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
7888c2ecf20Sopenharmony_ci	local_info_t *local;
7898c2ecf20Sopenharmony_ci	int res, rlen = 0;
7908c2ecf20Sopenharmony_ci	struct hfa384x_rid_hdr rec;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
7938c2ecf20Sopenharmony_ci	local = iface->local;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	if (local->no_pri) {
7968c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: cannot get RID %04x (len=%d) - no PRI "
7978c2ecf20Sopenharmony_ci		       "f/w\n", dev->name, rid, len);
7988c2ecf20Sopenharmony_ci		return -ENOTTY; /* Well.. not really correct, but return
7998c2ecf20Sopenharmony_ci				 * something unique enough.. */
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if ((local->func->card_present && !local->func->card_present(local)) ||
8038c2ecf20Sopenharmony_ci	    local->hw_downloading)
8048c2ecf20Sopenharmony_ci		return -ENODEV;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	res = mutex_lock_interruptible(&local->rid_bap_mtx);
8078c2ecf20Sopenharmony_ci	if (res)
8088c2ecf20Sopenharmony_ci		return res;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS, rid, NULL, NULL);
8118c2ecf20Sopenharmony_ci	if (res) {
8128c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_get_rid: CMDCODE_ACCESS failed "
8138c2ecf20Sopenharmony_ci		       "(res=%d, rid=%04x, len=%d)\n",
8148c2ecf20Sopenharmony_ci		       dev->name, res, rid, len);
8158c2ecf20Sopenharmony_ci		mutex_unlock(&local->rid_bap_mtx);
8168c2ecf20Sopenharmony_ci		return res;
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	spin_lock_bh(&local->baplock);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	res = hfa384x_setup_bap(dev, BAP0, rid, 0);
8228c2ecf20Sopenharmony_ci	if (res)
8238c2ecf20Sopenharmony_ci		goto unlock;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec));
8268c2ecf20Sopenharmony_ci	if (res)
8278c2ecf20Sopenharmony_ci		goto unlock;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	if (le16_to_cpu(rec.len) == 0) {
8308c2ecf20Sopenharmony_ci		/* RID not available */
8318c2ecf20Sopenharmony_ci		res = -ENODATA;
8328c2ecf20Sopenharmony_ci		goto unlock;
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	rlen = (le16_to_cpu(rec.len) - 1) * 2;
8368c2ecf20Sopenharmony_ci	if (exact_len && rlen != len) {
8378c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: "
8388c2ecf20Sopenharmony_ci		       "rid=0x%04x, len=%d (expected %d)\n",
8398c2ecf20Sopenharmony_ci		       dev->name, rid, rlen, len);
8408c2ecf20Sopenharmony_ci		res = -ENODATA;
8418c2ecf20Sopenharmony_ci	}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	res = hfa384x_from_bap(dev, BAP0, buf, len);
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ciunlock:
8468c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->baplock);
8478c2ecf20Sopenharmony_ci	mutex_unlock(&local->rid_bap_mtx);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	if (res) {
8508c2ecf20Sopenharmony_ci		if (res != -ENODATA)
8518c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: hfa384x_get_rid (rid=%04x, "
8528c2ecf20Sopenharmony_ci			       "len=%d) - failed - res=%d\n", dev->name, rid,
8538c2ecf20Sopenharmony_ci			       len, res);
8548c2ecf20Sopenharmony_ci		if (res == -ETIMEDOUT)
8558c2ecf20Sopenharmony_ci			prism2_hw_reset(dev);
8568c2ecf20Sopenharmony_ci		return res;
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	return rlen;
8608c2ecf20Sopenharmony_ci}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_cistatic int hfa384x_set_rid(struct net_device *dev, u16 rid, void *buf, int len)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
8668c2ecf20Sopenharmony_ci	local_info_t *local;
8678c2ecf20Sopenharmony_ci	struct hfa384x_rid_hdr rec;
8688c2ecf20Sopenharmony_ci	int res;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
8718c2ecf20Sopenharmony_ci	local = iface->local;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	if (local->no_pri) {
8748c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: cannot set RID %04x (len=%d) - no PRI "
8758c2ecf20Sopenharmony_ci		       "f/w\n", dev->name, rid, len);
8768c2ecf20Sopenharmony_ci		return -ENOTTY; /* Well.. not really correct, but return
8778c2ecf20Sopenharmony_ci				 * something unique enough.. */
8788c2ecf20Sopenharmony_ci	}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	if ((local->func->card_present && !local->func->card_present(local)) ||
8818c2ecf20Sopenharmony_ci	    local->hw_downloading)
8828c2ecf20Sopenharmony_ci		return -ENODEV;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	rec.rid = cpu_to_le16(rid);
8858c2ecf20Sopenharmony_ci	/* RID len in words and +1 for rec.rid */
8868c2ecf20Sopenharmony_ci	rec.len = cpu_to_le16(len / 2 + len % 2 + 1);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	res = mutex_lock_interruptible(&local->rid_bap_mtx);
8898c2ecf20Sopenharmony_ci	if (res)
8908c2ecf20Sopenharmony_ci		return res;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	spin_lock_bh(&local->baplock);
8938c2ecf20Sopenharmony_ci	res = hfa384x_setup_bap(dev, BAP0, rid, 0);
8948c2ecf20Sopenharmony_ci	if (!res)
8958c2ecf20Sopenharmony_ci		res = hfa384x_to_bap(dev, BAP0, &rec, sizeof(rec));
8968c2ecf20Sopenharmony_ci	if (!res)
8978c2ecf20Sopenharmony_ci		res = hfa384x_to_bap(dev, BAP0, buf, len);
8988c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->baplock);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	if (res) {
9018c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_set_rid (rid=%04x, len=%d) - "
9028c2ecf20Sopenharmony_ci		       "failed - res=%d\n", dev->name, rid, len, res);
9038c2ecf20Sopenharmony_ci		mutex_unlock(&local->rid_bap_mtx);
9048c2ecf20Sopenharmony_ci		return res;
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS_WRITE, rid, NULL, NULL);
9088c2ecf20Sopenharmony_ci	mutex_unlock(&local->rid_bap_mtx);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	if (res) {
9118c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hfa384x_set_rid: CMDCODE_ACCESS_WRITE "
9128c2ecf20Sopenharmony_ci		       "failed (res=%d, rid=%04x, len=%d)\n",
9138c2ecf20Sopenharmony_ci		       dev->name, res, rid, len);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		if (res == -ETIMEDOUT)
9168c2ecf20Sopenharmony_ci			prism2_hw_reset(dev);
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	return res;
9208c2ecf20Sopenharmony_ci}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_cistatic void hfa384x_disable_interrupts(struct net_device *dev)
9248c2ecf20Sopenharmony_ci{
9258c2ecf20Sopenharmony_ci	/* disable interrupts and clear event status */
9268c2ecf20Sopenharmony_ci	HFA384X_OUTW(0, HFA384X_INTEN_OFF);
9278c2ecf20Sopenharmony_ci	HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
9288c2ecf20Sopenharmony_ci}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_cistatic void hfa384x_enable_interrupts(struct net_device *dev)
9328c2ecf20Sopenharmony_ci{
9338c2ecf20Sopenharmony_ci	/* ack pending events and enable interrupts from selected events */
9348c2ecf20Sopenharmony_ci	HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
9358c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF);
9368c2ecf20Sopenharmony_ci}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_cistatic void hfa384x_events_no_bap0(struct net_device *dev)
9408c2ecf20Sopenharmony_ci{
9418c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_EVENT_MASK & ~HFA384X_BAP0_EVENTS,
9428c2ecf20Sopenharmony_ci		     HFA384X_INTEN_OFF);
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_cistatic void hfa384x_events_all(struct net_device *dev)
9478c2ecf20Sopenharmony_ci{
9488c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF);
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_cistatic void hfa384x_events_only_cmd(struct net_device *dev)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_INTEN_OFF);
9558c2ecf20Sopenharmony_ci}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_cistatic u16 hfa384x_allocate_fid(struct net_device *dev, int len)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	u16 fid;
9618c2ecf20Sopenharmony_ci	unsigned long delay;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	/* FIX: this could be replace with hfa384x_cmd() if the Alloc event
9648c2ecf20Sopenharmony_ci	 * below would be handled like CmdCompl event (sleep here, wake up from
9658c2ecf20Sopenharmony_ci	 * interrupt handler */
9668c2ecf20Sopenharmony_ci	if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_ALLOC, len)) {
9678c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: cannot allocate fid, len=%d\n",
9688c2ecf20Sopenharmony_ci		       dev->name, len);
9698c2ecf20Sopenharmony_ci		return 0xffff;
9708c2ecf20Sopenharmony_ci	}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	delay = jiffies + HFA384X_ALLOC_COMPL_TIMEOUT;
9738c2ecf20Sopenharmony_ci	while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC) &&
9748c2ecf20Sopenharmony_ci	       time_before(jiffies, delay))
9758c2ecf20Sopenharmony_ci		yield();
9768c2ecf20Sopenharmony_ci	if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC)) {
9778c2ecf20Sopenharmony_ci		printk("%s: fid allocate, len=%d - timeout\n", dev->name, len);
9788c2ecf20Sopenharmony_ci		return 0xffff;
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	fid = HFA384X_INW(HFA384X_ALLOCFID_OFF);
9828c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	return fid;
9858c2ecf20Sopenharmony_ci}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_cistatic int prism2_reset_port(struct net_device *dev)
9898c2ecf20Sopenharmony_ci{
9908c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
9918c2ecf20Sopenharmony_ci	local_info_t *local;
9928c2ecf20Sopenharmony_ci	int res;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
9958c2ecf20Sopenharmony_ci	local = iface->local;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	if (!local->dev_enabled)
9988c2ecf20Sopenharmony_ci		return 0;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	res = hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0,
10018c2ecf20Sopenharmony_ci			  NULL, NULL);
10028c2ecf20Sopenharmony_ci	if (res)
10038c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: reset port failed to disable port\n",
10048c2ecf20Sopenharmony_ci		       dev->name);
10058c2ecf20Sopenharmony_ci	else {
10068c2ecf20Sopenharmony_ci		res = hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0,
10078c2ecf20Sopenharmony_ci				  NULL, NULL);
10088c2ecf20Sopenharmony_ci		if (res)
10098c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: reset port failed to enable "
10108c2ecf20Sopenharmony_ci			       "port\n", dev->name);
10118c2ecf20Sopenharmony_ci	}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	/* It looks like at least some STA firmware versions reset
10148c2ecf20Sopenharmony_ci	 * fragmentation threshold back to 2346 after enable command. Restore
10158c2ecf20Sopenharmony_ci	 * the configured value, if it differs from this default. */
10168c2ecf20Sopenharmony_ci	if (local->fragm_threshold != 2346 &&
10178c2ecf20Sopenharmony_ci	    hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD,
10188c2ecf20Sopenharmony_ci			    local->fragm_threshold)) {
10198c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: failed to restore fragmentation "
10208c2ecf20Sopenharmony_ci		       "threshold (%d) after Port0 enable\n",
10218c2ecf20Sopenharmony_ci		       dev->name, local->fragm_threshold);
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	/* Some firmwares lose antenna selection settings on reset */
10258c2ecf20Sopenharmony_ci	(void) hostap_set_antsel(local);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	return res;
10288c2ecf20Sopenharmony_ci}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_cistatic int prism2_get_version_info(struct net_device *dev, u16 rid,
10328c2ecf20Sopenharmony_ci				   const char *txt)
10338c2ecf20Sopenharmony_ci{
10348c2ecf20Sopenharmony_ci	struct hfa384x_comp_ident comp;
10358c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
10368c2ecf20Sopenharmony_ci	local_info_t *local;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
10398c2ecf20Sopenharmony_ci	local = iface->local;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	if (local->no_pri) {
10428c2ecf20Sopenharmony_ci		/* PRI f/w not yet available - cannot read RIDs */
10438c2ecf20Sopenharmony_ci		return -1;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci	if (hfa384x_get_rid(dev, rid, &comp, sizeof(comp), 1) < 0) {
10468c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "Could not get RID for component %s\n", txt);
10478c2ecf20Sopenharmony_ci		return -1;
10488c2ecf20Sopenharmony_ci	}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: %s: id=0x%02x v%d.%d.%d\n", dev->name, txt,
10518c2ecf20Sopenharmony_ci	       __le16_to_cpu(comp.id), __le16_to_cpu(comp.major),
10528c2ecf20Sopenharmony_ci	       __le16_to_cpu(comp.minor), __le16_to_cpu(comp.variant));
10538c2ecf20Sopenharmony_ci	return 0;
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_cistatic int prism2_setup_rids(struct net_device *dev)
10588c2ecf20Sopenharmony_ci{
10598c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
10608c2ecf20Sopenharmony_ci	local_info_t *local;
10618c2ecf20Sopenharmony_ci	__le16 tmp;
10628c2ecf20Sopenharmony_ci	int ret = 0;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
10658c2ecf20Sopenharmony_ci	local = iface->local;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	hostap_set_word(dev, HFA384X_RID_TICKTIME, 2000);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	if (!local->fw_ap) {
10708c2ecf20Sopenharmony_ci		u16 tmp1 = hostap_get_porttype(local);
10718c2ecf20Sopenharmony_ci		ret = hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, tmp1);
10728c2ecf20Sopenharmony_ci		if (ret) {
10738c2ecf20Sopenharmony_ci			printk("%s: Port type setting to %d failed\n",
10748c2ecf20Sopenharmony_ci			       dev->name, tmp1);
10758c2ecf20Sopenharmony_ci			goto fail;
10768c2ecf20Sopenharmony_ci		}
10778c2ecf20Sopenharmony_ci	}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	/* Setting SSID to empty string seems to kill the card in Host AP mode
10808c2ecf20Sopenharmony_ci	 */
10818c2ecf20Sopenharmony_ci	if (local->iw_mode != IW_MODE_MASTER || local->essid[0] != '\0') {
10828c2ecf20Sopenharmony_ci		ret = hostap_set_string(dev, HFA384X_RID_CNFOWNSSID,
10838c2ecf20Sopenharmony_ci					local->essid);
10848c2ecf20Sopenharmony_ci		if (ret) {
10858c2ecf20Sopenharmony_ci			printk("%s: AP own SSID setting failed\n", dev->name);
10868c2ecf20Sopenharmony_ci			goto fail;
10878c2ecf20Sopenharmony_ci		}
10888c2ecf20Sopenharmony_ci	}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	ret = hostap_set_word(dev, HFA384X_RID_CNFMAXDATALEN,
10918c2ecf20Sopenharmony_ci			      PRISM2_DATA_MAXLEN);
10928c2ecf20Sopenharmony_ci	if (ret) {
10938c2ecf20Sopenharmony_ci		printk("%s: MAC data length setting to %d failed\n",
10948c2ecf20Sopenharmony_ci		       dev->name, PRISM2_DATA_MAXLEN);
10958c2ecf20Sopenharmony_ci		goto fail;
10968c2ecf20Sopenharmony_ci	}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	if (hfa384x_get_rid(dev, HFA384X_RID_CHANNELLIST, &tmp, 2, 1) < 0) {
10998c2ecf20Sopenharmony_ci		printk("%s: Channel list read failed\n", dev->name);
11008c2ecf20Sopenharmony_ci		ret = -EINVAL;
11018c2ecf20Sopenharmony_ci		goto fail;
11028c2ecf20Sopenharmony_ci	}
11038c2ecf20Sopenharmony_ci	local->channel_mask = le16_to_cpu(tmp);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	if (local->channel < 1 || local->channel > 14 ||
11068c2ecf20Sopenharmony_ci	    !(local->channel_mask & (1 << (local->channel - 1)))) {
11078c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: Channel setting out of range "
11088c2ecf20Sopenharmony_ci		       "(%d)!\n", dev->name, local->channel);
11098c2ecf20Sopenharmony_ci		ret = -EBUSY;
11108c2ecf20Sopenharmony_ci		goto fail;
11118c2ecf20Sopenharmony_ci	}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	ret = hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel);
11148c2ecf20Sopenharmony_ci	if (ret) {
11158c2ecf20Sopenharmony_ci		printk("%s: Channel setting to %d failed\n",
11168c2ecf20Sopenharmony_ci		       dev->name, local->channel);
11178c2ecf20Sopenharmony_ci		goto fail;
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	ret = hostap_set_word(dev, HFA384X_RID_CNFBEACONINT,
11218c2ecf20Sopenharmony_ci			      local->beacon_int);
11228c2ecf20Sopenharmony_ci	if (ret) {
11238c2ecf20Sopenharmony_ci		printk("%s: Beacon interval setting to %d failed\n",
11248c2ecf20Sopenharmony_ci		       dev->name, local->beacon_int);
11258c2ecf20Sopenharmony_ci		/* this may fail with Symbol/Lucent firmware */
11268c2ecf20Sopenharmony_ci		if (ret == -ETIMEDOUT)
11278c2ecf20Sopenharmony_ci			goto fail;
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	ret = hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD,
11318c2ecf20Sopenharmony_ci			      local->dtim_period);
11328c2ecf20Sopenharmony_ci	if (ret) {
11338c2ecf20Sopenharmony_ci		printk("%s: DTIM period setting to %d failed\n",
11348c2ecf20Sopenharmony_ci		       dev->name, local->dtim_period);
11358c2ecf20Sopenharmony_ci		/* this may fail with Symbol/Lucent firmware */
11368c2ecf20Sopenharmony_ci		if (ret == -ETIMEDOUT)
11378c2ecf20Sopenharmony_ci			goto fail;
11388c2ecf20Sopenharmony_ci	}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	ret = hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE,
11418c2ecf20Sopenharmony_ci			      local->is_promisc);
11428c2ecf20Sopenharmony_ci	if (ret)
11438c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: Setting promiscuous mode (%d) failed\n",
11448c2ecf20Sopenharmony_ci		       dev->name, local->is_promisc);
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	if (!local->fw_ap) {
11478c2ecf20Sopenharmony_ci		ret = hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID,
11488c2ecf20Sopenharmony_ci					local->essid);
11498c2ecf20Sopenharmony_ci		if (ret) {
11508c2ecf20Sopenharmony_ci			printk("%s: Desired SSID setting failed\n", dev->name);
11518c2ecf20Sopenharmony_ci			goto fail;
11528c2ecf20Sopenharmony_ci		}
11538c2ecf20Sopenharmony_ci	}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	/* Setup TXRateControl, defaults to allow use of 1, 2, 5.5, and
11568c2ecf20Sopenharmony_ci	 * 11 Mbps in automatic TX rate fallback and 1 and 2 Mbps as basic
11578c2ecf20Sopenharmony_ci	 * rates */
11588c2ecf20Sopenharmony_ci	if (local->tx_rate_control == 0) {
11598c2ecf20Sopenharmony_ci		local->tx_rate_control =
11608c2ecf20Sopenharmony_ci			HFA384X_RATES_1MBPS |
11618c2ecf20Sopenharmony_ci			HFA384X_RATES_2MBPS |
11628c2ecf20Sopenharmony_ci			HFA384X_RATES_5MBPS |
11638c2ecf20Sopenharmony_ci			HFA384X_RATES_11MBPS;
11648c2ecf20Sopenharmony_ci	}
11658c2ecf20Sopenharmony_ci	if (local->basic_rates == 0)
11668c2ecf20Sopenharmony_ci		local->basic_rates = HFA384X_RATES_1MBPS | HFA384X_RATES_2MBPS;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	if (!local->fw_ap) {
11698c2ecf20Sopenharmony_ci		ret = hostap_set_word(dev, HFA384X_RID_TXRATECONTROL,
11708c2ecf20Sopenharmony_ci				      local->tx_rate_control);
11718c2ecf20Sopenharmony_ci		if (ret) {
11728c2ecf20Sopenharmony_ci			printk("%s: TXRateControl setting to %d failed\n",
11738c2ecf20Sopenharmony_ci			       dev->name, local->tx_rate_control);
11748c2ecf20Sopenharmony_ci			goto fail;
11758c2ecf20Sopenharmony_ci		}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci		ret = hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES,
11788c2ecf20Sopenharmony_ci				      local->tx_rate_control);
11798c2ecf20Sopenharmony_ci		if (ret) {
11808c2ecf20Sopenharmony_ci			printk("%s: cnfSupportedRates setting to %d failed\n",
11818c2ecf20Sopenharmony_ci			       dev->name, local->tx_rate_control);
11828c2ecf20Sopenharmony_ci		}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci		ret = hostap_set_word(dev, HFA384X_RID_CNFBASICRATES,
11858c2ecf20Sopenharmony_ci				      local->basic_rates);
11868c2ecf20Sopenharmony_ci		if (ret) {
11878c2ecf20Sopenharmony_ci			printk("%s: cnfBasicRates setting to %d failed\n",
11888c2ecf20Sopenharmony_ci			       dev->name, local->basic_rates);
11898c2ecf20Sopenharmony_ci		}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci		ret = hostap_set_word(dev, HFA384X_RID_CREATEIBSS, 1);
11928c2ecf20Sopenharmony_ci		if (ret) {
11938c2ecf20Sopenharmony_ci			printk("%s: Create IBSS setting to 1 failed\n",
11948c2ecf20Sopenharmony_ci			       dev->name);
11958c2ecf20Sopenharmony_ci		}
11968c2ecf20Sopenharmony_ci	}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	if (local->name_set)
11998c2ecf20Sopenharmony_ci		(void) hostap_set_string(dev, HFA384X_RID_CNFOWNNAME,
12008c2ecf20Sopenharmony_ci					 local->name);
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	if (hostap_set_encryption(local)) {
12038c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: could not configure encryption\n",
12048c2ecf20Sopenharmony_ci		       dev->name);
12058c2ecf20Sopenharmony_ci	}
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	(void) hostap_set_antsel(local);
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	if (hostap_set_roaming(local)) {
12108c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: could not set host roaming\n",
12118c2ecf20Sopenharmony_ci		       dev->name);
12128c2ecf20Sopenharmony_ci	}
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	if (local->sta_fw_ver >= PRISM2_FW_VER(1,6,3) &&
12158c2ecf20Sopenharmony_ci	    hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, local->enh_sec))
12168c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: cnfEnhSecurity setting to 0x%x failed\n",
12178c2ecf20Sopenharmony_ci		       dev->name, local->enh_sec);
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	/* 32-bit tallies were added in STA f/w 0.8.0, but they were apparently
12208c2ecf20Sopenharmony_ci	 * not working correctly (last seven counters report bogus values).
12218c2ecf20Sopenharmony_ci	 * This has been fixed in 0.8.2, so enable 32-bit tallies only
12228c2ecf20Sopenharmony_ci	 * beginning with that firmware version. Another bug fix for 32-bit
12238c2ecf20Sopenharmony_ci	 * tallies in 1.4.0; should 16-bit tallies be used for some other
12248c2ecf20Sopenharmony_ci	 * versions, too? */
12258c2ecf20Sopenharmony_ci	if (local->sta_fw_ver >= PRISM2_FW_VER(0,8,2)) {
12268c2ecf20Sopenharmony_ci		if (hostap_set_word(dev, HFA384X_RID_CNFTHIRTY2TALLY, 1)) {
12278c2ecf20Sopenharmony_ci			printk(KERN_INFO "%s: cnfThirty2Tally setting "
12288c2ecf20Sopenharmony_ci			       "failed\n", dev->name);
12298c2ecf20Sopenharmony_ci			local->tallies32 = 0;
12308c2ecf20Sopenharmony_ci		} else
12318c2ecf20Sopenharmony_ci			local->tallies32 = 1;
12328c2ecf20Sopenharmony_ci	} else
12338c2ecf20Sopenharmony_ci		local->tallies32 = 0;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	hostap_set_auth_algs(local);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	if (hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD,
12388c2ecf20Sopenharmony_ci			    local->fragm_threshold)) {
12398c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: setting FragmentationThreshold to %d "
12408c2ecf20Sopenharmony_ci		       "failed\n", dev->name, local->fragm_threshold);
12418c2ecf20Sopenharmony_ci	}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	if (hostap_set_word(dev, HFA384X_RID_RTSTHRESHOLD,
12448c2ecf20Sopenharmony_ci			    local->rts_threshold)) {
12458c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: setting RTSThreshold to %d failed\n",
12468c2ecf20Sopenharmony_ci		       dev->name, local->rts_threshold);
12478c2ecf20Sopenharmony_ci	}
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	if (local->manual_retry_count >= 0 &&
12508c2ecf20Sopenharmony_ci	    hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT,
12518c2ecf20Sopenharmony_ci			    local->manual_retry_count)) {
12528c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: setting cnfAltRetryCount to %d failed\n",
12538c2ecf20Sopenharmony_ci		       dev->name, local->manual_retry_count);
12548c2ecf20Sopenharmony_ci	}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1) &&
12578c2ecf20Sopenharmony_ci	    hfa384x_get_rid(dev, HFA384X_RID_CNFDBMADJUST, &tmp, 2, 1) == 2) {
12588c2ecf20Sopenharmony_ci		local->rssi_to_dBm = le16_to_cpu(tmp);
12598c2ecf20Sopenharmony_ci	}
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->wpa &&
12628c2ecf20Sopenharmony_ci	    hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1)) {
12638c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: setting ssnHandlingMode to 1 failed\n",
12648c2ecf20Sopenharmony_ci		       dev->name);
12658c2ecf20Sopenharmony_ci	}
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->generic_elem &&
12688c2ecf20Sopenharmony_ci	    hfa384x_set_rid(dev, HFA384X_RID_GENERICELEMENT,
12698c2ecf20Sopenharmony_ci			    local->generic_elem, local->generic_elem_len)) {
12708c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: setting genericElement failed\n",
12718c2ecf20Sopenharmony_ci		       dev->name);
12728c2ecf20Sopenharmony_ci	}
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci fail:
12758c2ecf20Sopenharmony_ci	return ret;
12768c2ecf20Sopenharmony_ci}
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_cistatic int prism2_hw_init(struct net_device *dev, int initial)
12808c2ecf20Sopenharmony_ci{
12818c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
12828c2ecf20Sopenharmony_ci	local_info_t *local;
12838c2ecf20Sopenharmony_ci	int ret, first = 1;
12848c2ecf20Sopenharmony_ci	unsigned long start, delay;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_FLOW, "prism2_hw_init()\n");
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
12898c2ecf20Sopenharmony_ci	local = iface->local;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci init:
12948c2ecf20Sopenharmony_ci	/* initialize HFA 384x */
12958c2ecf20Sopenharmony_ci	ret = hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_INIT, 0);
12968c2ecf20Sopenharmony_ci	if (ret) {
12978c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: first command failed - assuming card "
12988c2ecf20Sopenharmony_ci		       "does not have primary firmware\n", dev_info);
12998c2ecf20Sopenharmony_ci	}
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	if (first && (HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) {
13028c2ecf20Sopenharmony_ci		/* EvStat has Cmd bit set in some cases, so retry once if no
13038c2ecf20Sopenharmony_ci		 * wait was needed */
13048c2ecf20Sopenharmony_ci		HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
13058c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: init command completed too quickly - "
13068c2ecf20Sopenharmony_ci		       "retrying\n", dev->name);
13078c2ecf20Sopenharmony_ci		first = 0;
13088c2ecf20Sopenharmony_ci		goto init;
13098c2ecf20Sopenharmony_ci	}
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	start = jiffies;
13128c2ecf20Sopenharmony_ci	delay = jiffies + HFA384X_INIT_TIMEOUT;
13138c2ecf20Sopenharmony_ci	while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) &&
13148c2ecf20Sopenharmony_ci	       time_before(jiffies, delay))
13158c2ecf20Sopenharmony_ci		yield();
13168c2ecf20Sopenharmony_ci	if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) {
13178c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: assuming no Primary image in "
13188c2ecf20Sopenharmony_ci		       "flash - card initialization not completed\n",
13198c2ecf20Sopenharmony_ci		       dev_info);
13208c2ecf20Sopenharmony_ci		local->no_pri = 1;
13218c2ecf20Sopenharmony_ci#ifdef PRISM2_DOWNLOAD_SUPPORT
13228c2ecf20Sopenharmony_ci			if (local->sram_type == -1)
13238c2ecf20Sopenharmony_ci				local->sram_type = prism2_get_ram_size(local);
13248c2ecf20Sopenharmony_ci#endif /* PRISM2_DOWNLOAD_SUPPORT */
13258c2ecf20Sopenharmony_ci		return 1;
13268c2ecf20Sopenharmony_ci	}
13278c2ecf20Sopenharmony_ci	local->no_pri = 0;
13288c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "prism2_hw_init: initialized in %lu ms\n",
13298c2ecf20Sopenharmony_ci	       (jiffies - start) * 1000 / HZ);
13308c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
13318c2ecf20Sopenharmony_ci	return 0;
13328c2ecf20Sopenharmony_ci}
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_cistatic int prism2_hw_init2(struct net_device *dev, int initial)
13368c2ecf20Sopenharmony_ci{
13378c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
13388c2ecf20Sopenharmony_ci	local_info_t *local;
13398c2ecf20Sopenharmony_ci	int i;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
13428c2ecf20Sopenharmony_ci	local = iface->local;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci#ifdef PRISM2_DOWNLOAD_SUPPORT
13458c2ecf20Sopenharmony_ci	kfree(local->pda);
13468c2ecf20Sopenharmony_ci	if (local->no_pri)
13478c2ecf20Sopenharmony_ci		local->pda = NULL;
13488c2ecf20Sopenharmony_ci	else
13498c2ecf20Sopenharmony_ci		local->pda = prism2_read_pda(dev);
13508c2ecf20Sopenharmony_ci#endif /* PRISM2_DOWNLOAD_SUPPORT */
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	hfa384x_disable_interrupts(dev);
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci#ifndef final_version
13558c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_MAGIC, HFA384X_SWSUPPORT0_OFF);
13568c2ecf20Sopenharmony_ci	if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) {
13578c2ecf20Sopenharmony_ci		printk("SWSUPPORT0 write/read failed: %04X != %04X\n",
13588c2ecf20Sopenharmony_ci		       HFA384X_INW(HFA384X_SWSUPPORT0_OFF), HFA384X_MAGIC);
13598c2ecf20Sopenharmony_ci		goto failed;
13608c2ecf20Sopenharmony_ci	}
13618c2ecf20Sopenharmony_ci#endif
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	if (initial || local->pri_only) {
13648c2ecf20Sopenharmony_ci		hfa384x_events_only_cmd(dev);
13658c2ecf20Sopenharmony_ci		/* get card version information */
13668c2ecf20Sopenharmony_ci		if (prism2_get_version_info(dev, HFA384X_RID_NICID, "NIC") ||
13678c2ecf20Sopenharmony_ci		    prism2_get_version_info(dev, HFA384X_RID_PRIID, "PRI")) {
13688c2ecf20Sopenharmony_ci			hfa384x_disable_interrupts(dev);
13698c2ecf20Sopenharmony_ci			goto failed;
13708c2ecf20Sopenharmony_ci		}
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci		if (prism2_get_version_info(dev, HFA384X_RID_STAID, "STA")) {
13738c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: Failed to read STA f/w version "
13748c2ecf20Sopenharmony_ci			       "- only Primary f/w present\n", dev->name);
13758c2ecf20Sopenharmony_ci			local->pri_only = 1;
13768c2ecf20Sopenharmony_ci			return 0;
13778c2ecf20Sopenharmony_ci		}
13788c2ecf20Sopenharmony_ci		local->pri_only = 0;
13798c2ecf20Sopenharmony_ci		hfa384x_disable_interrupts(dev);
13808c2ecf20Sopenharmony_ci	}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	/* FIX: could convert allocate_fid to use sleeping CmdCompl wait and
13838c2ecf20Sopenharmony_ci	 * enable interrupts before this. This would also require some sort of
13848c2ecf20Sopenharmony_ci	 * sleeping AllocEv waiting */
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	/* allocate TX FIDs */
13878c2ecf20Sopenharmony_ci	local->txfid_len = PRISM2_TXFID_LEN;
13888c2ecf20Sopenharmony_ci	for (i = 0; i < PRISM2_TXFID_COUNT; i++) {
13898c2ecf20Sopenharmony_ci		local->txfid[i] = hfa384x_allocate_fid(dev, local->txfid_len);
13908c2ecf20Sopenharmony_ci		if (local->txfid[i] == 0xffff && local->txfid_len > 1600) {
13918c2ecf20Sopenharmony_ci			local->txfid[i] = hfa384x_allocate_fid(dev, 1600);
13928c2ecf20Sopenharmony_ci			if (local->txfid[i] != 0xffff) {
13938c2ecf20Sopenharmony_ci				printk(KERN_DEBUG "%s: Using shorter TX FID "
13948c2ecf20Sopenharmony_ci				       "(1600 bytes)\n", dev->name);
13958c2ecf20Sopenharmony_ci				local->txfid_len = 1600;
13968c2ecf20Sopenharmony_ci			}
13978c2ecf20Sopenharmony_ci		}
13988c2ecf20Sopenharmony_ci		if (local->txfid[i] == 0xffff)
13998c2ecf20Sopenharmony_ci			goto failed;
14008c2ecf20Sopenharmony_ci		local->intransmitfid[i] = PRISM2_TXFID_EMPTY;
14018c2ecf20Sopenharmony_ci	}
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	hfa384x_events_only_cmd(dev);
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	if (initial) {
14068c2ecf20Sopenharmony_ci		struct list_head *ptr;
14078c2ecf20Sopenharmony_ci		prism2_check_sta_fw_version(local);
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci		if (hfa384x_get_rid(dev, HFA384X_RID_CNFOWNMACADDR,
14108c2ecf20Sopenharmony_ci				    dev->dev_addr, 6, 1) < 0) {
14118c2ecf20Sopenharmony_ci			printk("%s: could not get own MAC address\n",
14128c2ecf20Sopenharmony_ci			       dev->name);
14138c2ecf20Sopenharmony_ci		}
14148c2ecf20Sopenharmony_ci		list_for_each(ptr, &local->hostap_interfaces) {
14158c2ecf20Sopenharmony_ci			iface = list_entry(ptr, struct hostap_interface, list);
14168c2ecf20Sopenharmony_ci			eth_hw_addr_inherit(iface->dev, dev);
14178c2ecf20Sopenharmony_ci		}
14188c2ecf20Sopenharmony_ci	} else if (local->fw_ap)
14198c2ecf20Sopenharmony_ci		prism2_check_sta_fw_version(local);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	prism2_setup_rids(dev);
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	/* MAC is now configured, but port 0 is not yet enabled */
14248c2ecf20Sopenharmony_ci	return 0;
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci failed:
14278c2ecf20Sopenharmony_ci	if (!local->no_pri)
14288c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: Initialization failed\n", dev_info);
14298c2ecf20Sopenharmony_ci	return 1;
14308c2ecf20Sopenharmony_ci}
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_cistatic int prism2_hw_enable(struct net_device *dev, int initial)
14348c2ecf20Sopenharmony_ci{
14358c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
14368c2ecf20Sopenharmony_ci	local_info_t *local;
14378c2ecf20Sopenharmony_ci	int was_resetting;
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
14408c2ecf20Sopenharmony_ci	local = iface->local;
14418c2ecf20Sopenharmony_ci	was_resetting = local->hw_resetting;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	if (hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, NULL)) {
14448c2ecf20Sopenharmony_ci		printk("%s: MAC port 0 enabling failed\n", dev->name);
14458c2ecf20Sopenharmony_ci		return 1;
14468c2ecf20Sopenharmony_ci	}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	local->hw_ready = 1;
14498c2ecf20Sopenharmony_ci	local->hw_reset_tries = 0;
14508c2ecf20Sopenharmony_ci	local->hw_resetting = 0;
14518c2ecf20Sopenharmony_ci	hfa384x_enable_interrupts(dev);
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	/* at least D-Link DWL-650 seems to require additional port reset
14548c2ecf20Sopenharmony_ci	 * before it starts acting as an AP, so reset port automatically
14558c2ecf20Sopenharmony_ci	 * here just in case */
14568c2ecf20Sopenharmony_ci	if (initial && prism2_reset_port(dev)) {
14578c2ecf20Sopenharmony_ci		printk("%s: MAC port 0 resetting failed\n", dev->name);
14588c2ecf20Sopenharmony_ci		return 1;
14598c2ecf20Sopenharmony_ci	}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	if (was_resetting && netif_queue_stopped(dev)) {
14628c2ecf20Sopenharmony_ci		/* If hw_reset() was called during pending transmit, netif
14638c2ecf20Sopenharmony_ci		 * queue was stopped. Wake it up now since the wlan card has
14648c2ecf20Sopenharmony_ci		 * been resetted. */
14658c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
14668c2ecf20Sopenharmony_ci	}
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	return 0;
14698c2ecf20Sopenharmony_ci}
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_cistatic int prism2_hw_config(struct net_device *dev, int initial)
14738c2ecf20Sopenharmony_ci{
14748c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
14758c2ecf20Sopenharmony_ci	local_info_t *local;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
14788c2ecf20Sopenharmony_ci	local = iface->local;
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	if (local->hw_downloading)
14818c2ecf20Sopenharmony_ci		return 1;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	if (prism2_hw_init(dev, initial)) {
14848c2ecf20Sopenharmony_ci		return local->no_pri ? 0 : 1;
14858c2ecf20Sopenharmony_ci	}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	if (prism2_hw_init2(dev, initial))
14888c2ecf20Sopenharmony_ci		return 1;
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	/* Enable firmware if secondary image is loaded and at least one of the
14918c2ecf20Sopenharmony_ci	 * netdevices is up. */
14928c2ecf20Sopenharmony_ci	if (!local->pri_only &&
14938c2ecf20Sopenharmony_ci	    (initial == 0 || (initial == 2 && local->num_dev_open > 0))) {
14948c2ecf20Sopenharmony_ci		if (!local->dev_enabled)
14958c2ecf20Sopenharmony_ci			prism2_callback(local, PRISM2_CALLBACK_ENABLE);
14968c2ecf20Sopenharmony_ci		local->dev_enabled = 1;
14978c2ecf20Sopenharmony_ci		return prism2_hw_enable(dev, initial);
14988c2ecf20Sopenharmony_ci	}
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	return 0;
15018c2ecf20Sopenharmony_ci}
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_cistatic void prism2_hw_shutdown(struct net_device *dev, int no_disable)
15058c2ecf20Sopenharmony_ci{
15068c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
15078c2ecf20Sopenharmony_ci	local_info_t *local;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
15108c2ecf20Sopenharmony_ci	local = iface->local;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	/* Allow only command completion events during disable */
15138c2ecf20Sopenharmony_ci	hfa384x_events_only_cmd(dev);
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	local->hw_ready = 0;
15168c2ecf20Sopenharmony_ci	if (local->dev_enabled)
15178c2ecf20Sopenharmony_ci		prism2_callback(local, PRISM2_CALLBACK_DISABLE);
15188c2ecf20Sopenharmony_ci	local->dev_enabled = 0;
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	if (local->func->card_present && !local->func->card_present(local)) {
15218c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: card already removed or not configured "
15228c2ecf20Sopenharmony_ci		       "during shutdown\n", dev->name);
15238c2ecf20Sopenharmony_ci		return;
15248c2ecf20Sopenharmony_ci	}
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	if ((no_disable & HOSTAP_HW_NO_DISABLE) == 0 &&
15278c2ecf20Sopenharmony_ci	    hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, NULL))
15288c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: Shutdown failed\n", dev_info);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	hfa384x_disable_interrupts(dev);
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	if (no_disable & HOSTAP_HW_ENABLE_CMDCOMPL)
15338c2ecf20Sopenharmony_ci		hfa384x_events_only_cmd(dev);
15348c2ecf20Sopenharmony_ci	else
15358c2ecf20Sopenharmony_ci		prism2_clear_cmd_queue(local);
15368c2ecf20Sopenharmony_ci}
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_cistatic void prism2_hw_reset(struct net_device *dev)
15408c2ecf20Sopenharmony_ci{
15418c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
15428c2ecf20Sopenharmony_ci	local_info_t *local;
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci#if 0
15458c2ecf20Sopenharmony_ci	static long last_reset = 0;
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci	/* do not reset card more than once per second to avoid ending up in a
15488c2ecf20Sopenharmony_ci	 * busy loop resetting the card */
15498c2ecf20Sopenharmony_ci	if (time_before_eq(jiffies, last_reset + HZ))
15508c2ecf20Sopenharmony_ci		return;
15518c2ecf20Sopenharmony_ci	last_reset = jiffies;
15528c2ecf20Sopenharmony_ci#endif
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
15558c2ecf20Sopenharmony_ci	local = iface->local;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	if (local->hw_downloading)
15588c2ecf20Sopenharmony_ci		return;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	if (local->hw_resetting) {
15618c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: %s: already resetting card - "
15628c2ecf20Sopenharmony_ci		       "ignoring reset request\n", dev_info, dev->name);
15638c2ecf20Sopenharmony_ci		return;
15648c2ecf20Sopenharmony_ci	}
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	local->hw_reset_tries++;
15678c2ecf20Sopenharmony_ci	if (local->hw_reset_tries > 10) {
15688c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: too many reset tries, skipping\n",
15698c2ecf20Sopenharmony_ci		       dev->name);
15708c2ecf20Sopenharmony_ci		return;
15718c2ecf20Sopenharmony_ci	}
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	printk(KERN_WARNING "%s: %s: resetting card\n", dev_info, dev->name);
15748c2ecf20Sopenharmony_ci	hfa384x_disable_interrupts(dev);
15758c2ecf20Sopenharmony_ci	local->hw_resetting = 1;
15768c2ecf20Sopenharmony_ci	if (local->func->cor_sreset) {
15778c2ecf20Sopenharmony_ci		/* Host system seems to hang in some cases with high traffic
15788c2ecf20Sopenharmony_ci		 * load or shared interrupts during COR sreset. Disable shared
15798c2ecf20Sopenharmony_ci		 * interrupts during reset to avoid these crashes. COS sreset
15808c2ecf20Sopenharmony_ci		 * takes quite a long time, so it is unfortunate that this
15818c2ecf20Sopenharmony_ci		 * seems to be needed. Anyway, I do not know of any better way
15828c2ecf20Sopenharmony_ci		 * of avoiding the crash. */
15838c2ecf20Sopenharmony_ci		disable_irq(dev->irq);
15848c2ecf20Sopenharmony_ci		local->func->cor_sreset(local);
15858c2ecf20Sopenharmony_ci		enable_irq(dev->irq);
15868c2ecf20Sopenharmony_ci	}
15878c2ecf20Sopenharmony_ci	prism2_hw_shutdown(dev, 1);
15888c2ecf20Sopenharmony_ci	prism2_hw_config(dev, 0);
15898c2ecf20Sopenharmony_ci	local->hw_resetting = 0;
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci#ifdef PRISM2_DOWNLOAD_SUPPORT
15928c2ecf20Sopenharmony_ci	if (local->dl_pri) {
15938c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: persistent download of primary "
15948c2ecf20Sopenharmony_ci		       "firmware\n", dev->name);
15958c2ecf20Sopenharmony_ci		if (prism2_download_genesis(local, local->dl_pri) < 0)
15968c2ecf20Sopenharmony_ci			printk(KERN_WARNING "%s: download (PRI) failed\n",
15978c2ecf20Sopenharmony_ci			       dev->name);
15988c2ecf20Sopenharmony_ci	}
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	if (local->dl_sec) {
16018c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: persistent download of secondary "
16028c2ecf20Sopenharmony_ci		       "firmware\n", dev->name);
16038c2ecf20Sopenharmony_ci		if (prism2_download_volatile(local, local->dl_sec) < 0)
16048c2ecf20Sopenharmony_ci			printk(KERN_WARNING "%s: download (SEC) failed\n",
16058c2ecf20Sopenharmony_ci			       dev->name);
16068c2ecf20Sopenharmony_ci	}
16078c2ecf20Sopenharmony_ci#endif /* PRISM2_DOWNLOAD_SUPPORT */
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	/* TODO: restore beacon TIM bits for STAs that have buffered frames */
16108c2ecf20Sopenharmony_ci}
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_cistatic void prism2_schedule_reset(local_info_t *local)
16148c2ecf20Sopenharmony_ci{
16158c2ecf20Sopenharmony_ci	schedule_work(&local->reset_queue);
16168c2ecf20Sopenharmony_ci}
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci/* Called only as scheduled task after noticing card timeout in interrupt
16208c2ecf20Sopenharmony_ci * context */
16218c2ecf20Sopenharmony_cistatic void handle_reset_queue(struct work_struct *work)
16228c2ecf20Sopenharmony_ci{
16238c2ecf20Sopenharmony_ci	local_info_t *local = container_of(work, local_info_t, reset_queue);
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "%s: scheduled card reset\n", local->dev->name);
16268c2ecf20Sopenharmony_ci	prism2_hw_reset(local->dev);
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	if (netif_queue_stopped(local->dev)) {
16298c2ecf20Sopenharmony_ci		int i;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci		for (i = 0; i < PRISM2_TXFID_COUNT; i++)
16328c2ecf20Sopenharmony_ci			if (local->intransmitfid[i] == PRISM2_TXFID_EMPTY) {
16338c2ecf20Sopenharmony_ci				PDEBUG(DEBUG_EXTRA, "prism2_tx_timeout: "
16348c2ecf20Sopenharmony_ci				       "wake up queue\n");
16358c2ecf20Sopenharmony_ci				netif_wake_queue(local->dev);
16368c2ecf20Sopenharmony_ci				break;
16378c2ecf20Sopenharmony_ci			}
16388c2ecf20Sopenharmony_ci	}
16398c2ecf20Sopenharmony_ci}
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_cistatic int prism2_get_txfid_idx(local_info_t *local)
16438c2ecf20Sopenharmony_ci{
16448c2ecf20Sopenharmony_ci	int idx, end;
16458c2ecf20Sopenharmony_ci	unsigned long flags;
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	spin_lock_irqsave(&local->txfidlock, flags);
16488c2ecf20Sopenharmony_ci	end = idx = local->next_txfid;
16498c2ecf20Sopenharmony_ci	do {
16508c2ecf20Sopenharmony_ci		if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) {
16518c2ecf20Sopenharmony_ci			local->intransmitfid[idx] = PRISM2_TXFID_RESERVED;
16528c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&local->txfidlock, flags);
16538c2ecf20Sopenharmony_ci			return idx;
16548c2ecf20Sopenharmony_ci		}
16558c2ecf20Sopenharmony_ci		idx++;
16568c2ecf20Sopenharmony_ci		if (idx >= PRISM2_TXFID_COUNT)
16578c2ecf20Sopenharmony_ci			idx = 0;
16588c2ecf20Sopenharmony_ci	} while (idx != end);
16598c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&local->txfidlock, flags);
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_EXTRA2, "prism2_get_txfid_idx: no room in txfid buf: "
16628c2ecf20Sopenharmony_ci	       "packet dropped\n");
16638c2ecf20Sopenharmony_ci	local->dev->stats.tx_dropped++;
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	return -1;
16668c2ecf20Sopenharmony_ci}
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci/* Called only from hardware IRQ */
16708c2ecf20Sopenharmony_cistatic void prism2_transmit_cb(struct net_device *dev, long context,
16718c2ecf20Sopenharmony_ci			       u16 resp0, u16 res)
16728c2ecf20Sopenharmony_ci{
16738c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
16748c2ecf20Sopenharmony_ci	local_info_t *local;
16758c2ecf20Sopenharmony_ci	int idx = (int) context;
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
16788c2ecf20Sopenharmony_ci	local = iface->local;
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	if (res) {
16818c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: prism2_transmit_cb - res=0x%02x\n",
16828c2ecf20Sopenharmony_ci		       dev->name, res);
16838c2ecf20Sopenharmony_ci		return;
16848c2ecf20Sopenharmony_ci	}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	if (idx < 0 || idx >= PRISM2_TXFID_COUNT) {
16878c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: prism2_transmit_cb called with invalid "
16888c2ecf20Sopenharmony_ci		       "idx=%d\n", dev->name, idx);
16898c2ecf20Sopenharmony_ci		return;
16908c2ecf20Sopenharmony_ci	}
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci	if (!test_and_clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) {
16938c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: driver bug: prism2_transmit_cb called "
16948c2ecf20Sopenharmony_ci		       "with no pending transmit\n", dev->name);
16958c2ecf20Sopenharmony_ci	}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	if (netif_queue_stopped(dev)) {
16988c2ecf20Sopenharmony_ci		/* ready for next TX, so wake up queue that was stopped in
16998c2ecf20Sopenharmony_ci		 * prism2_transmit() */
17008c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
17018c2ecf20Sopenharmony_ci	}
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	spin_lock(&local->txfidlock);
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	/* With reclaim, Resp0 contains new txfid for transmit; the old txfid
17068c2ecf20Sopenharmony_ci	 * will be automatically allocated for the next TX frame */
17078c2ecf20Sopenharmony_ci	local->intransmitfid[idx] = resp0;
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_FID, "%s: prism2_transmit_cb: txfid[%d]=0x%04x, "
17108c2ecf20Sopenharmony_ci	       "resp0=0x%04x, transmit_txfid=0x%04x\n",
17118c2ecf20Sopenharmony_ci	       dev->name, idx, local->txfid[idx],
17128c2ecf20Sopenharmony_ci	       resp0, local->intransmitfid[local->next_txfid]);
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	idx++;
17158c2ecf20Sopenharmony_ci	if (idx >= PRISM2_TXFID_COUNT)
17168c2ecf20Sopenharmony_ci		idx = 0;
17178c2ecf20Sopenharmony_ci	local->next_txfid = idx;
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	/* check if all TX buffers are occupied */
17208c2ecf20Sopenharmony_ci	do {
17218c2ecf20Sopenharmony_ci		if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) {
17228c2ecf20Sopenharmony_ci			spin_unlock(&local->txfidlock);
17238c2ecf20Sopenharmony_ci			return;
17248c2ecf20Sopenharmony_ci		}
17258c2ecf20Sopenharmony_ci		idx++;
17268c2ecf20Sopenharmony_ci		if (idx >= PRISM2_TXFID_COUNT)
17278c2ecf20Sopenharmony_ci			idx = 0;
17288c2ecf20Sopenharmony_ci	} while (idx != local->next_txfid);
17298c2ecf20Sopenharmony_ci	spin_unlock(&local->txfidlock);
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	/* no empty TX buffers, stop queue */
17328c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
17338c2ecf20Sopenharmony_ci}
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci/* Called only from software IRQ if PCI bus master is not used (with bus master
17378c2ecf20Sopenharmony_ci * this can be called both from software and hardware IRQ) */
17388c2ecf20Sopenharmony_cistatic int prism2_transmit(struct net_device *dev, int idx)
17398c2ecf20Sopenharmony_ci{
17408c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
17418c2ecf20Sopenharmony_ci	local_info_t *local;
17428c2ecf20Sopenharmony_ci	int res;
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
17458c2ecf20Sopenharmony_ci	local = iface->local;
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci	/* The driver tries to stop netif queue so that there would not be
17488c2ecf20Sopenharmony_ci	 * more than one attempt to transmit frames going on; check that this
17498c2ecf20Sopenharmony_ci	 * is really the case */
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	if (test_and_set_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) {
17528c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: driver bug - prism2_transmit() called "
17538c2ecf20Sopenharmony_ci		       "when previous TX was pending\n", dev->name);
17548c2ecf20Sopenharmony_ci		return -1;
17558c2ecf20Sopenharmony_ci	}
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci	/* stop the queue for the time that transmit is pending */
17588c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci	/* transmit packet */
17618c2ecf20Sopenharmony_ci	res = hfa384x_cmd_callback(
17628c2ecf20Sopenharmony_ci		dev,
17638c2ecf20Sopenharmony_ci		HFA384X_CMDCODE_TRANSMIT | HFA384X_CMD_TX_RECLAIM,
17648c2ecf20Sopenharmony_ci		local->txfid[idx],
17658c2ecf20Sopenharmony_ci		prism2_transmit_cb, (long) idx);
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	if (res) {
17688c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: prism2_transmit: CMDCODE_TRANSMIT "
17698c2ecf20Sopenharmony_ci		       "failed (res=%d)\n", dev->name, res);
17708c2ecf20Sopenharmony_ci		dev->stats.tx_dropped++;
17718c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
17728c2ecf20Sopenharmony_ci		return -1;
17738c2ecf20Sopenharmony_ci	}
17748c2ecf20Sopenharmony_ci	netif_trans_update(dev);
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	/* Since we did not wait for command completion, the card continues
17778c2ecf20Sopenharmony_ci	 * to process on the background and we will finish handling when
17788c2ecf20Sopenharmony_ci	 * command completion event is handled (prism2_cmd_ev() function) */
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	return 0;
17818c2ecf20Sopenharmony_ci}
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci/* Send IEEE 802.11 frame (convert the header into Prism2 TX descriptor and
17858c2ecf20Sopenharmony_ci * send the payload with this descriptor) */
17868c2ecf20Sopenharmony_ci/* Called only from software IRQ */
17878c2ecf20Sopenharmony_cistatic int prism2_tx_80211(struct sk_buff *skb, struct net_device *dev)
17888c2ecf20Sopenharmony_ci{
17898c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
17908c2ecf20Sopenharmony_ci	local_info_t *local;
17918c2ecf20Sopenharmony_ci	struct hfa384x_tx_frame txdesc;
17928c2ecf20Sopenharmony_ci	struct hostap_skb_tx_data *meta;
17938c2ecf20Sopenharmony_ci	int hdr_len, data_len, idx, res, ret = -1;
17948c2ecf20Sopenharmony_ci	u16 tx_control;
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
17978c2ecf20Sopenharmony_ci	local = iface->local;
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	meta = (struct hostap_skb_tx_data *) skb->cb;
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	prism2_callback(local, PRISM2_CALLBACK_TX_START);
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci	if ((local->func->card_present && !local->func->card_present(local)) ||
18048c2ecf20Sopenharmony_ci	    !local->hw_ready || local->hw_downloading || local->pri_only) {
18058c2ecf20Sopenharmony_ci		if (net_ratelimit()) {
18068c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: prism2_tx_80211: hw not ready -"
18078c2ecf20Sopenharmony_ci			       " skipping\n", dev->name);
18088c2ecf20Sopenharmony_ci		}
18098c2ecf20Sopenharmony_ci		goto fail;
18108c2ecf20Sopenharmony_ci	}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	memset(&txdesc, 0, sizeof(txdesc));
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci	/* skb->data starts with txdesc->frame_control */
18158c2ecf20Sopenharmony_ci	hdr_len = 24;
18168c2ecf20Sopenharmony_ci	skb_copy_from_linear_data(skb, &txdesc.frame_control, hdr_len);
18178c2ecf20Sopenharmony_ci	if (ieee80211_is_data(txdesc.frame_control) &&
18188c2ecf20Sopenharmony_ci	    ieee80211_has_a4(txdesc.frame_control) &&
18198c2ecf20Sopenharmony_ci	    skb->len >= 30) {
18208c2ecf20Sopenharmony_ci		/* Addr4 */
18218c2ecf20Sopenharmony_ci		skb_copy_from_linear_data_offset(skb, hdr_len, txdesc.addr4,
18228c2ecf20Sopenharmony_ci						 ETH_ALEN);
18238c2ecf20Sopenharmony_ci		hdr_len += ETH_ALEN;
18248c2ecf20Sopenharmony_ci	}
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	tx_control = local->tx_control;
18278c2ecf20Sopenharmony_ci	if (meta->tx_cb_idx) {
18288c2ecf20Sopenharmony_ci		tx_control |= HFA384X_TX_CTRL_TX_OK;
18298c2ecf20Sopenharmony_ci		txdesc.sw_support = cpu_to_le32(meta->tx_cb_idx);
18308c2ecf20Sopenharmony_ci	}
18318c2ecf20Sopenharmony_ci	txdesc.tx_control = cpu_to_le16(tx_control);
18328c2ecf20Sopenharmony_ci	txdesc.tx_rate = meta->rate;
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci	data_len = skb->len - hdr_len;
18358c2ecf20Sopenharmony_ci	txdesc.data_len = cpu_to_le16(data_len);
18368c2ecf20Sopenharmony_ci	txdesc.len = cpu_to_be16(data_len);
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	idx = prism2_get_txfid_idx(local);
18398c2ecf20Sopenharmony_ci	if (idx < 0)
18408c2ecf20Sopenharmony_ci		goto fail;
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	if (local->frame_dump & PRISM2_DUMP_TX_HDR)
18438c2ecf20Sopenharmony_ci		hostap_dump_tx_header(dev->name, &txdesc);
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	spin_lock(&local->baplock);
18468c2ecf20Sopenharmony_ci	res = hfa384x_setup_bap(dev, BAP0, local->txfid[idx], 0);
18478c2ecf20Sopenharmony_ci
18488c2ecf20Sopenharmony_ci	if (!res)
18498c2ecf20Sopenharmony_ci		res = hfa384x_to_bap(dev, BAP0, &txdesc, sizeof(txdesc));
18508c2ecf20Sopenharmony_ci	if (!res)
18518c2ecf20Sopenharmony_ci		res = hfa384x_to_bap(dev, BAP0, skb->data + hdr_len,
18528c2ecf20Sopenharmony_ci				     skb->len - hdr_len);
18538c2ecf20Sopenharmony_ci	spin_unlock(&local->baplock);
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	if (!res)
18568c2ecf20Sopenharmony_ci		res = prism2_transmit(dev, idx);
18578c2ecf20Sopenharmony_ci	if (res) {
18588c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: prism2_tx_80211 - to BAP0 failed\n",
18598c2ecf20Sopenharmony_ci		       dev->name);
18608c2ecf20Sopenharmony_ci		local->intransmitfid[idx] = PRISM2_TXFID_EMPTY;
18618c2ecf20Sopenharmony_ci		schedule_work(&local->reset_queue);
18628c2ecf20Sopenharmony_ci		goto fail;
18638c2ecf20Sopenharmony_ci	}
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	ret = 0;
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_cifail:
18688c2ecf20Sopenharmony_ci	prism2_callback(local, PRISM2_CALLBACK_TX_END);
18698c2ecf20Sopenharmony_ci	return ret;
18708c2ecf20Sopenharmony_ci}
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci/* Some SMP systems have reported number of odd errors with hostap_pci. fid
18748c2ecf20Sopenharmony_ci * register has changed values between consecutive reads for an unknown reason.
18758c2ecf20Sopenharmony_ci * This should really not happen, so more debugging is needed. This test
18768c2ecf20Sopenharmony_ci * version is a bit slower, but it will detect most of such register changes
18778c2ecf20Sopenharmony_ci * and will try to get the correct fid eventually. */
18788c2ecf20Sopenharmony_ci#define EXTRA_FID_READ_TESTS
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_cistatic u16 prism2_read_fid_reg(struct net_device *dev, u16 reg)
18818c2ecf20Sopenharmony_ci{
18828c2ecf20Sopenharmony_ci#ifdef EXTRA_FID_READ_TESTS
18838c2ecf20Sopenharmony_ci	u16 val, val2, val3;
18848c2ecf20Sopenharmony_ci	int i;
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
18878c2ecf20Sopenharmony_ci		val = HFA384X_INW(reg);
18888c2ecf20Sopenharmony_ci		val2 = HFA384X_INW(reg);
18898c2ecf20Sopenharmony_ci		val3 = HFA384X_INW(reg);
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci		if (val == val2 && val == val3)
18928c2ecf20Sopenharmony_ci			return val;
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: detected fid change (try=%d, reg=%04x):"
18958c2ecf20Sopenharmony_ci		       " %04x %04x %04x\n",
18968c2ecf20Sopenharmony_ci		       dev->name, i, reg, val, val2, val3);
18978c2ecf20Sopenharmony_ci		if ((val == val2 || val == val3) && val != 0)
18988c2ecf20Sopenharmony_ci			return val;
18998c2ecf20Sopenharmony_ci		if (val2 == val3 && val2 != 0)
19008c2ecf20Sopenharmony_ci			return val2;
19018c2ecf20Sopenharmony_ci	}
19028c2ecf20Sopenharmony_ci	printk(KERN_WARNING "%s: Uhhuh.. could not read good fid from reg "
19038c2ecf20Sopenharmony_ci	       "%04x (%04x %04x %04x)\n", dev->name, reg, val, val2, val3);
19048c2ecf20Sopenharmony_ci	return val;
19058c2ecf20Sopenharmony_ci#else /* EXTRA_FID_READ_TESTS */
19068c2ecf20Sopenharmony_ci	return HFA384X_INW(reg);
19078c2ecf20Sopenharmony_ci#endif /* EXTRA_FID_READ_TESTS */
19088c2ecf20Sopenharmony_ci}
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
19128c2ecf20Sopenharmony_cistatic void prism2_rx(local_info_t *local)
19138c2ecf20Sopenharmony_ci{
19148c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
19158c2ecf20Sopenharmony_ci	int res, rx_pending = 0;
19168c2ecf20Sopenharmony_ci	u16 len, hdr_len, rxfid, status, macport;
19178c2ecf20Sopenharmony_ci	struct hfa384x_rx_frame rxdesc;
19188c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	prism2_callback(local, PRISM2_CALLBACK_RX_START);
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	rxfid = prism2_read_fid_reg(dev, HFA384X_RXFID_OFF);
19238c2ecf20Sopenharmony_ci#ifndef final_version
19248c2ecf20Sopenharmony_ci	if (rxfid == 0) {
19258c2ecf20Sopenharmony_ci		rxfid = HFA384X_INW(HFA384X_RXFID_OFF);
19268c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "prism2_rx: rxfid=0 (next 0x%04x)\n",
19278c2ecf20Sopenharmony_ci		       rxfid);
19288c2ecf20Sopenharmony_ci		if (rxfid == 0) {
19298c2ecf20Sopenharmony_ci			schedule_work(&local->reset_queue);
19308c2ecf20Sopenharmony_ci			goto rx_dropped;
19318c2ecf20Sopenharmony_ci		}
19328c2ecf20Sopenharmony_ci		/* try to continue with the new rxfid value */
19338c2ecf20Sopenharmony_ci	}
19348c2ecf20Sopenharmony_ci#endif
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	spin_lock(&local->baplock);
19378c2ecf20Sopenharmony_ci	res = hfa384x_setup_bap(dev, BAP0, rxfid, 0);
19388c2ecf20Sopenharmony_ci	if (!res)
19398c2ecf20Sopenharmony_ci		res = hfa384x_from_bap(dev, BAP0, &rxdesc, sizeof(rxdesc));
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci	if (res) {
19428c2ecf20Sopenharmony_ci		spin_unlock(&local->baplock);
19438c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: copy from BAP0 failed %d\n", dev->name,
19448c2ecf20Sopenharmony_ci		       res);
19458c2ecf20Sopenharmony_ci		if (res == -ETIMEDOUT) {
19468c2ecf20Sopenharmony_ci			schedule_work(&local->reset_queue);
19478c2ecf20Sopenharmony_ci		}
19488c2ecf20Sopenharmony_ci		goto rx_dropped;
19498c2ecf20Sopenharmony_ci	}
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci	len = le16_to_cpu(rxdesc.data_len);
19528c2ecf20Sopenharmony_ci	hdr_len = sizeof(rxdesc);
19538c2ecf20Sopenharmony_ci	status = le16_to_cpu(rxdesc.status);
19548c2ecf20Sopenharmony_ci	macport = (status >> 8) & 0x07;
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_ci	/* Drop frames with too large reported payload length. Monitor mode
19578c2ecf20Sopenharmony_ci	 * seems to sometimes pass frames (e.g., ctrl::ack) with signed and
19588c2ecf20Sopenharmony_ci	 * negative value, so allow also values 65522 .. 65534 (-14 .. -2) for
19598c2ecf20Sopenharmony_ci	 * macport 7 */
19608c2ecf20Sopenharmony_ci	if (len > PRISM2_DATA_MAXLEN + 8 /* WEP */) {
19618c2ecf20Sopenharmony_ci		if (macport == 7 && local->iw_mode == IW_MODE_MONITOR) {
19628c2ecf20Sopenharmony_ci			if (len >= (u16) -14) {
19638c2ecf20Sopenharmony_ci				hdr_len -= 65535 - len;
19648c2ecf20Sopenharmony_ci				hdr_len--;
19658c2ecf20Sopenharmony_ci			}
19668c2ecf20Sopenharmony_ci			len = 0;
19678c2ecf20Sopenharmony_ci		} else {
19688c2ecf20Sopenharmony_ci			spin_unlock(&local->baplock);
19698c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: Received frame with invalid "
19708c2ecf20Sopenharmony_ci			       "length 0x%04x\n", dev->name, len);
19718c2ecf20Sopenharmony_ci			hostap_dump_rx_header(dev->name, &rxdesc);
19728c2ecf20Sopenharmony_ci			goto rx_dropped;
19738c2ecf20Sopenharmony_ci		}
19748c2ecf20Sopenharmony_ci	}
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(len + hdr_len);
19778c2ecf20Sopenharmony_ci	if (!skb) {
19788c2ecf20Sopenharmony_ci		spin_unlock(&local->baplock);
19798c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: RX failed to allocate skb\n",
19808c2ecf20Sopenharmony_ci		       dev->name);
19818c2ecf20Sopenharmony_ci		goto rx_dropped;
19828c2ecf20Sopenharmony_ci	}
19838c2ecf20Sopenharmony_ci	skb->dev = dev;
19848c2ecf20Sopenharmony_ci	skb_put_data(skb, &rxdesc, hdr_len);
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci	if (len > 0)
19878c2ecf20Sopenharmony_ci		res = hfa384x_from_bap(dev, BAP0, skb_put(skb, len), len);
19888c2ecf20Sopenharmony_ci	spin_unlock(&local->baplock);
19898c2ecf20Sopenharmony_ci	if (res) {
19908c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: RX failed to read "
19918c2ecf20Sopenharmony_ci		       "frame data\n", dev->name);
19928c2ecf20Sopenharmony_ci		goto rx_dropped;
19938c2ecf20Sopenharmony_ci	}
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	skb_queue_tail(&local->rx_list, skb);
19968c2ecf20Sopenharmony_ci	tasklet_schedule(&local->rx_tasklet);
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci rx_exit:
19998c2ecf20Sopenharmony_ci	prism2_callback(local, PRISM2_CALLBACK_RX_END);
20008c2ecf20Sopenharmony_ci	if (!rx_pending) {
20018c2ecf20Sopenharmony_ci		HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF);
20028c2ecf20Sopenharmony_ci	}
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	return;
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci rx_dropped:
20078c2ecf20Sopenharmony_ci	dev->stats.rx_dropped++;
20088c2ecf20Sopenharmony_ci	if (skb)
20098c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
20108c2ecf20Sopenharmony_ci	goto rx_exit;
20118c2ecf20Sopenharmony_ci}
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
20158c2ecf20Sopenharmony_cistatic void hostap_rx_skb(local_info_t *local, struct sk_buff *skb)
20168c2ecf20Sopenharmony_ci{
20178c2ecf20Sopenharmony_ci	struct hfa384x_rx_frame *rxdesc;
20188c2ecf20Sopenharmony_ci	struct net_device *dev = skb->dev;
20198c2ecf20Sopenharmony_ci	struct hostap_80211_rx_status stats;
20208c2ecf20Sopenharmony_ci	int hdrlen, rx_hdrlen;
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	rx_hdrlen = sizeof(*rxdesc);
20238c2ecf20Sopenharmony_ci	if (skb->len < sizeof(*rxdesc)) {
20248c2ecf20Sopenharmony_ci		/* Allow monitor mode to receive shorter frames */
20258c2ecf20Sopenharmony_ci		if (local->iw_mode == IW_MODE_MONITOR &&
20268c2ecf20Sopenharmony_ci		    skb->len >= sizeof(*rxdesc) - 30) {
20278c2ecf20Sopenharmony_ci			rx_hdrlen = skb->len;
20288c2ecf20Sopenharmony_ci		} else {
20298c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
20308c2ecf20Sopenharmony_ci			return;
20318c2ecf20Sopenharmony_ci		}
20328c2ecf20Sopenharmony_ci	}
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci	rxdesc = (struct hfa384x_rx_frame *) skb->data;
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci	if (local->frame_dump & PRISM2_DUMP_RX_HDR &&
20378c2ecf20Sopenharmony_ci	    skb->len >= sizeof(*rxdesc))
20388c2ecf20Sopenharmony_ci		hostap_dump_rx_header(dev->name, rxdesc);
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	if (le16_to_cpu(rxdesc->status) & HFA384X_RX_STATUS_FCSERR &&
20418c2ecf20Sopenharmony_ci	    (!local->monitor_allow_fcserr ||
20428c2ecf20Sopenharmony_ci	     local->iw_mode != IW_MODE_MONITOR))
20438c2ecf20Sopenharmony_ci		goto drop;
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci	if (skb->len > PRISM2_DATA_MAXLEN) {
20468c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: RX: len(%d) > MAX(%d)\n",
20478c2ecf20Sopenharmony_ci		       dev->name, skb->len, PRISM2_DATA_MAXLEN);
20488c2ecf20Sopenharmony_ci		goto drop;
20498c2ecf20Sopenharmony_ci	}
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_ci	stats.mac_time = le32_to_cpu(rxdesc->time);
20528c2ecf20Sopenharmony_ci	stats.signal = rxdesc->signal - local->rssi_to_dBm;
20538c2ecf20Sopenharmony_ci	stats.noise = rxdesc->silence - local->rssi_to_dBm;
20548c2ecf20Sopenharmony_ci	stats.rate = rxdesc->rate;
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_ci	/* Convert Prism2 RX structure into IEEE 802.11 header */
20578c2ecf20Sopenharmony_ci	hdrlen = hostap_80211_get_hdrlen(rxdesc->frame_control);
20588c2ecf20Sopenharmony_ci	if (hdrlen > rx_hdrlen)
20598c2ecf20Sopenharmony_ci		hdrlen = rx_hdrlen;
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	memmove(skb_pull(skb, rx_hdrlen - hdrlen),
20628c2ecf20Sopenharmony_ci		&rxdesc->frame_control, hdrlen);
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	hostap_80211_rx(dev, skb, &stats);
20658c2ecf20Sopenharmony_ci	return;
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci drop:
20688c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
20698c2ecf20Sopenharmony_ci}
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
20738c2ecf20Sopenharmony_cistatic void hostap_rx_tasklet(struct tasklet_struct *t)
20748c2ecf20Sopenharmony_ci{
20758c2ecf20Sopenharmony_ci	local_info_t *local = from_tasklet(local, t, rx_tasklet);
20768c2ecf20Sopenharmony_ci	struct sk_buff *skb;
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&local->rx_list)) != NULL)
20798c2ecf20Sopenharmony_ci		hostap_rx_skb(local, skb);
20808c2ecf20Sopenharmony_ci}
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci/* Called only from hardware IRQ */
20848c2ecf20Sopenharmony_cistatic void prism2_alloc_ev(struct net_device *dev)
20858c2ecf20Sopenharmony_ci{
20868c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
20878c2ecf20Sopenharmony_ci	local_info_t *local;
20888c2ecf20Sopenharmony_ci	int idx;
20898c2ecf20Sopenharmony_ci	u16 fid;
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
20928c2ecf20Sopenharmony_ci	local = iface->local;
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	fid = prism2_read_fid_reg(dev, HFA384X_ALLOCFID_OFF);
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_FID, "FID: interrupt: ALLOC - fid=0x%04x\n", fid);
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci	spin_lock(&local->txfidlock);
20998c2ecf20Sopenharmony_ci	idx = local->next_alloc;
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci	do {
21028c2ecf20Sopenharmony_ci		if (local->txfid[idx] == fid) {
21038c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_FID, "FID: found matching txfid[%d]\n",
21048c2ecf20Sopenharmony_ci			       idx);
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci#ifndef final_version
21078c2ecf20Sopenharmony_ci			if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY)
21088c2ecf20Sopenharmony_ci				printk("Already released txfid found at idx "
21098c2ecf20Sopenharmony_ci				       "%d\n", idx);
21108c2ecf20Sopenharmony_ci			if (local->intransmitfid[idx] == PRISM2_TXFID_RESERVED)
21118c2ecf20Sopenharmony_ci				printk("Already reserved txfid found at idx "
21128c2ecf20Sopenharmony_ci				       "%d\n", idx);
21138c2ecf20Sopenharmony_ci#endif
21148c2ecf20Sopenharmony_ci			local->intransmitfid[idx] = PRISM2_TXFID_EMPTY;
21158c2ecf20Sopenharmony_ci			idx++;
21168c2ecf20Sopenharmony_ci			local->next_alloc = idx >= PRISM2_TXFID_COUNT ? 0 :
21178c2ecf20Sopenharmony_ci				idx;
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci			if (!test_bit(HOSTAP_BITS_TRANSMIT, &local->bits) &&
21208c2ecf20Sopenharmony_ci			    netif_queue_stopped(dev))
21218c2ecf20Sopenharmony_ci				netif_wake_queue(dev);
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_ci			spin_unlock(&local->txfidlock);
21248c2ecf20Sopenharmony_ci			return;
21258c2ecf20Sopenharmony_ci		}
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci		idx++;
21288c2ecf20Sopenharmony_ci		if (idx >= PRISM2_TXFID_COUNT)
21298c2ecf20Sopenharmony_ci			idx = 0;
21308c2ecf20Sopenharmony_ci	} while (idx != local->next_alloc);
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci	printk(KERN_WARNING "%s: could not find matching txfid (0x%04x, new "
21338c2ecf20Sopenharmony_ci	       "read 0x%04x) for alloc event\n", dev->name, fid,
21348c2ecf20Sopenharmony_ci	       HFA384X_INW(HFA384X_ALLOCFID_OFF));
21358c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "TXFIDs:");
21368c2ecf20Sopenharmony_ci	for (idx = 0; idx < PRISM2_TXFID_COUNT; idx++)
21378c2ecf20Sopenharmony_ci		printk(" %04x[%04x]", local->txfid[idx],
21388c2ecf20Sopenharmony_ci		       local->intransmitfid[idx]);
21398c2ecf20Sopenharmony_ci	printk("\n");
21408c2ecf20Sopenharmony_ci	spin_unlock(&local->txfidlock);
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ci	/* FIX: should probably schedule reset; reference to one txfid was lost
21438c2ecf20Sopenharmony_ci	 * completely.. Bad things will happen if we run out of txfids
21448c2ecf20Sopenharmony_ci	 * Actually, this will cause netdev watchdog to notice TX timeout and
21458c2ecf20Sopenharmony_ci	 * then card reset after all txfids have been leaked. */
21468c2ecf20Sopenharmony_ci}
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
21508c2ecf20Sopenharmony_cistatic void hostap_tx_callback(local_info_t *local,
21518c2ecf20Sopenharmony_ci			       struct hfa384x_tx_frame *txdesc, int ok,
21528c2ecf20Sopenharmony_ci			       char *payload)
21538c2ecf20Sopenharmony_ci{
21548c2ecf20Sopenharmony_ci	u16 sw_support, hdrlen, len;
21558c2ecf20Sopenharmony_ci	struct sk_buff *skb;
21568c2ecf20Sopenharmony_ci	struct hostap_tx_callback_info *cb;
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ci	/* Make sure that frame was from us. */
21598c2ecf20Sopenharmony_ci	if (!ether_addr_equal(txdesc->addr2, local->dev->dev_addr)) {
21608c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: TX callback - foreign frame\n",
21618c2ecf20Sopenharmony_ci		       local->dev->name);
21628c2ecf20Sopenharmony_ci		return;
21638c2ecf20Sopenharmony_ci	}
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_ci	sw_support = le32_to_cpu(txdesc->sw_support);
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci	spin_lock(&local->lock);
21688c2ecf20Sopenharmony_ci	cb = local->tx_callback;
21698c2ecf20Sopenharmony_ci	while (cb != NULL && cb->idx != sw_support)
21708c2ecf20Sopenharmony_ci		cb = cb->next;
21718c2ecf20Sopenharmony_ci	spin_unlock(&local->lock);
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci	if (cb == NULL) {
21748c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: could not find TX callback (idx %d)\n",
21758c2ecf20Sopenharmony_ci		       local->dev->name, sw_support);
21768c2ecf20Sopenharmony_ci		return;
21778c2ecf20Sopenharmony_ci	}
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_ci	hdrlen = hostap_80211_get_hdrlen(txdesc->frame_control);
21808c2ecf20Sopenharmony_ci	len = le16_to_cpu(txdesc->data_len);
21818c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(hdrlen + len);
21828c2ecf20Sopenharmony_ci	if (skb == NULL) {
21838c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: hostap_tx_callback failed to allocate "
21848c2ecf20Sopenharmony_ci		       "skb\n", local->dev->name);
21858c2ecf20Sopenharmony_ci		return;
21868c2ecf20Sopenharmony_ci	}
21878c2ecf20Sopenharmony_ci
21888c2ecf20Sopenharmony_ci	skb_put_data(skb, (void *)&txdesc->frame_control, hdrlen);
21898c2ecf20Sopenharmony_ci	if (payload)
21908c2ecf20Sopenharmony_ci		skb_put_data(skb, payload, len);
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci	skb->dev = local->dev;
21938c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
21948c2ecf20Sopenharmony_ci
21958c2ecf20Sopenharmony_ci	cb->func(skb, ok, cb->data);
21968c2ecf20Sopenharmony_ci}
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
22008c2ecf20Sopenharmony_cistatic int hostap_tx_compl_read(local_info_t *local, int error,
22018c2ecf20Sopenharmony_ci				struct hfa384x_tx_frame *txdesc,
22028c2ecf20Sopenharmony_ci				char **payload)
22038c2ecf20Sopenharmony_ci{
22048c2ecf20Sopenharmony_ci	u16 fid, len;
22058c2ecf20Sopenharmony_ci	int res, ret = 0;
22068c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_ci	fid = prism2_read_fid_reg(dev, HFA384X_TXCOMPLFID_OFF);
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_FID, "interrupt: TX (err=%d) - fid=0x%04x\n", fid, error);
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	spin_lock(&local->baplock);
22138c2ecf20Sopenharmony_ci	res = hfa384x_setup_bap(dev, BAP0, fid, 0);
22148c2ecf20Sopenharmony_ci	if (!res)
22158c2ecf20Sopenharmony_ci		res = hfa384x_from_bap(dev, BAP0, txdesc, sizeof(*txdesc));
22168c2ecf20Sopenharmony_ci	if (res) {
22178c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_EXTRA, "%s: TX (err=%d) - fid=0x%04x - could not "
22188c2ecf20Sopenharmony_ci		       "read txdesc\n", dev->name, error, fid);
22198c2ecf20Sopenharmony_ci		if (res == -ETIMEDOUT) {
22208c2ecf20Sopenharmony_ci			schedule_work(&local->reset_queue);
22218c2ecf20Sopenharmony_ci		}
22228c2ecf20Sopenharmony_ci		ret = -1;
22238c2ecf20Sopenharmony_ci		goto fail;
22248c2ecf20Sopenharmony_ci	}
22258c2ecf20Sopenharmony_ci	if (txdesc->sw_support) {
22268c2ecf20Sopenharmony_ci		len = le16_to_cpu(txdesc->data_len);
22278c2ecf20Sopenharmony_ci		if (len < PRISM2_DATA_MAXLEN) {
22288c2ecf20Sopenharmony_ci			*payload = kmalloc(len, GFP_ATOMIC);
22298c2ecf20Sopenharmony_ci			if (*payload == NULL ||
22308c2ecf20Sopenharmony_ci			    hfa384x_from_bap(dev, BAP0, *payload, len)) {
22318c2ecf20Sopenharmony_ci				PDEBUG(DEBUG_EXTRA, "%s: could not read TX "
22328c2ecf20Sopenharmony_ci				       "frame payload\n", dev->name);
22338c2ecf20Sopenharmony_ci				kfree(*payload);
22348c2ecf20Sopenharmony_ci				*payload = NULL;
22358c2ecf20Sopenharmony_ci				ret = -1;
22368c2ecf20Sopenharmony_ci				goto fail;
22378c2ecf20Sopenharmony_ci			}
22388c2ecf20Sopenharmony_ci		}
22398c2ecf20Sopenharmony_ci	}
22408c2ecf20Sopenharmony_ci
22418c2ecf20Sopenharmony_ci fail:
22428c2ecf20Sopenharmony_ci	spin_unlock(&local->baplock);
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci	return ret;
22458c2ecf20Sopenharmony_ci}
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_ci
22488c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
22498c2ecf20Sopenharmony_cistatic void prism2_tx_ev(local_info_t *local)
22508c2ecf20Sopenharmony_ci{
22518c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
22528c2ecf20Sopenharmony_ci	char *payload = NULL;
22538c2ecf20Sopenharmony_ci	struct hfa384x_tx_frame txdesc;
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci	if (hostap_tx_compl_read(local, 0, &txdesc, &payload))
22568c2ecf20Sopenharmony_ci		goto fail;
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	if (local->frame_dump & PRISM2_DUMP_TX_HDR) {
22598c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_EXTRA, "%s: TX - status=0x%04x "
22608c2ecf20Sopenharmony_ci		       "retry_count=%d tx_rate=%d seq_ctrl=%d "
22618c2ecf20Sopenharmony_ci		       "duration_id=%d\n",
22628c2ecf20Sopenharmony_ci		       dev->name, le16_to_cpu(txdesc.status),
22638c2ecf20Sopenharmony_ci		       txdesc.retry_count, txdesc.tx_rate,
22648c2ecf20Sopenharmony_ci		       le16_to_cpu(txdesc.seq_ctrl),
22658c2ecf20Sopenharmony_ci		       le16_to_cpu(txdesc.duration_id));
22668c2ecf20Sopenharmony_ci	}
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	if (txdesc.sw_support)
22698c2ecf20Sopenharmony_ci		hostap_tx_callback(local, &txdesc, 1, payload);
22708c2ecf20Sopenharmony_ci	kfree(payload);
22718c2ecf20Sopenharmony_ci
22728c2ecf20Sopenharmony_ci fail:
22738c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_EV_TX, HFA384X_EVACK_OFF);
22748c2ecf20Sopenharmony_ci}
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
22788c2ecf20Sopenharmony_cistatic void hostap_sta_tx_exc_tasklet(struct tasklet_struct *t)
22798c2ecf20Sopenharmony_ci{
22808c2ecf20Sopenharmony_ci	local_info_t *local = from_tasklet(local, t, sta_tx_exc_tasklet);
22818c2ecf20Sopenharmony_ci	struct sk_buff *skb;
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&local->sta_tx_exc_list)) != NULL) {
22848c2ecf20Sopenharmony_ci		struct hfa384x_tx_frame *txdesc =
22858c2ecf20Sopenharmony_ci			(struct hfa384x_tx_frame *) skb->data;
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_ci		if (skb->len >= sizeof(*txdesc)) {
22888c2ecf20Sopenharmony_ci			/* Convert Prism2 RX structure into IEEE 802.11 header
22898c2ecf20Sopenharmony_ci			 */
22908c2ecf20Sopenharmony_ci			int hdrlen = hostap_80211_get_hdrlen(txdesc->frame_control);
22918c2ecf20Sopenharmony_ci			memmove(skb_pull(skb, sizeof(*txdesc) - hdrlen),
22928c2ecf20Sopenharmony_ci				&txdesc->frame_control, hdrlen);
22938c2ecf20Sopenharmony_ci
22948c2ecf20Sopenharmony_ci			hostap_handle_sta_tx_exc(local, skb);
22958c2ecf20Sopenharmony_ci		}
22968c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
22978c2ecf20Sopenharmony_ci	}
22988c2ecf20Sopenharmony_ci}
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
23028c2ecf20Sopenharmony_cistatic void prism2_txexc(local_info_t *local)
23038c2ecf20Sopenharmony_ci{
23048c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
23058c2ecf20Sopenharmony_ci	u16 status, fc;
23068c2ecf20Sopenharmony_ci	int show_dump, res;
23078c2ecf20Sopenharmony_ci	char *payload = NULL;
23088c2ecf20Sopenharmony_ci	struct hfa384x_tx_frame txdesc;
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_ci	show_dump = local->frame_dump & PRISM2_DUMP_TXEXC_HDR;
23118c2ecf20Sopenharmony_ci	dev->stats.tx_errors++;
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_ci	res = hostap_tx_compl_read(local, 1, &txdesc, &payload);
23148c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_EV_TXEXC, HFA384X_EVACK_OFF);
23158c2ecf20Sopenharmony_ci	if (res)
23168c2ecf20Sopenharmony_ci		return;
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_ci	status = le16_to_cpu(txdesc.status);
23198c2ecf20Sopenharmony_ci
23208c2ecf20Sopenharmony_ci	/* We produce a TXDROP event only for retry or lifetime
23218c2ecf20Sopenharmony_ci	 * exceeded, because that's the only status that really mean
23228c2ecf20Sopenharmony_ci	 * that this particular node went away.
23238c2ecf20Sopenharmony_ci	 * Other errors means that *we* screwed up. - Jean II */
23248c2ecf20Sopenharmony_ci	if (status & (HFA384X_TX_STATUS_RETRYERR | HFA384X_TX_STATUS_AGEDERR))
23258c2ecf20Sopenharmony_ci	{
23268c2ecf20Sopenharmony_ci		union iwreq_data wrqu;
23278c2ecf20Sopenharmony_ci
23288c2ecf20Sopenharmony_ci		/* Copy 802.11 dest address. */
23298c2ecf20Sopenharmony_ci		memcpy(wrqu.addr.sa_data, txdesc.addr1, ETH_ALEN);
23308c2ecf20Sopenharmony_ci		wrqu.addr.sa_family = ARPHRD_ETHER;
23318c2ecf20Sopenharmony_ci		wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
23328c2ecf20Sopenharmony_ci	} else
23338c2ecf20Sopenharmony_ci		show_dump = 1;
23348c2ecf20Sopenharmony_ci
23358c2ecf20Sopenharmony_ci	if (local->iw_mode == IW_MODE_MASTER ||
23368c2ecf20Sopenharmony_ci	    local->iw_mode == IW_MODE_REPEAT ||
23378c2ecf20Sopenharmony_ci	    local->wds_type & HOSTAP_WDS_AP_CLIENT) {
23388c2ecf20Sopenharmony_ci		struct sk_buff *skb;
23398c2ecf20Sopenharmony_ci		skb = dev_alloc_skb(sizeof(txdesc));
23408c2ecf20Sopenharmony_ci		if (skb) {
23418c2ecf20Sopenharmony_ci			skb_put_data(skb, &txdesc, sizeof(txdesc));
23428c2ecf20Sopenharmony_ci			skb_queue_tail(&local->sta_tx_exc_list, skb);
23438c2ecf20Sopenharmony_ci			tasklet_schedule(&local->sta_tx_exc_tasklet);
23448c2ecf20Sopenharmony_ci		}
23458c2ecf20Sopenharmony_ci	}
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	if (txdesc.sw_support)
23488c2ecf20Sopenharmony_ci		hostap_tx_callback(local, &txdesc, 0, payload);
23498c2ecf20Sopenharmony_ci	kfree(payload);
23508c2ecf20Sopenharmony_ci
23518c2ecf20Sopenharmony_ci	if (!show_dump)
23528c2ecf20Sopenharmony_ci		return;
23538c2ecf20Sopenharmony_ci
23548c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_EXTRA, "%s: TXEXC - status=0x%04x (%s%s%s%s)"
23558c2ecf20Sopenharmony_ci	       " tx_control=%04x\n",
23568c2ecf20Sopenharmony_ci	       dev->name, status,
23578c2ecf20Sopenharmony_ci	       status & HFA384X_TX_STATUS_RETRYERR ? "[RetryErr]" : "",
23588c2ecf20Sopenharmony_ci	       status & HFA384X_TX_STATUS_AGEDERR ? "[AgedErr]" : "",
23598c2ecf20Sopenharmony_ci	       status & HFA384X_TX_STATUS_DISCON ? "[Discon]" : "",
23608c2ecf20Sopenharmony_ci	       status & HFA384X_TX_STATUS_FORMERR ? "[FormErr]" : "",
23618c2ecf20Sopenharmony_ci	       le16_to_cpu(txdesc.tx_control));
23628c2ecf20Sopenharmony_ci
23638c2ecf20Sopenharmony_ci	fc = le16_to_cpu(txdesc.frame_control);
23648c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_EXTRA, "   retry_count=%d tx_rate=%d fc=0x%04x "
23658c2ecf20Sopenharmony_ci	       "(%s%s%s::%d%s%s)\n",
23668c2ecf20Sopenharmony_ci	       txdesc.retry_count, txdesc.tx_rate, fc,
23678c2ecf20Sopenharmony_ci	       ieee80211_is_mgmt(txdesc.frame_control) ? "Mgmt" : "",
23688c2ecf20Sopenharmony_ci	       ieee80211_is_ctl(txdesc.frame_control) ? "Ctrl" : "",
23698c2ecf20Sopenharmony_ci	       ieee80211_is_data(txdesc.frame_control) ? "Data" : "",
23708c2ecf20Sopenharmony_ci	       (fc & IEEE80211_FCTL_STYPE) >> 4,
23718c2ecf20Sopenharmony_ci	       ieee80211_has_tods(txdesc.frame_control) ? " ToDS" : "",
23728c2ecf20Sopenharmony_ci	       ieee80211_has_fromds(txdesc.frame_control) ? " FromDS" : "");
23738c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_EXTRA, "   A1=%pM A2=%pM A3=%pM A4=%pM\n",
23748c2ecf20Sopenharmony_ci	       txdesc.addr1, txdesc.addr2,
23758c2ecf20Sopenharmony_ci	       txdesc.addr3, txdesc.addr4);
23768c2ecf20Sopenharmony_ci}
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
23808c2ecf20Sopenharmony_cistatic void hostap_info_tasklet(struct tasklet_struct *t)
23818c2ecf20Sopenharmony_ci{
23828c2ecf20Sopenharmony_ci	local_info_t *local = from_tasklet(local, t, info_tasklet);
23838c2ecf20Sopenharmony_ci	struct sk_buff *skb;
23848c2ecf20Sopenharmony_ci
23858c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&local->info_list)) != NULL) {
23868c2ecf20Sopenharmony_ci		hostap_info_process(local, skb);
23878c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
23888c2ecf20Sopenharmony_ci	}
23898c2ecf20Sopenharmony_ci}
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_ci
23928c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
23938c2ecf20Sopenharmony_cistatic void prism2_info(local_info_t *local)
23948c2ecf20Sopenharmony_ci{
23958c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
23968c2ecf20Sopenharmony_ci	u16 fid;
23978c2ecf20Sopenharmony_ci	int res, left;
23988c2ecf20Sopenharmony_ci	struct hfa384x_info_frame info;
23998c2ecf20Sopenharmony_ci	struct sk_buff *skb;
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_ci	fid = HFA384X_INW(HFA384X_INFOFID_OFF);
24028c2ecf20Sopenharmony_ci
24038c2ecf20Sopenharmony_ci	spin_lock(&local->baplock);
24048c2ecf20Sopenharmony_ci	res = hfa384x_setup_bap(dev, BAP0, fid, 0);
24058c2ecf20Sopenharmony_ci	if (!res)
24068c2ecf20Sopenharmony_ci		res = hfa384x_from_bap(dev, BAP0, &info, sizeof(info));
24078c2ecf20Sopenharmony_ci	if (res) {
24088c2ecf20Sopenharmony_ci		spin_unlock(&local->baplock);
24098c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "Could not get info frame (fid=0x%04x)\n",
24108c2ecf20Sopenharmony_ci		       fid);
24118c2ecf20Sopenharmony_ci		if (res == -ETIMEDOUT) {
24128c2ecf20Sopenharmony_ci			schedule_work(&local->reset_queue);
24138c2ecf20Sopenharmony_ci		}
24148c2ecf20Sopenharmony_ci		goto out;
24158c2ecf20Sopenharmony_ci	}
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ci	left = (le16_to_cpu(info.len) - 1) * 2;
24188c2ecf20Sopenharmony_ci
24198c2ecf20Sopenharmony_ci	if (info.len & cpu_to_le16(0x8000) || info.len == 0 || left > 2060) {
24208c2ecf20Sopenharmony_ci		/* data register seems to give 0x8000 in some error cases even
24218c2ecf20Sopenharmony_ci		 * though busy bit is not set in offset register;
24228c2ecf20Sopenharmony_ci		 * in addition, length must be at least 1 due to type field */
24238c2ecf20Sopenharmony_ci		spin_unlock(&local->baplock);
24248c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: Received info frame with invalid "
24258c2ecf20Sopenharmony_ci		       "length 0x%04x (type 0x%04x)\n", dev->name,
24268c2ecf20Sopenharmony_ci		       le16_to_cpu(info.len), le16_to_cpu(info.type));
24278c2ecf20Sopenharmony_ci		goto out;
24288c2ecf20Sopenharmony_ci	}
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(sizeof(info) + left);
24318c2ecf20Sopenharmony_ci	if (skb == NULL) {
24328c2ecf20Sopenharmony_ci		spin_unlock(&local->baplock);
24338c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: Could not allocate skb for info "
24348c2ecf20Sopenharmony_ci		       "frame\n", dev->name);
24358c2ecf20Sopenharmony_ci		goto out;
24368c2ecf20Sopenharmony_ci	}
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci	skb_put_data(skb, &info, sizeof(info));
24398c2ecf20Sopenharmony_ci	if (left > 0 && hfa384x_from_bap(dev, BAP0, skb_put(skb, left), left))
24408c2ecf20Sopenharmony_ci	{
24418c2ecf20Sopenharmony_ci		spin_unlock(&local->baplock);
24428c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: Info frame read failed (fid=0x%04x, "
24438c2ecf20Sopenharmony_ci		       "len=0x%04x, type=0x%04x\n", dev->name, fid,
24448c2ecf20Sopenharmony_ci		       le16_to_cpu(info.len), le16_to_cpu(info.type));
24458c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
24468c2ecf20Sopenharmony_ci		goto out;
24478c2ecf20Sopenharmony_ci	}
24488c2ecf20Sopenharmony_ci	spin_unlock(&local->baplock);
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_ci	skb_queue_tail(&local->info_list, skb);
24518c2ecf20Sopenharmony_ci	tasklet_schedule(&local->info_tasklet);
24528c2ecf20Sopenharmony_ci
24538c2ecf20Sopenharmony_ci out:
24548c2ecf20Sopenharmony_ci	HFA384X_OUTW(HFA384X_EV_INFO, HFA384X_EVACK_OFF);
24558c2ecf20Sopenharmony_ci}
24568c2ecf20Sopenharmony_ci
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci/* Called only as a tasklet (software IRQ) */
24598c2ecf20Sopenharmony_cistatic void hostap_bap_tasklet(struct tasklet_struct *t)
24608c2ecf20Sopenharmony_ci{
24618c2ecf20Sopenharmony_ci	local_info_t *local = from_tasklet(local, t, bap_tasklet);
24628c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
24638c2ecf20Sopenharmony_ci	u16 ev;
24648c2ecf20Sopenharmony_ci	int frames = 30;
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci	if (local->func->card_present && !local->func->card_present(local))
24678c2ecf20Sopenharmony_ci		return;
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_ci	set_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits);
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_ci	/* Process all pending BAP events without generating new interrupts
24728c2ecf20Sopenharmony_ci	 * for them */
24738c2ecf20Sopenharmony_ci	while (frames-- > 0) {
24748c2ecf20Sopenharmony_ci		ev = HFA384X_INW(HFA384X_EVSTAT_OFF);
24758c2ecf20Sopenharmony_ci		if (ev == 0xffff || !(ev & HFA384X_BAP0_EVENTS))
24768c2ecf20Sopenharmony_ci			break;
24778c2ecf20Sopenharmony_ci		if (ev & HFA384X_EV_RX)
24788c2ecf20Sopenharmony_ci			prism2_rx(local);
24798c2ecf20Sopenharmony_ci		if (ev & HFA384X_EV_INFO)
24808c2ecf20Sopenharmony_ci			prism2_info(local);
24818c2ecf20Sopenharmony_ci		if (ev & HFA384X_EV_TX)
24828c2ecf20Sopenharmony_ci			prism2_tx_ev(local);
24838c2ecf20Sopenharmony_ci		if (ev & HFA384X_EV_TXEXC)
24848c2ecf20Sopenharmony_ci			prism2_txexc(local);
24858c2ecf20Sopenharmony_ci	}
24868c2ecf20Sopenharmony_ci
24878c2ecf20Sopenharmony_ci	set_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits);
24888c2ecf20Sopenharmony_ci	clear_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits);
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ci	/* Enable interrupts for new BAP events */
24918c2ecf20Sopenharmony_ci	hfa384x_events_all(dev);
24928c2ecf20Sopenharmony_ci	clear_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits);
24938c2ecf20Sopenharmony_ci}
24948c2ecf20Sopenharmony_ci
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_ci/* Called only from hardware IRQ */
24978c2ecf20Sopenharmony_cistatic void prism2_infdrop(struct net_device *dev)
24988c2ecf20Sopenharmony_ci{
24998c2ecf20Sopenharmony_ci	static unsigned long last_inquire = 0;
25008c2ecf20Sopenharmony_ci
25018c2ecf20Sopenharmony_ci	PDEBUG(DEBUG_EXTRA, "%s: INFDROP event\n", dev->name);
25028c2ecf20Sopenharmony_ci
25038c2ecf20Sopenharmony_ci	/* some firmware versions seem to get stuck with
25048c2ecf20Sopenharmony_ci	 * full CommTallies in high traffic load cases; every
25058c2ecf20Sopenharmony_ci	 * packet will then cause INFDROP event and CommTallies
25068c2ecf20Sopenharmony_ci	 * info frame will not be sent automatically. Try to
25078c2ecf20Sopenharmony_ci	 * get out of this state by inquiring CommTallies. */
25088c2ecf20Sopenharmony_ci	if (!last_inquire || time_after(jiffies, last_inquire + HZ)) {
25098c2ecf20Sopenharmony_ci		hfa384x_cmd_callback(dev, HFA384X_CMDCODE_INQUIRE,
25108c2ecf20Sopenharmony_ci				     HFA384X_INFO_COMMTALLIES, NULL, 0);
25118c2ecf20Sopenharmony_ci		last_inquire = jiffies;
25128c2ecf20Sopenharmony_ci	}
25138c2ecf20Sopenharmony_ci}
25148c2ecf20Sopenharmony_ci
25158c2ecf20Sopenharmony_ci
25168c2ecf20Sopenharmony_ci/* Called only from hardware IRQ */
25178c2ecf20Sopenharmony_cistatic void prism2_ev_tick(struct net_device *dev)
25188c2ecf20Sopenharmony_ci{
25198c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
25208c2ecf20Sopenharmony_ci	local_info_t *local;
25218c2ecf20Sopenharmony_ci	u16 evstat, inten;
25228c2ecf20Sopenharmony_ci	static int prev_stuck = 0;
25238c2ecf20Sopenharmony_ci
25248c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
25258c2ecf20Sopenharmony_ci	local = iface->local;
25268c2ecf20Sopenharmony_ci
25278c2ecf20Sopenharmony_ci	if (time_after(jiffies, local->last_tick_timer + 5 * HZ) &&
25288c2ecf20Sopenharmony_ci	    local->last_tick_timer) {
25298c2ecf20Sopenharmony_ci		evstat = HFA384X_INW(HFA384X_EVSTAT_OFF);
25308c2ecf20Sopenharmony_ci		inten = HFA384X_INW(HFA384X_INTEN_OFF);
25318c2ecf20Sopenharmony_ci		if (!prev_stuck) {
25328c2ecf20Sopenharmony_ci			printk(KERN_INFO "%s: SW TICK stuck? "
25338c2ecf20Sopenharmony_ci			       "bits=0x%lx EvStat=%04x IntEn=%04x\n",
25348c2ecf20Sopenharmony_ci			       dev->name, local->bits, evstat, inten);
25358c2ecf20Sopenharmony_ci		}
25368c2ecf20Sopenharmony_ci		local->sw_tick_stuck++;
25378c2ecf20Sopenharmony_ci		if ((evstat & HFA384X_BAP0_EVENTS) &&
25388c2ecf20Sopenharmony_ci		    (inten & HFA384X_BAP0_EVENTS)) {
25398c2ecf20Sopenharmony_ci			printk(KERN_INFO "%s: trying to recover from IRQ "
25408c2ecf20Sopenharmony_ci			       "hang\n", dev->name);
25418c2ecf20Sopenharmony_ci			hfa384x_events_no_bap0(dev);
25428c2ecf20Sopenharmony_ci		}
25438c2ecf20Sopenharmony_ci		prev_stuck = 1;
25448c2ecf20Sopenharmony_ci	} else
25458c2ecf20Sopenharmony_ci		prev_stuck = 0;
25468c2ecf20Sopenharmony_ci}
25478c2ecf20Sopenharmony_ci
25488c2ecf20Sopenharmony_ci
25498c2ecf20Sopenharmony_ci/* Called only from hardware IRQ */
25508c2ecf20Sopenharmony_cistatic void prism2_check_magic(local_info_t *local)
25518c2ecf20Sopenharmony_ci{
25528c2ecf20Sopenharmony_ci	/* at least PCI Prism2.5 with bus mastering seems to sometimes
25538c2ecf20Sopenharmony_ci	 * return 0x0000 in SWSUPPORT0 for unknown reason, but re-reading the
25548c2ecf20Sopenharmony_ci	 * register once or twice seems to get the correct value.. PCI cards
25558c2ecf20Sopenharmony_ci	 * cannot anyway be removed during normal operation, so there is not
25568c2ecf20Sopenharmony_ci	 * really any need for this verification with them. */
25578c2ecf20Sopenharmony_ci
25588c2ecf20Sopenharmony_ci#ifndef PRISM2_PCI
25598c2ecf20Sopenharmony_ci#ifndef final_version
25608c2ecf20Sopenharmony_ci	static unsigned long last_magic_err = 0;
25618c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
25628c2ecf20Sopenharmony_ci
25638c2ecf20Sopenharmony_ci	if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) {
25648c2ecf20Sopenharmony_ci		if (!local->hw_ready)
25658c2ecf20Sopenharmony_ci			return;
25668c2ecf20Sopenharmony_ci		HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
25678c2ecf20Sopenharmony_ci		if (time_after(jiffies, last_magic_err + 10 * HZ)) {
25688c2ecf20Sopenharmony_ci			printk("%s: Interrupt, but SWSUPPORT0 does not match: "
25698c2ecf20Sopenharmony_ci			       "%04X != %04X - card removed?\n", dev->name,
25708c2ecf20Sopenharmony_ci			       HFA384X_INW(HFA384X_SWSUPPORT0_OFF),
25718c2ecf20Sopenharmony_ci			       HFA384X_MAGIC);
25728c2ecf20Sopenharmony_ci			last_magic_err = jiffies;
25738c2ecf20Sopenharmony_ci		} else if (net_ratelimit()) {
25748c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: interrupt - SWSUPPORT0=%04x "
25758c2ecf20Sopenharmony_ci			       "MAGIC=%04x\n", dev->name,
25768c2ecf20Sopenharmony_ci			       HFA384X_INW(HFA384X_SWSUPPORT0_OFF),
25778c2ecf20Sopenharmony_ci			       HFA384X_MAGIC);
25788c2ecf20Sopenharmony_ci		}
25798c2ecf20Sopenharmony_ci		if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != 0xffff)
25808c2ecf20Sopenharmony_ci			schedule_work(&local->reset_queue);
25818c2ecf20Sopenharmony_ci		return;
25828c2ecf20Sopenharmony_ci	}
25838c2ecf20Sopenharmony_ci#endif /* final_version */
25848c2ecf20Sopenharmony_ci#endif /* !PRISM2_PCI */
25858c2ecf20Sopenharmony_ci}
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_ci
25888c2ecf20Sopenharmony_ci/* Called only from hardware IRQ */
25898c2ecf20Sopenharmony_cistatic irqreturn_t prism2_interrupt(int irq, void *dev_id)
25908c2ecf20Sopenharmony_ci{
25918c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
25928c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
25938c2ecf20Sopenharmony_ci	local_info_t *local;
25948c2ecf20Sopenharmony_ci	int events = 0;
25958c2ecf20Sopenharmony_ci	u16 ev;
25968c2ecf20Sopenharmony_ci
25978c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
25988c2ecf20Sopenharmony_ci	local = iface->local;
25998c2ecf20Sopenharmony_ci
26008c2ecf20Sopenharmony_ci	/* Detect early interrupt before driver is fully configured */
26018c2ecf20Sopenharmony_ci	spin_lock(&local->irq_init_lock);
26028c2ecf20Sopenharmony_ci	if (!dev->base_addr) {
26038c2ecf20Sopenharmony_ci		if (net_ratelimit()) {
26048c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: Interrupt, but dev not configured\n",
26058c2ecf20Sopenharmony_ci			       dev->name);
26068c2ecf20Sopenharmony_ci		}
26078c2ecf20Sopenharmony_ci		spin_unlock(&local->irq_init_lock);
26088c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
26098c2ecf20Sopenharmony_ci	}
26108c2ecf20Sopenharmony_ci	spin_unlock(&local->irq_init_lock);
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0);
26138c2ecf20Sopenharmony_ci
26148c2ecf20Sopenharmony_ci	if (local->func->card_present && !local->func->card_present(local)) {
26158c2ecf20Sopenharmony_ci		if (net_ratelimit()) {
26168c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: Interrupt, but dev not OK\n",
26178c2ecf20Sopenharmony_ci			       dev->name);
26188c2ecf20Sopenharmony_ci		}
26198c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
26208c2ecf20Sopenharmony_ci	}
26218c2ecf20Sopenharmony_ci
26228c2ecf20Sopenharmony_ci	prism2_check_magic(local);
26238c2ecf20Sopenharmony_ci
26248c2ecf20Sopenharmony_ci	for (;;) {
26258c2ecf20Sopenharmony_ci		ev = HFA384X_INW(HFA384X_EVSTAT_OFF);
26268c2ecf20Sopenharmony_ci		if (ev == 0xffff) {
26278c2ecf20Sopenharmony_ci			if (local->shutdown)
26288c2ecf20Sopenharmony_ci				return IRQ_HANDLED;
26298c2ecf20Sopenharmony_ci			HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
26308c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: prism2_interrupt: ev=0xffff\n",
26318c2ecf20Sopenharmony_ci			       dev->name);
26328c2ecf20Sopenharmony_ci			return IRQ_HANDLED;
26338c2ecf20Sopenharmony_ci		}
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_ci		ev &= HFA384X_INW(HFA384X_INTEN_OFF);
26368c2ecf20Sopenharmony_ci		if (ev == 0)
26378c2ecf20Sopenharmony_ci			break;
26388c2ecf20Sopenharmony_ci
26398c2ecf20Sopenharmony_ci		if (ev & HFA384X_EV_CMD) {
26408c2ecf20Sopenharmony_ci			prism2_cmd_ev(dev);
26418c2ecf20Sopenharmony_ci		}
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_ci		/* Above events are needed even before hw is ready, but other
26448c2ecf20Sopenharmony_ci		 * events should be skipped during initialization. This may
26458c2ecf20Sopenharmony_ci		 * change for AllocEv if allocate_fid is implemented without
26468c2ecf20Sopenharmony_ci		 * busy waiting. */
26478c2ecf20Sopenharmony_ci		if (!local->hw_ready || local->hw_resetting ||
26488c2ecf20Sopenharmony_ci		    !local->dev_enabled) {
26498c2ecf20Sopenharmony_ci			ev = HFA384X_INW(HFA384X_EVSTAT_OFF);
26508c2ecf20Sopenharmony_ci			if (ev & HFA384X_EV_CMD)
26518c2ecf20Sopenharmony_ci				goto next_event;
26528c2ecf20Sopenharmony_ci			if ((ev & HFA384X_EVENT_MASK) == 0)
26538c2ecf20Sopenharmony_ci				return IRQ_HANDLED;
26548c2ecf20Sopenharmony_ci			if (local->dev_enabled && (ev & ~HFA384X_EV_TICK) &&
26558c2ecf20Sopenharmony_ci			    net_ratelimit()) {
26568c2ecf20Sopenharmony_ci				printk(KERN_DEBUG "%s: prism2_interrupt: hw "
26578c2ecf20Sopenharmony_ci				       "not ready; skipping events 0x%04x "
26588c2ecf20Sopenharmony_ci				       "(IntEn=0x%04x)%s%s%s\n",
26598c2ecf20Sopenharmony_ci				       dev->name, ev,
26608c2ecf20Sopenharmony_ci				       HFA384X_INW(HFA384X_INTEN_OFF),
26618c2ecf20Sopenharmony_ci				       !local->hw_ready ? " (!hw_ready)" : "",
26628c2ecf20Sopenharmony_ci				       local->hw_resetting ?
26638c2ecf20Sopenharmony_ci				       " (hw_resetting)" : "",
26648c2ecf20Sopenharmony_ci				       !local->dev_enabled ?
26658c2ecf20Sopenharmony_ci				       " (!dev_enabled)" : "");
26668c2ecf20Sopenharmony_ci			}
26678c2ecf20Sopenharmony_ci			HFA384X_OUTW(ev, HFA384X_EVACK_OFF);
26688c2ecf20Sopenharmony_ci			return IRQ_HANDLED;
26698c2ecf20Sopenharmony_ci		}
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_ci		if (ev & HFA384X_EV_TICK) {
26728c2ecf20Sopenharmony_ci			prism2_ev_tick(dev);
26738c2ecf20Sopenharmony_ci			HFA384X_OUTW(HFA384X_EV_TICK, HFA384X_EVACK_OFF);
26748c2ecf20Sopenharmony_ci		}
26758c2ecf20Sopenharmony_ci
26768c2ecf20Sopenharmony_ci		if (ev & HFA384X_EV_ALLOC) {
26778c2ecf20Sopenharmony_ci			prism2_alloc_ev(dev);
26788c2ecf20Sopenharmony_ci			HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF);
26798c2ecf20Sopenharmony_ci		}
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci		/* Reading data from the card is quite time consuming, so do it
26828c2ecf20Sopenharmony_ci		 * in tasklets. TX, TXEXC, RX, and INFO events will be ACKed
26838c2ecf20Sopenharmony_ci		 * and unmasked after needed data has been read completely. */
26848c2ecf20Sopenharmony_ci		if (ev & HFA384X_BAP0_EVENTS) {
26858c2ecf20Sopenharmony_ci			hfa384x_events_no_bap0(dev);
26868c2ecf20Sopenharmony_ci			tasklet_schedule(&local->bap_tasklet);
26878c2ecf20Sopenharmony_ci		}
26888c2ecf20Sopenharmony_ci
26898c2ecf20Sopenharmony_ci#ifndef final_version
26908c2ecf20Sopenharmony_ci		if (ev & HFA384X_EV_WTERR) {
26918c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_EXTRA, "%s: WTERR event\n", dev->name);
26928c2ecf20Sopenharmony_ci			HFA384X_OUTW(HFA384X_EV_WTERR, HFA384X_EVACK_OFF);
26938c2ecf20Sopenharmony_ci		}
26948c2ecf20Sopenharmony_ci#endif /* final_version */
26958c2ecf20Sopenharmony_ci
26968c2ecf20Sopenharmony_ci		if (ev & HFA384X_EV_INFDROP) {
26978c2ecf20Sopenharmony_ci			prism2_infdrop(dev);
26988c2ecf20Sopenharmony_ci			HFA384X_OUTW(HFA384X_EV_INFDROP, HFA384X_EVACK_OFF);
26998c2ecf20Sopenharmony_ci		}
27008c2ecf20Sopenharmony_ci
27018c2ecf20Sopenharmony_ci	next_event:
27028c2ecf20Sopenharmony_ci		events++;
27038c2ecf20Sopenharmony_ci		if (events >= PRISM2_MAX_INTERRUPT_EVENTS) {
27048c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_EXTRA, "prism2_interrupt: >%d events "
27058c2ecf20Sopenharmony_ci			       "(EvStat=0x%04x)\n",
27068c2ecf20Sopenharmony_ci			       PRISM2_MAX_INTERRUPT_EVENTS,
27078c2ecf20Sopenharmony_ci			       HFA384X_INW(HFA384X_EVSTAT_OFF));
27088c2ecf20Sopenharmony_ci			break;
27098c2ecf20Sopenharmony_ci		}
27108c2ecf20Sopenharmony_ci	}
27118c2ecf20Sopenharmony_ci	prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 1);
27128c2ecf20Sopenharmony_ci	return IRQ_RETVAL(events);
27138c2ecf20Sopenharmony_ci}
27148c2ecf20Sopenharmony_ci
27158c2ecf20Sopenharmony_ci
27168c2ecf20Sopenharmony_cistatic void prism2_check_sta_fw_version(local_info_t *local)
27178c2ecf20Sopenharmony_ci{
27188c2ecf20Sopenharmony_ci	struct hfa384x_comp_ident comp;
27198c2ecf20Sopenharmony_ci	int id, variant, major, minor;
27208c2ecf20Sopenharmony_ci
27218c2ecf20Sopenharmony_ci	if (hfa384x_get_rid(local->dev, HFA384X_RID_STAID,
27228c2ecf20Sopenharmony_ci			    &comp, sizeof(comp), 1) < 0)
27238c2ecf20Sopenharmony_ci		return;
27248c2ecf20Sopenharmony_ci
27258c2ecf20Sopenharmony_ci	local->fw_ap = 0;
27268c2ecf20Sopenharmony_ci	id = le16_to_cpu(comp.id);
27278c2ecf20Sopenharmony_ci	if (id != HFA384X_COMP_ID_STA) {
27288c2ecf20Sopenharmony_ci		if (id == HFA384X_COMP_ID_FW_AP)
27298c2ecf20Sopenharmony_ci			local->fw_ap = 1;
27308c2ecf20Sopenharmony_ci		return;
27318c2ecf20Sopenharmony_ci	}
27328c2ecf20Sopenharmony_ci
27338c2ecf20Sopenharmony_ci	major = __le16_to_cpu(comp.major);
27348c2ecf20Sopenharmony_ci	minor = __le16_to_cpu(comp.minor);
27358c2ecf20Sopenharmony_ci	variant = __le16_to_cpu(comp.variant);
27368c2ecf20Sopenharmony_ci	local->sta_fw_ver = PRISM2_FW_VER(major, minor, variant);
27378c2ecf20Sopenharmony_ci
27388c2ecf20Sopenharmony_ci	/* Station firmware versions before 1.4.x seem to have a bug in
27398c2ecf20Sopenharmony_ci	 * firmware-based WEP encryption when using Host AP mode, so use
27408c2ecf20Sopenharmony_ci	 * host_encrypt as a default for them. Firmware version 1.4.9 is the
27418c2ecf20Sopenharmony_ci	 * first one that has been seen to produce correct encryption, but the
27428c2ecf20Sopenharmony_ci	 * bug might be fixed before that (although, at least 1.4.2 is broken).
27438c2ecf20Sopenharmony_ci	 */
27448c2ecf20Sopenharmony_ci	local->fw_encrypt_ok = local->sta_fw_ver >= PRISM2_FW_VER(1,4,9);
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_ci	if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt &&
27478c2ecf20Sopenharmony_ci	    !local->fw_encrypt_ok) {
27488c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: defaulting to host-based encryption as "
27498c2ecf20Sopenharmony_ci		       "a workaround for firmware bug in Host AP mode WEP\n",
27508c2ecf20Sopenharmony_ci		       local->dev->name);
27518c2ecf20Sopenharmony_ci		local->host_encrypt = 1;
27528c2ecf20Sopenharmony_ci	}
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_ci	/* IEEE 802.11 standard compliant WDS frames (4 addresses) were broken
27558c2ecf20Sopenharmony_ci	 * in station firmware versions before 1.5.x. With these versions, the
27568c2ecf20Sopenharmony_ci	 * driver uses a workaround with bogus frame format (4th address after
27578c2ecf20Sopenharmony_ci	 * the payload). This is not compatible with other AP devices. Since
27588c2ecf20Sopenharmony_ci	 * the firmware bug is fixed in the latest station firmware versions,
27598c2ecf20Sopenharmony_ci	 * automatically enable standard compliant mode for cards using station
27608c2ecf20Sopenharmony_ci	 * firmware version 1.5.0 or newer. */
27618c2ecf20Sopenharmony_ci	if (local->sta_fw_ver >= PRISM2_FW_VER(1,5,0))
27628c2ecf20Sopenharmony_ci		local->wds_type |= HOSTAP_WDS_STANDARD_FRAME;
27638c2ecf20Sopenharmony_ci	else {
27648c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: defaulting to bogus WDS frame as a "
27658c2ecf20Sopenharmony_ci		       "workaround for firmware bug in Host AP mode WDS\n",
27668c2ecf20Sopenharmony_ci		       local->dev->name);
27678c2ecf20Sopenharmony_ci	}
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_ci	hostap_check_sta_fw_version(local->ap, local->sta_fw_ver);
27708c2ecf20Sopenharmony_ci}
27718c2ecf20Sopenharmony_ci
27728c2ecf20Sopenharmony_ci
27738c2ecf20Sopenharmony_cistatic void hostap_passive_scan(struct timer_list *t)
27748c2ecf20Sopenharmony_ci{
27758c2ecf20Sopenharmony_ci	local_info_t *local = from_timer(local, t, passive_scan_timer);
27768c2ecf20Sopenharmony_ci	struct net_device *dev = local->dev;
27778c2ecf20Sopenharmony_ci	u16 chan;
27788c2ecf20Sopenharmony_ci
27798c2ecf20Sopenharmony_ci	if (local->passive_scan_interval <= 0)
27808c2ecf20Sopenharmony_ci		return;
27818c2ecf20Sopenharmony_ci
27828c2ecf20Sopenharmony_ci	if (local->passive_scan_state == PASSIVE_SCAN_LISTEN) {
27838c2ecf20Sopenharmony_ci		int max_tries = 16;
27848c2ecf20Sopenharmony_ci
27858c2ecf20Sopenharmony_ci		/* Even though host system does not really know when the WLAN
27868c2ecf20Sopenharmony_ci		 * MAC is sending frames, try to avoid changing channels for
27878c2ecf20Sopenharmony_ci		 * passive scanning when a host-generated frame is being
27888c2ecf20Sopenharmony_ci		 * transmitted */
27898c2ecf20Sopenharmony_ci		if (test_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) {
27908c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: passive scan detected pending "
27918c2ecf20Sopenharmony_ci			       "TX - delaying\n", dev->name);
27928c2ecf20Sopenharmony_ci			local->passive_scan_timer.expires = jiffies + HZ / 10;
27938c2ecf20Sopenharmony_ci			add_timer(&local->passive_scan_timer);
27948c2ecf20Sopenharmony_ci			return;
27958c2ecf20Sopenharmony_ci		}
27968c2ecf20Sopenharmony_ci
27978c2ecf20Sopenharmony_ci		do {
27988c2ecf20Sopenharmony_ci			local->passive_scan_channel++;
27998c2ecf20Sopenharmony_ci			if (local->passive_scan_channel > 14)
28008c2ecf20Sopenharmony_ci				local->passive_scan_channel = 1;
28018c2ecf20Sopenharmony_ci			max_tries--;
28028c2ecf20Sopenharmony_ci		} while (!(local->channel_mask &
28038c2ecf20Sopenharmony_ci			   (1 << (local->passive_scan_channel - 1))) &&
28048c2ecf20Sopenharmony_ci			 max_tries > 0);
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_ci		if (max_tries == 0) {
28078c2ecf20Sopenharmony_ci			printk(KERN_INFO "%s: no allowed passive scan channels"
28088c2ecf20Sopenharmony_ci			       " found\n", dev->name);
28098c2ecf20Sopenharmony_ci			return;
28108c2ecf20Sopenharmony_ci		}
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: passive scan channel %d\n",
28138c2ecf20Sopenharmony_ci		       dev->name, local->passive_scan_channel);
28148c2ecf20Sopenharmony_ci		chan = local->passive_scan_channel;
28158c2ecf20Sopenharmony_ci		local->passive_scan_state = PASSIVE_SCAN_WAIT;
28168c2ecf20Sopenharmony_ci		local->passive_scan_timer.expires = jiffies + HZ / 10;
28178c2ecf20Sopenharmony_ci	} else {
28188c2ecf20Sopenharmony_ci		chan = local->channel;
28198c2ecf20Sopenharmony_ci		local->passive_scan_state = PASSIVE_SCAN_LISTEN;
28208c2ecf20Sopenharmony_ci		local->passive_scan_timer.expires = jiffies +
28218c2ecf20Sopenharmony_ci			local->passive_scan_interval * HZ;
28228c2ecf20Sopenharmony_ci	}
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_ci	if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_TEST |
28258c2ecf20Sopenharmony_ci				 (HFA384X_TEST_CHANGE_CHANNEL << 8),
28268c2ecf20Sopenharmony_ci				 chan, NULL, 0))
28278c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: passive scan channel set %d "
28288c2ecf20Sopenharmony_ci		       "failed\n", dev->name, chan);
28298c2ecf20Sopenharmony_ci
28308c2ecf20Sopenharmony_ci	add_timer(&local->passive_scan_timer);
28318c2ecf20Sopenharmony_ci}
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci
28348c2ecf20Sopenharmony_ci/* Called only as a scheduled task when communications quality values should
28358c2ecf20Sopenharmony_ci * be updated. */
28368c2ecf20Sopenharmony_cistatic void handle_comms_qual_update(struct work_struct *work)
28378c2ecf20Sopenharmony_ci{
28388c2ecf20Sopenharmony_ci	local_info_t *local =
28398c2ecf20Sopenharmony_ci		container_of(work, local_info_t, comms_qual_update);
28408c2ecf20Sopenharmony_ci	prism2_update_comms_qual(local->dev);
28418c2ecf20Sopenharmony_ci}
28428c2ecf20Sopenharmony_ci
28438c2ecf20Sopenharmony_ci
28448c2ecf20Sopenharmony_ci/* Software watchdog - called as a timer. Hardware interrupt (Tick event) is
28458c2ecf20Sopenharmony_ci * used to monitor that local->last_tick_timer is being updated. If not,
28468c2ecf20Sopenharmony_ci * interrupt busy-loop is assumed and driver tries to recover by masking out
28478c2ecf20Sopenharmony_ci * some events. */
28488c2ecf20Sopenharmony_cistatic void hostap_tick_timer(struct timer_list *t)
28498c2ecf20Sopenharmony_ci{
28508c2ecf20Sopenharmony_ci	static unsigned long last_inquire = 0;
28518c2ecf20Sopenharmony_ci	local_info_t *local = from_timer(local, t, tick_timer);
28528c2ecf20Sopenharmony_ci	local->last_tick_timer = jiffies;
28538c2ecf20Sopenharmony_ci
28548c2ecf20Sopenharmony_ci	/* Inquire CommTallies every 10 seconds to keep the statistics updated
28558c2ecf20Sopenharmony_ci	 * more often during low load and when using 32-bit tallies. */
28568c2ecf20Sopenharmony_ci	if ((!last_inquire || time_after(jiffies, last_inquire + 10 * HZ)) &&
28578c2ecf20Sopenharmony_ci	    !local->hw_downloading && local->hw_ready &&
28588c2ecf20Sopenharmony_ci	    !local->hw_resetting && local->dev_enabled) {
28598c2ecf20Sopenharmony_ci		hfa384x_cmd_callback(local->dev, HFA384X_CMDCODE_INQUIRE,
28608c2ecf20Sopenharmony_ci				     HFA384X_INFO_COMMTALLIES, NULL, 0);
28618c2ecf20Sopenharmony_ci		last_inquire = jiffies;
28628c2ecf20Sopenharmony_ci	}
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_ci	if ((local->last_comms_qual_update == 0 ||
28658c2ecf20Sopenharmony_ci	     time_after(jiffies, local->last_comms_qual_update + 10 * HZ)) &&
28668c2ecf20Sopenharmony_ci	    (local->iw_mode == IW_MODE_INFRA ||
28678c2ecf20Sopenharmony_ci	     local->iw_mode == IW_MODE_ADHOC)) {
28688c2ecf20Sopenharmony_ci		schedule_work(&local->comms_qual_update);
28698c2ecf20Sopenharmony_ci	}
28708c2ecf20Sopenharmony_ci
28718c2ecf20Sopenharmony_ci	local->tick_timer.expires = jiffies + 2 * HZ;
28728c2ecf20Sopenharmony_ci	add_timer(&local->tick_timer);
28738c2ecf20Sopenharmony_ci}
28748c2ecf20Sopenharmony_ci
28758c2ecf20Sopenharmony_ci
28768c2ecf20Sopenharmony_ci#if !defined(PRISM2_NO_PROCFS_DEBUG) && defined(CONFIG_PROC_FS)
28778c2ecf20Sopenharmony_cistatic u16 hfa384x_read_reg(struct net_device *dev, u16 reg)
28788c2ecf20Sopenharmony_ci{
28798c2ecf20Sopenharmony_ci	return HFA384X_INW(reg);
28808c2ecf20Sopenharmony_ci}
28818c2ecf20Sopenharmony_ci
28828c2ecf20Sopenharmony_cistatic int prism2_registers_proc_show(struct seq_file *m, void *v)
28838c2ecf20Sopenharmony_ci{
28848c2ecf20Sopenharmony_ci	local_info_t *local = m->private;
28858c2ecf20Sopenharmony_ci
28868c2ecf20Sopenharmony_ci#define SHOW_REG(n) \
28878c2ecf20Sopenharmony_ci  seq_printf(m, #n "=%04x\n", hfa384x_read_reg(local->dev, HFA384X_##n##_OFF))
28888c2ecf20Sopenharmony_ci
28898c2ecf20Sopenharmony_ci	SHOW_REG(CMD);
28908c2ecf20Sopenharmony_ci	SHOW_REG(PARAM0);
28918c2ecf20Sopenharmony_ci	SHOW_REG(PARAM1);
28928c2ecf20Sopenharmony_ci	SHOW_REG(PARAM2);
28938c2ecf20Sopenharmony_ci	SHOW_REG(STATUS);
28948c2ecf20Sopenharmony_ci	SHOW_REG(RESP0);
28958c2ecf20Sopenharmony_ci	SHOW_REG(RESP1);
28968c2ecf20Sopenharmony_ci	SHOW_REG(RESP2);
28978c2ecf20Sopenharmony_ci	SHOW_REG(INFOFID);
28988c2ecf20Sopenharmony_ci	SHOW_REG(CONTROL);
28998c2ecf20Sopenharmony_ci	SHOW_REG(SELECT0);
29008c2ecf20Sopenharmony_ci	SHOW_REG(SELECT1);
29018c2ecf20Sopenharmony_ci	SHOW_REG(OFFSET0);
29028c2ecf20Sopenharmony_ci	SHOW_REG(OFFSET1);
29038c2ecf20Sopenharmony_ci	SHOW_REG(RXFID);
29048c2ecf20Sopenharmony_ci	SHOW_REG(ALLOCFID);
29058c2ecf20Sopenharmony_ci	SHOW_REG(TXCOMPLFID);
29068c2ecf20Sopenharmony_ci	SHOW_REG(SWSUPPORT0);
29078c2ecf20Sopenharmony_ci	SHOW_REG(SWSUPPORT1);
29088c2ecf20Sopenharmony_ci	SHOW_REG(SWSUPPORT2);
29098c2ecf20Sopenharmony_ci	SHOW_REG(EVSTAT);
29108c2ecf20Sopenharmony_ci	SHOW_REG(INTEN);
29118c2ecf20Sopenharmony_ci	SHOW_REG(EVACK);
29128c2ecf20Sopenharmony_ci	/* Do not read data registers, because they change the state of the
29138c2ecf20Sopenharmony_ci	 * MAC (offset += 2) */
29148c2ecf20Sopenharmony_ci	/* SHOW_REG(DATA0); */
29158c2ecf20Sopenharmony_ci	/* SHOW_REG(DATA1); */
29168c2ecf20Sopenharmony_ci	SHOW_REG(AUXPAGE);
29178c2ecf20Sopenharmony_ci	SHOW_REG(AUXOFFSET);
29188c2ecf20Sopenharmony_ci	/* SHOW_REG(AUXDATA); */
29198c2ecf20Sopenharmony_ci#ifdef PRISM2_PCI
29208c2ecf20Sopenharmony_ci	SHOW_REG(PCICOR);
29218c2ecf20Sopenharmony_ci	SHOW_REG(PCIHCR);
29228c2ecf20Sopenharmony_ci	SHOW_REG(PCI_M0_ADDRH);
29238c2ecf20Sopenharmony_ci	SHOW_REG(PCI_M0_ADDRL);
29248c2ecf20Sopenharmony_ci	SHOW_REG(PCI_M0_LEN);
29258c2ecf20Sopenharmony_ci	SHOW_REG(PCI_M0_CTL);
29268c2ecf20Sopenharmony_ci	SHOW_REG(PCI_STATUS);
29278c2ecf20Sopenharmony_ci	SHOW_REG(PCI_M1_ADDRH);
29288c2ecf20Sopenharmony_ci	SHOW_REG(PCI_M1_ADDRL);
29298c2ecf20Sopenharmony_ci	SHOW_REG(PCI_M1_LEN);
29308c2ecf20Sopenharmony_ci	SHOW_REG(PCI_M1_CTL);
29318c2ecf20Sopenharmony_ci#endif /* PRISM2_PCI */
29328c2ecf20Sopenharmony_ci
29338c2ecf20Sopenharmony_ci	return 0;
29348c2ecf20Sopenharmony_ci}
29358c2ecf20Sopenharmony_ci#endif
29368c2ecf20Sopenharmony_ci
29378c2ecf20Sopenharmony_cistruct set_tim_data {
29388c2ecf20Sopenharmony_ci	struct list_head list;
29398c2ecf20Sopenharmony_ci	int aid;
29408c2ecf20Sopenharmony_ci	int set;
29418c2ecf20Sopenharmony_ci};
29428c2ecf20Sopenharmony_ci
29438c2ecf20Sopenharmony_cistatic int prism2_set_tim(struct net_device *dev, int aid, int set)
29448c2ecf20Sopenharmony_ci{
29458c2ecf20Sopenharmony_ci	struct list_head *ptr;
29468c2ecf20Sopenharmony_ci	struct set_tim_data *new_entry;
29478c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
29488c2ecf20Sopenharmony_ci	local_info_t *local;
29498c2ecf20Sopenharmony_ci
29508c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
29518c2ecf20Sopenharmony_ci	local = iface->local;
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_ci	new_entry = kzalloc(sizeof(*new_entry), GFP_ATOMIC);
29548c2ecf20Sopenharmony_ci	if (new_entry == NULL)
29558c2ecf20Sopenharmony_ci		return -ENOMEM;
29568c2ecf20Sopenharmony_ci
29578c2ecf20Sopenharmony_ci	new_entry->aid = aid;
29588c2ecf20Sopenharmony_ci	new_entry->set = set;
29598c2ecf20Sopenharmony_ci
29608c2ecf20Sopenharmony_ci	spin_lock_bh(&local->set_tim_lock);
29618c2ecf20Sopenharmony_ci	list_for_each(ptr, &local->set_tim_list) {
29628c2ecf20Sopenharmony_ci		struct set_tim_data *entry =
29638c2ecf20Sopenharmony_ci			list_entry(ptr, struct set_tim_data, list);
29648c2ecf20Sopenharmony_ci		if (entry->aid == aid) {
29658c2ecf20Sopenharmony_ci			PDEBUG(DEBUG_PS2, "%s: prism2_set_tim: aid=%d "
29668c2ecf20Sopenharmony_ci			       "set=%d ==> %d\n",
29678c2ecf20Sopenharmony_ci			       local->dev->name, aid, entry->set, set);
29688c2ecf20Sopenharmony_ci			entry->set = set;
29698c2ecf20Sopenharmony_ci			kfree(new_entry);
29708c2ecf20Sopenharmony_ci			new_entry = NULL;
29718c2ecf20Sopenharmony_ci			break;
29728c2ecf20Sopenharmony_ci		}
29738c2ecf20Sopenharmony_ci	}
29748c2ecf20Sopenharmony_ci	if (new_entry)
29758c2ecf20Sopenharmony_ci		list_add_tail(&new_entry->list, &local->set_tim_list);
29768c2ecf20Sopenharmony_ci	spin_unlock_bh(&local->set_tim_lock);
29778c2ecf20Sopenharmony_ci
29788c2ecf20Sopenharmony_ci	schedule_work(&local->set_tim_queue);
29798c2ecf20Sopenharmony_ci
29808c2ecf20Sopenharmony_ci	return 0;
29818c2ecf20Sopenharmony_ci}
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_ci
29848c2ecf20Sopenharmony_cistatic void handle_set_tim_queue(struct work_struct *work)
29858c2ecf20Sopenharmony_ci{
29868c2ecf20Sopenharmony_ci	local_info_t *local = container_of(work, local_info_t, set_tim_queue);
29878c2ecf20Sopenharmony_ci	struct set_tim_data *entry;
29888c2ecf20Sopenharmony_ci	u16 val;
29898c2ecf20Sopenharmony_ci
29908c2ecf20Sopenharmony_ci	for (;;) {
29918c2ecf20Sopenharmony_ci		entry = NULL;
29928c2ecf20Sopenharmony_ci		spin_lock_bh(&local->set_tim_lock);
29938c2ecf20Sopenharmony_ci		if (!list_empty(&local->set_tim_list)) {
29948c2ecf20Sopenharmony_ci			entry = list_entry(local->set_tim_list.next,
29958c2ecf20Sopenharmony_ci					   struct set_tim_data, list);
29968c2ecf20Sopenharmony_ci			list_del(&entry->list);
29978c2ecf20Sopenharmony_ci		}
29988c2ecf20Sopenharmony_ci		spin_unlock_bh(&local->set_tim_lock);
29998c2ecf20Sopenharmony_ci		if (!entry)
30008c2ecf20Sopenharmony_ci			break;
30018c2ecf20Sopenharmony_ci
30028c2ecf20Sopenharmony_ci		PDEBUG(DEBUG_PS2, "%s: handle_set_tim_queue: aid=%d set=%d\n",
30038c2ecf20Sopenharmony_ci		       local->dev->name, entry->aid, entry->set);
30048c2ecf20Sopenharmony_ci
30058c2ecf20Sopenharmony_ci		val = entry->aid;
30068c2ecf20Sopenharmony_ci		if (entry->set)
30078c2ecf20Sopenharmony_ci			val |= 0x8000;
30088c2ecf20Sopenharmony_ci		if (hostap_set_word(local->dev, HFA384X_RID_CNFTIMCTRL, val)) {
30098c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: set_tim failed (aid=%d "
30108c2ecf20Sopenharmony_ci			       "set=%d)\n",
30118c2ecf20Sopenharmony_ci			       local->dev->name, entry->aid, entry->set);
30128c2ecf20Sopenharmony_ci		}
30138c2ecf20Sopenharmony_ci
30148c2ecf20Sopenharmony_ci		kfree(entry);
30158c2ecf20Sopenharmony_ci	}
30168c2ecf20Sopenharmony_ci}
30178c2ecf20Sopenharmony_ci
30188c2ecf20Sopenharmony_ci
30198c2ecf20Sopenharmony_cistatic void prism2_clear_set_tim_queue(local_info_t *local)
30208c2ecf20Sopenharmony_ci{
30218c2ecf20Sopenharmony_ci	struct list_head *ptr, *n;
30228c2ecf20Sopenharmony_ci
30238c2ecf20Sopenharmony_ci	list_for_each_safe(ptr, n, &local->set_tim_list) {
30248c2ecf20Sopenharmony_ci		struct set_tim_data *entry;
30258c2ecf20Sopenharmony_ci		entry = list_entry(ptr, struct set_tim_data, list);
30268c2ecf20Sopenharmony_ci		list_del(&entry->list);
30278c2ecf20Sopenharmony_ci		kfree(entry);
30288c2ecf20Sopenharmony_ci	}
30298c2ecf20Sopenharmony_ci}
30308c2ecf20Sopenharmony_ci
30318c2ecf20Sopenharmony_ci
30328c2ecf20Sopenharmony_ci/*
30338c2ecf20Sopenharmony_ci * HostAP uses two layers of net devices, where the inner
30348c2ecf20Sopenharmony_ci * layer gets called all the time from the outer layer.
30358c2ecf20Sopenharmony_ci * This is a natural nesting, which needs a split lock type.
30368c2ecf20Sopenharmony_ci */
30378c2ecf20Sopenharmony_cistatic struct lock_class_key hostap_netdev_xmit_lock_key;
30388c2ecf20Sopenharmony_cistatic struct lock_class_key hostap_netdev_addr_lock_key;
30398c2ecf20Sopenharmony_ci
30408c2ecf20Sopenharmony_cistatic void prism2_set_lockdep_class_one(struct net_device *dev,
30418c2ecf20Sopenharmony_ci					 struct netdev_queue *txq,
30428c2ecf20Sopenharmony_ci					 void *_unused)
30438c2ecf20Sopenharmony_ci{
30448c2ecf20Sopenharmony_ci	lockdep_set_class(&txq->_xmit_lock,
30458c2ecf20Sopenharmony_ci			  &hostap_netdev_xmit_lock_key);
30468c2ecf20Sopenharmony_ci}
30478c2ecf20Sopenharmony_ci
30488c2ecf20Sopenharmony_cistatic void prism2_set_lockdep_class(struct net_device *dev)
30498c2ecf20Sopenharmony_ci{
30508c2ecf20Sopenharmony_ci	lockdep_set_class(&dev->addr_list_lock,
30518c2ecf20Sopenharmony_ci			  &hostap_netdev_addr_lock_key);
30528c2ecf20Sopenharmony_ci	netdev_for_each_tx_queue(dev, prism2_set_lockdep_class_one, NULL);
30538c2ecf20Sopenharmony_ci}
30548c2ecf20Sopenharmony_ci
30558c2ecf20Sopenharmony_cistatic struct net_device *
30568c2ecf20Sopenharmony_ciprism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx,
30578c2ecf20Sopenharmony_ci		       struct device *sdev)
30588c2ecf20Sopenharmony_ci{
30598c2ecf20Sopenharmony_ci	struct net_device *dev;
30608c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
30618c2ecf20Sopenharmony_ci	struct local_info *local;
30628c2ecf20Sopenharmony_ci	int len, i, ret;
30638c2ecf20Sopenharmony_ci
30648c2ecf20Sopenharmony_ci	if (funcs == NULL)
30658c2ecf20Sopenharmony_ci		return NULL;
30668c2ecf20Sopenharmony_ci
30678c2ecf20Sopenharmony_ci	len = strlen(dev_template);
30688c2ecf20Sopenharmony_ci	if (len >= IFNAMSIZ || strstr(dev_template, "%d") == NULL) {
30698c2ecf20Sopenharmony_ci		printk(KERN_WARNING "hostap: Invalid dev_template='%s'\n",
30708c2ecf20Sopenharmony_ci		       dev_template);
30718c2ecf20Sopenharmony_ci		return NULL;
30728c2ecf20Sopenharmony_ci	}
30738c2ecf20Sopenharmony_ci
30748c2ecf20Sopenharmony_ci	len = sizeof(struct hostap_interface) +
30758c2ecf20Sopenharmony_ci		3 + sizeof(struct local_info) +
30768c2ecf20Sopenharmony_ci		3 + sizeof(struct ap_data);
30778c2ecf20Sopenharmony_ci
30788c2ecf20Sopenharmony_ci	dev = alloc_etherdev(len);
30798c2ecf20Sopenharmony_ci	if (dev == NULL)
30808c2ecf20Sopenharmony_ci		return NULL;
30818c2ecf20Sopenharmony_ci
30828c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
30838c2ecf20Sopenharmony_ci	local = (struct local_info *) ((((long) (iface + 1)) + 3) & ~3);
30848c2ecf20Sopenharmony_ci	local->ap = (struct ap_data *) ((((long) (local + 1)) + 3) & ~3);
30858c2ecf20Sopenharmony_ci	local->dev = iface->dev = dev;
30868c2ecf20Sopenharmony_ci	iface->local = local;
30878c2ecf20Sopenharmony_ci	iface->type = HOSTAP_INTERFACE_MASTER;
30888c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&local->hostap_interfaces);
30898c2ecf20Sopenharmony_ci
30908c2ecf20Sopenharmony_ci	local->hw_module = THIS_MODULE;
30918c2ecf20Sopenharmony_ci
30928c2ecf20Sopenharmony_ci#ifdef PRISM2_IO_DEBUG
30938c2ecf20Sopenharmony_ci	local->io_debug_enabled = 1;
30948c2ecf20Sopenharmony_ci#endif /* PRISM2_IO_DEBUG */
30958c2ecf20Sopenharmony_ci
30968c2ecf20Sopenharmony_ci	local->func = funcs;
30978c2ecf20Sopenharmony_ci	local->func->cmd = hfa384x_cmd;
30988c2ecf20Sopenharmony_ci	local->func->read_regs = hfa384x_read_regs;
30998c2ecf20Sopenharmony_ci	local->func->get_rid = hfa384x_get_rid;
31008c2ecf20Sopenharmony_ci	local->func->set_rid = hfa384x_set_rid;
31018c2ecf20Sopenharmony_ci	local->func->hw_enable = prism2_hw_enable;
31028c2ecf20Sopenharmony_ci	local->func->hw_config = prism2_hw_config;
31038c2ecf20Sopenharmony_ci	local->func->hw_reset = prism2_hw_reset;
31048c2ecf20Sopenharmony_ci	local->func->hw_shutdown = prism2_hw_shutdown;
31058c2ecf20Sopenharmony_ci	local->func->reset_port = prism2_reset_port;
31068c2ecf20Sopenharmony_ci	local->func->schedule_reset = prism2_schedule_reset;
31078c2ecf20Sopenharmony_ci#ifdef PRISM2_DOWNLOAD_SUPPORT
31088c2ecf20Sopenharmony_ci	local->func->read_aux_proc_ops = &prism2_download_aux_dump_proc_ops;
31098c2ecf20Sopenharmony_ci	local->func->download = prism2_download;
31108c2ecf20Sopenharmony_ci#endif /* PRISM2_DOWNLOAD_SUPPORT */
31118c2ecf20Sopenharmony_ci	local->func->tx = prism2_tx_80211;
31128c2ecf20Sopenharmony_ci	local->func->set_tim = prism2_set_tim;
31138c2ecf20Sopenharmony_ci	local->func->need_tx_headroom = 0; /* no need to add txdesc in
31148c2ecf20Sopenharmony_ci					    * skb->data (FIX: maybe for DMA bus
31158c2ecf20Sopenharmony_ci					    * mastering? */
31168c2ecf20Sopenharmony_ci
31178c2ecf20Sopenharmony_ci	local->mtu = mtu;
31188c2ecf20Sopenharmony_ci
31198c2ecf20Sopenharmony_ci	rwlock_init(&local->iface_lock);
31208c2ecf20Sopenharmony_ci	spin_lock_init(&local->txfidlock);
31218c2ecf20Sopenharmony_ci	spin_lock_init(&local->cmdlock);
31228c2ecf20Sopenharmony_ci	spin_lock_init(&local->baplock);
31238c2ecf20Sopenharmony_ci	spin_lock_init(&local->lock);
31248c2ecf20Sopenharmony_ci	spin_lock_init(&local->irq_init_lock);
31258c2ecf20Sopenharmony_ci	mutex_init(&local->rid_bap_mtx);
31268c2ecf20Sopenharmony_ci
31278c2ecf20Sopenharmony_ci	if (card_idx < 0 || card_idx >= MAX_PARM_DEVICES)
31288c2ecf20Sopenharmony_ci		card_idx = 0;
31298c2ecf20Sopenharmony_ci	local->card_idx = card_idx;
31308c2ecf20Sopenharmony_ci
31318c2ecf20Sopenharmony_ci	len = strlen(essid);
31328c2ecf20Sopenharmony_ci	memcpy(local->essid, essid,
31338c2ecf20Sopenharmony_ci	       len > MAX_SSID_LEN ? MAX_SSID_LEN : len);
31348c2ecf20Sopenharmony_ci	local->essid[MAX_SSID_LEN] = '\0';
31358c2ecf20Sopenharmony_ci	i = GET_INT_PARM(iw_mode, card_idx);
31368c2ecf20Sopenharmony_ci	if ((i >= IW_MODE_ADHOC && i <= IW_MODE_REPEAT) ||
31378c2ecf20Sopenharmony_ci	    i == IW_MODE_MONITOR) {
31388c2ecf20Sopenharmony_ci		local->iw_mode = i;
31398c2ecf20Sopenharmony_ci	} else {
31408c2ecf20Sopenharmony_ci		printk(KERN_WARNING "prism2: Unknown iw_mode %d; using "
31418c2ecf20Sopenharmony_ci		       "IW_MODE_MASTER\n", i);
31428c2ecf20Sopenharmony_ci		local->iw_mode = IW_MODE_MASTER;
31438c2ecf20Sopenharmony_ci	}
31448c2ecf20Sopenharmony_ci	local->channel = GET_INT_PARM(channel, card_idx);
31458c2ecf20Sopenharmony_ci	local->beacon_int = GET_INT_PARM(beacon_int, card_idx);
31468c2ecf20Sopenharmony_ci	local->dtim_period = GET_INT_PARM(dtim_period, card_idx);
31478c2ecf20Sopenharmony_ci	local->wds_max_connections = 16;
31488c2ecf20Sopenharmony_ci	local->tx_control = HFA384X_TX_CTRL_FLAGS;
31498c2ecf20Sopenharmony_ci	local->manual_retry_count = -1;
31508c2ecf20Sopenharmony_ci	local->rts_threshold = 2347;
31518c2ecf20Sopenharmony_ci	local->fragm_threshold = 2346;
31528c2ecf20Sopenharmony_ci	local->rssi_to_dBm = 100; /* default; to be overriden by
31538c2ecf20Sopenharmony_ci				   * cnfDbmAdjust, if available */
31548c2ecf20Sopenharmony_ci	local->auth_algs = PRISM2_AUTH_OPEN | PRISM2_AUTH_SHARED_KEY;
31558c2ecf20Sopenharmony_ci	local->sram_type = -1;
31568c2ecf20Sopenharmony_ci	local->scan_channel_mask = 0xffff;
31578c2ecf20Sopenharmony_ci	local->monitor_type = PRISM2_MONITOR_RADIOTAP;
31588c2ecf20Sopenharmony_ci
31598c2ecf20Sopenharmony_ci	/* Initialize task queue structures */
31608c2ecf20Sopenharmony_ci	INIT_WORK(&local->reset_queue, handle_reset_queue);
31618c2ecf20Sopenharmony_ci	INIT_WORK(&local->set_multicast_list_queue,
31628c2ecf20Sopenharmony_ci		  hostap_set_multicast_list_queue);
31638c2ecf20Sopenharmony_ci
31648c2ecf20Sopenharmony_ci	INIT_WORK(&local->set_tim_queue, handle_set_tim_queue);
31658c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&local->set_tim_list);
31668c2ecf20Sopenharmony_ci	spin_lock_init(&local->set_tim_lock);
31678c2ecf20Sopenharmony_ci
31688c2ecf20Sopenharmony_ci	INIT_WORK(&local->comms_qual_update, handle_comms_qual_update);
31698c2ecf20Sopenharmony_ci
31708c2ecf20Sopenharmony_ci	/* Initialize tasklets for handling hardware IRQ related operations
31718c2ecf20Sopenharmony_ci	 * outside hw IRQ handler */
31728c2ecf20Sopenharmony_ci#define HOSTAP_TASKLET_INIT(q, f, d) \
31738c2ecf20Sopenharmony_cido { memset((q), 0, sizeof(*(q))); (q)->func = (void(*)(unsigned long))(f); } \
31748c2ecf20Sopenharmony_ciwhile (0)
31758c2ecf20Sopenharmony_ci	HOSTAP_TASKLET_INIT(&local->bap_tasklet, hostap_bap_tasklet,
31768c2ecf20Sopenharmony_ci			    (unsigned long) local);
31778c2ecf20Sopenharmony_ci
31788c2ecf20Sopenharmony_ci	HOSTAP_TASKLET_INIT(&local->info_tasklet, hostap_info_tasklet,
31798c2ecf20Sopenharmony_ci			    (unsigned long) local);
31808c2ecf20Sopenharmony_ci	hostap_info_init(local);
31818c2ecf20Sopenharmony_ci
31828c2ecf20Sopenharmony_ci	HOSTAP_TASKLET_INIT(&local->rx_tasklet,
31838c2ecf20Sopenharmony_ci			    hostap_rx_tasklet, (unsigned long) local);
31848c2ecf20Sopenharmony_ci	skb_queue_head_init(&local->rx_list);
31858c2ecf20Sopenharmony_ci
31868c2ecf20Sopenharmony_ci	HOSTAP_TASKLET_INIT(&local->sta_tx_exc_tasklet,
31878c2ecf20Sopenharmony_ci			    hostap_sta_tx_exc_tasklet, (unsigned long) local);
31888c2ecf20Sopenharmony_ci	skb_queue_head_init(&local->sta_tx_exc_list);
31898c2ecf20Sopenharmony_ci
31908c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&local->cmd_queue);
31918c2ecf20Sopenharmony_ci	init_waitqueue_head(&local->hostscan_wq);
31928c2ecf20Sopenharmony_ci
31938c2ecf20Sopenharmony_ci	lib80211_crypt_info_init(&local->crypt_info, dev->name, &local->lock);
31948c2ecf20Sopenharmony_ci
31958c2ecf20Sopenharmony_ci	timer_setup(&local->passive_scan_timer, hostap_passive_scan, 0);
31968c2ecf20Sopenharmony_ci	timer_setup(&local->tick_timer, hostap_tick_timer, 0);
31978c2ecf20Sopenharmony_ci	local->tick_timer.expires = jiffies + 2 * HZ;
31988c2ecf20Sopenharmony_ci	add_timer(&local->tick_timer);
31998c2ecf20Sopenharmony_ci
32008c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&local->bss_list);
32018c2ecf20Sopenharmony_ci
32028c2ecf20Sopenharmony_ci	hostap_setup_dev(dev, local, HOSTAP_INTERFACE_MASTER);
32038c2ecf20Sopenharmony_ci
32048c2ecf20Sopenharmony_ci	dev->type = ARPHRD_IEEE80211;
32058c2ecf20Sopenharmony_ci	dev->header_ops = &hostap_80211_ops;
32068c2ecf20Sopenharmony_ci
32078c2ecf20Sopenharmony_ci	rtnl_lock();
32088c2ecf20Sopenharmony_ci	ret = dev_alloc_name(dev, "wifi%d");
32098c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, sdev);
32108c2ecf20Sopenharmony_ci	if (ret >= 0)
32118c2ecf20Sopenharmony_ci		ret = register_netdevice(dev);
32128c2ecf20Sopenharmony_ci
32138c2ecf20Sopenharmony_ci	prism2_set_lockdep_class(dev);
32148c2ecf20Sopenharmony_ci	rtnl_unlock();
32158c2ecf20Sopenharmony_ci	if (ret < 0) {
32168c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: register netdevice failed!\n",
32178c2ecf20Sopenharmony_ci		       dev_info);
32188c2ecf20Sopenharmony_ci		goto fail;
32198c2ecf20Sopenharmony_ci	}
32208c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: Registered netdevice %s\n", dev_info, dev->name);
32218c2ecf20Sopenharmony_ci
32228c2ecf20Sopenharmony_ci	hostap_init_data(local);
32238c2ecf20Sopenharmony_ci	return dev;
32248c2ecf20Sopenharmony_ci
32258c2ecf20Sopenharmony_ci fail:
32268c2ecf20Sopenharmony_ci	free_netdev(dev);
32278c2ecf20Sopenharmony_ci	return NULL;
32288c2ecf20Sopenharmony_ci}
32298c2ecf20Sopenharmony_ci
32308c2ecf20Sopenharmony_ci
32318c2ecf20Sopenharmony_cistatic int hostap_hw_ready(struct net_device *dev)
32328c2ecf20Sopenharmony_ci{
32338c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
32348c2ecf20Sopenharmony_ci	struct local_info *local;
32358c2ecf20Sopenharmony_ci
32368c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
32378c2ecf20Sopenharmony_ci	local = iface->local;
32388c2ecf20Sopenharmony_ci	local->ddev = hostap_add_interface(local, HOSTAP_INTERFACE_MAIN, 0,
32398c2ecf20Sopenharmony_ci					   "", dev_template);
32408c2ecf20Sopenharmony_ci
32418c2ecf20Sopenharmony_ci	if (local->ddev) {
32428c2ecf20Sopenharmony_ci		if (local->iw_mode == IW_MODE_INFRA ||
32438c2ecf20Sopenharmony_ci		    local->iw_mode == IW_MODE_ADHOC) {
32448c2ecf20Sopenharmony_ci			netif_carrier_off(local->dev);
32458c2ecf20Sopenharmony_ci			netif_carrier_off(local->ddev);
32468c2ecf20Sopenharmony_ci		}
32478c2ecf20Sopenharmony_ci		hostap_init_proc(local);
32488c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_PROCFS_DEBUG
32498c2ecf20Sopenharmony_ci		proc_create_single_data("registers", 0, local->proc,
32508c2ecf20Sopenharmony_ci				 prism2_registers_proc_show, local);
32518c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_PROCFS_DEBUG */
32528c2ecf20Sopenharmony_ci		hostap_init_ap_proc(local);
32538c2ecf20Sopenharmony_ci		return 0;
32548c2ecf20Sopenharmony_ci	}
32558c2ecf20Sopenharmony_ci
32568c2ecf20Sopenharmony_ci	return -1;
32578c2ecf20Sopenharmony_ci}
32588c2ecf20Sopenharmony_ci
32598c2ecf20Sopenharmony_ci
32608c2ecf20Sopenharmony_cistatic void prism2_free_local_data(struct net_device *dev)
32618c2ecf20Sopenharmony_ci{
32628c2ecf20Sopenharmony_ci	struct hostap_tx_callback_info *tx_cb, *tx_cb_prev;
32638c2ecf20Sopenharmony_ci	int i;
32648c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
32658c2ecf20Sopenharmony_ci	struct local_info *local;
32668c2ecf20Sopenharmony_ci	struct list_head *ptr, *n;
32678c2ecf20Sopenharmony_ci
32688c2ecf20Sopenharmony_ci	if (dev == NULL)
32698c2ecf20Sopenharmony_ci		return;
32708c2ecf20Sopenharmony_ci
32718c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
32728c2ecf20Sopenharmony_ci	local = iface->local;
32738c2ecf20Sopenharmony_ci
32748c2ecf20Sopenharmony_ci	/* Unregister all netdevs before freeing local data. */
32758c2ecf20Sopenharmony_ci	list_for_each_safe(ptr, n, &local->hostap_interfaces) {
32768c2ecf20Sopenharmony_ci		iface = list_entry(ptr, struct hostap_interface, list);
32778c2ecf20Sopenharmony_ci		if (iface->type == HOSTAP_INTERFACE_MASTER) {
32788c2ecf20Sopenharmony_ci			/* special handling for this interface below */
32798c2ecf20Sopenharmony_ci			continue;
32808c2ecf20Sopenharmony_ci		}
32818c2ecf20Sopenharmony_ci		hostap_remove_interface(iface->dev, 0, 1);
32828c2ecf20Sopenharmony_ci	}
32838c2ecf20Sopenharmony_ci
32848c2ecf20Sopenharmony_ci	unregister_netdev(local->dev);
32858c2ecf20Sopenharmony_ci
32868c2ecf20Sopenharmony_ci	flush_work(&local->reset_queue);
32878c2ecf20Sopenharmony_ci	flush_work(&local->set_multicast_list_queue);
32888c2ecf20Sopenharmony_ci	flush_work(&local->set_tim_queue);
32898c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_STATION_MODES
32908c2ecf20Sopenharmony_ci	flush_work(&local->info_queue);
32918c2ecf20Sopenharmony_ci#endif
32928c2ecf20Sopenharmony_ci	flush_work(&local->comms_qual_update);
32938c2ecf20Sopenharmony_ci
32948c2ecf20Sopenharmony_ci	lib80211_crypt_info_free(&local->crypt_info);
32958c2ecf20Sopenharmony_ci
32968c2ecf20Sopenharmony_ci	if (timer_pending(&local->passive_scan_timer))
32978c2ecf20Sopenharmony_ci		del_timer(&local->passive_scan_timer);
32988c2ecf20Sopenharmony_ci
32998c2ecf20Sopenharmony_ci	if (timer_pending(&local->tick_timer))
33008c2ecf20Sopenharmony_ci		del_timer(&local->tick_timer);
33018c2ecf20Sopenharmony_ci
33028c2ecf20Sopenharmony_ci	prism2_clear_cmd_queue(local);
33038c2ecf20Sopenharmony_ci
33048c2ecf20Sopenharmony_ci	skb_queue_purge(&local->info_list);
33058c2ecf20Sopenharmony_ci	skb_queue_purge(&local->rx_list);
33068c2ecf20Sopenharmony_ci	skb_queue_purge(&local->sta_tx_exc_list);
33078c2ecf20Sopenharmony_ci
33088c2ecf20Sopenharmony_ci	if (local->dev_enabled)
33098c2ecf20Sopenharmony_ci		prism2_callback(local, PRISM2_CALLBACK_DISABLE);
33108c2ecf20Sopenharmony_ci
33118c2ecf20Sopenharmony_ci	if (local->ap != NULL)
33128c2ecf20Sopenharmony_ci		hostap_free_data(local->ap);
33138c2ecf20Sopenharmony_ci
33148c2ecf20Sopenharmony_ci#ifndef PRISM2_NO_PROCFS_DEBUG
33158c2ecf20Sopenharmony_ci	if (local->proc != NULL)
33168c2ecf20Sopenharmony_ci		remove_proc_entry("registers", local->proc);
33178c2ecf20Sopenharmony_ci#endif /* PRISM2_NO_PROCFS_DEBUG */
33188c2ecf20Sopenharmony_ci	hostap_remove_proc(local);
33198c2ecf20Sopenharmony_ci
33208c2ecf20Sopenharmony_ci	tx_cb = local->tx_callback;
33218c2ecf20Sopenharmony_ci	while (tx_cb != NULL) {
33228c2ecf20Sopenharmony_ci		tx_cb_prev = tx_cb;
33238c2ecf20Sopenharmony_ci		tx_cb = tx_cb->next;
33248c2ecf20Sopenharmony_ci		kfree(tx_cb_prev);
33258c2ecf20Sopenharmony_ci	}
33268c2ecf20Sopenharmony_ci
33278c2ecf20Sopenharmony_ci	hostap_set_hostapd(local, 0, 0);
33288c2ecf20Sopenharmony_ci	hostap_set_hostapd_sta(local, 0, 0);
33298c2ecf20Sopenharmony_ci
33308c2ecf20Sopenharmony_ci	for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) {
33318c2ecf20Sopenharmony_ci		if (local->frag_cache[i].skb != NULL)
33328c2ecf20Sopenharmony_ci			dev_kfree_skb(local->frag_cache[i].skb);
33338c2ecf20Sopenharmony_ci	}
33348c2ecf20Sopenharmony_ci
33358c2ecf20Sopenharmony_ci#ifdef PRISM2_DOWNLOAD_SUPPORT
33368c2ecf20Sopenharmony_ci	prism2_download_free_data(local->dl_pri);
33378c2ecf20Sopenharmony_ci	prism2_download_free_data(local->dl_sec);
33388c2ecf20Sopenharmony_ci#endif /* PRISM2_DOWNLOAD_SUPPORT */
33398c2ecf20Sopenharmony_ci
33408c2ecf20Sopenharmony_ci	prism2_clear_set_tim_queue(local);
33418c2ecf20Sopenharmony_ci
33428c2ecf20Sopenharmony_ci	list_for_each_safe(ptr, n, &local->bss_list) {
33438c2ecf20Sopenharmony_ci		struct hostap_bss_info *bss =
33448c2ecf20Sopenharmony_ci			list_entry(ptr, struct hostap_bss_info, list);
33458c2ecf20Sopenharmony_ci		kfree(bss);
33468c2ecf20Sopenharmony_ci	}
33478c2ecf20Sopenharmony_ci
33488c2ecf20Sopenharmony_ci	kfree(local->pda);
33498c2ecf20Sopenharmony_ci	kfree(local->last_scan_results);
33508c2ecf20Sopenharmony_ci	kfree(local->generic_elem);
33518c2ecf20Sopenharmony_ci
33528c2ecf20Sopenharmony_ci	free_netdev(local->dev);
33538c2ecf20Sopenharmony_ci}
33548c2ecf20Sopenharmony_ci
33558c2ecf20Sopenharmony_ci
33568c2ecf20Sopenharmony_ci#if defined(PRISM2_PCI) || defined(PRISM2_PCCARD)
33578c2ecf20Sopenharmony_cistatic void __maybe_unused prism2_suspend(struct net_device *dev)
33588c2ecf20Sopenharmony_ci{
33598c2ecf20Sopenharmony_ci	struct hostap_interface *iface;
33608c2ecf20Sopenharmony_ci	struct local_info *local;
33618c2ecf20Sopenharmony_ci	union iwreq_data wrqu;
33628c2ecf20Sopenharmony_ci
33638c2ecf20Sopenharmony_ci	iface = netdev_priv(dev);
33648c2ecf20Sopenharmony_ci	local = iface->local;
33658c2ecf20Sopenharmony_ci
33668c2ecf20Sopenharmony_ci	/* Send disconnect event, e.g., to trigger reassociation after resume
33678c2ecf20Sopenharmony_ci	 * if wpa_supplicant is used. */
33688c2ecf20Sopenharmony_ci	memset(&wrqu, 0, sizeof(wrqu));
33698c2ecf20Sopenharmony_ci	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
33708c2ecf20Sopenharmony_ci	wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL);
33718c2ecf20Sopenharmony_ci
33728c2ecf20Sopenharmony_ci	/* Disable hardware and firmware */
33738c2ecf20Sopenharmony_ci	prism2_hw_shutdown(dev, 0);
33748c2ecf20Sopenharmony_ci}
33758c2ecf20Sopenharmony_ci#endif /* PRISM2_PCI || PRISM2_PCCARD */
33768c2ecf20Sopenharmony_ci
33778c2ecf20Sopenharmony_ci
33788c2ecf20Sopenharmony_ci/* These might at some point be compiled separately and used as separate
33798c2ecf20Sopenharmony_ci * kernel modules or linked into one */
33808c2ecf20Sopenharmony_ci#ifdef PRISM2_DOWNLOAD_SUPPORT
33818c2ecf20Sopenharmony_ci#include "hostap_download.c"
33828c2ecf20Sopenharmony_ci#endif /* PRISM2_DOWNLOAD_SUPPORT */
33838c2ecf20Sopenharmony_ci
33848c2ecf20Sopenharmony_ci#ifdef PRISM2_CALLBACK
33858c2ecf20Sopenharmony_ci/* External hostap_callback.c file can be used to, e.g., blink activity led.
33868c2ecf20Sopenharmony_ci * This can use platform specific code and must define prism2_callback()
33878c2ecf20Sopenharmony_ci * function (if PRISM2_CALLBACK is not defined, these function calls are not
33888c2ecf20Sopenharmony_ci * used. */
33898c2ecf20Sopenharmony_ci#include "hostap_callback.c"
33908c2ecf20Sopenharmony_ci#endif /* PRISM2_CALLBACK */
3391