aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/usbhid/hid-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/usbhid/hid-core.c')
-rw-r--r--drivers/hid/usbhid/hid-core.c150
1 files changed, 70 insertions, 80 deletions
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 83772fa7d92a6f..76013eb5cb7fae 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -70,8 +70,6 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying "
/*
* Input submission and I/O error handler.
*/
-static DEFINE_MUTEX(hid_open_mut);
-
static void hid_io_error(struct hid_device *hid);
static int hid_submit_out(struct hid_device *hid);
static int hid_submit_ctrl(struct hid_device *hid);
@@ -85,10 +83,10 @@ static int hid_start_in(struct hid_device *hid)
struct usbhid_device *usbhid = hid->driver_data;
spin_lock_irqsave(&usbhid->lock, flags);
- if ((hid->open > 0 || hid->quirks & HID_QUIRK_ALWAYS_POLL) &&
- !test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
- !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
- !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
+ 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)) {
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
if (rc != 0) {
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
@@ -272,13 +270,13 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
static void hid_irq_in(struct urb *urb)
{
struct hid_device *hid = urb->context;
- struct usbhid_device *usbhid = hid->driver_data;
+ struct usbhid_device *usbhid = hid->driver_data;
int status;
switch (urb->status) {
case 0: /* success */
usbhid->retry_delay = 0;
- if ((hid->quirks & HID_QUIRK_ALWAYS_POLL) && !hid->open)
+ if (!test_bit(HID_OPENED, &usbhid->iofl))
break;
usbhid_mark_busy(usbhid);
if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
@@ -677,73 +675,74 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
return result;
}
-int usbhid_open(struct hid_device *hid)
+static int usbhid_open(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
- int res = 0;
-
- mutex_lock(&hid_open_mut);
- if (!hid->open++) {
- res = usb_autopm_get_interface(usbhid->intf);
- /* the device must be awake to reliably request remote wakeup */
- if (res < 0) {
- hid->open--;
- res = -EIO;
- goto done;
- }
- usbhid->intf->needs_remote_wakeup = 1;
- set_bit(HID_RESUME_RUNNING, &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 */
- hid->open--;
- res = -EBUSY;
- usbhid->intf->needs_remote_wakeup = 0;
- }
- }
- usb_autopm_put_interface(usbhid->intf);
+ int res;
- /*
- * 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 && !(hid->quirks & HID_QUIRK_ALWAYS_POLL))
- msleep(50);
- clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
+ 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 */
+ if (res < 0)
+ return -EIO;
+
+ usbhid->intf->needs_remote_wakeup = 1;
+
+ set_bit(HID_RESUME_RUNNING, &usbhid->iofl);
+ set_bit(HID_OPENED, &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;
+ }
}
-done:
- mutex_unlock(&hid_open_mut);
+
+ 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);
return res;
}
-void usbhid_close(struct hid_device *hid)
+static void usbhid_close(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
- mutex_lock(&hid_open_mut);
+ if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
+ return;
- /* protecting hid->open to make sure we don't restart
- * data acquistion due to a resumption we no longer
- * care about
+ /*
+ * Make sure we don't restart data acquisition due to
+ * a resumption we no longer care about by avoiding racing
+ * with hid_start_in().
*/
spin_lock_irq(&usbhid->lock);
- if (!--hid->open) {
- spin_unlock_irq(&usbhid->lock);
- hid_cancel_delayed_stuff(usbhid);
- if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) {
- usb_kill_urb(usbhid->urbin);
- usbhid->intf->needs_remote_wakeup = 0;
- }
- } else {
- spin_unlock_irq(&usbhid->lock);
- }
- mutex_unlock(&hid_open_mut);
+ clear_bit(HID_IN_POLLING, &usbhid->iofl);
+ clear_bit(HID_OPENED, &usbhid->iofl);
+ spin_unlock_irq(&usbhid->lock);
+
+ hid_cancel_delayed_stuff(usbhid);
+ usb_kill_urb(usbhid->urbin);
+ usbhid->intf->needs_remote_wakeup = 0;
}
/*
@@ -1135,6 +1134,7 @@ static int usbhid_start(struct hid_device *hid)
ret = usb_autopm_get_interface(usbhid->intf);
if (ret)
goto fail;
+ set_bit(HID_IN_POLLING, &usbhid->iofl);
usbhid->intf->needs_remote_wakeup = 1;
ret = hid_start_in(hid);
if (ret) {
@@ -1176,8 +1176,10 @@ static void usbhid_stop(struct hid_device *hid)
if (WARN_ON(!usbhid))
return;
- if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
+ if (hid->quirks & HID_QUIRK_ALWAYS_POLL) {
+ clear_bit(HID_IN_POLLING, &usbhid->iofl);
usbhid->intf->needs_remote_wakeup = 0;
+ }
clear_bit(HID_STARTED, &usbhid->iofl);
spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */
@@ -1203,16 +1205,19 @@ static void usbhid_stop(struct hid_device *hid)
static int usbhid_power(struct hid_device *hid, int lvl)
{
+ struct usbhid_device *usbhid = hid->driver_data;
int r = 0;
switch (lvl) {
case PM_HINT_FULLON:
- r = usbhid_get_power(hid);
+ r = usb_autopm_get_interface(usbhid->intf);
break;
+
case PM_HINT_NORMAL:
- usbhid_put_power(hid);
+ usb_autopm_put_interface(usbhid->intf);
break;
}
+
return r;
}
@@ -1492,21 +1497,6 @@ static int hid_post_reset(struct usb_interface *intf)
return 0;
}
-int usbhid_get_power(struct hid_device *hid)
-{
- struct usbhid_device *usbhid = hid->driver_data;
-
- return usb_autopm_get_interface(usbhid->intf);
-}
-
-void usbhid_put_power(struct hid_device *hid)
-{
- struct usbhid_device *usbhid = hid->driver_data;
-
- usb_autopm_put_interface(usbhid->intf);
-}
-
-
#ifdef CONFIG_PM
static int hid_resume_common(struct hid_device *hid, bool driver_suspended)
{