speedtch usbatm.c,1.16,1.17

Roman Kagan rkagan at mail.ru
Fri Apr 8 02:42:26 EDT 2005


On Thu, Apr 07, 2005 at 11:10:16PM +0100, Duncan Sands wrote:
> Modified Files:
> 	usbatm.c 
> Log Message:
> Use the new output macros.  Also, rename dev to usb_dev in some places, and
> some other small tweaks.

Uff, I updated my patch to match this, please find it below.  Note that
due to the nature of the proposed changes, the readability of the
resulting code is IMHO better than that of the patch.

Any chance to have it commented / applied?

Cheers,
  Roman.

 usbatm.c |  771 ++++++++++++++++++++++++++-------------------------------------
 usbatm.h |   83 +-----
 2 files changed, 334 insertions(+), 520 deletions(-)


Index: usbatm.h
===================================================================
RCS file: /home/cvs/usbatm/usbatm.h,v
retrieving revision 1.12
diff -u -p -w -r1.12 usbatm.h
--- usbatm.h	7 Apr 2005 21:52:43 -0000	1.12
+++ usbatm.h	8 Apr 2005 06:12:05 -0000
@@ -114,55 +114,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_channel *channel;
 };
 
-struct usbatm_sender {
-	struct list_head list;
-	struct usbatm_send_buffer *buffer;
-	struct urb *urb;
-	struct usbatm_data *instance;
-};
-
-
 /* main driver data */
 
 struct usbatm_data {
@@ -179,10 +148,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;
@@ -202,31 +167,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;
-
-	/* send */
-	struct usbatm_sender senders[UDSL_MAX_SND_URBS];
-	struct usbatm_send_buffer send_buffers[UDSL_MAX_SND_BUFS];
-
-	struct sk_buff_head sndqueue;
+	struct usbatm_transceiver *transceivers;
 
-	spinlock_t send_lock;
-	struct list_head spare_senders;
-	struct list_head spare_send_buffers;
+	struct usbatm_channel rx_channel;
+	struct usbatm_channel tx_channel;
 
-	struct tasklet_struct send_tasklet;
+	struct sk_buff_head sndqueue;
 	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.17
diff -u -p -w -r1.17 usbatm.c
--- usbatm.c	7 Apr 2005 22:10:14 -0000	1.17
+++ usbatm.c	8 Apr 2005 06:12:06 -0000
@@ -95,22 +95,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 */
 
@@ -129,11 +125,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)
@@ -141,8 +135,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;
 
@@ -158,27 +150,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, 0444);
-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, 0444);
-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, 0444);
 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, 0444);
 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) ")");
 
@@ -205,6 +185,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)
 {
@@ -214,6 +199,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  **
 *************/
@@ -230,24 +282,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);
 
@@ -263,6 +312,7 @@ static void usbatm_extract_cells(struct 
 		}
 
 		vcc = vcc_data->vcc;
+
 		sarb = vcc_data->sarb;
 
 		if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
@@ -275,7 +325,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;
@@ -290,7 +340,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",
@@ -343,343 +393,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;
+	unsigned int num_written;
 
-	ctrl->atm_data.vcc = vcc;
+	/* 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);
+
+	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;
+
+		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;
+
+		memcpy(ptr, skb->data, data_len);
+		ptr += data_len;
+		skb_pull(skb, data_len);
 
-	ctrl->num_cells = UDSL_NUM_CELLS(skb->len);
-	ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD;
+		if(!left)
+			continue;
 
-	zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER;
+		memset(ptr, 0, left);
 
-	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;
-
-	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;
-
-	ctrl->aal5_trailer[4] = crc >> 24;
-	ctrl->aal5_trailer[5] = crc >> 16;
-	ctrl->aal5_trailer[6] = crc >> 8;
-	ctrl->aal5_trailer[7] = crc;
-}
+		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;
 
-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;
+			ctrl->crc = ~ crc32_be(ctrl->crc, ptr, left - 4);
 
-	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);
+			trailer[4] = ctrl->crc >> 24;
+			trailer[5] = ctrl->crc >> 16;
+			trailer[6] = ctrl->crc >> 8;
+			trailer[7] = ctrl->crc;
 
-	nc = ctrl->num_cells;
-	ne = min(howmany, ctrl->num_entire);
+			target[3] |= 0x2;	/* adjust PTI */
 
-	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;
+			ctrl->len = 0;		/* tag this skb finished */
 		}
-		__skb_pull(skb, ATM_CELL_PAYLOAD);
-	}
-
-	ctrl->num_entire -= ne;
-
-	if (!(ctrl->num_cells -= ne) || !(howmany -= ne))
-		goto out;
-
-	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;
-
-	if (--ctrl->num_cells) {
-		if (!--howmany) {
-			ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
-			goto out;
+		else
+			ctrl->crc = crc32_be(ctrl->crc, ptr, left);
 		}
 
-		if (instance->tx_padding) {
-			memset(target, 0, instance->tx_padding);
-			target += instance->tx_padding;
-		}
-		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);
-	}
-
-	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)
+static void usbatm_rx_process(unsigned long data)
 {
-	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);
+	struct usbatm_data *instance = (struct usbatm_data *)data;
+	struct usbatm_transceiver *rx;
 
-	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);
+	while ((rx = usbatm_pop_transceiver(&instance->rx_channel))) {
+		struct urb *urb = rx->urb;
 
-	UDSL_ASSERT(buf->filled_cells <= rcv_buf_size);
+		vdbg("%s: processing rx 0x%p", __func__, rx);
 
-	/* 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);
+		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);
 
-static void usbatm_process_receive(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;
-
-		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);
-
-		vdbg("%s: sending urb 0x%p, rcv 0x%p, buf 0x%p",
-				__func__, rcv->urb, rcv, buf);
-
-		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 (usbatm_submit(rx))
+			return;
 	}
-
-	spin_lock_irq(&instance->receive_lock);
-	if (list_empty(&instance->filled_receive_buffers)) {
-		spin_unlock_irq(&instance->receive_lock);
-		return;		/* done - no more buffers */
-	}
-	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;
+	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;
+
+	if (!skb)
+		skb = skb_dequeue(&instance->sndqueue);
+
+	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;
+		}
+
+		num_written += usbatm_write_cells(instance, skb,
+						  buffer + num_written,
+						  buf_size - 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);
-
-		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 ((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 */
-		}
-
-		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 */
+		vdbg("%s: wrote %u bytes from skb 0x%p to sender 0x%p",
+		     __func__, num_written, skb, tx);
 
-	skb = instance->current_skb;
-
-	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;
 
@@ -688,26 +548,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);
@@ -733,9 +594,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;
 
@@ -755,8 +620,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);
 }
@@ -838,7 +703,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;
 
@@ -875,9 +739,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);
@@ -886,9 +749,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);
@@ -896,9 +759,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;
 }
@@ -922,9 +783,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;
@@ -957,7 +818,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);
@@ -985,6 +846,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:
@@ -1038,6 +903,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)
 {
@@ -1070,10 +958,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);
@@ -1114,74 +1004,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);
-	}
-
-	for (i = 0; i < num_rcv_bufs; i++) {
-		struct usbatm_receive_buffer *buf = &(instance->receive_buffers[i]);
+	memset(instance->transceivers, 0, sizeof(*instance->transceivers) * (num_rcv_urbs + num_snd_urbs));
 
-		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);
+	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;
+
+		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) {
@@ -1203,18 +1089,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;
@@ -1227,7 +1108,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__);
@@ -1243,49 +1124,37 @@ 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);
 
-	if (instance->driver->unbind)
-		instance->driver->unbind(instance, intf);
-
-	/* 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);
+	for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++)
+		usb_kill_urb(instance->transceivers[i].urb);
 
-	/* send finalize */
+	flush_scheduled_work();		/* in case we scheduled clear_halt */
 
-	for (i = 0; i < num_snd_urbs; i++)
-		usb_kill_urb(instance->senders[i].urb);
+	if (instance->driver->unbind)
+		instance->driver->unbind(instance, intf);
 
+	/* 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)
@@ -1311,8 +1180,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