Skip to content

Commit 18a1b06

Browse files
dtorbentiss
authored andcommitted
HID: hiddev: fix mess in hiddev_open()
The open method of hiddev handler fails to bring the device out of autosuspend state as was promised in 0361a28, as it actually has 2 blocks that try to start the transport (call hid_hw_open()) with both being guarded by the "open" counter, so the 2nd block is never executed as the first block increments the counter so it is never at 0 when we check it for the second block. Additionally hiddev_open() was leaving counter incremented on errors, causing the device to never be reopened properly if there was ever an error. Let's fix all of this by factoring out code that creates client structure and powers up the device into a separate function that is being called from usbhid_open() with the "existancelock" being held. Fixes: 0361a28 ("HID: autosuspend support for USB HID") Signed-off-by: Dmitry Torokhov <[email protected]> Signed-off-by: Benjamin Tissoires <[email protected]>
1 parent 4f38821 commit 18a1b06

File tree

1 file changed

+42
-55
lines changed

1 file changed

+42
-55
lines changed

drivers/hid/usbhid/hiddev.c

Lines changed: 42 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -241,12 +241,51 @@ static int hiddev_release(struct inode * inode, struct file * file)
241241
return 0;
242242
}
243243

244+
static int __hiddev_open(struct hiddev *hiddev, struct file *file)
245+
{
246+
struct hiddev_list *list;
247+
int error;
248+
249+
lockdep_assert_held(&hiddev->existancelock);
250+
251+
list = vzalloc(sizeof(*list));
252+
if (!list)
253+
return -ENOMEM;
254+
255+
mutex_init(&list->thread_lock);
256+
list->hiddev = hiddev;
257+
258+
if (!hiddev->open++) {
259+
error = hid_hw_power(hiddev->hid, PM_HINT_FULLON);
260+
if (error < 0)
261+
goto err_drop_count;
262+
263+
error = hid_hw_open(hiddev->hid);
264+
if (error < 0)
265+
goto err_normal_power;
266+
}
267+
268+
spin_lock_irq(&hiddev->list_lock);
269+
list_add_tail(&list->node, &hiddev->list);
270+
spin_unlock_irq(&hiddev->list_lock);
271+
272+
file->private_data = list;
273+
274+
return 0;
275+
276+
err_normal_power:
277+
hid_hw_power(hiddev->hid, PM_HINT_NORMAL);
278+
err_drop_count:
279+
hiddev->open--;
280+
vfree(list);
281+
return error;
282+
}
283+
244284
/*
245285
* open file op
246286
*/
247287
static int hiddev_open(struct inode *inode, struct file *file)
248288
{
249-
struct hiddev_list *list;
250289
struct usb_interface *intf;
251290
struct hid_device *hid;
252291
struct hiddev *hiddev;
@@ -255,66 +294,14 @@ static int hiddev_open(struct inode *inode, struct file *file)
255294
intf = usbhid_find_interface(iminor(inode));
256295
if (!intf)
257296
return -ENODEV;
297+
258298
hid = usb_get_intfdata(intf);
259299
hiddev = hid->hiddev;
260300

261-
if (!(list = vzalloc(sizeof(struct hiddev_list))))
262-
return -ENOMEM;
263-
mutex_init(&list->thread_lock);
264-
list->hiddev = hiddev;
265-
file->private_data = list;
266-
267-
/*
268-
* no need for locking because the USB major number
269-
* is shared which usbcore guards against disconnect
270-
*/
271-
if (list->hiddev->exist) {
272-
if (!list->hiddev->open++) {
273-
res = hid_hw_open(hiddev->hid);
274-
if (res < 0)
275-
goto bail;
276-
}
277-
} else {
278-
res = -ENODEV;
279-
goto bail;
280-
}
281-
282-
spin_lock_irq(&list->hiddev->list_lock);
283-
list_add_tail(&list->node, &hiddev->list);
284-
spin_unlock_irq(&list->hiddev->list_lock);
285-
286301
mutex_lock(&hiddev->existancelock);
287-
/*
288-
* recheck exist with existance lock held to
289-
* avoid opening a disconnected device
290-
*/
291-
if (!list->hiddev->exist) {
292-
res = -ENODEV;
293-
goto bail_unlock;
294-
}
295-
if (!list->hiddev->open++)
296-
if (list->hiddev->exist) {
297-
struct hid_device *hid = hiddev->hid;
298-
res = hid_hw_power(hid, PM_HINT_FULLON);
299-
if (res < 0)
300-
goto bail_unlock;
301-
res = hid_hw_open(hid);
302-
if (res < 0)
303-
goto bail_normal_power;
304-
}
305-
mutex_unlock(&hiddev->existancelock);
306-
return 0;
307-
bail_normal_power:
308-
hid_hw_power(hid, PM_HINT_NORMAL);
309-
bail_unlock:
302+
res = hiddev->exist ? __hiddev_open(hiddev, file) : -ENODEV;
310303
mutex_unlock(&hiddev->existancelock);
311304

312-
spin_lock_irq(&list->hiddev->list_lock);
313-
list_del(&list->node);
314-
spin_unlock_irq(&list->hiddev->list_lock);
315-
bail:
316-
file->private_data = NULL;
317-
vfree(list);
318305
return res;
319306
}
320307

0 commit comments

Comments
 (0)