# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.516   -> 1.517  
#	drivers/usb/misc/Config.help	1.4     -> 1.5    
#	drivers/usb/misc/Makefile	1.8     -> 1.9    
#	drivers/usb/misc/Config.in	1.1     -> 1.2    
#	drivers/usb/Makefile	1.35    -> 1.36   
#	               (new)	        -> 1.1     drivers/usb/misc/atmsar.c
#	               (new)	        -> 1.1     drivers/usb/misc/atmsar.h
#	               (new)	        -> 1.1     drivers/usb/misc/speedtouch.c
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/08/21	greg@kroah.com	1.517
# USB: added the speedtouch usb driver.
# 
# Patch originally from Richard Purdie <rpurdie@rpsys.net> but tweaked by me.
# --------------------------------------------
#
diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile
--- a/drivers/usb/Makefile	Wed Aug 21 15:45:22 2002
+++ b/drivers/usb/Makefile	Wed Aug 21 15:45:22 2002
@@ -59,6 +59,7 @@
 obj-$(CONFIG_USB_EMI26)		+= misc/
 obj-$(CONFIG_USB_LCD)		+= misc/
 obj-$(CONFIG_USB_RIO500)	+= misc/
+obj-$(CONFIG_USB_SPEEDTOUCH)	+= misc/
 obj-$(CONFIG_USB_TIGL)		+= misc/
 obj-$(CONFIG_USB_USS720)	+= misc/
 
diff -Nru a/drivers/usb/misc/Config.help b/drivers/usb/misc/Config.help
--- a/drivers/usb/misc/Config.help	Wed Aug 21 15:45:22 2002
+++ b/drivers/usb/misc/Config.help	Wed Aug 21 15:45:22 2002
@@ -84,3 +84,31 @@
   inserted in and removed from the running kernel whenever you want).
   The module will be called uss720.o. If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_USB_SPEEDTCH
+
+  This driver provides support for the Alcatel SpeedTouch ADSL USB 
+  modem.
+
+  The driver requires the ATM interface and ATM SAR so you need to 
+  enable ATM (under Networking options) and the ATM SAR option. You 
+  will also need PPP over ATM (under Network device support).
+
+  This driver is a slightly revised version of Johan Verrept's 1.5 
+  SpeedTouch Driver which has been altered to work on the 2.5 series 
+  kernels.
+
+  To use the device you also need a user-mode daemon that downloads
+  the firmware and (re)initializes the modem. The offical version is 
+  a closed source one from Alcatel that you can get at
+  <http://www.alcateldsl.com/support.htm>.
+
+  A piece of code has recently been sent to linux-usb-devel which 
+  allows the open source user space driver's firmware program 
+  modem_run to be used with this driver instead. You will still 
+  need the Alcatel daemon package to extract the modem firmware from 
+  it (or the windows drivers instead).
+
+  For more information, see Johan Verrept's webpages at
+  <http://linux-usb.sourceforge.net/SpeedTouch/>.
+
diff -Nru a/drivers/usb/misc/Config.in b/drivers/usb/misc/Config.in
--- a/drivers/usb/misc/Config.in	Wed Aug 21 15:45:22 2002
+++ b/drivers/usb/misc/Config.in	Wed Aug 21 15:45:22 2002
@@ -8,3 +8,4 @@
 dep_tristate '  USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL
 dep_tristate '  Tieman Voyager USB Braille display support (EXPERIMENTAL)' CONFIG_USB_BRLVGER $CONFIG_USB $CONFIG_EXPERIMENTAL
 dep_tristate '  USB LCD driver support' CONFIG_USB_LCD $CONFIG_USB
+dep_tristate '  Alcatel Speedtouch ADSL USB Modem' CONFIG_USB_SPEEDTOUCH $CONFIG_USB    
diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
--- a/drivers/usb/misc/Makefile	Wed Aug 21 15:45:22 2002
+++ b/drivers/usb/misc/Makefile	Wed Aug 21 15:45:22 2002
@@ -3,11 +3,14 @@
 # (the ones that don't fit into any other categories)
 #
 
+export-objs	:= atmsar.o
+
 obj-$(CONFIG_USB_AUERSWALD)	+= auerswald.o
 obj-$(CONFIG_USB_BRLVGER)	+= brlvger.o
 obj-$(CONFIG_USB_EMI26)		+= emi26.o
 obj-$(CONFIG_USB_LCD)		+= usblcd.o
 obj-$(CONFIG_USB_RIO500)	+= rio500.o
+obj-$(CONFIG_USB_SPEEDTOUCH)	+= speedtouch.o atmsar.o
 obj-$(CONFIG_USB_TIGL)		+= tiglusb.o
 obj-$(CONFIG_USB_USS720)	+= uss720.o
 
diff -Nru a/drivers/usb/misc/atmsar.c b/drivers/usb/misc/atmsar.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/misc/atmsar.c	Wed Aug 21 15:45:22 2002
@@ -0,0 +1,720 @@
+/*
+ *  General SAR library for ATM devices. 
+ * 
+ *  Written By Johan Verrept ( Johan.Verrept@advalvas.be )
+ *
+ *  Copyright (c) 2000, Johan Verrept
+ *
+ *  This code falls under the GNU General Public License, see COPYING for details
+ *
+ *  This package is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
+
+Version 0.2.4A:
+       - Version for inclusion in 2.5 series kernel
+       - Modifcations by Richard Purdie (rpurdie@rpsys.net)
+ 	- replaced "sarlib" with "atmsar"
+       - adaptations for inclusion in kernel tree
+
+Version 0.2.4:
+	- Fixed wrong buffer overrun check in atmsar_decode_rawcell()
+	  reported by Stephen Robinson <stephen.robinson@zen.co.uk>
+	- Fixed bug when input skb did not contain a multple of 52/53 bytes.
+	  (would happen when the speedtouch device resynced)
+	  also reported by Stephen Robinson <stephen.robinson@zen.co.uk>
+
+Version 0.2.3:
+	- Fixed wrong allocation size. caused memory corruption in some
+	  cases. Reported by Vladimir Dergachev <volodya@mindspring.com>
+	- Added some comments
+
+Version 0.2.2:
+	- Fixed CRCASM (patch from Linus Flannagan <linusf@netservices.eng.net>)
+	- Fixed problem when user did NOT use the ATMSAR_USE_53BYTE_CELL flag.
+          (reported by  Piers Scannell <email@lot105.com> )
+	- No more in-buffer rewriting for cloned buffers.
+	- Removed the PII specific CFLAGS in the Makefile.
+
+Version 0.2.1:
+	- removed dependancy on alloc_tx. tis presented problems when using
+		this with the  br2684 code.
+
+Version 0.2:
+        - added AAL0 reassembly
+        - added alloc_tx support                  
+        - replaced alloc_skb in decode functions to dev_alloc_skb to allow
+                 calling from interrupt
+        - fixed embarassing AAL5 bug. I was setting the pti bit in the wrong
+                byte...
+        - fixed another emabrassing bug.. picked up the wrong crc type and
+                forgot to invert the crc result...
+        - fixed AAL5 length calculations.
+        - removed automatic skb freeing from encode functions.
+                This caused problems because i did kfree_skb it, while it
+                needed to be popped. I cannot determine though whether it
+                needs to be popped or not. Figu'e it out ye'self ;-)
+        - added mru field. This is the buffersize. atmsar_decode_aal0 will
+                use when it allocates a receive buffer. A stop gap for real
+		buffer management.
+
+Version 0.1:
+	- library created.
+	- only contains AAL5, AAL0 can be easily added. ( actually, only
+		AAL0 reassembly is missing)
+*/
+
+#include "atmsar.h"
+#include <linux/module.h>
+#include <linux/init.h>
+
+#define DRIVER_AUTHOR "Johan Verrept, Johan.Verrept@advalvas.be"
+#define DRIVER_DESC "General SAR library for ATM devices"
+#define DRIVER_VERSION "0.2.4A"
+
+/***********************
+ **
+ **  things to remember
+ **
+ ***********************/
+
+/*
+  1. the atmsar_vcc_data list pointer MUST be initialized to NULL
+  2. atmsar_encode_rawcell will drop incomplete cells.
+  3. ownership of the skb goes to the library !
+*/
+
+#define ATM_HDR_VPVC_MASK  (ATM_HDR_VPI_MASK | ATM_HDR_VCI_MASK)
+
+/***********************
+ **
+ **  LOCAL STRUCTURES
+ **
+ ***********************/
+
+/***********************
+ **
+ **  LOCAL MACROS
+ **
+ ***********************/
+/*
+#define DEBUG 1
+*/
+#ifdef DEBUG
+#define PDEBUG(arg...)  printk(KERN_DEBUG "atmsar: " arg)
+#else
+#define PDEBUG(arg...)
+#endif
+
+#define ADD_HEADER(dest, header) \
+  *dest++ = (unsigned char) (header >> 24); \
+  *dest++ = (unsigned char) (header >> 16); \
+  *dest++ = (unsigned char) (header >> 8); \
+  *dest++ = (unsigned char) (header & 0xff);
+
+/*
+ * CRC Routines from  net/wan/sbni.c)
+ * table generated by Rocksoft^tm Model CRC Algorithm Table Generation Program V1.0
+ */
+#define CRC32_REMAINDER CBF43926
+#define CRC32_INITIAL 0xffffffff
+#define CRC32(c,crc) (crc32tab[((size_t)(crc>>24) ^ (c)) & 0xff] ^ (((crc) << 8)))
+unsigned long crc32tab[256] = {
+	0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
+	0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
+	0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,
+	0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,
+	0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,
+	0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,
+	0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,
+	0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,
+	0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,
+	0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,
+	0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,
+	0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,
+	0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,
+	0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,
+	0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,
+	0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,
+	0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,
+	0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,
+	0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,
+	0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,
+	0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,
+	0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,
+	0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,
+	0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,
+	0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,
+	0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,
+	0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,
+	0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,
+	0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,
+	0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,
+	0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,
+	0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,
+	0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,
+	0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,
+	0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,
+	0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,
+	0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,
+	0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,
+	0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,
+	0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,
+	0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,
+	0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,
+	0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,
+	0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,
+	0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,
+	0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,
+	0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,
+	0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,
+	0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,
+	0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,
+	0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,
+	0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,
+	0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,
+	0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,
+	0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,
+	0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,
+	0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,
+	0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,
+	0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,
+	0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,
+	0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,
+	0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,
+	0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
+	0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
+};
+
+#ifdef CRCASM
+
+unsigned long calc_crc (char *mem, int len, unsigned initial)
+{
+	unsigned crc, dummy_len;
+      __asm__ ("xorl %%eax,%%eax\n\t" "1:\n\t" "movl %%edx,%%eax\n\t" "shrl $16,%%eax\n\t" "lodsb\n\t" "xorb %%ah,%%al\n\t" "andl $255,%%eax\n\t" "shll $8,%%edx\n\t" "xorl (%%edi,%%eax,4),%%edx\n\t" "loop 1b":"=d" (crc),
+		 "=c"
+		 (dummy_len)
+      :	 "S" (mem), "D" (&crc32tab[0]), "1" (len), "0" (initial)
+      :	 "eax");
+	return crc;
+}
+
+#else
+
+unsigned long calc_crc (char *mem, int len, unsigned initial)
+{
+	unsigned crc;
+	crc = initial;
+
+	for (; len; mem++, len--) {
+		crc = CRC32 (*mem, crc);
+	}
+	return (crc);
+}
+#endif
+
+#define crc32( crc, mem, len) calc_crc(mem, len, crc);
+
+/* initialiation routines. not used at the moment 
+ * I will avoid these as long as possible !!
+ */
+
+int open_atmsar (void)
+{
+	return 0;
+}
+
+int remove_atmsar (void)
+{
+	return 0;
+}
+
+/* ATOMIC version of alloc_tx */
+struct sk_buff *atmsar_alloc_skb_wrapper (struct atm_vcc *vcc, unsigned int size)
+{
+	struct sk_buff *skb;
+
+	if (atomic_read (&vcc->tx_inuse) && !atm_may_send (vcc, size)) {
+		PDEBUG ("Sorry: tx_inuse = %d, size = %d, sndbuf = %d\n",
+			atomic_read (&vcc->tx_inuse), size, vcc->sk->sndbuf);
+		return NULL;
+	}
+	skb = alloc_skb (size, GFP_ATOMIC);
+	if (!skb)
+		return NULL;
+	atomic_add (skb->truesize + ATM_PDU_OVHD, &vcc->tx_inuse);
+	return skb;
+}
+
+struct sk_buff *atmsar_alloc_tx (struct atmsar_vcc_data *vcc, unsigned int size)
+{
+	struct sk_buff *tmp = NULL;
+	int bufsize = 0;
+
+	switch (vcc->type) {
+	case ATMSAR_TYPE_AAL0:
+		/* reserving adequate headroom */
+		bufsize =
+		    size + (((size / 48) + 1) * ((vcc->flags & ATMSAR_USE_53BYTE_CELL) ? 5 : 4));
+		break;
+	case ATMSAR_TYPE_AAL1:
+		/* reserving adequate headroom */
+		bufsize =
+		    size + (((size / 47) + 1) * ((vcc->flags & ATMSAR_USE_53BYTE_CELL) ? 5 : 4));
+		break;
+	case ATMSAR_TYPE_AAL2:
+	case ATMSAR_TYPE_AAL34:
+		/* not supported */
+		break;
+	case ATMSAR_TYPE_AAL5:
+		/* reserving adequate tailroom */
+		bufsize = size + (((size + 8 + 47) / 48) * 48);
+		break;
+	}
+
+	PDEBUG ("Requested size %d, Allocating size %d\n", size, bufsize);
+	tmp = vcc->alloc_tx (vcc->vcc, bufsize);
+	skb_put (tmp, bufsize);
+
+	return tmp;
+}
+
+struct atmsar_vcc_data *atmsar_open (struct atmsar_vcc_data **list, struct atm_vcc *vcc, uint type,
+				     ushort vpi, ushort vci, unchar pti, unchar gfc, uint flags)
+{
+	struct atmsar_vcc_data *new;
+
+	new = kmalloc (sizeof (struct atmsar_vcc_data), GFP_KERNEL);
+
+	if (!new)
+		return NULL;
+
+	if (!vcc)
+		return NULL;
+
+	memset (new, 0, sizeof (struct atmsar_vcc_data));
+	new->vcc = vcc;
+/*
+ * This gives problems with the ATM layer alloc_tx().
+ * It is not usable from interrupt context and for
+ * some reason this is used in interurpt context 
+ * with br2684.c
+ *
+  if (vcc->alloc_tx)
+    new->alloc_tx  = vcc->alloc_tx;
+  else
+*/
+	new->alloc_tx = atmsar_alloc_skb_wrapper;
+
+	new->stats = vcc->stats;
+	new->type = type;
+	new->next = NULL;
+	new->gfc = gfc;
+	new->vp = vpi;
+	new->vc = vci;
+	new->pti = pti;
+
+	switch (type) {
+	case ATMSAR_TYPE_AAL0:
+		new->mtu = ATMSAR_DEF_MTU_AAL0;
+		break;
+	case ATMSAR_TYPE_AAL1:
+		new->mtu = ATMSAR_DEF_MTU_AAL1;
+		break;
+	case ATMSAR_TYPE_AAL2:
+		new->mtu = ATMSAR_DEF_MTU_AAL2;
+		break;
+	case ATMSAR_TYPE_AAL34:
+		/* not supported */
+		new->mtu = ATMSAR_DEF_MTU_AAL34;
+		break;
+	case ATMSAR_TYPE_AAL5:
+		new->mtu = ATMSAR_DEF_MTU_AAL5;
+		break;
+	}
+
+	new->atmHeader = ((unsigned long) gfc << ATM_HDR_GFC_SHIFT)
+	    | ((unsigned long) vpi << ATM_HDR_VPI_SHIFT)
+	    | ((unsigned long) vci << ATM_HDR_VCI_SHIFT)
+	    | ((unsigned long) pti << ATM_HDR_PTI_SHIFT);
+	new->flags = flags;
+	new->next = NULL;
+	new->reasBuffer = NULL;
+
+	new->next = *list;
+	*list = new;
+
+	PDEBUG ("Allocated new SARLib vcc 0x%p with vp %d vc %d\n", new, vpi, vci);
+
+	return new;
+}
+
+void atmsar_close (struct atmsar_vcc_data **list, struct atmsar_vcc_data *vcc)
+{
+	struct atmsar_vcc_data *work;
+
+	if (*list == vcc) {
+		*list = (*list)->next;
+	} else {
+		for (work = *list; work && work->next && (work->next != vcc); work = work->next);
+
+		/* return if not found */
+		if (work->next != vcc)
+			return;
+
+		work->next = work->next->next;
+	}
+
+	if (vcc->reasBuffer) {
+		dev_kfree_skb (vcc->reasBuffer);
+	}
+
+	PDEBUG ("Allocated SARLib vcc 0x%p with vp %d vc %d\n", vcc, vcc->vp, vcc->vc);
+
+	kfree (vcc);
+}
+
+/***********************
+ **
+ **    ENCODE FUNCTIONS
+ **
+ ***********************/
+
+struct sk_buff *atmsar_encode_rawcell (struct atmsar_vcc_data *ctx, struct sk_buff *skb)
+{
+	int number_of_cells = (skb->len) / 48;
+	int total_length = number_of_cells * (ctx->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52);
+	unsigned char *source;
+	unsigned char *target;
+	struct sk_buff *out = NULL;
+	int i;
+
+	PDEBUG ("atmsar_encode_rawcell (0x%p, 0x%p) called\n", ctx, skb);
+
+	if (skb_cloned (skb)
+	    || (skb_headroom (skb) <
+		(number_of_cells * (ctx->flags & ATMSAR_USE_53BYTE_CELL ? 5 : 4)))) {
+		PDEBUG
+		    ("atmsar_encode_rawcell allocating new skb. ctx->alloc_tx = 0x%p, ctx->vcc = 0x%p\n",
+		     ctx->alloc_tx, ctx->vcc);
+		/* get new skb */
+		out = ctx->alloc_tx (ctx->vcc, total_length);
+		if (!out)
+			return NULL;
+
+		skb_put (out, total_length);
+		source = skb->data;
+		target = out->data;
+	} else {
+		PDEBUG ("atmsar_encode_rawcell: sufficient headroom\n");
+		source = skb->data;
+		skb_push (skb, number_of_cells * ((ctx->flags & ATMSAR_USE_53BYTE_CELL) ? 5 : 4));
+		target = skb->data;
+		out = skb;
+	}
+
+	PDEBUG ("source 0x=%p, target 0x%p\n", source, target);
+
+	if (ctx->flags & ATMSAR_USE_53BYTE_CELL) {
+		for (i = 0; i < number_of_cells; i++) {
+			ADD_HEADER (target, ctx->atmHeader);
+			*target++ = (char) 0xEC;
+			memcpy (target, source, 48);
+			target += 48;
+			source += 48;
+			PDEBUG ("source 0x=%p, target 0x%p\n", source, target);
+		}
+	} else {
+		for (i = 0; i < number_of_cells; i++) {
+			ADD_HEADER (target, ctx->atmHeader);
+			memcpy (target, source, 48);
+			target += 48;
+			source += 48;
+			PDEBUG ("source 0x=%p, target 0x%p\n", source, target);
+		};
+	}
+
+	if (ctx->flags & ATMSAR_SET_PTI) {
+		/* setting pti bit in last cell */
+		*(target - (ctx->flags & ATMSAR_USE_53BYTE_CELL ? 50 : 49)) |= 0x2;
+	}
+
+	/* update stats */
+	if (ctx->stats && (ctx->type <= ATMSAR_TYPE_AAL1))
+		atomic_add (number_of_cells, &(ctx->stats->tx));
+
+	PDEBUG ("atmsar_encode_rawcell return 0x%p (length %d)\n", out, out->len);
+	return out;
+}
+
+struct sk_buff *atmsar_encode_aal5 (struct atmsar_vcc_data *ctx, struct sk_buff *skb)
+{
+	int length, pdu_length;
+	unsigned char *trailer;
+	unsigned char *pad;
+	uint crc = 0xffffffff;
+
+	PDEBUG ("atmsar_encode_aal5 (0x%p, 0x%p) called\n", ctx, skb);
+
+	/* determine aal5 length */
+	pdu_length = skb->len;
+	length = ((pdu_length + 8 + 47) / 48) * 48;
+
+	if (skb_tailroom (skb) < (length - pdu_length)) {
+		struct sk_buff *out;
+		PDEBUG
+		    ("atmsar_encode_aal5 allocating new skb. ctx->alloc_tx = 0x%p, ctx->vcc = 0x%p\n",
+		     ctx->alloc_tx, ctx->vcc);
+		/* get new skb */
+		out = ctx->alloc_tx (ctx->vcc, length);
+		if (!out)
+			return NULL;
+
+		PDEBUG ("out->data = 0x%p\n", out->data);
+		PDEBUG ("atmsar_encode_aal5 pdu length %d, allocated length %d\n", skb->len,
+			length);
+		memcpy (out->data, skb->data, skb->len);
+		skb_put (out, skb->len);
+
+		skb = out;
+	}
+
+	PDEBUG ("skb->data = 0x%p\n", skb->data);
+	/* note end of pdu and add length */
+	pad = skb_put (skb, length - pdu_length);
+	trailer = skb->tail - 8;
+
+	PDEBUG ("trailer = 0x%p\n", trailer);
+
+	/* zero padding space */
+	memset (pad, 0, length - pdu_length - 8);
+
+	/* add trailer */
+	*trailer++ = (unsigned char) 0;	/* UU  = 0 */
+	*trailer++ = (unsigned char) 0;	/* CPI = 0 */
+	*trailer++ = (unsigned char) (pdu_length >> 8);
+	*trailer++ = (unsigned char) (pdu_length & 0xff);
+	crc = ~crc32 (crc, skb->data, length - 4);
+	*trailer++ = (unsigned char) (crc >> 24);
+	*trailer++ = (unsigned char) (crc >> 16);
+	*trailer++ = (unsigned char) (crc >> 8);
+	*trailer++ = (unsigned char) (crc & 0xff);
+
+	/* update stats */
+	if (ctx->stats)
+		atomic_inc (&ctx->stats->tx);
+
+	PDEBUG ("atmsar_encode_aal5 return 0x%p (length %d)\n", skb, skb->len);
+	return skb;
+}
+
+/***********************
+ **
+ **  DECODE FUNCTIONS
+ **
+ ***********************/
+
+struct sk_buff *atmsar_decode_rawcell (struct atmsar_vcc_data *list, struct sk_buff *skb,
+				       struct atmsar_vcc_data **ctx)
+{
+	while (skb->len) {
+		unsigned char *cell = skb->data;
+		unsigned char *cell_payload;
+		struct atmsar_vcc_data *vcc = list;
+		unsigned long atmHeader =
+		    ((unsigned long) (cell[0]) << 24) | ((unsigned long) (cell[1]) << 16) |
+		    ((unsigned long) (cell[2]) << 8) | (cell[3] & 0xff);
+
+		PDEBUG ("atmsar_decode_rawcell (0x%p, 0x%p, 0x%p) called\n", list, skb, ctx);
+		PDEBUG ("atmsar_decode_rawcell skb->data %p, skb->tail %p\n", skb->data, skb->tail);
+
+		if (!list || !skb || !ctx)
+			return NULL;
+		if (!skb->data || !skb->tail)
+			return NULL;
+
+		/* here should the header CRC check be... */
+
+		/* look up correct vcc */
+		for (;
+		     vcc
+		     && ((vcc->atmHeader & ATM_HDR_VPVC_MASK) != (atmHeader & ATM_HDR_VPVC_MASK));
+		     vcc = vcc->next);
+
+		PDEBUG ("atmsar_decode_rawcell found vcc %p for packet on vp %d, vc %d\n", vcc,
+			(int) ((atmHeader & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT),
+			(int) ((atmHeader & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT));
+
+		if (vcc && (skb->len >= (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52))) {
+			cell_payload = cell + (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 5 : 4);
+
+			switch (vcc->type) {
+			case ATMSAR_TYPE_AAL0:
+				/* case ATMSAR_TYPE_AAL1: when we have a decode AAL1 function... */
+				{
+					struct sk_buff *tmp = dev_alloc_skb (vcc->mtu);
+
+					if (tmp) {
+						memcpy (tmp->tail, cell_payload, 48);
+						skb_put (tmp, 48);
+
+						if (vcc->stats)
+							atomic_inc (&vcc->stats->rx);
+
+						skb_pull (skb,
+							  (vcc->
+							   flags & ATMSAR_USE_53BYTE_CELL ? 53 :
+							   52));
+						PDEBUG
+						    ("atmsar_decode_rawcell returns ATMSAR_TYPE_AAL0 pdu 0x%p with length %d\n",
+						     tmp, tmp->len);
+						return tmp;
+					};
+				}
+				break;
+			case ATMSAR_TYPE_AAL1:
+			case ATMSAR_TYPE_AAL2:
+			case ATMSAR_TYPE_AAL34:
+				/* not supported */
+				break;
+			case ATMSAR_TYPE_AAL5:
+				if (!vcc->reasBuffer)
+					vcc->reasBuffer = dev_alloc_skb (vcc->mtu);
+
+				/* if alloc fails, we just drop the cell. it is possible that we can still
+				 * receive cells on other vcc's 
+				 */
+				if (vcc->reasBuffer) {
+					/* if (buffer overrun) discard received cells until now */
+					if ((vcc->reasBuffer->len) > (vcc->mtu - 48))
+						skb_trim (vcc->reasBuffer, 0);
+
+					/* copy data */
+					memcpy (vcc->reasBuffer->tail, cell_payload, 48);
+					skb_put (vcc->reasBuffer, 48);
+
+					/* check for end of buffer */
+					if (cell[3] & 0x2) {
+						struct sk_buff *tmp;
+
+						/* the aal5 buffer ends here, cut the buffer. */
+						/* buffer will always have at least one whole cell, so */
+						/* don't need to check return from skb_pull */
+						skb_pull (skb,
+							  (vcc->
+							   flags & ATMSAR_USE_53BYTE_CELL ? 53 :
+							   52));
+						*ctx = vcc;
+						tmp = vcc->reasBuffer;
+						vcc->reasBuffer = NULL;
+
+						PDEBUG
+						    ("atmsar_decode_rawcell returns ATMSAR_TYPE_AAL5 pdu 0x%p with length %d\n",
+						     tmp, tmp->len);
+						return tmp;
+					}
+				}
+				break;
+			};
+			/* flush the cell */
+			/* buffer will always contain at least one whole cell, so don't */
+			/* need to check return value from skb_pull */
+			skb_pull (skb, (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52));
+		} else {
+			/* If data is corrupt and skb doesn't hold a whole cell, flush the lot */
+			if (skb_pull (skb, (list->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52)) ==
+			    NULL) {
+				skb_trim (skb, 0);
+			}
+		}
+	}
+
+	return NULL;
+};
+
+struct sk_buff *atmsar_decode_aal5 (struct atmsar_vcc_data *ctx, struct sk_buff *skb)
+{
+	uint crc = 0xffffffff;
+	uint length, pdu_crc, pdu_length;
+
+	PDEBUG ("atmsar_decode_aal5 (0x%p, 0x%p) called\n", ctx, skb);
+
+	if (skb->len && (skb->len % 48))
+		return NULL;
+
+	length = (skb->tail[-6] << 8) + skb->tail[-5];
+	pdu_crc =
+	    (skb->tail[-4] << 24) + (skb->tail[-3] << 16) + (skb->tail[-2] << 8) + skb->tail[-1];
+	pdu_length = ((length + 47 + 8) / 48) * 48;
+
+	PDEBUG ("atmsar_decode_aal5: skb->len = %d, length = %d, pdu_crc = 0x%x, pdu_length = %d\n",
+		skb->len, length, pdu_crc, pdu_length);
+
+	/* is skb long enough ? */
+	if (skb->len < pdu_length) {
+		if (ctx->stats)
+			atomic_inc (&ctx->stats->rx_err);
+		return NULL;
+	}
+
+	/* is skb too long ? */
+	if (skb->len > pdu_length) {
+		PDEBUG ("atmsar_decode_aal5: Warning: readjusting illeagl size %d -> %d\n",
+			skb->len, pdu_length);
+		/* buffer is too long. we can try to recover
+		 * if we discard the first part of the skb.
+		 * the crc will decide whether this was ok
+		 */
+		skb_pull (skb, skb->len - pdu_length);
+	}
+
+	crc = ~crc32 (crc, skb->data, pdu_length - 4);
+
+	/* check crc */
+	if (pdu_crc != crc) {
+		PDEBUG ("atmsar_decode_aal5: crc check failed!\n");
+		if (ctx->stats)
+			atomic_inc (&ctx->stats->rx_err);
+		return NULL;
+	}
+
+	/* pdu is ok */
+	skb_trim (skb, length);
+
+	/* update stats */
+	if (ctx->stats)
+		atomic_inc (&ctx->stats->rx);
+
+	PDEBUG ("atmsar_decode_aal5 returns pdu 0x%p with length %d\n", skb, skb->len);
+	return skb;
+};
+
+
+static int start (void)
+{
+	return 0;
+}
+
+static void cleanup (void)
+{
+}
+
+module_init (start);
+module_exit (cleanup);
+
+EXPORT_SYMBOL (atmsar_open);
+EXPORT_SYMBOL (atmsar_close);
+EXPORT_SYMBOL (atmsar_encode_rawcell);
+EXPORT_SYMBOL (atmsar_encode_aal5);
+EXPORT_SYMBOL (atmsar_decode_rawcell);
+EXPORT_SYMBOL (atmsar_decode_aal5);
+EXPORT_SYMBOL (atmsar_alloc_tx);
+
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
diff -Nru a/drivers/usb/misc/atmsar.h b/drivers/usb/misc/atmsar.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/misc/atmsar.h	Wed Aug 21 15:45:22 2002
@@ -0,0 +1,81 @@
+/*
+ *
+ *  General SAR library for ATM devices.
+ *
+ *  Copyright (c) 2000, Johan Verrept
+ *
+ *  This code falls under the GNU General Public License, see COPYING for details.
+ *
+ */
+
+#ifndef _ATMSAR_H_
+#define _ATMSAR_H_
+
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/atmdev.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/atm.h>
+
+#define ATMSAR_USE_53BYTE_CELL  0x1L
+#define ATMSAR_SET_PTI          0x2L
+
+
+/* types */
+#define ATMSAR_TYPE_AAL0        ATM_AAL0
+#define ATMSAR_TYPE_AAL1        ATM_AAL1
+#define ATMSAR_TYPE_AAL2        ATM_AAL2
+#define ATMSAR_TYPE_AAL34       ATM_AAL34
+#define ATMSAR_TYPE_AAL5        ATM_AAL5
+
+
+/* default MTU's */
+#define ATMSAR_DEF_MTU_AAL0         48
+#define ATMSAR_DEF_MTU_AAL1         47
+#define ATMSAR_DEF_MTU_AAL2          0	/* not supported */
+#define ATMSAR_DEF_MTU_AAL34         0	/* not supported */
+#define ATMSAR_DEF_MTU_AAL5      65535	/* max mtu ..    */
+
+struct atmsar_vcc_data {
+	struct atmsar_vcc_data *next;
+
+	/* general atmsar flags, per connection */
+	int flags;
+	int type;
+
+	/* connection specific non-atmsar data */
+	struct sk_buff *(*alloc_tx) (struct atm_vcc * vcc, unsigned int size);
+	struct atm_vcc *vcc;
+	struct k_atm_aal_stats *stats;
+	unsigned short mtu;	/* max is actually  65k for AAL5... */
+
+	/* cell data */
+	unsigned int vp;
+	unsigned int vc;
+	unsigned char gfc;
+	unsigned char pti;
+	unsigned int headerFlags;
+	unsigned long atmHeader;
+
+	/* raw cell reassembly */
+	struct sk_buff *reasBuffer;
+};
+
+
+extern struct atmsar_vcc_data *atmsar_open (struct atmsar_vcc_data **list, struct atm_vcc *vcc,
+					    uint type, ushort vpi, ushort vci, unchar pti,
+					    unchar gfc, uint flags);
+extern void atmsar_close (struct atmsar_vcc_data **list, struct atmsar_vcc_data *vcc);
+
+extern struct sk_buff *atmsar_encode_rawcell (struct atmsar_vcc_data *ctx, struct sk_buff *skb);
+extern struct sk_buff *atmsar_encode_aal5 (struct atmsar_vcc_data *ctx, struct sk_buff *skb);
+
+struct sk_buff *atmsar_decode_rawcell (struct atmsar_vcc_data *list, struct sk_buff *skb,
+				       struct atmsar_vcc_data **ctx);
+struct sk_buff *atmsar_decode_aal5 (struct atmsar_vcc_data *ctx, struct sk_buff *skb);
+
+struct sk_buff *atmsar_alloc_tx (struct atmsar_vcc_data *vcc, unsigned int size);
+
+#endif				/* _ATMSAR_H_ */
diff -Nru a/drivers/usb/misc/speedtouch.c b/drivers/usb/misc/speedtouch.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/misc/speedtouch.c	Wed Aug 21 15:45:22 2002
@@ -0,0 +1,1068 @@
+/*
+ *  Driver Module for Alcatel SpeedTouch USB xDSL modem
+ *  Copyright 2001, Alcatel
+ *  Written by Johan Verrept (Johan.Verrept@advalvas.be)
+ *
+
+1.5A:   - Version for inclusion in 2.5 series kernel
+        - Modifcations by Richard Purdie (rpurdie@rpsys.net)
+        - made compatible with kernel 2.5.6 onwards by changing
+	  udsl_usb_send_data_context->urb changed to a pointer 
+	  and adding code to alloc and free it
+        - remove_wait_queue() added to udsl_atm_processqueue_thread() 
+
+1.5:	- fixed memory leak when atmsar_decode_aal5 returned NULL.
+	 (reported by stephen.robinson@zen.co.uk)
+
+1.4:	- changed the spin_lock() under interrupt to spin_lock_irqsave()
+	- unlink all active send urbs of a vcc that is being closed.
+
+1.3.1:	- added the version number
+
+1.3:	- Added multiple send urb support
+	- fixed memory leak and vcc->tx_inuse starvation bug
+	  when not enough memory left in vcc.
+
+1.2:	- Fixed race condition in udsl_usb_send_data()
+1.1:	- Turned off packet debugging
+ 
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include "atmsar.h"
+
+const char *udsl_version = "1.5A";
+
+/*
+#define DEBUG 1
+#define DEBUG_PACKET 1
+*/
+
+#ifdef DEBUG
+#define PDEBUG(arg...)  printk(KERN_DEBUG "SpeedTouch USB: " arg)
+#else
+#define PDEBUG(arg...)
+#endif
+
+
+#ifdef DEBUG_PACKET
+#define PACKETDEBUG(arg...) udsl_print_packet ( arg )
+#else
+#define PACKETDEBUG(arg...)
+#endif
+
+#define DRIVER_AUTHOR	"Johan Verrept, Johan.Verrept@advalvas.be"
+#define DRIVER_DESC	"Driver for the Alcatel Speed Touch USB ADSL modem"
+#define DRIVER_VERSION	"1.5A"
+
+#define SPEEDTOUCH_VENDORID		0x06b9
+#define SPEEDTOUCH_PRODUCTID		0x4061
+
+#define MAX_UDSL			1
+#define UDSL_OBUF_SIZE			32768
+#define UDSL_MINOR			48
+#define UDSL_NUMBER_RCV_URBS		1
+#define UDSL_NUMBER_SND_URBS		1
+#define UDSL_RECEIVE_BUFFER_SIZE	64*53
+/* 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
+#define UDSL_SEND_CONTEXTS		8
+
+#define UDSL_IOCTL_START		1
+#define UDSL_IOCTL_STOP			2
+
+/* endpoint declarations */
+
+#define UDSL_ENDPOINT_DATA_OUT		0x07
+#define UDSL_ENDPOINT_DATA_IN		0x87
+
+/* usb_device_id struct */
+
+static struct usb_device_id udsl_usb_ids[] = {
+	{USB_DEVICE (SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)},
+	{}			/* list terminator */
+};
+
+/* not exporting this prevents the depmod from generating the map that causes the modules to be isnserted as driver.
+ * we do not want this, we want the script run.
+MODULE_DEVICE_TABLE ( usb, udsl_usb_ids);
+*/
+/* context declarations */
+
+struct udsl_data_ctx {
+	struct sk_buff *skb;
+	struct urb *urb;
+	struct udsl_instance_data *instance;
+};
+
+struct udsl_usb_send_data_context {
+	struct urb *urb;
+	struct sk_buff *skb;
+	struct atm_vcc *vcc;
+	struct udsl_instance_data *instance;
+};
+
+/*
+ * UDSL main driver data
+ */
+
+struct udsl_instance_data {
+	int minor;
+
+	/* usb device part */
+	struct usb_device *usb_dev;
+	struct udsl_data_ctx *rcvbufs;
+	struct sk_buff_head sndqueue;
+	spinlock_t sndqlock;
+	struct udsl_usb_send_data_context send_ctx[UDSL_NUMBER_SND_URBS];
+	int data_started;
+
+	/* atm device part */
+	struct atm_dev *atm_dev;
+	struct sk_buff_head recvqueue;
+	spinlock_t recvqlock;
+
+	struct atmsar_vcc_data *atmsar_vcc_list;
+};
+
+struct udsl_instance_data *minor_data[MAX_UDSL];
+
+static const char udsl_driver_name[] = "Alcatel SpeedTouch USB";
+
+/* data thread */
+static int datapid = 0;
+DECLARE_WAIT_QUEUE_HEAD (udsl_wqh);
+
+#ifdef DEBUG_PACKET
+int udsl_print_packet (const unsigned char *data, int len);
+#endif
+
+/*
+ * atm driver prototypes and stuctures
+ */
+
+static int udsl_atm_open (struct atm_vcc *vcc, short vpi, int vci);
+static void udsl_atm_close (struct atm_vcc *vcc);
+static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void *arg);
+static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb);
+int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t * pos, char *page);
+void udsl_atm_processqueue (struct udsl_instance_data *instance);
+
+static struct atmdev_ops udsl_atm_devops = {
+	open:udsl_atm_open,
+	close:udsl_atm_close,
+	ioctl:udsl_atm_ioctl,
+	send:udsl_atm_send,
+	proc_read:udsl_atm_proc_read,
+};
+
+struct udsl_atm_dev_data {
+	struct atmsar_vcc_data *atmsar_vcc;
+};
+
+/*
+ * usb driver prototypes and structures
+ */
+static void *udsl_usb_probe (struct usb_device *dev, unsigned int ifnum,
+			     const struct usb_device_id *id);
+static void udsl_usb_disconnect (struct usb_device *dev, void *ptr);
+int udsl_usb_send_data (struct udsl_instance_data *instance, struct atm_vcc *vcc,
+			struct sk_buff *skb);
+static int udsl_usb_ioctl (struct usb_device *hub, unsigned int code, void *user_data);
+static int udsl_usb_cancelsends (struct udsl_instance_data *instance, struct atm_vcc *vcc);
+
+static struct usb_driver udsl_usb_driver = {
+	name:udsl_driver_name,
+	probe:udsl_usb_probe,
+	disconnect:udsl_usb_disconnect,
+	ioctl:udsl_usb_ioctl,
+	id_table:udsl_usb_ids,
+};
+
+/************
+**   ATM   **
+************/
+
+/***************************************************************************
+*
+* init functions
+*
+****************************************************************************/
+
+struct atm_dev *udsl_atm_startdevice (struct udsl_instance_data *instance,
+				      struct atmdev_ops *devops)
+{
+	MOD_INC_USE_COUNT;
+	instance->atm_dev = atm_dev_register (udsl_driver_name, devops, -1, 0);
+	instance->atm_dev->dev_data = instance;
+	instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
+	instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX;
+	instance->atm_dev->signal = ATM_PHY_SIG_LOST;
+
+	skb_queue_head_init (&instance->recvqueue);
+
+	/* tmp init atm device, set to 128kbit */
+	instance->atm_dev->link_rate = 128 * 1000 / 424;
+
+	return instance->atm_dev;
+}
+
+void udsl_atm_stopdevice (struct udsl_instance_data *instance)
+{
+	struct atm_vcc *walk;
+	struct sk_buff *skb;
+	struct atm_dev *atm_dev;
+	unsigned long iflags;
+	if (!instance->atm_dev)
+		return;
+
+	atm_dev = instance->atm_dev;
+
+	/* clean queue */
+	spin_lock_irqsave (&instance->recvqlock, iflags);
+	while (!skb_queue_empty (&instance->recvqueue)) {
+		skb = skb_dequeue (&instance->recvqueue);
+		dev_kfree_skb (skb);
+	};
+	spin_unlock_irqrestore (&instance->recvqlock, iflags);
+
+	atm_dev->signal = ATM_PHY_SIG_LOST;
+	walk = atm_dev->vccs;
+	shutdown_atm_dev (atm_dev);
+
+	for (; walk; walk = walk->next)
+		wake_up (&walk->sleep);
+
+	instance->atm_dev = NULL;
+	MOD_DEC_USE_COUNT;
+}
+
+void udsl_atm_set_mac (struct udsl_instance_data *instance, const char mac[6])
+{
+	if (!instance->atm_dev)
+		return;
+
+	memcpy (instance->atm_dev->esi, mac, 6);
+}
+
+/***************************************************************************
+*
+* ATM helper functions
+*
+****************************************************************************/
+struct sk_buff *udsl_atm_alloc_tx (struct atm_vcc *vcc, unsigned int size)
+{
+	struct atmsar_vcc_data *atmsar_vcc =
+	    ((struct udsl_atm_dev_data *) vcc->dev_data)->atmsar_vcc;
+	if (atmsar_vcc)
+		return atmsar_alloc_tx (atmsar_vcc, size);
+
+	printk (KERN_INFO
+		"SpeedTouch USB: udsl_atm_alloc_tx could not find correct alloc_tx function !\n");
+	return NULL;
+}
+
+int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t * pos, char *page)
+{
+	struct udsl_instance_data *instance = (struct udsl_instance_data *) atm_dev->dev_data;
+	int left = *pos;
+
+	if (!left--)
+		return sprintf (page, "Speed Touch USB:%d (%02x:%02x:%02x:%02x:%02x:%02x)\n",
+				instance->minor, atm_dev->esi[0], atm_dev->esi[1], atm_dev->esi[2],
+				atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]);
+
+	if (!left--)
+		return sprintf (page, "AAL0: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
+				atomic_read (&atm_dev->stats.aal0.tx),
+				atomic_read (&atm_dev->stats.aal0.tx_err),
+				atomic_read (&atm_dev->stats.aal0.rx),
+				atomic_read (&atm_dev->stats.aal0.rx_err),
+				atomic_read (&atm_dev->stats.aal0.rx_drop));
+
+	if (!left--)
+		return sprintf (page, "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
+				atomic_read (&atm_dev->stats.aal5.tx),
+				atomic_read (&atm_dev->stats.aal5.tx_err),
+				atomic_read (&atm_dev->stats.aal5.rx),
+				atomic_read (&atm_dev->stats.aal5.rx_err),
+				atomic_read (&atm_dev->stats.aal5.rx_drop));
+
+	return 0;
+}
+
+/***************************************************************************
+*
+* ATM DATA functions
+*
+****************************************************************************/
+int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb)
+{
+	struct udsl_atm_dev_data *dev_data = (struct udsl_atm_dev_data *) vcc->dev_data;
+	struct udsl_instance_data *instance = (struct udsl_instance_data *) vcc->dev->dev_data;
+	struct sk_buff *new = NULL;
+	int err;
+
+	PDEBUG ("udsl_atm_send called\n");
+
+	if (!dev_data)
+		return -EINVAL;
+
+	switch (vcc->qos.aal) {
+	case ATM_AAL5:
+		new = atmsar_encode_aal5 (dev_data->atmsar_vcc, skb);
+		if (!new)
+			goto nomem;
+		if (new != skb) {
+			vcc->pop (vcc, skb);
+			skb = new;
+		}
+		new = atmsar_encode_rawcell (dev_data->atmsar_vcc, skb);
+		if (!new)
+			goto nomem;
+		if (new != skb) {
+			vcc->pop (vcc, skb);
+			skb = new;
+		}
+		err = udsl_usb_send_data (instance, vcc, skb);
+		PDEBUG ("udsl_atm_send successfull (%d)\n", err);
+		return err;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	PDEBUG ("udsl_atm_send unsuccessfull\n");
+	return 0;
+      nomem:
+	vcc->pop (vcc, skb);
+	return -ENOMEM;
+};
+
+
+void udsl_atm_processqueue (struct udsl_instance_data *instance)
+{
+	struct atmsar_vcc_data *atmsar_vcc = NULL;
+	struct sk_buff *new = NULL, *skb = NULL, *tmp = NULL;
+	unsigned long iflags;
+
+	/* quick check */
+	spin_lock_irqsave (&instance->recvqlock, iflags);
+	if (skb_queue_empty (&instance->recvqueue)) {
+		spin_unlock_irqrestore (&instance->recvqlock, iflags);
+		return;
+	}
+	PDEBUG ("udsl_atm_processqueue entered\n");
+
+	while (!skb_queue_empty (&instance->recvqueue)) {
+		skb = skb_dequeue (&instance->recvqueue);
+
+		spin_unlock_irqrestore (&instance->recvqlock, iflags);
+
+		PDEBUG ("skb = %p, skb->len = %d\n", skb, skb->len);
+		PACKETDEBUG (skb->data, skb->len);
+
+		while ((new =
+			atmsar_decode_rawcell (instance->atmsar_vcc_list, skb,
+					       &atmsar_vcc)) != NULL) {
+			PDEBUG ("(after cell processing)skb->len = %d\n", new->len);
+			switch (atmsar_vcc->type) {
+			case ATMSAR_TYPE_AAL5:
+				tmp = new;
+				new = atmsar_decode_aal5 (atmsar_vcc, new);
+
+				/* we can't send NULL skbs upstream, the ATM layer would try to close the vcc... */
+				if (new) {
+					PDEBUG ("(after aal5 decap) skb->len = %d\n", new->len);
+					if (new->len && atm_charge (atmsar_vcc->vcc, new->truesize)) {
+						PACKETDEBUG (new->data, new->len);
+						atmsar_vcc->vcc->push (atmsar_vcc->vcc, new);
+					} else {
+						PDEBUG
+						    ("dropping incoming packet : rx_inuse = %d, vcc->sk->rcvbuf = %d, skb->true_size = %d\n",
+						     atomic_read (&atmsar_vcc->vcc->rx_inuse),
+						     atmsar_vcc->vcc->sk->rcvbuf, new->truesize);
+						dev_kfree_skb (new);
+					}
+				} else {
+					PDEBUG ("atmsar_decode_aal5 returned NULL!\n");
+					dev_kfree_skb (tmp);
+				}
+				break;
+			default:
+				/* not supported. we delete the skb. */
+				printk (KERN_INFO
+					"SpeedTouch USB: illegal vcc type. Dropping packet.\n");
+				dev_kfree_skb (new);
+				break;
+			}
+		};
+		dev_kfree_skb (skb);
+		spin_lock_irqsave (&instance->recvqlock, iflags);
+	};
+
+	spin_unlock_irqrestore (&instance->recvqlock, iflags);
+	PDEBUG ("udsl_atm_processqueue successfull\n");
+}
+
+int udsl_atm_processqueue_thread (void *data)
+{
+	int i = 0;
+	DECLARE_WAITQUEUE (wait, current);
+
+	lock_kernel ();
+	daemonize ();
+
+	/* Setup a nice name */
+	strcpy (current->comm, "kSpeedSARd");
+
+	add_wait_queue (&udsl_wqh, &wait);
+
+	for (;;) {
+		interruptible_sleep_on (&udsl_wqh);
+		if (signal_pending (current))
+			break;
+		PDEBUG ("SpeedSARd awoke\n");
+		for (i = 0; i < MAX_UDSL; i++)
+			if (minor_data[i])
+				udsl_atm_processqueue (minor_data[i]);
+	};
+
+	remove_wait_queue (&udsl_wqh, &wait);
+	datapid = 0;
+	PDEBUG ("SpeedSARd is exiting\n");
+	return 0;
+}
+
+
+void udsl_atm_sar_start (void)
+{
+	datapid = kernel_thread (udsl_atm_processqueue_thread, (void *) NULL,
+				 CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+}
+
+void udsl_atm_sar_stop (void)
+{
+	int ret;
+	/* Kill the thread */
+	ret = kill_proc (datapid, SIGTERM, 1);
+	if (!ret) {
+		/* Wait 10 seconds */
+		int count = 10 * 100;
+
+		while (datapid && --count) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout (1);
+		}
+
+		if (!count)
+			err ("giving up on killing SpeedSAR thread.");
+	}
+}
+
+/***************************************************************************
+*
+* SAR driver entries
+*
+****************************************************************************/
+int udsl_atm_open (struct atm_vcc *vcc, short vpi, int vci)
+{
+	struct udsl_atm_dev_data *dev_data;
+	struct udsl_instance_data *instance = (struct udsl_instance_data *) vcc->dev->dev_data;
+
+	PDEBUG ("udsl_atm_open called\n");
+
+	/* at the moment only AAL5 support */
+	if (vcc->qos.aal != ATM_AAL5)
+		return -EINVAL;
+
+	MOD_INC_USE_COUNT;
+	dev_data =
+	    (struct udsl_atm_dev_data *) kmalloc (sizeof (struct udsl_atm_dev_data), GFP_KERNEL);
+	if (!dev_data)
+		return -ENOMEM;
+
+	dev_data->atmsar_vcc =
+	    atmsar_open (&(instance->atmsar_vcc_list), vcc, ATMSAR_TYPE_AAL5, vpi, vci, 0, 0,
+			 ATMSAR_USE_53BYTE_CELL | ATMSAR_SET_PTI);
+	if (!dev_data->atmsar_vcc) {
+		kfree (dev_data);
+		return -ENOMEM;	/* this is the only reason atmsar_open can fail... */
+	}
+
+	vcc->vpi = vpi;
+	vcc->vci = vci;
+	set_bit (ATM_VF_ADDR, &vcc->flags);
+	set_bit (ATM_VF_PARTIAL, &vcc->flags);
+	set_bit (ATM_VF_READY, &vcc->flags);
+	vcc->dev_data = dev_data;
+	vcc->alloc_tx = udsl_atm_alloc_tx;
+
+	dev_data->atmsar_vcc->mtu = UDSL_MAX_AAL5_MRU;
+
+	PDEBUG ("udsl_atm_open successfull\n");
+	return 0;
+}
+
+void udsl_atm_close (struct atm_vcc *vcc)
+{
+	struct udsl_atm_dev_data *dev_data = (struct udsl_atm_dev_data *) vcc->dev_data;
+	struct udsl_instance_data *instance = (struct udsl_instance_data *) vcc->dev->dev_data;
+
+	PDEBUG ("udsl_atm_close called\n");
+
+	/* freeing resources */
+	/* cancel all sends on this vcc */
+	udsl_usb_cancelsends (instance, vcc);
+
+	atmsar_close (&(instance->atmsar_vcc_list), dev_data->atmsar_vcc);
+	kfree (dev_data);
+	vcc->dev_data = NULL;
+	clear_bit (ATM_VF_PARTIAL, &vcc->flags);
+
+	/* freeing address */
+	vcc->vpi = ATM_VPI_UNSPEC;
+	vcc->vci = ATM_VCI_UNSPEC;
+	clear_bit (ATM_VF_ADDR, &vcc->flags);
+
+	MOD_DEC_USE_COUNT;
+
+	PDEBUG ("udsl_atm_close successfull\n");
+	return;
+};
+
+int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void *arg)
+{
+	switch (cmd) {
+	case ATM_QUERYLOOP:
+		return put_user (ATM_LM_NONE, (int *) arg) ? -EFAULT : 0;
+	default:
+		return -ENOIOCTLCMD;
+	}
+};
+
+
+/************
+**   USB   **
+************/
+
+/***************************************************************************
+*
+* usb data functions
+*
+****************************************************************************/
+
+struct udsl_cb {
+	struct atm_vcc *vcc;
+};
+
+static void udsl_usb_send_data_complete (struct urb *urb)
+{
+	struct udsl_usb_send_data_context *ctx = (struct udsl_usb_send_data_context *) urb->context;
+	struct udsl_instance_data *instance = ctx->instance;
+	int err;
+	unsigned long flags;
+
+	PDEBUG ("udsl_usb_send_data_completion (vcc = %p, skb = %p, status %d)\n", ctx->vcc,
+		ctx->skb, urb->status);
+
+	ctx->vcc->pop (ctx->vcc, ctx->skb);
+	ctx->skb = NULL;
+
+	spin_lock_irqsave (&instance->sndqlock, flags);
+	if (skb_queue_empty (&instance->sndqueue)) {
+		spin_unlock_irqrestore (&instance->sndqlock, flags);
+		return;
+	}
+	/* submit next skb */
+	ctx->skb = skb_dequeue (&(instance->sndqueue));
+	ctx->vcc = ((struct udsl_cb *) (ctx->skb->cb))->vcc;
+	spin_unlock_irqrestore (&instance->sndqlock, flags);
+	FILL_BULK_URB (urb,
+		       instance->usb_dev,
+		       usb_sndbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_OUT),
+		       (unsigned char *) ctx->skb->data,
+		       ctx->skb->len, (usb_complete_t) udsl_usb_send_data_complete, ctx);
+
+	err = usb_submit_urb (urb, GFP_KERNEL);
+
+	PDEBUG ("udsl_usb_send_data_completion (send packet %p with length %d), retval = %d\n",
+		ctx->skb, ctx->skb->len, err);
+}
+
+int udsl_usb_cancelsends (struct udsl_instance_data *instance, struct atm_vcc *vcc)
+{
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave (&instance->sndqlock, flags);
+	for (i = 0; i < UDSL_NUMBER_SND_URBS; i++) {
+		if (!instance->send_ctx[i].skb)
+			continue;
+		if (instance->send_ctx[i].vcc == vcc) {
+			usb_unlink_urb (instance->send_ctx[i].urb);
+			usb_free_urb (instance->send_ctx[i].urb);
+			instance->send_ctx[i].vcc->pop (instance->send_ctx[i].vcc,
+							instance->send_ctx[i].skb);
+			instance->send_ctx[i].skb = NULL;
+		}
+	}
+	spin_unlock_irqrestore (&instance->sndqlock, flags);
+
+	return 0;
+}
+
+/**** send ******/
+int udsl_usb_send_data (struct udsl_instance_data *instance, struct atm_vcc *vcc,
+			struct sk_buff *skb)
+{
+	int err, i;
+	struct urb *urb;
+	unsigned long flags;
+
+	PDEBUG ("udsl_usb_send_data entered, sending packet %p with length %d\n", skb, skb->len);
+
+	if (!instance->data_started)
+		return -EAGAIN;
+
+	PACKETDEBUG (skb->data, skb->len);
+
+	spin_lock_irqsave (&instance->sndqlock, flags);
+	((struct udsl_cb *) skb->cb)->vcc = vcc;
+
+	/* we are already queueing */
+	if (!skb_queue_empty (&instance->sndqueue)) {
+		skb_queue_tail (&instance->sndqueue, skb);
+		spin_unlock_irqrestore (&instance->sndqlock, flags);
+		PDEBUG ("udsl_usb_send_data: already queing, skb (0x%p) queued\n", skb);
+		return 0;
+	}
+
+	for (i = 0; i < UDSL_NUMBER_SND_URBS; i++)
+		if (instance->send_ctx[i].skb == NULL)
+			break;
+
+	/* we must start queueing */
+	if (i == UDSL_NUMBER_SND_URBS) {
+		skb_queue_tail (&instance->sndqueue, skb);
+		spin_unlock_irqrestore (&instance->sndqlock, flags);
+		PDEBUG ("udsl_usb_send_data: skb (0x%p) queued\n", skb);
+		return 0;
+	};
+
+	/* init context */
+	urb = instance->send_ctx[i].urb;
+	instance->send_ctx[i].skb = skb;
+	instance->send_ctx[i].vcc = vcc;
+	instance->send_ctx[i].instance = instance;
+
+	spin_unlock_irqrestore (&instance->sndqlock, flags);
+
+	/* submit packet */
+	FILL_BULK_URB (urb,
+		       instance->usb_dev,
+		       usb_sndbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_OUT),
+		       (unsigned char *) skb->data,
+		       skb->len,
+		       (usb_complete_t) udsl_usb_send_data_complete, &(instance->send_ctx[i])
+	    );
+
+	err = usb_submit_urb (urb, GFP_KERNEL);
+
+	if (err != 0)
+		skb_unlink (skb);
+
+	PDEBUG ("udsl_usb_send_data done (retval = %d)\n", err);
+	return err;
+}
+
+/********* receive *******/
+void udsl_usb_data_receive (struct urb *urb)
+{
+	struct udsl_data_ctx *ctx;
+	struct udsl_instance_data *instance;
+	unsigned long flags;
+
+	if (!urb)
+		return;
+
+	PDEBUG ("udsl_usb_receive_data entered, got packet %p with length %d an status %d\n", urb,
+		urb->actual_length, urb->status);
+
+	ctx = (struct udsl_data_ctx *) urb->context;
+	if (!ctx || !ctx->skb)
+		return;
+
+	instance = ctx->instance;
+
+	switch (urb->status) {
+	case 0:
+		PDEBUG ("udsl_usb_data_receive: processing urb with ctx %p, urb %p (%p), skb %p\n",
+			ctx, ctx ? ctx->urb : NULL, urb, ctx ? ctx->skb : NULL);
+		/* update the skb structure */
+		skb_put (ctx->skb, urb->actual_length);
+
+		/* queue the skb for processing and wake the SAR */
+		spin_lock_irqsave (&instance->recvqlock, flags);
+		skb_queue_tail (&instance->recvqueue, ctx->skb);
+		spin_unlock_irqrestore (&instance->recvqlock, flags);
+		wake_up (&udsl_wqh);
+		/* get a new skb */
+		ctx->skb = dev_alloc_skb (UDSL_RECEIVE_BUFFER_SIZE);
+		if (!ctx->skb) {
+			PDEBUG ("No skb, loosing urb.\n");
+			usb_free_urb (ctx->urb);
+			ctx->urb = NULL;
+			return;
+		}
+		break;
+	case -EPIPE:		/* stall or babble */
+		usb_clear_halt (urb->dev, usb_rcvbulkpipe (urb->dev, UDSL_ENDPOINT_DATA_IN));
+		break;
+	case -ENOENT:		/* buffer was unlinked */
+	case -EILSEQ:		/* unplug or timeout */
+	case -ETIMEDOUT:	/* unplug or timeout */
+		/* 
+		 * we don't do anything here and we don't resubmit
+		 */
+		return;
+	}
+
+	FILL_BULK_URB (urb,
+		       instance->usb_dev,
+		       usb_rcvbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_IN),
+		       (unsigned char *) ctx->skb->data,
+		       UDSL_RECEIVE_BUFFER_SIZE, (usb_complete_t) udsl_usb_data_receive, ctx);
+	usb_submit_urb (urb, GFP_KERNEL);
+	return;
+};
+
+int udsl_usb_data_init (struct udsl_instance_data *instance)
+{
+	int i, succes;
+
+	if (instance->data_started)
+		return 1;
+
+	/* set alternate setting 1 on interface 1 */
+	usb_set_interface (instance->usb_dev, 1, 2);
+
+	PDEBUG ("max packet size on endpoint %d is %d\n", UDSL_ENDPOINT_DATA_OUT,
+		usb_maxpacket (instance->usb_dev,
+			       usb_sndbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_OUT), 0));
+
+	instance->rcvbufs =
+	    (struct udsl_data_ctx *) kmalloc (sizeof (struct udsl_data_ctx) * UDSL_NUMBER_RCV_URBS,
+					      GFP_KERNEL);
+	if (!instance->rcvbufs)
+		return -ENOMEM;
+
+	memset (instance->rcvbufs, 0, sizeof (struct udsl_data_ctx) * UDSL_NUMBER_RCV_URBS);
+
+	skb_queue_head_init (&instance->sndqueue);
+
+	for (i = 0, succes = 0; i < UDSL_NUMBER_RCV_URBS; i++) {
+		struct udsl_data_ctx *ctx = &(instance->rcvbufs[i]);
+
+		ctx->urb = NULL;
+		ctx->skb = dev_alloc_skb (UDSL_RECEIVE_BUFFER_SIZE);
+		if (!ctx->skb)
+			continue;
+
+		ctx->urb = usb_alloc_urb (0, GFP_KERNEL);
+		if (!ctx->urb) {
+			kfree_skb (ctx->skb);
+			ctx->skb = NULL;
+			break;
+		};
+
+		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,
+			       (usb_complete_t) udsl_usb_data_receive, ctx);
+
+
+		ctx->instance = instance;
+
+		PDEBUG ("udsl_usb_data_init: usb with skb->truesize = %d (Asked for %d)\n",
+			ctx->skb->truesize, UDSL_RECEIVE_BUFFER_SIZE);
+
+		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++) {
+		instance->send_ctx[i].urb = usb_alloc_urb (0, GFP_KERNEL);
+		PDEBUG ("udsl_usb_data_init: send urb allocted address %p\n",
+			instance->send_ctx[i].urb);
+		if (instance->send_ctx[i].urb)
+			succes++;
+	}
+
+	PDEBUG ("udsl_usb_data_init %d urb%s queued for send\n", succes, (succes != 1) ? "s" : "");
+
+	instance->data_started = 1;
+	instance->atm_dev->signal = ATM_PHY_SIG_FOUND;
+
+	return 0;
+}
+
+int udsl_usb_data_exit (struct udsl_instance_data *instance)
+{
+	int i;
+
+	if (!instance->data_started)
+		return 0;
+
+	if (!instance->rcvbufs)
+		return 0;
+
+	/* destroy urbs */
+	for (i = 0; i < UDSL_NUMBER_RCV_URBS; i++) {
+		struct udsl_data_ctx *ctx = &(instance->rcvbufs[i]);
+
+		if ((!ctx->urb) || (!ctx->skb))
+			continue;
+
+		if (ctx->urb->status == -EINPROGRESS)
+			usb_unlink_urb (ctx->urb);
+
+		usb_free_urb (ctx->urb);
+		kfree_skb (ctx->skb);
+		ctx->skb = NULL;
+	}
+
+	for (i = 0; i < UDSL_NUMBER_SND_URBS; i++) {
+		struct udsl_usb_send_data_context *ctx = &(instance->send_ctx[i]);
+
+		if (ctx->urb->status == -EINPROGRESS)
+			usb_unlink_urb (ctx->urb);
+
+		if (ctx->skb)
+			ctx->vcc->pop (ctx->vcc, ctx->skb);
+		ctx->skb = NULL;
+
+		usb_free_urb (ctx->urb);
+
+	}
+
+	/* free receive contexts */
+	kfree (instance->rcvbufs);
+	instance->rcvbufs = NULL;
+
+	instance->data_started = 0;
+	instance->atm_dev->signal = ATM_PHY_SIG_LOST;
+
+	return 0;
+};
+
+
+/***************************************************************************
+*
+* usb driver entries
+*
+****************************************************************************/
+#define hex2int(c) ( (c >= '0')&&(c <= '9') ?  (c - '0') : ((c & 0xf)+9) )
+
+
+static int udsl_usb_ioctl (struct usb_device *dev, unsigned int code, void *user_data)
+{
+	struct udsl_instance_data *instance;
+	int i;
+
+	for (i = 0; i < MAX_UDSL; i++)
+		if (minor_data[i] && (minor_data[i]->usb_dev == dev))
+			break;
+
+	if (i == MAX_UDSL)
+		return -EINVAL;
+
+	instance = minor_data[i];
+
+	switch (code) {
+	case UDSL_IOCTL_START:
+		return udsl_usb_data_init (instance);
+		break;
+	case UDSL_IOCTL_STOP:
+		return udsl_usb_data_exit (instance);
+		break;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+void *udsl_usb_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id)
+{
+	int i;
+	unsigned char mac[6];
+	unsigned char mac_str[13];
+	struct udsl_instance_data *instance = NULL;
+
+	PDEBUG ("Trying device with Vendor=0x%x, Product=0x%x, ifnum %d\n",
+		dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
+
+	if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) ||
+	    (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) ||
+	    (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1))
+		return NULL;
+
+	MOD_INC_USE_COUNT;
+
+	for (i = 0; i < MAX_UDSL; i++)
+		if (minor_data[i] == NULL)
+			break;
+
+	if (i >= MAX_UDSL) {
+		printk (KERN_INFO "No minor table space available for SpeedTouch USB\n");
+		return NULL;
+	};
+
+	PDEBUG ("Device Accepted, assigning minor %d\n", i);
+
+	/* device init */
+	instance = kmalloc (sizeof (struct udsl_instance_data), GFP_KERNEL);
+	if (!instance) {
+		PDEBUG ("No memory for Instance data!\n");
+		return NULL;
+	}
+
+	/* initialize structure */
+	memset (instance, 0, sizeof (struct udsl_instance_data));
+	instance->minor = i;
+	instance->usb_dev = dev;
+	instance->rcvbufs = NULL;
+	spin_lock_init (&instance->sndqlock);
+	spin_lock_init (&instance->recvqlock);
+
+	udsl_atm_startdevice (instance, &udsl_atm_devops);
+
+	/* set MAC address, it is stored in the serial number */
+	usb_string (instance->usb_dev, instance->usb_dev->descriptor.iSerialNumber, mac_str, 13);
+	for (i = 0; i < 6; i++)
+		mac[i] = (hex2int (mac_str[i * 2]) * 16) + (hex2int (mac_str[i * 2 + 1]));
+
+	PDEBUG ("MAC is %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4],
+		mac[5]);
+	udsl_atm_set_mac (instance, mac);
+
+	minor_data[instance->minor] = instance;
+
+	return instance;
+}
+
+void udsl_usb_disconnect (struct usb_device *dev, void *ptr)
+{
+	struct udsl_instance_data *instance = (struct udsl_instance_data *) ptr;
+	int i = instance->minor;
+
+	/* unlinking receive buffers */
+	udsl_usb_data_exit (instance);
+
+	/* removing atm device */
+	if (instance->atm_dev)
+		udsl_atm_stopdevice (instance);
+
+	PDEBUG ("disconnecting minor %d\n", i);
+
+	while (MOD_IN_USE > 1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout (1);
+	}
+
+	kfree (instance);
+	minor_data[i] = NULL;
+
+	MOD_DEC_USE_COUNT;
+}
+
+/***************************************************************************
+*
+* Driver Init
+*
+****************************************************************************/
+
+int udsl_usb_init (void)
+{
+	int i;
+
+	PDEBUG ("Initializing SpeedTouch Driver Version %s\n", udsl_version);
+
+	for (i = 0; i < MAX_UDSL; i++)
+		minor_data[i] = NULL;
+
+	init_waitqueue_head (&udsl_wqh);
+	udsl_atm_sar_start ();
+
+	return usb_register (&udsl_usb_driver);
+}
+
+int udsl_usb_cleanup (void)
+{
+	/* killing threads */
+	udsl_atm_sar_stop ();
+	usb_deregister (&udsl_usb_driver);
+	return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+	return udsl_usb_init ();
+}
+
+int cleanup_module (void)
+{
+	return udsl_usb_cleanup ();
+}
+#endif
+
+#ifdef DEBUG_PACKET
+/*******************************************************************************
+*
+* Debug 
+*
+*******************************************************************************/
+
+int udsl_print_packet (const unsigned char *data, int len)
+{
+	unsigned char buffer[256];
+	int i = 0, j = 0;
+
+	for (i = 0; i < len;) {
+		buffer[0] = '\0';
+		sprintf (buffer, "%.3d :", i);
+		for (j = 0; (j < 16) && (i < len); j++, i++) {
+			sprintf (buffer, "%s %2.2x", buffer, data[i]);
+		}
+		PDEBUG ("%s\n", buffer);
+	}
+	return i;
+};
+
+#endif				/* PACKETDEBUG */
+
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
