]> git.neil.brown.name Git - history.git/commitdiff
USB: added the speedtouch usb driver.
authorGreg Kroah-Hartman <greg@kroah.com>
Wed, 21 Aug 2002 07:54:14 +0000 (00:54 -0700)
committerGreg Kroah-Hartman <greg@kroah.com>
Wed, 21 Aug 2002 07:54:14 +0000 (00:54 -0700)
Patch originally from Richard Purdie <rpurdie@rpsys.net> but tweaked by me.

drivers/usb/Makefile
drivers/usb/misc/Config.help
drivers/usb/misc/Config.in
drivers/usb/misc/Makefile
drivers/usb/misc/atmsar.c [new file with mode: 0644]
drivers/usb/misc/atmsar.h [new file with mode: 0644]
drivers/usb/misc/speedtouch.c [new file with mode: 0644]

index 8b23d38f29aed9e5a7db9a2f2f1f9fb7e42ecb95..ca36bcf8ffa4b0db8491b31887aeedc84685638d 100644 (file)
@@ -59,6 +59,7 @@ obj-$(CONFIG_USB_BRLVGER)     += misc/
 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/
 
index 6321df2998c1e50920bdc7823d5cd650a5f62b82..c2c4c6fe052fed0c07063409b6b70847c0e4a4bf 100644 (file)
@@ -84,3 +84,31 @@ CONFIG_USB_USS720
   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/>.
+
index c7227225db965f921624f73374cb1cb1d7e3baa6..3b21ec8d862dfb9edf14797076b5d8f7aea6bdc7 100644 (file)
@@ -8,3 +8,4 @@ dep_tristate '  USB Auerswald ISDN support (EXPERIMENTAL)' CONFIG_USB_AUERSWALD
 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    
index 9d09b28c251bb883b849557c101f71bfc45a8eb6..1a89e1fffa6414150443f616ba8bd0d5e03f3458 100644 (file)
@@ -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 --git a/drivers/usb/misc/atmsar.c b/drivers/usb/misc/atmsar.c
new file mode 100644 (file)
index 0000000..9bed94f
--- /dev/null
@@ -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 --git a/drivers/usb/misc/atmsar.h b/drivers/usb/misc/atmsar.h
new file mode 100644 (file)
index 0000000..e7f05dc
--- /dev/null
@@ -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 --git a/drivers/usb/misc/speedtouch.c b/drivers/usb/misc/speedtouch.c
new file mode 100644 (file)
index 0000000..29fc6db
--- /dev/null
@@ -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");