Commit 3118f2e8 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

USB: added the speedtouch usb driver.

Patch originally from Richard Purdie <rpurdie@rpsys.net> but tweaked by me.
parent 5499622f
......@@ -59,6 +59,7 @@ obj-$(CONFIG_USB_BRLVGER) += misc/
obj-$(CONFIG_USB_EMI26) += misc/
obj-$(CONFIG_USB_LCD) += misc/
obj-$(CONFIG_USB_RIO500) += misc/
obj-$(CONFIG_USB_SPEEDTOUCH) += misc/
obj-$(CONFIG_USB_TIGL) += misc/
obj-$(CONFIG_USB_USS720) += misc/
......
......@@ -84,3 +84,31 @@ CONFIG_USB_USS720
inserted in and removed from the running kernel whenever you want).
The module will be called uss720.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
CONFIG_USB_SPEEDTCH
This driver provides support for the Alcatel SpeedTouch ADSL USB
modem.
The driver requires the ATM interface and ATM SAR so you need to
enable ATM (under Networking options) and the ATM SAR option. You
will also need PPP over ATM (under Network device support).
This driver is a slightly revised version of Johan Verrept's 1.5
SpeedTouch Driver which has been altered to work on the 2.5 series
kernels.
To use the device you also need a user-mode daemon that downloads
the firmware and (re)initializes the modem. The offical version is
a closed source one from Alcatel that you can get at
<http://www.alcateldsl.com/support.htm>.
A piece of code has recently been sent to linux-usb-devel which
allows the open source user space driver's firmware program
modem_run to be used with this driver instead. You will still
need the Alcatel daemon package to extract the modem firmware from
it (or the windows drivers instead).
For more information, see Johan Verrept's webpages at
<http://linux-usb.sourceforge.net/SpeedTouch/>.
......@@ -8,3 +8,4 @@ dep_tristate ' USB Auerswald ISDN support (EXPERIMENTAL)' CONFIG_USB_AUERSWALD
dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB $CONFIG_EXPERIMENTAL
dep_tristate ' Tieman Voyager USB Braille display support (EXPERIMENTAL)' CONFIG_USB_BRLVGER $CONFIG_USB $CONFIG_EXPERIMENTAL
dep_tristate ' USB LCD driver support' CONFIG_USB_LCD $CONFIG_USB
dep_tristate ' Alcatel Speedtouch ADSL USB Modem' CONFIG_USB_SPEEDTOUCH $CONFIG_USB
......@@ -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
......
/*
* 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");
/*
*
* 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_ */
/*
* 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");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment