summaryrefslogtreecommitdiff
path: root/drivers/net/usb
diff options
context:
space:
mode:
authorMichael Hsu <mhsu@nvidia.com>2012-01-24 22:01:59 -0800
committerRohan Somvanshi <rsomvanshi@nvidia.com>2012-01-31 04:28:20 -0800
commited3149c88bc2a6e5761e284d5453895572f5ab29 (patch)
tree9ebb021b9c92cf20aa39d9d5f2d727207cebde32 /drivers/net/usb
parent7024f44804b81e3eeb3b2a580e235460d3d4d73b (diff)
net: usb: raw-ip: Defer tx urb submit to work function.
Defer tx urb submit to work function to avoid problems with autopm not fully resumed. BUG 929408 Reviewed-on: http://git-master/r/77228 Change-Id: Id559d65384780067e48faee6c79134eb35d298fc Signed-off-by: Michael Hsu <mhsu@nvidia.com> Signed-off-by: Varun Wadekar <vwadekar@nvidia.com> Reviewed-on: http://git-master/r/78014 Reviewed-by: Rohan Somvanshi <rsomvanshi@nvidia.com> Tested-by: Rohan Somvanshi <rsomvanshi@nvidia.com>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r--drivers/net/usb/raw_ip_net.c238
1 files changed, 182 insertions, 56 deletions
diff --git a/drivers/net/usb/raw_ip_net.c b/drivers/net/usb/raw_ip_net.c
index cd0013b0e50a..f2e1c1dd6250 100644
--- a/drivers/net/usb/raw_ip_net.c
+++ b/drivers/net/usb/raw_ip_net.c
@@ -82,6 +82,9 @@ struct baseband_usb {
struct urb *rx_urb;
/* currently active tx urb */
struct urb *tx_urb;
+ struct usb_anchor tx_urb_deferred;
+ struct workqueue_struct *tx_workqueue;
+ struct work_struct tx_work;
} usb;
};
@@ -94,6 +97,10 @@ static struct usb_interface *g_usb_interface[MAX_INTFS];
static int usb_net_raw_ip_rx_urb_submit(struct baseband_usb *usb);
static void usb_net_raw_ip_rx_urb_comp(struct urb *urb);
+
+static int usb_net_raw_ip_tx_urb_submit(struct baseband_usb *usb,
+ struct sk_buff *skb);
+static void usb_net_raw_ip_tx_urb_work(struct work_struct *work);
static void usb_net_raw_ip_tx_urb_comp(struct urb *urb);
static int baseband_usb_driver_probe(struct usb_interface *intf,
@@ -416,10 +423,8 @@ static int baseband_usb_netdev_stop(struct net_device *dev)
static netdev_tx_t baseband_usb_netdev_start_xmit(
struct sk_buff *skb, struct net_device *dev)
{
- int i = 0;
- struct baseband_usb *usb = baseband_usb_net[i];
- struct urb *urb;
- unsigned char *buf;
+ int i;
+ struct baseband_usb *usb;
int err;
pr_debug("baseband_usb_netdev_start_xmit\n");
@@ -429,63 +434,29 @@ static netdev_tx_t baseband_usb_netdev_start_xmit(
pr_err("no skb\n");
return -EINVAL;
}
-
- /* allocate urb */
- urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!urb) {
- pr_err("usb_alloc_urb() failed\n");
- kfree_skb(skb);
- return -ENOMEM;
- }
- buf = kzalloc(skb->len - 14, GFP_ATOMIC);
- if (!buf) {
- pr_err("usb buffer kzalloc() failed\n");
- usb_free_urb(urb);
- kfree_skb(skb);
- return -ENOMEM;
- }
- err = skb_copy_bits(skb, 14, buf, skb->len - 14);
- if (err < 0) {
- pr_err("skb_copy_bits() failed - %d\n", err);
- kfree(buf);
- usb_free_urb(urb);
- kfree_skb(skb);
- return err;
+ if (!dev) {
+ pr_err("no net dev\n");
+ return -EINVAL;
}
- usb_fill_bulk_urb(urb, usb->usb.device, usb->usb.pipe.bulk.out,
- buf, skb->len - 14,
- usb_net_raw_ip_tx_urb_comp,
- usb);
- urb->transfer_flags = URB_ZERO_PACKET;
- /* autoresume before tx */
- err = usb_autopm_get_interface_async(usb->usb.interface);
- if (err < 0) {
- pr_err("%s: usb_autopm_get_interface(%p) failed %d\n",
- __func__, usb->usb.interface, err);
- kfree(urb->transfer_buffer);
- usb_free_urb(urb);
- kfree_skb(skb);
- return err;
+ /* find index of network device which is transmitting */
+ for (i = 0; i < max_intfs; i++) {
+ if (usb_net_raw_ip_dev[i] == dev)
+ break;
+ }
+ if (i >= max_intfs) {
+ pr_err("unknown net dev %p\n", dev);
+ return -EINVAL;
}
+ usb = baseband_usb_net[i];
/* submit tx urb */
- usb_mark_last_busy(usb->usb.device);
- usb->usb.tx_urb = urb;
- err = usb_submit_urb(urb, GFP_ATOMIC);
+ err = usb_net_raw_ip_tx_urb_submit(usb, skb);
if (err < 0) {
- pr_err("usb_submit_urb() failed - err %d\n", err);
- usb_autopm_put_interface_async(usb->usb.interface);
- usb->usb.tx_urb = (struct urb *) 0;
- kfree(urb->transfer_buffer);
- usb_free_urb(urb);
- kfree_skb(skb);
- return err;
+ pr_err("tx urb submit error\n");
+ return -EINVAL;
}
- /* free skb */
- consume_skb(skb);
-
return NETDEV_TX_OK;
}
@@ -506,9 +477,13 @@ static int usb_net_raw_ip_rx_urb_submit(struct baseband_usb *usb)
pr_debug("usb_net_raw_ip_rx_urb_submit { usb %p\n", usb);
/* check input */
+ if (!usb) {
+ pr_err("%s: !usb\n", __func__);
+ return -EINVAL;
+ }
if (usb->usb.rx_urb) {
pr_err("previous urb still active\n");
- return -1;
+ return -EBUSY;
}
if (!usb->usb.interface) {
pr_err("usb interface disconnected - not submitting rx urb\n");
@@ -643,6 +618,106 @@ static void usb_net_raw_ip_rx_urb_comp(struct urb *urb)
pr_debug("usb_net_raw_ip_rx_urb_comp }\n");
}
+static int usb_net_raw_ip_tx_urb_submit(struct baseband_usb *usb,
+ struct sk_buff *skb)
+{
+ struct urb *urb;
+ unsigned char *buf;
+ int err;
+
+ pr_debug("usb_net_raw_ip_tx_urb_submit {\n");
+
+ /* check input */
+ if (!usb) {
+ pr_err("%s: !usb\n", __func__);
+ return -EINVAL;
+ }
+ if (!skb) {
+ pr_err("%s: !skb\n", __func__);
+ return -EINVAL;
+ }
+
+ /* allocate urb */
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ pr_err("usb_alloc_urb() failed\n");
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+ buf = kzalloc(skb->len - 14, GFP_ATOMIC);
+ if (!buf) {
+ pr_err("usb buffer kzalloc() failed\n");
+ usb_free_urb(urb);
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+ err = skb_copy_bits(skb, 14, buf, skb->len - 14);
+ if (err < 0) {
+ pr_err("skb_copy_bits() failed - %d\n", err);
+ kfree(buf);
+ usb_free_urb(urb);
+ kfree_skb(skb);
+ return err;
+ }
+ usb_fill_bulk_urb(urb, usb->usb.device, usb->usb.pipe.bulk.out,
+ buf, skb->len - 14,
+ usb_net_raw_ip_tx_urb_comp,
+ usb);
+ urb->transfer_flags = URB_ZERO_PACKET;
+
+ /* queue tx urb work */
+ usb_anchor_urb(urb, &usb->usb.tx_urb_deferred);
+ queue_work(usb->usb.tx_workqueue, &usb->usb.tx_work);
+
+ /* free skb */
+ consume_skb(skb);
+
+ pr_debug("usb_net_raw_ip_tx_urb_submit }\n");
+ return 0;
+}
+
+static void usb_net_raw_ip_tx_urb_work(struct work_struct *work)
+{
+ struct baseband_usb *usb
+ = container_of(work, struct baseband_usb, usb.tx_work);
+ struct urb *urb;
+ int err;
+
+ pr_debug("usb_net_raw_ip_tx_urb_work {\n");
+
+ /* check if tx urb(s) queued */
+ if (!usb->usb.tx_urb && usb_anchor_empty(&usb->usb.tx_urb_deferred)) {
+ pr_debug("%s: nothing to do!\n", __func__);
+ return;
+ }
+
+ /* submit queued tx urb(s) */
+ while ((urb = usb_get_from_anchor(&usb->usb.tx_urb_deferred))
+ != (struct urb *) 0) {
+ /* autoresume before tx */
+ usb_mark_last_busy(usb->usb.device);
+ err = usb_autopm_get_interface(usb->usb.interface);
+ if (err < 0) {
+ pr_err("%s: usb_autopm_get_interface(%p) failed %d\n",
+ __func__, usb->usb.interface, err);
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ continue;
+ }
+ /* submit tx urb */
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ pr_err("usb_submit_urb() failed - err %d\n", err);
+ usb_autopm_put_interface(usb->usb.interface);
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ continue;
+ }
+ }
+
+ pr_debug("usb_net_raw_ip_tx_urb_work }\n");
+}
+
static void usb_net_raw_ip_tx_urb_comp(struct urb *urb)
{
struct baseband_usb *usb = (struct baseband_usb *) urb->context;
@@ -655,10 +730,9 @@ static void usb_net_raw_ip_tx_urb_comp(struct urb *urb)
urb->transfer_buffer = (void *) 0;
}
usb_free_urb(urb);
- usb->usb.tx_urb = (struct urb *) 0;
/* autosuspend after tx completed */
- usb_autopm_put_interface_async(usb->usb.interface);
+ usb_autopm_put_interface(usb->usb.interface);
pr_debug("usb_net_raw_ip_tx_urb_comp }\n");
}
@@ -667,6 +741,7 @@ static int usb_net_raw_ip_init(void)
{
int i;
int err;
+ char name[32];
pr_debug("usb_net_raw_ip_init {\n");
@@ -707,6 +782,19 @@ static int usb_net_raw_ip_init(void)
pr_err("submit rx failed - err %d\n", err);
goto error_exit;
}
+ /* start usb tx */
+ init_usb_anchor(&baseband_usb_net[i]->usb.tx_urb_deferred);
+ sprintf(name, "usb_net_raw_ip_tx_workqueue-%p-%d",
+ baseband_usb_net[i],
+ baseband_usb_net[i]->baseband_index);
+ baseband_usb_net[i]->usb.tx_workqueue
+ = create_singlethread_workqueue(name);
+ if (!baseband_usb_net[i]->usb.tx_workqueue) {
+ pr_err("cannot create workqueue\n");
+ goto error_exit;
+ }
+ INIT_WORK(&baseband_usb_net[i]->usb.tx_work,
+ usb_net_raw_ip_tx_urb_work);
}
pr_debug("usb_net_raw_ip_init }\n");
@@ -723,6 +811,25 @@ error_exit:
}
/* close baseband usb */
if (baseband_usb_net[i]) {
+ /* stop usb tx */
+ if (baseband_usb_net[i]->usb.tx_workqueue) {
+ destroy_workqueue(baseband_usb_net[i]
+ ->usb.tx_workqueue);
+ baseband_usb_net[i]->usb.tx_workqueue
+ = (struct workqueue_struct *) 0;
+ }
+ if (baseband_usb_net[i]->usb.tx_urb) {
+ usb_kill_urb(baseband_usb_net[i]->usb.tx_urb);
+ baseband_usb_net[i]->usb.tx_urb
+ = (struct urb *) 0;
+ }
+ /* stop usb rx */
+ if (baseband_usb_net[i]->usb.rx_urb) {
+ usb_kill_urb(baseband_usb_net[i]->usb.rx_urb);
+ baseband_usb_net[i]->usb.rx_urb
+ = (struct urb *) 0;
+ }
+ /* close usb */
baseband_usb_close(baseband_usb_net[i]);
baseband_usb_net[i] = (struct baseband_usb *) 0;
}
@@ -747,6 +854,25 @@ static void usb_net_raw_ip_exit(void)
}
/* close baseband usb */
if (baseband_usb_net[i]) {
+ /* stop usb tx */
+ if (baseband_usb_net[i]->usb.tx_workqueue) {
+ destroy_workqueue(baseband_usb_net[i]
+ ->usb.tx_workqueue);
+ baseband_usb_net[i]->usb.tx_workqueue
+ = (struct workqueue_struct *) 0;
+ }
+ if (baseband_usb_net[i]->usb.tx_urb) {
+ usb_kill_urb(baseband_usb_net[i]->usb.tx_urb);
+ baseband_usb_net[i]->usb.tx_urb
+ = (struct urb *) 0;
+ }
+ /* stop usb rx */
+ if (baseband_usb_net[i]->usb.rx_urb) {
+ usb_kill_urb(baseband_usb_net[i]->usb.rx_urb);
+ baseband_usb_net[i]->usb.rx_urb
+ = (struct urb *) 0;
+ }
+ /* close usb */
baseband_usb_close(baseband_usb_net[i]);
baseband_usb_net[i] = (struct baseband_usb *) 0;
}