2019-05-27 14:55:01 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* USB HID support for Linux
|
|
|
|
*
|
|
|
|
* Copyright (c) 1999 Andreas Gal
|
2005-09-05 13:12:01 +08:00
|
|
|
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
|
|
|
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
2008-12-17 22:38:03 +08:00
|
|
|
* Copyright (c) 2007-2008 Oliver Neukum
|
2010-02-03 03:46:34 +08:00
|
|
|
* Copyright (c) 2006-2010 Jiri Kosina
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/mm.h>
|
2008-10-27 19:16:15 +08:00
|
|
|
#include <linux/mutex.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <asm/unaligned.h>
|
|
|
|
#include <asm/byteorder.h>
|
|
|
|
#include <linux/input.h>
|
|
|
|
#include <linux/wait.h>
|
2008-12-17 22:38:03 +08:00
|
|
|
#include <linux/workqueue.h>
|
2012-04-03 23:04:15 +08:00
|
|
|
#include <linux/string.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <linux/usb.h>
|
|
|
|
|
2006-12-09 01:40:44 +08:00
|
|
|
#include <linux/hid.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/hiddev.h>
|
2007-01-25 18:43:31 +08:00
|
|
|
#include <linux/hid-debug.h>
|
2007-05-14 15:57:40 +08:00
|
|
|
#include <linux/hidraw.h>
|
2006-12-09 01:41:03 +08:00
|
|
|
#include "usbhid.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Version Information
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DRIVER_DESC "USB HID core driver"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Module parameters.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static unsigned int hid_mousepoll_interval;
|
|
|
|
module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
|
|
|
|
MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
|
|
|
|
|
2017-02-26 03:27:27 +08:00
|
|
|
static unsigned int hid_jspoll_interval;
|
|
|
|
module_param_named(jspoll, hid_jspoll_interval, uint, 0644);
|
|
|
|
MODULE_PARM_DESC(jspoll, "Polling interval of joysticks");
|
|
|
|
|
2018-03-22 00:28:25 +08:00
|
|
|
static unsigned int hid_kbpoll_interval;
|
|
|
|
module_param_named(kbpoll, hid_kbpoll_interval, uint, 0644);
|
|
|
|
MODULE_PARM_DESC(kbpoll, "Polling interval of keyboards");
|
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
static unsigned int ignoreled;
|
|
|
|
module_param_named(ignoreled, ignoreled, uint, 0644);
|
|
|
|
MODULE_PARM_DESC(ignoreled, "Autosuspend with active leds");
|
|
|
|
|
2007-04-19 20:56:12 +08:00
|
|
|
/* Quirks specified at module load time */
|
2014-06-06 03:20:51 +08:00
|
|
|
static char *quirks_param[MAX_USBHID_BOOT_QUIRKS];
|
2007-04-19 20:56:12 +08:00
|
|
|
module_param_array_named(quirks, quirks_param, charp, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying "
|
|
|
|
" quirks=vendorID:productID:quirks"
|
|
|
|
" where vendorID, productID, and quirks are all in"
|
|
|
|
" 0x-prefixed hex");
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
2007-04-04 05:39:37 +08:00
|
|
|
* Input submission and I/O error handler.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2007-04-04 05:39:37 +08:00
|
|
|
static void hid_io_error(struct hid_device *hid);
|
2008-12-17 22:38:03 +08:00
|
|
|
static int hid_submit_out(struct hid_device *hid);
|
|
|
|
static int hid_submit_ctrl(struct hid_device *hid);
|
|
|
|
static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid);
|
2007-04-04 05:39:37 +08:00
|
|
|
|
|
|
|
/* Start up the input URB */
|
|
|
|
static int hid_start_in(struct hid_device *hid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
2007-04-04 05:39:37 +08:00
|
|
|
int rc = 0;
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_lock_irqsave(&usbhid->lock, flags);
|
2017-06-07 14:59:33 +08:00
|
|
|
if (test_bit(HID_IN_POLLING, &usbhid->iofl) &&
|
|
|
|
!test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
|
|
|
|
!test_bit(HID_SUSPENDED, &usbhid->iofl) &&
|
|
|
|
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
|
2007-04-04 05:39:37 +08:00
|
|
|
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
|
2012-03-28 19:16:19 +08:00
|
|
|
if (rc != 0) {
|
2007-04-04 05:39:37 +08:00
|
|
|
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
2012-03-28 19:16:19 +08:00
|
|
|
if (rc == -ENOSPC)
|
|
|
|
set_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
|
|
|
|
} else {
|
|
|
|
clear_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_unlock_irqrestore(&usbhid->lock, flags);
|
2007-04-04 05:39:37 +08:00
|
|
|
return rc;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
/* I/O retry timer routine */
|
2017-10-05 08:53:24 +08:00
|
|
|
static void hid_retry_timeout(struct timer_list *t)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-10-05 08:53:24 +08:00
|
|
|
struct usbhid_device *usbhid = from_timer(usbhid, t, io_retry);
|
|
|
|
struct hid_device *hid = usbhid->hid;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
dev_dbg(&usbhid->intf->dev, "retrying intr urb\n");
|
|
|
|
if (hid_start_in(hid))
|
|
|
|
hid_io_error(hid);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
/* Workqueue routine to reset the device or clear a halt */
|
|
|
|
static void hid_reset(struct work_struct *work)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-04-04 05:39:37 +08:00
|
|
|
struct usbhid_device *usbhid =
|
|
|
|
container_of(work, struct usbhid_device, reset_work);
|
|
|
|
struct hid_device *hid = usbhid->hid;
|
2014-09-02 23:39:15 +08:00
|
|
|
int rc;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
|
|
|
|
dev_dbg(&usbhid->intf->dev, "clear halt\n");
|
|
|
|
rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe);
|
|
|
|
clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
|
2008-11-05 00:29:27 +08:00
|
|
|
if (rc == 0) {
|
2014-09-02 23:39:15 +08:00
|
|
|
hid_start_in(hid);
|
|
|
|
} else {
|
|
|
|
dev_dbg(&usbhid->intf->dev,
|
|
|
|
"clear-halt failed: %d\n", rc);
|
|
|
|
set_bit(HID_RESET_PENDING, &usbhid->iofl);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-02 23:39:15 +08:00
|
|
|
if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
|
|
|
|
dev_dbg(&usbhid->intf->dev, "resetting device\n");
|
|
|
|
usb_queue_reset_device(usbhid->intf);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
/* Main I/O error handler */
|
|
|
|
static void hid_io_error(struct hid_device *hid)
|
2006-12-09 01:40:44 +08:00
|
|
|
{
|
2007-04-04 05:39:37 +08:00
|
|
|
unsigned long flags;
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2006-12-09 01:40:44 +08:00
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_lock_irqsave(&usbhid->lock, flags);
|
2006-12-09 01:40:44 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
/* Stop when disconnected */
|
2008-03-31 22:27:30 +08:00
|
|
|
if (test_bit(HID_DISCONNECTED, &usbhid->iofl))
|
2007-04-04 05:39:37 +08:00
|
|
|
goto done;
|
2006-12-09 01:40:44 +08:00
|
|
|
|
2007-03-21 02:03:31 +08:00
|
|
|
/* If it has been a while since the last error, we'll assume
|
|
|
|
* this a brand new error and reset the retry timeout. */
|
|
|
|
if (time_after(jiffies, usbhid->stop_retry + HZ/2))
|
|
|
|
usbhid->retry_delay = 0;
|
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
/* When an error occurs, retry at increasing intervals */
|
|
|
|
if (usbhid->retry_delay == 0) {
|
|
|
|
usbhid->retry_delay = 13; /* Then 26, 52, 104, 104, ... */
|
|
|
|
usbhid->stop_retry = jiffies + msecs_to_jiffies(1000);
|
|
|
|
} else if (usbhid->retry_delay < 100)
|
|
|
|
usbhid->retry_delay *= 2;
|
2006-12-09 01:40:44 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
if (time_after(jiffies, usbhid->stop_retry)) {
|
2006-12-09 01:41:03 +08:00
|
|
|
|
2012-03-28 19:16:19 +08:00
|
|
|
/* Retries failed, so do a port reset unless we lack bandwidth*/
|
2015-08-11 00:06:53 +08:00
|
|
|
if (!test_bit(HID_NO_BANDWIDTH, &usbhid->iofl)
|
2012-03-28 19:16:19 +08:00
|
|
|
&& !test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) {
|
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
schedule_work(&usbhid->reset_work);
|
|
|
|
goto done;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
mod_timer(&usbhid->io_retry,
|
|
|
|
jiffies + msecs_to_jiffies(usbhid->retry_delay));
|
|
|
|
done:
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_unlock_irqrestore(&usbhid->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usbhid_mark_busy(struct usbhid_device *usbhid)
|
|
|
|
{
|
|
|
|
struct usb_interface *intf = usbhid->intf;
|
|
|
|
|
|
|
|
usb_mark_last_busy(interface_to_usbdev(intf));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usbhid_restart_out_queue(struct usbhid_device *usbhid)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = usb_get_intfdata(usbhid->intf);
|
|
|
|
int kicked;
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
int r;
|
2008-12-17 22:38:03 +08:00
|
|
|
|
2012-07-20 04:08:54 +08:00
|
|
|
if (!hid || test_bit(HID_RESET_PENDING, &usbhid->iofl) ||
|
|
|
|
test_bit(HID_SUSPENDED, &usbhid->iofl))
|
2008-12-17 22:38:03 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((kicked = (usbhid->outhead != usbhid->outtail))) {
|
2012-05-02 12:32:55 +08:00
|
|
|
hid_dbg(hid, "Kicking head %d tail %d", usbhid->outhead, usbhid->outtail);
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
|
2012-07-20 04:08:31 +08:00
|
|
|
/* Try to wake up from autosuspend... */
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
r = usb_autopm_get_interface_async(usbhid->intf);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2012-07-20 04:08:31 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If still suspended, don't submit. Submission will
|
|
|
|
* occur if/when resume drains the queue.
|
|
|
|
*/
|
2012-07-20 04:08:45 +08:00
|
|
|
if (test_bit(HID_SUSPENDED, &usbhid->iofl)) {
|
2012-07-20 04:08:31 +08:00
|
|
|
usb_autopm_put_interface_no_suspend(usbhid->intf);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
/* Asynchronously flush queue. */
|
|
|
|
set_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
2008-12-17 22:38:03 +08:00
|
|
|
if (hid_submit_out(hid)) {
|
|
|
|
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
usb_autopm_put_interface_async(usbhid->intf);
|
2008-12-17 22:38:03 +08:00
|
|
|
}
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
wake_up(&usbhid->wait);
|
2008-12-17 22:38:03 +08:00
|
|
|
}
|
|
|
|
return kicked;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = usb_get_intfdata(usbhid->intf);
|
|
|
|
int kicked;
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
int r;
|
2008-12-17 22:38:03 +08:00
|
|
|
|
|
|
|
WARN_ON(hid == NULL);
|
2012-07-20 04:08:54 +08:00
|
|
|
if (!hid || test_bit(HID_RESET_PENDING, &usbhid->iofl) ||
|
|
|
|
test_bit(HID_SUSPENDED, &usbhid->iofl))
|
2008-12-17 22:38:03 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) {
|
2012-05-02 12:32:55 +08:00
|
|
|
hid_dbg(hid, "Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail);
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
|
2012-07-20 04:08:31 +08:00
|
|
|
/* Try to wake up from autosuspend... */
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
r = usb_autopm_get_interface_async(usbhid->intf);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2012-07-20 04:08:31 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If still suspended, don't submit. Submission will
|
|
|
|
* occur if/when resume drains the queue.
|
|
|
|
*/
|
2012-07-20 04:08:45 +08:00
|
|
|
if (test_bit(HID_SUSPENDED, &usbhid->iofl)) {
|
2012-07-20 04:08:31 +08:00
|
|
|
usb_autopm_put_interface_no_suspend(usbhid->intf);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
/* Asynchronously flush queue. */
|
|
|
|
set_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
2008-12-17 22:38:03 +08:00
|
|
|
if (hid_submit_ctrl(hid)) {
|
|
|
|
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
usb_autopm_put_interface_async(usbhid->intf);
|
2008-12-17 22:38:03 +08:00
|
|
|
}
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
wake_up(&usbhid->wait);
|
2008-12-17 22:38:03 +08:00
|
|
|
}
|
|
|
|
return kicked;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
/*
|
|
|
|
* Input interrupt completion handler.
|
|
|
|
*/
|
2005-05-29 15:28:00 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
static void hid_irq_in(struct urb *urb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-04-04 05:39:37 +08:00
|
|
|
struct hid_device *hid = urb->context;
|
2017-06-07 14:59:33 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2007-04-04 05:39:37 +08:00
|
|
|
int status;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
switch (urb->status) {
|
2008-06-19 05:55:41 +08:00
|
|
|
case 0: /* success */
|
|
|
|
usbhid->retry_delay = 0;
|
2017-06-07 14:59:33 +08:00
|
|
|
if (!test_bit(HID_OPENED, &usbhid->iofl))
|
2014-09-06 00:08:47 +08:00
|
|
|
break;
|
2015-11-05 19:55:27 +08:00
|
|
|
usbhid_mark_busy(usbhid);
|
HID: usbhid: prevent unwanted events to be sent when re-opening the device
When events occurs while no one is listening to the node (hid->open == 0
and usb_kill_urb() called) some events are still stacked somewhere in
the USB (kernel or device?) stack. When the node gets reopened, these
events are drained, and this results in spurious touch down/up, or mouse
button clicks.
The problem was spotted with touchscreens in fdo bug #81781 [1], but it
actually occurs with any mouse using hid-generic or touchscreen.
A way to reproduce it is to call:
$ xinput disable 9 ; sleep 5 ; xinput enable 9
With 9 being the device ID for the touchscreen/mouse. During the "sleep",
produce some touch events or click events. When "xinput enable" is called,
at least one click is generated.
This patch tries to fix this by draining the queue for 50 msec and
during this time frame, not forwarding these old events to the hid layer.
Hans completed the explanation:
"""
Devices like mice (basically any hid device) will have a fifo
on the device side, when we stop submitting urbs to get hid reports from
it, that fifo will fill up, and when we resume we will get whatever
is there in that fifo.
"""
[1] https://bugs.freedesktop.org/show_bug.cgi?id=81781
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2014-10-01 02:28:22 +08:00
|
|
|
if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
|
|
|
|
hid_input_report(urb->context, HID_INPUT_REPORT,
|
|
|
|
urb->transfer_buffer,
|
|
|
|
urb->actual_length, 1);
|
|
|
|
/*
|
|
|
|
* autosuspend refused while keys are pressed
|
|
|
|
* because most keyboards don't wake up when
|
|
|
|
* a key is released
|
|
|
|
*/
|
|
|
|
if (hid_check_keys_pressed(hid))
|
|
|
|
set_bit(HID_KEYS_PRESSED, &usbhid->iofl);
|
|
|
|
else
|
|
|
|
clear_bit(HID_KEYS_PRESSED, &usbhid->iofl);
|
|
|
|
}
|
2008-06-19 05:55:41 +08:00
|
|
|
break;
|
|
|
|
case -EPIPE: /* stall */
|
2008-12-17 22:38:03 +08:00
|
|
|
usbhid_mark_busy(usbhid);
|
2008-06-19 05:55:41 +08:00
|
|
|
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
|
|
|
set_bit(HID_CLEAR_HALT, &usbhid->iofl);
|
|
|
|
schedule_work(&usbhid->reset_work);
|
|
|
|
return;
|
|
|
|
case -ECONNRESET: /* unlink */
|
|
|
|
case -ENOENT:
|
|
|
|
case -ESHUTDOWN: /* unplug */
|
|
|
|
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
|
|
|
return;
|
|
|
|
case -EILSEQ: /* protocol error or unplug */
|
|
|
|
case -EPROTO: /* protocol error or unplug */
|
|
|
|
case -ETIME: /* protocol error or unplug */
|
|
|
|
case -ETIMEDOUT: /* Should never happen, but... */
|
2008-12-17 22:38:03 +08:00
|
|
|
usbhid_mark_busy(usbhid);
|
2008-06-19 05:55:41 +08:00
|
|
|
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
|
|
|
hid_io_error(hid);
|
|
|
|
return;
|
|
|
|
default: /* error */
|
2010-12-10 11:29:03 +08:00
|
|
|
hid_warn(urb->dev, "input irq status %d received\n",
|
|
|
|
urb->status);
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
status = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
|
if (status) {
|
|
|
|
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
|
|
|
if (status != -EPERM) {
|
2010-12-10 11:29:03 +08:00
|
|
|
hid_err(hid, "can't resubmit intr, %s-%s/input%d, status %d\n",
|
|
|
|
hid_to_usb_dev(hid)->bus->bus_name,
|
|
|
|
hid_to_usb_dev(hid)->devpath,
|
|
|
|
usbhid->ifnum, status);
|
2007-04-04 05:39:37 +08:00
|
|
|
hid_io_error(hid);
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
static int hid_submit_out(struct hid_device *hid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-04-04 05:39:37 +08:00
|
|
|
struct hid_report *report;
|
2008-10-04 20:44:06 +08:00
|
|
|
char *raw_report;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2010-12-22 22:33:40 +08:00
|
|
|
int r;
|
2006-12-09 01:41:03 +08:00
|
|
|
|
2008-10-04 20:44:06 +08:00
|
|
|
report = usbhid->out[usbhid->outtail].report;
|
|
|
|
raw_report = usbhid->out[usbhid->outtail].raw_report;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-11-27 23:02:36 +08:00
|
|
|
usbhid->urbout->transfer_buffer_length = hid_report_len(report);
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
usbhid->urbout->dev = hid_to_usb_dev(hid);
|
2012-07-20 04:08:21 +08:00
|
|
|
if (raw_report) {
|
|
|
|
memcpy(usbhid->outbuf, raw_report,
|
|
|
|
usbhid->urbout->transfer_buffer_length);
|
|
|
|
kfree(raw_report);
|
|
|
|
usbhid->out[usbhid->outtail].raw_report = NULL;
|
|
|
|
}
|
2006-03-30 04:41:07 +08:00
|
|
|
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
dbg_hid("submitting out urb\n");
|
2006-10-18 20:47:37 +08:00
|
|
|
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
r = usb_submit_urb(usbhid->urbout, GFP_ATOMIC);
|
|
|
|
if (r < 0) {
|
|
|
|
hid_err(hid, "usb_submit_urb(out) failed: %d\n", r);
|
|
|
|
return r;
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
usbhid->last_out = jiffies;
|
2007-04-04 05:39:37 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hid_submit_ctrl(struct hid_device *hid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct hid_report *report;
|
2007-04-04 05:39:37 +08:00
|
|
|
unsigned char dir;
|
2008-10-04 20:44:06 +08:00
|
|
|
char *raw_report;
|
2010-12-22 22:33:40 +08:00
|
|
|
int len, r;
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
report = usbhid->ctrl[usbhid->ctrltail].report;
|
2008-10-04 20:44:06 +08:00
|
|
|
raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report;
|
2007-04-04 05:39:37 +08:00
|
|
|
dir = usbhid->ctrl[usbhid->ctrltail].dir;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
|
|
|
|
if (dir == USB_DIR_OUT) {
|
|
|
|
usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
|
|
|
|
usbhid->urbctrl->transfer_buffer_length = len;
|
2012-07-20 04:08:21 +08:00
|
|
|
if (raw_report) {
|
|
|
|
memcpy(usbhid->ctrlbuf, raw_report, len);
|
|
|
|
kfree(raw_report);
|
|
|
|
usbhid->ctrl[usbhid->ctrltail].raw_report = NULL;
|
|
|
|
}
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
} else {
|
|
|
|
int maxpacket, padlen;
|
|
|
|
|
|
|
|
usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
|
|
|
|
maxpacket = usb_maxpacket(hid_to_usb_dev(hid),
|
|
|
|
usbhid->urbctrl->pipe, 0);
|
|
|
|
if (maxpacket > 0) {
|
|
|
|
padlen = DIV_ROUND_UP(len, maxpacket);
|
|
|
|
padlen *= maxpacket;
|
|
|
|
if (padlen > usbhid->bufsize)
|
|
|
|
padlen = usbhid->bufsize;
|
|
|
|
} else
|
|
|
|
padlen = 0;
|
|
|
|
usbhid->urbctrl->transfer_buffer_length = padlen;
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
usbhid->urbctrl->dev = hid_to_usb_dev(hid);
|
|
|
|
|
|
|
|
usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
|
|
|
|
usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT :
|
|
|
|
HID_REQ_GET_REPORT;
|
|
|
|
usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) |
|
|
|
|
report->id);
|
|
|
|
usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
|
|
|
|
usbhid->cr->wLength = cpu_to_le16(len);
|
|
|
|
|
|
|
|
dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
|
|
|
|
usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" :
|
|
|
|
"Get_Report",
|
|
|
|
usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
|
|
|
|
|
|
|
|
r = usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC);
|
|
|
|
if (r < 0) {
|
|
|
|
hid_err(hid, "usb_submit_urb(ctrl) failed: %d\n", r);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
usbhid->last_ctrl = jiffies;
|
2007-04-04 05:39:37 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
/*
|
|
|
|
* Output interrupt completion handler.
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
static void hid_irq_out(struct urb *urb)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = urb->context;
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
unsigned long flags;
|
|
|
|
int unplug = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
switch (urb->status) {
|
2008-06-19 05:55:41 +08:00
|
|
|
case 0: /* success */
|
|
|
|
break;
|
|
|
|
case -ESHUTDOWN: /* unplug */
|
|
|
|
unplug = 1;
|
|
|
|
case -EILSEQ: /* protocol error or unplug */
|
|
|
|
case -EPROTO: /* protocol error or unplug */
|
|
|
|
case -ECONNRESET: /* unlink */
|
|
|
|
case -ENOENT:
|
|
|
|
break;
|
|
|
|
default: /* error */
|
2010-12-10 11:29:03 +08:00
|
|
|
hid_warn(urb->dev, "output irq status %d received\n",
|
|
|
|
urb->status);
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_lock_irqsave(&usbhid->lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-07-20 04:08:39 +08:00
|
|
|
if (unplug) {
|
2007-04-04 05:39:37 +08:00
|
|
|
usbhid->outtail = usbhid->outhead;
|
2012-07-20 04:08:39 +08:00
|
|
|
} else {
|
2007-04-04 05:39:37 +08:00
|
|
|
usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-07-20 04:08:39 +08:00
|
|
|
if (usbhid->outhead != usbhid->outtail &&
|
|
|
|
hid_submit_out(hid) == 0) {
|
|
|
|
/* Successfully submitted next urb in queue */
|
|
|
|
spin_unlock_irqrestore(&usbhid->lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_unlock_irqrestore(&usbhid->lock, flags);
|
2010-12-22 22:33:40 +08:00
|
|
|
usb_autopm_put_interface_async(usbhid->intf);
|
2008-03-20 04:55:04 +08:00
|
|
|
wake_up(&usbhid->wait);
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
2005-07-11 14:08:40 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
/*
|
|
|
|
* Control pipe completion handler.
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
static void hid_ctrl(struct urb *urb)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = urb->context;
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2018-06-20 05:59:44 +08:00
|
|
|
unsigned long flags;
|
2008-12-17 22:38:03 +08:00
|
|
|
int unplug = 0, status = urb->status;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
switch (status) {
|
2008-06-19 05:55:41 +08:00
|
|
|
case 0: /* success */
|
|
|
|
if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
|
|
|
|
hid_input_report(urb->context,
|
|
|
|
usbhid->ctrl[usbhid->ctrltail].report->type,
|
|
|
|
urb->transfer_buffer, urb->actual_length, 0);
|
|
|
|
break;
|
|
|
|
case -ESHUTDOWN: /* unplug */
|
|
|
|
unplug = 1;
|
|
|
|
case -EILSEQ: /* protocol error or unplug */
|
|
|
|
case -EPROTO: /* protocol error or unplug */
|
|
|
|
case -ECONNRESET: /* unlink */
|
|
|
|
case -ENOENT:
|
|
|
|
case -EPIPE: /* report not available */
|
|
|
|
break;
|
|
|
|
default: /* error */
|
2010-12-10 11:29:03 +08:00
|
|
|
hid_warn(urb->dev, "ctrl urb status %d received\n", status);
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-06-20 05:59:44 +08:00
|
|
|
spin_lock_irqsave(&usbhid->lock, flags);
|
2015-11-21 04:19:02 +08:00
|
|
|
|
2012-07-20 04:08:39 +08:00
|
|
|
if (unplug) {
|
2007-04-04 05:39:37 +08:00
|
|
|
usbhid->ctrltail = usbhid->ctrlhead;
|
2012-07-20 04:08:39 +08:00
|
|
|
} else {
|
2007-04-04 05:39:37 +08:00
|
|
|
usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-07-20 04:08:39 +08:00
|
|
|
if (usbhid->ctrlhead != usbhid->ctrltail &&
|
|
|
|
hid_submit_ctrl(hid) == 0) {
|
|
|
|
/* Successfully submitted next urb in queue */
|
2018-06-20 05:59:44 +08:00
|
|
|
spin_unlock_irqrestore(&usbhid->lock, flags);
|
2012-07-20 04:08:39 +08:00
|
|
|
return;
|
|
|
|
}
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
2018-06-20 05:59:44 +08:00
|
|
|
spin_unlock_irqrestore(&usbhid->lock, flags);
|
2010-12-22 22:33:40 +08:00
|
|
|
usb_autopm_put_interface_async(usbhid->intf);
|
2008-03-20 04:55:04 +08:00
|
|
|
wake_up(&usbhid->wait);
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-08-18 06:37:18 +08:00
|
|
|
static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *report,
|
|
|
|
unsigned char dir)
|
2007-04-04 05:39:37 +08:00
|
|
|
{
|
|
|
|
int head;
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-07-25 13:13:01 +08:00
|
|
|
if (((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) ||
|
|
|
|
test_bit(HID_DISCONNECTED, &usbhid->iofl))
|
2007-04-04 05:39:37 +08:00
|
|
|
return;
|
2006-06-30 15:44:03 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) {
|
|
|
|
if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) {
|
2010-12-10 11:29:03 +08:00
|
|
|
hid_warn(hid, "output queue full\n");
|
2007-04-04 05:39:37 +08:00
|
|
|
return;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
HID: fix data access in implement()
implement() is setting bytes in LE data stream. In case the data is not
aligned to 64bits, it reads past the allocated buffer. It doesn't really
change any value there (it's properly bitmasked), but in case that this
read past the boundary hits a page boundary, pagefault happens when
accessing 64bits of 'x' in implement(), and kernel oopses.
This happens much more often when numbered reports are in use, as the
initial 8bit skip in the buffer makes the whole process work on values
which are not aligned to 64bits.
This problem dates back to attempts in 2005 and 2006 to make implement()
and extract() as generic as possible, and even back then the problem
was realized by Adam Kroperlin, but falsely assumed to be impossible
to cause any harm:
http://www.mail-archive.com/linux-usb-devel@lists.sourceforge.net/msg47690.html
I have made several attempts at fixing it "on the spot" directly in
implement(), but the results were horrible; the special casing for processing
last 64bit chunk and switching to different math makes it unreadable mess.
I therefore took a path to allocate a few bytes more which will never make
it into final report, but are there as a cushion for all the 64bit math
operations happening in implement() and extract().
All callers of hid_output_report() are converted at the same time to allocate
the buffer by newly introduced hid_alloc_report_buf() helper.
Bruno noticed that the whole raw_size test can be dropped as well, as
hid_alloc_report_buf() makes sure that the buffer is always of a proper
size.
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2013-07-11 01:56:27 +08:00
|
|
|
usbhid->out[usbhid->outhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC);
|
2008-10-04 20:44:06 +08:00
|
|
|
if (!usbhid->out[usbhid->outhead].raw_report) {
|
2010-12-10 11:29:03 +08:00
|
|
|
hid_warn(hid, "output queueing failed\n");
|
2008-10-04 20:44:06 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
hid_output_report(report, usbhid->out[usbhid->outhead].raw_report);
|
|
|
|
usbhid->out[usbhid->outhead].report = report;
|
2007-04-04 05:39:37 +08:00
|
|
|
usbhid->outhead = head;
|
2005-06-03 13:18:12 +08:00
|
|
|
|
2012-07-20 04:08:31 +08:00
|
|
|
/* If the queue isn't running, restart it */
|
|
|
|
if (!test_bit(HID_OUT_RUNNING, &usbhid->iofl)) {
|
|
|
|
usbhid_restart_out_queue(usbhid);
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
|
2012-07-20 04:08:31 +08:00
|
|
|
/* Otherwise see if an earlier request has timed out */
|
|
|
|
} else if (time_after(jiffies, usbhid->last_out + HZ * 5)) {
|
|
|
|
|
|
|
|
/* Prevent autosuspend following the unlink */
|
|
|
|
usb_autopm_get_interface_no_resume(usbhid->intf);
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
|
2010-02-12 20:02:28 +08:00
|
|
|
/*
|
2012-07-20 04:08:31 +08:00
|
|
|
* Prevent resubmission in case the URB completes
|
|
|
|
* before we can unlink it. We don't want to cancel
|
|
|
|
* the wrong transfer!
|
2010-02-12 20:02:28 +08:00
|
|
|
*/
|
2012-07-20 04:08:31 +08:00
|
|
|
usb_block_urb(usbhid->urbout);
|
|
|
|
|
|
|
|
/* Drop lock to avoid deadlock if the callback runs */
|
|
|
|
spin_unlock(&usbhid->lock);
|
|
|
|
|
|
|
|
usb_unlink_urb(usbhid->urbout);
|
|
|
|
spin_lock(&usbhid->lock);
|
|
|
|
usb_unblock_urb(usbhid->urbout);
|
|
|
|
|
|
|
|
/* Unlink might have stopped the queue */
|
|
|
|
if (!test_bit(HID_OUT_RUNNING, &usbhid->iofl))
|
|
|
|
usbhid_restart_out_queue(usbhid);
|
|
|
|
|
|
|
|
/* Now we can allow autosuspend again */
|
|
|
|
usb_autopm_put_interface_async(usbhid->intf);
|
2010-02-12 20:02:28 +08:00
|
|
|
}
|
2007-04-04 05:39:37 +08:00
|
|
|
return;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) {
|
2010-12-10 11:29:03 +08:00
|
|
|
hid_warn(hid, "control queue full\n");
|
2007-04-04 05:39:37 +08:00
|
|
|
return;
|
|
|
|
}
|
2006-09-16 22:17:34 +08:00
|
|
|
|
2008-10-04 20:44:06 +08:00
|
|
|
if (dir == USB_DIR_OUT) {
|
HID: fix data access in implement()
implement() is setting bytes in LE data stream. In case the data is not
aligned to 64bits, it reads past the allocated buffer. It doesn't really
change any value there (it's properly bitmasked), but in case that this
read past the boundary hits a page boundary, pagefault happens when
accessing 64bits of 'x' in implement(), and kernel oopses.
This happens much more often when numbered reports are in use, as the
initial 8bit skip in the buffer makes the whole process work on values
which are not aligned to 64bits.
This problem dates back to attempts in 2005 and 2006 to make implement()
and extract() as generic as possible, and even back then the problem
was realized by Adam Kroperlin, but falsely assumed to be impossible
to cause any harm:
http://www.mail-archive.com/linux-usb-devel@lists.sourceforge.net/msg47690.html
I have made several attempts at fixing it "on the spot" directly in
implement(), but the results were horrible; the special casing for processing
last 64bit chunk and switching to different math makes it unreadable mess.
I therefore took a path to allocate a few bytes more which will never make
it into final report, but are there as a cushion for all the 64bit math
operations happening in implement() and extract().
All callers of hid_output_report() are converted at the same time to allocate
the buffer by newly introduced hid_alloc_report_buf() helper.
Bruno noticed that the whole raw_size test can be dropped as well, as
hid_alloc_report_buf() makes sure that the buffer is always of a proper
size.
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2013-07-11 01:56:27 +08:00
|
|
|
usbhid->ctrl[usbhid->ctrlhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC);
|
2008-10-04 20:44:06 +08:00
|
|
|
if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) {
|
2010-12-10 11:29:03 +08:00
|
|
|
hid_warn(hid, "control queueing failed\n");
|
2008-10-04 20:44:06 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
hid_output_report(report, usbhid->ctrl[usbhid->ctrlhead].raw_report);
|
|
|
|
}
|
2007-04-04 05:39:37 +08:00
|
|
|
usbhid->ctrl[usbhid->ctrlhead].report = report;
|
|
|
|
usbhid->ctrl[usbhid->ctrlhead].dir = dir;
|
|
|
|
usbhid->ctrlhead = head;
|
2006-09-16 22:17:34 +08:00
|
|
|
|
2012-07-20 04:08:31 +08:00
|
|
|
/* If the queue isn't running, restart it */
|
|
|
|
if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl)) {
|
|
|
|
usbhid_restart_ctrl_queue(usbhid);
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
|
2012-07-20 04:08:31 +08:00
|
|
|
/* Otherwise see if an earlier request has timed out */
|
|
|
|
} else if (time_after(jiffies, usbhid->last_ctrl + HZ * 5)) {
|
|
|
|
|
|
|
|
/* Prevent autosuspend following the unlink */
|
|
|
|
usb_autopm_get_interface_no_resume(usbhid->intf);
|
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core
de-activates the keyboard's LEDs upon suspend(). It does this by sending
individual EV_LED[LED_X]=0 events to the underlying device driver by
directly calling the driver's registered event() handler.
The usb-hid driver event() handler processes each request by immediately
attempting to submit a CTRL URB to turn off the LED. USB URB submission
is asynchronous. First the URB is added to the head of the ctrl queue.
Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately
(and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true,
then the newly queued URB is submitted in the ctrl completion handler when
all previously submitted URBs have completed. When all queued URBs have
been submitted, the completion handler clears the CTRL_RUNNING flag.
In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get
queued, with only the first actually submitted. Soon after input
suspend() handler finishes, the usb-hid suspend() handler gets called.
Since this is NOT a PM_EVENT_AUTO suspend, the handler sets
REPORTED_IDLE, then waits for io to complete.
Unfortunately, this usually happens while the first LED request is
actually still being processed. Thus when the completion handler tries
to submit the second LED request it fails, since REPORTED_IDLE is
already set! This REPORTED_IDLE check failure causes the completion
handler to complete, however without clearing the CTRL_RUNNING flag.
This, in turn, means that the suspend() handler's wait_io() condition
is never satisfied, and instead it times out after 10 seconds, aborting
the original system suspend.
This patch changes the behavior to the following:
(1) allow completion handler to finish submitting all queued URBs, even if
REPORTED_IDLE is set. This guarantees that all URBs queued before the
hid-core suspend() call will be submitted before the system is
suspended.
(2) if REPORTED_IDLE is set and the URB queue is empty, queue, but
don't submit, new URB submission requests. These queued requests get
submitted when resume() flushes the URB queue. This is similar to the
existing behavior, however, any requests that arrive while the queue is
not yet empty will still get submitted before suspend.
(3) set the RUNNING flag when flushing the URB queue in resume().
This keeps URBs that were queued in (2) from colliding with any new
URBs that are being submitted during the resume process. The new URB
submission requests upon resume get properly queued behind the ones
being flushed instead of the current situation where they collide,
causing memory corruption and oopses.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-11-17 19:23:49 +08:00
|
|
|
|
2010-02-12 20:02:28 +08:00
|
|
|
/*
|
2012-07-20 04:08:31 +08:00
|
|
|
* Prevent resubmission in case the URB completes
|
|
|
|
* before we can unlink it. We don't want to cancel
|
|
|
|
* the wrong transfer!
|
2010-02-12 20:02:28 +08:00
|
|
|
*/
|
2012-07-20 04:08:31 +08:00
|
|
|
usb_block_urb(usbhid->urbctrl);
|
|
|
|
|
|
|
|
/* Drop lock to avoid deadlock if the callback runs */
|
|
|
|
spin_unlock(&usbhid->lock);
|
|
|
|
|
|
|
|
usb_unlink_urb(usbhid->urbctrl);
|
|
|
|
spin_lock(&usbhid->lock);
|
|
|
|
usb_unblock_urb(usbhid->urbctrl);
|
|
|
|
|
|
|
|
/* Unlink might have stopped the queue */
|
|
|
|
if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
|
|
|
|
usbhid_restart_ctrl_queue(usbhid);
|
|
|
|
|
|
|
|
/* Now we can allow autosuspend again */
|
|
|
|
usb_autopm_put_interface_async(usbhid->intf);
|
2010-02-12 20:02:28 +08:00
|
|
|
}
|
2008-12-17 22:38:03 +08:00
|
|
|
}
|
2006-09-15 22:23:35 +08:00
|
|
|
|
2013-02-25 18:31:46 +08:00
|
|
|
static void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
|
2008-12-17 22:38:03 +08:00
|
|
|
{
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
unsigned long flags;
|
2006-09-15 22:23:35 +08:00
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_lock_irqsave(&usbhid->lock, flags);
|
|
|
|
__usbhid_submit_report(hid, report, dir);
|
|
|
|
spin_unlock_irqrestore(&usbhid->lock, flags);
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
2006-10-28 03:08:54 +08:00
|
|
|
|
2013-02-25 18:31:47 +08:00
|
|
|
static int usbhid_wait_io(struct hid_device *hid)
|
2007-04-04 05:39:37 +08:00
|
|
|
{
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2007-03-01 16:54:44 +08:00
|
|
|
|
2008-03-20 04:55:04 +08:00
|
|
|
if (!wait_event_timeout(usbhid->wait,
|
|
|
|
(!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) &&
|
|
|
|
!test_bit(HID_OUT_RUNNING, &usbhid->iofl)),
|
2007-04-04 05:39:37 +08:00
|
|
|
10*HZ)) {
|
2007-05-30 21:07:13 +08:00
|
|
|
dbg_hid("timeout waiting for ctrl or out queue to clear\n");
|
2007-04-04 05:39:37 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2005-06-06 15:22:37 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle)
|
|
|
|
{
|
|
|
|
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
|
|
HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report,
|
|
|
|
ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
|
|
|
|
unsigned char type, void *buf, int size)
|
|
|
|
{
|
|
|
|
int result, retries = 4;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
memset(buf, 0, size);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
do {
|
|
|
|
result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
|
|
|
|
USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
|
|
|
|
(type << 8), ifnum, buf, size, USB_CTRL_GET_TIMEOUT);
|
|
|
|
retries--;
|
|
|
|
} while (result < size && retries);
|
|
|
|
return result;
|
|
|
|
}
|
2006-01-14 13:25:39 +08:00
|
|
|
|
2017-06-07 14:59:31 +08:00
|
|
|
static int usbhid_open(struct hid_device *hid)
|
2007-04-04 05:39:37 +08:00
|
|
|
{
|
2007-07-11 20:48:58 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2017-06-07 14:59:36 +08:00
|
|
|
int res;
|
HID: usbhid: prevent unwanted events to be sent when re-opening the device
When events occurs while no one is listening to the node (hid->open == 0
and usb_kill_urb() called) some events are still stacked somewhere in
the USB (kernel or device?) stack. When the node gets reopened, these
events are drained, and this results in spurious touch down/up, or mouse
button clicks.
The problem was spotted with touchscreens in fdo bug #81781 [1], but it
actually occurs with any mouse using hid-generic or touchscreen.
A way to reproduce it is to call:
$ xinput disable 9 ; sleep 5 ; xinput enable 9
With 9 being the device ID for the touchscreen/mouse. During the "sleep",
produce some touch events or click events. When "xinput enable" is called,
at least one click is generated.
This patch tries to fix this by draining the queue for 50 msec and
during this time frame, not forwarding these old events to the hid layer.
Hans completed the explanation:
"""
Devices like mice (basically any hid device) will have a fifo
on the device side, when we stop submitting urbs to get hid reports from
it, that fifo will fill up, and when we resume we will get whatever
is there in that fifo.
"""
[1] https://bugs.freedesktop.org/show_bug.cgi?id=81781
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2014-10-01 02:28:22 +08:00
|
|
|
|
2017-07-13 08:14:24 +08:00
|
|
|
set_bit(HID_OPENED, &usbhid->iofl);
|
|
|
|
|
2017-06-07 14:59:36 +08:00
|
|
|
if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
res = usb_autopm_get_interface(usbhid->intf);
|
|
|
|
/* the device must be awake to reliably request remote wakeup */
|
2017-07-13 08:14:24 +08:00
|
|
|
if (res < 0) {
|
|
|
|
clear_bit(HID_OPENED, &usbhid->iofl);
|
2017-06-07 14:59:36 +08:00
|
|
|
return -EIO;
|
2017-07-13 08:14:24 +08:00
|
|
|
}
|
2017-06-07 14:59:36 +08:00
|
|
|
|
|
|
|
usbhid->intf->needs_remote_wakeup = 1;
|
|
|
|
|
|
|
|
set_bit(HID_RESUME_RUNNING, &usbhid->iofl);
|
|
|
|
set_bit(HID_IN_POLLING, &usbhid->iofl);
|
|
|
|
|
|
|
|
res = hid_start_in(hid);
|
|
|
|
if (res) {
|
|
|
|
if (res != -ENOSPC) {
|
|
|
|
hid_io_error(hid);
|
|
|
|
res = 0;
|
|
|
|
} else {
|
|
|
|
/* no use opening if resources are insufficient */
|
|
|
|
res = -EBUSY;
|
|
|
|
clear_bit(HID_OPENED, &usbhid->iofl);
|
|
|
|
clear_bit(HID_IN_POLLING, &usbhid->iofl);
|
|
|
|
usbhid->intf->needs_remote_wakeup = 0;
|
|
|
|
}
|
2007-07-11 20:48:58 +08:00
|
|
|
}
|
2017-06-07 14:59:36 +08:00
|
|
|
|
|
|
|
usb_autopm_put_interface(usbhid->intf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In case events are generated while nobody was listening,
|
|
|
|
* some are released when the device is re-opened.
|
|
|
|
* Wait 50 msec for the queue to empty before allowing events
|
|
|
|
* to go through hid.
|
|
|
|
*/
|
|
|
|
if (res == 0)
|
|
|
|
msleep(50);
|
|
|
|
|
|
|
|
clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
|
2012-03-28 19:16:19 +08:00
|
|
|
return res;
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
2007-02-05 17:06:01 +08:00
|
|
|
|
2017-06-07 14:59:31 +08:00
|
|
|
static void usbhid_close(struct hid_device *hid)
|
2007-04-04 05:39:37 +08:00
|
|
|
{
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2006-01-14 23:08:06 +08:00
|
|
|
|
2017-06-07 14:59:36 +08:00
|
|
|
/*
|
|
|
|
* Make sure we don't restart data acquisition due to
|
|
|
|
* a resumption we no longer care about by avoiding racing
|
|
|
|
* with hid_start_in().
|
2008-12-17 22:38:03 +08:00
|
|
|
*/
|
|
|
|
spin_lock_irq(&usbhid->lock);
|
2017-06-07 14:59:36 +08:00
|
|
|
clear_bit(HID_OPENED, &usbhid->iofl);
|
2017-07-13 08:14:24 +08:00
|
|
|
if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL))
|
|
|
|
clear_bit(HID_IN_POLLING, &usbhid->iofl);
|
2017-06-07 14:59:36 +08:00
|
|
|
spin_unlock_irq(&usbhid->lock);
|
|
|
|
|
2017-07-13 08:14:24 +08:00
|
|
|
if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
|
|
|
|
return;
|
|
|
|
|
2017-06-07 14:59:36 +08:00
|
|
|
hid_cancel_delayed_stuff(usbhid);
|
|
|
|
usb_kill_urb(usbhid->urbin);
|
|
|
|
usbhid->intf->needs_remote_wakeup = 0;
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
2006-03-30 04:41:07 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
/*
|
|
|
|
* Initialize all reports
|
|
|
|
*/
|
2006-11-05 11:49:53 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
void usbhid_init_reports(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
struct hid_report *report;
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2013-08-22 20:51:09 +08:00
|
|
|
struct hid_report_enum *report_enum;
|
2007-04-04 05:39:37 +08:00
|
|
|
int err, ret;
|
2006-11-05 11:49:53 +08:00
|
|
|
|
HID: remove initial reading of reports at connect
It looks like a bunch of devices do not like to be polled
for their reports at init time. When you look into the details,
it seems that for those that are requiring the quirk
HID_QUIRK_NO_INIT_REPORTS, the driver fails to retrieve part
of the features/inputs while others (more generic) work.
IMO, it should be acceptable to remove the need for the quirk
in the general case. On the small amount of cases where
we actually need to read the current values, the driver
in charge (hid-mt or wacom) already retrieves the features
manually.
There are 2 cases where we might need to retrieve the reports at
init:
1. hiddev devices with specific use-space tool
2. a device that would require the driver to fetch a specific
feature/input at plug
For case 2, I have seen this a few time on hid-multitouch. It
is solved in hid-multitouch directly by fetching the feature.
I hope it won't be too common and this can be solved on a per-case
basis (crossing fingers).
For case 1, we moved the implementation of HID_QUIRK_NO_INIT_REPORTS
in hiddev. When somebody starts calling ioctls that needs an initial
update, the hiddev device will fetch the initial state of the reports
to mimic the current behavior. This adds a small amount of time during
the first HIDIOCGUSAGE(S), but it should be acceptable in
most cases. To keep the currently known broken devices, we have to
keep around HID_QUIRK_NO_INIT_REPORTS, but the scope will only be
for hiddev.
Note that I don't think hidraw would be affected and I checked that
the FF drivers that need to interact with the report fields are all
using output reports, which are not initialized by
usbhid_init_reports().
NO_INIT_INPUT_REPORTS is then replaced by HID_QUIRK_NO_INIT_REPORTS:
there is no point keeping it for just one device.
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2017-03-08 22:11:14 +08:00
|
|
|
report_enum = &hid->report_enum[HID_INPUT_REPORT];
|
|
|
|
list_for_each_entry(report, &report_enum->report_list, list)
|
|
|
|
usbhid_submit_report(hid, report, USB_DIR_IN);
|
2007-01-11 22:51:17 +08:00
|
|
|
|
2013-08-22 20:51:09 +08:00
|
|
|
report_enum = &hid->report_enum[HID_FEATURE_REPORT];
|
|
|
|
list_for_each_entry(report, &report_enum->report_list, list)
|
2007-04-04 05:39:37 +08:00
|
|
|
usbhid_submit_report(hid, report, USB_DIR_IN);
|
2007-01-16 12:11:52 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
err = 0;
|
|
|
|
ret = usbhid_wait_io(hid);
|
|
|
|
while (ret) {
|
|
|
|
err |= ret;
|
|
|
|
if (test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
|
|
|
|
usb_kill_urb(usbhid->urbctrl);
|
|
|
|
if (test_bit(HID_OUT_RUNNING, &usbhid->iofl))
|
|
|
|
usb_kill_urb(usbhid->urbout);
|
|
|
|
ret = usbhid_wait_io(hid);
|
|
|
|
}
|
2007-02-06 08:40:57 +08:00
|
|
|
|
2007-04-04 05:39:37 +08:00
|
|
|
if (err)
|
2010-12-10 11:29:03 +08:00
|
|
|
hid_warn(hid, "timeout initializing reports\n");
|
2007-04-04 05:39:37 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-06 20:33:18 +08:00
|
|
|
/*
|
|
|
|
* Reset LEDs which BIOS might have left on. For now, just NumLock (0x01).
|
|
|
|
*/
|
|
|
|
static int hid_find_field_early(struct hid_device *hid, unsigned int page,
|
|
|
|
unsigned int hid_code, struct hid_field **pfield)
|
|
|
|
{
|
|
|
|
struct hid_report *report;
|
|
|
|
struct hid_field *field;
|
|
|
|
struct hid_usage *usage;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
list_for_each_entry(report, &hid->report_enum[HID_OUTPUT_REPORT].report_list, list) {
|
|
|
|
for (i = 0; i < report->maxfield; i++) {
|
|
|
|
field = report->field[i];
|
|
|
|
for (j = 0; j < field->maxusage; j++) {
|
|
|
|
usage = &field->usage[j];
|
|
|
|
if ((usage->hid & HID_USAGE_PAGE) == page &&
|
|
|
|
(usage->hid & 0xFFFF) == hid_code) {
|
|
|
|
*pfield = field;
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-07-16 01:10:10 +08:00
|
|
|
static void usbhid_set_leds(struct hid_device *hid)
|
2007-04-06 20:33:18 +08:00
|
|
|
{
|
|
|
|
struct hid_field *field;
|
|
|
|
int offset;
|
|
|
|
|
|
|
|
if ((offset = hid_find_field_early(hid, HID_UP_LED, 0x01, &field)) != -1) {
|
|
|
|
hid_set_field(field, offset, 0);
|
|
|
|
usbhid_submit_report(hid, field->report, USB_DIR_OUT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-05 13:12:01 +08:00
|
|
|
/*
|
|
|
|
* Traverse the supplied list of reports and find the longest
|
|
|
|
*/
|
2008-03-29 00:06:41 +08:00
|
|
|
static void hid_find_max_report(struct hid_device *hid, unsigned int type,
|
|
|
|
unsigned int *max)
|
2005-09-05 13:12:01 +08:00
|
|
|
{
|
|
|
|
struct hid_report *report;
|
2008-03-29 00:06:41 +08:00
|
|
|
unsigned int size;
|
2005-09-05 13:12:01 +08:00
|
|
|
|
|
|
|
list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
|
2008-10-17 21:01:15 +08:00
|
|
|
size = ((report->size - 1) >> 3) + 1 + hid->report_enum[type].numbered;
|
2005-09-05 13:12:01 +08:00
|
|
|
if (*max < size)
|
|
|
|
*max = size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
|
|
|
|
{
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
|
2010-04-12 19:17:25 +08:00
|
|
|
usbhid->inbuf = usb_alloc_coherent(dev, usbhid->bufsize, GFP_KERNEL,
|
2008-11-24 23:20:06 +08:00
|
|
|
&usbhid->inbuf_dma);
|
2010-04-12 19:17:25 +08:00
|
|
|
usbhid->outbuf = usb_alloc_coherent(dev, usbhid->bufsize, GFP_KERNEL,
|
2008-11-24 23:20:06 +08:00
|
|
|
&usbhid->outbuf_dma);
|
2010-03-06 04:10:17 +08:00
|
|
|
usbhid->cr = kmalloc(sizeof(*usbhid->cr), GFP_KERNEL);
|
2010-04-12 19:17:25 +08:00
|
|
|
usbhid->ctrlbuf = usb_alloc_coherent(dev, usbhid->bufsize, GFP_KERNEL,
|
2008-11-24 23:20:06 +08:00
|
|
|
&usbhid->ctrlbuf_dma);
|
|
|
|
if (!usbhid->inbuf || !usbhid->outbuf || !usbhid->cr ||
|
|
|
|
!usbhid->ctrlbuf)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-01-18 16:04:39 +08:00
|
|
|
static int usbhid_get_raw_report(struct hid_device *hid,
|
|
|
|
unsigned char report_number, __u8 *buf, size_t count,
|
|
|
|
unsigned char report_type)
|
|
|
|
{
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
struct usb_device *dev = hid_to_usb_dev(hid);
|
|
|
|
struct usb_interface *intf = usbhid->intf;
|
|
|
|
struct usb_host_interface *interface = intf->cur_altsetting;
|
|
|
|
int skipped_report_id = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Byte 0 is the report number. Report data starts at byte 1.*/
|
|
|
|
buf[0] = report_number;
|
|
|
|
if (report_number == 0x0) {
|
|
|
|
/* Offset the return buffer by 1, so that the report ID
|
|
|
|
will remain in byte 0. */
|
|
|
|
buf++;
|
|
|
|
count--;
|
|
|
|
skipped_report_id = 1;
|
|
|
|
}
|
|
|
|
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
|
|
|
|
HID_REQ_GET_REPORT,
|
|
|
|
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
|
|
|
((report_type + 1) << 8) | report_number,
|
|
|
|
interface->desc.bInterfaceNumber, buf, count,
|
|
|
|
USB_CTRL_SET_TIMEOUT);
|
|
|
|
|
|
|
|
/* count also the report id */
|
|
|
|
if (ret > 0 && skipped_report_id)
|
|
|
|
ret++;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-01-23 02:49:42 +08:00
|
|
|
static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum,
|
|
|
|
__u8 *buf, size_t count, unsigned char rtype)
|
|
|
|
{
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
struct usb_device *dev = hid_to_usb_dev(hid);
|
|
|
|
struct usb_interface *intf = usbhid->intf;
|
|
|
|
struct usb_host_interface *interface = intf->cur_altsetting;
|
|
|
|
int ret, skipped_report_id = 0;
|
|
|
|
|
|
|
|
/* Byte 0 is the report number. Report data starts at byte 1.*/
|
2014-03-09 11:52:42 +08:00
|
|
|
if ((rtype == HID_OUTPUT_REPORT) &&
|
|
|
|
(hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORT_ID))
|
|
|
|
buf[0] = 0;
|
|
|
|
else
|
|
|
|
buf[0] = reportnum;
|
|
|
|
|
2014-01-23 02:49:42 +08:00
|
|
|
if (buf[0] == 0x0) {
|
|
|
|
/* Don't send the Report ID */
|
|
|
|
buf++;
|
|
|
|
count--;
|
|
|
|
skipped_report_id = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
|
|
|
HID_REQ_SET_REPORT,
|
|
|
|
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
|
|
|
((rtype + 1) << 8) | reportnum,
|
|
|
|
interface->desc.bInterfaceNumber, buf, count,
|
|
|
|
USB_CTRL_SET_TIMEOUT);
|
|
|
|
/* count also the report id, if this was a numbered report. */
|
|
|
|
if (ret > 0 && skipped_report_id)
|
|
|
|
ret++;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
struct usb_device *dev = hid_to_usb_dev(hid);
|
|
|
|
int actual_length, skipped_report_id = 0, ret;
|
|
|
|
|
|
|
|
if (!usbhid->urbout)
|
2014-02-11 01:58:51 +08:00
|
|
|
return -ENOSYS;
|
2014-01-23 02:49:42 +08:00
|
|
|
|
|
|
|
if (buf[0] == 0x0) {
|
|
|
|
/* Don't send the Report ID */
|
|
|
|
buf++;
|
|
|
|
count--;
|
|
|
|
skipped_report_id = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = usb_interrupt_msg(dev, usbhid->urbout->pipe,
|
|
|
|
buf, count, &actual_length,
|
|
|
|
USB_CTRL_SET_TIMEOUT);
|
|
|
|
/* return the number of bytes transferred */
|
|
|
|
if (ret == 0) {
|
|
|
|
ret = actual_length;
|
|
|
|
/* count also the report id */
|
|
|
|
if (skipped_report_id)
|
|
|
|
ret++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
|
|
|
|
{
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
|
2010-04-12 19:17:25 +08:00
|
|
|
usb_free_coherent(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma);
|
|
|
|
usb_free_coherent(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma);
|
2010-03-06 04:10:17 +08:00
|
|
|
kfree(usbhid->cr);
|
2010-04-12 19:17:25 +08:00
|
|
|
usb_free_coherent(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
static int usbhid_parse(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
struct usb_interface *intf = to_usb_interface(hid->dev.parent);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct usb_host_interface *interface = intf->cur_altsetting;
|
|
|
|
struct usb_device *dev = interface_to_usbdev (intf);
|
|
|
|
struct hid_descriptor *hdesc;
|
2007-04-19 19:27:04 +08:00
|
|
|
u32 quirks = 0;
|
2008-05-16 17:49:16 +08:00
|
|
|
unsigned int rsize = 0;
|
2005-09-15 15:01:47 +08:00
|
|
|
char *rdesc;
|
2008-05-16 17:49:16 +08:00
|
|
|
int ret, n;
|
2017-09-28 18:16:30 +08:00
|
|
|
int num_descriptors;
|
|
|
|
size_t offset = offsetof(struct hid_descriptor, desc);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-11-20 18:48:41 +08:00
|
|
|
quirks = hid_lookup_quirk(hid);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-01-29 07:15:51 +08:00
|
|
|
if (quirks & HID_QUIRK_IGNORE)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2006-05-16 02:49:04 +08:00
|
|
|
/* Many keyboards and mice don't like to be polled for reports,
|
|
|
|
* so we will always set the HID_QUIRK_NOGET flag for them. */
|
|
|
|
if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
|
|
|
|
if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD ||
|
|
|
|
interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
|
|
|
|
quirks |= HID_QUIRK_NOGET;
|
|
|
|
}
|
|
|
|
|
2005-09-15 15:01:47 +08:00
|
|
|
if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
|
|
|
|
(!interface->desc.bNumEndpoints ||
|
|
|
|
usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
|
2007-05-30 21:07:13 +08:00
|
|
|
dbg_hid("class descriptor not present\n");
|
2008-05-16 17:49:16 +08:00
|
|
|
return -ENODEV;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2017-09-28 18:16:30 +08:00
|
|
|
if (hdesc->bLength < sizeof(struct hid_descriptor)) {
|
|
|
|
dbg_hid("hid descriptor is too short\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
hid->version = le16_to_cpu(hdesc->bcdHID);
|
|
|
|
hid->country = hdesc->bCountryCode;
|
|
|
|
|
2017-09-28 18:16:30 +08:00
|
|
|
num_descriptors = min_t(int, hdesc->bNumDescriptors,
|
|
|
|
(hdesc->bLength - offset) / sizeof(struct hid_class_descriptor));
|
|
|
|
|
|
|
|
for (n = 0; n < num_descriptors; n++)
|
2005-04-17 06:20:36 +08:00
|
|
|
if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
|
|
|
|
rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
|
|
|
|
|
|
|
|
if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
|
2007-05-30 21:07:13 +08:00
|
|
|
dbg_hid("weird size of report descriptor (%u)\n", rsize);
|
2008-05-16 17:49:16 +08:00
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2017-03-02 09:35:07 +08:00
|
|
|
rdesc = kmalloc(rsize, GFP_KERNEL);
|
|
|
|
if (!rdesc)
|
2008-05-16 17:49:16 +08:00
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-05-29 15:28:00 +08:00
|
|
|
hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
|
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
ret = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber,
|
|
|
|
HID_DT_REPORT, rdesc, rsize);
|
|
|
|
if (ret < 0) {
|
2007-05-30 21:07:13 +08:00
|
|
|
dbg_hid("reading report descriptor failed\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(rdesc);
|
2008-05-16 17:49:16 +08:00
|
|
|
goto err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
ret = hid_parse_report(hid, rdesc, rsize);
|
|
|
|
kfree(rdesc);
|
|
|
|
if (ret) {
|
2007-05-30 21:07:13 +08:00
|
|
|
dbg_hid("parsing report descriptor failed\n");
|
2008-05-16 17:49:16 +08:00
|
|
|
goto err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-05-06 22:30:21 +08:00
|
|
|
hid->quirks |= quirks;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
return 0;
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usbhid_start(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
struct usb_interface *intf = to_usb_interface(hid->dev.parent);
|
|
|
|
struct usb_host_interface *interface = intf->cur_altsetting;
|
|
|
|
struct usb_device *dev = interface_to_usbdev(intf);
|
2008-10-27 19:16:15 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2008-05-16 17:49:16 +08:00
|
|
|
unsigned int n, insize = 0;
|
|
|
|
int ret;
|
|
|
|
|
2008-11-02 06:41:46 +08:00
|
|
|
clear_bit(HID_DISCONNECTED, &usbhid->iofl);
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->bufsize = HID_MIN_BUFFER_SIZE;
|
|
|
|
hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize);
|
|
|
|
hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize);
|
|
|
|
hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize);
|
2005-09-05 13:12:01 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usbhid->bufsize > HID_MAX_BUFFER_SIZE)
|
|
|
|
usbhid->bufsize = HID_MAX_BUFFER_SIZE;
|
2005-09-05 13:12:01 +08:00
|
|
|
|
|
|
|
hid_find_max_report(hid, HID_INPUT_REPORT, &insize);
|
|
|
|
|
|
|
|
if (insize > HID_MAX_BUFFER_SIZE)
|
|
|
|
insize = HID_MAX_BUFFER_SIZE;
|
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
if (hid_alloc_buffers(dev, hid)) {
|
|
|
|
ret = -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
2008-03-06 20:23:14 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
for (n = 0; n < interface->desc.bNumEndpoints; n++) {
|
|
|
|
struct usb_endpoint_descriptor *endpoint;
|
|
|
|
int pipe;
|
|
|
|
int interval;
|
|
|
|
|
|
|
|
endpoint = &interface->endpoint[n].desc;
|
2008-11-24 23:20:08 +08:00
|
|
|
if (!usb_endpoint_xfer_int(endpoint))
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
interval = endpoint->bInterval;
|
|
|
|
|
2008-03-06 20:23:14 +08:00
|
|
|
/* Some vendors give fullspeed interval on highspeed devides */
|
2008-05-16 17:49:16 +08:00
|
|
|
if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL &&
|
2008-03-06 20:23:14 +08:00
|
|
|
dev->speed == USB_SPEED_HIGH) {
|
|
|
|
interval = fls(endpoint->bInterval*8);
|
2017-03-02 09:35:07 +08:00
|
|
|
pr_info("%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
|
|
|
|
hid->name, endpoint->bInterval, interval);
|
2008-03-06 20:23:14 +08:00
|
|
|
}
|
|
|
|
|
2018-03-22 00:28:25 +08:00
|
|
|
/* Change the polling interval of mice, joysticks
|
|
|
|
* and keyboards.
|
|
|
|
*/
|
2017-02-26 03:27:27 +08:00
|
|
|
switch (hid->collection->usage) {
|
|
|
|
case HID_GD_MOUSE:
|
|
|
|
if (hid_mousepoll_interval > 0)
|
|
|
|
interval = hid_mousepoll_interval;
|
|
|
|
break;
|
|
|
|
case HID_GD_JOYSTICK:
|
|
|
|
if (hid_jspoll_interval > 0)
|
|
|
|
interval = hid_jspoll_interval;
|
|
|
|
break;
|
2018-03-22 00:28:25 +08:00
|
|
|
case HID_GD_KEYBOARD:
|
|
|
|
if (hid_kbpoll_interval > 0)
|
|
|
|
interval = hid_kbpoll_interval;
|
|
|
|
break;
|
2017-02-26 03:27:27 +08:00
|
|
|
}
|
2005-05-29 15:29:01 +08:00
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
ret = -ENOMEM;
|
2006-10-27 00:02:51 +08:00
|
|
|
if (usb_endpoint_dir_in(endpoint)) {
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usbhid->urbin)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL)))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
|
|
|
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize,
|
2005-04-17 06:20:36 +08:00
|
|
|
hid_irq_in, hid, interval);
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->urbin->transfer_dma = usbhid->inbuf_dma;
|
|
|
|
usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
2006-12-09 01:41:03 +08:00
|
|
|
if (usbhid->urbout)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
2006-12-09 01:41:03 +08:00
|
|
|
if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL)))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
|
|
|
pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress);
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0,
|
2005-04-17 06:20:36 +08:00
|
|
|
hid_irq_out, hid, interval);
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->urbout->transfer_dma = usbhid->outbuf_dma;
|
|
|
|
usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
|
2008-05-16 17:49:16 +08:00
|
|
|
if (!usbhid->urbctrl) {
|
|
|
|
ret = -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
goto fail;
|
2008-05-16 17:49:16 +08:00
|
|
|
}
|
2005-09-15 15:01:47 +08:00
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
|
|
|
|
usbhid->ctrlbuf, 1, hid_ctrl, hid);
|
|
|
|
usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
|
2010-03-06 04:10:17 +08:00
|
|
|
usbhid->urbctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
2008-05-16 17:49:16 +08:00
|
|
|
|
2008-10-27 19:16:15 +08:00
|
|
|
set_bit(HID_STARTED, &usbhid->iofl);
|
|
|
|
|
2014-09-06 00:08:47 +08:00
|
|
|
if (hid->quirks & HID_QUIRK_ALWAYS_POLL) {
|
|
|
|
ret = usb_autopm_get_interface(usbhid->intf);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
2017-06-07 14:59:33 +08:00
|
|
|
set_bit(HID_IN_POLLING, &usbhid->iofl);
|
2014-09-06 00:08:47 +08:00
|
|
|
usbhid->intf->needs_remote_wakeup = 1;
|
|
|
|
ret = hid_start_in(hid);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(&hid->dev,
|
|
|
|
"failed to start in urb: %d\n", ret);
|
|
|
|
}
|
|
|
|
usb_autopm_put_interface(usbhid->intf);
|
|
|
|
}
|
|
|
|
|
2008-10-31 06:58:51 +08:00
|
|
|
/* Some keyboards don't work until their LEDs have been set.
|
|
|
|
* Since BIOSes do set the LEDs, it must be safe for any device
|
|
|
|
* that supports the keyboard boot protocol.
|
2010-04-03 01:21:58 +08:00
|
|
|
* In addition, enable remote wakeup by default for all keyboard
|
|
|
|
* devices supporting the boot protocol.
|
2008-10-31 06:58:51 +08:00
|
|
|
*/
|
|
|
|
if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT &&
|
|
|
|
interface->desc.bInterfaceProtocol ==
|
2010-04-03 01:21:58 +08:00
|
|
|
USB_INTERFACE_PROTOCOL_KEYBOARD) {
|
2008-10-31 06:58:51 +08:00
|
|
|
usbhid_set_leds(hid);
|
2010-04-03 01:21:58 +08:00
|
|
|
device_set_wakeup_enable(&dev->dev, 1);
|
|
|
|
}
|
2008-05-16 17:49:16 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
fail:
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_free_urb(usbhid->urbin);
|
|
|
|
usb_free_urb(usbhid->urbout);
|
|
|
|
usb_free_urb(usbhid->urbctrl);
|
2008-11-02 06:41:46 +08:00
|
|
|
usbhid->urbin = NULL;
|
|
|
|
usbhid->urbout = NULL;
|
|
|
|
usbhid->urbctrl = NULL;
|
2007-08-01 18:32:27 +08:00
|
|
|
hid_free_buffers(dev, hid);
|
2008-05-16 17:49:16 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
static void usbhid_stop(struct hid_device *hid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-05-16 17:49:16 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
if (WARN_ON(!usbhid))
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
|
2017-06-07 14:59:33 +08:00
|
|
|
if (hid->quirks & HID_QUIRK_ALWAYS_POLL) {
|
|
|
|
clear_bit(HID_IN_POLLING, &usbhid->iofl);
|
2014-09-06 00:08:47 +08:00
|
|
|
usbhid->intf->needs_remote_wakeup = 0;
|
2017-06-07 14:59:33 +08:00
|
|
|
}
|
2014-09-06 00:08:47 +08:00
|
|
|
|
2008-10-27 19:16:15 +08:00
|
|
|
clear_bit(HID_STARTED, &usbhid->iofl);
|
2011-11-17 19:23:50 +08:00
|
|
|
spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */
|
2008-03-31 22:27:30 +08:00
|
|
|
set_bit(HID_DISCONNECTED, &usbhid->iofl);
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_unlock_irq(&usbhid->lock);
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_kill_urb(usbhid->urbin);
|
|
|
|
usb_kill_urb(usbhid->urbout);
|
|
|
|
usb_kill_urb(usbhid->urbctrl);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
hid_cancel_delayed_stuff(usbhid);
|
2006-02-01 01:58:38 +08:00
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
hid->claimed = 0;
|
|
|
|
|
2006-12-09 01:41:03 +08:00
|
|
|
usb_free_urb(usbhid->urbin);
|
|
|
|
usb_free_urb(usbhid->urbctrl);
|
|
|
|
usb_free_urb(usbhid->urbout);
|
2008-11-02 06:41:46 +08:00
|
|
|
usbhid->urbin = NULL; /* don't mess up next start */
|
|
|
|
usbhid->urbctrl = NULL;
|
|
|
|
usbhid->urbout = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-01-20 01:28:17 +08:00
|
|
|
hid_free_buffers(hid_to_usb_dev(hid), hid);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
static int usbhid_power(struct hid_device *hid, int lvl)
|
|
|
|
{
|
2017-06-07 14:59:32 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2008-12-17 22:38:03 +08:00
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
switch (lvl) {
|
|
|
|
case PM_HINT_FULLON:
|
2017-06-07 14:59:32 +08:00
|
|
|
r = usb_autopm_get_interface(usbhid->intf);
|
2008-12-17 22:38:03 +08:00
|
|
|
break;
|
2017-06-07 14:59:32 +08:00
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
case PM_HINT_NORMAL:
|
2017-06-07 14:59:32 +08:00
|
|
|
usb_autopm_put_interface(usbhid->intf);
|
2008-12-17 22:38:03 +08:00
|
|
|
break;
|
|
|
|
}
|
2017-06-07 14:59:32 +08:00
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-02-25 18:31:43 +08:00
|
|
|
static void usbhid_request(struct hid_device *hid, struct hid_report *rep, int reqtype)
|
|
|
|
{
|
|
|
|
switch (reqtype) {
|
|
|
|
case HID_REQ_GET_REPORT:
|
|
|
|
usbhid_submit_report(hid, rep, USB_DIR_IN);
|
|
|
|
break;
|
|
|
|
case HID_REQ_SET_REPORT:
|
|
|
|
usbhid_submit_report(hid, rep, USB_DIR_OUT);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-23 02:49:42 +08:00
|
|
|
static int usbhid_raw_request(struct hid_device *hid, unsigned char reportnum,
|
|
|
|
__u8 *buf, size_t len, unsigned char rtype,
|
|
|
|
int reqtype)
|
|
|
|
{
|
|
|
|
switch (reqtype) {
|
|
|
|
case HID_REQ_GET_REPORT:
|
|
|
|
return usbhid_get_raw_report(hid, reportnum, buf, len, rtype);
|
|
|
|
case HID_REQ_SET_REPORT:
|
|
|
|
return usbhid_set_raw_report(hid, reportnum, buf, len, rtype);
|
|
|
|
default:
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-27 23:38:17 +08:00
|
|
|
static int usbhid_idle(struct hid_device *hid, int report, int idle,
|
|
|
|
int reqtype)
|
|
|
|
{
|
|
|
|
struct usb_device *dev = hid_to_usb_dev(hid);
|
|
|
|
struct usb_interface *intf = to_usb_interface(hid->dev.parent);
|
|
|
|
struct usb_host_interface *interface = intf->cur_altsetting;
|
|
|
|
int ifnum = interface->desc.bInterfaceNumber;
|
|
|
|
|
|
|
|
if (reqtype != HID_REQ_SET_IDLE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return hid_set_idle(dev, ifnum, report, idle);
|
|
|
|
}
|
|
|
|
|
2017-07-25 00:46:18 +08:00
|
|
|
struct hid_ll_driver usb_hid_driver = {
|
2008-05-16 17:49:16 +08:00
|
|
|
.parse = usbhid_parse,
|
|
|
|
.start = usbhid_start,
|
|
|
|
.stop = usbhid_stop,
|
|
|
|
.open = usbhid_open,
|
|
|
|
.close = usbhid_close,
|
2008-12-17 22:38:03 +08:00
|
|
|
.power = usbhid_power,
|
2013-02-25 18:31:43 +08:00
|
|
|
.request = usbhid_request,
|
2013-02-25 18:31:44 +08:00
|
|
|
.wait = usbhid_wait_io,
|
2014-01-23 02:49:42 +08:00
|
|
|
.raw_request = usbhid_raw_request,
|
|
|
|
.output_report = usbhid_output_report,
|
2013-02-27 23:38:17 +08:00
|
|
|
.idle = usbhid_idle,
|
2008-05-16 17:49:16 +08:00
|
|
|
};
|
2017-07-25 00:46:18 +08:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hid_driver);
|
2008-05-16 17:49:16 +08:00
|
|
|
|
2009-09-15 22:27:45 +08:00
|
|
|
static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-11-14 19:03:47 +08:00
|
|
|
struct usb_host_interface *interface = intf->cur_altsetting;
|
2008-05-16 17:49:16 +08:00
|
|
|
struct usb_device *dev = interface_to_usbdev(intf);
|
2008-10-27 19:16:15 +08:00
|
|
|
struct usbhid_device *usbhid;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct hid_device *hid;
|
2008-11-14 19:03:47 +08:00
|
|
|
unsigned int n, has_in = 0;
|
2008-05-16 17:49:16 +08:00
|
|
|
size_t len;
|
|
|
|
int ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-05-30 21:07:13 +08:00
|
|
|
dbg_hid("HID probe called for ifnum %d\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
intf->altsetting->desc.bInterfaceNumber);
|
|
|
|
|
2008-11-14 19:03:47 +08:00
|
|
|
for (n = 0; n < interface->desc.bNumEndpoints; n++)
|
|
|
|
if (usb_endpoint_is_int_in(&interface->endpoint[n].desc))
|
|
|
|
has_in++;
|
|
|
|
if (!has_in) {
|
2010-12-10 11:29:03 +08:00
|
|
|
hid_err(intf, "couldn't find an input interrupt endpoint\n");
|
2008-11-14 19:03:47 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
hid = hid_allocate_device();
|
|
|
|
if (IS_ERR(hid))
|
|
|
|
return PTR_ERR(hid);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
usb_set_intfdata(intf, hid);
|
2008-05-16 17:49:16 +08:00
|
|
|
hid->ll_driver = &usb_hid_driver;
|
2008-09-18 18:23:33 +08:00
|
|
|
hid->ff_init = hid_pidff_init;
|
2008-05-16 17:49:16 +08:00
|
|
|
#ifdef CONFIG_USB_HIDDEV
|
2008-06-27 06:04:24 +08:00
|
|
|
hid->hiddev_connect = hiddev_connect;
|
2009-09-15 22:27:45 +08:00
|
|
|
hid->hiddev_disconnect = hiddev_disconnect;
|
2008-05-16 17:49:16 +08:00
|
|
|
hid->hiddev_hid_event = hiddev_hid_event;
|
|
|
|
hid->hiddev_report_event = hiddev_report_event;
|
|
|
|
#endif
|
|
|
|
hid->dev.parent = &intf->dev;
|
|
|
|
hid->bus = BUS_USB;
|
|
|
|
hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
|
|
|
|
hid->product = le16_to_cpu(dev->descriptor.idProduct);
|
2017-10-04 18:29:57 +08:00
|
|
|
hid->version = le16_to_cpu(dev->descriptor.bcdDevice);
|
2008-05-16 17:49:16 +08:00
|
|
|
hid->name[0] = 0;
|
2008-10-22 20:45:11 +08:00
|
|
|
if (intf->cur_altsetting->desc.bInterfaceProtocol ==
|
|
|
|
USB_INTERFACE_PROTOCOL_MOUSE)
|
|
|
|
hid->type = HID_TYPE_USBMOUSE;
|
2011-05-24 06:45:44 +08:00
|
|
|
else if (intf->cur_altsetting->desc.bInterfaceProtocol == 0)
|
|
|
|
hid->type = HID_TYPE_USBNONE;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
if (dev->manufacturer)
|
|
|
|
strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
if (dev->product) {
|
|
|
|
if (dev->manufacturer)
|
|
|
|
strlcat(hid->name, " ", sizeof(hid->name));
|
|
|
|
strlcat(hid->name, dev->product, sizeof(hid->name));
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
if (!strlen(hid->name))
|
|
|
|
snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
|
|
|
|
le16_to_cpu(dev->descriptor.idVendor),
|
|
|
|
le16_to_cpu(dev->descriptor.idProduct));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
usb_make_path(dev, hid->phys, sizeof(hid->phys));
|
|
|
|
strlcat(hid->phys, "/input", sizeof(hid->phys));
|
|
|
|
len = strlen(hid->phys);
|
|
|
|
if (len < sizeof(hid->phys) - 1)
|
|
|
|
snprintf(hid->phys + len, sizeof(hid->phys) - len,
|
|
|
|
"%d", intf->altsetting[0].desc.bInterfaceNumber);
|
|
|
|
|
|
|
|
if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
|
|
|
|
hid->uniq[0] = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-10-27 19:16:15 +08:00
|
|
|
usbhid = kzalloc(sizeof(*usbhid), GFP_KERNEL);
|
|
|
|
if (usbhid == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
hid->driver_data = usbhid;
|
|
|
|
usbhid->hid = hid;
|
2010-02-17 21:25:01 +08:00
|
|
|
usbhid->intf = intf;
|
|
|
|
usbhid->ifnum = interface->desc.bInterfaceNumber;
|
2008-10-27 19:16:15 +08:00
|
|
|
|
2010-05-07 22:41:10 +08:00
|
|
|
init_waitqueue_head(&usbhid->wait);
|
|
|
|
INIT_WORK(&usbhid->reset_work, hid_reset);
|
2017-10-05 08:53:24 +08:00
|
|
|
timer_setup(&usbhid->io_retry, hid_retry_timeout, 0);
|
2010-05-07 22:41:10 +08:00
|
|
|
spin_lock_init(&usbhid->lock);
|
|
|
|
|
2008-05-16 17:49:15 +08:00
|
|
|
ret = hid_add_device(hid);
|
|
|
|
if (ret) {
|
2008-05-16 17:49:20 +08:00
|
|
|
if (ret != -ENODEV)
|
2010-12-10 11:29:03 +08:00
|
|
|
hid_err(intf, "can't add hid device: %d\n", ret);
|
2008-10-27 19:16:15 +08:00
|
|
|
goto err_free;
|
2008-05-16 17:49:15 +08:00
|
|
|
}
|
2008-05-16 17:49:16 +08:00
|
|
|
|
|
|
|
return 0;
|
2008-10-27 19:16:15 +08:00
|
|
|
err_free:
|
|
|
|
kfree(usbhid);
|
2008-05-16 17:49:16 +08:00
|
|
|
err:
|
|
|
|
hid_destroy_device(hid);
|
2008-05-16 17:49:15 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-09-15 22:27:45 +08:00
|
|
|
static void usbhid_disconnect(struct usb_interface *intf)
|
2008-05-16 17:49:16 +08:00
|
|
|
{
|
|
|
|
struct hid_device *hid = usb_get_intfdata(intf);
|
2008-10-27 19:16:15 +08:00
|
|
|
struct usbhid_device *usbhid;
|
2008-05-16 17:49:16 +08:00
|
|
|
|
|
|
|
if (WARN_ON(!hid))
|
|
|
|
return;
|
|
|
|
|
2008-10-27 19:16:15 +08:00
|
|
|
usbhid = hid->driver_data;
|
2014-07-25 13:13:01 +08:00
|
|
|
spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */
|
|
|
|
set_bit(HID_DISCONNECTED, &usbhid->iofl);
|
|
|
|
spin_unlock_irq(&usbhid->lock);
|
2008-05-16 17:49:16 +08:00
|
|
|
hid_destroy_device(hid);
|
2008-10-27 19:16:15 +08:00
|
|
|
kfree(usbhid);
|
2008-05-16 17:49:16 +08:00
|
|
|
}
|
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid)
|
|
|
|
{
|
|
|
|
del_timer_sync(&usbhid->io_retry);
|
|
|
|
cancel_work_sync(&usbhid->reset_work);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hid_cease_io(struct usbhid_device *usbhid)
|
|
|
|
{
|
2011-10-14 00:21:58 +08:00
|
|
|
del_timer_sync(&usbhid->io_retry);
|
2008-12-17 22:38:03 +08:00
|
|
|
usb_kill_urb(usbhid->urbin);
|
|
|
|
usb_kill_urb(usbhid->urbctrl);
|
|
|
|
usb_kill_urb(usbhid->urbout);
|
|
|
|
}
|
|
|
|
|
2016-03-24 00:17:09 +08:00
|
|
|
static void hid_restart_io(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
int clear_halt = test_bit(HID_CLEAR_HALT, &usbhid->iofl);
|
|
|
|
int reset_pending = test_bit(HID_RESET_PENDING, &usbhid->iofl);
|
|
|
|
|
|
|
|
spin_lock_irq(&usbhid->lock);
|
|
|
|
clear_bit(HID_SUSPENDED, &usbhid->iofl);
|
|
|
|
usbhid_mark_busy(usbhid);
|
|
|
|
|
|
|
|
if (clear_halt || reset_pending)
|
|
|
|
schedule_work(&usbhid->reset_work);
|
|
|
|
usbhid->retry_delay = 0;
|
|
|
|
spin_unlock_irq(&usbhid->lock);
|
|
|
|
|
|
|
|
if (reset_pending || !test_bit(HID_STARTED, &usbhid->iofl))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!clear_halt) {
|
|
|
|
if (hid_start_in(hid) < 0)
|
|
|
|
hid_io_error(hid);
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irq(&usbhid->lock);
|
|
|
|
if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
|
|
|
|
usbhid_restart_out_queue(usbhid);
|
|
|
|
if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
|
|
|
|
usbhid_restart_ctrl_queue(usbhid);
|
|
|
|
spin_unlock_irq(&usbhid->lock);
|
|
|
|
}
|
|
|
|
|
2009-02-20 19:47:08 +08:00
|
|
|
/* Treat USB reset pretty much the same as suspend/resume */
|
|
|
|
static int hid_pre_reset(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = usb_get_intfdata(intf);
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
|
|
|
|
|
|
|
spin_lock_irq(&usbhid->lock);
|
|
|
|
set_bit(HID_RESET_PENDING, &usbhid->iofl);
|
|
|
|
spin_unlock_irq(&usbhid->lock);
|
|
|
|
hid_cease_io(usbhid);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Same routine used for post_reset and reset_resume */
|
|
|
|
static int hid_post_reset(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct usb_device *dev = interface_to_usbdev (intf);
|
|
|
|
struct hid_device *hid = usb_get_intfdata(intf);
|
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2012-04-03 23:04:15 +08:00
|
|
|
struct usb_host_interface *interface = intf->cur_altsetting;
|
2009-02-20 19:47:08 +08:00
|
|
|
int status;
|
2012-04-03 23:04:15 +08:00
|
|
|
char *rdesc;
|
|
|
|
|
|
|
|
/* Fetch and examine the HID report descriptor. If this
|
|
|
|
* has changed, then rebind. Since usbcore's check of the
|
|
|
|
* configuration descriptors passed, we already know that
|
|
|
|
* the size of the HID report descriptor has not changed.
|
|
|
|
*/
|
2012-09-21 03:00:32 +08:00
|
|
|
rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL);
|
2017-03-02 09:35:07 +08:00
|
|
|
if (!rdesc)
|
2016-12-05 19:37:24 +08:00
|
|
|
return -ENOMEM;
|
2017-03-02 09:35:07 +08:00
|
|
|
|
2012-04-03 23:04:15 +08:00
|
|
|
status = hid_get_class_descriptor(dev,
|
|
|
|
interface->desc.bInterfaceNumber,
|
2012-09-21 03:00:32 +08:00
|
|
|
HID_DT_REPORT, rdesc, hid->dev_rsize);
|
2012-04-03 23:04:15 +08:00
|
|
|
if (status < 0) {
|
|
|
|
dbg_hid("reading report descriptor failed (post_reset)\n");
|
|
|
|
kfree(rdesc);
|
2016-12-05 19:37:24 +08:00
|
|
|
return status;
|
2012-04-03 23:04:15 +08:00
|
|
|
}
|
2012-09-21 03:00:32 +08:00
|
|
|
status = memcmp(rdesc, hid->dev_rdesc, hid->dev_rsize);
|
2012-04-03 23:04:15 +08:00
|
|
|
kfree(rdesc);
|
|
|
|
if (status != 0) {
|
|
|
|
dbg_hid("report descriptor changed\n");
|
2016-12-05 19:37:24 +08:00
|
|
|
return -EPERM;
|
2012-04-03 23:04:15 +08:00
|
|
|
}
|
2009-06-04 21:48:38 +08:00
|
|
|
|
2016-03-24 00:17:09 +08:00
|
|
|
/* No need to do another reset or clear a halted endpoint */
|
2009-02-20 19:47:08 +08:00
|
|
|
spin_lock_irq(&usbhid->lock);
|
|
|
|
clear_bit(HID_RESET_PENDING, &usbhid->iofl);
|
2016-03-24 00:17:09 +08:00
|
|
|
clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
|
2009-02-20 19:47:08 +08:00
|
|
|
spin_unlock_irq(&usbhid->lock);
|
|
|
|
hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
|
2016-03-24 00:17:09 +08:00
|
|
|
|
|
|
|
hid_restart_io(hid);
|
2009-02-20 19:47:08 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-19 16:17:18 +08:00
|
|
|
#ifdef CONFIG_PM
|
2012-07-20 04:09:01 +08:00
|
|
|
static int hid_resume_common(struct hid_device *hid, bool driver_suspended)
|
|
|
|
{
|
2016-03-24 00:17:09 +08:00
|
|
|
int status = 0;
|
2012-07-20 04:09:01 +08:00
|
|
|
|
2016-03-24 00:17:09 +08:00
|
|
|
hid_restart_io(hid);
|
2012-07-20 04:09:01 +08:00
|
|
|
if (driver_suspended && hid->driver && hid->driver->resume)
|
|
|
|
status = hid->driver->resume(hid);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2005-04-19 08:39:22 +08:00
|
|
|
static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-12-17 22:38:03 +08:00
|
|
|
struct hid_device *hid = usb_get_intfdata(intf);
|
2006-12-09 01:41:03 +08:00
|
|
|
struct usbhid_device *usbhid = hid->driver_data;
|
2013-03-15 12:08:55 +08:00
|
|
|
int status = 0;
|
2012-07-20 04:09:01 +08:00
|
|
|
bool driver_suspended = false;
|
2013-07-16 01:10:13 +08:00
|
|
|
unsigned int ledcount;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-08-20 05:49:48 +08:00
|
|
|
if (PMSG_IS_AUTO(message)) {
|
2013-07-16 01:10:13 +08:00
|
|
|
ledcount = hidinput_count_leds(hid);
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_lock_irq(&usbhid->lock); /* Sync with error handler */
|
|
|
|
if (!test_bit(HID_RESET_PENDING, &usbhid->iofl)
|
|
|
|
&& !test_bit(HID_CLEAR_HALT, &usbhid->iofl)
|
|
|
|
&& !test_bit(HID_OUT_RUNNING, &usbhid->iofl)
|
|
|
|
&& !test_bit(HID_CTRL_RUNNING, &usbhid->iofl)
|
|
|
|
&& !test_bit(HID_KEYS_PRESSED, &usbhid->iofl)
|
2013-07-16 01:10:13 +08:00
|
|
|
&& (!ledcount || ignoreled))
|
2008-12-17 22:38:03 +08:00
|
|
|
{
|
2012-07-20 04:08:45 +08:00
|
|
|
set_bit(HID_SUSPENDED, &usbhid->iofl);
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_unlock_irq(&usbhid->lock);
|
2010-04-26 03:40:03 +08:00
|
|
|
if (hid->driver && hid->driver->suspend) {
|
|
|
|
status = hid->driver->suspend(hid, message);
|
|
|
|
if (status < 0)
|
2012-07-20 04:09:01 +08:00
|
|
|
goto failed;
|
2010-04-26 03:40:03 +08:00
|
|
|
}
|
2012-07-20 04:09:01 +08:00
|
|
|
driver_suspended = true;
|
2008-12-17 22:38:03 +08:00
|
|
|
} else {
|
|
|
|
usbhid_mark_busy(usbhid);
|
|
|
|
spin_unlock_irq(&usbhid->lock);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2008-10-27 19:16:15 +08:00
|
|
|
|
2008-12-17 22:38:03 +08:00
|
|
|
} else {
|
2013-03-15 12:08:55 +08:00
|
|
|
/* TODO: resume() might need to handle suspend failure */
|
|
|
|
if (hid->driver && hid->driver->suspend)
|
2010-04-26 03:40:03 +08:00
|
|
|
status = hid->driver->suspend(hid, message);
|
2012-07-20 04:09:01 +08:00
|
|
|
driver_suspended = true;
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_lock_irq(&usbhid->lock);
|
2012-07-20 04:08:45 +08:00
|
|
|
set_bit(HID_SUSPENDED, &usbhid->iofl);
|
2008-12-17 22:38:03 +08:00
|
|
|
spin_unlock_irq(&usbhid->lock);
|
2013-03-15 12:08:55 +08:00
|
|
|
if (usbhid_wait_io(hid) < 0)
|
2012-07-20 04:09:01 +08:00
|
|
|
status = -EIO;
|
2008-12-17 22:38:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
hid_cancel_delayed_stuff(usbhid);
|
|
|
|
hid_cease_io(usbhid);
|
|
|
|
|
2011-08-20 05:49:48 +08:00
|
|
|
if (PMSG_IS_AUTO(message) && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
|
2008-12-17 22:38:03 +08:00
|
|
|
/* lost race against keypresses */
|
2012-07-20 04:09:01 +08:00
|
|
|
status = -EBUSY;
|
|
|
|
goto failed;
|
2008-12-17 22:38:03 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
dev_dbg(&intf->dev, "suspend\n");
|
2013-03-15 12:08:55 +08:00
|
|
|
return status;
|
2012-07-20 04:09:01 +08:00
|
|
|
|
|
|
|
failed:
|
|
|
|
hid_resume_common(hid, driver_suspended);
|
|
|
|
return status;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int hid_resume(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct hid_device *hid = usb_get_intfdata (intf);
|
|
|
|
int status;
|
|
|
|
|
2012-07-20 04:09:01 +08:00
|
|
|
status = hid_resume_common(hid, true);
|
2005-04-17 06:20:36 +08:00
|
|
|
dev_dbg(&intf->dev, "resume status %d\n", status);
|
2007-05-31 03:38:16 +08:00
|
|
|
return 0;
|
2006-06-02 01:55:28 +08:00
|
|
|
}
|
|
|
|
|
2009-02-18 18:46:45 +08:00
|
|
|
static int hid_reset_resume(struct usb_interface *intf)
|
2006-06-02 01:55:28 +08:00
|
|
|
{
|
2009-02-18 18:46:45 +08:00
|
|
|
struct hid_device *hid = usb_get_intfdata(intf);
|
2010-04-26 03:40:03 +08:00
|
|
|
int status;
|
2006-06-02 01:55:28 +08:00
|
|
|
|
2010-04-26 03:40:03 +08:00
|
|
|
status = hid_post_reset(intf);
|
|
|
|
if (status >= 0 && hid->driver && hid->driver->reset_resume) {
|
|
|
|
int ret = hid->driver->reset_resume(hid);
|
|
|
|
if (ret < 0)
|
|
|
|
status = ret;
|
|
|
|
}
|
|
|
|
return status;
|
2006-06-02 01:55:28 +08:00
|
|
|
}
|
|
|
|
|
2009-02-20 19:47:08 +08:00
|
|
|
#endif /* CONFIG_PM */
|
2006-06-02 01:55:28 +08:00
|
|
|
|
2010-01-11 00:59:22 +08:00
|
|
|
static const struct usb_device_id hid_usb_ids[] = {
|
2005-04-17 06:20:36 +08:00
|
|
|
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
|
|
|
|
.bInterfaceClass = USB_INTERFACE_CLASS_HID },
|
|
|
|
{ } /* Terminating entry */
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE (usb, hid_usb_ids);
|
|
|
|
|
|
|
|
static struct usb_driver hid_driver = {
|
|
|
|
.name = "usbhid",
|
2009-09-15 22:27:45 +08:00
|
|
|
.probe = usbhid_probe,
|
|
|
|
.disconnect = usbhid_disconnect,
|
2009-01-19 16:17:18 +08:00
|
|
|
#ifdef CONFIG_PM
|
2005-04-17 06:20:36 +08:00
|
|
|
.suspend = hid_suspend,
|
|
|
|
.resume = hid_resume,
|
2009-02-18 18:46:45 +08:00
|
|
|
.reset_resume = hid_reset_resume,
|
2009-01-19 16:17:18 +08:00
|
|
|
#endif
|
2006-06-02 01:55:28 +08:00
|
|
|
.pre_reset = hid_pre_reset,
|
|
|
|
.post_reset = hid_post_reset,
|
2005-04-17 06:20:36 +08:00
|
|
|
.id_table = hid_usb_ids,
|
2007-07-11 20:48:58 +08:00
|
|
|
.supports_autosuspend = 1,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2010-09-13 03:32:35 +08:00
|
|
|
struct usb_interface *usbhid_find_interface(int minor)
|
|
|
|
{
|
|
|
|
return usb_find_interface(&hid_driver, minor);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int __init hid_init(void)
|
|
|
|
{
|
2008-12-17 22:38:03 +08:00
|
|
|
int retval = -ENOMEM;
|
|
|
|
|
2017-11-20 18:48:41 +08:00
|
|
|
retval = hid_quirks_init(quirks_param, BUS_USB, MAX_USBHID_BOOT_QUIRKS);
|
2007-04-19 20:56:12 +08:00
|
|
|
if (retval)
|
|
|
|
goto usbhid_quirks_init_fail;
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = usb_register(&hid_driver);
|
|
|
|
if (retval)
|
|
|
|
goto usb_register_fail;
|
2017-03-02 09:35:07 +08:00
|
|
|
pr_info(KBUILD_MODNAME ": " DRIVER_DESC "\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
usb_register_fail:
|
2017-11-20 18:48:41 +08:00
|
|
|
hid_quirks_exit(BUS_USB);
|
2007-04-19 20:56:12 +08:00
|
|
|
usbhid_quirks_init_fail:
|
2005-04-17 06:20:36 +08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit hid_exit(void)
|
|
|
|
{
|
|
|
|
usb_deregister(&hid_driver);
|
2017-11-20 18:48:41 +08:00
|
|
|
hid_quirks_exit(BUS_USB);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(hid_init);
|
|
|
|
module_exit(hid_exit);
|
|
|
|
|
2009-10-03 00:29:34 +08:00
|
|
|
MODULE_AUTHOR("Andreas Gal");
|
|
|
|
MODULE_AUTHOR("Vojtech Pavlik");
|
|
|
|
MODULE_AUTHOR("Jiri Kosina");
|
2005-04-17 06:20:36 +08:00
|
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
2017-01-06 03:07:04 +08:00
|
|
|
MODULE_LICENSE("GPL");
|