public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [2.6 Patch] isdn4linux: Siemens Gigaset base driver: fix disconnect handling
@ 2006-04-19 18:38 Tilman Schmidt
  2006-04-20  7:11 ` Andrew Morton
  0 siblings, 1 reply; 7+ messages in thread
From: Tilman Schmidt @ 2006-04-19 18:38 UTC (permalink / raw)
  To: Andrew Morton, Karsten Keil, isdn4linux, linux-kernel; +Cc: Hansjoerg Lipp

From: Tilman Schmidt <tilman@imap.cc>

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@imap.cc>
Acked-by: Hansjoerg Lipp <hjlipp@web.de>

---

 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@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_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 */

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [2.6 Patch] isdn4linux: Siemens Gigaset base driver: fix disconnect handling
  2006-04-19 18:38 [2.6 Patch] isdn4linux: Siemens Gigaset base driver: fix disconnect handling Tilman Schmidt
@ 2006-04-20  7:11 ` Andrew Morton
  2006-04-20 12:07   ` Tilman Schmidt
  0 siblings, 1 reply; 7+ messages in thread
From: Andrew Morton @ 2006-04-20  7:11 UTC (permalink / raw)
  To: Tilman Schmidt; +Cc: kkeil, isdn4linux, linux-kernel, hjlipp

Tilman Schmidt <tilman@imap.cc> wrote:
>
> 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.

It seems to do quite a lot more than that.

>  Please merge at your earliest convenience.

Well OK, but I'd ask you to confirm that this was actually the patch which
was supposed to go with that changelog.

>  --- 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

eek, please don't do that - it confuses my scripts.

--- linux-2.6.17-rc2-work/drivers/isdn/gigaset/bas-gigaset.c
+++ linux-2.6.17-rc2-work/drivers/isdn/gigaset/bas-gigaset.c

is preferred.  There are nice tools around which help with this.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [2.6 Patch] isdn4linux: Siemens Gigaset base driver: fix disconnect handling
  2006-04-20  7:11 ` Andrew Morton
@ 2006-04-20 12:07   ` Tilman Schmidt
  2006-04-20 15:21     ` Randy.Dunlap
  0 siblings, 1 reply; 7+ messages in thread
From: Tilman Schmidt @ 2006-04-20 12:07 UTC (permalink / raw)
  To: Andrew Morton
  Cc: kkeil, i4ldeveloper, linux-kernel, hjlipp, Greg KH,
	linux-usb-devel

[-- Attachment #1: Type: text/plain, Size: 2474 bytes --]

On 20.04.2006 09:11, Andrew Morton wrote:
> Tilman Schmidt <tilman@imap.cc> wrote:
> 
>>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.
> 
> It seems to do quite a lot more than that.
> [...] I'd ask you to confirm that this was actually the patch which
> was supposed to go with that changelog.

It is. However the changelog was not quite complete. There were a couple
of smaller changes which I neglected to mention. Sorry for that. I'll
try to be more careful when composing future changelogs.

The missing changes are:

Avoid unsafe checks of URB status fields outside the URB completion
handlers, keep track of in-use URBs myself instead.
If an isochronous transfer URB completes with status==0, also check the
status of the frame descriptors.
Verify length of interrupt messages received from the device.
Align the length limit on transmitted AT commands with the device
documentation.
In case of AT response receive overrun, keep newly arrived instead of
old unread data.
Remove redundant check of device ID in the USB probe function.
Correct and improve some comments and formatting.

>> --- 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
> 
> eek, please don't do that - it confuses my scripts.
> 
> --- linux-2.6.17-rc2-work/drivers/isdn/gigaset/bas-gigaset.c
> +++ linux-2.6.17-rc2-work/drivers/isdn/gigaset/bas-gigaset.c
> 
> is preferred.  There are nice tools around which help with this.

I'll be happy to comply with whatever your scripts need, but I don't
quite understand yet what it is that's causing you problems:
The presence of file timestamps?
The timestamp of the second file being earlier than the first one?
The ".orig" suffix in the first path?
The second path starting with "local" instead of "linux-..."?
All of this can be easily corrected; just tell me what's required.

Thanks
Tilman

-- 
Tilman Schmidt                          E-Mail: tilman@imap.cc
Bonn, Germany
It is well known that a vital ingredient of success is not knowing that
what you're attempting can't be done. [Terry Pratchett, "Equal Rites"]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 253 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [2.6 Patch] isdn4linux: Siemens Gigaset base driver: fix disconnect handling
  2006-04-20 12:07   ` Tilman Schmidt
@ 2006-04-20 15:21     ` Randy.Dunlap
  2006-04-20 15:32       ` Adrian Bunk
  0 siblings, 1 reply; 7+ messages in thread
From: Randy.Dunlap @ 2006-04-20 15:21 UTC (permalink / raw)
  To: Tilman Schmidt
  Cc: akpm, kkeil, i4ldeveloper, linux-kernel, hjlipp, gregkh,
	linux-usb-devel

On Thu, 20 Apr 2006 14:07:11 +0200 Tilman Schmidt wrote:

> On 20.04.2006 09:11, Andrew Morton wrote:
> > Tilman Schmidt <tilman@imap.cc> wrote:
> > 
> >>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.
> > 
> > It seems to do quite a lot more than that.
> > [...] I'd ask you to confirm that this was actually the patch which
> > was supposed to go with that changelog.
> 
> It is. However the changelog was not quite complete. There were a couple
> of smaller changes which I neglected to mention. Sorry for that. I'll
> try to be more careful when composing future changelogs.

...

> >> --- 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
> > 
> > eek, please don't do that - it confuses my scripts.
> > 
> > --- linux-2.6.17-rc2-work/drivers/isdn/gigaset/bas-gigaset.c
> > +++ linux-2.6.17-rc2-work/drivers/isdn/gigaset/bas-gigaset.c
> > 
> > is preferred.  There are nice tools around which help with this.
> 
> I'll be happy to comply with whatever your scripts need, but I don't
> quite understand yet what it is that's causing you problems:
> The presence of file timestamps?
> The timestamp of the second file being earlier than the first one?
> The ".orig" suffix in the first path?
> The second path starting with "local" instead of "linux-..."?
> All of this can be easily corrected; just tell me what's required.

Both filename lines should begin one level above "drivers/".
They can be named linux* or a/ and b/ or foo/ and bar/ or whatever.
The patch needs to apply using "patch -p1".

This is all well-documented.  Please review:

linux-2.6.current/Documentation/SubmittingPatches
http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt
http://linux.yyz.us/patch-format.html

and let us know if anything there needs to be clarified.

---
~Randy

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [2.6 Patch] isdn4linux: Siemens Gigaset base driver: fix disconnect handling
  2006-04-20 15:21     ` Randy.Dunlap
@ 2006-04-20 15:32       ` Adrian Bunk
  2006-04-20 16:14         ` Randy.Dunlap
  2006-04-20 18:29         ` Tilman Schmidt
  0 siblings, 2 replies; 7+ messages in thread
From: Adrian Bunk @ 2006-04-20 15:32 UTC (permalink / raw)
  To: Randy.Dunlap
  Cc: Tilman Schmidt, akpm, kkeil, i4ldeveloper, linux-kernel, hjlipp,
	gregkh, linux-usb-devel

On Thu, Apr 20, 2006 at 08:21:20AM -0700, Randy.Dunlap wrote:
> On Thu, 20 Apr 2006 14:07:11 +0200 Tilman Schmidt wrote:
> 
> > On 20.04.2006 09:11, Andrew Morton wrote:
> > > Tilman Schmidt <tilman@imap.cc> wrote:
> > > 
> > >>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.
> > > 
> > > It seems to do quite a lot more than that.
> > > [...] I'd ask you to confirm that this was actually the patch which
> > > was supposed to go with that changelog.
> > 
> > It is. However the changelog was not quite complete. There were a couple
> > of smaller changes which I neglected to mention. Sorry for that. I'll
> > try to be more careful when composing future changelogs.
> 
> ...
> 
> > >> --- 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
> > > 
> > > eek, please don't do that - it confuses my scripts.
> > > 
> > > --- linux-2.6.17-rc2-work/drivers/isdn/gigaset/bas-gigaset.c
> > > +++ linux-2.6.17-rc2-work/drivers/isdn/gigaset/bas-gigaset.c
> > > 
> > > is preferred.  There are nice tools around which help with this.
> > 
> > I'll be happy to comply with whatever your scripts need, but I don't
> > quite understand yet what it is that's causing you problems:
> > The presence of file timestamps?
> > The timestamp of the second file being earlier than the first one?
> > The ".orig" suffix in the first path?
> > The second path starting with "local" instead of "linux-..."?
> > All of this can be easily corrected; just tell me what's required.
> 
> Both filename lines should begin one level above "drivers/".
> They can be named linux* or a/ and b/ or foo/ and bar/ or whatever.
> The patch needs to apply using "patch -p1".
>...

His patch already was nearly correct.

The only thing that is wrong is
  linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/bas-gigaset.c
                                            ^^^^^
If anything else confused Andrew's scripts, he'd better fix his scripts.

> ~Randy

cu
Adrian

-- 

       "Is there not promise of rain?" Ling Tan asked suddenly out
        of the darkness. There had been need of rain for many days.
       "Only a promise," Lao Er said.
                                       Pearl S. Buck - Dragon Seed


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [2.6 Patch] isdn4linux: Siemens Gigaset base driver: fix disconnect handling
  2006-04-20 15:32       ` Adrian Bunk
@ 2006-04-20 16:14         ` Randy.Dunlap
  2006-04-20 18:29         ` Tilman Schmidt
  1 sibling, 0 replies; 7+ messages in thread
From: Randy.Dunlap @ 2006-04-20 16:14 UTC (permalink / raw)
  To: Adrian Bunk
  Cc: tilman, akpm, kkeil, i4ldeveloper, linux-kernel, hjlipp, gregkh,
	linux-usb-devel

On Thu, 20 Apr 2006 17:32:36 +0200 Adrian Bunk wrote:

> On Thu, Apr 20, 2006 at 08:21:20AM -0700, Randy.Dunlap wrote:
> > On Thu, 20 Apr 2006 14:07:11 +0200 Tilman Schmidt wrote:
> > 
> > > On 20.04.2006 09:11, Andrew Morton wrote:
> > > > Tilman Schmidt <tilman@imap.cc> wrote:
> > > > 
> > > >>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.
> > > > 
> > > > It seems to do quite a lot more than that.
> > > > [...] I'd ask you to confirm that this was actually the patch which
> > > > was supposed to go with that changelog.
> > > 
> > > It is. However the changelog was not quite complete. There were a couple
> > > of smaller changes which I neglected to mention. Sorry for that. I'll
> > > try to be more careful when composing future changelogs.
> > 
> > ...
> > 
> > > >> --- 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
> > > > 
> > > > eek, please don't do that - it confuses my scripts.
> > > > 
> > > > --- linux-2.6.17-rc2-work/drivers/isdn/gigaset/bas-gigaset.c
> > > > +++ linux-2.6.17-rc2-work/drivers/isdn/gigaset/bas-gigaset.c
> > > > 
> > > > is preferred.  There are nice tools around which help with this.
> > > 
> > > I'll be happy to comply with whatever your scripts need, but I don't
> > > quite understand yet what it is that's causing you problems:
> > > The presence of file timestamps?
> > > The timestamp of the second file being earlier than the first one?
> > > The ".orig" suffix in the first path?
> > > The second path starting with "local" instead of "linux-..."?
> > > All of this can be easily corrected; just tell me what's required.
> > 
> > Both filename lines should begin one level above "drivers/".
> > They can be named linux* or a/ and b/ or foo/ and bar/ or whatever.
> > The patch needs to apply using "patch -p1".
> >...
> 
> His patch already was nearly correct.
> 
> The only thing that is wrong is
>   linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/bas-gigaset.c
>                                             ^^^^^
> If anything else confused Andrew's scripts, he'd better fix his scripts.

Ah, I see, thanks.

---
~Randy

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [2.6 Patch] isdn4linux: Siemens Gigaset base driver: fix disconnect handling
  2006-04-20 15:32       ` Adrian Bunk
  2006-04-20 16:14         ` Randy.Dunlap
@ 2006-04-20 18:29         ` Tilman Schmidt
  1 sibling, 0 replies; 7+ messages in thread
From: Tilman Schmidt @ 2006-04-20 18:29 UTC (permalink / raw)
  To: Adrian Bunk
  Cc: Randy.Dunlap, akpm, kkeil, i4ldeveloper, linux-kernel, hjlipp,
	gregkh, linux-usb-devel

[-- Attachment #1: Type: text/plain, Size: 1643 bytes --]

On 20.04.2006 17:32, Adrian Bunk wrote:

> On Thu, Apr 20, 2006 at 08:21:20AM -0700, Randy.Dunlap wrote:
[...]
>>>>Tilman Schmidt <tilman@imap.cc> wrote:
>>>>
>>>>>--- 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
>>
>>Both filename lines should begin one level above "drivers/".
>>They can be named linux* or a/ and b/ or foo/ and bar/ or whatever.
>>The patch needs to apply using "patch -p1".

Well, so it does.

> The only thing that is wrong is
>   linux-2.6.17-rc2-work/drivers/isdn/gigaset.orig/bas-gigaset.c
>                                             ^^^^^

I see. I'll avoid that in the future.

Randy.Dunlap again:

>> This is all well-documented.  Please review:
>> 
>> linux-2.6.current/Documentation/SubmittingPatches
>> http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt
>> http://linux.yyz.us/patch-format.html
>> 
>> and let us know if anything there needs to be clarified.

What wasn't obvious to me from these documents is that the "---" path
must also be "-p1 conforming" (so to say), as the "patch -p1" command
itself only requires that for the "+++" path.

Unfortunately I can't think of a wording that would have prevented that
misunderstanding for me. Perhaps a native English speaker might come up
with something.

Thanks
Tilman

-- 
Tilman Schmidt                          E-Mail: tilman@imap.cc
Bonn, Germany
Diese Nachricht besteht zu 100% aus wiederverwerteten Bits.
Ungeöffnet mindestens haltbar bis: (siehe Rückseite)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 253 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2006-04-20 18:28 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-04-19 18:38 [2.6 Patch] isdn4linux: Siemens Gigaset base driver: fix disconnect handling Tilman Schmidt
2006-04-20  7:11 ` Andrew Morton
2006-04-20 12:07   ` Tilman Schmidt
2006-04-20 15:21     ` Randy.Dunlap
2006-04-20 15:32       ` Adrian Bunk
2006-04-20 16:14         ` Randy.Dunlap
2006-04-20 18:29         ` Tilman Schmidt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox