Commit 68353b96 authored by David Woodhouse's avatar David Woodhouse

USB: Generic USB ATM/DSL core and completed SpeedTouch driver

This can now load the firmware and initialise the modem for itself,
with no need for any userspace help (except for putting the firmware
in /lib/firmware in the first place). 

The core packet I/O code is split out into a separate file where it can
be used by drivers for some of the other similar modems.
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent d3f61e5a
......@@ -91,6 +91,8 @@ source "drivers/usb/serial/Kconfig"
source "drivers/usb/misc/Kconfig"
source "drivers/usb/atm/Kconfig"
source "drivers/usb/gadget/Kconfig"
endmenu
......
......@@ -62,8 +62,10 @@ obj-$(CONFIG_USB_LCD) += misc/
obj-$(CONFIG_USB_LED) += misc/
obj-$(CONFIG_USB_LEGOTOWER) += misc/
obj-$(CONFIG_USB_RIO500) += misc/
obj-$(CONFIG_USB_SPEEDTOUCH) += misc/
obj-$(CONFIG_USB_TEST) += misc/
obj-$(CONFIG_USB_TIGL) += misc/
obj-$(CONFIG_USB_USS720) += misc/
obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
#
# USB ATM driver configuration
#
comment "USB ATM/DSL drivers"
depends on USB
config USB_ATM
tristate "Generic USB ATM/DSL core I/O support"
depends on USB && ATM
select CRC32
default n
help
This provides a library which is used for packet I/O by USB DSL
modems, such as the SpeedTouch driver below.
To compile this driver as a module, choose M here: the
module will be called usb_atm.
config USB_SPEEDTOUCH
tristate "Alcatel Speedtouch USB support"
depends on USB && ATM
select USB_ATM
help
Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330
modem. In order to use your modem you will need to install the
two parts of the firmware, extracted by the user space tools; see
<http://www.linux-usb.org/SpeedTouch/> for details.
To compile this driver as a module, choose M here: the
module will be called speedtch.
#
# Makefile for the rest of the USB drivers
# (the ones that don't fit into any other categories)
#
obj-$(CONFIG_USB_ATM) += usb_atm.o
obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
/******************************************************************************
* speedtch.c - Alcatel SpeedTouch USB xDSL modem driver
*
* Copyright (C) 2001, Alcatel
* Copyright (C) 2003, Duncan Sands
* Copyright (C) 2004, David Woodhouse
*
* This program 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.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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.
*
******************************************************************************/
#include <linux/module.h>
#include <linux/moduleparam.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/wait.h>
#include <linux/list.h>
#include <asm/uaccess.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/crc32.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include "usb_atm.h"
/*
#define DEBUG
#define VERBOSE_DEBUG
*/
#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
# define DEBUG
#endif
#include <linux/usb.h>
#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
# define USE_FW_LOADER
#endif
#ifdef DEBUG
#define DEBUG_ON(x) BUG_ON(x)
#else
#define DEBUG_ON(x) do { if (x); } while (0)
#endif
#ifdef VERBOSE_DEBUG
static int udsl_print_packet (const unsigned char *data, int len);
#define PACKETDEBUG(arg...) udsl_print_packet (arg)
#define vdbg(arg...) dbg (arg)
#else
#define PACKETDEBUG(arg...)
#define vdbg(arg...)
#endif
#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
#define DRIVER_VERSION "1.8"
#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
static const char speedtch_driver_name [] = "speedtch";
#define SPEEDTOUCH_VENDORID 0x06b9
#define SPEEDTOUCH_PRODUCTID 0x4061
/* Timeout in jiffies */
#define CTRL_TIMEOUT (2*HZ)
#define DATA_TIMEOUT (2*HZ)
#define OFFSET_7 0 /* size 1 */
#define OFFSET_b 1 /* size 8 */
#define OFFSET_d 9 /* size 4 */
#define OFFSET_e 13 /* size 1 */
#define OFFSET_f 14 /* size 1 */
#define TOTAL 15
#define SIZE_7 1
#define SIZE_b 8
#define SIZE_d 4
#define SIZE_e 1
#define SIZE_f 1
static int dl_512_first = 0;
static int sw_buffering = 0;
module_param (dl_512_first, bool, 0444);
MODULE_PARM_DESC (dl_512_first, "Read 512 bytes before sending firmware");
module_param (sw_buffering, uint, 0444);
MODULE_PARM_DESC (sw_buffering, "Enable software buffering");
#define UDSL_IOCTL_LINE_UP 1
#define UDSL_IOCTL_LINE_DOWN 2
#define SPEEDTCH_ENDPOINT_INT 0x81
#define SPEEDTCH_ENDPOINT_DATA 0x07
#define SPEEDTCH_ENDPOINT_FIRMWARE 0x05
#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
static struct usb_device_id speedtch_usb_ids [] = {
{ USB_DEVICE (SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID) },
{ }
};
MODULE_DEVICE_TABLE (usb, speedtch_usb_ids);
struct speedtch_instance_data {
struct udsl_instance_data u;
u16 revision;
/* Status */
struct urb *int_urb;
unsigned char int_data[16];
struct work_struct poll_work;
struct timer_list poll_timer;
char fwname[25];
};
/* USB */
static int speedtch_usb_probe (struct usb_interface *intf, const struct usb_device_id *id);
static void speedtch_usb_disconnect (struct usb_interface *intf);
static int speedtch_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data);
static void speedtch_handle_int (struct urb *urb, struct pt_regs *regs);
static void speedtch_poll_status (struct speedtch_instance_data *instance);
static struct usb_driver speedtch_usb_driver = {
.owner = THIS_MODULE,
.name = speedtch_driver_name,
.probe = speedtch_usb_probe,
.disconnect = speedtch_usb_disconnect,
.ioctl = speedtch_usb_ioctl,
.id_table = speedtch_usb_ids,
};
/***************
** firmware **
***************/
static void speedtch_got_firmware (struct speedtch_instance_data *instance, int got_it)
{
int err;
struct usb_interface *intf;
down (&instance->u.serialize); /* vs self, speedtch_firmware_start */
if (instance->u.status == UDSL_LOADED_FIRMWARE)
goto out;
if (!got_it) {
instance->u.status = UDSL_NO_FIRMWARE;
goto out;
}
if ((err = usb_set_interface (instance->u.usb_dev, 1, 1)) < 0) {
dbg ("speedtch_got_firmware: usb_set_interface returned %d!", err);
instance->u.status = UDSL_NO_FIRMWARE;
goto out;
}
/* Set up interrupt endpoint */
intf = usb_ifnum_to_if(instance->u.usb_dev, 0);
if (intf && !usb_driver_claim_interface (&speedtch_usb_driver, intf, NULL)) {
instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
if (instance->int_urb) {
usb_fill_int_urb(instance->int_urb, instance->u.usb_dev,
usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT),
instance->int_data, sizeof(instance->int_data),
speedtch_handle_int, instance, 50);
err = usb_submit_urb(instance->int_urb, GFP_KERNEL);
if (err) {
/* Doesn't matter; we'll poll anyway */
dbg ("speedtch_got_firmware: Submission of interrupt URB failed %d", err);
usb_free_urb(instance->int_urb);
instance->int_urb = NULL;
usb_driver_release_interface (&speedtch_usb_driver, intf);
}
}
}
/* Start status polling */
mod_timer(&instance->poll_timer, jiffies + (1*HZ));
instance->u.status = UDSL_LOADED_FIRMWARE;
tasklet_schedule (&instance->u.receive_tasklet);
out:
up (&instance->u.serialize);
wake_up_interruptible (&instance->u.firmware_waiters);
}
static int speedtch_set_swbuff (struct speedtch_instance_data *instance, int state)
{
struct usb_device *dev = instance->u.usb_dev;
int ret;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x32, 0x40, state?0x01:0x00,
0x00, NULL, 0, 100);
if (ret < 0) {
printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n",
state?"En":"Dis", ret);
return ret;
}
dbg("speedtch_set_swbuff: %sbled SW buffering", state?"En":"Dis");
return 0;
}
static void speedtch_test_sequence(struct speedtch_instance_data *instance)
{
struct usb_device *dev = instance->u.usb_dev;
unsigned char buf[10];
int ret;
/* URB 147 */
buf[0] = 0x1c; buf[1] = 0x50;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x01, 0x40, 0x0b, 0x00, buf, 2, 100);
if (ret < 0)
printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret);
/* URB 148 */
buf[0] = 0x32; buf[1] = 0x00;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x01, 0x40, 0x02, 0x00, buf, 2, 100);
if (ret < 0)
printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret);
/* URB 149 */
buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x01, 0x40, 0x03, 0x00, buf, 3, 100);
if (ret < 0)
printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret);
/* URB 150 */
buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x01, 0x40, 0x04, 0x00, buf, 3, 100);
if (ret < 0)
printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret);
}
static int speedtch_start_synchro (struct speedtch_instance_data *instance)
{
struct usb_device *dev = instance->u.usb_dev;
unsigned char buf[2];
int ret;
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x12, 0xc0, 0x04, 0x00,
buf, sizeof(buf), CTRL_TIMEOUT);
if (ret < 0) {
printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret);
return ret;
}
dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]);
return 0;
}
static void speedtch_handle_int (struct urb *urb, struct pt_regs *regs)
{
struct speedtch_instance_data *instance = urb->context;
unsigned int count = urb->actual_length;
int ret;
/* The magic interrupt for "up state" */
const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00};
/* The magic interrupt for "down state" */
const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00};
switch (urb->status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d", __func__, urb->status);
return;
default:
dbg("%s - nonzero urb status received: %d", __func__, urb->status);
goto exit;
}
if (count < 6) {
dbg("%s - int packet too short", __func__);
goto exit;
}
if (!memcmp(up_int, instance->int_data, 6)) {
del_timer(&instance->poll_timer);
printk(KERN_NOTICE "DSL line goes up\n");
} else if (!memcmp(down_int, instance->int_data, 6)) {
del_timer(&instance->poll_timer);
printk(KERN_NOTICE "DSL line goes down\n");
} else {
int i;
printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count);
for (i=0; i < count; i++)
printk(" %02x", instance->int_data[i]);
printk("\n");
}
schedule_work(&instance->poll_work);
exit:
rmb();
if (!instance->int_urb)
return;
ret = usb_submit_urb (urb, GFP_ATOMIC);
if (ret)
err ("%s - usb_submit_urb failed with result %d",
__func__, ret);
}
static int speedtch_get_status(struct speedtch_instance_data *instance, unsigned char *buf)
{
struct usb_device *dev = instance->u.usb_dev;
int ret;
memset(buf,0,TOTAL);
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x12, 0xc0, 0x07, 0x00, buf+OFFSET_7, SIZE_7, CTRL_TIMEOUT);
if (ret<0) {
dbg("MSG 7 failed");
return(ret);
}
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x12, 0xc0, 0x0b, 0x00, buf+OFFSET_b, SIZE_b, CTRL_TIMEOUT);
if (ret<0) {
dbg("MSG B failed");
return(ret);
}
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x12, 0xc0, 0x0d, 0x00, buf+OFFSET_d, SIZE_d, CTRL_TIMEOUT);
if (ret<0) {
dbg("MSG D failed");
return(ret);
}
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x01, 0xc0, 0x0e, 0x00, buf+OFFSET_e, SIZE_e, CTRL_TIMEOUT);
if (ret<0) {
dbg("MSG E failed");
return(ret);
}
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x01, 0xc0, 0x0f, 0x00, buf+OFFSET_f, SIZE_f, CTRL_TIMEOUT);
if (ret<0) {
dbg("MSG F failed");
return(ret);
}
return 0;
}
static void speedtch_poll_status(struct speedtch_instance_data *instance)
{
unsigned char buf[TOTAL];
int ret;
ret = speedtch_get_status(instance, buf);
if (ret) {
printk(KERN_WARNING "SpeedTouch: Error %d fetching device status\n", ret);
return;
}
dbg("Line state %02x", buf[OFFSET_7]);
switch (buf[OFFSET_7]) {
case 0:
if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
printk(KERN_NOTICE "ADSL line is down\n");
}
break;
case 0x08:
if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
printk(KERN_NOTICE "ADSL line is blocked?\n");
}
break;
case 0x10:
if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
printk(KERN_NOTICE "ADSL line is synchronising\n");
}
break;
case 0x20:
if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) {
int down_speed = buf[OFFSET_b] | (buf[OFFSET_b+1]<<8)
| (buf[OFFSET_b+2]<<16) | (buf[OFFSET_b+3]<<24);
int up_speed = buf[OFFSET_b+4] | (buf[OFFSET_b+5]<<8)
| (buf[OFFSET_b+6]<<16) | (buf[OFFSET_b+7]<<24);
if(!(down_speed & 0x0000ffff) &&
!(up_speed & 0x0000ffff)) {
down_speed>>=16;
up_speed>>=16;
}
instance->u.atm_dev->link_rate = down_speed * 1000 / 424;
instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
printk(KERN_NOTICE "ADSL line is up (%d Kib/s down | %d Kib/s up)\n",
down_speed, up_speed);
}
break;
default:
if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]);
}
break;
}
}
static void speedtch_timer_poll (unsigned long data)
{
struct speedtch_instance_data *instance = (struct speedtch_instance_data *) data;
schedule_work(&instance->poll_work);
mod_timer(&instance->poll_timer, jiffies + (5*HZ));
}
static void speedtch_firmware_load(const struct firmware *fw1, void *context)
{
const struct firmware *fw2;
unsigned char *buffer;
struct speedtch_instance_data *instance = context;
struct usb_interface *intf;
struct usb_device *dev = instance->u.usb_dev;
int actual_length, ret;
int pg;
dbg ("speedtch_firmware_load");
ret = -1;
BUG_ON(!instance);
if (!(intf = usb_ifnum_to_if (dev, 2))) {
dbg ("speedtch_firmware_load: interface not found!");
goto fail;
}
if (!(buffer = kmalloc (0x1000, GFP_KERNEL))) {
dbg ("speedtch_firmware_load: no memory for buffer!");
goto fail;
}
if (!fw1) {
dbg ("speedtch_firmware_load: no firmware 'speedtch_boot_rev%d.%02x",
instance->revision >> 8, instance->revision & 0xff);
snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_boot_rev%d",
instance->revision >> 8);
ret = request_firmware(&fw1, instance->fwname, &dev->dev);
if (ret < 0) {
dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname);
strlcpy(instance->fwname, "speedtch_boot", sizeof(instance->fwname));
ret = request_firmware(&fw1, instance->fwname, &dev->dev);
}
if (ret < 0) {
dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname);
printk(KERN_INFO "speedtch: No boot firmware found. Assuming userspace firmware loader\n");
goto fail_buf;
}
}
snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_main_rev%d.%02x",
instance->revision >> 8, instance->revision & 0xff);
ret = request_firmware(&fw2, instance->fwname, &dev->dev);
if (ret < 0) {
dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname);
snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_main_rev%d",
instance->revision >> 8);
ret = request_firmware(&fw2, instance->fwname, &dev->dev);
}
if (ret < 0) {
dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname);
strlcpy(instance->fwname, "speedtch_main", sizeof(instance->fwname));
ret = request_firmware(&fw2, instance->fwname, &dev->dev);
}
if (ret < 0) {
dbg ("speedtch_firmware_load: no firmware '%s'", instance->fwname);
printk(KERN_INFO "speedtch: No main firmware found. Assuming userspace firmware loader\n");
goto fail_buf;
}
/* OK, we have the firmware. So try to claim interface #2 and actually
try uploading it. There's a slight possibility that the userspace
modem_run could be running too, and may have beaten us to it */
if ((ret = usb_driver_claim_interface (&speedtch_usb_driver, intf, NULL)) < 0) {
dbg ("speedtch_firmware_start: interface in use (%d)!", ret);
goto fail_fw2;
}
/* URB 7 */
if (dl_512_first) { /* some modems need a read before writing the firmware */
ret = usb_bulk_msg (instance->u.usb_dev,
usb_rcvbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
buffer,
0x200,
&actual_length,
2 * HZ);
if (ret < 0 && ret != -ETIMEDOUT)
dbg ("speedtch_firmware_load: read BLOCK0 from modem failed (%d)!", ret);
else
dbg("speedtch_firmware_load: BLOCK0 downloaded (%d bytes)", ret);
}
/* URB 8 : both leds are static green */
for (pg = 0; pg * 0x1000 < fw1->size; pg++) {
int thislen = min_t(int, 0x1000, fw1->size - (pg*0x1000));
memcpy(buffer, fw1->data + (pg*0x1000), thislen);
ret = usb_bulk_msg (instance->u.usb_dev,
usb_sndbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
buffer,
thislen,
&actual_length,
DATA_TIMEOUT);
if (ret < 0) {
dbg ("speedtch_firmware_load: write BLOCK1 to modem failed (%d)!", ret);
goto fail_release;
}
dbg("speedtch_firmware_load: BLOCK1 uploaded (%d bytes)", fw1->size);
}
/* USB led blinking green, ADSL led off */
/* URB 11 */
ret = usb_bulk_msg (instance->u.usb_dev,
usb_rcvbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
buffer,
0x200,
&actual_length,
DATA_TIMEOUT);
if (ret < 0) {
dbg ("speedtch_firmware_load: read BLOCK2 from modem failed (%d)!", ret);
goto fail_release;
}
dbg("speedtch_firmware_load: BLOCK2 downloaded (%d bytes)", actual_length);
/* URBs 12 to 139 - USB led blinking green, ADSL led off */
for (pg = 0; pg * 0x1000 < fw2->size; pg++) {
int thislen = min_t(int, 0x1000, fw2->size - (pg*0x1000));
memcpy(buffer, fw2->data + (pg*0x1000), thislen);
ret = usb_bulk_msg (instance->u.usb_dev,
usb_sndbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
buffer,
thislen,
&actual_length,
DATA_TIMEOUT);
if (ret < 0) {
dbg ("speedtch_firmware_load: write BLOCK3 to modem failed (%d)!", ret);
goto fail_release;
}
}
dbg("speedtch_firmware_load: BLOCK3 uploaded (%d bytes)", fw2->size);
/* USB led static green, ADSL led static red */
/* URB 142 */
ret = usb_bulk_msg (instance->u.usb_dev,
usb_rcvbulkpipe (instance->u.usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
buffer,
0x200,
&actual_length,
DATA_TIMEOUT);
if (ret < 0) {
dbg("speedtch_firmware_load: read BLOCK4 from modem failed (%d)!", ret);
goto fail_release;
}
/* success */
dbg("speedtch_firmware_load: BLOCK4 downloaded (%d bytes)", actual_length);
/* Delay to allow firmware to start up. We can do this here
because we're in our own kernel thread anyway. */
msleep(1000);
/* Enable software buffering, if requested */
if (sw_buffering)
speedtch_set_swbuff(instance, 1);
/* Magic spell; don't ask us what this does */
speedtch_test_sequence(instance);
/* Start modem synchronisation */
if (speedtch_start_synchro(instance))
dbg("speedtch_start_synchro: failed\n");
speedtch_got_firmware(instance, 1);
goto fail_fw2; /* The got_firmware(0) is a NOP anyway */
fail_release:
/* We only release interface #2 if loading the firmware failed; we don't
do it if we succeeded. This prevents the userspace modem_run tool from
trying to load the firmware itself */
usb_driver_release_interface (&speedtch_usb_driver, intf);
fail_fw2:
release_firmware(fw2);
fail_buf:
kfree (buffer);
fail:
speedtch_got_firmware (instance, 0);
udsl_put_instance(&instance->u);
}
static void speedtch_firmware_start (struct speedtch_instance_data *instance)
{
#ifdef USE_FW_LOADER
int ret;
#endif
dbg ("speedtch_firmware_start");
down (&instance->u.serialize); /* vs self, speedtch_got_firmware */
if (instance->u.status >= UDSL_LOADING_FIRMWARE) {
up (&instance->u.serialize);
return;
}
instance->u.status = UDSL_LOADING_FIRMWARE;
up (&instance->u.serialize);
udsl_get_instance(&instance->u);
#ifdef USE_FW_LOADER
snprintf(instance->fwname, sizeof(instance->fwname), "speedtch_boot_rev%d.%02x",
instance->revision >> 8, instance->revision & 0xff);
printk("Look for %s\n", instance->fwname);
ret = request_firmware_nowait (THIS_MODULE,
instance->fwname,
&instance->u.usb_dev->dev,
instance,
speedtch_firmware_load);
if (ret >= 0)
return; /* OK */
dbg ("speedtch_firmware_start: request_firmware_nowait failed (%d)!", ret);
/* Just pretend it never happened... hope modem_run happens */
#endif /* USE_FW_LOADER */
speedtch_got_firmware (instance, 0);
udsl_put_instance(&instance->u);
}
static int speedtch_firmware_wait (struct udsl_instance_data *instance)
{
speedtch_firmware_start ((void *) instance);
if (wait_event_interruptible (instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0)
return -ERESTARTSYS;
return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN;
}
/**********
** USB **
**********/
static int speedtch_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data)
{
struct speedtch_instance_data *instance = usb_get_intfdata (intf);
dbg ("speedtch_usb_ioctl entered");
if (!instance) {
dbg ("speedtch_usb_ioctl: NULL instance!");
return -ENODEV;
}
switch (code) {
case UDSL_IOCTL_LINE_UP:
instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
speedtch_got_firmware (instance, 1);
return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO;
case UDSL_IOCTL_LINE_DOWN:
instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
return 0;
default:
return -ENOTTY;
}
}
static int speedtch_usb_probe (struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
int ifnum = intf->altsetting->desc.bInterfaceNumber;
struct speedtch_instance_data *instance;
unsigned char mac_str [13];
int ret, i;
char buf7[SIZE_7];
dbg ("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d",
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 -ENODEV;
dbg ("speedtch_usb_probe: device accepted");
/* instance init */
if (!(instance = kmalloc (sizeof (struct speedtch_instance_data), GFP_KERNEL))) {
dbg ("speedtch_usb_probe: no memory for instance data!");
return -ENOMEM;
}
memset (instance, 0, sizeof (struct speedtch_instance_data));
if ((ret = usb_set_interface (dev, 0, 0)) < 0)
goto fail;
if ((ret = usb_set_interface (dev, 2, 0)) < 0)
goto fail;
instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA;
instance->u.firmware_wait = speedtch_firmware_wait;
instance->u.driver_name = speedtch_driver_name;
ret = udsl_instance_setup(dev, &instance->u);
if (ret)
goto fail;
init_timer(&instance->poll_timer);
instance->poll_timer.function = speedtch_timer_poll;
instance->poll_timer.data = (unsigned long)instance;
INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance);
switch (dev->descriptor.bcdDevice) {
case 0x0000:
case 0x0200:
case 0x0400:
case 0x0401:
instance->revision = dev->descriptor.bcdDevice;
break;
default:
instance->revision = 0x200;
printk(KERN_INFO "Unexpected SpeedTouch revision %04x, treating as Rev 2.00.\n",
dev->descriptor.bcdDevice);
break;
}
/* set MAC address, it is stored in the serial number */
memset (instance->u.atm_dev->esi, 0, sizeof (instance->u.atm_dev->esi));
if (usb_string (dev, dev->descriptor.iSerialNumber, mac_str, sizeof (mac_str)) == 12)
for (i = 0; i < 6; i++)
instance->u.atm_dev->esi [i] = (hex2int (mac_str [i * 2]) * 16) + (hex2int (mac_str [i * 2 + 1]));
/* First check whether the modem already seems to be alive */
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, HZ/2);
if (ret == SIZE_7) {
dbg("firmware appears to be already loaded");
speedtch_got_firmware(instance, 1);
speedtch_poll_status(instance);
} else {
speedtch_firmware_start (instance);
}
usb_set_intfdata (intf, instance);
return 0;
fail:
kfree (instance);
return -ENOMEM;
}
static void speedtch_usb_disconnect (struct usb_interface *intf)
{
struct speedtch_instance_data *instance = usb_get_intfdata (intf);
dbg ("speedtch_usb_disconnect entered");
if (!instance) {
dbg ("speedtch_usb_disconnect: NULL instance!");
return;
}
if (instance->int_urb) {
struct urb *int_urb = instance->int_urb;
instance->int_urb = NULL;
wmb();
usb_unlink_urb(int_urb);
usb_free_urb(int_urb);
}
instance->int_data[0] = 1;
del_timer_sync(&instance->poll_timer);
wmb();
flush_scheduled_work();
udsl_instance_disconnect(&instance->u);
/* clean up */
usb_set_intfdata (intf, NULL);
udsl_put_instance(&instance->u);
}
/***********
** init **
***********/
static int __init speedtch_usb_init (void)
{
dbg ("speedtch_usb_init: driver version " DRIVER_VERSION);
return usb_register (&speedtch_usb_driver);
}
static void __exit speedtch_usb_cleanup (void)
{
dbg ("speedtch_usb_cleanup entered");
usb_deregister (&speedtch_usb_driver);
}
module_init (speedtch_usb_init);
module_exit (speedtch_usb_cleanup);
MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_LICENSE ("GPL");
MODULE_VERSION (DRIVER_VERSION);
/******************************************************************************
* speedtouch.c - Alcatel SpeedTouch USB xDSL modem driver
* usb_atm.c - Generic USB xDSL driver core
*
* Copyright (C) 2001, Alcatel
* Copyright (C) 2003, Duncan Sands
* Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
* Copyright (C) 2004, David Woodhouse
*
* This program 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
......@@ -61,7 +62,7 @@
*
*/
#include <asm/semaphore.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
......@@ -70,6 +71,7 @@
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <asm/uaccess.h>
#include <linux/smp_lock.h>
......@@ -78,6 +80,9 @@
#include <linux/atmdev.h>
#include <linux/crc32.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include "usb_atm.h"
/*
#define DEBUG
......@@ -109,24 +114,6 @@ static int udsl_print_packet (const unsigned char *data, int len);
#define DRIVER_VERSION "1.8"
#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
static const char udsl_driver_name [] = "speedtch";
#define SPEEDTOUCH_VENDORID 0x06b9
#define SPEEDTOUCH_PRODUCTID 0x4061
#define UDSL_MAX_RCV_URBS 4
#define UDSL_MAX_SND_URBS 4
#define UDSL_MAX_RCV_BUFS 8
#define UDSL_MAX_SND_BUFS 8
#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */
#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */
#define UDSL_DEFAULT_RCV_URBS 2
#define UDSL_DEFAULT_SND_URBS 2
#define UDSL_DEFAULT_RCV_BUFS 4
#define UDSL_DEFAULT_SND_BUFS 4
#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */
#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */
static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS;
......@@ -152,118 +139,6 @@ MODULE_PARM_DESC (rcv_buf_size, "Size of the buffers used for reception (range:
module_param (snd_buf_size, uint, 0444);
MODULE_PARM_DESC (snd_buf_size, "Size of the buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUF_SIZE) ")");
#define UDSL_IOCTL_LINE_UP 1
#define UDSL_IOCTL_LINE_DOWN 2
#define UDSL_ENDPOINT_DATA_OUT 0x07
#define UDSL_ENDPOINT_DATA_IN 0x87
#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD)
#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
static struct usb_device_id udsl_usb_ids [] = {
{ USB_DEVICE (SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID) },
{ }
};
MODULE_DEVICE_TABLE (usb, udsl_usb_ids);
/* receive */
struct udsl_receive_buffer {
struct list_head list;
unsigned char *base;
unsigned int filled_cells;
};
struct udsl_receiver {
struct list_head list;
struct udsl_receive_buffer *buffer;
struct urb *urb;
struct udsl_instance_data *instance;
};
struct udsl_vcc_data {
/* vpi/vci lookup */
struct list_head list;
short vpi;
int vci;
struct atm_vcc *vcc;
/* raw cell reassembly */
struct sk_buff *sarb;
};
/* send */
struct udsl_send_buffer {
struct list_head list;
unsigned char *base;
unsigned char *free_start;
unsigned int free_cells;
};
struct udsl_sender {
struct list_head list;
struct udsl_send_buffer *buffer;
struct urb *urb;
struct udsl_instance_data *instance;
};
struct udsl_control {
struct atm_skb_data atm_data;
unsigned int num_cells;
unsigned int num_entire;
unsigned int pdu_padding;
unsigned char cell_header [ATM_CELL_HEADER];
unsigned char aal5_trailer [ATM_AAL5_TRAILER];
};
#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb)
/* main driver data */
struct udsl_instance_data {
struct semaphore serialize;
/* USB device part */
struct usb_device *usb_dev;
char description [64];
int firmware_loaded;
/* ATM device part */
struct atm_dev *atm_dev;
struct list_head vcc_list;
/* receive */
struct udsl_receiver receivers [UDSL_MAX_RCV_URBS];
struct udsl_receive_buffer receive_buffers [UDSL_MAX_RCV_BUFS];
spinlock_t receive_lock;
struct list_head spare_receivers;
struct list_head filled_receive_buffers;
struct tasklet_struct receive_tasklet;
struct list_head spare_receive_buffers;
/* send */
struct udsl_sender senders [UDSL_MAX_SND_URBS];
struct udsl_send_buffer send_buffers [UDSL_MAX_SND_BUFS];
struct sk_buff_head sndqueue;
spinlock_t send_lock;
struct list_head spare_senders;
struct list_head spare_send_buffers;
struct tasklet_struct send_tasklet;
struct sk_buff *current_skb; /* being emptied */
struct udsl_send_buffer *current_buffer; /* being filled */
struct list_head filled_send_buffers;
};
/* ATM */
static void udsl_atm_dev_close (struct atm_dev *dev);
......@@ -283,21 +158,6 @@ static struct atmdev_ops udsl_atm_devops = {
.owner = THIS_MODULE,
};
/* USB */
static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_id *id);
static void udsl_usb_disconnect (struct usb_interface *intf);
static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data);
static struct usb_driver udsl_usb_driver = {
.owner = THIS_MODULE,
.name = udsl_driver_name,
.probe = udsl_usb_probe,
.disconnect = udsl_usb_disconnect,
.ioctl = udsl_usb_ioctl,
.id_table = udsl_usb_ids,
};
/***********
** misc **
......@@ -339,7 +199,7 @@ static void udsl_extract_cells (struct udsl_instance_data *instance, unsigned ch
short cached_vpi = 0;
short vpi;
for (i = 0; i < howmany; i++, source += ATM_CELL_SIZE) {
for (i = 0; i < howmany; i++, source += ATM_CELL_SIZE + instance->rcv_padding) {
vpi = ((source [0] & 0x0f) << 4) | (source [1] >> 4);
vci = ((source [1] & 0x0f) << 12) | (source [2] << 4) | (source [3] >> 4);
pti = (source [3] & 0x2) != 0;
......@@ -475,7 +335,8 @@ static void udsl_groom_skb (struct atm_vcc *vcc, struct sk_buff *skb)
ctrl->aal5_trailer [7] = crc;
}
static unsigned int udsl_write_cells (unsigned int howmany, struct sk_buff *skb, unsigned char **target_p)
static unsigned int udsl_write_cells(struct udsl_instance_data *instance, unsigned int howmany,
struct sk_buff *skb, unsigned char **target_p)
{
struct udsl_control *ctrl = UDSL_SKB (skb);
unsigned char *target = *target_p;
......@@ -491,6 +352,10 @@ static unsigned int udsl_write_cells (unsigned int howmany, struct sk_buff *skb,
target += ATM_CELL_HEADER;
memcpy (target, skb->data, ATM_CELL_PAYLOAD);
target += ATM_CELL_PAYLOAD;
if (instance->snd_padding) {
memset (target, 0, instance->snd_padding);
target += instance->snd_padding;
}
__skb_pull (skb, ATM_CELL_PAYLOAD);
}
......@@ -499,6 +364,10 @@ static unsigned int udsl_write_cells (unsigned int howmany, struct sk_buff *skb,
if (!(ctrl->num_cells -= ne) || !(howmany -= ne))
goto out;
if (instance->snd_padding) {
memset (target, 0, instance->snd_padding);
target += instance->snd_padding;
}
memcpy (target, ctrl->cell_header, ATM_CELL_HEADER);
target += ATM_CELL_HEADER;
memcpy (target, skb->data, skb->len);
......@@ -525,7 +394,10 @@ static unsigned int udsl_write_cells (unsigned int howmany, struct sk_buff *skb,
target += ATM_AAL5_TRAILER;
/* set pti bit in last cell */
*(target + 3 - ATM_CELL_SIZE) |= 0x2;
if (instance->snd_padding) {
memset (target, 0, instance->snd_padding);
target += instance->snd_padding;
}
out:
*target_p = target;
return nc - ctrl->num_cells;
......@@ -551,7 +423,7 @@ static void udsl_complete_receive (struct urb *urb, struct pt_regs *regs)
instance = rcv->instance;
buf = rcv->buffer;
buf->filled_cells = urb->actual_length / ATM_CELL_SIZE;
buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rcv_padding);
vdbg ("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf);
......@@ -591,9 +463,9 @@ static void udsl_process_receive (unsigned long data)
usb_fill_bulk_urb (rcv->urb,
instance->usb_dev,
usb_rcvbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_IN),
usb_rcvbulkpipe (instance->usb_dev, instance->data_endpoint),
buf->base,
rcv_buf_size * ATM_CELL_SIZE,
rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding),
udsl_complete_receive,
rcv);
......@@ -676,9 +548,9 @@ static void udsl_process_send (unsigned long data)
snd->buffer = buf;
usb_fill_bulk_urb (snd->urb,
instance->usb_dev,
usb_sndbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_OUT),
usb_sndbulkpipe (instance->usb_dev, instance->data_endpoint),
buf->base,
(snd_buf_size - buf->free_cells) * ATM_CELL_SIZE,
(snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding),
udsl_complete_send,
snd);
......@@ -719,7 +591,7 @@ static void udsl_process_send (unsigned long data)
instance->current_buffer = buf;
}
num_written = udsl_write_cells (buf->free_cells, skb, &buf->free_start);
num_written = udsl_write_cells(instance, buf->free_cells, skb, &buf->free_start);
vdbg ("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p", num_written, skb, buf);
......@@ -773,7 +645,7 @@ static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb)
vdbg ("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len);
if (!instance || !instance->usb_dev) {
if (!instance) {
dbg ("udsl_atm_send: NULL data!");
err = -ENODEV;
goto fail;
......@@ -805,6 +677,33 @@ static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb)
}
/********************
** bean counting **
********************/
static void udsl_destroy_instance(struct kref *kref)
{
struct udsl_instance_data *instance = container_of (kref, struct udsl_instance_data, refcount);
tasklet_kill (&instance->receive_tasklet);
tasklet_kill (&instance->send_tasklet);
usb_put_dev (instance->usb_dev);
kfree (instance);
}
void udsl_get_instance(struct udsl_instance_data *instance)
{
kref_get (&instance->refcount);
}
void udsl_put_instance(struct udsl_instance_data *instance)
{
kref_put (&instance->refcount, udsl_destroy_instance);
}
/**********
** ATM **
**********/
......@@ -813,17 +712,8 @@ static void udsl_atm_dev_close (struct atm_dev *dev)
{
struct udsl_instance_data *instance = dev->dev_data;
if (!instance) {
dbg ("udsl_atm_dev_close: NULL instance!");
return;
}
dbg ("udsl_atm_dev_close: queue has %u elements", instance->sndqueue.qlen);
tasklet_kill (&instance->receive_tasklet);
tasklet_kill (&instance->send_tasklet);
kfree (instance);
dev->dev_data = NULL;
udsl_put_instance (instance);
}
static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page)
......@@ -865,13 +755,16 @@ static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page)
break;
}
if (instance->usb_dev) {
if (!instance->firmware_loaded)
strcat (page, ", no firmware\n");
else
strcat (page, ", firmware loaded\n");
} else
if (instance->usb_dev->state == USB_STATE_NOTATTACHED)
strcat (page, ", disconnected\n");
else {
if (instance->status == UDSL_LOADED_FIRMWARE)
strcat (page, ", firmware loaded\n");
else if (instance->status == UDSL_LOADING_FIRMWARE)
strcat (page, ", firmware loading\n");
else
strcat (page, ", no firmware\n");
}
return strlen (page);
}
......@@ -886,10 +779,11 @@ static int udsl_atm_open (struct atm_vcc *vcc)
unsigned int max_pdu;
int vci = vcc->vci;
short vpi = vcc->vpi;
int err;
dbg ("udsl_atm_open: vpi %hd, vci %d", vpi, vci);
if (!instance || !instance->usb_dev) {
if (!instance) {
dbg ("udsl_atm_open: NULL data!");
return -ENODEV;
}
......@@ -900,9 +794,10 @@ static int udsl_atm_open (struct atm_vcc *vcc)
return -EINVAL;
}
if (!instance->firmware_loaded) {
dbg ("udsl_atm_open: firmware not loaded!");
return -EAGAIN;
if (instance->firmware_wait &&
(err = instance->firmware_wait (instance)) < 0) {
dbg ("udsl_atm_open: firmware not loaded (%d)!", err);
return err;
}
down (&instance->serialize); /* vs self, udsl_atm_close */
......@@ -1006,75 +901,14 @@ static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void __user *a
** USB **
**********/
static int udsl_set_alternate (struct udsl_instance_data *instance)
{
down (&instance->serialize); /* vs self */
if (!instance->firmware_loaded) {
int ret;
if ((ret = usb_set_interface (instance->usb_dev, 1, 1)) < 0) {
dbg ("udsl_set_alternate: usb_set_interface returned %d!", ret);
up (&instance->serialize);
return ret;
}
instance->firmware_loaded = 1;
}
up (&instance->serialize);
tasklet_schedule (&instance->receive_tasklet);
return 0;
}
static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data)
int udsl_instance_setup(struct usb_device *dev, struct udsl_instance_data *instance)
{
struct udsl_instance_data *instance = usb_get_intfdata (intf);
dbg ("udsl_usb_ioctl entered");
if (!instance) {
dbg ("udsl_usb_ioctl: NULL instance!");
return -ENODEV;
}
switch (code) {
case UDSL_IOCTL_LINE_UP:
instance->atm_dev->signal = ATM_PHY_SIG_FOUND;
return udsl_set_alternate (instance);
case UDSL_IOCTL_LINE_DOWN:
instance->atm_dev->signal = ATM_PHY_SIG_LOST;
return 0;
default:
return -ENOTTY;
}
}
static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
int ifnum = intf->altsetting->desc.bInterfaceNumber;
struct udsl_instance_data *instance;
unsigned char mac_str [13];
int i, length;
char *buf;
int i, length;
dbg ("udsl_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d",
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 -ENODEV;
dbg ("udsl_usb_probe: device accepted");
/* instance init */
if (!(instance = kmalloc (sizeof (struct udsl_instance_data), GFP_KERNEL))) {
dbg ("udsl_usb_probe: no memory for instance data!");
return -ENOMEM;
}
kref_init (&instance->refcount); /* one for USB */
udsl_get_instance (instance); /* one for ATM */
memset (instance, 0, sizeof (struct udsl_instance_data));
init_MUTEX (&instance->serialize);
......@@ -1082,6 +916,9 @@ static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_i
INIT_LIST_HEAD (&instance->vcc_list);
instance->status = UDSL_NO_FIRMWARE;
init_waitqueue_head (&instance->firmware_waiters);
spin_lock_init (&instance->receive_lock);
INIT_LIST_HEAD (&instance->spare_receivers);
INIT_LIST_HEAD (&instance->filled_receive_buffers);
......@@ -1115,7 +952,7 @@ static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_i
for (i = 0; i < num_rcv_bufs; i++) {
struct udsl_receive_buffer *buf = &(instance->receive_buffers [i]);
if (!(buf->base = kmalloc (rcv_buf_size * ATM_CELL_SIZE, GFP_KERNEL))) {
if (!(buf->base = kmalloc (rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), GFP_KERNEL))) {
dbg ("udsl_usb_probe: no memory for receive buffer %d!", i);
goto fail;
}
......@@ -1140,7 +977,7 @@ static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_i
for (i = 0; i < num_snd_bufs; i++) {
struct udsl_send_buffer *buf = &(instance->send_buffers [i]);
if (!(buf->base = kmalloc (snd_buf_size * ATM_CELL_SIZE, GFP_KERNEL))) {
if (!(buf->base = kmalloc (snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding), GFP_KERNEL))) {
dbg ("udsl_usb_probe: no memory for send buffer %d!", i);
goto fail;
}
......@@ -1149,7 +986,7 @@ static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_i
}
/* ATM init */
if (!(instance->atm_dev = atm_dev_register (udsl_driver_name, &udsl_atm_devops, -1, NULL))) {
if (!(instance->atm_dev = atm_dev_register (instance->driver_name, &udsl_atm_devops, -1, NULL))) {
dbg ("udsl_usb_probe: failed to register ATM device!");
goto fail;
}
......@@ -1161,12 +998,6 @@ static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_i
/* temp init ATM device, set to 128kbit */
instance->atm_dev->link_rate = 128 * 1000 / 424;
/* set MAC address, it is stored in the serial number */
memset (instance->atm_dev->esi, 0, sizeof (instance->atm_dev->esi));
if (usb_string (dev, dev->descriptor.iSerialNumber, mac_str, sizeof (mac_str)) == 12)
for (i = 0; i < 6; i++)
instance->atm_dev->esi [i] = (hex2int (mac_str [i * 2]) * 16) + (hex2int (mac_str [i * 2 + 1]));
/* device description */
buf = instance->description;
length = sizeof (instance->description);
......@@ -1194,7 +1025,7 @@ static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_i
wmb ();
instance->atm_dev->dev_data = instance;
usb_set_intfdata (intf, instance);
usb_get_dev (dev);
return 0;
......@@ -1211,24 +1042,19 @@ static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_i
for (i = 0; i < num_rcv_urbs; i++)
usb_free_urb (instance->receivers [i].urb);
kfree (instance);
return -ENOMEM;
}
static void udsl_usb_disconnect (struct usb_interface *intf)
void udsl_instance_disconnect (struct udsl_instance_data *instance)
{
struct udsl_instance_data *instance = usb_get_intfdata (intf);
struct list_head *pos;
unsigned int count;
int result, i;
dbg ("udsl_usb_disconnect entered");
usb_set_intfdata (intf, NULL);
dbg ("udsl_instance_disconnect entered");
if (!instance) {
dbg ("udsl_usb_disconnect: NULL instance!");
dbg ("udsl_instance_disconnect: NULL instance!");
return;
}
......@@ -1237,7 +1063,7 @@ static void udsl_usb_disconnect (struct usb_interface *intf)
for (i = 0; i < num_rcv_urbs; i++)
if ((result = usb_unlink_urb (instance->receivers [i].urb)) < 0)
dbg ("udsl_usb_disconnect: usb_unlink_urb on receive urb %d returned %d!", i, result);
dbg ("udsl_instance_disconnect: usb_unlink_urb on receive urb %d returned %d!", i, result);
/* wait for completion handlers to finish */
do {
......@@ -1247,7 +1073,7 @@ static void udsl_usb_disconnect (struct usb_interface *intf)
DEBUG_ON (++count > num_rcv_urbs);
spin_unlock_irq (&instance->receive_lock);
dbg ("udsl_usb_disconnect: found %u spare receivers", count);
dbg ("udsl_instance_disconnect: found %u spare receivers", count);
if (count == num_rcv_urbs)
break;
......@@ -1273,7 +1099,7 @@ static void udsl_usb_disconnect (struct usb_interface *intf)
for (i = 0; i < num_snd_urbs; i++)
if ((result = usb_unlink_urb (instance->senders [i].urb)) < 0)
dbg ("udsl_usb_disconnect: usb_unlink_urb on send urb %d returned %d!", i, result);
dbg ("udsl_instance_disconnect: usb_unlink_urb on send urb %d returned %d!", i, result);
/* wait for completion handlers to finish */
do {
......@@ -1283,7 +1109,7 @@ static void udsl_usb_disconnect (struct usb_interface *intf)
DEBUG_ON (++count > num_snd_urbs);
spin_unlock_irq (&instance->send_lock);
dbg ("udsl_usb_disconnect: found %u spare senders", count);
dbg ("udsl_instance_disconnect: found %u spare senders", count);
if (count == num_snd_urbs)
break;
......@@ -1305,13 +1131,14 @@ static void udsl_usb_disconnect (struct usb_interface *intf)
for (i = 0; i < num_snd_bufs; i++)
kfree (instance->send_buffers [i].base);
wmb ();
instance->usb_dev = NULL;
/* ATM finalize */
shutdown_atm_dev (instance->atm_dev); /* frees instance, kills tasklets */
shutdown_atm_dev (instance->atm_dev);
}
EXPORT_SYMBOL_GPL(udsl_get_instance);
EXPORT_SYMBOL_GPL(udsl_put_instance);
EXPORT_SYMBOL_GPL(udsl_instance_setup);
EXPORT_SYMBOL_GPL(udsl_instance_disconnect);
/***********
** init **
......@@ -1331,18 +1158,10 @@ static int __init udsl_usb_init (void)
(rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE) || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
return -EINVAL;
return usb_register (&udsl_usb_driver);
}
static void __exit udsl_usb_cleanup (void)
{
dbg ("udsl_usb_cleanup entered");
usb_deregister (&udsl_usb_driver);
return 0;
}
module_init (udsl_usb_init);
module_exit (udsl_usb_cleanup);
MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_DESCRIPTION (DRIVER_DESC);
......
/******************************************************************************
* usb_atm.h - Generic USB xDSL driver core
*
* Copyright (C) 2001, Alcatel
* Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
* Copyright (C) 2004, David Woodhouse
*
* This program 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.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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.
*
******************************************************************************/
#include <linux/list.h>
#include <linux/usb.h>
#include <linux/kref.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <asm/semaphore.h>
#define UDSL_MAX_RCV_URBS 4
#define UDSL_MAX_SND_URBS 4
#define UDSL_MAX_RCV_BUFS 8
#define UDSL_MAX_SND_BUFS 8
#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */
#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */
#define UDSL_DEFAULT_RCV_URBS 2
#define UDSL_DEFAULT_SND_URBS 2
#define UDSL_DEFAULT_RCV_BUFS 4
#define UDSL_DEFAULT_SND_BUFS 4
#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */
#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */
#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD)
/* receive */
struct udsl_receive_buffer {
struct list_head list;
unsigned char *base;
unsigned int filled_cells;
};
struct udsl_receiver {
struct list_head list;
struct udsl_receive_buffer *buffer;
struct urb *urb;
struct udsl_instance_data *instance;
};
struct udsl_vcc_data {
/* vpi/vci lookup */
struct list_head list;
short vpi;
int vci;
struct atm_vcc *vcc;
/* raw cell reassembly */
struct sk_buff *sarb;
};
/* send */
struct udsl_send_buffer {
struct list_head list;
unsigned char *base;
unsigned char *free_start;
unsigned int free_cells;
};
struct udsl_sender {
struct list_head list;
struct udsl_send_buffer *buffer;
struct urb *urb;
struct udsl_instance_data *instance;
};
struct udsl_control {
struct atm_skb_data atm_data;
unsigned int num_cells;
unsigned int num_entire;
unsigned int pdu_padding;
unsigned char cell_header [ATM_CELL_HEADER];
unsigned char aal5_trailer [ATM_AAL5_TRAILER];
};
#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb)
/* main driver data */
enum udsl_status {
UDSL_NO_FIRMWARE,
UDSL_LOADING_FIRMWARE,
UDSL_LOADED_FIRMWARE
};
struct udsl_instance_data {
struct kref refcount;
struct semaphore serialize;
/* USB device part */
struct usb_device *usb_dev;
char description [64];
int data_endpoint;
int snd_padding;
int rcv_padding;
const char *driver_name;
/* ATM device part */
struct atm_dev *atm_dev;
struct list_head vcc_list;
/* firmware */
int (*firmware_wait) (struct udsl_instance_data *);
enum udsl_status status;
wait_queue_head_t firmware_waiters;
/* receive */
struct udsl_receiver receivers [UDSL_MAX_RCV_URBS];
struct udsl_receive_buffer receive_buffers [UDSL_MAX_RCV_BUFS];
spinlock_t receive_lock;
struct list_head spare_receivers;
struct list_head filled_receive_buffers;
struct tasklet_struct receive_tasklet;
struct list_head spare_receive_buffers;
/* send */
struct udsl_sender senders [UDSL_MAX_SND_URBS];
struct udsl_send_buffer send_buffers [UDSL_MAX_SND_BUFS];
struct sk_buff_head sndqueue;
spinlock_t send_lock;
struct list_head spare_senders;
struct list_head spare_send_buffers;
struct tasklet_struct send_tasklet;
struct sk_buff *current_skb; /* being emptied */
struct udsl_send_buffer *current_buffer; /* being filled */
struct list_head filled_send_buffers;
};
extern int udsl_instance_setup(struct usb_device *dev, struct udsl_instance_data *instance);
extern void udsl_instance_disconnect (struct udsl_instance_data *instance);
extern void udsl_get_instance(struct udsl_instance_data *instance);
extern void udsl_put_instance(struct udsl_instance_data *instance);
......@@ -121,18 +121,6 @@ config USB_CYTHERM
To compile this driver as a module, choose M here: the
module will be called cytherm.
config USB_SPEEDTOUCH
tristate "Alcatel Speedtouch USB support"
depends on USB && ATM
select CRC32
help
Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330
modem. In order to use your modem you will need to install some user
space tools, see <http://www.linux-usb.org/SpeedTouch/> for details.
To compile this driver as a module, choose M here: the
module will be called speedtch.
config USB_PHIDGETSERVO
tristate "USB PhidgetServo support"
depends on USB
......
......@@ -11,7 +11,6 @@ obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LED) += usbled.o
obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
obj-$(CONFIG_USB_RIO500) += rio500.o
obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_TIGL) += tiglusb.o
obj-$(CONFIG_USB_USS720) += uss720.o
......
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