blob: 55b72573066b93ce070708b2f9496d4570e0c381 [file] [log] [blame]
Thomas Gleixner1a59d1b82019-05-27 08:55:05 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * Copyright (c) 2001 Paul Stewart
4 * Copyright (c) 2001 Vojtech Pavlik
5 *
6 * HID char devices, giving access to raw HID device events.
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 */
8
9/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 *
11 * Should you need to contact me, the author, you can do so either by
12 * e-mail - mail your message to Paul Stewart <stewart@wetlogic.net>
13 */
14
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/poll.h>
16#include <linux/slab.h>
Ingo Molnar174cd4b2017-02-02 19:15:33 +010017#include <linux/sched/signal.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/module.h>
19#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <linux/input.h>
21#include <linux/usb.h>
Jiri Kosinadde58452006-12-08 18:40:44 +010022#include <linux/hid.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/hiddev.h>
Philip Langdalebb6c8d82007-10-14 12:03:58 +020024#include <linux/compat.h>
Havard Skinnemoend4f0e4da2012-04-26 11:16:00 -070025#include <linux/vmalloc.h>
Gustavo A. R. Silva4f652452018-06-29 17:08:44 -050026#include <linux/nospec.h>
Jiri Kosinadde58452006-12-08 18:40:44 +010027#include "usbhid.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#ifdef CONFIG_USB_DYNAMIC_MINORS
30#define HIDDEV_MINOR_BASE 0
31#define HIDDEV_MINORS 256
32#else
33#define HIDDEV_MINOR_BASE 96
34#define HIDDEV_MINORS 16
35#endif
Jiri Kosinaaffbb8c2009-08-20 12:04:14 +020036#define HIDDEV_BUFFER_SIZE 2048
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
Linus Torvalds1da177e2005-04-16 15:20:36 -070038struct hiddev_list {
39 struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
40 int head;
41 int tail;
42 unsigned flags;
43 struct fasync_struct *fasync;
44 struct hiddev *hiddev;
Dmitry Torokhov826d5982006-07-19 01:09:10 -040045 struct list_head node;
Oliver Neukum07903402008-12-16 10:55:15 +010046 struct mutex thread_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047};
48
Linus Torvalds1da177e2005-04-16 15:20:36 -070049/*
50 * Find a report, given the report's type and ID. The ID can be specified
51 * indirectly by REPORT_ID_FIRST (which returns the first report of the given
52 * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the
53 * given type which follows old_id.
54 */
55static struct hid_report *
56hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
57{
Dmitry Torokhov826d5982006-07-19 01:09:10 -040058 unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
59 unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 struct hid_report_enum *report_enum;
Dmitry Torokhov826d5982006-07-19 01:09:10 -040061 struct hid_report *report;
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 struct list_head *list;
63
64 if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
Dmitry Torokhov826d5982006-07-19 01:09:10 -040065 rinfo->report_type > HID_REPORT_TYPE_MAX)
66 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68 report_enum = hid->report_enum +
69 (rinfo->report_type - HID_REPORT_TYPE_MIN);
70
71 switch (flags) {
72 case 0: /* Nothing to do -- report_id is already set correctly */
73 break;
74
75 case HID_REPORT_ID_FIRST:
Dmitry Torokhov826d5982006-07-19 01:09:10 -040076 if (list_empty(&report_enum->report_list))
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 return NULL;
Dmitry Torokhov826d5982006-07-19 01:09:10 -040078
79 list = report_enum->report_list.next;
80 report = list_entry(list, struct hid_report, list);
81 rinfo->report_id = report->id;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 break;
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -050083
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 case HID_REPORT_ID_NEXT:
Dmitry Torokhov826d5982006-07-19 01:09:10 -040085 report = report_enum->report_id_hash[rid];
86 if (!report)
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 return NULL;
Dmitry Torokhov826d5982006-07-19 01:09:10 -040088
89 list = report->list.next;
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 if (list == &report_enum->report_list)
91 return NULL;
Dmitry Torokhov826d5982006-07-19 01:09:10 -040092
93 report = list_entry(list, struct hid_report, list);
94 rinfo->report_id = report->id;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 break;
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -050096
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 default:
98 return NULL;
99 }
100
101 return report_enum->report_id_hash[rinfo->report_id];
102}
103
104/*
105 * Perform an exhaustive search of the report table for a usage, given its
106 * type and usage id.
107 */
108static struct hid_field *
109hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
110{
111 int i, j;
112 struct hid_report *report;
113 struct hid_report_enum *report_enum;
114 struct hid_field *field;
115
116 if (uref->report_type < HID_REPORT_TYPE_MIN ||
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400117 uref->report_type > HID_REPORT_TYPE_MAX)
118 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
120 report_enum = hid->report_enum +
121 (uref->report_type - HID_REPORT_TYPE_MIN);
122
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400123 list_for_each_entry(report, &report_enum->report_list, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 for (i = 0; i < report->maxfield; i++) {
125 field = report->field[i];
126 for (j = 0; j < field->maxusage; j++) {
127 if (field->usage[j].hid == uref->usage_code) {
128 uref->report_id = report->id;
129 uref->field_index = i;
130 uref->usage_index = j;
131 return field;
132 }
133 }
134 }
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400135 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
137 return NULL;
138}
139
140static void hiddev_send_event(struct hid_device *hid,
141 struct hiddev_usage_ref *uref)
142{
143 struct hiddev *hiddev = hid->hiddev;
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400144 struct hiddev_list *list;
Jiri Kosinacdcb44e2007-05-10 08:45:56 +0200145 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
Jiri Kosinacdcb44e2007-05-10 08:45:56 +0200147 spin_lock_irqsave(&hiddev->list_lock, flags);
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400148 list_for_each_entry(list, &hiddev->list, node) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 if (uref->field_index != HID_FIELD_INDEX_NONE ||
150 (list->flags & HIDDEV_FLAG_REPORT) != 0) {
151 list->buffer[list->head] = *uref;
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500152 list->head = (list->head + 1) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 (HIDDEV_BUFFER_SIZE - 1);
154 kill_fasync(&list->fasync, SIGIO, POLL_IN);
155 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 }
Jiri Kosinacdcb44e2007-05-10 08:45:56 +0200157 spin_unlock_irqrestore(&hiddev->list_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
159 wake_up_interruptible(&hiddev->wait);
160}
161
162/*
163 * This is where hid.c calls into hiddev to pass an event that occurred over
164 * the interrupt pipe
165 */
166void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
David Howells7d12e782006-10-05 14:55:46 +0100167 struct hid_usage *usage, __s32 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168{
169 unsigned type = field->report_type;
170 struct hiddev_usage_ref uref;
171
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500172 uref.report_type =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500174 ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400175 ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 uref.report_id = field->report->id;
177 uref.field_index = field->index;
178 uref.usage_index = (usage - field->usage);
179 uref.usage_code = usage->hid;
180 uref.value = value;
181
182 hiddev_send_event(hid, &uref);
183}
Jiri Kosina229695e2006-12-08 18:40:53 +0100184EXPORT_SYMBOL_GPL(hiddev_hid_event);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
186void hiddev_report_event(struct hid_device *hid, struct hid_report *report)
187{
188 unsigned type = report->type;
189 struct hiddev_usage_ref uref;
190
191 memset(&uref, 0, sizeof(uref));
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500192 uref.report_type =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500194 ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400195 ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 uref.report_id = report->id;
197 uref.field_index = HID_FIELD_INDEX_NONE;
198
199 hiddev_send_event(hid, &uref);
200}
Jiri Kosinaaa8de2f2006-12-08 18:41:17 +0100201
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202/*
203 * fasync file op
204 */
205static int hiddev_fasync(int fd, struct file *file, int on)
206{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 struct hiddev_list *list = file->private_data;
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400208
Jonathan Corbet60aa4922009-02-01 14:52:56 -0700209 return fasync_helper(fd, file, on, &list->fasync);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210}
211
212
213/*
214 * release file op
215 */
216static int hiddev_release(struct inode * inode, struct file * file)
217{
218 struct hiddev_list *list = file->private_data;
Jiri Kosinacdcb44e2007-05-10 08:45:56 +0200219 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
Jiri Kosinacdcb44e2007-05-10 08:45:56 +0200221 spin_lock_irqsave(&list->hiddev->list_lock, flags);
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400222 list_del(&list->node);
Jiri Kosinacdcb44e2007-05-10 08:45:56 +0200223 spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224
Jiri Kosina6cb4b042011-05-20 10:50:13 +0200225 mutex_lock(&list->hiddev->existancelock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 if (!--list->hiddev->open) {
Oliver Neukum0361a282008-12-17 15:38:03 +0100227 if (list->hiddev->exist) {
Dmitry Torokhovd36b7d42017-06-06 23:59:31 -0700228 hid_hw_close(list->hiddev->hid);
Dmitry Torokhov9a835632017-06-06 23:59:32 -0700229 hid_hw_power(list->hiddev->hid, PM_HINT_NORMAL);
Oliver Neukum0361a282008-12-17 15:38:03 +0100230 } else {
Dan Carpenter5c699d72011-05-26 11:49:16 +0300231 mutex_unlock(&list->hiddev->existancelock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 kfree(list->hiddev);
Havard Skinnemoend4f0e4da2012-04-26 11:16:00 -0700233 vfree(list);
Dan Carpenter5c699d72011-05-26 11:49:16 +0300234 return 0;
Oliver Neukum0361a282008-12-17 15:38:03 +0100235 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 }
237
Jiri Kosina6cb4b042011-05-20 10:50:13 +0200238 mutex_unlock(&list->hiddev->existancelock);
Havard Skinnemoend4f0e4da2012-04-26 11:16:00 -0700239 vfree(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240
241 return 0;
242}
243
244/*
245 * open file op
246 */
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400247static int hiddev_open(struct inode *inode, struct file *file)
248{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 struct hiddev_list *list;
Arnd Bergmannbd25f4d2010-07-11 15:34:05 +0200250 struct usb_interface *intf;
Jiri Kosina9c9e54a2010-08-13 12:19:45 +0200251 struct hid_device *hid;
Arnd Bergmannbd25f4d2010-07-11 15:34:05 +0200252 struct hiddev *hiddev;
253 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
Guillaume Chazarain8fe294c2010-09-12 21:32:35 +0200255 intf = usbhid_find_interface(iminor(inode));
Arnd Bergmannbd25f4d2010-07-11 15:34:05 +0200256 if (!intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 return -ENODEV;
Jiri Kosina9c9e54a2010-08-13 12:19:45 +0200258 hid = usb_get_intfdata(intf);
259 hiddev = hid->hiddev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
Havard Skinnemoend4f0e4da2012-04-26 11:16:00 -0700261 if (!(list = vzalloc(sizeof(struct hiddev_list))))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 return -ENOMEM;
Oliver Neukum07903402008-12-16 10:55:15 +0100263 mutex_init(&list->thread_lock);
Arnd Bergmannbd25f4d2010-07-11 15:34:05 +0200264 list->hiddev = hiddev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 file->private_data = list;
266
Oliver Neukum07903402008-12-16 10:55:15 +0100267 /*
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++) {
Dmitry Torokhovd36b7d42017-06-06 23:59:31 -0700273 res = hid_hw_open(hiddev->hid);
274 if (res < 0)
Oliver Neukum07903402008-12-16 10:55:15 +0100275 goto bail;
Oliver Neukum07903402008-12-16 10:55:15 +0100276 }
277 } else {
278 res = -ENODEV;
279 goto bail;
280 }
281
282 spin_lock_irq(&list->hiddev->list_lock);
Arnd Bergmannbd25f4d2010-07-11 15:34:05 +0200283 list_add_tail(&list->node, &hiddev->list);
Oliver Neukum07903402008-12-16 10:55:15 +0100284 spin_unlock_irq(&list->hiddev->list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
Jiri Kosina6cb4b042011-05-20 10:50:13 +0200286 mutex_lock(&hiddev->existancelock);
Oliver Neukum0361a282008-12-17 15:38:03 +0100287 if (!list->hiddev->open++)
288 if (list->hiddev->exist) {
Arnd Bergmannbd25f4d2010-07-11 15:34:05 +0200289 struct hid_device *hid = hiddev->hid;
Dmitry Torokhov9a835632017-06-06 23:59:32 -0700290 res = hid_hw_power(hid, PM_HINT_FULLON);
291 if (res < 0)
Jiri Kosina6cb4b042011-05-20 10:50:13 +0200292 goto bail_unlock;
Dmitry Torokhovd36b7d42017-06-06 23:59:31 -0700293 res = hid_hw_open(hid);
294 if (res < 0)
Dmitry Torokhov9a835632017-06-06 23:59:32 -0700295 goto bail_normal_power;
Oliver Neukum0361a282008-12-17 15:38:03 +0100296 }
Jiri Kosina6cb4b042011-05-20 10:50:13 +0200297 mutex_unlock(&hiddev->existancelock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 return 0;
Dmitry Torokhov9a835632017-06-06 23:59:32 -0700299bail_normal_power:
300 hid_hw_power(hid, PM_HINT_NORMAL);
Jiri Kosina6cb4b042011-05-20 10:50:13 +0200301bail_unlock:
302 mutex_unlock(&hiddev->existancelock);
Oliver Neukum07903402008-12-16 10:55:15 +0100303bail:
304 file->private_data = NULL;
Havard Skinnemoend4f0e4da2012-04-26 11:16:00 -0700305 vfree(list);
Oliver Neukum07903402008-12-16 10:55:15 +0100306 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307}
308
309/*
310 * "write" file op
311 */
312static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
313{
314 return -EINVAL;
315}
316
317/*
318 * "read" file op
319 */
320static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
321{
Johannes Weiner96fe2ab2009-03-10 22:44:01 +0100322 DEFINE_WAIT(wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 struct hiddev_list *list = file->private_data;
324 int event_size;
Oliver Neukum07903402008-12-16 10:55:15 +0100325 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
327 event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
328 sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
329
330 if (count < event_size)
331 return 0;
332
Oliver Neukum07903402008-12-16 10:55:15 +0100333 /* lock against other threads */
334 retval = mutex_lock_interruptible(&list->thread_lock);
335 if (retval)
336 return -ERESTARTSYS;
337
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 while (retval == 0) {
339 if (list->head == list->tail) {
Oliver Neukum07903402008-12-16 10:55:15 +0100340 prepare_to_wait(&list->hiddev->wait, &wait, TASK_INTERRUPTIBLE);
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500341
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 while (list->head == list->tail) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 if (signal_pending(current)) {
344 retval = -ERESTARTSYS;
345 break;
346 }
347 if (!list->hiddev->exist) {
348 retval = -EIO;
349 break;
350 }
Jiri Kosina13f19622012-11-28 00:10:44 +0100351 if (file->f_flags & O_NONBLOCK) {
352 retval = -EAGAIN;
353 break;
354 }
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500355
Oliver Neukum07903402008-12-16 10:55:15 +0100356 /* let O_NONBLOCK tasks run */
357 mutex_unlock(&list->thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 schedule();
Peter Waechtler06268b22011-04-28 20:53:58 +0200359 if (mutex_lock_interruptible(&list->thread_lock)) {
360 finish_wait(&list->hiddev->wait, &wait);
Oliver Neukum07903402008-12-16 10:55:15 +0100361 return -EINTR;
Peter Waechtler06268b22011-04-28 20:53:58 +0200362 }
Micon, David48d70552006-05-20 14:59:59 -0700363 set_current_state(TASK_INTERRUPTIBLE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 }
Oliver Neukum07903402008-12-16 10:55:15 +0100365 finish_wait(&list->hiddev->wait, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 }
368
Oliver Neukum07903402008-12-16 10:55:15 +0100369 if (retval) {
370 mutex_unlock(&list->thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 return retval;
Oliver Neukum07903402008-12-16 10:55:15 +0100372 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
374
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500375 while (list->head != list->tail &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 retval + event_size <= count) {
377 if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
Oliver Neukum07903402008-12-16 10:55:15 +0100378 if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 struct hiddev_event event;
Oliver Neukum07903402008-12-16 10:55:15 +0100380
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 event.hid = list->buffer[list->tail].usage_code;
382 event.value = list->buffer[list->tail].value;
Oliver Neukum07903402008-12-16 10:55:15 +0100383 if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) {
384 mutex_unlock(&list->thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 return -EFAULT;
Oliver Neukum07903402008-12-16 10:55:15 +0100386 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 retval += sizeof(struct hiddev_event);
388 }
389 } else {
390 if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
391 (list->flags & HIDDEV_FLAG_REPORT) != 0) {
Oliver Neukum07903402008-12-16 10:55:15 +0100392
393 if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) {
394 mutex_unlock(&list->thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 return -EFAULT;
Oliver Neukum07903402008-12-16 10:55:15 +0100396 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 retval += sizeof(struct hiddev_usage_ref);
398 }
399 }
400 list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
401 }
402
403 }
Oliver Neukum07903402008-12-16 10:55:15 +0100404 mutex_unlock(&list->thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
406 return retval;
407}
408
409/*
410 * "poll" file op
411 * No kernel lock - fine
412 */
Al Viroafc9a422017-07-03 06:39:46 -0400413static __poll_t hiddev_poll(struct file *file, poll_table *wait)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414{
415 struct hiddev_list *list = file->private_data;
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400416
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 poll_wait(file, &list->hiddev->wait, wait);
418 if (list->head != list->tail)
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800419 return EPOLLIN | EPOLLRDNORM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 if (!list->hiddev->exist)
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800421 return EPOLLERR | EPOLLHUP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 return 0;
423}
424
425/*
426 * "ioctl" file op
427 */
Jean Delvarecf2a2992008-03-03 11:48:43 +0100428static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
429{
430 struct hid_device *hid = hiddev->hid;
431 struct hiddev_report_info rinfo;
432 struct hiddev_usage_ref_multi *uref_multi = NULL;
433 struct hiddev_usage_ref *uref;
434 struct hid_report *report;
435 struct hid_field *field;
436 int i;
437
438 uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
439 if (!uref_multi)
440 return -ENOMEM;
441 uref = &uref_multi->uref;
442 if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
443 if (copy_from_user(uref_multi, user_arg,
444 sizeof(*uref_multi)))
445 goto fault;
446 } else {
447 if (copy_from_user(uref, user_arg, sizeof(*uref)))
448 goto fault;
449 }
450
451 switch (cmd) {
452 case HIDIOCGUCODE:
453 rinfo.report_type = uref->report_type;
454 rinfo.report_id = uref->report_id;
455 if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
456 goto inval;
457
458 if (uref->field_index >= report->maxfield)
459 goto inval;
Gustavo A. R. Silva4f652452018-06-29 17:08:44 -0500460 uref->field_index = array_index_nospec(uref->field_index,
461 report->maxfield);
Jean Delvarecf2a2992008-03-03 11:48:43 +0100462
463 field = report->field[uref->field_index];
464 if (uref->usage_index >= field->maxusage)
465 goto inval;
Gustavo A. R. Silva4f652452018-06-29 17:08:44 -0500466 uref->usage_index = array_index_nospec(uref->usage_index,
467 field->maxusage);
Jean Delvarecf2a2992008-03-03 11:48:43 +0100468
469 uref->usage_code = field->usage[uref->usage_index].hid;
470
471 if (copy_to_user(user_arg, uref, sizeof(*uref)))
472 goto fault;
473
Jiri Slabyeb991082008-10-23 01:47:34 +0200474 goto goodreturn;
Jean Delvarecf2a2992008-03-03 11:48:43 +0100475
476 default:
477 if (cmd != HIDIOCGUSAGE &&
478 cmd != HIDIOCGUSAGES &&
479 uref->report_type == HID_REPORT_TYPE_INPUT)
480 goto inval;
481
482 if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
483 field = hiddev_lookup_usage(hid, uref);
484 if (field == NULL)
485 goto inval;
486 } else {
487 rinfo.report_type = uref->report_type;
488 rinfo.report_id = uref->report_id;
489 if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
490 goto inval;
491
492 if (uref->field_index >= report->maxfield)
493 goto inval;
Gustavo A. R. Silva4f652452018-06-29 17:08:44 -0500494 uref->field_index = array_index_nospec(uref->field_index,
495 report->maxfield);
Jean Delvarecf2a2992008-03-03 11:48:43 +0100496
497 field = report->field[uref->field_index];
498
499 if (cmd == HIDIOCGCOLLECTIONINDEX) {
500 if (uref->usage_index >= field->maxusage)
501 goto inval;
Breno Leitaof1127432018-10-19 17:01:33 -0300502 uref->usage_index =
503 array_index_nospec(uref->usage_index,
504 field->maxusage);
Jean Delvarecf2a2992008-03-03 11:48:43 +0100505 } else if (uref->usage_index >= field->report_count)
506 goto inval;
Dan Carpenterac065bf2011-03-26 04:47:35 +0300507 }
Jean Delvarecf2a2992008-03-03 11:48:43 +0100508
Breno Leitaof1127432018-10-19 17:01:33 -0300509 if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
510 if (uref_multi->num_values > HID_MAX_MULTI_USAGES ||
511 uref->usage_index + uref_multi->num_values >
512 field->report_count)
513 goto inval;
514
515 uref->usage_index =
516 array_index_nospec(uref->usage_index,
517 field->report_count -
518 uref_multi->num_values);
519 }
Scott Bauer93a20012016-06-23 08:59:47 -0600520
Jean Delvarecf2a2992008-03-03 11:48:43 +0100521 switch (cmd) {
522 case HIDIOCGUSAGE:
523 uref->value = field->value[uref->usage_index];
524 if (copy_to_user(user_arg, uref, sizeof(*uref)))
525 goto fault;
526 goto goodreturn;
527
528 case HIDIOCSUSAGE:
529 field->value[uref->usage_index] = uref->value;
530 goto goodreturn;
531
532 case HIDIOCGCOLLECTIONINDEX:
Jiri Slaby48594842009-06-19 23:24:11 +0200533 i = field->usage[uref->usage_index].collection_index;
Jean Delvarecf2a2992008-03-03 11:48:43 +0100534 kfree(uref_multi);
Jiri Slaby48594842009-06-19 23:24:11 +0200535 return i;
Jean Delvarecf2a2992008-03-03 11:48:43 +0100536 case HIDIOCGUSAGES:
537 for (i = 0; i < uref_multi->num_values; i++)
538 uref_multi->values[i] =
539 field->value[uref->usage_index + i];
540 if (copy_to_user(user_arg, uref_multi,
541 sizeof(*uref_multi)))
542 goto fault;
543 goto goodreturn;
544 case HIDIOCSUSAGES:
545 for (i = 0; i < uref_multi->num_values; i++)
546 field->value[uref->usage_index + i] =
547 uref_multi->values[i];
548 goto goodreturn;
549 }
550
551goodreturn:
552 kfree(uref_multi);
553 return 0;
554fault:
555 kfree(uref_multi);
556 return -EFAULT;
557inval:
558 kfree(uref_multi);
559 return -EINVAL;
560 }
561}
562
563static noinline int hiddev_ioctl_string(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
564{
565 struct hid_device *hid = hiddev->hid;
566 struct usb_device *dev = hid_to_usb_dev(hid);
567 int idx, len;
568 char *buf;
569
570 if (get_user(idx, (int __user *)user_arg))
571 return -EFAULT;
572
573 if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
574 return -ENOMEM;
575
576 if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
577 kfree(buf);
578 return -EINVAL;
579 }
580
581 if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
582 kfree(buf);
583 return -EFAULT;
584 }
585
586 kfree(buf);
587
588 return len;
589}
590
Alan Cox7961df12008-05-26 11:25:20 +0200591static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592{
593 struct hiddev_list *list = file->private_data;
594 struct hiddev *hiddev = list->hiddev;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300595 struct hid_device *hid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 struct hiddev_collection_info cinfo;
597 struct hiddev_report_info rinfo;
598 struct hiddev_field_info finfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 struct hiddev_devinfo dinfo;
600 struct hid_report *report;
601 struct hid_field *field;
602 void __user *user_arg = (void __user *)arg;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300603 int i, r = -EINVAL;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300604
Alan Cox7961df12008-05-26 11:25:20 +0200605 /* Called without BKL by compat methods so no BKL taken */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300607 mutex_lock(&hiddev->existancelock);
608 if (!hiddev->exist) {
609 r = -ENODEV;
610 goto ret_unlock;
611 }
612
613 hid = hiddev->hid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
615 switch (cmd) {
616
617 case HIDIOCGVERSION:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300618 r = put_user(HID_VERSION, (int __user *)arg) ?
619 -EFAULT : 0;
620 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
622 case HIDIOCAPPLICATION:
Tushar Beherad339f612012-11-16 12:20:43 +0530623 if (arg >= hid->maxapplication)
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300624 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
626 for (i = 0; i < hid->maxcollection; i++)
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500627 if (hid->collection[i].type ==
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 HID_COLLECTION_APPLICATION && arg-- == 0)
629 break;
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500630
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300631 if (i < hid->maxcollection)
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300632 r = hid->collection[i].usage;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300633 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634
635 case HIDIOCGDEVINFO:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300636 {
637 struct usb_device *dev = hid_to_usb_dev(hid);
638 struct usbhid_device *usbhid = hid->driver_data;
639
Dan Carpenter9561f7f2011-09-23 09:21:13 +0300640 memset(&dinfo, 0, sizeof(dinfo));
641
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300642 dinfo.bustype = BUS_USB;
643 dinfo.busnum = dev->bus->busnum;
644 dinfo.devnum = dev->devnum;
645 dinfo.ifnum = usbhid->ifnum;
646 dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor);
647 dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
648 dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
649 dinfo.num_applications = hid->maxapplication;
650
651 r = copy_to_user(user_arg, &dinfo, sizeof(dinfo)) ?
652 -EFAULT : 0;
653 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300654 }
655
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 case HIDIOCGFLAG:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300657 r = put_user(list->flags, (int __user *)arg) ?
658 -EFAULT : 0;
659 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660
661 case HIDIOCSFLAG:
662 {
663 int newflags;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300664
665 if (get_user(newflags, (int __user *)arg)) {
666 r = -EFAULT;
667 break;
668 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
670 if ((newflags & ~HIDDEV_FLAGS) != 0 ||
671 ((newflags & HIDDEV_FLAG_REPORT) != 0 &&
672 (newflags & HIDDEV_FLAG_UREF) == 0))
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300673 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
675 list->flags = newflags;
676
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300677 r = 0;
678 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 }
680
681 case HIDIOCGSTRING:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300682 r = hiddev_ioctl_string(hiddev, cmd, user_arg);
683 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
685 case HIDIOCINITREPORT:
Jiri Kosina4916b3a2006-12-08 18:41:03 +0100686 usbhid_init_reports(hid);
Benjamin Tissoires91430592017-03-08 15:11:14 +0100687 hiddev->initialized = true;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300688 r = 0;
689 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
691 case HIDIOCGREPORT:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300692 if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
693 r = -EFAULT;
694 break;
695 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
697 if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300698 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300700 report = hiddev_lookup_report(hid, &rinfo);
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300701 if (report == NULL)
702 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300703
Benjamin Tissoiresd88142722013-02-25 11:31:46 +0100704 hid_hw_request(hid, report, HID_REQ_GET_REPORT);
Benjamin Tissoiresb7966a42013-02-25 11:31:47 +0100705 hid_hw_wait(hid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300707 r = 0;
708 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
710 case HIDIOCSREPORT:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300711 if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
712 r = -EFAULT;
713 break;
714 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715
716 if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300717 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300719 report = hiddev_lookup_report(hid, &rinfo);
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300720 if (report == NULL)
721 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300722
Benjamin Tissoiresd88142722013-02-25 11:31:46 +0100723 hid_hw_request(hid, report, HID_REQ_SET_REPORT);
Benjamin Tissoiresb7966a42013-02-25 11:31:47 +0100724 hid_hw_wait(hid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300726 r = 0;
727 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
729 case HIDIOCGREPORTINFO:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300730 if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
731 r = -EFAULT;
732 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300733 }
734
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300735 report = hiddev_lookup_report(hid, &rinfo);
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300736 if (report == NULL)
737 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
739 rinfo.num_fields = report->maxfield;
740
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300741 r = copy_to_user(user_arg, &rinfo, sizeof(rinfo)) ?
742 -EFAULT : 0;
743 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
745 case HIDIOCGFIELDINFO:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300746 if (copy_from_user(&finfo, user_arg, sizeof(finfo))) {
747 r = -EFAULT;
748 break;
749 }
750
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 rinfo.report_type = finfo.report_type;
752 rinfo.report_id = finfo.report_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300754 report = hiddev_lookup_report(hid, &rinfo);
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300755 if (report == NULL)
756 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300757
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300758 if (finfo.field_index >= report->maxfield)
759 break;
Gustavo A. R. Silva4f652452018-06-29 17:08:44 -0500760 finfo.field_index = array_index_nospec(finfo.field_index,
761 report->maxfield);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
763 field = report->field[finfo.field_index];
764 memset(&finfo, 0, sizeof(finfo));
765 finfo.report_type = rinfo.report_type;
766 finfo.report_id = rinfo.report_id;
767 finfo.field_index = field->report_count - 1;
768 finfo.maxusage = field->maxusage;
769 finfo.flags = field->flags;
770 finfo.physical = field->physical;
771 finfo.logical = field->logical;
772 finfo.application = field->application;
773 finfo.logical_minimum = field->logical_minimum;
774 finfo.logical_maximum = field->logical_maximum;
775 finfo.physical_minimum = field->physical_minimum;
776 finfo.physical_maximum = field->physical_maximum;
777 finfo.unit_exponent = field->unit_exponent;
778 finfo.unit = field->unit;
779
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300780 r = copy_to_user(user_arg, &finfo, sizeof(finfo)) ?
781 -EFAULT : 0;
782 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783
784 case HIDIOCGUCODE:
Jean Delvarecf2a2992008-03-03 11:48:43 +0100785 /* fall through */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 case HIDIOCGUSAGE:
787 case HIDIOCSUSAGE:
788 case HIDIOCGUSAGES:
789 case HIDIOCSUSAGES:
790 case HIDIOCGCOLLECTIONINDEX:
Benjamin Tissoires91430592017-03-08 15:11:14 +0100791 if (!hiddev->initialized) {
792 usbhid_init_reports(hid);
793 hiddev->initialized = true;
794 }
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300795 r = hiddev_ioctl_usage(hiddev, cmd, user_arg);
796 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797
798 case HIDIOCGCOLLECTIONINFO:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300799 if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) {
800 r = -EFAULT;
801 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300802 }
803
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300804 if (cinfo.index >= hid->maxcollection)
805 break;
Gustavo A. R. Silva4f652452018-06-29 17:08:44 -0500806 cinfo.index = array_index_nospec(cinfo.index,
807 hid->maxcollection);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
809 cinfo.type = hid->collection[cinfo.index].type;
810 cinfo.usage = hid->collection[cinfo.index].usage;
811 cinfo.level = hid->collection[cinfo.index].level;
812
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300813 r = copy_to_user(user_arg, &cinfo, sizeof(cinfo)) ?
814 -EFAULT : 0;
815 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
817 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300819 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
821 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
Daniel Mackdd2ed482011-05-15 18:07:42 +0200822 int len = strlen(hid->name) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 if (len > _IOC_SIZE(cmd))
824 len = _IOC_SIZE(cmd);
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300825 r = copy_to_user(user_arg, hid->name, len) ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 -EFAULT : len;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300827 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 }
829
830 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
Daniel Mackdd2ed482011-05-15 18:07:42 +0200831 int len = strlen(hid->phys) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 if (len > _IOC_SIZE(cmd))
833 len = _IOC_SIZE(cmd);
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300834 r = copy_to_user(user_arg, hid->phys, len) ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 -EFAULT : len;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300836 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 }
838 }
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300839
840ret_unlock:
841 mutex_unlock(&hiddev->existancelock);
842 return r;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843}
844
Philip Langdalebb6c8d82007-10-14 12:03:58 +0200845#ifdef CONFIG_COMPAT
846static long hiddev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
847{
Jiri Kosina88af45b2008-05-27 11:36:40 +0200848 return hiddev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
Philip Langdalebb6c8d82007-10-14 12:03:58 +0200849}
850#endif
851
Luiz Fernando N. Capitulino066202d2006-08-05 20:37:11 -0300852static const struct file_operations hiddev_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 .owner = THIS_MODULE,
854 .read = hiddev_read,
855 .write = hiddev_write,
856 .poll = hiddev_poll,
857 .open = hiddev_open,
858 .release = hiddev_release,
Alan Cox7961df12008-05-26 11:25:20 +0200859 .unlocked_ioctl = hiddev_ioctl,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 .fasync = hiddev_fasync,
Philip Langdalebb6c8d82007-10-14 12:03:58 +0200861#ifdef CONFIG_COMPAT
862 .compat_ioctl = hiddev_compat_ioctl,
863#endif
Arnd Bergmann6038f372010-08-15 18:52:59 +0200864 .llseek = noop_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865};
866
Al Viro2c9ede52011-07-23 20:24:48 -0400867static char *hiddev_devnode(struct device *dev, umode_t *mode)
Kay Sieversf7a386c2009-04-30 15:23:42 +0200868{
869 return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
870}
871
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872static struct usb_class_driver hiddev_class = {
Greg Kroah-Hartmand6e5bcf2005-06-20 21:15:16 -0700873 .name = "hiddev%d",
Kay Sieverse454cea2009-09-18 23:01:12 +0200874 .devnode = hiddev_devnode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 .fops = &hiddev_fops,
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500876 .minor_base = HIDDEV_MINOR_BASE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877};
878
879/*
880 * This is where hid.c calls us to connect a hid device to the hiddev driver
881 */
Jiri Slaby93c10132008-06-27 00:04:24 +0200882int hiddev_connect(struct hid_device *hid, unsigned int force)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883{
884 struct hiddev *hiddev;
Jiri Kosina4916b3a2006-12-08 18:41:03 +0100885 struct usbhid_device *usbhid = hid->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 int retval;
887
Jiri Slaby93c10132008-06-27 00:04:24 +0200888 if (!force) {
889 unsigned int i;
890 for (i = 0; i < hid->maxcollection; i++)
891 if (hid->collection[i].type ==
892 HID_COLLECTION_APPLICATION &&
893 !IS_INPUT_APPLICATION(hid->collection[i].usage))
894 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
Jiri Slaby93c10132008-06-27 00:04:24 +0200896 if (i == hid->maxcollection)
897 return -1;
898 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Oliver Neukumbbdb7da2006-01-06 20:54:29 +0100900 if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
Oliver Neukum07903402008-12-16 10:55:15 +0100903 init_waitqueue_head(&hiddev->wait);
904 INIT_LIST_HEAD(&hiddev->list);
905 spin_lock_init(&hiddev->list_lock);
906 mutex_init(&hiddev->existancelock);
Jiri Kosina76052742009-01-07 13:25:36 +0100907 hid->hiddev = hiddev;
Oliver Neukum07903402008-12-16 10:55:15 +0100908 hiddev->hid = hid;
909 hiddev->exist = 1;
Jiri Kosina4916b3a2006-12-08 18:41:03 +0100910 retval = usb_register_dev(usbhid->intf, &hiddev_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 if (retval) {
Joe Perches4291ee32010-12-09 19:29:03 -0800912 hid_err(hid, "Not able to get a minor for this device\n");
Jiri Kosina76052742009-01-07 13:25:36 +0100913 hid->hiddev = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 kfree(hiddev);
915 return -1;
916 }
Benjamin Tissoires91430592017-03-08 15:11:14 +0100917
918 /*
919 * If HID_QUIRK_NO_INIT_REPORTS is set, make sure we don't initialize
920 * the reports.
921 */
922 hiddev->initialized = hid->quirks & HID_QUIRK_NO_INIT_REPORTS;
923
Jaejoong Kim733aca92017-03-03 17:54:01 +0900924 hiddev->minor = usbhid->intf->minor;
925
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 return 0;
927}
928
929/*
930 * This is where hid.c calls us to disconnect a hiddev device from the
931 * corresponding hid device (usually because the usb device has disconnected)
932 */
933static struct usb_class_driver hiddev_class;
934void hiddev_disconnect(struct hid_device *hid)
935{
936 struct hiddev *hiddev = hid->hiddev;
Jiri Kosina4916b3a2006-12-08 18:41:03 +0100937 struct usbhid_device *usbhid = hid->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
Ming Leiba183112012-01-12 17:42:22 +0800939 usb_deregister_dev(usbhid->intf, &hiddev_class);
940
Oliver Neukum07903402008-12-16 10:55:15 +0100941 mutex_lock(&hiddev->existancelock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 hiddev->exist = 0;
943
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 if (hiddev->open) {
Jiri Kosina7f778972011-05-24 11:43:18 +0200945 mutex_unlock(&hiddev->existancelock);
Dmitry Torokhovd36b7d42017-06-06 23:59:31 -0700946 hid_hw_close(hiddev->hid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 wake_up_interruptible(&hiddev->wait);
948 } else {
Jiri Kosina7f778972011-05-24 11:43:18 +0200949 mutex_unlock(&hiddev->existancelock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 kfree(hiddev);
951 }
952}