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

📄 zd1201.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *	Driver for ZyDAS zd1201 based wireless USB devices. * *	Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org) * *	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. * *	Parts of this driver have been derived from a wlan-ng version *	modified by ZyDAS. They also made documentation available, thanks! *	Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved. */#include <linux/module.h>#include <linux/usb.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/wireless.h>#include <net/iw_handler.h>#include <linux/string.h>#include <linux/if_arp.h>#include <linux/firmware.h>#include <net/ieee80211.h>#include "zd1201.h"static struct usb_device_id zd1201_table[] = {	{USB_DEVICE(0x0586, 0x3400)}, /* Peabird Wireless USB Adapter */	{USB_DEVICE(0x0ace, 0x1201)}, /* ZyDAS ZD1201 Wireless USB Adapter */	{USB_DEVICE(0x050d, 0x6051)}, /* Belkin F5D6051 usb  adapter */	{USB_DEVICE(0x0db0, 0x6823)}, /* MSI UB11B usb  adapter */	{USB_DEVICE(0x1044, 0x8005)}, /* GIGABYTE GN-WLBZ201 usb adapter */	{}};static int ap = 0;	/* Are we an AP or a normal station? */#define ZD1201_VERSION	"0.15"MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");MODULE_DESCRIPTION("Driver for ZyDAS ZD1201 based USB Wireless adapters");MODULE_VERSION(ZD1201_VERSION);MODULE_LICENSE("GPL");module_param(ap, int, 0);MODULE_PARM_DESC(ap, "If non-zero Access Point firmware will be loaded");MODULE_DEVICE_TABLE(usb, zd1201_table);static int zd1201_fw_upload(struct usb_device *dev, int apfw){	const struct firmware *fw_entry;	char* data;	unsigned long len;	int err;	unsigned char ret;	char *buf;	char *fwfile;	if (apfw)		fwfile = "zd1201-ap.fw";	else		fwfile = "zd1201.fw";	err = request_firmware(&fw_entry, fwfile, &dev->dev);	if (err) {		dev_err(&dev->dev, "Failed to load %s firmware file!\n", fwfile);		dev_err(&dev->dev, "Make sure the hotplug firmware loader is installed.\n");		dev_err(&dev->dev, "Goto http://linux-lc100020.sourceforge.net for more info\n");		return err;	}	data = fw_entry->data;        len = fw_entry->size;	buf = kmalloc(1024, GFP_ATOMIC);	if (!buf)		goto exit;		while (len > 0) {		int translen = (len > 1024) ? 1024 : len;		memcpy(buf, data, translen);		err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0,		    USB_DIR_OUT | 0x40, 0, 0, buf, translen,		    ZD1201_FW_TIMEOUT);		if (err < 0)			goto exit;		len -= translen;		data += translen;	}                                        	err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x2,	    USB_DIR_OUT | 0x40, 0, 0, NULL, 0, ZD1201_FW_TIMEOUT);	if (err < 0)		goto exit;                                                                                                                                                                	err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4,	    USB_DIR_IN | 0x40, 0,0, &ret, sizeof(ret), ZD1201_FW_TIMEOUT);	if (err < 0)		goto exit;                                                                                                                                                                                                                                                                                        	if (ret & 0x80) {		err = -EIO;		goto exit;	}	err = 0;exit:	kfree(buf);	release_firmware(fw_entry);	return err;}static void zd1201_usbfree(struct urb *urb, struct pt_regs *regs){	struct zd1201 *zd = urb->context;	switch(urb->status) {		case -EILSEQ:		case -ENODEV:		case -ETIMEDOUT:		case -ENOENT:		case -EPIPE:		case -EOVERFLOW:		case -ESHUTDOWN:			dev_warn(&zd->usb->dev, "%s: urb failed: %d\n", 			    zd->dev->name, urb->status);	}	kfree(urb->transfer_buffer);	usb_free_urb(urb);	return;}/* cmdreq message: 	u32 type	u16 cmd	u16 parm0	u16 parm1	u16 parm2	u8  pad[4]	total: 4 + 2 + 2 + 2 + 2 + 4 = 16*/static int zd1201_docmd(struct zd1201 *zd, int cmd, int parm0,			int parm1, int parm2){	unsigned char *command;	int ret;	struct urb *urb;	command = kmalloc(16, GFP_ATOMIC);	if (!command)		return -ENOMEM;	*((__le32*)command) = cpu_to_le32(ZD1201_USB_CMDREQ);	*((__le16*)&command[4]) = cpu_to_le16(cmd);	*((__le16*)&command[6]) = cpu_to_le16(parm0);	*((__le16*)&command[8]) = cpu_to_le16(parm1);	*((__le16*)&command[10])= cpu_to_le16(parm2);	urb = usb_alloc_urb(0, GFP_ATOMIC);	if (!urb) {		kfree(command);		return -ENOMEM;	}	usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2),	     command, 16, zd1201_usbfree, zd);	ret = usb_submit_urb(urb, GFP_ATOMIC);	if (ret) {		kfree(command);		usb_free_urb(urb);	}		return ret;}/* Callback after sending out a packet */static void zd1201_usbtx(struct urb *urb, struct pt_regs *regs){	struct zd1201 *zd = urb->context;	netif_wake_queue(zd->dev);	return;}/* Incoming data */static void zd1201_usbrx(struct urb *urb, struct pt_regs *regs){	struct zd1201 *zd = urb->context;	int free = 0;	unsigned char *data = urb->transfer_buffer;	struct sk_buff *skb;	unsigned char type;	if (!zd) {		free = 1;		goto exit;	}	switch(urb->status) {		case -EILSEQ:		case -ENODEV:		case -ETIMEDOUT:		case -ENOENT:		case -EPIPE:		case -EOVERFLOW:		case -ESHUTDOWN:			dev_warn(&zd->usb->dev, "%s: rx urb failed: %d\n",			    zd->dev->name, urb->status);			free = 1;			goto exit;	}		if (urb->status != 0 || urb->actual_length == 0)		goto resubmit;	type = data[0];	if (type == ZD1201_PACKET_EVENTSTAT || type == ZD1201_PACKET_RESOURCE) {		memcpy(zd->rxdata, data, urb->actual_length);		zd->rxlen = urb->actual_length;		zd->rxdatas = 1;		wake_up(&zd->rxdataq);	}	/* Info frame */	if (type == ZD1201_PACKET_INQUIRE) {		int i = 0;		unsigned short infotype, framelen, copylen;		framelen = le16_to_cpu(*(__le16*)&data[4]);		infotype = le16_to_cpu(*(__le16*)&data[6]);		if (infotype == ZD1201_INF_LINKSTATUS) {			short linkstatus;			linkstatus = le16_to_cpu(*(__le16*)&data[8]);			switch(linkstatus) {				case 1:					netif_carrier_on(zd->dev);					break;				case 2:					netif_carrier_off(zd->dev);					break;				case 3:					netif_carrier_off(zd->dev);					break;				case 4:					netif_carrier_on(zd->dev);					break;				default:					netif_carrier_off(zd->dev);			}			goto resubmit;		}		if (infotype == ZD1201_INF_ASSOCSTATUS) {			short status = le16_to_cpu(*(__le16*)(data+8));			int event;			union iwreq_data wrqu;			switch (status) {				case ZD1201_ASSOCSTATUS_STAASSOC:				case ZD1201_ASSOCSTATUS_REASSOC:					event = IWEVREGISTERED;					break;				case ZD1201_ASSOCSTATUS_DISASSOC:				case ZD1201_ASSOCSTATUS_ASSOCFAIL:				case ZD1201_ASSOCSTATUS_AUTHFAIL:				default:					event = IWEVEXPIRED;			}			memcpy(wrqu.addr.sa_data, data+10, ETH_ALEN);			wrqu.addr.sa_family = ARPHRD_ETHER;			/* Send event to user space */			wireless_send_event(zd->dev, event, &wrqu, NULL);			goto resubmit;		}		if (infotype == ZD1201_INF_AUTHREQ) {			union iwreq_data wrqu;			memcpy(wrqu.addr.sa_data, data+8, ETH_ALEN);			wrqu.addr.sa_family = ARPHRD_ETHER;			/* There isn't a event that trully fits this request.			   We assume that userspace will be smart enough to			   see a new station being expired and sends back a			   authstation ioctl to authorize it. */			wireless_send_event(zd->dev, IWEVEXPIRED, &wrqu, NULL);			goto resubmit;		}		/* Other infotypes are handled outside this handler */		zd->rxlen = 0;		while (i < urb->actual_length) {			copylen = le16_to_cpu(*(__le16*)&data[i+2]);			/* Sanity check, sometimes we get junk */			if (copylen+zd->rxlen > sizeof(zd->rxdata))				break;			memcpy(zd->rxdata+zd->rxlen, data+i+4, copylen);			zd->rxlen += copylen;			i += 64;		}		if (i >= urb->actual_length) {			zd->rxdatas = 1;			wake_up(&zd->rxdataq);		}		goto  resubmit;	}	/* Actual data */	if (data[urb->actual_length-1] == ZD1201_PACKET_RXDATA) {		int datalen = urb->actual_length-1;		unsigned short len, fc, seq;		struct hlist_node *node;		len = ntohs(*(__be16 *)&data[datalen-2]);		if (len>datalen)			len=datalen;		fc = le16_to_cpu(*(__le16 *)&data[datalen-16]);		seq = le16_to_cpu(*(__le16 *)&data[datalen-24]);		if(zd->monitor) {			if (datalen < 24)				goto resubmit;			if (!(skb = dev_alloc_skb(datalen+24)))				goto resubmit;						memcpy(skb_put(skb, 2), &data[datalen-16], 2);			memcpy(skb_put(skb, 2), &data[datalen-2], 2);			memcpy(skb_put(skb, 6), &data[datalen-14], 6);			memcpy(skb_put(skb, 6), &data[datalen-22], 6);			memcpy(skb_put(skb, 6), &data[datalen-8], 6);			memcpy(skb_put(skb, 2), &data[datalen-24], 2);			memcpy(skb_put(skb, len), data, len);			skb->dev = zd->dev;			skb->dev->last_rx = jiffies;			skb->protocol = eth_type_trans(skb, zd->dev);			zd->stats.rx_packets++;			zd->stats.rx_bytes += skb->len;			netif_rx(skb);			goto resubmit;		}					if ((seq & IEEE80211_SCTL_FRAG) ||		    (fc & IEEE80211_FCTL_MOREFRAGS)) {			struct zd1201_frag *frag = NULL;			char *ptr;			if (datalen<14)				goto resubmit;			if ((seq & IEEE80211_SCTL_FRAG) == 0) {				frag = kmalloc(sizeof(*frag), GFP_ATOMIC);				if (!frag)					goto resubmit;				skb = dev_alloc_skb(IEEE80211_DATA_LEN +14+2);				if (!skb) {					kfree(frag);					goto resubmit;				}				frag->skb = skb;				frag->seq = seq & IEEE80211_SCTL_SEQ;				skb_reserve(skb, 2);				memcpy(skb_put(skb, 12), &data[datalen-14], 12);				memcpy(skb_put(skb, 2), &data[6], 2);				memcpy(skb_put(skb, len), data+8, len);				hlist_add_head(&frag->fnode, &zd->fraglist);				goto resubmit;			}			hlist_for_each_entry(frag, node, &zd->fraglist, fnode)				if(frag->seq == (seq&IEEE80211_SCTL_SEQ))					break;			if (!frag)				goto resubmit;			skb = frag->skb;			ptr = skb_put(skb, len);			if (ptr)				memcpy(ptr, data+8, len);			if (fc & IEEE80211_FCTL_MOREFRAGS)				goto resubmit;			hlist_del_init(&frag->fnode);			kfree(frag);			/* Fallthrough */		} else {			if (datalen<14)				goto resubmit;			skb = dev_alloc_skb(len + 14 + 2);			if (!skb)				goto resubmit;			skb_reserve(skb, 2);			memcpy(skb_put(skb, 12), &data[datalen-14], 12);			memcpy(skb_put(skb, 2), &data[6], 2);			memcpy(skb_put(skb, len), data+8, len);		}		skb->dev = zd->dev;		skb->dev->last_rx = jiffies;		skb->protocol = eth_type_trans(skb, zd->dev);		zd->stats.rx_packets++;		zd->stats.rx_bytes += skb->len;		netif_rx(skb);	}resubmit:	memset(data, 0, ZD1201_RXSIZE);	urb->status = 0;	urb->dev = zd->usb;	if(usb_submit_urb(urb, GFP_ATOMIC))		free = 1;exit:	if (free) {		zd->rxlen = 0;		zd->rxdatas = 1;		wake_up(&zd->rxdataq);		kfree(urb->transfer_buffer);	}	return;}static int zd1201_getconfig(struct zd1201 *zd, int rid, void *riddata,	unsigned int riddatalen){	int err;	int i = 0;	int code;	int rid_fid;	int length;	unsigned char *pdata;		zd->rxdatas = 0;	err = zd1201_docmd(zd, ZD1201_CMDCODE_ACCESS, rid, 0, 0);	if (err)		return err;	wait_event_interruptible(zd->rxdataq, zd->rxdatas);	if (!zd->rxlen)		return -EIO;	code = le16_to_cpu(*(__le16*)(&zd->rxdata[4]));	rid_fid = le16_to_cpu(*(__le16*)(&zd->rxdata[6]));	length = le16_to_cpu(*(__le16*)(&zd->rxdata[8]));	if (length > zd->rxlen)		length = zd->rxlen-6;	/* If access bit is not on, then error */	if ((code & ZD1201_ACCESSBIT) != ZD1201_ACCESSBIT || rid_fid != rid )		return -EINVAL;	/* Not enough buffer for allocating data */	if (riddatalen != (length - 4)) {		dev_dbg(&zd->usb->dev, "riddatalen mismatches, expected=%u, (packet=%u) length=%u, rid=0x%04X, rid_fid=0x%04X\n",		    riddatalen, zd->rxlen, length, rid, rid_fid);		return -ENODATA;	}	zd->rxdatas = 0;	/* Issue SetRxRid commnd */				err = zd1201_docmd(zd, ZD1201_CMDCODE_SETRXRID, rid, 0, length);	if (err)		return err;	/* Receive RID record from resource packets */	wait_event_interruptible(zd->rxdataq, zd->rxdatas);	if (!zd->rxlen)		return -EIO;	if (zd->rxdata[zd->rxlen - 1] != ZD1201_PACKET_RESOURCE) {		dev_dbg(&zd->usb->dev, "Packet type mismatch: 0x%x not 0x3\n",		    zd->rxdata[zd->rxlen-1]);		return -EINVAL;	}	/* Set the data pointer and received data length */	pdata = zd->rxdata;	length = zd->rxlen;	do {		int  actual_length;		actual_length = (length > 64) ? 64 : length;		if(pdata[0] != 0x3) {			dev_dbg(&zd->usb->dev, "Rx Resource packet type error: %02X\n",			    pdata[0]);			return -EINVAL;		}		if (actual_length != 64) {			/* Trim the last packet type byte */			actual_length--;		}

⌨️ 快捷键说明

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