Commit 606febd3 authored by Duncan Sands's avatar Duncan Sands Committed by Greg Kroah-Hartman

[PATCH] USB speedtouch: re-recycle failed speedtouch receive urbs

  speedtouch: more robust handling of receive urb failure: retry failed urbs whenever
  a new connection is opened.  This should work well with pppd's persist option.
parent 5180c534
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
* *
*/ */
#include <asm/semaphore.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -137,6 +138,8 @@ struct udsl_usb_send_data_context { ...@@ -137,6 +138,8 @@ struct udsl_usb_send_data_context {
*/ */
struct udsl_instance_data { struct udsl_instance_data {
struct semaphore serialize;
/* usb device part */ /* usb device part */
struct usb_device *usb_dev; struct usb_device *usb_dev;
struct udsl_data_ctx rcvbufs [UDSL_NUMBER_RCV_URBS]; struct udsl_data_ctx rcvbufs [UDSL_NUMBER_RCV_URBS];
...@@ -150,6 +153,9 @@ struct udsl_instance_data { ...@@ -150,6 +153,9 @@ struct udsl_instance_data {
struct atmsar_vcc_data *atmsar_vcc_list; struct atmsar_vcc_data *atmsar_vcc_list;
/* receiving */ /* receiving */
spinlock_t spare_receivers_lock;
struct list_head spare_receivers;
spinlock_t completed_receivers_lock; spinlock_t completed_receivers_lock;
struct list_head completed_receivers; struct list_head completed_receivers;
...@@ -428,6 +434,10 @@ static void udsl_atm_processqueue (unsigned long data) ...@@ -428,6 +434,10 @@ static void udsl_atm_processqueue (unsigned long data)
PDEBUG ("udsl_atm_processqueue: submission failed\n"); PDEBUG ("udsl_atm_processqueue: submission failed\n");
/* fall through */ /* fall through */
default: /* error or urb unlinked */ default: /* error or urb unlinked */
PDEBUG ("udsl_atm_processqueue: adding to spare_receivers\n");
spin_lock_irqsave (&instance->spare_receivers_lock, flags);
list_add (&ctx->list, &instance->spare_receivers);
spin_unlock_irqrestore (&instance->spare_receivers_lock, flags);
break; break;
} }
...@@ -443,6 +453,43 @@ static void udsl_atm_processqueue (unsigned long data) ...@@ -443,6 +453,43 @@ static void udsl_atm_processqueue (unsigned long data)
* SAR driver entries * SAR driver entries
* *
****************************************************************************/ ****************************************************************************/
static void udsl_fire_receivers (struct udsl_instance_data *instance)
{
struct list_head receivers, *pos, *n;
unsigned long flags;
INIT_LIST_HEAD (&receivers);
down (&instance->serialize);
spin_lock_irqsave (&instance->spare_receivers_lock, flags);
list_splice_init (&instance->spare_receivers, &receivers);
spin_unlock_irqrestore (&instance->spare_receivers_lock, flags);
list_for_each_safe (pos, n, &receivers) {
struct udsl_data_ctx *ctx = list_entry (pos, struct udsl_data_ctx, list);
PDEBUG ("udsl_fire_receivers: firing urb %p\n", ctx->urb);
usb_fill_bulk_urb (ctx->urb,
instance->usb_dev,
usb_rcvbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_IN),
(unsigned char *) ctx->skb->data,
UDSL_RECEIVE_BUFFER_SIZE,
udsl_usb_data_receive, ctx);
if (usb_submit_urb (ctx->urb, GFP_KERNEL) < 0) {
PDEBUG ("udsl_fire_receivers: submit failed!\n");
spin_lock_irqsave (&instance->spare_receivers_lock, flags);
list_move (pos, &instance->spare_receivers);
spin_unlock_irqrestore (&instance->spare_receivers_lock, flags);
}
}
up (&instance->serialize);
}
static int udsl_atm_open (struct atm_vcc *vcc, short vpi, int vci) static int udsl_atm_open (struct atm_vcc *vcc, short vpi, int vci)
{ {
struct udsl_atm_dev_data *dev_data; struct udsl_atm_dev_data *dev_data;
...@@ -482,6 +529,9 @@ static int udsl_atm_open (struct atm_vcc *vcc, short vpi, int vci) ...@@ -482,6 +529,9 @@ static int udsl_atm_open (struct atm_vcc *vcc, short vpi, int vci)
dev_data->atmsar_vcc->mtu = UDSL_MAX_AAL5_MRU; dev_data->atmsar_vcc->mtu = UDSL_MAX_AAL5_MRU;
if (instance->data_started)
udsl_fire_receivers (instance);
PDEBUG ("udsl_atm_open successfull\n"); PDEBUG ("udsl_atm_open successfull\n");
return 0; return 0;
} }
...@@ -692,24 +742,7 @@ static int udsl_usb_data_init (struct udsl_instance_data *instance) ...@@ -692,24 +742,7 @@ static int udsl_usb_data_init (struct udsl_instance_data *instance)
skb_queue_head_init (&instance->sndqueue); skb_queue_head_init (&instance->sndqueue);
for (i = 0, succes = 0; i < UDSL_NUMBER_RCV_URBS; i++) { udsl_fire_receivers (instance);
struct udsl_data_ctx *ctx = &(instance->rcvbufs[i]);
usb_fill_bulk_urb (ctx->urb,
instance->usb_dev,
usb_rcvbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_IN),
(unsigned char *) ctx->skb->data,
UDSL_RECEIVE_BUFFER_SIZE,
udsl_usb_data_receive, ctx);
if (usb_submit_urb (ctx->urb, GFP_KERNEL) < 0)
PDEBUG ("udsl_usb_data_init: Submit failed, loosing urb.\n");
else
succes++;
}
PDEBUG ("udsl_usb_data_init %d urb%s queued for receive\n", succes,
(succes != 1) ? "s" : "");
for (i = 0, succes = 0; i < UDSL_NUMBER_SND_URBS; i++) { for (i = 0, succes = 0; i < UDSL_NUMBER_SND_URBS; i++) {
instance->send_ctx[i].urb = usb_alloc_urb (0, GFP_KERNEL); instance->send_ctx[i].urb = usb_alloc_urb (0, GFP_KERNEL);
...@@ -729,34 +762,9 @@ static int udsl_usb_data_init (struct udsl_instance_data *instance) ...@@ -729,34 +762,9 @@ static int udsl_usb_data_init (struct udsl_instance_data *instance)
static int udsl_usb_data_exit (struct udsl_instance_data *instance) static int udsl_usb_data_exit (struct udsl_instance_data *instance)
{ {
int i;
if (!instance->data_started) if (!instance->data_started)
return 0; return 0;
tasklet_disable (&instance->recvqueue_tasklet);
for (i = 0; i < UDSL_NUMBER_RCV_URBS; i++)
usb_unlink_urb (instance->rcvbufs[i].urb);
INIT_LIST_HEAD (&instance->completed_receivers);
tasklet_enable (&instance->recvqueue_tasklet);
tasklet_kill (&instance->recvqueue_tasklet);
for (i = 0; i < UDSL_NUMBER_SND_URBS; i++) {
struct udsl_usb_send_data_context *ctx = &(instance->send_ctx[i]);
usb_unlink_urb (ctx->urb);
if (ctx->skb)
ctx->vcc->pop (ctx->vcc, ctx->skb);
ctx->skb = NULL;
usb_free_urb (ctx->urb);
}
instance->data_started = 0; instance->data_started = 0;
instance->atm_dev->signal = ATM_PHY_SIG_LOST; instance->atm_dev->signal = ATM_PHY_SIG_LOST;
...@@ -819,8 +827,13 @@ static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_i ...@@ -819,8 +827,13 @@ static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_i
memset (instance, 0, sizeof (struct udsl_instance_data)); memset (instance, 0, sizeof (struct udsl_instance_data));
init_MUTEX (&instance->serialize);
instance->usb_dev = dev; instance->usb_dev = dev;
spin_lock_init (&instance->spare_receivers_lock);
INIT_LIST_HEAD (&instance->spare_receivers);
spin_lock_init (&instance->completed_receivers_lock); spin_lock_init (&instance->completed_receivers_lock);
INIT_LIST_HEAD (&instance->completed_receivers); INIT_LIST_HEAD (&instance->completed_receivers);
...@@ -844,6 +857,8 @@ static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_i ...@@ -844,6 +857,8 @@ static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_i
ctx->instance = instance; ctx->instance = instance;
list_add (&ctx->list, &instance->spare_receivers);
PDEBUG ("skb->truesize = %d (asked for %d)\n", ctx->skb->truesize, UDSL_RECEIVE_BUFFER_SIZE); PDEBUG ("skb->truesize = %d (asked for %d)\n", ctx->skb->truesize, UDSL_RECEIVE_BUFFER_SIZE);
} }
...@@ -908,9 +923,23 @@ static void udsl_usb_disconnect (struct usb_interface *intf) ...@@ -908,9 +923,23 @@ static void udsl_usb_disconnect (struct usb_interface *intf)
return; return;
} }
/* unlinking receive buffers */ tasklet_disable (&instance->recvqueue_tasklet);
udsl_usb_data_exit (instance);
down (&instance->serialize);
/* no need to take the spinlock - recvqueue_tasklet is not running */
INIT_LIST_HEAD (&instance->spare_receivers);
up (&instance->serialize);
for (i = 0; i < UDSL_NUMBER_RCV_URBS; i++)
usb_unlink_urb (instance->rcvbufs[i].urb);
/* no need to take the spinlock - no completion handlers running */
INIT_LIST_HEAD (&instance->completed_receivers);
tasklet_enable (&instance->recvqueue_tasklet);
tasklet_kill (&instance->recvqueue_tasklet);
PDEBUG ("udsl_usb_disconnect: freeing receivers\n");
for (i = 0; i < UDSL_NUMBER_RCV_URBS; i++) { for (i = 0; i < UDSL_NUMBER_RCV_URBS; i++) {
struct udsl_data_ctx *ctx = &(instance->rcvbufs[i]); struct udsl_data_ctx *ctx = &(instance->rcvbufs[i]);
...@@ -918,6 +947,20 @@ static void udsl_usb_disconnect (struct usb_interface *intf) ...@@ -918,6 +947,20 @@ static void udsl_usb_disconnect (struct usb_interface *intf)
kfree_skb (ctx->skb); kfree_skb (ctx->skb);
} }
for (i = 0; i < UDSL_NUMBER_SND_URBS; i++) {
struct udsl_usb_send_data_context *ctx = &(instance->send_ctx[i]);
usb_unlink_urb (ctx->urb);
if (ctx->skb)
ctx->vcc->pop (ctx->vcc, ctx->skb);
ctx->skb = NULL;
usb_free_urb (ctx->urb);
}
/* removing atm device */ /* removing atm device */
if (instance->atm_dev) if (instance->atm_dev)
udsl_atm_stopdevice (instance); udsl_atm_stopdevice (instance);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment