aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/gdm72xx/gdm_usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/gdm72xx/gdm_usb.c')
-rw-r--r--drivers/staging/gdm72xx/gdm_usb.c798
1 files changed, 798 insertions, 0 deletions
diff --git a/drivers/staging/gdm72xx/gdm_usb.c b/drivers/staging/gdm72xx/gdm_usb.c
new file mode 100644
index 000000000000..1e9dc0d90362
--- /dev/null
+++ b/drivers/staging/gdm72xx/gdm_usb.c
@@ -0,0 +1,798 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <asm/byteorder.h>
+
+#include "gdm_usb.h"
+#include "gdm_wimax.h"
+#include "usb_boot.h"
+#include "hci.h"
+
+#include "usb_ids.h"
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+#define TX_BUF_SIZE 2048
+#if defined(CONFIG_WIMAX_GDM72XX_WIMAX2)
+#define RX_BUF_SIZE (128*1024) /* For packet aggregation */
+#else
+#define RX_BUF_SIZE 2048
+#endif
+
+#define GDM7205_PADDING 256
+
+#define H2B(x) __cpu_to_be16(x)
+#define B2H(x) __be16_to_cpu(x)
+#define DB2H(x) __be32_to_cpu(x)
+
+#define DOWNLOAD_CONF_VALUE 0x21
+
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+
+static DECLARE_WAIT_QUEUE_HEAD(k_wait);
+static LIST_HEAD(k_list);
+static DEFINE_SPINLOCK(k_lock);
+static int k_mode_stop;
+
+#define K_WAIT_TIME (2 * HZ / 100)
+
+#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
+
+static int init_usb(struct usbwm_dev *udev);
+static void release_usb(struct usbwm_dev *udev);
+
+/*#define DEBUG */
+#ifdef DEBUG
+static void hexdump(char *title, u8 *data, int len)
+{
+ int i;
+
+ printk(KERN_DEBUG "%s: length = %d\n", title, len);
+ for (i = 0; i < len; i++) {
+ printk(KERN_DEBUG "%02x ", data[i]);
+ if ((i & 0xf) == 0xf)
+ printk(KERN_DEBUG "\n");
+ }
+ printk(KERN_DEBUG "\n");
+}
+#endif
+
+static struct usb_tx *alloc_tx_struct(struct tx_cxt *tx)
+{
+ struct usb_tx *t = NULL;
+
+ t = kmalloc(sizeof(*t), GFP_ATOMIC);
+ if (t == NULL)
+ goto out;
+
+ memset(t, 0, sizeof(*t));
+
+ t->urb = usb_alloc_urb(0, GFP_ATOMIC);
+ t->buf = kmalloc(TX_BUF_SIZE, GFP_ATOMIC);
+ if (t->urb == NULL || t->buf == NULL)
+ goto out;
+
+ t->tx_cxt = tx;
+
+ return t;
+out:
+ if (t) {
+ usb_free_urb(t->urb);
+ kfree(t->buf);
+ kfree(t);
+ }
+ return NULL;
+}
+
+static void free_tx_struct(struct usb_tx *t)
+{
+ if (t) {
+ usb_free_urb(t->urb);
+ kfree(t->buf);
+ kfree(t);
+ }
+}
+
+static struct usb_rx *alloc_rx_struct(struct rx_cxt *rx)
+{
+ struct usb_rx *r = NULL;
+
+ r = kmalloc(sizeof(*r), GFP_ATOMIC);
+ if (r == NULL)
+ goto out;
+
+ memset(r, 0, sizeof(*r));
+
+ r->urb = usb_alloc_urb(0, GFP_ATOMIC);
+ r->buf = kmalloc(RX_BUF_SIZE, GFP_ATOMIC);
+ if (r->urb == NULL || r->buf == NULL)
+ goto out;
+
+ r->rx_cxt = rx;
+ return r;
+out:
+ if (r) {
+ usb_free_urb(r->urb);
+ kfree(r->buf);
+ kfree(r);
+ }
+ return NULL;
+}
+
+static void free_rx_struct(struct usb_rx *r)
+{
+ if (r) {
+ usb_free_urb(r->urb);
+ kfree(r->buf);
+ kfree(r);
+ }
+}
+
+/* Before this function is called, spin lock should be locked. */
+static struct usb_tx *get_tx_struct(struct tx_cxt *tx, int *no_spc)
+{
+ struct usb_tx *t;
+
+ if (list_empty(&tx->free_list)) {
+ *no_spc = 1;
+ return NULL;
+ }
+
+ t = list_entry(tx->free_list.next, struct usb_tx, list);
+ list_del(&t->list);
+
+ *no_spc = list_empty(&tx->free_list) ? 1 : 0;
+
+ return t;
+}
+
+/* Before this function is called, spin lock should be locked. */
+static void put_tx_struct(struct tx_cxt *tx, struct usb_tx *t)
+{
+ list_add_tail(&t->list, &tx->free_list);
+}
+
+/* Before this function is called, spin lock should be locked. */
+static struct usb_rx *get_rx_struct(struct rx_cxt *rx)
+{
+ struct usb_rx *r;
+
+ if (list_empty(&rx->free_list)) {
+ r = alloc_rx_struct(rx);
+ if (r == NULL)
+ return NULL;
+
+ list_add(&r->list, &rx->free_list);
+ }
+
+ r = list_entry(rx->free_list.next, struct usb_rx, list);
+ list_del(&r->list);
+ list_add_tail(&r->list, &rx->used_list);
+
+ return r;
+}
+
+/* Before this function is called, spin lock should be locked. */
+static void put_rx_struct(struct rx_cxt *rx, struct usb_rx *r)
+{
+ list_del(&r->list);
+ list_add(&r->list, &rx->free_list);
+}
+
+static int init_usb(struct usbwm_dev *udev)
+{
+ int ret = 0, i;
+ struct tx_cxt *tx = &udev->tx;
+ struct rx_cxt *rx = &udev->rx;
+ struct usb_tx *t;
+ struct usb_rx *r;
+
+ INIT_LIST_HEAD(&tx->free_list);
+ INIT_LIST_HEAD(&tx->sdu_list);
+ INIT_LIST_HEAD(&tx->hci_list);
+#if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE)
+ INIT_LIST_HEAD(&tx->pending_list);
+#endif
+
+ INIT_LIST_HEAD(&rx->free_list);
+ INIT_LIST_HEAD(&rx->used_list);
+
+ spin_lock_init(&tx->lock);
+ spin_lock_init(&rx->lock);
+
+ for (i = 0; i < MAX_NR_SDU_BUF; i++) {
+ t = alloc_tx_struct(tx);
+ if (t == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ list_add(&t->list, &tx->free_list);
+ }
+
+ r = alloc_rx_struct(rx);
+ if (r == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ list_add(&r->list, &rx->free_list);
+ return ret;
+
+fail:
+ release_usb(udev);
+ return ret;
+}
+
+static void release_usb(struct usbwm_dev *udev)
+{
+ struct tx_cxt *tx = &udev->tx;
+ struct rx_cxt *rx = &udev->rx;
+ struct usb_tx *t, *t_next;
+ struct usb_rx *r, *r_next;
+
+ list_for_each_entry_safe(t, t_next, &tx->sdu_list, list) {
+ list_del(&t->list);
+ free_tx_struct(t);
+ }
+
+ list_for_each_entry_safe(t, t_next, &tx->hci_list, list) {
+ list_del(&t->list);
+ free_tx_struct(t);
+ }
+
+ list_for_each_entry_safe(t, t_next, &tx->free_list, list) {
+ list_del(&t->list);
+ free_tx_struct(t);
+ }
+
+ list_for_each_entry_safe(r, r_next, &rx->free_list, list) {
+ list_del(&r->list);
+ free_rx_struct(r);
+ }
+
+ list_for_each_entry_safe(r, r_next, &rx->used_list, list) {
+ list_del(&r->list);
+ free_rx_struct(r);
+ }
+}
+
+static void gdm_usb_send_complete(struct urb *urb)
+{
+ struct usb_tx *t = urb->context;
+ struct tx_cxt *tx = t->tx_cxt;
+ u8 *pkt = t->buf;
+ u16 cmd_evt;
+ unsigned long flags;
+
+ /* Completion by usb_unlink_urb */
+ if (urb->status == -ECONNRESET)
+ return;
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ if (t->callback)
+ t->callback(t->cb_data);
+
+ /* Delete from sdu list or hci list. */
+ list_del(&t->list);
+
+ cmd_evt = (pkt[0] << 8) | pkt[1];
+ if (cmd_evt == WIMAX_TX_SDU)
+ put_tx_struct(tx, t);
+ else
+ free_tx_struct(t);
+
+ spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static int gdm_usb_send(void *priv_dev, void *data, int len,
+ void (*cb)(void *data), void *cb_data)
+{
+ struct usbwm_dev *udev = priv_dev;
+ struct usb_device *usbdev = udev->usbdev;
+ struct tx_cxt *tx = &udev->tx;
+ struct usb_tx *t;
+ int padding = udev->padding;
+ int no_spc = 0, ret;
+ u8 *pkt = data;
+ u16 cmd_evt;
+ unsigned long flags;
+
+ if (!udev->usbdev) {
+ printk(KERN_ERR "%s: No such device\n", __func__);
+ return -ENODEV;
+ }
+
+ BUG_ON(len > TX_BUF_SIZE - padding - 1);
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ cmd_evt = (pkt[0] << 8) | pkt[1];
+ if (cmd_evt == WIMAX_TX_SDU) {
+ t = get_tx_struct(tx, &no_spc);
+ if (t == NULL) {
+ /* This case must not happen. */
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return -ENOSPC;
+ }
+ list_add_tail(&t->list, &tx->sdu_list);
+ } else {
+ t = alloc_tx_struct(tx);
+ if (t == NULL) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return -ENOMEM;
+ }
+ list_add_tail(&t->list, &tx->hci_list);
+ }
+
+ memcpy(t->buf + padding, data, len);
+ t->callback = cb;
+ t->cb_data = cb_data;
+
+ /*
+ * In some cases, USB Module of WiMax is blocked when data size is
+ * the multiple of 512. So, increment length by one in that case.
+ */
+ if ((len % 512) == 0)
+ len++;
+
+ usb_fill_bulk_urb(t->urb,
+ usbdev,
+ usb_sndbulkpipe(usbdev, 1),
+ t->buf,
+ len + padding,
+ gdm_usb_send_complete,
+ t);
+
+#ifdef DEBUG
+ hexdump("usb_send", t->buf, len + padding);
+#endif
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ if (usbdev->state & USB_STATE_SUSPENDED) {
+ list_add_tail(&t->p_list, &tx->pending_list);
+ schedule_work(&udev->pm_ws);
+ goto out;
+ }
+#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
+
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+ if (udev->bw_switch) {
+ list_add_tail(&t->p_list, &tx->pending_list);
+ goto out;
+ } else if (cmd_evt == WIMAX_SCAN) {
+ struct rx_cxt *rx;
+ struct usb_rx *r;
+
+ rx = &udev->rx;
+
+ list_for_each_entry(r, &rx->used_list, list)
+ usb_unlink_urb(r->urb);
+ udev->bw_switch = 1;
+
+ spin_lock(&k_lock);
+ list_add_tail(&udev->list, &k_list);
+ spin_unlock(&k_lock);
+
+ wake_up(&k_wait);
+ }
+#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
+
+ ret = usb_submit_urb(t->urb, GFP_ATOMIC);
+ if (ret)
+ goto send_fail;
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ usb_mark_last_busy(usbdev);
+#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
+
+#if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE)
+out:
+#endif
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ if (no_spc)
+ return -ENOSPC;
+
+ return 0;
+
+send_fail:
+ t->callback = NULL;
+ gdm_usb_send_complete(t->urb);
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return ret;
+}
+
+static void gdm_usb_rcv_complete(struct urb *urb)
+{
+ struct usb_rx *r = urb->context;
+ struct rx_cxt *rx = r->rx_cxt;
+ struct usbwm_dev *udev = container_of(r->rx_cxt, struct usbwm_dev, rx);
+ struct tx_cxt *tx = &udev->tx;
+ struct usb_tx *t;
+ u16 cmd_evt;
+ unsigned long flags;
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ struct usb_device *dev = urb->dev;
+#endif
+
+ /* Completion by usb_unlink_urb */
+ if (urb->status == -ECONNRESET)
+ return;
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ if (!urb->status) {
+ cmd_evt = (r->buf[0] << 8) | (r->buf[1]);
+#ifdef DEBUG
+ hexdump("usb_receive", r->buf, urb->actual_length);
+#endif
+ if (cmd_evt == WIMAX_SDU_TX_FLOW) {
+ if (r->buf[4] == 0) {
+#ifdef DEBUG
+ printk(KERN_DEBUG "WIMAX ==> STOP SDU TX\n");
+#endif
+ list_for_each_entry(t, &tx->sdu_list, list)
+ usb_unlink_urb(t->urb);
+ } else if (r->buf[4] == 1) {
+#ifdef DEBUG
+ printk(KERN_DEBUG "WIMAX ==> START SDU TX\n");
+#endif
+ list_for_each_entry(t, &tx->sdu_list, list) {
+ usb_submit_urb(t->urb, GFP_ATOMIC);
+ }
+ /*
+ * If free buffer for sdu tx doesn't
+ * exist, then tx queue should not be
+ * woken. For this reason, don't pass
+ * the command, START_SDU_TX.
+ */
+ if (list_empty(&tx->free_list))
+ urb->actual_length = 0;
+ }
+ }
+ }
+
+ if (!urb->status && r->callback)
+ r->callback(r->cb_data, r->buf, urb->actual_length);
+
+ spin_lock(&rx->lock);
+ put_rx_struct(rx, r);
+ spin_unlock(&rx->lock);
+
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ usb_mark_last_busy(dev);
+#endif
+}
+
+static int gdm_usb_receive(void *priv_dev,
+ void (*cb)(void *cb_data, void *data, int len),
+ void *cb_data)
+{
+ struct usbwm_dev *udev = priv_dev;
+ struct usb_device *usbdev = udev->usbdev;
+ struct rx_cxt *rx = &udev->rx;
+ struct usb_rx *r;
+ unsigned long flags;
+
+ if (!udev->usbdev) {
+ printk(KERN_ERR "%s: No such device\n", __func__);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&rx->lock, flags);
+ r = get_rx_struct(rx);
+ spin_unlock_irqrestore(&rx->lock, flags);
+
+ if (r == NULL)
+ return -ENOMEM;
+
+ r->callback = cb;
+ r->cb_data = cb_data;
+
+ usb_fill_bulk_urb(r->urb,
+ usbdev,
+ usb_rcvbulkpipe(usbdev, 0x82),
+ r->buf,
+ RX_BUF_SIZE,
+ gdm_usb_rcv_complete,
+ r);
+
+ return usb_submit_urb(r->urb, GFP_ATOMIC);
+}
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+static void do_pm_control(struct work_struct *work)
+{
+ struct usbwm_dev *udev = container_of(work, struct usbwm_dev, pm_ws);
+ struct tx_cxt *tx = &udev->tx;
+ int ret;
+ unsigned long flags;
+
+ ret = usb_autopm_get_interface(udev->intf);
+ if (!ret)
+ usb_autopm_put_interface(udev->intf);
+
+ spin_lock_irqsave(&tx->lock, flags);
+ if (!(udev->usbdev->state & USB_STATE_SUSPENDED)
+ && (!list_empty(&tx->hci_list) || !list_empty(&tx->sdu_list))) {
+ struct usb_tx *t, *temp;
+
+ list_for_each_entry_safe(t, temp, &tx->pending_list, p_list) {
+ list_del(&t->p_list);
+ ret = usb_submit_urb(t->urb, GFP_ATOMIC);
+
+ if (ret) {
+ t->callback = NULL;
+ gdm_usb_send_complete(t->urb);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&tx->lock, flags);
+}
+#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
+
+static int gdm_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret = 0;
+ u8 bConfigurationValue;
+ struct phy_dev *phy_dev = NULL;
+ struct usbwm_dev *udev = NULL;
+ u16 idVendor, idProduct, bcdDevice;
+
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+
+ usb_get_dev(usbdev);
+ bConfigurationValue = usbdev->actconfig->desc.bConfigurationValue;
+
+ /*USB description is set up with Little-Endian*/
+ idVendor = L2H(usbdev->descriptor.idVendor);
+ idProduct = L2H(usbdev->descriptor.idProduct);
+ bcdDevice = L2H(usbdev->descriptor.bcdDevice);
+
+ printk(KERN_INFO "Found GDM USB VID = 0x%04x PID = 0x%04x...\n",
+ idVendor, idProduct);
+ printk(KERN_INFO "GCT WiMax driver version %s\n", DRIVER_VERSION);
+
+
+ if (idProduct == EMERGENCY_PID) {
+ ret = usb_emergency(usbdev);
+ goto out;
+ }
+
+ /* Support for EEPROM bootloader */
+ if (bConfigurationValue == DOWNLOAD_CONF_VALUE ||
+ idProduct & B_DOWNLOAD) {
+ ret = usb_boot(usbdev, bcdDevice);
+ goto out;
+ }
+
+ phy_dev = kmalloc(sizeof(*phy_dev), GFP_KERNEL);
+ if (phy_dev == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ udev = kmalloc(sizeof(*udev), GFP_KERNEL);
+ if (udev == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(phy_dev, 0, sizeof(*phy_dev));
+ memset(udev, 0, sizeof(*udev));
+
+ if (idProduct == 0x7205 || idProduct == 0x7206)
+ udev->padding = GDM7205_PADDING;
+ else
+ udev->padding = 0;
+
+ phy_dev->priv_dev = (void *)udev;
+ phy_dev->send_func = gdm_usb_send;
+ phy_dev->rcv_func = gdm_usb_receive;
+
+ ret = init_usb(udev);
+ if (ret < 0)
+ goto out;
+
+ udev->usbdev = usbdev;
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ udev->intf = intf;
+
+ intf->needs_remote_wakeup = 1;
+ device_init_wakeup(&intf->dev, 1);
+
+ pm_runtime_set_autosuspend_delay(&usbdev->dev, 10 * 1000); /* msec */
+
+ INIT_WORK(&udev->pm_ws, do_pm_control);
+#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
+
+ ret = register_wimax_device(phy_dev, &intf->dev);
+
+out:
+ if (ret) {
+ kfree(phy_dev);
+ kfree(udev);
+ }
+ usb_set_intfdata(intf, phy_dev);
+ return ret;
+}
+
+static void gdm_usb_disconnect(struct usb_interface *intf)
+{
+ u8 bConfigurationValue;
+ struct phy_dev *phy_dev;
+ struct usbwm_dev *udev;
+ u16 idProduct;
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+
+ bConfigurationValue = usbdev->actconfig->desc.bConfigurationValue;
+ phy_dev = usb_get_intfdata(intf);
+
+ /*USB description is set up with Little-Endian*/
+ idProduct = L2H(usbdev->descriptor.idProduct);
+
+ if (idProduct != EMERGENCY_PID &&
+ bConfigurationValue != DOWNLOAD_CONF_VALUE &&
+ (idProduct & B_DOWNLOAD) == 0) {
+ udev = phy_dev->priv_dev;
+ udev->usbdev = NULL;
+
+ unregister_wimax_device(phy_dev);
+ release_usb(udev);
+ kfree(udev);
+ kfree(phy_dev);
+ }
+
+ usb_put_dev(usbdev);
+}
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+static int gdm_suspend(struct usb_interface *intf, pm_message_t pm_msg)
+{
+ struct phy_dev *phy_dev;
+ struct usbwm_dev *udev;
+ struct rx_cxt *rx;
+ struct usb_rx *r;
+
+ phy_dev = usb_get_intfdata(intf);
+ udev = phy_dev->priv_dev;
+ rx = &udev->rx;
+
+ list_for_each_entry(r, &rx->used_list, list)
+ usb_unlink_urb(r->urb);
+
+ return 0;
+}
+
+static int gdm_resume(struct usb_interface *intf)
+{
+ struct phy_dev *phy_dev;
+ struct usbwm_dev *udev;
+ struct rx_cxt *rx;
+ struct usb_rx *r;
+
+ phy_dev = usb_get_intfdata(intf);
+ udev = phy_dev->priv_dev;
+ rx = &udev->rx;
+
+ list_for_each_entry(r, &rx->used_list, list)
+ usb_submit_urb(r->urb, GFP_ATOMIC);
+
+ return 0;
+}
+
+#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
+
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+static int k_mode_thread(void *arg)
+{
+ struct usbwm_dev *udev;
+ struct tx_cxt *tx;
+ struct rx_cxt *rx;
+ struct usb_tx *t, *temp;
+ struct usb_rx *r;
+ unsigned long flags, flags2, expire;
+ int ret;
+
+ daemonize("k_mode_wimax");
+
+ while (!k_mode_stop) {
+
+ spin_lock_irqsave(&k_lock, flags2);
+ while (!list_empty(&k_list)) {
+
+ udev = list_entry(k_list.next, struct usbwm_dev, list);
+ tx = &udev->tx;
+ rx = &udev->rx;
+
+ list_del(&udev->list);
+ spin_unlock_irqrestore(&k_lock, flags2);
+
+ expire = jiffies + K_WAIT_TIME;
+ while (jiffies < expire)
+ schedule_timeout(K_WAIT_TIME);
+
+ list_for_each_entry(r, &rx->used_list, list)
+ usb_submit_urb(r->urb, GFP_ATOMIC);
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ list_for_each_entry_safe(t, temp, &tx->pending_list,
+ p_list) {
+ list_del(&t->p_list);
+ ret = usb_submit_urb(t->urb, GFP_ATOMIC);
+
+ if (ret) {
+ t->callback = NULL;
+ gdm_usb_send_complete(t->urb);
+ }
+ }
+
+ udev->bw_switch = 0;
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ spin_lock_irqsave(&k_lock, flags2);
+ }
+ spin_unlock_irqrestore(&k_lock, flags2);
+
+ interruptible_sleep_on(&k_wait);
+ }
+ return 0;
+}
+#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
+
+static struct usb_driver gdm_usb_driver = {
+ .name = "gdm_wimax",
+ .probe = gdm_usb_probe,
+ .disconnect = gdm_usb_disconnect,
+ .id_table = id_table,
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ .supports_autosuspend = 1,
+ .suspend = gdm_suspend,
+ .resume = gdm_resume,
+ .reset_resume = gdm_resume,
+#endif
+};
+
+static int __init usb_gdm_wimax_init(void)
+{
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+ kernel_thread(k_mode_thread, NULL, CLONE_KERNEL);
+#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
+ return usb_register(&gdm_usb_driver);
+}
+
+static void __exit usb_gdm_wimax_exit(void)
+{
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+ k_mode_stop = 1;
+ wake_up(&k_wait);
+#endif
+ usb_deregister(&gdm_usb_driver);
+}
+
+module_init(usb_gdm_wimax_init);
+module_exit(usb_gdm_wimax_exit);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_DESCRIPTION("GCT WiMax Device Driver");
+MODULE_AUTHOR("Ethan Park");
+MODULE_LICENSE("GPL");