blob: 4140dea693e90988d80bac6129458499fb2d03af [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
Dmitry Torokhov18a1b062019-12-17 14:50:21 -0800244static 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
276err_normal_power:
277 hid_hw_power(hiddev->hid, PM_HINT_NORMAL);
278err_drop_count:
279 hiddev->open--;
280 vfree(list);
281 return error;
282}
283
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284/*
285 * open file op
286 */
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400287static int hiddev_open(struct inode *inode, struct file *file)
288{
Arnd Bergmannbd25f4d2010-07-11 15:34:05 +0200289 struct usb_interface *intf;
Jiri Kosina9c9e54a2010-08-13 12:19:45 +0200290 struct hid_device *hid;
Arnd Bergmannbd25f4d2010-07-11 15:34:05 +0200291 struct hiddev *hiddev;
292 int res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
Guillaume Chazarain8fe294c2010-09-12 21:32:35 +0200294 intf = usbhid_find_interface(iminor(inode));
Arnd Bergmannbd25f4d2010-07-11 15:34:05 +0200295 if (!intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 return -ENODEV;
Dmitry Torokhov18a1b062019-12-17 14:50:21 -0800297
Jiri Kosina9c9e54a2010-08-13 12:19:45 +0200298 hid = usb_get_intfdata(intf);
299 hiddev = hid->hiddev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
Jiri Kosina6cb4b042011-05-20 10:50:13 +0200301 mutex_lock(&hiddev->existancelock);
Dmitry Torokhov18a1b062019-12-17 14:50:21 -0800302 res = hiddev->exist ? __hiddev_open(hiddev, file) : -ENODEV;
Jiri Kosina6cb4b042011-05-20 10:50:13 +0200303 mutex_unlock(&hiddev->existancelock);
Hillf Danton6d4472d2019-08-06 16:40:15 +0800304
Oliver Neukum07903402008-12-16 10:55:15 +0100305 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306}
307
308/*
309 * "write" file op
310 */
311static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
312{
313 return -EINVAL;
314}
315
316/*
317 * "read" file op
318 */
319static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
320{
Johannes Weiner96fe2ab2009-03-10 22:44:01 +0100321 DEFINE_WAIT(wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 struct hiddev_list *list = file->private_data;
323 int event_size;
Oliver Neukum07903402008-12-16 10:55:15 +0100324 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
326 event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
327 sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
328
329 if (count < event_size)
330 return 0;
331
Oliver Neukum07903402008-12-16 10:55:15 +0100332 /* lock against other threads */
333 retval = mutex_lock_interruptible(&list->thread_lock);
334 if (retval)
335 return -ERESTARTSYS;
336
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 while (retval == 0) {
338 if (list->head == list->tail) {
Oliver Neukum07903402008-12-16 10:55:15 +0100339 prepare_to_wait(&list->hiddev->wait, &wait, TASK_INTERRUPTIBLE);
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500340
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 while (list->head == list->tail) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 if (signal_pending(current)) {
343 retval = -ERESTARTSYS;
344 break;
345 }
346 if (!list->hiddev->exist) {
347 retval = -EIO;
348 break;
349 }
Jiri Kosina13f19622012-11-28 00:10:44 +0100350 if (file->f_flags & O_NONBLOCK) {
351 retval = -EAGAIN;
352 break;
353 }
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500354
Oliver Neukum07903402008-12-16 10:55:15 +0100355 /* let O_NONBLOCK tasks run */
356 mutex_unlock(&list->thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 schedule();
Peter Waechtler06268b22011-04-28 20:53:58 +0200358 if (mutex_lock_interruptible(&list->thread_lock)) {
359 finish_wait(&list->hiddev->wait, &wait);
Oliver Neukum07903402008-12-16 10:55:15 +0100360 return -EINTR;
Peter Waechtler06268b22011-04-28 20:53:58 +0200361 }
Micon, David48d70552006-05-20 14:59:59 -0700362 set_current_state(TASK_INTERRUPTIBLE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 }
Oliver Neukum07903402008-12-16 10:55:15 +0100364 finish_wait(&list->hiddev->wait, &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 }
367
Oliver Neukum07903402008-12-16 10:55:15 +0100368 if (retval) {
369 mutex_unlock(&list->thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 return retval;
Oliver Neukum07903402008-12-16 10:55:15 +0100371 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372
373
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500374 while (list->head != list->tail &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 retval + event_size <= count) {
376 if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
Oliver Neukum07903402008-12-16 10:55:15 +0100377 if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 struct hiddev_event event;
Oliver Neukum07903402008-12-16 10:55:15 +0100379
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 event.hid = list->buffer[list->tail].usage_code;
381 event.value = list->buffer[list->tail].value;
Oliver Neukum07903402008-12-16 10:55:15 +0100382 if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) {
383 mutex_unlock(&list->thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 return -EFAULT;
Oliver Neukum07903402008-12-16 10:55:15 +0100385 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 retval += sizeof(struct hiddev_event);
387 }
388 } else {
389 if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
390 (list->flags & HIDDEV_FLAG_REPORT) != 0) {
Oliver Neukum07903402008-12-16 10:55:15 +0100391
392 if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) {
393 mutex_unlock(&list->thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 return -EFAULT;
Oliver Neukum07903402008-12-16 10:55:15 +0100395 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 retval += sizeof(struct hiddev_usage_ref);
397 }
398 }
399 list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
400 }
401
402 }
Oliver Neukum07903402008-12-16 10:55:15 +0100403 mutex_unlock(&list->thread_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
405 return retval;
406}
407
408/*
409 * "poll" file op
410 * No kernel lock - fine
411 */
Al Viroafc9a422017-07-03 06:39:46 -0400412static __poll_t hiddev_poll(struct file *file, poll_table *wait)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413{
414 struct hiddev_list *list = file->private_data;
Dmitry Torokhov826d5982006-07-19 01:09:10 -0400415
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 poll_wait(file, &list->hiddev->wait, wait);
417 if (list->head != list->tail)
Fabian Hennekec801aff2019-07-18 22:50:58 +0200418 return EPOLLIN | EPOLLRDNORM | EPOLLOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 if (!list->hiddev->exist)
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800420 return EPOLLERR | EPOLLHUP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 return 0;
422}
423
424/*
425 * "ioctl" file op
426 */
Jean Delvarecf2a2992008-03-03 11:48:43 +0100427static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
428{
429 struct hid_device *hid = hiddev->hid;
430 struct hiddev_report_info rinfo;
431 struct hiddev_usage_ref_multi *uref_multi = NULL;
432 struct hiddev_usage_ref *uref;
433 struct hid_report *report;
434 struct hid_field *field;
435 int i;
436
437 uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
438 if (!uref_multi)
439 return -ENOMEM;
440 uref = &uref_multi->uref;
441 if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
442 if (copy_from_user(uref_multi, user_arg,
443 sizeof(*uref_multi)))
444 goto fault;
445 } else {
446 if (copy_from_user(uref, user_arg, sizeof(*uref)))
447 goto fault;
448 }
449
450 switch (cmd) {
451 case HIDIOCGUCODE:
452 rinfo.report_type = uref->report_type;
453 rinfo.report_id = uref->report_id;
454 if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
455 goto inval;
456
457 if (uref->field_index >= report->maxfield)
458 goto inval;
Gustavo A. R. Silva4f652452018-06-29 17:08:44 -0500459 uref->field_index = array_index_nospec(uref->field_index,
460 report->maxfield);
Jean Delvarecf2a2992008-03-03 11:48:43 +0100461
462 field = report->field[uref->field_index];
463 if (uref->usage_index >= field->maxusage)
464 goto inval;
Gustavo A. R. Silva4f652452018-06-29 17:08:44 -0500465 uref->usage_index = array_index_nospec(uref->usage_index,
466 field->maxusage);
Jean Delvarecf2a2992008-03-03 11:48:43 +0100467
468 uref->usage_code = field->usage[uref->usage_index].hid;
469
470 if (copy_to_user(user_arg, uref, sizeof(*uref)))
471 goto fault;
472
Jiri Slabyeb991082008-10-23 01:47:34 +0200473 goto goodreturn;
Jean Delvarecf2a2992008-03-03 11:48:43 +0100474
475 default:
476 if (cmd != HIDIOCGUSAGE &&
477 cmd != HIDIOCGUSAGES &&
478 uref->report_type == HID_REPORT_TYPE_INPUT)
479 goto inval;
480
481 if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
482 field = hiddev_lookup_usage(hid, uref);
483 if (field == NULL)
484 goto inval;
485 } else {
486 rinfo.report_type = uref->report_type;
487 rinfo.report_id = uref->report_id;
488 if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
489 goto inval;
490
491 if (uref->field_index >= report->maxfield)
492 goto inval;
Gustavo A. R. Silva4f652452018-06-29 17:08:44 -0500493 uref->field_index = array_index_nospec(uref->field_index,
494 report->maxfield);
Jean Delvarecf2a2992008-03-03 11:48:43 +0100495
496 field = report->field[uref->field_index];
497
498 if (cmd == HIDIOCGCOLLECTIONINDEX) {
499 if (uref->usage_index >= field->maxusage)
500 goto inval;
Breno Leitaof1127432018-10-19 17:01:33 -0300501 uref->usage_index =
502 array_index_nospec(uref->usage_index,
503 field->maxusage);
Jean Delvarecf2a2992008-03-03 11:48:43 +0100504 } else if (uref->usage_index >= field->report_count)
505 goto inval;
Dan Carpenterac065bf2011-03-26 04:47:35 +0300506 }
Jean Delvarecf2a2992008-03-03 11:48:43 +0100507
Breno Leitaof1127432018-10-19 17:01:33 -0300508 if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
509 if (uref_multi->num_values > HID_MAX_MULTI_USAGES ||
510 uref->usage_index + uref_multi->num_values >
511 field->report_count)
512 goto inval;
513
514 uref->usage_index =
515 array_index_nospec(uref->usage_index,
516 field->report_count -
517 uref_multi->num_values);
518 }
Scott Bauer93a20012016-06-23 08:59:47 -0600519
Jean Delvarecf2a2992008-03-03 11:48:43 +0100520 switch (cmd) {
521 case HIDIOCGUSAGE:
522 uref->value = field->value[uref->usage_index];
523 if (copy_to_user(user_arg, uref, sizeof(*uref)))
524 goto fault;
525 goto goodreturn;
526
527 case HIDIOCSUSAGE:
528 field->value[uref->usage_index] = uref->value;
529 goto goodreturn;
530
531 case HIDIOCGCOLLECTIONINDEX:
Jiri Slaby48594842009-06-19 23:24:11 +0200532 i = field->usage[uref->usage_index].collection_index;
Jean Delvarecf2a2992008-03-03 11:48:43 +0100533 kfree(uref_multi);
Jiri Slaby48594842009-06-19 23:24:11 +0200534 return i;
Jean Delvarecf2a2992008-03-03 11:48:43 +0100535 case HIDIOCGUSAGES:
536 for (i = 0; i < uref_multi->num_values; i++)
537 uref_multi->values[i] =
538 field->value[uref->usage_index + i];
539 if (copy_to_user(user_arg, uref_multi,
540 sizeof(*uref_multi)))
541 goto fault;
542 goto goodreturn;
543 case HIDIOCSUSAGES:
544 for (i = 0; i < uref_multi->num_values; i++)
545 field->value[uref->usage_index + i] =
546 uref_multi->values[i];
547 goto goodreturn;
548 }
549
550goodreturn:
551 kfree(uref_multi);
552 return 0;
553fault:
554 kfree(uref_multi);
555 return -EFAULT;
556inval:
557 kfree(uref_multi);
558 return -EINVAL;
559 }
560}
561
562static noinline int hiddev_ioctl_string(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
563{
564 struct hid_device *hid = hiddev->hid;
565 struct usb_device *dev = hid_to_usb_dev(hid);
566 int idx, len;
567 char *buf;
568
569 if (get_user(idx, (int __user *)user_arg))
570 return -EFAULT;
571
572 if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
573 return -ENOMEM;
574
575 if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
576 kfree(buf);
577 return -EINVAL;
578 }
579
580 if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
581 kfree(buf);
582 return -EFAULT;
583 }
584
585 kfree(buf);
586
587 return len;
588}
589
Alan Cox7961df12008-05-26 11:25:20 +0200590static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591{
592 struct hiddev_list *list = file->private_data;
593 struct hiddev *hiddev = list->hiddev;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300594 struct hid_device *hid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 struct hiddev_collection_info cinfo;
596 struct hiddev_report_info rinfo;
597 struct hiddev_field_info finfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 struct hiddev_devinfo dinfo;
599 struct hid_report *report;
600 struct hid_field *field;
601 void __user *user_arg = (void __user *)arg;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300602 int i, r = -EINVAL;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300603
Alan Cox7961df12008-05-26 11:25:20 +0200604 /* Called without BKL by compat methods so no BKL taken */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300606 mutex_lock(&hiddev->existancelock);
607 if (!hiddev->exist) {
608 r = -ENODEV;
609 goto ret_unlock;
610 }
611
612 hid = hiddev->hid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
614 switch (cmd) {
615
616 case HIDIOCGVERSION:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300617 r = put_user(HID_VERSION, (int __user *)arg) ?
618 -EFAULT : 0;
619 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620
621 case HIDIOCAPPLICATION:
Tushar Beherad339f612012-11-16 12:20:43 +0530622 if (arg >= hid->maxapplication)
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300623 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
625 for (i = 0; i < hid->maxcollection; i++)
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500626 if (hid->collection[i].type ==
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 HID_COLLECTION_APPLICATION && arg-- == 0)
628 break;
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500629
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300630 if (i < hid->maxcollection)
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300631 r = hid->collection[i].usage;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300632 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
634 case HIDIOCGDEVINFO:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300635 {
636 struct usb_device *dev = hid_to_usb_dev(hid);
637 struct usbhid_device *usbhid = hid->driver_data;
638
Dan Carpenter9561f7f2011-09-23 09:21:13 +0300639 memset(&dinfo, 0, sizeof(dinfo));
640
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300641 dinfo.bustype = BUS_USB;
642 dinfo.busnum = dev->bus->busnum;
643 dinfo.devnum = dev->devnum;
644 dinfo.ifnum = usbhid->ifnum;
645 dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor);
646 dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
647 dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
648 dinfo.num_applications = hid->maxapplication;
649
650 r = copy_to_user(user_arg, &dinfo, sizeof(dinfo)) ?
651 -EFAULT : 0;
652 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300653 }
654
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 case HIDIOCGFLAG:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300656 r = put_user(list->flags, (int __user *)arg) ?
657 -EFAULT : 0;
658 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659
660 case HIDIOCSFLAG:
661 {
662 int newflags;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300663
664 if (get_user(newflags, (int __user *)arg)) {
665 r = -EFAULT;
666 break;
667 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
669 if ((newflags & ~HIDDEV_FLAGS) != 0 ||
670 ((newflags & HIDDEV_FLAG_REPORT) != 0 &&
671 (newflags & HIDDEV_FLAG_UREF) == 0))
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300672 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
674 list->flags = newflags;
675
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300676 r = 0;
677 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 }
679
680 case HIDIOCGSTRING:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300681 r = hiddev_ioctl_string(hiddev, cmd, user_arg);
682 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683
684 case HIDIOCINITREPORT:
Jiri Kosina4916b3a2006-12-08 18:41:03 +0100685 usbhid_init_reports(hid);
Benjamin Tissoires91430592017-03-08 15:11:14 +0100686 hiddev->initialized = true;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300687 r = 0;
688 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689
690 case HIDIOCGREPORT:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300691 if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
692 r = -EFAULT;
693 break;
694 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
696 if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300697 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300699 report = hiddev_lookup_report(hid, &rinfo);
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300700 if (report == NULL)
701 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300702
Benjamin Tissoiresd88142722013-02-25 11:31:46 +0100703 hid_hw_request(hid, report, HID_REQ_GET_REPORT);
Benjamin Tissoiresb7966a42013-02-25 11:31:47 +0100704 hid_hw_wait(hid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300706 r = 0;
707 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
709 case HIDIOCSREPORT:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300710 if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
711 r = -EFAULT;
712 break;
713 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
715 if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300716 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300718 report = hiddev_lookup_report(hid, &rinfo);
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300719 if (report == NULL)
720 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300721
Benjamin Tissoiresd88142722013-02-25 11:31:46 +0100722 hid_hw_request(hid, report, HID_REQ_SET_REPORT);
Benjamin Tissoiresb7966a42013-02-25 11:31:47 +0100723 hid_hw_wait(hid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300725 r = 0;
726 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
728 case HIDIOCGREPORTINFO:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300729 if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
730 r = -EFAULT;
731 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300732 }
733
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300734 report = hiddev_lookup_report(hid, &rinfo);
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300735 if (report == NULL)
736 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
738 rinfo.num_fields = report->maxfield;
739
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300740 r = copy_to_user(user_arg, &rinfo, sizeof(rinfo)) ?
741 -EFAULT : 0;
742 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
744 case HIDIOCGFIELDINFO:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300745 if (copy_from_user(&finfo, user_arg, sizeof(finfo))) {
746 r = -EFAULT;
747 break;
748 }
749
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 rinfo.report_type = finfo.report_type;
751 rinfo.report_id = finfo.report_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300753 report = hiddev_lookup_report(hid, &rinfo);
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300754 if (report == NULL)
755 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300756
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300757 if (finfo.field_index >= report->maxfield)
758 break;
Gustavo A. R. Silva4f652452018-06-29 17:08:44 -0500759 finfo.field_index = array_index_nospec(finfo.field_index,
760 report->maxfield);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
762 field = report->field[finfo.field_index];
763 memset(&finfo, 0, sizeof(finfo));
764 finfo.report_type = rinfo.report_type;
765 finfo.report_id = rinfo.report_id;
766 finfo.field_index = field->report_count - 1;
767 finfo.maxusage = field->maxusage;
768 finfo.flags = field->flags;
769 finfo.physical = field->physical;
770 finfo.logical = field->logical;
771 finfo.application = field->application;
772 finfo.logical_minimum = field->logical_minimum;
773 finfo.logical_maximum = field->logical_maximum;
774 finfo.physical_minimum = field->physical_minimum;
775 finfo.physical_maximum = field->physical_maximum;
776 finfo.unit_exponent = field->unit_exponent;
777 finfo.unit = field->unit;
778
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300779 r = copy_to_user(user_arg, &finfo, sizeof(finfo)) ?
780 -EFAULT : 0;
781 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
783 case HIDIOCGUCODE:
Jean Delvarecf2a2992008-03-03 11:48:43 +0100784 /* fall through */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 case HIDIOCGUSAGE:
786 case HIDIOCSUSAGE:
787 case HIDIOCGUSAGES:
788 case HIDIOCSUSAGES:
789 case HIDIOCGCOLLECTIONINDEX:
Benjamin Tissoires91430592017-03-08 15:11:14 +0100790 if (!hiddev->initialized) {
791 usbhid_init_reports(hid);
792 hiddev->initialized = true;
793 }
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300794 r = hiddev_ioctl_usage(hiddev, cmd, user_arg);
795 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
797 case HIDIOCGCOLLECTIONINFO:
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300798 if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) {
799 r = -EFAULT;
800 break;
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300801 }
802
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300803 if (cinfo.index >= hid->maxcollection)
804 break;
Gustavo A. R. Silva4f652452018-06-29 17:08:44 -0500805 cinfo.index = array_index_nospec(cinfo.index,
806 hid->maxcollection);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807
808 cinfo.type = hid->collection[cinfo.index].type;
809 cinfo.usage = hid->collection[cinfo.index].usage;
810 cinfo.level = hid->collection[cinfo.index].level;
811
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300812 r = copy_to_user(user_arg, &cinfo, sizeof(cinfo)) ?
813 -EFAULT : 0;
814 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
816 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300818 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
820 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
Daniel Mackdd2ed482011-05-15 18:07:42 +0200821 int len = strlen(hid->name) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 if (len > _IOC_SIZE(cmd))
823 len = _IOC_SIZE(cmd);
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300824 r = copy_to_user(user_arg, hid->name, len) ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 -EFAULT : len;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300826 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 }
828
829 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
Daniel Mackdd2ed482011-05-15 18:07:42 +0200830 int len = strlen(hid->phys) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 if (len > _IOC_SIZE(cmd))
832 len = _IOC_SIZE(cmd);
Valentine Barshak1a8e8fa2010-12-06 17:51:41 +0300833 r = copy_to_user(user_arg, hid->phys, len) ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 -EFAULT : len;
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300835 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 }
837 }
Valentine Barshak33d6eb52010-12-06 18:16:11 +0300838
839ret_unlock:
840 mutex_unlock(&hiddev->existancelock);
841 return r;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842}
843
Luiz Fernando N. Capitulino066202d2006-08-05 20:37:11 -0300844static const struct file_operations hiddev_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 .owner = THIS_MODULE,
846 .read = hiddev_read,
847 .write = hiddev_write,
848 .poll = hiddev_poll,
849 .open = hiddev_open,
850 .release = hiddev_release,
Alan Cox7961df12008-05-26 11:25:20 +0200851 .unlocked_ioctl = hiddev_ioctl,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 .fasync = hiddev_fasync,
Arnd Bergmann407e9ef2018-09-11 17:23:00 +0200853 .compat_ioctl = compat_ptr_ioctl,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200854 .llseek = noop_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855};
856
Al Viro2c9ede52011-07-23 20:24:48 -0400857static char *hiddev_devnode(struct device *dev, umode_t *mode)
Kay Sieversf7a386c2009-04-30 15:23:42 +0200858{
859 return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
860}
861
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862static struct usb_class_driver hiddev_class = {
Greg Kroah-Hartmand6e5bcf2005-06-20 21:15:16 -0700863 .name = "hiddev%d",
Kay Sieverse454cea2009-09-18 23:01:12 +0200864 .devnode = hiddev_devnode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 .fops = &hiddev_fops,
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500866 .minor_base = HIDDEV_MINOR_BASE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867};
868
869/*
870 * This is where hid.c calls us to connect a hid device to the hiddev driver
871 */
Jiri Slaby93c10132008-06-27 00:04:24 +0200872int hiddev_connect(struct hid_device *hid, unsigned int force)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873{
874 struct hiddev *hiddev;
Jiri Kosina4916b3a2006-12-08 18:41:03 +0100875 struct usbhid_device *usbhid = hid->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 int retval;
877
Jiri Slaby93c10132008-06-27 00:04:24 +0200878 if (!force) {
879 unsigned int i;
880 for (i = 0; i < hid->maxcollection; i++)
881 if (hid->collection[i].type ==
882 HID_COLLECTION_APPLICATION &&
883 !IS_INPUT_APPLICATION(hid->collection[i].usage))
884 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885
Jiri Slaby93c10132008-06-27 00:04:24 +0200886 if (i == hid->maxcollection)
887 return -1;
888 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
Oliver Neukumbbdb7da2006-01-06 20:54:29 +0100890 if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892
Oliver Neukum07903402008-12-16 10:55:15 +0100893 init_waitqueue_head(&hiddev->wait);
894 INIT_LIST_HEAD(&hiddev->list);
895 spin_lock_init(&hiddev->list_lock);
896 mutex_init(&hiddev->existancelock);
Jiri Kosina76052742009-01-07 13:25:36 +0100897 hid->hiddev = hiddev;
Oliver Neukum07903402008-12-16 10:55:15 +0100898 hiddev->hid = hid;
899 hiddev->exist = 1;
Jiri Kosina4916b3a2006-12-08 18:41:03 +0100900 retval = usb_register_dev(usbhid->intf, &hiddev_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 if (retval) {
Joe Perches4291ee32010-12-09 19:29:03 -0800902 hid_err(hid, "Not able to get a minor for this device\n");
Jiri Kosina76052742009-01-07 13:25:36 +0100903 hid->hiddev = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 kfree(hiddev);
905 return -1;
906 }
Benjamin Tissoires91430592017-03-08 15:11:14 +0100907
908 /*
909 * If HID_QUIRK_NO_INIT_REPORTS is set, make sure we don't initialize
910 * the reports.
911 */
912 hiddev->initialized = hid->quirks & HID_QUIRK_NO_INIT_REPORTS;
913
Jaejoong Kim733aca92017-03-03 17:54:01 +0900914 hiddev->minor = usbhid->intf->minor;
915
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 return 0;
917}
918
919/*
920 * This is where hid.c calls us to disconnect a hiddev device from the
921 * corresponding hid device (usually because the usb device has disconnected)
922 */
923static struct usb_class_driver hiddev_class;
924void hiddev_disconnect(struct hid_device *hid)
925{
926 struct hiddev *hiddev = hid->hiddev;
Jiri Kosina4916b3a2006-12-08 18:41:03 +0100927 struct usbhid_device *usbhid = hid->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
Ming Leiba183112012-01-12 17:42:22 +0800929 usb_deregister_dev(usbhid->intf, &hiddev_class);
930
Oliver Neukum07903402008-12-16 10:55:15 +0100931 mutex_lock(&hiddev->existancelock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 hiddev->exist = 0;
933
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 if (hiddev->open) {
Dmitry Torokhovd36b7d42017-06-06 23:59:31 -0700935 hid_hw_close(hiddev->hid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 wake_up_interruptible(&hiddev->wait);
dan.carpenter@oracle.com5c02c442020-01-15 20:46:28 +0300937 mutex_unlock(&hiddev->existancelock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 } else {
Jiri Kosina7f778972011-05-24 11:43:18 +0200939 mutex_unlock(&hiddev->existancelock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 kfree(hiddev);
941 }
942}