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