📄 zd_usb.c
字号:
/* 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 + -