All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tilman Schmidt <tilman@imap.cc>
To: davem@davemloft.net, linux-kernel@vger.kernel.org,
	netdev@vger.kernel.org, i4ldeveloper@listserv.isdn4linux.de
Cc: Hansjoerg Lipp <hjlipp@web.de>
Subject: [PATCH 07/12] gigaset: improve error recovery
Date: Sun,  6 Sep 2009 20:58:52 +0200 (CEST)	[thread overview]
Message-ID: <20090906-patch-gigaset-07.tilman@imap.cc> (raw)
In-Reply-To: <20090906-patch-gigaset-00.tilman@imap.cc>

When the Gigaset base stops responding, try resetting the USB
connection to recover.

Impact: error handling improvement
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
---
 drivers/isdn/gigaset/bas-gigaset.c |   69 +++++++++++++++++++++++------------
 1 files changed, 45 insertions(+), 24 deletions(-)

diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 9e7108a..44bcfd9 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -134,6 +134,7 @@ struct bas_cardstate {
 #define BS_ATRDPEND	0x040	/* urb_cmd_in in use */
 #define BS_ATWRPEND	0x080	/* urb_cmd_out in use */
 #define BS_SUSPEND	0x100	/* USB port suspended */
+#define BS_RESETTING	0x200	/* waiting for HD_RESET_INTERRUPT_PIPE_ACK */
 
 
 static struct gigaset_driver *driver = NULL;
@@ -319,6 +320,21 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
 	return -EINVAL;
 }
 
+/* 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;
+
+	spin_lock_irqsave(&ucs->lock, flags);
+	state = ucs->basstate;
+	ucs->basstate = (state & ~clear) | set;
+	spin_unlock_irqrestore(&ucs->lock, flags);
+	return state;
+}
+
 /* error_hangup
  * hang up any existing connection because of an unrecoverable error
  * This function may be called from any context and takes care of scheduling
@@ -350,12 +366,9 @@ static inline void error_hangup(struct bc_state *bcs)
  */
 static inline void error_reset(struct cardstate *cs)
 {
-	/* close AT command channel to recover (ignore errors) */
-	req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT);
-
-	//FIXME try to recover without bothering the user
-	dev_err(cs->dev,
-	    "unrecoverable error - please disconnect Gigaset base to reset\n");
+	/* reset interrupt pipe to recover (ignore errors) */
+	update_basstate(cs->hw.bas, BS_RESETTING, 0);
+	req_submit(cs->bcs, HD_RESET_INTERRUPT_PIPE, 0, BAS_TIMEOUT);
 }
 
 /* check_pending
@@ -398,8 +411,13 @@ static void check_pending(struct bas_cardstate *ucs)
 	case HD_DEVICE_INIT_ACK:		/* no reply expected */
 		ucs->pending = 0;
 		break;
-	/* HD_READ_ATMESSAGE, HD_WRITE_ATMESSAGE, HD_RESET_INTERRUPTPIPE
-	 * are handled separately and should never end up here
+	case HD_RESET_INTERRUPT_PIPE:
+		if (!(ucs->basstate & BS_RESETTING))
+			ucs->pending = 0;
+		break;
+	/*
+	 * HD_READ_ATMESSAGE and HD_WRITE_ATMESSAGE are handled separately
+	 * and should never end up here
 	 */
 	default:
 		dev_warn(&ucs->interface->dev,
@@ -449,21 +467,6 @@ static void cmd_in_timeout(unsigned long data)
 	error_reset(cs);
 }
 
-/* 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;
-
-	spin_lock_irqsave(&ucs->lock, flags);
-	state = ucs->basstate;
-	ucs->basstate = (state & ~clear) | set;
-	spin_unlock_irqrestore(&ucs->lock, flags);
-	return state;
-}
-
 /* read_ctrl_callback
  * USB completion handler for control pipe input
  * called by the USB subsystem in interrupt context
@@ -762,7 +765,8 @@ static void read_int_callback(struct urb *urb)
 		break;
 
 	case HD_RESET_INTERRUPT_PIPE_ACK:
-		gig_dbg(DEBUG_USBREQ, "HD_RESET_INTERRUPT_PIPE_ACK");
+		update_basstate(ucs, 0, BS_RESETTING);
+		dev_notice(cs->dev, "interrupt pipe reset\n");
 		break;
 
 	case HD_SUSPEND_END:
@@ -1429,6 +1433,7 @@ static void req_timeout(unsigned long data)
 
 	case HD_CLOSE_ATCHANNEL:
 		dev_err(bcs->cs->dev, "timeout closing AT channel\n");
+		error_reset(bcs->cs);
 		break;
 
 	case HD_CLOSE_B2CHANNEL:
@@ -1438,6 +1443,13 @@ static void req_timeout(unsigned long data)
 		error_reset(bcs->cs);
 		break;
 
+	case HD_RESET_INTERRUPT_PIPE:
+		/* error recovery escalation */
+		dev_err(bcs->cs->dev,
+			"reset interrupt pipe timeout, attempting USB reset\n");
+		usb_queue_reset_device(bcs->cs->hw.bas->interface);
+		break;
+
 	default:
 		dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n",
 			 pending);
@@ -1930,6 +1942,15 @@ static int gigaset_write_cmd(struct cardstate *cs,
 		goto notqueued;
 	}
 
+	/* translate "+++" escape sequence sent as a single separate command
+	 * into "close AT channel" command for error recovery
+	 * The next command will reopen the AT channel automatically.
+	 */
+	if (len == 3 && !memcmp(buf, "+++", 3)) {
+		rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT);
+		goto notqueued;
+	}
+
 	if (len > IF_WRITEBUF)
 		len = IF_WRITEBUF;
 	if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
-- 
1.6.2.1.214.ge986c


WARNING: multiple messages have this Message-ID (diff)
From: Tilman Schmidt <tilman-ZTO5kqT2PaM@public.gmane.org>
To: davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	i4ldeveloper-JX7+OpRa80SjiSfgN6Y1Ib39b6g2fGNp@public.gmane.org
Cc: Hansjoerg Lipp <hjlipp-S0/GAf8tV78@public.gmane.org>
Subject: [PATCH 07/12] gigaset: improve error recovery
Date: Sun,  6 Sep 2009 20:58:52 +0200 (CEST)	[thread overview]
Message-ID: <20090906-patch-gigaset-07.tilman@imap.cc> (raw)
In-Reply-To: <20090906-patch-gigaset-00.tilman-ZTO5kqT2PaM@public.gmane.org>

When the Gigaset base stops responding, try resetting the USB
connection to recover.

Impact: error handling improvement
Signed-off-by: Tilman Schmidt <tilman-ZTO5kqT2PaM@public.gmane.org>
---
 drivers/isdn/gigaset/bas-gigaset.c |   69 +++++++++++++++++++++++------------
 1 files changed, 45 insertions(+), 24 deletions(-)

diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 9e7108a..44bcfd9 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -134,6 +134,7 @@ struct bas_cardstate {
 #define BS_ATRDPEND	0x040	/* urb_cmd_in in use */
 #define BS_ATWRPEND	0x080	/* urb_cmd_out in use */
 #define BS_SUSPEND	0x100	/* USB port suspended */
+#define BS_RESETTING	0x200	/* waiting for HD_RESET_INTERRUPT_PIPE_ACK */
 
 
 static struct gigaset_driver *driver = NULL;
@@ -319,6 +320,21 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
 	return -EINVAL;
 }
 
+/* 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;
+
+	spin_lock_irqsave(&ucs->lock, flags);
+	state = ucs->basstate;
+	ucs->basstate = (state & ~clear) | set;
+	spin_unlock_irqrestore(&ucs->lock, flags);
+	return state;
+}
+
 /* error_hangup
  * hang up any existing connection because of an unrecoverable error
  * This function may be called from any context and takes care of scheduling
@@ -350,12 +366,9 @@ static inline void error_hangup(struct bc_state *bcs)
  */
 static inline void error_reset(struct cardstate *cs)
 {
-	/* close AT command channel to recover (ignore errors) */
-	req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT);
-
-	//FIXME try to recover without bothering the user
-	dev_err(cs->dev,
-	    "unrecoverable error - please disconnect Gigaset base to reset\n");
+	/* reset interrupt pipe to recover (ignore errors) */
+	update_basstate(cs->hw.bas, BS_RESETTING, 0);
+	req_submit(cs->bcs, HD_RESET_INTERRUPT_PIPE, 0, BAS_TIMEOUT);
 }
 
 /* check_pending
@@ -398,8 +411,13 @@ static void check_pending(struct bas_cardstate *ucs)
 	case HD_DEVICE_INIT_ACK:		/* no reply expected */
 		ucs->pending = 0;
 		break;
-	/* HD_READ_ATMESSAGE, HD_WRITE_ATMESSAGE, HD_RESET_INTERRUPTPIPE
-	 * are handled separately and should never end up here
+	case HD_RESET_INTERRUPT_PIPE:
+		if (!(ucs->basstate & BS_RESETTING))
+			ucs->pending = 0;
+		break;
+	/*
+	 * HD_READ_ATMESSAGE and HD_WRITE_ATMESSAGE are handled separately
+	 * and should never end up here
 	 */
 	default:
 		dev_warn(&ucs->interface->dev,
@@ -449,21 +467,6 @@ static void cmd_in_timeout(unsigned long data)
 	error_reset(cs);
 }
 
-/* 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;
-
-	spin_lock_irqsave(&ucs->lock, flags);
-	state = ucs->basstate;
-	ucs->basstate = (state & ~clear) | set;
-	spin_unlock_irqrestore(&ucs->lock, flags);
-	return state;
-}
-
 /* read_ctrl_callback
  * USB completion handler for control pipe input
  * called by the USB subsystem in interrupt context
@@ -762,7 +765,8 @@ static void read_int_callback(struct urb *urb)
 		break;
 
 	case HD_RESET_INTERRUPT_PIPE_ACK:
-		gig_dbg(DEBUG_USBREQ, "HD_RESET_INTERRUPT_PIPE_ACK");
+		update_basstate(ucs, 0, BS_RESETTING);
+		dev_notice(cs->dev, "interrupt pipe reset\n");
 		break;
 
 	case HD_SUSPEND_END:
@@ -1429,6 +1433,7 @@ static void req_timeout(unsigned long data)
 
 	case HD_CLOSE_ATCHANNEL:
 		dev_err(bcs->cs->dev, "timeout closing AT channel\n");
+		error_reset(bcs->cs);
 		break;
 
 	case HD_CLOSE_B2CHANNEL:
@@ -1438,6 +1443,13 @@ static void req_timeout(unsigned long data)
 		error_reset(bcs->cs);
 		break;
 
+	case HD_RESET_INTERRUPT_PIPE:
+		/* error recovery escalation */
+		dev_err(bcs->cs->dev,
+			"reset interrupt pipe timeout, attempting USB reset\n");
+		usb_queue_reset_device(bcs->cs->hw.bas->interface);
+		break;
+
 	default:
 		dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n",
 			 pending);
@@ -1930,6 +1942,15 @@ static int gigaset_write_cmd(struct cardstate *cs,
 		goto notqueued;
 	}
 
+	/* translate "+++" escape sequence sent as a single separate command
+	 * into "close AT channel" command for error recovery
+	 * The next command will reopen the AT channel automatically.
+	 */
+	if (len == 3 && !memcmp(buf, "+++", 3)) {
+		rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT);
+		goto notqueued;
+	}
+
 	if (len > IF_WRITEBUF)
 		len = IF_WRITEBUF;
 	if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
-- 
1.6.2.1.214.ge986c

  parent reply	other threads:[~2009-09-06 19:01 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-09-06 18:58 [PATCH 00/12] Gigaset driver patches for 2.6.32 Tilman Schmidt
2009-09-06 18:58 ` [PATCH 11/12] gigaset: allow building without I4L Tilman Schmidt
2009-09-06 18:58 ` [PATCH 05/12] gigaset: announce if built with debugging Tilman Schmidt
2009-09-06 18:58 ` [PATCH 02/12] gigaset: fix reject/hangup handling Tilman Schmidt
2009-09-06 18:58 ` [PATCH 06/12] gigaset: fix device ERROR response handling Tilman Schmidt
2009-09-06 18:58 ` [PATCH 09/12] gigaset: add kerneldoc comments Tilman Schmidt
2009-09-06 18:58 ` [PATCH 03/12] gigaset: linearize skb Tilman Schmidt
2009-09-06 18:58   ` Tilman Schmidt
2009-09-06 18:58 ` [PATCH 10/12] gigaset: prepare for CAPI implementation Tilman Schmidt
2009-09-06 18:58   ` Tilman Schmidt
2009-09-06 18:58 ` [PATCH 12/12] gigaset: add Kernel CAPI interface Tilman Schmidt
2009-09-06 18:58   ` Tilman Schmidt
2009-09-06 18:58 ` [PATCH 08/12] gigaset: correct debugging output selection Tilman Schmidt
2009-09-06 18:58   ` Tilman Schmidt
2009-09-06 18:58 ` [PATCH 04/12] gigaset: handle isoc frame errors more gracefully Tilman Schmidt
2009-09-06 18:58   ` Tilman Schmidt
2009-09-06 18:58 ` [PATCH 01/12] gigaset: really fix chars_in_buffer Tilman Schmidt
2009-09-06 18:58 ` Tilman Schmidt [this message]
2009-09-06 18:58   ` [PATCH 07/12] gigaset: improve error recovery Tilman Schmidt
2009-09-07  8:59   ` [PATCH 07/12 v2] " Tilman Schmidt
2009-09-07  8:59     ` Tilman Schmidt
2009-09-07  1:26 ` [PATCH 00/12] Gigaset driver patches for 2.6.32 Daniel Walker
2009-09-07  9:07   ` Tilman Schmidt
2009-09-07 14:30     ` Daniel Walker
2009-09-07 14:30     ` Daniel Walker
2009-09-07  9:00 ` David Miller
2009-09-07  9:11   ` Tilman Schmidt
2009-09-07  9:18     ` David Miller
2009-09-07 18:56       ` Karsten Keil

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090906-patch-gigaset-07.tilman@imap.cc \
    --to=tilman@imap.cc \
    --cc=davem@davemloft.net \
    --cc=hjlipp@web.de \
    --cc=i4ldeveloper@listserv.isdn4linux.de \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.