#include <linux/ctype.h>
#include <linux/kthread.h>
#include <linux/version.h>
+#include <linux/mutex.h>
#include <asm/unaligned.h>
#include "usbatm.h"
-#define EAGLEUSBVERSION "ueagle 1.1"
+#define EAGLEUSBVERSION "ueagle 1.4"
/*
dev_dbg(&(usb_dev)->dev, \
"[ueagle-atm dbg] %s: " format, \
__FUNCTION__, ##args); \
- } while (0)
+ } while (0)
#define uea_vdbg(usb_dev, format, args...) \
do { \
if (debug >= 2) \
dev_dbg(&(usb_dev)->dev, \
"[ueagle-atm vdbg] " format, ##args); \
- } while (0)
+ } while (0)
#define uea_enters(usb_dev) \
uea_vdbg(usb_dev, "entering %s\n", __FUNCTION__)
#define UEA_CHIP_VERSION(x) \
((x)->driver_info & 0xf)
-#define IS_ISDN(sc) \
- (le16_to_cpu(sc->usb_dev->descriptor.bcdDevice) & 0x80)
+#define IS_ISDN(usb_dev) \
+ (le16_to_cpu((usb_dev)->descriptor.bcdDevice) & 0x80)
#define INS_TO_USBDEV(ins) ins->usb_dev
#define BULK_TIMEOUT 300
#define CTRL_TIMEOUT 1000
-#define ACK_TIMEOUT msecs_to_jiffies(1500)
+#define ACK_TIMEOUT msecs_to_jiffies(3000)
#define UEA_INTR_IFACE_NO 0
#define UEA_US_IFACE_NO 1
((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 INTR_PKT_SIZE 28
static struct usb_driver uea_driver;
-static DECLARE_MUTEX(uea_semaphore);
+static DEFINE_MUTEX(uea_mutex);
static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III"};
static int modem_index;
static unsigned int debug;
+static int use_iso[NB_MODEM] = {[0 ... (NB_MODEM - 1)] = 1};
static int sync_wait[NB_MODEM];
static char *cmv_file[NB_MODEM];
module_param(debug, uint, 0644);
MODULE_PARM_DESC(debug, "module debug level (0=off,1=on,2=verbose)");
+module_param_array(use_iso, bool, NULL, 0644);
+MODULE_PARM_DESC(use_iso, "use isochronous usb pipe for incoming traffic");
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);
char *dsp_name;
if (UEA_CHIP_VERSION(sc) == ADI930) {
- if (IS_ISDN(sc))
+ if (IS_ISDN(sc->usb_dev))
dsp_name = FW_DIR "DSP9i.bin";
else
dsp_name = FW_DIR "DSP9p.bin";
} else {
- if (IS_ISDN(sc))
+ if (IS_ISDN(sc->usb_dev))
dsp_name = FW_DIR "DSPei.bin";
else
dsp_name = FW_DIR "DSPep.bin";
}
- ret = request_firmware(&sc->dsp_firm,
- dsp_name, &sc->usb_dev->dev);
+ ret = request_firmware(&sc->dsp_firm, dsp_name, &sc->usb_dev->dev);
if (ret < 0) {
uea_err(INS_TO_USBDEV(sc),
"requesting firmware %s failed with error %d\n",
/*
* The uea_load_page() function must be called within a process context
*/
-static void uea_load_page(void *xsc)
+static void uea_load_page(struct work_struct *work)
{
- struct uea_softc *sc = xsc;
+ struct uea_softc *sc = container_of(work, struct uea_softc, task);
u16 pageno = sc->pageno;
u16 ovl = sc->ovl;
struct block_info bi;
uea_err(INS_TO_USBDEV(sc), "sending DSP block %u failed\n", i);
return;
bad1:
- uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n",pageno);
+ uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n", pageno);
}
static 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);
}
static inline int wait_cmv_ack(struct uea_softc *sc)
{
- int ret = wait_event_timeout(sc->cmv_ack_wait,
+ int ret = wait_event_interruptible_timeout(sc->cmv_ack_wait,
sc->cmv_ack, ACK_TIMEOUT);
sc->cmv_ack = 0;
+ uea_dbg(INS_TO_USBDEV(sc), "wait_event_timeout : %d ms\n",
+ jiffies_to_msecs(ret));
+
if (ret < 0)
return ret;
return (ret == 0) ? -ETIMEDOUT : 0;
-
}
#define UCDC_SEND_ENCAPSULATED_COMMAND 0x00
struct cmv 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);
/* we send a request, but we expect a reply */
sc->cmv_function = function | 0x2;
sc->cmv_idx++;
ret = uea_request(sc, UEA_SET_BLOCK, UEA_MPTX_START, CMV_SIZE, &cmv);
if (ret < 0)
return ret;
- return wait_cmv_ack(sc);
+ ret = wait_cmv_ack(sc);
+ uea_leaves(INS_TO_USBDEV(sc));
+ return ret;
}
static inline int uea_read_cmv(struct uea_softc *sc,
break;
case 3: /* fail ... */
- uea_info(INS_TO_USBDEV(sc), "modem synchronization failed\n");
+ uea_info(INS_TO_USBDEV(sc), "modem synchronization failed"
+ " (may be try other cmv/dsp)\n");
return -EAGAIN;
case 4 ... 6: /* test state */
release_firmware(sc->dsp_firm);
sc->dsp_firm = NULL;
}
-
- ret = uea_read_cmv(sc, SA_INFO, 10, &sc->stats.phy.firmid);
- if (ret < 0)
- return ret;
- uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
- sc->stats.phy.firmid);
}
/* always update it as atm layer could not be init when we switch to
* we check the status again in order to detect the failure earlier
*/
if (sc->stats.phy.flags) {
- uea_dbg(INS_TO_USBDEV(sc), "Stat flag = %d\n",
+ uea_dbg(INS_TO_USBDEV(sc), "Stat flag = 0x%x\n",
sc->stats.phy.flags);
return 0;
}
* 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
int ret, size;
u8 *data;
char *file;
- static char cmv_name[256] = FW_DIR;
+ char cmv_name[FIRMWARE_NAME_MAX]; /* 30 bytes stack variable */
if (cmv_file[sc->modem_index] == NULL) {
if (UEA_CHIP_VERSION(sc) == ADI930)
- file = (IS_ISDN(sc)) ? "CMV9i.bin" : "CMV9p.bin";
+ file = (IS_ISDN(sc->usb_dev)) ? "CMV9i.bin" : "CMV9p.bin";
else
- file = (IS_ISDN(sc)) ? "CMVei.bin" : "CMVep.bin";
+ file = (IS_ISDN(sc->usb_dev)) ? "CMVei.bin" : "CMVep.bin";
} else
file = cmv_file[sc->modem_index];
uea_enters(INS_TO_USBDEV(sc));
uea_info(INS_TO_USBDEV(sc), "(re)booting started\n");
+ /* mask interrupt */
sc->booting = 1;
+ /* We need to set this here because, a ack timeout could have occured,
+ * but before we start the reboot, the ack occurs and set this to 1.
+ * So we will failed to wait Ready CMV.
+ */
+ sc->cmv_ack = 0;
UPDATE_ATM_STAT(signal, ATM_PHY_SIG_LOST);
/* reset statistics */
msleep(1000);
sc->cmv_function = MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY);
+ /* demask interrupt */
sc->booting = 0;
/* start loading DSP */
if (ret < 0)
return ret;
+ 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);
+ 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)
}
/* 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;
if (!ret)
ret = uea_stat(sc);
if (ret != -EAGAIN)
- msleep(1000);
+ msleep_interruptible(1000);
+ if (try_to_freeze())
+ uea_err(INS_TO_USBDEV(sc), "suspend/resume not supported, "
+ "please unplug/replug your modem\n");
}
uea_leaves(INS_TO_USBDEV(sc));
return ret;
}
}
- /* finish to send the fpga
- */
+ /* finish to send the fpga */
ret = uea_request(sc, 0xe, 1, 0, NULL);
if (ret < 0) {
uea_err(INS_TO_USBDEV(sc),
goto err1;
}
- /*
- * Tell the modem we finish : de-assert reset
- */
+ /* Tell the modem we finish : de-assert reset */
value = 0;
ret = uea_send_modem_cmd(sc->usb_dev, 0xe, 1, &value);
if (ret < 0)
return ret;
}
+/* The modem send us an ack. First with check if it right */
static void uea_dispatch_cmv(struct uea_softc *sc, struct cmv* cmv)
{
uea_enters(INS_TO_USBDEV(sc));
if (cmv->bFunction == MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY)) {
wake_up_cmv_ack(sc);
+ uea_leaves(INS_TO_USBDEV(sc));
return;
}
sc->data = sc->data << 16 | sc->data >> 16;
wake_up_cmv_ack(sc);
+ uea_leaves(INS_TO_USBDEV(sc));
return;
bad2:
"Function : %d, Subfunction : %d\n",
FUNCTION_TYPE(cmv->bFunction),
FUNCTION_SUBTYPE(cmv->bFunction));
+ uea_leaves(INS_TO_USBDEV(sc));
return;
bad1:
uea_err(INS_TO_USBDEV(sc), "invalid cmv received, "
"wPreamble %d, bDirection %d\n",
le16_to_cpu(cmv->wPreamble), cmv->bDirection);
+ uea_leaves(INS_TO_USBDEV(sc));
}
/*
* interrupt handler
*/
-static void uea_intr(struct urb *urb, struct pt_regs *regs)
+static void uea_intr(struct urb *urb)
{
- struct uea_softc *sc = (struct uea_softc *)urb->context;
- struct intr_pkt *intr;
+ struct uea_softc *sc = urb->context;
+ struct intr_pkt *intr = urb->transfer_buffer;
uea_enters(INS_TO_USBDEV(sc));
- if (urb->status < 0) {
+ if (unlikely(urb->status < 0)) {
uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n",
urb->status);
return;
}
- intr = (struct intr_pkt *) urb->transfer_buffer;
-
/* device-to-host interrupt */
if (intr->bType != 0x08 || sc->booting) {
- uea_err(INS_TO_USBDEV(sc), "wrong intr\n");
- // rebooting ?
- // sc->reset = 1;
+ uea_err(INS_TO_USBDEV(sc), "wrong interrupt\n");
goto resubmit;
}
break;
default:
- uea_err(INS_TO_USBDEV(sc), "unknown intr %u\n",
+ uea_err(INS_TO_USBDEV(sc), "unknown interrupt %u\n",
le16_to_cpu(intr->wInterrupt));
}
uea_enters(INS_TO_USBDEV(sc));
- INIT_WORK(&sc->task, uea_load_page, sc);
+ INIT_WORK(&sc->task, uea_load_page);
init_waitqueue_head(&sc->sync_q);
init_waitqueue_head(&sc->cmv_ack_wait);
if (ret < 0) {
uea_err(INS_TO_USBDEV(sc),
"urb submition failed with error %d\n", ret);
- goto err1;
+ goto err;
}
sc->kthread = kthread_run(uea_kthread, sc, "ueagle-atm");
err2:
usb_kill_urb(sc->urb_int);
-err1:
- kfree(intr);
err:
usb_free_urb(sc->urb_int);
+ sc->urb_int = NULL;
+ kfree(intr);
uea_leaves(INS_TO_USBDEV(sc));
return -ENOMEM;
}
int ret;
uea_enters(INS_TO_USBDEV(sc));
ret = kthread_stop(sc->kthread);
- uea_info(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret);
+ uea_dbg(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret);
/* stop any pending boot process */
flush_scheduled_work();
int ret = -ENODEV;
struct uea_softc *sc;
- down(&uea_semaphore);
+ mutex_lock(&uea_mutex);
sc = dev_to_uea(dev);
if (!sc)
goto out;
ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.state);
out:
- up(&uea_semaphore);
+ mutex_unlock(&uea_mutex);
return ret;
}
int ret = -ENODEV;
struct uea_softc *sc;
- down(&uea_semaphore);
+ mutex_lock(&uea_mutex);
sc = dev_to_uea(dev);
if (!sc)
goto out;
sc->reset = 1;
ret = count;
out:
- up(&uea_semaphore);
+ mutex_unlock(&uea_mutex);
return ret;
}
int ret = -ENODEV;
struct uea_softc *sc;
- down(&uea_semaphore);
+ mutex_lock(&uea_mutex);
sc = dev_to_uea(dev);
if (!sc)
goto out;
break;
}
out:
- up(&uea_semaphore);
+ mutex_unlock(&uea_mutex);
return ret;
}
int ret = -ENODEV;
struct uea_softc *sc;
- down(&uea_semaphore);
+ mutex_lock(&uea_mutex);
sc = dev_to_uea(dev);
if (!sc)
goto out;
else
ret = sprintf(buf, "GOOD\n");
out:
- up(&uea_semaphore);
+ mutex_unlock(&uea_mutex);
return ret;
}
int ret = -ENODEV; \
struct uea_softc *sc; \
\
- down(&uea_semaphore); \
+ mutex_lock(&uea_mutex); \
sc = dev_to_uea(dev); \
if (!sc) \
goto out; \
if (reset) \
sc->stats.phy.name = 0; \
out: \
- up(&uea_semaphore); \
+ mutex_unlock(&uea_mutex); \
return ret; \
} \
\
UEA_ATTR(dscorr, 0);
UEA_ATTR(usunc, 0);
UEA_ATTR(dsunc, 0);
+UEA_ATTR(firmid, 0);
/* Retrieve the device End System Identifier (MAC) */
{
struct uea_softc *sc = usbatm->driver_data;
- wait_event(sc->sync_q, IS_OPERATIONAL(sc));
+ wait_event_interruptible(sc->sync_q, IS_OPERATIONAL(sc));
return 0;
return ret;
}
-static void create_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
-{
- /* sysfs interface */
- device_create_file(&intf->dev, &dev_attr_stat_status);
- device_create_file(&intf->dev, &dev_attr_stat_mflags);
- device_create_file(&intf->dev, &dev_attr_stat_human_status);
- device_create_file(&intf->dev, &dev_attr_stat_delin);
- device_create_file(&intf->dev, &dev_attr_stat_vidcpe);
- device_create_file(&intf->dev, &dev_attr_stat_usrate);
- device_create_file(&intf->dev, &dev_attr_stat_dsrate);
- device_create_file(&intf->dev, &dev_attr_stat_usattenuation);
- device_create_file(&intf->dev, &dev_attr_stat_dsattenuation);
- device_create_file(&intf->dev, &dev_attr_stat_usmargin);
- device_create_file(&intf->dev, &dev_attr_stat_dsmargin);
- device_create_file(&intf->dev, &dev_attr_stat_txflow);
- device_create_file(&intf->dev, &dev_attr_stat_rxflow);
- device_create_file(&intf->dev, &dev_attr_stat_uscorr);
- device_create_file(&intf->dev, &dev_attr_stat_dscorr);
- device_create_file(&intf->dev, &dev_attr_stat_usunc);
- device_create_file(&intf->dev, &dev_attr_stat_dsunc);
-}
+static struct attribute *attrs[] = {
+ &dev_attr_stat_status.attr,
+ &dev_attr_stat_mflags.attr,
+ &dev_attr_stat_human_status.attr,
+ &dev_attr_stat_delin.attr,
+ &dev_attr_stat_vidcpe.attr,
+ &dev_attr_stat_usrate.attr,
+ &dev_attr_stat_dsrate.attr,
+ &dev_attr_stat_usattenuation.attr,
+ &dev_attr_stat_dsattenuation.attr,
+ &dev_attr_stat_usmargin.attr,
+ &dev_attr_stat_dsmargin.attr,
+ &dev_attr_stat_txflow.attr,
+ &dev_attr_stat_rxflow.attr,
+ &dev_attr_stat_uscorr.attr,
+ &dev_attr_stat_dscorr.attr,
+ &dev_attr_stat_usunc.attr,
+ &dev_attr_stat_dsunc.attr,
+ &dev_attr_stat_firmid.attr,
+ NULL,
+};
+static struct attribute_group attr_grp = {
+ .attrs = attrs,
+};
static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
- const struct usb_device_id *id, int *heavy)
+ const struct usb_device_id *id)
{
struct usb_device *usb = interface_to_usbdev(intf);
struct uea_softc *sc;
if (ifnum != UEA_INTR_IFACE_NO)
return -ENODEV;
- *heavy = sync_wait[modem_index];
+ usbatm->flags = (sync_wait[modem_index] ? 0 : UDSL_SKIP_HEAVY_INIT);
/* interface 1 is for outbound traffic */
ret = claim_interface(usb, usbatm, UEA_US_IFACE_NO);
if (ret < 0)
return ret;
- /* ADI930 has only 2 interfaces and inbound traffic
- * is on interface 1
- */
+ /* ADI930 has only 2 interfaces and inbound traffic is on interface 1 */
if (UEA_CHIP_VERSION(id) != ADI930) {
/* interface 2 is for inbound traffic */
ret = claim_interface(usb, usbatm, UEA_DS_IFACE_NO);
sc = kzalloc(sizeof(struct uea_softc), GFP_KERNEL);
if (!sc) {
- uea_err(INS_TO_USBDEV(sc), "uea_init: not enough memory !\n");
+ uea_err(usb, "uea_init: not enough memory !\n");
return -ENOMEM;
}
sc->modem_index = (modem_index < NB_MODEM) ? modem_index++ : 0;
sc->driver_info = id->driver_info;
- ret = uea_boot(sc);
- if (ret < 0) {
- kfree(sc);
- return ret;
+ /* 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;
+
+ if (i > 0) {
+ uea_dbg(usb, "set alternate %d for 2 interface\n", i);
+ 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");
+ }
}
- create_fs_entries(sc, intf);
- return 0;
-}
+ ret = sysfs_create_group(&intf->dev.kobj, &attr_grp);
+ if (ret < 0)
+ goto error;
-static void destroy_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
-{
- /* sysfs interface */
- device_remove_file(&intf->dev, &dev_attr_stat_status);
- device_remove_file(&intf->dev, &dev_attr_stat_mflags);
- device_remove_file(&intf->dev, &dev_attr_stat_human_status);
- device_remove_file(&intf->dev, &dev_attr_stat_delin);
- device_remove_file(&intf->dev, &dev_attr_stat_vidcpe);
- device_remove_file(&intf->dev, &dev_attr_stat_usrate);
- device_remove_file(&intf->dev, &dev_attr_stat_dsrate);
- device_remove_file(&intf->dev, &dev_attr_stat_usattenuation);
- device_remove_file(&intf->dev, &dev_attr_stat_dsattenuation);
- device_remove_file(&intf->dev, &dev_attr_stat_usmargin);
- device_remove_file(&intf->dev, &dev_attr_stat_dsmargin);
- device_remove_file(&intf->dev, &dev_attr_stat_txflow);
- device_remove_file(&intf->dev, &dev_attr_stat_rxflow);
- device_remove_file(&intf->dev, &dev_attr_stat_uscorr);
- device_remove_file(&intf->dev, &dev_attr_stat_dscorr);
- device_remove_file(&intf->dev, &dev_attr_stat_usunc);
- device_remove_file(&intf->dev, &dev_attr_stat_dsunc);
+ ret = uea_boot(sc);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+error:
+ kfree(sc);
+ return ret;
}
static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
{
struct uea_softc *sc = usbatm->driver_data;
- destroy_fs_entries(sc, intf);
+ sysfs_remove_group(&intf->dev.kobj, &attr_grp);
uea_stop(sc);
kfree(sc);
}
static struct usbatm_driver uea_usbatm_driver = {
.driver_name = "ueagle-atm",
- .owner = THIS_MODULE,
.bind = uea_bind,
.atm_start = uea_atm_open,
.unbind = uea_unbind,
.heavy_init = uea_heavy,
- .in = UEA_BULK_DATA_PIPE,
- .out = UEA_BULK_DATA_PIPE,
+ .bulk_in = UEA_BULK_DATA_PIPE,
+ .bulk_out = UEA_BULK_DATA_PIPE,
+ .isoc_in = UEA_ISO_DATA_PIPE,
};
static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
struct usb_device *usb = interface_to_usbdev(intf);
uea_enters(usb);
- uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) : %s\n",
+ 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)]);
+ chip_name[UEA_CHIP_VERSION(id)], IS_ISDN(usb)?"isdn":"pots");
usb_reset_device(usb);
* Pre-firmware device has one interface
*/
if (usb->config->desc.bNumInterfaces != 1 && ifnum == 0) {
- down(&uea_semaphore);
+ mutex_lock(&uea_mutex);
usbatm_usb_disconnect(intf);
- up(&uea_semaphore);
+ mutex_unlock(&uea_mutex);
uea_info(usb, "ADSL device removed\n");
}