[2.6 Patch] isdn4linux: Siemens Gigaset base driver: fix disconnect handling

From: Tilman Schmidt
Date: Wed Apr 19 2006 - 14:38:39 EST


From: Tilman Schmidt <tilman@xxxxxxx>

This patch fixes a possible Oops in the Siemens Gigaset base driver when
the device is unplugged while an ISDN connection is still active, and
makes sure that the isdn4linux link level (LL) is properly informed if a
connection is broken by the USB cable being unplugged.
It also improves some kernel messages generated by the driver.

Please merge at your earliest convenience.

Signed-off-by: Tilman Schmidt <tilman@xxxxxxx>
Acked-by: Hansjoerg Lipp <hjlipp@xxxxxx>

---

bas-gigaset.c | 599 +++++++++++++++++++++++++++++++---------------------------
common.c | 3
ev-layer.c | 3
gigaset.h | 7
i4l.c | 2
isocdata.c | 10
6 files changed, 343 insertions(+), 281 deletions(-)

diff -purX dont-diff linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/bas-gigaset.c local/drivers/isdn/gigaset/bas-gigaset.c
--- linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/bas-gigaset.c 2006-04-19 15:15:49.000000000 +0200
+++ local/drivers/isdn/gigaset/bas-gigaset.c 2006-04-19 01:19:41.000000000 +0200
@@ -5,8 +5,6 @@
* Tilman Schmidt <tilman@xxxxxxx>,
* 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_inte
/* 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_dri
.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 b
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
{
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 cardstat
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 cardstat
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 cardstat
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
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
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
}
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
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 ur
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 ur
}
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 ur
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 ur
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
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
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
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 ur
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 *bc
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 *bc
}

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 *bc
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 *bc
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
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
/* 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 i
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 i
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 (rc) {
+ if (unlikely(nframe == 0))
+ return 0; /* no data to send */
+ urb->number_of_packets = nframe;
+
+ 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 l
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 l
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 l
/* 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 l
}
#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 l
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 lo
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 lo
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 u
* 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 *b
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 *b
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
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
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(struc
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
* 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 cardsta
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
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 card
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 cards
*/
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 b

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_inte
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_inte
* - 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_inte
(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_inte

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 us
{
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);
diff -purX dont-diff linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/common.c local/drivers/isdn/gigaset/common.c
--- linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/common.c 2006-04-19 19:27:24.000000000 +0200
+++ local/drivers/isdn/gigaset/common.c 2006-04-14 00:17:15.000000000 +0200
@@ -781,8 +781,7 @@ error: if (cs)
}
EXPORT_SYMBOL_GPL(gigaset_initcs);

-/* ReInitialize the b-channel structure */
-/* e.g. called on hangup, disconnect */
+/* ReInitialize the b-channel structure on hangup */
void gigaset_bcs_reinit(struct bc_state *bcs)
{
struct sk_buff *skb;
diff -purX dont-diff linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/ev-layer.c local/drivers/isdn/gigaset/ev-layer.c
--- linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/ev-layer.c 2006-04-19 15:15:49.000000000 +0200
+++ local/drivers/isdn/gigaset/ev-layer.c 2006-04-19 01:20:52.000000000 +0200
@@ -373,6 +373,9 @@ struct reply_t gigaset_tab_cid_m10x[] =

{EV_TIMEOUT, 750,750, -1, 0, 0, {ACT_CONNTIMEOUT}},

+ /* B channel closed (general case) */
+ {EV_BC_CLOSED, -1, -1, -1, -1,-1, {ACT_NOTIFY_BC_DOWN}}, //FIXME
+
/* misc. */
{EV_PROTO_L2, -1, -1, -1, -1,-1, {ACT_PROTO_L2}}, //FIXME

diff -purX dont-diff linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/gigaset.h local/drivers/isdn/gigaset/gigaset.h
--- linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/gigaset.h 2006-04-19 19:27:24.000000000 +0200
+++ local/drivers/isdn/gigaset/gigaset.h 2006-04-19 01:21:46.000000000 +0200
@@ -75,7 +75,7 @@ extern int gigaset_debuglevel; /* "needs
* e.g. 'insmod usb_gigaset.o debug=0x2c' will set DEBUG_OPEN, DEBUG_CMD and
* DEBUG_INTR.
*/
-enum debuglevel { /* up to 24 bits (atomic_t) */
+enum debuglevel {
DEBUG_REG = 0x0002, /* serial port I/O register operations */
DEBUG_OPEN = 0x0004, /* open/close serial port */
DEBUG_INTR = 0x0008, /* interrupt processing */
@@ -141,7 +141,7 @@ enum debuglevel { /* up to 24 bits (atom
printk(KERN_DEBUG KBUILD_MODNAME ": " format "\n", \
## arg); \
} while (0)
-#define DEBUG_DEFAULT (DEBUG_INIT | DEBUG_TRANSCMD | DEBUG_CMD | DEBUG_USBREQ)
+#define DEBUG_DEFAULT (DEBUG_TRANSCMD | DEBUG_CMD | DEBUG_USBREQ)

#else

@@ -627,8 +627,7 @@ struct gigaset_ops {
/* Called by gigaset_freecs() for freeing bcs->hw.xxx */
int (*freebcshw)(struct bc_state *bcs);

- /* Called by gigaset_stop() or gigaset_bchannel_down() for resetting
- bcs->hw.xxx */
+ /* Called by gigaset_bchannel_down() for resetting bcs->hw.xxx */
void (*reinitbcshw)(struct bc_state *bcs);

/* Called by gigaset_initcs() for setting up cs->hw.xxx */
diff -purX dont-diff linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/i4l.c local/drivers/isdn/gigaset/i4l.c
--- linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/i4l.c 2006-04-19 15:15:49.000000000 +0200
+++ local/drivers/isdn/gigaset/i4l.c 2006-04-15 19:56:10.000000000 +0200
@@ -73,7 +73,7 @@ static int writebuf_from_LL(int driverID
len, skblen, (unsigned) skb->head[0], (unsigned) skb->head[1]);

/* pass to device-specific module */
- return cs->ops->send_skb(bcs, skb); //FIXME cs->ops->send_skb() must handle !cs->connected correctly
+ return cs->ops->send_skb(bcs, skb);
}

void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
diff -purX dont-diff linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/isocdata.c local/drivers/isdn/gigaset/isocdata.c
--- linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/isocdata.c 2006-04-19 15:15:49.000000000 +0200
+++ local/drivers/isdn/gigaset/isocdata.c 2006-04-15 20:03:00.000000000 +0200
@@ -992,14 +992,18 @@ int gigaset_isoc_send_skb(struct bc_stat
int len = skb->len;
unsigned long flags;

+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ if (!bcs->cs->connected) {
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ return -ENODEV;
+ }
+
skb_queue_tail(&bcs->squeue, skb);
gig_dbg(DEBUG_ISO, "%s: skb queued, qlen=%d",
__func__, skb_queue_len(&bcs->squeue));

/* tasklet submits URB if necessary */
- spin_lock_irqsave(&bcs->cs->lock, flags);
- if (bcs->cs->connected)
- tasklet_schedule(&bcs->hw.bas->sent_tasklet);
+ tasklet_schedule(&bcs->hw.bas->sent_tasklet);
spin_unlock_irqrestore(&bcs->cs->lock, flags);

return len; /* ok so far */
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/