# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.516 -> 1.517 # drivers/usb/misc/Config.help 1.4 -> 1.5 # drivers/usb/misc/Makefile 1.8 -> 1.9 # drivers/usb/misc/Config.in 1.1 -> 1.2 # drivers/usb/Makefile 1.35 -> 1.36 # (new) -> 1.1 drivers/usb/misc/atmsar.c # (new) -> 1.1 drivers/usb/misc/atmsar.h # (new) -> 1.1 drivers/usb/misc/speedtouch.c # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/08/21 greg@kroah.com 1.517 # USB: added the speedtouch usb driver. # # Patch originally from Richard Purdie but tweaked by me. # -------------------------------------------- # diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Wed Aug 21 15:45:22 2002 +++ b/drivers/usb/Makefile Wed Aug 21 15:45:22 2002 @@ -59,6 +59,7 @@ obj-$(CONFIG_USB_EMI26) += misc/ obj-$(CONFIG_USB_LCD) += misc/ obj-$(CONFIG_USB_RIO500) += misc/ +obj-$(CONFIG_USB_SPEEDTOUCH) += misc/ obj-$(CONFIG_USB_TIGL) += misc/ obj-$(CONFIG_USB_USS720) += misc/ diff -Nru a/drivers/usb/misc/Config.help b/drivers/usb/misc/Config.help --- a/drivers/usb/misc/Config.help Wed Aug 21 15:45:22 2002 +++ b/drivers/usb/misc/Config.help Wed Aug 21 15:45:22 2002 @@ -84,3 +84,31 @@ inserted in and removed from the running kernel whenever you want). The module will be called uss720.o. If you want to compile it as a module, say M here and read . + +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 + . + + 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 + . + diff -Nru a/drivers/usb/misc/Config.in b/drivers/usb/misc/Config.in --- a/drivers/usb/misc/Config.in Wed Aug 21 15:45:22 2002 +++ b/drivers/usb/misc/Config.in Wed Aug 21 15:45:22 2002 @@ -8,3 +8,4 @@ dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate ' Tieman Voyager USB Braille display support (EXPERIMENTAL)' CONFIG_USB_BRLVGER $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate ' USB LCD driver support' CONFIG_USB_LCD $CONFIG_USB +dep_tristate ' Alcatel Speedtouch ADSL USB Modem' CONFIG_USB_SPEEDTOUCH $CONFIG_USB diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile --- a/drivers/usb/misc/Makefile Wed Aug 21 15:45:22 2002 +++ b/drivers/usb/misc/Makefile Wed Aug 21 15:45:22 2002 @@ -3,11 +3,14 @@ # (the ones that don't fit into any other categories) # +export-objs := atmsar.o + obj-$(CONFIG_USB_AUERSWALD) += auerswald.o obj-$(CONFIG_USB_BRLVGER) += brlvger.o obj-$(CONFIG_USB_EMI26) += emi26.o obj-$(CONFIG_USB_LCD) += usblcd.o obj-$(CONFIG_USB_RIO500) += rio500.o +obj-$(CONFIG_USB_SPEEDTOUCH) += speedtouch.o atmsar.o obj-$(CONFIG_USB_TIGL) += tiglusb.o obj-$(CONFIG_USB_USS720) += uss720.o diff -Nru a/drivers/usb/misc/atmsar.c b/drivers/usb/misc/atmsar.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/misc/atmsar.c Wed Aug 21 15:45:22 2002 @@ -0,0 +1,720 @@ +/* + * General SAR library for ATM devices. + * + * Written By Johan Verrept ( Johan.Verrept@advalvas.be ) + * + * Copyright (c) 2000, Johan Verrept + * + * This code falls under the GNU General Public License, see COPYING for details + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Version 0.2.4A: + - Version for inclusion in 2.5 series kernel + - Modifcations by Richard Purdie (rpurdie@rpsys.net) + - replaced "sarlib" with "atmsar" + - adaptations for inclusion in kernel tree + +Version 0.2.4: + - Fixed wrong buffer overrun check in atmsar_decode_rawcell() + reported by Stephen Robinson + - 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 + +Version 0.2.3: + - Fixed wrong allocation size. caused memory corruption in some + cases. Reported by Vladimir Dergachev + - Added some comments + +Version 0.2.2: + - Fixed CRCASM (patch from Linus Flannagan ) + - Fixed problem when user did NOT use the ATMSAR_USE_53BYTE_CELL flag. + (reported by Piers Scannell ) + - 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 +#include + +#define DRIVER_AUTHOR "Johan Verrept, Johan.Verrept@advalvas.be" +#define DRIVER_DESC "General SAR library for ATM devices" +#define DRIVER_VERSION "0.2.4A" + +/*********************** + ** + ** things to remember + ** + ***********************/ + +/* + 1. the atmsar_vcc_data list pointer MUST be initialized to NULL + 2. atmsar_encode_rawcell will drop incomplete cells. + 3. ownership of the skb goes to the library ! +*/ + +#define ATM_HDR_VPVC_MASK (ATM_HDR_VPI_MASK | ATM_HDR_VCI_MASK) + +/*********************** + ** + ** LOCAL STRUCTURES + ** + ***********************/ + +/*********************** + ** + ** LOCAL MACROS + ** + ***********************/ +/* +#define DEBUG 1 +*/ +#ifdef DEBUG +#define PDEBUG(arg...) printk(KERN_DEBUG "atmsar: " arg) +#else +#define PDEBUG(arg...) +#endif + +#define ADD_HEADER(dest, header) \ + *dest++ = (unsigned char) (header >> 24); \ + *dest++ = (unsigned char) (header >> 16); \ + *dest++ = (unsigned char) (header >> 8); \ + *dest++ = (unsigned char) (header & 0xff); + +/* + * CRC Routines from net/wan/sbni.c) + * table generated by Rocksoft^tm Model CRC Algorithm Table Generation Program V1.0 + */ +#define CRC32_REMAINDER CBF43926 +#define CRC32_INITIAL 0xffffffff +#define CRC32(c,crc) (crc32tab[((size_t)(crc>>24) ^ (c)) & 0xff] ^ (((crc) << 8))) +unsigned long crc32tab[256] = { + 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, + 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, + 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, + 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, + 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, + 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, + 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, + 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, + 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, + 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, + 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, + 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, + 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, + 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, + 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, + 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, + 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, + 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, + 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, + 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, + 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, + 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, + 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, + 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, + 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, + 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, + 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, + 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, + 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, + 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, + 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, + 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, + 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, + 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, + 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, + 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, + 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, + 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, + 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, + 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, + 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, + 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, + 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, + 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, + 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, + 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, + 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, + 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, + 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, + 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, + 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, + 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, + 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, + 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, + 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, + 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, + 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, + 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, + 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, + 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, + 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, + 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, + 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, + 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L +}; + +#ifdef CRCASM + +unsigned long calc_crc (char *mem, int len, unsigned initial) +{ + unsigned crc, dummy_len; + __asm__ ("xorl %%eax,%%eax\n\t" "1:\n\t" "movl %%edx,%%eax\n\t" "shrl $16,%%eax\n\t" "lodsb\n\t" "xorb %%ah,%%al\n\t" "andl $255,%%eax\n\t" "shll $8,%%edx\n\t" "xorl (%%edi,%%eax,4),%%edx\n\t" "loop 1b":"=d" (crc), + "=c" + (dummy_len) + : "S" (mem), "D" (&crc32tab[0]), "1" (len), "0" (initial) + : "eax"); + return crc; +} + +#else + +unsigned long calc_crc (char *mem, int len, unsigned initial) +{ + unsigned crc; + crc = initial; + + for (; len; mem++, len--) { + crc = CRC32 (*mem, crc); + } + return (crc); +} +#endif + +#define crc32( crc, mem, len) calc_crc(mem, len, crc); + +/* initialiation routines. not used at the moment + * I will avoid these as long as possible !! + */ + +int open_atmsar (void) +{ + return 0; +} + +int remove_atmsar (void) +{ + return 0; +} + +/* ATOMIC version of alloc_tx */ +struct sk_buff *atmsar_alloc_skb_wrapper (struct atm_vcc *vcc, unsigned int size) +{ + struct sk_buff *skb; + + if (atomic_read (&vcc->tx_inuse) && !atm_may_send (vcc, size)) { + PDEBUG ("Sorry: tx_inuse = %d, size = %d, sndbuf = %d\n", + atomic_read (&vcc->tx_inuse), size, vcc->sk->sndbuf); + return NULL; + } + skb = alloc_skb (size, GFP_ATOMIC); + if (!skb) + return NULL; + atomic_add (skb->truesize + ATM_PDU_OVHD, &vcc->tx_inuse); + return skb; +} + +struct sk_buff *atmsar_alloc_tx (struct atmsar_vcc_data *vcc, unsigned int size) +{ + struct sk_buff *tmp = NULL; + int bufsize = 0; + + switch (vcc->type) { + case ATMSAR_TYPE_AAL0: + /* reserving adequate headroom */ + bufsize = + size + (((size / 48) + 1) * ((vcc->flags & ATMSAR_USE_53BYTE_CELL) ? 5 : 4)); + break; + case ATMSAR_TYPE_AAL1: + /* reserving adequate headroom */ + bufsize = + size + (((size / 47) + 1) * ((vcc->flags & ATMSAR_USE_53BYTE_CELL) ? 5 : 4)); + break; + case ATMSAR_TYPE_AAL2: + case ATMSAR_TYPE_AAL34: + /* not supported */ + break; + case ATMSAR_TYPE_AAL5: + /* reserving adequate tailroom */ + bufsize = size + (((size + 8 + 47) / 48) * 48); + break; + } + + PDEBUG ("Requested size %d, Allocating size %d\n", size, bufsize); + tmp = vcc->alloc_tx (vcc->vcc, bufsize); + skb_put (tmp, bufsize); + + return tmp; +} + +struct atmsar_vcc_data *atmsar_open (struct atmsar_vcc_data **list, struct atm_vcc *vcc, uint type, + ushort vpi, ushort vci, unchar pti, unchar gfc, uint flags) +{ + struct atmsar_vcc_data *new; + + new = kmalloc (sizeof (struct atmsar_vcc_data), GFP_KERNEL); + + if (!new) + return NULL; + + if (!vcc) + return NULL; + + memset (new, 0, sizeof (struct atmsar_vcc_data)); + new->vcc = vcc; +/* + * This gives problems with the ATM layer alloc_tx(). + * It is not usable from interrupt context and for + * some reason this is used in interurpt context + * with br2684.c + * + if (vcc->alloc_tx) + new->alloc_tx = vcc->alloc_tx; + else +*/ + new->alloc_tx = atmsar_alloc_skb_wrapper; + + new->stats = vcc->stats; + new->type = type; + new->next = NULL; + new->gfc = gfc; + new->vp = vpi; + new->vc = vci; + new->pti = pti; + + switch (type) { + case ATMSAR_TYPE_AAL0: + new->mtu = ATMSAR_DEF_MTU_AAL0; + break; + case ATMSAR_TYPE_AAL1: + new->mtu = ATMSAR_DEF_MTU_AAL1; + break; + case ATMSAR_TYPE_AAL2: + new->mtu = ATMSAR_DEF_MTU_AAL2; + break; + case ATMSAR_TYPE_AAL34: + /* not supported */ + new->mtu = ATMSAR_DEF_MTU_AAL34; + break; + case ATMSAR_TYPE_AAL5: + new->mtu = ATMSAR_DEF_MTU_AAL5; + break; + } + + new->atmHeader = ((unsigned long) gfc << ATM_HDR_GFC_SHIFT) + | ((unsigned long) vpi << ATM_HDR_VPI_SHIFT) + | ((unsigned long) vci << ATM_HDR_VCI_SHIFT) + | ((unsigned long) pti << ATM_HDR_PTI_SHIFT); + new->flags = flags; + new->next = NULL; + new->reasBuffer = NULL; + + new->next = *list; + *list = new; + + PDEBUG ("Allocated new SARLib vcc 0x%p with vp %d vc %d\n", new, vpi, vci); + + return new; +} + +void atmsar_close (struct atmsar_vcc_data **list, struct atmsar_vcc_data *vcc) +{ + struct atmsar_vcc_data *work; + + if (*list == vcc) { + *list = (*list)->next; + } else { + for (work = *list; work && work->next && (work->next != vcc); work = work->next); + + /* return if not found */ + if (work->next != vcc) + return; + + work->next = work->next->next; + } + + if (vcc->reasBuffer) { + dev_kfree_skb (vcc->reasBuffer); + } + + PDEBUG ("Allocated SARLib vcc 0x%p with vp %d vc %d\n", vcc, vcc->vp, vcc->vc); + + kfree (vcc); +} + +/*********************** + ** + ** ENCODE FUNCTIONS + ** + ***********************/ + +struct sk_buff *atmsar_encode_rawcell (struct atmsar_vcc_data *ctx, struct sk_buff *skb) +{ + int number_of_cells = (skb->len) / 48; + int total_length = number_of_cells * (ctx->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52); + unsigned char *source; + unsigned char *target; + struct sk_buff *out = NULL; + int i; + + PDEBUG ("atmsar_encode_rawcell (0x%p, 0x%p) called\n", ctx, skb); + + if (skb_cloned (skb) + || (skb_headroom (skb) < + (number_of_cells * (ctx->flags & ATMSAR_USE_53BYTE_CELL ? 5 : 4)))) { + PDEBUG + ("atmsar_encode_rawcell allocating new skb. ctx->alloc_tx = 0x%p, ctx->vcc = 0x%p\n", + ctx->alloc_tx, ctx->vcc); + /* get new skb */ + out = ctx->alloc_tx (ctx->vcc, total_length); + if (!out) + return NULL; + + skb_put (out, total_length); + source = skb->data; + target = out->data; + } else { + PDEBUG ("atmsar_encode_rawcell: sufficient headroom\n"); + source = skb->data; + skb_push (skb, number_of_cells * ((ctx->flags & ATMSAR_USE_53BYTE_CELL) ? 5 : 4)); + target = skb->data; + out = skb; + } + + PDEBUG ("source 0x=%p, target 0x%p\n", source, target); + + if (ctx->flags & ATMSAR_USE_53BYTE_CELL) { + for (i = 0; i < number_of_cells; i++) { + ADD_HEADER (target, ctx->atmHeader); + *target++ = (char) 0xEC; + memcpy (target, source, 48); + target += 48; + source += 48; + PDEBUG ("source 0x=%p, target 0x%p\n", source, target); + } + } else { + for (i = 0; i < number_of_cells; i++) { + ADD_HEADER (target, ctx->atmHeader); + memcpy (target, source, 48); + target += 48; + source += 48; + PDEBUG ("source 0x=%p, target 0x%p\n", source, target); + }; + } + + if (ctx->flags & ATMSAR_SET_PTI) { + /* setting pti bit in last cell */ + *(target - (ctx->flags & ATMSAR_USE_53BYTE_CELL ? 50 : 49)) |= 0x2; + } + + /* update stats */ + if (ctx->stats && (ctx->type <= ATMSAR_TYPE_AAL1)) + atomic_add (number_of_cells, &(ctx->stats->tx)); + + PDEBUG ("atmsar_encode_rawcell return 0x%p (length %d)\n", out, out->len); + return out; +} + +struct sk_buff *atmsar_encode_aal5 (struct atmsar_vcc_data *ctx, struct sk_buff *skb) +{ + int length, pdu_length; + unsigned char *trailer; + unsigned char *pad; + uint crc = 0xffffffff; + + PDEBUG ("atmsar_encode_aal5 (0x%p, 0x%p) called\n", ctx, skb); + + /* determine aal5 length */ + pdu_length = skb->len; + length = ((pdu_length + 8 + 47) / 48) * 48; + + if (skb_tailroom (skb) < (length - pdu_length)) { + struct sk_buff *out; + PDEBUG + ("atmsar_encode_aal5 allocating new skb. ctx->alloc_tx = 0x%p, ctx->vcc = 0x%p\n", + ctx->alloc_tx, ctx->vcc); + /* get new skb */ + out = ctx->alloc_tx (ctx->vcc, length); + if (!out) + return NULL; + + PDEBUG ("out->data = 0x%p\n", out->data); + PDEBUG ("atmsar_encode_aal5 pdu length %d, allocated length %d\n", skb->len, + length); + memcpy (out->data, skb->data, skb->len); + skb_put (out, skb->len); + + skb = out; + } + + PDEBUG ("skb->data = 0x%p\n", skb->data); + /* note end of pdu and add length */ + pad = skb_put (skb, length - pdu_length); + trailer = skb->tail - 8; + + PDEBUG ("trailer = 0x%p\n", trailer); + + /* zero padding space */ + memset (pad, 0, length - pdu_length - 8); + + /* add trailer */ + *trailer++ = (unsigned char) 0; /* UU = 0 */ + *trailer++ = (unsigned char) 0; /* CPI = 0 */ + *trailer++ = (unsigned char) (pdu_length >> 8); + *trailer++ = (unsigned char) (pdu_length & 0xff); + crc = ~crc32 (crc, skb->data, length - 4); + *trailer++ = (unsigned char) (crc >> 24); + *trailer++ = (unsigned char) (crc >> 16); + *trailer++ = (unsigned char) (crc >> 8); + *trailer++ = (unsigned char) (crc & 0xff); + + /* update stats */ + if (ctx->stats) + atomic_inc (&ctx->stats->tx); + + PDEBUG ("atmsar_encode_aal5 return 0x%p (length %d)\n", skb, skb->len); + return skb; +} + +/*********************** + ** + ** DECODE FUNCTIONS + ** + ***********************/ + +struct sk_buff *atmsar_decode_rawcell (struct atmsar_vcc_data *list, struct sk_buff *skb, + struct atmsar_vcc_data **ctx) +{ + while (skb->len) { + unsigned char *cell = skb->data; + unsigned char *cell_payload; + struct atmsar_vcc_data *vcc = list; + unsigned long atmHeader = + ((unsigned long) (cell[0]) << 24) | ((unsigned long) (cell[1]) << 16) | + ((unsigned long) (cell[2]) << 8) | (cell[3] & 0xff); + + PDEBUG ("atmsar_decode_rawcell (0x%p, 0x%p, 0x%p) called\n", list, skb, ctx); + PDEBUG ("atmsar_decode_rawcell skb->data %p, skb->tail %p\n", skb->data, skb->tail); + + if (!list || !skb || !ctx) + return NULL; + if (!skb->data || !skb->tail) + return NULL; + + /* here should the header CRC check be... */ + + /* look up correct vcc */ + for (; + vcc + && ((vcc->atmHeader & ATM_HDR_VPVC_MASK) != (atmHeader & ATM_HDR_VPVC_MASK)); + vcc = vcc->next); + + PDEBUG ("atmsar_decode_rawcell found vcc %p for packet on vp %d, vc %d\n", vcc, + (int) ((atmHeader & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT), + (int) ((atmHeader & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT)); + + if (vcc && (skb->len >= (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52))) { + cell_payload = cell + (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 5 : 4); + + switch (vcc->type) { + case ATMSAR_TYPE_AAL0: + /* case ATMSAR_TYPE_AAL1: when we have a decode AAL1 function... */ + { + struct sk_buff *tmp = dev_alloc_skb (vcc->mtu); + + if (tmp) { + memcpy (tmp->tail, cell_payload, 48); + skb_put (tmp, 48); + + if (vcc->stats) + atomic_inc (&vcc->stats->rx); + + skb_pull (skb, + (vcc-> + flags & ATMSAR_USE_53BYTE_CELL ? 53 : + 52)); + PDEBUG + ("atmsar_decode_rawcell returns ATMSAR_TYPE_AAL0 pdu 0x%p with length %d\n", + tmp, tmp->len); + return tmp; + }; + } + break; + case ATMSAR_TYPE_AAL1: + case ATMSAR_TYPE_AAL2: + case ATMSAR_TYPE_AAL34: + /* not supported */ + break; + case ATMSAR_TYPE_AAL5: + if (!vcc->reasBuffer) + vcc->reasBuffer = dev_alloc_skb (vcc->mtu); + + /* if alloc fails, we just drop the cell. it is possible that we can still + * receive cells on other vcc's + */ + if (vcc->reasBuffer) { + /* if (buffer overrun) discard received cells until now */ + if ((vcc->reasBuffer->len) > (vcc->mtu - 48)) + skb_trim (vcc->reasBuffer, 0); + + /* copy data */ + memcpy (vcc->reasBuffer->tail, cell_payload, 48); + skb_put (vcc->reasBuffer, 48); + + /* check for end of buffer */ + if (cell[3] & 0x2) { + struct sk_buff *tmp; + + /* the aal5 buffer ends here, cut the buffer. */ + /* buffer will always have at least one whole cell, so */ + /* don't need to check return from skb_pull */ + skb_pull (skb, + (vcc-> + flags & ATMSAR_USE_53BYTE_CELL ? 53 : + 52)); + *ctx = vcc; + tmp = vcc->reasBuffer; + vcc->reasBuffer = NULL; + + PDEBUG + ("atmsar_decode_rawcell returns ATMSAR_TYPE_AAL5 pdu 0x%p with length %d\n", + tmp, tmp->len); + return tmp; + } + } + break; + }; + /* flush the cell */ + /* buffer will always contain at least one whole cell, so don't */ + /* need to check return value from skb_pull */ + skb_pull (skb, (vcc->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52)); + } else { + /* If data is corrupt and skb doesn't hold a whole cell, flush the lot */ + if (skb_pull (skb, (list->flags & ATMSAR_USE_53BYTE_CELL ? 53 : 52)) == + NULL) { + skb_trim (skb, 0); + } + } + } + + return NULL; +}; + +struct sk_buff *atmsar_decode_aal5 (struct atmsar_vcc_data *ctx, struct sk_buff *skb) +{ + uint crc = 0xffffffff; + uint length, pdu_crc, pdu_length; + + PDEBUG ("atmsar_decode_aal5 (0x%p, 0x%p) called\n", ctx, skb); + + if (skb->len && (skb->len % 48)) + return NULL; + + length = (skb->tail[-6] << 8) + skb->tail[-5]; + pdu_crc = + (skb->tail[-4] << 24) + (skb->tail[-3] << 16) + (skb->tail[-2] << 8) + skb->tail[-1]; + pdu_length = ((length + 47 + 8) / 48) * 48; + + PDEBUG ("atmsar_decode_aal5: skb->len = %d, length = %d, pdu_crc = 0x%x, pdu_length = %d\n", + skb->len, length, pdu_crc, pdu_length); + + /* is skb long enough ? */ + if (skb->len < pdu_length) { + if (ctx->stats) + atomic_inc (&ctx->stats->rx_err); + return NULL; + } + + /* is skb too long ? */ + if (skb->len > pdu_length) { + PDEBUG ("atmsar_decode_aal5: Warning: readjusting illeagl size %d -> %d\n", + skb->len, pdu_length); + /* buffer is too long. we can try to recover + * if we discard the first part of the skb. + * the crc will decide whether this was ok + */ + skb_pull (skb, skb->len - pdu_length); + } + + crc = ~crc32 (crc, skb->data, pdu_length - 4); + + /* check crc */ + if (pdu_crc != crc) { + PDEBUG ("atmsar_decode_aal5: crc check failed!\n"); + if (ctx->stats) + atomic_inc (&ctx->stats->rx_err); + return NULL; + } + + /* pdu is ok */ + skb_trim (skb, length); + + /* update stats */ + if (ctx->stats) + atomic_inc (&ctx->stats->rx); + + PDEBUG ("atmsar_decode_aal5 returns pdu 0x%p with length %d\n", skb, skb->len); + return skb; +}; + + +static int start (void) +{ + return 0; +} + +static void cleanup (void) +{ +} + +module_init (start); +module_exit (cleanup); + +EXPORT_SYMBOL (atmsar_open); +EXPORT_SYMBOL (atmsar_close); +EXPORT_SYMBOL (atmsar_encode_rawcell); +EXPORT_SYMBOL (atmsar_encode_aal5); +EXPORT_SYMBOL (atmsar_decode_rawcell); +EXPORT_SYMBOL (atmsar_decode_aal5); +EXPORT_SYMBOL (atmsar_alloc_tx); + +MODULE_AUTHOR (DRIVER_AUTHOR); +MODULE_DESCRIPTION (DRIVER_DESC); +MODULE_LICENSE ("GPL"); diff -Nru a/drivers/usb/misc/atmsar.h b/drivers/usb/misc/atmsar.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/misc/atmsar.h Wed Aug 21 15:45:22 2002 @@ -0,0 +1,81 @@ +/* + * + * General SAR library for ATM devices. + * + * Copyright (c) 2000, Johan Verrept + * + * This code falls under the GNU General Public License, see COPYING for details. + * + */ + +#ifndef _ATMSAR_H_ +#define _ATMSAR_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define ATMSAR_USE_53BYTE_CELL 0x1L +#define ATMSAR_SET_PTI 0x2L + + +/* types */ +#define ATMSAR_TYPE_AAL0 ATM_AAL0 +#define ATMSAR_TYPE_AAL1 ATM_AAL1 +#define ATMSAR_TYPE_AAL2 ATM_AAL2 +#define ATMSAR_TYPE_AAL34 ATM_AAL34 +#define ATMSAR_TYPE_AAL5 ATM_AAL5 + + +/* default MTU's */ +#define ATMSAR_DEF_MTU_AAL0 48 +#define ATMSAR_DEF_MTU_AAL1 47 +#define ATMSAR_DEF_MTU_AAL2 0 /* not supported */ +#define ATMSAR_DEF_MTU_AAL34 0 /* not supported */ +#define ATMSAR_DEF_MTU_AAL5 65535 /* max mtu .. */ + +struct atmsar_vcc_data { + struct atmsar_vcc_data *next; + + /* general atmsar flags, per connection */ + int flags; + int type; + + /* connection specific non-atmsar data */ + struct sk_buff *(*alloc_tx) (struct atm_vcc * vcc, unsigned int size); + struct atm_vcc *vcc; + struct k_atm_aal_stats *stats; + unsigned short mtu; /* max is actually 65k for AAL5... */ + + /* cell data */ + unsigned int vp; + unsigned int vc; + unsigned char gfc; + unsigned char pti; + unsigned int headerFlags; + unsigned long atmHeader; + + /* raw cell reassembly */ + struct sk_buff *reasBuffer; +}; + + +extern struct atmsar_vcc_data *atmsar_open (struct atmsar_vcc_data **list, struct atm_vcc *vcc, + uint type, ushort vpi, ushort vci, unchar pti, + unchar gfc, uint flags); +extern void atmsar_close (struct atmsar_vcc_data **list, struct atmsar_vcc_data *vcc); + +extern struct sk_buff *atmsar_encode_rawcell (struct atmsar_vcc_data *ctx, struct sk_buff *skb); +extern struct sk_buff *atmsar_encode_aal5 (struct atmsar_vcc_data *ctx, struct sk_buff *skb); + +struct sk_buff *atmsar_decode_rawcell (struct atmsar_vcc_data *list, struct sk_buff *skb, + struct atmsar_vcc_data **ctx); +struct sk_buff *atmsar_decode_aal5 (struct atmsar_vcc_data *ctx, struct sk_buff *skb); + +struct sk_buff *atmsar_alloc_tx (struct atmsar_vcc_data *vcc, unsigned int size); + +#endif /* _ATMSAR_H_ */ diff -Nru a/drivers/usb/misc/speedtouch.c b/drivers/usb/misc/speedtouch.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/misc/speedtouch.c Wed Aug 21 15:45:22 2002 @@ -0,0 +1,1068 @@ +/* + * Driver Module for Alcatel SpeedTouch USB xDSL modem + * Copyright 2001, Alcatel + * Written by Johan Verrept (Johan.Verrept@advalvas.be) + * + +1.5A: - Version for inclusion in 2.5 series kernel + - Modifcations by Richard Purdie (rpurdie@rpsys.net) + - made compatible with kernel 2.5.6 onwards by changing + udsl_usb_send_data_context->urb changed to a pointer + and adding code to alloc and free it + - remove_wait_queue() added to udsl_atm_processqueue_thread() + +1.5: - fixed memory leak when atmsar_decode_aal5 returned NULL. + (reported by stephen.robinson@zen.co.uk) + +1.4: - changed the spin_lock() under interrupt to spin_lock_irqsave() + - unlink all active send urbs of a vcc that is being closed. + +1.3.1: - added the version number + +1.3: - Added multiple send urb support + - fixed memory leak and vcc->tx_inuse starvation bug + when not enough memory left in vcc. + +1.2: - Fixed race condition in udsl_usb_send_data() +1.1: - Turned off packet debugging + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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");