mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-29 23:28:52 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
68
drivers/usb/atm/Kconfig
Normal file
68
drivers/usb/atm/Kconfig
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#
|
||||
# USB/ATM DSL configuration
|
||||
#
|
||||
|
||||
menuconfig USB_ATM
|
||||
tristate "USB DSL modem support"
|
||||
depends on ATM
|
||||
select CRC32
|
||||
default n
|
||||
help
|
||||
Say Y here if you want to connect a USB Digital Subscriber Line (DSL)
|
||||
modem to your computer's USB port. You will then need to choose your
|
||||
modem from the list below.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbatm.
|
||||
|
||||
if USB_ATM
|
||||
|
||||
config USB_SPEEDTOUCH
|
||||
tristate "Speedtouch USB support"
|
||||
select FW_LOADER
|
||||
help
|
||||
Say Y here if you have an 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.
|
||||
|
||||
config USB_CXACRU
|
||||
tristate "Conexant AccessRunner USB support"
|
||||
select FW_LOADER
|
||||
help
|
||||
Say Y here if you have an ADSL USB modem based on the Conexant
|
||||
AccessRunner chipset. In order to use your modem you will need to
|
||||
install the firmware, extracted by the user space tools; see
|
||||
<http://accessrunner.sourceforge.net/> for details.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cxacru.
|
||||
|
||||
config USB_UEAGLEATM
|
||||
tristate "ADI 930 and eagle USB DSL modem"
|
||||
select FW_LOADER
|
||||
help
|
||||
Say Y here if you have an ADSL USB modem based on the ADI 930
|
||||
or eagle chipset. In order to use your modem you will need to
|
||||
install firmwares and CMV (Command Management Variables); see
|
||||
<https://gna.org/projects/ueagleatm/> for details.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ueagle-atm.
|
||||
|
||||
config USB_XUSBATM
|
||||
tristate "Other USB DSL modem support"
|
||||
help
|
||||
Say Y here if you have a DSL USB modem not explicitly supported by
|
||||
another USB DSL drivers. In order to use your modem you will need to
|
||||
pass the vendor ID, product ID, and endpoint numbers for transmission
|
||||
and reception as module parameters. You may need to initialize
|
||||
the modem using a user space utility (a firmware loader for example).
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called xusbatm.
|
||||
|
||||
endif # USB_ATM
|
||||
8
drivers/usb/atm/Makefile
Normal file
8
drivers/usb/atm/Makefile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# Makefile for USB ATM/xDSL drivers
|
||||
#
|
||||
obj-$(CONFIG_USB_CXACRU) += cxacru.o
|
||||
obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
|
||||
obj-$(CONFIG_USB_UEAGLEATM) += ueagle-atm.o
|
||||
obj-$(CONFIG_USB_ATM) += usbatm.o
|
||||
obj-$(CONFIG_USB_XUSBATM) += xusbatm.o
|
||||
1380
drivers/usb/atm/cxacru.c
Normal file
1380
drivers/usb/atm/cxacru.c
Normal file
File diff suppressed because it is too large
Load diff
960
drivers/usb/atm/speedtch.c
Normal file
960
drivers/usb/atm/speedtch.c
Normal file
|
|
@ -0,0 +1,960 @@
|
|||
/******************************************************************************
|
||||
* speedtch.c - Alcatel SpeedTouch USB xDSL modem driver
|
||||
*
|
||||
* Copyright (C) 2001, Alcatel
|
||||
* Copyright (C) 2003, Duncan Sands
|
||||
* Copyright (C) 2004, David Woodhouse
|
||||
*
|
||||
* Based on "modem_run.c", copyright (C) 2001, Benoit Papillault
|
||||
*
|
||||
* 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 <asm/page.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "usbatm.h"
|
||||
|
||||
#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
|
||||
#define DRIVER_VERSION "1.10"
|
||||
#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
|
||||
|
||||
static const char speedtch_driver_name[] = "speedtch";
|
||||
|
||||
#define CTRL_TIMEOUT 2000 /* milliseconds */
|
||||
#define DATA_TIMEOUT 2000 /* milliseconds */
|
||||
|
||||
#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 SIZE_7 1
|
||||
#define SIZE_b 8
|
||||
#define SIZE_d 4
|
||||
#define SIZE_e 1
|
||||
#define SIZE_f 1
|
||||
|
||||
#define MIN_POLL_DELAY 5000 /* milliseconds */
|
||||
#define MAX_POLL_DELAY 60000 /* milliseconds */
|
||||
|
||||
#define RESUBMIT_DELAY 1000 /* milliseconds */
|
||||
|
||||
#define DEFAULT_BULK_ALTSETTING 1
|
||||
#define DEFAULT_ISOC_ALTSETTING 3
|
||||
#define DEFAULT_DL_512_FIRST 0
|
||||
#define DEFAULT_ENABLE_ISOC 0
|
||||
#define DEFAULT_SW_BUFFERING 0
|
||||
|
||||
static unsigned int altsetting = 0; /* zero means: use the default */
|
||||
static bool dl_512_first = DEFAULT_DL_512_FIRST;
|
||||
static bool enable_isoc = DEFAULT_ENABLE_ISOC;
|
||||
static bool sw_buffering = DEFAULT_SW_BUFFERING;
|
||||
|
||||
#define DEFAULT_B_MAX_DSL 8128
|
||||
#define DEFAULT_MODEM_MODE 11
|
||||
#define MODEM_OPTION_LENGTH 16
|
||||
static const unsigned char DEFAULT_MODEM_OPTION[MODEM_OPTION_LENGTH] = {
|
||||
0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static unsigned int BMaxDSL = DEFAULT_B_MAX_DSL;
|
||||
static unsigned char ModemMode = DEFAULT_MODEM_MODE;
|
||||
static unsigned char ModemOption[MODEM_OPTION_LENGTH];
|
||||
static unsigned int num_ModemOption;
|
||||
|
||||
module_param(altsetting, uint, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(altsetting,
|
||||
"Alternative setting for data interface (bulk_default: "
|
||||
__MODULE_STRING(DEFAULT_BULK_ALTSETTING) "; isoc_default: "
|
||||
__MODULE_STRING(DEFAULT_ISOC_ALTSETTING) ")");
|
||||
|
||||
module_param(dl_512_first, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(dl_512_first,
|
||||
"Read 512 bytes before sending firmware (default: "
|
||||
__MODULE_STRING(DEFAULT_DL_512_FIRST) ")");
|
||||
|
||||
module_param(enable_isoc, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(enable_isoc,
|
||||
"Use isochronous transfers if available (default: "
|
||||
__MODULE_STRING(DEFAULT_ENABLE_ISOC) ")");
|
||||
|
||||
module_param(sw_buffering, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(sw_buffering,
|
||||
"Enable software buffering (default: "
|
||||
__MODULE_STRING(DEFAULT_SW_BUFFERING) ")");
|
||||
|
||||
module_param(BMaxDSL, uint, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(BMaxDSL,
|
||||
"default: " __MODULE_STRING(DEFAULT_B_MAX_DSL));
|
||||
|
||||
module_param(ModemMode, byte, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(ModemMode,
|
||||
"default: " __MODULE_STRING(DEFAULT_MODEM_MODE));
|
||||
|
||||
module_param_array(ModemOption, byte, &num_ModemOption, S_IRUGO);
|
||||
MODULE_PARM_DESC(ModemOption, "default: 0x10,0x00,0x00,0x00,0x20");
|
||||
|
||||
#define INTERFACE_DATA 1
|
||||
#define ENDPOINT_INT 0x81
|
||||
#define ENDPOINT_BULK_DATA 0x07
|
||||
#define ENDPOINT_ISOC_DATA 0x07
|
||||
#define ENDPOINT_FIRMWARE 0x05
|
||||
|
||||
struct speedtch_params {
|
||||
unsigned int altsetting;
|
||||
unsigned int BMaxDSL;
|
||||
unsigned char ModemMode;
|
||||
unsigned char ModemOption[MODEM_OPTION_LENGTH];
|
||||
};
|
||||
|
||||
struct speedtch_instance_data {
|
||||
struct usbatm_data *usbatm;
|
||||
|
||||
struct speedtch_params params; /* set in probe, constant afterwards */
|
||||
|
||||
struct timer_list status_check_timer;
|
||||
struct work_struct status_check_work;
|
||||
|
||||
unsigned char last_status;
|
||||
|
||||
int poll_delay; /* milliseconds */
|
||||
|
||||
struct timer_list resubmit_timer;
|
||||
struct urb *int_urb;
|
||||
unsigned char int_data[16];
|
||||
|
||||
unsigned char scratch_buffer[16];
|
||||
};
|
||||
|
||||
/***************
|
||||
** firmware **
|
||||
***************/
|
||||
|
||||
static void speedtch_set_swbuff(struct speedtch_instance_data *instance, int state)
|
||||
{
|
||||
struct usbatm_data *usbatm = instance->usbatm;
|
||||
struct usb_device *usb_dev = usbatm->usb_dev;
|
||||
int ret;
|
||||
|
||||
ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
0x32, 0x40, state ? 0x01 : 0x00, 0x00, NULL, 0, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm,
|
||||
"%sabling SW buffering: usb_control_msg returned %d\n",
|
||||
state ? "En" : "Dis", ret);
|
||||
else
|
||||
usb_dbg(usbatm, "speedtch_set_swbuff: %sbled SW buffering\n", state ? "En" : "Dis");
|
||||
}
|
||||
|
||||
static void speedtch_test_sequence(struct speedtch_instance_data *instance)
|
||||
{
|
||||
struct usbatm_data *usbatm = instance->usbatm;
|
||||
struct usb_device *usb_dev = usbatm->usb_dev;
|
||||
unsigned char *buf = instance->scratch_buffer;
|
||||
int ret;
|
||||
|
||||
/* URB 147 */
|
||||
buf[0] = 0x1c;
|
||||
buf[1] = 0x50;
|
||||
ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
0x01, 0x40, 0x0b, 0x00, buf, 2, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm, "%s failed on URB147: %d\n", __func__, ret);
|
||||
|
||||
/* URB 148 */
|
||||
buf[0] = 0x32;
|
||||
buf[1] = 0x00;
|
||||
ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
0x01, 0x40, 0x02, 0x00, buf, 2, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm, "%s failed on URB148: %d\n", __func__, ret);
|
||||
|
||||
/* URB 149 */
|
||||
buf[0] = 0x01;
|
||||
buf[1] = 0x00;
|
||||
buf[2] = 0x01;
|
||||
ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
0x01, 0x40, 0x03, 0x00, buf, 3, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm, "%s failed on URB149: %d\n", __func__, ret);
|
||||
|
||||
/* URB 150 */
|
||||
buf[0] = 0x01;
|
||||
buf[1] = 0x00;
|
||||
buf[2] = 0x01;
|
||||
ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
0x01, 0x40, 0x04, 0x00, buf, 3, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm, "%s failed on URB150: %d\n", __func__, ret);
|
||||
|
||||
/* Extra initialisation in recent drivers - gives higher speeds */
|
||||
|
||||
/* URBext1 */
|
||||
buf[0] = instance->params.ModemMode;
|
||||
ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
0x01, 0x40, 0x11, 0x00, buf, 1, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm, "%s failed on URBext1: %d\n", __func__, ret);
|
||||
|
||||
/* URBext2 */
|
||||
/* This seems to be the one which actually triggers the higher sync
|
||||
rate -- it does require the new firmware too, although it works OK
|
||||
with older firmware */
|
||||
ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
0x01, 0x40, 0x14, 0x00,
|
||||
instance->params.ModemOption,
|
||||
MODEM_OPTION_LENGTH, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm, "%s failed on URBext2: %d\n", __func__, ret);
|
||||
|
||||
/* URBext3 */
|
||||
buf[0] = instance->params.BMaxDSL & 0xff;
|
||||
buf[1] = instance->params.BMaxDSL >> 8;
|
||||
ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
|
||||
0x01, 0x40, 0x12, 0x00, buf, 2, CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
usb_warn(usbatm, "%s failed on URBext3: %d\n", __func__, ret);
|
||||
}
|
||||
|
||||
static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
|
||||
const struct firmware *fw1,
|
||||
const struct firmware *fw2)
|
||||
{
|
||||
unsigned char *buffer;
|
||||
struct usbatm_data *usbatm = instance->usbatm;
|
||||
struct usb_device *usb_dev = usbatm->usb_dev;
|
||||
int actual_length;
|
||||
int ret = 0;
|
||||
int offset;
|
||||
|
||||
usb_dbg(usbatm, "%s entered\n", __func__);
|
||||
|
||||
if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) {
|
||||
ret = -ENOMEM;
|
||||
usb_dbg(usbatm, "%s: no memory for buffer!\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!usb_ifnum_to_if(usb_dev, 2)) {
|
||||
ret = -ENODEV;
|
||||
usb_dbg(usbatm, "%s: interface not found!\n", __func__);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* URB 7 */
|
||||
if (dl_512_first) { /* some modems need a read before writing the firmware */
|
||||
ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
|
||||
buffer, 0x200, &actual_length, 2000);
|
||||
|
||||
if (ret < 0 && ret != -ETIMEDOUT)
|
||||
usb_warn(usbatm, "%s: read BLOCK0 from modem failed (%d)!\n", __func__, ret);
|
||||
else
|
||||
usb_dbg(usbatm, "%s: BLOCK0 downloaded (%d bytes)\n", __func__, ret);
|
||||
}
|
||||
|
||||
/* URB 8 : both leds are static green */
|
||||
for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) {
|
||||
int thislen = min_t(int, PAGE_SIZE, fw1->size - offset);
|
||||
memcpy(buffer, fw1->data + offset, thislen);
|
||||
|
||||
ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
|
||||
buffer, thislen, &actual_length, DATA_TIMEOUT);
|
||||
|
||||
if (ret < 0) {
|
||||
usb_err(usbatm, "%s: write BLOCK1 to modem failed (%d)!\n", __func__, ret);
|
||||
goto out_free;
|
||||
}
|
||||
usb_dbg(usbatm, "%s: BLOCK1 uploaded (%zu bytes)\n", __func__, fw1->size);
|
||||
}
|
||||
|
||||
/* USB led blinking green, ADSL led off */
|
||||
|
||||
/* URB 11 */
|
||||
ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
|
||||
buffer, 0x200, &actual_length, DATA_TIMEOUT);
|
||||
|
||||
if (ret < 0) {
|
||||
usb_err(usbatm, "%s: read BLOCK2 from modem failed (%d)!\n", __func__, ret);
|
||||
goto out_free;
|
||||
}
|
||||
usb_dbg(usbatm, "%s: BLOCK2 downloaded (%d bytes)\n", __func__, actual_length);
|
||||
|
||||
/* URBs 12 to 139 - USB led blinking green, ADSL led off */
|
||||
for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) {
|
||||
int thislen = min_t(int, PAGE_SIZE, fw2->size - offset);
|
||||
memcpy(buffer, fw2->data + offset, thislen);
|
||||
|
||||
ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
|
||||
buffer, thislen, &actual_length, DATA_TIMEOUT);
|
||||
|
||||
if (ret < 0) {
|
||||
usb_err(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret);
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
usb_dbg(usbatm, "%s: BLOCK3 uploaded (%zu bytes)\n", __func__, fw2->size);
|
||||
|
||||
/* USB led static green, ADSL led static red */
|
||||
|
||||
/* URB 142 */
|
||||
ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
|
||||
buffer, 0x200, &actual_length, DATA_TIMEOUT);
|
||||
|
||||
if (ret < 0) {
|
||||
usb_err(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* success */
|
||||
usb_dbg(usbatm, "%s: BLOCK4 downloaded (%d bytes)\n", __func__, actual_length);
|
||||
|
||||
/* Delay to allow firmware to start up. We can do this here
|
||||
because we're in our own kernel thread anyway. */
|
||||
msleep_interruptible(1000);
|
||||
|
||||
if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->params.altsetting)) < 0) {
|
||||
usb_err(usbatm, "%s: setting interface to %d failed (%d)!\n", __func__, instance->params.altsetting, ret);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
ret = 0;
|
||||
|
||||
out_free:
|
||||
free_page((unsigned long)buffer);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int speedtch_find_firmware(struct usbatm_data *usbatm, struct usb_interface *intf,
|
||||
int phase, const struct firmware **fw_p)
|
||||
{
|
||||
struct device *dev = &intf->dev;
|
||||
const u16 bcdDevice = le16_to_cpu(interface_to_usbdev(intf)->descriptor.bcdDevice);
|
||||
const u8 major_revision = bcdDevice >> 8;
|
||||
const u8 minor_revision = bcdDevice & 0xff;
|
||||
char buf[24];
|
||||
|
||||
sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
|
||||
usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf);
|
||||
|
||||
if (request_firmware(fw_p, buf, dev)) {
|
||||
sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision);
|
||||
usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf);
|
||||
|
||||
if (request_firmware(fw_p, buf, dev)) {
|
||||
sprintf(buf, "speedtch-%d.bin", phase);
|
||||
usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf);
|
||||
|
||||
if (request_firmware(fw_p, buf, dev)) {
|
||||
usb_err(usbatm, "%s: no stage %d firmware found!\n", __func__, phase);
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usb_info(usbatm, "found stage %d firmware %s\n", phase, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int speedtch_heavy_init(struct usbatm_data *usbatm, struct usb_interface *intf)
|
||||
{
|
||||
const struct firmware *fw1, *fw2;
|
||||
struct speedtch_instance_data *instance = usbatm->driver_data;
|
||||
int ret;
|
||||
|
||||
if ((ret = speedtch_find_firmware(usbatm, intf, 1, &fw1)) < 0)
|
||||
return ret;
|
||||
|
||||
if ((ret = speedtch_find_firmware(usbatm, intf, 2, &fw2)) < 0) {
|
||||
release_firmware(fw1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = speedtch_upload_firmware(instance, fw1, fw2)) < 0)
|
||||
usb_err(usbatm, "%s: firmware upload failed (%d)!\n", __func__, ret);
|
||||
|
||||
release_firmware(fw2);
|
||||
release_firmware(fw1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
** ATM **
|
||||
**********/
|
||||
|
||||
static int speedtch_read_status(struct speedtch_instance_data *instance)
|
||||
{
|
||||
struct usbatm_data *usbatm = instance->usbatm;
|
||||
struct usb_device *usb_dev = usbatm->usb_dev;
|
||||
unsigned char *buf = instance->scratch_buffer;
|
||||
int ret;
|
||||
|
||||
memset(buf, 0, 16);
|
||||
|
||||
ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
|
||||
CTRL_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
atm_dbg(usbatm, "%s: MSG 7 failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b,
|
||||
CTRL_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
atm_dbg(usbatm, "%s: MSG B failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d,
|
||||
CTRL_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
atm_dbg(usbatm, "%s: MSG D failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e,
|
||||
CTRL_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
atm_dbg(usbatm, "%s: MSG E failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f,
|
||||
CTRL_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
atm_dbg(usbatm, "%s: MSG F failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int speedtch_start_synchro(struct speedtch_instance_data *instance)
|
||||
{
|
||||
struct usbatm_data *usbatm = instance->usbatm;
|
||||
struct usb_device *usb_dev = usbatm->usb_dev;
|
||||
unsigned char *buf = instance->scratch_buffer;
|
||||
int ret;
|
||||
|
||||
atm_dbg(usbatm, "%s entered\n", __func__);
|
||||
|
||||
memset(buf, 0, 2);
|
||||
|
||||
ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
0x12, 0xc0, 0x04, 0x00,
|
||||
buf, 2, CTRL_TIMEOUT);
|
||||
|
||||
if (ret < 0)
|
||||
atm_warn(usbatm, "failed to start ADSL synchronisation: %d\n", ret);
|
||||
else
|
||||
atm_dbg(usbatm, "%s: modem prodded. %d bytes returned: %02x %02x\n",
|
||||
__func__, ret, buf[0], buf[1]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void speedtch_check_status(struct work_struct *work)
|
||||
{
|
||||
struct speedtch_instance_data *instance =
|
||||
container_of(work, struct speedtch_instance_data,
|
||||
status_check_work);
|
||||
struct usbatm_data *usbatm = instance->usbatm;
|
||||
struct atm_dev *atm_dev = usbatm->atm_dev;
|
||||
unsigned char *buf = instance->scratch_buffer;
|
||||
int down_speed, up_speed, ret;
|
||||
unsigned char status;
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
atm_dbg(usbatm, "%s entered\n", __func__);
|
||||
#endif
|
||||
|
||||
ret = speedtch_read_status(instance);
|
||||
if (ret < 0) {
|
||||
atm_warn(usbatm, "error %d fetching device status\n", ret);
|
||||
instance->poll_delay = min(2 * instance->poll_delay, MAX_POLL_DELAY);
|
||||
return;
|
||||
}
|
||||
|
||||
instance->poll_delay = max(instance->poll_delay / 2, MIN_POLL_DELAY);
|
||||
|
||||
status = buf[OFFSET_7];
|
||||
|
||||
if ((status != instance->last_status) || !status) {
|
||||
atm_dbg(usbatm, "%s: line state 0x%02x\n", __func__, status);
|
||||
|
||||
switch (status) {
|
||||
case 0:
|
||||
atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
|
||||
if (instance->last_status)
|
||||
atm_info(usbatm, "ADSL line is down\n");
|
||||
/* It may never resync again unless we ask it to... */
|
||||
ret = speedtch_start_synchro(instance);
|
||||
break;
|
||||
|
||||
case 0x08:
|
||||
atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN);
|
||||
atm_info(usbatm, "ADSL line is blocked?\n");
|
||||
break;
|
||||
|
||||
case 0x10:
|
||||
atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
|
||||
atm_info(usbatm, "ADSL line is synchronising\n");
|
||||
break;
|
||||
|
||||
case 0x20:
|
||||
down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8)
|
||||
| (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24);
|
||||
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;
|
||||
}
|
||||
|
||||
atm_dev->link_rate = down_speed * 1000 / 424;
|
||||
atm_dev_signal_change(atm_dev, ATM_PHY_SIG_FOUND);
|
||||
|
||||
atm_info(usbatm,
|
||||
"ADSL line is up (%d kb/s down | %d kb/s up)\n",
|
||||
down_speed, up_speed);
|
||||
break;
|
||||
|
||||
default:
|
||||
atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN);
|
||||
atm_info(usbatm, "unknown line state %02x\n", status);
|
||||
break;
|
||||
}
|
||||
|
||||
instance->last_status = status;
|
||||
}
|
||||
}
|
||||
|
||||
static void speedtch_status_poll(unsigned long data)
|
||||
{
|
||||
struct speedtch_instance_data *instance = (void *)data;
|
||||
|
||||
schedule_work(&instance->status_check_work);
|
||||
|
||||
/* The following check is racy, but the race is harmless */
|
||||
if (instance->poll_delay < MAX_POLL_DELAY)
|
||||
mod_timer(&instance->status_check_timer, jiffies + msecs_to_jiffies(instance->poll_delay));
|
||||
else
|
||||
atm_warn(instance->usbatm, "Too many failures - disabling line status polling\n");
|
||||
}
|
||||
|
||||
static void speedtch_resubmit_int(unsigned long data)
|
||||
{
|
||||
struct speedtch_instance_data *instance = (void *)data;
|
||||
struct urb *int_urb = instance->int_urb;
|
||||
int ret;
|
||||
|
||||
atm_dbg(instance->usbatm, "%s entered\n", __func__);
|
||||
|
||||
if (int_urb) {
|
||||
ret = usb_submit_urb(int_urb, GFP_ATOMIC);
|
||||
if (!ret)
|
||||
schedule_work(&instance->status_check_work);
|
||||
else {
|
||||
atm_dbg(instance->usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret);
|
||||
mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void speedtch_handle_int(struct urb *int_urb)
|
||||
{
|
||||
struct speedtch_instance_data *instance = int_urb->context;
|
||||
struct usbatm_data *usbatm = instance->usbatm;
|
||||
unsigned int count = int_urb->actual_length;
|
||||
int status = int_urb->status;
|
||||
int ret;
|
||||
|
||||
/* The magic interrupt for "up state" */
|
||||
static const unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 };
|
||||
/* The magic interrupt for "down state" */
|
||||
static const unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
atm_dbg(usbatm, "%s entered\n", __func__);
|
||||
|
||||
if (status < 0) {
|
||||
atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, status);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((count == 6) && !memcmp(up_int, instance->int_data, 6)) {
|
||||
del_timer(&instance->status_check_timer);
|
||||
atm_info(usbatm, "DSL line goes up\n");
|
||||
} else if ((count == 6) && !memcmp(down_int, instance->int_data, 6)) {
|
||||
atm_info(usbatm, "DSL line goes down\n");
|
||||
} else {
|
||||
int i;
|
||||
|
||||
atm_dbg(usbatm, "%s: unknown interrupt packet of length %d:", __func__, count);
|
||||
for (i = 0; i < count; i++)
|
||||
printk(" %02x", instance->int_data[i]);
|
||||
printk("\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((int_urb = instance->int_urb)) {
|
||||
ret = usb_submit_urb(int_urb, GFP_ATOMIC);
|
||||
schedule_work(&instance->status_check_work);
|
||||
if (ret < 0) {
|
||||
atm_dbg(usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
if ((int_urb = instance->int_urb))
|
||||
mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY));
|
||||
}
|
||||
|
||||
static int speedtch_atm_start(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
|
||||
{
|
||||
struct usb_device *usb_dev = usbatm->usb_dev;
|
||||
struct speedtch_instance_data *instance = usbatm->driver_data;
|
||||
int i, ret;
|
||||
unsigned char mac_str[13];
|
||||
|
||||
atm_dbg(usbatm, "%s entered\n", __func__);
|
||||
|
||||
/* Set MAC address, it is stored in the serial number */
|
||||
memset(atm_dev->esi, 0, sizeof(atm_dev->esi));
|
||||
if (usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) {
|
||||
for (i = 0; i < 6; i++)
|
||||
atm_dev->esi[i] = (hex_to_bin(mac_str[i * 2]) << 4) +
|
||||
hex_to_bin(mac_str[i * 2 + 1]);
|
||||
}
|
||||
|
||||
/* Start modem synchronisation */
|
||||
ret = speedtch_start_synchro(instance);
|
||||
|
||||
/* Set up interrupt endpoint */
|
||||
if (instance->int_urb) {
|
||||
ret = usb_submit_urb(instance->int_urb, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
/* Doesn't matter; we'll poll anyway */
|
||||
atm_dbg(usbatm, "%s: submission of interrupt URB failed (%d)!\n", __func__, ret);
|
||||
usb_free_urb(instance->int_urb);
|
||||
instance->int_urb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start status polling */
|
||||
mod_timer(&instance->status_check_timer, jiffies + msecs_to_jiffies(1000));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
|
||||
{
|
||||
struct speedtch_instance_data *instance = usbatm->driver_data;
|
||||
struct urb *int_urb = instance->int_urb;
|
||||
|
||||
atm_dbg(usbatm, "%s entered\n", __func__);
|
||||
|
||||
del_timer_sync(&instance->status_check_timer);
|
||||
|
||||
/*
|
||||
* Since resubmit_timer and int_urb can schedule themselves and
|
||||
* each other, shutting them down correctly takes some care
|
||||
*/
|
||||
instance->int_urb = NULL; /* signal shutdown */
|
||||
mb();
|
||||
usb_kill_urb(int_urb);
|
||||
del_timer_sync(&instance->resubmit_timer);
|
||||
/*
|
||||
* At this point, speedtch_handle_int and speedtch_resubmit_int
|
||||
* can run or be running, but instance->int_urb == NULL means that
|
||||
* they will not reschedule
|
||||
*/
|
||||
usb_kill_urb(int_urb);
|
||||
del_timer_sync(&instance->resubmit_timer);
|
||||
usb_free_urb(int_urb);
|
||||
|
||||
flush_work(&instance->status_check_work);
|
||||
}
|
||||
|
||||
static int speedtch_pre_reset(struct usb_interface *intf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int speedtch_post_reset(struct usb_interface *intf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
** USB **
|
||||
**********/
|
||||
|
||||
static struct usb_device_id speedtch_usb_ids[] = {
|
||||
{USB_DEVICE(0x06b9, 0x4061)},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, speedtch_usb_ids);
|
||||
|
||||
static int speedtch_usb_probe(struct usb_interface *, const struct usb_device_id *);
|
||||
|
||||
static struct usb_driver speedtch_usb_driver = {
|
||||
.name = speedtch_driver_name,
|
||||
.probe = speedtch_usb_probe,
|
||||
.disconnect = usbatm_usb_disconnect,
|
||||
.pre_reset = speedtch_pre_reset,
|
||||
.post_reset = speedtch_post_reset,
|
||||
.id_table = speedtch_usb_ids
|
||||
};
|
||||
|
||||
static void speedtch_release_interfaces(struct usb_device *usb_dev,
|
||||
int num_interfaces)
|
||||
{
|
||||
struct usb_interface *cur_intf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_interfaces; i++)
|
||||
if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) {
|
||||
usb_set_intfdata(cur_intf, NULL);
|
||||
usb_driver_release_interface(&speedtch_usb_driver, cur_intf);
|
||||
}
|
||||
}
|
||||
|
||||
static int speedtch_bind(struct usbatm_data *usbatm,
|
||||
struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
struct usb_interface *cur_intf, *data_intf;
|
||||
struct speedtch_instance_data *instance;
|
||||
int ifnum = intf->altsetting->desc.bInterfaceNumber;
|
||||
int num_interfaces = usb_dev->actconfig->desc.bNumInterfaces;
|
||||
int i, ret;
|
||||
int use_isoc;
|
||||
|
||||
usb_dbg(usbatm, "%s entered\n", __func__);
|
||||
|
||||
/* sanity checks */
|
||||
|
||||
if (usb_dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) {
|
||||
usb_err(usbatm, "%s: wrong device class %d\n", __func__, usb_dev->descriptor.bDeviceClass);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!(data_intf = usb_ifnum_to_if(usb_dev, INTERFACE_DATA))) {
|
||||
usb_err(usbatm, "%s: data interface not found!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* claim all interfaces */
|
||||
|
||||
for (i = 0; i < num_interfaces; i++) {
|
||||
cur_intf = usb_ifnum_to_if(usb_dev, i);
|
||||
|
||||
if ((i != ifnum) && cur_intf) {
|
||||
ret = usb_driver_claim_interface(&speedtch_usb_driver, cur_intf, usbatm);
|
||||
|
||||
if (ret < 0) {
|
||||
usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, i, ret);
|
||||
speedtch_release_interfaces(usb_dev, i);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instance = kzalloc(sizeof(*instance), GFP_KERNEL);
|
||||
|
||||
if (!instance) {
|
||||
usb_err(usbatm, "%s: no memory for instance data!\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto fail_release;
|
||||
}
|
||||
|
||||
instance->usbatm = usbatm;
|
||||
|
||||
/* module parameters may change at any moment, so take a snapshot */
|
||||
instance->params.altsetting = altsetting;
|
||||
instance->params.BMaxDSL = BMaxDSL;
|
||||
instance->params.ModemMode = ModemMode;
|
||||
memcpy(instance->params.ModemOption, DEFAULT_MODEM_OPTION, MODEM_OPTION_LENGTH);
|
||||
memcpy(instance->params.ModemOption, ModemOption, num_ModemOption);
|
||||
use_isoc = enable_isoc;
|
||||
|
||||
if (instance->params.altsetting)
|
||||
if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->params.altsetting)) < 0) {
|
||||
usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, instance->params.altsetting, ret);
|
||||
instance->params.altsetting = 0; /* fall back to default */
|
||||
}
|
||||
|
||||
if (!instance->params.altsetting && use_isoc)
|
||||
if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_ISOC_ALTSETTING)) < 0) {
|
||||
usb_dbg(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_ISOC_ALTSETTING, ret);
|
||||
use_isoc = 0; /* fall back to bulk */
|
||||
}
|
||||
|
||||
if (use_isoc) {
|
||||
const struct usb_host_interface *desc = data_intf->cur_altsetting;
|
||||
const __u8 target_address = USB_DIR_IN | usbatm->driver->isoc_in;
|
||||
|
||||
use_isoc = 0; /* fall back to bulk if endpoint not found */
|
||||
|
||||
for (i = 0; i < desc->desc.bNumEndpoints; i++) {
|
||||
const struct usb_endpoint_descriptor *endpoint_desc = &desc->endpoint[i].desc;
|
||||
|
||||
if ((endpoint_desc->bEndpointAddress == target_address)) {
|
||||
use_isoc =
|
||||
usb_endpoint_xfer_isoc(endpoint_desc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_isoc)
|
||||
usb_info(usbatm, "isochronous transfer not supported - using bulk\n");
|
||||
}
|
||||
|
||||
if (!use_isoc && !instance->params.altsetting)
|
||||
if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_BULK_ALTSETTING)) < 0) {
|
||||
usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_BULK_ALTSETTING, ret);
|
||||
goto fail_free;
|
||||
}
|
||||
|
||||
if (!instance->params.altsetting)
|
||||
instance->params.altsetting = use_isoc ? DEFAULT_ISOC_ALTSETTING : DEFAULT_BULK_ALTSETTING;
|
||||
|
||||
usbatm->flags |= (use_isoc ? UDSL_USE_ISOC : 0);
|
||||
|
||||
INIT_WORK(&instance->status_check_work, speedtch_check_status);
|
||||
init_timer(&instance->status_check_timer);
|
||||
|
||||
instance->status_check_timer.function = speedtch_status_poll;
|
||||
instance->status_check_timer.data = (unsigned long)instance;
|
||||
instance->last_status = 0xff;
|
||||
instance->poll_delay = MIN_POLL_DELAY;
|
||||
|
||||
init_timer(&instance->resubmit_timer);
|
||||
instance->resubmit_timer.function = speedtch_resubmit_int;
|
||||
instance->resubmit_timer.data = (unsigned long)instance;
|
||||
|
||||
instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
|
||||
if (instance->int_urb)
|
||||
usb_fill_int_urb(instance->int_urb, usb_dev,
|
||||
usb_rcvintpipe(usb_dev, ENDPOINT_INT),
|
||||
instance->int_data, sizeof(instance->int_data),
|
||||
speedtch_handle_int, instance, 16);
|
||||
else
|
||||
usb_dbg(usbatm, "%s: no memory for interrupt urb!\n", __func__);
|
||||
|
||||
/* check whether the modem already seems to be alive */
|
||||
ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
|
||||
0x12, 0xc0, 0x07, 0x00,
|
||||
instance->scratch_buffer + OFFSET_7, SIZE_7, 500);
|
||||
|
||||
usbatm->flags |= (ret == SIZE_7 ? UDSL_SKIP_HEAVY_INIT : 0);
|
||||
|
||||
usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, usbatm->flags & UDSL_SKIP_HEAVY_INIT ? "already" : "not");
|
||||
|
||||
if (!(usbatm->flags & UDSL_SKIP_HEAVY_INIT))
|
||||
if ((ret = usb_reset_device(usb_dev)) < 0) {
|
||||
usb_err(usbatm, "%s: device reset failed (%d)!\n", __func__, ret);
|
||||
goto fail_free;
|
||||
}
|
||||
|
||||
usbatm->driver_data = instance;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_free:
|
||||
usb_free_urb(instance->int_urb);
|
||||
kfree(instance);
|
||||
fail_release:
|
||||
speedtch_release_interfaces(usb_dev, num_interfaces);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void speedtch_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
struct speedtch_instance_data *instance = usbatm->driver_data;
|
||||
|
||||
usb_dbg(usbatm, "%s entered\n", __func__);
|
||||
|
||||
speedtch_release_interfaces(usb_dev, usb_dev->actconfig->desc.bNumInterfaces);
|
||||
usb_free_urb(instance->int_urb);
|
||||
kfree(instance);
|
||||
}
|
||||
|
||||
|
||||
/***********
|
||||
** init **
|
||||
***********/
|
||||
|
||||
static struct usbatm_driver speedtch_usbatm_driver = {
|
||||
.driver_name = speedtch_driver_name,
|
||||
.bind = speedtch_bind,
|
||||
.heavy_init = speedtch_heavy_init,
|
||||
.unbind = speedtch_unbind,
|
||||
.atm_start = speedtch_atm_start,
|
||||
.atm_stop = speedtch_atm_stop,
|
||||
.bulk_in = ENDPOINT_BULK_DATA,
|
||||
.bulk_out = ENDPOINT_BULK_DATA,
|
||||
.isoc_in = ENDPOINT_ISOC_DATA
|
||||
};
|
||||
|
||||
static int speedtch_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
return usbatm_usb_probe(intf, id, &speedtch_usbatm_driver);
|
||||
}
|
||||
|
||||
module_usb_driver(speedtch_usb_driver);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
2807
drivers/usb/atm/ueagle-atm.c
Normal file
2807
drivers/usb/atm/ueagle-atm.c
Normal file
File diff suppressed because it is too large
Load diff
1344
drivers/usb/atm/usbatm.c
Normal file
1344
drivers/usb/atm/usbatm.c
Normal file
File diff suppressed because it is too large
Load diff
199
drivers/usb/atm/usbatm.h
Normal file
199
drivers/usb/atm/usbatm.h
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/******************************************************************************
|
||||
* usbatm.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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _USBATM_H_
|
||||
#define _USBATM_H_
|
||||
|
||||
#include <linux/atm.h>
|
||||
#include <linux/atmdev.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/ratelimit.h>
|
||||
|
||||
/*
|
||||
#define VERBOSE_DEBUG
|
||||
*/
|
||||
|
||||
#define usb_err(instance, format, arg...) \
|
||||
dev_err(&(instance)->usb_intf->dev , format , ## arg)
|
||||
#define usb_info(instance, format, arg...) \
|
||||
dev_info(&(instance)->usb_intf->dev , format , ## arg)
|
||||
#define usb_warn(instance, format, arg...) \
|
||||
dev_warn(&(instance)->usb_intf->dev , format , ## arg)
|
||||
#define usb_dbg(instance, format, arg...) \
|
||||
dev_dbg(&(instance)->usb_intf->dev , format , ## arg)
|
||||
|
||||
/* FIXME: move to dev_* once ATM is driver model aware */
|
||||
#define atm_printk(level, instance, format, arg...) \
|
||||
printk(level "ATM dev %d: " format , \
|
||||
(instance)->atm_dev->number , ## arg)
|
||||
|
||||
#define atm_err(instance, format, arg...) \
|
||||
atm_printk(KERN_ERR, instance , format , ## arg)
|
||||
#define atm_info(instance, format, arg...) \
|
||||
atm_printk(KERN_INFO, instance , format , ## arg)
|
||||
#define atm_warn(instance, format, arg...) \
|
||||
atm_printk(KERN_WARNING, instance , format , ## arg)
|
||||
#define atm_dbg(instance, format, ...) \
|
||||
pr_debug("ATM dev %d: " format, \
|
||||
(instance)->atm_dev->number, ##__VA_ARGS__)
|
||||
#define atm_rldbg(instance, format, ...) \
|
||||
pr_debug_ratelimited("ATM dev %d: " format, \
|
||||
(instance)->atm_dev->number, ##__VA_ARGS__)
|
||||
|
||||
/* flags, set by mini-driver in bind() */
|
||||
|
||||
#define UDSL_SKIP_HEAVY_INIT (1<<0)
|
||||
#define UDSL_USE_ISOC (1<<1)
|
||||
#define UDSL_IGNORE_EILSEQ (1<<2)
|
||||
|
||||
|
||||
/* mini driver */
|
||||
|
||||
struct usbatm_data;
|
||||
|
||||
/*
|
||||
* Assuming all methods exist and succeed, they are called in this order:
|
||||
*
|
||||
* bind, heavy_init, atm_start, ..., atm_stop, unbind
|
||||
*/
|
||||
|
||||
struct usbatm_driver {
|
||||
const char *driver_name;
|
||||
|
||||
/* init device ... can sleep, or cause probe() failure */
|
||||
int (*bind) (struct usbatm_data *, struct usb_interface *,
|
||||
const struct usb_device_id *id);
|
||||
|
||||
/* additional device initialization that is too slow to be done in probe() */
|
||||
int (*heavy_init) (struct usbatm_data *, struct usb_interface *);
|
||||
|
||||
/* cleanup device ... can sleep, but can't fail */
|
||||
void (*unbind) (struct usbatm_data *, struct usb_interface *);
|
||||
|
||||
/* init ATM device ... can sleep, or cause ATM initialization failure */
|
||||
int (*atm_start) (struct usbatm_data *, struct atm_dev *);
|
||||
|
||||
/* cleanup ATM device ... can sleep, but can't fail */
|
||||
void (*atm_stop) (struct usbatm_data *, struct atm_dev *);
|
||||
|
||||
int bulk_in; /* bulk rx endpoint */
|
||||
int isoc_in; /* isochronous rx endpoint */
|
||||
int bulk_out; /* bulk tx endpoint */
|
||||
|
||||
unsigned rx_padding;
|
||||
unsigned tx_padding;
|
||||
};
|
||||
|
||||
extern int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
|
||||
struct usbatm_driver *driver);
|
||||
extern void usbatm_usb_disconnect(struct usb_interface *intf);
|
||||
|
||||
|
||||
struct usbatm_channel {
|
||||
int endpoint; /* usb pipe */
|
||||
unsigned int stride; /* ATM cell size + padding */
|
||||
unsigned int buf_size; /* urb buffer size */
|
||||
unsigned int packet_size; /* endpoint maxpacket */
|
||||
spinlock_t lock;
|
||||
struct list_head list;
|
||||
struct tasklet_struct tasklet;
|
||||
struct timer_list delay;
|
||||
struct usbatm_data *usbatm;
|
||||
};
|
||||
|
||||
/* main driver data */
|
||||
|
||||
struct usbatm_data {
|
||||
/******************
|
||||
* public fields *
|
||||
******************/
|
||||
|
||||
/* mini driver */
|
||||
struct usbatm_driver *driver;
|
||||
void *driver_data;
|
||||
char driver_name[16];
|
||||
unsigned int flags; /* set by mini-driver in bind() */
|
||||
|
||||
/* USB device */
|
||||
struct usb_device *usb_dev;
|
||||
struct usb_interface *usb_intf;
|
||||
char description[64];
|
||||
|
||||
/* ATM device */
|
||||
struct atm_dev *atm_dev;
|
||||
|
||||
/********************************
|
||||
* private fields - do not use *
|
||||
********************************/
|
||||
|
||||
struct kref refcount;
|
||||
struct mutex serialize;
|
||||
int disconnected;
|
||||
|
||||
/* heavy init */
|
||||
struct task_struct *thread;
|
||||
struct completion thread_started;
|
||||
struct completion thread_exited;
|
||||
|
||||
/* ATM device */
|
||||
struct list_head vcc_list;
|
||||
|
||||
struct usbatm_channel rx_channel;
|
||||
struct usbatm_channel tx_channel;
|
||||
|
||||
struct sk_buff_head sndqueue;
|
||||
struct sk_buff *current_skb; /* being emptied */
|
||||
|
||||
struct usbatm_vcc_data *cached_vcc;
|
||||
int cached_vci;
|
||||
short cached_vpi;
|
||||
|
||||
unsigned char *cell_buf; /* holds partial rx cell */
|
||||
unsigned int buf_usage;
|
||||
|
||||
struct urb *urbs[0];
|
||||
};
|
||||
|
||||
static inline void *to_usbatm_driver_data(struct usb_interface *intf)
|
||||
{
|
||||
struct usbatm_data *usbatm_instance;
|
||||
|
||||
if (intf == NULL)
|
||||
return NULL;
|
||||
|
||||
usbatm_instance = usb_get_intfdata(intf);
|
||||
|
||||
if (usbatm_instance == NULL) /* set NULL before unbind() */
|
||||
return NULL;
|
||||
|
||||
return usbatm_instance->driver_data; /* set NULL after unbind() */
|
||||
}
|
||||
|
||||
#endif /* _USBATM_H_ */
|
||||
229
drivers/usb/atm/xusbatm.c
Normal file
229
drivers/usb/atm/xusbatm.c
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/******************************************************************************
|
||||
* xusbatm.c - dumb usbatm-based driver for modems initialized in userspace
|
||||
*
|
||||
* Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru)
|
||||
*
|
||||
* 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/etherdevice.h> /* for eth_random_addr() */
|
||||
|
||||
#include "usbatm.h"
|
||||
|
||||
|
||||
#define XUSBATM_DRIVERS_MAX 8
|
||||
|
||||
#define XUSBATM_PARM(name, type, parmtype, desc) \
|
||||
static type name[XUSBATM_DRIVERS_MAX]; \
|
||||
static unsigned int num_##name; \
|
||||
module_param_array(name, parmtype, &num_##name, 0444); \
|
||||
MODULE_PARM_DESC(name, desc)
|
||||
|
||||
XUSBATM_PARM(vendor, unsigned short, ushort, "USB device vendor");
|
||||
XUSBATM_PARM(product, unsigned short, ushort, "USB device product");
|
||||
|
||||
XUSBATM_PARM(rx_endpoint, unsigned char, byte, "rx endpoint number");
|
||||
XUSBATM_PARM(tx_endpoint, unsigned char, byte, "tx endpoint number");
|
||||
XUSBATM_PARM(rx_padding, unsigned char, byte, "rx padding (default 0)");
|
||||
XUSBATM_PARM(tx_padding, unsigned char, byte, "tx padding (default 0)");
|
||||
XUSBATM_PARM(rx_altsetting, unsigned char, byte, "rx altsetting (default 0)");
|
||||
XUSBATM_PARM(tx_altsetting, unsigned char, byte, "rx altsetting (default 0)");
|
||||
|
||||
static const char xusbatm_driver_name[] = "xusbatm";
|
||||
|
||||
static struct usbatm_driver xusbatm_drivers[XUSBATM_DRIVERS_MAX];
|
||||
static struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1];
|
||||
static struct usb_driver xusbatm_usb_driver;
|
||||
|
||||
static struct usb_interface *xusbatm_find_intf(struct usb_device *usb_dev, int altsetting, u8 ep)
|
||||
{
|
||||
struct usb_host_interface *alt;
|
||||
struct usb_interface *intf;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++)
|
||||
if ((intf = usb_dev->actconfig->interface[i]) && (alt = usb_altnum_to_altsetting(intf, altsetting)))
|
||||
for (j = 0; j < alt->desc.bNumEndpoints; j++)
|
||||
if (alt->endpoint[j].desc.bEndpointAddress == ep)
|
||||
return intf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int xusbatm_capture_intf(struct usbatm_data *usbatm, struct usb_device *usb_dev,
|
||||
struct usb_interface *intf, int altsetting, int claim)
|
||||
{
|
||||
int ifnum = intf->altsetting->desc.bInterfaceNumber;
|
||||
int ret;
|
||||
|
||||
if (claim && (ret = usb_driver_claim_interface(&xusbatm_usb_driver, intf, usbatm))) {
|
||||
usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, ifnum, ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = usb_set_interface(usb_dev, ifnum, altsetting))) {
|
||||
usb_err(usbatm, "%s: altsetting %2d for interface %2d failed (%d)!\n", __func__, altsetting, ifnum, ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xusbatm_release_intf(struct usb_device *usb_dev, struct usb_interface *intf, int claimed)
|
||||
{
|
||||
if (claimed) {
|
||||
usb_set_intfdata(intf, NULL);
|
||||
usb_driver_release_interface(&xusbatm_usb_driver, intf);
|
||||
}
|
||||
}
|
||||
|
||||
static int xusbatm_bind(struct usbatm_data *usbatm,
|
||||
struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
int drv_ix = id - xusbatm_usb_ids;
|
||||
int rx_alt = rx_altsetting[drv_ix];
|
||||
int tx_alt = tx_altsetting[drv_ix];
|
||||
struct usb_interface *rx_intf = xusbatm_find_intf(usb_dev, rx_alt, rx_endpoint[drv_ix]);
|
||||
struct usb_interface *tx_intf = xusbatm_find_intf(usb_dev, tx_alt, tx_endpoint[drv_ix]);
|
||||
int ret;
|
||||
|
||||
usb_dbg(usbatm, "%s: binding driver %d: vendor %04x product %04x"
|
||||
" rx: ep %02x padd %d alt %2d tx: ep %02x padd %d alt %2d\n",
|
||||
__func__, drv_ix, vendor[drv_ix], product[drv_ix],
|
||||
rx_endpoint[drv_ix], rx_padding[drv_ix], rx_alt,
|
||||
tx_endpoint[drv_ix], tx_padding[drv_ix], tx_alt);
|
||||
|
||||
if (!rx_intf || !tx_intf) {
|
||||
if (!rx_intf)
|
||||
usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n",
|
||||
__func__, rx_endpoint[drv_ix], rx_alt);
|
||||
if (!tx_intf)
|
||||
usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n",
|
||||
__func__, tx_endpoint[drv_ix], tx_alt);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if ((rx_intf != intf) && (tx_intf != intf))
|
||||
return -ENODEV;
|
||||
|
||||
if ((rx_intf == tx_intf) && (rx_alt != tx_alt)) {
|
||||
usb_err(usbatm, "%s: altsettings clash on interface %2d (%2d vs %2d)!\n", __func__,
|
||||
rx_intf->altsetting->desc.bInterfaceNumber, rx_alt, tx_alt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
usb_dbg(usbatm, "%s: rx If#=%2d; tx If#=%2d\n", __func__,
|
||||
rx_intf->altsetting->desc.bInterfaceNumber,
|
||||
tx_intf->altsetting->desc.bInterfaceNumber);
|
||||
|
||||
if ((ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf)))
|
||||
return ret;
|
||||
|
||||
if ((tx_intf != rx_intf) && (ret = xusbatm_capture_intf(usbatm, usb_dev, tx_intf, tx_alt, tx_intf != intf))) {
|
||||
xusbatm_release_intf(usb_dev, rx_intf, rx_intf != intf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xusbatm_unbind(struct usbatm_data *usbatm,
|
||||
struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *usb_dev = interface_to_usbdev(intf);
|
||||
int i;
|
||||
|
||||
usb_dbg(usbatm, "%s entered\n", __func__);
|
||||
|
||||
for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
|
||||
struct usb_interface *cur_intf = usb_dev->actconfig->interface[i];
|
||||
|
||||
if (cur_intf && (usb_get_intfdata(cur_intf) == usbatm)) {
|
||||
usb_set_intfdata(cur_intf, NULL);
|
||||
usb_driver_release_interface(&xusbatm_usb_driver, cur_intf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int xusbatm_atm_start(struct usbatm_data *usbatm,
|
||||
struct atm_dev *atm_dev)
|
||||
{
|
||||
atm_dbg(usbatm, "%s entered\n", __func__);
|
||||
|
||||
/* use random MAC as we've no way to get it from the device */
|
||||
eth_random_addr(atm_dev->esi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int xusbatm_usb_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
return usbatm_usb_probe(intf, id,
|
||||
xusbatm_drivers + (id - xusbatm_usb_ids));
|
||||
}
|
||||
|
||||
static struct usb_driver xusbatm_usb_driver = {
|
||||
.name = xusbatm_driver_name,
|
||||
.probe = xusbatm_usb_probe,
|
||||
.disconnect = usbatm_usb_disconnect,
|
||||
.id_table = xusbatm_usb_ids
|
||||
};
|
||||
|
||||
static int __init xusbatm_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!num_vendor ||
|
||||
num_vendor != num_product ||
|
||||
num_vendor != num_rx_endpoint ||
|
||||
num_vendor != num_tx_endpoint) {
|
||||
printk(KERN_WARNING "xusbatm: malformed module parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_vendor; i++) {
|
||||
rx_endpoint[i] |= USB_DIR_IN;
|
||||
tx_endpoint[i] &= USB_ENDPOINT_NUMBER_MASK;
|
||||
|
||||
xusbatm_usb_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
|
||||
xusbatm_usb_ids[i].idVendor = vendor[i];
|
||||
xusbatm_usb_ids[i].idProduct = product[i];
|
||||
|
||||
xusbatm_drivers[i].driver_name = xusbatm_driver_name;
|
||||
xusbatm_drivers[i].bind = xusbatm_bind;
|
||||
xusbatm_drivers[i].unbind = xusbatm_unbind;
|
||||
xusbatm_drivers[i].atm_start = xusbatm_atm_start;
|
||||
xusbatm_drivers[i].bulk_in = rx_endpoint[i];
|
||||
xusbatm_drivers[i].bulk_out = tx_endpoint[i];
|
||||
xusbatm_drivers[i].rx_padding = rx_padding[i];
|
||||
xusbatm_drivers[i].tx_padding = tx_padding[i];
|
||||
}
|
||||
|
||||
return usb_register(&xusbatm_usb_driver);
|
||||
}
|
||||
module_init(xusbatm_init);
|
||||
|
||||
static void __exit xusbatm_exit(void)
|
||||
{
|
||||
usb_deregister(&xusbatm_usb_driver);
|
||||
}
|
||||
module_exit(xusbatm_exit);
|
||||
|
||||
MODULE_AUTHOR("Roman Kagan, Duncan Sands");
|
||||
MODULE_DESCRIPTION("Driver for USB ADSL modems initialized in userspace");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.1");
|
||||
Loading…
Add table
Add a link
Reference in a new issue