0
0
Fork 0
hgicf/utils/if_usb.c

378 lines
11 KiB
C

#include <linux/version.h>
#include <linux/module.h>
#include <linux/completion.h>
#include <linux/usb.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0)
#include <linux/atomic.h>
#else
#include <asm/atomic.h>
#endif
#include <linux/slab.h>
#include <linux/netdevice.h>
#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