Patch originally from Richard Purdie <rpurdie@rpsys.net> but tweaked by me.
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/
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/>.
+
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
# (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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ *
+ * 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_ */
--- /dev/null
+/*
+ * 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");