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

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

isdn/gigaset: fix bas_gigaset AT read error handling



Rework the handling of USB errors in AT response reads
to fix a possible infinite retry loop and a memory leak,
and silence a few overly verbose kernel messages.

Signed-off-by: default avatarTilman Schmidt <tilman@imap.cc>
CC: stable <stable@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b33ffa5c
Loading
Loading
Loading
Loading
+33 −50
Original line number Diff line number Diff line
@@ -438,24 +438,28 @@ static void cmd_in_timeout(unsigned long data)
		return;
	}

	if (ucs->retry_cmd_in++ < BAS_RETRY) {
		dev_notice(cs->dev, "control read: timeout, retry %d\n",
			   ucs->retry_cmd_in);
		rc = atread_submit(cs, BAS_TIMEOUT);
		if (rc >= 0 || rc == -ENODEV)
			/* resubmitted or disconnected */
			/* - bypass regular exit block */
			return;
	} else {
	if (ucs->retry_cmd_in++ >= BAS_RETRY) {
		dev_err(cs->dev,
			"control read: timeout, giving up after %d tries\n",
			ucs->retry_cmd_in);
		kfree(ucs->rcvbuf);
		ucs->rcvbuf = NULL;
		ucs->rcvbuf_size = 0;
		error_reset(cs);
		return;
	}

	gig_dbg(DEBUG_USBREQ, "%s: timeout, retry %d",
		__func__, ucs->retry_cmd_in);
	rc = atread_submit(cs, BAS_TIMEOUT);
	if (rc < 0) {
		kfree(ucs->rcvbuf);
		ucs->rcvbuf = NULL;
		ucs->rcvbuf_size = 0;
		if (rc != -ENODEV)
			error_reset(cs);
	}
}

/* read_ctrl_callback
 * USB completion handler for control pipe input
@@ -470,18 +474,11 @@ static void read_ctrl_callback(struct urb *urb)
	struct cardstate *cs = inbuf->cs;
	struct bas_cardstate *ucs = cs->hw.bas;
	int status = urb->status;
	int have_data = 0;
	unsigned numbytes;
	int rc;

	update_basstate(ucs, 0, BS_ATRDPEND);
	wake_up(&ucs->waitqueue);

	if (!ucs->rcvbuf_size) {
		dev_warn(cs->dev, "%s: no receive in progress\n", __func__);
		return;
	}

	del_timer(&ucs->timer_cmd_in);

	switch (status) {
@@ -495,19 +492,10 @@ static void read_ctrl_callback(struct urb *urb)
				numbytes = ucs->rcvbuf_size;
		}

		/* copy received bytes to inbuf */
		have_data = gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes);

		if (unlikely(numbytes < ucs->rcvbuf_size)) {
			/* incomplete - resubmit for remaining bytes */
			ucs->rcvbuf_size -= numbytes;
			ucs->retry_cmd_in = 0;
			rc = atread_submit(cs, BAS_TIMEOUT);
			if (rc >= 0 || rc == -ENODEV)
				/* resubmitted or disconnected */
				/* - bypass regular exit block */
				return;
			error_reset(cs);
		/* copy received bytes to inbuf, notify event layer */
		if (gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes)) {
			gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
			gigaset_schedule_event(cs);
		}
		break;

@@ -516,37 +504,32 @@ static void read_ctrl_callback(struct urb *urb)
	case -EINPROGRESS:		/* pending */
	case -ENODEV:			/* device removed */
	case -ESHUTDOWN:		/* device shut down */
		/* no action necessary */
		/* no further action necessary */
		gig_dbg(DEBUG_USBREQ, "%s: %s",
			__func__, get_usb_statmsg(status));
		break;

	default:			/* severe trouble */
		dev_warn(cs->dev, "control read: %s\n",
			 get_usb_statmsg(status));
	default:			/* other errors: retry */
		if (ucs->retry_cmd_in++ < BAS_RETRY) {
			dev_notice(cs->dev, "control read: retry %d\n",
				   ucs->retry_cmd_in);
			gig_dbg(DEBUG_USBREQ, "%s: %s, retry %d", __func__,
				get_usb_statmsg(status), ucs->retry_cmd_in);
			rc = atread_submit(cs, BAS_TIMEOUT);
			if (rc >= 0 || rc == -ENODEV)
				/* resubmitted or disconnected */
				/* - bypass regular exit block */
			if (rc >= 0)
				/* successfully resubmitted, skip freeing */
				return;
		} else {
			dev_err(cs->dev,
				"control read: giving up after %d tries\n",
				ucs->retry_cmd_in);
			if (rc == -ENODEV)
				/* disconnect, no further action necessary */
				break;
		}
		dev_err(cs->dev, "control read: %s, giving up after %d tries\n",
			get_usb_statmsg(status), ucs->retry_cmd_in);
		error_reset(cs);
	}

	/* read finished, free buffer */
	kfree(ucs->rcvbuf);
	ucs->rcvbuf = NULL;
	ucs->rcvbuf_size = 0;
	if (have_data) {
		gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
		gigaset_schedule_event(cs);
	}
}

/* atread_submit