public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH]  Eagle and ADI 930 usb adsl modem driver
@ 2005-10-29 22:37 matthieu castet
  2005-10-31 23:58 ` Andrew Morton
  2005-11-01 22:45 ` Greg KH
  0 siblings, 2 replies; 34+ messages in thread
From: matthieu castet @ 2005-10-29 22:37 UTC (permalink / raw)
  To: linux-usb-devel, usbatm, Linux Kernel list

[-- Attachment #1: Type: text/plain, Size: 1691 bytes --]

Hi,

attached is the driver for USB ADSL modems based on the ADI eagle
chipset using the usb_atm infrastructure.

The managing part was taken from bsd ueagle driver, other parts were
written from scratch.

The driver uses the in-kernel firmware loader :
- to load  a first usb firmware when the modem is in pre-firmware state
- to load the dsp firmware that are swapped in host memory.
- to load CMV (configuration and management variables) when the modem
boot. (We can't use options or sysfs for this as there many possible
values. See
https://mail.gna.org/public/eagleusb-dev/2005-04/msg00031.html for a
description of some)
- to load fpga code for 930 chipset.

The device had 4 endpoints :
* 2 for data (use by usbatm). The incoming
endpoint could be iso or bulk. The modem seems buggy and produce lot's
of atm errors when using it in bulk mode for speed > 3Mbps, so iso
endpoint is need for speed > 3Mbps. At the moment iso endpoint need a
patched usbatm library and for this reason is not included in this patch.

* One bulk endpoint for uploading dsp firmware

* One irq endpoint that notices the driver
    - if we need to upload a page of the dsp firmware
    - an ack for read or write CMV and the value (for the read case).

If order to make the driver cleaner, we design synchronous
(read|write)_cmv :
-send a synchronous control message to the modem
-wait for an ack or a timeout
-return the value if needed.

In order to run these synchronous usb messages we need a kernel thread.



Please comment and consider for inclusion.

The driver has been tested  with sagem fast 800 modems with different 
eagle chipset revision and with ADI 930 since April 2005.

Thanks.

Matthieu


[-- Attachment #2: ueagle-atm.patch --]
[-- Type: text/x-patch, Size: 53256 bytes --]

diff -rNu -x '*.ko*' -x '*.mod*' -x '*.o*' linux-2.6.14/drivers/usb/atm.old/Kconfig linux-2.6.14/drivers/usb/atm/Kconfig
--- linux-2.6.14/drivers/usb/atm.old/Kconfig	2005-10-28 02:02:08.000000000 +0200
+++ linux-2.6.14/drivers/usb/atm/Kconfig	2005-10-30 00:17:33.000000000 +0200
@@ -44,6 +44,19 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called cxacru.
 
+config USB_UEAGLEATM
+	tristate "ADI 930 and eagle USB DSL modem"
+	depends on USB_ATM
+	select FW_LOADER
+	help
+	  Say Y here if you have an ADSL USB modem based on the ADI 930
+	  or eagle chipset. In order to use your modem you will need to
+	  install firmwares and CMV (Command Management Variables); see
+	  <https://gna.org/projects/ueagleatm/> for details.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ueagle-atm.
+	  
 config USB_XUSBATM
 	tristate "Other USB DSL modem support"
 	depends on USB_ATM
diff -rNu -x '*.ko*' -x '*.mod*' -x '*.o*' linux-2.6.14/drivers/usb/atm.old/Makefile linux-2.6.14/drivers/usb/atm/Makefile
--- linux-2.6.14/drivers/usb/atm.old/Makefile	2005-10-28 02:02:08.000000000 +0200
+++ linux-2.6.14/drivers/usb/atm/Makefile	2005-10-30 00:17:33.000000000 +0200
@@ -4,5 +4,6 @@
 
 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
diff -rNu -x '*.ko*' -x '*.mod*' -x '*.o*' linux-2.6.14/drivers/usb/atm.old/ueagle-atm.c linux-2.6.14/drivers/usb/atm/ueagle-atm.c
--- linux-2.6.14/drivers/usb/atm.old/ueagle-atm.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.14/drivers/usb/atm/ueagle-atm.c	2005-10-30 00:36:37.000000000 +0200
@@ -0,0 +1,1607 @@
+/*-
+ * Copyright (c) 2003, 2004
+ *	Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
+ *
+ * Copyright (c) 2005 Matthieu Castet <castet.matthieu@free.fr>
+ *
+ * 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * GPL license :
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * HISTORY : some part of the code was base on ueagle 1.3 BSD driver,
+ * Damien Bergamini agree to put his code under a DUAL GPL/BSD license.
+ *
+ * The rest of the code was was rewritten from scratch.
+ */
+
+#define EAGLEUSBVERSION "ueagle-svn $Id: ueagle.c 141 2005-09-03 20:12:24Z matc $"
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/crc32.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/ctype.h>
+#include <linux/kthread.h>
+#include <linux/version.h>
+
+#include "ueagle-atm.h"
+
+static int uea_read_proc(char *page, char **start, off_t off, int count,
+			int *eof, void *data);
+
+static struct usb_driver uea_driver;
+static struct proc_dir_entry *uea_procdir;
+DECLARE_MUTEX(uea_semaphore);
+static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III"};
+
+/*
+ * User supplied debug level
+ */
+static unsigned int debug = 0;
+
+static int modem_index = 0;
+static int sync_wait[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = 0 };
+static char *cmv_file[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = NULL };
+
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "module debug level (0=off,1=on,2=verbose)");
+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");
+
+#define UPDATE_ATM_STAT(type, val) \
+	do { \
+		if (sc->usbatm->atm_dev) \
+			sc->usbatm->atm_dev->type = val; \
+	} while (0)
+
+/*
+ * sometime hotplug don't have time to give the firmware the
+ * first time, retry it.
+ */
+static int sleepy_request_firmware(const struct firmware **fw, 
+		const char *name, struct device *dev)
+{
+	if (request_firmware(fw, name, dev) == 0)
+		return 0;
+	msleep(1000);
+	return request_firmware(fw, name, dev);
+}
+
+/* Firmware loading */
+#define LOAD_INTERNAL     0xA0
+#define F8051_USBCS       0x7f92
+
+/**
+ * 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)
+{
+	int ret = -ENOMEM;
+	u8 *xfer_buff;
+
+	xfer_buff = kmalloc(size, GFP_KERNEL);
+	if (xfer_buff) {
+		memcpy(xfer_buff, buff, size);
+		ret = usb_control_msg(usb,
+				      usb_sndctrlpipe(usb, 0),
+				      LOAD_INTERNAL,
+				      USB_DIR_OUT | USB_TYPE_VENDOR |
+				      USB_RECIP_DEVICE, addr, 0, xfer_buff,
+				      size, CTRL_TIMEOUT);
+		kfree(xfer_buff);
+	}
+
+	if (ret < 0)
+		return ret;
+
+	return (ret == size) ? 0 : -EIO;
+}
+
+/**
+ * uea_load_firmware - Load usb firmware for pre-firmware devices.
+ */
+static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
+{
+	int ret, size;
+	const struct firmware *fw_entry;
+	u8 *pfw, value;
+	char *fw_name = FW_DIR "eagle.fw";
+	u32 crc = 0;
+
+	uea_enters(usb);
+	uea_info(usb, "pre-firmware device, uploading firmware\n");
+
+	switch (ver) {
+	case ADI930:
+		fw_name = FW_DIR "adi930.fw";
+		break;
+	case EAGLE_I:
+		fw_name = FW_DIR "eagleI.fw";
+		break;
+	case EAGLE_II:
+		fw_name = FW_DIR "eagleII.fw";
+		break;
+	case EAGLE_III:
+		fw_name = FW_DIR "eagleIII.fw";
+		break;
+	}
+
+	ret = sleepy_request_firmware(&fw_entry, fw_name, &usb->dev);
+	if (ret) {
+		uea_err(usb, "firmware %s is not available\n", fw_name);
+		goto err0;
+	}
+
+	pfw = fw_entry->data;
+	size = fw_entry->size;
+
+	crc = FW_GET_LONG(pfw);
+	pfw += 4;
+	size -= 4;
+	if (crc32_be(0, pfw, size) != crc) {
+		uea_err(usb, "firmware %s is corrupted\n", fw_name);
+		ret = -EILSEQ;
+		goto err1;
+	}
+
+	/*
+	 * Start to upload formware : send reset
+	 */
+	value = 1;
+	ret = uea_send_modem_cmd(usb, F8051_USBCS, sizeof(value), &value);
+
+	if (ret < 0) {
+		uea_err(usb, "modem reset failed with error %d\n", ret);
+		goto err1;
+	}
+
+	while (size > 0) {
+		u8 len = FW_GET_BYTE(pfw);
+		u16 add = FW_GET_WORD(pfw + 1);
+		ret = uea_send_modem_cmd(usb, add, len, pfw + 3);
+		if (ret < 0) {
+			uea_err(usb, "uploading firmware data failed " 
+					"with error %d\n", ret);
+			goto err1;
+		}
+		pfw += len + 3;
+		size -= len + 3;
+	}
+
+	/*
+	 * Tell the modem we finish : de-assert reset
+	 */
+	value = 0;
+	ret = uea_send_modem_cmd(usb, F8051_USBCS, 1, &value);
+	if (ret < 0)
+		uea_err(usb, "modem de-assert failed with error %d\n", ret);
+	else
+		uea_info(usb, "firmware uploaded\n");
+
+      err1:
+	release_firmware(fw_entry);
+      err0:
+	uea_leaves(usb);
+	return ret;
+}
+
+/* modem management : dsp firmware, send/read CMV, monitoring statistic
+ */
+
+/*
+ * Make sure that the DSP code provided is safe to use.
+ */
+static int check_dsp(u8 *dsp, int len)
+{
+	u8 *p, *end, *pp;
+	u8 pagecount, blockcount;
+	u16 blocksize;
+	u32 pageoffset;
+	int i, j;
+
+	end = dsp + len;
+
+	/* enough space for pagecount? */
+	if (dsp + 1 > end)
+		return 1;
+
+	p = dsp;
+	pagecount = FW_GET_BYTE(p);
+	p += 1;
+
+	/* enough space for page offsets? */
+	if (p + 4 * pagecount > end)
+		return 1;
+
+	for (i = 0; i < pagecount; i++) {
+
+		pageoffset = FW_GET_LONG(p);
+		p += 4;
+
+		if (pageoffset == 0)
+			continue;
+
+		/* enough space for blockcount? */
+		if (dsp + pageoffset + 1 > end)
+			return 1;
+
+		pp = dsp + pageoffset;
+		blockcount = FW_GET_BYTE(pp);
+		pp += 1;
+
+		for (j = 0; j < blockcount; j++) {
+
+			/* enough space for block header? */
+			if (pp + 4 > end)
+				return 1;
+
+			pp += 2;	/* skip blockaddr */
+			blocksize = FW_GET_WORD(pp);
+			pp += 2;
+
+			/* enough space for block data? */
+			if (pp + blocksize > end)
+				return 1;
+
+			pp += blocksize;
+		}
+	}
+
+	return 0;
+}
+
+/* 
+ * send data to the idma pipe 
+ * */
+static int uea_idma_write(struct uea_softc *sc, void *data, u32 size)
+{
+	int ret = -ENOMEM;
+	u8 *xfer_buff;
+	int bytes_read;
+
+	xfer_buff = kmalloc(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);
+
+	kfree(xfer_buff);
+	if (ret < 0) {
+		return ret;
+	}
+	if (size != bytes_read) {
+		uea_err(INS_TO_USBDEV(sc), "size != bytes_read %d %d\n", size,
+		       bytes_read);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int request_dsp(struct uea_softc *sc)
+{
+	int ret;
+	char *dsp_name;
+
+	if (UEA_CHIP_VERSION(sc) == ADI930) {
+		if (IS_ISDN(sc)) 
+			dsp_name = FW_DIR "DSP9i.bin";
+		else
+			dsp_name = FW_DIR "DSP9p.bin";
+	} else {
+		if (IS_ISDN(sc))
+			dsp_name = FW_DIR "DSPei.bin";
+		else
+			dsp_name = FW_DIR "DSPep.bin";
+	}
+	
+	ret = sleepy_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);
+		return ret;
+	}
+
+	if (check_dsp(sc->dsp_firm->data, sc->dsp_firm->size)) {
+		uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
+		       dsp_name);
+		release_firmware(sc->dsp_firm);
+		sc->dsp_firm = NULL;
+		return -EILSEQ;
+	}
+
+	return 0;
+}
+
+/*
+ * The uea_load_page() function must be called within a process context
+ */
+static void uea_load_page(void *xsc)
+{
+	struct uea_softc *sc = xsc;
+	u16 pageno = sc->pageno;
+	u16 ovl = sc->ovl;
+	block_info_t bi;
+
+	u8 *p;
+	u8 pagecount, blockcount;
+	u16 blockaddr, blocksize;
+	u32 pageoffset;
+	int i;
+
+	/* reload firmware when reboot start and it's loaded already */
+	if (ovl == 0 && 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 = sc->dsp_firm->data;
+	pagecount = FW_GET_BYTE(p);
+	p += 1;
+
+	if (pageno >= pagecount)
+		goto bad1;
+
+	p += 4 * pageno;
+	pageoffset = FW_GET_LONG(p);
+
+	if (pageoffset == 0)
+		goto bad1;
+
+	p = sc->dsp_firm->data + pageoffset;
+	blockcount = FW_GET_BYTE(p);
+	p += 1;
+
+	uea_dbg(INS_TO_USBDEV(sc),
+	       "sending %u blocks for DSP page %u\n", blockcount, pageno);
+
+	bi.wHdr = cpu_to_le16(UEA_BIHDR);
+	bi.wOvl = cpu_to_le16(ovl);
+	bi.wOvlOffset = cpu_to_le16(ovl | 0x8000);
+
+	for (i = 0; i < blockcount; i++) {
+		blockaddr = FW_GET_WORD(p);
+		p += 2;
+
+		blocksize = FW_GET_WORD(p);
+		p += 2;
+
+		bi.wSize = cpu_to_le16(blocksize);
+		bi.wAddress = cpu_to_le16(blockaddr);
+		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))
+			goto bad2;
+
+		/* send block data through the IDMA pipe */
+		if (uea_idma_write(sc, p, blocksize))
+			goto bad2;
+
+		p += blocksize;
+	}
+
+	return;
+
+      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);
+}
+
+static inline void wake_up_cmv_ack(struct uea_softc *sc)
+{
+	sc->cmv_ack = 1;
+	wake_up(&sc->cmv_ack_wait);
+}
+
+static inline int wait_cmv_ack(struct uea_softc *sc)
+{
+	int ret = wait_event_timeout(sc->cmv_ack_wait,
+						   sc->cmv_ack, ACK_TIMEOUT);
+	sc->cmv_ack = 0;
+
+	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)
+{
+	u8 *xfer_buff;
+	int ret = -ENOMEM;
+
+	xfer_buff = kmalloc(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,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      value, index, xfer_buff, size, CTRL_TIMEOUT);
+
+	kfree(xfer_buff);
+	if (ret < 0) {
+		uea_err(INS_TO_USBDEV(sc), "usb_control_msg error %d\n", ret);
+		return ret;
+	}
+
+	if (ret != size) {
+		uea_err(INS_TO_USBDEV(sc),
+		       "usb_control_msg send only %d bytes (instead of %d)\n",
+		       ret, size);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int uea_cmv(struct uea_softc *sc, 
+		u8 function, u32 address, u16 offset, u32 data)
+{
+	cmv_t cmv;
+	int ret;
+
+	/* 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;
+
+	cmv.wPreamble = cpu_to_le16(PREAMBLE);
+	cmv.bDirection = HOSTTOMODEM;
+	cmv.bFunction = function;
+	cmv.wIndex = cpu_to_le16(sc->cmv_idx);
+	put_unaligned(cpu_to_le32(address), &cmv.dwSymbolicAddress);
+	cmv.wOffsetAddress = cpu_to_le16(offset);
+	put_unaligned(cpu_to_le32(data >> 16 | data << 16), &cmv.dwData);
+
+	ret = uea_request(sc, UEA_SET_BLOCK, UEA_MPTX_START, CMV_SIZE, &cmv);
+	if (ret < 0)
+		return ret;
+	return wait_cmv_ack(sc);
+}
+
+static inline int uea_read_cmv(struct uea_softc *sc, 
+		u32 address, u16 offset, u32 *data)
+{
+	int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, 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;
+
+	return ret;
+}
+
+static inline int uea_write_cmv(struct uea_softc *sc, 
+		u32 address, u16 offset, u32 data)
+{
+	int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTWRITE),
+			  address, offset, data);
+	if (ret < 0)
+		uea_err(INS_TO_USBDEV(sc), 
+			"writing cmv failed with error %d\n", ret); 
+
+	return ret;
+}
+
+/* 
+ * 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)
+{
+	u32 data;
+	int ret;
+
+	uea_enters(INS_TO_USBDEV(sc));
+	data = sc->stats.phy.state;
+	
+	ret = uea_read_cmv(sc, SA_STAT, 0, &sc->stats.phy.state);
+	if (ret < 0)
+		return ret;
+
+	switch (GET_STATUS(sc->stats.phy.state)) {
+	case 0:		/* not yet synchronized */
+		uea_dbg(INS_TO_USBDEV(sc),
+		       "modem not yet synchronized\n");
+		return 0;
+	
+	case 1:		/* initialization */
+		uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n");
+		return 0;
+
+	case 2:		/* operational */
+		uea_vdbg(INS_TO_USBDEV(sc), "modem operational\n");
+		break;
+
+	case 3:		/* fail ... */
+		uea_info(INS_TO_USBDEV(sc), "modem synchronization failed\n");
+		return -EAGAIN;
+
+	case 4 ... 6:	/* test state */
+		uea_warn(INS_TO_USBDEV(sc), 
+				"modem in test mode - not supported\n");
+		return -EAGAIN;
+
+	case 7:		/* fast-retain ... */
+		uea_info(INS_TO_USBDEV(sc), "modem in fast-retain mode\n");
+		return 0;
+	default:
+		uea_err(INS_TO_USBDEV(sc), "modem invalid SW mode %d\n", 
+			GET_STATUS(sc->stats.phy.state));
+		return -EAGAIN;
+	}
+	
+	if (GET_STATUS(data) != 2) {
+		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
+		 */
+		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);
+	}
+	
+	/* always update it as atm layer could not be init when we switch to
+	 * operational state
+	 */
+	UPDATE_ATM_STAT(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);
+	if (ret < 0)
+		return ret;
+	sc->stats.phy.mflags |= sc->stats.phy.flags;
+
+#if 0
+	if (sc->data & 0x10) {
+		DPRINTF(("delineation LOSS\n"));
+		return -EAGAIN;
+	}
+#else
+	/* in case of a flags (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 = %d\n",
+		       sc->stats.phy.flags);
+		return 0;
+	}
+#endif
+
+	ret = uea_read_cmv(sc, 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->usbatm->driver->in == UEA_BULK_DATA_PIPE
+		    && sc->stats.phy.dsrate != (data >> 16) * 32) {
+		/* Original timming from ADI(used in windows driver)
+		 * 0x20ffff>>16 * 32 = 32 * 32 = 1Mbits
+		 */
+		int 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":"");
+	}
+	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);
+	if (ret < 0)
+		return ret;
+	sc->stats.phy.dsattenuation = (data & 0xff) / 2;
+
+	ret = uea_read_cmv(sc, 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);
+	if (ret < 0)
+		return ret;
+	
+	ret = uea_read_cmv(sc, SA_DIAG, 49, &sc->stats.phy.usmargin);
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, SA_DIAG, 51, &sc->stats.phy.rxflow);
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, SA_DIAG, 52, &sc->stats.phy.txflow);
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, 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); 
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, 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);
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, SA_INFO, 8, &sc->stats.phy.vidco);
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, SA_INFO, 13, &sc->stats.phy.vidcpe);
+	if (ret < 0)
+		return ret;
+	
+	ret = uea_read_cmv(sc, SA_INFO, 17, &data);
+	if (ret < 0)
+		return ret;
+	sc->stats.phy.viditu = 
+		data | (sc->stats.phy.viditu & 0xffffffff00000000ULL);
+	
+	ret = uea_read_cmv(sc, SA_INFO, 18, &data);
+	if (ret < 0)
+		return ret;
+	sc->stats.phy.viditu = 
+		((u64) (data) << 32) | (sc->stats.phy.viditu & 0xffffffffULL);
+
+	return 0;
+}
+
+static int request_cmvs(struct uea_softc *sc,
+		 struct uea_cmvs **cmvs, const struct firmware **fw)
+{
+	int ret, size;
+	u8 *data;
+	char *file;
+	char cmv_name[256] = FW_DIR;
+	
+	if (cmv_file[sc->modem_index] == NULL) {
+		if (UEA_CHIP_VERSION(sc) == ADI930) 
+			file = (IS_ISDN(sc)) ? "CMV9i.bin" : "CMV9p.bin";
+		else 
+			file = (IS_ISDN(sc)) ? "CMVei.bin" : "CMVep.bin";
+	} else
+		file = cmv_file[sc->modem_index];
+
+	strlcat(cmv_name, file, sizeof(cmv_name));
+	
+	ret = sleepy_request_firmware(fw, cmv_name, &sc->usb_dev->dev);
+	if (ret < 0) {
+		uea_err(INS_TO_USBDEV(sc),
+		       "requesting firmware %s failed with error %d\n",
+		       cmv_name, ret);
+		return ret;
+	}
+
+	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;
+	}
+
+	*cmvs = (struct uea_cmvs *)(data + 1);
+	return *data;
+}
+
+/* Start boot post firmware modem:
+ * - send reset commands through usb control pipe
+ * - start workqueue for DSP loading
+ * - send CMV options to modem 
+ */
+
+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;
+
+	uea_enters(INS_TO_USBDEV(sc));
+	uea_info(INS_TO_USBDEV(sc), "(re)booting started\n");
+
+	sc->booting = 1;
+	UPDATE_ATM_STAT(signal, ATM_PHY_SIG_LOST);
+
+	/* reset statistics */
+	memset(&sc->stats, 0, sizeof(struct uea_stats));
+
+	/* tell the modem that we want to boot in IDMA mode */
+	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 */
+	uea_request(sc, UEA_SET_MODE, UEA_START_RESET, 0, NULL);
+
+	/* original driver use 200ms, but windows driver use 100ms */
+	msleep(100);
+
+	/* 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);
+
+	msleep(1000);
+	sc->cmv_function = MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY);
+	sc->booting = 0;
+
+	/* start loading DSP */
+	sc->pageno = 0;
+	sc->ovl = 0;
+	schedule_work(&sc->task);
+
+	/* wait for modem ready CMV */
+	ret = wait_cmv_ack(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;
+
+	/* get options */
+ 	ret = len = request_cmvs(sc, &cmvs, &cmvs_fw);
+	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;
+}
+
+/*
+ * In case of an error wait 1s before rebooting the modem
+ * if the modem don't request reboot (-EAGAIN).
+ * Monitor the modem every 1s.
+ */
+
+static int uea_kthread(void *data)
+{
+	struct uea_softc *sc = (struct uea_softc *)data;
+	int ret = -EAGAIN;
+	
+	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);
+		if (ret != -EAGAIN)
+			msleep(1000);
+	}
+	uea_leaves(INS_TO_USBDEV(sc));
+	return ret;
+}
+
+/* Load second usb firmware for ADI930 chip */
+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";
+#if 0
+	u32 crc = 0;
+#endif
+
+	uea_enters(INS_TO_USBDEV(sc));
+
+	ret = request_firmware(&fw_entry, fw_name, &sc->usb_dev->dev);
+	if (ret) {
+		uea_err(INS_TO_USBDEV(sc), "firmware %s is not available\n",
+		       fw_name);
+		goto err0;
+	}
+
+	pfw = fw_entry->data;
+	size = fw_entry->size;
+#if 0
+	crc = FW_GET_LONG(pfw);
+	pfw += 4;
+	size -= 4;
+
+	if (crc32_be(0, pfw, size) != crc) {
+#else
+	if (size != 0x577B) {
+#endif
+		uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
+		       fw_name);
+		ret = -EILSEQ;
+		goto err1;
+	}
+	for (u = 0; u < size; u += ln) {
+		ln = min(size - u, 64);
+		ret = uea_request(sc, 0xe, 0, ln, pfw + u);
+		if (ret < 0) {
+			uea_err(INS_TO_USBDEV(sc),
+			       "elsa download data failed (%d)\n", ret);
+			goto err1;
+		}
+	}
+
+	/* finish to send the fpga
+	 */
+	ret = uea_request(sc, 0xe, 1, 0, NULL);
+	if (ret < 0) {
+		uea_err(INS_TO_USBDEV(sc),
+				"elsa download data failed (%d)\n", ret);
+		goto err1;
+	}
+	
+	/*
+	 * 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);
+
+
+      err1:
+	release_firmware(fw_entry);
+      err0:
+	uea_leaves(INS_TO_USBDEV(sc));
+	return ret;
+}
+
+static void uea_dispatch_cmv(struct uea_softc *sc, cmv_t * cmv)
+{
+	uea_enters(INS_TO_USBDEV(sc));
+	if (le16_to_cpu(cmv->wPreamble) != PREAMBLE)
+		goto bad1;
+
+	if (cmv->bDirection != MODEMTOHOST)
+		goto bad1;
+
+	/* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to
+	 * the first MEMACESS cmv. Ignore it...
+	 */
+	if (cmv->bFunction != sc->cmv_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
+			goto bad2;
+	}
+
+	if (cmv->bFunction == MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY)) {
+		wake_up_cmv_ack(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)
+		goto bad2;
+
+	sc->data = le32_to_cpu(get_unaligned(&cmv->dwData));
+	sc->data = sc->data << 16 | sc->data >> 16;
+
+	wake_up_cmv_ack(sc);
+	return;
+
+      bad2:
+	uea_err(INS_TO_USBDEV(sc), "unexpected cmv received,"
+			"Function : %d, Subfunction : %d\n",
+			FUNCTION_TYPE(cmv->bFunction), 
+			FUNCTION_SUBTYPE(cmv->bFunction));
+	return;
+
+      bad1:
+	uea_err(INS_TO_USBDEV(sc), "invalid cmv received, "
+			"wPreamble %d, bDirection %d\n", 
+			le16_to_cpu(cmv->wPreamble), cmv->bDirection);
+}
+
+/* 
+ * interrupt handler
+ */
+static void uea_intr(struct urb *urb, struct pt_regs *regs)
+{
+	struct uea_softc *sc = (struct uea_softc *)urb->context;
+	intr_pkt_t *intr;
+	uea_enters(INS_TO_USBDEV(sc));
+
+	if (urb->status < 0) {
+		uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n",
+		       urb->status);
+		return;
+	}
+
+	intr = (intr_pkt_t *) 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;
+		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);
+		break;
+
+	case INT_INCOMINGCMV:
+		uea_dispatch_cmv(sc, &intr->u.s2.cmv);
+		break;
+
+	default:
+		uea_err(INS_TO_USBDEV(sc), "unknown intr %u\n",
+		       le16_to_cpu(intr->wInterrupt));
+	}
+
+      resubmit:
+	usb_submit_urb(sc->urb_int, GFP_ATOMIC);
+}
+
+/*
+ * Start the modem : init the data and start kernel thread
+ */
+static int uea_boot(struct uea_softc *sc)
+{
+	int ret;
+	intr_pkt_t *intr;
+
+	uea_enters(INS_TO_USBDEV(sc));
+
+	INIT_WORK(&sc->task, uea_load_page, sc);
+	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);
+	if (!intr) {
+		uea_err(INS_TO_USBDEV(sc),
+		       "cannot allocate interrupt package\n");
+		uea_leaves(INS_TO_USBDEV(sc));
+		return -ENOMEM;
+	}
+
+	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;
+	}
+
+	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,
+			 sc->usb_dev->actconfig->interface[0]->altsetting[0].
+			 endpoint[0].desc.bInterval);
+
+	ret = usb_submit_urb(sc->urb_int, GFP_KERNEL);
+	if (ret < 0) {
+		uea_err(INS_TO_USBDEV(sc),
+		       "urb submition failed with error %d\n", ret);
+		goto err1;
+	}
+
+	sc->kthread = kthread_run(uea_kthread, sc, "ueagle-atm");
+	if (sc->kthread == ERR_PTR(-ENOMEM)) {
+		uea_err(INS_TO_USBDEV(sc), "failed to create thread\n");
+		goto err2;
+	}
+
+	uea_leaves(INS_TO_USBDEV(sc));
+	return 0;
+
+      err2:
+	usb_kill_urb(sc->urb_int);
+      err1:
+	kfree(intr);
+      err:
+	usb_free_urb(sc->urb_int);
+	uea_leaves(INS_TO_USBDEV(sc));
+	return -ENOMEM;
+}
+
+/*
+ * Stop the modem : kill kernel thread and free data
+ */
+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_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL);
+
+	usb_kill_urb(sc->urb_int);
+	kfree(sc->urb_int->transfer_buffer);
+	usb_free_urb(sc->urb_int);
+
+	if (sc->dsp_firm)
+		release_firmware(sc->dsp_firm);
+	uea_leaves(INS_TO_USBDEV(sc));
+}
+
+
+/* syfs interface */
+static struct uea_softc *dev_to_uea(struct device *dev)
+{
+	struct usb_interface *intf;
+	struct usbatm_data *usbatm;
+
+	intf = to_usb_interface(dev);
+	if (!intf)
+		return NULL;
+
+	usbatm = usb_get_intfdata(intf);
+	if (!usbatm)
+		return NULL;
+
+	return (struct uea_softc *)usbatm->driver_data;
+}
+
+/* we need to use semaphore until sysfs and removable devices is fixed
+ * the problem is explained on http://marc.theaimsgroup.com/?t=112006484100003
+ */
+static ssize_t read_status(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	int ret = -ENODEV;
+	struct uea_softc *sc;
+	
+	down(&uea_semaphore);
+	sc = dev_to_uea(dev);
+	if (!sc)
+		goto out;
+	ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.state);
+out:
+	up(&uea_semaphore);
+	return ret;
+}
+
+static ssize_t reboot(struct device *dev, struct device_attribute *attr, 
+		const char *buf, size_t count)
+{
+	int ret = -ENODEV;
+	struct uea_softc *sc;
+
+	down(&uea_semaphore);
+	sc = dev_to_uea(dev);
+	if (!sc)
+		goto out;
+	sc->reset = 1;
+	ret = count;
+out:
+	up(&uea_semaphore);
+	return ret;
+}
+
+static DEVICE_ATTR(status, S_IWUGO | S_IRUGO, read_status, reboot);
+
+#define UEA_ATTR(name, reset) 					\
+								\
+static ssize_t read_##name(struct device *dev, 			\
+		struct device_attribute *attr, char *buf)	\
+{ 								\
+	int ret = -ENODEV; 					\
+	struct uea_softc *sc; 					\
+ 								\
+	down(&uea_semaphore); 					\
+	sc = dev_to_uea(dev);					\
+	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; 						\
+} 								\
+								\
+static DEVICE_ATTR(name, S_IRUGO, read_##name, NULL)
+
+UEA_ATTR(mflags, 1);
+
+/* 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];
+	int i;
+	if (usb_string
+	    (sc->usb_dev, sc->usb_dev->descriptor.iSerialNumber, mac_str,
+	     sizeof(mac_str)) != 2 * ETH_ALEN)
+		return 1;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		esi[i] = htoi(mac_str[2 * i]) * 16 + htoi(mac_str[2 * i + 1]);
+
+	return 0;
+}
+
+/* ATM stuff */
+static int uea_atm_open(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
+{
+	struct uea_softc *sc = (struct uea_softc *)usbatm->driver_data;
+
+	return uea_getesi(sc, atm_dev->esi);
+}
+
+static int uea_heavy(struct usbatm_data *usbatm, struct usb_interface *intf)
+{
+	struct uea_softc *sc = (struct uea_softc *)usbatm->driver_data;
+
+	wait_event(sc->sync_q, IS_OPERATIONAL(sc));
+
+	return 0;
+
+}
+
+static int claim_interface(struct usb_device *usb_dev,
+			   struct usbatm_data *usbatm, int ifnum)
+{
+	int ret;
+	struct usb_interface *intf = usb_ifnum_to_if(usb_dev, ifnum);
+
+	if (!intf) {
+		uea_err(usb_dev, "interface %d not found\n", ifnum);
+		return -ENODEV;
+	}
+
+	ret = usb_driver_claim_interface(&uea_driver, intf, usbatm);
+	if (ret != 0)
+		uea_err(usb_dev, "can't claim interface %d, error %d\n", ifnum,
+		       ret);
+	return ret;
+}
+
+static void create_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+{
+	char path[32];
+	struct proc_dir_entry *pde;
+
+	/* procfs interface */
+	if (uea_procdir) {
+		int busnum = sc->usb_dev->bus->busnum;
+		int devnum = sc->usb_dev->devnum;
+
+		snprintf(path, 32, "%03d-%03d", busnum, devnum);
+		pde = create_proc_read_entry(path,
+					     0, uea_procdir, uea_read_proc, sc);
+
+		if (pde) {
+			pde->owner = THIS_MODULE;
+			uea_info(INS_TO_USBDEV(sc), 
+					"created proc entry at: "
+					"/proc/driver/ueagle-atm/%s\n", path);
+		} else
+			uea_err(INS_TO_USBDEV(sc), 
+					"can't create proc entry at: "
+					"/proc/driver/ueagle-atm/%s\n", path);
+	}
+
+	/* sysfs interface */
+	device_create_file(&intf->dev, &dev_attr_status);
+	device_create_file(&intf->dev, &dev_attr_mflags);
+}
+
+static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
+		   const struct usb_device_id *id, int *heavy)
+{
+	struct usb_device *usb = interface_to_usbdev(intf);
+	struct uea_softc *sc;
+	int ret, ifnum = intf->altsetting->desc.bInterfaceNumber;
+
+	uea_enters(usb);
+
+	/* interface 0 is for firmware/monitoring */
+	if (ifnum != UEA_INTR_IFACE_NO)
+		return -ENODEV;
+
+	*heavy = sync_wait[modem_index];
+
+	/* 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
+	 */
+	if (UEA_CHIP_VERSION(id) != ADI930) {
+		/* interface 2 is for inbound traffic */
+		ret = claim_interface(usb, usbatm, UEA_DS_IFACE_NO);
+		if (ret < 0)
+			return ret;
+	}
+
+	sc = kcalloc(1, sizeof(struct uea_softc), GFP_KERNEL);
+	if (!sc) {
+		uea_err(INS_TO_USBDEV(sc), "uea_init: not enough memory !\n");
+		return -ENOMEM;
+	}
+
+	sc->usb_dev = usb;
+	usbatm->driver_data = sc;
+	sc->usbatm = usbatm;
+	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;
+	}
+ 
+	create_fs_entries(sc, intf);
+	return 0;
+}
+
+static void destroy_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+{
+	char path[32];
+
+	/* procfs interface */
+	if (uea_procdir) {
+		int busnum = sc->usb_dev->bus->busnum;
+		int devnum = sc->usb_dev->devnum;
+
+		snprintf(path, 32, "%03d-%03d", busnum, devnum);
+		remove_proc_entry(path, uea_procdir);
+	}
+
+	/* sysfs interface */
+	device_remove_file(&intf->dev, &dev_attr_status);
+	device_remove_file(&intf->dev, &dev_attr_mflags);
+}
+
+static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
+{
+	struct uea_softc *sc = (struct uea_softc *)usbatm->driver_data;
+
+	destroy_fs_entries(sc, intf);
+	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,
+};
+
+static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *usb = interface_to_usbdev(intf);
+
+	uea_enters(usb);
+	uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) \n",
+	       le16_to_cpu(usb->descriptor.idVendor),
+	       le16_to_cpu(usb->descriptor.idProduct));
+
+	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);
+}
+
+static void uea_disconnect(struct usb_interface *intf)
+{
+	struct usb_device *usb = interface_to_usbdev(intf);
+	int ifnum = intf->altsetting->desc.bInterfaceNumber;
+	uea_enters(usb);
+
+	/* ADI930 has 2 interfaces and eagle 3 interfaces.
+	 * Pre-firmware device has one interface
+	 */
+	if (usb->config->desc.bNumInterfaces != 1 && ifnum == 0) {
+		down(&uea_semaphore);
+		usbatm_usb_disconnect(intf);
+		up(&uea_semaphore);
+		uea_info(usb, "ADSL device removed\n");
+	}
+
+	uea_leaves(usb);
+}
+
+/**
+ * uea_read_proc : /proc information
+ */
+static int uea_read_proc(char *page, 
+		char **start, off_t off, int count, int *eof, void *data)
+{
+	struct uea_softc *sc = (struct uea_softc *)data;
+	char *p = page;
+
+	/*
+	 * If this output needs to be larger than 4K (PAGE_SIZE), 
+	 * we need to do this differently
+	 */
+
+	*start = NULL;
+	*eof = 1;
+	if (off != 0)
+		return 0;
+
+	p += sprintf(p, "ueagle-atm status display\n");
+	p += sprintf(p,
+		     "-------------------------------------------------------------\n");
+	p += sprintf(p, "Driver version: %s     Chipset: %s\n",
+		     EAGLEUSBVERSION, chip_name[UEA_CHIP_VERSION(sc) % 4]);
+
+	if (sc->usb_dev != NULL) {
+		p += sprintf(p,
+			     "Vendor ID : 0x%x     Product ID : 0x%x   Rev: 0x%x(%s)\n",
+			     le16_to_cpu(sc->usb_dev->descriptor.idVendor),
+			     le16_to_cpu(sc->usb_dev->descriptor.idProduct),
+			     le16_to_cpu(sc->usb_dev->descriptor.bcdDevice),
+			     IS_ISDN(sc)? "isdn" : "pots");
+		p += sprintf(p,
+			     "USB Bus : %03d\t USB Device : %03d\t Dbg : %d\n",
+			     sc->usb_dev->bus->busnum, sc->usb_dev->devnum,
+			     debug);
+	}
+	p += sprintf(p, "VID-CPE   %10d    VID-ITU   %llx\n\n",
+		     sc->stats.phy.vidcpe, sc->stats.phy.viditu);
+
+	p += sprintf(p, "Tx Rate   %10d Kps Rx Rate   %10d Kps\n",
+		     sc->stats.phy.usrate, sc->stats.phy.dsrate);
+	p += sprintf(p, "Tx Atten  %10d dB Rx Atten  %10d dB\n",
+		     sc->stats.phy.usattenuation, sc->stats.phy.dsattenuation);
+	p += sprintf(p, "Tx Margin %10d dB Rx Margin %10d dB\n",
+		     sc->stats.phy.usmargin, sc->stats.phy.dsmargin);
+	p += sprintf(p, "Tx Blocks %10d    Rx Blocks %10d\n",
+		     sc->stats.phy.txflow, sc->stats.phy.rxflow);
+	p += sprintf(p, "Tx FEC    %10d    Rx FEC    %10d\n",
+		     sc->stats.phy.uscorr, sc->stats.phy.dscorr);
+	p += sprintf(p, "Tx Error  %10d    Rx Error  %10d\n",
+		     sc->stats.phy.usunc, sc->stats.phy.dsunc);
+	p += sprintf(p, "Delin          ");
+
+	if (sc->stats.phy.flags & 0x0C00)
+		p += sprintf(p, "ERROR");
+	else if (sc->stats.phy.flags & 0x0030)
+		p += sprintf(p, " LOSS");
+	else
+		p += sprintf(p, " GOOD");
+
+	p += sprintf(p, "    Flags     %10.10x\n", sc->stats.phy.flags);
+	switch (GET_STATUS(sc->stats.phy.state)) {
+	case 0:
+		p += sprintf(p, "Modem is booting\n");
+		break;
+	case 1:
+		p += sprintf(p, "Modem is initializing\n");
+		break;
+	case 2:
+		p += sprintf(p, "Modem is operational\n");
+		break;
+	default:
+		p += sprintf(p, "Modem synchronization failed\n");
+		break;
+	}
+	p += sprintf(p, "\n");
+
+	return p - page;
+}
+
+#define UEA_DEVICE(vid, pid, info) \
+	{USB_DEVICE((vid), (pid)), .driver_info = (info)}
+
+/*
+ * List of supported VID/PID
+ */
+static const struct usb_device_id uea_ids[] = {
+	UEA_DEVICE(ELSA_VID,	ELSA_PID_PREFIRM,	ADI930 | PREFIRM),
+	UEA_DEVICE(ELSA_VID,	ELSA_PID_PSTFIRM,	ADI930 | PSTFIRM),
+	UEA_DEVICE(EAGLE_VID,	EAGLE_I_PID_PREFIRM,	EAGLE_I | PREFIRM),
+	UEA_DEVICE(EAGLE_VID,	EAGLE_I_PID_PSTFIRM,	EAGLE_I | PSTFIRM),
+	UEA_DEVICE(EAGLE_VID,	EAGLE_II_PID_PREFIRM,	EAGLE_II | PREFIRM),
+	UEA_DEVICE(EAGLE_VID,	EAGLE_II_PID_PSTFIRM,	EAGLE_II | PSTFIRM),
+	UEA_DEVICE(EAGLE_VID,	EAGLE_IIC_PID_PREFIRM,	EAGLE_II | PREFIRM),
+	UEA_DEVICE(EAGLE_VID,	EAGLE_IIC_PID_PSTFIRM,	EAGLE_II | PSTFIRM),
+	UEA_DEVICE(EAGLE_VID,	EAGLE_III_PID_PREFIRM,	EAGLE_III | PREFIRM),
+	UEA_DEVICE(EAGLE_VID,	EAGLE_III_PID_PSTFIRM,	EAGLE_III | PSTFIRM),
+	UEA_DEVICE(USR_VID,	MILLER_A_PID_PREFIRM,	EAGLE_I | PREFIRM),
+	UEA_DEVICE(USR_VID,	MILLER_A_PID_PSTFIRM,	EAGLE_I | PSTFIRM),
+	UEA_DEVICE(USR_VID,	MILLER_B_PID_PREFIRM,	EAGLE_I | PREFIRM),
+	UEA_DEVICE(USR_VID,	MILLER_B_PID_PSTFIRM,	EAGLE_I | PSTFIRM),
+	UEA_DEVICE(USR_VID,	HEINEKEN_A_PID_PREFIRM, EAGLE_I | PREFIRM),
+	UEA_DEVICE(USR_VID,	HEINEKEN_A_PID_PSTFIRM, EAGLE_I | PSTFIRM),
+	UEA_DEVICE(USR_VID,	HEINEKEN_B_PID_PREFIRM,	EAGLE_I | PREFIRM),
+	UEA_DEVICE(USR_VID,	HEINEKEN_B_PID_PSTFIRM,	EAGLE_I | PSTFIRM),
+	{}
+};
+
+/*
+ * USB driver descriptor
+ */
+static struct usb_driver uea_driver = {
+	.owner = THIS_MODULE,
+	.name = "ueagle-atm",
+	.id_table = uea_ids,
+	.probe = uea_probe,
+	.disconnect = uea_disconnect,
+};
+
+MODULE_DEVICE_TABLE(usb, uea_ids);
+
+/**
+ * uea_init - Initialize the module.
+ *      Creates /proc/driver/ueagle-atm directory
+ *      Register to USB subsystem
+ */
+static int __init uea_init(void)
+{
+	printk(KERN_INFO "[ueagle-atm] driver " EAGLEUSBVERSION " loaded\n");
+
+	uea_procdir = proc_mkdir("driver/ueagle-atm", NULL);
+	if (uea_procdir)
+		uea_procdir->owner = THIS_MODULE;
+	else
+		printk(KERN_ERR "[UEAGLE-ATM] " 
+				"could not create /proc/driver/ueagle-atm/\n");
+
+	usb_register(&uea_driver);
+
+	return 0;
+}
+
+module_init(uea_init);
+
+/**
+ * uea_exit  -  Destroy module
+ *
+ *    Deregister with USB subsystem
+ *    Remove /proc/drivers/eeagle-atm directory
+ */
+static void __exit uea_exit(void)
+{
+	/*
+	 * This calls automatically the uea_disconnect method if necessary:
+	 */
+	usb_deregister(&uea_driver);
+
+	/* remove non existing proc entries is ok */
+	remove_proc_entry("driver/ueagle-atm", NULL);
+
+	printk(KERN_INFO "[ueagle-atm] driver unloaded\n");
+}
+
+module_exit(uea_exit);
+
+MODULE_AUTHOR("Damien Bergamini/Matthieu Castet/Stanislaw W. Gruszka");
+MODULE_DESCRIPTION("ADI 930/Eagle USB ADSL Modem driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff -rNu -x '*.ko*' -x '*.mod*' -x '*.o*' linux-2.6.14/drivers/usb/atm.old/ueagle-atm.h linux-2.6.14/drivers/usb/atm/ueagle-atm.h
--- linux-2.6.14/drivers/usb/atm.old/ueagle-atm.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.14/drivers/usb/atm/ueagle-atm.h	2005-10-30 00:25:27.000000000 +0200
@@ -0,0 +1,353 @@
+/*-
+ * Copyright (c) 2003, 2004
+ *	Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
+ *
+ * Copyright (c) 2005 Matthieu Castet <castet.matthieu@free.fr>
+ *
+ * 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * GPL license :
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * HISTORY : some part of the code was base on ueagle 1.3 BSD driver,
+ * Damien Bergamini agree to put his code under a DUAL GPL/BSD license.
+ *
+ * The rest of the code was rewriting from stratch.
+ */
+
+#ifndef UEAGLE_ATM_H
+#define UEAGLE_ATM_H
+
+#include "usbatm.h"
+#include <asm/unaligned.h>
+
+/*
+ * Debug macros
+ */
+#define uea_dbg(usb_dev, format, args...)	\
+	do { \
+		if (debug >= 1) \
+			dev_dbg(&(usb_dev)->dev, \
+				"[ueagle-atm dbg] %s: " format, \
+					__FUNCTION__, ##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)
+
+#define uea_enters(usb_dev) \
+	uea_vdbg(usb_dev, "entering %s\n", __FUNCTION__)
+
+#define uea_leaves(usb_dev) \
+	uea_vdbg(usb_dev, "leaving  %s\n", __FUNCTION__)
+
+#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_info(usb_dev, format,args...) \
+	dev_info(&(usb_dev)->dev ,"[ueagle-atm] " format, ##args)
+
+struct uea_cmvs {
+	u32 address;
+	u16 offset;
+	u32 data;
+} __attribute__ ((packed));
+
+struct uea_softc {
+	struct usb_device *usb_dev;
+	struct usbatm_data *usbatm;
+
+	int modem_index;
+	unsigned int driver_info;
+
+	int booting;
+	int reset;
+
+	wait_queue_head_t sync_q;
+
+	struct task_struct *kthread;
+	u32 data;
+	wait_queue_head_t cmv_ack_wait;
+	int cmv_ack;
+
+	struct work_struct task;
+	u16 pageno;
+	u16 ovl;
+
+	const struct firmware *dsp_firm;
+	struct urb *urb_int;
+
+	u8 cmv_function;
+	u16 cmv_idx;
+	u32 cmv_address;
+	u16 cmv_offset;
+
+	/* keep in sync with eaglectl */
+	struct uea_stats {
+		struct {
+			u32 state;
+			u32 flags;
+			u32 mflags;
+			u32 vidcpe;
+			u32 vidco;
+			u32 dsrate;
+			u32 usrate;
+			u32 dsunc;
+			u32 usunc;
+			u32 dscorr;
+			u32 uscorr;
+			u32 txflow;
+			u32 rxflow;
+			u32 usattenuation;
+			u32 dsattenuation;
+			u32 dsmargin;
+			u32 usmargin;
+			u64 viditu;
+			u32 firmid;
+		} phy;
+	} stats;
+};
+
+/*
+ * Elsa IDs
+ */
+#define ELSA_VID		0x05CC
+#define ELSA_PID_PSTFIRM	0x3350
+#define ELSA_PID_PREFIRM	0x3351
+
+/*
+ * Sagem USB IDs
+ */
+#define EAGLE_VID		0x1110
+#define EAGLE_I_PID_PREFIRM	0x9010	/* Eagle I */
+#define EAGLE_I_PID_PSTFIRM	0x900F	/* Eagle I */
+
+#define EAGLE_IIC_PID_PREFIRM	0x9024	/* Eagle IIC */
+#define EAGLE_IIC_PID_PSTFIRM	0x9023	/* Eagle IIC */
+
+#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 */
+
+/*
+ * USR USB IDs
+ */
+#define USR_VID			0x0BAF
+#define MILLER_A_PID_PREFIRM	0x00F2
+#define MILLER_A_PID_PSTFIRM	0x00F1
+#define MILLER_B_PID_PREFIRM	0x00FA
+#define MILLER_B_PID_PSTFIRM	0x00F9
+#define HEINEKEN_A_PID_PREFIRM	0x00F6
+#define HEINEKEN_A_PID_PSTFIRM	0x00F5
+#define HEINEKEN_B_PID_PREFIRM	0x00F8
+#define HEINEKEN_B_PID_PSTFIRM	0x00F7
+
+#define PREFIRM 0
+#define PSTFIRM (1<<7)
+enum {
+	ADI930 = 0,
+	EAGLE_I,
+	EAGLE_II,
+	EAGLE_III
+};
+
+/* macros for both struct usb_device_id and struct uea_softc */
+#define UEA_IS_PREFIRM(x) \
+	(!((x)->driver_info & PSTFIRM))
+#define UEA_CHIP_VERSION(x) \
+	((x)->driver_info & 0xf)
+
+#define IS_ISDN(sc) \
+	(le16_to_cpu(sc->usb_dev->descriptor.bcdDevice) & 0x80)
+
+#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)
+
+/* 
+ * 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_DIR "ueagle-atm/"
+#define NB_MODEM 4
+
+#define BULK_TIMEOUT 300
+#define CTRL_TIMEOUT 1000
+
+#define ACK_TIMEOUT msecs_to_jiffies(1500)
+
+#define UEA_INTR_IFACE_NO 	0
+#define UEA_US_IFACE_NO		1
+#define UEA_DS_IFACE_NO		2
+
+#define FASTEST_ISO_INTF	8
+
+#define UEA_BULK_DATA_PIPE	0x02
+#define UEA_IDMA_PIPE		0x04
+#define UEA_INTR_PIPE		0x04
+#define UEA_ISO_DATA_PIPE	0x08
+
+#define UEA_SET_BLOCK    	0x0001
+#define UEA_SET_MODE     	0x0003
+#define UEA_SET_2183_DATA	0x0004
+#define UEA_SET_TIMEOUT		0x0011
+
+#define UEA_LOOPBACK_OFF	0x0002
+#define UEA_LOOPBACK_ON		0x0003
+#define UEA_BOOT_IDMA		0x0006
+#define UEA_START_RESET		0x0007
+#define UEA_END_RESET		0x0008
+
+#define UEA_SWAP_MAILBOX	(0x3fcd | 0x4000)
+#define UEA_MPTX_START		(0x3fce | 0x4000)
+#define UEA_MPTX_MAILBOX	(0x3fd6 | 0x4000)
+#define UEA_MPRX_MAILBOX	(0x3fdf | 0x4000)
+
+#define PACKED __attribute__ ((packed))
+
+/* structure describing a block within a DSP page */
+typedef struct {
+	__le16 wHdr;
+#define UEA_BIHDR 0xabcd
+	__le16 wAddress;
+	__le16 wSize;
+	__le16 wOvlOffset;
+	__le16 wOvl;		/* overlay */
+	__le16 wLast;
+} PACKED block_info_t;
+#define BLOCK_INFO_SIZE 12
+
+/* structure representing a CMV (Configuration and Management Variable) */
+typedef struct {
+	__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 FUNCTION_SUBTYPE(f) ((f) & 0x0f)
+/* for MEMACCESS */
+#define REQUESTREAD	0x0
+#define REQUESTWRITE	0x1
+#define REPLYREAD	0x2
+#define REPLYWRITE	0x3
+/* for ADSLDIRECTIVE */
+#define KERNELREADY	0x0
+#define MODEMREADY	0x1
+
+#define MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf))
+	__le16 wIndex;
+	__le32 dwSymbolicAddress;
+#define 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')
+	__le16 wOffsetAddress;
+	__le32 dwData;
+} PACKED cmv_t;
+#define CMV_SIZE 16
+
+/* structure representing swap information */
+typedef struct {
+	__u8 bSwapPageNo;
+	__u8 bOvl;		/* overlay */
+} PACKED swap_info_t;
+
+/* structure representing interrupt data */
+typedef struct {
+	__u8 bType;
+	__u8 bNotification;
+	__le16 wValue;
+	__le16 wIndex;
+	__le16 wLength;
+	__le16 wInterrupt;
+#define INT_LOADSWAPPAGE 0x0001
+#define INT_INCOMINGCMV  0x0002
+	union {
+		struct {
+			swap_info_t swapinfo;
+			__le16 wDataSize;
+		} PACKED s1;
+
+		struct {
+			cmv_t cmv;
+			__le16 wDataSize;
+		} PACKED s2;
+	} PACKED u;
+#define bSwapPageNo	u.s1.swapinfo.bSwapPageNo
+#define bOvl		u.s1.swapinfo.bOvl
+} PACKED intr_pkt_t;
+#define INTR_PKT_SIZE 28
+
+#endif /* UEAGLE_ATM_H */

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-10-29 22:37 [PATCH] Eagle and ADI 930 usb adsl modem driver matthieu castet
@ 2005-10-31 23:58 ` Andrew Morton
  2005-11-01 12:40   ` Duncan Sands
  2005-11-01 14:08   ` matthieu castet
  2005-11-01 22:45 ` Greg KH
  1 sibling, 2 replies; 34+ messages in thread
From: Andrew Morton @ 2005-10-31 23:58 UTC (permalink / raw)
  To: matthieu castet; +Cc: linux-usb-devel, usbatm, linux-kernel

matthieu castet <castet.matthieu@free.fr> wrote:
>
> Hi,
> 
> attached is the driver for USB ADSL modems based on the ADI eagle
> chipset using the usb_atm infrastructure.
> 

Dunno much about USB drivers, but..

> +/*
> + * sometime hotplug don't have time to give the firmware the
> + * first time, retry it.
> + */
> +static int sleepy_request_firmware(const struct firmware **fw, 
> +		const char *name, struct device *dev)
> +{
> +	if (request_firmware(fw, name, dev) == 0)
> +		return 0;
> +	msleep(1000);
> +	return request_firmware(fw, name, dev);
> +}

egad.   Is there no better way?

> ...
> +static int request_cmvs(struct uea_softc *sc,
> +		 struct uea_cmvs **cmvs, const struct firmware **fw)
> +{
> +	int ret, size;
> +	u8 *data;
> +	char *file;
> +	char cmv_name[256] = FW_DIR;

That's rather a lot of stack.  Can this be made static, of kmalloced?

> +
> +	*cmvs = (struct uea_cmvs *)(data + 1);

That's a bit rude - asking the compiler to perform a structure copy from an
odd address.  memcpy() would be saner.


> ...
> +static int uea_kthread(void *data)
> +{
> +	struct uea_softc *sc = (struct uea_softc *)data;

Unneeded cast.

> ...
> +/**
> + * uea_read_proc : /proc information
> + */
> +static int uea_read_proc(char *page, 
> +		char **start, off_t off, int count, int *eof, void *data)

People get shouted at for adding /proc handlers.  Greg may have thoughts...


General: the patch adds tons of trailing whitespace.  I stripped that off
the patch I added to -mm.


Trivial fixups:


 drivers/usb/atm/ueagle-atm.c |   31 ++++++++++++++-----------------
 1 files changed, 14 insertions(+), 17 deletions(-)

diff -puN drivers/usb/atm/ueagle-atm.c~eagle-and-adi-930-usb-adsl-modem-driver-tidies drivers/usb/atm/ueagle-atm.c
--- 25/drivers/usb/atm/ueagle-atm.c~eagle-and-adi-930-usb-adsl-modem-driver-tidies	Mon Oct 31 15:56:36 2005
+++ 25-akpm/drivers/usb/atm/ueagle-atm.c	Mon Oct 31 15:56:36 2005
@@ -74,17 +74,17 @@ static int uea_read_proc(char *page, cha
 
 static struct usb_driver uea_driver;
 static struct proc_dir_entry *uea_procdir;
-DECLARE_MUTEX(uea_semaphore);
+static DECLARE_MUTEX(uea_semaphore);
 static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III"};
 
 /*
  * User supplied debug level
  */
-static unsigned int debug = 0;
+static unsigned int debug;
 
-static int modem_index = 0;
-static int sync_wait[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = 0 };
-static char *cmv_file[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = NULL };
+static int modem_index;
+static int sync_wait[NB_MODEM];
+static char *cmv_file[NB_MODEM];
 
 module_param(debug, uint, 0644);
 MODULE_PARM_DESC(debug, "module debug level (0=off,1=on,2=verbose)");
@@ -314,15 +314,13 @@ static int uea_idma_write(struct uea_sof
 
 	memcpy(xfer_buff, data, size);
 
-	ret =
-	    usb_bulk_msg(sc->usb_dev,
+	ret = usb_bulk_msg(sc->usb_dev,
 			 usb_sndbulkpipe(sc->usb_dev, UEA_IDMA_PIPE),
 			 xfer_buff, size, &bytes_read, BULK_TIMEOUT);
 
 	kfree(xfer_buff);
-	if (ret < 0) {
+	if (ret < 0)
 		return ret;
-	}
 	if (size != bytes_read) {
 		uea_err(INS_TO_USBDEV(sc), "size != bytes_read %d %d\n", size,
 		       bytes_read);
@@ -873,7 +871,7 @@ out:
 
 static int uea_kthread(void *data)
 {
-	struct uea_softc *sc = (struct uea_softc *)data;
+	struct uea_softc *sc = data;
 	int ret = -EAGAIN;
 
 	uea_enters(INS_TO_USBDEV(sc));
@@ -1135,7 +1133,7 @@ static void uea_stop(struct uea_softc *s
 	/* stop any pending boot process */
 	flush_scheduled_work();
 
-     	uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL);
+	uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL);
 
 	usb_kill_urb(sc->urb_int);
 	kfree(sc->urb_int->transfer_buffer);
@@ -1146,7 +1144,6 @@ static void uea_stop(struct uea_softc *s
 	uea_leaves(INS_TO_USBDEV(sc));
 }
 
-
 /* syfs interface */
 static struct uea_softc *dev_to_uea(struct device *dev)
 {
@@ -1161,7 +1158,7 @@ static struct uea_softc *dev_to_uea(stru
 	if (!usbatm)
 		return NULL;
 
-	return (struct uea_softc *)usbatm->driver_data;
+	return usbatm->driver_data;
 }
 
 /* we need to use semaphore until sysfs and removable devices is fixed
@@ -1247,14 +1244,14 @@ static int uea_getesi(struct uea_softc *
 /* ATM stuff */
 static int uea_atm_open(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
 {
-	struct uea_softc *sc = (struct uea_softc *)usbatm->driver_data;
+	struct uea_softc *sc = usbatm->driver_data;
 
 	return uea_getesi(sc, atm_dev->esi);
 }
 
 static int uea_heavy(struct usbatm_data *usbatm, struct usb_interface *intf)
 {
-	struct uea_softc *sc = (struct uea_softc *)usbatm->driver_data;
+	struct uea_softc *sc = usbatm->driver_data;
 
 	wait_event(sc->sync_q, IS_OPERATIONAL(sc));
 
@@ -1340,7 +1337,7 @@ static int uea_bind(struct usbatm_data *
 			return ret;
 	}
 
-	sc = kcalloc(1, sizeof(struct uea_softc), GFP_KERNEL);
+	sc = kzalloc(sizeof(struct uea_softc), GFP_KERNEL);
 	if (!sc) {
 		uea_err(INS_TO_USBDEV(sc), "uea_init: not enough memory !\n");
 		return -ENOMEM;
@@ -1382,7 +1379,7 @@ static void destroy_fs_entries(struct ue
 
 static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
 {
-	struct uea_softc *sc = (struct uea_softc *)usbatm->driver_data;
+	struct uea_softc *sc = usbatm->driver_data;
 
 	destroy_fs_entries(sc, intf);
 	uea_stop(sc);
_


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-10-31 23:58 ` Andrew Morton
@ 2005-11-01 12:40   ` Duncan Sands
  2005-11-01 13:04     ` David Woodhouse
  2005-11-01 13:49     ` matthieu castet
  2005-11-01 14:08   ` matthieu castet
  1 sibling, 2 replies; 34+ messages in thread
From: Duncan Sands @ 2005-11-01 12:40 UTC (permalink / raw)
  To: Andrew Morton; +Cc: matthieu castet, linux-usb-devel, usbatm, linux-kernel

Hi Andrew,

> > +/*
> > + * sometime hotplug don't have time to give the firmware the
> > + * first time, retry it.
> > + */
> > +static int sleepy_request_firmware(const struct firmware **fw, 
> > +		const char *name, struct device *dev)
> > +{
> > +	if (request_firmware(fw, name, dev) == 0)
> > +		return 0;
> > +	msleep(1000);
> > +	return request_firmware(fw, name, dev);
> > +}
> 
> egad.   Is there no better way?

this code looks like a 'orrible hack to work around a common problem
with USB modem's of this type: if the modem is plugged in while the
system boots, the driver may look for firmware before the filesystem
holding the firmware is mounted; I guess the delay usually gives
the filesystem enough time to be mounted.  I'm told that the correct
solution is to stick the firmware in an initramfs as well.  That's a
pity: it would be nice if users could just dump the firmware in an
appropriate directory and have everything work [*].  As it is, they
also have to regenerate an initramfs.

Ciao,

Duncan.

[*] For legal reasons, users usually have to download and install
the firmware themselves.  For the speedtouch modems I don't know
of any distribution which comes with the firmware preinstalled.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 12:40   ` Duncan Sands
@ 2005-11-01 13:04     ` David Woodhouse
  2005-11-01 13:12       ` Marco d'Itri
                         ` (2 more replies)
  2005-11-01 13:49     ` matthieu castet
  1 sibling, 3 replies; 34+ messages in thread
From: David Woodhouse @ 2005-11-01 13:04 UTC (permalink / raw)
  To: Duncan Sands; +Cc: Andrew Morton, linux-kernel, linux-usb-devel, usbatm

On Tue, 2005-11-01 at 13:40 +0100, Duncan Sands wrote:
> this code looks like a 'orrible hack to work around a common problem
> with USB modem's of this type: if the modem is plugged in while the
> system boots, the driver may look for firmware before the filesystem
> holding the firmware is mounted; I guess the delay usually gives
> the filesystem enough time to be mounted.  I'm told that the correct
> solution is to stick the firmware in an initramfs as well. 

Why can't we request the firmware again when the device is first used,
if it wasn't present when the driver was first loaded?

-- 
dwmw2


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 13:04     ` David Woodhouse
@ 2005-11-01 13:12       ` Marco d'Itri
  2005-11-02  7:42       ` Duncan Sands
  2005-11-02 10:46       ` Roman Kagan
  2 siblings, 0 replies; 34+ messages in thread
From: Marco d'Itri @ 2005-11-01 13:12 UTC (permalink / raw)
  To: David Woodhouse
  Cc: Duncan Sands, Andrew Morton, linux-kernel, usbatm,
	linux-usb-devel

[-- Attachment #1: Type: text/plain, Size: 534 bytes --]

On Nov 01, David Woodhouse <dwmw2@infradead.org> wrote:

> Why can't we request the firmware again when the device is first used,
> if it wasn't present when the driver was first loaded?
For a start, because currently there is no device to use until the
firmware has been loaded.

If people really think that this is a problem which needs to be solved
then maybe a better solution would be to extend the $DEVPATH/loading to
allow loading the firmware at any time, even asyncronously after the
timeout.

-- 
ciao,
Marco

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 12:40   ` Duncan Sands
  2005-11-01 13:04     ` David Woodhouse
@ 2005-11-01 13:49     ` matthieu castet
  2005-11-02  5:29       ` Andrew Morton
  2005-11-02  7:45       ` Duncan Sands
  1 sibling, 2 replies; 34+ messages in thread
From: matthieu castet @ 2005-11-01 13:49 UTC (permalink / raw)
  To: Duncan Sands; +Cc: Andrew Morton, linux-usb-devel, usbatm, linux-kernel

Hi Duncan,

Duncan Sands wrote:
> Hi Andrew,
> 
> 
> this code looks like a 'orrible hack to work around a common problem
> with USB modem's of this type: if the modem is plugged in while the
> system boots, the driver may look for firmware before the filesystem

No, it wasn't the problem, even when loading with insmod/modprobe the 
timeout occurs on some configurations. For example on 
http://atm.eagle-usb.org/wakka.php?wiki=TestUEagleAtmBaud123, you could 
see the 'firmware ueagle-atm/eagleIII.fw is not available' error.

It is only happen for pre-firmware modem (uea_load_firmware) ie where we 
just do a request_firmware in the probe without any initialisation before.
So the problem seems to appear when we do a request_firmware too early 
in the usb_probe.


Matthieu

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-10-31 23:58 ` Andrew Morton
  2005-11-01 12:40   ` Duncan Sands
@ 2005-11-01 14:08   ` matthieu castet
  2005-11-02  5:34     ` Andrew Morton
  2005-11-02  7:47     ` Duncan Sands
  1 sibling, 2 replies; 34+ messages in thread
From: matthieu castet @ 2005-11-01 14:08 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-usb-devel, usbatm, linux-kernel

Hi Andrew,

thanks for the review.

Andrew Morton wrote:
> matthieu castet <castet.matthieu@free.fr> wrote:
> 

>>...
>>+static int request_cmvs(struct uea_softc *sc,
>>+		 struct uea_cmvs **cmvs, const struct firmware **fw)
>>+{
>>+	int ret, size;
>>+	u8 *data;
>>+	char *file;
>>+	char cmv_name[256] = FW_DIR;
> 
> 
> That's rather a lot of stack.  Can this be made static, of kmalloced?
> 
> 
I think we'll made it static.

>>+
>>+	*cmvs = (struct uea_cmvs *)(data + 1);
> 
> 
> That's a bit rude - asking the compiler to perform a structure copy from an
> odd address.  memcpy() would be saner.
> 
Could you elaborate a bit more ?
I don't see where there is a copy.
*cmvs is a pointer to the structure, not the structure. And when we 
parse the structure, we use get_unaligned functions.


>>...
>>+/**
>>+ * uea_read_proc : /proc information
>>+ */
>>+static int uea_read_proc(char *page, 
>>+		char **start, off_t off, int count, int *eof, void *data)
> 
> 
> People get shouted at for adding /proc handlers.  Greg may have thoughts...
> 
Ok, we may be convert some values to sysfs. It would be nice if usbatm 
allow us to export some common value (margin, ...).


Matthieu

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-10-29 22:37 [PATCH] Eagle and ADI 930 usb adsl modem driver matthieu castet
  2005-10-31 23:58 ` Andrew Morton
@ 2005-11-01 22:45 ` Greg KH
  2005-11-02  7:54   ` Duncan Sands
                     ` (2 more replies)
  1 sibling, 3 replies; 34+ messages in thread
From: Greg KH @ 2005-11-01 22:45 UTC (permalink / raw)
  To: matthieu castet; +Cc: linux-usb-devel, usbatm, Linux Kernel list

On Sun, Oct 30, 2005 at 12:37:41AM +0200, matthieu castet wrote:
> Please comment and consider for inclusion.

I need a "Signed-off-by:" line in order to be able to add it.  Care to
redo things based on the comments you have had and resend it with this
line?

> + *
> + * 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
> + * General Public License (GPL) Version 2, available from the file
> + * COPYING in the main directory of this source tree, or the
> + * BSD license below:
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:

<snip>  You don't need the whole GPL 2 copy here, just put the first
paragraph you have before this one in.

> +#define EAGLEUSBVERSION "ueagle-svn $Id: ueagle.c 141 2005-09-03 20:12:24Z matc $"

We don't use $Id within the kernel tree, please remove this.

> +static int uea_read_proc(char *page, char **start, off_t off, int count,
> +			int *eof, void *data);

No new proc files will be added for drivers, please use sysfs instead
(with only one file per value.)

> +/*
> + * sometime hotplug don't have time to give the firmware the
> + * first time, retry it.
> + */
> +static int sleepy_request_firmware(const struct firmware **fw, 
> +		const char *name, struct device *dev)
> +{
> +	if (request_firmware(fw, name, dev) == 0)
> +		return 0;
> +	msleep(1000);
> +	return request_firmware(fw, name, dev);
> +}

No, use the async firmware download mode instead of this.  That will
solve all of your problems.

> +/* we need to use semaphore until sysfs and removable devices is fixed
> + * the problem is explained on http://marc.theaimsgroup.com/?t=112006484100003
> + */

This is the proper fix, why do you think it should be fixed in the
driver core?

> +#define UEA_DEVICE(vid, pid, info) \
> +	{USB_DEVICE((vid), (pid)), .driver_info = (info)}

Why create a macro for such a simple thing?  That's not needed.

> diff -rNu -x '*.ko*' -x '*.mod*' -x '*.o*' linux-2.6.14/drivers/usb/atm.old/ueagle-atm.h linux-2.6.14/drivers/usb/atm/ueagle-atm.h
> --- linux-2.6.14/drivers/usb/atm.old/ueagle-atm.h	1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.14/drivers/usb/atm/ueagle-atm.h	2005-10-30 00:25:27.000000000 +0200

Why do you need a header file for a single .c file?

> +#define PACKED __attribute__ ((packed))

No, spell it out please.

> +/* structure describing a block within a DSP page */
> +typedef struct {
> +	__le16 wHdr;
> +#define UEA_BIHDR 0xabcd
> +	__le16 wAddress;
> +	__le16 wSize;
> +	__le16 wOvlOffset;
> +	__le16 wOvl;		/* overlay */
> +	__le16 wLast;
> +} PACKED block_info_t;

Do not create new typedefs.  Please get rid of all of them.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 13:49     ` matthieu castet
@ 2005-11-02  5:29       ` Andrew Morton
  2005-11-02 22:27         ` Greg KH
  2005-11-02  7:45       ` Duncan Sands
  1 sibling, 1 reply; 34+ messages in thread
From: Andrew Morton @ 2005-11-02  5:29 UTC (permalink / raw)
  To: matthieu castet; +Cc: duncan.sands, linux-usb-devel, usbatm, linux-kernel

matthieu castet <castet.matthieu@free.fr> wrote:
>
> Hi Duncan,
> 
> Duncan Sands wrote:
> > Hi Andrew,
> > 
> > 
> > this code looks like a 'orrible hack to work around a common problem
> > with USB modem's of this type: if the modem is plugged in while the
> > system boots, the driver may look for firmware before the filesystem
> 
> No, it wasn't the problem, even when loading with insmod/modprobe the 
> timeout occurs on some configurations. For example on 
> http://atm.eagle-usb.org/wakka.php?wiki=TestUEagleAtmBaud123, you could 
> see the 'firmware ueagle-atm/eagleIII.fw is not available' error.
> 
> It is only happen for pre-firmware modem (uea_load_firmware) ie where we 
> just do a request_firmware in the probe without any initialisation before.
> So the problem seems to appear when we do a request_firmware too early 
> in the usb_probe.
> 

Can you please work out exactly what's happening and come up with a more
solid solution than msleep(1)?

Because other drivers may hit this problem (whatever it is) and fixes in
core kernel might be needed.  If we just work around stuff, core kernel
remains unfixed...

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 14:08   ` matthieu castet
@ 2005-11-02  5:34     ` Andrew Morton
  2005-11-02  7:47     ` Duncan Sands
  1 sibling, 0 replies; 34+ messages in thread
From: Andrew Morton @ 2005-11-02  5:34 UTC (permalink / raw)
  To: matthieu castet; +Cc: linux-usb-devel, usbatm, linux-kernel

matthieu castet <castet.matthieu@free.fr> wrote:
>
> >>+
>  >>+	*cmvs = (struct uea_cmvs *)(data + 1);
>  > 
>  > 
>  > That's a bit rude - asking the compiler to perform a structure copy from an
>  > odd address.  memcpy() would be saner.
>  > 
>  Could you elaborate a bit more ?
>  I don't see where there is a copy.
>  *cmvs is a pointer to the structure, not the structure. And when we 
>  parse the structure, we use get_unaligned functions.
> 
> 

Ah, I misread the code.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 13:04     ` David Woodhouse
  2005-11-01 13:12       ` Marco d'Itri
@ 2005-11-02  7:42       ` Duncan Sands
  2005-11-02  7:45         ` David Woodhouse
  2005-11-02 10:46       ` Roman Kagan
  2 siblings, 1 reply; 34+ messages in thread
From: Duncan Sands @ 2005-11-02  7:42 UTC (permalink / raw)
  To: David Woodhouse; +Cc: Andrew Morton, linux-kernel, linux-usb-devel, usbatm

Hi David,

On Tuesday 1 November 2005 14:04, David Woodhouse wrote:
> On Tue, 2005-11-01 at 13:40 +0100, Duncan Sands wrote:
> > this code looks like a 'orrible hack to work around a common problem
> > with USB modem's of this type: if the modem is plugged in while the
> > system boots, the driver may look for firmware before the filesystem
> > holding the firmware is mounted; I guess the delay usually gives
> > the filesystem enough time to be mounted.  I'm told that the correct
> > solution is to stick the firmware in an initramfs as well. 
> 
> Why can't we request the firmware again when the device is first used,
> if it wasn't present when the driver was first loaded?

we could do this for the speedtouch - in fact we used to do this: when
someone tried to open a connection, we loaded the firmware if it hadn't
been loaded yet.  The problem is with other modems, like the connexant
access runner, for which you can't get all the info needed to create
an ATM device before the firmware is loaded (the MAC address for example).

Ciao,

D.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02  7:42       ` Duncan Sands
@ 2005-11-02  7:45         ` David Woodhouse
  2005-11-02  8:02           ` Duncan Sands
  0 siblings, 1 reply; 34+ messages in thread
From: David Woodhouse @ 2005-11-02  7:45 UTC (permalink / raw)
  To: Duncan Sands; +Cc: Andrew Morton, linux-kernel, linux-usb-devel, usbatm

On Wed, 2005-11-02 at 08:42 +0100, Duncan Sands wrote:
> we could do this for the speedtouch - in fact we used to do this: when
> someone tried to open a connection, we loaded the firmware if it
> hadn't been loaded yet.  The problem is with other modems, like the
> connexant access runner, for which you can't get all the info needed
> to create an ATM device before the firmware is loaded (the MAC address
> for example).

Don't we also have Ethernet devices like that -- where the MAC address
doesn't get set until you bring the device up?

Can we get away with changing the MAC address later? Or is there other
stuff we need earlier?

-- 
dwmw2



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 13:49     ` matthieu castet
  2005-11-02  5:29       ` Andrew Morton
@ 2005-11-02  7:45       ` Duncan Sands
  1 sibling, 0 replies; 34+ messages in thread
From: Duncan Sands @ 2005-11-02  7:45 UTC (permalink / raw)
  To: usbatm; +Cc: matthieu castet, Andrew Morton, linux-kernel, linux-usb-devel

> > this code looks like a 'orrible hack to work around a common problem
> > with USB modem's of this type: if the modem is plugged in while the
> > system boots, the driver may look for firmware before the filesystem
> 
> No, it wasn't the problem, even when loading with insmod/modprobe the 
> timeout occurs on some configurations. For example on 
> http://atm.eagle-usb.org/wakka.php?wiki=TestUEagleAtmBaud123, you could 
> see the 'firmware ueagle-atm/eagleIII.fw is not available' error.
> 
> It is only happen for pre-firmware modem (uea_load_firmware) ie where we 
> just do a request_firmware in the probe without any initialisation before.
> So the problem seems to appear when we do a request_firmware too early 
> in the usb_probe.

That sounds pretty strange.  Maybe when probe is called the driver model
device (sysfs) is not completely setup, in any case not enough to support
firmware loading?

Ciao,

Duncan.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 14:08   ` matthieu castet
  2005-11-02  5:34     ` Andrew Morton
@ 2005-11-02  7:47     ` Duncan Sands
  1 sibling, 0 replies; 34+ messages in thread
From: Duncan Sands @ 2005-11-02  7:47 UTC (permalink / raw)
  To: usbatm; +Cc: matthieu castet, Andrew Morton, linux-kernel, linux-usb-devel

> > People get shouted at for adding /proc handlers.  Greg may have thoughts...
> > 
> Ok, we may be convert some values to sysfs. It would be nice if usbatm 
> allow us to export some common value (margin, ...).

Yes it would be nice.  Patches welcome :)

D.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 22:45 ` Greg KH
@ 2005-11-02  7:54   ` Duncan Sands
  2005-11-02  8:03     ` Greg KH
  2005-11-02 15:56   ` Alan Stern
  2005-11-02 20:15   ` matthieu castet
  2 siblings, 1 reply; 34+ messages in thread
From: Duncan Sands @ 2005-11-02  7:54 UTC (permalink / raw)
  To: usbatm; +Cc: Greg KH, matthieu castet, Linux Kernel list, linux-usb-devel

> > + * sometime hotplug don't have time to give the firmware the
> > + * first time, retry it.
> > + */
> > +static int sleepy_request_firmware(const struct firmware **fw, 
> > +		const char *name, struct device *dev)
> > +{
> > +	if (request_firmware(fw, name, dev) == 0)
> > +		return 0;
> > +	msleep(1000);
> > +	return request_firmware(fw, name, dev);
> > +}
> 
> No, use the async firmware download mode instead of this.  That will
> solve all of your problems.

Hi Greg, it looks like you understand what the problem is here.  Could
you please explain to us lesser mortals ;)

Thanks,

Duncan.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02  7:45         ` David Woodhouse
@ 2005-11-02  8:02           ` Duncan Sands
  0 siblings, 0 replies; 34+ messages in thread
From: Duncan Sands @ 2005-11-02  8:02 UTC (permalink / raw)
  To: usbatm; +Cc: David Woodhouse, Andrew Morton, linux-kernel, linux-usb-devel

On Wednesday 2 November 2005 08:45, David Woodhouse wrote:
> On Wed, 2005-11-02 at 08:42 +0100, Duncan Sands wrote:
> > we could do this for the speedtouch - in fact we used to do this: when
> > someone tried to open a connection, we loaded the firmware if it
> > hadn't been loaded yet.  The problem is with other modems, like the
> > connexant access runner, for which you can't get all the info needed
> > to create an ATM device before the firmware is loaded (the MAC address
> > for example).
> 
> Don't we also have Ethernet devices like that -- where the MAC address
> doesn't get set until you bring the device up?
> 
> Can we get away with changing the MAC address later? Or is there other
> stuff we need earlier?

Hi David, it's only the MAC address.  I didn't check carefully, but I'm pretty
sure that in general the MAC address is the only device dependant info needed
to setup an ATM device.  But isn't changing the MAC address later a horrible
hack that is sure to break stuff?

Duncan.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02  7:54   ` Duncan Sands
@ 2005-11-02  8:03     ` Greg KH
  2005-11-02  8:45       ` [linux-usb-devel] " Oliver Neukum
  0 siblings, 1 reply; 34+ messages in thread
From: Greg KH @ 2005-11-02  8:03 UTC (permalink / raw)
  To: Duncan Sands; +Cc: usbatm, matthieu castet, Linux Kernel list, linux-usb-devel

On Wed, Nov 02, 2005 at 08:54:22AM +0100, Duncan Sands wrote:
> > > + * sometime hotplug don't have time to give the firmware the
> > > + * first time, retry it.
> > > + */
> > > +static int sleepy_request_firmware(const struct firmware **fw, 
> > > +		const char *name, struct device *dev)
> > > +{
> > > +	if (request_firmware(fw, name, dev) == 0)
> > > +		return 0;
> > > +	msleep(1000);
> > > +	return request_firmware(fw, name, dev);
> > > +}
> > 
> > No, use the async firmware download mode instead of this.  That will
> > solve all of your problems.
> 
> Hi Greg, it looks like you understand what the problem is here.  Could
> you please explain to us lesser mortals ;)

If you use the async mode, there is no timeout.  When userspace gets
around to giving you the firmware, then you continue on with the rest of
your device initialization (don't block the usb probe function though.)

Everyone should switch to that interface, then we would not have any
timeout issues anymore...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [linux-usb-devel] Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02  8:03     ` Greg KH
@ 2005-11-02  8:45       ` Oliver Neukum
  2005-11-02  8:52         ` Duncan Sands
  2005-11-02 21:37         ` Greg KH
  0 siblings, 2 replies; 34+ messages in thread
From: Oliver Neukum @ 2005-11-02  8:45 UTC (permalink / raw)
  To: linux-usb-devel
  Cc: Greg KH, Duncan Sands, usbatm, matthieu castet, Linux Kernel list

Am Mittwoch, 2. November 2005 09:03 schrieb Greg KH:
> On Wed, Nov 02, 2005 at 08:54:22AM +0100, Duncan Sands wrote:
> > > > + * sometime hotplug don't have time to give the firmware the
> > > > + * first time, retry it.
> > > > + */
> > > > +static int sleepy_request_firmware(const struct firmware **fw, 
> > > > +		const char *name, struct device *dev)
> > > > +{
> > > > +	if (request_firmware(fw, name, dev) == 0)
> > > > +		return 0;
> > > > +	msleep(1000);
> > > > +	return request_firmware(fw, name, dev);
> > > > +}
> > > 
> > > No, use the async firmware download mode instead of this.  That will
> > > solve all of your problems.
> > 
> > Hi Greg, it looks like you understand what the problem is here.  Could
> > you please explain to us lesser mortals ;)
> 
> If you use the async mode, there is no timeout.  When userspace gets
> around to giving you the firmware, then you continue on with the rest of
> your device initialization (don't block the usb probe function though.)

How would you handle errors in setting up the device?
A driver cannot reject a device after probe, yet you need to handle
errors appearing only after the firmware is in the device.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [linux-usb-devel] Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02  8:45       ` [linux-usb-devel] " Oliver Neukum
@ 2005-11-02  8:52         ` Duncan Sands
  2005-11-02 21:39           ` Greg KH
  2005-11-02 21:37         ` Greg KH
  1 sibling, 1 reply; 34+ messages in thread
From: Duncan Sands @ 2005-11-02  8:52 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-usb-devel, Greg KH, usbatm, matthieu castet,
	Linux Kernel list

On Wednesday 2 November 2005 09:45, Oliver Neukum wrote:
> Am Mittwoch, 2. November 2005 09:03 schrieb Greg KH:
> > On Wed, Nov 02, 2005 at 08:54:22AM +0100, Duncan Sands wrote:
> > > > > + * sometime hotplug don't have time to give the firmware the
> > > > > + * first time, retry it.
> > > > > + */
> > > > > +static int sleepy_request_firmware(const struct firmware **fw, 
> > > > > +		const char *name, struct device *dev)
> > > > > +{
> > > > > +	if (request_firmware(fw, name, dev) == 0)
> > > > > +		return 0;
> > > > > +	msleep(1000);
> > > > > +	return request_firmware(fw, name, dev);
> > > > > +}
> > > > 
> > > > No, use the async firmware download mode instead of this.  That will
> > > > solve all of your problems.
> > > 
> > > Hi Greg, it looks like you understand what the problem is here.  Could
> > > you please explain to us lesser mortals ;)
> > 
> > If you use the async mode, there is no timeout.  When userspace gets
> > around to giving you the firmware, then you continue on with the rest of
> > your device initialization (don't block the usb probe function though.)
> 
> How would you handle errors in setting up the device?
> A driver cannot reject a device after probe, yet you need to handle
> errors appearing only after the firmware is in the device.

Isn't that the case anyway with the sync version?  After all, sync loading
of firmware from the probe method is unacceptable, since it can block khubd
for a long time (also, if the firmware lives on a USB device that gets
disconnected just as probe for the device that loads firmware is called,
or some variation of this theme, couldn't horrible blockage happen?).

Ciao,

D.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 13:04     ` David Woodhouse
  2005-11-01 13:12       ` Marco d'Itri
  2005-11-02  7:42       ` Duncan Sands
@ 2005-11-02 10:46       ` Roman Kagan
  2005-11-02 11:01         ` Duncan Sands
  2 siblings, 1 reply; 34+ messages in thread
From: Roman Kagan @ 2005-11-02 10:46 UTC (permalink / raw)
  To: David Woodhouse
  Cc: Duncan Sands, Andrew Morton, linux-kernel, usbatm,
	linux-usb-devel

On Tue, Nov 01, 2005 at 01:04:02PM +0000, David Woodhouse wrote:
> On Tue, 2005-11-01 at 13:40 +0100, Duncan Sands wrote:
> > this code looks like a 'orrible hack to work around a common problem
> > with USB modem's of this type: if the modem is plugged in while the
> > system boots, the driver may look for firmware before the filesystem
> > holding the firmware is mounted; I guess the delay usually gives
> > the filesystem enough time to be mounted.  I'm told that the correct
> > solution is to stick the firmware in an initramfs as well. 
> 
> Why can't we request the firmware again when the device is first used,
> if it wasn't present when the driver was first loaded?

Because the firmware loading can take long, and apps may legitimately
give up opening the device after a timeout.

Besides, it doesn't look logical.  An uninitialized device is not
particularly useful for anything but initialization.  You don't create,
say, a network device for your ethernet card until you're finished with
its PCI setup, do you?

I think the async firmware loading can do the job nicely, in a generic
manner.  BTW the usbatm drivers do it already (wasn't it you who
implemented it? :), long before request_firmware_nowait() was available.
So it's only a matter of tools adjusting, which seems to be going on.

Roman.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02 10:46       ` Roman Kagan
@ 2005-11-02 11:01         ` Duncan Sands
  0 siblings, 0 replies; 34+ messages in thread
From: Duncan Sands @ 2005-11-02 11:01 UTC (permalink / raw)
  To: Roman Kagan
  Cc: David Woodhouse, Andrew Morton, linux-kernel, usbatm,
	linux-usb-devel

Hi Roman, glad to see you're still alive!

On Wednesday 2 November 2005 11:46, Roman Kagan wrote:
> On Tue, Nov 01, 2005 at 01:04:02PM +0000, David Woodhouse wrote:
> > On Tue, 2005-11-01 at 13:40 +0100, Duncan Sands wrote:
> > > this code looks like a 'orrible hack to work around a common problem
> > > with USB modem's of this type: if the modem is plugged in while the
> > > system boots, the driver may look for firmware before the filesystem
> > > holding the firmware is mounted; I guess the delay usually gives
> > > the filesystem enough time to be mounted.  I'm told that the correct
> > > solution is to stick the firmware in an initramfs as well. 
> > 
> > Why can't we request the firmware again when the device is first used,
> > if it wasn't present when the driver was first loaded?
> 
> Because the firmware loading can take long, and apps may legitimately
> give up opening the device after a timeout.
> 
> Besides, it doesn't look logical.  An uninitialized device is not
> particularly useful for anything but initialization.  You don't create,
> say, a network device for your ethernet card until you're finished with
> its PCI setup, do you?
> 
> I think the async firmware loading can do the job nicely, in a generic
> manner.  BTW the usbatm drivers do it already (wasn't it you who
> implemented it? :), long before request_firmware_nowait() was available.
> So it's only a matter of tools adjusting, which seems to be going on.

I can't help feeling that it is wrong to add ad-hoc code to drivers (such
as: if the firmware wasn't there, try to load it again later) in an attempt
to work around what is, in the end, a userspace configuration problem.  The
fact that configuring userspace correctly seems to be tricky is sad, but not
the driver's problem.

I don't mind using request_firmware_nowait by the way.  The lack of a
timeout is no problem as long as we make it possible for the user to shoot
the firmware loading down by sending a signal.

Ciao,

Duncan.

PS: On the other hand, users are feeling the pain, which means I get to feel
their pain, which tempts me to hack in a workaround ;)

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [linux-usb-devel] Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 22:45 ` Greg KH
  2005-11-02  7:54   ` Duncan Sands
@ 2005-11-02 15:56   ` Alan Stern
  2005-11-02 20:15   ` matthieu castet
  2 siblings, 0 replies; 34+ messages in thread
From: Alan Stern @ 2005-11-02 15:56 UTC (permalink / raw)
  To: Greg KH; +Cc: matthieu castet, linux-usb-devel, usbatm, Linux Kernel list

On Tue, 1 Nov 2005, Greg KH wrote:

> > +/* we need to use semaphore until sysfs and removable devices is fixed
> > + * the problem is explained on http://marc.theaimsgroup.com/?t=112006484100003
> > + */
> 
> This is the proper fix, why do you think it should be fixed in the
> driver core?

I missed the earlier discussion about this when it appeared on lkml.

This is no doubt a widespread problem, and it should be brought to many 
people's attention.  Greg, can you think of any good way to make this more 
visible?  Maybe a short piece in Linux Journal combined with a more 
eye-catching $SUBJECT in lkml?

Maybe this could be combined with a brief discussion of the open-remove 
race as well (and the use of BKL to help resolve it).

Alan Stern


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-01 22:45 ` Greg KH
  2005-11-02  7:54   ` Duncan Sands
  2005-11-02 15:56   ` Alan Stern
@ 2005-11-02 20:15   ` matthieu castet
  2005-11-02 21:18     ` matthieu castet
                       ` (2 more replies)
  2 siblings, 3 replies; 34+ messages in thread
From: matthieu castet @ 2005-11-02 20:15 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-usb-devel, usbatm, Linux Kernel list

Hi Greg,

thanks for your review.

Greg KH wrote:
> On Sun, Oct 30, 2005 at 12:37:41AM +0200, matthieu castet wrote:
> 
>>Please comment and consider for inclusion.
> 
> 
> I need a "Signed-off-by:" line in order to be able to add it.  Care to
> redo things based on the comments you have had and resend it with this
> line?
> 
> 
no problem ;)
>>+ *
>>+ * 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
>>+ * General Public License (GPL) Version 2, available from the file
>>+ * COPYING in the main directory of this source tree, or the
>>+ * BSD license below:
>>+ *
>>+ * Redistribution and use in source and binary forms, with or without
>>+ * modification, are permitted provided that the following conditions
>>+ * are met:
> 
> 
> <snip>  You don't need the whole GPL 2 copy here, just put the first
> paragraph you have before this one in.
> 
The paragraph you quote is the BSD licence, and point 1 is :
Redistributions of source code must retain the above copyright
  *    notice unmodified, this list of conditions, and the following
  *    disclaimer

So could I remove it ?


>>+/*
>>+ * sometime hotplug don't have time to give the firmware the
>>+ * first time, retry it.
>>+ */
>>+static int sleepy_request_firmware(const struct firmware **fw, 
>>+		const char *name, struct device *dev)
>>+{
>>+	if (request_firmware(fw, name, dev) == 0)
>>+		return 0;
>>+	msleep(1000);
>>+	return request_firmware(fw, name, dev);
>>+}
> 
> 
> No, use the async firmware download mode instead of this.  That will
> solve all of your problems.
> 
> 
Thanks, but does userspace will retry if it fails the first time ?
The device needs the firmware quickly and after 3-5 seconds without it, 
it goes berserk.


>>+/* we need to use semaphore until sysfs and removable devices is fixed
>>+ * the problem is explained on http://marc.theaimsgroup.com/?t=112006484100003
>>+ */
> 
> 
> This is the proper fix, why do you think it should be fixed in the
> driver core?
> 
I don't remember, but aren't any possible race in sysfs code ?
In the read, after the up, the usb_disconnect is scheduled, and call 
device_remove_file. Is that ok for the sysfs code to be still in the 
read code ?
An other case : a process open a file, and start a read. Before the down 
, the usb_disconnect is scheduled and the module is removed.
What will do the read, run code from the removed code ?



> 
>>diff -rNu -x '*.ko*' -x '*.mod*' -x '*.o*' linux-2.6.14/drivers/usb/atm.old/ueagle-atm.h linux-2.6.14/drivers/usb/atm/ueagle-atm.h
>>--- linux-2.6.14/drivers/usb/atm.old/ueagle-atm.h	1970-01-01 01:00:00.000000000 +0100
>>+++ linux-2.6.14/drivers/usb/atm/ueagle-atm.h	2005-10-30 00:25:27.000000000 +0200
> 
> 
> Why do you need a header file for a single .c file?
> 
I think it makes things cleaner. I even like the bsd style where there 
is an header for reg (hardware values) and an other for val (driver 
structures).

 >>+#define PACKED __attribute__ ((packed))
 >
 >
 > No, spell it out please.
 >
 >
 >>+/* structure describing a block within a DSP page */
 >>+typedef struct {
 >>+	__le16 wHdr;
 >>+#define UEA_BIHDR 0xabcd
 >>+	__le16 wAddress;
 >>+	__le16 wSize;
 >>+	__le16 wOvlOffset;
 >>+	__le16 wOvl;		/* overlay */
 >>+	__le16 wLast;
 >>+} PACKED block_info_t;
 >
 >
 > Do not create new typedefs.  Please get rid of all of them.
It comes from bsd driver, it will be cleaned.

thanks,

Matthieu



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02 20:15   ` matthieu castet
@ 2005-11-02 21:18     ` matthieu castet
  2005-11-07 17:47       ` Greg KH
  2005-11-06 18:44     ` matthieu castet
  2005-11-07 17:46     ` Greg KH
  2 siblings, 1 reply; 34+ messages in thread
From: matthieu castet @ 2005-11-02 21:18 UTC (permalink / raw)
  To: matthieu castet; +Cc: Greg KH, Linux Kernel list, linux-usb-devel, usbatm

matthieu castet wrote:
> Hi Greg,
>>> +/*
>>> + * sometime hotplug don't have time to give the firmware the
>>> + * first time, retry it.
>>> + */
>>> +static int sleepy_request_firmware(const struct firmware **fw, 
>>> +        const char *name, struct device *dev)
>>> +{
>>> +    if (request_firmware(fw, name, dev) == 0)
>>> +        return 0;
>>> +    msleep(1000);
>>> +    return request_firmware(fw, name, dev);
>>> +}
>>
>>
>>
>> No, use the async firmware download mode instead of this.  That will
>> solve all of your problems.
>>
>>
> Thanks, but does userspace will retry if it fails the first time ?
> The device needs the firmware quickly and after 3-5 seconds without it, 
> it goes berserk.
> 
In request_firmware_nowait, when kernel_thread failed, where fw_work is 
freed ?
Aren't there a memleack ?

Matthieu


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [linux-usb-devel] Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02  8:45       ` [linux-usb-devel] " Oliver Neukum
  2005-11-02  8:52         ` Duncan Sands
@ 2005-11-02 21:37         ` Greg KH
  1 sibling, 0 replies; 34+ messages in thread
From: Greg KH @ 2005-11-02 21:37 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-usb-devel, Duncan Sands, usbatm, matthieu castet,
	Linux Kernel list

On Wed, Nov 02, 2005 at 09:45:28AM +0100, Oliver Neukum wrote:
> Am Mittwoch, 2. November 2005 09:03 schrieb Greg KH:
> > On Wed, Nov 02, 2005 at 08:54:22AM +0100, Duncan Sands wrote:
> > > > > + * sometime hotplug don't have time to give the firmware the
> > > > > + * first time, retry it.
> > > > > + */
> > > > > +static int sleepy_request_firmware(const struct firmware **fw, 
> > > > > +		const char *name, struct device *dev)
> > > > > +{
> > > > > +	if (request_firmware(fw, name, dev) == 0)
> > > > > +		return 0;
> > > > > +	msleep(1000);
> > > > > +	return request_firmware(fw, name, dev);
> > > > > +}
> > > > 
> > > > No, use the async firmware download mode instead of this.  That will
> > > > solve all of your problems.
> > > 
> > > Hi Greg, it looks like you understand what the problem is here.  Could
> > > you please explain to us lesser mortals ;)
> > 
> > If you use the async mode, there is no timeout.  When userspace gets
> > around to giving you the firmware, then you continue on with the rest of
> > your device initialization (don't block the usb probe function though.)
> 
> How would you handle errors in setting up the device?
> A driver cannot reject a device after probe, yet you need to handle
> errors appearing only after the firmware is in the device.

Yes, you can disconnect from a device after you accept it.  And you
_know_ you want this device anyway, as it is of the type you handle.  If
something bad happens, it's ok to still have the device "claimed" as
what else would you be able to do with it?  :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [linux-usb-devel] Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02  8:52         ` Duncan Sands
@ 2005-11-02 21:39           ` Greg KH
  0 siblings, 0 replies; 34+ messages in thread
From: Greg KH @ 2005-11-02 21:39 UTC (permalink / raw)
  To: Duncan Sands
  Cc: Oliver Neukum, linux-usb-devel, usbatm, matthieu castet,
	Linux Kernel list

On Wed, Nov 02, 2005 at 09:52:56AM +0100, Duncan Sands wrote:
> On Wednesday 2 November 2005 09:45, Oliver Neukum wrote:
> > Am Mittwoch, 2. November 2005 09:03 schrieb Greg KH:
> > > On Wed, Nov 02, 2005 at 08:54:22AM +0100, Duncan Sands wrote:
> > > > > > + * sometime hotplug don't have time to give the firmware the
> > > > > > + * first time, retry it.
> > > > > > + */
> > > > > > +static int sleepy_request_firmware(const struct firmware **fw, 
> > > > > > +		const char *name, struct device *dev)
> > > > > > +{
> > > > > > +	if (request_firmware(fw, name, dev) == 0)
> > > > > > +		return 0;
> > > > > > +	msleep(1000);
> > > > > > +	return request_firmware(fw, name, dev);
> > > > > > +}
> > > > > 
> > > > > No, use the async firmware download mode instead of this.  That will
> > > > > solve all of your problems.
> > > > 
> > > > Hi Greg, it looks like you understand what the problem is here.  Could
> > > > you please explain to us lesser mortals ;)
> > > 
> > > If you use the async mode, there is no timeout.  When userspace gets
> > > around to giving you the firmware, then you continue on with the rest of
> > > your device initialization (don't block the usb probe function though.)
> > 
> > How would you handle errors in setting up the device?
> > A driver cannot reject a device after probe, yet you need to handle
> > errors appearing only after the firmware is in the device.
> 
> Isn't that the case anyway with the sync version?

Yes.

> After all, sync loading of firmware from the probe method is
> unacceptable, since it can block khubd for a long time (also, if the
> firmware lives on a USB device that gets disconnected just as probe
> for the device that loads firmware is called, or some variation of
> this theme, couldn't horrible blockage happen?).

Yes it could.  That's why you should use the async version :)

One of these days I'll start multi-threading the device probe stuff, and
it will not be that big of a deal...[1]

thanks,

greg k-h

[1] Yes, I know the usb core and drivers will not expect this, and work
will be needed to get this working properly.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02  5:29       ` Andrew Morton
@ 2005-11-02 22:27         ` Greg KH
  0 siblings, 0 replies; 34+ messages in thread
From: Greg KH @ 2005-11-02 22:27 UTC (permalink / raw)
  To: Andrew Morton
  Cc: matthieu castet, duncan.sands, linux-usb-devel, usbatm,
	linux-kernel

On Wed, Nov 02, 2005 at 04:29:58PM +1100, Andrew Morton wrote:
> matthieu castet <castet.matthieu@free.fr> wrote:
> >
> > Hi Duncan,
> > 
> > Duncan Sands wrote:
> > > Hi Andrew,
> > > 
> > > 
> > > this code looks like a 'orrible hack to work around a common problem
> > > with USB modem's of this type: if the modem is plugged in while the
> > > system boots, the driver may look for firmware before the filesystem
> > 
> > No, it wasn't the problem, even when loading with insmod/modprobe the 
> > timeout occurs on some configurations. For example on 
> > http://atm.eagle-usb.org/wakka.php?wiki=TestUEagleAtmBaud123, you could 
> > see the 'firmware ueagle-atm/eagleIII.fw is not available' error.
> > 
> > It is only happen for pre-firmware modem (uea_load_firmware) ie where we 
> > just do a request_firmware in the probe without any initialisation before.
> > So the problem seems to appear when we do a request_firmware too early 
> > in the usb_probe.
> > 
> 
> Can you please work out exactly what's happening and come up with a more
> solid solution than msleep(1)?

The fix is to use the async firmware interface, no sleeping or timeouts
needed.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02 20:15   ` matthieu castet
  2005-11-02 21:18     ` matthieu castet
@ 2005-11-06 18:44     ` matthieu castet
  2005-11-07 17:47       ` Greg KH
  2005-11-07 17:46     ` Greg KH
  2 siblings, 1 reply; 34+ messages in thread
From: matthieu castet @ 2005-11-06 18:44 UTC (permalink / raw)
  Cc: Greg KH, Linux Kernel list, linux-usb-devel, usbatm

Hi Greg,

matthieu castet wrote:

>>> + *
>>> + * 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
>>> + * General Public License (GPL) Version 2, available from the file
>>> + * COPYING in the main directory of this source tree, or the
>>> + * BSD license below:
>>> + *
>>> + * Redistribution and use in source and binary forms, with or without
>>> + * modification, are permitted provided that the following conditions
>>> + * are met:
>>
>>
>>
>> <snip>  You don't need the whole GPL 2 copy here, just put the first
>> paragraph you have before this one in.
>>
> The paragraph you quote is the BSD licence, and point 1 is :
> Redistributions of source code must retain the above copyright
>  *    notice unmodified, this list of conditions, and the following
>  *    disclaimer
> 
> So could I remove it ?
> 
> 
>>
>>> diff -rNu -x '*.ko*' -x '*.mod*' -x '*.o*' 
>>> linux-2.6.14/drivers/usb/atm.old/ueagle-atm.h 
>>> linux-2.6.14/drivers/usb/atm/ueagle-atm.h
>>> --- linux-2.6.14/drivers/usb/atm.old/ueagle-atm.h    1970-01-01 
>>> 01:00:00.000000000 +0100
>>> +++ linux-2.6.14/drivers/usb/atm/ueagle-atm.h    2005-10-30 
>>> 00:25:27.000000000 +0200
>>
>>
>>
>> Why do you need a header file for a single .c file?
>>
> I think it makes things cleaner. I even like the bsd style where there 
> is an header for reg (hardware values) and an other for val (driver 
> structures).
> 

We patched our driver with the comments sent, but we still don't know 
what to do with this 2 points :
- For the license stuff, all the dual bsd/gpl drivers I saw in the 
kernel tree have the complete bsd header.
- For the header file I prefer a separate header file, but if Linux 
policy is to merge header and source file, that's fine.


Regards,

Matthieu

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02 20:15   ` matthieu castet
  2005-11-02 21:18     ` matthieu castet
  2005-11-06 18:44     ` matthieu castet
@ 2005-11-07 17:46     ` Greg KH
  2005-11-07 18:47       ` matthieu castet
  2005-11-07 22:27       ` matthieu castet
  2 siblings, 2 replies; 34+ messages in thread
From: Greg KH @ 2005-11-07 17:46 UTC (permalink / raw)
  To: matthieu castet; +Cc: linux-usb-devel, usbatm, Linux Kernel list

On Wed, Nov 02, 2005 at 09:15:58PM +0100, matthieu castet wrote:
> Hi Greg,
> 
> thanks for your review.
> 
> Greg KH wrote:
> >On Sun, Oct 30, 2005 at 12:37:41AM +0200, matthieu castet wrote:
> >
> >>Please comment and consider for inclusion.
> >
> >
> >I need a "Signed-off-by:" line in order to be able to add it.  Care to
> >redo things based on the comments you have had and resend it with this
> >line?
> >
> >
> no problem ;)
> >>+ *
> >>+ * 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
> >>+ * General Public License (GPL) Version 2, available from the file
> >>+ * COPYING in the main directory of this source tree, or the
> >>+ * BSD license below:
> >>+ *
> >>+ * Redistribution and use in source and binary forms, with or without
> >>+ * modification, are permitted provided that the following conditions
> >>+ * are met:
> >
> >
> ><snip>  You don't need the whole GPL 2 copy here, just put the first
> >paragraph you have before this one in.
> >
> The paragraph you quote is the BSD licence, and point 1 is :
> Redistributions of source code must retain the above copyright
>  *    notice unmodified, this list of conditions, and the following
>  *    disclaimer
> 
> So could I remove it ?

Ugh, you are right, I missed that this was BSD also, sorry.  It's fine.

> >>+/*
> >>+ * sometime hotplug don't have time to give the firmware the
> >>+ * first time, retry it.
> >>+ */
> >>+static int sleepy_request_firmware(const struct firmware **fw, 
> >>+		const char *name, struct device *dev)
> >>+{
> >>+	if (request_firmware(fw, name, dev) == 0)
> >>+		return 0;
> >>+	msleep(1000);
> >>+	return request_firmware(fw, name, dev);
> >>+}
> >
> >
> >No, use the async firmware download mode instead of this.  That will
> >solve all of your problems.
> >
> >
> Thanks, but does userspace will retry if it fails the first time ?
> The device needs the firmware quickly and after 3-5 seconds without it, 
> it goes berserk.

That sounds like a pretty broken device :)

I don't know, have your own timer that disconnects it after X seconds if
you haven't gotten the firmware yet?  Don't rely on userspace getting
around to you real quickly, as we have seen lots of problems with
drivers relying on that timeout.

> >>+/* we need to use semaphore until sysfs and removable devices is fixed
> >>+ * the problem is explained on 
> >>http://marc.theaimsgroup.com/?t=112006484100003
> >>+ */
> >
> >
> >This is the proper fix, why do you think it should be fixed in the
> >driver core?
> >
> I don't remember, but aren't any possible race in sysfs code ?
> In the read, after the up, the usb_disconnect is scheduled, and call 
> device_remove_file. Is that ok for the sysfs code to be still in the 
> read code ?

Depends on what that code does.

> An other case : a process open a file, and start a read. Before the down 
> , the usb_disconnect is scheduled and the module is removed.
> What will do the read, run code from the removed code ?

Again, depends on how your code is structured, and what you are
referencing in that read code.

> >>diff -rNu -x '*.ko*' -x '*.mod*' -x '*.o*' 
> >>linux-2.6.14/drivers/usb/atm.old/ueagle-atm.h 
> >>linux-2.6.14/drivers/usb/atm/ueagle-atm.h
> >>--- linux-2.6.14/drivers/usb/atm.old/ueagle-atm.h	1970-01-01 
> >>01:00:00.000000000 +0100
> >>+++ linux-2.6.14/drivers/usb/atm/ueagle-atm.h	2005-10-30 
> >>00:25:27.000000000 +0200
> >
> >
> >Why do you need a header file for a single .c file?
> >
> I think it makes things cleaner. I even like the bsd style where there 
> is an header for reg (hardware values) and an other for val (driver 
> structures).

This isn't BSD :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-06 18:44     ` matthieu castet
@ 2005-11-07 17:47       ` Greg KH
  0 siblings, 0 replies; 34+ messages in thread
From: Greg KH @ 2005-11-07 17:47 UTC (permalink / raw)
  To: matthieu castet; +Cc: Linux Kernel list, linux-usb-devel, usbatm

On Sun, Nov 06, 2005 at 07:44:10PM +0100, matthieu castet wrote:
> - For the license stuff, all the dual bsd/gpl drivers I saw in the 
> kernel tree have the complete bsd header.

That's fine.

> - For the header file I prefer a separate header file, but if Linux 
> policy is to merge header and source file, that's fine.

It's ok, as long as it is local, and it is what you want to do.  I don't
have a strong feeling toward it.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-02 21:18     ` matthieu castet
@ 2005-11-07 17:47       ` Greg KH
  0 siblings, 0 replies; 34+ messages in thread
From: Greg KH @ 2005-11-07 17:47 UTC (permalink / raw)
  To: matthieu castet; +Cc: Linux Kernel list, linux-usb-devel, usbatm

On Wed, Nov 02, 2005 at 10:18:13PM +0100, matthieu castet wrote:
> matthieu castet wrote:
> >Hi Greg,
> >>>+/*
> >>>+ * sometime hotplug don't have time to give the firmware the
> >>>+ * first time, retry it.
> >>>+ */
> >>>+static int sleepy_request_firmware(const struct firmware **fw, 
> >>>+        const char *name, struct device *dev)
> >>>+{
> >>>+    if (request_firmware(fw, name, dev) == 0)
> >>>+        return 0;
> >>>+    msleep(1000);
> >>>+    return request_firmware(fw, name, dev);
> >>>+}
> >>
> >>
> >>
> >>No, use the async firmware download mode instead of this.  That will
> >>solve all of your problems.
> >>
> >>
> >Thanks, but does userspace will retry if it fails the first time ?
> >The device needs the firmware quickly and after 3-5 seconds without it, 
> >it goes berserk.
> >
> In request_firmware_nowait, when kernel_thread failed, where fw_work is 
> freed ?
> Aren't there a memleack ?

I really do not know, as I have not looked at that code.  If you see
problems in it, please feel free to fix it up, as there is no living
maintainer for it anymore :(

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-07 17:46     ` Greg KH
@ 2005-11-07 18:47       ` matthieu castet
  2005-11-07 22:27       ` matthieu castet
  1 sibling, 0 replies; 34+ messages in thread
From: matthieu castet @ 2005-11-07 18:47 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-usb-devel, usbatm, Linux Kernel list

Hi Greg,

Greg KH wrote:

>>Thanks, but does userspace will retry if it fails the first time ?
>>The device needs the firmware quickly and after 3-5 seconds without it, 
>>it goes berserk.
> 
> 
> That sounds like a pretty broken device :)
If it was only that (don't work in bulk mode with down rate > 3Mbps ; 
empty iso urb report errors, ...)...



> This isn't BSD :)
> 
 > It's ok, as long as it is local, and it is what you want to do.  I don't
 > have a strong feeling toward it.

I finally merge them like other usbatm driver did.


The corrected version is ready, I will wait some time in order others 
developers could do final checking.
I will send it this evening or tomorrow.


Thanks

Matthieu

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-07 17:46     ` Greg KH
  2005-11-07 18:47       ` matthieu castet
@ 2005-11-07 22:27       ` matthieu castet
  2005-11-07 23:02         ` matthieu castet
  1 sibling, 1 reply; 34+ messages in thread
From: matthieu castet @ 2005-11-07 22:27 UTC (permalink / raw)
  To: Greg KH, Andrew Morton; +Cc: linux-usb-devel, usbatm, Linux Kernel list

[-- Attachment #1: Type: text/plain, Size: 270 bytes --]

Hi,

here the corrected version of ueagle-atm.

The comments of Adrew Morton and Greg KH have been applied.
We also fix a bug in the check_dsp routine (reported on our mailling 
list) and kill some unsued code.


Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr>

[-- Attachment #2: ueagle-atm.diff --]
[-- Type: text/x-patch, Size: 48413 bytes --]

diff -ruN -x '*.o*' -x '*.mod*' -x '*.ko*' linux-2.6.14/drivers/usb/atm.old/Kconfig linux-2.6.14/drivers/usb/atm/Kconfig
--- linux-2.6.14/drivers/usb/atm.old/Kconfig	2005-10-28 02:02:08.000000000 +0200
+++ linux-2.6.14/drivers/usb/atm/Kconfig	2005-10-30 00:17:33.000000000 +0200
@@ -44,6 +44,19 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called cxacru.
 
+config USB_UEAGLEATM
+	tristate "ADI 930 and eagle USB DSL modem"
+	depends on USB_ATM
+	select FW_LOADER
+	help
+	  Say Y here if you have an ADSL USB modem based on the ADI 930
+	  or eagle chipset. In order to use your modem you will need to
+	  install firmwares and CMV (Command Management Variables); see
+	  <https://gna.org/projects/ueagleatm/> for details.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ueagle-atm.
+	  
 config USB_XUSBATM
 	tristate "Other USB DSL modem support"
 	depends on USB_ATM
diff -ruN -x '*.o*' -x '*.mod*' -x '*.ko*' linux-2.6.14/drivers/usb/atm.old/Makefile linux-2.6.14/drivers/usb/atm/Makefile
--- linux-2.6.14/drivers/usb/atm.old/Makefile	2005-10-28 02:02:08.000000000 +0200
+++ linux-2.6.14/drivers/usb/atm/Makefile	2005-10-30 00:17:33.000000000 +0200
@@ -4,5 +4,6 @@
 
 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
diff -ruN -x '*.o*' -x '*.mod*' -x '*.ko*' linux-2.6.14/drivers/usb/atm.old/ueagle-atm.c linux-2.6.14/drivers/usb/atm/ueagle-atm.c
--- linux-2.6.14/drivers/usb/atm.old/ueagle-atm.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.14/drivers/usb/atm/ueagle-atm.c	2005-11-07 19:35:55.000000000 +0100
@@ -0,0 +1,1813 @@
+/*-
+ * Copyright (c) 2003, 2004
+ *	Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
+ *
+ * Copyright (c) 2005 Matthieu Castet <castet.matthieu@free.fr>
+ *
+ * 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * GPL license :
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *
+ * HISTORY : some part of the code was base on ueagle 1.3 BSD driver,
+ * Damien Bergamini agree to put his code under a DUAL GPL/BSD license.
+ *
+ * The rest of the code was was rewritten from scratch.
+ */
+
+#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/kthread.h>
+#include <linux/version.h>
+#include <asm/unaligned.h>
+
+#include "usbatm.h"
+
+#define EAGLEUSBVERSION "ueagle 1.1"
+
+
+/*
+ * Debug macros
+ */
+#define uea_dbg(usb_dev, format, args...)	\
+	do { \
+		if (debug >= 1) \
+			dev_dbg(&(usb_dev)->dev, \
+				"[ueagle-atm dbg] %s: " format, \
+					__FUNCTION__, ##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)
+
+#define uea_enters(usb_dev) \
+	uea_vdbg(usb_dev, "entering %s\n", __FUNCTION__)
+
+#define uea_leaves(usb_dev) \
+	uea_vdbg(usb_dev, "leaving  %s\n", __FUNCTION__)
+
+#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_info(usb_dev, format,args...) \
+	dev_info(&(usb_dev)->dev ,"[ueagle-atm] " format, ##args)
+
+struct uea_cmvs {
+	u32 address;
+	u16 offset;
+	u32 data;
+} __attribute__ ((packed));
+
+struct uea_softc {
+	struct usb_device *usb_dev;
+	struct usbatm_data *usbatm;
+
+	int modem_index;
+	unsigned int driver_info;
+
+	int booting;
+	int reset;
+
+	wait_queue_head_t sync_q;
+
+	struct task_struct *kthread;
+	u32 data;
+	wait_queue_head_t cmv_ack_wait;
+	int cmv_ack;
+
+	struct work_struct task;
+	u16 pageno;
+	u16 ovl;
+
+	const struct firmware *dsp_firm;
+	struct urb *urb_int;
+
+	u8 cmv_function;
+	u16 cmv_idx;
+	u32 cmv_address;
+	u16 cmv_offset;
+
+	/* keep in sync with eaglectl */
+	struct uea_stats {
+		struct {
+			u32 state;
+			u32 flags;
+			u32 mflags;
+			u32 vidcpe;
+			u32 vidco;
+			u32 dsrate;
+			u32 usrate;
+			u32 dsunc;
+			u32 usunc;
+			u32 dscorr;
+			u32 uscorr;
+			u32 txflow;
+			u32 rxflow;
+			u32 usattenuation;
+			u32 dsattenuation;
+			u32 dsmargin;
+			u32 usmargin;
+			u32 firmid;
+		} phy;
+	} stats;
+};
+
+/*
+ * Elsa IDs
+ */
+#define ELSA_VID		0x05CC
+#define ELSA_PID_PSTFIRM	0x3350
+#define ELSA_PID_PREFIRM	0x3351
+
+/*
+ * Sagem USB IDs
+ */
+#define EAGLE_VID		0x1110
+#define EAGLE_I_PID_PREFIRM	0x9010	/* Eagle I */
+#define EAGLE_I_PID_PSTFIRM	0x900F	/* Eagle I */
+
+#define EAGLE_IIC_PID_PREFIRM	0x9024	/* Eagle IIC */
+#define EAGLE_IIC_PID_PSTFIRM	0x9023	/* Eagle IIC */
+
+#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 */
+
+/*
+ * USR USB IDs
+ */
+#define USR_VID			0x0BAF
+#define MILLER_A_PID_PREFIRM	0x00F2
+#define MILLER_A_PID_PSTFIRM	0x00F1
+#define MILLER_B_PID_PREFIRM	0x00FA
+#define MILLER_B_PID_PSTFIRM	0x00F9
+#define HEINEKEN_A_PID_PREFIRM	0x00F6
+#define HEINEKEN_A_PID_PSTFIRM	0x00F5
+#define HEINEKEN_B_PID_PREFIRM	0x00F8
+#define HEINEKEN_B_PID_PSTFIRM	0x00F7
+
+#define PREFIRM 0
+#define PSTFIRM (1<<7)
+enum {
+	ADI930 = 0,
+	EAGLE_I,
+	EAGLE_II,
+	EAGLE_III
+};
+
+/* macros for both struct usb_device_id and struct uea_softc */
+#define UEA_IS_PREFIRM(x) \
+	(!((x)->driver_info & PSTFIRM))
+#define UEA_CHIP_VERSION(x) \
+	((x)->driver_info & 0xf)
+
+#define IS_ISDN(sc) \
+	(le16_to_cpu(sc->usb_dev->descriptor.bcdDevice) & 0x80)
+
+#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)
+
+/*
+ * 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_DIR "ueagle-atm/"
+#define NB_MODEM 4
+
+#define BULK_TIMEOUT 300
+#define CTRL_TIMEOUT 1000
+
+#define ACK_TIMEOUT msecs_to_jiffies(1500)
+
+#define UEA_INTR_IFACE_NO 	0
+#define UEA_US_IFACE_NO		1
+#define UEA_DS_IFACE_NO		2
+
+#define FASTEST_ISO_INTF	8
+
+#define UEA_BULK_DATA_PIPE	0x02
+#define UEA_IDMA_PIPE		0x04
+#define UEA_INTR_PIPE		0x04
+#define UEA_ISO_DATA_PIPE	0x08
+
+#define UEA_SET_BLOCK    	0x0001
+#define UEA_SET_MODE     	0x0003
+#define UEA_SET_2183_DATA	0x0004
+#define UEA_SET_TIMEOUT		0x0011
+
+#define UEA_LOOPBACK_OFF	0x0002
+#define UEA_LOOPBACK_ON		0x0003
+#define UEA_BOOT_IDMA		0x0006
+#define UEA_START_RESET		0x0007
+#define UEA_END_RESET		0x0008
+
+#define UEA_SWAP_MAILBOX	(0x3fcd | 0x4000)
+#define UEA_MPTX_START		(0x3fce | 0x4000)
+#define UEA_MPTX_MAILBOX	(0x3fd6 | 0x4000)
+#define UEA_MPRX_MAILBOX	(0x3fdf | 0x4000)
+
+/* structure describing a block within a DSP page */
+struct block_info {
+	__le16 wHdr;
+#define UEA_BIHDR 0xabcd
+	__le16 wAddress;
+	__le16 wSize;
+	__le16 wOvlOffset;
+	__le16 wOvl;		/* overlay */
+	__le16 wLast;
+} __attribute__ ((packed));
+#define BLOCK_INFO_SIZE 12
+
+/* 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 FUNCTION_SUBTYPE(f) ((f) & 0x0f)
+/* for MEMACCESS */
+#define REQUESTREAD	0x0
+#define REQUESTWRITE	0x1
+#define REPLYREAD	0x2
+#define REPLYWRITE	0x3
+/* for ADSLDIRECTIVE */
+#define KERNELREADY	0x0
+#define MODEMREADY	0x1
+
+#define MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf))
+	__le16 wIndex;
+	__le32 dwSymbolicAddress;
+#define 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')
+	__le16 wOffsetAddress;
+	__le32 dwData;
+} __attribute__ ((packed));
+#define CMV_SIZE 16
+
+/* structure representing swap information */
+struct swap_info {
+	__u8 bSwapPageNo;
+	__u8 bOvl;		/* overlay */
+} __attribute__ ((packed));
+
+/* structure representing interrupt data */
+struct intr_pkt {
+	__u8 bType;
+	__u8 bNotification;
+	__le16 wValue;
+	__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;
+
+		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
+
+static struct usb_driver uea_driver;
+static DECLARE_MUTEX(uea_semaphore);
+static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III"};
+
+static int modem_index;
+static unsigned int debug;
+static int sync_wait[NB_MODEM];
+static char *cmv_file[NB_MODEM];
+
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "module debug level (0=off,1=on,2=verbose)");
+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");
+
+#define UPDATE_ATM_STAT(type, val) \
+	do { \
+		if (sc->usbatm->atm_dev) \
+			sc->usbatm->atm_dev->type = val; \
+	} while (0)
+
+/* Firmware loading */
+#define LOAD_INTERNAL     0xA0
+#define F8051_USBCS       0x7f92
+
+/**
+ * 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)
+{
+	int ret = -ENOMEM;
+	u8 *xfer_buff;
+
+	xfer_buff = kmalloc(size, GFP_KERNEL);
+	if (xfer_buff) {
+		memcpy(xfer_buff, buff, size);
+		ret = usb_control_msg(usb,
+				      usb_sndctrlpipe(usb, 0),
+				      LOAD_INTERNAL,
+				      USB_DIR_OUT | USB_TYPE_VENDOR |
+				      USB_RECIP_DEVICE, addr, 0, xfer_buff,
+				      size, CTRL_TIMEOUT);
+		kfree(xfer_buff);
+	}
+
+	if (ret < 0)
+		return ret;
+
+	return (ret == size) ? 0 : -EIO;
+}
+
+static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *context)
+{
+	struct usb_device *usb = context;
+	u8 *pfw, value;
+	u32 crc = 0;
+	int ret, size;
+
+	uea_enters(usb);
+	if (!fw_entry) {
+		uea_err(usb, "firmware is not available\n");
+		goto err;
+	}
+
+	pfw = fw_entry->data;
+	size = fw_entry->size;
+
+	crc = FW_GET_LONG(pfw);
+	pfw += 4;
+	size -= 4;
+	if (crc32_be(0, pfw, size) != crc) {
+		uea_err(usb, "firmware is corrupted\n");
+		goto err;
+	}
+
+	/*
+	 * Start to upload formware : send reset
+	 */
+	value = 1;
+	ret = uea_send_modem_cmd(usb, F8051_USBCS, sizeof(value), &value);
+
+	if (ret < 0) {
+		uea_err(usb, "modem reset failed with error %d\n", ret);
+		goto err;
+	}
+
+	while (size > 0) {
+		u8 len = FW_GET_BYTE(pfw);
+		u16 add = FW_GET_WORD(pfw + 1);
+		ret = uea_send_modem_cmd(usb, add, len, pfw + 3);
+		if (ret < 0) {
+			uea_err(usb, "uploading firmware data failed "
+					"with error %d\n", ret);
+			goto err;
+		}
+		pfw += len + 3;
+		size -= len + 3;
+	}
+
+	/*
+	 * Tell the modem we finish : de-assert reset
+	 */
+	value = 0;
+	ret = uea_send_modem_cmd(usb, F8051_USBCS, 1, &value);
+	if (ret < 0)
+		uea_err(usb, "modem de-assert failed with error %d\n", ret);
+	else
+		uea_info(usb, "firmware uploaded\n");
+
+err:
+	uea_leaves(usb);
+}
+
+/**
+ * uea_load_firmware - Load usb firmware for pre-firmware devices.
+ */
+static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
+{
+	int ret;
+	char *fw_name = FW_DIR "eagle.fw";
+
+	uea_enters(usb);
+	uea_info(usb, "pre-firmware device, uploading firmware\n");
+
+	switch (ver) {
+	case ADI930:
+		fw_name = FW_DIR "adi930.fw";
+		break;
+	case EAGLE_I:
+		fw_name = FW_DIR "eagleI.fw";
+		break;
+	case EAGLE_II:
+		fw_name = FW_DIR "eagleII.fw";
+		break;
+	case EAGLE_III:
+		fw_name = FW_DIR "eagleIII.fw";
+		break;
+	}
+
+	ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, usb, uea_upload_pre_firmware);
+	if (ret)
+		uea_err(usb, "firmware %s is not available\n", fw_name);
+	else
+		uea_info(usb, "loading firmware %s\n", fw_name);
+
+	uea_leaves(usb);
+	return ret;
+}
+
+/* modem management : dsp firmware, send/read CMV, monitoring statistic
+ */
+
+/*
+ * Make sure that the DSP code provided is safe to use.
+ */
+static int check_dsp(u8 *dsp, unsigned int len)
+{
+	u8 pagecount, blockcount;
+	u16 blocksize;
+	u32 pageoffset;
+	unsigned int i, j, p, pp;
+
+	/* enough space for pagecount? */
+	if (len < 1)
+		return 1;
+
+	pagecount = FW_GET_BYTE(dsp);
+	p = 1;
+
+	/* enough space for page offsets? */
+	if (p + 4 * pagecount > len)
+		return 1;
+
+	for (i = 0; i < pagecount; i++) {
+
+		pageoffset = FW_GET_LONG(dsp + p);
+		p += 4;
+
+		if (pageoffset == 0)
+			continue;
+
+		/* enough space for blockcount? */
+		if (pageoffset >= len)
+			return 1;
+
+		pp = pageoffset;
+		blockcount = FW_GET_BYTE(dsp + pp);
+		pp += 1;
+
+		for (j = 0; j < blockcount; j++) {
+
+			/* enough space for block header? */
+			if (pp + 4 > len)
+				return 1;
+
+			pp += 2;	/* skip blockaddr */
+			blocksize = FW_GET_WORD(dsp + pp);
+			pp += 2;
+
+			/* enough space for block data? */
+			if (pp + blocksize > len)
+				return 1;
+
+			pp += blocksize;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * send data to the idma pipe
+ * */
+static int uea_idma_write(struct uea_softc *sc, void *data, u32 size)
+{
+	int ret = -ENOMEM;
+	u8 *xfer_buff;
+	int bytes_read;
+
+	xfer_buff = kmalloc(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);
+
+	kfree(xfer_buff);
+	if (ret < 0)
+		return ret;
+	if (size != bytes_read) {
+		uea_err(INS_TO_USBDEV(sc), "size != bytes_read %d %d\n", size,
+		       bytes_read);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int request_dsp(struct uea_softc *sc)
+{
+	int ret;
+	char *dsp_name;
+
+	if (UEA_CHIP_VERSION(sc) == ADI930) {
+		if (IS_ISDN(sc))
+			dsp_name = FW_DIR "DSP9i.bin";
+		else
+			dsp_name = FW_DIR "DSP9p.bin";
+	} else {
+		if (IS_ISDN(sc))
+			dsp_name = FW_DIR "DSPei.bin";
+		else
+			dsp_name = FW_DIR "DSPep.bin";
+	}
+	
+	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);
+		return ret;
+	}
+
+	if (check_dsp(sc->dsp_firm->data, sc->dsp_firm->size)) {
+		uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
+		       dsp_name);
+		release_firmware(sc->dsp_firm);
+		sc->dsp_firm = NULL;
+		return -EILSEQ;
+	}
+
+	return 0;
+}
+
+/*
+ * The uea_load_page() function must be called within a process context
+ */
+static void uea_load_page(void *xsc)
+{
+	struct uea_softc *sc = xsc;
+	u16 pageno = sc->pageno;
+	u16 ovl = sc->ovl;
+	struct block_info bi;
+
+	u8 *p;
+	u8 pagecount, blockcount;
+	u16 blockaddr, blocksize;
+	u32 pageoffset;
+	int i;
+
+	/* reload firmware when reboot start and it's loaded already */
+	if (ovl == 0 && 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 = sc->dsp_firm->data;
+	pagecount = FW_GET_BYTE(p);
+	p += 1;
+
+	if (pageno >= pagecount)
+		goto bad1;
+
+	p += 4 * pageno;
+	pageoffset = FW_GET_LONG(p);
+
+	if (pageoffset == 0)
+		goto bad1;
+
+	p = sc->dsp_firm->data + pageoffset;
+	blockcount = FW_GET_BYTE(p);
+	p += 1;
+
+	uea_dbg(INS_TO_USBDEV(sc),
+	       "sending %u blocks for DSP page %u\n", blockcount, pageno);
+
+	bi.wHdr = cpu_to_le16(UEA_BIHDR);
+	bi.wOvl = cpu_to_le16(ovl);
+	bi.wOvlOffset = cpu_to_le16(ovl | 0x8000);
+
+	for (i = 0; i < blockcount; i++) {
+		blockaddr = FW_GET_WORD(p);
+		p += 2;
+
+		blocksize = FW_GET_WORD(p);
+		p += 2;
+
+		bi.wSize = cpu_to_le16(blocksize);
+		bi.wAddress = cpu_to_le16(blockaddr);
+		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))
+			goto bad2;
+
+		/* send block data through the IDMA pipe */
+		if (uea_idma_write(sc, p, blocksize))
+			goto bad2;
+
+		p += blocksize;
+	}
+
+	return;
+
+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);
+}
+
+static inline void wake_up_cmv_ack(struct uea_softc *sc)
+{
+	sc->cmv_ack = 1;
+	wake_up(&sc->cmv_ack_wait);
+}
+
+static inline int wait_cmv_ack(struct uea_softc *sc)
+{
+	int ret = wait_event_timeout(sc->cmv_ack_wait,
+						   sc->cmv_ack, ACK_TIMEOUT);
+	sc->cmv_ack = 0;
+
+	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)
+{
+	u8 *xfer_buff;
+	int ret = -ENOMEM;
+
+	xfer_buff = kmalloc(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,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      value, index, xfer_buff, size, CTRL_TIMEOUT);
+
+	kfree(xfer_buff);
+	if (ret < 0) {
+		uea_err(INS_TO_USBDEV(sc), "usb_control_msg error %d\n", ret);
+		return ret;
+	}
+
+	if (ret != size) {
+		uea_err(INS_TO_USBDEV(sc),
+		       "usb_control_msg send only %d bytes (instead of %d)\n",
+		       ret, size);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int uea_cmv(struct uea_softc *sc,
+		u8 function, u32 address, u16 offset, u32 data)
+{
+	struct cmv cmv;
+	int ret;
+
+	/* 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;
+
+	cmv.wPreamble = cpu_to_le16(PREAMBLE);
+	cmv.bDirection = HOSTTOMODEM;
+	cmv.bFunction = function;
+	cmv.wIndex = cpu_to_le16(sc->cmv_idx);
+	put_unaligned(cpu_to_le32(address), &cmv.dwSymbolicAddress);
+	cmv.wOffsetAddress = cpu_to_le16(offset);
+	put_unaligned(cpu_to_le32(data >> 16 | data << 16), &cmv.dwData);
+
+	ret = uea_request(sc, UEA_SET_BLOCK, UEA_MPTX_START, CMV_SIZE, &cmv);
+	if (ret < 0)
+		return ret;
+	return wait_cmv_ack(sc);
+}
+
+static inline int uea_read_cmv(struct uea_softc *sc,
+		u32 address, u16 offset, u32 *data)
+{
+	int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, 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;
+
+	return ret;
+}
+
+static inline int uea_write_cmv(struct uea_softc *sc,
+		u32 address, u16 offset, u32 data)
+{
+	int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTWRITE),
+			  address, offset, data);
+	if (ret < 0)
+		uea_err(INS_TO_USBDEV(sc),
+			"writing cmv failed with error %d\n", ret);
+
+	return ret;
+}
+
+/*
+ * 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)
+{
+	u32 data;
+	int ret;
+
+	uea_enters(INS_TO_USBDEV(sc));
+	data = sc->stats.phy.state;
+	
+	ret = uea_read_cmv(sc, SA_STAT, 0, &sc->stats.phy.state);
+	if (ret < 0)
+		return ret;
+
+	switch (GET_STATUS(sc->stats.phy.state)) {
+	case 0:		/* not yet synchronized */
+		uea_dbg(INS_TO_USBDEV(sc),
+		       "modem not yet synchronized\n");
+		return 0;
+	
+	case 1:		/* initialization */
+		uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n");
+		return 0;
+
+	case 2:		/* operational */
+		uea_vdbg(INS_TO_USBDEV(sc), "modem operational\n");
+		break;
+
+	case 3:		/* fail ... */
+		uea_info(INS_TO_USBDEV(sc), "modem synchronization failed\n");
+		return -EAGAIN;
+
+	case 4 ... 6:	/* test state */
+		uea_warn(INS_TO_USBDEV(sc),
+				"modem in test mode - not supported\n");
+		return -EAGAIN;
+
+	case 7:		/* fast-retain ... */
+		uea_info(INS_TO_USBDEV(sc), "modem in fast-retain mode\n");
+		return 0;
+	default:
+		uea_err(INS_TO_USBDEV(sc), "modem invalid SW mode %d\n",
+			GET_STATUS(sc->stats.phy.state));
+		return -EAGAIN;
+	}
+	
+	if (GET_STATUS(data) != 2) {
+		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
+		 */
+		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);
+	}
+	
+	/* always update it as atm layer could not be init when we switch to
+	 * operational state
+	 */
+	UPDATE_ATM_STAT(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);
+	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 = %d\n",
+		       sc->stats.phy.flags);
+		return 0;
+	}
+
+	ret = uea_read_cmv(sc, 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":"");
+	}
+	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);
+	if (ret < 0)
+		return ret;
+	sc->stats.phy.dsattenuation = (data & 0xff) / 2;
+
+	ret = uea_read_cmv(sc, 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);
+	if (ret < 0)
+		return ret;
+	
+	ret = uea_read_cmv(sc, SA_DIAG, 49, &sc->stats.phy.usmargin);
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, SA_DIAG, 51, &sc->stats.phy.rxflow);
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, SA_DIAG, 52, &sc->stats.phy.txflow);
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, 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);
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, 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);
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, SA_INFO, 8, &sc->stats.phy.vidco);
+	if (ret < 0)
+		return ret;
+
+	ret = uea_read_cmv(sc, 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)
+{
+	int ret, size;
+	u8 *data;
+	char *file;
+	static char cmv_name[256] = FW_DIR;
+	
+	if (cmv_file[sc->modem_index] == NULL) {
+		if (UEA_CHIP_VERSION(sc) == ADI930)
+			file = (IS_ISDN(sc)) ? "CMV9i.bin" : "CMV9p.bin";
+		else
+			file = (IS_ISDN(sc)) ? "CMVei.bin" : "CMVep.bin";
+	} else
+		file = cmv_file[sc->modem_index];
+
+	strcpy(cmv_name, FW_DIR);
+	strlcat(cmv_name, file, sizeof(cmv_name));
+	
+	ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev);
+	if (ret < 0) {
+		uea_err(INS_TO_USBDEV(sc),
+		       "requesting firmware %s failed with error %d\n",
+		       cmv_name, ret);
+		return ret;
+	}
+
+	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;
+	}
+
+	*cmvs = (struct uea_cmvs *)(data + 1);
+	return *data;
+}
+
+/* Start boot post firmware modem:
+ * - send reset commands through usb control pipe
+ * - start workqueue for DSP loading
+ * - send CMV options to modem
+ */
+
+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;
+
+	uea_enters(INS_TO_USBDEV(sc));
+	uea_info(INS_TO_USBDEV(sc), "(re)booting started\n");
+
+	sc->booting = 1;
+	UPDATE_ATM_STAT(signal, ATM_PHY_SIG_LOST);
+
+	/* reset statistics */
+	memset(&sc->stats, 0, sizeof(struct uea_stats));
+
+	/* tell the modem that we want to boot in IDMA mode */
+	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 */
+	uea_request(sc, UEA_SET_MODE, UEA_START_RESET, 0, NULL);
+
+	/* original driver use 200ms, but windows driver use 100ms */
+	msleep(100);
+
+	/* 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);
+
+	msleep(1000);
+	sc->cmv_function = MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY);
+	sc->booting = 0;
+
+	/* start loading DSP */
+	sc->pageno = 0;
+	sc->ovl = 0;
+	schedule_work(&sc->task);
+
+	/* wait for modem ready CMV */
+	ret = wait_cmv_ack(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;
+
+	/* get options */
+ 	ret = len = request_cmvs(sc, &cmvs, &cmvs_fw);
+	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;
+}
+
+/*
+ * In case of an error wait 1s before rebooting the modem
+ * if the modem don't request reboot (-EAGAIN).
+ * Monitor the modem every 1s.
+ */
+
+static int uea_kthread(void *data)
+{
+	struct uea_softc *sc = data;
+	int ret = -EAGAIN;
+	
+	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);
+		if (ret != -EAGAIN)
+			msleep(1000);
+	}
+	uea_leaves(INS_TO_USBDEV(sc));
+	return ret;
+}
+
+/* Load second usb firmware for ADI930 chip */
+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";
+
+	uea_enters(INS_TO_USBDEV(sc));
+
+	ret = request_firmware(&fw_entry, fw_name, &sc->usb_dev->dev);
+	if (ret) {
+		uea_err(INS_TO_USBDEV(sc), "firmware %s is not available\n",
+		       fw_name);
+		goto err0;
+	}
+
+	pfw = fw_entry->data;
+	size = fw_entry->size;
+	if (size != 0x577B) {
+		uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
+		       fw_name);
+		ret = -EILSEQ;
+		goto err1;
+	}
+	for (u = 0; u < size; u += ln) {
+		ln = min(size - u, 64);
+		ret = uea_request(sc, 0xe, 0, ln, pfw + u);
+		if (ret < 0) {
+			uea_err(INS_TO_USBDEV(sc),
+			       "elsa download data failed (%d)\n", ret);
+			goto err1;
+		}
+	}
+
+	/* finish to send the fpga
+	 */
+	ret = uea_request(sc, 0xe, 1, 0, NULL);
+	if (ret < 0) {
+		uea_err(INS_TO_USBDEV(sc),
+				"elsa download data failed (%d)\n", ret);
+		goto err1;
+	}
+	
+	/*
+	 * 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);
+
+
+err1:
+	release_firmware(fw_entry);
+err0:
+	uea_leaves(INS_TO_USBDEV(sc));
+	return ret;
+}
+
+static void uea_dispatch_cmv(struct uea_softc *sc, struct cmv* cmv)
+{
+	uea_enters(INS_TO_USBDEV(sc));
+	if (le16_to_cpu(cmv->wPreamble) != PREAMBLE)
+		goto bad1;
+
+	if (cmv->bDirection != MODEMTOHOST)
+		goto bad1;
+
+	/* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to
+	 * the first MEMACESS cmv. Ignore it...
+	 */
+	if (cmv->bFunction != sc->cmv_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
+			goto bad2;
+	}
+
+	if (cmv->bFunction == MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY)) {
+		wake_up_cmv_ack(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)
+		goto bad2;
+
+	sc->data = le32_to_cpu(get_unaligned(&cmv->dwData));
+	sc->data = sc->data << 16 | sc->data >> 16;
+
+	wake_up_cmv_ack(sc);
+	return;
+
+bad2:
+	uea_err(INS_TO_USBDEV(sc), "unexpected cmv received,"
+			"Function : %d, Subfunction : %d\n",
+			FUNCTION_TYPE(cmv->bFunction),
+			FUNCTION_SUBTYPE(cmv->bFunction));
+	return;
+
+bad1:
+	uea_err(INS_TO_USBDEV(sc), "invalid cmv received, "
+			"wPreamble %d, bDirection %d\n",
+			le16_to_cpu(cmv->wPreamble), cmv->bDirection);
+}
+
+/*
+ * interrupt handler
+ */
+static void uea_intr(struct urb *urb, struct pt_regs *regs)
+{
+	struct uea_softc *sc = (struct uea_softc *)urb->context;
+	struct intr_pkt *intr;
+	uea_enters(INS_TO_USBDEV(sc));
+
+	if (urb->status < 0) {
+		uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n",
+		       urb->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;
+		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);
+		break;
+
+	case INT_INCOMINGCMV:
+		uea_dispatch_cmv(sc, &intr->u.s2.cmv);
+		break;
+
+	default:
+		uea_err(INS_TO_USBDEV(sc), "unknown intr %u\n",
+		       le16_to_cpu(intr->wInterrupt));
+	}
+
+resubmit:
+	usb_submit_urb(sc->urb_int, GFP_ATOMIC);
+}
+
+/*
+ * Start the modem : init the data and start kernel thread
+ */
+static int uea_boot(struct uea_softc *sc)
+{
+	int ret;
+	struct intr_pkt *intr;
+
+	uea_enters(INS_TO_USBDEV(sc));
+
+	INIT_WORK(&sc->task, uea_load_page, sc);
+	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);
+	if (!intr) {
+		uea_err(INS_TO_USBDEV(sc),
+		       "cannot allocate interrupt package\n");
+		uea_leaves(INS_TO_USBDEV(sc));
+		return -ENOMEM;
+	}
+
+	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;
+	}
+
+	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,
+			 sc->usb_dev->actconfig->interface[0]->altsetting[0].
+			 endpoint[0].desc.bInterval);
+
+	ret = usb_submit_urb(sc->urb_int, GFP_KERNEL);
+	if (ret < 0) {
+		uea_err(INS_TO_USBDEV(sc),
+		       "urb submition failed with error %d\n", ret);
+		goto err1;
+	}
+
+	sc->kthread = kthread_run(uea_kthread, sc, "ueagle-atm");
+	if (sc->kthread == ERR_PTR(-ENOMEM)) {
+		uea_err(INS_TO_USBDEV(sc), "failed to create thread\n");
+		goto err2;
+	}
+
+	uea_leaves(INS_TO_USBDEV(sc));
+	return 0;
+
+err2:
+	usb_kill_urb(sc->urb_int);
+err1:
+	kfree(intr);
+err:
+	usb_free_urb(sc->urb_int);
+	uea_leaves(INS_TO_USBDEV(sc));
+	return -ENOMEM;
+}
+
+/*
+ * Stop the modem : kill kernel thread and free data
+ */
+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_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL);
+
+	usb_kill_urb(sc->urb_int);
+	kfree(sc->urb_int->transfer_buffer);
+	usb_free_urb(sc->urb_int);
+
+	if (sc->dsp_firm)
+		release_firmware(sc->dsp_firm);
+	uea_leaves(INS_TO_USBDEV(sc));
+}
+
+/* syfs interface */
+static struct uea_softc *dev_to_uea(struct device *dev)
+{
+	struct usb_interface *intf;
+	struct usbatm_data *usbatm;
+
+	intf = to_usb_interface(dev);
+	if (!intf)
+		return NULL;
+
+	usbatm = usb_get_intfdata(intf);
+	if (!usbatm)
+		return NULL;
+
+	return usbatm->driver_data;
+}
+
+static ssize_t read_status(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	int ret = -ENODEV;
+	struct uea_softc *sc;
+	
+	down(&uea_semaphore);
+	sc = dev_to_uea(dev);
+	if (!sc)
+		goto out;
+	ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.state);
+out:
+	up(&uea_semaphore);
+	return ret;
+}
+
+static ssize_t reboot(struct device *dev, struct device_attribute *attr, 
+		const char *buf, size_t count)
+{
+	int ret = -ENODEV;
+	struct uea_softc *sc;
+
+	down(&uea_semaphore);
+	sc = dev_to_uea(dev);
+	if (!sc)
+		goto out;
+	sc->reset = 1;
+	ret = count;
+out:
+	up(&uea_semaphore);
+	return ret;
+}
+
+static DEVICE_ATTR(stat_status, S_IWUGO | S_IRUGO, read_status, reboot);
+
+static ssize_t read_human_status(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	int ret = -ENODEV;
+	struct uea_softc *sc;
+	
+	down(&uea_semaphore);
+	sc = dev_to_uea(dev);
+	if (!sc)
+		goto out;
+
+	switch (GET_STATUS(sc->stats.phy.state)) {
+	case 0:
+		ret = sprintf(buf, "Modem is booting\n");
+		break;
+	case 1:
+		ret = sprintf(buf, "Modem is initializing\n");
+		break;
+	case 2:
+		ret = sprintf(buf, "Modem is operational\n");
+		break;
+	default:
+		ret = sprintf(buf, "Modem synchronization failed\n");
+		break;
+	}
+out:
+	up(&uea_semaphore);
+	return ret;
+}
+
+static DEVICE_ATTR(stat_human_status, S_IWUGO | 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;
+	
+	down(&uea_semaphore);
+	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");
+out:
+	up(&uea_semaphore);
+	return ret;
+}
+
+static DEVICE_ATTR(stat_delin, S_IWUGO | S_IRUGO, read_delin, NULL);
+
+#define UEA_ATTR(name, reset) 					\
+								\
+static ssize_t read_##name(struct device *dev, 			\
+		struct device_attribute *attr, char *buf)	\
+{ 								\
+	int ret = -ENODEV; 					\
+	struct uea_softc *sc; 					\
+ 								\
+	down(&uea_semaphore); 					\
+	sc = dev_to_uea(dev);					\
+	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; 						\
+} 								\
+								\
+static DEVICE_ATTR(stat_##name, S_IRUGO, read_##name, NULL)
+
+UEA_ATTR(mflags, 1);
+UEA_ATTR(vidcpe, 0);
+UEA_ATTR(usrate, 0);
+UEA_ATTR(dsrate, 0);
+UEA_ATTR(usattenuation, 0);
+UEA_ATTR(dsattenuation, 0);
+UEA_ATTR(usmargin, 0);
+UEA_ATTR(dsmargin, 0);
+UEA_ATTR(txflow, 0);
+UEA_ATTR(rxflow, 0);
+UEA_ATTR(uscorr, 0);
+UEA_ATTR(dscorr, 0);
+UEA_ATTR(usunc, 0);
+UEA_ATTR(dsunc, 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];
+	int i;
+	if (usb_string
+	    (sc->usb_dev, sc->usb_dev->descriptor.iSerialNumber, mac_str,
+	     sizeof(mac_str)) != 2 * ETH_ALEN)
+		return 1;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		esi[i] = htoi(mac_str[2 * i]) * 16 + htoi(mac_str[2 * i + 1]);
+
+	return 0;
+}
+
+/* ATM stuff */
+static int uea_atm_open(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
+{
+	struct uea_softc *sc = usbatm->driver_data;
+
+	return uea_getesi(sc, atm_dev->esi);
+}
+
+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));
+
+	return 0;
+
+}
+
+static int claim_interface(struct usb_device *usb_dev,
+			   struct usbatm_data *usbatm, int ifnum)
+{
+	int ret;
+	struct usb_interface *intf = usb_ifnum_to_if(usb_dev, ifnum);
+
+	if (!intf) {
+		uea_err(usb_dev, "interface %d not found\n", ifnum);
+		return -ENODEV;
+	}
+
+	ret = usb_driver_claim_interface(&uea_driver, intf, usbatm);
+	if (ret != 0)
+		uea_err(usb_dev, "can't claim interface %d, error %d\n", ifnum,
+		       ret);
+	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 int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
+		   const struct usb_device_id *id, int *heavy)
+{
+	struct usb_device *usb = interface_to_usbdev(intf);
+	struct uea_softc *sc;
+	int ret, ifnum = intf->altsetting->desc.bInterfaceNumber;
+
+	uea_enters(usb);
+
+	/* interface 0 is for firmware/monitoring */
+	if (ifnum != UEA_INTR_IFACE_NO)
+		return -ENODEV;
+
+	*heavy = sync_wait[modem_index];
+
+	/* 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
+	 */
+	if (UEA_CHIP_VERSION(id) != ADI930) {
+		/* interface 2 is for inbound traffic */
+		ret = claim_interface(usb, usbatm, UEA_DS_IFACE_NO);
+		if (ret < 0)
+			return ret;
+	}
+
+	sc = kzalloc(sizeof(struct uea_softc), GFP_KERNEL);
+	if (!sc) {
+		uea_err(INS_TO_USBDEV(sc), "uea_init: not enough memory !\n");
+		return -ENOMEM;
+	}
+
+	sc->usb_dev = usb;
+	usbatm->driver_data = sc;
+	sc->usbatm = usbatm;
+	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;
+	}
+
+	create_fs_entries(sc, intf);
+	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);
+}
+
+static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
+{
+	struct uea_softc *sc = usbatm->driver_data;
+
+	destroy_fs_entries(sc, intf);
+	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,
+};
+
+static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *usb = interface_to_usbdev(intf);
+
+	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)]);
+
+	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);
+}
+
+static void uea_disconnect(struct usb_interface *intf)
+{
+	struct usb_device *usb = interface_to_usbdev(intf);
+	int ifnum = intf->altsetting->desc.bInterfaceNumber;
+	uea_enters(usb);
+
+	/* ADI930 has 2 interfaces and eagle 3 interfaces.
+	 * Pre-firmware device has one interface
+	 */
+	if (usb->config->desc.bNumInterfaces != 1 && ifnum == 0) {
+		down(&uea_semaphore);
+		usbatm_usb_disconnect(intf);
+		up(&uea_semaphore);
+		uea_info(usb, "ADSL device removed\n");
+	}
+
+	uea_leaves(usb);
+}
+
+/*
+ * 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 driver descriptor
+ */
+static struct usb_driver uea_driver = {
+	.owner = THIS_MODULE,
+	.name = "ueagle-atm",
+	.id_table = uea_ids,
+	.probe = uea_probe,
+	.disconnect = uea_disconnect,
+};
+
+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_AUTHOR("Damien Bergamini/Matthieu Castet/Stanislaw W. Gruszka");
+MODULE_DESCRIPTION("ADI 930/Eagle USB ADSL Modem driver");
+MODULE_LICENSE("Dual BSD/GPL");

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH]  Eagle and ADI 930 usb adsl modem driver
  2005-11-07 22:27       ` matthieu castet
@ 2005-11-07 23:02         ` matthieu castet
  0 siblings, 0 replies; 34+ messages in thread
From: matthieu castet @ 2005-11-07 23:02 UTC (permalink / raw)
  To: Greg KH; +Cc: Andrew Morton, linux-usb-devel, usbatm, Linux Kernel list

[-- Attachment #1: Type: text/plain, Size: 492 bytes --]

matthieu castet wrote:
> Hi,
> 
> here the corrected version of ueagle-atm.
> 
> The comments of Adrew Morton and Greg KH have been applied.
> We also fix a bug in the check_dsp routine (reported on our mailling 
> list) and kill some unsued code.
> 
> 
> Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr>
> 
Could you add this fix ?

More care on loading firmware, take into account fw->size can't be zero.

thanks

Matthieu


Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: ueagle-atm-hotfix.patch --]
[-- Type: text/x-patch; name="ueagle-atm-hotfix.patch", Size: 1405 bytes --]

--- linux-2.6.14/drivers/usb/atm/ueagle-atm.c	(révision 175)
+++ linux-2.6.14b/drivers/usb/atm/ueagle-atm.c	(copie de travail)
@@ -426,14 +426,14 @@
 
 	pfw = fw_entry->data;
 	size = fw_entry->size;
+	if (size < 4)
+		goto err_fw_corrupted;
 
 	crc = FW_GET_LONG(pfw);
 	pfw += 4;
 	size -= 4;
-	if (crc32_be(0, pfw, size) != crc) {
-		uea_err(usb, "firmware is corrupted\n");
-		goto err;
-	}
+	if (crc32_be(0, pfw, size) != crc)
+		goto err_fw_corrupted;
 
 	/*
 	 * Start to upload formware : send reset
@@ -446,9 +446,14 @@
 		goto err;
 	}
 
-	while (size > 0) {
+	while (size > 3) {
 		u8 len = FW_GET_BYTE(pfw);
 		u16 add = FW_GET_WORD(pfw + 1);
+
+		size -= len + 3;
+		if (size < 0)
+			goto err_fw_corrupted;
+
 		ret = uea_send_modem_cmd(usb, add, len, pfw + 3);
 		if (ret < 0) {
 			uea_err(usb, "uploading firmware data failed "
@@ -456,9 +461,11 @@
 			goto err;
 		}
 		pfw += len + 3;
-		size -= len + 3;
 	}
 
+	if (size != 0)
+		goto err_fw_corrupted;
+
 	/*
 	 * Tell the modem we finish : de-assert reset
 	 */
@@ -469,6 +476,11 @@
 	else
 		uea_info(usb, "firmware uploaded\n");
 
+	uea_leaves(usb);
+	return;
+
+err_fw_corrupted:
+	uea_err(usb, "firmware is corrupted\n");
 err:
 	uea_leaves(usb);
 }
@@ -522,10 +534,6 @@
 	u32 pageoffset;
 	unsigned int i, j, p, pp;
 
-	/* enough space for pagecount? */
-	if (len < 1)
-		return 1;
-
 	pagecount = FW_GET_BYTE(dsp);
 	p = 1;
 

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2005-11-07 23:02 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-10-29 22:37 [PATCH] Eagle and ADI 930 usb adsl modem driver matthieu castet
2005-10-31 23:58 ` Andrew Morton
2005-11-01 12:40   ` Duncan Sands
2005-11-01 13:04     ` David Woodhouse
2005-11-01 13:12       ` Marco d'Itri
2005-11-02  7:42       ` Duncan Sands
2005-11-02  7:45         ` David Woodhouse
2005-11-02  8:02           ` Duncan Sands
2005-11-02 10:46       ` Roman Kagan
2005-11-02 11:01         ` Duncan Sands
2005-11-01 13:49     ` matthieu castet
2005-11-02  5:29       ` Andrew Morton
2005-11-02 22:27         ` Greg KH
2005-11-02  7:45       ` Duncan Sands
2005-11-01 14:08   ` matthieu castet
2005-11-02  5:34     ` Andrew Morton
2005-11-02  7:47     ` Duncan Sands
2005-11-01 22:45 ` Greg KH
2005-11-02  7:54   ` Duncan Sands
2005-11-02  8:03     ` Greg KH
2005-11-02  8:45       ` [linux-usb-devel] " Oliver Neukum
2005-11-02  8:52         ` Duncan Sands
2005-11-02 21:39           ` Greg KH
2005-11-02 21:37         ` Greg KH
2005-11-02 15:56   ` Alan Stern
2005-11-02 20:15   ` matthieu castet
2005-11-02 21:18     ` matthieu castet
2005-11-07 17:47       ` Greg KH
2005-11-06 18:44     ` matthieu castet
2005-11-07 17:47       ` Greg KH
2005-11-07 17:46     ` Greg KH
2005-11-07 18:47       ` matthieu castet
2005-11-07 22:27       ` matthieu castet
2005-11-07 23:02         ` matthieu castet

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox