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

📄 zd_usb.c

📁 zd1211无线网卡驱动源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* zd_usb.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <asm/unaligned.h>#include <linux/init.h>#include <linux/module.h>#include <linux/firmware.h>#include <linux/device.h>#include <linux/errno.h>#include <linux/skbuff.h>#include <linux/usb.h>#include <net/ieee80211.h>#include "zd_def.h"#include "zd_netdev.h"#include "zd_mac.h"#include "zd_usb.h"#include "zd_util.h"static struct usb_device_id usb_ids[] = {	/* ZD1211 */	{ USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 },	{ USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 },	{ USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 },	{ USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 },	{ USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 },	{ USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 },	/* ZD1211B */	{ USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B },	{ USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B },	{}};MODULE_LICENSE("GPL");MODULE_DESCRIPTION("USB driver for devices with the ZD1211 chip.");MODULE_AUTHOR("Ulrich Kunitz");MODULE_AUTHOR("Daniel Drake");MODULE_VERSION("1.0");MODULE_DEVICE_TABLE(usb, usb_ids);#define FW_ZD1211_PREFIX	"zd1211/zd1211_"#define FW_ZD1211B_PREFIX	"zd1211/zd1211b_"/* register address handling */#ifdef DEBUGstatic int check_addr(struct zd_usb *usb, zd_addr_t addr){	u32 base = ZD_ADDR_BASE(addr);	u32 offset = ZD_OFFSET(addr);	if ((u32)addr & ADDR_ZERO_MASK)		goto invalid_address;	switch (base) {	case USB_BASE:		break;	case CR_BASE:		if (offset > CR_MAX_OFFSET) {			dev_dbg(zd_usb_dev(usb),				"CR offset %#010x larger than"				" CR_MAX_OFFSET %#10x\n",				offset, CR_MAX_OFFSET);			goto invalid_address;		}		if (offset & 1) {			dev_dbg(zd_usb_dev(usb),				"CR offset %#010x is not a multiple of 2\n",				offset);			goto invalid_address;		}		break;	case E2P_BASE:		if (offset > E2P_MAX_OFFSET) {			dev_dbg(zd_usb_dev(usb),				"E2P offset %#010x larger than"				" E2P_MAX_OFFSET %#010x\n",				offset, E2P_MAX_OFFSET);			goto invalid_address;		}		break;	case FW_BASE:		if (!usb->fw_base_offset) {			dev_dbg(zd_usb_dev(usb),			       "ERROR: fw base offset has not been set\n");			return -EAGAIN;		}		if (offset > FW_MAX_OFFSET) {			dev_dbg(zd_usb_dev(usb),				"FW offset %#10x is larger than"				" FW_MAX_OFFSET %#010x\n",				offset, FW_MAX_OFFSET);			goto invalid_address;		}		break;	default:		dev_dbg(zd_usb_dev(usb),			"address has unsupported base %#010x\n", addr);		goto invalid_address;	}	return 0;invalid_address:	dev_dbg(zd_usb_dev(usb),		"ERROR: invalid address: %#010x\n", addr);	return -EINVAL;}#endif /* DEBUG */static u16 usb_addr(struct zd_usb *usb, zd_addr_t addr){	u32 base;	u16 offset;	base = ZD_ADDR_BASE(addr);	offset = ZD_OFFSET(addr);	ZD_ASSERT(check_addr(usb, addr) == 0);	switch (base) {	case CR_BASE:		offset += CR_BASE_OFFSET;		break;	case E2P_BASE:		offset += E2P_BASE_OFFSET;		break;	case FW_BASE:		offset += usb->fw_base_offset;		break;	}	return offset;}/* USB device initialization */static int request_fw_file(	const struct firmware **fw, const char *name, struct device *device){	int r;	dev_dbg_f(device, "fw name %s\n", name);	r = request_firmware(fw, name, device);	if (r)		dev_err(device,		       "Could not load firmware file %s. Error number %d\n",		       name, r);	return r;}static inline u16 get_bcdDevice(const struct usb_device *udev){	return le16_to_cpu(udev->descriptor.bcdDevice);}enum upload_code_flags {	REBOOT = 1,};/* Ensures that MAX_TRANSFER_SIZE is even. */#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)static int upload_code(struct usb_device *udev,	const u8 *data, size_t size, u16 code_offset, int flags){	u8 *p;	int r;	/* USB request blocks need "kmalloced" buffers.	 */	p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL);	if (!p) {		dev_err(&udev->dev, "out of memory\n");		r = -ENOMEM;		goto error;	}	size &= ~1;	while (size > 0) {		size_t transfer_size = size <= MAX_TRANSFER_SIZE ?			size : MAX_TRANSFER_SIZE;		dev_dbg_f(&udev->dev, "transfer size %zu\n", transfer_size);		memcpy(p, data, transfer_size);		r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),			USB_REQ_FIRMWARE_DOWNLOAD,			USB_DIR_OUT | USB_TYPE_VENDOR,			code_offset, 0, p, transfer_size, 1000 /* ms */);		if (r < 0) {			dev_err(&udev->dev,			       "USB control request for firmware upload"			       " failed. Error number %d\n", r);			goto error;		}		transfer_size = r & ~1;		size -= transfer_size;		data += transfer_size;		code_offset += transfer_size/sizeof(u16);	}	if (flags & REBOOT) {		u8 ret;		r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),			USB_REQ_FIRMWARE_CONFIRM,			USB_DIR_IN | USB_TYPE_VENDOR,			0, 0, &ret, sizeof(ret), 5000 /* ms */);		if (r != sizeof(ret)) {			dev_err(&udev->dev,				"control request firmeware confirmation failed."				" Return value %d\n", r);			if (r >= 0)				r = -ENODEV;			goto error;		}		if (ret & 0x80) {			dev_err(&udev->dev,				"Internal error while downloading."				" Firmware confirm return value %#04x\n",				(unsigned int)ret);			r = -ENODEV;			goto error;		}		dev_dbg_f(&udev->dev, "firmware confirm return value %#04x\n",			(unsigned int)ret);	}	r = 0;error:	kfree(p);	return r;}static u16 get_word(const void *data, u16 offset){	const __le16 *p = data;	return le16_to_cpu(p[offset]);}static char *get_fw_name(char *buffer, size_t size, u8 device_type,	               const char* postfix){	scnprintf(buffer, size, "%s%s",		device_type == DEVICE_ZD1211B ?			FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX,		postfix);	return buffer;}static int upload_firmware(struct usb_device *udev, u8 device_type){	int r;	u16 fw_bcdDevice;	u16 bcdDevice;	const struct firmware *ub_fw = NULL;	const struct firmware *uph_fw = NULL;	char fw_name[128];	bcdDevice = get_bcdDevice(udev);	r = request_fw_file(&ub_fw,		get_fw_name(fw_name, sizeof(fw_name), device_type,  "ub"),		&udev->dev);	if (r)		goto error;	fw_bcdDevice = get_word(ub_fw->data, EEPROM_REGS_OFFSET);	/* FIXME: do we have any reason to perform the kludge that the vendor	 * driver does when there is a version mismatch? (their driver uploads	 * different firmwares and stuff)	 */	if (fw_bcdDevice != bcdDevice) {		dev_info(&udev->dev,			"firmware device id %#06x and actual device id "			"%#06x differ, continuing anyway\n",			fw_bcdDevice, bcdDevice);	} else {		dev_dbg_f(&udev->dev,			"firmware device id %#06x is equal to the "			"actual device id\n", fw_bcdDevice);	}	r = request_fw_file(&uph_fw,		get_fw_name(fw_name, sizeof(fw_name), device_type, "uphr"),		&udev->dev);	if (r)		goto error;	r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START_OFFSET,		        REBOOT);	if (r) {		dev_err(&udev->dev,			"Could not upload firmware code uph. Error number %d\n",			r);	}	/* FALL-THROUGH */error:	release_firmware(ub_fw);	release_firmware(uph_fw);	return r;}static void disable_read_regs_int(struct zd_usb *usb){	struct zd_usb_interrupt *intr = &usb->intr;	spin_lock(&intr->lock);	intr->read_regs_enabled = 0;	spin_unlock(&intr->lock);}#define urb_dev(urb) (&(urb)->dev->dev)static inline void handle_regs_int(struct urb *urb){	struct zd_usb *usb = urb->context;	struct zd_usb_interrupt *intr = &usb->intr;	int len;	ZD_ASSERT(in_interrupt());	spin_lock(&intr->lock);	if (intr->read_regs_enabled) {		intr->read_regs.length = len = urb->actual_length;		if (len > sizeof(intr->read_regs.buffer))			len = sizeof(intr->read_regs.buffer);		memcpy(intr->read_regs.buffer, urb->transfer_buffer, len);		intr->read_regs_enabled = 0;		complete(&intr->read_regs.completion);		goto out;	}	dev_dbg_f(urb_dev(urb), "regs interrupt ignored\n");out:	spin_unlock(&intr->lock);}static inline void handle_retry_failed_int(struct urb *urb){	dev_dbg_f(urb_dev(urb), "retry failed interrupt\n");}static void int_urb_complete(struct urb *urb, struct pt_regs *pt_regs){	int r;	struct usb_int_header *hdr;	switch (urb->status) {	case 0:		break;	case -ESHUTDOWN:	case -EINVAL:	case -ENODEV:	case -ENOENT:	case -ECONNRESET:	case -EPIPE:		goto kfree;	default:		goto resubmit;	}	if (urb->actual_length < sizeof(hdr)) {		dev_dbg_f(urb_dev(urb), "error: urb %p to small\n", urb);		goto resubmit;	}	hdr = urb->transfer_buffer;	if (hdr->type != USB_INT_TYPE) {		dev_dbg_f(urb_dev(urb), "error: urb %p wrong type\n", urb);		goto resubmit;	}	switch (hdr->id) {	case USB_INT_ID_REGS:		handle_regs_int(urb);		break;	case USB_INT_ID_RETRY_FAILED:		handle_retry_failed_int(urb);		break;	default:		dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb,			(unsigned int)hdr->id);		goto resubmit;	}resubmit:	r = usb_submit_urb(urb, GFP_ATOMIC);	if (r) {		dev_dbg_f(urb_dev(urb), "resubmit urb %p\n", urb);		goto kfree;	}	return;kfree:	kfree(urb->transfer_buffer);}static inline int int_urb_interval(struct usb_device *udev){	switch (udev->speed) {	case USB_SPEED_HIGH:		return 4;	case USB_SPEED_LOW:		return 10;	case USB_SPEED_FULL:	default:		return 1;	}}static inline int usb_int_enabled(struct zd_usb *usb){	unsigned long flags;	struct zd_usb_interrupt *intr = &usb->intr;	struct urb *urb;	spin_lock_irqsave(&intr->lock, flags);	urb = intr->urb;	spin_unlock_irqrestore(&intr->lock, flags);	return urb != NULL;}int zd_usb_enable_int(struct zd_usb *usb){	int r;	struct usb_device *udev;	struct zd_usb_interrupt *intr = &usb->intr;	void *transfer_buffer = NULL;	struct urb *urb;	dev_dbg_f(zd_usb_dev(usb), "\n");	urb = usb_alloc_urb(0, GFP_NOFS);	if (!urb) {		r = -ENOMEM;		goto out;	}	ZD_ASSERT(!irqs_disabled());	spin_lock_irq(&intr->lock);	if (intr->urb) {		spin_unlock_irq(&intr->lock);		r = 0;		goto error_free_urb;	}	intr->urb = urb;	spin_unlock_irq(&intr->lock);	/* TODO: make it a DMA buffer */	r = -ENOMEM;	transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_NOFS);	if (!transfer_buffer) {		dev_dbg_f(zd_usb_dev(usb),			"couldn't allocate transfer_buffer\n");		goto error_set_urb_null;	}	udev = zd_usb_to_usbdev(usb);	usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN),			 transfer_buffer, USB_MAX_EP_INT_BUFFER,			 int_urb_complete, usb,			 intr->interval);	dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb);	r = usb_submit_urb(urb, GFP_NOFS);	if (r) {		dev_dbg_f(zd_usb_dev(usb),			 "Couldn't submit urb. Error number %d\n", r);		goto error;	}	return 0;error:	kfree(transfer_buffer);error_set_urb_null:	spin_lock_irq(&intr->lock);	intr->urb = NULL;	spin_unlock_irq(&intr->lock);error_free_urb:	usb_free_urb(urb);out:	return r;}void zd_usb_disable_int(struct zd_usb *usb){	unsigned long flags;	struct zd_usb_interrupt *intr = &usb->intr;	struct urb *urb;	spin_lock_irqsave(&intr->lock, flags);	urb = intr->urb;	if (!urb) {		spin_unlock_irqrestore(&intr->lock, flags);		return;	}	intr->urb = NULL;	spin_unlock_irqrestore(&intr->lock, flags);	usb_kill_urb(urb);	dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb);	usb_free_urb(urb);}static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer,			     unsigned int length){	int i;	struct zd_mac *mac = zd_usb_to_mac(usb);	const struct rx_length_info *length_info;	if (length < sizeof(struct rx_length_info)) {		/* It's not a complete packet anyhow. */		return;	}	length_info = (struct rx_length_info *)		(buffer + length - sizeof(struct rx_length_info));	/* It might be that three frames are merged into a single URB	 * transaction. We have to check for the length info tag.	 *	 * While testing we discovered that length_info might be unaligned,	 * because if USB transactions are merged, the last packet will not	 * be padded. Unaligned access might also happen if the length_info	 * structure is not present.	 */	if (get_unaligned(&length_info->tag) == cpu_to_le16(RX_LENGTH_INFO_TAG))	{		unsigned int l, k, n;		for (i = 0, l = 0;; i++) {			k = le16_to_cpu(get_unaligned(&length_info->length[i]));			n = l+k;			if (n > length)				return;			zd_mac_rx(mac, buffer+l, k);			if (i >= 2)				return;			l = (n+3) & ~3;		}	} else {		zd_mac_rx(mac, buffer, length);	}}static void rx_urb_complete(struct urb *urb, struct pt_regs *pt_regs){	struct zd_usb *usb;	struct zd_usb_rx *rx;	const u8 *buffer;	unsigned int length;	switch (urb->status) {	case 0:		break;	case -ESHUTDOWN:	case -EINVAL:	case -ENODEV:	case -ENOENT:	case -ECONNRESET:	case -EPIPE:		return;	default:		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);		goto resubmit;	}	buffer = urb->transfer_buffer;	length = urb->actual_length;	usb = urb->context;	rx = &usb->rx;	if (length%rx->usb_packet_size > rx->usb_packet_size-4) {		/* If there is an old first fragment, we don't care. */		dev_dbg_f(urb_dev(urb), "*** first fragment ***\n");		ZD_ASSERT(length <= ARRAY_SIZE(rx->fragment));		spin_lock(&rx->lock);		memcpy(rx->fragment, buffer, length);		rx->fragment_length = length;		spin_unlock(&rx->lock);		goto resubmit;	}	spin_lock(&rx->lock);	if (rx->fragment_length > 0) {		/* We are on a second fragment, we believe */		ZD_ASSERT(length + rx->fragment_length <=			  ARRAY_SIZE(rx->fragment));		dev_dbg_f(urb_dev(urb), "*** second fragment ***\n");		memcpy(rx->fragment+rx->fragment_length, buffer, length);		handle_rx_packet(usb, rx->fragment,			         rx->fragment_length + length);		rx->fragment_length = 0;		spin_unlock(&rx->lock);	} else {		spin_unlock(&rx->lock);		handle_rx_packet(usb, buffer, length);	}resubmit:	usb_submit_urb(urb, GFP_ATOMIC);}struct urb *alloc_urb(struct zd_usb *usb){	struct usb_device *udev = zd_usb_to_usbdev(usb);	struct urb *urb;	void *buffer;	urb = usb_alloc_urb(0, GFP_NOFS);	if (!urb)		return NULL;	buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_NOFS,		                  &urb->transfer_dma);	if (!buffer) {		usb_free_urb(urb);		return NULL;	}	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),		          buffer, USB_MAX_RX_SIZE,			  rx_urb_complete, usb);	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;	return urb;}void free_urb(struct urb *urb){	if (!urb)		return;	usb_buffer_free(urb->dev, urb->transfer_buffer_length,		        urb->transfer_buffer, urb->transfer_dma);	usb_free_urb(urb);}

⌨️ 快捷键说明

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