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

Commit 6d8fc4d2 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

USB HID: Handle STALL on interrupt endpoint



The USB HID driver doesn't include any code to handle a STALL on the
interrupt endpoint.  While this may be uncommon, it does happen
sometimes.  This patch (as805) adds a fix.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 88fafff9
Loading
Loading
Loading
Loading
+26 −13
Original line number Original line Diff line number Diff line
@@ -968,12 +968,20 @@ static void hid_retry_timeout(unsigned long _hid)
		hid_io_error(hid);
		hid_io_error(hid);
}
}


/* Workqueue routine to reset the device */
/* Workqueue routine to reset the device or clear a halt */
static void hid_reset(void *_hid)
static void hid_reset(void *_hid)
{
{
	struct hid_device *hid = (struct hid_device *) _hid;
	struct hid_device *hid = (struct hid_device *) _hid;
	int rc_lock, rc;
	int rc_lock, rc = 0;


	if (test_bit(HID_CLEAR_HALT, &hid->iofl)) {
		dev_dbg(&hid->intf->dev, "clear halt\n");
		rc = usb_clear_halt(hid->dev, hid->urbin->pipe);
		clear_bit(HID_CLEAR_HALT, &hid->iofl);
		hid_start_in(hid);
	}

	else if (test_bit(HID_RESET_PENDING, &hid->iofl)) {
		dev_dbg(&hid->intf->dev, "resetting device\n");
		dev_dbg(&hid->intf->dev, "resetting device\n");
		rc = rc_lock = usb_lock_device_for_reset(hid->dev, hid->intf);
		rc = rc_lock = usb_lock_device_for_reset(hid->dev, hid->intf);
		if (rc_lock >= 0) {
		if (rc_lock >= 0) {
@@ -982,6 +990,7 @@ static void hid_reset(void *_hid)
				usb_unlock_device(hid->dev);
				usb_unlock_device(hid->dev);
		}
		}
		clear_bit(HID_RESET_PENDING, &hid->iofl);
		clear_bit(HID_RESET_PENDING, &hid->iofl);
	}


	switch (rc) {
	switch (rc) {
	case 0:
	case 0:
@@ -1023,9 +1032,8 @@ static void hid_io_error(struct hid_device *hid)


		/* Retries failed, so do a port reset */
		/* Retries failed, so do a port reset */
		if (!test_and_set_bit(HID_RESET_PENDING, &hid->iofl)) {
		if (!test_and_set_bit(HID_RESET_PENDING, &hid->iofl)) {
			if (schedule_work(&hid->reset_work))
			schedule_work(&hid->reset_work);
			goto done;
			goto done;
			clear_bit(HID_RESET_PENDING, &hid->iofl);
		}
		}
	}
	}


@@ -1049,6 +1057,11 @@ static void hid_irq_in(struct urb *urb)
			hid->retry_delay = 0;
			hid->retry_delay = 0;
			hid_input_report(HID_INPUT_REPORT, urb, 1);
			hid_input_report(HID_INPUT_REPORT, urb, 1);
			break;
			break;
		case -EPIPE:		/* stall */
			clear_bit(HID_IN_RUNNING, &hid->iofl);
			set_bit(HID_CLEAR_HALT, &hid->iofl);
			schedule_work(&hid->reset_work);
			return;
		case -ECONNRESET:	/* unlink */
		case -ECONNRESET:	/* unlink */
		case -ENOENT:
		case -ENOENT:
		case -ESHUTDOWN:	/* unplug */
		case -ESHUTDOWN:	/* unplug */
+1 −0
Original line number Original line Diff line number Diff line
@@ -385,6 +385,7 @@ struct hid_control_fifo {
#define HID_IN_RUNNING		3
#define HID_IN_RUNNING		3
#define HID_RESET_PENDING	4
#define HID_RESET_PENDING	4
#define HID_SUSPENDED		5
#define HID_SUSPENDED		5
#define HID_CLEAR_HALT		6


struct hid_input {
struct hid_input {
	struct list_head list;
	struct list_head list;