#include #include #include #include #if LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0) #include #else #include #endif #include #include #include "../hgic_def.h" #include "utils.h" #define HGIC_PKT_MAX_LEN 2048 #define HGIC_TX_URB_CNT 64 #define HGIC_RX_URB_CNT 64 #define HGIC_USB_STATUS_STOP BIT(0) #define HGIC_USB_STATUS_ERR BIT(1) #define HGIC_USB_BUF_FLAG_USED 0x00000001 #define USB_TX_HEADROM 4 struct hgic_usb_buf { struct list_head list; struct hgic_usb *usbdev; struct urb *urb; struct sk_buff *skb; int flag; }; struct hgic_usb { struct usb_device *udev; uint ep_in, ep_out; uint ep_out_size, ep_in_size; uint status; struct list_head tx_freeq; struct list_head rx_freeq; struct list_head used; struct semaphore tx_sema; spinlock_t qlock; struct hgic_bus bus; }; static int txq_cnt = HGIC_TX_URB_CNT; static int rxq_cnt = HGIC_RX_URB_CNT; static const struct usb_device_id hgic_usb_wdev_ids[] = { { USB_DEVICE(HGIC_VENDOR_ID, HGIC_WLAN_AH_4001) }, { USB_DEVICE(HGIC_VENDOR_ID, HGIC_WLAN_AH_4002) }, { USB_DEVICE(HGIC_VENDOR_ID, HGIC_WLAN_AH_4102) }, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(usb, hgic_usb_wdev_ids); static void hgic_usb_receive(struct urb *urb); static void hgic_usb_free(struct hgic_usb *usbdev) { unsigned long flags; struct hgic_usb_buf *buf, *n; spin_lock_irqsave(&usbdev->qlock, flags); list_for_each_entry_safe(buf, n, &usbdev->tx_freeq, list) { list_del(&buf->list); usb_free_urb(buf->urb); kfree(buf); //hgic_dbg("free usb tx buff\r\n"); } list_for_each_entry_safe(buf, n, &usbdev->rx_freeq, list) { list_del(&buf->list); usb_free_urb(buf->urb); kfree(buf); //hgic_dbg("free usb rx buff\r\n"); } list_for_each_entry_safe(buf, n, &usbdev->used, list) { usb_kill_urb(buf->urb); list_del(&buf->list); usb_free_urb(buf->urb); kfree(buf); } spin_unlock_irqrestore(&usbdev->qlock, flags); } static int hgic_usb_qinit(struct hgic_usb *usb, struct list_head *q, int qsize) { int i = 0; struct hgic_usb_buf *buf = NULL; for (i = 0; i < qsize; i++) { buf = kzalloc(sizeof(struct hgic_usb_buf), GFP_ATOMIC); if (buf == NULL) { break; } buf->usbdev = usb; buf->urb = usb_alloc_urb(0, GFP_KERNEL); if (buf->urb) { list_add_tail(&buf->list, q); } else { kfree(buf); break; } } return i; } struct hgic_usb_buf *hgic_usb_deq(struct hgic_usb *usbdev, struct list_head *q) { struct hgic_usb_buf *buf = NULL; unsigned long flags; spin_lock_irqsave(&usbdev->qlock, flags); if (!list_empty(q)) { buf = list_first_entry(q, struct hgic_usb_buf, list); buf->flag |= HGIC_USB_BUF_FLAG_USED; list_del(&buf->list); list_add_tail(&buf->list, &usbdev->used); } spin_unlock_irqrestore(&usbdev->qlock, flags); return buf; } void hgic_usb_enq(struct hgic_usb_buf *buf, struct list_head *q) { unsigned long flags; spin_lock_irqsave(&buf->usbdev->qlock, flags); buf->skb = NULL; buf->flag &= ~HGIC_USB_BUF_FLAG_USED; list_del(&buf->list); list_add_tail(&buf->list, q); spin_unlock_irqrestore(&buf->usbdev->qlock, flags); } static int hgic_usb_submit_rx_urb(struct hgic_usb_buf *buf) { int ret = -1; buf->skb = dev_alloc_skb(HGIC_PKT_MAX_LEN); if (!buf->skb) { hgic_err("alloc skb failed\r\n"); hgic_usb_enq(buf, &buf->usbdev->rx_freeq); return -1; } usb_fill_bulk_urb(buf->urb, buf->usbdev->udev, usb_rcvbulkpipe(buf->usbdev->udev, buf->usbdev->ep_in), buf->skb->data, HGIC_PKT_MAX_LEN, hgic_usb_receive, buf); ret = usb_submit_urb(buf->urb, GFP_ATOMIC); if (ret) { hgic_err("submit rx urb failed: %d\n", ret); dev_kfree_skb_any(buf->skb); hgic_usb_enq(buf, &buf->usbdev->rx_freeq); buf->usbdev->status |= HGIC_USB_STATUS_ERR; return -1; } return 0; } static int hgic_usb_submit_rx_urbs(struct hgic_usb *usbdev) { int ret = 0; struct hgic_usb_buf *buf = NULL; while ((buf = hgic_usb_deq(usbdev, &usbdev->rx_freeq))) { ret = hgic_usb_submit_rx_urb(buf); if (ret) { break; } } return ret; } static void hgic_usb_receive(struct urb *urb) { struct hgic_usb_buf *buf = (struct hgic_usb_buf *)urb->context; if (buf->usbdev->status & (HGIC_USB_STATUS_STOP | HGIC_USB_STATUS_ERR)) { dev_kfree_skb_any(buf->skb); hgic_usb_enq(buf, &buf->usbdev->rx_freeq); return; } if (urb->actual_length > 0) { buf->usbdev->bus.rx_packet(buf->usbdev->bus.bus_priv, buf->skb, urb->actual_length); } else { dev_kfree_skb_any(buf->skb); } hgic_usb_submit_rx_urb(buf); } static void hgic_usb_tx_complete(struct urb *urb) { struct hgic_usb_buf *buf = (struct hgic_usb_buf *)urb->context; //hgic_skip_padding(buf->skb); buf->usbdev->bus.tx_complete(buf->usbdev->bus.bus_priv, buf->skb, !urb->status); hgic_usb_enq(buf, &buf->usbdev->tx_freeq); up(&buf->usbdev->tx_sema); } static int hgic_usb_tx_packet(void *bus, struct sk_buff *skb) { int ret = 0; int len = ALIGN(skb->len, 4); struct hgic_usb_buf *buf = NULL; struct hgic_usb *usbdev = container_of(bus, struct hgic_usb, bus); if ((len & (usbdev->ep_out_size - 1)) == 0) { len += 4; } if (usbdev->status & (HGIC_USB_STATUS_STOP | HGIC_USB_STATUS_ERR)) { ret = -EIO; goto __fail; } ret = down_timeout(&usbdev->tx_sema, 1000); if(ret){ ret = -EIO; goto __fail; } buf = hgic_usb_deq(usbdev, &usbdev->tx_freeq); if (buf == NULL) { ret = -ENOMEM; goto __fail; } buf->skb = skb; usb_fill_bulk_urb(buf->urb, usbdev->udev, usb_sndbulkpipe(usbdev->udev, usbdev->ep_out), skb->data, len, /*ALIGN(skb->len, 4)*/ hgic_usb_tx_complete, buf); buf->urb->transfer_flags |= URB_ZERO_PACKET; ret = usb_submit_urb(buf->urb, GFP_ATOMIC); if (ret) { hgic_err("usb_submit_urb failed, ret:%d\n", ret); goto __fail; } return ret; __fail: if (buf) { hgic_usb_enq(buf, &usbdev->tx_freeq); } usbdev->bus.tx_complete(usbdev->bus.bus_priv, skb, 0); return ret; } static struct hgic_bus hgic_bus_usb = { .type = HGIC_BUS_USB, .drv_tx_headroom = USB_TX_HEADROM, .tx_packet = hgic_usb_tx_packet, .bootdl_pktlen = 2048, .bootdl_cksum = HGIC_BUS_BOOTDL_CHECK_0XFD, }; static int hgic_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { int i = 0; int ret = 0; struct usb_device *udev = NULL; struct hgic_usb *usbdev = NULL; struct usb_host_interface *iface_desc = NULL; struct usb_endpoint_descriptor *endpoint = NULL; hgic_dbg("new usb card: vendor:%x, id:%x\n", id->idVendor, id->idProduct); udev = interface_to_usbdev(intf); iface_desc = intf->cur_altsetting; usbdev = kzalloc(sizeof(struct hgic_usb), GFP_KERNEL); if (!usbdev) { return -ENOMEM; } memset(usbdev, 0, sizeof(struct hgic_usb)); usbdev->udev = udev; usbdev->bus = hgic_bus_usb; usbdev->bus.dev_id = id->idProduct; usbdev->status = HGIC_USB_STATUS_STOP; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; if (usb_endpoint_is_bulk_in(endpoint)) { usbdev->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); usbdev->ep_in = usb_endpoint_num(endpoint); hgic_dbg("IN BULK: ep_in_size:%x, ep_in:%x\n", usbdev->ep_in_size, usbdev->ep_in); } else if (usb_endpoint_is_bulk_out(endpoint)) { usbdev->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); usbdev->ep_out = usb_endpoint_num(endpoint); hgic_dbg("OUT BULK: ep_out_size:%x, ep_out:%x\n", usbdev->ep_out_size, usbdev->ep_out); } } usbdev->bus.blk_size = usbdev->ep_out_size; spin_lock_init(&usbdev->qlock); INIT_LIST_HEAD(&usbdev->tx_freeq); INIT_LIST_HEAD(&usbdev->rx_freeq); INIT_LIST_HEAD(&usbdev->used); ret = hgic_usb_qinit(usbdev, &usbdev->tx_freeq, txq_cnt); sema_init(&usbdev->tx_sema, ret); hgic_dbg("usb txq:%d\r\n", ret); ret = hgic_usb_qinit(usbdev, &usbdev->rx_freeq, rxq_cnt); hgic_dbg("usb rxq:%d\r\n", ret); hgic_core_probe(&udev->dev, &usbdev->bus); if (!usbdev->bus.bus_priv) { ret = -ENOMEM; goto __failed; } ret = hgic_usb_submit_rx_urbs(usbdev); if (ret) { goto __failed; } usb_get_dev(udev); usb_set_intfdata(intf, usbdev); usbdev->status &= ~HGIC_USB_STATUS_STOP; hgic_core_probe_post(usbdev->bus.bus_priv); return ret; __failed: hgic_core_remove(usbdev->bus.bus_priv); hgic_usb_free(usbdev); kfree(usbdev); return -1; } static void hgic_usb_disconnect(struct usb_interface *intf) { struct hgic_usb *usbdev = usb_get_intfdata(intf); hgic_dbg("hgic_usb_disconnect>>>>\r\n"); if (usbdev) { usbdev->status |= HGIC_USB_STATUS_STOP; hgic_core_remove(usbdev->bus.bus_priv); hgic_usb_free(usbdev); kfree(usbdev); usb_set_intfdata(intf, NULL); } } static struct usb_driver hgic_usb_driver = { .name = KBUILD_MODNAME, .probe = hgic_usb_probe, .disconnect = hgic_usb_disconnect, .id_table = hgic_usb_wdev_ids, .suspend = NULL, .resume = NULL, .supports_autosuspend = 1, }; int __init hgic_usb_init(void) { int ret = 0; hgic_dbg("Enter\n"); ret = usb_register(&hgic_usb_driver); if (ret) { hgic_err("driver register failed: %d\n", ret); } hgic_dbg("Leave\n"); return ret; } void __exit hgic_usb_exit(void) { hgic_dbg("Enter\n"); usb_deregister(&hgic_usb_driver); hgic_dbg("Leave\n"); } #ifndef CONFIG_HGIC_USBIN module_init(hgic_usb_init); module_exit(hgic_usb_exit); MODULE_DESCRIPTION("HUGE-IC WLAN Driver"); MODULE_AUTHOR("Dongyun"); MODULE_LICENSE("GPL"); #endif