aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/atm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/atm')
-rw-r--r--drivers/usb/atm/Kconfig17
-rw-r--r--drivers/usb/atm/Makefile5
-rw-r--r--drivers/usb/atm/cxacru.c846
-rw-r--r--drivers/usb/atm/speedtch.c336
-rw-r--r--drivers/usb/atm/ueagle-atm.c1919
-rw-r--r--drivers/usb/atm/usbatm.c724
-rw-r--r--drivers/usb/atm/usbatm.h94
-rw-r--r--drivers/usb/atm/xusbatm.c150
8 files changed, 2936 insertions, 1155 deletions
diff --git a/drivers/usb/atm/Kconfig b/drivers/usb/atm/Kconfig
index 550ddfa71a4..0f922942a07 100644
--- a/drivers/usb/atm/Kconfig
+++ b/drivers/usb/atm/Kconfig
@@ -2,12 +2,9 @@
# USB/ATM DSL configuration
#
-menu "USB DSL modem support"
- depends on USB
-
-config USB_ATM
+menuconfig USB_ATM
tristate "USB DSL modem support"
- depends on USB && ATM
+ depends on ATM
select CRC32
default n
help
@@ -18,9 +15,10 @@ config USB_ATM
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"
- depends on USB_ATM
select FW_LOADER
help
Say Y here if you have an SpeedTouch USB or SpeedTouch 330
@@ -33,7 +31,6 @@ config USB_SPEEDTOUCH
config USB_CXACRU
tristate "Conexant AccessRunner USB support"
- depends on USB_ATM
select FW_LOADER
help
Say Y here if you have an ADSL USB modem based on the Conexant
@@ -46,7 +43,6 @@ config USB_CXACRU
config USB_UEAGLEATM
tristate "ADI 930 and eagle USB DSL modem"
- depends on USB_ATM
select FW_LOADER
help
Say Y here if you have an ADSL USB modem based on the ADI 930
@@ -59,15 +55,14 @@ config USB_UEAGLEATM
config USB_XUSBATM
tristate "Other USB DSL modem support"
- depends on USB_ATM
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
+ 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.
-endmenu
+endif # USB_ATM
diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile
index 4c4a776ab1c..ac278946b06 100644
--- a/drivers/usb/atm/Makefile
+++ b/drivers/usb/atm/Makefile
@@ -1,13 +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
-
-ifeq ($(CONFIG_USB_DEBUG),y)
-EXTRA_CFLAGS += -DDEBUG
-endif
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index af0a41e7870..813d4d3a51c 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -4,6 +4,8 @@
*
* Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan
* Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru)
+ * Copyright (C) 2007 Simon Arlott
+ * Copyright (C) 2009 Simon Arlott
*
* 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
@@ -33,14 +35,15 @@
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/device.h> /* FIXME: linux/firmware.h should include it itself */
+#include <linux/device.h>
#include <linux/firmware.h>
+#include <linux/mutex.h>
+#include <asm/unaligned.h>
#include "usbatm.h"
-#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands"
-#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands, Simon Arlott"
+#define DRIVER_VERSION "0.4"
#define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver"
static const char cxacru_driver_name[] = "cxacru";
@@ -49,6 +52,7 @@ static const char cxacru_driver_name[] = "cxacru";
#define CXACRU_EP_DATA 0x02 /* Bulk in/out */
#define CMD_PACKET_SIZE 64 /* Should be maxpacket(ep)? */
+#define CMD_MAX_CONFIG ((CMD_PACKET_SIZE / 4 - 1) / 2)
/* Addresses */
#define PLLFCLK_ADDR 0x00350068
@@ -63,7 +67,7 @@ static const char cxacru_driver_name[] = "cxacru";
#define SDRAM_ENA 0x1
#define CMD_TIMEOUT 2000 /* msecs */
-#define POLL_INTERVAL 5000 /* msecs */
+#define POLL_INTERVAL 1 /* secs */
/* commands for interaction with the modem through the control channel before
* firmware is loaded */
@@ -102,6 +106,26 @@ enum cxacru_cm_request {
CM_REQUEST_MAX,
};
+/* commands for interaction with the flash memory
+ *
+ * read: response is the contents of the first 60 bytes of flash memory
+ * write: request contains the 60 bytes of data to write to flash memory
+ * response is the contents of the first 60 bytes of flash memory
+ *
+ * layout: PP PP VV VV MM MM MM MM MM MM ?? ?? SS SS SS SS SS SS SS SS
+ * SS SS SS SS SS SS SS SS 00 00 00 00 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ *
+ * P: le16 USB Product ID
+ * V: le16 USB Vendor ID
+ * M: be48 MAC Address
+ * S: le16 ASCII Serial Number
+ */
+enum cxacru_cm_flash {
+ CM_FLASH_READ = 0xa1,
+ CM_FLASH_WRITE = 0xa2
+};
+
/* reply codes to the commands above */
enum cxacru_cm_status {
CM_STATUS_UNDEFINED,
@@ -145,6 +169,13 @@ enum cxacru_info_idx {
CXINF_MAX = 0x1c,
};
+enum cxacru_poll_state {
+ CXPOLL_STOPPING,
+ CXPOLL_STOPPED,
+ CXPOLL_POLLING,
+ CXPOLL_SHUTDOWN
+};
+
struct cxacru_modem_type {
u32 pll_f_clk;
u32 pll_b_clk;
@@ -157,10 +188,15 @@ struct cxacru_data {
const struct cxacru_modem_type *modem_type;
int line_status;
- struct work_struct poll_work;
+ struct mutex adsl_state_serialize;
+ int adsl_status;
+ struct delayed_work poll_work;
+ u32 card_info[CXINF_MAX];
+ struct mutex poll_state_serialize;
+ enum cxacru_poll_state poll_state;
/* contol handles */
- struct semaphore cm_serialize;
+ struct mutex cm_serialize;
u8 *rcv_buf;
u8 *snd_buf;
struct urb *rcv_urb;
@@ -169,10 +205,356 @@ struct cxacru_data {
struct completion snd_done;
};
+static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
+ u8 *wdata, int wsize, u8 *rdata, int rsize);
+static void cxacru_poll_status(struct work_struct *work);
+
+/* Card info exported through sysfs */
+#define CXACRU__ATTR_INIT(_name) \
+static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL)
+
+#define CXACRU_CMD_INIT(_name) \
+static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \
+ cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name)
+
+#define CXACRU_SET_INIT(_name) \
+static DEVICE_ATTR(_name, S_IWUSR, \
+ NULL, cxacru_sysfs_store_##_name)
+
+#define CXACRU_ATTR_INIT(_value, _type, _name) \
+static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct cxacru_data *instance = to_usbatm_driver_data(\
+ to_usb_interface(dev)); \
+\
+ if (instance == NULL) \
+ return -ENODEV; \
+\
+ return cxacru_sysfs_showattr_##_type(instance->card_info[_value], buf); \
+} \
+CXACRU__ATTR_INIT(_name)
+
+#define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name)
+#define CXACRU_CMD_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
+#define CXACRU_SET_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
+#define CXACRU__ATTR_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
+
+#define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name)
+#define CXACRU_CMD_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
+#define CXACRU_SET_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
+#define CXACRU__ATTR_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
+
+static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+}
+
+static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf)
+{
+ if (likely(value >= 0)) {
+ return snprintf(buf, PAGE_SIZE, "%u.%02u\n",
+ value / 100, value % 100);
+ } else {
+ value = -value;
+ return snprintf(buf, PAGE_SIZE, "-%u.%02u\n",
+ value / 100, value % 100);
+ }
+}
+
+static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf)
+{
+ static char *str[] = { "no", "yes" };
+ if (unlikely(value >= ARRAY_SIZE(str)))
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+ return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+}
+
+static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf)
+{
+ static char *str[] = { NULL, "not connected", "connected", "lost" };
+ if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL))
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+ return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+}
+
+static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf)
+{
+ static char *str[] = { "down", "attempting to activate",
+ "training", "channel analysis", "exchange", "up",
+ "waiting", "initialising"
+ };
+ if (unlikely(value >= ARRAY_SIZE(str)))
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+ return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+}
+
+static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf)
+{
+ static char *str[] = {
+ "",
+ "ANSI T1.413",
+ "ITU-T G.992.1 (G.DMT)",
+ "ITU-T G.992.2 (G.LITE)"
+ };
+ if (unlikely(value >= ARRAY_SIZE(str)))
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+ return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+}
+
+/*
+ * This could use MAC_ADDRESS_HIGH and MAC_ADDRESS_LOW, but since
+ * this data is already in atm_dev there's no point.
+ *
+ * MAC_ADDRESS_HIGH = 0x????5544
+ * MAC_ADDRESS_LOW = 0x33221100
+ * Where 00-55 are bytes 0-5 of the MAC.
+ */
+static ssize_t cxacru_sysfs_show_mac_address(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cxacru_data *instance = to_usbatm_driver_data(
+ to_usb_interface(dev));
+
+ if (instance == NULL || instance->usbatm->atm_dev == NULL)
+ return -ENODEV;
+
+ return snprintf(buf, PAGE_SIZE, "%pM\n",
+ instance->usbatm->atm_dev->esi);
+}
+
+static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ static char *str[] = { "running", "stopped" };
+ struct cxacru_data *instance = to_usbatm_driver_data(
+ to_usb_interface(dev));
+ u32 value;
+
+ if (instance == NULL)
+ return -ENODEV;
+
+ value = instance->card_info[CXINF_LINE_STARTABLE];
+ if (unlikely(value >= ARRAY_SIZE(str)))
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+ return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+}
+
+static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cxacru_data *instance = to_usbatm_driver_data(
+ to_usb_interface(dev));
+ int ret;
+ int poll = -1;
+ char str_cmd[8];
+ int len = strlen(buf);
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ ret = sscanf(buf, "%7s", str_cmd);
+ if (ret != 1)
+ return -EINVAL;
+ ret = 0;
+
+ if (instance == NULL)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&instance->adsl_state_serialize))
+ return -ERESTARTSYS;
+
+ if (!strcmp(str_cmd, "stop") || !strcmp(str_cmd, "restart")) {
+ ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_STOP, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ atm_err(instance->usbatm, "change adsl state:"
+ " CHIP_ADSL_LINE_STOP returned %d\n", ret);
+
+ ret = -EIO;
+ } else {
+ ret = len;
+ poll = CXPOLL_STOPPED;
+ }
+ }
+
+ /* Line status is only updated every second
+ * and the device appears to only react to
+ * START/STOP every second too. Wait 1.5s to
+ * be sure that restart will have an effect. */
+ if (!strcmp(str_cmd, "restart"))
+ msleep(1500);
+
+ if (!strcmp(str_cmd, "start") || !strcmp(str_cmd, "restart")) {
+ ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ atm_err(instance->usbatm, "change adsl state:"
+ " CHIP_ADSL_LINE_START returned %d\n", ret);
+
+ ret = -EIO;
+ } else {
+ ret = len;
+ poll = CXPOLL_POLLING;
+ }
+ }
+
+ if (!strcmp(str_cmd, "poll")) {
+ ret = len;
+ poll = CXPOLL_POLLING;
+ }
+
+ if (ret == 0) {
+ ret = -EINVAL;
+ poll = -1;
+ }
+
+ if (poll == CXPOLL_POLLING) {
+ mutex_lock(&instance->poll_state_serialize);
+ switch (instance->poll_state) {
+ case CXPOLL_STOPPED:
+ /* start polling */
+ instance->poll_state = CXPOLL_POLLING;
+ break;
+
+ case CXPOLL_STOPPING:
+ /* abort stop request */
+ instance->poll_state = CXPOLL_POLLING;
+ case CXPOLL_POLLING:
+ case CXPOLL_SHUTDOWN:
+ /* don't start polling */
+ poll = -1;
+ }
+ mutex_unlock(&instance->poll_state_serialize);
+ } else if (poll == CXPOLL_STOPPED) {
+ mutex_lock(&instance->poll_state_serialize);
+ /* request stop */
+ if (instance->poll_state == CXPOLL_POLLING)
+ instance->poll_state = CXPOLL_STOPPING;
+ mutex_unlock(&instance->poll_state_serialize);
+ }
+
+ mutex_unlock(&instance->adsl_state_serialize);
+
+ if (poll == CXPOLL_POLLING)
+ cxacru_poll_status(&instance->poll_work.work);
+
+ return ret;
+}
+
+/* CM_REQUEST_CARD_DATA_GET times out, so no show attribute */
+
+static ssize_t cxacru_sysfs_store_adsl_config(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cxacru_data *instance = to_usbatm_driver_data(
+ to_usb_interface(dev));
+ int len = strlen(buf);
+ int ret, pos, num;
+ __le32 data[CMD_PACKET_SIZE / 4];
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ if (instance == NULL)
+ return -ENODEV;
+
+ pos = 0;
+ num = 0;
+ while (pos < len) {
+ int tmp;
+ u32 index;
+ u32 value;
+
+ ret = sscanf(buf + pos, "%x=%x%n", &index, &value, &tmp);
+ if (ret < 2)
+ return -EINVAL;
+ if (index < 0 || index > 0x7f)
+ return -EINVAL;
+ pos += tmp;
+
+ /* skip trailing newline */
+ if (buf[pos] == '\n' && pos == len-1)
+ pos++;
+
+ data[num * 2 + 1] = cpu_to_le32(index);
+ data[num * 2 + 2] = cpu_to_le32(value);
+ num++;
+
+ /* send config values when data buffer is full
+ * or no more data
+ */
+ if (pos >= len || num >= CMD_MAX_CONFIG) {
+ char log[CMD_MAX_CONFIG * 12 + 1]; /* %02x=%08x */
+
+ data[0] = cpu_to_le32(num);
+ ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET,
+ (u8 *) data, 4 + num * 8, NULL, 0);
+ if (ret < 0) {
+ atm_err(instance->usbatm,
+ "set card data returned %d\n", ret);
+ return -EIO;
+ }
+
+ for (tmp = 0; tmp < num; tmp++)
+ snprintf(log + tmp*12, 13, " %02x=%08x",
+ le32_to_cpu(data[tmp * 2 + 1]),
+ le32_to_cpu(data[tmp * 2 + 2]));
+ atm_info(instance->usbatm, "config%s\n", log);
+ num = 0;
+ }
+ }
+
+ return len;
+}
+
+/*
+ * All device attributes are included in CXACRU_ALL_FILES
+ * so that the same list can be used multiple times:
+ * INIT (define the device attributes)
+ * CREATE (create all the device files)
+ * REMOVE (remove all the device files)
+ *
+ * With the last two being defined as needed in the functions
+ * they are used in before calling CXACRU_ALL_FILES()
+ */
+#define CXACRU_ALL_FILES(_action) \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_RATE, u32, downstream_rate); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_RATE, u32, upstream_rate); \
+CXACRU_ATTR_##_action(CXINF_LINK_STATUS, LINK, link_status); \
+CXACRU_ATTR_##_action(CXINF_LINE_STATUS, LINE, line_status); \
+CXACRU__ATTR_##_action( mac_address); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_SNR_MARGIN, dB, upstream_snr_margin); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_SNR_MARGIN, dB, downstream_snr_margin); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_ATTENUATION, dB, upstream_attenuation); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_ATTENUATION, dB, downstream_attenuation); \
+CXACRU_ATTR_##_action(CXINF_TRANSMITTER_POWER, s8, transmitter_power); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_BITS_PER_FRAME, u32, upstream_bits_per_frame); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_BITS_PER_FRAME, u32, downstream_bits_per_frame); \
+CXACRU_ATTR_##_action(CXINF_STARTUP_ATTEMPTS, u32, startup_attempts); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_CRC_ERRORS, u32, upstream_crc_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_CRC_ERRORS, u32, downstream_crc_errors); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_FEC_ERRORS, u32, upstream_fec_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_FEC_ERRORS, u32, downstream_fec_errors); \
+CXACRU_ATTR_##_action(CXINF_UPSTREAM_HEC_ERRORS, u32, upstream_hec_errors); \
+CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_HEC_ERRORS, u32, downstream_hec_errors); \
+CXACRU_ATTR_##_action(CXINF_LINE_STARTABLE, bool, line_startable); \
+CXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \
+CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND, u32, adsl_headend); \
+CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT, u32, adsl_headend_environment); \
+CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version); \
+CXACRU_CMD_##_action( adsl_state); \
+CXACRU_SET_##_action( adsl_config);
+
+CXACRU_ALL_FILES(INIT);
+
/* the following three functions are stolen from drivers/usb/core/message.c */
-static void cxacru_blocking_completion(struct urb *urb, struct pt_regs *regs)
+static void cxacru_blocking_completion(struct urb *urb)
{
- complete((struct completion *)urb->context);
+ complete(urb->context);
}
static void cxacru_timeout_kill(unsigned long data)
@@ -181,10 +563,9 @@ static void cxacru_timeout_kill(unsigned long data)
}
static int cxacru_start_wait_urb(struct urb *urb, struct completion *done,
- int* actual_length)
+ int *actual_length)
{
struct timer_list timer;
- int status;
init_timer(&timer);
timer.expires = jiffies + msecs_to_jiffies(CMD_TIMEOUT);
@@ -192,14 +573,11 @@ static int cxacru_start_wait_urb(struct urb *urb, struct completion *done,
timer.function = cxacru_timeout_kill;
add_timer(&timer);
wait_for_completion(done);
- status = urb->status;
- if (status == -ECONNRESET)
- status = -ETIMEDOUT;
del_timer_sync(&timer);
if (actual_length)
*actual_length = urb->actual_length;
- return status;
+ return urb->status; /* must read status after completion */
}
static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
@@ -214,19 +592,22 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
int rbuflen = ((rsize - 1) / stride + 1) * CMD_PACKET_SIZE;
if (wbuflen > PAGE_SIZE || rbuflen > PAGE_SIZE) {
- dbg("too big transfer requested");
+ if (printk_ratelimit())
+ usb_err(instance->usbatm, "requested transfer size too large (%d, %d)\n",
+ wbuflen, rbuflen);
ret = -ENOMEM;
- goto fail;
+ goto err;
}
- down(&instance->cm_serialize);
+ mutex_lock(&instance->cm_serialize);
/* submit reading urb before the writing one */
init_completion(&instance->rcv_done);
ret = usb_submit_urb(instance->rcv_urb, GFP_KERNEL);
if (ret < 0) {
- dbg("submitting read urb for cm %#x failed", cm);
- ret = ret;
+ if (printk_ratelimit())
+ usb_err(instance->usbatm, "submit of read urb for cm %#x failed (%d)\n",
+ cm, ret);
goto fail;
}
@@ -242,27 +623,29 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
init_completion(&instance->snd_done);
ret = usb_submit_urb(instance->snd_urb, GFP_KERNEL);
if (ret < 0) {
- dbg("submitting write urb for cm %#x failed", cm);
- ret = ret;
+ if (printk_ratelimit())
+ usb_err(instance->usbatm, "submit of write urb for cm %#x failed (%d)\n",
+ cm, ret);
goto fail;
}
ret = cxacru_start_wait_urb(instance->snd_urb, &instance->snd_done, NULL);
if (ret < 0) {
- dbg("sending cm %#x failed", cm);
- ret = ret;
+ if (printk_ratelimit())
+ usb_err(instance->usbatm, "send of cm %#x failed (%d)\n", cm, ret);
goto fail;
}
ret = cxacru_start_wait_urb(instance->rcv_urb, &instance->rcv_done, &actlen);
if (ret < 0) {
- dbg("receiving cm %#x failed", cm);
- ret = ret;
+ if (printk_ratelimit())
+ usb_err(instance->usbatm, "receive of cm %#x failed (%d)\n", cm, ret);
goto fail;
}
if (actlen % CMD_PACKET_SIZE || !actlen) {
- dbg("response is not a positive multiple of %d: %#x",
- CMD_PACKET_SIZE, actlen);
+ if (printk_ratelimit())
+ usb_err(instance->usbatm, "invalid response length to cm %#x: %d\n",
+ cm, actlen);
ret = -EIO;
goto fail;
}
@@ -270,12 +653,16 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
/* check the return status and copy the data to the output buffer, if needed */
for (offb = offd = 0; offd < rsize && offb < actlen; offb += CMD_PACKET_SIZE) {
if (rbuf[offb] != cm) {
- dbg("wrong cm %#x in response", rbuf[offb]);
+ if (printk_ratelimit())
+ usb_err(instance->usbatm, "wrong cm %#x in response to cm %#x\n",
+ rbuf[offb], cm);
ret = -EIO;
goto fail;
}
if (rbuf[offb + 1] != CM_STATUS_SUCCESS) {
- dbg("response failed: %#x", rbuf[offb + 1]);
+ if (printk_ratelimit())
+ usb_err(instance->usbatm, "response to cm %#x failed: %#x\n",
+ cm, rbuf[offb + 1]);
ret = -EIO;
goto fail;
}
@@ -286,9 +673,10 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
}
ret = offd;
- dbg("cm %#x", cm);
+ usb_dbg(instance->usbatm, "cm %#x\n", cm);
fail:
- up(&instance->cm_serialize);
+ mutex_unlock(&instance->cm_serialize);
+err:
return ret;
}
@@ -296,8 +684,9 @@ static int cxacru_cm_get_array(struct cxacru_data *instance, enum cxacru_cm_requ
u32 *data, int size)
{
int ret, len;
- u32 *buf;
- int offb, offd;
+ __le32 *buf;
+ int offb;
+ unsigned int offd;
const int stride = CMD_PACKET_SIZE / (4 * 2) - 1;
int buflen = ((size - 1) / stride + 1 + size * 2) * 4;
@@ -313,15 +702,19 @@ static int cxacru_cm_get_array(struct cxacru_data *instance, enum cxacru_cm_requ
len = ret / 4;
for (offb = 0; offb < len; ) {
int l = le32_to_cpu(buf[offb++]);
- if (l > stride || l > (len - offb) / 2) {
- dbg("wrong data length %#x in response", l);
+ if (l < 0 || l > stride || l > (len - offb) / 2) {
+ if (printk_ratelimit())
+ usb_err(instance->usbatm, "invalid data length from cm %#x: %d\n",
+ cm, l);
ret = -EIO;
goto cleanup;
}
while (l--) {
offd = le32_to_cpu(buf[offb++]);
if (offd >= size) {
- dbg("wrong index %#x in response", offd);
+ if (printk_ratelimit())
+ usb_err(instance->usbatm, "wrong index %#x in response to cm %#x\n",
+ offd, cm);
ret = -EIO;
goto cleanup;
}
@@ -340,118 +733,205 @@ static int cxacru_card_status(struct cxacru_data *instance)
{
int ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0);
if (ret < 0) { /* firmware not loaded */
- dbg("cxacru_adsl_start: CARD_GET_STATUS returned %d", ret);
+ usb_dbg(instance->usbatm, "cxacru_adsl_start: CARD_GET_STATUS returned %d\n", ret);
return ret;
}
return 0;
}
-static void cxacru_poll_status(struct cxacru_data *instance);
+static void cxacru_remove_device_files(struct usbatm_data *usbatm_instance,
+ struct atm_dev *atm_dev)
+{
+ struct usb_interface *intf = usbatm_instance->usb_intf;
+
+ #define CXACRU_DEVICE_REMOVE_FILE(_name) \
+ device_remove_file(&intf->dev, &dev_attr_##_name);
+ CXACRU_ALL_FILES(REMOVE);
+ #undef CXACRU_DEVICE_REMOVE_FILE
+}
static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
struct atm_dev *atm_dev)
{
struct cxacru_data *instance = usbatm_instance->driver_data;
- struct device *dev = &usbatm_instance->usb_intf->dev;
- /*
- struct atm_dev *atm_dev = usbatm_instance->atm_dev;
- */
+ struct usb_interface *intf = usbatm_instance->usb_intf;
int ret;
+ int start_polling = 1;
- dbg("cxacru_atm_start");
+ dev_dbg(&intf->dev, "%s\n", __func__);
/* Read MAC address */
ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_MAC_ADDRESS, NULL, 0,
atm_dev->esi, sizeof(atm_dev->esi));
if (ret < 0) {
- dev_err(dev, "cxacru_atm_start: CARD_GET_MAC_ADDRESS returned %d\n", ret);
+ atm_err(usbatm_instance, "cxacru_atm_start: CARD_GET_MAC_ADDRESS returned %d\n", ret);
return ret;
}
+ #define CXACRU_DEVICE_CREATE_FILE(_name) \
+ ret = device_create_file(&intf->dev, &dev_attr_##_name); \
+ if (unlikely(ret)) \
+ goto fail_sysfs;
+ CXACRU_ALL_FILES(CREATE);
+ #undef CXACRU_DEVICE_CREATE_FILE
+
/* start ADSL */
+ mutex_lock(&instance->adsl_state_serialize);
ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
- if (ret < 0) {
- dev_err(dev, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);
- return ret;
- }
+ if (ret < 0)
+ atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);
/* Start status polling */
- cxacru_poll_status(instance);
+ mutex_lock(&instance->poll_state_serialize);
+ switch (instance->poll_state) {
+ case CXPOLL_STOPPED:
+ /* start polling */
+ instance->poll_state = CXPOLL_POLLING;
+ break;
+
+ case CXPOLL_STOPPING:
+ /* abort stop request */
+ instance->poll_state = CXPOLL_POLLING;
+ case CXPOLL_POLLING:
+ case CXPOLL_SHUTDOWN:
+ /* don't start polling */
+ start_polling = 0;
+ }
+ mutex_unlock(&instance->poll_state_serialize);
+ mutex_unlock(&instance->adsl_state_serialize);
+
+ printk(KERN_INFO "%s%d: %s %pM\n", atm_dev->type, atm_dev->number,
+ usbatm_instance->description, atm_dev->esi);
+
+ if (start_polling)
+ cxacru_poll_status(&instance->poll_work.work);
return 0;
+
+fail_sysfs:
+ usb_err(usbatm_instance, "cxacru_atm_start: device_create_file failed (%d)\n", ret);
+ cxacru_remove_device_files(usbatm_instance, atm_dev);
+ return ret;
}
-static void cxacru_poll_status(struct cxacru_data *instance)
+static void cxacru_poll_status(struct work_struct *work)
{
+ struct cxacru_data *instance =
+ container_of(work, struct cxacru_data, poll_work.work);
u32 buf[CXINF_MAX] = {};
- struct device *dev = &instance->usbatm->usb_intf->dev;
- struct atm_dev *atm_dev = instance->usbatm->atm_dev;
+ struct usbatm_data *usbatm = instance->usbatm;
+ struct atm_dev *atm_dev = usbatm->atm_dev;
+ int keep_polling = 1;
int ret;
ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX);
if (ret < 0) {
- dev_warn(dev, "poll status: error %d\n", ret);
+ if (ret != -ESHUTDOWN)
+ atm_warn(usbatm, "poll status: error %d\n", ret);
+
+ mutex_lock(&instance->poll_state_serialize);
+ if (instance->poll_state != CXPOLL_SHUTDOWN) {
+ instance->poll_state = CXPOLL_STOPPED;
+
+ if (ret != -ESHUTDOWN)
+ atm_warn(usbatm, "polling disabled, set adsl_state"
+ " to 'start' or 'poll' to resume\n");
+ }
+ mutex_unlock(&instance->poll_state_serialize);
goto reschedule;
}
+ memcpy(instance->card_info, buf, sizeof(instance->card_info));
+
+ if (instance->adsl_status != buf[CXINF_LINE_STARTABLE]) {
+ instance->adsl_status = buf[CXINF_LINE_STARTABLE];
+
+ switch (instance->adsl_status) {
+ case 0:
+ atm_printk(KERN_INFO, usbatm, "ADSL state: running\n");
+ break;
+
+ case 1:
+ atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n");
+ break;
+
+ default:
+ atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status);
+ break;
+ }
+ }
+
if (instance->line_status == buf[CXINF_LINE_STATUS])
goto reschedule;
instance->line_status = buf[CXINF_LINE_STATUS];
switch (instance->line_status) {
case 0:
- atm_dev->signal = ATM_PHY_SIG_LOST;
- dev_info(dev, "ADSL line: down\n");
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
+ atm_info(usbatm, "ADSL line: down\n");
break;
case 1:
- atm_dev->signal = ATM_PHY_SIG_LOST;
- dev_info(dev, "ADSL line: attemtping to activate\n");
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
+ atm_info(usbatm, "ADSL line: attempting to activate\n");
break;
case 2:
- atm_dev->signal = ATM_PHY_SIG_LOST;
- dev_info(dev, "ADSL line: training\n");
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
+ atm_info(usbatm, "ADSL line: training\n");
break;
case 3:
- atm_dev->signal = ATM_PHY_SIG_LOST;
- dev_info(dev, "ADSL line: channel analysis\n");
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
+ atm_info(usbatm, "ADSL line: channel analysis\n");
break;
case 4:
- atm_dev->signal = ATM_PHY_SIG_LOST;
- dev_info(dev, "ADSL line: exchange\n");
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
+ atm_info(usbatm, "ADSL line: exchange\n");
break;
case 5:
atm_dev->link_rate = buf[CXINF_DOWNSTREAM_RATE] * 1000 / 424;
- atm_dev->signal = ATM_PHY_SIG_FOUND;
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_FOUND);
- dev_info(dev, "ADSL line: up (%d kb/s down | %d kb/s up)\n",
+ atm_info(usbatm, "ADSL line: up (%d kb/s down | %d kb/s up)\n",
buf[CXINF_DOWNSTREAM_RATE], buf[CXINF_UPSTREAM_RATE]);
break;
case 6:
- atm_dev->signal = ATM_PHY_SIG_LOST;
- dev_info(dev, "ADSL line: waiting\n");
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
+ atm_info(usbatm, "ADSL line: waiting\n");
break;
case 7:
- atm_dev->signal = ATM_PHY_SIG_LOST;
- dev_info(dev, "ADSL line: initializing\n");
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
+ atm_info(usbatm, "ADSL line: initializing\n");
break;
default:
- atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
- dev_info(dev, "Unknown line state %02x\n", instance->line_status);
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN);
+ atm_info(usbatm, "Unknown line state %02x\n", instance->line_status);
break;
}
reschedule:
- schedule_delayed_work(&instance->poll_work, msecs_to_jiffies(POLL_INTERVAL));
+
+ mutex_lock(&instance->poll_state_serialize);
+ if (instance->poll_state == CXPOLL_STOPPING &&
+ instance->adsl_status == 1 && /* stopped */
+ instance->line_status == 0) /* down */
+ instance->poll_state = CXPOLL_STOPPED;
+
+ if (instance->poll_state == CXPOLL_STOPPED)
+ keep_polling = 0;
+ mutex_unlock(&instance->poll_state_serialize);
+
+ if (keep_polling)
+ schedule_delayed_work(&instance->poll_work,
+ round_jiffies_relative(POLL_INTERVAL*HZ));
}
static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
- u8 code1, u8 code2, u32 addr, u8 *data, int size)
+ u8 code1, u8 code2, u32 addr, const u8 *data, int size)
{
int ret;
u8 *buf;
@@ -469,10 +949,10 @@ static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
buf[offb++] = l;
buf[offb++] = code1;
buf[offb++] = code2;
- *((u32 *) (buf + offb)) = cpu_to_le32(addr);
+ put_unaligned(cpu_to_le32(addr), (__le32 *)(buf + offb));
offb += 4;
addr += l;
- if(l)
+ if (l)
memcpy(buf + offb, data + offd, l);
if (l < stride)
memset(buf + offb + l, 0, stride - l);
@@ -482,13 +962,13 @@ static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD),
buf, offb, NULL, CMD_TIMEOUT);
if (ret < 0) {
- dbg("sending fw %#x failed", fw);
+ dev_dbg(&usb_dev->dev, "sending fw %#x failed\n", fw);
goto cleanup;
}
offb = 0;
}
- } while(offd < size);
- dbg("sent fw %#x", fw);
+ } while (offd < size);
+ dev_dbg(&usb_dev->dev, "sent fw %#x\n", fw);
ret = 0;
@@ -499,23 +979,22 @@ cleanup:
static void cxacru_upload_firmware(struct cxacru_data *instance,
const struct firmware *fw,
- const struct firmware *bp,
- const struct firmware *cf)
+ const struct firmware *bp)
{
int ret;
- int off;
- struct usb_device *usb_dev = instance->usbatm->usb_dev;
- struct device *dev = &instance->usbatm->usb_intf->dev;
- u16 signature[] = { usb_dev->descriptor.idVendor, usb_dev->descriptor.idProduct };
- u32 val;
+ struct usbatm_data *usbatm = instance->usbatm;
+ struct usb_device *usb_dev = usbatm->usb_dev;
+ __le16 signature[] = { usb_dev->descriptor.idVendor,
+ usb_dev->descriptor.idProduct };
+ __le32 val;
- dbg("cxacru_upload_firmware");
+ usb_dbg(usbatm, "%s\n", __func__);
/* FirmwarePllFClkValue */
val = cpu_to_le32(instance->modem_type->pll_f_clk);
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLFCLK_ADDR, (u8 *) &val, 4);
if (ret) {
- dev_err(dev, "FirmwarePllFClkValue failed: %d\n", ret);
+ usb_err(usbatm, "FirmwarePllFClkValue failed: %d\n", ret);
return;
}
@@ -523,7 +1002,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
val = cpu_to_le32(instance->modem_type->pll_b_clk);
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLBCLK_ADDR, (u8 *) &val, 4);
if (ret) {
- dev_err(dev, "FirmwarePllBClkValue failed: %d\n", ret);
+ usb_err(usbatm, "FirmwarePllBClkValue failed: %d\n", ret);
return;
}
@@ -531,22 +1010,24 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
val = cpu_to_le32(SDRAM_ENA);
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SDRAMEN_ADDR, (u8 *) &val, 4);
if (ret) {
- dev_err(dev, "Enable SDRAM failed: %d\n", ret);
+ usb_err(usbatm, "Enable SDRAM failed: %d\n", ret);
return;
}
/* Firmware */
+ usb_info(usbatm, "loading firmware\n");
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, FW_ADDR, fw->data, fw->size);
if (ret) {
- dev_err(dev, "Firmware upload failed: %d\n", ret);
+ usb_err(usbatm, "Firmware upload failed: %d\n", ret);
return;
}
/* Boot ROM patch */
if (instance->modem_type->boot_rom_patch) {
+ usb_info(usbatm, "loading boot ROM patch\n");
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_ADDR, bp->data, bp->size);
if (ret) {
- dev_err(dev, "Boot ROM patching failed: %d\n", ret);
+ usb_err(usbatm, "Boot ROM patching failed: %d\n", ret);
return;
}
}
@@ -554,19 +1035,19 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
/* Signature */
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SIG_ADDR, (u8 *) signature, 4);
if (ret) {
- dev_err(dev, "Signature storing failed: %d\n", ret);
+ usb_err(usbatm, "Signature storing failed: %d\n", ret);
return;
}
+ usb_info(usbatm, "starting device\n");
if (instance->modem_type->boot_rom_patch) {
val = cpu_to_le32(BR_ADDR);
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_STACK_ADDR, (u8 *) &val, 4);
- }
- else {
+ } else {
ret = cxacru_fw(usb_dev, FW_GOTO_MEM, 0x0, 0x0, FW_ADDR, NULL, 0);
}
if (ret) {
- dev_err(dev, "Passing control to firmware failed: %d\n", ret);
+ usb_err(usbatm, "Passing control to firmware failed: %d\n", ret);
return;
}
@@ -580,46 +1061,27 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0);
if (ret < 0) {
- dev_err(dev, "modem failed to initialize: %d\n", ret);
+ usb_err(usbatm, "modem failed to initialize: %d\n", ret);
return;
}
-
- /* Load config data (le32), doing one packet at a time */
- if (cf)
- for (off = 0; off < cf->size / 4; ) {
- u32 buf[CMD_PACKET_SIZE / 4 - 1];
- int i, len = min_t(int, cf->size / 4 - off, CMD_PACKET_SIZE / 4 / 2 - 1);
- buf[0] = cpu_to_le32(len);
- for (i = 0; i < len; i++, off++) {
- buf[i * 2 + 1] = cpu_to_le32(off);
- memcpy(buf + i * 2 + 2, cf->data + off * 4, 4);
- }
- ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET,
- (u8 *) buf, len, NULL, 0);
- if (ret < 0) {
- dev_err(dev, "load config data failed: %d\n", ret);
- return;
- }
- }
-
- msleep_interruptible(4000);
}
static int cxacru_find_firmware(struct cxacru_data *instance,
- char* phase, const struct firmware **fw_p)
+ char *phase, const struct firmware **fw_p)
{
- struct device *dev = &instance->usbatm->usb_intf->dev;
+ struct usbatm_data *usbatm = instance->usbatm;
+ struct device *dev = &usbatm->usb_intf->dev;
char buf[16];
sprintf(buf, "cxacru-%s.bin", phase);
- dbg("cxacru_find_firmware: looking for %s", buf);
+ usb_dbg(usbatm, "cxacru_find_firmware: looking for %s\n", buf);
if (request_firmware(fw_p, buf, dev)) {
- dev_dbg(dev, "no stage %s firmware found\n", phase);
+ usb_dbg(usbatm, "no stage %s firmware found\n", phase);
return -ENOENT;
}
- dev_info(dev, "found firmware %s\n", buf);
+ usb_info(usbatm, "found firmware %s\n", buf);
return 0;
}
@@ -627,107 +1089,125 @@ static int cxacru_find_firmware(struct cxacru_data *instance,
static int cxacru_heavy_init(struct usbatm_data *usbatm_instance,
struct usb_interface *usb_intf)
{
- struct device *dev = &usbatm_instance->usb_intf->dev;
- const struct firmware *fw, *bp, *cf;
+ const struct firmware *fw, *bp;
struct cxacru_data *instance = usbatm_instance->driver_data;
int ret = cxacru_find_firmware(instance, "fw", &fw);
if (ret) {
- dev_warn(dev, "firmware (cxacru-fw.bin) unavailable (hotplug misconfiguration?)\n");
+ usb_warn(usbatm_instance, "firmware (cxacru-fw.bin) unavailable (system misconfigured?)\n");
return ret;
}
if (instance->modem_type->boot_rom_patch) {
ret = cxacru_find_firmware(instance, "bp", &bp);
if (ret) {
- dev_warn(dev, "boot ROM patch (cxacru-bp.bin) unavailable (hotplug misconfiguration?)\n");
+ usb_warn(usbatm_instance, "boot ROM patch (cxacru-bp.bin) unavailable (system misconfigured?)\n");
release_firmware(fw);
return ret;
}
}
- if (cxacru_find_firmware(instance, "cf", &cf)) /* optional */
- cf = NULL;
+ cxacru_upload_firmware(instance, fw, bp);
- cxacru_upload_firmware(instance, fw, bp, cf);
-
- if (cf)
- release_firmware(cf);
if (instance->modem_type->boot_rom_patch)
release_firmware(bp);
release_firmware(fw);
ret = cxacru_card_status(instance);
if (ret)
- dbg("modem initialisation failed");
+ usb_dbg(usbatm_instance, "modem initialisation failed\n");
else
- dbg("done setting up the modem");
+ usb_dbg(usbatm_instance, "done setting up the modem\n");
return ret;
}
static int cxacru_bind(struct usbatm_data *usbatm_instance,
- struct usb_interface *intf, const struct usb_device_id *id,
- int *need_heavy_init)
+ struct usb_interface *intf, const struct usb_device_id *id)
{
struct cxacru_data *instance;
struct usb_device *usb_dev = interface_to_usbdev(intf);
+ struct usb_host_endpoint *cmd_ep = usb_dev->ep_in[CXACRU_EP_CMD];
int ret;
/* instance init */
- instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+ instance = kzalloc(sizeof(*instance), GFP_KERNEL);
if (!instance) {
- dbg("cxacru_bind: no memory for instance data");
+ usb_dbg(usbatm_instance, "cxacru_bind: no memory for instance data\n");
return -ENOMEM;
}
- memset(instance, 0, sizeof(*instance));
-
instance->usbatm = usbatm_instance;
instance->modem_type = (struct cxacru_modem_type *) id->driver_info;
+ mutex_init(&instance->poll_state_serialize);
+ instance->poll_state = CXPOLL_STOPPED;
+ instance->line_status = -1;
+ instance->adsl_status = -1;
+
+ mutex_init(&instance->adsl_state_serialize);
+
instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL);
if (!instance->rcv_buf) {
- dbg("cxacru_bind: no memory for rcv_buf");
+ usb_dbg(usbatm_instance, "cxacru_bind: no memory for rcv_buf\n");
ret = -ENOMEM;
goto fail;
}
instance->snd_buf = (u8 *) __get_free_page(GFP_KERNEL);
if (!instance->snd_buf) {
- dbg("cxacru_bind: no memory for snd_buf");
+ usb_dbg(usbatm_instance, "cxacru_bind: no memory for snd_buf\n");
ret = -ENOMEM;
goto fail;
}
instance->rcv_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!instance->rcv_urb) {
- dbg("cxacru_bind: no memory for rcv_urb");
+ usb_dbg(usbatm_instance, "cxacru_bind: no memory for rcv_urb\n");
ret = -ENOMEM;
goto fail;
}
instance->snd_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!instance->snd_urb) {
- dbg("cxacru_bind: no memory for snd_urb");
+ usb_dbg(usbatm_instance, "cxacru_bind: no memory for snd_urb\n");
ret = -ENOMEM;
goto fail;
}
- usb_fill_int_urb(instance->rcv_urb,
+ if (!cmd_ep) {
+ usb_dbg(usbatm_instance, "cxacru_bind: no command endpoint\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ if ((cmd_ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_INT) {
+ usb_fill_int_urb(instance->rcv_urb,
usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD),
instance->rcv_buf, PAGE_SIZE,
cxacru_blocking_completion, &instance->rcv_done, 1);
- usb_fill_int_urb(instance->snd_urb,
+ usb_fill_int_urb(instance->snd_urb,
usb_dev, usb_sndintpipe(usb_dev, CXACRU_EP_CMD),
instance->snd_buf, PAGE_SIZE,
cxacru_blocking_completion, &instance->snd_done, 4);
+ } else {
+ usb_fill_bulk_urb(instance->rcv_urb,
+ usb_dev, usb_rcvbulkpipe(usb_dev, CXACRU_EP_CMD),
+ instance->rcv_buf, PAGE_SIZE,
+ cxacru_blocking_completion, &instance->rcv_done);
+
+ usb_fill_bulk_urb(instance->snd_urb,
+ usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD),
+ instance->snd_buf, PAGE_SIZE,
+ cxacru_blocking_completion, &instance->snd_done);
+ }
- init_MUTEX(&instance->cm_serialize);
+ mutex_init(&instance->cm_serialize);
- INIT_WORK(&instance->poll_work, (void *)cxacru_poll_status, instance);
+ INIT_DELAYED_WORK(&instance->poll_work, cxacru_poll_status);
usbatm_instance->driver_data = instance;
- *need_heavy_init = cxacru_card_status(instance);
+ usbatm_instance->flags = (cxacru_card_status(instance) ? 0 : UDSL_SKIP_HEAVY_INIT);
return 0;
@@ -745,16 +1225,29 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
struct usb_interface *intf)
{
struct cxacru_data *instance = usbatm_instance->driver_data;
+ int is_polling = 1;
- dbg("cxacru_unbind entered");
+ usb_dbg(usbatm_instance, "cxacru_unbind entered\n");
if (!instance) {
- dbg("cxacru_unbind: NULL instance!");
+ usb_dbg(usbatm_instance, "cxacru_unbind: NULL instance!\n");
return;
}
- while (!cancel_delayed_work(&instance->poll_work))
- flush_scheduled_work();
+ mutex_lock(&instance->poll_state_serialize);
+ BUG_ON(instance->poll_state == CXPOLL_SHUTDOWN);
+
+ /* ensure that status polling continues unless
+ * it has already stopped */
+ if (instance->poll_state == CXPOLL_STOPPED)
+ is_polling = 0;
+
+ /* stop polling from being stopped or started */
+ instance->poll_state = CXPOLL_SHUTDOWN;
+ mutex_unlock(&instance->poll_state_serialize);
+
+ if (is_polling)
+ cancel_delayed_work_sync(&instance->poll_work);
usb_kill_urb(instance->snd_urb);
usb_kill_urb(instance->rcv_urb);
@@ -763,6 +1256,7 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
free_page((unsigned long) instance->snd_buf);
free_page((unsigned long) instance->rcv_buf);
+
kfree(instance);
usbatm_instance->driver_data = NULL;
@@ -787,15 +1281,18 @@ static const struct usb_device_id cxacru_usb_ids[] = {
{ /* V = Conexant P = ADSL modem (Hasbani project) */
USB_DEVICE(0x0572, 0xcb00), .driver_info = (unsigned long) &cxacru_cb00
},
- { /* V = Conexant P = ADSL modem (Well PTI-800 */
- USB_DEVICE(0x0572, 0xcb02), .driver_info = (unsigned long) &cxacru_cb00
- },
{ /* V = Conexant P = ADSL modem */
USB_DEVICE(0x0572, 0xcb01), .driver_info = (unsigned long) &cxacru_cb00
},
+ { /* V = Conexant P = ADSL modem (Well PTI-800) */
+ USB_DEVICE(0x0572, 0xcb02), .driver_info = (unsigned long) &cxacru_cb00
+ },
{ /* V = Conexant P = ADSL modem */
USB_DEVICE(0x0572, 0xcb06), .driver_info = (unsigned long) &cxacru_cb00
},
+ { /* V = Conexant P = ADSL modem (ZTE ZXDSL 852) */
+ USB_DEVICE(0x0572, 0xcb07), .driver_info = (unsigned long) &cxacru_cb00
+ },
{ /* V = Olitec P = ADSL modem version 2 */
USB_DEVICE(0x08e3, 0x0100), .driver_info = (unsigned long) &cxacru_cafe
},
@@ -835,20 +1332,36 @@ static const struct usb_device_id cxacru_usb_ids[] = {
MODULE_DEVICE_TABLE(usb, cxacru_usb_ids);
static struct usbatm_driver cxacru_driver = {
- .owner = THIS_MODULE,
.driver_name = cxacru_driver_name,
.bind = cxacru_bind,
.heavy_init = cxacru_heavy_init,
.unbind = cxacru_unbind,
.atm_start = cxacru_atm_start,
- .in = CXACRU_EP_DATA,
- .out = CXACRU_EP_DATA,
+ .atm_stop = cxacru_remove_device_files,
+ .bulk_in = CXACRU_EP_DATA,
+ .bulk_out = CXACRU_EP_DATA,
.rx_padding = 3,
.tx_padding = 11,
};
-static int cxacru_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+static int cxacru_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
{
+ struct usb_device *usb_dev = interface_to_usbdev(intf);
+ char buf[15];
+
+ /* Avoid ADSL routers (cx82310_eth).
+ * Abort if bDeviceClass is 0xff and iProduct is "USB NET CARD".
+ */
+ if (usb_dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC
+ && usb_string(usb_dev, usb_dev->descriptor.iProduct,
+ buf, sizeof(buf)) > 0) {
+ if (!strcmp(buf, "USB NET CARD")) {
+ dev_info(&intf->dev, "ignoring cx82310_eth device\n");
+ return -ENODEV;
+ }
+ }
+
return usbatm_usb_probe(intf, id, &cxacru_driver);
}
@@ -859,18 +1372,7 @@ static struct usb_driver cxacru_usb_driver = {
.id_table = cxacru_usb_ids
};
-static int __init cxacru_init(void)
-{
- return usb_register(&cxacru_usb_driver);
-}
-
-static void __exit cxacru_cleanup(void)
-{
- usb_deregister(&cxacru_usb_driver);
-}
-
-module_init(cxacru_init);
-module_exit(cxacru_cleanup);
+module_usb_driver(cxacru_usb_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
index c1b47d74e20..0dc8c06a7b5 100644
--- a/drivers/usb/atm/speedtch.c
+++ b/drivers/usb/atm/speedtch.c
@@ -27,20 +27,20 @@
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/firmware.h>
-#include <linux/gfp.h>
-#include <linux/init.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.9"
+#define DRIVER_VERSION "1.10"
#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
static const char speedtch_driver_name[] = "speedtch";
@@ -53,7 +53,6 @@ static const char speedtch_driver_name[] = "speedtch";
#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
@@ -66,39 +65,81 @@ static const char speedtch_driver_name[] = "speedtch";
#define RESUBMIT_DELAY 1000 /* milliseconds */
-#define DEFAULT_ALTSETTING 1
+#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 int altsetting = DEFAULT_ALTSETTING;
-static int dl_512_first = DEFAULT_DL_512_FIRST;
-static int sw_buffering = DEFAULT_SW_BUFFERING;
+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;
-module_param(altsetting, int, S_IRUGO | S_IWUSR);
+#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 (default: "
- __MODULE_STRING(DEFAULT_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_DATA 0x07
+#define ENDPOINT_BULK_DATA 0x07
+#define ENDPOINT_ISOC_DATA 0x07
#define ENDPOINT_FIRMWARE 0x05
-#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
+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 work_struct status_checker;
+ 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;
@@ -108,7 +149,7 @@ struct speedtch_instance_data {
struct urb *int_urb;
unsigned char int_data[16];
- unsigned char scratch_buffer[TOTAL];
+ unsigned char scratch_buffer[16];
};
/***************
@@ -128,7 +169,7 @@ static void speedtch_set_swbuff(struct speedtch_instance_data *instance, int sta
"%sabling SW buffering: usb_control_msg returned %d\n",
state ? "En" : "Dis", ret);
else
- dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis");
+ usb_dbg(usbatm, "speedtch_set_swbuff: %sbled SW buffering\n", state ? "En" : "Dis");
}
static void speedtch_test_sequence(struct speedtch_instance_data *instance)
@@ -171,6 +212,34 @@ static void speedtch_test_sequence(struct speedtch_instance_data *instance)
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,
@@ -179,7 +248,6 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
{
unsigned char *buffer;
struct usbatm_data *usbatm = instance->usbatm;
- struct usb_interface *intf;
struct usb_device *usb_dev = usbatm->usb_dev;
int actual_length;
int ret = 0;
@@ -193,7 +261,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
goto out;
}
- if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
+ if (!usb_ifnum_to_if(usb_dev, 2)) {
ret = -ENODEV;
usb_dbg(usbatm, "%s: interface not found!\n", __func__);
goto out_free;
@@ -205,7 +273,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
buffer, 0x200, &actual_length, 2000);
if (ret < 0 && ret != -ETIMEDOUT)
- usb_dbg(usbatm, "%s: read BLOCK0 from modem failed (%d)!\n", __func__, ret);
+ 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);
}
@@ -219,7 +287,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
buffer, thislen, &actual_length, DATA_TIMEOUT);
if (ret < 0) {
- usb_dbg(usbatm, "%s: write BLOCK1 to modem failed (%d)!\n", __func__, ret);
+ 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);
@@ -232,7 +300,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
buffer, 0x200, &actual_length, DATA_TIMEOUT);
if (ret < 0) {
- usb_dbg(usbatm, "%s: read BLOCK2 from modem failed (%d)!\n", __func__, ret);
+ 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);
@@ -246,7 +314,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
buffer, thislen, &actual_length, DATA_TIMEOUT);
if (ret < 0) {
- usb_dbg(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret);
+ usb_err(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret);
goto out_free;
}
}
@@ -259,7 +327,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
buffer, 0x200, &actual_length, DATA_TIMEOUT);
if (ret < 0) {
- usb_dbg(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret);
+ usb_err(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret);
goto out_free;
}
@@ -270,6 +338,11 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
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);
@@ -285,8 +358,8 @@ out:
return ret;
}
-static int speedtch_find_firmware(struct usb_interface *intf, int phase,
- const struct firmware **fw_p)
+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);
@@ -295,24 +368,24 @@ static int speedtch_find_firmware(struct usb_interface *intf, int phase,
char buf[24];
sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
- dev_dbg(dev, "%s: looking for %s\n", __func__, buf);
+ 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);
- dev_dbg(dev, "%s: looking for %s\n", __func__, buf);
+ usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf);
if (request_firmware(fw_p, buf, dev)) {
sprintf(buf, "speedtch-%d.bin", phase);
- dev_dbg(dev, "%s: looking for %s\n", __func__, buf);
+ usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf);
if (request_firmware(fw_p, buf, dev)) {
- dev_warn(dev, "no stage %d firmware found!\n", phase);
+ usb_err(usbatm, "%s: no stage %d firmware found!\n", __func__, phase);
return -ENOENT;
}
}
}
- dev_info(dev, "found stage %d firmware %s\n", phase, buf);
+ usb_info(usbatm, "found stage %d firmware %s\n", phase, buf);
return 0;
}
@@ -323,15 +396,16 @@ static int speedtch_heavy_init(struct usbatm_data *usbatm, struct usb_interface
struct speedtch_instance_data *instance = usbatm->driver_data;
int ret;
- if ((ret = speedtch_find_firmware(intf, 1, &fw1)) < 0)
- return ret;
+ if ((ret = speedtch_find_firmware(usbatm, intf, 1, &fw1)) < 0)
+ return ret;
- if ((ret = speedtch_find_firmware(intf, 2, &fw2)) < 0) {
+ if ((ret = speedtch_find_firmware(usbatm, intf, 2, &fw2)) < 0) {
release_firmware(fw1);
return ret;
}
- ret = speedtch_upload_firmware(instance, fw1, fw2);
+ 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);
@@ -351,7 +425,7 @@ static int speedtch_read_status(struct speedtch_instance_data *instance)
unsigned char *buf = instance->scratch_buffer;
int ret;
- memset(buf, 0, TOTAL);
+ memset(buf, 0, 16);
ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
@@ -420,15 +494,20 @@ static int speedtch_start_synchro(struct speedtch_instance_data *instance)
return ret;
}
-static void speedtch_check_status(struct speedtch_instance_data *instance)
+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) {
@@ -441,12 +520,12 @@ static void speedtch_check_status(struct speedtch_instance_data *instance)
status = buf[OFFSET_7];
- atm_dbg(usbatm, "%s: line state %02x\n", __func__, status);
-
if ((status != instance->last_status) || !status) {
+ atm_dbg(usbatm, "%s: line state 0x%02x\n", __func__, status);
+
switch (status) {
case 0:
- atm_dev->signal = ATM_PHY_SIG_LOST;
+ 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... */
@@ -454,12 +533,12 @@ static void speedtch_check_status(struct speedtch_instance_data *instance)
break;
case 0x08:
- atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN);
atm_info(usbatm, "ADSL line is blocked?\n");
break;
case 0x10:
- atm_dev->signal = ATM_PHY_SIG_LOST;
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
atm_info(usbatm, "ADSL line is synchronising\n");
break;
@@ -475,7 +554,7 @@ static void speedtch_check_status(struct speedtch_instance_data *instance)
}
atm_dev->link_rate = down_speed * 1000 / 424;
- atm_dev->signal = ATM_PHY_SIG_FOUND;
+ 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",
@@ -483,8 +562,8 @@ static void speedtch_check_status(struct speedtch_instance_data *instance)
break;
default:
- atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
- atm_info(usbatm, "Unknown line state %02x\n", status);
+ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN);
+ atm_info(usbatm, "unknown line state %02x\n", status);
break;
}
@@ -496,11 +575,11 @@ static void speedtch_status_poll(unsigned long data)
{
struct speedtch_instance_data *instance = (void *)data;
- schedule_work(&instance->status_checker);
+ 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_checker.timer, jiffies + msecs_to_jiffies(instance->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");
}
@@ -516,7 +595,7 @@ static void speedtch_resubmit_int(unsigned long data)
if (int_urb) {
ret = usb_submit_urb(int_urb, GFP_ATOMIC);
if (!ret)
- schedule_work(&instance->status_checker);
+ 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));
@@ -524,12 +603,13 @@ static void speedtch_resubmit_int(unsigned long data)
}
}
-static void speedtch_handle_int(struct urb *int_urb, struct pt_regs *regs)
+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 ret = int_urb->status;
+ 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 };
@@ -538,13 +618,13 @@ static void speedtch_handle_int(struct urb *int_urb, struct pt_regs *regs)
atm_dbg(usbatm, "%s entered\n", __func__);
- if (ret < 0) {
- atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, ret);
+ 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_checker.timer);
+ 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");
@@ -560,7 +640,7 @@ static void speedtch_handle_int(struct urb *int_urb, struct pt_regs *regs)
if ((int_urb = instance->int_urb)) {
ret = usb_submit_urb(int_urb, GFP_ATOMIC);
- schedule_work(&instance->status_checker);
+ 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;
@@ -583,16 +663,12 @@ static int speedtch_atm_start(struct usbatm_data *usbatm, struct atm_dev *atm_de
atm_dbg(usbatm, "%s entered\n", __func__);
- if ((ret = usb_set_interface(usb_dev, 1, altsetting)) < 0) {
- atm_dbg(usbatm, "%s: usb_set_interface returned %d!\n", __func__, ret);
- return ret;
- }
-
/* 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] = (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1]));
+ atm_dev->esi[i] = (hex_to_bin(mac_str[i * 2]) << 4) +
+ hex_to_bin(mac_str[i * 2 + 1]);
}
/* Start modem synchronisation */
@@ -610,7 +686,7 @@ static int speedtch_atm_start(struct usbatm_data *usbatm, struct atm_dev *atm_de
}
/* Start status polling */
- mod_timer(&instance->status_checker.timer, jiffies + msecs_to_jiffies(1000));
+ mod_timer(&instance->status_check_timer, jiffies + msecs_to_jiffies(1000));
return 0;
}
@@ -622,7 +698,7 @@ static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_de
atm_dbg(usbatm, "%s entered\n", __func__);
- del_timer_sync(&instance->status_checker.timer);
+ del_timer_sync(&instance->status_check_timer);
/*
* Since resubmit_timer and int_urb can schedule themselves and
@@ -641,7 +717,17 @@ static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_de
del_timer_sync(&instance->resubmit_timer);
usb_free_urb(int_urb);
- flush_scheduled_work();
+ 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;
}
@@ -662,14 +748,18 @@ 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) {
+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++)
+ 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);
@@ -678,55 +768,112 @@ static void speedtch_release_interfaces(struct usb_device *usb_dev, int num_inte
static int speedtch_bind(struct usbatm_data *usbatm,
struct usb_interface *intf,
- const struct usb_device_id *id,
- int *need_heavy_init)
+ const struct usb_device_id *id)
{
struct usb_device *usb_dev = interface_to_usbdev(intf);
- struct usb_interface *cur_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_dbg(usbatm, "%s: wrong device class %d\n", __func__, usb_dev->descriptor.bDeviceClass);
+ 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++) {
+ 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_dbg(usbatm, "%s: failed to claim interface %d (%d)\n", __func__, i, ret);
+ usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, i, ret);
speedtch_release_interfaces(usb_dev, i);
return ret;
}
}
}
- instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+ instance = kzalloc(sizeof(*instance), GFP_KERNEL);
if (!instance) {
- usb_dbg(usbatm, "%s: no memory for instance data!\n", __func__);
+ usb_err(usbatm, "%s: no memory for instance data!\n", __func__);
ret = -ENOMEM;
goto fail_release;
}
- memset(instance, 0, sizeof(struct speedtch_instance_data));
-
instance->usbatm = usbatm;
- INIT_WORK(&instance->status_checker, (void *)speedtch_check_status, instance);
+ /* 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;
+ }
+ }
- instance->status_checker.timer.function = speedtch_status_poll;
- instance->status_checker.timer.data = (unsigned long)instance;
+ 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;
@@ -740,7 +887,7 @@ static int speedtch_bind(struct usbatm_data *usbatm,
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, 50);
+ speedtch_handle_int, instance, 16);
else
usb_dbg(usbatm, "%s: no memory for interrupt urb!\n", __func__);
@@ -749,13 +896,15 @@ static int speedtch_bind(struct usbatm_data *usbatm,
0x12, 0xc0, 0x07, 0x00,
instance->scratch_buffer + OFFSET_7, SIZE_7, 500);
- *need_heavy_init = (ret != SIZE_7);
+ usbatm->flags |= (ret == SIZE_7 ? UDSL_SKIP_HEAVY_INIT : 0);
- usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, need_heavy_init ? "not" : "already");
+ usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, usbatm->flags & UDSL_SKIP_HEAVY_INIT ? "already" : "not");
- if (*need_heavy_init)
- if ((ret = usb_reset_device(usb_dev)) < 0)
+ 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;
@@ -787,15 +936,15 @@ static void speedtch_unbind(struct usbatm_data *usbatm, struct usb_interface *in
***********/
static struct usbatm_driver speedtch_usbatm_driver = {
- .owner = THIS_MODULE,
.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,
- .in = ENDPOINT_DATA,
- .out = ENDPOINT_DATA
+ .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)
@@ -803,22 +952,7 @@ static int speedtch_usb_probe(struct usb_interface *intf, const struct usb_devic
return usbatm_usb_probe(intf, id, &speedtch_usbatm_driver);
}
-static int __init speedtch_usb_init(void)
-{
- dbg("%s: driver version %s", __func__, DRIVER_VERSION);
-
- return usb_register(&speedtch_usb_driver);
-}
-
-static void __exit speedtch_usb_cleanup(void)
-{
- dbg("%s", __func__);
-
- usb_deregister(&speedtch_usb_driver);
-}
-
-module_init(speedtch_usb_init);
-module_exit(speedtch_usb_cleanup);
+module_usb_driver(speedtch_usb_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index 7d2a679989e..5a459377574 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -2,7 +2,8 @@
* Copyright (c) 2003, 2004
* Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
*
- * Copyright (c) 2005 Matthieu Castet <castet.matthieu@free.fr>
+ * Copyright (c) 2005-2007 Matthieu Castet <castet.matthieu@free.fr>
+ * Copyright (c) 2005-2007 Stanislaw Gruszka <stf_xl@wp.pl>
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -56,18 +57,22 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
#include <linux/crc32.h>
#include <linux/usb.h>
#include <linux/firmware.h>
#include <linux/ctype.h>
+#include <linux/sched.h>
#include <linux/kthread.h>
-#include <linux/version.h>
+#include <linux/mutex.h>
+#include <linux/freezer.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+
#include <asm/unaligned.h>
#include "usbatm.h"
-#define EAGLEUSBVERSION "ueagle 1.1"
+#define EAGLEUSBVERSION "ueagle 1.4"
/*
@@ -78,36 +83,66 @@
if (debug >= 1) \
dev_dbg(&(usb_dev)->dev, \
"[ueagle-atm dbg] %s: " format, \
- __FUNCTION__, ##args); \
- } while (0)
+ __func__, ##args); \
+ } while (0)
#define uea_vdbg(usb_dev, format, args...) \
do { \
if (debug >= 2) \
dev_dbg(&(usb_dev)->dev, \
"[ueagle-atm vdbg] " format, ##args); \
- } while (0)
+ } while (0)
#define uea_enters(usb_dev) \
- uea_vdbg(usb_dev, "entering %s\n", __FUNCTION__)
+ uea_vdbg(usb_dev, "entering %s\n" , __func__)
#define uea_leaves(usb_dev) \
- uea_vdbg(usb_dev, "leaving %s\n", __FUNCTION__)
+ uea_vdbg(usb_dev, "leaving %s\n" , __func__)
-#define uea_err(usb_dev, format,args...) \
- dev_err(&(usb_dev)->dev ,"[UEAGLE-ATM] " format , ##args)
+#define uea_err(usb_dev, format, args...) \
+ dev_err(&(usb_dev)->dev , "[UEAGLE-ATM] " format , ##args)
-#define uea_warn(usb_dev, format,args...) \
- dev_warn(&(usb_dev)->dev ,"[Ueagle-atm] " format, ##args)
+#define uea_warn(usb_dev, format, args...) \
+ dev_warn(&(usb_dev)->dev , "[Ueagle-atm] " format, ##args)
-#define uea_info(usb_dev, format,args...) \
- dev_info(&(usb_dev)->dev ,"[ueagle-atm] " format, ##args)
+#define uea_info(usb_dev, format, args...) \
+ dev_info(&(usb_dev)->dev , "[ueagle-atm] " format, ##args)
-struct uea_cmvs {
+struct intr_pkt;
+
+/* cmv's from firmware */
+struct uea_cmvs_v1 {
u32 address;
u16 offset;
u32 data;
-} __attribute__ ((packed));
+} __packed;
+
+struct uea_cmvs_v2 {
+ u32 group;
+ u32 address;
+ u32 offset;
+ u32 data;
+} __packed;
+
+/* information about currently processed cmv */
+struct cmv_dsc_e1 {
+ u8 function;
+ u16 idx;
+ u32 address;
+ u16 offset;
+};
+
+struct cmv_dsc_e4 {
+ u16 function;
+ u16 offset;
+ u16 address;
+ u16 group;
+};
+
+union cmv_dsc {
+ struct cmv_dsc_e1 e1;
+ struct cmv_dsc_e4 e4;
+};
struct uea_softc {
struct usb_device *usb_dev;
@@ -115,6 +150,9 @@ struct uea_softc {
int modem_index;
unsigned int driver_info;
+ int annex;
+#define ANNEXA 0
+#define ANNEXB 1
int booting;
int reset;
@@ -123,8 +161,10 @@ struct uea_softc {
struct task_struct *kthread;
u32 data;
- wait_queue_head_t cmv_ack_wait;
+ u32 data1;
+
int cmv_ack;
+ union cmv_dsc cmv_dsc;
struct work_struct task;
u16 pageno;
@@ -133,10 +173,10 @@ struct uea_softc {
const struct firmware *dsp_firm;
struct urb *urb_int;
- u8 cmv_function;
- u16 cmv_idx;
- u32 cmv_address;
- u16 cmv_offset;
+ void (*dispatch_cmv) (struct uea_softc *, struct intr_pkt *);
+ void (*schedule_load_page) (struct uea_softc *, struct intr_pkt *);
+ int (*stat) (struct uea_softc *);
+ int (*send_cmvs) (struct uea_softc *);
/* keep in sync with eaglectl */
struct uea_stats {
@@ -170,10 +210,34 @@ struct uea_softc {
#define ELSA_PID_PSTFIRM 0x3350
#define ELSA_PID_PREFIRM 0x3351
+#define ELSA_PID_A_PREFIRM 0x3352
+#define ELSA_PID_A_PSTFIRM 0x3353
+#define ELSA_PID_B_PREFIRM 0x3362
+#define ELSA_PID_B_PSTFIRM 0x3363
+
+/*
+ * Devolo IDs : pots if (pid & 0x10)
+ */
+#define DEVOLO_VID 0x1039
+#define DEVOLO_EAGLE_I_A_PID_PSTFIRM 0x2110
+#define DEVOLO_EAGLE_I_A_PID_PREFIRM 0x2111
+
+#define DEVOLO_EAGLE_I_B_PID_PSTFIRM 0x2100
+#define DEVOLO_EAGLE_I_B_PID_PREFIRM 0x2101
+
+#define DEVOLO_EAGLE_II_A_PID_PSTFIRM 0x2130
+#define DEVOLO_EAGLE_II_A_PID_PREFIRM 0x2131
+
+#define DEVOLO_EAGLE_II_B_PID_PSTFIRM 0x2120
+#define DEVOLO_EAGLE_II_B_PID_PREFIRM 0x2121
+
/*
- * Sagem USB IDs
+ * Reference design USB IDs
*/
-#define EAGLE_VID 0x1110
+#define ANALOG_VID 0x1110
+#define ADI930_PID_PREFIRM 0x9001
+#define ADI930_PID_PSTFIRM 0x9000
+
#define EAGLE_I_PID_PREFIRM 0x9010 /* Eagle I */
#define EAGLE_I_PID_PSTFIRM 0x900F /* Eagle I */
@@ -183,12 +247,12 @@ struct uea_softc {
#define EAGLE_II_PID_PREFIRM 0x9022 /* Eagle II */
#define EAGLE_II_PID_PSTFIRM 0x9021 /* Eagle II */
-/*
- * Eagle III Pid
- */
#define EAGLE_III_PID_PREFIRM 0x9032 /* Eagle III */
#define EAGLE_III_PID_PSTFIRM 0x9031 /* Eagle III */
+#define EAGLE_IV_PID_PREFIRM 0x9042 /* Eagle IV */
+#define EAGLE_IV_PID_PSTFIRM 0x9041 /* Eagle IV */
+
/*
* USR USB IDs
*/
@@ -204,11 +268,15 @@ struct uea_softc {
#define PREFIRM 0
#define PSTFIRM (1<<7)
+#define AUTO_ANNEX_A (1<<8)
+#define AUTO_ANNEX_B (1<<9)
+
enum {
ADI930 = 0,
EAGLE_I,
EAGLE_II,
- EAGLE_III
+ EAGLE_III,
+ EAGLE_IV
};
/* macros for both struct usb_device_id and struct uea_softc */
@@ -217,34 +285,64 @@ enum {
#define UEA_CHIP_VERSION(x) \
((x)->driver_info & 0xf)
-#define IS_ISDN(sc) \
- (le16_to_cpu(sc->usb_dev->descriptor.bcdDevice) & 0x80)
+#define IS_ISDN(x) \
+ ((x)->annex & ANNEXB)
-#define INS_TO_USBDEV(ins) ins->usb_dev
+#define INS_TO_USBDEV(ins) (ins->usb_dev)
#define GET_STATUS(data) \
((data >> 8) & 0xf)
+
#define IS_OPERATIONAL(sc) \
- (GET_STATUS(sc->stats.phy.state) == 2)
+ ((UEA_CHIP_VERSION(sc) != EAGLE_IV) ? \
+ (GET_STATUS(sc->stats.phy.state) == 2) : \
+ (sc->stats.phy.state == 7))
/*
* Set of macros to handle unaligned data in the firmware blob.
* The FW_GET_BYTE() macro is provided only for consistency.
*/
-#define FW_GET_BYTE(p) *((__u8 *) (p))
-#define FW_GET_WORD(p) le16_to_cpu(get_unaligned((__le16 *) (p)))
-#define FW_GET_LONG(p) le32_to_cpu(get_unaligned((__le32 *) (p)))
+#define FW_GET_BYTE(p) (*((__u8 *) (p)))
#define FW_DIR "ueagle-atm/"
+#define EAGLE_FIRMWARE FW_DIR "eagle.fw"
+#define ADI930_FIRMWARE FW_DIR "adi930.fw"
+#define EAGLE_I_FIRMWARE FW_DIR "eagleI.fw"
+#define EAGLE_II_FIRMWARE FW_DIR "eagleII.fw"
+#define EAGLE_III_FIRMWARE FW_DIR "eagleIII.fw"
+#define EAGLE_IV_FIRMWARE FW_DIR "eagleIV.fw"
+
+#define DSP4I_FIRMWARE FW_DIR "DSP4i.bin"
+#define DSP4P_FIRMWARE FW_DIR "DSP4p.bin"
+#define DSP9I_FIRMWARE FW_DIR "DSP9i.bin"
+#define DSP9P_FIRMWARE FW_DIR "DSP9p.bin"
+#define DSPEI_FIRMWARE FW_DIR "DSPei.bin"
+#define DSPEP_FIRMWARE FW_DIR "DSPep.bin"
+#define FPGA930_FIRMWARE FW_DIR "930-fpga.bin"
+
+#define CMV4P_FIRMWARE FW_DIR "CMV4p.bin"
+#define CMV4PV2_FIRMWARE FW_DIR "CMV4p.bin.v2"
+#define CMV4I_FIRMWARE FW_DIR "CMV4i.bin"
+#define CMV4IV2_FIRMWARE FW_DIR "CMV4i.bin.v2"
+#define CMV9P_FIRMWARE FW_DIR "CMV9p.bin"
+#define CMV9PV2_FIRMWARE FW_DIR "CMV9p.bin.v2"
+#define CMV9I_FIRMWARE FW_DIR "CMV9i.bin"
+#define CMV9IV2_FIRMWARE FW_DIR "CMV9i.bin.v2"
+#define CMVEP_FIRMWARE FW_DIR "CMVep.bin"
+#define CMVEPV2_FIRMWARE FW_DIR "CMVep.bin.v2"
+#define CMVEI_FIRMWARE FW_DIR "CMVei.bin"
+#define CMVEIV2_FIRMWARE FW_DIR "CMVei.bin.v2"
+
+#define UEA_FW_NAME_MAX 30
#define NB_MODEM 4
#define BULK_TIMEOUT 300
#define CTRL_TIMEOUT 1000
-#define ACK_TIMEOUT msecs_to_jiffies(1500)
+#define ACK_TIMEOUT msecs_to_jiffies(3000)
-#define UEA_INTR_IFACE_NO 0
+#define UEA_INTR_IFACE_NO 0
#define UEA_US_IFACE_NO 1
#define UEA_DS_IFACE_NO 2
@@ -255,8 +353,9 @@ enum {
#define UEA_INTR_PIPE 0x04
#define UEA_ISO_DATA_PIPE 0x08
-#define UEA_SET_BLOCK 0x0001
-#define UEA_SET_MODE 0x0003
+#define UEA_E1_SET_BLOCK 0x0001
+#define UEA_E4_SET_BLOCK 0x002c
+#define UEA_SET_MODE 0x0003
#define UEA_SET_2183_DATA 0x0004
#define UEA_SET_TIMEOUT 0x0011
@@ -271,67 +370,180 @@ enum {
#define UEA_MPTX_MAILBOX (0x3fd6 | 0x4000)
#define UEA_MPRX_MAILBOX (0x3fdf | 0x4000)
-/* structure describing a block within a DSP page */
-struct block_info {
+/* block information in eagle4 dsp firmware */
+struct block_index {
+ __le32 PageOffset;
+ __le32 NotLastBlock;
+ __le32 dummy;
+ __le32 PageSize;
+ __le32 PageAddress;
+ __le16 dummy1;
+ __le16 PageNumber;
+} __packed;
+
+#define E4_IS_BOOT_PAGE(PageSize) ((le32_to_cpu(PageSize)) & 0x80000000)
+#define E4_PAGE_BYTES(PageSize) ((le32_to_cpu(PageSize) & 0x7fffffff) * 4)
+
+#define E4_L1_STRING_HEADER 0x10
+#define E4_MAX_PAGE_NUMBER 0x58
+#define E4_NO_SWAPPAGE_HEADERS 0x31
+
+/* l1_code is eagle4 dsp firmware format */
+struct l1_code {
+ u8 string_header[E4_L1_STRING_HEADER];
+ u8 page_number_to_block_index[E4_MAX_PAGE_NUMBER];
+ struct block_index page_header[E4_NO_SWAPPAGE_HEADERS];
+ u8 code[0];
+} __packed;
+
+/* structures describing a block within a DSP page */
+struct block_info_e1 {
__le16 wHdr;
-#define UEA_BIHDR 0xabcd
__le16 wAddress;
__le16 wSize;
__le16 wOvlOffset;
__le16 wOvl; /* overlay */
__le16 wLast;
-} __attribute__ ((packed));
-#define BLOCK_INFO_SIZE 12
+} __packed;
+#define E1_BLOCK_INFO_SIZE 12
+
+struct block_info_e4 {
+ __be16 wHdr;
+ __u8 bBootPage;
+ __u8 bPageNumber;
+ __be32 dwSize;
+ __be32 dwAddress;
+ __be16 wReserved;
+} __packed;
+#define E4_BLOCK_INFO_SIZE 14
-/* structure representing a CMV (Configuration and Management Variable) */
-struct cmv {
- __le16 wPreamble;
-#define PREAMBLE 0x535c
- __u8 bDirection;
-#define MODEMTOHOST 0x01
-#define HOSTTOMODEM 0x10
- __u8 bFunction;
-#define FUNCTION_TYPE(f) ((f) >> 4)
-#define MEMACCESS 0x1
-#define ADSLDIRECTIVE 0x7
+#define UEA_BIHDR 0xabcd
+#define UEA_RESERVED 0xffff
+
+/* constants describing cmv type */
+#define E1_PREAMBLE 0x535c
+#define E1_MODEMTOHOST 0x01
+#define E1_HOSTTOMODEM 0x10
+
+#define E1_MEMACCESS 0x1
+#define E1_ADSLDIRECTIVE 0x7
+#define E1_FUNCTION_TYPE(f) ((f) >> 4)
+#define E1_FUNCTION_SUBTYPE(f) ((f) & 0x0f)
+
+#define E4_MEMACCESS 0
+#define E4_ADSLDIRECTIVE 0xf
+#define E4_FUNCTION_TYPE(f) ((f) >> 8)
+#define E4_FUNCTION_SIZE(f) ((f) & 0x0f)
+#define E4_FUNCTION_SUBTYPE(f) (((f) >> 4) & 0x0f)
-#define FUNCTION_SUBTYPE(f) ((f) & 0x0f)
/* for MEMACCESS */
-#define REQUESTREAD 0x0
-#define REQUESTWRITE 0x1
-#define REPLYREAD 0x2
-#define REPLYWRITE 0x3
+#define E1_REQUESTREAD 0x0
+#define E1_REQUESTWRITE 0x1
+#define E1_REPLYREAD 0x2
+#define E1_REPLYWRITE 0x3
+
+#define E4_REQUESTREAD 0x0
+#define E4_REQUESTWRITE 0x4
+#define E4_REPLYREAD (E4_REQUESTREAD | 1)
+#define E4_REPLYWRITE (E4_REQUESTWRITE | 1)
+
/* for ADSLDIRECTIVE */
-#define KERNELREADY 0x0
-#define MODEMREADY 0x1
+#define E1_KERNELREADY 0x0
+#define E1_MODEMREADY 0x1
-#define MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf))
- __le16 wIndex;
- __le32 dwSymbolicAddress;
-#define MAKESA(a, b, c, d) \
+#define E4_KERNELREADY 0x0
+#define E4_MODEMREADY 0x1
+
+#define E1_MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf))
+#define E4_MAKEFUNCTION(t, st, s) (((t) & 0xf) << 8 | \
+ ((st) & 0xf) << 4 | ((s) & 0xf))
+
+#define E1_MAKESA(a, b, c, d) \
(((c) & 0xff) << 24 | \
((d) & 0xff) << 16 | \
((a) & 0xff) << 8 | \
((b) & 0xff))
-#define SA_CNTL MAKESA('C', 'N', 'T', 'L')
-#define SA_DIAG MAKESA('D', 'I', 'A', 'G')
-#define SA_INFO MAKESA('I', 'N', 'F', 'O')
-#define SA_OPTN MAKESA('O', 'P', 'T', 'N')
-#define SA_RATE MAKESA('R', 'A', 'T', 'E')
-#define SA_STAT MAKESA('S', 'T', 'A', 'T')
+#define E1_GETSA1(a) ((a >> 8) & 0xff)
+#define E1_GETSA2(a) (a & 0xff)
+#define E1_GETSA3(a) ((a >> 24) & 0xff)
+#define E1_GETSA4(a) ((a >> 16) & 0xff)
+
+#define E1_SA_CNTL E1_MAKESA('C', 'N', 'T', 'L')
+#define E1_SA_DIAG E1_MAKESA('D', 'I', 'A', 'G')
+#define E1_SA_INFO E1_MAKESA('I', 'N', 'F', 'O')
+#define E1_SA_OPTN E1_MAKESA('O', 'P', 'T', 'N')
+#define E1_SA_RATE E1_MAKESA('R', 'A', 'T', 'E')
+#define E1_SA_STAT E1_MAKESA('S', 'T', 'A', 'T')
+
+#define E4_SA_CNTL 1
+#define E4_SA_STAT 2
+#define E4_SA_INFO 3
+#define E4_SA_TEST 4
+#define E4_SA_OPTN 5
+#define E4_SA_RATE 6
+#define E4_SA_DIAG 7
+#define E4_SA_CNFG 8
+
+/* structures representing a CMV (Configuration and Management Variable) */
+struct cmv_e1 {
+ __le16 wPreamble;
+ __u8 bDirection;
+ __u8 bFunction;
+ __le16 wIndex;
+ __le32 dwSymbolicAddress;
__le16 wOffsetAddress;
__le32 dwData;
-} __attribute__ ((packed));
-#define CMV_SIZE 16
-
-/* structure representing swap information */
-struct swap_info {
+} __packed;
+
+struct cmv_e4 {
+ __be16 wGroup;
+ __be16 wFunction;
+ __be16 wOffset;
+ __be16 wAddress;
+ __be32 dwData[6];
+} __packed;
+
+/* structures representing swap information */
+struct swap_info_e1 {
__u8 bSwapPageNo;
__u8 bOvl; /* overlay */
-} __attribute__ ((packed));
+} __packed;
+
+struct swap_info_e4 {
+ __u8 bSwapPageNo;
+} __packed;
+
+/* structures representing interrupt data */
+#define e1_bSwapPageNo u.e1.s1.swapinfo.bSwapPageNo
+#define e1_bOvl u.e1.s1.swapinfo.bOvl
+#define e4_bSwapPageNo u.e4.s1.swapinfo.bSwapPageNo
+
+#define INT_LOADSWAPPAGE 0x0001
+#define INT_INCOMINGCMV 0x0002
+
+union intr_data_e1 {
+ struct {
+ struct swap_info_e1 swapinfo;
+ __le16 wDataSize;
+ } __packed s1;
+ struct {
+ struct cmv_e1 cmv;
+ __le16 wDataSize;
+ } __packed s2;
+} __packed;
+
+union intr_data_e4 {
+ struct {
+ struct swap_info_e4 swapinfo;
+ __le16 wDataSize;
+ } __packed s1;
+ struct {
+ struct cmv_e4 cmv;
+ __le16 wDataSize;
+ } __packed s2;
+} __packed;
-/* structure representing interrupt data */
struct intr_pkt {
__u8 bType;
__u8 bNotification;
@@ -339,40 +551,50 @@ struct intr_pkt {
__le16 wIndex;
__le16 wLength;
__le16 wInterrupt;
-#define INT_LOADSWAPPAGE 0x0001
-#define INT_INCOMINGCMV 0x0002
union {
- struct {
- struct swap_info swapinfo;
- __le16 wDataSize;
- } __attribute__ ((packed)) s1;
+ union intr_data_e1 e1;
+ union intr_data_e4 e4;
+ } u;
+} __packed;
- struct {
- struct cmv cmv;
- __le16 wDataSize;
- } __attribute__ ((packed)) s2;
- } __attribute__ ((packed)) u;
-#define bSwapPageNo u.s1.swapinfo.bSwapPageNo
-#define bOvl u.s1.swapinfo.bOvl
-} __attribute__ ((packed));
-#define INTR_PKT_SIZE 28
+#define E1_INTR_PKT_SIZE 28
+#define E4_INTR_PKT_SIZE 64
static struct usb_driver uea_driver;
-static DECLARE_MUTEX(uea_semaphore);
-static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III"};
+static DEFINE_MUTEX(uea_mutex);
+static const char * const chip_name[] = {
+ "ADI930", "Eagle I", "Eagle II", "Eagle III", "Eagle IV"};
static int modem_index;
static unsigned int debug;
-static int sync_wait[NB_MODEM];
+static unsigned int altsetting[NB_MODEM] = {
+ [0 ... (NB_MODEM - 1)] = FASTEST_ISO_INTF};
+static bool sync_wait[NB_MODEM];
static char *cmv_file[NB_MODEM];
+static int annex[NB_MODEM];
module_param(debug, uint, 0644);
MODULE_PARM_DESC(debug, "module debug level (0=off,1=on,2=verbose)");
+module_param_array(altsetting, uint, NULL, 0644);
+MODULE_PARM_DESC(altsetting, "alternate setting for incoming traffic: 0=bulk, "
+ "1=isoc slowest, ... , 8=isoc fastest (default)");
module_param_array(sync_wait, bool, NULL, 0644);
MODULE_PARM_DESC(sync_wait, "wait the synchronisation before starting ATM");
module_param_array(cmv_file, charp, NULL, 0644);
MODULE_PARM_DESC(cmv_file,
"file name with configuration and management variables");
+module_param_array(annex, uint, NULL, 0644);
+MODULE_PARM_DESC(annex,
+ "manually set annex a/b (0=auto, 1=annex a, 2=annex b)");
+
+#define uea_wait(sc, cond, timeo) \
+({ \
+ int _r = wait_event_interruptible_timeout(sc->sync_q, \
+ (cond) || kthread_should_stop(), timeo); \
+ if (kthread_should_stop()) \
+ _r = -ENODEV; \
+ _r; \
+})
#define UPDATE_ATM_STAT(type, val) \
do { \
@@ -380,6 +602,13 @@ MODULE_PARM_DESC(cmv_file,
sc->usbatm->atm_dev->type = val; \
} while (0)
+#define UPDATE_ATM_SIGNAL(val) \
+ do { \
+ if (sc->usbatm->atm_dev) \
+ atm_dev_signal_change(sc->usbatm->atm_dev, val); \
+ } while (0)
+
+
/* Firmware loading */
#define LOAD_INTERNAL 0xA0
#define F8051_USBCS 0x7f92
@@ -388,14 +617,13 @@ MODULE_PARM_DESC(cmv_file,
* uea_send_modem_cmd - Send a command for pre-firmware devices.
*/
static int uea_send_modem_cmd(struct usb_device *usb,
- u16 addr, u16 size, u8 * buff)
+ u16 addr, u16 size, const u8 *buff)
{
int ret = -ENOMEM;
u8 *xfer_buff;
- xfer_buff = kmalloc(size, GFP_KERNEL);
+ xfer_buff = kmemdup(buff, size, GFP_KERNEL);
if (xfer_buff) {
- memcpy(xfer_buff, buff, size);
ret = usb_control_msg(usb,
usb_sndctrlpipe(usb, 0),
LOAD_INTERNAL,
@@ -411,10 +639,12 @@ static int uea_send_modem_cmd(struct usb_device *usb,
return (ret == size) ? 0 : -EIO;
}
-static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *context)
+static void uea_upload_pre_firmware(const struct firmware *fw_entry,
+ void *context)
{
struct usb_device *usb = context;
- u8 *pfw, value;
+ const u8 *pfw;
+ u8 value;
u32 crc = 0;
int ret, size;
@@ -429,14 +659,14 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *conte
if (size < 4)
goto err_fw_corrupted;
- crc = FW_GET_LONG(pfw);
+ crc = get_unaligned_le32(pfw);
pfw += 4;
size -= 4;
if (crc32_be(0, pfw, size) != crc)
goto err_fw_corrupted;
/*
- * Start to upload formware : send reset
+ * Start to upload firmware : send reset
*/
value = 1;
ret = uea_send_modem_cmd(usb, F8051_USBCS, sizeof(value), &value);
@@ -448,7 +678,7 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *conte
while (size > 3) {
u8 len = FW_GET_BYTE(pfw);
- u16 add = FW_GET_WORD(pfw + 1);
+ u16 add = get_unaligned_le16(pfw + 1);
size -= len + 3;
if (size < 0)
@@ -476,12 +706,12 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *conte
else
uea_info(usb, "firmware uploaded\n");
- uea_leaves(usb);
- return;
+ goto err;
err_fw_corrupted:
uea_err(usb, "firmware is corrupted\n");
err:
+ release_firmware(fw_entry);
uea_leaves(usb);
}
@@ -491,27 +721,32 @@ err:
static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
{
int ret;
- char *fw_name = FW_DIR "eagle.fw";
+ char *fw_name = EAGLE_FIRMWARE;
uea_enters(usb);
uea_info(usb, "pre-firmware device, uploading firmware\n");
switch (ver) {
case ADI930:
- fw_name = FW_DIR "adi930.fw";
+ fw_name = ADI930_FIRMWARE;
break;
case EAGLE_I:
- fw_name = FW_DIR "eagleI.fw";
+ fw_name = EAGLE_I_FIRMWARE;
break;
case EAGLE_II:
- fw_name = FW_DIR "eagleII.fw";
+ fw_name = EAGLE_II_FIRMWARE;
break;
case EAGLE_III:
- fw_name = FW_DIR "eagleIII.fw";
+ fw_name = EAGLE_III_FIRMWARE;
+ break;
+ case EAGLE_IV:
+ fw_name = EAGLE_IV_FIRMWARE;
break;
}
- ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, usb, uea_upload_pre_firmware);
+ ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev,
+ GFP_KERNEL, usb,
+ uea_upload_pre_firmware);
if (ret)
uea_err(usb, "firmware %s is not available\n", fw_name);
else
@@ -527,7 +762,7 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
/*
* Make sure that the DSP code provided is safe to use.
*/
-static int check_dsp(u8 *dsp, unsigned int len)
+static int check_dsp_e1(const u8 *dsp, unsigned int len)
{
u8 pagecount, blockcount;
u16 blocksize;
@@ -543,7 +778,7 @@ static int check_dsp(u8 *dsp, unsigned int len)
for (i = 0; i < pagecount; i++) {
- pageoffset = FW_GET_LONG(dsp + p);
+ pageoffset = get_unaligned_le32(dsp + p);
p += 4;
if (pageoffset == 0)
@@ -564,7 +799,7 @@ static int check_dsp(u8 *dsp, unsigned int len)
return 1;
pp += 2; /* skip blockaddr */
- blocksize = FW_GET_WORD(dsp + pp);
+ blocksize = get_unaligned_le16(dsp + pp);
pp += 2;
/* enough space for block data? */
@@ -578,23 +813,66 @@ static int check_dsp(u8 *dsp, unsigned int len)
return 0;
}
+static int check_dsp_e4(const u8 *dsp, int len)
+{
+ int i;
+ struct l1_code *p = (struct l1_code *) dsp;
+ unsigned int sum = p->code - dsp;
+
+ if (len < sum)
+ return 1;
+
+ if (strcmp("STRATIPHY ANEXA", p->string_header) != 0 &&
+ strcmp("STRATIPHY ANEXB", p->string_header) != 0)
+ return 1;
+
+ for (i = 0; i < E4_MAX_PAGE_NUMBER; i++) {
+ struct block_index *blockidx;
+ u8 blockno = p->page_number_to_block_index[i];
+ if (blockno >= E4_NO_SWAPPAGE_HEADERS)
+ continue;
+
+ do {
+ u64 l;
+
+ if (blockno >= E4_NO_SWAPPAGE_HEADERS)
+ return 1;
+
+ blockidx = &p->page_header[blockno++];
+ if ((u8 *)(blockidx + 1) - dsp >= len)
+ return 1;
+
+ if (le16_to_cpu(blockidx->PageNumber) != i)
+ return 1;
+
+ l = E4_PAGE_BYTES(blockidx->PageSize);
+ sum += l;
+ l += le32_to_cpu(blockidx->PageOffset);
+ if (l > len)
+ return 1;
+
+ /* zero is zero regardless endianes */
+ } while (blockidx->NotLastBlock);
+ }
+
+ return (sum == len) ? 0 : 1;
+}
+
/*
* send data to the idma pipe
* */
-static int uea_idma_write(struct uea_softc *sc, void *data, u32 size)
+static int uea_idma_write(struct uea_softc *sc, const void *data, u32 size)
{
int ret = -ENOMEM;
u8 *xfer_buff;
int bytes_read;
- xfer_buff = kmalloc(size, GFP_KERNEL);
+ xfer_buff = kmemdup(data, size, GFP_KERNEL);
if (!xfer_buff) {
uea_err(INS_TO_USBDEV(sc), "can't allocate xfer_buff\n");
return ret;
}
- memcpy(xfer_buff, data, size);
-
ret = usb_bulk_msg(sc->usb_dev,
usb_sndbulkpipe(sc->usb_dev, UEA_IDMA_PIPE),
xfer_buff, size, &bytes_read, BULK_TIMEOUT);
@@ -616,28 +894,37 @@ static int request_dsp(struct uea_softc *sc)
int ret;
char *dsp_name;
- if (UEA_CHIP_VERSION(sc) == ADI930) {
+ if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+ if (IS_ISDN(sc))
+ dsp_name = DSP4I_FIRMWARE;
+ else
+ dsp_name = DSP4P_FIRMWARE;
+ } else if (UEA_CHIP_VERSION(sc) == ADI930) {
if (IS_ISDN(sc))
- dsp_name = FW_DIR "DSP9i.bin";
+ dsp_name = DSP9I_FIRMWARE;
else
- dsp_name = FW_DIR "DSP9p.bin";
+ dsp_name = DSP9P_FIRMWARE;
} else {
if (IS_ISDN(sc))
- dsp_name = FW_DIR "DSPei.bin";
+ dsp_name = DSPEI_FIRMWARE;
else
- dsp_name = FW_DIR "DSPep.bin";
+ dsp_name = DSPEP_FIRMWARE;
}
- ret = request_firmware(&sc->dsp_firm,
- dsp_name, &sc->usb_dev->dev);
+ ret = request_firmware(&sc->dsp_firm, dsp_name, &sc->usb_dev->dev);
if (ret < 0) {
uea_err(INS_TO_USBDEV(sc),
"requesting firmware %s failed with error %d\n",
- dsp_name, ret);
+ dsp_name, ret);
return ret;
}
- if (check_dsp(sc->dsp_firm->data, sc->dsp_firm->size)) {
+ if (UEA_CHIP_VERSION(sc) == EAGLE_IV)
+ ret = check_dsp_e4(sc->dsp_firm->data, sc->dsp_firm->size);
+ else
+ ret = check_dsp_e1(sc->dsp_firm->data, sc->dsp_firm->size);
+
+ if (ret) {
uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
dsp_name);
release_firmware(sc->dsp_firm);
@@ -651,14 +938,14 @@ static int request_dsp(struct uea_softc *sc)
/*
* The uea_load_page() function must be called within a process context
*/
-static void uea_load_page(void *xsc)
+static void uea_load_page_e1(struct work_struct *work)
{
- struct uea_softc *sc = xsc;
+ struct uea_softc *sc = container_of(work, struct uea_softc, task);
u16 pageno = sc->pageno;
u16 ovl = sc->ovl;
- struct block_info bi;
+ struct block_info_e1 bi;
- u8 *p;
+ const u8 *p;
u8 pagecount, blockcount;
u16 blockaddr, blocksize;
u32 pageoffset;
@@ -681,7 +968,7 @@ static void uea_load_page(void *xsc)
goto bad1;
p += 4 * pageno;
- pageoffset = FW_GET_LONG(p);
+ pageoffset = get_unaligned_le32(p);
if (pageoffset == 0)
goto bad1;
@@ -698,10 +985,10 @@ static void uea_load_page(void *xsc)
bi.wOvlOffset = cpu_to_le16(ovl | 0x8000);
for (i = 0; i < blockcount; i++) {
- blockaddr = FW_GET_WORD(p);
+ blockaddr = get_unaligned_le16(p);
p += 2;
- blocksize = FW_GET_WORD(p);
+ blocksize = get_unaligned_le16(p);
p += 2;
bi.wSize = cpu_to_le16(blocksize);
@@ -709,7 +996,7 @@ static void uea_load_page(void *xsc)
bi.wLast = cpu_to_le16((i == blockcount - 1) ? 1 : 0);
/* send block info through the IDMA pipe */
- if (uea_idma_write(sc, &bi, BLOCK_INFO_SIZE))
+ if (uea_idma_write(sc, &bi, E1_BLOCK_INFO_SIZE))
goto bad2;
/* send block data through the IDMA pipe */
@@ -725,42 +1012,145 @@ bad2:
uea_err(INS_TO_USBDEV(sc), "sending DSP block %u failed\n", i);
return;
bad1:
- uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n",pageno);
+ uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n", pageno);
+}
+
+static void __uea_load_page_e4(struct uea_softc *sc, u8 pageno, int boot)
+{
+ struct block_info_e4 bi;
+ struct block_index *blockidx;
+ struct l1_code *p = (struct l1_code *) sc->dsp_firm->data;
+ u8 blockno = p->page_number_to_block_index[pageno];
+
+ bi.wHdr = cpu_to_be16(UEA_BIHDR);
+ bi.bBootPage = boot;
+ bi.bPageNumber = pageno;
+ bi.wReserved = cpu_to_be16(UEA_RESERVED);
+
+ do {
+ const u8 *blockoffset;
+ unsigned int blocksize;
+
+ blockidx = &p->page_header[blockno];
+ blocksize = E4_PAGE_BYTES(blockidx->PageSize);
+ blockoffset = sc->dsp_firm->data + le32_to_cpu(
+ blockidx->PageOffset);
+
+ bi.dwSize = cpu_to_be32(blocksize);
+ bi.dwAddress = cpu_to_be32(le32_to_cpu(blockidx->PageAddress));
+
+ uea_dbg(INS_TO_USBDEV(sc),
+ "sending block %u for DSP page "
+ "%u size %u address %x\n",
+ blockno, pageno, blocksize,
+ le32_to_cpu(blockidx->PageAddress));
+
+ /* send block info through the IDMA pipe */
+ if (uea_idma_write(sc, &bi, E4_BLOCK_INFO_SIZE))
+ goto bad;
+
+ /* send block data through the IDMA pipe */
+ if (uea_idma_write(sc, blockoffset, blocksize))
+ goto bad;
+
+ blockno++;
+ } while (blockidx->NotLastBlock);
+
+ return;
+
+bad:
+ uea_err(INS_TO_USBDEV(sc), "sending DSP block %u failed\n", blockno);
+ return;
+}
+
+static void uea_load_page_e4(struct work_struct *work)
+{
+ struct uea_softc *sc = container_of(work, struct uea_softc, task);
+ u8 pageno = sc->pageno;
+ int i;
+ struct block_info_e4 bi;
+ struct l1_code *p;
+
+ uea_dbg(INS_TO_USBDEV(sc), "sending DSP page %u\n", pageno);
+
+ /* reload firmware when reboot start and it's loaded already */
+ if (pageno == 0 && sc->dsp_firm) {
+ release_firmware(sc->dsp_firm);
+ sc->dsp_firm = NULL;
+ }
+
+ if (sc->dsp_firm == NULL && request_dsp(sc) < 0)
+ return;
+
+ p = (struct l1_code *) sc->dsp_firm->data;
+ if (pageno >= le16_to_cpu(p->page_header[0].PageNumber)) {
+ uea_err(INS_TO_USBDEV(sc), "invalid DSP "
+ "page %u requested\n", pageno);
+ return;
+ }
+
+ if (pageno != 0) {
+ __uea_load_page_e4(sc, pageno, 0);
+ return;
+ }
+
+ uea_dbg(INS_TO_USBDEV(sc),
+ "sending Main DSP page %u\n", p->page_header[0].PageNumber);
+
+ for (i = 0; i < le16_to_cpu(p->page_header[0].PageNumber); i++) {
+ if (E4_IS_BOOT_PAGE(p->page_header[i].PageSize))
+ __uea_load_page_e4(sc, i, 1);
+ }
+
+ uea_dbg(INS_TO_USBDEV(sc) , "sending start bi\n");
+
+ bi.wHdr = cpu_to_be16(UEA_BIHDR);
+ bi.bBootPage = 0;
+ bi.bPageNumber = 0xff;
+ bi.wReserved = cpu_to_be16(UEA_RESERVED);
+ bi.dwSize = cpu_to_be32(E4_PAGE_BYTES(p->page_header[0].PageSize));
+ bi.dwAddress = cpu_to_be32(le32_to_cpu(p->page_header[0].PageAddress));
+
+ /* send block info through the IDMA pipe */
+ if (uea_idma_write(sc, &bi, E4_BLOCK_INFO_SIZE))
+ uea_err(INS_TO_USBDEV(sc), "sending DSP start bi failed\n");
}
static inline void wake_up_cmv_ack(struct uea_softc *sc)
{
+ BUG_ON(sc->cmv_ack);
sc->cmv_ack = 1;
- wake_up(&sc->cmv_ack_wait);
+ wake_up(&sc->sync_q);
}
static inline int wait_cmv_ack(struct uea_softc *sc)
{
- int ret = wait_event_timeout(sc->cmv_ack_wait,
- sc->cmv_ack, ACK_TIMEOUT);
+ int ret = uea_wait(sc, sc->cmv_ack , ACK_TIMEOUT);
+
sc->cmv_ack = 0;
+ uea_dbg(INS_TO_USBDEV(sc), "wait_event_timeout : %d ms\n",
+ jiffies_to_msecs(ret));
+
if (ret < 0)
return ret;
return (ret == 0) ? -ETIMEDOUT : 0;
-
}
#define UCDC_SEND_ENCAPSULATED_COMMAND 0x00
static int uea_request(struct uea_softc *sc,
- u16 value, u16 index, u16 size, void *data)
+ u16 value, u16 index, u16 size, const void *data)
{
u8 *xfer_buff;
int ret = -ENOMEM;
- xfer_buff = kmalloc(size, GFP_KERNEL);
+ xfer_buff = kmemdup(data, size, GFP_KERNEL);
if (!xfer_buff) {
uea_err(INS_TO_USBDEV(sc), "can't allocate xfer_buff\n");
return ret;
}
- memcpy(xfer_buff, data, size);
ret = usb_control_msg(sc->usb_dev, usb_sndctrlpipe(sc->usb_dev, 0),
UCDC_SEND_ENCAPSULATED_COMMAND,
@@ -783,50 +1173,115 @@ static int uea_request(struct uea_softc *sc,
return 0;
}
-static int uea_cmv(struct uea_softc *sc,
+static int uea_cmv_e1(struct uea_softc *sc,
u8 function, u32 address, u16 offset, u32 data)
{
- struct cmv cmv;
+ struct cmv_e1 cmv;
int ret;
+ uea_enters(INS_TO_USBDEV(sc));
+ uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, "
+ "offset : 0x%04x, data : 0x%08x\n",
+ E1_FUNCTION_TYPE(function),
+ E1_FUNCTION_SUBTYPE(function),
+ E1_GETSA1(address), E1_GETSA2(address),
+ E1_GETSA3(address),
+ E1_GETSA4(address), offset, data);
+
/* we send a request, but we expect a reply */
- sc->cmv_function = function | 0x2;
- sc->cmv_idx++;
- sc->cmv_address = address;
- sc->cmv_offset = offset;
+ sc->cmv_dsc.e1.function = function | 0x2;
+ sc->cmv_dsc.e1.idx++;
+ sc->cmv_dsc.e1.address = address;
+ sc->cmv_dsc.e1.offset = offset;
- cmv.wPreamble = cpu_to_le16(PREAMBLE);
- cmv.bDirection = HOSTTOMODEM;
+ cmv.wPreamble = cpu_to_le16(E1_PREAMBLE);
+ cmv.bDirection = E1_HOSTTOMODEM;
cmv.bFunction = function;
- cmv.wIndex = cpu_to_le16(sc->cmv_idx);
- put_unaligned(cpu_to_le32(address), &cmv.dwSymbolicAddress);
+ cmv.wIndex = cpu_to_le16(sc->cmv_dsc.e1.idx);
+ put_unaligned_le32(address, &cmv.dwSymbolicAddress);
cmv.wOffsetAddress = cpu_to_le16(offset);
- put_unaligned(cpu_to_le32(data >> 16 | data << 16), &cmv.dwData);
+ put_unaligned_le32(data >> 16 | data << 16, &cmv.dwData);
- ret = uea_request(sc, UEA_SET_BLOCK, UEA_MPTX_START, CMV_SIZE, &cmv);
+ ret = uea_request(sc, UEA_E1_SET_BLOCK, UEA_MPTX_START,
+ sizeof(cmv), &cmv);
if (ret < 0)
return ret;
- return wait_cmv_ack(sc);
+ ret = wait_cmv_ack(sc);
+ uea_leaves(INS_TO_USBDEV(sc));
+ return ret;
}
-static inline int uea_read_cmv(struct uea_softc *sc,
+static int uea_cmv_e4(struct uea_softc *sc,
+ u16 function, u16 group, u16 address, u16 offset, u32 data)
+{
+ struct cmv_e4 cmv;
+ int ret;
+
+ uea_enters(INS_TO_USBDEV(sc));
+ memset(&cmv, 0, sizeof(cmv));
+
+ uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Group : 0x%04x, "
+ "Address : 0x%04x, offset : 0x%04x, data : 0x%08x\n",
+ E4_FUNCTION_TYPE(function), E4_FUNCTION_SUBTYPE(function),
+ group, address, offset, data);
+
+ /* we send a request, but we expect a reply */
+ sc->cmv_dsc.e4.function = function | (0x1 << 4);
+ sc->cmv_dsc.e4.offset = offset;
+ sc->cmv_dsc.e4.address = address;
+ sc->cmv_dsc.e4.group = group;
+
+ cmv.wFunction = cpu_to_be16(function);
+ cmv.wGroup = cpu_to_be16(group);
+ cmv.wAddress = cpu_to_be16(address);
+ cmv.wOffset = cpu_to_be16(offset);
+ cmv.dwData[0] = cpu_to_be32(data);
+
+ ret = uea_request(sc, UEA_E4_SET_BLOCK, UEA_MPTX_START,
+ sizeof(cmv), &cmv);
+ if (ret < 0)
+ return ret;
+ ret = wait_cmv_ack(sc);
+ uea_leaves(INS_TO_USBDEV(sc));
+ return ret;
+}
+
+static inline int uea_read_cmv_e1(struct uea_softc *sc,
u32 address, u16 offset, u32 *data)
{
- int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTREAD),
+ int ret = uea_cmv_e1(sc, E1_MAKEFUNCTION(E1_MEMACCESS, E1_REQUESTREAD),
address, offset, 0);
if (ret < 0)
uea_err(INS_TO_USBDEV(sc),
"reading cmv failed with error %d\n", ret);
else
- *data = sc->data;
+ *data = sc->data;
+
+ return ret;
+}
+static inline int uea_read_cmv_e4(struct uea_softc *sc,
+ u8 size, u16 group, u16 address, u16 offset, u32 *data)
+{
+ int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS,
+ E4_REQUESTREAD, size),
+ group, address, offset, 0);
+ if (ret < 0)
+ uea_err(INS_TO_USBDEV(sc),
+ "reading cmv failed with error %d\n", ret);
+ else {
+ *data = sc->data;
+ /* size is in 16-bit word quantities */
+ if (size > 2)
+ *(data + 1) = sc->data1;
+ }
return ret;
}
-static inline int uea_write_cmv(struct uea_softc *sc,
+static inline int uea_write_cmv_e1(struct uea_softc *sc,
u32 address, u16 offset, u32 data)
{
- int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTWRITE),
+ int ret = uea_cmv_e1(sc, E1_MAKEFUNCTION(E1_MEMACCESS, E1_REQUESTWRITE),
address, offset, data);
if (ret < 0)
uea_err(INS_TO_USBDEV(sc),
@@ -835,12 +1290,49 @@ static inline int uea_write_cmv(struct uea_softc *sc,
return ret;
}
+static inline int uea_write_cmv_e4(struct uea_softc *sc,
+ u8 size, u16 group, u16 address, u16 offset, u32 data)
+{
+ int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS,
+ E4_REQUESTWRITE, size),
+ group, address, offset, data);
+ if (ret < 0)
+ uea_err(INS_TO_USBDEV(sc),
+ "writing cmv failed with error %d\n", ret);
+
+ return ret;
+}
+
+static void uea_set_bulk_timeout(struct uea_softc *sc, u32 dsrate)
+{
+ int ret;
+ u16 timeout;
+
+ /* in bulk mode the modem have problem with high rate
+ * changing internal timing could improve things, but the
+ * value is mysterious.
+ * ADI930 don't support it (-EPIPE error).
+ */
+
+ if (UEA_CHIP_VERSION(sc) == ADI930 ||
+ altsetting[sc->modem_index] > 0 ||
+ sc->stats.phy.dsrate == dsrate)
+ return;
+
+ /* Original timming (1Mbit/s) from ADI (used in windows driver) */
+ timeout = (dsrate <= 1024*1024) ? 0 : 1;
+ ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL);
+ uea_info(INS_TO_USBDEV(sc), "setting new timeout %d%s\n",
+ timeout, ret < 0 ? " failed" : "");
+
+}
+
/*
* Monitor the modem and update the stat
* return 0 if everything is ok
* return < 0 if an error occurs (-EAGAIN reboot needed)
*/
-static int uea_stat(struct uea_softc *sc)
+static int uea_stat_e1(struct uea_softc *sc)
{
u32 data;
int ret;
@@ -848,7 +1340,7 @@ static int uea_stat(struct uea_softc *sc)
uea_enters(INS_TO_USBDEV(sc));
data = sc->stats.phy.state;
- ret = uea_read_cmv(sc, SA_STAT, 0, &sc->stats.phy.state);
+ ret = uea_read_cmv_e1(sc, E1_SA_STAT, 0, &sc->stats.phy.state);
if (ret < 0)
return ret;
@@ -867,7 +1359,8 @@ static int uea_stat(struct uea_softc *sc)
break;
case 3: /* fail ... */
- uea_info(INS_TO_USBDEV(sc), "modem synchronization failed\n");
+ uea_info(INS_TO_USBDEV(sc), "modem synchronization failed"
+ " (may be try other cmv/dsp)\n");
return -EAGAIN;
case 4 ... 6: /* test state */
@@ -891,27 +1384,19 @@ static int uea_stat(struct uea_softc *sc)
/* release the dsp firmware as it is not needed until
* the next failure
*/
- if (sc->dsp_firm) {
- release_firmware(sc->dsp_firm);
- sc->dsp_firm = NULL;
- }
-
- ret = uea_read_cmv(sc, SA_INFO, 10, &sc->stats.phy.firmid);
- if (ret < 0)
- return ret;
- uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
- sc->stats.phy.firmid);
+ release_firmware(sc->dsp_firm);
+ sc->dsp_firm = NULL;
}
/* always update it as atm layer could not be init when we switch to
* operational state
*/
- UPDATE_ATM_STAT(signal, ATM_PHY_SIG_FOUND);
+ UPDATE_ATM_SIGNAL(ATM_PHY_SIG_FOUND);
/* wake up processes waiting for synchronization */
wake_up(&sc->sync_q);
- ret = uea_read_cmv(sc, SA_DIAG, 2, &sc->stats.phy.flags);
+ ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 2, &sc->stats.phy.flags);
if (ret < 0)
return ret;
sc->stats.phy.mflags |= sc->stats.phy.flags;
@@ -920,109 +1405,230 @@ static int uea_stat(struct uea_softc *sc)
* we check the status again in order to detect the failure earlier
*/
if (sc->stats.phy.flags) {
- uea_dbg(INS_TO_USBDEV(sc), "Stat flag = %d\n",
+ uea_dbg(INS_TO_USBDEV(sc), "Stat flag = 0x%x\n",
sc->stats.phy.flags);
return 0;
}
- ret = uea_read_cmv(sc, SA_RATE, 0, &data);
+ ret = uea_read_cmv_e1(sc, E1_SA_RATE, 0, &data);
if (ret < 0)
return ret;
- /* in bulk mode the modem have problem with high rate
- * changing internal timing could improve things, but the
- * value is misterious.
- * ADI930 don't support it (-EPIPE error).
- */
- if (UEA_CHIP_VERSION(sc) != ADI930
- && sc->stats.phy.dsrate != (data >> 16) * 32) {
- /* Original timming from ADI(used in windows driver)
- * 0x20ffff>>16 * 32 = 32 * 32 = 1Mbits
- */
- u16 timeout = (data <= 0x20ffff) ? 0 : 1;
- ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL);
- uea_info(INS_TO_USBDEV(sc),
- "setting new timeout %d%s\n", timeout,
- ret < 0?" failed":"");
- }
+ uea_set_bulk_timeout(sc, (data >> 16) * 32);
sc->stats.phy.dsrate = (data >> 16) * 32;
sc->stats.phy.usrate = (data & 0xffff) * 32;
UPDATE_ATM_STAT(link_rate, sc->stats.phy.dsrate * 1000 / 424);
- ret = uea_read_cmv(sc, SA_DIAG, 23, &data);
+ ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 23, &data);
if (ret < 0)
return ret;
sc->stats.phy.dsattenuation = (data & 0xff) / 2;
- ret = uea_read_cmv(sc, SA_DIAG, 47, &data);
+ ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 47, &data);
if (ret < 0)
return ret;
sc->stats.phy.usattenuation = (data & 0xff) / 2;
- ret = uea_read_cmv(sc, SA_DIAG, 25, &sc->stats.phy.dsmargin);
+ ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 25, &sc->stats.phy.dsmargin);
if (ret < 0)
return ret;
- ret = uea_read_cmv(sc, SA_DIAG, 49, &sc->stats.phy.usmargin);
+ ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 49, &sc->stats.phy.usmargin);
if (ret < 0)
return ret;
- ret = uea_read_cmv(sc, SA_DIAG, 51, &sc->stats.phy.rxflow);
+ ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 51, &sc->stats.phy.rxflow);
if (ret < 0)
return ret;
- ret = uea_read_cmv(sc, SA_DIAG, 52, &sc->stats.phy.txflow);
+ ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 52, &sc->stats.phy.txflow);
if (ret < 0)
return ret;
- ret = uea_read_cmv(sc, SA_DIAG, 54, &sc->stats.phy.dsunc);
+ ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 54, &sc->stats.phy.dsunc);
if (ret < 0)
return ret;
/* only for atu-c */
- ret = uea_read_cmv(sc, SA_DIAG, 58, &sc->stats.phy.usunc);
+ ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 58, &sc->stats.phy.usunc);
if (ret < 0)
return ret;
- ret = uea_read_cmv(sc, SA_DIAG, 53, &sc->stats.phy.dscorr);
+ ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 53, &sc->stats.phy.dscorr);
if (ret < 0)
return ret;
/* only for atu-c */
- ret = uea_read_cmv(sc, SA_DIAG, 57, &sc->stats.phy.uscorr);
+ ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 57, &sc->stats.phy.uscorr);
if (ret < 0)
return ret;
- ret = uea_read_cmv(sc, SA_INFO, 8, &sc->stats.phy.vidco);
+ ret = uea_read_cmv_e1(sc, E1_SA_INFO, 8, &sc->stats.phy.vidco);
if (ret < 0)
return ret;
- ret = uea_read_cmv(sc, SA_INFO, 13, &sc->stats.phy.vidcpe);
+ ret = uea_read_cmv_e1(sc, E1_SA_INFO, 13, &sc->stats.phy.vidcpe);
if (ret < 0)
return ret;
return 0;
}
-static int request_cmvs(struct uea_softc *sc,
- struct uea_cmvs **cmvs, const struct firmware **fw)
+static int uea_stat_e4(struct uea_softc *sc)
{
- int ret, size;
- u8 *data;
+ u32 data;
+ u32 tmp_arr[2];
+ int ret;
+
+ uea_enters(INS_TO_USBDEV(sc));
+ data = sc->stats.phy.state;
+
+ /* XXX only need to be done before operationnal... */
+ ret = uea_read_cmv_e4(sc, 1, E4_SA_STAT, 0, 0, &sc->stats.phy.state);
+ if (ret < 0)
+ return ret;
+
+ switch (sc->stats.phy.state) {
+ case 0x0: /* not yet synchronized */
+ case 0x1:
+ case 0x3:
+ case 0x4:
+ uea_dbg(INS_TO_USBDEV(sc), "modem not yet "
+ "synchronized\n");
+ return 0;
+ case 0x5: /* initialization */
+ case 0x6:
+ case 0x9:
+ case 0xa:
+ uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n");
+ return 0;
+ case 0x2: /* fail ... */
+ uea_info(INS_TO_USBDEV(sc), "modem synchronization "
+ "failed (may be try other cmv/dsp)\n");
+ return -EAGAIN;
+ case 0x7: /* operational */
+ break;
+ default:
+ uea_warn(INS_TO_USBDEV(sc), "unknown state: %x\n",
+ sc->stats.phy.state);
+ return 0;
+ }
+
+ if (data != 7) {
+ uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_OFF, 0, NULL);
+ uea_info(INS_TO_USBDEV(sc), "modem operational\n");
+
+ /* release the dsp firmware as it is not needed until
+ * the next failure
+ */
+ release_firmware(sc->dsp_firm);
+ sc->dsp_firm = NULL;
+ }
+
+ /* always update it as atm layer could not be init when we switch to
+ * operational state
+ */
+ UPDATE_ATM_SIGNAL(ATM_PHY_SIG_FOUND);
+
+ /* wake up processes waiting for synchronization */
+ wake_up(&sc->sync_q);
+
+ /* TODO improve this state machine :
+ * we need some CMV info : what they do and their unit
+ * we should find the equivalent of eagle3- CMV
+ */
+ /* check flags */
+ ret = uea_read_cmv_e4(sc, 1, E4_SA_DIAG, 0, 0, &sc->stats.phy.flags);
+ if (ret < 0)
+ return ret;
+ sc->stats.phy.mflags |= sc->stats.phy.flags;
+
+ /* in case of a flags ( for example delineation LOSS (& 0x10)),
+ * we check the status again in order to detect the failure earlier
+ */
+ if (sc->stats.phy.flags) {
+ uea_dbg(INS_TO_USBDEV(sc), "Stat flag = 0x%x\n",
+ sc->stats.phy.flags);
+ if (sc->stats.phy.flags & 1) /* delineation LOSS */
+ return -EAGAIN;
+ if (sc->stats.phy.flags & 0x4000) /* Reset Flag */
+ return -EAGAIN;
+ return 0;
+ }
+
+ /* rate data may be in upper or lower half of 64 bit word, strange */
+ ret = uea_read_cmv_e4(sc, 4, E4_SA_RATE, 0, 0, tmp_arr);
+ if (ret < 0)
+ return ret;
+ data = (tmp_arr[0]) ? tmp_arr[0] : tmp_arr[1];
+ sc->stats.phy.usrate = data / 1000;
+
+ ret = uea_read_cmv_e4(sc, 4, E4_SA_RATE, 1, 0, tmp_arr);
+ if (ret < 0)
+ return ret;
+ data = (tmp_arr[0]) ? tmp_arr[0] : tmp_arr[1];
+ uea_set_bulk_timeout(sc, data / 1000);
+ sc->stats.phy.dsrate = data / 1000;
+ UPDATE_ATM_STAT(link_rate, sc->stats.phy.dsrate * 1000 / 424);
+
+ ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 68, 1, &data);
+ if (ret < 0)
+ return ret;
+ sc->stats.phy.dsattenuation = data / 10;
+
+ ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 69, 1, &data);
+ if (ret < 0)
+ return ret;
+ sc->stats.phy.usattenuation = data / 10;
+
+ ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 68, 3, &data);
+ if (ret < 0)
+ return ret;
+ sc->stats.phy.dsmargin = data / 2;
+
+ ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 69, 3, &data);
+ if (ret < 0)
+ return ret;
+ sc->stats.phy.usmargin = data / 10;
+
+ return 0;
+}
+
+static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver)
+{
+ char file_arr[] = "CMVxy.bin";
char *file;
- static char cmv_name[256] = FW_DIR;
+ kparam_block_sysfs_write(cmv_file);
+ /* set proper name corresponding modem version and line type */
if (cmv_file[sc->modem_index] == NULL) {
if (UEA_CHIP_VERSION(sc) == ADI930)
- file = (IS_ISDN(sc)) ? "CMV9i.bin" : "CMV9p.bin";
+ file_arr[3] = '9';
+ else if (UEA_CHIP_VERSION(sc) == EAGLE_IV)
+ file_arr[3] = '4';
else
- file = (IS_ISDN(sc)) ? "CMVei.bin" : "CMVep.bin";
+ file_arr[3] = 'e';
+
+ file_arr[4] = IS_ISDN(sc) ? 'i' : 'p';
+ file = file_arr;
} else
file = cmv_file[sc->modem_index];
strcpy(cmv_name, FW_DIR);
- strlcat(cmv_name, file, sizeof(cmv_name));
+ strlcat(cmv_name, file, UEA_FW_NAME_MAX);
+ if (ver == 2)
+ strlcat(cmv_name, ".v2", UEA_FW_NAME_MAX);
+ kparam_unblock_sysfs_write(cmv_file);
+}
+
+static int request_cmvs_old(struct uea_softc *sc,
+ void **cmvs, const struct firmware **fw)
+{
+ int ret, size;
+ u8 *data;
+ char cmv_name[UEA_FW_NAME_MAX]; /* 30 bytes stack variable */
+ cmvs_file_name(sc, cmv_name, 1);
ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev);
if (ret < 0) {
uea_err(INS_TO_USBDEV(sc),
@@ -1032,16 +1638,202 @@ static int request_cmvs(struct uea_softc *sc,
}
data = (u8 *) (*fw)->data;
- size = *data * sizeof(struct uea_cmvs) + 1;
- if (size != (*fw)->size) {
- uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
- cmv_name);
- release_firmware(*fw);
- return -EILSEQ;
+ size = (*fw)->size;
+ if (size < 1)
+ goto err_fw_corrupted;
+
+ if (size != *data * sizeof(struct uea_cmvs_v1) + 1)
+ goto err_fw_corrupted;
+
+ *cmvs = (void *)(data + 1);
+ return *data;
+
+err_fw_corrupted:
+ uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", cmv_name);
+ release_firmware(*fw);
+ return -EILSEQ;
+}
+
+static int request_cmvs(struct uea_softc *sc,
+ void **cmvs, const struct firmware **fw, int *ver)
+{
+ int ret, size;
+ u32 crc;
+ u8 *data;
+ char cmv_name[UEA_FW_NAME_MAX]; /* 30 bytes stack variable */
+
+ cmvs_file_name(sc, cmv_name, 2);
+ ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev);
+ if (ret < 0) {
+ /* if caller can handle old version, try to provide it */
+ if (*ver == 1) {
+ uea_warn(INS_TO_USBDEV(sc), "requesting "
+ "firmware %s failed, "
+ "try to get older cmvs\n", cmv_name);
+ return request_cmvs_old(sc, cmvs, fw);
+ }
+ uea_err(INS_TO_USBDEV(sc),
+ "requesting firmware %s failed with error %d\n",
+ cmv_name, ret);
+ return ret;
+ }
+
+ size = (*fw)->size;
+ data = (u8 *) (*fw)->data;
+ if (size < 4 || strncmp(data, "cmv2", 4) != 0) {
+ if (*ver == 1) {
+ uea_warn(INS_TO_USBDEV(sc), "firmware %s is corrupted,"
+ " try to get older cmvs\n", cmv_name);
+ release_firmware(*fw);
+ return request_cmvs_old(sc, cmvs, fw);
+ }
+ goto err_fw_corrupted;
}
- *cmvs = (struct uea_cmvs *)(data + 1);
+ *ver = 2;
+
+ data += 4;
+ size -= 4;
+ if (size < 5)
+ goto err_fw_corrupted;
+
+ crc = get_unaligned_le32(data);
+ data += 4;
+ size -= 4;
+ if (crc32_be(0, data, size) != crc)
+ goto err_fw_corrupted;
+
+ if (size != *data * sizeof(struct uea_cmvs_v2) + 1)
+ goto err_fw_corrupted;
+
+ *cmvs = (void *) (data + 1);
return *data;
+
+err_fw_corrupted:
+ uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", cmv_name);
+ release_firmware(*fw);
+ return -EILSEQ;
+}
+
+static int uea_send_cmvs_e1(struct uea_softc *sc)
+{
+ int i, ret, len;
+ void *cmvs_ptr;
+ const struct firmware *cmvs_fw;
+ int ver = 1; /* we can handle v1 cmv firmware version; */
+
+ /* Enter in R-IDLE (cmv) until instructed otherwise */
+ ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Dump firmware version */
+ ret = uea_read_cmv_e1(sc, E1_SA_INFO, 10, &sc->stats.phy.firmid);
+ if (ret < 0)
+ return ret;
+ uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
+ sc->stats.phy.firmid);
+
+ /* get options */
+ ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver);
+ if (ret < 0)
+ return ret;
+
+ /* send options */
+ if (ver == 1) {
+ struct uea_cmvs_v1 *cmvs_v1 = cmvs_ptr;
+
+ uea_warn(INS_TO_USBDEV(sc), "use deprecated cmvs version, "
+ "please update your firmware\n");
+
+ for (i = 0; i < len; i++) {
+ ret = uea_write_cmv_e1(sc,
+ get_unaligned_le32(&cmvs_v1[i].address),
+ get_unaligned_le16(&cmvs_v1[i].offset),
+ get_unaligned_le32(&cmvs_v1[i].data));
+ if (ret < 0)
+ goto out;
+ }
+ } else if (ver == 2) {
+ struct uea_cmvs_v2 *cmvs_v2 = cmvs_ptr;
+
+ for (i = 0; i < len; i++) {
+ ret = uea_write_cmv_e1(sc,
+ get_unaligned_le32(&cmvs_v2[i].address),
+ (u16) get_unaligned_le32(&cmvs_v2[i].offset),
+ get_unaligned_le32(&cmvs_v2[i].data));
+ if (ret < 0)
+ goto out;
+ }
+ } else {
+ /* This really should not happen */
+ uea_err(INS_TO_USBDEV(sc), "bad cmvs version %d\n", ver);
+ goto out;
+ }
+
+ /* Enter in R-ACT-REQ */
+ ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 2);
+ uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
+ uea_info(INS_TO_USBDEV(sc), "modem started, waiting "
+ "synchronization...\n");
+out:
+ release_firmware(cmvs_fw);
+ return ret;
+}
+
+static int uea_send_cmvs_e4(struct uea_softc *sc)
+{
+ int i, ret, len;
+ void *cmvs_ptr;
+ const struct firmware *cmvs_fw;
+ int ver = 2; /* we can only handle v2 cmv firmware version; */
+
+ /* Enter in R-IDLE (cmv) until instructed otherwise */
+ ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Dump firmware version */
+ /* XXX don't read the 3th byte as it is always 6 */
+ ret = uea_read_cmv_e4(sc, 2, E4_SA_INFO, 55, 0, &sc->stats.phy.firmid);
+ if (ret < 0)
+ return ret;
+ uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
+ sc->stats.phy.firmid);
+
+
+ /* get options */
+ ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver);
+ if (ret < 0)
+ return ret;
+
+ /* send options */
+ if (ver == 2) {
+ struct uea_cmvs_v2 *cmvs_v2 = cmvs_ptr;
+
+ for (i = 0; i < len; i++) {
+ ret = uea_write_cmv_e4(sc, 1,
+ get_unaligned_le32(&cmvs_v2[i].group),
+ get_unaligned_le32(&cmvs_v2[i].address),
+ get_unaligned_le32(&cmvs_v2[i].offset),
+ get_unaligned_le32(&cmvs_v2[i].data));
+ if (ret < 0)
+ goto out;
+ }
+ } else {
+ /* This really should not happen */
+ uea_err(INS_TO_USBDEV(sc), "bad cmvs version %d\n", ver);
+ goto out;
+ }
+
+ /* Enter in R-ACT-REQ */
+ ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 2);
+ uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
+ uea_info(INS_TO_USBDEV(sc), "modem started, waiting "
+ "synchronization...\n");
+out:
+ release_firmware(cmvs_fw);
+ return ret;
}
/* Start boot post firmware modem:
@@ -1053,15 +1845,19 @@ static int request_cmvs(struct uea_softc *sc,
static int uea_start_reset(struct uea_softc *sc)
{
u16 zero = 0; /* ;-) */
- int i, len, ret;
- struct uea_cmvs *cmvs;
- const struct firmware *cmvs_fw;
+ int ret;
uea_enters(INS_TO_USBDEV(sc));
uea_info(INS_TO_USBDEV(sc), "(re)booting started\n");
+ /* mask interrupt */
sc->booting = 1;
- UPDATE_ATM_STAT(signal, ATM_PHY_SIG_LOST);
+ /* We need to set this here because, a ack timeout could have occurred,
+ * but before we start the reboot, the ack occurs and set this to 1.
+ * So we will failed to wait Ready CMV.
+ */
+ sc->cmv_ack = 0;
+ UPDATE_ATM_SIGNAL(ATM_PHY_SIG_LOST);
/* reset statistics */
memset(&sc->stats, 0, sizeof(struct uea_stats));
@@ -1070,22 +1866,36 @@ static int uea_start_reset(struct uea_softc *sc)
uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL);
uea_request(sc, UEA_SET_MODE, UEA_BOOT_IDMA, 0, NULL);
- /* enter reset mode */
+ /* enter reset mode */
uea_request(sc, UEA_SET_MODE, UEA_START_RESET, 0, NULL);
/* original driver use 200ms, but windows driver use 100ms */
- msleep(100);
+ ret = uea_wait(sc, 0, msecs_to_jiffies(100));
+ if (ret < 0)
+ return ret;
/* leave reset mode */
uea_request(sc, UEA_SET_MODE, UEA_END_RESET, 0, NULL);
- /* clear tx and rx mailboxes */
- uea_request(sc, UEA_SET_2183_DATA, UEA_MPTX_MAILBOX, 2, &zero);
- uea_request(sc, UEA_SET_2183_DATA, UEA_MPRX_MAILBOX, 2, &zero);
- uea_request(sc, UEA_SET_2183_DATA, UEA_SWAP_MAILBOX, 2, &zero);
+ if (UEA_CHIP_VERSION(sc) != EAGLE_IV) {
+ /* clear tx and rx mailboxes */
+ uea_request(sc, UEA_SET_2183_DATA, UEA_MPTX_MAILBOX, 2, &zero);
+ uea_request(sc, UEA_SET_2183_DATA, UEA_MPRX_MAILBOX, 2, &zero);
+ uea_request(sc, UEA_SET_2183_DATA, UEA_SWAP_MAILBOX, 2, &zero);
+ }
+
+ ret = uea_wait(sc, 0, msecs_to_jiffies(1000));
+ if (ret < 0)
+ return ret;
+
+ if (UEA_CHIP_VERSION(sc) == EAGLE_IV)
+ sc->cmv_dsc.e4.function = E4_MAKEFUNCTION(E4_ADSLDIRECTIVE,
+ E4_MODEMREADY, 1);
+ else
+ sc->cmv_dsc.e1.function = E1_MAKEFUNCTION(E1_ADSLDIRECTIVE,
+ E1_MODEMREADY);
- msleep(1000);
- sc->cmv_function = MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY);
+ /* demask interrupt */
sc->booting = 0;
/* start loading DSP */
@@ -1098,28 +1908,12 @@ static int uea_start_reset(struct uea_softc *sc)
if (ret < 0)
return ret;
- /* Enter in R-IDLE (cmv) until instructed otherwise */
- ret = uea_write_cmv(sc, SA_CNTL, 0, 1);
- if (ret < 0)
- return ret;
+ uea_vdbg(INS_TO_USBDEV(sc), "Ready CMV received\n");
- /* get options */
- ret = len = request_cmvs(sc, &cmvs, &cmvs_fw);
+ ret = sc->send_cmvs(sc);
if (ret < 0)
return ret;
- /* send options */
- for (i = 0; i < len; i++) {
- ret = uea_write_cmv(sc, FW_GET_LONG(&cmvs[i].address),
- FW_GET_WORD(&cmvs[i].offset),
- FW_GET_LONG(&cmvs[i].data));
- if (ret < 0)
- goto out;
- }
- /* Enter in R-ACT-REQ */
- ret = uea_write_cmv(sc, SA_CNTL, 0, 2);
-out:
- release_firmware(cmvs_fw);
sc->reset = 0;
uea_leaves(INS_TO_USBDEV(sc));
return ret;
@@ -1136,14 +1930,16 @@ static int uea_kthread(void *data)
struct uea_softc *sc = data;
int ret = -EAGAIN;
+ set_freezable();
uea_enters(INS_TO_USBDEV(sc));
while (!kthread_should_stop()) {
if (ret < 0 || sc->reset)
ret = uea_start_reset(sc);
if (!ret)
- ret = uea_stat(sc);
+ ret = sc->stat(sc);
if (ret != -EAGAIN)
- msleep(1000);
+ uea_wait(sc, 0, msecs_to_jiffies(1000));
+ try_to_freeze();
}
uea_leaves(INS_TO_USBDEV(sc));
return ret;
@@ -1154,8 +1950,9 @@ static int load_XILINX_firmware(struct uea_softc *sc)
{
const struct firmware *fw_entry;
int ret, size, u, ln;
- u8 *pfw, value;
- char *fw_name = FW_DIR "930-fpga.bin";
+ const u8 *pfw;
+ u8 value;
+ char *fw_name = FPGA930_FIRMWARE;
uea_enters(INS_TO_USBDEV(sc));
@@ -1184,8 +1981,7 @@ static int load_XILINX_firmware(struct uea_softc *sc)
}
}
- /* finish to send the fpga
- */
+ /* finish to send the fpga */
ret = uea_request(sc, 0xe, 1, 0, NULL);
if (ret < 0) {
uea_err(INS_TO_USBDEV(sc),
@@ -1193,14 +1989,12 @@ static int load_XILINX_firmware(struct uea_softc *sc)
goto err1;
}
- /*
- * Tell the modem we finish : de-assert reset
- */
+ /* Tell the modem we finish : de-assert reset */
value = 0;
ret = uea_send_modem_cmd(sc->usb_dev, 0xe, 1, &value);
if (ret < 0)
- uea_err(sc->usb_dev, "elsa de-assert failed with error %d\n", ret);
-
+ uea_err(sc->usb_dev, "elsa de-assert failed with error"
+ " %d\n", ret);
err1:
release_firmware(fw_entry);
@@ -1209,98 +2003,160 @@ err0:
return ret;
}
-static void uea_dispatch_cmv(struct uea_softc *sc, struct cmv* cmv)
+/* The modem send us an ack. First with check if it right */
+static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr)
{
+ struct cmv_dsc_e1 *dsc = &sc->cmv_dsc.e1;
+ struct cmv_e1 *cmv = &intr->u.e1.s2.cmv;
+
uea_enters(INS_TO_USBDEV(sc));
- if (le16_to_cpu(cmv->wPreamble) != PREAMBLE)
+ if (le16_to_cpu(cmv->wPreamble) != E1_PREAMBLE)
goto bad1;
- if (cmv->bDirection != MODEMTOHOST)
+ if (cmv->bDirection != E1_MODEMTOHOST)
goto bad1;
/* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to
- * the first MEMACESS cmv. Ignore it...
+ * the first MEMACCESS cmv. Ignore it...
*/
- if (cmv->bFunction != sc->cmv_function) {
+ if (cmv->bFunction != dsc->function) {
if (UEA_CHIP_VERSION(sc) == ADI930
- && cmv->bFunction == MAKEFUNCTION(2, 2)) {
- cmv->wIndex = cpu_to_le16(sc->cmv_idx);
- put_unaligned(cpu_to_le32(sc->cmv_address), &cmv->dwSymbolicAddress);
- cmv->wOffsetAddress = cpu_to_le16(sc->cmv_offset);
- }
- else
+ && cmv->bFunction == E1_MAKEFUNCTION(2, 2)) {
+ cmv->wIndex = cpu_to_le16(dsc->idx);
+ put_unaligned_le32(dsc->address,
+ &cmv->dwSymbolicAddress);
+ cmv->wOffsetAddress = cpu_to_le16(dsc->offset);
+ } else
goto bad2;
}
- if (cmv->bFunction == MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY)) {
+ if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE,
+ E1_MODEMREADY)) {
wake_up_cmv_ack(sc);
+ uea_leaves(INS_TO_USBDEV(sc));
return;
}
/* in case of MEMACCESS */
- if (le16_to_cpu(cmv->wIndex) != sc->cmv_idx ||
- le32_to_cpu(get_unaligned(&cmv->dwSymbolicAddress)) !=
- sc->cmv_address
- || le16_to_cpu(cmv->wOffsetAddress) != sc->cmv_offset)
+ if (le16_to_cpu(cmv->wIndex) != dsc->idx ||
+ get_unaligned_le32(&cmv->dwSymbolicAddress) != dsc->address ||
+ le16_to_cpu(cmv->wOffsetAddress) != dsc->offset)
goto bad2;
- sc->data = le32_to_cpu(get_unaligned(&cmv->dwData));
+ sc->data = get_unaligned_le32(&cmv->dwData);
sc->data = sc->data << 16 | sc->data >> 16;
wake_up_cmv_ack(sc);
+ uea_leaves(INS_TO_USBDEV(sc));
return;
bad2:
- uea_err(INS_TO_USBDEV(sc), "unexpected cmv received,"
+ uea_err(INS_TO_USBDEV(sc), "unexpected cmv received, "
"Function : %d, Subfunction : %d\n",
- FUNCTION_TYPE(cmv->bFunction),
- FUNCTION_SUBTYPE(cmv->bFunction));
+ E1_FUNCTION_TYPE(cmv->bFunction),
+ E1_FUNCTION_SUBTYPE(cmv->bFunction));
+ uea_leaves(INS_TO_USBDEV(sc));
return;
bad1:
uea_err(INS_TO_USBDEV(sc), "invalid cmv received, "
"wPreamble %d, bDirection %d\n",
le16_to_cpu(cmv->wPreamble), cmv->bDirection);
+ uea_leaves(INS_TO_USBDEV(sc));
+}
+
+/* The modem send us an ack. First with check if it right */
+static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr)
+{
+ struct cmv_dsc_e4 *dsc = &sc->cmv_dsc.e4;
+ struct cmv_e4 *cmv = &intr->u.e4.s2.cmv;
+
+ uea_enters(INS_TO_USBDEV(sc));
+ uea_dbg(INS_TO_USBDEV(sc), "cmv %x %x %x %x %x %x\n",
+ be16_to_cpu(cmv->wGroup), be16_to_cpu(cmv->wFunction),
+ be16_to_cpu(cmv->wOffset), be16_to_cpu(cmv->wAddress),
+ be32_to_cpu(cmv->dwData[0]), be32_to_cpu(cmv->dwData[1]));
+
+ if (be16_to_cpu(cmv->wFunction) != dsc->function)
+ goto bad2;
+
+ if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE,
+ E4_MODEMREADY, 1)) {
+ wake_up_cmv_ack(sc);
+ uea_leaves(INS_TO_USBDEV(sc));
+ return;
+ }
+
+ /* in case of MEMACCESS */
+ if (be16_to_cpu(cmv->wOffset) != dsc->offset ||
+ be16_to_cpu(cmv->wGroup) != dsc->group ||
+ be16_to_cpu(cmv->wAddress) != dsc->address)
+ goto bad2;
+
+ sc->data = be32_to_cpu(cmv->dwData[0]);
+ sc->data1 = be32_to_cpu(cmv->dwData[1]);
+ wake_up_cmv_ack(sc);
+ uea_leaves(INS_TO_USBDEV(sc));
+ return;
+
+bad2:
+ uea_err(INS_TO_USBDEV(sc), "unexpected cmv received, "
+ "Function : %d, Subfunction : %d\n",
+ E4_FUNCTION_TYPE(cmv->wFunction),
+ E4_FUNCTION_SUBTYPE(cmv->wFunction));
+ uea_leaves(INS_TO_USBDEV(sc));
+ return;
+}
+
+static void uea_schedule_load_page_e1(struct uea_softc *sc,
+ struct intr_pkt *intr)
+{
+ sc->pageno = intr->e1_bSwapPageNo;
+ sc->ovl = intr->e1_bOvl >> 4 | intr->e1_bOvl << 4;
+ schedule_work(&sc->task);
+}
+
+static void uea_schedule_load_page_e4(struct uea_softc *sc,
+ struct intr_pkt *intr)
+{
+ sc->pageno = intr->e4_bSwapPageNo;
+ schedule_work(&sc->task);
}
/*
* interrupt handler
*/
-static void uea_intr(struct urb *urb, struct pt_regs *regs)
+static void uea_intr(struct urb *urb)
{
- struct uea_softc *sc = (struct uea_softc *)urb->context;
- struct intr_pkt *intr;
+ struct uea_softc *sc = urb->context;
+ struct intr_pkt *intr = urb->transfer_buffer;
+ int status = urb->status;
+
uea_enters(INS_TO_USBDEV(sc));
- if (urb->status < 0) {
+ if (unlikely(status < 0)) {
uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n",
- urb->status);
+ status);
return;
}
- intr = (struct intr_pkt *) urb->transfer_buffer;
-
/* device-to-host interrupt */
if (intr->bType != 0x08 || sc->booting) {
- uea_err(INS_TO_USBDEV(sc), "wrong intr\n");
- // rebooting ?
- // sc->reset = 1;
+ uea_err(INS_TO_USBDEV(sc), "wrong interrupt\n");
goto resubmit;
}
switch (le16_to_cpu(intr->wInterrupt)) {
case INT_LOADSWAPPAGE:
- sc->pageno = intr->bSwapPageNo;
- sc->ovl = intr->bOvl >> 4 | intr->bOvl << 4;
- schedule_work(&sc->task);
+ sc->schedule_load_page(sc, intr);
break;
case INT_INCOMINGCMV:
- uea_dispatch_cmv(sc, &intr->u.s2.cmv);
+ sc->dispatch_cmv(sc, intr);
break;
default:
- uea_err(INS_TO_USBDEV(sc), "unknown intr %u\n",
+ uea_err(INS_TO_USBDEV(sc), "unknown interrupt %u\n",
le16_to_cpu(intr->wInterrupt));
}
@@ -1313,35 +2169,48 @@ resubmit:
*/
static int uea_boot(struct uea_softc *sc)
{
- int ret;
+ int ret, size;
struct intr_pkt *intr;
uea_enters(INS_TO_USBDEV(sc));
- INIT_WORK(&sc->task, uea_load_page, sc);
+ if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+ size = E4_INTR_PKT_SIZE;
+ sc->dispatch_cmv = uea_dispatch_cmv_e4;
+ sc->schedule_load_page = uea_schedule_load_page_e4;
+ sc->stat = uea_stat_e4;
+ sc->send_cmvs = uea_send_cmvs_e4;
+ INIT_WORK(&sc->task, uea_load_page_e4);
+ } else {
+ size = E1_INTR_PKT_SIZE;
+ sc->dispatch_cmv = uea_dispatch_cmv_e1;
+ sc->schedule_load_page = uea_schedule_load_page_e1;
+ sc->stat = uea_stat_e1;
+ sc->send_cmvs = uea_send_cmvs_e1;
+ INIT_WORK(&sc->task, uea_load_page_e1);
+ }
+
init_waitqueue_head(&sc->sync_q);
- init_waitqueue_head(&sc->cmv_ack_wait);
if (UEA_CHIP_VERSION(sc) == ADI930)
load_XILINX_firmware(sc);
- intr = kmalloc(INTR_PKT_SIZE, GFP_KERNEL);
+ intr = kmalloc(size, GFP_KERNEL);
if (!intr) {
uea_err(INS_TO_USBDEV(sc),
"cannot allocate interrupt package\n");
- uea_leaves(INS_TO_USBDEV(sc));
- return -ENOMEM;
+ goto err0;
}
sc->urb_int = usb_alloc_urb(0, GFP_KERNEL);
if (!sc->urb_int) {
uea_err(INS_TO_USBDEV(sc), "cannot allocate interrupt URB\n");
- goto err;
+ goto err1;
}
usb_fill_int_urb(sc->urb_int, sc->usb_dev,
usb_rcvintpipe(sc->usb_dev, UEA_INTR_PIPE),
- intr, INTR_PKT_SIZE, uea_intr, sc,
+ intr, size, uea_intr, sc,
sc->usb_dev->actconfig->interface[0]->altsetting[0].
endpoint[0].desc.bInterval);
@@ -1352,8 +2221,11 @@ static int uea_boot(struct uea_softc *sc)
goto err1;
}
- sc->kthread = kthread_run(uea_kthread, sc, "ueagle-atm");
- if (sc->kthread == ERR_PTR(-ENOMEM)) {
+ /* Create worker thread, but don't start it here. Start it after
+ * all usbatm generic initialization is done.
+ */
+ sc->kthread = kthread_create(uea_kthread, sc, "ueagle-atm");
+ if (IS_ERR(sc->kthread)) {
uea_err(INS_TO_USBDEV(sc), "failed to create thread\n");
goto err2;
}
@@ -1364,9 +2236,10 @@ static int uea_boot(struct uea_softc *sc)
err2:
usb_kill_urb(sc->urb_int);
err1:
- kfree(intr);
-err:
usb_free_urb(sc->urb_int);
+ sc->urb_int = NULL;
+ kfree(intr);
+err0:
uea_leaves(INS_TO_USBDEV(sc));
return -ENOMEM;
}
@@ -1379,10 +2252,7 @@ static void uea_stop(struct uea_softc *sc)
int ret;
uea_enters(INS_TO_USBDEV(sc));
ret = kthread_stop(sc->kthread);
- uea_info(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret);
-
- /* stop any pending boot process */
- flush_scheduled_work();
+ uea_dbg(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret);
uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL);
@@ -1390,8 +2260,10 @@ static void uea_stop(struct uea_softc *sc)
kfree(sc->urb_int->transfer_buffer);
usb_free_urb(sc->urb_int);
- if (sc->dsp_firm)
- release_firmware(sc->dsp_firm);
+ /* flush the work item, when no one can schedule it */
+ flush_work(&sc->task);
+
+ release_firmware(sc->dsp_firm);
uea_leaves(INS_TO_USBDEV(sc));
}
@@ -1418,13 +2290,13 @@ static ssize_t read_status(struct device *dev, struct device_attribute *attr,
int ret = -ENODEV;
struct uea_softc *sc;
- down(&uea_semaphore);
+ mutex_lock(&uea_mutex);
sc = dev_to_uea(dev);
if (!sc)
goto out;
ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.state);
out:
- up(&uea_semaphore);
+ mutex_unlock(&uea_mutex);
return ret;
}
@@ -1434,31 +2306,59 @@ static ssize_t reboot(struct device *dev, struct device_attribute *attr,
int ret = -ENODEV;
struct uea_softc *sc;
- down(&uea_semaphore);
+ mutex_lock(&uea_mutex);
sc = dev_to_uea(dev);
if (!sc)
goto out;
sc->reset = 1;
ret = count;
out:
- up(&uea_semaphore);
+ mutex_unlock(&uea_mutex);
return ret;
}
-static DEVICE_ATTR(stat_status, S_IWUGO | S_IRUGO, read_status, reboot);
+static DEVICE_ATTR(stat_status, S_IWUSR | S_IRUGO, read_status, reboot);
-static ssize_t read_human_status(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t read_human_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
int ret = -ENODEV;
+ int modem_state;
struct uea_softc *sc;
- down(&uea_semaphore);
+ mutex_lock(&uea_mutex);
sc = dev_to_uea(dev);
if (!sc)
goto out;
- switch (GET_STATUS(sc->stats.phy.state)) {
+ if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+ switch (sc->stats.phy.state) {
+ case 0x0: /* not yet synchronized */
+ case 0x1:
+ case 0x3:
+ case 0x4:
+ modem_state = 0;
+ break;
+ case 0x5: /* initialization */
+ case 0x6:
+ case 0x9:
+ case 0xa:
+ modem_state = 1;
+ break;
+ case 0x7: /* operational */
+ modem_state = 2;
+ break;
+ case 0x2: /* fail ... */
+ modem_state = 3;
+ break;
+ default: /* unknown */
+ modem_state = 4;
+ break;
+ }
+ } else
+ modem_state = GET_STATUS(sc->stats.phy.state);
+
+ switch (modem_state) {
case 0:
ret = sprintf(buf, "Modem is booting\n");
break;
@@ -1468,60 +2368,71 @@ static ssize_t read_human_status(struct device *dev, struct device_attribute *at
case 2:
ret = sprintf(buf, "Modem is operational\n");
break;
- default:
+ case 3:
ret = sprintf(buf, "Modem synchronization failed\n");
break;
+ default:
+ ret = sprintf(buf, "Modem state is unknown\n");
+ break;
}
out:
- up(&uea_semaphore);
+ mutex_unlock(&uea_mutex);
return ret;
}
-static DEVICE_ATTR(stat_human_status, S_IWUGO | S_IRUGO, read_human_status, NULL);
+static DEVICE_ATTR(stat_human_status, S_IRUGO, read_human_status, NULL);
static ssize_t read_delin(struct device *dev, struct device_attribute *attr,
char *buf)
{
int ret = -ENODEV;
struct uea_softc *sc;
+ char *delin = "GOOD";
- down(&uea_semaphore);
+ mutex_lock(&uea_mutex);
sc = dev_to_uea(dev);
if (!sc)
goto out;
- if (sc->stats.phy.flags & 0x0C00)
- ret = sprintf(buf, "ERROR\n");
- else if (sc->stats.phy.flags & 0x0030)
- ret = sprintf(buf, "LOSS\n");
- else
- ret = sprintf(buf, "GOOD\n");
+ if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+ if (sc->stats.phy.flags & 0x4000)
+ delin = "RESET";
+ else if (sc->stats.phy.flags & 0x0001)
+ delin = "LOSS";
+ } else {
+ if (sc->stats.phy.flags & 0x0C00)
+ delin = "ERROR";
+ else if (sc->stats.phy.flags & 0x0030)
+ delin = "LOSS";
+ }
+
+ ret = sprintf(buf, "%s\n", delin);
out:
- up(&uea_semaphore);
+ mutex_unlock(&uea_mutex);
return ret;
}
-static DEVICE_ATTR(stat_delin, S_IWUGO | S_IRUGO, read_delin, NULL);
+static DEVICE_ATTR(stat_delin, S_IRUGO, read_delin, NULL);
-#define UEA_ATTR(name, reset) \
+#define UEA_ATTR(name, reset) \
\
-static ssize_t read_##name(struct device *dev, \
+static ssize_t read_##name(struct device *dev, \
struct device_attribute *attr, char *buf) \
-{ \
- int ret = -ENODEV; \
- struct uea_softc *sc; \
- \
- down(&uea_semaphore); \
+{ \
+ int ret = -ENODEV; \
+ struct uea_softc *sc; \
+ \
+ mutex_lock(&uea_mutex); \
sc = dev_to_uea(dev); \
- if (!sc) \
- goto out; \
+ if (!sc) \
+ goto out; \
ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.name); \
if (reset) \
sc->stats.phy.name = 0; \
-out: \
- up(&uea_semaphore); \
- return ret; \
-} \
+out: \
+ mutex_unlock(&uea_mutex); \
+ return ret; \
+} \
\
static DEVICE_ATTR(stat_##name, S_IRUGO, read_##name, NULL)
@@ -1539,10 +2450,10 @@ UEA_ATTR(uscorr, 0);
UEA_ATTR(dscorr, 0);
UEA_ATTR(usunc, 0);
UEA_ATTR(dsunc, 0);
+UEA_ATTR(firmid, 0);
/* Retrieve the device End System Identifier (MAC) */
-#define htoi(x) (isdigit(x) ? x-'0' : toupper(x)-'A'+10)
static int uea_getesi(struct uea_softc *sc, u_char * esi)
{
unsigned char mac_str[2 * ETH_ALEN + 1];
@@ -1553,7 +2464,8 @@ static int uea_getesi(struct uea_softc *sc, u_char * esi)
return 1;
for (i = 0; i < ETH_ALEN; i++)
- esi[i] = htoi(mac_str[2 * i]) * 16 + htoi(mac_str[2 * i + 1]);
+ esi[i] = hex_to_bin(mac_str[2 * i]) * 16 +
+ hex_to_bin(mac_str[2 * i + 1]);
return 0;
}
@@ -1570,7 +2482,7 @@ static int uea_heavy(struct usbatm_data *usbatm, struct usb_interface *intf)
{
struct uea_softc *sc = usbatm->driver_data;
- wait_event(sc->sync_q, IS_OPERATIONAL(sc));
+ wait_event_interruptible(sc->sync_q, IS_OPERATIONAL(sc));
return 0;
@@ -1594,34 +2506,38 @@ static int claim_interface(struct usb_device *usb_dev,
return ret;
}
-static void create_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
-{
- /* sysfs interface */
- device_create_file(&intf->dev, &dev_attr_stat_status);
- device_create_file(&intf->dev, &dev_attr_stat_mflags);
- device_create_file(&intf->dev, &dev_attr_stat_human_status);
- device_create_file(&intf->dev, &dev_attr_stat_delin);
- device_create_file(&intf->dev, &dev_attr_stat_vidcpe);
- device_create_file(&intf->dev, &dev_attr_stat_usrate);
- device_create_file(&intf->dev, &dev_attr_stat_dsrate);
- device_create_file(&intf->dev, &dev_attr_stat_usattenuation);
- device_create_file(&intf->dev, &dev_attr_stat_dsattenuation);
- device_create_file(&intf->dev, &dev_attr_stat_usmargin);
- device_create_file(&intf->dev, &dev_attr_stat_dsmargin);
- device_create_file(&intf->dev, &dev_attr_stat_txflow);
- device_create_file(&intf->dev, &dev_attr_stat_rxflow);
- device_create_file(&intf->dev, &dev_attr_stat_uscorr);
- device_create_file(&intf->dev, &dev_attr_stat_dscorr);
- device_create_file(&intf->dev, &dev_attr_stat_usunc);
- device_create_file(&intf->dev, &dev_attr_stat_dsunc);
-}
+static struct attribute *attrs[] = {
+ &dev_attr_stat_status.attr,
+ &dev_attr_stat_mflags.attr,
+ &dev_attr_stat_human_status.attr,
+ &dev_attr_stat_delin.attr,
+ &dev_attr_stat_vidcpe.attr,
+ &dev_attr_stat_usrate.attr,
+ &dev_attr_stat_dsrate.attr,
+ &dev_attr_stat_usattenuation.attr,
+ &dev_attr_stat_dsattenuation.attr,
+ &dev_attr_stat_usmargin.attr,
+ &dev_attr_stat_dsmargin.attr,
+ &dev_attr_stat_txflow.attr,
+ &dev_attr_stat_rxflow.attr,
+ &dev_attr_stat_uscorr.attr,
+ &dev_attr_stat_dscorr.attr,
+ &dev_attr_stat_usunc.attr,
+ &dev_attr_stat_dsunc.attr,
+ &dev_attr_stat_firmid.attr,
+ NULL,
+};
+static struct attribute_group attr_grp = {
+ .attrs = attrs,
+};
static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
- const struct usb_device_id *id, int *heavy)
+ const struct usb_device_id *id)
{
struct usb_device *usb = interface_to_usbdev(intf);
struct uea_softc *sc;
int ret, ifnum = intf->altsetting->desc.bInterfaceNumber;
+ unsigned int alt;
uea_enters(usb);
@@ -1629,16 +2545,14 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
if (ifnum != UEA_INTR_IFACE_NO)
return -ENODEV;
- *heavy = sync_wait[modem_index];
+ usbatm->flags = (sync_wait[modem_index] ? 0 : UDSL_SKIP_HEAVY_INIT);
/* interface 1 is for outbound traffic */
ret = claim_interface(usb, usbatm, UEA_US_IFACE_NO);
if (ret < 0)
return ret;
- /* ADI930 has only 2 interfaces and inbound traffic
- * is on interface 1
- */
+ /* ADI930 has only 2 interfaces and inbound traffic is on interface 1 */
if (UEA_CHIP_VERSION(id) != ADI930) {
/* interface 2 is for inbound traffic */
ret = claim_interface(usb, usbatm, UEA_DS_IFACE_NO);
@@ -1648,7 +2562,7 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
sc = kzalloc(sizeof(struct uea_softc), GFP_KERNEL);
if (!sc) {
- uea_err(INS_TO_USBDEV(sc), "uea_init: not enough memory !\n");
+ uea_err(usb, "uea_init: not enough memory !\n");
return -ENOMEM;
}
@@ -1658,74 +2572,101 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
sc->modem_index = (modem_index < NB_MODEM) ? modem_index++ : 0;
sc->driver_info = id->driver_info;
- ret = uea_boot(sc);
- if (ret < 0) {
- kfree(sc);
- return ret;
+ /* first try to use module parameter */
+ if (annex[sc->modem_index] == 1)
+ sc->annex = ANNEXA;
+ else if (annex[sc->modem_index] == 2)
+ sc->annex = ANNEXB;
+ /* try to autodetect annex */
+ else if (sc->driver_info & AUTO_ANNEX_A)
+ sc->annex = ANNEXA;
+ else if (sc->driver_info & AUTO_ANNEX_B)
+ sc->annex = ANNEXB;
+ else
+ sc->annex = (le16_to_cpu
+ (sc->usb_dev->descriptor.bcdDevice) & 0x80) ? ANNEXB : ANNEXA;
+
+ alt = altsetting[sc->modem_index];
+ /* ADI930 don't support iso */
+ if (UEA_CHIP_VERSION(id) != ADI930 && alt > 0) {
+ if (alt <= 8 &&
+ usb_set_interface(usb, UEA_DS_IFACE_NO, alt) == 0) {
+ uea_dbg(usb, "set alternate %u for 2 interface\n", alt);
+ uea_info(usb, "using iso mode\n");
+ usbatm->flags |= UDSL_USE_ISOC | UDSL_IGNORE_EILSEQ;
+ } else {
+ uea_err(usb, "setting alternate %u failed for "
+ "2 interface, using bulk mode\n", alt);
+ }
}
- create_fs_entries(sc, intf);
+ ret = sysfs_create_group(&intf->dev.kobj, &attr_grp);
+ if (ret < 0)
+ goto error;
+
+ ret = uea_boot(sc);
+ if (ret < 0)
+ goto error_rm_grp;
+
return 0;
-}
-static void destroy_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
-{
- /* sysfs interface */
- device_remove_file(&intf->dev, &dev_attr_stat_status);
- device_remove_file(&intf->dev, &dev_attr_stat_mflags);
- device_remove_file(&intf->dev, &dev_attr_stat_human_status);
- device_remove_file(&intf->dev, &dev_attr_stat_delin);
- device_remove_file(&intf->dev, &dev_attr_stat_vidcpe);
- device_remove_file(&intf->dev, &dev_attr_stat_usrate);
- device_remove_file(&intf->dev, &dev_attr_stat_dsrate);
- device_remove_file(&intf->dev, &dev_attr_stat_usattenuation);
- device_remove_file(&intf->dev, &dev_attr_stat_dsattenuation);
- device_remove_file(&intf->dev, &dev_attr_stat_usmargin);
- device_remove_file(&intf->dev, &dev_attr_stat_dsmargin);
- device_remove_file(&intf->dev, &dev_attr_stat_txflow);
- device_remove_file(&intf->dev, &dev_attr_stat_rxflow);
- device_remove_file(&intf->dev, &dev_attr_stat_uscorr);
- device_remove_file(&intf->dev, &dev_attr_stat_dscorr);
- device_remove_file(&intf->dev, &dev_attr_stat_usunc);
- device_remove_file(&intf->dev, &dev_attr_stat_dsunc);
+error_rm_grp:
+ sysfs_remove_group(&intf->dev.kobj, &attr_grp);
+error:
+ kfree(sc);
+ return ret;
}
static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
{
struct uea_softc *sc = usbatm->driver_data;
- destroy_fs_entries(sc, intf);
+ sysfs_remove_group(&intf->dev.kobj, &attr_grp);
uea_stop(sc);
kfree(sc);
}
static struct usbatm_driver uea_usbatm_driver = {
.driver_name = "ueagle-atm",
- .owner = THIS_MODULE,
.bind = uea_bind,
.atm_start = uea_atm_open,
.unbind = uea_unbind,
.heavy_init = uea_heavy,
- .in = UEA_BULK_DATA_PIPE,
- .out = UEA_BULK_DATA_PIPE,
+ .bulk_in = UEA_BULK_DATA_PIPE,
+ .bulk_out = UEA_BULK_DATA_PIPE,
+ .isoc_in = UEA_ISO_DATA_PIPE,
};
static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *usb = interface_to_usbdev(intf);
+ int ret;
uea_enters(usb);
- uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) : %s\n",
- le16_to_cpu(usb->descriptor.idVendor),
- le16_to_cpu(usb->descriptor.idProduct),
- chip_name[UEA_CHIP_VERSION(id)]);
+ uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) Rev (%#X): %s\n",
+ le16_to_cpu(usb->descriptor.idVendor),
+ le16_to_cpu(usb->descriptor.idProduct),
+ le16_to_cpu(usb->descriptor.bcdDevice),
+ chip_name[UEA_CHIP_VERSION(id)]);
usb_reset_device(usb);
if (UEA_IS_PREFIRM(id))
return uea_load_firmware(usb, UEA_CHIP_VERSION(id));
- return usbatm_usb_probe(intf, id, &uea_usbatm_driver);
+ ret = usbatm_usb_probe(intf, id, &uea_usbatm_driver);
+ if (ret == 0) {
+ struct usbatm_data *usbatm = usb_get_intfdata(intf);
+ struct uea_softc *sc = usbatm->driver_data;
+
+ /* Ensure carrier is initialized to off as early as possible */
+ UPDATE_ATM_SIGNAL(ATM_PHY_SIG_LOST);
+
+ /* Only start the worker thread when all init is done */
+ wake_up_process(sc->kthread);
+ }
+
+ return ret;
}
static void uea_disconnect(struct usb_interface *intf)
@@ -1738,9 +2679,9 @@ static void uea_disconnect(struct usb_interface *intf)
* Pre-firmware device has one interface
*/
if (usb->config->desc.bNumInterfaces != 1 && ifnum == 0) {
- down(&uea_semaphore);
+ mutex_lock(&uea_mutex);
usbatm_usb_disconnect(intf);
- up(&uea_semaphore);
+ mutex_unlock(&uea_mutex);
uea_info(usb, "ADSL device removed\n");
}
@@ -1751,24 +2692,74 @@ static void uea_disconnect(struct usb_interface *intf)
* List of supported VID/PID
*/
static const struct usb_device_id uea_ids[] = {
- {USB_DEVICE(ELSA_VID, ELSA_PID_PREFIRM), .driver_info = ADI930 | PREFIRM},
- {USB_DEVICE(ELSA_VID, ELSA_PID_PSTFIRM), .driver_info = ADI930 | PSTFIRM},
- {USB_DEVICE(EAGLE_VID, EAGLE_I_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM},
- {USB_DEVICE(EAGLE_VID, EAGLE_I_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM},
- {USB_DEVICE(EAGLE_VID, EAGLE_II_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
- {USB_DEVICE(EAGLE_VID, EAGLE_II_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM},
- {USB_DEVICE(EAGLE_VID, EAGLE_IIC_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
- {USB_DEVICE(EAGLE_VID, EAGLE_IIC_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM},
- {USB_DEVICE(EAGLE_VID, EAGLE_III_PID_PREFIRM), .driver_info = EAGLE_III | PREFIRM},
- {USB_DEVICE(EAGLE_VID, EAGLE_III_PID_PSTFIRM), .driver_info = EAGLE_III | PSTFIRM},
- {USB_DEVICE(USR_VID, MILLER_A_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM},
- {USB_DEVICE(USR_VID, MILLER_A_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM},
- {USB_DEVICE(USR_VID, MILLER_B_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM},
- {USB_DEVICE(USR_VID, MILLER_B_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM},
- {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM},
- {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM},
- {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM},
- {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM},
+ {USB_DEVICE(ANALOG_VID, ADI930_PID_PREFIRM),
+ .driver_info = ADI930 | PREFIRM},
+ {USB_DEVICE(ANALOG_VID, ADI930_PID_PSTFIRM),
+ .driver_info = ADI930 | PSTFIRM},
+ {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PREFIRM),
+ .driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PSTFIRM),
+ .driver_info = EAGLE_I | PSTFIRM},
+ {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PREFIRM),
+ .driver_info = EAGLE_II | PREFIRM},
+ {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PSTFIRM),
+ .driver_info = EAGLE_II | PSTFIRM},
+ {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PREFIRM),
+ .driver_info = EAGLE_II | PREFIRM},
+ {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PSTFIRM),
+ .driver_info = EAGLE_II | PSTFIRM},
+ {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PREFIRM),
+ .driver_info = EAGLE_III | PREFIRM},
+ {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PSTFIRM),
+ .driver_info = EAGLE_III | PSTFIRM},
+ {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PREFIRM),
+ .driver_info = EAGLE_IV | PREFIRM},
+ {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PSTFIRM),
+ .driver_info = EAGLE_IV | PSTFIRM},
+ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PREFIRM),
+ .driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PSTFIRM),
+ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
+ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PREFIRM),
+ .driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PSTFIRM),
+ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
+ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PREFIRM),
+ .driver_info = EAGLE_II | PREFIRM},
+ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PSTFIRM),
+ .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_A},
+ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PREFIRM),
+ .driver_info = EAGLE_II | PREFIRM},
+ {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PSTFIRM),
+ .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_B},
+ {USB_DEVICE(ELSA_VID, ELSA_PID_PREFIRM),
+ .driver_info = ADI930 | PREFIRM},
+ {USB_DEVICE(ELSA_VID, ELSA_PID_PSTFIRM),
+ .driver_info = ADI930 | PSTFIRM},
+ {USB_DEVICE(ELSA_VID, ELSA_PID_A_PREFIRM),
+ .driver_info = ADI930 | PREFIRM},
+ {USB_DEVICE(ELSA_VID, ELSA_PID_A_PSTFIRM),
+ .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_A},
+ {USB_DEVICE(ELSA_VID, ELSA_PID_B_PREFIRM),
+ .driver_info = ADI930 | PREFIRM},
+ {USB_DEVICE(ELSA_VID, ELSA_PID_B_PSTFIRM),
+ .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_B},
+ {USB_DEVICE(USR_VID, MILLER_A_PID_PREFIRM),
+ .driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(USR_VID, MILLER_A_PID_PSTFIRM),
+ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
+ {USB_DEVICE(USR_VID, MILLER_B_PID_PREFIRM),
+ .driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(USR_VID, MILLER_B_PID_PSTFIRM),
+ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
+ {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PREFIRM),
+ .driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PSTFIRM),
+ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
+ {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PREFIRM),
+ .driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PSTFIRM),
+ .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
{}
};
@@ -1784,37 +2775,33 @@ static struct usb_driver uea_driver = {
MODULE_DEVICE_TABLE(usb, uea_ids);
-/**
- * uea_init - Initialize the module.
- * Register to USB subsystem
- */
-static int __init uea_init(void)
-{
- printk(KERN_INFO "[ueagle-atm] driver " EAGLEUSBVERSION " loaded\n");
-
- usb_register(&uea_driver);
-
- return 0;
-}
-
-module_init(uea_init);
-
-/**
- * uea_exit - Destroy module
- * Deregister with USB subsystem
- */
-static void __exit uea_exit(void)
-{
- /*
- * This calls automatically the uea_disconnect method if necessary:
- */
- usb_deregister(&uea_driver);
-
- printk(KERN_INFO "[ueagle-atm] driver unloaded\n");
-}
-
-module_exit(uea_exit);
+module_usb_driver(uea_driver);
MODULE_AUTHOR("Damien Bergamini/Matthieu Castet/Stanislaw W. Gruszka");
MODULE_DESCRIPTION("ADI 930/Eagle USB ADSL Modem driver");
MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(EAGLE_FIRMWARE);
+MODULE_FIRMWARE(ADI930_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_I_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_II_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_III_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_IV_FIRMWARE);
+MODULE_FIRMWARE(DSP4I_FIRMWARE);
+MODULE_FIRMWARE(DSP4P_FIRMWARE);
+MODULE_FIRMWARE(DSP9I_FIRMWARE);
+MODULE_FIRMWARE(DSP9P_FIRMWARE);
+MODULE_FIRMWARE(DSPEI_FIRMWARE);
+MODULE_FIRMWARE(DSPEP_FIRMWARE);
+MODULE_FIRMWARE(FPGA930_FIRMWARE);
+MODULE_FIRMWARE(CMV4P_FIRMWARE);
+MODULE_FIRMWARE(CMV4PV2_FIRMWARE);
+MODULE_FIRMWARE(CMV4I_FIRMWARE);
+MODULE_FIRMWARE(CMV4IV2_FIRMWARE);
+MODULE_FIRMWARE(CMV9P_FIRMWARE);
+MODULE_FIRMWARE(CMV9PV2_FIRMWARE);
+MODULE_FIRMWARE(CMV9I_FIRMWARE);
+MODULE_FIRMWARE(CMV9IV2_FIRMWARE);
+MODULE_FIRMWARE(CMVEP_FIRMWARE);
+MODULE_FIRMWARE(CMVEPV2_FIRMWARE);
+MODULE_FIRMWARE(CMVEI_FIRMWARE);
+MODULE_FIRMWARE(CMVEIV2_FIRMWARE);
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index 7af1883d4bf..dada0146cd7 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -72,38 +72,39 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/stat.h>
#include <linux/timer.h>
#include <linux/wait.h>
+#include <linux/kthread.h>
+#include <linux/ratelimit.h>
#ifdef VERBOSE_DEBUG
-static int usbatm_print_packet(const unsigned char *data, int len);
-#define PACKETDEBUG(arg...) usbatm_print_packet (arg)
-#define vdbg(arg...) dbg (arg)
+static int usbatm_print_packet(struct usbatm_data *instance, const unsigned char *data, int len);
+#define PACKETDEBUG(arg...) usbatm_print_packet(arg)
+#define vdbg(arg...) dev_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.9"
+#define DRIVER_VERSION "1.10"
#define DRIVER_DESC "Generic USB ATM/DSL I/O, version " DRIVER_VERSION
static const char usbatm_driver_name[] = "usbatm";
#define UDSL_MAX_RCV_URBS 16
#define UDSL_MAX_SND_URBS 16
-#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */
-#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */
+#define UDSL_MAX_BUF_SIZE 65536
#define UDSL_DEFAULT_RCV_URBS 4
#define UDSL_DEFAULT_SND_URBS 4
-#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */
-#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */
+#define UDSL_DEFAULT_RCV_BUF_SIZE 3392 /* 64 * ATM_CELL_SIZE */
+#define UDSL_DEFAULT_SND_BUF_SIZE 3392 /* 64 * ATM_CELL_SIZE */
#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
@@ -111,8 +112,8 @@ static const char usbatm_driver_name[] = "usbatm";
static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
-static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE;
-static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE;
+static unsigned int rcv_buf_bytes = UDSL_DEFAULT_RCV_BUF_SIZE;
+static unsigned int snd_buf_bytes = UDSL_DEFAULT_SND_BUF_SIZE;
module_param(num_rcv_urbs, uint, S_IRUGO);
MODULE_PARM_DESC(num_rcv_urbs,
@@ -126,16 +127,16 @@ MODULE_PARM_DESC(num_snd_urbs,
__MODULE_STRING(UDSL_MAX_SND_URBS) ", default: "
__MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")");
-module_param(rcv_buf_size, uint, S_IRUGO);
-MODULE_PARM_DESC(rcv_buf_size,
- "Size of the buffers used for reception in ATM cells (range: 1-"
- __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: "
+module_param(rcv_buf_bytes, uint, S_IRUGO);
+MODULE_PARM_DESC(rcv_buf_bytes,
+ "Size of the buffers used for reception, in bytes (range: 1-"
+ __MODULE_STRING(UDSL_MAX_BUF_SIZE) ", default: "
__MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")");
-module_param(snd_buf_size, uint, S_IRUGO);
-MODULE_PARM_DESC(snd_buf_size,
- "Size of the buffers used for transmission in ATM cells (range: 1-"
- __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: "
+module_param(snd_buf_bytes, uint, S_IRUGO);
+MODULE_PARM_DESC(snd_buf_bytes,
+ "Size of the buffers used for transmission, in bytes (range: 1-"
+ __MODULE_STRING(UDSL_MAX_BUF_SIZE) ", default: "
__MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")");
@@ -166,12 +167,12 @@ struct usbatm_control {
/* ATM */
-static void usbatm_atm_dev_close(struct atm_dev *dev);
+static void usbatm_atm_dev_close(struct atm_dev *atm_dev);
static int usbatm_atm_open(struct atm_vcc *vcc);
static void usbatm_atm_close(struct atm_vcc *vcc);
-static int usbatm_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg);
+static int usbatm_atm_ioctl(struct atm_dev *atm_dev, unsigned int cmd, void __user *arg);
static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb);
-static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page);
+static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t *pos, char *page);
static struct atmdev_ops usbatm_atm_devops = {
.dev_close = usbatm_atm_dev_close,
@@ -199,7 +200,7 @@ static inline void usbatm_pop(struct atm_vcc *vcc, struct sk_buff *skb)
if (vcc->pop)
vcc->pop(vcc, skb);
else
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
}
@@ -229,13 +230,14 @@ static int usbatm_submit_urb(struct urb *urb)
struct usbatm_channel *channel = urb->context;
int ret;
- vdbg("%s: submitting urb 0x%p, size %u",
- __func__, urb, urb->transfer_buffer_length);
+ /* vdbg("%s: submitting urb 0x%p, size %u",
+ __func__, urb, urb->transfer_buffer_length); */
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret) {
- atm_dbg(channel->usbatm, "%s: urb 0x%p submission failed (%d)!\n",
- __func__, urb, ret);
+ if (printk_ratelimit())
+ atm_warn(channel->usbatm, "%s: urb 0x%p submission failed (%d)!\n",
+ __func__, urb, ret);
/* consider all errors transient and return the buffer back to the queue */
urb->status = -EAGAIN;
@@ -253,13 +255,14 @@ static int usbatm_submit_urb(struct urb *urb)
return ret;
}
-static void usbatm_complete(struct urb *urb, struct pt_regs *regs)
+static void usbatm_complete(struct urb *urb)
{
struct usbatm_channel *channel = urb->context;
unsigned long flags;
+ int status = urb->status;
- vdbg("%s: urb 0x%p, status %d, actual_length %d",
- __func__, urb, urb->status, urb->actual_length);
+ /* vdbg("%s: urb 0x%p, status %d, actual_length %d",
+ __func__, urb, status, urb->actual_length); */
/* usually in_interrupt(), but not always */
spin_lock_irqsave(&channel->lock, flags);
@@ -269,10 +272,18 @@ static void usbatm_complete(struct urb *urb, struct pt_regs *regs)
spin_unlock_irqrestore(&channel->lock, flags);
- if (unlikely(urb->status))
+ if (unlikely(status) &&
+ (!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) ||
+ status != -EILSEQ)) {
+ if (status == -ESHUTDOWN)
+ return;
+
+ if (printk_ratelimit())
+ atm_warn(channel->usbatm, "%s: urb 0x%p failed (%d)!\n",
+ __func__, urb, status);
/* throttle processing in case of an error */
mod_timer(&channel->delay, jiffies + msecs_to_jiffies(THROTTLE_MSECS));
- else
+ } else
tasklet_schedule(&channel->tasklet);
}
@@ -284,129 +295,169 @@ static void usbatm_complete(struct urb *urb, struct pt_regs *regs)
static inline struct usbatm_vcc_data *usbatm_find_vcc(struct usbatm_data *instance,
short vpi, int vci)
{
- struct usbatm_vcc_data *vcc;
+ struct usbatm_vcc_data *vcc_data;
- list_for_each_entry(vcc, &instance->vcc_list, list)
- if ((vcc->vci == vci) && (vcc->vpi == vpi))
- return vcc;
+ list_for_each_entry(vcc_data, &instance->vcc_list, list)
+ if ((vcc_data->vci == vci) && (vcc_data->vpi == vpi))
+ return vcc_data;
return NULL;
}
-static void usbatm_extract_cells(struct usbatm_data *instance,
- unsigned char *source, unsigned int avail_data)
+static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char *source)
{
- struct usbatm_vcc_data *cached_vcc = NULL;
struct atm_vcc *vcc;
struct sk_buff *sarb;
- unsigned int stride = instance->rx_channel.stride;
- int vci, cached_vci = 0;
- short vpi, cached_vpi = 0;
- u8 pti;
+ short vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4);
+ int vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
+ u8 pti = ((source[3] & 0xe) >> 1);
- for (; avail_data >= stride; avail_data -= stride, source += stride) {
- vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4);
- vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
- pti = ((source[3] & 0xe) >> 1);
+ if ((vci != instance->cached_vci) || (vpi != instance->cached_vpi)) {
+ instance->cached_vpi = vpi;
+ instance->cached_vci = vci;
- vdbg("%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti);
+ instance->cached_vcc = usbatm_find_vcc(instance, vpi, vci);
- if ((vci != cached_vci) || (vpi != cached_vpi)) {
- cached_vpi = vpi;
- cached_vci = vci;
-
- cached_vcc = usbatm_find_vcc(instance, vpi, vci);
+ if (!instance->cached_vcc)
+ atm_rldbg(instance, "%s: unknown vpi/vci (%hd/%d)!\n", __func__, vpi, vci);
+ }
- if (!cached_vcc)
- atm_dbg(instance, "%s: unknown vpi/vci (%hd/%d)!\n", __func__, vpi, vci);
- }
+ if (!instance->cached_vcc)
+ return;
- if (!cached_vcc)
- continue;
+ vcc = instance->cached_vcc->vcc;
- vcc = cached_vcc->vcc;
+ /* OAM F5 end-to-end */
+ if (pti == ATM_PTI_E2EF5) {
+ if (printk_ratelimit())
+ atm_warn(instance, "%s: OAM not supported (vpi %d, vci %d)!\n",
+ __func__, vpi, vci);
+ atomic_inc(&vcc->stats->rx_err);
+ return;
+ }
- /* OAM F5 end-to-end */
- if (pti == ATM_PTI_E2EF5) {
- atm_warn(instance, "%s: OAM not supported (vpi %d, vci %d)!\n", __func__, vpi, vci);
- atomic_inc(&vcc->stats->rx_err);
- continue;
- }
+ sarb = instance->cached_vcc->sarb;
- sarb = cached_vcc->sarb;
+ if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
+ atm_rldbg(instance, "%s: buffer overrun (sarb->len %u, vcc: 0x%p)!\n",
+ __func__, sarb->len, vcc);
+ /* discard cells already received */
+ skb_trim(sarb, 0);
+ }
- if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
- atm_dbg(instance, "%s: buffer overrun (sarb->len %u, vcc: 0x%p)!\n",
- __func__, sarb->len, vcc);
- /* discard cells already received */
- skb_trim(sarb, 0);
- UDSL_ASSERT(sarb->tail + ATM_CELL_PAYLOAD <= sarb->end);
- }
+ memcpy(skb_tail_pointer(sarb), source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
+ __skb_put(sarb, ATM_CELL_PAYLOAD);
- memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
- __skb_put(sarb, ATM_CELL_PAYLOAD);
+ if (pti & 1) {
+ struct sk_buff *skb;
+ unsigned int length;
+ unsigned int pdu_length;
- if (pti & 1) {
- struct sk_buff *skb;
- unsigned int length;
- unsigned int pdu_length;
+ length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5];
- length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5];
+ /* guard against overflow */
+ if (length > ATM_MAX_AAL5_PDU) {
+ atm_rldbg(instance, "%s: bogus length %u (vcc: 0x%p)!\n",
+ __func__, length, vcc);
+ atomic_inc(&vcc->stats->rx_err);
+ goto out;
+ }
- /* guard against overflow */
- if (length > ATM_MAX_AAL5_PDU) {
- atm_dbg(instance, "%s: bogus length %u (vcc: 0x%p)!\n",
- __func__, length, vcc);
- atomic_inc(&vcc->stats->rx_err);
- goto out;
- }
+ pdu_length = usbatm_pdu_length(length);
- pdu_length = usbatm_pdu_length(length);
+ if (sarb->len < pdu_length) {
+ atm_rldbg(instance, "%s: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!\n",
+ __func__, pdu_length, sarb->len, vcc);
+ atomic_inc(&vcc->stats->rx_err);
+ goto out;
+ }
- if (sarb->len < pdu_length) {
- atm_dbg(instance, "%s: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!\n",
- __func__, pdu_length, sarb->len, vcc);
- atomic_inc(&vcc->stats->rx_err);
- goto out;
- }
+ if (crc32_be(~0, skb_tail_pointer(sarb) - pdu_length, pdu_length) != 0xc704dd7b) {
+ atm_rldbg(instance, "%s: packet failed crc check (vcc: 0x%p)!\n",
+ __func__, vcc);
+ atomic_inc(&vcc->stats->rx_err);
+ goto out;
+ }
- if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
- atm_dbg(instance, "%s: packet failed crc check (vcc: 0x%p)!\n",
- __func__, vcc);
- atomic_inc(&vcc->stats->rx_err);
- goto out;
- }
+ vdbg(&instance->usb_intf->dev,
+ "%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)",
+ __func__, length, pdu_length, vcc);
- vdbg("%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", __func__, length, pdu_length, vcc);
+ if (!(skb = dev_alloc_skb(length))) {
+ if (printk_ratelimit())
+ atm_err(instance, "%s: no memory for skb (length: %u)!\n",
+ __func__, length);
+ atomic_inc(&vcc->stats->rx_drop);
+ goto out;
+ }
- if (!(skb = dev_alloc_skb(length))) {
- atm_dbg(instance, "%s: no memory for skb (length: %u)!\n", __func__, length);
- atomic_inc(&vcc->stats->rx_drop);
- goto out;
- }
+ vdbg(&instance->usb_intf->dev,
+ "%s: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)",
+ __func__, skb, skb->truesize);
- vdbg("%s: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", __func__, skb, skb->truesize);
+ if (!atm_charge(vcc, skb->truesize)) {
+ atm_rldbg(instance, "%s: failed atm_charge (skb->truesize: %u)!\n",
+ __func__, skb->truesize);
+ dev_kfree_skb_any(skb);
+ goto out; /* atm_charge increments rx_drop */
+ }
- if (!atm_charge(vcc, skb->truesize)) {
- atm_dbg(instance, "%s: failed atm_charge (skb->truesize: %u)!\n", __func__, skb->truesize);
- dev_kfree_skb(skb);
- goto out; /* atm_charge increments rx_drop */
- }
+ skb_copy_to_linear_data(skb,
+ skb_tail_pointer(sarb) - pdu_length,
+ length);
+ __skb_put(skb, length);
- memcpy(skb->data, sarb->tail - pdu_length, length);
- __skb_put(skb, length);
+ vdbg(&instance->usb_intf->dev,
+ "%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
+ __func__, skb, skb->len, skb->truesize);
- vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
- __func__, skb, skb->len, skb->truesize);
+ PACKETDEBUG(instance, skb->data, skb->len);
- PACKETDEBUG(skb->data, skb->len);
+ vcc->push(vcc, skb);
- vcc->push(vcc, skb);
+ atomic_inc(&vcc->stats->rx);
+ out:
+ skb_trim(sarb, 0);
+ }
+}
- atomic_inc(&vcc->stats->rx);
- out:
- skb_trim(sarb, 0);
+static void usbatm_extract_cells(struct usbatm_data *instance,
+ unsigned char *source, unsigned int avail_data)
+{
+ unsigned int stride = instance->rx_channel.stride;
+ unsigned int buf_usage = instance->buf_usage;
+
+ /* extract cells from incoming data, taking into account that
+ * the length of avail data may not be a multiple of stride */
+
+ if (buf_usage > 0) {
+ /* we have a partially received atm cell */
+ unsigned char *cell_buf = instance->cell_buf;
+ unsigned int space_left = stride - buf_usage;
+
+ if (avail_data >= space_left) {
+ /* add new data and process cell */
+ memcpy(cell_buf + buf_usage, source, space_left);
+ source += space_left;
+ avail_data -= space_left;
+ usbatm_extract_one_cell(instance, cell_buf);
+ instance->buf_usage = 0;
+ } else {
+ /* not enough data to fill the cell */
+ memcpy(cell_buf + buf_usage, source, avail_data);
+ instance->buf_usage = buf_usage + avail_data;
+ return;
}
}
+
+ for (; avail_data >= stride; avail_data -= stride, source += stride)
+ usbatm_extract_one_cell(instance, source);
+
+ if (avail_data > 0) {
+ /* length was not a multiple of stride -
+ * save remaining data for next call */
+ memcpy(instance->cell_buf, source, avail_data);
+ instance->buf_usage = avail_data;
+ }
}
@@ -420,14 +471,11 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
{
struct usbatm_control *ctrl = UDSL_SKB(skb);
struct atm_vcc *vcc = ctrl->atm.vcc;
- unsigned int num_written;
+ unsigned int bytes_written;
unsigned int stride = instance->tx_channel.stride;
- vdbg("%s: skb->len=%d, avail_space=%u", __func__, skb->len, avail_space);
- UDSL_ASSERT(!(avail_space % stride));
-
- for (num_written = 0; num_written < avail_space && ctrl->len;
- num_written += stride, target += stride) {
+ for (bytes_written = 0; bytes_written < avail_space && ctrl->len;
+ bytes_written += stride, target += stride) {
unsigned int data_len = min_t(unsigned int, skb->len, ATM_CELL_PAYLOAD);
unsigned int left = ATM_CELL_PAYLOAD - data_len;
u8 *ptr = target;
@@ -439,11 +487,11 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
ptr[4] = 0xec;
ptr += ATM_CELL_HEADER;
- memcpy(ptr, skb->data, data_len);
+ skb_copy_from_linear_data(skb, ptr, data_len);
ptr += data_len;
__skb_pull(skb, data_len);
- if(!left)
+ if (!left)
continue;
memset(ptr, 0, left);
@@ -455,7 +503,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
trailer[2] = ctrl->len >> 8;
trailer[3] = ctrl->len;
- ctrl->crc = ~ crc32_be(ctrl->crc, ptr, left - 4);
+ ctrl->crc = ~crc32_be(ctrl->crc, ptr, left - 4);
trailer[4] = ctrl->crc >> 24;
trailer[5] = ctrl->crc >> 16;
@@ -465,12 +513,11 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
target[3] |= 0x2; /* adjust PTI */
ctrl->len = 0; /* tag this skb finished */
- }
- else
+ } else
ctrl->crc = crc32_be(ctrl->crc, ptr, left);
}
- return num_written;
+ return bytes_written;
}
@@ -484,19 +531,42 @@ static void usbatm_rx_process(unsigned long data)
struct urb *urb;
while ((urb = usbatm_pop_urb(&instance->rx_channel))) {
- vdbg("%s: processing urb 0x%p", __func__, urb);
+ vdbg(&instance->usb_intf->dev,
+ "%s: processing urb 0x%p", __func__, urb);
if (usb_pipeisoc(urb->pipe)) {
+ unsigned char *merge_start = NULL;
+ unsigned int merge_length = 0;
+ const unsigned int packet_size = instance->rx_channel.packet_size;
int i;
- for (i = 0; i < urb->number_of_packets; i++)
- if (!urb->iso_frame_desc[i].status)
- usbatm_extract_cells(instance,
- (u8 *)urb->transfer_buffer + urb->iso_frame_desc[i].offset,
- urb->iso_frame_desc[i].actual_length);
- }
- else
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ if (!urb->iso_frame_desc[i].status) {
+ unsigned int actual_length = urb->iso_frame_desc[i].actual_length;
+
+ if (!merge_length)
+ merge_start = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ merge_length += actual_length;
+ if (merge_length && (actual_length < packet_size)) {
+ usbatm_extract_cells(instance, merge_start, merge_length);
+ merge_length = 0;
+ }
+ } else {
+ atm_rldbg(instance, "%s: status %d in frame %d!\n", __func__, urb->status, i);
+ if (merge_length)
+ usbatm_extract_cells(instance, merge_start, merge_length);
+ merge_length = 0;
+ instance->buf_usage = 0;
+ }
+ }
+
+ if (merge_length)
+ usbatm_extract_cells(instance, merge_start, merge_length);
+ } else
if (!urb->status)
usbatm_extract_cells(instance, urb->transfer_buffer, urb->actual_length);
+ else
+ instance->buf_usage = 0;
if (usbatm_submit_urb(urb))
return;
@@ -514,7 +584,7 @@ static void usbatm_tx_process(unsigned long data)
struct sk_buff *skb = instance->current_skb;
struct urb *urb = NULL;
const unsigned int buf_size = instance->tx_channel.buf_size;
- unsigned int num_written = 0;
+ unsigned int bytes_written = 0;
u8 *buffer = NULL;
if (!skb)
@@ -526,16 +596,17 @@ static void usbatm_tx_process(unsigned long data)
if (!urb)
break; /* no more senders */
buffer = urb->transfer_buffer;
- num_written = (urb->status == -EAGAIN) ?
+ bytes_written = (urb->status == -EAGAIN) ?
urb->transfer_buffer_length : 0;
}
- num_written += usbatm_write_cells(instance, skb,
- buffer + num_written,
- buf_size - num_written);
+ bytes_written += usbatm_write_cells(instance, skb,
+ buffer + bytes_written,
+ buf_size - bytes_written);
- vdbg("%s: wrote %u bytes from skb 0x%p to urb 0x%p",
- __func__, num_written, skb, urb);
+ vdbg(&instance->usb_intf->dev,
+ "%s: wrote %u bytes from skb 0x%p to urb 0x%p",
+ __func__, bytes_written, skb, urb);
if (!UDSL_SKB(skb)->len) {
struct atm_vcc *vcc = UDSL_SKB(skb)->atm.vcc;
@@ -546,8 +617,8 @@ static void usbatm_tx_process(unsigned long data)
skb = skb_dequeue(&instance->sndqueue);
}
- if (num_written == buf_size || (!skb && num_written)) {
- urb->transfer_buffer_length = num_written;
+ if (bytes_written == buf_size || (!skb && bytes_written)) {
+ urb->transfer_buffer_length = bytes_written;
if (usbatm_submit_urb(urb))
break;
@@ -563,16 +634,14 @@ static void usbatm_cancel_send(struct usbatm_data *instance,
{
struct sk_buff *skb, *n;
- atm_dbg(instance, "%s entered\n", __func__);
spin_lock_irq(&instance->sndqueue.lock);
- for (skb = instance->sndqueue.next, n = skb->next;
- skb != (struct sk_buff *)&instance->sndqueue;
- skb = n, n = skb->next)
+ skb_queue_walk_safe(&instance->sndqueue, skb, n) {
if (UDSL_SKB(skb)->atm.vcc == vcc) {
atm_dbg(instance, "%s: popping skb 0x%p\n", __func__, skb);
__skb_unlink(skb, &instance->sndqueue);
usbatm_pop(vcc, skb);
}
+ }
spin_unlock_irq(&instance->sndqueue.lock);
tasklet_disable(&instance->tx_channel.tasklet);
@@ -582,7 +651,6 @@ static void usbatm_cancel_send(struct usbatm_data *instance,
usbatm_pop(vcc, skb);
}
tasklet_enable(&instance->tx_channel.tasklet);
- atm_dbg(instance, "%s done\n", __func__);
}
static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
@@ -591,28 +659,29 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
struct usbatm_control *ctrl = UDSL_SKB(skb);
int err;
- vdbg("%s called (skb 0x%p, len %u)", __func__, skb, skb->len);
-
- if (!instance) {
- dbg("%s: NULL data!", __func__);
+ /* racy disconnection check - fine */
+ if (!instance || instance->disconnected) {
+#ifdef VERBOSE_DEBUG
+ printk_ratelimited(KERN_DEBUG "%s: %s!\n", __func__, instance ? "disconnected" : "NULL instance");
+#endif
err = -ENODEV;
goto fail;
}
if (vcc->qos.aal != ATM_AAL5) {
- atm_dbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal);
+ atm_rldbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal);
err = -EINVAL;
goto fail;
}
if (skb->len > ATM_MAX_AAL5_PDU) {
- atm_dbg(instance, "%s: packet too long (%d vs %d)!\n",
+ atm_rldbg(instance, "%s: packet too long (%d vs %d)!\n",
__func__, skb->len, ATM_MAX_AAL5_PDU);
err = -EINVAL;
goto fail;
}
- PACKETDEBUG(skb->data, skb->len);
+ PACKETDEBUG(instance, skb->data, skb->len);
/* initialize the control block */
ctrl->atm.vcc = vcc;
@@ -638,8 +707,6 @@ static void usbatm_destroy_instance(struct kref *kref)
{
struct usbatm_data *instance = container_of(kref, struct usbatm_data, refcount);
- dbg("%s", __func__);
-
tasklet_kill(&instance->rx_channel.tasklet);
tasklet_kill(&instance->tx_channel.tasklet);
usb_put_dev(instance->usb_dev);
@@ -648,15 +715,11 @@ static void usbatm_destroy_instance(struct kref *kref)
static void usbatm_get_instance(struct usbatm_data *instance)
{
- dbg("%s", __func__);
-
kref_get(&instance->refcount);
}
static void usbatm_put_instance(struct usbatm_data *instance)
{
- dbg("%s", __func__);
-
kref_put(&instance->refcount, usbatm_destroy_instance);
}
@@ -665,37 +728,30 @@ static void usbatm_put_instance(struct usbatm_data *instance)
** ATM **
**********/
-static void usbatm_atm_dev_close(struct atm_dev *dev)
+static void usbatm_atm_dev_close(struct atm_dev *atm_dev)
{
- struct usbatm_data *instance = dev->dev_data;
-
- dbg("%s", __func__);
+ struct usbatm_data *instance = atm_dev->dev_data;
if (!instance)
return;
- dev->dev_data = NULL;
+ atm_dev->dev_data = NULL; /* catch bugs */
usbatm_put_instance(instance); /* taken in usbatm_atm_init */
}
-static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page)
+static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t *pos, char *page)
{
struct usbatm_data *instance = atm_dev->dev_data;
int left = *pos;
- if (!instance) {
- dbg("%s: NULL instance!", __func__);
+ if (!instance)
return -ENODEV;
- }
if (!left--)
return sprintf(page, "%s\n", instance->description);
if (!left--)
- return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
- atm_dev->esi[0], atm_dev->esi[1],
- atm_dev->esi[2], atm_dev->esi[3],
- atm_dev->esi[4], atm_dev->esi[5]);
+ return sprintf(page, "MAC: %pM\n", atm_dev->esi);
if (!left--)
return sprintf(page,
@@ -706,15 +762,19 @@ static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *pag
atomic_read(&atm_dev->stats.aal5.rx_err),
atomic_read(&atm_dev->stats.aal5.rx_drop));
- if (!left--)
- switch (atm_dev->signal) {
- case ATM_PHY_SIG_FOUND:
- return sprintf(page, "Line up\n");
- case ATM_PHY_SIG_LOST:
- return sprintf(page, "Line down\n");
- default:
- return sprintf(page, "Line state unknown\n");
- }
+ if (!left--) {
+ if (instance->disconnected)
+ return sprintf(page, "Disconnected\n");
+ else
+ switch (atm_dev->signal) {
+ case ATM_PHY_SIG_FOUND:
+ return sprintf(page, "Line up\n");
+ case ATM_PHY_SIG_LOST:
+ return sprintf(page, "Line down\n");
+ default:
+ return sprintf(page, "Line state unknown\n");
+ }
+ }
return 0;
}
@@ -727,21 +787,28 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
int vci = vcc->vci;
short vpi = vcc->vpi;
- if (!instance) {
- dbg("%s: NULL data!", __func__);
+ if (!instance)
return -ENODEV;
- }
-
- atm_dbg(instance, "%s: vpi %hd, vci %d\n", __func__, vpi, vci);
/* only support AAL5 */
- if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0)
- || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
- atm_dbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal);
+ if ((vcc->qos.aal != ATM_AAL5)) {
+ atm_warn(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal);
return -EINVAL;
}
- down(&instance->serialize); /* vs self, usbatm_atm_close */
+ /* sanity checks */
+ if ((vcc->qos.rxtp.max_sdu < 0) || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
+ atm_dbg(instance, "%s: max_sdu %d out of range!\n", __func__, vcc->qos.rxtp.max_sdu);
+ return -EINVAL;
+ }
+
+ mutex_lock(&instance->serialize); /* vs self, usbatm_atm_close, usbatm_usb_disconnect */
+
+ if (instance->disconnected) {
+ atm_dbg(instance, "%s: disconnected!\n", __func__);
+ ret = -ENODEV;
+ goto fail;
+ }
if (usbatm_find_vcc(instance, vpi, vci)) {
atm_dbg(instance, "%s: %hd/%d already in use!\n", __func__, vpi, vci);
@@ -749,20 +816,19 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
goto fail;
}
- if (!(new = kmalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL))) {
- atm_dbg(instance, "%s: no memory for vcc_data!\n", __func__);
+ if (!(new = kzalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL))) {
+ atm_err(instance, "%s: no memory for vcc_data!\n", __func__);
ret = -ENOMEM;
goto fail;
}
- memset(new, 0, sizeof(struct usbatm_vcc_data));
new->vcc = vcc;
new->vpi = vpi;
new->vci = vci;
new->sarb = alloc_skb(usbatm_pdu_length(vcc->qos.rxtp.max_sdu), GFP_KERNEL);
if (!new->sarb) {
- atm_dbg(instance, "%s: no memory for SAR buffer!\n", __func__);
+ atm_err(instance, "%s: no memory for SAR buffer!\n", __func__);
ret = -ENOMEM;
goto fail;
}
@@ -770,6 +836,9 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
vcc->dev_data = new;
tasklet_disable(&instance->rx_channel.tasklet);
+ instance->cached_vcc = new;
+ instance->cached_vpi = vpi;
+ instance->cached_vci = vci;
list_add(&new->list, &instance->vcc_list);
tasklet_enable(&instance->rx_channel.tasklet);
@@ -777,7 +846,7 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
set_bit(ATM_VF_PARTIAL, &vcc->flags);
set_bit(ATM_VF_READY, &vcc->flags);
- up(&instance->serialize);
+ mutex_unlock(&instance->serialize);
atm_dbg(instance, "%s: allocated vcc data 0x%p\n", __func__, new);
@@ -785,7 +854,7 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
fail:
kfree(new);
- up(&instance->serialize);
+ mutex_unlock(&instance->serialize);
return ret;
}
@@ -794,21 +863,19 @@ static void usbatm_atm_close(struct atm_vcc *vcc)
struct usbatm_data *instance = vcc->dev->dev_data;
struct usbatm_vcc_data *vcc_data = vcc->dev_data;
- if (!instance || !vcc_data) {
- dbg("%s: NULL data!", __func__);
+ if (!instance || !vcc_data)
return;
- }
-
- atm_dbg(instance, "%s entered\n", __func__);
-
- atm_dbg(instance, "%s: deallocating vcc 0x%p with vpi %d vci %d\n",
- __func__, vcc_data, vcc_data->vpi, vcc_data->vci);
usbatm_cancel_send(instance, vcc);
- down(&instance->serialize); /* vs self, usbatm_atm_open */
+ mutex_lock(&instance->serialize); /* vs self, usbatm_atm_open, usbatm_usb_disconnect */
tasklet_disable(&instance->rx_channel.tasklet);
+ if (instance->cached_vcc == vcc_data) {
+ instance->cached_vcc = NULL;
+ instance->cached_vpi = ATM_VPI_UNSPEC;
+ instance->cached_vci = ATM_VCI_UNSPEC;
+ }
list_del(&vcc_data->list);
tasklet_enable(&instance->rx_channel.tasklet);
@@ -824,14 +891,17 @@ static void usbatm_atm_close(struct atm_vcc *vcc)
clear_bit(ATM_VF_PARTIAL, &vcc->flags);
clear_bit(ATM_VF_ADDR, &vcc->flags);
- up(&instance->serialize);
-
- atm_dbg(instance, "%s successful\n", __func__);
+ mutex_unlock(&instance->serialize);
}
-static int usbatm_atm_ioctl(struct atm_dev *dev, unsigned int cmd,
- void __user * arg)
+static int usbatm_atm_ioctl(struct atm_dev *atm_dev, unsigned int cmd,
+ void __user *arg)
{
+ struct usbatm_data *instance = atm_dev->dev_data;
+
+ if (!instance || instance->disconnected)
+ return -ENODEV;
+
switch (cmd) {
case ATM_QUERYLOOP:
return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0;
@@ -845,10 +915,15 @@ static int usbatm_atm_init(struct usbatm_data *instance)
struct atm_dev *atm_dev;
int ret, i;
- /* ATM init */
- atm_dev = atm_dev_register(instance->driver_name, &usbatm_atm_devops, -1, NULL);
+ /* ATM init. The ATM initialization scheme suffers from an intrinsic race
+ * condition: callbacks we register can be executed at once, before we have
+ * initialized the struct atm_dev. To protect against this, all callbacks
+ * abort if atm_dev->dev_data is NULL. */
+ atm_dev = atm_dev_register(instance->driver_name,
+ &instance->usb_intf->dev, &usbatm_atm_devops,
+ -1, NULL);
if (!atm_dev) {
- usb_dbg(instance, "%s: failed to register ATM device!\n", __func__);
+ usb_err(instance, "%s: failed to register ATM device!\n", __func__);
return -1;
}
@@ -862,12 +937,13 @@ static int usbatm_atm_init(struct usbatm_data *instance)
atm_dev->link_rate = 128 * 1000 / 424;
if (instance->driver->atm_start && ((ret = instance->driver->atm_start(instance, atm_dev)) < 0)) {
- atm_dbg(instance, "%s: atm_start failed: %d!\n", __func__, ret);
+ atm_err(instance, "%s: atm_start failed: %d!\n", __func__, ret);
goto fail;
}
- /* ready for ATM callbacks */
usbatm_get_instance(instance); /* dropped in usbatm_atm_dev_close */
+
+ /* ready for ATM callbacks */
mb();
atm_dev->dev_data = instance;
@@ -893,9 +969,7 @@ static int usbatm_do_heavy_init(void *arg)
struct usbatm_data *instance = arg;
int ret;
- daemonize(instance->driver->driver_name);
allow_signal(SIGTERM);
-
complete(&instance->thread_started);
ret = instance->driver->heavy_init(instance, instance->usb_intf);
@@ -903,26 +977,27 @@ static int usbatm_do_heavy_init(void *arg)
if (!ret)
ret = usbatm_atm_init(instance);
- down(&instance->serialize);
- instance->thread_pid = -1;
- up(&instance->serialize);
+ mutex_lock(&instance->serialize);
+ instance->thread = NULL;
+ mutex_unlock(&instance->serialize);
complete_and_exit(&instance->thread_exited, ret);
}
static int usbatm_heavy_init(struct usbatm_data *instance)
{
- int ret = kernel_thread(usbatm_do_heavy_init, instance, CLONE_KERNEL);
-
- if (ret < 0) {
- usb_dbg(instance, "%s: failed to create kernel_thread (%d)!\n", __func__, ret);
- return ret;
+ struct task_struct *t;
+
+ t = kthread_create(usbatm_do_heavy_init, instance, "%s",
+ instance->driver->driver_name);
+ if (IS_ERR(t)) {
+ usb_err(instance, "%s: failed to create kernel_thread (%ld)!\n",
+ __func__, PTR_ERR(t));
+ return PTR_ERR(t);
}
- down(&instance->serialize);
- instance->thread_pid = ret;
- up(&instance->serialize);
-
+ instance->thread = t;
+ wake_up_process(t);
wait_for_completion(&instance->thread_started);
return 0;
@@ -933,7 +1008,7 @@ static void usbatm_tasklet_schedule(unsigned long data)
tasklet_schedule((struct tasklet_struct *) data);
}
-static inline void usbatm_init_channel(struct usbatm_channel *channel)
+static void usbatm_init_channel(struct usbatm_channel *channel)
{
spin_lock_init(&channel->lock);
INIT_LIST_HEAD(&channel->list);
@@ -951,25 +1026,20 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
char *buf;
int error = -ENOMEM;
int i, length;
- int need_heavy;
-
- dev_dbg(dev, "%s: trying driver %s with vendor=0x%x, product=0x%x, ifnum %d\n",
- __func__, driver->driver_name,
- le16_to_cpu(usb_dev->descriptor.idVendor),
- le16_to_cpu(usb_dev->descriptor.idProduct),
- intf->altsetting->desc.bInterfaceNumber);
+ unsigned int maxpacket, num_packets;
/* instance init */
instance = kzalloc(sizeof(*instance) + sizeof(struct urb *) * (num_rcv_urbs + num_snd_urbs), GFP_KERNEL);
if (!instance) {
- dev_dbg(dev, "%s: no memory for instance data!\n", __func__);
+ dev_err(dev, "%s: no memory for instance data!\n", __func__);
return -ENOMEM;
}
/* public fields */
instance->driver = driver;
- snprintf(instance->driver_name, sizeof(instance->driver_name), driver->driver_name);
+ strlcpy(instance->driver_name, driver->driver_name,
+ sizeof(instance->driver_name));
instance->usb_dev = usb_dev;
instance->usb_intf = intf;
@@ -996,66 +1066,93 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
snprintf(buf, length, ")");
bind:
- need_heavy = 1;
- if (driver->bind && (error = driver->bind(instance, intf, id, &need_heavy)) < 0) {
- dev_dbg(dev, "%s: bind failed: %d!\n", __func__, error);
+ if (driver->bind && (error = driver->bind(instance, intf, id)) < 0) {
+ dev_err(dev, "%s: bind failed: %d!\n", __func__, error);
goto fail_free;
}
/* private fields */
kref_init(&instance->refcount); /* dropped in usbatm_usb_disconnect */
- init_MUTEX(&instance->serialize);
+ mutex_init(&instance->serialize);
- instance->thread_pid = -1;
+ instance->thread = NULL;
init_completion(&instance->thread_started);
init_completion(&instance->thread_exited);
INIT_LIST_HEAD(&instance->vcc_list);
+ skb_queue_head_init(&instance->sndqueue);
usbatm_init_channel(&instance->rx_channel);
usbatm_init_channel(&instance->tx_channel);
tasklet_init(&instance->rx_channel.tasklet, usbatm_rx_process, (unsigned long)instance);
tasklet_init(&instance->tx_channel.tasklet, usbatm_tx_process, (unsigned long)instance);
- instance->rx_channel.endpoint = usb_rcvbulkpipe(usb_dev, driver->in);
- instance->tx_channel.endpoint = usb_sndbulkpipe(usb_dev, driver->out);
instance->rx_channel.stride = ATM_CELL_SIZE + driver->rx_padding;
instance->tx_channel.stride = ATM_CELL_SIZE + driver->tx_padding;
- instance->rx_channel.buf_size = rcv_buf_size * instance->rx_channel.stride;
- instance->tx_channel.buf_size = snd_buf_size * instance->tx_channel.stride;
instance->rx_channel.usbatm = instance->tx_channel.usbatm = instance;
- skb_queue_head_init(&instance->sndqueue);
+ if ((instance->flags & UDSL_USE_ISOC) && driver->isoc_in)
+ instance->rx_channel.endpoint = usb_rcvisocpipe(usb_dev, driver->isoc_in);
+ else
+ instance->rx_channel.endpoint = usb_rcvbulkpipe(usb_dev, driver->bulk_in);
+
+ instance->tx_channel.endpoint = usb_sndbulkpipe(usb_dev, driver->bulk_out);
+
+ /* tx buffer size must be a positive multiple of the stride */
+ instance->tx_channel.buf_size = max(instance->tx_channel.stride,
+ snd_buf_bytes - (snd_buf_bytes % instance->tx_channel.stride));
+
+ /* rx buffer size must be a positive multiple of the endpoint maxpacket */
+ maxpacket = usb_maxpacket(usb_dev, instance->rx_channel.endpoint, 0);
+
+ if ((maxpacket < 1) || (maxpacket > UDSL_MAX_BUF_SIZE)) {
+ dev_err(dev, "%s: invalid endpoint %02x!\n", __func__,
+ usb_pipeendpoint(instance->rx_channel.endpoint));
+ error = -EINVAL;
+ goto fail_unbind;
+ }
+
+ num_packets = max(1U, (rcv_buf_bytes + maxpacket / 2) / maxpacket); /* round */
+
+ if (num_packets * maxpacket > UDSL_MAX_BUF_SIZE)
+ num_packets--;
+
+ instance->rx_channel.buf_size = num_packets * maxpacket;
+ instance->rx_channel.packet_size = maxpacket;
+
+ for (i = 0; i < 2; i++) {
+ struct usbatm_channel *channel = i ?
+ &instance->tx_channel : &instance->rx_channel;
+
+ dev_dbg(dev, "%s: using %d byte buffer for %s channel 0x%p\n",
+ __func__, channel->buf_size, i ? "tx" : "rx", channel);
+ }
+
+ /* initialize urbs */
for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
- struct urb *urb;
u8 *buffer;
- unsigned int iso_packets = 0, iso_size = 0;
struct usbatm_channel *channel = i < num_rcv_urbs ?
&instance->rx_channel : &instance->tx_channel;
-
- if (usb_pipeisoc(channel->endpoint)) {
- /* don't expect iso out endpoints */
- iso_size = usb_maxpacket(instance->usb_dev, channel->endpoint, 0);
- iso_size -= iso_size % channel->stride; /* alignment */
- BUG_ON(!iso_size);
- iso_packets = (channel->buf_size - 1) / iso_size + 1;
- }
+ struct urb *urb;
+ unsigned int iso_packets = usb_pipeisoc(channel->endpoint) ? channel->buf_size / channel->packet_size : 0;
urb = usb_alloc_urb(iso_packets, GFP_KERNEL);
if (!urb) {
- dev_dbg(dev, "%s: no memory for urb %d!\n", __func__, i);
+ dev_err(dev, "%s: no memory for urb %d!\n", __func__, i);
+ error = -ENOMEM;
goto fail_unbind;
}
instance->urbs[i] = urb;
- buffer = kmalloc(channel->buf_size, GFP_KERNEL);
+ /* zero the tx padding to avoid leaking information */
+ buffer = kzalloc(channel->buf_size, GFP_KERNEL);
if (!buffer) {
- dev_dbg(dev, "%s: no memory for buffer %d!\n", __func__, i);
+ dev_err(dev, "%s: no memory for buffer %d!\n", __func__, i);
+ error = -ENOMEM;
goto fail_unbind;
}
- memset(buffer, 0, channel->buf_size);
usb_fill_bulk_urb(urb, instance->usb_dev, channel->endpoint,
buffer, channel->buf_size, usbatm_complete, channel);
@@ -1065,9 +1162,8 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
urb->transfer_flags = URB_ISO_ASAP;
urb->number_of_packets = iso_packets;
for (j = 0; j < iso_packets; j++) {
- urb->iso_frame_desc[j].offset = iso_size * j;
- urb->iso_frame_desc[j].length = min_t(int, iso_size,
- channel->buf_size - urb->iso_frame_desc[j].offset);
+ urb->iso_frame_desc[j].offset = channel->packet_size * j;
+ urb->iso_frame_desc[j].length = channel->packet_size;
}
}
@@ -1075,11 +1171,21 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
if (i >= num_rcv_urbs)
list_add_tail(&urb->urb_list, &channel->list);
- vdbg("%s: alloced buffer 0x%p buf size %u urb 0x%p",
+ vdbg(&intf->dev, "%s: alloced buffer 0x%p buf size %u urb 0x%p",
__func__, urb->transfer_buffer, urb->transfer_buffer_length, urb);
}
- if (need_heavy && driver->heavy_init) {
+ instance->cached_vpi = ATM_VPI_UNSPEC;
+ instance->cached_vci = ATM_VCI_UNSPEC;
+ instance->cell_buf = kmalloc(instance->rx_channel.stride, GFP_KERNEL);
+
+ if (!instance->cell_buf) {
+ dev_err(dev, "%s: no memory for cell buffer!\n", __func__);
+ error = -ENOMEM;
+ goto fail_unbind;
+ }
+
+ if (!(instance->flags & UDSL_SKIP_HEAVY_INIT) && driver->heavy_init) {
error = usbatm_heavy_init(instance);
} else {
complete(&instance->thread_exited); /* pretend that heavy_init was run */
@@ -1098,13 +1204,15 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
if (instance->driver->unbind)
instance->driver->unbind(instance, intf);
fail_free:
+ kfree(instance->cell_buf);
+
for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
if (instance->urbs[i])
kfree(instance->urbs[i]->transfer_buffer);
usb_free_urb(instance->urbs[i]);
}
- kfree (instance);
+ kfree(instance);
return error;
}
@@ -1114,10 +1222,9 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
{
struct device *dev = &intf->dev;
struct usbatm_data *instance = usb_get_intfdata(intf);
+ struct usbatm_vcc_data *vcc_data;
int i;
- dev_dbg(dev, "%s entered\n", __func__);
-
if (!instance) {
dev_dbg(dev, "%s: NULL instance!\n", __func__);
return;
@@ -1125,13 +1232,19 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
- down(&instance->serialize);
- if (instance->thread_pid >= 0)
- kill_proc(instance->thread_pid, SIGTERM, 1);
- up(&instance->serialize);
+ mutex_lock(&instance->serialize);
+ instance->disconnected = 1;
+ if (instance->thread != NULL)
+ send_sig(SIGTERM, instance->thread, 1);
+ mutex_unlock(&instance->serialize);
wait_for_completion(&instance->thread_exited);
+ mutex_lock(&instance->serialize);
+ list_for_each_entry(vcc_data, &instance->vcc_list, list)
+ vcc_release_async(vcc_data->vcc, -EPIPE);
+ mutex_unlock(&instance->serialize);
+
tasklet_disable(&instance->rx_channel.tasklet);
tasklet_disable(&instance->tx_channel.tasklet);
@@ -1141,6 +1254,14 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
del_timer_sync(&instance->rx_channel.delay);
del_timer_sync(&instance->tx_channel.delay);
+ /* turn usbatm_[rt]x_process into something close to a no-op */
+ /* no need to take the spinlock */
+ INIT_LIST_HEAD(&instance->rx_channel.list);
+ INIT_LIST_HEAD(&instance->tx_channel.list);
+
+ tasklet_enable(&instance->rx_channel.tasklet);
+ tasklet_enable(&instance->tx_channel.tasklet);
+
if (instance->atm_dev && instance->driver->atm_stop)
instance->driver->atm_stop(instance, instance->atm_dev);
@@ -1149,22 +1270,18 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
instance->driver_data = NULL;
- /* turn usbatm_[rt]x_process into noop */
- /* no need to take the spinlock */
- INIT_LIST_HEAD(&instance->rx_channel.list);
- INIT_LIST_HEAD(&instance->tx_channel.list);
-
- tasklet_enable(&instance->rx_channel.tasklet);
- tasklet_enable(&instance->tx_channel.tasklet);
-
for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) {
kfree(instance->urbs[i]->transfer_buffer);
usb_free_urb(instance->urbs[i]);
}
+ kfree(instance->cell_buf);
+
/* ATM finalize */
- if (instance->atm_dev)
+ if (instance->atm_dev) {
atm_dev_deregister(instance->atm_dev);
+ instance->atm_dev = NULL;
+ }
usbatm_put_instance(instance); /* taken in usbatm_usb_probe */
}
@@ -1177,19 +1294,17 @@ EXPORT_SYMBOL_GPL(usbatm_usb_disconnect);
static int __init usbatm_usb_init(void)
{
- dbg("%s: driver version %s", __func__, DRIVER_VERSION);
-
- if (sizeof(struct usbatm_control) > sizeof(((struct sk_buff *) 0)->cb)) {
+ if (sizeof(struct usbatm_control) > FIELD_SIZEOF(struct sk_buff, cb)) {
printk(KERN_ERR "%s unusable with this kernel!\n", usbatm_driver_name);
return -EIO;
}
if ((num_rcv_urbs > UDSL_MAX_RCV_URBS)
|| (num_snd_urbs > UDSL_MAX_SND_URBS)
- || (rcv_buf_size < 1)
- || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE)
- || (snd_buf_size < 1)
- || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
+ || (rcv_buf_bytes < 1)
+ || (rcv_buf_bytes > UDSL_MAX_BUF_SIZE)
+ || (snd_buf_bytes < 1)
+ || (snd_buf_bytes > UDSL_MAX_BUF_SIZE))
return -EINVAL;
return 0;
@@ -1198,7 +1313,6 @@ module_init(usbatm_usb_init);
static void __exit usbatm_usb_exit(void)
{
- dbg("%s", __func__);
}
module_exit(usbatm_usb_exit);
@@ -1212,7 +1326,8 @@ MODULE_VERSION(DRIVER_VERSION);
************/
#ifdef VERBOSE_DEBUG
-static int usbatm_print_packet(const unsigned char *data, int len)
+static int usbatm_print_packet(struct usbatm_data *instance,
+ const unsigned char *data, int len)
{
unsigned char buffer[256];
int i = 0, j = 0;
@@ -1220,10 +1335,9 @@ static int usbatm_print_packet(const unsigned char *data, int len)
for (i = 0; i < len;) {
buffer[0] = '\0';
sprintf(buffer, "%.3d :", i);
- for (j = 0; (j < 16) && (i < len); j++, i++) {
+ for (j = 0; (j < 16) && (i < len); j++, i++)
sprintf(buffer, "%s %2.2x", buffer, data[i]);
- }
- dbg("%s", buffer);
+ dev_dbg(&instance->usb_intf->dev, "%s", buffer);
}
return i;
}
diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h
index 1adacd60d71..f3eecd967a8 100644
--- a/drivers/usb/atm/usbatm.h
+++ b/drivers/usb/atm/usbatm.h
@@ -24,27 +24,21 @@
#ifndef _USBATM_H_
#define _USBATM_H_
-#include <linux/config.h>
-
-/*
-#define VERBOSE_DEBUG
-*/
-
-#include <asm/semaphore.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>
-#ifdef DEBUG
-#define UDSL_ASSERT(x) BUG_ON(!(x))
-#else
-#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '%s' at line %d", __stringify(x), __LINE__); } while(0)
-#endif
+/*
+#define VERBOSE_DEBUG
+*/
#define usb_err(instance, format, arg...) \
dev_err(&(instance)->usb_intf->dev , format , ## arg)
@@ -66,13 +60,18 @@
atm_printk(KERN_INFO, instance , format , ## arg)
#define atm_warn(instance, format, arg...) \
atm_printk(KERN_WARNING, instance , format , ## arg)
-#ifdef DEBUG
-#define atm_dbg(instance, format, arg...) \
- atm_printk(KERN_DEBUG, instance , format , ## arg)
-#else
-#define atm_dbg(instance, format, arg...) \
- do {} while (0)
-#endif
+#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 */
@@ -82,26 +81,21 @@ struct usbatm_data;
/*
* Assuming all methods exist and succeed, they are called in this order:
*
-* bind, heavy_init, atm_start, ..., atm_stop, unbind
+* bind, heavy_init, atm_start, ..., atm_stop, unbind
*/
struct usbatm_driver {
- struct module *owner;
-
const char *driver_name;
- /*
- * init device ... can sleep, or cause probe() failure. Drivers with a heavy_init
- * method can avoid having it called by setting need_heavy_init to zero.
- */
- int (*bind) (struct usbatm_data *, struct usb_interface *,
- const struct usb_device_id *id, int *need_heavy_init);
+ /* 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 *);
+ 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 *);
+ 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 *);
@@ -109,8 +103,9 @@ struct usbatm_driver {
/* cleanup ATM device ... can sleep, but can't fail */
void (*atm_stop) (struct usbatm_data *, struct atm_dev *);
- int in; /* rx endpoint */
- int out; /* tx endpoint */
+ int bulk_in; /* bulk rx endpoint */
+ int isoc_in; /* isochronous rx endpoint */
+ int bulk_out; /* bulk tx endpoint */
unsigned rx_padding;
unsigned tx_padding;
@@ -125,6 +120,7 @@ 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;
@@ -137,12 +133,13 @@ struct usbatm_channel {
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;
@@ -154,13 +151,14 @@ struct usbatm_data {
/********************************
* private fields - do not use *
- ********************************/
+ ********************************/
struct kref refcount;
- struct semaphore serialize;
+ struct mutex serialize;
+ int disconnected;
/* heavy init */
- int thread_pid;
+ struct task_struct *thread;
struct completion thread_started;
struct completion thread_exited;
@@ -171,9 +169,31 @@ struct usbatm_data {
struct usbatm_channel tx_channel;
struct sk_buff_head sndqueue;
- struct sk_buff *current_skb; /* being emptied */
+ 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_ */
diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c
index 5c76e3aaaa5..b3b1bb78b2e 100644
--- a/drivers/usb/atm/xusbatm.c
+++ b/drivers/usb/atm/xusbatm.c
@@ -20,8 +20,7 @@
******************************************************************************/
#include <linux/module.h>
-#include <linux/netdevice.h> /* FIXME: required by linux/etherdevice.h */
-#include <linux/etherdevice.h> /* for random_ether_addr() */
+#include <linux/etherdevice.h> /* for eth_random_addr() */
#include "usbatm.h"
@@ -30,7 +29,7 @@
#define XUSBATM_PARM(name, type, parmtype, desc) \
static type name[XUSBATM_DRIVERS_MAX]; \
- static int num_##name; \
+ static unsigned int num_##name; \
module_param_array(name, parmtype, &num_##name, 0444); \
MODULE_PARM_DESC(name, desc)
@@ -41,6 +40,8 @@ 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";
@@ -48,85 +49,121 @@ 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 int usb_intf_has_ep(const struct usb_interface *intf, u8 ep)
+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 < intf->num_altsetting; i++) {
- struct usb_host_interface *alt = intf->altsetting;
- for (j = 0; j < alt->desc.bNumEndpoints; j++)
- if ((alt->endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) == ep)
- return 1;
+ 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 int xusbatm_bind(struct usbatm_data *usbatm_instance,
- struct usb_interface *intf, const struct usb_device_id *id,
- int *need_heavy_init)
+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_ep_present = usb_intf_has_ep(intf, rx_endpoint[drv_ix]);
- int tx_ep_present = usb_intf_has_ep(intf, tx_endpoint[drv_ix]);
- u8 searched_ep = rx_ep_present ? tx_endpoint[drv_ix] : rx_endpoint[drv_ix];
- int i, ret;
-
- usb_dbg(usbatm_instance, "%s: binding driver %d: vendor %#x product %#x"
- " rx: ep %#x padd %d tx: ep %#x padd %d\n",
+ 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],
- tx_endpoint[drv_ix], tx_padding[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_ep_present && !tx_ep_present) {
- usb_dbg(usbatm_instance, "%s: intf #%d has neither rx (%#x) nor tx (%#x) endpoint\n",
- __func__, intf->altsetting->desc.bInterfaceNumber,
- rx_endpoint[drv_ix], tx_endpoint[drv_ix]);
+ 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;
}
- if (rx_ep_present && tx_ep_present)
- return 0;
+ usb_dbg(usbatm, "%s: rx If#=%2d; tx If#=%2d\n", __func__,
+ rx_intf->altsetting->desc.bInterfaceNumber,
+ tx_intf->altsetting->desc.bInterfaceNumber);
- for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
- struct usb_interface *cur_if = usb_dev->actconfig->interface[i];
+ if ((ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf)))
+ return ret;
- if (cur_if != intf && usb_intf_has_ep(cur_if, searched_ep)) {
- ret = usb_driver_claim_interface(&xusbatm_usb_driver,
- cur_if, usbatm_instance);
- if (!ret)
- usb_err(usbatm_instance, "%s: failed to claim interface #%d (%d)\n",
- __func__, cur_if->altsetting->desc.bInterfaceNumber, ret);
- 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;
}
- usb_err(usbatm_instance, "%s: no interface has endpoint %#x\n",
- __func__, searched_ep);
- return -ENODEV;
+ return 0;
}
-static void xusbatm_unbind(struct usbatm_data *usbatm_instance,
+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_instance, "%s entered\n", __func__);
- for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
- struct usb_interface *cur_if = usb_dev->actconfig->interface[i];
- usb_set_intfdata(cur_if, NULL);
- usb_driver_release_interface(&xusbatm_usb_driver, cur_if);
+ 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_instance,
+static int xusbatm_atm_start(struct usbatm_data *usbatm,
struct atm_dev *atm_dev)
{
- atm_dbg(usbatm_instance, "%s entered\n", __func__);
+ atm_dbg(usbatm, "%s entered\n", __func__);
/* use random MAC as we've no way to get it from the device */
- random_ether_addr(atm_dev->esi);
+ eth_random_addr(atm_dev->esi);
return 0;
}
@@ -150,29 +187,28 @@ static int __init xusbatm_init(void)
{
int i;
- dbg("xusbatm_init");
-
if (!num_vendor ||
num_vendor != num_product ||
num_vendor != num_rx_endpoint ||
num_vendor != num_tx_endpoint) {
- warn("malformed module parameters");
+ 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].owner = THIS_MODULE;
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].in = rx_endpoint[i];
- xusbatm_drivers[i].out = tx_endpoint[i];
+ 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];
}
@@ -183,8 +219,6 @@ module_init(xusbatm_init);
static void __exit xusbatm_exit(void)
{
- dbg("xusbatm_exit entered");
-
usb_deregister(&xusbatm_usb_driver);
}
module_exit(xusbatm_exit);