diff options
Diffstat (limited to 'drivers/net/usb/raw_ip_net.c')
-rw-r--r-- | drivers/net/usb/raw_ip_net.c | 191 |
1 files changed, 156 insertions, 35 deletions
diff --git a/drivers/net/usb/raw_ip_net.c b/drivers/net/usb/raw_ip_net.c index 35b696966479..1b3b89b3c274 100644 --- a/drivers/net/usb/raw_ip_net.c +++ b/drivers/net/usb/raw_ip_net.c @@ -67,8 +67,13 @@ module_param(usb_net_raw_ip_tx_debug, ulong, 0644); MODULE_PARM_DESC(usb_net_raw_ip_tx_debug, "usb net (raw-ip) - tx debug"); struct baseband_usb { + /* semaphore between disconnect/suspend/resume */ + struct semaphore sem; + /* instance */ int baseband_index; + /* network statistics */ struct net_device_stats stats; + /* usb context */ struct { struct usb_driver *driver; struct usb_device *device; @@ -87,8 +92,11 @@ struct baseband_usb { struct workqueue_struct *tx_workqueue; struct work_struct tx_work; } usb; + /* re-usable rx urb */ struct urb *urb_r; void *buff; + /* suspend count */ + int susp_count; }; static struct baseband_usb *baseband_usb_net[MAX_INTFS] = { 0, 0, 0}; @@ -158,6 +166,11 @@ static void baseband_usb_driver_disconnect(struct usb_interface *intf) baseband_usb_net[i]->usb.interface, intf); continue; } + /* acquire semaphore */ + if (down_interruptible(&baseband_usb_net[i]->sem)) { + pr_err("%s: cannot acquire semaphore\n", __func__); + continue; + } /* kill usb tx */ while ((urb = usb_get_from_anchor(&baseband_usb_net[i]-> usb.tx_urb_deferred)) != (struct urb *) 0) { @@ -192,6 +205,8 @@ static void baseband_usb_driver_disconnect(struct usb_interface *intf) /* mark interface as disconnected */ baseband_usb_net[i]->usb.interface = (struct usb_interface *) 0; + /* release semaphore */ + up(&baseband_usb_net[i]->sem); } } @@ -200,10 +215,14 @@ static void baseband_usb_driver_disconnect(struct usb_interface *intf) static int baseband_usb_driver_suspend(struct usb_interface *intf, pm_message_t message) { - int i; + int i, susp_count; pr_debug("%s intf %p\n", __func__, intf); + pr_debug("%s: cnt %d intf=%p &intf->dev=%p kobj=%s\n", + __func__, atomic_read(&intf->dev.power.usage_count), + intf, &intf->dev, kobject_name(&intf->dev.kobj)); + for (i = 0; i < max_intfs; i++) { pr_debug("[%d]\n", i); if (!baseband_usb_net[i]) @@ -213,13 +232,47 @@ static int baseband_usb_driver_suspend(struct usb_interface *intf, baseband_usb_net[i]->usb.interface, intf); continue; } + /* increment suspend count */ + susp_count = (baseband_usb_net[i]->susp_count)++; + if (susp_count > 0) { + pr_debug("%s: susp_count %d > 0 (already suspended)\n", + __func__, susp_count); + continue; + } + if (susp_count < 0) { + pr_debug("%s: susp_count %d < 0 (ILLEGAL VALUE)\n", + __func__, susp_count); + baseband_usb_net[i]->susp_count = 0; + continue; + } + pr_debug("%s: susp_count = %d (suspending...)\n", + __func__, susp_count); + /* acquire semaphore */ + if (down_interruptible(&baseband_usb_net[i]->sem)) { + pr_err("%s: cannot acquire semaphore\n", __func__); + continue; + } /* kill usb rx */ if (!baseband_usb_net[i]->usb.rx_urb) { pr_debug("rx_usb already killed\n"); + up(&baseband_usb_net[i]->sem); continue; } + pr_debug("%s: kill rx_urb {\n",__func__); usb_kill_urb(baseband_usb_net[i]->usb.rx_urb); + pr_debug("%s: kill rx_urb }\n",__func__); baseband_usb_net[i]->usb.rx_urb = (struct urb *) 0; + /* cancel tx urb work (will restart after resume) */ + if (!baseband_usb_net[i]->usb.tx_workqueue) { + pr_err("%s: !tx_workqueue\n", __func__); + up(&baseband_usb_net[i]->sem); + continue; + } + pr_debug("%s: cancel_work_sync {\n",__func__); + cancel_work_sync(&baseband_usb_net[i]->usb.tx_work); + pr_debug("%s: cancel_work_sync }\n",__func__); + /* release semaphore */ + up(&baseband_usb_net[i]->sem); } return 0; @@ -227,10 +280,14 @@ static int baseband_usb_driver_suspend(struct usb_interface *intf, static int baseband_usb_driver_resume(struct usb_interface *intf) { - int i, err; + int i, err, susp_count; pr_debug("%s intf %p\n", __func__, intf); + pr_debug("%s: cnt %d intf=%p &intf->dev=%p kobj=%s\n", + __func__, atomic_read(&intf->dev.power.usage_count), + intf, &intf->dev, kobject_name(&intf->dev.kobj)); + for (i = 0; i < max_intfs; i++) { pr_debug("[%d]\n", i); if (!baseband_usb_net[i]) @@ -240,16 +297,48 @@ static int baseband_usb_driver_resume(struct usb_interface *intf) baseband_usb_net[i]->usb.interface, intf); continue; } + /* decrement suspend count */ + susp_count = --(baseband_usb_net[i]->susp_count); + if (susp_count > 0) { + pr_debug("%s: susp_count %d > 0 (not resuming yet)\n", + __func__, susp_count); + continue; + } + if (susp_count < 0) { + pr_debug("%s: susp_count %d < 0 (ILLEGAL VALUE)\n", + __func__, susp_count); + baseband_usb_net[i]->susp_count = 0; + continue; + } + pr_debug("%s: susp_count = %d (resuming...)\n", + __func__, susp_count); + /* acquire semaphore */ + if (down_interruptible(&baseband_usb_net[i]->sem)) { + pr_err("%s: cannot acquire semaphore\n", __func__); + continue; + } /* start usb rx */ if (baseband_usb_net[i]->usb.rx_urb) { pr_debug("rx_usb already exists\n"); + up(&baseband_usb_net[i]->sem); continue; } err = usb_net_raw_ip_rx_urb_submit(baseband_usb_net[i]); if (err < 0) { pr_err("submit rx failed - err %d\n", err); + up(&baseband_usb_net[i]->sem); + continue; + } + /* restart tx urb work (cancelled in suspend) */ + if (!baseband_usb_net[i]->usb.tx_workqueue) { + pr_err("%s: !tx_workqueue\n", __func__); + up(&baseband_usb_net[i]->sem); continue; } + queue_work(baseband_usb_net[i]->usb.tx_workqueue, + &baseband_usb_net[i]->usb.tx_work); + /* release semaphore */ + up(&baseband_usb_net[i]->sem); } return 0; @@ -361,6 +450,9 @@ struct baseband_usb *baseband_usb_open(int index, if (!usb) return (struct baseband_usb *) 0; + /* create semaphores */ + sema_init(&usb->sem, 1); + /* open usb driver */ sprintf(baseband_usb_driver_name[index], "baseband_usb_%x_%x_%x", @@ -424,6 +516,9 @@ void baseband_usb_close(struct baseband_usb *usb) pr_debug("close usb driver }\n"); } + /* destroy semaphores */ + memset(&usb->sem, 0, sizeof(usb->sem)); + /* free baseband usb structure */ kfree(usb); @@ -485,10 +580,21 @@ static netdev_tx_t baseband_usb_netdev_start_xmit( } usb = baseband_usb_net[i]; + /* autoresume if suspended */ + if (usb->usb.interface) { + usb_autopm_get_interface_async(usb->usb.interface); + } else { + pr_err("%s: tx get interface error\n", __func__); + netif_stop_queue(dev); + usb->stats.tx_errors++; + return NETDEV_TX_BUSY; + } + /* submit tx urb */ err = usb_net_raw_ip_tx_urb_submit(usb, skb); if (err < 0) { - pr_err("tx urb submit error\n"); + pr_err("%s: tx urb submit error\n", __func__); + netif_stop_queue(dev); usb->stats.tx_errors++; return NETDEV_TX_BUSY; } @@ -570,8 +676,8 @@ 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) { - struct baseband_usb *usb = (struct baseband_usb *) urb->context; - int i = usb->baseband_index; + struct baseband_usb *usb; + int i; struct sk_buff *skb; unsigned char *dst; unsigned char ethernet_header[14] = { @@ -594,17 +700,20 @@ static void usb_net_raw_ip_rx_urb_comp(struct urb *urb) pr_err("no urb\n"); return; } + usb = (struct baseband_usb *)urb->context; + i = usb->baseband_index; switch (urb->status) { case 0: break; - case -ENOENT: - /* fall through */ case -ESHUTDOWN: /* fall through */ - case -EPROTO: pr_info("%s: rx urb %p - link shutdown %d\n", __func__, urb, urb->status); goto err_exit; + case -EPROTO: + pr_info("%s: rx urb %p - link shutdown %d EPROTO\n", + __func__, urb, urb->status); + goto err_exit; default: pr_info("%s: rx urb %p - status %d\n", __func__, urb, urb->status); @@ -612,7 +721,7 @@ static void usb_net_raw_ip_rx_urb_comp(struct urb *urb) } /* put rx urb data in rx buffer */ - if (urb->actual_length) { + if (urb->actual_length > 0) { pr_debug("usb_net_raw_ip_rx_urb_comp - " "urb->actual_length %d\n", urb->actual_length); /* allocate skb with space for @@ -661,6 +770,10 @@ static void usb_net_raw_ip_rx_urb_comp(struct urb *urb) /* mark rx urb complete */ usb->usb.rx_urb = (struct urb *) 0; + /* do not submit urb if interface is suspending */ + if (urb->status == -ENOENT) + return; + /* submit next rx urb */ usb_net_raw_ip_rx_urb_submit(usb); return; @@ -742,13 +855,13 @@ static int usb_net_raw_ip_tx_urb_submit(struct baseband_usb *usb, pr_err("%s: !usb\n", __func__); return -EINVAL; } - if (!skb) { - pr_err("%s: !skb\n", __func__); - return -EINVAL; - } if (!usb->usb.interface) { pr_err("usb interface disconnected - not submitting tx urb\n"); - kfree_skb(skb); + return -EINVAL; + } + if (!skb) { + pr_err("%s: !skb\n", __func__); + usb_autopm_put_interface_async(usb->usb.interface); return -EINVAL; } @@ -756,14 +869,14 @@ static int usb_net_raw_ip_tx_urb_submit(struct baseband_usb *usb, urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { pr_err("usb_alloc_urb() failed\n"); - kfree_skb(skb); + usb_autopm_put_interface_async(usb->usb.interface); 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); + usb_autopm_put_interface_async(usb->usb.interface); return -ENOMEM; } err = skb_copy_bits(skb, 14, buf, skb->len - 14); @@ -771,7 +884,7 @@ static int usb_net_raw_ip_tx_urb_submit(struct baseband_usb *usb, pr_err("skb_copy_bits() failed - %d\n", err); kfree(buf); usb_free_urb(urb); - kfree_skb(skb); + usb_autopm_put_interface_async(usb->usb.interface); return err; } usb_fill_bulk_urb(urb, usb->usb.device, usb->usb.pipe.bulk.out, @@ -801,11 +914,27 @@ static void usb_net_raw_ip_tx_urb_work(struct work_struct *work) 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)) { + if (usb == NULL || + (!usb->usb.tx_urb && + usb_anchor_empty(&usb->usb.tx_urb_deferred))) { pr_debug("%s: nothing to do!\n", __func__); return; } + /* check if usb interface disconnected */ + if (!usb->usb.interface) { + pr_err("%s: not submitting tx urb %p -interface disconnected\n", + __func__, urb); + return; + } + + /* check if suspended */ + if (usb->susp_count > 0) { + pr_info("%s: usb->susp_count %d > 0 (suspended)\n", + __func__, usb->susp_count); + return; + } + /* submit queued tx urb(s) */ while ((urb = usb_get_from_anchor(&usb->usb.tx_urb_deferred)) != (struct urb *) 0) { @@ -821,33 +950,23 @@ static void usb_net_raw_ip_tx_urb_work(struct work_struct *work) urb->transfer_buffer = (void *) 0; } usb_free_urb(urb); + usb->stats.tx_errors++; continue; } /* 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); - if (urb->transfer_buffer) { - kfree(urb->transfer_buffer); - urb->transfer_buffer = (void *) 0; - } - usb_free_urb(urb); - usb->stats.tx_errors++; - continue; - } /* submit tx urb */ err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { pr_err("%s: usb_submit_urb(%p) failed - err %d\n", __func__, urb, err); - usb_autopm_put_interface(usb->usb.interface); + usb_autopm_put_interface_async(usb->usb.interface); if (urb->transfer_buffer) { kfree(urb->transfer_buffer); urb->transfer_buffer = (void *) 0; } usb_free_urb(urb); + usb->stats.tx_errors++; continue; } /* free tx urb @@ -865,7 +984,7 @@ 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) { - struct baseband_usb *usb = (struct baseband_usb *) urb->context; + struct baseband_usb *usb; pr_debug("usb_net_raw_ip_tx_urb_comp {\n"); @@ -874,6 +993,7 @@ static void usb_net_raw_ip_tx_urb_comp(struct urb *urb) pr_err("no urb\n"); return; } + usb = (struct baseband_usb *)urb->context; switch (urb->status) { case 0: break; @@ -884,6 +1004,7 @@ static void usb_net_raw_ip_tx_urb_comp(struct urb *urb) case -EPROTO: pr_info("%s: tx urb %p - link shutdown %d\n", __func__, urb, urb->status); + usb_autopm_put_interface_async(usb->usb.interface); goto err_exit; default: pr_info("%s: tx urb %p - status %d\n", @@ -896,6 +1017,7 @@ static void usb_net_raw_ip_tx_urb_comp(struct urb *urb) usb->stats.tx_packets++; usb->stats.tx_bytes += urb->transfer_buffer_length; } + /* autosuspend after tx completed */ if (!usb->usb.interface) { pr_err("%s: usb interface disconnected" @@ -903,7 +1025,7 @@ static void usb_net_raw_ip_tx_urb_comp(struct urb *urb) __func__); goto err_exit; } - usb_autopm_put_interface(usb->usb.interface); + usb_autopm_put_interface_async(usb->usb.interface); err_exit: /* free tx urb transfer buffer */ @@ -911,7 +1033,6 @@ err_exit: kfree(urb->transfer_buffer); urb->transfer_buffer = (void *) 0; } - pr_debug("usb_net_raw_ip_tx_urb_comp }\n"); } @@ -934,6 +1055,7 @@ static int usb_net_raw_ip_init(void) err = -1; goto error_exit; } + init_usb_anchor(&baseband_usb_net[i]->usb.tx_urb_deferred); /* register network device */ usb_net_raw_ip_dev[i] = alloc_netdev(0, BASEBAND_USB_NET_DEV_NAME, @@ -966,7 +1088,6 @@ static int usb_net_raw_ip_init(void) goto error_exit; } /* start usb tx */ - init_usb_anchor(&baseband_usb_net[i]->usb.tx_urb_deferred); sprintf(name, "raw_ip_tx_wq-%d", baseband_usb_net[i]->baseband_index); baseband_usb_net[i]->usb.tx_workqueue |