📄 hostap_hw.c
字号:
/* * Host AP (software wireless LAN access point) driver for * Intersil Prism2/2.5/3. * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen * <jkmaline@cc.hut.fi> * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See README and COPYING for * more details. * * FIX: * - there is currently no way of associating TX packets to correct wds device * when TX Exc/OK event occurs, so all tx_packets and some * tx_errors/tx_dropped are added to the main netdevice; using sw_support * field in txdesc might be used to fix this (using Alloc event to increment * tx_packets would need some further info in txfid table) * * Buffer Access Path (BAP) usage: * Prism2 cards have two separate BAPs for accessing the card memory. These * should allow concurrent access to two different frames and the driver * previously used BAP0 for sending data and BAP1 for receiving data. * However, there seems to be number of issues with concurrent access and at * least one know hardware bug in using BAP0 and BAP1 concurrently with PCI * Prism2.5. Therefore, the driver now only uses BAP0 for moving data between * host and card memories. BAP0 accesses are protected with local->baplock * (spin_lock_bh) to prevent concurrent use. */#include <linux/config.h>#include <asm/delay.h>#include <asm/uaccess.h>#include <linux/slab.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/proc_fs.h>#include <linux/if_arp.h>#include <linux/delay.h>#include <linux/random.h>#include <linux/wait.h>#include <linux/sched.h>#include <linux/rtnetlink.h>#include <linux/wireless.h>#include <net/iw_handler.h>#include <net/ieee80211.h>#include <net/ieee80211_crypt.h>#include <asm/irq.h>#include "hostap_80211.h"#include "hostap.h"#include "hostap_ap.h"/* #define final_version */static int mtu = 1500;module_param(mtu, int, 0444);MODULE_PARM_DESC(mtu, "Maximum transfer unit");static int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS };module_param_array(channel, int, NULL, 0444);MODULE_PARM_DESC(channel, "Initial channel");static char essid[33] = "test";module_param_string(essid, essid, sizeof(essid), 0444);MODULE_PARM_DESC(essid, "Host AP's ESSID");static int iw_mode[MAX_PARM_DEVICES] = { IW_MODE_MASTER, DEF_INTS };module_param_array(iw_mode, int, NULL, 0444);MODULE_PARM_DESC(iw_mode, "Initial operation mode");static int beacon_int[MAX_PARM_DEVICES] = { 100, DEF_INTS };module_param_array(beacon_int, int, NULL, 0444);MODULE_PARM_DESC(beacon_int, "Beacon interval (1 = 1024 usec)");static int dtim_period[MAX_PARM_DEVICES] = { 1, DEF_INTS };module_param_array(dtim_period, int, NULL, 0444);MODULE_PARM_DESC(dtim_period, "DTIM period");static char dev_template[16] = "wlan%d";module_param_string(dev_template, dev_template, sizeof(dev_template), 0444);MODULE_PARM_DESC(dev_template, "Prefix for network device name (default: " "wlan%d)");#ifdef final_version#define EXTRA_EVENTS_WTERR 0#else/* check WTERR events (Wait Time-out) in development versions */#define EXTRA_EVENTS_WTERR HFA384X_EV_WTERR#endif/* Events that will be using BAP0 */#define HFA384X_BAP0_EVENTS \ (HFA384X_EV_TXEXC | HFA384X_EV_RX | HFA384X_EV_INFO | HFA384X_EV_TX)/* event mask, i.e., events that will result in an interrupt */#define HFA384X_EVENT_MASK \ (HFA384X_BAP0_EVENTS | HFA384X_EV_ALLOC | HFA384X_EV_INFDROP | \ HFA384X_EV_CMD | HFA384X_EV_TICK | \ EXTRA_EVENTS_WTERR)/* Default TX control flags: use 802.11 headers and request interrupt for * failed transmits. Frames that request ACK callback, will add * _TX_OK flag and _ALT_RTRY flag may be used to select different retry policy. */#define HFA384X_TX_CTRL_FLAGS \ (HFA384X_TX_CTRL_802_11 | HFA384X_TX_CTRL_TX_EX)/* ca. 1 usec */#define HFA384X_CMD_BUSY_TIMEOUT 5000#define HFA384X_BAP_BUSY_TIMEOUT 50000/* ca. 10 usec */#define HFA384X_CMD_COMPL_TIMEOUT 20000#define HFA384X_DL_COMPL_TIMEOUT 1000000/* Wait times for initialization; yield to other processes to avoid busy * waiting for long time. */#define HFA384X_INIT_TIMEOUT (HZ / 2) /* 500 ms */#define HFA384X_ALLOC_COMPL_TIMEOUT (HZ / 20) /* 50 ms */static void prism2_hw_reset(struct net_device *dev);static void prism2_check_sta_fw_version(local_info_t *local);#ifdef PRISM2_DOWNLOAD_SUPPORT/* hostap_download.c */static int prism2_download_aux_dump(struct net_device *dev, unsigned int addr, int len, u8 *buf);static u8 * prism2_read_pda(struct net_device *dev);static int prism2_download(local_info_t *local, struct prism2_download_param *param);static void prism2_download_free_data(struct prism2_download_data *dl);static int prism2_download_volatile(local_info_t *local, struct prism2_download_data *param);static int prism2_download_genesis(local_info_t *local, struct prism2_download_data *param);static int prism2_get_ram_size(local_info_t *local);#endif /* PRISM2_DOWNLOAD_SUPPORT */#ifndef final_version/* magic value written to SWSUPPORT0 reg. for detecting whether card is still * present */#define HFA384X_MAGIC 0x8A32#endifstatic u16 hfa384x_read_reg(struct net_device *dev, u16 reg){ return HFA384X_INW(reg);}static void hfa384x_read_regs(struct net_device *dev, struct hfa384x_regs *regs){ regs->cmd = HFA384X_INW(HFA384X_CMD_OFF); regs->evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); regs->offset0 = HFA384X_INW(HFA384X_OFFSET0_OFF); regs->offset1 = HFA384X_INW(HFA384X_OFFSET1_OFF); regs->swsupport0 = HFA384X_INW(HFA384X_SWSUPPORT0_OFF);}/** * __hostap_cmd_queue_free - Free Prism2 command queue entry (private) * @local: pointer to private Host AP driver data * @entry: Prism2 command queue entry to be freed * @del_req: request the entry to be removed * * Internal helper function for freeing Prism2 command queue entries. * Caller must have acquired local->cmdlock before calling this function. */static inline void __hostap_cmd_queue_free(local_info_t *local, struct hostap_cmd_queue *entry, int del_req){ if (del_req) { entry->del_req = 1; if (!list_empty(&entry->list)) { list_del_init(&entry->list); local->cmd_queue_len--; } } if (atomic_dec_and_test(&entry->usecnt) && entry->del_req) kfree(entry);}/** * hostap_cmd_queue_free - Free Prism2 command queue entry * @local: pointer to private Host AP driver data * @entry: Prism2 command queue entry to be freed * @del_req: request the entry to be removed * * Free a Prism2 command queue entry. */static inline void hostap_cmd_queue_free(local_info_t *local, struct hostap_cmd_queue *entry, int del_req){ unsigned long flags; spin_lock_irqsave(&local->cmdlock, flags); __hostap_cmd_queue_free(local, entry, del_req); spin_unlock_irqrestore(&local->cmdlock, flags);}/** * prism2_clear_cmd_queue - Free all pending Prism2 command queue entries * @local: pointer to private Host AP driver data */static void prism2_clear_cmd_queue(local_info_t *local){ struct list_head *ptr, *n; unsigned long flags; struct hostap_cmd_queue *entry; spin_lock_irqsave(&local->cmdlock, flags); list_for_each_safe(ptr, n, &local->cmd_queue) { entry = list_entry(ptr, struct hostap_cmd_queue, list); atomic_inc(&entry->usecnt); printk(KERN_DEBUG "%s: removed pending cmd_queue entry " "(type=%d, cmd=0x%04x, param0=0x%04x)\n", local->dev->name, entry->type, entry->cmd, entry->param0); __hostap_cmd_queue_free(local, entry, 1); } if (local->cmd_queue_len) { /* This should not happen; print debug message and clear * queue length. */ printk(KERN_DEBUG "%s: cmd_queue_len (%d) not zero after " "flush\n", local->dev->name, local->cmd_queue_len); local->cmd_queue_len = 0; } spin_unlock_irqrestore(&local->cmdlock, flags);}/** * hfa384x_cmd_issue - Issue a Prism2 command to the hardware * @dev: pointer to net_device * @entry: Prism2 command queue entry to be issued */static inline int hfa384x_cmd_issue(struct net_device *dev, struct hostap_cmd_queue *entry){ struct hostap_interface *iface; local_info_t *local; int tries; u16 reg; unsigned long flags; iface = netdev_priv(dev); local = iface->local; if (local->func->card_present && !local->func->card_present(local)) return -ENODEV; if (entry->issued) { printk(KERN_DEBUG "%s: driver bug - re-issuing command @%p\n", dev->name, entry); } /* wait until busy bit is clear; this should always be clear since the * commands are serialized */ tries = HFA384X_CMD_BUSY_TIMEOUT; while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { tries--; udelay(1); }#ifndef final_version if (tries != HFA384X_CMD_BUSY_TIMEOUT) { prism2_io_debug_error(dev, 1); printk(KERN_DEBUG "%s: hfa384x_cmd_issue: cmd reg was busy " "for %d usec\n", dev->name, HFA384X_CMD_BUSY_TIMEOUT - tries); }#endif if (tries == 0) { reg = HFA384X_INW(HFA384X_CMD_OFF); prism2_io_debug_error(dev, 2); printk(KERN_DEBUG "%s: hfa384x_cmd_issue - timeout - " "reg=0x%04x\n", dev->name, reg); return -ETIMEDOUT; } /* write command */ spin_lock_irqsave(&local->cmdlock, flags); HFA384X_OUTW(entry->param0, HFA384X_PARAM0_OFF); HFA384X_OUTW(entry->param1, HFA384X_PARAM1_OFF); HFA384X_OUTW(entry->cmd, HFA384X_CMD_OFF); entry->issued = 1; spin_unlock_irqrestore(&local->cmdlock, flags); return 0;}/** * hfa384x_cmd - Issue a Prism2 command and wait (sleep) for completion * @dev: pointer to net_device * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) * @param0: value for Param0 register * @param1: value for Param1 register (pointer; %NULL if not used) * @resp0: pointer for Resp0 data or %NULL if Resp0 is not needed * * Issue given command (possibly after waiting in command queue) and sleep * until the command is completed (or timed out or interrupted). This can be * called only from user process context. */static int hfa384x_cmd(struct net_device *dev, u16 cmd, u16 param0, u16 *param1, u16 *resp0){ struct hostap_interface *iface; local_info_t *local; int err, res, issue, issued = 0; unsigned long flags; struct hostap_cmd_queue *entry; DECLARE_WAITQUEUE(wait, current); iface = netdev_priv(dev); local = iface->local; if (in_interrupt()) { printk(KERN_DEBUG "%s: hfa384x_cmd called from interrupt " "context\n", dev->name); return -1; } if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN) { printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", dev->name); return -1; } if (signal_pending(current)) return -EINTR; entry = (struct hostap_cmd_queue *) kmalloc(sizeof(*entry), GFP_ATOMIC); if (entry == NULL) { printk(KERN_DEBUG "%s: hfa384x_cmd - kmalloc failed\n", dev->name); return -ENOMEM; } memset(entry, 0, sizeof(*entry)); atomic_set(&entry->usecnt, 1); entry->type = CMD_SLEEP; entry->cmd = cmd; entry->param0 = param0; if (param1) entry->param1 = *param1; init_waitqueue_head(&entry->compl); /* prepare to wait for command completion event, but do not sleep yet */ add_wait_queue(&entry->compl, &wait); set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&local->cmdlock, flags); issue = list_empty(&local->cmd_queue); if (issue) entry->issuing = 1; list_add_tail(&entry->list, &local->cmd_queue); local->cmd_queue_len++; spin_unlock_irqrestore(&local->cmdlock, flags); err = 0; if (!issue) goto wait_completion; if (signal_pending(current)) err = -EINTR; if (!err) { if (hfa384x_cmd_issue(dev, entry)) err = -ETIMEDOUT; else issued = 1; } wait_completion: if (!err && entry->type != CMD_COMPLETED) { /* sleep until command is completed or timed out */ res = schedule_timeout(2 * HZ); } else res = -1; if (!err && signal_pending(current)) err = -EINTR; if (err && issued) { /* the command was issued, so a CmdCompl event should occur * soon; however, there's a pending signal and * schedule_timeout() would be interrupted; wait a short period * of time to avoid removing entry from the list before * CmdCompl event */ udelay(300); } set_current_state(TASK_RUNNING); remove_wait_queue(&entry->compl, &wait); /* If entry->list is still in the list, it must be removed * first and in this case prism2_cmd_ev() does not yet have * local reference to it, and the data can be kfree()'d * here. If the command completion event is still generated, * it will be assigned to next (possibly) pending command, but * the driver will reset the card anyway due to timeout * * If the entry is not in the list prism2_cmd_ev() has a local * reference to it, but keeps cmdlock as long as the data is * needed, so the data can be kfree()'d here. */ /* FIX: if the entry->list is in the list, it has not been completed * yet, so removing it here is somewhat wrong.. this could cause * references to freed memory and next list_del() causing NULL pointer * dereference.. it would probably be better to leave the entry in the * list and the list should be emptied during hw reset */ spin_lock_irqsave(&local->cmdlock, flags); if (!list_empty(&entry->list)) { printk(KERN_DEBUG "%s: hfa384x_cmd: entry still in list? " "(entry=%p, type=%d, res=%d)\n", dev->name, entry, entry->type, res); list_del_init(&entry->list); local->cmd_queue_len--; } spin_unlock_irqrestore(&local->cmdlock, flags); if (err) { printk(KERN_DEBUG "%s: hfa384x_cmd: interrupted; err=%d\n", dev->name, err); res = err; goto done; } if (entry->type != CMD_COMPLETED) { u16 reg = HFA384X_INW(HFA384X_EVSTAT_OFF); printk(KERN_DEBUG "%s: hfa384x_cmd: command was not " "completed (res=%d, entry=%p, type=%d, cmd=0x%04x, " "param0=0x%04x, EVSTAT=%04x INTEN=%04x)\n", dev->name, res, entry, entry->type, entry->cmd, entry->param0, reg, HFA384X_INW(HFA384X_INTEN_OFF)); if (reg & HFA384X_EV_CMD) { /* Command completion event is pending, but the * interrupt was not delivered - probably an issue * with pcmcia-cs configuration. */ printk(KERN_WARNING "%s: interrupt delivery does not " "seem to work\n", dev->name); } prism2_io_debug_error(dev, 3); res = -ETIMEDOUT; goto done; } if (resp0 != NULL) *resp0 = entry->resp0;#ifndef final_version if (entry->res) { printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x, " "resp0=0x%04x\n", dev->name, cmd, entry->res, entry->resp0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -