Merge branch 'virtex-for-2.6.25' of git://git.secretlab.ca/git/linux-2.6-virtex into...
[sfrench/cifs-2.6.git] / drivers / usb / atm / ueagle-atm.c
index 29807d048b0413ee0c83b2654dbdda560cf3a741..c5ec1a55eee3e9956806a1b48222fc67dc6e76f4 100644 (file)
@@ -2,7 +2,8 @@
  * Copyright (c) 2003, 2004
  *     Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
  *
- * Copyright (c) 2005 Matthieu Castet <castet.matthieu@free.fr>
+ * Copyright (c) 2005-2007 Matthieu Castet <castet.matthieu@free.fr>
+ * Copyright (c) 2005-2007 Stanislaw Gruszka <stf_xl@wp.pl>
  *
  * This software is available to you under a choice of one of two
  * licenses. You may choose to be licensed under the terms of the GNU
 #define uea_info(usb_dev, format,args...) \
        dev_info(&(usb_dev)->dev ,"[ueagle-atm] " format, ##args)
 
-struct uea_cmvs {
+struct intr_pkt;
+
+/* cmv's from firmware */
+struct uea_cmvs_v1 {
        u32 address;
        u16 offset;
        u32 data;
 } __attribute__ ((packed));
 
+struct uea_cmvs_v2 {
+       u32 group;
+       u32 address;
+       u32 offset;
+       u32 data;
+} __attribute__ ((packed));
+
+/* information about currently processed cmv */
+struct cmv_dsc_e1 {
+       u8 function;
+       u16 idx;
+       u32 address;
+       u16 offset;
+};
+
+struct cmv_dsc_e4 {
+       u16 function;
+       u16 offset;
+       u16 address;
+       u16 group;
+};
+
+union cmv_dsc {
+       struct cmv_dsc_e1 e1;
+       struct cmv_dsc_e4 e4;
+};
+
 struct uea_softc {
        struct usb_device *usb_dev;
        struct usbatm_data *usbatm;
 
        int modem_index;
        unsigned int driver_info;
+       int annex;
+#define ANNEXA 0
+#define ANNEXB 1
 
        int booting;
        int reset;
@@ -127,20 +161,23 @@ struct uea_softc {
 
        struct task_struct *kthread;
        u32 data;
-       wait_queue_head_t cmv_ack_wait;
+       u32 data1;
+
        int cmv_ack;
+       union cmv_dsc cmv_dsc;
 
        struct work_struct task;
+       struct workqueue_struct *work_q;
        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;
+       void (*dispatch_cmv) (struct uea_softc *, struct intr_pkt *);
+       void (*schedule_load_page) (struct uea_softc *, struct intr_pkt *);
+       int (*stat) (struct uea_softc *);
+       int (*send_cmvs) (struct uea_softc *);
 
        /* keep in sync with eaglectl */
        struct uea_stats {
@@ -174,10 +211,34 @@ struct uea_softc {
 #define ELSA_PID_PSTFIRM       0x3350
 #define ELSA_PID_PREFIRM       0x3351
 
+#define ELSA_PID_A_PREFIRM     0x3352
+#define ELSA_PID_A_PSTFIRM     0x3353
+#define ELSA_PID_B_PREFIRM     0x3362
+#define ELSA_PID_B_PSTFIRM     0x3363
+
 /*
- * Sagem USB IDs
+ * Devolo IDs : pots if (pid & 0x10)
  */
-#define EAGLE_VID              0x1110
+#define DEVOLO_VID                     0x1039
+#define DEVOLO_EAGLE_I_A_PID_PSTFIRM   0x2110
+#define DEVOLO_EAGLE_I_A_PID_PREFIRM   0x2111
+
+#define DEVOLO_EAGLE_I_B_PID_PSTFIRM   0x2100
+#define DEVOLO_EAGLE_I_B_PID_PREFIRM   0x2101
+
+#define DEVOLO_EAGLE_II_A_PID_PSTFIRM  0x2130
+#define DEVOLO_EAGLE_II_A_PID_PREFIRM  0x2131
+
+#define DEVOLO_EAGLE_II_B_PID_PSTFIRM  0x2120
+#define DEVOLO_EAGLE_II_B_PID_PREFIRM  0x2121
+
+/*
+ * Reference design USB IDs
+ */
+#define ANALOG_VID             0x1110
+#define ADI930_PID_PREFIRM     0x9001
+#define ADI930_PID_PSTFIRM     0x9000
+
 #define EAGLE_I_PID_PREFIRM    0x9010  /* Eagle I */
 #define EAGLE_I_PID_PSTFIRM    0x900F  /* Eagle I */
 
@@ -187,12 +248,12 @@ struct uea_softc {
 #define EAGLE_II_PID_PREFIRM   0x9022  /* Eagle II */
 #define EAGLE_II_PID_PSTFIRM   0x9021  /* Eagle II */
 
-/*
- *  Eagle III Pid
- */
 #define EAGLE_III_PID_PREFIRM  0x9032  /* Eagle III */
 #define EAGLE_III_PID_PSTFIRM  0x9031  /* Eagle III */
 
+#define EAGLE_IV_PID_PREFIRM   0x9042  /* Eagle IV */
+#define EAGLE_IV_PID_PSTFIRM   0x9041  /* Eagle IV */
+
 /*
  * USR USB IDs
  */
@@ -208,11 +269,15 @@ struct uea_softc {
 
 #define PREFIRM 0
 #define PSTFIRM (1<<7)
+#define AUTO_ANNEX_A (1<<8)
+#define AUTO_ANNEX_B (1<<9)
+
 enum {
        ADI930 = 0,
        EAGLE_I,
        EAGLE_II,
-       EAGLE_III
+       EAGLE_III,
+       EAGLE_IV
 };
 
 /* macros for both struct usb_device_id and struct uea_softc */
@@ -221,15 +286,18 @@ enum {
 #define UEA_CHIP_VERSION(x) \
        ((x)->driver_info & 0xf)
 
-#define IS_ISDN(usb_dev) \
-       (le16_to_cpu((usb_dev)->descriptor.bcdDevice) & 0x80)
+#define IS_ISDN(x) \
+       ((x)->annex & ANNEXB)
 
 #define INS_TO_USBDEV(ins) ins->usb_dev
 
 #define GET_STATUS(data) \
        ((data >> 8) & 0xf)
+
 #define IS_OPERATIONAL(sc) \
-       (GET_STATUS(sc->stats.phy.state) == 2)
+       ((UEA_CHIP_VERSION(sc) != EAGLE_IV) ? \
+       (GET_STATUS(sc->stats.phy.state) == 2) : \
+       (sc->stats.phy.state == 7))
 
 /*
  * Set of macros to handle unaligned data in the firmware blob.
@@ -259,7 +327,8 @@ enum {
 #define UEA_INTR_PIPE          0x04
 #define UEA_ISO_DATA_PIPE      0x08
 
-#define UEA_SET_BLOCK          0x0001
+#define UEA_E1_SET_BLOCK       0x0001
+#define UEA_E4_SET_BLOCK       0x002c
 #define UEA_SET_MODE           0x0003
 #define UEA_SET_2183_DATA      0x0004
 #define UEA_SET_TIMEOUT                0x0011
@@ -275,71 +344,179 @@ enum {
 #define UEA_MPTX_MAILBOX       (0x3fd6 | 0x4000)
 #define UEA_MPRX_MAILBOX       (0x3fdf | 0x4000)
 
-/* structure describing a block within a DSP page */
-struct block_info {
+/* block information in eagle4 dsp firmware  */
+struct block_index {
+       __le32 PageOffset;
+       __le32 NotLastBlock;
+       __le32 dummy;
+       __le32 PageSize;
+       __le32 PageAddress;
+       __le16 dummy1;
+       __le16 PageNumber;
+} __attribute__ ((packed));
+
+#define E4_IS_BOOT_PAGE(PageSize) ((le32_to_cpu(PageSize)) & 0x80000000)
+#define E4_PAGE_BYTES(PageSize) ((le32_to_cpu(PageSize) & 0x7fffffff) * 4)
+
+#define E4_L1_STRING_HEADER 0x10
+#define E4_MAX_PAGE_NUMBER 0x58
+#define E4_NO_SWAPPAGE_HEADERS 0x31
+
+/* l1_code is eagle4 dsp firmware format */
+struct l1_code {
+       u8 string_header[E4_L1_STRING_HEADER];
+       u8 page_number_to_block_index[E4_MAX_PAGE_NUMBER];
+       struct block_index page_header[E4_NO_SWAPPAGE_HEADERS];
+       u8 code [0];
+} __attribute__ ((packed));
+
+/* structures describing a block within a DSP page */
+struct block_info_e1 {
        __le16 wHdr;
-#define UEA_BIHDR 0xabcd
        __le16 wAddress;
        __le16 wSize;
        __le16 wOvlOffset;
        __le16 wOvl;            /* overlay */
        __le16 wLast;
 } __attribute__ ((packed));
-#define BLOCK_INFO_SIZE 12
+#define E1_BLOCK_INFO_SIZE 12
+
+struct block_info_e4 {
+       __be16 wHdr;
+       __u8 bBootPage;
+       __u8 bPageNumber;
+       __be32 dwSize;
+       __be32 dwAddress;
+       __be16 wReserved;
+} __attribute__ ((packed));
+#define E4_BLOCK_INFO_SIZE 14
 
-/* structure representing a CMV (Configuration and Management Variable) */
-struct cmv {
-       __le16 wPreamble;
-#define PREAMBLE 0x535c
-       __u8 bDirection;
-#define MODEMTOHOST 0x01
-#define HOSTTOMODEM 0x10
-       __u8 bFunction;
-#define FUNCTION_TYPE(f)    ((f) >> 4)
-#define MEMACCESS      0x1
-#define ADSLDIRECTIVE  0x7
+#define UEA_BIHDR 0xabcd
+#define UEA_RESERVED 0xffff
+
+/* constants describing cmv type */
+#define E1_PREAMBLE 0x535c
+#define E1_MODEMTOHOST 0x01
+#define E1_HOSTTOMODEM 0x10
+
+#define E1_MEMACCESS 0x1
+#define E1_ADSLDIRECTIVE 0x7
+#define E1_FUNCTION_TYPE(f) ((f) >> 4)
+#define E1_FUNCTION_SUBTYPE(f) ((f) & 0x0f)
+
+#define E4_MEMACCESS 0
+#define E4_ADSLDIRECTIVE 0xf
+#define E4_FUNCTION_TYPE(f) ((f) >> 8)
+#define E4_FUNCTION_SIZE(f) ((f) & 0x0f)
+#define E4_FUNCTION_SUBTYPE(f) (((f) >> 4) & 0x0f)
 
-#define FUNCTION_SUBTYPE(f) ((f) & 0x0f)
 /* for MEMACCESS */
-#define REQUESTREAD    0x0
-#define REQUESTWRITE   0x1
-#define REPLYREAD      0x2
-#define REPLYWRITE     0x3
+#define E1_REQUESTREAD 0x0
+#define E1_REQUESTWRITE        0x1
+#define E1_REPLYREAD   0x2
+#define E1_REPLYWRITE  0x3
+
+#define E4_REQUESTREAD 0x0
+#define E4_REQUESTWRITE        0x4
+#define E4_REPLYREAD   (E4_REQUESTREAD | 1)
+#define E4_REPLYWRITE  (E4_REQUESTWRITE | 1)
+
 /* for ADSLDIRECTIVE */
-#define KERNELREADY    0x0
-#define MODEMREADY     0x1
+#define E1_KERNELREADY 0x0
+#define E1_MODEMREADY  0x1
 
-#define MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf))
-       __le16 wIndex;
-       __le32 dwSymbolicAddress;
-#define MAKESA(a, b, c, d)                                             \
+#define E4_KERNELREADY 0x0
+#define E4_MODEMREADY  0x1
+
+#define E1_MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf))
+#define E4_MAKEFUNCTION(t, st, s) (((t) & 0xf) << 8 | ((st) & 0xf) << 4 | ((s) & 0xf))
+
+#define E1_MAKESA(a, b, c, d)                                          \
        (((c) & 0xff) << 24 |                                           \
         ((d) & 0xff) << 16 |                                           \
         ((a) & 0xff) << 8  |                                           \
         ((b) & 0xff))
-#define GETSA1(a) ((a >> 8) & 0xff)
-#define GETSA2(a) (a & 0xff)
-#define GETSA3(a) ((a >> 24) & 0xff)
-#define GETSA4(a) ((a >> 16) & 0xff)
-
-#define SA_CNTL MAKESA('C', 'N', 'T', 'L')
-#define SA_DIAG MAKESA('D', 'I', 'A', 'G')
-#define SA_INFO MAKESA('I', 'N', 'F', 'O')
-#define SA_OPTN MAKESA('O', 'P', 'T', 'N')
-#define SA_RATE MAKESA('R', 'A', 'T', 'E')
-#define SA_STAT MAKESA('S', 'T', 'A', 'T')
+
+#define E1_GETSA1(a) ((a >> 8) & 0xff)
+#define E1_GETSA2(a) (a & 0xff)
+#define E1_GETSA3(a) ((a >> 24) & 0xff)
+#define E1_GETSA4(a) ((a >> 16) & 0xff)
+
+#define E1_SA_CNTL E1_MAKESA('C', 'N', 'T', 'L')
+#define E1_SA_DIAG E1_MAKESA('D', 'I', 'A', 'G')
+#define E1_SA_INFO E1_MAKESA('I', 'N', 'F', 'O')
+#define E1_SA_OPTN E1_MAKESA('O', 'P', 'T', 'N')
+#define E1_SA_RATE E1_MAKESA('R', 'A', 'T', 'E')
+#define E1_SA_STAT E1_MAKESA('S', 'T', 'A', 'T')
+
+#define E4_SA_CNTL 1
+#define E4_SA_STAT 2
+#define E4_SA_INFO 3
+#define E4_SA_TEST 4
+#define E4_SA_OPTN 5
+#define E4_SA_RATE 6
+#define E4_SA_DIAG 7
+#define E4_SA_CNFG 8
+
+/* structures representing a CMV (Configuration and Management Variable) */
+struct cmv_e1 {
+       __le16 wPreamble;
+       __u8 bDirection;
+       __u8 bFunction;
+       __le16 wIndex;
+       __le32 dwSymbolicAddress;
        __le16 wOffsetAddress;
        __le32 dwData;
 } __attribute__ ((packed));
-#define CMV_SIZE 16
 
-/* structure representing swap information */
-struct swap_info {
+struct cmv_e4 {
+       __be16 wGroup;
+       __be16 wFunction;
+       __be16 wOffset;
+       __be16 wAddress;
+       __be32 dwData [6];
+} __attribute__ ((packed));
+
+/* structures representing swap information */
+struct swap_info_e1 {
        __u8 bSwapPageNo;
        __u8 bOvl;              /* overlay */
 } __attribute__ ((packed));
 
-/* structure representing interrupt data */
+struct swap_info_e4 {
+       __u8 bSwapPageNo;
+} __attribute__ ((packed));
+
+/* structures representing interrupt data */
+#define e1_bSwapPageNo u.e1.s1.swapinfo.bSwapPageNo
+#define e1_bOvl                u.e1.s1.swapinfo.bOvl
+#define e4_bSwapPageNo  u.e4.s1.swapinfo.bSwapPageNo
+
+#define INT_LOADSWAPPAGE 0x0001
+#define INT_INCOMINGCMV  0x0002
+
+union intr_data_e1 {
+       struct {
+               struct swap_info_e1 swapinfo;
+               __le16 wDataSize;
+       } __attribute__ ((packed)) s1;
+       struct {
+               struct cmv_e1 cmv;
+               __le16 wDataSize;
+       } __attribute__ ((packed)) s2;
+} __attribute__ ((packed));
+
+union intr_data_e4 {
+       struct {
+               struct swap_info_e4 swapinfo;
+               __le16 wDataSize;
+       } __attribute__ ((packed)) s1;
+       struct {
+               struct cmv_e4 cmv;
+               __le16 wDataSize;
+       } __attribute__ ((packed)) s2;
+} __attribute__ ((packed));
+
 struct intr_pkt {
        __u8 bType;
        __u8 bNotification;
@@ -347,43 +524,48 @@ struct intr_pkt {
        __le16 wIndex;
        __le16 wLength;
        __le16 wInterrupt;
-#define INT_LOADSWAPPAGE 0x0001
-#define INT_INCOMINGCMV  0x0002
        union {
-               struct {
-                       struct swap_info swapinfo;
-                       __le16 wDataSize;
-               } __attribute__ ((packed)) s1;
-
-               struct {
-                       struct cmv cmv;
-                       __le16 wDataSize;
-               } __attribute__ ((packed)) s2;
-       } __attribute__ ((packed)) u;
-#define bSwapPageNo    u.s1.swapinfo.bSwapPageNo
-#define bOvl           u.s1.swapinfo.bOvl
+               union intr_data_e1 e1;
+               union intr_data_e4 e4;
+       } u;
 } __attribute__ ((packed));
-#define INTR_PKT_SIZE 28
+
+#define E1_INTR_PKT_SIZE 28
+#define E4_INTR_PKT_SIZE 64
 
 static struct usb_driver uea_driver;
 static DEFINE_MUTEX(uea_mutex);
-static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III"};
+static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III", "Eagle IV"};
 
 static int modem_index;
 static unsigned int debug;
-static int use_iso[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = 1};
+static unsigned int altsetting[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = FASTEST_ISO_INTF};
 static int sync_wait[NB_MODEM];
 static char *cmv_file[NB_MODEM];
+static int annex[NB_MODEM];
 
 module_param(debug, uint, 0644);
 MODULE_PARM_DESC(debug, "module debug level (0=off,1=on,2=verbose)");
-module_param_array(use_iso, bool, NULL, 0644);
-MODULE_PARM_DESC(use_iso, "use isochronous usb pipe for incoming traffic");
+module_param_array(altsetting, uint, NULL, 0644);
+MODULE_PARM_DESC(altsetting, "alternate setting for incoming traffic: 0=bulk, "
+                            "1=isoc slowest, ... , 8=isoc fastest (default)");
 module_param_array(sync_wait, bool, NULL, 0644);
 MODULE_PARM_DESC(sync_wait, "wait the synchronisation before starting ATM");
 module_param_array(cmv_file, charp, NULL, 0644);
 MODULE_PARM_DESC(cmv_file,
                "file name with configuration and management variables");
+module_param_array(annex, uint, NULL, 0644);
+MODULE_PARM_DESC(annex,
+                 "manually set annex a/b (0=auto, 1=annex a, 2=annex b)");
+
+#define uea_wait(sc, cond, timeo) \
+({ \
+       int _r = wait_event_interruptible_timeout(sc->sync_q, \
+                       (cond) || kthread_should_stop(), timeo); \
+       if (kthread_should_stop()) \
+               _r = -ENODEV; \
+       _r; \
+})
 
 #define UPDATE_ATM_STAT(type, val) \
        do { \
@@ -519,6 +701,9 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
        case EAGLE_III:
                fw_name = FW_DIR "eagleIII.fw";
                break;
+       case EAGLE_IV:
+               fw_name = FW_DIR "eagleIV.fw";
+               break;
        }
 
        ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, usb, uea_upload_pre_firmware);
@@ -537,7 +722,7 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
 /*
  * Make sure that the DSP code provided is safe to use.
  */
-static int check_dsp(u8 *dsp, unsigned int len)
+static int check_dsp_e1(u8 *dsp, unsigned int len)
 {
        u8 pagecount, blockcount;
        u16 blocksize;
@@ -588,6 +773,51 @@ static int check_dsp(u8 *dsp, unsigned int len)
        return 0;
 }
 
+static int check_dsp_e4(u8 *dsp, int len)
+{
+       int i;
+       struct l1_code *p = (struct l1_code *) dsp;
+       unsigned int sum = p->code - dsp;
+
+       if (len < sum)
+               return 1;
+
+       if (strcmp("STRATIPHY ANEXA", p->string_header) != 0 &&
+           strcmp("STRATIPHY ANEXB", p->string_header) != 0)
+               return 1;
+
+       for (i = 0; i < E4_MAX_PAGE_NUMBER; i++) {
+               struct block_index *blockidx;
+               u8 blockno = p->page_number_to_block_index[i];
+               if (blockno >= E4_NO_SWAPPAGE_HEADERS)
+                       continue;
+
+               do {
+                       u64 l;
+
+                       if (blockno >= E4_NO_SWAPPAGE_HEADERS)
+                               return 1;
+
+                       blockidx = &p->page_header[blockno++];
+                       if ((u8 *)(blockidx + 1) - dsp  >= len)
+                               return 1;
+
+                       if (le16_to_cpu(blockidx->PageNumber) != i)
+                               return 1;
+
+                       l = E4_PAGE_BYTES(blockidx->PageSize);
+                       sum += l;
+                       l += le32_to_cpu(blockidx->PageOffset);
+                       if (l > len)
+                               return 1;
+
+               /* zero is zero regardless endianes */
+               } while (blockidx->NotLastBlock);
+       }
+
+       return (sum == len) ? 0 : 1;
+}
+
 /*
  * send data to the idma pipe
  * */
@@ -624,13 +854,18 @@ static int request_dsp(struct uea_softc *sc)
        int ret;
        char *dsp_name;
 
-       if (UEA_CHIP_VERSION(sc) == ADI930) {
-               if (IS_ISDN(sc->usb_dev))
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+               if (IS_ISDN(sc))
+                       dsp_name = FW_DIR "DSP4i.bin";
+               else
+                       dsp_name = FW_DIR "DSP4p.bin";
+       } else 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->usb_dev))
+               if (IS_ISDN(sc))
                        dsp_name = FW_DIR "DSPei.bin";
                else
                        dsp_name = FW_DIR "DSPep.bin";
@@ -640,11 +875,16 @@ static int request_dsp(struct uea_softc *sc)
        if (ret < 0) {
                uea_err(INS_TO_USBDEV(sc),
                       "requesting firmware %s failed with error %d\n",
-                      dsp_name, ret);
+                       dsp_name, ret);
                return ret;
        }
 
-       if (check_dsp(sc->dsp_firm->data, sc->dsp_firm->size)) {
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV)
+               ret = check_dsp_e4(sc->dsp_firm->data, sc->dsp_firm->size);
+       else
+               ret = check_dsp_e1(sc->dsp_firm->data, sc->dsp_firm->size);
+
+       if (ret) {
                uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
                       dsp_name);
                release_firmware(sc->dsp_firm);
@@ -658,12 +898,12 @@ static int request_dsp(struct uea_softc *sc)
 /*
  * The uea_load_page() function must be called within a process context
  */
-static void uea_load_page(struct work_struct *work)
+static void uea_load_page_e1(struct work_struct *work)
 {
        struct uea_softc *sc = container_of(work, struct uea_softc, task);
        u16 pageno = sc->pageno;
        u16 ovl = sc->ovl;
-       struct block_info bi;
+       struct block_info_e1 bi;
 
        u8 *p;
        u8 pagecount, blockcount;
@@ -716,7 +956,7 @@ static void uea_load_page(struct work_struct *work)
                bi.wLast = cpu_to_le16((i == blockcount - 1) ? 1 : 0);
 
                /* send block info through the IDMA pipe */
-               if (uea_idma_write(sc, &bi, BLOCK_INFO_SIZE))
+               if (uea_idma_write(sc, &bi, E1_BLOCK_INFO_SIZE))
                        goto bad2;
 
                /* send block data through the IDMA pipe */
@@ -735,17 +975,114 @@ bad1:
        uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n", pageno);
 }
 
+static void __uea_load_page_e4(struct uea_softc *sc, u8 pageno, int boot)
+{
+       struct block_info_e4 bi;
+       struct block_index *blockidx;
+       struct l1_code *p = (struct l1_code *) sc->dsp_firm->data;
+       u8 blockno = p->page_number_to_block_index[pageno];
+
+       bi.wHdr = cpu_to_be16(UEA_BIHDR);
+       bi.bBootPage = boot;
+       bi.bPageNumber = pageno;
+       bi.wReserved = cpu_to_be16(UEA_RESERVED);
+
+       do {
+               u8 *blockoffset;
+               unsigned int blocksize;
+
+               blockidx = &p->page_header[blockno];
+               blocksize = E4_PAGE_BYTES(blockidx->PageSize);
+               blockoffset = sc->dsp_firm->data + le32_to_cpu(blockidx->PageOffset);
+
+               bi.dwSize = cpu_to_be32(blocksize);
+               bi.dwAddress = swab32(blockidx->PageAddress);
+
+               uea_dbg(INS_TO_USBDEV(sc),
+                      "sending block %u for DSP page %u size %u address %x\n",
+                      blockno, pageno, blocksize, le32_to_cpu(blockidx->PageAddress));
+
+               /* send block info through the IDMA pipe */
+               if (uea_idma_write(sc, &bi, E4_BLOCK_INFO_SIZE))
+                       goto bad;
+
+               /* send block data through the IDMA pipe */
+               if (uea_idma_write(sc, blockoffset, blocksize))
+                       goto bad;
+
+               blockno++;
+       } while (blockidx->NotLastBlock);
+
+       return;
+
+bad:
+       uea_err(INS_TO_USBDEV(sc), "sending DSP block %u failed\n", blockno);
+       return;
+}
+
+static void uea_load_page_e4(struct work_struct *work)
+{
+       struct uea_softc *sc = container_of(work, struct uea_softc, task);
+       u8 pageno = sc->pageno;
+       int i;
+       struct block_info_e4 bi;
+       struct l1_code *p;
+
+       uea_dbg(INS_TO_USBDEV(sc), "sending DSP page %u\n", pageno);
+
+       /* reload firmware when reboot start and it's loaded already */
+       if (pageno == 0 && sc->dsp_firm) {
+               release_firmware(sc->dsp_firm);
+               sc->dsp_firm = NULL;
+       }
+
+       if (sc->dsp_firm == NULL && request_dsp(sc) < 0)
+               return;
+
+       p = (struct l1_code *) sc->dsp_firm->data;
+       if (pageno >= p->page_header[0].PageNumber) {
+               uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n", pageno);
+               return;
+       }
+
+       if (pageno != 0) {
+               __uea_load_page_e4(sc, pageno, 0);
+               return;
+       }
+
+       uea_dbg(INS_TO_USBDEV(sc),
+              "sending Main DSP page %u\n", p->page_header[0].PageNumber);
+
+       for (i = 0; i < le16_to_cpu(p->page_header[0].PageNumber); i++) {
+               if (E4_IS_BOOT_PAGE(p->page_header[i].PageSize))
+                       __uea_load_page_e4(sc, i, 1);
+       }
+
+       uea_dbg(INS_TO_USBDEV(sc),"sending start bi\n");
+
+       bi.wHdr = cpu_to_be16(UEA_BIHDR);
+       bi.bBootPage = 0;
+       bi.bPageNumber = 0xff;
+       bi.wReserved = cpu_to_be16(UEA_RESERVED);
+       bi.dwSize = cpu_to_be32(E4_PAGE_BYTES(p->page_header[0].PageSize));
+       bi.dwAddress = swab32(p->page_header[0].PageAddress);
+
+       /* send block info through the IDMA pipe */
+       if (uea_idma_write(sc, &bi, E4_BLOCK_INFO_SIZE))
+               uea_err(INS_TO_USBDEV(sc), "sending DSP start bi failed\n");
+}
+
 static inline void wake_up_cmv_ack(struct uea_softc *sc)
 {
        BUG_ON(sc->cmv_ack);
        sc->cmv_ack = 1;
-       wake_up(&sc->cmv_ack_wait);
+       wake_up(&sc->sync_q);
 }
 
 static inline int wait_cmv_ack(struct uea_softc *sc)
 {
-       int ret = wait_event_interruptible_timeout(sc->cmv_ack_wait,
-                                                  sc->cmv_ack, ACK_TIMEOUT);
+       int ret = uea_wait(sc, sc->cmv_ack , ACK_TIMEOUT);
+
        sc->cmv_ack = 0;
 
        uea_dbg(INS_TO_USBDEV(sc), "wait_event_timeout : %d ms\n",
@@ -792,33 +1129,68 @@ static int uea_request(struct uea_softc *sc,
        return 0;
 }
 
-static int uea_cmv(struct uea_softc *sc,
+static int uea_cmv_e1(struct uea_softc *sc,
                u8 function, u32 address, u16 offset, u32 data)
 {
-       struct cmv cmv;
+       struct cmv_e1 cmv;
        int ret;
 
        uea_enters(INS_TO_USBDEV(sc));
        uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, "
                        "offset : 0x%04x, data : 0x%08x\n",
-                       FUNCTION_TYPE(function), FUNCTION_SUBTYPE(function),
-                       GETSA1(address), GETSA2(address), GETSA3(address),
-                       GETSA4(address), offset, data);
+                       E1_FUNCTION_TYPE(function), E1_FUNCTION_SUBTYPE(function),
+                       E1_GETSA1(address), E1_GETSA2(address), E1_GETSA3(address),
+                       E1_GETSA4(address), offset, data);
+
        /* we send a request, but we expect a reply */
-       sc->cmv_function = function | 0x2;
-       sc->cmv_idx++;
-       sc->cmv_address = address;
-       sc->cmv_offset = offset;
+       sc->cmv_dsc.e1.function = function | 0x2;
+       sc->cmv_dsc.e1.idx++;
+       sc->cmv_dsc.e1.address = address;
+       sc->cmv_dsc.e1.offset = offset;
 
-       cmv.wPreamble = cpu_to_le16(PREAMBLE);
-       cmv.bDirection = HOSTTOMODEM;
+       cmv.wPreamble = cpu_to_le16(E1_PREAMBLE);
+       cmv.bDirection = E1_HOSTTOMODEM;
        cmv.bFunction = function;
-       cmv.wIndex = cpu_to_le16(sc->cmv_idx);
+       cmv.wIndex = cpu_to_le16(sc->cmv_dsc.e1.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);
+       ret = uea_request(sc, UEA_E1_SET_BLOCK, UEA_MPTX_START, sizeof(cmv), &cmv);
+       if (ret < 0)
+               return ret;
+       ret = wait_cmv_ack(sc);
+       uea_leaves(INS_TO_USBDEV(sc));
+       return ret;
+}
+
+static int uea_cmv_e4(struct uea_softc *sc,
+               u16 function, u16 group, u16 address, u16 offset, u32 data)
+{
+       struct cmv_e4 cmv;
+       int ret;
+
+       uea_enters(INS_TO_USBDEV(sc));
+       memset(&cmv, 0, sizeof(cmv));
+
+       uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Group : 0x%04x, "
+                "Address : 0x%04x, offset : 0x%04x, data : 0x%08x\n",
+                E4_FUNCTION_TYPE(function), E4_FUNCTION_SUBTYPE(function),
+                group, address, offset, data);
+
+       /* we send a request, but we expect a reply */
+       sc->cmv_dsc.e4.function = function | (0x1 << 4);
+       sc->cmv_dsc.e4.offset = offset;
+       sc->cmv_dsc.e4.address = address;
+       sc->cmv_dsc.e4.group = group;
+
+       cmv.wFunction = cpu_to_be16(function);
+       cmv.wGroup = cpu_to_be16(group);
+       cmv.wAddress = cpu_to_be16(address);
+       cmv.wOffset = cpu_to_be16(offset);
+       cmv.dwData[0] = cpu_to_be32(data);
+
+       ret = uea_request(sc, UEA_E4_SET_BLOCK, UEA_MPTX_START, sizeof(cmv), &cmv);
        if (ret < 0)
                return ret;
        ret = wait_cmv_ack(sc);
@@ -826,10 +1198,10 @@ static int uea_cmv(struct uea_softc *sc,
        return ret;
 }
 
-static inline int uea_read_cmv(struct uea_softc *sc,
+static inline int uea_read_cmv_e1(struct uea_softc *sc,
                u32 address, u16 offset, u32 *data)
 {
-       int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTREAD),
+       int ret = uea_cmv_e1(sc, E1_MAKEFUNCTION(E1_MEMACCESS, E1_REQUESTREAD),
                          address, offset, 0);
        if (ret < 0)
                uea_err(INS_TO_USBDEV(sc),
@@ -840,10 +1212,27 @@ static inline int uea_read_cmv(struct uea_softc *sc,
        return ret;
 }
 
-static inline int uea_write_cmv(struct uea_softc *sc,
+static inline int uea_read_cmv_e4(struct uea_softc *sc,
+               u8 size, u16 group, u16 address, u16 offset, u32 *data)
+{
+       int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, E4_REQUESTREAD, size),
+                         group, address, offset, 0);
+       if (ret < 0)
+               uea_err(INS_TO_USBDEV(sc),
+                       "reading cmv failed with error %d\n", ret);
+       else {
+               *data = sc->data;
+               /* size is in 16-bit word quantities */
+               if (size > 2)
+                       *(data + 1) = sc->data1;
+       }
+       return ret;
+}
+
+static inline int uea_write_cmv_e1(struct uea_softc *sc,
                u32 address, u16 offset, u32 data)
 {
-       int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTWRITE),
+       int ret = uea_cmv_e1(sc, E1_MAKEFUNCTION(E1_MEMACCESS, E1_REQUESTWRITE),
                          address, offset, data);
        if (ret < 0)
                uea_err(INS_TO_USBDEV(sc),
@@ -852,12 +1241,48 @@ static inline int uea_write_cmv(struct uea_softc *sc,
        return ret;
 }
 
+static inline int uea_write_cmv_e4(struct uea_softc *sc,
+               u8 size, u16 group, u16 address, u16 offset, u32 data)
+{
+       int ret = uea_cmv_e4(sc, E4_MAKEFUNCTION(E4_MEMACCESS, E4_REQUESTWRITE, size),
+                         group, address, offset, data);
+       if (ret < 0)
+               uea_err(INS_TO_USBDEV(sc),
+                       "writing cmv failed with error %d\n", ret);
+
+       return ret;
+}
+
+static void uea_set_bulk_timeout(struct uea_softc *sc, u32 dsrate)
+{
+       int ret;
+       u16 timeout;
+
+       /* in bulk mode the modem have problem with high rate
+        * changing internal timing could improve things, but the
+        * value is misterious.
+        * ADI930 don't support it (-EPIPE error).
+        */
+
+       if (UEA_CHIP_VERSION(sc) == ADI930 ||
+           altsetting[sc->modem_index] > 0 ||
+           sc->stats.phy.dsrate == dsrate)
+               return;
+
+       /* Original timming (1Mbit/s) from ADI (used in windows driver) */
+       timeout = (dsrate <= 1024*1024) ? 0 : 1;
+       ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL);
+       uea_info(INS_TO_USBDEV(sc), "setting new timeout %d%s\n",
+                timeout,  ret < 0 ? " failed" : "");
+
+}
+
 /*
  * Monitor the modem and update the stat
  * return 0 if everything is ok
  * return < 0 if an error occurs (-EAGAIN reboot needed)
  */
-static int uea_stat(struct uea_softc *sc)
+static int uea_stat_e1(struct uea_softc *sc)
 {
        u32 data;
        int ret;
@@ -865,7 +1290,7 @@ static int uea_stat(struct uea_softc *sc)
        uea_enters(INS_TO_USBDEV(sc));
        data = sc->stats.phy.state;
 
-       ret = uea_read_cmv(sc, SA_STAT, 0, &sc->stats.phy.state);
+       ret = uea_read_cmv_e1(sc, E1_SA_STAT, 0, &sc->stats.phy.state);
        if (ret < 0)
                return ret;
 
@@ -885,7 +1310,7 @@ static int uea_stat(struct uea_softc *sc)
 
        case 3:         /* fail ... */
                uea_info(INS_TO_USBDEV(sc), "modem synchronization failed"
-                               " (may be try other cmv/dsp)\n");
+                                       " (may be try other cmv/dsp)\n");
                return -EAGAIN;
 
        case 4 ... 6:   /* test state */
@@ -923,7 +1348,7 @@ static int uea_stat(struct uea_softc *sc)
        /* wake up processes waiting for synchronization */
        wake_up(&sc->sync_q);
 
-       ret = uea_read_cmv(sc, SA_DIAG, 2, &sc->stats.phy.flags);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 2, &sc->stats.phy.flags);
        if (ret < 0)
                return ret;
        sc->stats.phy.mflags |= sc->stats.phy.flags;
@@ -937,105 +1362,223 @@ static int uea_stat(struct uea_softc *sc)
                return 0;
        }
 
-       ret = uea_read_cmv(sc, SA_RATE, 0, &data);
+       ret = uea_read_cmv_e1(sc, E1_SA_RATE, 0, &data);
        if (ret < 0)
                return ret;
 
-       /* in bulk mode the modem have problem with high rate
-        * changing internal timing could improve things, but the
-        * value is misterious.
-        * ADI930 don't support it (-EPIPE error).
-        */
-       if (UEA_CHIP_VERSION(sc) != ADI930
-                   && !use_iso[sc->modem_index]
-                   && sc->stats.phy.dsrate != (data >> 16) * 32) {
-               /* Original timming from ADI(used in windows driver)
-                * 0x20ffff>>16 * 32 = 32 * 32 = 1Mbits
-                */
-               u16 timeout = (data <= 0x20ffff) ? 0 : 1;
-               ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL);
-               uea_info(INS_TO_USBDEV(sc),
-                               "setting new timeout %d%s\n", timeout,
-                               ret < 0?" failed":"");
-       }
+       uea_set_bulk_timeout(sc, (data >> 16) * 32);
        sc->stats.phy.dsrate = (data >> 16) * 32;
        sc->stats.phy.usrate = (data & 0xffff) * 32;
        UPDATE_ATM_STAT(link_rate, sc->stats.phy.dsrate * 1000 / 424);
 
-       ret = uea_read_cmv(sc, SA_DIAG, 23, &data);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 23, &data);
        if (ret < 0)
                return ret;
        sc->stats.phy.dsattenuation = (data & 0xff) / 2;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 47, &data);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 47, &data);
        if (ret < 0)
                return ret;
        sc->stats.phy.usattenuation = (data & 0xff) / 2;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 25, &sc->stats.phy.dsmargin);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 25, &sc->stats.phy.dsmargin);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 49, &sc->stats.phy.usmargin);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 49, &sc->stats.phy.usmargin);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 51, &sc->stats.phy.rxflow);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 51, &sc->stats.phy.rxflow);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 52, &sc->stats.phy.txflow);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 52, &sc->stats.phy.txflow);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 54, &sc->stats.phy.dsunc);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 54, &sc->stats.phy.dsunc);
        if (ret < 0)
                return ret;
 
        /* only for atu-c */
-       ret = uea_read_cmv(sc, SA_DIAG, 58, &sc->stats.phy.usunc);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 58, &sc->stats.phy.usunc);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_DIAG, 53, &sc->stats.phy.dscorr);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 53, &sc->stats.phy.dscorr);
        if (ret < 0)
                return ret;
 
        /* only for atu-c */
-       ret = uea_read_cmv(sc, SA_DIAG, 57, &sc->stats.phy.uscorr);
+       ret = uea_read_cmv_e1(sc, E1_SA_DIAG, 57, &sc->stats.phy.uscorr);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_INFO, 8, &sc->stats.phy.vidco);
+       ret = uea_read_cmv_e1(sc, E1_SA_INFO, 8, &sc->stats.phy.vidco);
        if (ret < 0)
                return ret;
 
-       ret = uea_read_cmv(sc, SA_INFO, 13, &sc->stats.phy.vidcpe);
+       ret = uea_read_cmv_e1(sc, E1_SA_INFO, 13, &sc->stats.phy.vidcpe);
        if (ret < 0)
                return ret;
 
        return 0;
 }
 
-static int request_cmvs(struct uea_softc *sc,
-                struct uea_cmvs **cmvs, const struct firmware **fw)
+static int uea_stat_e4(struct uea_softc *sc)
 {
-       int ret, size;
-       u8 *data;
+       u32 data;
+       u32 tmp_arr[2];
+       int ret;
+
+       uea_enters(INS_TO_USBDEV(sc));
+       data = sc->stats.phy.state;
+
+       /* XXX only need to be done before operationnal... */
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_STAT, 0, 0, &sc->stats.phy.state);
+       if (ret < 0)
+               return ret;
+
+       switch (sc->stats.phy.state) {
+               case 0x0:       /* not yet synchronized */
+               case 0x1:
+               case 0x3:
+               case 0x4:
+                       uea_dbg(INS_TO_USBDEV(sc), "modem not yet synchronized\n");
+                       return 0;
+               case 0x5:       /* initialization */
+               case 0x6:
+               case 0x9:
+               case 0xa:
+                       uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n");
+                       return 0;
+               case 0x2:       /* fail ... */
+                       uea_info(INS_TO_USBDEV(sc), "modem synchronization failed"
+                                       " (may be try other cmv/dsp)\n");
+                       return -EAGAIN;
+               case 0x7:       /* operational */
+                       break;
+               default:
+                       uea_warn(INS_TO_USBDEV(sc), "unknown state: %x\n", sc->stats.phy.state);
+                       return 0;
+       }
+
+       if (data != 7) {
+               uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_OFF, 0, NULL);
+               uea_info(INS_TO_USBDEV(sc), "modem operational\n");
+
+               /* release the dsp firmware as it is not needed until
+                * the next failure
+                */
+               if (sc->dsp_firm) {
+                       release_firmware(sc->dsp_firm);
+                       sc->dsp_firm = NULL;
+               }
+       }
+
+       /* always update it as atm layer could not be init when we switch to
+        * operational state
+        */
+       UPDATE_ATM_STAT(signal, ATM_PHY_SIG_FOUND);
+
+       /* wake up processes waiting for synchronization */
+       wake_up(&sc->sync_q);
+
+       /* TODO improve this state machine :
+        * we need some CMV info : what they do and their unit
+        * we should find the equivalent of eagle3- CMV
+        */
+       /* check flags */
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_DIAG, 0, 0, &sc->stats.phy.flags);
+       if (ret < 0)
+               return ret;
+       sc->stats.phy.mflags |= sc->stats.phy.flags;
+
+       /* in case of a flags ( for example delineation LOSS (& 0x10)),
+        * we check the status again in order to detect the failure earlier
+        */
+       if (sc->stats.phy.flags) {
+               uea_dbg(INS_TO_USBDEV(sc), "Stat flag = 0x%x\n",
+                      sc->stats.phy.flags);
+               if (sc->stats.phy.flags & 1) //delineation LOSS
+                       return -EAGAIN;
+               if (sc->stats.phy.flags & 0x4000) //Reset Flag
+                       return -EAGAIN;
+               return 0;
+       }
+
+       /* rate data may be in upper or lower half of 64 bit word, strange */
+       ret = uea_read_cmv_e4(sc, 4, E4_SA_RATE, 0, 0, tmp_arr);
+       if (ret < 0)
+               return ret;
+       data = (tmp_arr[0]) ? tmp_arr[0] : tmp_arr[1];
+       sc->stats.phy.usrate = data / 1000;
+
+       ret = uea_read_cmv_e4(sc, 4, E4_SA_RATE, 1, 0, tmp_arr);
+       if (ret < 0)
+               return ret;
+       data = (tmp_arr[0]) ? tmp_arr[0] : tmp_arr[1];
+       uea_set_bulk_timeout(sc, data / 1000);
+       sc->stats.phy.dsrate = data / 1000;
+       UPDATE_ATM_STAT(link_rate, sc->stats.phy.dsrate * 1000 / 424);
+
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 68, 1, &data);
+       if (ret < 0)
+               return ret;
+       sc->stats.phy.dsattenuation = data / 10;
+
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 69, 1, &data);
+       if (ret < 0)
+               return ret;
+       sc->stats.phy.usattenuation = data / 10;
+
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 68, 3, &data);
+       if (ret < 0)
+               return ret;
+       sc->stats.phy.dsmargin = data / 2;
+
+       ret = uea_read_cmv_e4(sc, 1, E4_SA_INFO, 69, 3, &data);
+       if (ret < 0)
+               return ret;
+       sc->stats.phy.usmargin = data / 10;
+
+       return 0;
+}
+
+static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver)
+{
+       char file_arr[] = "CMVxy.bin";
        char *file;
-       char cmv_name[FIRMWARE_NAME_MAX]; /* 30 bytes stack variable */
 
+       /* set proper name corresponding modem version and line type */
        if (cmv_file[sc->modem_index] == NULL) {
                if (UEA_CHIP_VERSION(sc) == ADI930)
-                       file = (IS_ISDN(sc->usb_dev)) ? "CMV9i.bin" : "CMV9p.bin";
+                       file_arr[3] = '9';
+               else if (UEA_CHIP_VERSION(sc) == EAGLE_IV)
+                       file_arr[3] = '4';
                else
-                       file = (IS_ISDN(sc->usb_dev)) ? "CMVei.bin" : "CMVep.bin";
+                       file_arr[3] = 'e';
+
+               file_arr[4] = IS_ISDN(sc) ? 'i' : 'p';
+               file = file_arr;
        } else
                file = cmv_file[sc->modem_index];
 
        strcpy(cmv_name, FW_DIR);
-       strlcat(cmv_name, file, sizeof(cmv_name));
+       strlcat(cmv_name, file, FIRMWARE_NAME_MAX);
+       if (ver == 2)
+               strlcat(cmv_name, ".v2", FIRMWARE_NAME_MAX);
+}
 
+static int request_cmvs_old(struct uea_softc *sc,
+                void **cmvs, const struct firmware **fw)
+{
+       int ret, size;
+       u8 *data;
+       char cmv_name[FIRMWARE_NAME_MAX]; /* 30 bytes stack variable */
+
+       cmvs_file_name(sc, cmv_name, 1);
        ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev);
        if (ret < 0) {
                uea_err(INS_TO_USBDEV(sc),
@@ -1045,16 +1588,197 @@ static int request_cmvs(struct uea_softc *sc,
        }
 
        data = (u8 *) (*fw)->data;
-       size = *data * sizeof(struct uea_cmvs) + 1;
-       if (size != (*fw)->size) {
-               uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
-                      cmv_name);
-               release_firmware(*fw);
-               return -EILSEQ;
+       size = (*fw)->size;
+       if (size < 1)
+               goto err_fw_corrupted;
+
+       if (size != *data * sizeof(struct uea_cmvs_v1) + 1)
+               goto err_fw_corrupted;
+
+       *cmvs = (void *)(data + 1);
+       return *data;
+
+err_fw_corrupted:
+       uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", cmv_name);
+       release_firmware(*fw);
+       return -EILSEQ;
+}
+
+static int request_cmvs(struct uea_softc *sc,
+                void **cmvs, const struct firmware **fw, int *ver)
+{
+       int ret, size;
+       u32 crc;
+       u8 *data;
+       char cmv_name[FIRMWARE_NAME_MAX]; /* 30 bytes stack variable */
+
+       cmvs_file_name(sc, cmv_name, 2);
+       ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev);
+       if (ret < 0) {
+               /* if caller can handle old version, try to provide it */
+               if (*ver == 1) {
+                       uea_warn(INS_TO_USBDEV(sc), "requesting firmware %s failed, "
+                               "try to get older cmvs\n", cmv_name);
+                       return request_cmvs_old(sc, cmvs, fw);
+               }
+               uea_err(INS_TO_USBDEV(sc),
+                      "requesting firmware %s failed with error %d\n",
+                      cmv_name, ret);
+               return ret;
+       }
+
+       size = (*fw)->size;
+       data = (u8 *) (*fw)->data;
+       if (size < 4 || strncmp(data, "cmv2", 4) != 0) {
+               if (*ver == 1) {
+                       uea_warn(INS_TO_USBDEV(sc), "firmware %s is corrupted, "
+                               "try to get older cmvs\n", cmv_name);
+                       release_firmware(*fw);
+                       return request_cmvs_old(sc, cmvs, fw);
+               }
+               goto err_fw_corrupted;
        }
 
-       *cmvs = (struct uea_cmvs *)(data + 1);
+       *ver = 2;
+
+       data += 4;
+       size -= 4;
+       if (size < 5)
+               goto err_fw_corrupted;
+
+       crc = FW_GET_LONG(data);
+       data += 4;
+       size -= 4;
+       if (crc32_be(0, data, size) != crc)
+               goto err_fw_corrupted;
+
+       if (size != *data * sizeof(struct uea_cmvs_v2) + 1)
+               goto err_fw_corrupted;
+
+       *cmvs = (void *) (data + 1);
        return *data;
+
+err_fw_corrupted:
+       uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", cmv_name);
+       release_firmware(*fw);
+       return -EILSEQ;
+}
+
+static int uea_send_cmvs_e1(struct uea_softc *sc)
+{
+       int i, ret, len;
+       void *cmvs_ptr;
+       const struct firmware *cmvs_fw;
+       int ver = 1; // we can handle v1 cmv firmware version;
+
+       /* Enter in R-IDLE (cmv) until instructed otherwise */
+       ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Dump firmware version */
+       ret = uea_read_cmv_e1(sc, E1_SA_INFO, 10, &sc->stats.phy.firmid);
+       if (ret < 0)
+               return ret;
+       uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
+                       sc->stats.phy.firmid);
+
+       /* get options */
+       ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver);
+       if (ret < 0)
+               return ret;
+
+       /* send options */
+       if (ver == 1) {
+               struct uea_cmvs_v1 *cmvs_v1 = cmvs_ptr;
+
+               uea_warn(INS_TO_USBDEV(sc), "use deprecated cmvs version, "
+                       "please update your firmware\n");
+
+               for (i = 0; i < len; i++) {
+                       ret = uea_write_cmv_e1(sc, FW_GET_LONG(&cmvs_v1[i].address),
+                                               FW_GET_WORD(&cmvs_v1[i].offset),
+                                               FW_GET_LONG(&cmvs_v1[i].data));
+                       if (ret < 0)
+                               goto out;
+               }
+       } else if (ver == 2) {
+               struct uea_cmvs_v2 *cmvs_v2 = cmvs_ptr;
+
+               for (i = 0; i < len; i++) {
+                       ret = uea_write_cmv_e1(sc, FW_GET_LONG(&cmvs_v2[i].address),
+                                               (u16) FW_GET_LONG(&cmvs_v2[i].offset),
+                                               FW_GET_LONG(&cmvs_v2[i].data));
+                       if (ret < 0)
+                               goto out;
+               }
+       } else {
+               /* This realy should not happen */
+               uea_err(INS_TO_USBDEV(sc), "bad cmvs version %d\n", ver);
+               goto out;
+       }
+
+       /* Enter in R-ACT-REQ */
+       ret = uea_write_cmv_e1(sc, E1_SA_CNTL, 0, 2);
+       uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
+       uea_info(INS_TO_USBDEV(sc), "modem started, waiting synchronization...\n");
+out:
+       release_firmware(cmvs_fw);
+       return ret;
+}
+
+static int uea_send_cmvs_e4(struct uea_softc *sc)
+{
+       int i, ret, len;
+       void *cmvs_ptr;
+       const struct firmware *cmvs_fw;
+       int ver = 2; // we can only handle v2 cmv firmware version;
+
+       /* Enter in R-IDLE (cmv) until instructed otherwise */
+       ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Dump firmware version */
+       /* XXX don't read the 3th byte as it is always 6 */
+       ret = uea_read_cmv_e4(sc, 2, E4_SA_INFO, 55, 0, &sc->stats.phy.firmid);
+       if (ret < 0)
+               return ret;
+       uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
+                       sc->stats.phy.firmid);
+
+
+       /* get options */
+       ret = len = request_cmvs(sc, &cmvs_ptr, &cmvs_fw, &ver);
+       if (ret < 0)
+               return ret;
+
+       /* send options */
+       if (ver == 2) {
+               struct uea_cmvs_v2 *cmvs_v2 = cmvs_ptr;
+
+               for (i = 0; i < len; i++) {
+                       ret = uea_write_cmv_e4(sc, 1,
+                                               FW_GET_LONG(&cmvs_v2[i].group),
+                                               FW_GET_LONG(&cmvs_v2[i].address),
+                                               FW_GET_LONG(&cmvs_v2[i].offset),
+                                               FW_GET_LONG(&cmvs_v2[i].data));
+                       if (ret < 0)
+                               goto out;
+               }
+       } else {
+               /* This realy should not happen */
+               uea_err(INS_TO_USBDEV(sc), "bad cmvs version %d\n", ver);
+               goto out;
+       }
+
+       /* Enter in R-ACT-REQ */
+       ret = uea_write_cmv_e4(sc, 1, E4_SA_CNTL, 0, 0, 2);
+       uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
+       uea_info(INS_TO_USBDEV(sc), "modem started, waiting synchronization...\n");
+out:
+       release_firmware(cmvs_fw);
+       return ret;
 }
 
 /* Start boot post firmware modem:
@@ -1066,9 +1790,7 @@ static int request_cmvs(struct uea_softc *sc,
 static int uea_start_reset(struct uea_softc *sc)
 {
        u16 zero = 0;   /* ;-) */
-       int i, len, ret;
-       struct uea_cmvs *cmvs;
-       const struct firmware *cmvs_fw;
+       int ret;
 
        uea_enters(INS_TO_USBDEV(sc));
        uea_info(INS_TO_USBDEV(sc), "(re)booting started\n");
@@ -1093,25 +1815,36 @@ static int uea_start_reset(struct uea_softc *sc)
        uea_request(sc, UEA_SET_MODE, UEA_START_RESET, 0, NULL);
 
        /* original driver use 200ms, but windows driver use 100ms */
-       msleep(100);
+       ret = uea_wait(sc, 0, msecs_to_jiffies(100));
+       if (ret < 0)
+               return ret;
 
        /* leave reset mode */
        uea_request(sc, UEA_SET_MODE, UEA_END_RESET, 0, NULL);
 
-       /* clear tx and rx mailboxes */
-       uea_request(sc, UEA_SET_2183_DATA, UEA_MPTX_MAILBOX, 2, &zero);
-       uea_request(sc, UEA_SET_2183_DATA, UEA_MPRX_MAILBOX, 2, &zero);
-       uea_request(sc, UEA_SET_2183_DATA, UEA_SWAP_MAILBOX, 2, &zero);
+       if (UEA_CHIP_VERSION(sc) != EAGLE_IV) {
+               /* clear tx and rx mailboxes */
+               uea_request(sc, UEA_SET_2183_DATA, UEA_MPTX_MAILBOX, 2, &zero);
+               uea_request(sc, UEA_SET_2183_DATA, UEA_MPRX_MAILBOX, 2, &zero);
+               uea_request(sc, UEA_SET_2183_DATA, UEA_SWAP_MAILBOX, 2, &zero);
+       }
+
+       ret = uea_wait(sc, 0, msecs_to_jiffies(1000));
+       if (ret < 0)
+               return ret;
+
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV)
+               sc->cmv_dsc.e4.function = E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1);
+       else
+               sc->cmv_dsc.e1.function = E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY);
 
-       msleep(1000);
-       sc->cmv_function = MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY);
        /* demask interrupt */
        sc->booting = 0;
 
        /* start loading DSP */
        sc->pageno = 0;
        sc->ovl = 0;
-       schedule_work(&sc->task);
+       queue_work(sc->work_q, &sc->task);
 
        /* wait for modem ready CMV */
        ret = wait_cmv_ack(sc);
@@ -1120,38 +1853,10 @@ static int uea_start_reset(struct uea_softc *sc)
 
        uea_vdbg(INS_TO_USBDEV(sc), "Ready CMV received\n");
 
-       /* Enter in R-IDLE (cmv) until instructed otherwise */
-       ret = uea_write_cmv(sc, SA_CNTL, 0, 1);
-       if (ret < 0)
-               return ret;
-
-       /* Dump firmware version */
-       ret = uea_read_cmv(sc, SA_INFO, 10, &sc->stats.phy.firmid);
+       ret = sc->send_cmvs(sc);
        if (ret < 0)
                return ret;
-       uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
-                       sc->stats.phy.firmid);
 
-       /* get options */
-       ret = len = request_cmvs(sc, &cmvs, &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);
-       uea_vdbg(INS_TO_USBDEV(sc), "Entering in R-ACT-REQ state\n");
-       uea_info(INS_TO_USBDEV(sc), "Modem started, "
-               "waiting synchronization\n");
-out:
-       release_firmware(cmvs_fw);
        sc->reset = 0;
        uea_leaves(INS_TO_USBDEV(sc));
        return ret;
@@ -1174,12 +1879,10 @@ static int uea_kthread(void *data)
                if (ret < 0 || sc->reset)
                        ret = uea_start_reset(sc);
                if (!ret)
-                       ret = uea_stat(sc);
+                       ret = sc->stat(sc);
                if (ret != -EAGAIN)
-                       msleep_interruptible(1000);
-               if (try_to_freeze())
-                       uea_err(INS_TO_USBDEV(sc), "suspend/resume not supported, "
-                               "please unplug/replug your modem\n");
+                       uea_wait(sc, 0, msecs_to_jiffies(1000));
+               try_to_freeze();
        }
        uea_leaves(INS_TO_USBDEV(sc));
        return ret;
@@ -1234,7 +1937,6 @@ static int load_XILINX_firmware(struct uea_softc *sc)
        if (ret < 0)
                uea_err(sc->usb_dev, "elsa de-assert failed with error %d\n", ret);
 
-
 err1:
        release_firmware(fw_entry);
 err0:
@@ -1243,40 +1945,41 @@ err0:
 }
 
 /* The modem send us an ack. First with check if it right */
-static void uea_dispatch_cmv(struct uea_softc *sc, struct cmv* cmv)
+static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr)
 {
+       struct cmv_dsc_e1 *dsc = &sc->cmv_dsc.e1;
+       struct cmv_e1 *cmv = &intr->u.e1.s2.cmv;
+
        uea_enters(INS_TO_USBDEV(sc));
-       if (le16_to_cpu(cmv->wPreamble) != PREAMBLE)
+       if (le16_to_cpu(cmv->wPreamble) != E1_PREAMBLE)
                goto bad1;
 
-       if (cmv->bDirection != MODEMTOHOST)
+       if (cmv->bDirection != E1_MODEMTOHOST)
                goto bad1;
 
        /* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to
         * the first MEMACESS cmv. Ignore it...
         */
-       if (cmv->bFunction != sc->cmv_function) {
+       if (cmv->bFunction != dsc->function) {
                if (UEA_CHIP_VERSION(sc) == ADI930
-                               && cmv->bFunction ==  MAKEFUNCTION(2, 2)) {
-                       cmv->wIndex = cpu_to_le16(sc->cmv_idx);
-                       put_unaligned(cpu_to_le32(sc->cmv_address), &cmv->dwSymbolicAddress);
-                       cmv->wOffsetAddress = cpu_to_le16(sc->cmv_offset);
-               }
-               else
+                               && cmv->bFunction ==  E1_MAKEFUNCTION(2, 2)) {
+                       cmv->wIndex = cpu_to_le16(dsc->idx);
+                       put_unaligned(cpu_to_le32(dsc->address), &cmv->dwSymbolicAddress);
+                       cmv->wOffsetAddress = cpu_to_le16(dsc->offset);
+               } else
                        goto bad2;
        }
 
-       if (cmv->bFunction == MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY)) {
+       if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY)) {
                wake_up_cmv_ack(sc);
                uea_leaves(INS_TO_USBDEV(sc));
                return;
        }
 
        /* in case of MEMACCESS */
-       if (le16_to_cpu(cmv->wIndex) != sc->cmv_idx ||
-           le32_to_cpu(get_unaligned(&cmv->dwSymbolicAddress)) !=
-           sc->cmv_address
-           || le16_to_cpu(cmv->wOffsetAddress) != sc->cmv_offset)
+       if (le16_to_cpu(cmv->wIndex) != dsc->idx ||
+           le32_to_cpu(get_unaligned(&cmv->dwSymbolicAddress)) != dsc->address ||
+           le16_to_cpu(cmv->wOffsetAddress) != dsc->offset)
                goto bad2;
 
        sc->data = le32_to_cpu(get_unaligned(&cmv->dwData));
@@ -1287,10 +1990,10 @@ static void uea_dispatch_cmv(struct uea_softc *sc, struct cmv* cmv)
        return;
 
 bad2:
-       uea_err(INS_TO_USBDEV(sc), "unexpected cmv received,"
+       uea_err(INS_TO_USBDEV(sc), "unexpected cmv received, "
                        "Function : %d, Subfunction : %d\n",
-                       FUNCTION_TYPE(cmv->bFunction),
-                       FUNCTION_SUBTYPE(cmv->bFunction));
+                       E1_FUNCTION_TYPE(cmv->bFunction),
+                       E1_FUNCTION_SUBTYPE(cmv->bFunction));
        uea_leaves(INS_TO_USBDEV(sc));
        return;
 
@@ -1301,6 +2004,61 @@ bad1:
        uea_leaves(INS_TO_USBDEV(sc));
 }
 
+/* The modem send us an ack. First with check if it right */
+static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr)
+{
+       struct cmv_dsc_e4 *dsc = &sc->cmv_dsc.e4;
+       struct cmv_e4 *cmv = &intr->u.e4.s2.cmv;
+
+       uea_enters(INS_TO_USBDEV(sc));
+       uea_dbg(INS_TO_USBDEV(sc), "cmv %x %x %x %x %x %x\n",
+               be16_to_cpu(cmv->wGroup), be16_to_cpu(cmv->wFunction),
+               be16_to_cpu(cmv->wOffset), be16_to_cpu(cmv->wAddress),
+               be32_to_cpu(cmv->dwData[0]), be32_to_cpu(cmv->dwData[1]));
+
+       if (be16_to_cpu(cmv->wFunction) != dsc->function)
+               goto bad2;
+
+       if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1)) {
+               wake_up_cmv_ack(sc);
+               uea_leaves(INS_TO_USBDEV(sc));
+               return;
+       }
+
+       /* in case of MEMACCESS */
+       if (be16_to_cpu(cmv->wOffset) != dsc->offset ||
+           be16_to_cpu(cmv->wGroup) != dsc->group ||
+           be16_to_cpu(cmv->wAddress) != dsc->address)
+               goto bad2;
+
+       sc->data = be32_to_cpu(cmv->dwData[0]);
+       sc->data1 = be32_to_cpu(cmv->dwData[1]);
+       wake_up_cmv_ack(sc);
+       uea_leaves(INS_TO_USBDEV(sc));
+       return;
+
+bad2:
+       uea_err(INS_TO_USBDEV(sc), "unexpected cmv received, "
+                       "Function : %d, Subfunction : %d\n",
+                       E4_FUNCTION_TYPE(cmv->wFunction),
+                       E4_FUNCTION_SUBTYPE(cmv->wFunction));
+       uea_leaves(INS_TO_USBDEV(sc));
+       return;
+}
+
+static void uea_schedule_load_page_e1(struct uea_softc *sc, struct intr_pkt *intr)
+{
+       sc->pageno = intr->e1_bSwapPageNo;
+       sc->ovl = intr->e1_bOvl >> 4 | intr->e1_bOvl << 4;
+       queue_work(sc->work_q, &sc->task);
+}
+
+static void uea_schedule_load_page_e4(struct uea_softc *sc, struct intr_pkt *intr)
+{
+       sc->pageno = intr->e4_bSwapPageNo;
+       queue_work(sc->work_q, &sc->task);
+}
+
 /*
  * interrupt handler
  */
@@ -1326,13 +2084,11 @@ static void uea_intr(struct urb *urb)
 
        switch (le16_to_cpu(intr->wInterrupt)) {
        case INT_LOADSWAPPAGE:
-               sc->pageno = intr->bSwapPageNo;
-               sc->ovl = intr->bOvl >> 4 | intr->bOvl << 4;
-               schedule_work(&sc->task);
+               sc->schedule_load_page(sc, intr);
                break;
 
        case INT_INCOMINGCMV:
-               uea_dispatch_cmv(sc, &intr->u.s2.cmv);
+               sc->dispatch_cmv(sc, intr);
                break;
 
        default:
@@ -1349,35 +2105,55 @@ resubmit:
  */
 static int uea_boot(struct uea_softc *sc)
 {
-       int ret;
+       int ret, size;
        struct intr_pkt *intr;
 
        uea_enters(INS_TO_USBDEV(sc));
 
-       INIT_WORK(&sc->task, uea_load_page);
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+               size = E4_INTR_PKT_SIZE;
+               sc->dispatch_cmv = uea_dispatch_cmv_e4;
+               sc->schedule_load_page = uea_schedule_load_page_e4;
+               sc->stat = uea_stat_e4;
+               sc->send_cmvs = uea_send_cmvs_e4;
+               INIT_WORK(&sc->task, uea_load_page_e4);
+       } else {
+               size = E1_INTR_PKT_SIZE;
+               sc->dispatch_cmv = uea_dispatch_cmv_e1;
+               sc->schedule_load_page = uea_schedule_load_page_e1;
+               sc->stat = uea_stat_e1;
+               sc->send_cmvs = uea_send_cmvs_e1;
+               INIT_WORK(&sc->task, uea_load_page_e1);
+       }
+
        init_waitqueue_head(&sc->sync_q);
-       init_waitqueue_head(&sc->cmv_ack_wait);
+
+       sc->work_q = create_workqueue("ueagle-dsp");
+       if (!sc->work_q) {
+               uea_err(INS_TO_USBDEV(sc), "cannot allocate workqueue\n");
+               uea_leaves(INS_TO_USBDEV(sc));
+               return -ENOMEM;
+       }
 
        if (UEA_CHIP_VERSION(sc) == ADI930)
                load_XILINX_firmware(sc);
 
-       intr = kmalloc(INTR_PKT_SIZE, GFP_KERNEL);
+       intr = kmalloc(size, GFP_KERNEL);
        if (!intr) {
                uea_err(INS_TO_USBDEV(sc),
                       "cannot allocate interrupt package\n");
-               uea_leaves(INS_TO_USBDEV(sc));
-               return -ENOMEM;
+               goto err0;
        }
 
        sc->urb_int = usb_alloc_urb(0, GFP_KERNEL);
        if (!sc->urb_int) {
                uea_err(INS_TO_USBDEV(sc), "cannot allocate interrupt URB\n");
-               goto err;
+               goto err1;
        }
 
        usb_fill_int_urb(sc->urb_int, sc->usb_dev,
                         usb_rcvintpipe(sc->usb_dev, UEA_INTR_PIPE),
-                        intr, INTR_PKT_SIZE, uea_intr, sc,
+                        intr, size, uea_intr, sc,
                         sc->usb_dev->actconfig->interface[0]->altsetting[0].
                         endpoint[0].desc.bInterval);
 
@@ -1385,7 +2161,7 @@ static int uea_boot(struct uea_softc *sc)
        if (ret < 0) {
                uea_err(INS_TO_USBDEV(sc),
                       "urb submition failed with error %d\n", ret);
-               goto err;
+               goto err1;
        }
 
        sc->kthread = kthread_run(uea_kthread, sc, "ueagle-atm");
@@ -1399,10 +2175,12 @@ static int uea_boot(struct uea_softc *sc)
 
 err2:
        usb_kill_urb(sc->urb_int);
-err:
+err1:
        usb_free_urb(sc->urb_int);
        sc->urb_int = NULL;
        kfree(intr);
+err0:
+       destroy_workqueue(sc->work_q);
        uea_leaves(INS_TO_USBDEV(sc));
        return -ENOMEM;
 }
@@ -1417,15 +2195,15 @@ static void uea_stop(struct uea_softc *sc)
        ret = kthread_stop(sc->kthread);
        uea_dbg(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);
 
+       /* stop any pending boot process, when no one can schedule work */
+       destroy_workqueue(sc->work_q);
+
        if (sc->dsp_firm)
                release_firmware(sc->dsp_firm);
        uea_leaves(INS_TO_USBDEV(sc));
@@ -1487,6 +2265,7 @@ static ssize_t read_human_status(struct device *dev, struct device_attribute *at
                char *buf)
 {
        int ret = -ENODEV;
+       int modem_state;
        struct uea_softc *sc;
 
        mutex_lock(&uea_mutex);
@@ -1494,7 +2273,34 @@ static ssize_t read_human_status(struct device *dev, struct device_attribute *at
        if (!sc)
                goto out;
 
-       switch (GET_STATUS(sc->stats.phy.state)) {
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+               switch (sc->stats.phy.state) {
+               case 0x0:       /* not yet synchronized */
+               case 0x1:
+               case 0x3:
+               case 0x4:
+                       modem_state = 0;
+                       break;
+               case 0x5:       /* initialization */
+               case 0x6:
+               case 0x9:
+               case 0xa:
+                       modem_state = 1;
+                       break;
+               case 0x7:       /* operational */
+                       modem_state = 2;
+                       break;
+               case 0x2:       /* fail ... */
+                       modem_state = 3;
+                       break;
+               default:        /* unknown */
+                       modem_state = 4;
+                       break;
+               }
+       } else
+               modem_state = GET_STATUS(sc->stats.phy.state);
+
+       switch (modem_state) {
        case 0:
                ret = sprintf(buf, "Modem is booting\n");
                break;
@@ -1504,9 +2310,12 @@ static ssize_t read_human_status(struct device *dev, struct device_attribute *at
        case 2:
                ret = sprintf(buf, "Modem is operational\n");
                break;
-       default:
+       case 3:
                ret = sprintf(buf, "Modem synchronization failed\n");
                break;
+       default:
+               ret = sprintf(buf, "Modem state is unknown\n");
+               break;
        }
 out:
        mutex_unlock(&uea_mutex);
@@ -1520,18 +2329,26 @@ static ssize_t read_delin(struct device *dev, struct device_attribute *attr,
 {
        int ret = -ENODEV;
        struct uea_softc *sc;
+       char *delin = "GOOD";
 
        mutex_lock(&uea_mutex);
        sc = dev_to_uea(dev);
        if (!sc)
                goto out;
 
-       if (sc->stats.phy.flags & 0x0C00)
-               ret = sprintf(buf, "ERROR\n");
-       else if (sc->stats.phy.flags & 0x0030)
-               ret = sprintf(buf, "LOSS\n");
-       else
-               ret = sprintf(buf, "GOOD\n");
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+               if (sc->stats.phy.flags & 0x4000)
+                       delin = "RESET";
+               else if (sc->stats.phy.flags & 0x0001)
+                       delin = "LOSS";
+       } else {
+               if (sc->stats.phy.flags & 0x0C00)
+                       delin = "ERROR";
+               else if (sc->stats.phy.flags & 0x0030)
+                       delin = "LOSS";
+       }
+
+       ret = sprintf(buf, "%s\n", delin);
 out:
        mutex_unlock(&uea_mutex);
        return ret;
@@ -1662,6 +2479,7 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
        struct usb_device *usb = interface_to_usbdev(intf);
        struct uea_softc *sc;
        int ret, ifnum = intf->altsetting->desc.bInterfaceNumber;
+       unsigned int alt;
 
        uea_enters(usb);
 
@@ -1696,22 +2514,29 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
        sc->modem_index = (modem_index < NB_MODEM) ? modem_index++ : 0;
        sc->driver_info = id->driver_info;
 
-       /* ADI930 don't support iso */
-       if (UEA_CHIP_VERSION(id) != ADI930 && use_iso[sc->modem_index]) {
-               int i;
-
-               /* try set fastest alternate for inbound traffic interface */
-               for (i = FASTEST_ISO_INTF; i > 0; i--)
-                       if (usb_set_interface(usb, UEA_DS_IFACE_NO, i) == 0)
-                               break;
+       /* first try to use module parameter */
+       if (annex[sc->modem_index] == 1)
+               sc->annex = ANNEXA;
+       else if (annex[sc->modem_index] == 2)
+               sc->annex = ANNEXB;
+       /* try to autodetect annex */
+       else if (sc->driver_info & AUTO_ANNEX_A)
+               sc->annex = ANNEXA;
+       else if (sc->driver_info & AUTO_ANNEX_B)
+               sc->annex = ANNEXB;
+       else
+               sc->annex = (le16_to_cpu(sc->usb_dev->descriptor.bcdDevice) & 0x80)?ANNEXB:ANNEXA;
 
-               if (i > 0) {
-                       uea_dbg(usb, "set alternate %d for 2 interface\n", i);
+       alt = altsetting[sc->modem_index];
+       /* ADI930 don't support iso */
+       if (UEA_CHIP_VERSION(id) != ADI930 && alt > 0) {
+               if (alt <= 8 && usb_set_interface(usb, UEA_DS_IFACE_NO, alt) == 0) {
+                       uea_dbg(usb, "set alternate %u for 2 interface\n", alt);
                        uea_info(usb, "using iso mode\n");
                        usbatm->flags |= UDSL_USE_ISOC | UDSL_IGNORE_EILSEQ;
                } else {
-                       uea_err(usb, "setting any alternate failed for "
-                                       "2 interface, using bulk mode\n");
+                       uea_err(usb, "setting alternate %u failed for "
+                                       "2 interface, using bulk mode\n", alt);
                }
        }
 
@@ -1757,10 +2582,11 @@ 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 %s\n",
-              le16_to_cpu(usb->descriptor.idVendor),
-              le16_to_cpu(usb->descriptor.idProduct),
-              chip_name[UEA_CHIP_VERSION(id)], IS_ISDN(usb)?"isdn":"pots");
+       uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) Rev (%#X): %s\n",
+               le16_to_cpu(usb->descriptor.idVendor),
+               le16_to_cpu(usb->descriptor.idProduct),
+               le16_to_cpu(usb->descriptor.bcdDevice),
+               chip_name[UEA_CHIP_VERSION(id)]);
 
        usb_reset_device(usb);
 
@@ -1793,24 +2619,40 @@ static void uea_disconnect(struct usb_interface *intf)
  * List of supported VID/PID
  */
 static const struct usb_device_id uea_ids[] = {
+       {USB_DEVICE(ANALOG_VID, ADI930_PID_PREFIRM),    .driver_info = ADI930 | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, ADI930_PID_PSTFIRM),    .driver_info = ADI930 | PSTFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PREFIRM),   .driver_info = EAGLE_I | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_I_PID_PSTFIRM),   .driver_info = EAGLE_I | PSTFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PREFIRM),  .driver_info = EAGLE_II | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_II_PID_PSTFIRM),  .driver_info = EAGLE_II | PSTFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_IIC_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PREFIRM), .driver_info = EAGLE_III | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_III_PID_PSTFIRM), .driver_info = EAGLE_III | PSTFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PREFIRM),  .driver_info = EAGLE_IV | PREFIRM},
+       {USB_DEVICE(ANALOG_VID, EAGLE_IV_PID_PSTFIRM),  .driver_info = EAGLE_IV | PSTFIRM},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PREFIRM),  .driver_info = EAGLE_I | PREFIRM},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_A_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PREFIRM),  .driver_info = EAGLE_I | PREFIRM},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_I_B_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_A_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_A},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
+       {USB_DEVICE(DEVOLO_VID, DEVOLO_EAGLE_II_B_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM | AUTO_ANNEX_B},
        {USB_DEVICE(ELSA_VID,   ELSA_PID_PREFIRM),      .driver_info = ADI930 | PREFIRM},
        {USB_DEVICE(ELSA_VID,   ELSA_PID_PSTFIRM),      .driver_info = ADI930 | PSTFIRM},
-       {USB_DEVICE(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(ELSA_VID,   ELSA_PID_A_PREFIRM),    .driver_info = ADI930 | PREFIRM},
+       {USB_DEVICE(ELSA_VID,   ELSA_PID_A_PSTFIRM),    .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_A},
+       {USB_DEVICE(ELSA_VID,   ELSA_PID_B_PREFIRM),    .driver_info = ADI930 | PREFIRM},
+       {USB_DEVICE(ELSA_VID,   ELSA_PID_B_PSTFIRM),    .driver_info = ADI930 | PSTFIRM | AUTO_ANNEX_B},
        {USB_DEVICE(USR_VID,    MILLER_A_PID_PREFIRM),  .driver_info = EAGLE_I | PREFIRM},
-       {USB_DEVICE(USR_VID,    MILLER_A_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM},
+       {USB_DEVICE(USR_VID,    MILLER_A_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM  | AUTO_ANNEX_A},
        {USB_DEVICE(USR_VID,    MILLER_B_PID_PREFIRM),  .driver_info = EAGLE_I | PREFIRM},
-       {USB_DEVICE(USR_VID,    MILLER_B_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM},
+       {USB_DEVICE(USR_VID,    MILLER_B_PID_PSTFIRM),  .driver_info = EAGLE_I | PSTFIRM  | AUTO_ANNEX_B},
        {USB_DEVICE(USR_VID,    HEINEKEN_A_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM},
-       {USB_DEVICE(USR_VID,    HEINEKEN_A_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM},
+       {USB_DEVICE(USR_VID,    HEINEKEN_A_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_A},
        {USB_DEVICE(USR_VID,    HEINEKEN_B_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM},
-       {USB_DEVICE(USR_VID,    HEINEKEN_B_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM},
+       {USB_DEVICE(USR_VID,    HEINEKEN_B_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM | AUTO_ANNEX_B},
        {}
 };