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

Commit 858155fb authored by Oliver Neukum's avatar Oliver Neukum Committed by Jiri Kosina
Browse files

HID: usbhid: introduce timeout for stuck ctrl/out URBs



Some devices do not react to a control request (seen on APC UPS's) resulting in
a slow stream of messages, "generic-usb ... control queue full".  Therefore
request needs a timeout.

Cc: stable@kernel.org
Signed-off-by: default avatarOliver Neukum <oliver@neukum.org>
Signed-off-by: default avatarDavid Fries <david@fries.net>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent c8a8602b
Loading
Loading
Loading
Loading
+26 −2
Original line number Diff line number Diff line
@@ -316,6 +316,7 @@ static int hid_submit_out(struct hid_device *hid)
			err_hid("usb_submit_urb(out) failed");
			return -1;
		}
		usbhid->last_out = jiffies;
	} else {
		/*
		 * queue work to wake up the device.
@@ -377,6 +378,7 @@ static int hid_submit_ctrl(struct hid_device *hid)
			err_hid("usb_submit_urb(ctrl) failed");
			return -1;
		}
		usbhid->last_ctrl = jiffies;
	} else {
		/*
		 * queue work to wake up the device.
@@ -512,9 +514,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
		usbhid->out[usbhid->outhead].report = report;
		usbhid->outhead = head;

		if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
		if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) {
			if (hid_submit_out(hid))
				clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
		} else {
			/*
			 * the queue is known to run
			 * but an earlier request may be stuck
			 * we may need to time out
			 * no race because this is called under
			 * spinlock
			 */
			if (time_after(jiffies, usbhid->last_out + HZ * 5))
				usb_unlink_urb(usbhid->urbout);
		}
		return;
	}

@@ -535,9 +548,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
	usbhid->ctrl[usbhid->ctrlhead].dir = dir;
	usbhid->ctrlhead = head;

	if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl))
	if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) {
		if (hid_submit_ctrl(hid))
			clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
	} else {
		/*
		 * the queue is known to run
		 * but an earlier request may be stuck
		 * we may need to time out
		 * no race because this is called under
		 * spinlock
		 */
		if (time_after(jiffies, usbhid->last_ctrl + HZ * 5))
			usb_unlink_urb(usbhid->urbctrl);
	}
}

void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
+2 −0
Original line number Diff line number Diff line
@@ -80,12 +80,14 @@ struct usbhid_device {
	unsigned char ctrlhead, ctrltail;                               /* Control fifo head & tail */
	char *ctrlbuf;                                                  /* Control buffer */
	dma_addr_t ctrlbuf_dma;                                         /* Control buffer dma */
	unsigned long last_ctrl;						/* record of last output for timeouts */

	struct urb *urbout;                                             /* Output URB */
	struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE];              /* Output pipe fifo */
	unsigned char outhead, outtail;                                 /* Output pipe fifo head & tail */
	char *outbuf;                                                   /* Output buffer */
	dma_addr_t outbuf_dma;                                          /* Output buffer dma */
	unsigned long last_out;							/* record of last output for timeouts */

	spinlock_t lock;						/* fifo spinlock */
	unsigned long iofl;                                             /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */