diff options
Diffstat (limited to 'drivers/usb/atm')
-rw-r--r-- | drivers/usb/atm/cxacru.c | 47 | ||||
-rw-r--r-- | drivers/usb/atm/speedtch.c | 10 | ||||
-rw-r--r-- | drivers/usb/atm/ueagle-atm.c | 1395 | ||||
-rw-r--r-- | drivers/usb/atm/usbatm.c | 11 |
4 files changed, 1161 insertions, 302 deletions
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 1bc884051e0..a51eeedc18d 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -456,7 +456,6 @@ static int cxacru_start_wait_urb(struct urb *urb, struct completion *done, int* actual_length) { struct timer_list timer; - int status; init_timer(&timer); timer.expires = jiffies + msecs_to_jiffies(CMD_TIMEOUT); @@ -464,12 +463,11 @@ static int cxacru_start_wait_urb(struct urb *urb, struct completion *done, timer.function = cxacru_timeout_kill; add_timer(&timer); wait_for_completion(done); - status = urb->status; del_timer_sync(&timer); if (actual_length) *actual_length = urb->actual_length; - return status; + return urb->status; /* must read status after completion */ } static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, @@ -484,7 +482,9 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, int rbuflen = ((rsize - 1) / stride + 1) * CMD_PACKET_SIZE; if (wbuflen > PAGE_SIZE || rbuflen > PAGE_SIZE) { - dbg("too big transfer requested"); + if (printk_ratelimit()) + usb_err(instance->usbatm, "requested transfer size too large (%d, %d)\n", + wbuflen, rbuflen); ret = -ENOMEM; goto fail; } @@ -495,8 +495,9 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, init_completion(&instance->rcv_done); ret = usb_submit_urb(instance->rcv_urb, GFP_KERNEL); if (ret < 0) { - dbg("submitting read urb for cm %#x failed", cm); - ret = ret; + if (printk_ratelimit()) + usb_err(instance->usbatm, "submit of read urb for cm %#x failed (%d)\n", + cm, ret); goto fail; } @@ -512,27 +513,29 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, init_completion(&instance->snd_done); ret = usb_submit_urb(instance->snd_urb, GFP_KERNEL); if (ret < 0) { - dbg("submitting write urb for cm %#x failed", cm); - ret = ret; + if (printk_ratelimit()) + usb_err(instance->usbatm, "submit of write urb for cm %#x failed (%d)\n", + cm, ret); goto fail; } ret = cxacru_start_wait_urb(instance->snd_urb, &instance->snd_done, NULL); if (ret < 0) { - dbg("sending cm %#x failed", cm); - ret = ret; + if (printk_ratelimit()) + usb_err(instance->usbatm, "send of cm %#x failed (%d)\n", cm, ret); goto fail; } ret = cxacru_start_wait_urb(instance->rcv_urb, &instance->rcv_done, &actlen); if (ret < 0) { - dbg("receiving cm %#x failed", cm); - ret = ret; + if (printk_ratelimit()) + usb_err(instance->usbatm, "receive of cm %#x failed (%d)\n", cm, ret); goto fail; } if (actlen % CMD_PACKET_SIZE || !actlen) { - dbg("response is not a positive multiple of %d: %#x", - CMD_PACKET_SIZE, actlen); + if (printk_ratelimit()) + usb_err(instance->usbatm, "invalid response length to cm %#x: %d\n", + cm, actlen); ret = -EIO; goto fail; } @@ -540,12 +543,16 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, /* check the return status and copy the data to the output buffer, if needed */ for (offb = offd = 0; offd < rsize && offb < actlen; offb += CMD_PACKET_SIZE) { if (rbuf[offb] != cm) { - dbg("wrong cm %#x in response", rbuf[offb]); + if (printk_ratelimit()) + usb_err(instance->usbatm, "wrong cm %#x in response to cm %#x\n", + rbuf[offb], cm); ret = -EIO; goto fail; } if (rbuf[offb + 1] != CM_STATUS_SUCCESS) { - dbg("response failed: %#x", rbuf[offb + 1]); + if (printk_ratelimit()) + usb_err(instance->usbatm, "response to cm %#x failed: %#x\n", + cm, rbuf[offb + 1]); ret = -EIO; goto fail; } @@ -584,14 +591,18 @@ static int cxacru_cm_get_array(struct cxacru_data *instance, enum cxacru_cm_requ for (offb = 0; offb < len; ) { int l = le32_to_cpu(buf[offb++]); if (l > stride || l > (len - offb) / 2) { - dbg("wrong data length %#x in response", l); + if (printk_ratelimit()) + usb_err(instance->usbatm, "invalid data length from cm %#x: %d\n", + cm, l); ret = -EIO; goto cleanup; } while (l--) { offd = le32_to_cpu(buf[offb++]); if (offd >= size) { - dbg("wrong index %#x in response", offd); + if (printk_ratelimit()) + usb_err(instance->usbatm, "wrong index #%x in response to cm #%x\n", + offd, cm); ret = -EIO; goto cleanup; } diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 638b8009b3b..8b132c4a503 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -251,7 +251,6 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance, { unsigned char *buffer; struct usbatm_data *usbatm = instance->usbatm; - struct usb_interface *intf; struct usb_device *usb_dev = usbatm->usb_dev; int actual_length; int ret = 0; @@ -265,7 +264,7 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance, goto out; } - if (!(intf = usb_ifnum_to_if(usb_dev, 2))) { + if (!usb_ifnum_to_if(usb_dev, 2)) { ret = -ENODEV; usb_dbg(usbatm, "%s: interface not found!\n", __func__); goto out_free; @@ -612,7 +611,8 @@ static void speedtch_handle_int(struct urb *int_urb) struct speedtch_instance_data *instance = int_urb->context; struct usbatm_data *usbatm = instance->usbatm; unsigned int count = int_urb->actual_length; - int ret = int_urb->status; + int status = int_urb->status; + int ret; /* The magic interrupt for "up state" */ static const unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 }; @@ -621,8 +621,8 @@ static void speedtch_handle_int(struct urb *int_urb) atm_dbg(usbatm, "%s entered\n", __func__); - if (ret < 0) { - atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, ret); + if (status < 0) { + atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, status); goto fail; } diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 8f046659b4e..389c5b164eb 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2,7 +2,8 @@ * Copyright (c) 2003, 2004 * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. * - * Copyright (c) 2005 Matthieu Castet <castet.matthieu@free.fr> + * Copyright (c) 2005-2007 Matthieu Castet <castet.matthieu@free.fr> + * Copyright (c) 2005-2007 Stanislaw Gruszka <stf_xl@wp.pl> * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -107,18 +108,51 @@ #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 adress %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,34 @@ 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); @@ -826,10 +1164,44 @@ static int uea_cmv(struct uea_softc *sc, return ret; } -static inline int uea_read_cmv(struct uea_softc *sc, +static int uea_cmv_e4(struct uea_softc *sc, + u16 function, u16 group, u16 address, u16 offset, u32 data) +{ + struct cmv_e4 cmv; + int ret; + + uea_enters(INS_TO_USBDEV(sc)); + memset(&cmv, 0, sizeof(cmv)); + + uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Group : 0x%04x, " + "Address : 0x%04x, offset : 0x%04x, data : 0x%08x\n", + E4_FUNCTION_TYPE(function), E4_FUNCTION_SUBTYPE(function), + group, address, offset, data); + + /* we send a request, but we expect a reply */ + sc->cmv_dsc.e4.function = function | (0x1 << 4); + sc->cmv_dsc.e4.offset = offset; + sc->cmv_dsc.e4.address = address; + sc->cmv_dsc.e4.group = group; + + cmv.wFunction = cpu_to_be16(function); + cmv.wGroup = cpu_to_be16(group); + cmv.wAddress = cpu_to_be16(address); + cmv.wOffset = cpu_to_be16(offset); + cmv.dwData[0] = cpu_to_be32(data); + + ret = uea_request(sc, UEA_E4_SET_BLOCK, UEA_MPTX_START, sizeof(cmv), &cmv); + if (ret < 0) + return ret; + ret = wait_cmv_ack(sc); + uea_leaves(INS_TO_USBDEV(sc)); + return ret; +} + +static inline int uea_read_cmv_e1(struct uea_softc *sc, u32 address, u16 offset, u32 *data) { - int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTREAD), + int ret = uea_cmv_e1(sc, E1_MAKEFUNCTION(E1_MEMACCESS, E1_REQUESTREAD), address, offset, 0); if (ret < 0) uea_err(INS_TO_USBDEV(sc), @@ -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; } - *cmvs = (struct uea_cmvs *)(data + 1); + 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; + } + + *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); + ret = sc->send_cmvs(sc); if (ret < 0) return ret; - /* Dump firmware version */ - 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); - - /* 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)); @@ -1289,8 +1992,8 @@ static void uea_dispatch_cmv(struct uea_softc *sc, struct cmv* cmv) bad2: 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 */ @@ -1308,11 +2066,13 @@ static void uea_intr(struct urb *urb) { struct uea_softc *sc = urb->context; struct intr_pkt *intr = urb->transfer_buffer; + int status = urb->status; + uea_enters(INS_TO_USBDEV(sc)); - if (unlikely(urb->status < 0)) { + if (unlikely(status < 0)) { uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n", - urb->status); + status); return; } @@ -1324,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: @@ -1347,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); @@ -1383,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"); @@ -1397,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; } @@ -1415,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)); @@ -1485,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); @@ -1492,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; @@ -1502,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); @@ -1518,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; @@ -1660,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); @@ -1694,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); } } @@ -1719,9 +2546,12 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf, ret = uea_boot(sc); if (ret < 0) - goto error; + goto error_rm_grp; return 0; + +error_rm_grp: + sysfs_remove_group(&intf->dev.kobj, &attr_grp); error: kfree(sc); return ret; @@ -1752,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); @@ -1788,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}, {} }; diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 11e9b15ca45..e717f5b1cae 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -257,9 +257,10 @@ static void usbatm_complete(struct urb *urb) { struct usbatm_channel *channel = urb->context; unsigned long flags; + int status = urb->status; vdbg("%s: urb 0x%p, status %d, actual_length %d", - __func__, urb, urb->status, urb->actual_length); + __func__, urb, status, urb->actual_length); /* usually in_interrupt(), but not always */ spin_lock_irqsave(&channel->lock, flags); @@ -269,16 +270,16 @@ static void usbatm_complete(struct urb *urb) spin_unlock_irqrestore(&channel->lock, flags); - if (unlikely(urb->status) && + if (unlikely(status) && (!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) || - urb->status != -EILSEQ )) + status != -EILSEQ )) { - if (urb->status == -ESHUTDOWN) + if (status == -ESHUTDOWN) return; if (printk_ratelimit()) atm_warn(channel->usbatm, "%s: urb 0x%p failed (%d)!\n", - __func__, urb, urb->status); + __func__, urb, status); /* throttle processing in case of an error */ mod_timer(&channel->delay, jiffies + msecs_to_jiffies(THROTTLE_MSECS)); } else |