⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hostap_hw.c

📁 IEEE 802.11a/b/g linux2.4/2.6 驱动程序源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * 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-2004, 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 <linux/version.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 "hostap_wext.h"#include <asm/irq.h>#include "hostap_80211.h"#include "hostap.h"#include "hostap_ap.h"/* #define final_version */static int mtu = 1500;MODULE_PARM(mtu, "i");MODULE_PARM_DESC(mtu, "Maximum transfer unit");static int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS };MODULE_PARM(channel, PARM_MIN_MAX "i");MODULE_PARM_DESC(channel, "Initial channel");static char *essid[MAX_PARM_DEVICES] = { "test" };MODULE_PARM(essid, PARM_MIN_MAX "s");MODULE_PARM_DESC(essid, "Host AP's ESSID");static int iw_mode[MAX_PARM_DEVICES] = { IW_MODE_MASTER, DEF_INTS };MODULE_PARM(iw_mode, PARM_MIN_MAX "i");MODULE_PARM_DESC(iw_mode, "Initial operation mode");static int beacon_int[MAX_PARM_DEVICES] = { 100, DEF_INTS };MODULE_PARM(beacon_int, PARM_MIN_MAX "i");MODULE_PARM_DESC(beacon_int, "Beacon interval (1 = 1024 usec)");static int dtim_period[MAX_PARM_DEVICES] = { 1, DEF_INTS };MODULE_PARM(dtim_period, PARM_MIN_MAX "i");MODULE_PARM_DESC(dtim_period, "DTIM period");#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER)static int bus_master_threshold_rx[MAX_PARM_DEVICES] = { 100, DEF_INTS };MODULE_PARM(bus_master_threshold_rx, "i");MODULE_PARM_DESC(bus_master_threshold_rx, "Packet length threshold for using "		 "PCI bus master on RX");static int bus_master_threshold_tx[MAX_PARM_DEVICES] = { 100, DEF_INTS };MODULE_PARM(bus_master_threshold_tx, "i");MODULE_PARM_DESC(bus_master_threshold_tx, "Packet length threshold for using "		 "PCI bus master on TX");#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */static char *dev_template = "wlan%d";MODULE_PARM(dev_template, "s");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#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER)#define EXTRA_EVENTS_BUS_MASTER (HFA384X_EV_PCI_M0 | HFA384X_EV_PCI_M1)#else#define EXTRA_EVENTS_BUS_MASTER 0#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 | EXTRA_EVENTS_BUS_MASTER)/* 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 = dev->priv;	local_info_t *local = iface->local;	int tries;	u16 reg;	unsigned long flags;	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 = dev->priv;	local_info_t *local = iface->local;	int err, res, issue, issued = 0;	unsigned long flags;	struct hostap_cmd_queue *entry;	DECLARE_WAITQUEUE(wait, current);	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);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -