ChangeSet 1.1043.1.4, 2003/02/16 09:38:10-08:00, duncan.sands@math.u-psud.fr

[PATCH] USB speedtouch: infrastructure for new speedtouch send path

Put in infrastructure for the new send code.  The only code changes are in the
udsl_usb_probe and udsl_usb_disconnect functions, changed to initialize/finalize the
new fields (plus cleaned up a bit).  I couldn't resist a real code change while I was
there: freeing the memory used by the ATM after shutting it down, rather than before!
This doesn't make any difference since the shutdown routine doesn't work - so it still
oopses.  I will fix the shutdown routine later.


diff -Nru a/drivers/usb/misc/speedtouch.c b/drivers/usb/misc/speedtouch.c
--- a/drivers/usb/misc/speedtouch.c	Tue Feb 18 16:43:27 2003
+++ b/drivers/usb/misc/speedtouch.c	Tue Feb 18 16:43:27 2003
@@ -90,7 +90,9 @@
 
 #define UDSL_NUMBER_RCV_URBS		1
 #define UDSL_NUMBER_SND_URBS		1
+#define UDSL_NUMBER_SND_BUFS		(2*UDSL_NUMBER_SND_URBS)
 #define UDSL_RCV_BUFFER_SIZE		(1*64) /* ATM cells */
+#define UDSL_SND_BUFFER_SIZE		(2*64) /* ATM cells */
 /* max should be (1500 IP mtu + 2 ppp bytes + 32 * 5 cellheader overhead) for
  * PPPoA and (1500 + 14 + 32*5 cellheader overhead) for PPPoE */
 #define UDSL_MAX_AAL5_MRU		2048
@@ -123,10 +125,19 @@
 	struct udsl_instance_data *instance;
 };
 
+struct udsl_send_buffer {
+	struct list_head list;
+	unsigned char *base;
+	unsigned char *free_start;
+	unsigned int free_cells;
+};
+
 struct udsl_usb_send_data_context {
-	struct urb *urb;
+	struct list_head list;
+	struct udsl_send_buffer *buffer;
 	struct sk_buff *skb;
 	struct atm_vcc *vcc;
+	struct urb *urb;
 	struct udsl_instance_data *instance;
 };
 
@@ -139,13 +150,10 @@
 
 	/* usb device part */
 	struct usb_device *usb_dev;
-	struct sk_buff_head sndqueue;
-	struct udsl_usb_send_data_context send_ctx [UDSL_NUMBER_SND_URBS];
 	int firmware_loaded;
 
 	/* atm device part */
 	struct atm_dev *atm_dev;
-
 	struct atmsar_vcc_data *atmsar_vcc_list;
 
 	/* receiving */
@@ -158,6 +166,21 @@
 	struct list_head completed_receivers;
 
 	struct tasklet_struct receive_tasklet;
+
+	/* sending */
+	struct udsl_usb_send_data_context send_ctx [UDSL_NUMBER_SND_URBS];
+	struct udsl_send_buffer all_buffers [UDSL_NUMBER_SND_BUFS];
+
+	struct sk_buff_head sndqueue;
+
+	spinlock_t send_lock;
+	struct list_head spare_senders;
+	struct list_head spare_buffers;
+
+	struct tasklet_struct send_tasklet;
+	struct sk_buff *current_skb;
+	struct udsl_send_buffer *current_buffer;
+	struct list_head filled_buffers;
 };
 
 static const char udsl_driver_name [] = "Alcatel SpeedTouch USB";
@@ -371,6 +394,15 @@
 }
 
 
+/***********
+**  send  **
+***********/
+
+static void udsl_process_send (unsigned long data)
+{
+}
+
+
 /************
 **   ATM   **
 ************/
@@ -770,7 +802,7 @@
 	struct udsl_instance_data *instance;
 	unsigned char mac_str [13];
 	unsigned char mac [6];
-	int i, err;
+	int i;
 
 	PDEBUG ("Trying device with Vendor=0x%x, Product=0x%x, ifnum %d\n",
 		dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
@@ -785,8 +817,7 @@
 	/* instance init */
 	if (!(instance = kmalloc (sizeof (struct udsl_instance_data), GFP_KERNEL))) {
 		PDEBUG ("No memory for Instance data!\n");
-		err = -ENOMEM;
-		goto fail_instance;
+		return -ENOMEM;
 	}
 
 	memset (instance, 0, sizeof (struct udsl_instance_data));
@@ -805,20 +836,25 @@
 
 	skb_queue_head_init (&instance->sndqueue);
 
-	/* receive urb init */
+	spin_lock_init (&instance->send_lock);
+	INIT_LIST_HEAD (&instance->spare_senders);
+	INIT_LIST_HEAD (&instance->spare_buffers);
+
+	tasklet_init (&instance->send_tasklet, udsl_process_send, (unsigned long) instance);
+	INIT_LIST_HEAD (&instance->filled_buffers);
+
+	/* receive init */
 	for (i = 0; i < UDSL_NUMBER_RCV_URBS; i++) {
 		struct udsl_receiver *rcv = &(instance->all_receivers[i]);
 
 		if (!(rcv->skb = dev_alloc_skb (UDSL_RCV_BUFFER_SIZE * ATM_CELL_SIZE))) {
 			PDEBUG ("No memory for skb %d!\n", i);
-			err = -ENOMEM;
-			goto fail_urbs;
+			goto fail;
 		}
 
 		if (!(rcv->urb = usb_alloc_urb (0, GFP_KERNEL))) {
 			PDEBUG ("No memory for receive urb %d!\n", i);
-			err = -ENOMEM;
-			goto fail_urbs;
+			goto fail;
 		}
 
 		rcv->instance = instance;
@@ -828,23 +864,35 @@
 		PDEBUG ("skb->truesize = %d (asked for %d)\n", rcv->skb->truesize, UDSL_RCV_BUFFER_SIZE * ATM_CELL_SIZE);
 	}
 
+	/* send init */
 	for (i = 0; i < UDSL_NUMBER_SND_URBS; i++) {
 		struct udsl_usb_send_data_context *snd = &(instance->send_ctx[i]);
 
 		if (!(snd->urb = usb_alloc_urb (0, GFP_KERNEL))) {
 			PDEBUG ("No memory for send urb %d!\n", i);
-			err = -ENOMEM;
-			goto fail_urbs;
+			goto fail;
 		}
 
 		snd->instance = instance;
+
+		list_add (&snd->list, &instance->spare_senders);
+	}
+
+	for (i = 0; i < UDSL_NUMBER_SND_BUFS; i++) {
+		struct udsl_send_buffer *buf = &(instance->all_buffers[i]);
+
+		if (!(buf->base = kmalloc (UDSL_SND_BUFFER_SIZE * ATM_CELL_SIZE, GFP_KERNEL))) {
+			PDEBUG ("No memory for send buffer %d!\n", i);
+			goto fail;
+		}
+
+		list_add (&buf->list, &instance->spare_buffers);
 	}
 
 	/* atm init */
 	if (!(instance->atm_dev = atm_dev_register (udsl_driver_name, &udsl_atm_devops, -1, 0))) {
 		PDEBUG ("failed to register ATM device!\n");
-		err = -ENOMEM;
-		goto fail_atm;
+		goto fail;
 	}
 
 	instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
@@ -871,14 +919,12 @@
 
 	return 0;
 
-fail_atm:
-fail_urbs:
-	for (i = 0; i < UDSL_NUMBER_SND_URBS; i++) {
-		struct udsl_usb_send_data_context *snd = &(instance->send_ctx[i]);
+fail:
+	for (i = 0; i < UDSL_NUMBER_SND_BUFS; i++)
+		kfree (instance->all_buffers[i].base);
 
-		if (snd->urb)
-			usb_free_urb (snd->urb);
-	}
+	for (i = 0; i < UDSL_NUMBER_SND_URBS; i++)
+		usb_free_urb (instance->send_ctx[i].urb);
 
 	for (i = 0; i < UDSL_NUMBER_RCV_URBS; i++) {
 		struct udsl_receiver *rcv = &(instance->all_receivers[i]);
@@ -886,12 +932,12 @@
 		if (rcv->skb)
 			kfree_skb (rcv->skb);
 
-		if (rcv->urb)
-			usb_free_urb (rcv->urb);
+		usb_free_urb (rcv->urb);
 	}
+
 	kfree (instance);
-fail_instance:
-	return err;
+
+	return -ENOMEM;
 }
 
 static void udsl_usb_disconnect (struct usb_interface *intf)
@@ -900,7 +946,7 @@
 	struct list_head *pos;
 	unsigned long flags;
 	unsigned int count = 0;
-	int i;
+	int result, i;
 
 	PDEBUG ("disconnecting\n");
 
@@ -913,8 +959,9 @@
 
 	tasklet_disable (&instance->receive_tasklet);
 
+	/* flush spare receivers */
 	down (&instance->serialize); /* vs udsl_fire_receivers */
-	/* no need to take the spinlock - receive_tasklet is not running */
+	/* no need to take the spinlock */
 	list_for_each (pos, &instance->spare_receivers)
 		if (++count > UDSL_NUMBER_RCV_URBS)
 			panic (__FILE__ ": memory corruption detected at line %d!\n", __LINE__);
@@ -926,7 +973,8 @@
 	count = UDSL_NUMBER_RCV_URBS - count;
 
 	for (i = 0; i < UDSL_NUMBER_RCV_URBS; i++)
-		usb_unlink_urb (instance->all_receivers[i].urb);
+		if ((result = usb_unlink_urb (instance->all_receivers[i].urb)) < 0)
+			PDEBUG ("udsl_usb_disconnect: usb_unlink_urb on receive urb %d returned %d\n", i, result);
 
 	/* wait for completion handlers to finish */
 	do {
@@ -943,12 +991,11 @@
 		if (completed == count)
 			break;
 
-		/* not all urbs completed */
 		yield ();
 	} while (1);
 
-	PDEBUG ("udsl_usb_disconnect: flushing %u completed receivers\n", count);
-	/* no need to take the spinlock - no completion handlers running */
+	PDEBUG ("udsl_usb_disconnect: flushing\n");
+	/* no need to take the spinlock */
 	INIT_LIST_HEAD (&instance->completed_receivers);
 
 	tasklet_enable (&instance->receive_tasklet);
@@ -962,24 +1009,49 @@
 		kfree_skb (rcv->skb);
 	}
 
-	for (i = 0; i < UDSL_NUMBER_SND_URBS; i++) {
-		struct udsl_usb_send_data_context *ctx = &(instance->send_ctx[i]);
+	udsl_atm_stopdevice (instance);
 
-		usb_unlink_urb (ctx->urb);
+	tasklet_disable (&instance->send_tasklet);
 
-		if (ctx->skb)
-			ctx->vcc->pop (ctx->vcc, ctx->skb);
-		ctx->skb = NULL;
+	for (i = 0; i < UDSL_NUMBER_SND_URBS; i++)
+		if ((result = usb_unlink_urb (instance->send_ctx[i].urb)) < 0)
+			PDEBUG ("udsl_usb_disconnect: usb_unlink_urb on send urb %d returned %d\n", i, result);
 
-		usb_free_urb (ctx->urb);
+	/* wait for completion handlers to finish */
+	do {
+		count = 0;
+		spin_lock_irqsave (&instance->send_lock, flags);
+		list_for_each (pos, &instance->spare_senders)
+			if (++count > UDSL_NUMBER_SND_URBS)
+				panic (__FILE__ ": memory corruption detected at line %d!\n", __LINE__);
+		spin_unlock_irqrestore (&instance->send_lock, flags);
 
-	}
+		PDEBUG ("udsl_usb_disconnect: found %u spare senders\n", count);
 
+		if (count == UDSL_NUMBER_SND_URBS)
+			break;
+
+		yield ();
+	} while (1);
+
+	PDEBUG ("udsl_usb_disconnect: flushing\n");
+	/* no need to take the spinlock */
+	INIT_LIST_HEAD (&instance->spare_senders);
+	INIT_LIST_HEAD (&instance->spare_buffers);
+	instance->current_buffer = NULL;
+
+	tasklet_enable (&instance->receive_tasklet);
+	tasklet_kill (&instance->receive_tasklet);
+
+	PDEBUG ("udsl_usb_disconnect: freeing senders\n");
+	for (i = 0; i < UDSL_NUMBER_SND_URBS; i++)
+		usb_free_urb (instance->send_ctx[i].urb);
 
-	/* removing atm device */
-	if (instance->atm_dev)
-		udsl_atm_stopdevice (instance);
+	PDEBUG ("udsl_usb_disconnect: freeing buffers\n");
+	for (i = 0; i < UDSL_NUMBER_SND_BUFS; i++)
+		kfree (instance->all_buffers[i].base);
 
+	PDEBUG ("udsl_usb_disconnect: freeing instance\n");
 	kfree (instance);
 }
 
