|
@@ -5,8 +5,6 @@
|
|
|
* Tilman Schmidt <tilman@imap.cc>,
|
|
|
* Stefan Eilers.
|
|
|
*
|
|
|
- * Based on usb-gigaset.c.
|
|
|
- *
|
|
|
* =====================================================================
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
* modify it under the terms of the GNU General Public License as
|
|
@@ -46,19 +44,20 @@ MODULE_PARM_DESC(cidmode, "Call-ID mode");
|
|
|
#define GIGASET_DEVFSNAME "gig/bas/"
|
|
|
#define GIGASET_DEVNAME "ttyGB"
|
|
|
|
|
|
-#define IF_WRITEBUF 256 //FIXME
|
|
|
+/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
|
|
|
+#define IF_WRITEBUF 264
|
|
|
|
|
|
/* Values for the Gigaset 307x */
|
|
|
#define USB_GIGA_VENDOR_ID 0x0681
|
|
|
-#define USB_GIGA_PRODUCT_ID 0x0001
|
|
|
-#define USB_4175_PRODUCT_ID 0x0002
|
|
|
+#define USB_3070_PRODUCT_ID 0x0001
|
|
|
+#define USB_3075_PRODUCT_ID 0x0002
|
|
|
#define USB_SX303_PRODUCT_ID 0x0021
|
|
|
#define USB_SX353_PRODUCT_ID 0x0022
|
|
|
|
|
|
/* table of devices that work with this driver */
|
|
|
static struct usb_device_id gigaset_table [] = {
|
|
|
- { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_GIGA_PRODUCT_ID) },
|
|
|
- { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_4175_PRODUCT_ID) },
|
|
|
+ { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3070_PRODUCT_ID) },
|
|
|
+ { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3075_PRODUCT_ID) },
|
|
|
{ USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX303_PRODUCT_ID) },
|
|
|
{ USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX353_PRODUCT_ID) },
|
|
|
{ } /* Terminating entry */
|
|
@@ -77,6 +76,10 @@ static int gigaset_probe(struct usb_interface *interface,
|
|
|
/* Function will be called if the device is unplugged */
|
|
|
static void gigaset_disconnect(struct usb_interface *interface);
|
|
|
|
|
|
+static void read_ctrl_callback(struct urb *, struct pt_regs *);
|
|
|
+static void stopurbs(struct bas_bc_state *);
|
|
|
+static int atwrite_submit(struct cardstate *, unsigned char *, int);
|
|
|
+static int start_cbsend(struct cardstate *);
|
|
|
|
|
|
/*==============================================================================*/
|
|
|
|
|
@@ -111,12 +114,14 @@ struct bas_cardstate {
|
|
|
};
|
|
|
|
|
|
/* status of direct USB connection to 307x base (bits in basstate) */
|
|
|
-#define BS_ATOPEN 0x001
|
|
|
-#define BS_B1OPEN 0x002
|
|
|
-#define BS_B2OPEN 0x004
|
|
|
-#define BS_ATREADY 0x008
|
|
|
-#define BS_INIT 0x010
|
|
|
-#define BS_ATTIMER 0x020
|
|
|
+#define BS_ATOPEN 0x001 /* AT channel open */
|
|
|
+#define BS_B1OPEN 0x002 /* B channel 1 open */
|
|
|
+#define BS_B2OPEN 0x004 /* B channel 2 open */
|
|
|
+#define BS_ATREADY 0x008 /* base ready for AT command */
|
|
|
+#define BS_INIT 0x010 /* base has signalled INIT_OK */
|
|
|
+#define BS_ATTIMER 0x020 /* waiting for HD_READY_SEND_ATDATA */
|
|
|
+#define BS_ATRDPEND 0x040 /* urb_cmd_in in use */
|
|
|
+#define BS_ATWRPEND 0x080 /* urb_cmd_out in use */
|
|
|
|
|
|
|
|
|
static struct gigaset_driver *driver = NULL;
|
|
@@ -130,6 +135,47 @@ static struct usb_driver gigaset_usb_driver = {
|
|
|
.id_table = gigaset_table,
|
|
|
};
|
|
|
|
|
|
+/* get message text for usb_submit_urb return code
|
|
|
+ */
|
|
|
+static char *get_usb_rcmsg(int rc)
|
|
|
+{
|
|
|
+ static char unkmsg[28];
|
|
|
+
|
|
|
+ switch (rc) {
|
|
|
+ case 0:
|
|
|
+ return "success";
|
|
|
+ case -ENOMEM:
|
|
|
+ return "out of memory";
|
|
|
+ case -ENODEV:
|
|
|
+ return "device not present";
|
|
|
+ case -ENOENT:
|
|
|
+ return "endpoint not present";
|
|
|
+ case -ENXIO:
|
|
|
+ return "URB type not supported";
|
|
|
+ case -EINVAL:
|
|
|
+ return "invalid argument";
|
|
|
+ case -EAGAIN:
|
|
|
+ return "start frame too early or too much scheduled";
|
|
|
+ case -EFBIG:
|
|
|
+ return "too many isochronous frames requested";
|
|
|
+ case -EPIPE:
|
|
|
+ return "endpoint stalled";
|
|
|
+ case -EMSGSIZE:
|
|
|
+ return "invalid packet size";
|
|
|
+ case -ENOSPC:
|
|
|
+ return "would overcommit USB bandwidth";
|
|
|
+ case -ESHUTDOWN:
|
|
|
+ return "device shut down";
|
|
|
+ case -EPERM:
|
|
|
+ return "reject flag set";
|
|
|
+ case -EHOSTUNREACH:
|
|
|
+ return "device suspended";
|
|
|
+ default:
|
|
|
+ snprintf(unkmsg, sizeof(unkmsg), "unknown error %d", rc);
|
|
|
+ return unkmsg;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/* get message text for USB status code
|
|
|
*/
|
|
|
static char *get_usb_statmsg(int status)
|
|
@@ -140,43 +186,37 @@ static char *get_usb_statmsg(int status)
|
|
|
case 0:
|
|
|
return "success";
|
|
|
case -ENOENT:
|
|
|
- return "canceled";
|
|
|
- case -ECONNRESET:
|
|
|
- return "canceled (async)";
|
|
|
+ return "unlinked (sync)";
|
|
|
case -EINPROGRESS:
|
|
|
return "pending";
|
|
|
case -EPROTO:
|
|
|
- return "bit stuffing or unknown USB error";
|
|
|
+ return "bit stuffing error, timeout, or unknown USB error";
|
|
|
case -EILSEQ:
|
|
|
- return "Illegal byte sequence (CRC mismatch)";
|
|
|
- case -EPIPE:
|
|
|
- return "babble detect or endpoint stalled";
|
|
|
- case -ENOSR:
|
|
|
- return "buffer error";
|
|
|
+ return "CRC mismatch, timeout, or unknown USB error";
|
|
|
case -ETIMEDOUT:
|
|
|
return "timed out";
|
|
|
- case -ENODEV:
|
|
|
- return "device not present";
|
|
|
+ case -EPIPE:
|
|
|
+ return "endpoint stalled";
|
|
|
+ case -ECOMM:
|
|
|
+ return "IN buffer overrun";
|
|
|
+ case -ENOSR:
|
|
|
+ return "OUT buffer underrun";
|
|
|
+ case -EOVERFLOW:
|
|
|
+ return "too much data";
|
|
|
case -EREMOTEIO:
|
|
|
return "short packet detected";
|
|
|
+ case -ENODEV:
|
|
|
+ return "device removed";
|
|
|
case -EXDEV:
|
|
|
return "partial isochronous transfer";
|
|
|
case -EINVAL:
|
|
|
return "invalid argument";
|
|
|
- case -ENXIO:
|
|
|
- return "URB already queued";
|
|
|
- case -EAGAIN:
|
|
|
- return "isochronous start frame too early or too much scheduled";
|
|
|
- case -EFBIG:
|
|
|
- return "too many isochronous frames requested";
|
|
|
- case -EMSGSIZE:
|
|
|
- return "endpoint message size zero";
|
|
|
+ case -ECONNRESET:
|
|
|
+ return "unlinked (async)";
|
|
|
case -ESHUTDOWN:
|
|
|
- return "endpoint shutdown";
|
|
|
- case -EBUSY:
|
|
|
- return "another request pending";
|
|
|
+ return "device shut down";
|
|
|
default:
|
|
|
- snprintf(unkmsg, sizeof(unkmsg), "unknown error %d", status);
|
|
|
+ snprintf(unkmsg, sizeof(unkmsg), "unknown status %d", status);
|
|
|
return unkmsg;
|
|
|
}
|
|
|
}
|
|
@@ -277,18 +317,17 @@ static inline void error_hangup(struct bc_state *bcs)
|
|
|
gig_dbg(DEBUG_ANY, "%s: scheduling HUP for channel %d",
|
|
|
__func__, bcs->channel);
|
|
|
|
|
|
- if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) {
|
|
|
- //FIXME what should we do?
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL))
|
|
|
+ dev_err(cs->dev, "event queue full\n");
|
|
|
|
|
|
gigaset_schedule_event(cs);
|
|
|
}
|
|
|
|
|
|
/* error_reset
|
|
|
* reset Gigaset device because of an unrecoverable error
|
|
|
- * This function may be called from any context and takes care of scheduling
|
|
|
- * the necessary actions for execution outside of interrupt context.
|
|
|
+ * This function may be called from any context, and should take care of
|
|
|
+ * scheduling the necessary actions for execution outside of interrupt context.
|
|
|
+ * Right now, it just generates a kernel message calling for help.
|
|
|
* argument:
|
|
|
* controller state structure
|
|
|
*/
|
|
@@ -364,36 +403,38 @@ static void cmd_in_timeout(unsigned long data)
|
|
|
{
|
|
|
struct cardstate *cs = (struct cardstate *) data;
|
|
|
struct bas_cardstate *ucs = cs->hw.bas;
|
|
|
- unsigned long flags;
|
|
|
|
|
|
- spin_lock_irqsave(&cs->lock, flags);
|
|
|
- if (unlikely(!cs->connected)) {
|
|
|
- gig_dbg(DEBUG_USBREQ, "%s: disconnected", __func__);
|
|
|
- spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
- return;
|
|
|
- }
|
|
|
if (!ucs->rcvbuf_size) {
|
|
|
gig_dbg(DEBUG_USBREQ, "%s: no receive in progress", __func__);
|
|
|
- spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
return;
|
|
|
}
|
|
|
- spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
|
|
|
dev_err(cs->dev, "timeout reading AT response\n");
|
|
|
error_reset(cs); //FIXME retry?
|
|
|
}
|
|
|
|
|
|
+/* set/clear bits in base connection state, return previous state
|
|
|
+ */
|
|
|
+inline static int update_basstate(struct bas_cardstate *ucs,
|
|
|
+ int set, int clear)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ int state;
|
|
|
|
|
|
-static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs);
|
|
|
+ spin_lock_irqsave(&ucs->lock, flags);
|
|
|
+ state = atomic_read(&ucs->basstate);
|
|
|
+ atomic_set(&ucs->basstate, (state & ~clear) | set);
|
|
|
+ spin_unlock_irqrestore(&ucs->lock, flags);
|
|
|
+ return state;
|
|
|
+}
|
|
|
|
|
|
/* atread_submit
|
|
|
- * submit an HD_READ_ATMESSAGE command URB
|
|
|
+ * submit an HD_READ_ATMESSAGE command URB and optionally start a timeout
|
|
|
* parameters:
|
|
|
* cs controller state structure
|
|
|
* timeout timeout in 1/10 sec., 0: none
|
|
|
* return value:
|
|
|
* 0 on success
|
|
|
- * -EINVAL if a NULL pointer is encountered somewhere
|
|
|
* -EBUSY if another request is pending
|
|
|
* any URB submission error code
|
|
|
*/
|
|
@@ -405,7 +446,7 @@ static int atread_submit(struct cardstate *cs, int timeout)
|
|
|
gig_dbg(DEBUG_USBREQ, "-------> HD_READ_ATMESSAGE (%d)",
|
|
|
ucs->rcvbuf_size);
|
|
|
|
|
|
- if (ucs->urb_cmd_in->status == -EINPROGRESS) {
|
|
|
+ if (update_basstate(ucs, BS_ATRDPEND, 0) & BS_ATRDPEND) {
|
|
|
dev_err(cs->dev,
|
|
|
"could not submit HD_READ_ATMESSAGE: URB busy\n");
|
|
|
return -EBUSY;
|
|
@@ -423,6 +464,7 @@ static int atread_submit(struct cardstate *cs, int timeout)
|
|
|
read_ctrl_callback, cs->inbuf);
|
|
|
|
|
|
if ((ret = usb_submit_urb(ucs->urb_cmd_in, SLAB_ATOMIC)) != 0) {
|
|
|
+ update_basstate(ucs, 0, BS_ATRDPEND);
|
|
|
dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: %s\n",
|
|
|
get_usb_statmsg(ret));
|
|
|
return ret;
|
|
@@ -438,26 +480,6 @@ static int atread_submit(struct cardstate *cs, int timeout)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void stopurbs(struct bas_bc_state *);
|
|
|
-static int start_cbsend(struct cardstate *);
|
|
|
-
|
|
|
-/* set/clear bits in base connection state
|
|
|
- */
|
|
|
-inline static void update_basstate(struct bas_cardstate *ucs,
|
|
|
- int set, int clear)
|
|
|
-{
|
|
|
- unsigned long flags;
|
|
|
- int state;
|
|
|
-
|
|
|
- spin_lock_irqsave(&ucs->lock, flags);
|
|
|
- state = atomic_read(&ucs->basstate);
|
|
|
- state &= ~clear;
|
|
|
- state |= set;
|
|
|
- atomic_set(&ucs->basstate, state);
|
|
|
- spin_unlock_irqrestore(&ucs->lock, flags);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
/* read_int_callback
|
|
|
* USB completion handler for interrupt pipe input
|
|
|
* called by the USB subsystem in interrupt context
|
|
@@ -471,20 +493,25 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
struct bas_cardstate *ucs = cs->hw.bas;
|
|
|
struct bc_state *bcs;
|
|
|
unsigned long flags;
|
|
|
- int status;
|
|
|
+ int rc;
|
|
|
unsigned l;
|
|
|
int channel;
|
|
|
|
|
|
switch (urb->status) {
|
|
|
case 0: /* success */
|
|
|
break;
|
|
|
- case -ENOENT: /* canceled */
|
|
|
- case -ECONNRESET: /* canceled (async) */
|
|
|
+ case -ENOENT: /* cancelled */
|
|
|
+ case -ECONNRESET: /* cancelled (async) */
|
|
|
case -EINPROGRESS: /* pending */
|
|
|
/* ignore silently */
|
|
|
gig_dbg(DEBUG_USBREQ, "%s: %s",
|
|
|
__func__, get_usb_statmsg(urb->status));
|
|
|
return;
|
|
|
+ case -ENODEV: /* device removed */
|
|
|
+ case -ESHUTDOWN: /* device shut down */
|
|
|
+ //FIXME use this as disconnect indicator?
|
|
|
+ gig_dbg(DEBUG_USBREQ, "%s: device disconnected", __func__);
|
|
|
+ return;
|
|
|
default: /* severe trouble */
|
|
|
dev_warn(cs->dev, "interrupt read: %s\n",
|
|
|
get_usb_statmsg(urb->status));
|
|
@@ -492,6 +519,13 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
goto resubmit;
|
|
|
}
|
|
|
|
|
|
+ /* drop incomplete packets even if the missing bytes wouldn't matter */
|
|
|
+ if (unlikely(urb->actual_length < 3)) {
|
|
|
+ dev_warn(cs->dev, "incomplete interrupt packet (%d bytes)\n",
|
|
|
+ urb->actual_length);
|
|
|
+ goto resubmit;
|
|
|
+ }
|
|
|
+
|
|
|
l = (unsigned) ucs->int_in_buf[1] +
|
|
|
(((unsigned) ucs->int_in_buf[2]) << 8);
|
|
|
|
|
@@ -558,25 +592,28 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
}
|
|
|
spin_lock_irqsave(&cs->lock, flags);
|
|
|
if (ucs->rcvbuf_size) {
|
|
|
- spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
+ /* throw away previous buffer - we have no queue */
|
|
|
dev_err(cs->dev,
|
|
|
- "receive AT data overrun, %d bytes lost\n", l);
|
|
|
- error_reset(cs); //FIXME reschedule
|
|
|
- break;
|
|
|
+ "receive AT data overrun, %d bytes lost\n",
|
|
|
+ ucs->rcvbuf_size);
|
|
|
+ kfree(ucs->rcvbuf);
|
|
|
+ ucs->rcvbuf_size = 0;
|
|
|
}
|
|
|
if ((ucs->rcvbuf = kmalloc(l, GFP_ATOMIC)) == NULL) {
|
|
|
spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
- dev_err(cs->dev, "out of memory, %d bytes lost\n", l);
|
|
|
- error_reset(cs); //FIXME reschedule
|
|
|
+ dev_err(cs->dev, "out of memory receiving AT data\n");
|
|
|
+ error_reset(cs);
|
|
|
break;
|
|
|
}
|
|
|
ucs->rcvbuf_size = l;
|
|
|
ucs->retry_cmd_in = 0;
|
|
|
- if ((status = atread_submit(cs, BAS_TIMEOUT)) < 0) {
|
|
|
+ if ((rc = atread_submit(cs, BAS_TIMEOUT)) < 0) {
|
|
|
kfree(ucs->rcvbuf);
|
|
|
ucs->rcvbuf = NULL;
|
|
|
ucs->rcvbuf_size = 0;
|
|
|
- error_reset(cs); //FIXME reschedule
|
|
|
+ if (rc != -ENODEV)
|
|
|
+ //FIXME corrective action?
|
|
|
+ error_reset(cs);
|
|
|
}
|
|
|
spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
break;
|
|
@@ -598,12 +635,10 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
check_pending(ucs);
|
|
|
|
|
|
resubmit:
|
|
|
- spin_lock_irqsave(&cs->lock, flags);
|
|
|
- status = cs->connected ? usb_submit_urb(urb, SLAB_ATOMIC) : -ENODEV;
|
|
|
- spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
- if (unlikely(status)) {
|
|
|
+ rc = usb_submit_urb(urb, SLAB_ATOMIC);
|
|
|
+ if (unlikely(rc != 0 && rc != -ENODEV)) {
|
|
|
dev_err(cs->dev, "could not resubmit interrupt URB: %s\n",
|
|
|
- get_usb_statmsg(status));
|
|
|
+ get_usb_rcmsg(rc));
|
|
|
error_reset(cs);
|
|
|
}
|
|
|
}
|
|
@@ -622,18 +657,12 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
struct bas_cardstate *ucs = cs->hw.bas;
|
|
|
int have_data = 0;
|
|
|
unsigned numbytes;
|
|
|
- unsigned long flags;
|
|
|
+ int rc;
|
|
|
|
|
|
- spin_lock_irqsave(&cs->lock, flags);
|
|
|
- if (unlikely(!cs->connected)) {
|
|
|
- warn("%s: disconnected", __func__);
|
|
|
- spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
- return;
|
|
|
- }
|
|
|
+ update_basstate(ucs, 0, BS_ATRDPEND);
|
|
|
|
|
|
if (!ucs->rcvbuf_size) {
|
|
|
dev_warn(cs->dev, "%s: no receive in progress\n", __func__);
|
|
|
- spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -666,9 +695,11 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
- case -ENOENT: /* canceled */
|
|
|
- case -ECONNRESET: /* canceled (async) */
|
|
|
+ case -ENOENT: /* cancelled */
|
|
|
+ case -ECONNRESET: /* cancelled (async) */
|
|
|
case -EINPROGRESS: /* pending */
|
|
|
+ case -ENODEV: /* device removed */
|
|
|
+ case -ESHUTDOWN: /* device shut down */
|
|
|
/* no action necessary */
|
|
|
gig_dbg(DEBUG_USBREQ, "%s: %s",
|
|
|
__func__, get_usb_statmsg(urb->status));
|
|
@@ -681,11 +712,11 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
if (ucs->retry_cmd_in++ < BAS_RETRY) {
|
|
|
dev_notice(cs->dev, "control read: retry %d\n",
|
|
|
ucs->retry_cmd_in);
|
|
|
- if (atread_submit(cs, BAS_TIMEOUT) >= 0) {
|
|
|
- /* resubmitted - bypass regular exit block */
|
|
|
- spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
+ rc = atread_submit(cs, BAS_TIMEOUT);
|
|
|
+ if (rc >= 0 || rc == -ENODEV)
|
|
|
+ /* resubmitted or disconnected */
|
|
|
+ /* - bypass regular exit block */
|
|
|
return;
|
|
|
- }
|
|
|
} else {
|
|
|
dev_err(cs->dev,
|
|
|
"control read: giving up after %d tries\n",
|
|
@@ -697,7 +728,6 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
kfree(ucs->rcvbuf);
|
|
|
ucs->rcvbuf = NULL;
|
|
|
ucs->rcvbuf_size = 0;
|
|
|
- spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
if (have_data) {
|
|
|
gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
|
|
|
gigaset_schedule_event(cs);
|
|
@@ -719,8 +749,11 @@ static void read_iso_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
int i, rc;
|
|
|
|
|
|
/* status codes not worth bothering the tasklet with */
|
|
|
- if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET ||
|
|
|
- urb->status == -EINPROGRESS)) {
|
|
|
+ if (unlikely(urb->status == -ENOENT ||
|
|
|
+ urb->status == -ECONNRESET ||
|
|
|
+ urb->status == -EINPROGRESS ||
|
|
|
+ urb->status == -ENODEV ||
|
|
|
+ urb->status == -ESHUTDOWN)) {
|
|
|
gig_dbg(DEBUG_ISO, "%s: %s",
|
|
|
__func__, get_usb_statmsg(urb->status));
|
|
|
return;
|
|
@@ -740,9 +773,9 @@ static void read_iso_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
for (i = 0; i < BAS_NUMFRAMES; i++) {
|
|
|
ubc->isoinlost += urb->iso_frame_desc[i].actual_length;
|
|
|
if (unlikely(urb->iso_frame_desc[i].status != 0 &&
|
|
|
- urb->iso_frame_desc[i].status != -EINPROGRESS)) {
|
|
|
+ urb->iso_frame_desc[i].status !=
|
|
|
+ -EINPROGRESS))
|
|
|
ubc->loststatus = urb->iso_frame_desc[i].status;
|
|
|
- }
|
|
|
urb->iso_frame_desc[i].status = 0;
|
|
|
urb->iso_frame_desc[i].actual_length = 0;
|
|
|
}
|
|
@@ -754,10 +787,10 @@ static void read_iso_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
gig_dbg(DEBUG_ISO, "%s: isoc read overrun/resubmit",
|
|
|
__func__);
|
|
|
rc = usb_submit_urb(urb, SLAB_ATOMIC);
|
|
|
- if (unlikely(rc != 0)) {
|
|
|
+ if (unlikely(rc != 0 && rc != -ENODEV)) {
|
|
|
dev_err(bcs->cs->dev,
|
|
|
"could not resubmit isochronous read "
|
|
|
- "URB: %s\n", get_usb_statmsg(rc));
|
|
|
+ "URB: %s\n", get_usb_rcmsg(rc));
|
|
|
dump_urb(DEBUG_ISO, "isoc read", urb);
|
|
|
error_hangup(bcs);
|
|
|
}
|
|
@@ -780,8 +813,11 @@ static void write_iso_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
unsigned long flags;
|
|
|
|
|
|
/* status codes not worth bothering the tasklet with */
|
|
|
- if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET ||
|
|
|
- urb->status == -EINPROGRESS)) {
|
|
|
+ if (unlikely(urb->status == -ENOENT ||
|
|
|
+ urb->status == -ECONNRESET ||
|
|
|
+ urb->status == -EINPROGRESS ||
|
|
|
+ urb->status == -ENODEV ||
|
|
|
+ urb->status == -ESHUTDOWN)) {
|
|
|
gig_dbg(DEBUG_ISO, "%s: %s",
|
|
|
__func__, get_usb_statmsg(urb->status));
|
|
|
return;
|
|
@@ -822,7 +858,6 @@ static int starturbs(struct bc_state *bcs)
|
|
|
for (k = 0; k < BAS_INURBS; k++) {
|
|
|
urb = ubc->isoinurbs[k];
|
|
|
if (!urb) {
|
|
|
- dev_err(bcs->cs->dev, "isoinurbs[%d]==NULL\n", k);
|
|
|
rc = -EFAULT;
|
|
|
goto error;
|
|
|
}
|
|
@@ -844,12 +879,8 @@ static int starturbs(struct bc_state *bcs)
|
|
|
}
|
|
|
|
|
|
dump_urb(DEBUG_ISO, "Initial isoc read", urb);
|
|
|
- if ((rc = usb_submit_urb(urb, SLAB_ATOMIC)) != 0) {
|
|
|
- dev_err(bcs->cs->dev,
|
|
|
- "could not submit isochronous read URB %d: %s\n",
|
|
|
- k, get_usb_statmsg(rc));
|
|
|
+ if ((rc = usb_submit_urb(urb, SLAB_ATOMIC)) != 0)
|
|
|
goto error;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
/* initialize L2 transmission */
|
|
@@ -859,7 +890,6 @@ static int starturbs(struct bc_state *bcs)
|
|
|
for (k = 0; k < BAS_OUTURBS; ++k) {
|
|
|
urb = ubc->isoouturbs[k].urb;
|
|
|
if (!urb) {
|
|
|
- dev_err(bcs->cs->dev, "isoouturbs[%d].urb==NULL\n", k);
|
|
|
rc = -EFAULT;
|
|
|
goto error;
|
|
|
}
|
|
@@ -885,12 +915,8 @@ static int starturbs(struct bc_state *bcs)
|
|
|
for (k = 0; k < 2; ++k) {
|
|
|
dump_urb(DEBUG_ISO, "Initial isoc write", urb);
|
|
|
rc = usb_submit_urb(ubc->isoouturbs[k].urb, SLAB_ATOMIC);
|
|
|
- if (rc != 0) {
|
|
|
- dev_err(bcs->cs->dev,
|
|
|
- "could not submit isochronous write URB %d: %s\n",
|
|
|
- k, get_usb_statmsg(rc));
|
|
|
+ if (rc != 0)
|
|
|
goto error;
|
|
|
- }
|
|
|
}
|
|
|
dump_urb(DEBUG_ISO, "Initial isoc write (free)", urb);
|
|
|
ubc->isooutfree = &ubc->isoouturbs[2];
|
|
@@ -916,15 +942,15 @@ static void stopurbs(struct bas_bc_state *ubc)
|
|
|
for (k = 0; k < BAS_INURBS; ++k) {
|
|
|
rc = usb_unlink_urb(ubc->isoinurbs[k]);
|
|
|
gig_dbg(DEBUG_ISO,
|
|
|
- "%s: isoc input URB %d unlinked, result = %d",
|
|
|
- __func__, k, rc);
|
|
|
+ "%s: isoc input URB %d unlinked, result = %s",
|
|
|
+ __func__, k, get_usb_rcmsg(rc));
|
|
|
}
|
|
|
|
|
|
for (k = 0; k < BAS_OUTURBS; ++k) {
|
|
|
rc = usb_unlink_urb(ubc->isoouturbs[k].urb);
|
|
|
gig_dbg(DEBUG_ISO,
|
|
|
- "%s: isoc output URB %d unlinked, result = %d",
|
|
|
- __func__, k, rc);
|
|
|
+ "%s: isoc output URB %d unlinked, result = %s",
|
|
|
+ __func__, k, get_usb_rcmsg(rc));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -934,7 +960,7 @@ static void stopurbs(struct bas_bc_state *ubc)
|
|
|
/* submit_iso_write_urb
|
|
|
* fill and submit the next isochronous write URB
|
|
|
* parameters:
|
|
|
- * bcs B channel state structure
|
|
|
+ * ucx context structure containing URB
|
|
|
* return value:
|
|
|
* number of frames submitted in URB
|
|
|
* 0 if URB not submitted because no data available (isooutbuf busy)
|
|
@@ -946,7 +972,6 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
|
|
|
struct bas_bc_state *ubc = ucx->bcs->hw.bas;
|
|
|
struct usb_iso_packet_descriptor *ifd;
|
|
|
int corrbytes, nframe, rc;
|
|
|
- unsigned long flags;
|
|
|
|
|
|
/* urb->dev is clobbered by USB subsystem */
|
|
|
urb->dev = ucx->bcs->cs->hw.bas->udev;
|
|
@@ -992,20 +1017,22 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
|
|
|
ifd->status = 0;
|
|
|
ifd->actual_length = 0;
|
|
|
}
|
|
|
- if ((urb->number_of_packets = nframe) > 0) {
|
|
|
- spin_lock_irqsave(&ucx->bcs->cs->lock, flags);
|
|
|
- rc = ucx->bcs->cs->connected ? usb_submit_urb(urb, SLAB_ATOMIC) : -ENODEV;
|
|
|
- spin_unlock_irqrestore(&ucx->bcs->cs->lock, flags);
|
|
|
+ if (unlikely(nframe == 0))
|
|
|
+ return 0; /* no data to send */
|
|
|
+ urb->number_of_packets = nframe;
|
|
|
|
|
|
- if (rc) {
|
|
|
+ rc = usb_submit_urb(urb, SLAB_ATOMIC);
|
|
|
+ if (unlikely(rc)) {
|
|
|
+ if (rc == -ENODEV)
|
|
|
+ /* device removed - give up silently */
|
|
|
+ gig_dbg(DEBUG_ISO, "%s: disconnected", __func__);
|
|
|
+ else
|
|
|
dev_err(ucx->bcs->cs->dev,
|
|
|
"could not submit isochronous write URB: %s\n",
|
|
|
- get_usb_statmsg(rc));
|
|
|
- dump_urb(DEBUG_ISO, "isoc write", urb);
|
|
|
- return rc;
|
|
|
- }
|
|
|
- ++ubc->numsub;
|
|
|
+ get_usb_rcmsg(rc));
|
|
|
+ return rc;
|
|
|
}
|
|
|
+ ++ubc->numsub;
|
|
|
return nframe;
|
|
|
}
|
|
|
|
|
@@ -1028,6 +1055,7 @@ static void write_iso_tasklet(unsigned long data)
|
|
|
int i;
|
|
|
struct sk_buff *skb;
|
|
|
int len;
|
|
|
+ int rc;
|
|
|
|
|
|
/* loop while completed URBs arrive in time */
|
|
|
for (;;) {
|
|
@@ -1057,7 +1085,8 @@ static void write_iso_tasklet(unsigned long data)
|
|
|
ubc->isooutfree = NULL;
|
|
|
spin_unlock_irqrestore(&ubc->isooutlock, flags);
|
|
|
if (next) {
|
|
|
- if (submit_iso_write_urb(next) <= 0) {
|
|
|
+ rc = submit_iso_write_urb(next);
|
|
|
+ if (unlikely(rc <= 0 && rc != -ENODEV)) {
|
|
|
/* could not submit URB, put it back */
|
|
|
spin_lock_irqsave(&ubc->isooutlock, flags);
|
|
|
if (ubc->isooutfree == NULL) {
|
|
@@ -1077,17 +1106,18 @@ static void write_iso_tasklet(unsigned long data)
|
|
|
/* process completed URB */
|
|
|
urb = done->urb;
|
|
|
switch (urb->status) {
|
|
|
+ case -EXDEV: /* partial completion */
|
|
|
+ gig_dbg(DEBUG_ISO, "%s: URB partially completed",
|
|
|
+ __func__);
|
|
|
+ /* fall through - what's the difference anyway? */
|
|
|
case 0: /* normal completion */
|
|
|
- break;
|
|
|
- case -EXDEV: /* inspect individual frames */
|
|
|
- /* assumptions (for lack of documentation):
|
|
|
- * - actual_length bytes of the frame in error are
|
|
|
+ /* inspect individual frames
|
|
|
+ * assumptions (for lack of documentation):
|
|
|
+ * - actual_length bytes of first frame in error are
|
|
|
* successfully sent
|
|
|
* - all following frames are not sent at all
|
|
|
*/
|
|
|
- gig_dbg(DEBUG_ISO, "%s: URB partially completed",
|
|
|
- __func__);
|
|
|
- offset = done->limit; /* just in case */
|
|
|
+ offset = done->limit; /* default (no error) */
|
|
|
for (i = 0; i < BAS_NUMFRAMES; i++) {
|
|
|
ifd = &urb->iso_frame_desc[i];
|
|
|
if (ifd->status ||
|
|
@@ -1122,7 +1152,7 @@ static void write_iso_tasklet(unsigned long data)
|
|
|
}
|
|
|
#endif
|
|
|
break;
|
|
|
- case -EPIPE: //FIXME is this the code for "underrun"?
|
|
|
+ case -EPIPE: /* stall - probably underrun */
|
|
|
dev_err(cs->dev, "isochronous write stalled\n");
|
|
|
error_hangup(bcs);
|
|
|
break;
|
|
@@ -1142,7 +1172,8 @@ static void write_iso_tasklet(unsigned long data)
|
|
|
spin_unlock_irqrestore(&ubc->isooutlock, flags);
|
|
|
if (next) {
|
|
|
/* only one URB still active - resubmit one */
|
|
|
- if (submit_iso_write_urb(next) <= 0) {
|
|
|
+ rc = submit_iso_write_urb(next);
|
|
|
+ if (unlikely(rc <= 0 && rc != -ENODEV)) {
|
|
|
/* couldn't submit */
|
|
|
error_hangup(bcs);
|
|
|
}
|
|
@@ -1222,10 +1253,9 @@ static void read_iso_tasklet(unsigned long data)
|
|
|
break;
|
|
|
case -ENOENT:
|
|
|
case -ECONNRESET:
|
|
|
- gig_dbg(DEBUG_ISO, "%s: URB canceled", __func__);
|
|
|
- continue; /* -> skip */
|
|
|
- case -EINPROGRESS: /* huh? */
|
|
|
- gig_dbg(DEBUG_ISO, "%s: URB still pending", __func__);
|
|
|
+ case -EINPROGRESS:
|
|
|
+ gig_dbg(DEBUG_ISO, "%s: %s",
|
|
|
+ __func__, get_usb_statmsg(urb->status));
|
|
|
continue; /* -> skip */
|
|
|
case -EPIPE:
|
|
|
dev_err(cs->dev, "isochronous read stalled\n");
|
|
@@ -1290,13 +1320,11 @@ static void read_iso_tasklet(unsigned long data)
|
|
|
urb->dev = bcs->cs->hw.bas->udev;
|
|
|
urb->transfer_flags = URB_ISO_ASAP;
|
|
|
urb->number_of_packets = BAS_NUMFRAMES;
|
|
|
- spin_lock_irqsave(&cs->lock, flags);
|
|
|
- rc = cs->connected ? usb_submit_urb(urb, SLAB_ATOMIC) : -ENODEV;
|
|
|
- spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
- if (rc) {
|
|
|
+ rc = usb_submit_urb(urb, SLAB_ATOMIC);
|
|
|
+ if (unlikely(rc != 0 && rc != -ENODEV)) {
|
|
|
dev_err(cs->dev,
|
|
|
"could not resubmit isochronous read URB: %s\n",
|
|
|
- get_usb_statmsg(rc));
|
|
|
+ get_usb_rcmsg(rc));
|
|
|
dump_urb(DEBUG_ISO, "resubmit iso read", urb);
|
|
|
error_hangup(bcs);
|
|
|
}
|
|
@@ -1397,7 +1425,6 @@ static void write_ctrl_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
* timeout timeout in seconds (0: no timeout)
|
|
|
* return value:
|
|
|
* 0 on success
|
|
|
- * -EINVAL if a NULL pointer is encountered somewhere
|
|
|
* -EBUSY if another request is pending
|
|
|
* any URB submission error code
|
|
|
*/
|
|
@@ -1418,12 +1445,6 @@ static int req_submit(struct bc_state *bcs, int req, int val, int timeout)
|
|
|
req, ucs->pending);
|
|
|
return -EBUSY;
|
|
|
}
|
|
|
- if (ucs->urb_ctrl->status == -EINPROGRESS) {
|
|
|
- spin_unlock_irqrestore(&ucs->lock, flags);
|
|
|
- dev_err(bcs->cs->dev,
|
|
|
- "could not submit request 0x%02x: URB busy\n", req);
|
|
|
- return -EBUSY;
|
|
|
- }
|
|
|
|
|
|
ucs->dr_ctrl.bRequestType = OUT_VENDOR_REQ;
|
|
|
ucs->dr_ctrl.bRequest = req;
|
|
@@ -1465,22 +1486,36 @@ static int req_submit(struct bc_state *bcs, int req, int val, int timeout)
|
|
|
static int gigaset_init_bchannel(struct bc_state *bcs)
|
|
|
{
|
|
|
int req, ret;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&bcs->cs->lock, flags);
|
|
|
+ if (unlikely(!bcs->cs->connected)) {
|
|
|
+ gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
|
|
|
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
|
|
|
if ((ret = starturbs(bcs)) < 0) {
|
|
|
dev_err(bcs->cs->dev,
|
|
|
- "could not start isochronous I/O for channel %d\n",
|
|
|
- bcs->channel + 1);
|
|
|
- error_hangup(bcs);
|
|
|
+ "could not start isochronous I/O for channel B%d: %s\n",
|
|
|
+ bcs->channel + 1,
|
|
|
+ ret == -EFAULT ? "null URB" : get_usb_rcmsg(ret));
|
|
|
+ if (ret != -ENODEV)
|
|
|
+ error_hangup(bcs);
|
|
|
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
req = bcs->channel ? HD_OPEN_B2CHANNEL : HD_OPEN_B1CHANNEL;
|
|
|
if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) {
|
|
|
- dev_err(bcs->cs->dev, "could not open channel %d: %s\n",
|
|
|
- bcs->channel + 1, get_usb_statmsg(ret));
|
|
|
+ dev_err(bcs->cs->dev, "could not open channel B%d\n",
|
|
|
+ bcs->channel + 1);
|
|
|
stopurbs(bcs->hw.bas);
|
|
|
- error_hangup(bcs);
|
|
|
+ if (ret != -ENODEV)
|
|
|
+ error_hangup(bcs);
|
|
|
}
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1497,19 +1532,30 @@ static int gigaset_init_bchannel(struct bc_state *bcs)
|
|
|
static int gigaset_close_bchannel(struct bc_state *bcs)
|
|
|
{
|
|
|
int req, ret;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&bcs->cs->lock, flags);
|
|
|
+ if (unlikely(!bcs->cs->connected)) {
|
|
|
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
|
|
+ gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
|
|
|
if (!(atomic_read(&bcs->cs->hw.bas->basstate) &
|
|
|
(bcs->channel ? BS_B2OPEN : BS_B1OPEN))) {
|
|
|
/* channel not running: just signal common.c */
|
|
|
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
|
|
gigaset_bchannel_down(bcs);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ /* channel running: tell device to close it */
|
|
|
req = bcs->channel ? HD_CLOSE_B2CHANNEL : HD_CLOSE_B1CHANNEL;
|
|
|
if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0)
|
|
|
- dev_err(bcs->cs->dev,
|
|
|
- "could not submit HD_CLOSE_BxCHANNEL request: %s\n",
|
|
|
- get_usb_statmsg(ret));
|
|
|
+ dev_err(bcs->cs->dev, "closing channel B%d failed\n",
|
|
|
+ bcs->channel + 1);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1545,8 +1591,6 @@ static void complete_cb(struct cardstate *cs)
|
|
|
kfree(cb);
|
|
|
}
|
|
|
|
|
|
-static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len);
|
|
|
-
|
|
|
/* write_command_callback
|
|
|
* USB completion handler for AT command transmission
|
|
|
* called by the USB subsystem in interrupt context
|
|
@@ -1560,13 +1604,17 @@ static void write_command_callback(struct urb *urb, struct pt_regs *regs)
|
|
|
struct bas_cardstate *ucs = cs->hw.bas;
|
|
|
unsigned long flags;
|
|
|
|
|
|
+ update_basstate(ucs, 0, BS_ATWRPEND);
|
|
|
+
|
|
|
/* check status */
|
|
|
switch (urb->status) {
|
|
|
case 0: /* normal completion */
|
|
|
break;
|
|
|
- case -ENOENT: /* canceled */
|
|
|
- case -ECONNRESET: /* canceled (async) */
|
|
|
+ case -ENOENT: /* cancelled */
|
|
|
+ case -ECONNRESET: /* cancelled (async) */
|
|
|
case -EINPROGRESS: /* pending */
|
|
|
+ case -ENODEV: /* device removed */
|
|
|
+ case -ESHUTDOWN: /* device shut down */
|
|
|
/* ignore silently */
|
|
|
gig_dbg(DEBUG_USBREQ, "%s: %s",
|
|
|
__func__, get_usb_statmsg(urb->status));
|
|
@@ -1627,19 +1675,17 @@ static void atrdy_timeout(unsigned long data)
|
|
|
* len length of command to send
|
|
|
* return value:
|
|
|
* 0 on success
|
|
|
- * -EFAULT if a NULL pointer is encountered somewhere
|
|
|
* -EBUSY if another request is pending
|
|
|
* any URB submission error code
|
|
|
*/
|
|
|
static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len)
|
|
|
{
|
|
|
struct bas_cardstate *ucs = cs->hw.bas;
|
|
|
- unsigned long flags;
|
|
|
- int ret;
|
|
|
+ int rc;
|
|
|
|
|
|
gig_dbg(DEBUG_USBREQ, "-------> HD_WRITE_ATMESSAGE (%d)", len);
|
|
|
|
|
|
- if (ucs->urb_cmd_out->status == -EINPROGRESS) {
|
|
|
+ if (update_basstate(ucs, BS_ATWRPEND, 0) & BS_ATWRPEND) {
|
|
|
dev_err(cs->dev,
|
|
|
"could not submit HD_WRITE_ATMESSAGE: URB busy\n");
|
|
|
return -EBUSY;
|
|
@@ -1654,29 +1700,22 @@ static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len)
|
|
|
usb_sndctrlpipe(ucs->udev, 0),
|
|
|
(unsigned char*) &ucs->dr_cmd_out, buf, len,
|
|
|
write_command_callback, cs);
|
|
|
-
|
|
|
- spin_lock_irqsave(&cs->lock, flags);
|
|
|
- ret = cs->connected ? usb_submit_urb(ucs->urb_cmd_out, SLAB_ATOMIC) : -ENODEV;
|
|
|
- spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
-
|
|
|
- if (ret) {
|
|
|
+ rc = usb_submit_urb(ucs->urb_cmd_out, SLAB_ATOMIC);
|
|
|
+ if (unlikely(rc)) {
|
|
|
+ update_basstate(ucs, 0, BS_ATWRPEND);
|
|
|
dev_err(cs->dev, "could not submit HD_WRITE_ATMESSAGE: %s\n",
|
|
|
- get_usb_statmsg(ret));
|
|
|
- return ret;
|
|
|
+ get_usb_rcmsg(rc));
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
- /* submitted successfully */
|
|
|
- update_basstate(ucs, 0, BS_ATREADY);
|
|
|
-
|
|
|
- /* start timeout if necessary */
|
|
|
- if (!(atomic_read(&ucs->basstate) & BS_ATTIMER)) {
|
|
|
+ /* submitted successfully, start timeout if necessary */
|
|
|
+ if (!(update_basstate(ucs, BS_ATTIMER, BS_ATREADY) & BS_ATTIMER)) {
|
|
|
gig_dbg(DEBUG_OUTPUT, "setting ATREADY timeout of %d/10 secs",
|
|
|
ATRDY_TIMEOUT);
|
|
|
ucs->timer_atrdy.expires = jiffies + ATRDY_TIMEOUT * HZ / 10;
|
|
|
ucs->timer_atrdy.data = (unsigned long) cs;
|
|
|
ucs->timer_atrdy.function = atrdy_timeout;
|
|
|
add_timer(&ucs->timer_atrdy);
|
|
|
- update_basstate(ucs, BS_ATTIMER, 0);
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
@@ -1702,7 +1741,6 @@ static int start_cbsend(struct cardstate *cs)
|
|
|
gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "AT channel not open");
|
|
|
rc = req_submit(cs->bcs, HD_OPEN_ATCHANNEL, 0, BAS_TIMEOUT);
|
|
|
if (rc < 0) {
|
|
|
- dev_err(cs->dev, "could not open AT channel\n");
|
|
|
/* flush command queue */
|
|
|
spin_lock_irqsave(&cs->cmdlock, flags);
|
|
|
while (cs->cmdbuf != NULL)
|
|
@@ -1786,8 +1824,14 @@ static int gigaset_write_cmd(struct cardstate *cs,
|
|
|
cs->lastcmdbuf = cb;
|
|
|
spin_unlock_irqrestore(&cs->cmdlock, flags);
|
|
|
|
|
|
+ spin_lock_irqsave(&cs->lock, flags);
|
|
|
+ if (unlikely(!cs->connected)) {
|
|
|
+ spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
+ gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
status = start_cbsend(cs);
|
|
|
-
|
|
|
+ spin_unlock_irqrestore(&cs->lock, flags);
|
|
|
return status < 0 ? status : len;
|
|
|
}
|
|
|
|
|
@@ -1849,12 +1893,32 @@ static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
|
|
|
*/
|
|
|
static int gigaset_freebcshw(struct bc_state *bcs)
|
|
|
{
|
|
|
- if (!bcs->hw.bas)
|
|
|
+ struct bas_bc_state *ubc = bcs->hw.bas;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (!ubc)
|
|
|
return 0;
|
|
|
|
|
|
- if (bcs->hw.bas->isooutbuf)
|
|
|
- kfree(bcs->hw.bas->isooutbuf);
|
|
|
- kfree(bcs->hw.bas);
|
|
|
+ /* kill URBs and tasklets before freeing - better safe than sorry */
|
|
|
+ atomic_set(&ubc->running, 0);
|
|
|
+ for (i = 0; i < BAS_OUTURBS; ++i)
|
|
|
+ if (ubc->isoouturbs[i].urb) {
|
|
|
+ gig_dbg(DEBUG_INIT, "%s: killing iso out URB %d",
|
|
|
+ __func__, i);
|
|
|
+ usb_kill_urb(ubc->isoouturbs[i].urb);
|
|
|
+ usb_free_urb(ubc->isoouturbs[i].urb);
|
|
|
+ }
|
|
|
+ for (i = 0; i < BAS_INURBS; ++i)
|
|
|
+ if (ubc->isoinurbs[i]) {
|
|
|
+ gig_dbg(DEBUG_INIT, "%s: killing iso in URB %d",
|
|
|
+ __func__, i);
|
|
|
+ usb_kill_urb(ubc->isoinurbs[i]);
|
|
|
+ usb_free_urb(ubc->isoinurbs[i]);
|
|
|
+ }
|
|
|
+ tasklet_kill(&ubc->sent_tasklet);
|
|
|
+ tasklet_kill(&ubc->rcvd_tasklet);
|
|
|
+ kfree(ubc->isooutbuf);
|
|
|
+ kfree(ubc);
|
|
|
bcs->hw.bas = NULL;
|
|
|
return 1;
|
|
|
}
|
|
@@ -1931,13 +1995,9 @@ static void gigaset_reinitbcshw(struct bc_state *bcs)
|
|
|
|
|
|
static void gigaset_freecshw(struct cardstate *cs)
|
|
|
{
|
|
|
- struct bas_cardstate *ucs = cs->hw.bas;
|
|
|
-
|
|
|
- del_timer(&ucs->timer_ctrl);
|
|
|
- del_timer(&ucs->timer_atrdy);
|
|
|
- del_timer(&ucs->timer_cmd_in);
|
|
|
-
|
|
|
+ /* timers, URBs and rcvbuf are disposed of in disconnect */
|
|
|
kfree(cs->hw.bas);
|
|
|
+ cs->hw.bas = NULL;
|
|
|
}
|
|
|
|
|
|
static int gigaset_initcshw(struct cardstate *cs)
|
|
@@ -2041,23 +2101,13 @@ static int gigaset_probe(struct usb_interface *interface,
|
|
|
struct bas_bc_state *ubc;
|
|
|
struct usb_endpoint_descriptor *endpoint;
|
|
|
int i, j;
|
|
|
- int ret;
|
|
|
+ int rc;
|
|
|
|
|
|
gig_dbg(DEBUG_ANY,
|
|
|
"%s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x)",
|
|
|
__func__, le16_to_cpu(udev->descriptor.idVendor),
|
|
|
le16_to_cpu(udev->descriptor.idProduct));
|
|
|
|
|
|
- /* See if the device offered us matches what we can accept */
|
|
|
- if ((le16_to_cpu(udev->descriptor.idVendor) != USB_GIGA_VENDOR_ID) ||
|
|
|
- (le16_to_cpu(udev->descriptor.idProduct) != USB_GIGA_PRODUCT_ID &&
|
|
|
- le16_to_cpu(udev->descriptor.idProduct) != USB_4175_PRODUCT_ID &&
|
|
|
- le16_to_cpu(udev->descriptor.idProduct) != USB_SX303_PRODUCT_ID &&
|
|
|
- le16_to_cpu(udev->descriptor.idProduct) != USB_SX353_PRODUCT_ID)) {
|
|
|
- gig_dbg(DEBUG_ANY, "%s: unmatched ID - exiting", __func__);
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
-
|
|
|
/* set required alternate setting */
|
|
|
hostif = interface->cur_altsetting;
|
|
|
if (hostif->desc.bAlternateSetting != 3) {
|
|
@@ -2105,45 +2155,22 @@ static int gigaset_probe(struct usb_interface *interface,
|
|
|
* - three for the different uses of the default control pipe
|
|
|
* - three for each isochronous pipe
|
|
|
*/
|
|
|
- ucs->urb_int_in = usb_alloc_urb(0, SLAB_KERNEL);
|
|
|
- if (!ucs->urb_int_in) {
|
|
|
- dev_err(cs->dev, "no free urbs available\n");
|
|
|
- goto error;
|
|
|
- }
|
|
|
- ucs->urb_cmd_in = usb_alloc_urb(0, SLAB_KERNEL);
|
|
|
- if (!ucs->urb_cmd_in) {
|
|
|
- dev_err(cs->dev, "no free urbs available\n");
|
|
|
- goto error;
|
|
|
- }
|
|
|
- ucs->urb_cmd_out = usb_alloc_urb(0, SLAB_KERNEL);
|
|
|
- if (!ucs->urb_cmd_out) {
|
|
|
- dev_err(cs->dev, "no free urbs available\n");
|
|
|
- goto error;
|
|
|
- }
|
|
|
- ucs->urb_ctrl = usb_alloc_urb(0, SLAB_KERNEL);
|
|
|
- if (!ucs->urb_ctrl) {
|
|
|
- dev_err(cs->dev, "no free urbs available\n");
|
|
|
- goto error;
|
|
|
- }
|
|
|
+ if (!(ucs->urb_int_in = usb_alloc_urb(0, SLAB_KERNEL)) ||
|
|
|
+ !(ucs->urb_cmd_in = usb_alloc_urb(0, SLAB_KERNEL)) ||
|
|
|
+ !(ucs->urb_cmd_out = usb_alloc_urb(0, SLAB_KERNEL)) ||
|
|
|
+ !(ucs->urb_ctrl = usb_alloc_urb(0, SLAB_KERNEL)))
|
|
|
+ goto allocerr;
|
|
|
|
|
|
for (j = 0; j < 2; ++j) {
|
|
|
ubc = cs->bcs[j].hw.bas;
|
|
|
- for (i = 0; i < BAS_OUTURBS; ++i) {
|
|
|
- ubc->isoouturbs[i].urb =
|
|
|
- usb_alloc_urb(BAS_NUMFRAMES, SLAB_KERNEL);
|
|
|
- if (!ubc->isoouturbs[i].urb) {
|
|
|
- dev_err(cs->dev, "no free urbs available\n");
|
|
|
- goto error;
|
|
|
- }
|
|
|
- }
|
|
|
- for (i = 0; i < BAS_INURBS; ++i) {
|
|
|
- ubc->isoinurbs[i] =
|
|
|
- usb_alloc_urb(BAS_NUMFRAMES, SLAB_KERNEL);
|
|
|
- if (!ubc->isoinurbs[i]) {
|
|
|
- dev_err(cs->dev, "no free urbs available\n");
|
|
|
- goto error;
|
|
|
- }
|
|
|
- }
|
|
|
+ for (i = 0; i < BAS_OUTURBS; ++i)
|
|
|
+ if (!(ubc->isoouturbs[i].urb =
|
|
|
+ usb_alloc_urb(BAS_NUMFRAMES, SLAB_KERNEL)))
|
|
|
+ goto allocerr;
|
|
|
+ for (i = 0; i < BAS_INURBS; ++i)
|
|
|
+ if (!(ubc->isoinurbs[i] =
|
|
|
+ usb_alloc_urb(BAS_NUMFRAMES, SLAB_KERNEL)))
|
|
|
+ goto allocerr;
|
|
|
}
|
|
|
|
|
|
ucs->rcvbuf = NULL;
|
|
@@ -2156,15 +2183,14 @@ static int gigaset_probe(struct usb_interface *interface,
|
|
|
(endpoint->bEndpointAddress) & 0x0f),
|
|
|
ucs->int_in_buf, 3, read_int_callback, cs,
|
|
|
endpoint->bInterval);
|
|
|
- ret = usb_submit_urb(ucs->urb_int_in, SLAB_KERNEL);
|
|
|
- if (ret) {
|
|
|
+ if ((rc = usb_submit_urb(ucs->urb_int_in, SLAB_KERNEL)) != 0) {
|
|
|
dev_err(cs->dev, "could not submit interrupt URB: %s\n",
|
|
|
- get_usb_statmsg(ret));
|
|
|
+ get_usb_rcmsg(rc));
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
/* tell the device that the driver is ready */
|
|
|
- if ((ret = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0)) != 0)
|
|
|
+ if ((rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0)) != 0)
|
|
|
goto error;
|
|
|
|
|
|
/* tell common part that the device is ready */
|
|
@@ -2179,6 +2205,8 @@ static int gigaset_probe(struct usb_interface *interface,
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
+allocerr:
|
|
|
+ dev_err(cs->dev, "could not allocate URBs\n");
|
|
|
error:
|
|
|
freeurbs(cs);
|
|
|
usb_set_intfdata(interface, NULL);
|
|
@@ -2193,19 +2221,34 @@ static void gigaset_disconnect(struct usb_interface *interface)
|
|
|
{
|
|
|
struct cardstate *cs;
|
|
|
struct bas_cardstate *ucs;
|
|
|
+ int j;
|
|
|
|
|
|
cs = usb_get_intfdata(interface);
|
|
|
|
|
|
ucs = cs->hw.bas;
|
|
|
|
|
|
dev_info(cs->dev, "disconnecting Gigaset base\n");
|
|
|
+
|
|
|
+ /* mark base as not ready, all channels disconnected */
|
|
|
+ atomic_set(&ucs->basstate, 0);
|
|
|
+
|
|
|
+ /* tell LL all channels are down */
|
|
|
+ //FIXME shouldn't gigaset_stop() do this?
|
|
|
+ for (j = 0; j < 2; ++j)
|
|
|
+ gigaset_bchannel_down(cs->bcs + j);
|
|
|
+
|
|
|
+ /* stop driver (common part) */
|
|
|
gigaset_stop(cs);
|
|
|
+
|
|
|
+ /* stop timers and URBs, free ressources */
|
|
|
+ del_timer_sync(&ucs->timer_ctrl);
|
|
|
+ del_timer_sync(&ucs->timer_atrdy);
|
|
|
+ del_timer_sync(&ucs->timer_cmd_in);
|
|
|
freeurbs(cs);
|
|
|
usb_set_intfdata(interface, NULL);
|
|
|
kfree(ucs->rcvbuf);
|
|
|
ucs->rcvbuf = NULL;
|
|
|
ucs->rcvbuf_size = 0;
|
|
|
- atomic_set(&ucs->basstate, 0);
|
|
|
usb_put_dev(ucs->udev);
|
|
|
ucs->interface = NULL;
|
|
|
ucs->udev = NULL;
|
|
@@ -2277,6 +2320,8 @@ error: if (cardstate)
|
|
|
*/
|
|
|
static void __exit bas_gigaset_exit(void)
|
|
|
{
|
|
|
+ struct bas_cardstate *ucs = cardstate->hw.bas;
|
|
|
+
|
|
|
gigaset_blockdriver(driver); /* => probe will fail
|
|
|
* => no gigaset_start any more
|
|
|
*/
|
|
@@ -2284,14 +2329,26 @@ static void __exit bas_gigaset_exit(void)
|
|
|
gigaset_shutdown(cardstate);
|
|
|
/* from now on, no isdn callback should be possible */
|
|
|
|
|
|
- if (atomic_read(&cardstate->hw.bas->basstate) & BS_ATOPEN) {
|
|
|
- gig_dbg(DEBUG_ANY, "closing AT channel");
|
|
|
- if (req_submit(cardstate->bcs,
|
|
|
- HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT) >= 0) {
|
|
|
- /* successfully submitted */
|
|
|
- //FIXME wait for completion?
|
|
|
- }
|
|
|
+ /* close all still open channels */
|
|
|
+ if (atomic_read(&ucs->basstate) & BS_B1OPEN) {
|
|
|
+ gig_dbg(DEBUG_INIT, "closing B1 channel");
|
|
|
+ usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0),
|
|
|
+ HD_CLOSE_B1CHANNEL, OUT_VENDOR_REQ, 0, 0,
|
|
|
+ NULL, 0, BAS_TIMEOUT);
|
|
|
+ }
|
|
|
+ if (atomic_read(&ucs->basstate) & BS_B2OPEN) {
|
|
|
+ gig_dbg(DEBUG_INIT, "closing B2 channel");
|
|
|
+ usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0),
|
|
|
+ HD_CLOSE_B2CHANNEL, OUT_VENDOR_REQ, 0, 0,
|
|
|
+ NULL, 0, BAS_TIMEOUT);
|
|
|
+ }
|
|
|
+ if (atomic_read(&ucs->basstate) & BS_ATOPEN) {
|
|
|
+ gig_dbg(DEBUG_INIT, "closing AT channel");
|
|
|
+ usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0),
|
|
|
+ HD_CLOSE_ATCHANNEL, OUT_VENDOR_REQ, 0, 0,
|
|
|
+ NULL, 0, BAS_TIMEOUT);
|
|
|
}
|
|
|
+ atomic_set(&ucs->basstate, 0);
|
|
|
|
|
|
/* deregister this driver with the USB subsystem */
|
|
|
usb_deregister(&gigaset_usb_driver);
|