Commit b5f581d5 authored by Tilman Schmidt's avatar Tilman Schmidt Committed by David S. Miller

gigaset: improve error recovery

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

Impact: error handling improvement
Signed-off-by: default avatarTilman Schmidt <tilman@imap.cc>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 05eae94f
...@@ -134,6 +134,7 @@ struct bas_cardstate { ...@@ -134,6 +134,7 @@ struct bas_cardstate {
#define BS_ATRDPEND 0x040 /* urb_cmd_in in use */ #define BS_ATRDPEND 0x040 /* urb_cmd_in in use */
#define BS_ATWRPEND 0x080 /* urb_cmd_out in use */ #define BS_ATWRPEND 0x080 /* urb_cmd_out in use */
#define BS_SUSPEND 0x100 /* USB port suspended */ #define BS_SUSPEND 0x100 /* USB port suspended */
#define BS_RESETTING 0x200 /* waiting for HD_RESET_INTERRUPT_PIPE_ACK */
static struct gigaset_driver *driver = NULL; static struct gigaset_driver *driver = NULL;
...@@ -319,6 +320,21 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag) ...@@ -319,6 +320,21 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
return -EINVAL; return -EINVAL;
} }
/* set/clear bits in base connection state, return previous state
*/
static inline 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 /* error_hangup
* hang up any existing connection because of an unrecoverable error * hang up any existing connection because of an unrecoverable error
* This function may be called from any context and takes care of scheduling * 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) ...@@ -350,12 +366,9 @@ static inline void error_hangup(struct bc_state *bcs)
*/ */
static inline void error_reset(struct cardstate *cs) static inline void error_reset(struct cardstate *cs)
{ {
/* close AT command channel to recover (ignore errors) */ /* reset interrupt pipe to recover (ignore errors) */
req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT); update_basstate(cs->hw.bas, BS_RESETTING, 0);
req_submit(cs->bcs, HD_RESET_INTERRUPT_PIPE, 0, BAS_TIMEOUT);
//FIXME try to recover without bothering the user
dev_err(cs->dev,
"unrecoverable error - please disconnect Gigaset base to reset\n");
} }
/* check_pending /* check_pending
...@@ -398,8 +411,13 @@ static void check_pending(struct bas_cardstate *ucs) ...@@ -398,8 +411,13 @@ static void check_pending(struct bas_cardstate *ucs)
case HD_DEVICE_INIT_ACK: /* no reply expected */ case HD_DEVICE_INIT_ACK: /* no reply expected */
ucs->pending = 0; ucs->pending = 0;
break; break;
/* HD_READ_ATMESSAGE, HD_WRITE_ATMESSAGE, HD_RESET_INTERRUPTPIPE case HD_RESET_INTERRUPT_PIPE:
* are handled separately and should never end up here 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: default:
dev_warn(&ucs->interface->dev, dev_warn(&ucs->interface->dev,
...@@ -449,21 +467,6 @@ static void cmd_in_timeout(unsigned long data) ...@@ -449,21 +467,6 @@ static void cmd_in_timeout(unsigned long data)
error_reset(cs); 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 /* read_ctrl_callback
* USB completion handler for control pipe input * USB completion handler for control pipe input
* called by the USB subsystem in interrupt context * called by the USB subsystem in interrupt context
...@@ -762,7 +765,8 @@ static void read_int_callback(struct urb *urb) ...@@ -762,7 +765,8 @@ static void read_int_callback(struct urb *urb)
break; break;
case HD_RESET_INTERRUPT_PIPE_ACK: 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; break;
case HD_SUSPEND_END: case HD_SUSPEND_END:
...@@ -1429,6 +1433,7 @@ static void req_timeout(unsigned long data) ...@@ -1429,6 +1433,7 @@ static void req_timeout(unsigned long data)
case HD_CLOSE_ATCHANNEL: case HD_CLOSE_ATCHANNEL:
dev_err(bcs->cs->dev, "timeout closing AT channel\n"); dev_err(bcs->cs->dev, "timeout closing AT channel\n");
error_reset(bcs->cs);
break; break;
case HD_CLOSE_B2CHANNEL: case HD_CLOSE_B2CHANNEL:
...@@ -1438,6 +1443,13 @@ static void req_timeout(unsigned long data) ...@@ -1438,6 +1443,13 @@ static void req_timeout(unsigned long data)
error_reset(bcs->cs); error_reset(bcs->cs);
break; 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: default:
dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n", dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n",
pending); pending);
...@@ -1930,6 +1942,15 @@ static int gigaset_write_cmd(struct cardstate *cs, ...@@ -1930,6 +1942,15 @@ static int gigaset_write_cmd(struct cardstate *cs,
goto notqueued; 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) if (len > IF_WRITEBUF)
len = IF_WRITEBUF; len = IF_WRITEBUF;
if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) { if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment