|
@@ -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)
|
|
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)
|
|
*/
|
|
*/
|
|
static inline void error_reset(struct cardstate *cs)
|
|
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
|
|
/* check_pending
|
|
@@ -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
|
|
|
|
- * 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:
|
|
default:
|
|
dev_warn(&ucs->interface->dev,
|
|
dev_warn(&ucs->interface->dev,
|
|
@@ -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)
|
|
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)
|
|
|
|
|
|
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)
|
|
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,
|
|
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))) {
|