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

📄 wl3501_cs.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * WL3501 Wireless LAN PCMCIA Card Driver for Linux * Written originally for Linux 2.0.30 by Fox Chen, mhchen@golf.ccl.itri.org.tw * Ported to 2.2, 2.4 & 2.5 by Arnaldo Carvalho de Melo <acme@conectiva.com.br> * Wireless extensions in 2.4 by Gustavo Niemeyer <niemeyer@conectiva.com> * * References used by Fox Chen while writing the original driver for 2.0.30: * *   1. WL24xx packet drivers (tooasm.asm) *   2. Access Point Firmware Interface Specification for IEEE 802.11 SUTRO *   3. IEEE 802.11 *   4. Linux network driver (/usr/src/linux/drivers/net) *   5. ISA card driver - wl24.c *   6. Linux PCMCIA skeleton driver - skeleton.c *   7. Linux PCMCIA 3c589 network driver - 3c589_cs.c * * Tested with WL2400 firmware 1.2, Linux 2.0.30, and pcmcia-cs-2.9.12 *   1. Performance: about 165 Kbytes/sec in TCP/IP with Ad-Hoc mode. *      rsh 192.168.1.3 "dd if=/dev/zero bs=1k count=1000" > /dev/null *      (Specification 2M bits/sec. is about 250 Kbytes/sec., but we must deduct *       ETHER/IP/UDP/TCP header, and acknowledgement overhead) * * Tested with Planet AP in 2.4.17, 184 Kbytes/s in UDP in Infrastructure mode, * 173 Kbytes/s in TCP. * * Tested with Planet AP in 2.5.73-bk, 216 Kbytes/s in Infrastructure mode * with a SMP machine (dual pentium 100), using pktgen, 432 pps (pkt_size = 60) */#undef REALLY_SLOW_IO	/* most systems can safely undef this */#include <linux/config.h>#include <linux/delay.h>#include <linux/types.h>#include <linux/ethtool.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/in.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/fcntl.h>#include <linux/if_arp.h>#include <linux/ioport.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/wireless.h>#include <net/iw_handler.h>#include <pcmcia/cs_types.h>#include <pcmcia/cs.h>#include <pcmcia/cistpl.h>#include <pcmcia/cisreg.h>#include <pcmcia/ds.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/system.h>#include "wl3501.h"#ifndef __i386__#define slow_down_io()#endif/* For rough constant delay */#define WL3501_NOPLOOP(n) { int x = 0; while (x++ < n) slow_down_io(); }/* * All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If you do not * define PCMCIA_DEBUG at all, all the debug code will be left out.  If you * compile with PCMCIA_DEBUG=0, the debug code will be present but disabled -- * but it can then be enabled for specific modules at load time with a * 'pc_debug=#' option to insmod. */#define PCMCIA_DEBUG 0#ifdef PCMCIA_DEBUGstatic int pc_debug = PCMCIA_DEBUG;module_param(pc_debug, int, 0);#define dprintk(n, format, args...) \	{ if (pc_debug > (n)) \		printk(KERN_INFO "%s: " format "\n", __FUNCTION__ , ##args); }#else#define dprintk(n, format, args...)#endif#define wl3501_outb(a, b) { outb(a, b); slow_down_io(); }#define wl3501_outb_p(a, b) { outb_p(a, b); slow_down_io(); }#define wl3501_outsb(a, b, c) { outsb(a, b, c); slow_down_io(); }#define WL3501_RELEASE_TIMEOUT (25 * HZ)#define WL3501_MAX_ADHOC_TRIES 16#define WL3501_RESUME	0#define WL3501_SUSPEND	1/* * The event() function is this driver's Card Services event handler.  It will * be called by Card Services when an appropriate card status event is * received. The config() and release() entry points are used to configure or * release a socket, in response to card insertion and ejection events.  They * are invoked from the wl24 event handler. */static void wl3501_config(dev_link_t *link);static void wl3501_release(dev_link_t *link);static int wl3501_event(event_t event, int pri, event_callback_args_t *args);/* * The dev_info variable is the "key" that is used to match up this * device driver with appropriate cards, through the card configuration * database. */static dev_info_t wl3501_dev_info = "wl3501_cs";static int wl3501_chan2freq[] = {	[0]  = 2412, [1]  = 2417, [2]  = 2422, [3]  = 2427, [4] = 2432,	[5]  = 2437, [6]  = 2442, [7]  = 2447, [8]  = 2452, [9] = 2457,	[10] = 2462, [11] = 2467, [12] = 2472, [13] = 2477,};static const struct {	int reg_domain;	int min, max, deflt;} iw_channel_table[] = {	{		.reg_domain = IW_REG_DOMAIN_FCC,		.min	    = 1,		.max	    = 11,		.deflt	    = 1,	},	{		.reg_domain = IW_REG_DOMAIN_DOC,		.min	    = 1,		.max	    = 11,		.deflt	    = 1,	},	{		.reg_domain = IW_REG_DOMAIN_ETSI,		.min	    = 1,		.max	    = 13,		.deflt	    = 1,	},	{		.reg_domain = IW_REG_DOMAIN_SPAIN,		.min	    = 10,		.max	    = 11,		.deflt	    = 10,	},	{		.reg_domain = IW_REG_DOMAIN_FRANCE,		.min	    = 10,		.max	    = 13,		.deflt	    = 10,	},	{		.reg_domain = IW_REG_DOMAIN_MKK,		.min	    = 14,		.max	    = 14,		.deflt	    = 14,	},	{		.reg_domain = IW_REG_DOMAIN_MKK1,		.min	    = 1,		.max	    = 14,		.deflt	    = 1,	},	{		.reg_domain = IW_REG_DOMAIN_ISRAEL,		.min	    = 3,		.max	    = 9,		.deflt	    = 9,	},};/** * iw_valid_channel - validate channel in regulatory domain * @reg_comain - regulatory domain * @channel - channel to validate * * Returns 0 if invalid in the specified regulatory domain, non-zero if valid. */static int iw_valid_channel(int reg_domain, int channel){	int i, rc = 0;	for (i = 0; i < ARRAY_SIZE(iw_channel_table); i++)		if (reg_domain == iw_channel_table[i].reg_domain) {			rc = channel >= iw_channel_table[i].min &&			     channel <= iw_channel_table[i].max;			break;		}	return rc;}/** * iw_default_channel - get default channel for a regulatory domain * @reg_comain - regulatory domain * * Returns the default channel for a regulatory domain */static int iw_default_channel(int reg_domain){	int i, rc = 1;	for (i = 0; i < ARRAY_SIZE(iw_channel_table); i++)		if (reg_domain == iw_channel_table[i].reg_domain) {			rc = iw_channel_table[i].deflt;			break;		}	return rc;}static void iw_set_mgmt_info_element(enum iw_mgmt_info_element_ids id,				     struct iw_mgmt_info_element *el,				     void *value, int len){	el->id  = id;	el->len = len;	memcpy(el->data, value, len);}static void iw_copy_mgmt_info_element(struct iw_mgmt_info_element *to,				      struct iw_mgmt_info_element *from){	iw_set_mgmt_info_element(from->id, to, from->data, from->len);}/* * A linked list of "instances" of the wl24 device.  Each actual PCMCIA card * corresponds to one device instance, and is described by one dev_link_t * structure (defined in ds.h). * * You may not want to use a linked list for this -- for example, the memory * card driver uses an array of dev_link_t pointers, where minor device numbers * are used to derive the corresponding array index. */static dev_link_t *wl3501_dev_list;static inline void wl3501_switch_page(struct wl3501_card *this, u8 page){	wl3501_outb(page, this->base_addr + WL3501_NIC_BSS);}/* * Get Ethernet MAC addresss. * * WARNING: We switch to FPAGE0 and switc back again. *          Making sure there is no other WL function beening called by ISR. */static int wl3501_get_flash_mac_addr(struct wl3501_card *this){	int base_addr = this->base_addr;	/* get MAC addr */	wl3501_outb(WL3501_BSS_FPAGE3, base_addr + WL3501_NIC_BSS); /* BSS */	wl3501_outb(0x00, base_addr + WL3501_NIC_LMAL);	/* LMAL */	wl3501_outb(0x40, base_addr + WL3501_NIC_LMAH);	/* LMAH */	/* wait for reading EEPROM */	WL3501_NOPLOOP(100);	this->mac_addr[0] = inb(base_addr + WL3501_NIC_IODPA);	WL3501_NOPLOOP(100);	this->mac_addr[1] = inb(base_addr + WL3501_NIC_IODPA);	WL3501_NOPLOOP(100);	this->mac_addr[2] = inb(base_addr + WL3501_NIC_IODPA);	WL3501_NOPLOOP(100);	this->mac_addr[3] = inb(base_addr + WL3501_NIC_IODPA);	WL3501_NOPLOOP(100);	this->mac_addr[4] = inb(base_addr + WL3501_NIC_IODPA);	WL3501_NOPLOOP(100);	this->mac_addr[5] = inb(base_addr + WL3501_NIC_IODPA);	WL3501_NOPLOOP(100);	this->reg_domain = inb(base_addr + WL3501_NIC_IODPA);	WL3501_NOPLOOP(100);	wl3501_outb(WL3501_BSS_FPAGE0, base_addr + WL3501_NIC_BSS);	wl3501_outb(0x04, base_addr + WL3501_NIC_LMAL);	wl3501_outb(0x40, base_addr + WL3501_NIC_LMAH);	WL3501_NOPLOOP(100);	this->version[0] = inb(base_addr + WL3501_NIC_IODPA);	WL3501_NOPLOOP(100);	this->version[1] = inb(base_addr + WL3501_NIC_IODPA);	/* switch to SRAM Page 0 (for safety) */	wl3501_switch_page(this, WL3501_BSS_SPAGE0);	/* The MAC addr should be 00:60:... */	return this->mac_addr[0] == 0x00 && this->mac_addr[1] == 0x60;}/** * wl3501_set_to_wla - Move 'size' bytes from PC to card * @dest: Card addressing space * @src: PC addressing space * @size: Bytes to move * * Move 'size' bytes from PC to card. (Shouldn't be interrupted) */static void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src,			      int size){	/* switch to SRAM Page 0 */	wl3501_switch_page(this, (dest & 0x8000) ? WL3501_BSS_SPAGE1 :						   WL3501_BSS_SPAGE0);	/* set LMAL and LMAH */	wl3501_outb(dest & 0xff, this->base_addr + WL3501_NIC_LMAL);	wl3501_outb(((dest >> 8) & 0x7f), this->base_addr + WL3501_NIC_LMAH);	/* rep out to Port A */	wl3501_outsb(this->base_addr + WL3501_NIC_IODPA, src, size);}/** * wl3501_get_from_wla - Move 'size' bytes from card to PC * @src: Card addressing space * @dest: PC addressing space * @size: Bytes to move * * Move 'size' bytes from card to PC. (Shouldn't be interrupted) */static void wl3501_get_from_wla(struct wl3501_card *this, u16 src, void *dest,				int size){	/* switch to SRAM Page 0 */	wl3501_switch_page(this, (src & 0x8000) ? WL3501_BSS_SPAGE1 :						  WL3501_BSS_SPAGE0);	/* set LMAL and LMAH */	wl3501_outb(src & 0xff, this->base_addr + WL3501_NIC_LMAL);	wl3501_outb((src >> 8) & 0x7f, this->base_addr + WL3501_NIC_LMAH);	/* rep get from Port A */	insb(this->base_addr + WL3501_NIC_IODPA, dest, size);}/* * Get/Allocate a free Tx Data Buffer * *  *--------------*-----------------*----------------------------------* *  |    PLCP      |    MAC Header   |  DST  SRC         Data ...       | *  |  (24 bytes)  |    (30 bytes)   |  (6)  (6)  (Ethernet Row Data)   | *  *--------------*-----------------*----------------------------------* *  \               \- IEEE 802.11 -/ \-------------- len --------------/ *   \-struct wl3501_80211_tx_hdr--/   \-------- Ethernet Frame -------/ * * Return = Postion in Card */static u16 wl3501_get_tx_buffer(struct wl3501_card *this, u16 len){	u16 next, blk_cnt = 0, zero = 0;	u16 full_len = sizeof(struct wl3501_80211_tx_hdr) + len;	u16 ret = 0;	if (full_len > this->tx_buffer_cnt * 254)		goto out;	ret = this->tx_buffer_head;	while (full_len) {		if (full_len < 254)			full_len = 0;		else			full_len -= 254;		wl3501_get_from_wla(this, this->tx_buffer_head, &next,				    sizeof(next));		if (!full_len)			wl3501_set_to_wla(this, this->tx_buffer_head, &zero,					  sizeof(zero));		this->tx_buffer_head = next;		blk_cnt++;		/* if buffer is not enough */		if (!next && full_len) {			this->tx_buffer_head = ret;			ret = 0;			goto out;		}	}	this->tx_buffer_cnt -= blk_cnt;out:	return ret;}/* * Free an allocated Tx Buffer. ptr must be correct position. */static void wl3501_free_tx_buffer(struct wl3501_card *this, u16 ptr){	/* check if all space is not free */	if (!this->tx_buffer_head)		this->tx_buffer_head = ptr;	else		wl3501_set_to_wla(this, this->tx_buffer_tail,				  &ptr, sizeof(ptr));	while (ptr) {		u16 next;		this->tx_buffer_cnt++;		wl3501_get_from_wla(this, ptr, &next, sizeof(next));		this->tx_buffer_tail = ptr;		ptr = next;	}}static int wl3501_esbq_req_test(struct wl3501_card *this){	u8 tmp;	wl3501_get_from_wla(this, this->esbq_req_head + 3, &tmp, sizeof(tmp));	return tmp & 0x80;}static void wl3501_esbq_req(struct wl3501_card *this, u16 *ptr){	u16 tmp = 0;	wl3501_set_to_wla(this, this->esbq_req_head, ptr, 2);	wl3501_set_to_wla(this, this->esbq_req_head + 2, &tmp, sizeof(tmp));	this->esbq_req_head += 4;	if (this->esbq_req_head >= this->esbq_req_end)		this->esbq_req_head = this->esbq_req_start;}static int wl3501_esbq_exec(struct wl3501_card *this, void *sig, int sig_size){	int rc = -EIO;	if (wl3501_esbq_req_test(this)) {		u16 ptr = wl3501_get_tx_buffer(this, sig_size);		if (ptr) {			wl3501_set_to_wla(this, ptr, sig, sig_size);			wl3501_esbq_req(this, &ptr);			rc = 0;		}	}	return rc;}static int wl3501_get_mib_value(struct wl3501_card *this, u8 index,				void *bf, int size){	struct wl3501_get_req sig = {		.sig_id	    = WL3501_SIG_GET_REQ,		.mib_attrib = index,	};	unsigned long flags;	int rc = -EIO;	spin_lock_irqsave(&this->lock, flags);	if (wl3501_esbq_req_test(this)) {		u16 ptr = wl3501_get_tx_buffer(this, sizeof(sig));		if (ptr) {			wl3501_set_to_wla(this, ptr, &sig, sizeof(sig));			wl3501_esbq_req(this, &ptr);			this->sig_get_confirm.mib_status = 255;			spin_unlock_irqrestore(&this->lock, flags);			rc = wait_event_interruptible(this->wait,				this->sig_get_confirm.mib_status != 255);			if (!rc)

⌨️ 快捷键说明

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