p54usb.c
来自「linux 内核源代码」· C语言 代码 · 共 908 行 · 第 1/2 页
C
908 行
/* * Linux device driver for USB based Prism54 * * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> * * Based on the islsm (softmac prism54) driver, which is: * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. * * 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. */#include <linux/init.h>#include <linux/usb.h>#include <linux/pci.h>#include <linux/firmware.h>#include <linux/etherdevice.h>#include <linux/delay.h>#include <linux/crc32.h>#include <net/mac80211.h>#include "p54.h"#include "p54usb.h"MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");MODULE_DESCRIPTION("Prism54 USB wireless driver");MODULE_LICENSE("GPL");MODULE_ALIAS("prism54usb");static struct usb_device_id p54u_table[] __devinitdata = { /* Version 1 devices (pci chip + net2280) */ {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */ {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */ {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */ {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */ {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */ {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */ {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */ {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */ {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */ {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */ {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */ {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */ {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */ {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */ {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */ /* Version 2 devices (3887) */ {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */ {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */ {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */ {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */ {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */ {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */ {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/ {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/ {USB_DEVICE(0x0cde, 0x0006)}, /* Medion MD40900 */ {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */ {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */ {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */ {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */ {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */ {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */ {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */ {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */ {}};MODULE_DEVICE_TABLE(usb, p54u_table);static void p54u_rx_cb(struct urb *urb){ struct sk_buff *skb = (struct sk_buff *) urb->context; struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb; struct ieee80211_hw *dev = info->dev; struct p54u_priv *priv = dev->priv; if (unlikely(urb->status)) { info->urb = NULL; usb_free_urb(urb); return; } skb_unlink(skb, &priv->rx_queue); skb_put(skb, urb->actual_length); if (!priv->hw_type) skb_pull(skb, sizeof(struct net2280_tx_hdr)); if (p54_rx(dev, skb)) { skb = dev_alloc_skb(MAX_RX_SIZE); if (unlikely(!skb)) { usb_free_urb(urb); /* TODO check rx queue length and refill *somewhere* */ return; } info = (struct p54u_rx_info *) skb->cb; info->urb = urb; info->dev = dev; urb->transfer_buffer = skb_tail_pointer(skb); urb->context = skb; skb_queue_tail(&priv->rx_queue, skb); } else { skb_trim(skb, 0); skb_queue_tail(&priv->rx_queue, skb); } usb_submit_urb(urb, GFP_ATOMIC);}static void p54u_tx_cb(struct urb *urb){ usb_free_urb(urb);}static void p54u_tx_free_cb(struct urb *urb){ kfree(urb->transfer_buffer); usb_free_urb(urb);}static int p54u_init_urbs(struct ieee80211_hw *dev){ struct p54u_priv *priv = dev->priv; struct urb *entry; struct sk_buff *skb; struct p54u_rx_info *info; while (skb_queue_len(&priv->rx_queue) < 32) { skb = __dev_alloc_skb(MAX_RX_SIZE, GFP_KERNEL); if (!skb) break; entry = usb_alloc_urb(0, GFP_KERNEL); if (!entry) { kfree_skb(skb); break; } usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), skb_tail_pointer(skb), MAX_RX_SIZE, p54u_rx_cb, skb); info = (struct p54u_rx_info *) skb->cb; info->urb = entry; info->dev = dev; skb_queue_tail(&priv->rx_queue, skb); usb_submit_urb(entry, GFP_KERNEL); } return 0;}static void p54u_free_urbs(struct ieee80211_hw *dev){ struct p54u_priv *priv = dev->priv; struct p54u_rx_info *info; struct sk_buff *skb; while ((skb = skb_dequeue(&priv->rx_queue))) { info = (struct p54u_rx_info *) skb->cb; if (!info->urb) continue; usb_kill_urb(info->urb); kfree_skb(skb); }}static void p54u_tx_3887(struct ieee80211_hw *dev, struct p54_control_hdr *data, size_t len, int free_on_tx){ struct p54u_priv *priv = dev->priv; struct urb *addr_urb, *data_urb; addr_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!addr_urb) return; data_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!data_urb) { usb_free_urb(addr_urb); return; } usb_fill_bulk_urb(addr_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), &data->req_id, sizeof(data->req_id), p54u_tx_cb, dev); usb_fill_bulk_urb(data_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), data, len, free_on_tx ? p54u_tx_free_cb : p54u_tx_cb, dev); usb_submit_urb(addr_urb, GFP_ATOMIC); usb_submit_urb(data_urb, GFP_ATOMIC);}static void p54u_tx_net2280(struct ieee80211_hw *dev, struct p54_control_hdr *data, size_t len, int free_on_tx){ struct p54u_priv *priv = dev->priv; struct urb *int_urb, *data_urb; struct net2280_tx_hdr *hdr; struct net2280_reg_write *reg; reg = kmalloc(sizeof(*reg), GFP_ATOMIC); if (!reg) return; int_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!int_urb) { kfree(reg); return; } data_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!data_urb) { kfree(reg); usb_free_urb(int_urb); return; } reg->port = cpu_to_le16(NET2280_DEV_U32); reg->addr = cpu_to_le32(P54U_DEV_BASE); reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); len += sizeof(*data); hdr = (void *)data - sizeof(*hdr); memset(hdr, 0, sizeof(*hdr)); hdr->device_addr = data->req_id; hdr->len = cpu_to_le16(len); usb_fill_bulk_urb(int_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), p54u_tx_free_cb, dev); usb_submit_urb(int_urb, GFP_ATOMIC); usb_fill_bulk_urb(data_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), hdr, len + sizeof(*hdr), free_on_tx ? p54u_tx_free_cb : p54u_tx_cb, dev); usb_submit_urb(data_urb, GFP_ATOMIC);}static int p54u_write(struct p54u_priv *priv, struct net2280_reg_write *buf, enum net2280_op_type type, __le32 addr, __le32 val){ unsigned int ep; int alen; if (type & 0x0800) ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV); else ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG); buf->port = cpu_to_le16(type); buf->addr = addr; buf->val = val; return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000);}static int p54u_read(struct p54u_priv *priv, void *buf, enum net2280_op_type type, __le32 addr, __le32 *val){ struct net2280_reg_read *read = buf; __le32 *reg = buf; unsigned int ep; int alen, err; if (type & 0x0800) ep = P54U_PIPE_DEV; else ep = P54U_PIPE_BRG; read->port = cpu_to_le16(type); read->addr = addr; err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), read, sizeof(*read), &alen, 1000); if (err) return err; err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep), reg, sizeof(*reg), &alen, 1000); if (err) return err; *val = *reg; return 0;}static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep, void *data, size_t len){ int alen; return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), data, len, &alen, 2000);}static int p54u_read_eeprom(struct ieee80211_hw *dev){ struct p54u_priv *priv = dev->priv; void *buf; struct p54_control_hdr *hdr; int err, alen; size_t offset = priv->hw_type ? 0x10 : 0x20; buf = kmalloc(0x2020, GFP_KERNEL); if (!buf) { printk(KERN_ERR "prism54usb: cannot allocate memory for " "eeprom readback!\n"); return -ENOMEM; } if (priv->hw_type) { *((u32 *) buf) = priv->common.rx_start; err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); if (err) { printk(KERN_ERR "prism54usb: addr send failed\n"); goto fail; } } else { struct net2280_reg_write *reg = buf; reg->port = cpu_to_le16(NET2280_DEV_U32); reg->addr = cpu_to_le32(P54U_DEV_BASE); reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); err = p54u_bulk_msg(priv, P54U_PIPE_DEV, buf, sizeof(*reg)); if (err) { printk(KERN_ERR "prism54usb: dev_int send failed\n"); goto fail; } } hdr = buf + priv->common.tx_hdr_len; p54_fill_eeprom_readback(hdr); hdr->req_id = cpu_to_le32(priv->common.rx_start); if (priv->common.tx_hdr_len) { struct net2280_tx_hdr *tx_hdr = buf; tx_hdr->device_addr = hdr->req_id; tx_hdr->len = cpu_to_le16(EEPROM_READBACK_LEN); } /* we can just pretend to send 0x2000 bytes of nothing in the headers */ err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, EEPROM_READBACK_LEN + priv->common.tx_hdr_len); if (err) { printk(KERN_ERR "prism54usb: eeprom req send failed\n"); goto fail; } err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 0x2020, &alen, 1000); if (!err && alen > offset) { p54_parse_eeprom(dev, (u8 *)buf + offset, alen - offset); } else { printk(KERN_ERR "prism54usb: eeprom read failed!\n"); err = -EINVAL; goto fail; } fail: kfree(buf); return err;}static int p54u_upload_firmware_3887(struct ieee80211_hw *dev){ static char start_string[] = "~~~~<\r"; struct p54u_priv *priv = dev->priv; const struct firmware *fw_entry = NULL; int err, alen; u8 carry = 0; u8 *buf, *tmp, *data; unsigned int left, remains, block_size; struct x2_header *hdr; unsigned long timeout; tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL); if (!buf) { printk(KERN_ERR "p54usb: cannot allocate firmware upload buffer!\n"); err = -ENOMEM; goto err_bufalloc; } memcpy(buf, start_string, 4); err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 4); if (err) { printk(KERN_ERR "p54usb: reset failed! (%d)\n", err); goto err_reset; } err = request_firmware(&fw_entry, "isl3887usb_bare", &priv->udev->dev); if (err) { printk(KERN_ERR "p54usb: cannot find firmware (isl3887usb_bare)!\n"); goto err_req_fw_failed; } p54_parse_firmware(dev, fw_entry); left = block_size = min((size_t)P54U_FW_BLOCK, fw_entry->size); strcpy(buf, start_string); left -= strlen(start_string); tmp += strlen(start_string); data = fw_entry->data; remains = fw_entry->size; hdr = (struct x2_header *)(buf + strlen(start_string)); memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE); hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR); hdr->fw_length = cpu_to_le32(fw_entry->size); hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr, sizeof(u32)*2)); left -= sizeof(*hdr); tmp += sizeof(*hdr); while (remains) { while (left--) { if (carry) { *tmp++ = carry; carry = 0; remains--; continue; } switch (*data) { case '~': *tmp++ = '}'; carry = '^'; break; case '}': *tmp++ = '}'; carry = ']'; break; default: *tmp++ = *data; remains--; break; } data++; } err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size); if (err) { printk(KERN_ERR "prism54usb: firmware upload failed!\n"); goto err_upload_failed; } tmp = buf; left = block_size = min((unsigned int)P54U_FW_BLOCK, remains); } *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, fw_entry->data, fw_entry->size));
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?