[RFC] [PATCH] usbatm.[ch]: logic changes + error handling

Roman Kagan rkagan at mail.ru
Fri Apr 15 06:18:11 EDT 2005


OK so here goes another update of my patch to the current CVS, in case
it gets somebody interested.

A few numbers to be proud of :) :

   text	   data	    bss	    dec	    hex	filename
  10275	    500	     48	  10823	   2a47	usbatm.orig/usbatm.ko
   8905	    468	      0	   9373	   249d	usbatm/usbatm.ko

 usbatm.h |   81 +-----
 usbatm.c |  753 +++++++++++++++++++++++++--------------------------------------
 2 files changed, 324 insertions(+), 510 deletions(-)

and that's without sacrificing functionality (rather the opposite: added
extra error handling and support for iso transfers).

Cheers,
  Roman.

Index: usbatm.h
===================================================================
RCS file: /home/cvs/usbatm/usbatm.h,v
retrieving revision 1.13
diff -u -p -d -w -r1.13 usbatm.h
--- usbatm.h	14 Apr 2005 10:20:26 -0000	1.13
+++ usbatm.h	15 Apr 2005 09:55:23 -0000
@@ -124,55 +124,24 @@ extern int usbatm_usb_probe(struct usb_i
 extern void usbatm_usb_disconnect(struct usb_interface *intf);
 
 
-/* usbatm */
-
-#define UDSL_MAX_RCV_URBS		4
-#define UDSL_MAX_SND_URBS		4
-#define UDSL_MAX_RCV_BUFS		8
-#define UDSL_MAX_SND_BUFS		8
-#define UDSL_MAX_RCV_BUF_SIZE		1024	/* ATM cells */
-#define UDSL_MAX_SND_BUF_SIZE		1024	/* ATM cells */
-#define UDSL_DEFAULT_RCV_URBS		2
-#define UDSL_DEFAULT_SND_URBS		2
-#define UDSL_DEFAULT_RCV_BUFS		4
-#define UDSL_DEFAULT_SND_BUFS		4
-#define UDSL_DEFAULT_RCV_BUF_SIZE	64	/* ATM cells */
-#define UDSL_DEFAULT_SND_BUF_SIZE	64	/* ATM cells */
-
-
-/* receive */
-
-struct usbatm_receive_buffer {
+struct usbatm_channel {
+	int endpoint;			/* usb pipe */
+	unsigned int stride;		/* ATM cell size + padding */
+	unsigned int buf_size;		/* urb buffer size */
+	spinlock_t lock;
 	struct list_head list;
-	unsigned char *base;
-	unsigned int filled_cells;
+	struct tasklet_struct tasklet;
+	struct timer_list delay;
+	struct work_struct clear_halt_work;
+	struct usbatm_data *usbatm;
 };
 
-struct usbatm_receiver {
-	struct list_head list;
-	struct usbatm_receive_buffer *buffer;
+struct usbatm_transceiver {
 	struct urb *urb;
-	struct usbatm_data *instance;
-};
-
-
-/* send */
-
-struct usbatm_send_buffer {
 	struct list_head list;
-	unsigned char *base;
-	unsigned char *free_start;
-	unsigned int free_cells;
-};
-
-struct usbatm_sender {
-	struct list_head list;
-	struct usbatm_send_buffer *buffer;
-	struct urb *urb;
-	struct usbatm_data *instance;
+	struct usbatm_channel *channel;
 };
 
-
 /* main driver data */
 
 struct usbatm_data {
@@ -189,10 +158,6 @@ struct usbatm_data {
 	struct usb_device *usb_dev;
 	struct usb_interface *usb_intf;
 	char description[64];
-	int tx_endpoint;
-	int rx_endpoint;
-	int tx_padding;
-	int rx_padding;
 
 	/* ATM device */
 	struct atm_dev *atm_dev;
@@ -212,31 +177,13 @@ struct usbatm_data {
 	/* ATM device */
 	struct list_head vcc_list;
 
-	/* receive */
-	struct usbatm_receiver receivers[UDSL_MAX_RCV_URBS];
-	struct usbatm_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS];
-
-	spinlock_t receive_lock;
-	struct list_head spare_receivers;
-	struct list_head filled_receive_buffers;
-
-	struct tasklet_struct receive_tasklet;
-	struct list_head spare_receive_buffers;
+	struct usbatm_transceiver *transceivers;
 
-	/* send */
-	struct usbatm_sender senders[UDSL_MAX_SND_URBS];
-	struct usbatm_send_buffer send_buffers[UDSL_MAX_SND_BUFS];
+	struct usbatm_channel rx_channel;
+	struct usbatm_channel tx_channel;
 
 	struct sk_buff_head sndqueue;
-
-	spinlock_t send_lock;
-	struct list_head spare_senders;
-	struct list_head spare_send_buffers;
-
-	struct tasklet_struct send_tasklet;
 	struct sk_buff *current_skb;			/* being emptied */
-	struct usbatm_send_buffer *current_buffer;	/* being filled */
-	struct list_head filled_send_buffers;
 };
 
 #endif	/* _USBATM_H_ */
Index: usbatm.c
===================================================================
RCS file: /home/cvs/usbatm/usbatm.c,v
retrieving revision 1.19
diff -u -p -d -w -r1.19 usbatm.c
--- usbatm.c	15 Apr 2005 08:49:51 -0000	1.19
+++ usbatm.c	15 Apr 2005 09:55:23 -0000
@@ -96,22 +96,18 @@ static int usbatm_print_packet(const uns
 
 static const char usbatm_driver_name[] = "usbatm";
 
-#define UDSL_MAX_RCV_URBS		4
-#define UDSL_MAX_SND_URBS		4
-#define UDSL_MAX_RCV_BUFS		8
-#define UDSL_MAX_SND_BUFS		8
+#define UDSL_MAX_RCV_URBS		16
+#define UDSL_MAX_SND_URBS		16
 #define UDSL_MAX_RCV_BUF_SIZE		1024	/* ATM cells */
 #define UDSL_MAX_SND_BUF_SIZE		1024	/* ATM cells */
-#define UDSL_DEFAULT_RCV_URBS		2
-#define UDSL_DEFAULT_SND_URBS		2
-#define UDSL_DEFAULT_RCV_BUFS		4
-#define UDSL_DEFAULT_SND_BUFS		4
+#define UDSL_DEFAULT_RCV_URBS		4
+#define UDSL_DEFAULT_SND_URBS		4
 #define UDSL_DEFAULT_RCV_BUF_SIZE	64	/* ATM cells */
 #define UDSL_DEFAULT_SND_BUF_SIZE	64	/* ATM cells */
 
 #define ATM_CELL_HEADER			(ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
-#define UDSL_NUM_CELLS(x)		(((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD)
 
+#define THROTTLE_MSECS			5	/* delay to recover processing after urb submission fails */
 
 /* receive */
 
@@ -130,11 +126,9 @@ struct usbatm_vcc_data {
 /* send */
 
 struct usbatm_control {
-	struct atm_skb_data atm_data;
-	unsigned int num_cells;
-	unsigned int num_entire;
-	unsigned int pdu_padding;
-	unsigned char aal5_trailer[ATM_AAL5_TRAILER];
+	struct atm_vcc *vcc;
+	u32 len;
+	u32 crc;
 };
 
 #define UDSL_SKB(x)		((struct usbatm_control *)(x)->cb)
@@ -142,8 +136,6 @@ struct usbatm_control {
 
 static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
 static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
-static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS;
-static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS;
 static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE;
 static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE;
 
@@ -159,27 +151,15 @@ MODULE_PARM_DESC(num_snd_urbs,
 		 __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: "
 		 __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")");
 
-module_param(num_rcv_bufs, uint, S_IRUGO);
-MODULE_PARM_DESC(num_rcv_bufs,
-		 "Number of buffers used for reception (range: 0-"
-		 __MODULE_STRING(UDSL_MAX_RCV_BUFS) ", default: "
-		 __MODULE_STRING(UDSL_DEFAULT_RCV_BUFS) ")");
-
-module_param(num_snd_bufs, uint, S_IRUGO);
-MODULE_PARM_DESC(num_snd_bufs,
-		 "Number of buffers used for transmission (range: 0-"
-		 __MODULE_STRING(UDSL_MAX_SND_BUFS) ", default: "
-		 __MODULE_STRING(UDSL_DEFAULT_SND_BUFS) ")");
-
 module_param(rcv_buf_size, uint, S_IRUGO);
 MODULE_PARM_DESC(rcv_buf_size,
-		 "Size of the buffers used for reception (range: 0-"
+		 "Size of the buffers used for reception in ATM cells (range: 0-"
 		 __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: "
 		 __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")");
 
 module_param(snd_buf_size, uint, S_IRUGO);
 MODULE_PARM_DESC(snd_buf_size,
-		 "Size of the buffers used for transmission (range: 0-"
+		 "Size of the buffers used for transmission in ATM cells (range: 0-"
 		 __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: "
 		 __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")");
 
@@ -206,6 +186,11 @@ static struct atmdev_ops usbatm_atm_devo
 /***********
 **  misc  **
 ***********/
+static inline unsigned int usbatm_pdu_length(unsigned int length)
+{
+	length += ATM_CELL_PAYLOAD - 1 + ATM_AAL5_TRAILER;
+	return length - length % ATM_CELL_PAYLOAD;
+}
 
 static inline void usbatm_pop(struct atm_vcc *vcc, struct sk_buff *skb)
 {
@@ -215,6 +200,73 @@ static inline void usbatm_pop(struct atm
 		dev_kfree_skb(skb);
 }
 
+/* buffer management */
+static inline struct usbatm_transceiver *usbatm_pop_transceiver(struct usbatm_channel *channel)
+{
+	struct usbatm_transceiver *trx;
+
+	spin_lock_irq(&channel->lock);
+	if (list_empty(&channel->list)) {
+		spin_unlock_irq(&channel->lock);
+		return NULL;
+	}
+	
+	trx = list_entry(channel->list.next, struct usbatm_transceiver, list);
+	list_del(&trx->list);
+	spin_unlock_irq(&channel->lock);
+
+	return trx;
+}
+
+static inline int usbatm_submit(struct usbatm_transceiver *trx)
+{
+	int ret;
+	struct usbatm_channel *channel = trx->channel;
+
+	vdbg("%s: submitting urb 0x%p, trx 0x%p, size %u",
+	     __func__, trx->urb, trx, trx->urb->transfer_buffer_length);
+
+	ret = usb_submit_urb(trx->urb, GFP_ATOMIC);
+	if (ret) {
+		usb_dbg(channel->usbatm, "%s: trx 0x%p urb 0x%p submission failed (%d)!\n",
+			__func__, trx, trx->urb, ret);
+
+		/* consider all errors transient and return the buffer back to the queue */
+		spin_lock_irq(&channel->lock);
+		list_add(&trx->list, &channel->list);
+		spin_unlock_irq(&channel->lock);
+
+		/* make sure the channel doesn't stall */
+		mod_timer(&channel->delay, jiffies + msecs_to_jiffies(THROTTLE_MSECS));
+	}
+
+	return ret;
+}
+
+static void usbatm_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct usbatm_transceiver *trx = urb->context;
+	struct usbatm_channel *channel = trx->channel;
+	unsigned long flags;
+
+	vdbg("%s: urb 0x%p, status %d, actual_length %d, trx 0x%p",
+	     __func__, urb, urb->status, urb->actual_length, trx);
+
+	/* may be in_interrupt() */
+	spin_lock_irqsave(&channel->lock, flags);
+	list_add_tail(&trx->list, &channel->list);
+	spin_unlock_irqrestore(&channel->lock, flags);
+
+	if (unlikely(urb->status)) {
+		/* throttle processing in case of an error */
+		mod_timer(&channel->delay, jiffies + msecs_to_jiffies(THROTTLE_MSECS));
+		if (urb->status == -EPIPE)		/* this one may be cleaned up at times */
+			schedule_work(&channel->clear_halt_work);
+	}
+	else
+		tasklet_schedule(&channel->tasklet);
+}
+
 /*************
 **  decode  **
 *************/
@@ -231,24 +283,21 @@ static inline struct usbatm_vcc_data *us
 }
 
 static void usbatm_extract_cells(struct usbatm_data *instance,
-			       unsigned char *source, unsigned int howmany)
+				 unsigned char *source, unsigned int avail_data)
 {
 	struct usbatm_vcc_data *cached_vcc = NULL;
 	struct atm_vcc *vcc;
 	struct sk_buff *sarb;
 	struct usbatm_vcc_data *vcc_data;
-	int cached_vci = 0;
-	unsigned int i;
-	int pti;
-	int vci;
-	short cached_vpi = 0;
-	short vpi;
+	u8 pti;
+	u32 vci, cached_vci = 0;
+	u16 vpi, cached_vpi = 0;
 
-	for (i = 0; i < howmany;
-	     i++, source += ATM_CELL_SIZE + instance->rx_padding) {
+	for (; avail_data >= instance->rx_channel.stride;
+	     avail_data -= instance->rx_channel.stride, source += instance->rx_channel.stride) {
 		vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4);
 		vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
-		pti = (source[3] & 0x2) != 0;
+		pti = ((source[3] & 0xe) >> 1);
 
 		vdbg("%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti);
 
@@ -264,6 +313,7 @@ static void usbatm_extract_cells(struct 
 		}
 
 		vcc = vcc_data->vcc;
+
 		sarb = vcc_data->sarb;
 
 		if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
@@ -276,7 +326,7 @@ static void usbatm_extract_cells(struct 
 		memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
 		__skb_put(sarb, ATM_CELL_PAYLOAD);
 
-		if (pti) {
+		if (pti & 1) {
 			struct sk_buff *skb;
 			unsigned int length;
 			unsigned int pdu_length;
@@ -291,7 +341,7 @@ static void usbatm_extract_cells(struct 
 				goto out;
 			}
 
-			pdu_length = UDSL_NUM_CELLS(length) * ATM_CELL_PAYLOAD;
+			pdu_length = usbatm_pdu_length(length);
 
 			if (sarb->len < pdu_length) {
 				atm_dbg(instance, "%s: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!\n",
@@ -344,343 +394,153 @@ static void usbatm_extract_cells(struct 
 **  encode  **
 *************/
 
-static inline void usbatm_fill_cell_header(unsigned char *target, struct atm_vcc *vcc)
-{
-	target[0] = vcc->vpi >> 4;
-	target[1] = (vcc->vpi << 4) | (vcc->vci >> 12);
-	target[2] = vcc->vci >> 4;
-	target[3] = vcc->vci << 4;
-	target[4] = 0xec;
-}
-
-static const unsigned char zeros[ATM_CELL_PAYLOAD];
-
-static void usbatm_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb)
+static unsigned int usbatm_write_cells(struct usbatm_data *instance,
+				       struct sk_buff *skb,
+				       u8 *target, unsigned int avail_space)
 {
 	struct usbatm_control *ctrl = UDSL_SKB(skb);
-	unsigned int zero_padding;
-	u32 crc;
-
-	ctrl->atm_data.vcc = vcc;
-
-	ctrl->num_cells = UDSL_NUM_CELLS(skb->len);
-	ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD;
-
-	zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER;
-
-	if (ctrl->num_entire + 1 < ctrl->num_cells)
-		ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
-	else
-		ctrl->pdu_padding = zero_padding;
-
-	ctrl->aal5_trailer[0] = 0;	/* UU = 0 */
-	ctrl->aal5_trailer[1] = 0;	/* CPI = 0 */
-	ctrl->aal5_trailer[2] = skb->len >> 8;
-	ctrl->aal5_trailer[3] = skb->len;
+	unsigned int num_written;
 
-	crc = crc32_be(~0, skb->data, skb->len);
-	crc = crc32_be(crc, zeros, zero_padding);
-	crc = crc32_be(crc, ctrl->aal5_trailer, 4);
-	crc = ~crc;
+	/* UDSL_ASSERT(!(avail_space % instance->tx_channel.stride)); */
+	BUG_ON(avail_space % instance->tx_channel.stride);
+	vdbg("%s: skb->len=%d, avail_space=%u",
+	     __func__, skb->len, avail_space);
 
-	ctrl->aal5_trailer[4] = crc >> 24;
-	ctrl->aal5_trailer[5] = crc >> 16;
-	ctrl->aal5_trailer[6] = crc >> 8;
-	ctrl->aal5_trailer[7] = crc;
-}
+	for (num_written = 0; num_written < avail_space && ctrl->len;
+	     num_written += instance->tx_channel.stride, target += instance->tx_channel.stride) {
+		unsigned int data_len = min_t(unsigned int, skb->len, ATM_CELL_PAYLOAD);
+		unsigned int left = ATM_CELL_PAYLOAD - data_len;
+		u8 *ptr = target;
 
-static unsigned int usbatm_write_cells(struct usbatm_data *instance,
-				     unsigned int howmany, struct sk_buff *skb,
-				     unsigned char **target_p)
-{
-	struct usbatm_control *ctrl = UDSL_SKB(skb);
-	unsigned char *target = *target_p;
-	unsigned int nc, ne, i;
+		ptr[0] = ctrl->vcc->vpi >> 4;
+		ptr[1] = (ctrl->vcc->vpi << 4) | (ctrl->vcc->vci >> 12);
+		ptr[2] = ctrl->vcc->vci >> 4;
+		ptr[3] = ctrl->vcc->vci << 4;
+		ptr[4] = 0xec;
+		ptr += ATM_CELL_HEADER;
 
-	vdbg("%s: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u",
-			__func__, howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding);
+		memcpy(ptr, skb->data, data_len);
+		ptr += data_len;
+		skb_pull(skb, data_len);
 
-	nc = ctrl->num_cells;
-	ne = min(howmany, ctrl->num_entire);
+		if(!left)
+			continue;
 
-	for (i = 0; i < ne; i++) {
-		usbatm_fill_cell_header(target, ctrl->atm_data.vcc);
-		target += ATM_CELL_HEADER;
-		memcpy(target, skb->data, ATM_CELL_PAYLOAD);
-		target += ATM_CELL_PAYLOAD;
-		if (instance->tx_padding) {
-			memset(target, 0, instance->tx_padding);
-			target += instance->tx_padding;
-		}
-		__skb_pull(skb, ATM_CELL_PAYLOAD);
-	}
+		memset(ptr, 0, left);
 
-	ctrl->num_entire -= ne;
+		if (left >= ATM_AAL5_TRAILER) {	/* trailer will go in this cell */
+			u8 *trailer = target + ATM_CELL_SIZE - ATM_AAL5_TRAILER;
+			/* trailer[0] = 0;		UU = 0 */
+			/* trailer[1] = 0;		CPI = 0 */
+			trailer[2] = ctrl->len >> 8;
+			trailer[3] = ctrl->len;
 
-	if (!(ctrl->num_cells -= ne) || !(howmany -= ne))
-		goto out;
+			ctrl->crc = ~ crc32_be(ctrl->crc, ptr, left - 4);
 
-	usbatm_fill_cell_header(target, ctrl->atm_data.vcc);
-	target += ATM_CELL_HEADER;
-	memcpy(target, skb->data, skb->len);
-	target += skb->len;
-	__skb_pull(skb, skb->len);
-	memset(target, 0, ctrl->pdu_padding);
-	target += ctrl->pdu_padding;
+			trailer[4] = ctrl->crc >> 24;
+			trailer[5] = ctrl->crc >> 16;
+			trailer[6] = ctrl->crc >> 8;
+			trailer[7] = ctrl->crc;
 
-	if (--ctrl->num_cells) {
-		if (!--howmany) {
-			ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
-			goto out;
-		}
+			target[3] |= 0x2;	/* adjust PTI */
 
-		if (instance->tx_padding) {
-			memset(target, 0, instance->tx_padding);
-			target += instance->tx_padding;
+			ctrl->len = 0;		/* tag this skb finished */
 		}
-		usbatm_fill_cell_header(target, ctrl->atm_data.vcc);
-		target += ATM_CELL_HEADER;
-		memset(target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
-		target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
-
-		--ctrl->num_cells;
-		UDSL_ASSERT(!ctrl->num_cells);
+		else
+			ctrl->crc = crc32_be(ctrl->crc, ptr, left);
 	}
 
-	memcpy(target, ctrl->aal5_trailer, ATM_AAL5_TRAILER);
-	target += ATM_AAL5_TRAILER;
-	/* set pti bit in last cell */
-	*(target + 3 - ATM_CELL_SIZE) |= 0x2;
-	if (instance->tx_padding) {
-		memset(target, 0, instance->tx_padding);
-		target += instance->tx_padding;
-	}
- out:
-	*target_p = target;
-	return nc - ctrl->num_cells;
+	return num_written;
 }
 
 /**************
 **  receive  **
 **************/
 
-static void usbatm_complete_receive(struct urb *urb, struct pt_regs *regs)
-{
-	struct usbatm_receive_buffer *buf;
-	struct usbatm_data *instance;
-	struct usbatm_receiver *rcv;
-	unsigned long flags;
-
-	if (!urb || !(rcv = urb->context)) {
-		dbg("%s: bad urb!", __func__);
-		return;
-	}
-
-	instance = rcv->instance;
-	buf = rcv->buffer;
-
-	buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rx_padding);
-
-	vdbg("%s: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p",
-			__func__, urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf);
-
-	UDSL_ASSERT(buf->filled_cells <= rcv_buf_size);
-
-	/* may not be in_interrupt() */
-	spin_lock_irqsave(&instance->receive_lock, flags);
-	list_add(&rcv->list, &instance->spare_receivers);
-	list_add_tail(&buf->list, &instance->filled_receive_buffers);
-	if (likely(!urb->status))
-		tasklet_schedule(&instance->receive_tasklet);
-	spin_unlock_irqrestore(&instance->receive_lock, flags);
-}
-
-static void usbatm_process_receive(unsigned long data)
+static void usbatm_rx_process(unsigned long data)
 {
-	struct usbatm_receive_buffer *buf;
 	struct usbatm_data *instance = (struct usbatm_data *)data;
-	struct usbatm_receiver *rcv;
-	int err;
-
- made_progress:
-	while (!list_empty(&instance->spare_receive_buffers)) {
-		spin_lock_irq(&instance->receive_lock);
-		if (list_empty(&instance->spare_receivers)) {
-			spin_unlock_irq(&instance->receive_lock);
-			break;
-		}
-		rcv = list_entry(instance->spare_receivers.next,
-				 struct usbatm_receiver, list);
-		list_del(&rcv->list);
-		spin_unlock_irq(&instance->receive_lock);
-
-		buf = list_entry(instance->spare_receive_buffers.next,
-				 struct usbatm_receive_buffer, list);
-		list_del(&buf->list);
-
-		rcv->buffer = buf;
+	struct usbatm_transceiver *rx;
 
-		usb_fill_bulk_urb(rcv->urb, instance->usb_dev,
-				  instance->rx_endpoint,
-				  buf->base,
-				  rcv_buf_size * (ATM_CELL_SIZE + instance->rx_padding),
-				  usbatm_complete_receive, rcv);
+	while ((rx = usbatm_pop_transceiver(&instance->rx_channel))) {
+		struct urb *urb = rx->urb;
 
-		vdbg("%s: sending urb 0x%p, rcv 0x%p, buf 0x%p",
-				__func__, rcv->urb, rcv, buf);
+		vdbg("%s: processing rx 0x%p", __func__, rx);
 
-		if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) {
-			atm_dbg(instance, "%s: urb submission failed (%d)!\n", __func__, err);
-			list_add(&buf->list, &instance->spare_receive_buffers);
-			spin_lock_irq(&instance->receive_lock);
-			list_add(&rcv->list, &instance->spare_receivers);
-			spin_unlock_irq(&instance->receive_lock);
-			break;
-		}
+		if (usb_pipeisoc(urb->pipe)) {
+			int i;
+			for (i = 0; i < urb->number_of_packets; i++)
+				if (!urb->iso_frame_desc[i].status)
+					usbatm_extract_cells(instance,
+							     (u8 *)urb->transfer_buffer + urb->iso_frame_desc[i].offset,
+							     urb->iso_frame_desc[i].actual_length);
 	}
+		else
+			if (!urb->status)
+				usbatm_extract_cells(instance, urb->transfer_buffer,
+						     urb->actual_length);
 
-	spin_lock_irq(&instance->receive_lock);
-	if (list_empty(&instance->filled_receive_buffers)) {
-		spin_unlock_irq(&instance->receive_lock);
-		return;		/* done - no more buffers */
+		if (usbatm_submit(rx))
+			return;
 	}
-	buf = list_entry(instance->filled_receive_buffers.next,
-			 struct usbatm_receive_buffer, list);
-	list_del(&buf->list);
-	spin_unlock_irq(&instance->receive_lock);
-
-	vdbg("%s: processing buf 0x%p", __func__, buf);
-	usbatm_extract_cells(instance, buf->base, buf->filled_cells);
-	list_add(&buf->list, &instance->spare_receive_buffers);
-	goto made_progress;
 }
 
 /***********
 **  send  **
 ***********/
 
-static void usbatm_complete_send(struct urb *urb, struct pt_regs *regs)
-{
-	struct usbatm_data *instance;
-	struct usbatm_sender *snd;
-	unsigned long flags;
-
-	if (!urb || !(snd = urb->context) || !(instance = snd->instance)) {
-		dbg("%s: bad urb!", __func__);
-		return;
-	}
-
-	vdbg("%s: urb 0x%p, status %d, snd 0x%p, buf 0x%p",
-			__func__, urb, urb->status, snd, snd->buffer);
-
-	/* may not be in_interrupt() */
-	spin_lock_irqsave(&instance->send_lock, flags);
-	list_add(&snd->list, &instance->spare_senders);
-	list_add(&snd->buffer->list, &instance->spare_send_buffers);
-	tasklet_schedule(&instance->send_tasklet);
-	spin_unlock_irqrestore(&instance->send_lock, flags);
-}
-
-static void usbatm_process_send(unsigned long data)
+static void usbatm_tx_process(unsigned long data)
 {
-	struct usbatm_send_buffer *buf;
 	struct usbatm_data *instance = (struct usbatm_data *)data;
-	struct sk_buff *skb;
-	struct usbatm_sender *snd;
-	int err;
-	unsigned int num_written;
-
- made_progress:
-	spin_lock_irq(&instance->send_lock);
-	while (!list_empty(&instance->spare_senders)) {
-		if (!list_empty(&instance->filled_send_buffers)) {
-			buf = list_entry(instance->filled_send_buffers.next,
-					 struct usbatm_send_buffer, list);
-			list_del(&buf->list);
-		} else if ((buf = instance->current_buffer)) {
-			instance->current_buffer = NULL;
-		} else		/* all buffers empty */
-			break;
-
-		snd = list_entry(instance->spare_senders.next,
-				 struct usbatm_sender, list);
-		list_del(&snd->list);
-		spin_unlock_irq(&instance->send_lock);
-
-		snd->buffer = buf;
-		usb_fill_bulk_urb(snd->urb, instance->usb_dev,
-				  instance->tx_endpoint,
-				  buf->base,
-				  (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->tx_padding),
-				  usbatm_complete_send, snd);
+	struct sk_buff *skb = instance->current_skb;
+	struct usbatm_transceiver *tx = NULL;
+	unsigned int num_written = 0;
+	const unsigned int buf_size = instance->tx_channel.buf_size;
+	u8 *buffer = NULL;
 
-		vdbg("%s: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p",
-				__func__, snd->urb, snd_buf_size - buf->free_cells, snd, buf);
+	if (!skb)
+		skb = skb_dequeue(&instance->sndqueue);
 
-		if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) {
-			atm_dbg(instance, "%s: urb submission failed (%d)!\n", __func__, err);
-			spin_lock_irq(&instance->send_lock);
-			list_add(&snd->list, &instance->spare_senders);
-			spin_unlock_irq(&instance->send_lock);
-			list_add(&buf->list, &instance->filled_send_buffers);
-			return;	/* bail out */
+	while (skb) {
+		if (!tx) {
+			tx = usbatm_pop_transceiver(&instance->tx_channel);
+			if (!tx)
+				break;		/* no more senders */
+			buffer = tx->urb->transfer_buffer;
+			num_written = 0;
 		}
 
-		spin_lock_irq(&instance->send_lock);
-	}			/* while */
-	spin_unlock_irq(&instance->send_lock);
-
-	if (!instance->current_skb)
-		instance->current_skb = skb_dequeue(&instance->sndqueue);
-	if (!instance->current_skb)
-		return;		/* done - no more skbs */
+		num_written += usbatm_write_cells(instance, skb,
+						  buffer + num_written,
+						  buf_size - num_written);
 
-	skb = instance->current_skb;
+		vdbg("%s: wrote %u bytes from skb 0x%p to sender 0x%p",
+		     __func__, num_written, skb, tx);
 
-	if (!(buf = instance->current_buffer)) {
-		spin_lock_irq(&instance->send_lock);
-		if (list_empty(&instance->spare_send_buffers)) {
-			instance->current_buffer = NULL;
-			spin_unlock_irq(&instance->send_lock);
-			return;	/* done - no more buffers */
-		}
-		buf = list_entry(instance->spare_send_buffers.next,
-			       struct usbatm_send_buffer, list);
-		list_del(&buf->list);
-		spin_unlock_irq(&instance->send_lock);
+		if (!UDSL_SKB(skb)->len) {
+			struct atm_vcc *vcc = UDSL_SKB(skb)->vcc;
 
-		buf->free_start = buf->base;
-		buf->free_cells = snd_buf_size;
+			usbatm_pop(vcc, skb);
+			atomic_inc(&vcc->stats->tx);
 
-		instance->current_buffer = buf;
+			skb = skb_dequeue(&instance->sndqueue);
 	}
 
-	num_written = usbatm_write_cells(instance, buf->free_cells, skb, &buf->free_start);
-
-	vdbg("%s: wrote %u cells from skb 0x%p to buffer 0x%p",
-			__func__, num_written, skb, buf);
+		if (num_written == buf_size || !skb) {
+			tx->urb->transfer_buffer_length = num_written;
 
-	if (!(buf->free_cells -= num_written)) {
-		list_add_tail(&buf->list, &instance->filled_send_buffers);
-		instance->current_buffer = NULL;
+			if (usbatm_submit(tx))
+				break;
+			tx = NULL;
 	}
 
-	vdbg("%s: buffer contains %d cells, %d left",
-			__func__, snd_buf_size - buf->free_cells, buf->free_cells);
-
-	if (!UDSL_SKB(skb)->num_cells) {
-		struct atm_vcc *vcc = UDSL_SKB(skb)->atm_data.vcc;
-
-		usbatm_pop(vcc, skb);
-		instance->current_skb = NULL;
-
-		atomic_inc(&vcc->stats->tx);
 	}
 
-	goto made_progress;
+	instance->current_skb = skb;
 }
 
-static void usbatm_cancel_send(struct usbatm_data *instance, struct atm_vcc *vcc)
+static void usbatm_cancel_send(struct usbatm_data *instance,
+			       struct atm_vcc *vcc)
 {
 	struct sk_buff *skb, *n;
 
@@ -689,26 +549,27 @@ static void usbatm_cancel_send(struct us
 	for (skb = instance->sndqueue.next, n = skb->next;
 	     skb != (struct sk_buff *)&instance->sndqueue;
 	     skb = n, n = skb->next)
-		if (UDSL_SKB(skb)->atm_data.vcc == vcc) {
+		if (UDSL_SKB(skb)->vcc == vcc) {
 			atm_dbg(instance, "%s: popping skb 0x%p\n", __func__, skb);
 			__skb_unlink(skb, &instance->sndqueue);
 			usbatm_pop(vcc, skb);
 		}
 	spin_unlock_irq(&instance->sndqueue.lock);
 
-	tasklet_disable(&instance->send_tasklet);
-	if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm_data.vcc == vcc)) {
+	tasklet_disable(&instance->tx_channel.tasklet);
+	if ((skb = instance->current_skb) && (UDSL_SKB(skb)->vcc == vcc)) {
 		atm_dbg(instance, "%s: popping current skb (0x%p)\n", __func__, skb);
 		instance->current_skb = NULL;
 		usbatm_pop(vcc, skb);
 	}
-	tasklet_enable(&instance->send_tasklet);
+	tasklet_enable(&instance->tx_channel.tasklet);
 	atm_dbg(instance, "%s done\n", __func__);
 }
 
 static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
 {
 	struct usbatm_data *instance = vcc->dev->dev_data;
+	struct usbatm_control *ctrl = UDSL_SKB(skb);
 	int err;
 
 	vdbg("%s called (skb 0x%p, len %u)", __func__, skb, skb->len);
@@ -734,9 +595,13 @@ static int usbatm_atm_send(struct atm_vc
 
 	PACKETDEBUG(skb->data, skb->len);
 
-	usbatm_groom_skb(vcc, skb);
+	/* initialize the control block */
+	ctrl->vcc = vcc;
+	ctrl->len = skb->len;
+	ctrl->crc = crc32_be(~0, skb->data, skb->len);
+
 	skb_queue_tail(&instance->sndqueue, skb);
-	tasklet_schedule(&instance->send_tasklet);
+	tasklet_schedule(&instance->tx_channel.tasklet);
 
 	return 0;
 
@@ -756,8 +621,8 @@ static void usbatm_destroy_instance(stru
 
 	dbg("%s", __func__);
 
-	tasklet_kill(&instance->receive_tasklet);
-	tasklet_kill(&instance->send_tasklet);
+	tasklet_kill(&instance->rx_channel.tasklet);
+	tasklet_kill(&instance->tx_channel.tasklet);
 	usb_put_dev(instance->usb_dev);
 	kfree(instance);
 }
@@ -839,7 +704,6 @@ static int usbatm_atm_open(struct atm_vc
 {
 	struct usbatm_data *instance = vcc->dev->dev_data;
 	struct usbatm_vcc_data *new;
-	unsigned int max_pdu;
 	int vci = vcc->vci;
 	short vpi = vcc->vpi;
 
@@ -876,9 +740,8 @@ static int usbatm_atm_open(struct atm_vc
 	new->vpi = vpi;
 	new->vci = vci;
 
-	/* usbatm_extract_cells requires at least one cell */
-	max_pdu = max(1, UDSL_NUM_CELLS(vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD;
-	if (!(new->sarb = alloc_skb(max_pdu, GFP_KERNEL))) {
+	new->sarb = alloc_skb(usbatm_pdu_length(vcc->qos.rxtp.max_sdu), GFP_KERNEL);
+	if (!new->sarb) {
 		atm_dbg(instance, "%s: no memory for SAR buffer!\n", __func__);
 		kfree(new);
 		up(&instance->serialize);
@@ -887,9 +750,9 @@ static int usbatm_atm_open(struct atm_vc
 
 	vcc->dev_data = new;
 
-	tasklet_disable(&instance->receive_tasklet);
+	tasklet_disable(&instance->rx_channel.tasklet);
 	list_add(&new->list, &instance->vcc_list);
-	tasklet_enable(&instance->receive_tasklet);
+	tasklet_enable(&instance->rx_channel.tasklet);
 
 	set_bit(ATM_VF_ADDR, &vcc->flags);
 	set_bit(ATM_VF_PARTIAL, &vcc->flags);
@@ -897,9 +760,7 @@ static int usbatm_atm_open(struct atm_vc
 
 	up(&instance->serialize);
 
-	tasklet_schedule(&instance->receive_tasklet);
-
-	atm_dbg(instance, "%s: allocated vcc data 0x%p (max_pdu: %u)\n", __func__, new, max_pdu);
+	atm_dbg(instance, "%s: allocated vcc data 0x%p\n", __func__, new);
 
 	return 0;
 }
@@ -923,9 +784,9 @@ static void usbatm_atm_close(struct atm_
 
 	down(&instance->serialize);	/* vs self, usbatm_atm_open */
 
-	tasklet_disable(&instance->receive_tasklet);
+	tasklet_disable(&instance->rx_channel.tasklet);
 	list_del(&vcc_data->list);
-	tasklet_enable(&instance->receive_tasklet);
+	tasklet_enable(&instance->rx_channel.tasklet);
 
 	kfree_skb(vcc_data->sarb);
 	vcc_data->sarb = NULL;
@@ -958,7 +819,7 @@ static int usbatm_atm_ioctl(struct atm_d
 static int usbatm_atm_init(struct usbatm_data *instance)
 {
 	struct atm_dev *atm_dev;
-	int ret;
+	int ret, i;
 
 	/* ATM init */
 	atm_dev = atm_dev_register(instance->driver_name, &usbatm_atm_devops, -1, NULL);
@@ -986,6 +847,10 @@ static int usbatm_atm_init(struct usbatm
 	mb();
 	atm_dev->dev_data = instance;
 
+	/* submit all rx URBs */
+	for (i = 0; i < num_rcv_urbs; i++)
+		usbatm_submit(instance->transceivers + i);
+
 	return 0;
 
  fail:
@@ -1039,6 +904,29 @@ static int usbatm_heavy_init(struct usba
 	return 0;
 }
 
+static void usbatm_tasklet_schedule(unsigned long data)
+{
+        tasklet_schedule((struct tasklet *) data);
+}
+
+static void usbatm_clear_halt(void *data)
+{
+	struct usbatm_channel *channel = data;
+
+	/* the processing will get restarted after throttling delay */
+	usb_clear_halt(channel->usbatm->usb_dev, channel->endpoint);
+}
+
+static inline void usbatm_init_channel(struct usbatm_channel *channel)
+{
+	spin_lock_init(&channel->lock);
+	INIT_LIST_HEAD(&channel->list);
+	channel->delay.function = usbatm_tasklet_schedule;
+	channel->delay.data = (unsigned long) &channel->tasklet;
+	init_timer(&channel->delay);
+	INIT_WORK(&channel->clear_halt_work, usbatm_clear_halt, channel);
+}
+
 int usbatm_usb_probe (struct usb_interface *intf, const struct usb_device_id *id,
 		struct usbatm_driver *driver)
 {
@@ -1071,10 +959,12 @@ int usbatm_usb_probe (struct usb_interfa
 
 	instance->usb_dev = usb_dev;
 	instance->usb_intf = intf;
-	instance->tx_endpoint = usb_sndbulkpipe(usb_dev, driver->out);
-	instance->rx_endpoint = usb_rcvbulkpipe(usb_dev, driver->in);
-	instance->tx_padding = driver->tx_padding;
-	instance->rx_padding = driver->rx_padding;
+	instance->rx_channel.endpoint = usb_rcvbulkpipe(usb_dev, driver->in);
+	instance->tx_channel.endpoint = usb_sndbulkpipe(usb_dev, driver->out);
+	instance->rx_channel.stride = ATM_CELL_SIZE + driver->rx_padding;
+	instance->tx_channel.stride = ATM_CELL_SIZE + driver->tx_padding;
+	instance->rx_channel.buf_size = rcv_buf_size * instance->rx_channel.stride;
+	instance->tx_channel.buf_size = snd_buf_size * instance->tx_channel.stride;
 
 	buf = instance->description;
 	length = sizeof(instance->description);
@@ -1115,74 +1005,70 @@ int usbatm_usb_probe (struct usb_interfa
 
 	INIT_LIST_HEAD(&instance->vcc_list);
 
-	spin_lock_init(&instance->receive_lock);
-	INIT_LIST_HEAD(&instance->spare_receivers);
-	INIT_LIST_HEAD(&instance->filled_receive_buffers);
-
-	tasklet_init(&instance->receive_tasklet, usbatm_process_receive, (unsigned long)instance);
-	INIT_LIST_HEAD(&instance->spare_receive_buffers);
+	tasklet_init(&instance->rx_channel.tasklet, usbatm_rx_process, (unsigned long)instance);
+	tasklet_init(&instance->tx_channel.tasklet, usbatm_tx_process, (unsigned long)instance);
+	usbatm_init_channel(&instance->rx_channel);
+	usbatm_init_channel(&instance->tx_channel);
+	instance->rx_channel.usbatm = instance->tx_channel.usbatm = instance;
 
 	skb_queue_head_init(&instance->sndqueue);
 
-	spin_lock_init(&instance->send_lock);
-	INIT_LIST_HEAD(&instance->spare_senders);
-	INIT_LIST_HEAD(&instance->spare_send_buffers);
-
-	tasklet_init(&instance->send_tasklet, usbatm_process_send, (unsigned long)instance);
-	INIT_LIST_HEAD(&instance->filled_send_buffers);
-
-	/* receive init */
-	for (i = 0; i < num_rcv_urbs; i++) {
-		struct usbatm_receiver *rcv = &(instance->receivers[i]);
-
-		if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
-			dev_dbg(dev, "%s: no memory for receive urb %d!\n", __func__, i);
+	instance->transceivers = kmalloc(sizeof(*instance->transceivers) * (num_rcv_urbs + num_snd_urbs),
+					 GFP_KERNEL);
+	if (!instance->transceivers) {
+		dev_dbg(dev, "%s: no memory for transceivers!\n", __func__);
 			goto fail_unbind;
 		}
 
-		rcv->instance = instance;
-
-		list_add(&rcv->list, &instance->spare_receivers);
-	}
+	memset(instance->transceivers, 0, sizeof(*instance->transceivers) * (num_rcv_urbs + num_snd_urbs));
 
-	for (i = 0; i < num_rcv_bufs; i++) {
-		struct usbatm_receive_buffer *buf = &(instance->receive_buffers[i]);
+	for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
+		struct usbatm_transceiver *trx = instance->transceivers + i;
+		u8 *buffer;
+		unsigned int iso_packets = 0, iso_size = 0;
+		trx->channel = i < num_rcv_urbs ? &instance->rx_channel : &instance->tx_channel;
 
-		buf->base = kmalloc(rcv_buf_size * (ATM_CELL_SIZE + instance->rx_padding),
-				    GFP_KERNEL);
-		if (!buf->base) {
-			dev_dbg(dev, "%s: no memory for receive buffer %d!\n", __func__, i);
+		buffer = kmalloc(trx->channel->buf_size, GFP_KERNEL);
+		if (!buffer) {
+			dev_dbg(dev, "%s: no memory for buffer %d!\n", __func__, i);
 			goto fail_unbind;
 		}
+		memset(buffer, 0, trx->channel->buf_size);
 
-		list_add(&buf->list, &instance->spare_receive_buffers);
+		if (usb_pipeisoc(trx->channel->endpoint)) {
+			/* don't expect iso out endpoints */
+			iso_size = usb_maxpacket(instance->usb_dev, trx->channel->endpoint, 0);
+			iso_size -= iso_size % trx->channel->stride;	/* alignment */
+			BUG_ON(!iso_size);
+			iso_packets = (trx->channel->buf_size - 1) / iso_size + 1;
 	}
 
-	/* send init */
-	for (i = 0; i < num_snd_urbs; i++) {
-		struct usbatm_sender *snd = &(instance->senders[i]);
-
-		if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
-			dev_dbg(dev, "%s: no memory for send urb %d!\n", __func__, i);
+		trx->urb = usb_alloc_urb(iso_packets, GFP_KERNEL);
+		if (!trx->urb) {
+			dev_dbg(dev, "%s: no memory for urb %d!\n", __func__, i);
 			goto fail_unbind;
 		}
 
-		snd->instance = instance;
-
-		list_add(&snd->list, &instance->spare_senders);
+		usb_fill_bulk_urb(trx->urb, instance->usb_dev, trx->channel->endpoint,
+				  buffer, trx->channel->buf_size, usbatm_complete, trx);
+		if (iso_packets) {
+			int j;
+			trx->urb->interval = 1;
+			trx->urb->transfer_flags = URB_ISO_ASAP;
+			trx->urb->number_of_packets = iso_packets;
+			for (j = 0; j < iso_packets; j++) {
+				trx->urb->iso_frame_desc[j].offset = iso_size * j;
+				trx->urb->iso_frame_desc[j].length = min_t(int, iso_size,
+									   trx->channel->buf_size - trx->urb->iso_frame_desc[j].offset);
 	}
-
-	for (i = 0; i < num_snd_bufs; i++) {
-		struct usbatm_send_buffer *buf = &(instance->send_buffers[i]);
-
-		buf->base = kmalloc(snd_buf_size * (ATM_CELL_SIZE + instance->tx_padding),
-				    GFP_KERNEL);
-		if (!buf->base) {
-			dev_dbg(dev, "%s: no memory for send buffer %d!\n", __func__, i);
-			goto fail_unbind;
 		}
 
-		list_add(&buf->list, &instance->spare_send_buffers);
+		/* put all tx URBs on the list of spares */
+		if (i >= num_rcv_urbs)
+			list_add(&trx->list, &trx->channel->list);
+
+		vdbg("%s: alloced trx 0x%p buffer 0x%p buf size %u urb 0x%p",
+		     __func__, trx, trx->urb->transfer_buffer, trx->urb->transfer_buffer_length, trx->urb);
 	}
 
 	if (need_heavy && driver->heavy_init) {
@@ -1204,18 +1090,13 @@ int usbatm_usb_probe (struct usb_interfa
 	if (instance->driver->unbind)
 		instance->driver->unbind(instance, intf);
  fail_free:
-	for (i = 0; i < num_snd_bufs; i++)
-		kfree(instance->send_buffers[i].base);
-
-	for (i = 0; i < num_snd_urbs; i++)
-		usb_free_urb(instance->senders[i].urb);
-
-	for (i = 0; i < num_rcv_bufs; i++)
-		kfree(instance->receive_buffers[i].base);
-
-	for (i = 0; i < num_rcv_urbs; i++)
-		usb_free_urb(instance->receivers[i].urb);
+	if (instance->transceivers)
+		for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
+			kfree(instance->transceivers[i].urb->transfer_buffer);
+			usb_free_urb(instance->transceivers[i].urb);
+		}
 
+	kfree(instance->transceivers);
 	kfree (instance);
 
 	return error;
@@ -1228,7 +1109,7 @@ void usbatm_usb_disconnect(struct usb_in
 	struct usbatm_data *instance = usb_get_intfdata(intf);
 	int i;
 
-	dev_dbg(dev, "%s entered\n", __func__);
+	dev_dbg(dev, "%s: disconnect entered\n", __func__);
 
 	if (!instance) {
 		dev_dbg(dev, "%s: NULL instance!\n", __func__);
@@ -1244,51 +1125,39 @@ void usbatm_usb_disconnect(struct usb_in
 
 	wait_for_completion(&instance->thread_exited);
 
-	tasklet_disable(&instance->receive_tasklet);
-	tasklet_disable(&instance->send_tasklet);
+	tasklet_disable(&instance->rx_channel.tasklet);
+	tasklet_disable(&instance->tx_channel.tasklet);
+
+	del_timer_sync(&instance->rx_channel.delay);
+	del_timer_sync(&instance->tx_channel.delay);
 
 	if (instance->atm_dev && instance->driver->atm_stop)
 		instance->driver->atm_stop(instance, instance->atm_dev);
 
+	for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++)
+		usb_kill_urb(instance->transceivers[i].urb);
+
+	flush_scheduled_work();		/* in case we scheduled clear_halt */
+
 	if (instance->driver->unbind)
 		instance->driver->unbind(instance, intf);
 
 	instance->driver_data = NULL;
 
-	/* receive finalize */
-
-	for (i = 0; i < num_rcv_urbs; i++)
-		usb_kill_urb(instance->receivers[i].urb);
-
-	/* no need to take the spinlock */
-	INIT_LIST_HEAD(&instance->filled_receive_buffers);
-	INIT_LIST_HEAD(&instance->spare_receive_buffers);
-
-	tasklet_enable(&instance->receive_tasklet);
-
-	for (i = 0; i < num_rcv_urbs; i++)
-		usb_free_urb(instance->receivers[i].urb);
-
-	for (i = 0; i < num_rcv_bufs; i++)
-		kfree(instance->receive_buffers[i].base);
-
-	/* send finalize */
-
-	for (i = 0; i < num_snd_urbs; i++)
-		usb_kill_urb(instance->senders[i].urb);
-
+	/* turn usbatm_[rt]x_process into noop */
 	/* no need to take the spinlock */
-	INIT_LIST_HEAD(&instance->spare_senders);
-	INIT_LIST_HEAD(&instance->spare_send_buffers);
-	instance->current_buffer = NULL;
+	INIT_LIST_HEAD(&instance->rx_channel.list);
+	INIT_LIST_HEAD(&instance->tx_channel.list);
 
-	tasklet_enable(&instance->send_tasklet);
+	tasklet_enable(&instance->rx_channel.tasklet);
+	tasklet_enable(&instance->tx_channel.tasklet);
 
-	for (i = 0; i < num_snd_urbs; i++)
-		usb_free_urb(instance->senders[i].urb);
+	for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
+		kfree(instance->transceivers[i].urb->transfer_buffer);
+		usb_free_urb(instance->transceivers[i].urb);
+	}
 
-	for (i = 0; i < num_snd_bufs; i++)
-		kfree(instance->send_buffers[i].base);
+	kfree(instance->transceivers);
 
 	/* ATM finalize */
 	if (instance->atm_dev)
@@ -1314,8 +1183,6 @@ static int __init usbatm_usb_init(void)
 
 	if ((num_rcv_urbs > UDSL_MAX_RCV_URBS)
 	    || (num_snd_urbs > UDSL_MAX_SND_URBS)
-	    || (num_rcv_bufs > UDSL_MAX_RCV_BUFS)
-	    || (num_snd_bufs > UDSL_MAX_SND_BUFS)
 	    || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE)
 	    || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
 		return -EINVAL;



More information about the Usbatm mailing list