Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+45 −24
Original line number Diff line number Diff line
@@ -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
 */
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
 * 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))) {