11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
26db3dfefSJiri Kosina /*
36db3dfefSJiri Kosina * Copyright (c) 2001 Paul Stewart
46db3dfefSJiri Kosina * Copyright (c) 2001 Vojtech Pavlik
56db3dfefSJiri Kosina *
66db3dfefSJiri Kosina * HID char devices, giving access to raw HID device events.
76db3dfefSJiri Kosina */
86db3dfefSJiri Kosina
96db3dfefSJiri Kosina /*
106db3dfefSJiri Kosina *
116db3dfefSJiri Kosina * Should you need to contact me, the author, you can do so either by
126db3dfefSJiri Kosina * e-mail - mail your message to Paul Stewart <stewart@wetlogic.net>
136db3dfefSJiri Kosina */
146db3dfefSJiri Kosina
156db3dfefSJiri Kosina #include <linux/poll.h>
166db3dfefSJiri Kosina #include <linux/slab.h>
17174cd4b1SIngo Molnar #include <linux/sched/signal.h>
186db3dfefSJiri Kosina #include <linux/module.h>
196db3dfefSJiri Kosina #include <linux/init.h>
206db3dfefSJiri Kosina #include <linux/input.h>
216db3dfefSJiri Kosina #include <linux/usb.h>
226db3dfefSJiri Kosina #include <linux/hid.h>
236db3dfefSJiri Kosina #include <linux/hiddev.h>
24bb6c8d8fSPhilip Langdale #include <linux/compat.h>
25d4f0e4daSHavard Skinnemoen #include <linux/vmalloc.h>
264f65245fSGustavo A. R. Silva #include <linux/nospec.h>
276db3dfefSJiri Kosina #include "usbhid.h"
286db3dfefSJiri Kosina
296db3dfefSJiri Kosina #ifdef CONFIG_USB_DYNAMIC_MINORS
306db3dfefSJiri Kosina #define HIDDEV_MINOR_BASE 0
316db3dfefSJiri Kosina #define HIDDEV_MINORS 256
326db3dfefSJiri Kosina #else
336db3dfefSJiri Kosina #define HIDDEV_MINOR_BASE 96
346db3dfefSJiri Kosina #define HIDDEV_MINORS 16
356db3dfefSJiri Kosina #endif
36affbb8c6SJiri Kosina #define HIDDEV_BUFFER_SIZE 2048
376db3dfefSJiri Kosina
386db3dfefSJiri Kosina struct hiddev_list {
396db3dfefSJiri Kosina struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
406db3dfefSJiri Kosina int head;
416db3dfefSJiri Kosina int tail;
426db3dfefSJiri Kosina unsigned flags;
436db3dfefSJiri Kosina struct fasync_struct *fasync;
446db3dfefSJiri Kosina struct hiddev *hiddev;
456db3dfefSJiri Kosina struct list_head node;
4607903407SOliver Neukum struct mutex thread_lock;
476db3dfefSJiri Kosina };
486db3dfefSJiri Kosina
496db3dfefSJiri Kosina /*
506db3dfefSJiri Kosina * Find a report, given the report's type and ID. The ID can be specified
516db3dfefSJiri Kosina * indirectly by REPORT_ID_FIRST (which returns the first report of the given
526db3dfefSJiri Kosina * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the
536db3dfefSJiri Kosina * given type which follows old_id.
546db3dfefSJiri Kosina */
556db3dfefSJiri Kosina static struct hid_report *
hiddev_lookup_report(struct hid_device * hid,struct hiddev_report_info * rinfo)566db3dfefSJiri Kosina hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
576db3dfefSJiri Kosina {
586db3dfefSJiri Kosina unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
596db3dfefSJiri Kosina unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK;
606db3dfefSJiri Kosina struct hid_report_enum *report_enum;
616db3dfefSJiri Kosina struct hid_report *report;
626db3dfefSJiri Kosina struct list_head *list;
636db3dfefSJiri Kosina
646db3dfefSJiri Kosina if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
656db3dfefSJiri Kosina rinfo->report_type > HID_REPORT_TYPE_MAX)
666db3dfefSJiri Kosina return NULL;
676db3dfefSJiri Kosina
686db3dfefSJiri Kosina report_enum = hid->report_enum +
696db3dfefSJiri Kosina (rinfo->report_type - HID_REPORT_TYPE_MIN);
706db3dfefSJiri Kosina
716db3dfefSJiri Kosina switch (flags) {
726db3dfefSJiri Kosina case 0: /* Nothing to do -- report_id is already set correctly */
736db3dfefSJiri Kosina break;
746db3dfefSJiri Kosina
756db3dfefSJiri Kosina case HID_REPORT_ID_FIRST:
766db3dfefSJiri Kosina if (list_empty(&report_enum->report_list))
776db3dfefSJiri Kosina return NULL;
786db3dfefSJiri Kosina
796db3dfefSJiri Kosina list = report_enum->report_list.next;
806db3dfefSJiri Kosina report = list_entry(list, struct hid_report, list);
816db3dfefSJiri Kosina rinfo->report_id = report->id;
826db3dfefSJiri Kosina break;
836db3dfefSJiri Kosina
846db3dfefSJiri Kosina case HID_REPORT_ID_NEXT:
856db3dfefSJiri Kosina report = report_enum->report_id_hash[rid];
866db3dfefSJiri Kosina if (!report)
876db3dfefSJiri Kosina return NULL;
886db3dfefSJiri Kosina
896db3dfefSJiri Kosina list = report->list.next;
906db3dfefSJiri Kosina if (list == &report_enum->report_list)
916db3dfefSJiri Kosina return NULL;
926db3dfefSJiri Kosina
936db3dfefSJiri Kosina report = list_entry(list, struct hid_report, list);
946db3dfefSJiri Kosina rinfo->report_id = report->id;
956db3dfefSJiri Kosina break;
966db3dfefSJiri Kosina
976db3dfefSJiri Kosina default:
986db3dfefSJiri Kosina return NULL;
996db3dfefSJiri Kosina }
1006db3dfefSJiri Kosina
1016db3dfefSJiri Kosina return report_enum->report_id_hash[rinfo->report_id];
1026db3dfefSJiri Kosina }
1036db3dfefSJiri Kosina
1046db3dfefSJiri Kosina /*
1056db3dfefSJiri Kosina * Perform an exhaustive search of the report table for a usage, given its
1066db3dfefSJiri Kosina * type and usage id.
1076db3dfefSJiri Kosina */
1086db3dfefSJiri Kosina static struct hid_field *
hiddev_lookup_usage(struct hid_device * hid,struct hiddev_usage_ref * uref)1096db3dfefSJiri Kosina hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
1106db3dfefSJiri Kosina {
1116db3dfefSJiri Kosina int i, j;
1126db3dfefSJiri Kosina struct hid_report *report;
1136db3dfefSJiri Kosina struct hid_report_enum *report_enum;
1146db3dfefSJiri Kosina struct hid_field *field;
1156db3dfefSJiri Kosina
1166db3dfefSJiri Kosina if (uref->report_type < HID_REPORT_TYPE_MIN ||
1176db3dfefSJiri Kosina uref->report_type > HID_REPORT_TYPE_MAX)
1186db3dfefSJiri Kosina return NULL;
1196db3dfefSJiri Kosina
1206db3dfefSJiri Kosina report_enum = hid->report_enum +
1216db3dfefSJiri Kosina (uref->report_type - HID_REPORT_TYPE_MIN);
1226db3dfefSJiri Kosina
1236db3dfefSJiri Kosina list_for_each_entry(report, &report_enum->report_list, list) {
1246db3dfefSJiri Kosina for (i = 0; i < report->maxfield; i++) {
1256db3dfefSJiri Kosina field = report->field[i];
1266db3dfefSJiri Kosina for (j = 0; j < field->maxusage; j++) {
1276db3dfefSJiri Kosina if (field->usage[j].hid == uref->usage_code) {
1286db3dfefSJiri Kosina uref->report_id = report->id;
1296db3dfefSJiri Kosina uref->field_index = i;
1306db3dfefSJiri Kosina uref->usage_index = j;
1316db3dfefSJiri Kosina return field;
1326db3dfefSJiri Kosina }
1336db3dfefSJiri Kosina }
1346db3dfefSJiri Kosina }
1356db3dfefSJiri Kosina }
1366db3dfefSJiri Kosina
1376db3dfefSJiri Kosina return NULL;
1386db3dfefSJiri Kosina }
1396db3dfefSJiri Kosina
hiddev_send_event(struct hid_device * hid,struct hiddev_usage_ref * uref)1406db3dfefSJiri Kosina static void hiddev_send_event(struct hid_device *hid,
1416db3dfefSJiri Kosina struct hiddev_usage_ref *uref)
1426db3dfefSJiri Kosina {
1436db3dfefSJiri Kosina struct hiddev *hiddev = hid->hiddev;
1446db3dfefSJiri Kosina struct hiddev_list *list;
145cdcb44e8SJiri Kosina unsigned long flags;
1466db3dfefSJiri Kosina
147cdcb44e8SJiri Kosina spin_lock_irqsave(&hiddev->list_lock, flags);
1486db3dfefSJiri Kosina list_for_each_entry(list, &hiddev->list, node) {
1496db3dfefSJiri Kosina if (uref->field_index != HID_FIELD_INDEX_NONE ||
1506db3dfefSJiri Kosina (list->flags & HIDDEV_FLAG_REPORT) != 0) {
1516db3dfefSJiri Kosina list->buffer[list->head] = *uref;
1526db3dfefSJiri Kosina list->head = (list->head + 1) &
1536db3dfefSJiri Kosina (HIDDEV_BUFFER_SIZE - 1);
1546db3dfefSJiri Kosina kill_fasync(&list->fasync, SIGIO, POLL_IN);
1556db3dfefSJiri Kosina }
1566db3dfefSJiri Kosina }
157cdcb44e8SJiri Kosina spin_unlock_irqrestore(&hiddev->list_lock, flags);
1586db3dfefSJiri Kosina
1596db3dfefSJiri Kosina wake_up_interruptible(&hiddev->wait);
1606db3dfefSJiri Kosina }
1616db3dfefSJiri Kosina
1626db3dfefSJiri Kosina /*
1636db3dfefSJiri Kosina * This is where hid.c calls into hiddev to pass an event that occurred over
1646db3dfefSJiri Kosina * the interrupt pipe
1656db3dfefSJiri Kosina */
hiddev_hid_event(struct hid_device * hid,struct hid_field * field,struct hid_usage * usage,__s32 value)1666db3dfefSJiri Kosina void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
1676db3dfefSJiri Kosina struct hid_usage *usage, __s32 value)
1686db3dfefSJiri Kosina {
1696db3dfefSJiri Kosina unsigned type = field->report_type;
1706db3dfefSJiri Kosina struct hiddev_usage_ref uref;
1716db3dfefSJiri Kosina
1726db3dfefSJiri Kosina uref.report_type =
1736db3dfefSJiri Kosina (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
1746db3dfefSJiri Kosina ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
1756db3dfefSJiri Kosina ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
1766db3dfefSJiri Kosina uref.report_id = field->report->id;
1776db3dfefSJiri Kosina uref.field_index = field->index;
1786db3dfefSJiri Kosina uref.usage_index = (usage - field->usage);
1796db3dfefSJiri Kosina uref.usage_code = usage->hid;
1806db3dfefSJiri Kosina uref.value = value;
1816db3dfefSJiri Kosina
1826db3dfefSJiri Kosina hiddev_send_event(hid, &uref);
1836db3dfefSJiri Kosina }
1846db3dfefSJiri Kosina EXPORT_SYMBOL_GPL(hiddev_hid_event);
1856db3dfefSJiri Kosina
hiddev_report_event(struct hid_device * hid,struct hid_report * report)1866db3dfefSJiri Kosina void hiddev_report_event(struct hid_device *hid, struct hid_report *report)
1876db3dfefSJiri Kosina {
1886db3dfefSJiri Kosina unsigned type = report->type;
1896db3dfefSJiri Kosina struct hiddev_usage_ref uref;
1906db3dfefSJiri Kosina
1916db3dfefSJiri Kosina memset(&uref, 0, sizeof(uref));
1926db3dfefSJiri Kosina uref.report_type =
1936db3dfefSJiri Kosina (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
1946db3dfefSJiri Kosina ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
1956db3dfefSJiri Kosina ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
1966db3dfefSJiri Kosina uref.report_id = report->id;
1976db3dfefSJiri Kosina uref.field_index = HID_FIELD_INDEX_NONE;
1986db3dfefSJiri Kosina
1996db3dfefSJiri Kosina hiddev_send_event(hid, &uref);
2006db3dfefSJiri Kosina }
2016db3dfefSJiri Kosina
2026db3dfefSJiri Kosina /*
2036db3dfefSJiri Kosina * fasync file op
2046db3dfefSJiri Kosina */
hiddev_fasync(int fd,struct file * file,int on)2056db3dfefSJiri Kosina static int hiddev_fasync(int fd, struct file *file, int on)
2066db3dfefSJiri Kosina {
2076db3dfefSJiri Kosina struct hiddev_list *list = file->private_data;
2086db3dfefSJiri Kosina
20960aa4924SJonathan Corbet return fasync_helper(fd, file, on, &list->fasync);
2106db3dfefSJiri Kosina }
2116db3dfefSJiri Kosina
2126db3dfefSJiri Kosina
2136db3dfefSJiri Kosina /*
2146db3dfefSJiri Kosina * release file op
2156db3dfefSJiri Kosina */
hiddev_release(struct inode * inode,struct file * file)2166db3dfefSJiri Kosina static int hiddev_release(struct inode * inode, struct file * file)
2176db3dfefSJiri Kosina {
2186db3dfefSJiri Kosina struct hiddev_list *list = file->private_data;
219cdcb44e8SJiri Kosina unsigned long flags;
2206db3dfefSJiri Kosina
221cdcb44e8SJiri Kosina spin_lock_irqsave(&list->hiddev->list_lock, flags);
2226db3dfefSJiri Kosina list_del(&list->node);
223cdcb44e8SJiri Kosina spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
2246db3dfefSJiri Kosina
2256cb4b040SJiri Kosina mutex_lock(&list->hiddev->existancelock);
2266db3dfefSJiri Kosina if (!--list->hiddev->open) {
2270361a28dSOliver Neukum if (list->hiddev->exist) {
228d36b7d4cSDmitry Torokhov hid_hw_close(list->hiddev->hid);
2299a83563fSDmitry Torokhov hid_hw_power(list->hiddev->hid, PM_HINT_NORMAL);
2300361a28dSOliver Neukum } else {
2315c699d7dSDan Carpenter mutex_unlock(&list->hiddev->existancelock);
2326db3dfefSJiri Kosina kfree(list->hiddev);
233d4f0e4daSHavard Skinnemoen vfree(list);
2345c699d7dSDan Carpenter return 0;
2356db3dfefSJiri Kosina }
2360361a28dSOliver Neukum }
2376db3dfefSJiri Kosina
2386cb4b040SJiri Kosina mutex_unlock(&list->hiddev->existancelock);
239d4f0e4daSHavard Skinnemoen vfree(list);
2406db3dfefSJiri Kosina
2416db3dfefSJiri Kosina return 0;
2426db3dfefSJiri Kosina }
2436db3dfefSJiri Kosina
__hiddev_open(struct hiddev * hiddev,struct file * file)24418a1b06eSDmitry Torokhov static int __hiddev_open(struct hiddev *hiddev, struct file *file)
24518a1b06eSDmitry Torokhov {
24618a1b06eSDmitry Torokhov struct hiddev_list *list;
24718a1b06eSDmitry Torokhov int error;
24818a1b06eSDmitry Torokhov
24918a1b06eSDmitry Torokhov lockdep_assert_held(&hiddev->existancelock);
25018a1b06eSDmitry Torokhov
25118a1b06eSDmitry Torokhov list = vzalloc(sizeof(*list));
25218a1b06eSDmitry Torokhov if (!list)
25318a1b06eSDmitry Torokhov return -ENOMEM;
25418a1b06eSDmitry Torokhov
25518a1b06eSDmitry Torokhov mutex_init(&list->thread_lock);
25618a1b06eSDmitry Torokhov list->hiddev = hiddev;
25718a1b06eSDmitry Torokhov
25818a1b06eSDmitry Torokhov if (!hiddev->open++) {
25918a1b06eSDmitry Torokhov error = hid_hw_power(hiddev->hid, PM_HINT_FULLON);
26018a1b06eSDmitry Torokhov if (error < 0)
26118a1b06eSDmitry Torokhov goto err_drop_count;
26218a1b06eSDmitry Torokhov
26318a1b06eSDmitry Torokhov error = hid_hw_open(hiddev->hid);
26418a1b06eSDmitry Torokhov if (error < 0)
26518a1b06eSDmitry Torokhov goto err_normal_power;
26618a1b06eSDmitry Torokhov }
26718a1b06eSDmitry Torokhov
26818a1b06eSDmitry Torokhov spin_lock_irq(&hiddev->list_lock);
26918a1b06eSDmitry Torokhov list_add_tail(&list->node, &hiddev->list);
27018a1b06eSDmitry Torokhov spin_unlock_irq(&hiddev->list_lock);
27118a1b06eSDmitry Torokhov
27218a1b06eSDmitry Torokhov file->private_data = list;
27318a1b06eSDmitry Torokhov
27418a1b06eSDmitry Torokhov return 0;
27518a1b06eSDmitry Torokhov
27618a1b06eSDmitry Torokhov err_normal_power:
27718a1b06eSDmitry Torokhov hid_hw_power(hiddev->hid, PM_HINT_NORMAL);
27818a1b06eSDmitry Torokhov err_drop_count:
27918a1b06eSDmitry Torokhov hiddev->open--;
28018a1b06eSDmitry Torokhov vfree(list);
28118a1b06eSDmitry Torokhov return error;
28218a1b06eSDmitry Torokhov }
28318a1b06eSDmitry Torokhov
2846db3dfefSJiri Kosina /*
2856db3dfefSJiri Kosina * open file op
2866db3dfefSJiri Kosina */
hiddev_open(struct inode * inode,struct file * file)2876db3dfefSJiri Kosina static int hiddev_open(struct inode *inode, struct file *file)
2886db3dfefSJiri Kosina {
289bd25f4ddSArnd Bergmann struct usb_interface *intf;
2909c9e54a8SJiri Kosina struct hid_device *hid;
291bd25f4ddSArnd Bergmann struct hiddev *hiddev;
292bd25f4ddSArnd Bergmann int res;
2936db3dfefSJiri Kosina
2948fe294caSGuillaume Chazarain intf = usbhid_find_interface(iminor(inode));
295bd25f4ddSArnd Bergmann if (!intf)
2966db3dfefSJiri Kosina return -ENODEV;
29718a1b06eSDmitry Torokhov
2989c9e54a8SJiri Kosina hid = usb_get_intfdata(intf);
2999c9e54a8SJiri Kosina hiddev = hid->hiddev;
3006db3dfefSJiri Kosina
3016cb4b040SJiri Kosina mutex_lock(&hiddev->existancelock);
30218a1b06eSDmitry Torokhov res = hiddev->exist ? __hiddev_open(hiddev, file) : -ENODEV;
3036cb4b040SJiri Kosina mutex_unlock(&hiddev->existancelock);
3046d4472d7SHillf Danton
30507903407SOliver Neukum return res;
3066db3dfefSJiri Kosina }
3076db3dfefSJiri Kosina
3086db3dfefSJiri Kosina /*
3096db3dfefSJiri Kosina * "write" file op
3106db3dfefSJiri Kosina */
hiddev_write(struct file * file,const char __user * buffer,size_t count,loff_t * ppos)3116db3dfefSJiri Kosina static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
3126db3dfefSJiri Kosina {
3136db3dfefSJiri Kosina return -EINVAL;
3146db3dfefSJiri Kosina }
3156db3dfefSJiri Kosina
3166db3dfefSJiri Kosina /*
3176db3dfefSJiri Kosina * "read" file op
3186db3dfefSJiri Kosina */
hiddev_read(struct file * file,char __user * buffer,size_t count,loff_t * ppos)3196db3dfefSJiri Kosina static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
3206db3dfefSJiri Kosina {
32196fe2ab8SJohannes Weiner DEFINE_WAIT(wait);
3226db3dfefSJiri Kosina struct hiddev_list *list = file->private_data;
3236db3dfefSJiri Kosina int event_size;
32407903407SOliver Neukum int retval;
3256db3dfefSJiri Kosina
3266db3dfefSJiri Kosina event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
3276db3dfefSJiri Kosina sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
3286db3dfefSJiri Kosina
3296db3dfefSJiri Kosina if (count < event_size)
3306db3dfefSJiri Kosina return 0;
3316db3dfefSJiri Kosina
33207903407SOliver Neukum /* lock against other threads */
33307903407SOliver Neukum retval = mutex_lock_interruptible(&list->thread_lock);
33407903407SOliver Neukum if (retval)
33507903407SOliver Neukum return -ERESTARTSYS;
33607903407SOliver Neukum
3376db3dfefSJiri Kosina while (retval == 0) {
3386db3dfefSJiri Kosina if (list->head == list->tail) {
33907903407SOliver Neukum prepare_to_wait(&list->hiddev->wait, &wait, TASK_INTERRUPTIBLE);
3406db3dfefSJiri Kosina
3416db3dfefSJiri Kosina while (list->head == list->tail) {
3426db3dfefSJiri Kosina if (signal_pending(current)) {
3436db3dfefSJiri Kosina retval = -ERESTARTSYS;
3446db3dfefSJiri Kosina break;
3456db3dfefSJiri Kosina }
3466db3dfefSJiri Kosina if (!list->hiddev->exist) {
3476db3dfefSJiri Kosina retval = -EIO;
3486db3dfefSJiri Kosina break;
3496db3dfefSJiri Kosina }
35013f19624SJiri Kosina if (file->f_flags & O_NONBLOCK) {
35113f19624SJiri Kosina retval = -EAGAIN;
35213f19624SJiri Kosina break;
35313f19624SJiri Kosina }
3546db3dfefSJiri Kosina
35507903407SOliver Neukum /* let O_NONBLOCK tasks run */
35607903407SOliver Neukum mutex_unlock(&list->thread_lock);
3576db3dfefSJiri Kosina schedule();
35806268b2aSPeter Waechtler if (mutex_lock_interruptible(&list->thread_lock)) {
35906268b2aSPeter Waechtler finish_wait(&list->hiddev->wait, &wait);
36007903407SOliver Neukum return -EINTR;
36106268b2aSPeter Waechtler }
3626db3dfefSJiri Kosina set_current_state(TASK_INTERRUPTIBLE);
3636db3dfefSJiri Kosina }
36407903407SOliver Neukum finish_wait(&list->hiddev->wait, &wait);
3656db3dfefSJiri Kosina
3666db3dfefSJiri Kosina }
3676db3dfefSJiri Kosina
36807903407SOliver Neukum if (retval) {
36907903407SOliver Neukum mutex_unlock(&list->thread_lock);
3706db3dfefSJiri Kosina return retval;
37107903407SOliver Neukum }
3726db3dfefSJiri Kosina
3736db3dfefSJiri Kosina
3746db3dfefSJiri Kosina while (list->head != list->tail &&
3756db3dfefSJiri Kosina retval + event_size <= count) {
3766db3dfefSJiri Kosina if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
37707903407SOliver Neukum if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE) {
3786db3dfefSJiri Kosina struct hiddev_event event;
37907903407SOliver Neukum
3806db3dfefSJiri Kosina event.hid = list->buffer[list->tail].usage_code;
3816db3dfefSJiri Kosina event.value = list->buffer[list->tail].value;
38207903407SOliver Neukum if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) {
38307903407SOliver Neukum mutex_unlock(&list->thread_lock);
3846db3dfefSJiri Kosina return -EFAULT;
38507903407SOliver Neukum }
3866db3dfefSJiri Kosina retval += sizeof(struct hiddev_event);
3876db3dfefSJiri Kosina }
3886db3dfefSJiri Kosina } else {
3896db3dfefSJiri Kosina if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
3906db3dfefSJiri Kosina (list->flags & HIDDEV_FLAG_REPORT) != 0) {
39107903407SOliver Neukum
39207903407SOliver Neukum if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) {
39307903407SOliver Neukum mutex_unlock(&list->thread_lock);
3946db3dfefSJiri Kosina return -EFAULT;
39507903407SOliver Neukum }
3966db3dfefSJiri Kosina retval += sizeof(struct hiddev_usage_ref);
3976db3dfefSJiri Kosina }
3986db3dfefSJiri Kosina }
3996db3dfefSJiri Kosina list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
4006db3dfefSJiri Kosina }
4016db3dfefSJiri Kosina
4026db3dfefSJiri Kosina }
40307903407SOliver Neukum mutex_unlock(&list->thread_lock);
4046db3dfefSJiri Kosina
4056db3dfefSJiri Kosina return retval;
4066db3dfefSJiri Kosina }
4076db3dfefSJiri Kosina
4086db3dfefSJiri Kosina /*
4096db3dfefSJiri Kosina * "poll" file op
4106db3dfefSJiri Kosina * No kernel lock - fine
4116db3dfefSJiri Kosina */
hiddev_poll(struct file * file,poll_table * wait)412afc9a42bSAl Viro static __poll_t hiddev_poll(struct file *file, poll_table *wait)
4136db3dfefSJiri Kosina {
4146db3dfefSJiri Kosina struct hiddev_list *list = file->private_data;
4156db3dfefSJiri Kosina
4166db3dfefSJiri Kosina poll_wait(file, &list->hiddev->wait, wait);
4176db3dfefSJiri Kosina if (list->head != list->tail)
418c801aff1SFabian Henneke return EPOLLIN | EPOLLRDNORM | EPOLLOUT;
4196db3dfefSJiri Kosina if (!list->hiddev->exist)
420a9a08845SLinus Torvalds return EPOLLERR | EPOLLHUP;
4216db3dfefSJiri Kosina return 0;
4226db3dfefSJiri Kosina }
4236db3dfefSJiri Kosina
4246db3dfefSJiri Kosina /*
4256db3dfefSJiri Kosina * "ioctl" file op
4266db3dfefSJiri Kosina */
hiddev_ioctl_usage(struct hiddev * hiddev,unsigned int cmd,void __user * user_arg)427cf2a299eSJean Delvare static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
428cf2a299eSJean Delvare {
429cf2a299eSJean Delvare struct hid_device *hid = hiddev->hid;
430cf2a299eSJean Delvare struct hiddev_report_info rinfo;
431cf2a299eSJean Delvare struct hiddev_usage_ref_multi *uref_multi = NULL;
432cf2a299eSJean Delvare struct hiddev_usage_ref *uref;
433cf2a299eSJean Delvare struct hid_report *report;
434cf2a299eSJean Delvare struct hid_field *field;
435cf2a299eSJean Delvare int i;
436cf2a299eSJean Delvare
437cf2a299eSJean Delvare uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
438cf2a299eSJean Delvare if (!uref_multi)
439cf2a299eSJean Delvare return -ENOMEM;
440cf2a299eSJean Delvare uref = &uref_multi->uref;
441cf2a299eSJean Delvare if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
442cf2a299eSJean Delvare if (copy_from_user(uref_multi, user_arg,
443cf2a299eSJean Delvare sizeof(*uref_multi)))
444cf2a299eSJean Delvare goto fault;
445cf2a299eSJean Delvare } else {
446cf2a299eSJean Delvare if (copy_from_user(uref, user_arg, sizeof(*uref)))
447cf2a299eSJean Delvare goto fault;
448cf2a299eSJean Delvare }
449cf2a299eSJean Delvare
450cf2a299eSJean Delvare switch (cmd) {
451cf2a299eSJean Delvare case HIDIOCGUCODE:
452cf2a299eSJean Delvare rinfo.report_type = uref->report_type;
453cf2a299eSJean Delvare rinfo.report_id = uref->report_id;
454cf2a299eSJean Delvare if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
455cf2a299eSJean Delvare goto inval;
456cf2a299eSJean Delvare
457cf2a299eSJean Delvare if (uref->field_index >= report->maxfield)
458cf2a299eSJean Delvare goto inval;
4594f65245fSGustavo A. R. Silva uref->field_index = array_index_nospec(uref->field_index,
4604f65245fSGustavo A. R. Silva report->maxfield);
461cf2a299eSJean Delvare
462cf2a299eSJean Delvare field = report->field[uref->field_index];
463cf2a299eSJean Delvare if (uref->usage_index >= field->maxusage)
464cf2a299eSJean Delvare goto inval;
4654f65245fSGustavo A. R. Silva uref->usage_index = array_index_nospec(uref->usage_index,
4664f65245fSGustavo A. R. Silva field->maxusage);
467cf2a299eSJean Delvare
468cf2a299eSJean Delvare uref->usage_code = field->usage[uref->usage_index].hid;
469cf2a299eSJean Delvare
470cf2a299eSJean Delvare if (copy_to_user(user_arg, uref, sizeof(*uref)))
471cf2a299eSJean Delvare goto fault;
472cf2a299eSJean Delvare
473eb991089SJiri Slaby goto goodreturn;
474cf2a299eSJean Delvare
475cf2a299eSJean Delvare default:
476cf2a299eSJean Delvare if (cmd != HIDIOCGUSAGE &&
477cf2a299eSJean Delvare cmd != HIDIOCGUSAGES &&
478cf2a299eSJean Delvare uref->report_type == HID_REPORT_TYPE_INPUT)
479cf2a299eSJean Delvare goto inval;
480cf2a299eSJean Delvare
481cf2a299eSJean Delvare if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
482cf2a299eSJean Delvare field = hiddev_lookup_usage(hid, uref);
483cf2a299eSJean Delvare if (field == NULL)
484cf2a299eSJean Delvare goto inval;
485cf2a299eSJean Delvare } else {
486cf2a299eSJean Delvare rinfo.report_type = uref->report_type;
487cf2a299eSJean Delvare rinfo.report_id = uref->report_id;
488cf2a299eSJean Delvare if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
489cf2a299eSJean Delvare goto inval;
490cf2a299eSJean Delvare
491cf2a299eSJean Delvare if (uref->field_index >= report->maxfield)
492cf2a299eSJean Delvare goto inval;
4934f65245fSGustavo A. R. Silva uref->field_index = array_index_nospec(uref->field_index,
4944f65245fSGustavo A. R. Silva report->maxfield);
495cf2a299eSJean Delvare
496cf2a299eSJean Delvare field = report->field[uref->field_index];
497cf2a299eSJean Delvare
498cf2a299eSJean Delvare if (cmd == HIDIOCGCOLLECTIONINDEX) {
499cf2a299eSJean Delvare if (uref->usage_index >= field->maxusage)
500cf2a299eSJean Delvare goto inval;
501f1127439SBreno Leitao uref->usage_index =
502f1127439SBreno Leitao array_index_nospec(uref->usage_index,
503f1127439SBreno Leitao field->maxusage);
504cf2a299eSJean Delvare } else if (uref->usage_index >= field->report_count)
505cf2a299eSJean Delvare goto inval;
50693a2001bSScott Bauer }
507cf2a299eSJean Delvare
508f1127439SBreno Leitao if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
509f1127439SBreno Leitao if (uref_multi->num_values > HID_MAX_MULTI_USAGES ||
510f1127439SBreno Leitao uref->usage_index + uref_multi->num_values >
511f1127439SBreno Leitao field->report_count)
512cf2a299eSJean Delvare goto inval;
513cf2a299eSJean Delvare
514f1127439SBreno Leitao uref->usage_index =
515f1127439SBreno Leitao array_index_nospec(uref->usage_index,
516f1127439SBreno Leitao field->report_count -
517f1127439SBreno Leitao uref_multi->num_values);
518f1127439SBreno Leitao }
519f1127439SBreno Leitao
520cf2a299eSJean Delvare switch (cmd) {
521cf2a299eSJean Delvare case HIDIOCGUSAGE:
52225a097f5SPeilin Ye if (uref->usage_index >= field->report_count)
52325a097f5SPeilin Ye goto inval;
524cf2a299eSJean Delvare uref->value = field->value[uref->usage_index];
525cf2a299eSJean Delvare if (copy_to_user(user_arg, uref, sizeof(*uref)))
526cf2a299eSJean Delvare goto fault;
527cf2a299eSJean Delvare goto goodreturn;
528cf2a299eSJean Delvare
529cf2a299eSJean Delvare case HIDIOCSUSAGE:
53025a097f5SPeilin Ye if (uref->usage_index >= field->report_count)
53125a097f5SPeilin Ye goto inval;
532cf2a299eSJean Delvare field->value[uref->usage_index] = uref->value;
533cf2a299eSJean Delvare goto goodreturn;
534cf2a299eSJean Delvare
535cf2a299eSJean Delvare case HIDIOCGCOLLECTIONINDEX:
5364859484bSJiri Slaby i = field->usage[uref->usage_index].collection_index;
537cf2a299eSJean Delvare kfree(uref_multi);
5384859484bSJiri Slaby return i;
539cf2a299eSJean Delvare case HIDIOCGUSAGES:
540cf2a299eSJean Delvare for (i = 0; i < uref_multi->num_values; i++)
541cf2a299eSJean Delvare uref_multi->values[i] =
542cf2a299eSJean Delvare field->value[uref->usage_index + i];
543cf2a299eSJean Delvare if (copy_to_user(user_arg, uref_multi,
544cf2a299eSJean Delvare sizeof(*uref_multi)))
545cf2a299eSJean Delvare goto fault;
546cf2a299eSJean Delvare goto goodreturn;
547cf2a299eSJean Delvare case HIDIOCSUSAGES:
548cf2a299eSJean Delvare for (i = 0; i < uref_multi->num_values; i++)
549cf2a299eSJean Delvare field->value[uref->usage_index + i] =
550cf2a299eSJean Delvare uref_multi->values[i];
551cf2a299eSJean Delvare goto goodreturn;
552cf2a299eSJean Delvare }
553cf2a299eSJean Delvare
554cf2a299eSJean Delvare goodreturn:
555cf2a299eSJean Delvare kfree(uref_multi);
556cf2a299eSJean Delvare return 0;
557cf2a299eSJean Delvare fault:
558cf2a299eSJean Delvare kfree(uref_multi);
559cf2a299eSJean Delvare return -EFAULT;
560cf2a299eSJean Delvare inval:
561cf2a299eSJean Delvare kfree(uref_multi);
562cf2a299eSJean Delvare return -EINVAL;
563cf2a299eSJean Delvare }
564cf2a299eSJean Delvare }
565cf2a299eSJean Delvare
hiddev_ioctl_string(struct hiddev * hiddev,unsigned int cmd,void __user * user_arg)566cf2a299eSJean Delvare static noinline int hiddev_ioctl_string(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
567cf2a299eSJean Delvare {
568cf2a299eSJean Delvare struct hid_device *hid = hiddev->hid;
569cf2a299eSJean Delvare struct usb_device *dev = hid_to_usb_dev(hid);
570cf2a299eSJean Delvare int idx, len;
571cf2a299eSJean Delvare char *buf;
572cf2a299eSJean Delvare
573cf2a299eSJean Delvare if (get_user(idx, (int __user *)user_arg))
574cf2a299eSJean Delvare return -EFAULT;
575cf2a299eSJean Delvare
576cf2a299eSJean Delvare if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
577cf2a299eSJean Delvare return -ENOMEM;
578cf2a299eSJean Delvare
579cf2a299eSJean Delvare if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
580cf2a299eSJean Delvare kfree(buf);
581cf2a299eSJean Delvare return -EINVAL;
582cf2a299eSJean Delvare }
583cf2a299eSJean Delvare
584cf2a299eSJean Delvare if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
585cf2a299eSJean Delvare kfree(buf);
586cf2a299eSJean Delvare return -EFAULT;
587cf2a299eSJean Delvare }
588cf2a299eSJean Delvare
589cf2a299eSJean Delvare kfree(buf);
590cf2a299eSJean Delvare
591cf2a299eSJean Delvare return len;
592cf2a299eSJean Delvare }
593cf2a299eSJean Delvare
hiddev_ioctl(struct file * file,unsigned int cmd,unsigned long arg)5947961df16SAlan Cox static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
5956db3dfefSJiri Kosina {
5966db3dfefSJiri Kosina struct hiddev_list *list = file->private_data;
5976db3dfefSJiri Kosina struct hiddev *hiddev = list->hiddev;
5981a8e8fabSValentine Barshak struct hid_device *hid;
5996db3dfefSJiri Kosina struct hiddev_collection_info cinfo;
6006db3dfefSJiri Kosina struct hiddev_report_info rinfo;
6016db3dfefSJiri Kosina struct hiddev_field_info finfo;
6026db3dfefSJiri Kosina struct hiddev_devinfo dinfo;
6036db3dfefSJiri Kosina struct hid_report *report;
6046db3dfefSJiri Kosina struct hid_field *field;
6056db3dfefSJiri Kosina void __user *user_arg = (void __user *)arg;
60633d6eb57SValentine Barshak int i, r = -EINVAL;
6076db3dfefSJiri Kosina
6087961df16SAlan Cox /* Called without BKL by compat methods so no BKL taken */
6097961df16SAlan Cox
6101a8e8fabSValentine Barshak mutex_lock(&hiddev->existancelock);
6111a8e8fabSValentine Barshak if (!hiddev->exist) {
6121a8e8fabSValentine Barshak r = -ENODEV;
6131a8e8fabSValentine Barshak goto ret_unlock;
6141a8e8fabSValentine Barshak }
6151a8e8fabSValentine Barshak
6161a8e8fabSValentine Barshak hid = hiddev->hid;
61733d6eb57SValentine Barshak
61833d6eb57SValentine Barshak switch (cmd) {
61933d6eb57SValentine Barshak
62033d6eb57SValentine Barshak case HIDIOCGVERSION:
62133d6eb57SValentine Barshak r = put_user(HID_VERSION, (int __user *)arg) ?
62233d6eb57SValentine Barshak -EFAULT : 0;
62333d6eb57SValentine Barshak break;
62433d6eb57SValentine Barshak
62533d6eb57SValentine Barshak case HIDIOCAPPLICATION:
626d339f61dSTushar Behera if (arg >= hid->maxapplication)
62733d6eb57SValentine Barshak break;
6286db3dfefSJiri Kosina
6296db3dfefSJiri Kosina for (i = 0; i < hid->maxcollection; i++)
6306db3dfefSJiri Kosina if (hid->collection[i].type ==
6316db3dfefSJiri Kosina HID_COLLECTION_APPLICATION && arg-- == 0)
6326db3dfefSJiri Kosina break;
6336db3dfefSJiri Kosina
63433d6eb57SValentine Barshak if (i < hid->maxcollection)
6351a8e8fabSValentine Barshak r = hid->collection[i].usage;
63633d6eb57SValentine Barshak break;
6376db3dfefSJiri Kosina
6386db3dfefSJiri Kosina case HIDIOCGDEVINFO:
63933d6eb57SValentine Barshak {
64033d6eb57SValentine Barshak struct usb_device *dev = hid_to_usb_dev(hid);
64133d6eb57SValentine Barshak struct usbhid_device *usbhid = hid->driver_data;
6421a8e8fabSValentine Barshak
6439561f7faSDan Carpenter memset(&dinfo, 0, sizeof(dinfo));
6449561f7faSDan Carpenter
6456db3dfefSJiri Kosina dinfo.bustype = BUS_USB;
6466db3dfefSJiri Kosina dinfo.busnum = dev->bus->busnum;
6476db3dfefSJiri Kosina dinfo.devnum = dev->devnum;
6486db3dfefSJiri Kosina dinfo.ifnum = usbhid->ifnum;
6496db3dfefSJiri Kosina dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor);
6506db3dfefSJiri Kosina dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
6516db3dfefSJiri Kosina dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
6526db3dfefSJiri Kosina dinfo.num_applications = hid->maxapplication;
6531a8e8fabSValentine Barshak
65433d6eb57SValentine Barshak r = copy_to_user(user_arg, &dinfo, sizeof(dinfo)) ?
65533d6eb57SValentine Barshak -EFAULT : 0;
65633d6eb57SValentine Barshak break;
65733d6eb57SValentine Barshak }
6586db3dfefSJiri Kosina
6596db3dfefSJiri Kosina case HIDIOCGFLAG:
66033d6eb57SValentine Barshak r = put_user(list->flags, (int __user *)arg) ?
66133d6eb57SValentine Barshak -EFAULT : 0;
66233d6eb57SValentine Barshak break;
6636db3dfefSJiri Kosina
6646db3dfefSJiri Kosina case HIDIOCSFLAG:
6656db3dfefSJiri Kosina {
6666db3dfefSJiri Kosina int newflags;
66733d6eb57SValentine Barshak
66833d6eb57SValentine Barshak if (get_user(newflags, (int __user *)arg)) {
66933d6eb57SValentine Barshak r = -EFAULT;
67033d6eb57SValentine Barshak break;
67133d6eb57SValentine Barshak }
6726db3dfefSJiri Kosina
6736db3dfefSJiri Kosina if ((newflags & ~HIDDEV_FLAGS) != 0 ||
6746db3dfefSJiri Kosina ((newflags & HIDDEV_FLAG_REPORT) != 0 &&
6756db3dfefSJiri Kosina (newflags & HIDDEV_FLAG_UREF) == 0))
67633d6eb57SValentine Barshak break;
6776db3dfefSJiri Kosina
6786db3dfefSJiri Kosina list->flags = newflags;
6796db3dfefSJiri Kosina
68033d6eb57SValentine Barshak r = 0;
68133d6eb57SValentine Barshak break;
6826db3dfefSJiri Kosina }
6836db3dfefSJiri Kosina
6846db3dfefSJiri Kosina case HIDIOCGSTRING:
68507903407SOliver Neukum r = hiddev_ioctl_string(hiddev, cmd, user_arg);
68633d6eb57SValentine Barshak break;
6876db3dfefSJiri Kosina
6886db3dfefSJiri Kosina case HIDIOCINITREPORT:
6896db3dfefSJiri Kosina usbhid_init_reports(hid);
6909143059fSBenjamin Tissoires hiddev->initialized = true;
69133d6eb57SValentine Barshak r = 0;
69233d6eb57SValentine Barshak break;
6936db3dfefSJiri Kosina
6946db3dfefSJiri Kosina case HIDIOCGREPORT:
69533d6eb57SValentine Barshak if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
69633d6eb57SValentine Barshak r = -EFAULT;
69733d6eb57SValentine Barshak break;
69833d6eb57SValentine Barshak }
6996db3dfefSJiri Kosina
7006db3dfefSJiri Kosina if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
70133d6eb57SValentine Barshak break;
7026db3dfefSJiri Kosina
7031a8e8fabSValentine Barshak report = hiddev_lookup_report(hid, &rinfo);
70433d6eb57SValentine Barshak if (report == NULL)
70533d6eb57SValentine Barshak break;
7061a8e8fabSValentine Barshak
707d8814272SBenjamin Tissoires hid_hw_request(hid, report, HID_REQ_GET_REPORT);
708b7966a4dSBenjamin Tissoires hid_hw_wait(hid);
7096db3dfefSJiri Kosina
71033d6eb57SValentine Barshak r = 0;
71133d6eb57SValentine Barshak break;
7126db3dfefSJiri Kosina
7136db3dfefSJiri Kosina case HIDIOCSREPORT:
71433d6eb57SValentine Barshak if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
71533d6eb57SValentine Barshak r = -EFAULT;
71633d6eb57SValentine Barshak break;
71733d6eb57SValentine Barshak }
7186db3dfefSJiri Kosina
7196db3dfefSJiri Kosina if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
72033d6eb57SValentine Barshak break;
7216db3dfefSJiri Kosina
7221a8e8fabSValentine Barshak report = hiddev_lookup_report(hid, &rinfo);
72333d6eb57SValentine Barshak if (report == NULL)
72433d6eb57SValentine Barshak break;
7251a8e8fabSValentine Barshak
726d8814272SBenjamin Tissoires hid_hw_request(hid, report, HID_REQ_SET_REPORT);
727b7966a4dSBenjamin Tissoires hid_hw_wait(hid);
7286db3dfefSJiri Kosina
72933d6eb57SValentine Barshak r = 0;
73033d6eb57SValentine Barshak break;
7316db3dfefSJiri Kosina
7326db3dfefSJiri Kosina case HIDIOCGREPORTINFO:
73333d6eb57SValentine Barshak if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
73433d6eb57SValentine Barshak r = -EFAULT;
73533d6eb57SValentine Barshak break;
7361a8e8fabSValentine Barshak }
7371a8e8fabSValentine Barshak
7381a8e8fabSValentine Barshak report = hiddev_lookup_report(hid, &rinfo);
73933d6eb57SValentine Barshak if (report == NULL)
74033d6eb57SValentine Barshak break;
7416db3dfefSJiri Kosina
7426db3dfefSJiri Kosina rinfo.num_fields = report->maxfield;
7436db3dfefSJiri Kosina
74433d6eb57SValentine Barshak r = copy_to_user(user_arg, &rinfo, sizeof(rinfo)) ?
74533d6eb57SValentine Barshak -EFAULT : 0;
74633d6eb57SValentine Barshak break;
7476db3dfefSJiri Kosina
7486db3dfefSJiri Kosina case HIDIOCGFIELDINFO:
74933d6eb57SValentine Barshak if (copy_from_user(&finfo, user_arg, sizeof(finfo))) {
75033d6eb57SValentine Barshak r = -EFAULT;
75133d6eb57SValentine Barshak break;
75233d6eb57SValentine Barshak }
75333d6eb57SValentine Barshak
7546db3dfefSJiri Kosina rinfo.report_type = finfo.report_type;
7556db3dfefSJiri Kosina rinfo.report_id = finfo.report_id;
7566db3dfefSJiri Kosina
7571a8e8fabSValentine Barshak report = hiddev_lookup_report(hid, &rinfo);
75833d6eb57SValentine Barshak if (report == NULL)
75933d6eb57SValentine Barshak break;
7601a8e8fabSValentine Barshak
76133d6eb57SValentine Barshak if (finfo.field_index >= report->maxfield)
76233d6eb57SValentine Barshak break;
7634f65245fSGustavo A. R. Silva finfo.field_index = array_index_nospec(finfo.field_index,
7644f65245fSGustavo A. R. Silva report->maxfield);
7656db3dfefSJiri Kosina
7666db3dfefSJiri Kosina field = report->field[finfo.field_index];
7676db3dfefSJiri Kosina memset(&finfo, 0, sizeof(finfo));
7686db3dfefSJiri Kosina finfo.report_type = rinfo.report_type;
7696db3dfefSJiri Kosina finfo.report_id = rinfo.report_id;
7706db3dfefSJiri Kosina finfo.field_index = field->report_count - 1;
7716db3dfefSJiri Kosina finfo.maxusage = field->maxusage;
7726db3dfefSJiri Kosina finfo.flags = field->flags;
7736db3dfefSJiri Kosina finfo.physical = field->physical;
7746db3dfefSJiri Kosina finfo.logical = field->logical;
7756db3dfefSJiri Kosina finfo.application = field->application;
7766db3dfefSJiri Kosina finfo.logical_minimum = field->logical_minimum;
7776db3dfefSJiri Kosina finfo.logical_maximum = field->logical_maximum;
7786db3dfefSJiri Kosina finfo.physical_minimum = field->physical_minimum;
7796db3dfefSJiri Kosina finfo.physical_maximum = field->physical_maximum;
7806db3dfefSJiri Kosina finfo.unit_exponent = field->unit_exponent;
7816db3dfefSJiri Kosina finfo.unit = field->unit;
7826db3dfefSJiri Kosina
78333d6eb57SValentine Barshak r = copy_to_user(user_arg, &finfo, sizeof(finfo)) ?
78433d6eb57SValentine Barshak -EFAULT : 0;
78533d6eb57SValentine Barshak break;
7866db3dfefSJiri Kosina
7876db3dfefSJiri Kosina case HIDIOCGUCODE:
7886db3dfefSJiri Kosina case HIDIOCGUSAGE:
7896db3dfefSJiri Kosina case HIDIOCSUSAGE:
7906db3dfefSJiri Kosina case HIDIOCGUSAGES:
7916db3dfefSJiri Kosina case HIDIOCSUSAGES:
7926db3dfefSJiri Kosina case HIDIOCGCOLLECTIONINDEX:
7939143059fSBenjamin Tissoires if (!hiddev->initialized) {
7949143059fSBenjamin Tissoires usbhid_init_reports(hid);
7959143059fSBenjamin Tissoires hiddev->initialized = true;
7969143059fSBenjamin Tissoires }
79707903407SOliver Neukum r = hiddev_ioctl_usage(hiddev, cmd, user_arg);
79833d6eb57SValentine Barshak break;
7996db3dfefSJiri Kosina
8006db3dfefSJiri Kosina case HIDIOCGCOLLECTIONINFO:
80133d6eb57SValentine Barshak if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) {
80233d6eb57SValentine Barshak r = -EFAULT;
80333d6eb57SValentine Barshak break;
8041a8e8fabSValentine Barshak }
8051a8e8fabSValentine Barshak
80633d6eb57SValentine Barshak if (cinfo.index >= hid->maxcollection)
80733d6eb57SValentine Barshak break;
8084f65245fSGustavo A. R. Silva cinfo.index = array_index_nospec(cinfo.index,
8094f65245fSGustavo A. R. Silva hid->maxcollection);
8106db3dfefSJiri Kosina
8116db3dfefSJiri Kosina cinfo.type = hid->collection[cinfo.index].type;
8126db3dfefSJiri Kosina cinfo.usage = hid->collection[cinfo.index].usage;
8136db3dfefSJiri Kosina cinfo.level = hid->collection[cinfo.index].level;
8146db3dfefSJiri Kosina
81533d6eb57SValentine Barshak r = copy_to_user(user_arg, &cinfo, sizeof(cinfo)) ?
81633d6eb57SValentine Barshak -EFAULT : 0;
81733d6eb57SValentine Barshak break;
8186db3dfefSJiri Kosina
8196db3dfefSJiri Kosina default:
8206db3dfefSJiri Kosina if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
82133d6eb57SValentine Barshak break;
8226db3dfefSJiri Kosina
8236db3dfefSJiri Kosina if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
824dd2ed487SDaniel Mack int len = strlen(hid->name) + 1;
8256db3dfefSJiri Kosina if (len > _IOC_SIZE(cmd))
8266db3dfefSJiri Kosina len = _IOC_SIZE(cmd);
8271a8e8fabSValentine Barshak r = copy_to_user(user_arg, hid->name, len) ?
8286db3dfefSJiri Kosina -EFAULT : len;
82933d6eb57SValentine Barshak break;
8306db3dfefSJiri Kosina }
8316db3dfefSJiri Kosina
8326db3dfefSJiri Kosina if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
833dd2ed487SDaniel Mack int len = strlen(hid->phys) + 1;
8346db3dfefSJiri Kosina if (len > _IOC_SIZE(cmd))
8356db3dfefSJiri Kosina len = _IOC_SIZE(cmd);
8361a8e8fabSValentine Barshak r = copy_to_user(user_arg, hid->phys, len) ?
8376db3dfefSJiri Kosina -EFAULT : len;
83833d6eb57SValentine Barshak break;
8396db3dfefSJiri Kosina }
8406db3dfefSJiri Kosina }
84133d6eb57SValentine Barshak
84233d6eb57SValentine Barshak ret_unlock:
84333d6eb57SValentine Barshak mutex_unlock(&hiddev->existancelock);
84433d6eb57SValentine Barshak return r;
8456db3dfefSJiri Kosina }
8466db3dfefSJiri Kosina
8476db3dfefSJiri Kosina static const struct file_operations hiddev_fops = {
8486db3dfefSJiri Kosina .owner = THIS_MODULE,
8496db3dfefSJiri Kosina .read = hiddev_read,
8506db3dfefSJiri Kosina .write = hiddev_write,
8516db3dfefSJiri Kosina .poll = hiddev_poll,
8526db3dfefSJiri Kosina .open = hiddev_open,
8536db3dfefSJiri Kosina .release = hiddev_release,
8547961df16SAlan Cox .unlocked_ioctl = hiddev_ioctl,
8556db3dfefSJiri Kosina .fasync = hiddev_fasync,
856407e9ef7SArnd Bergmann .compat_ioctl = compat_ptr_ioctl,
8576038f373SArnd Bergmann .llseek = noop_llseek,
8586db3dfefSJiri Kosina };
8596db3dfefSJiri Kosina
hiddev_devnode(const struct device * dev,umode_t * mode)860*5033ac5cSGreg Kroah-Hartman static char *hiddev_devnode(const struct device *dev, umode_t *mode)
861f7a386c5SKay Sievers {
862f7a386c5SKay Sievers return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
863f7a386c5SKay Sievers }
864f7a386c5SKay Sievers
8656db3dfefSJiri Kosina static struct usb_class_driver hiddev_class = {
8666db3dfefSJiri Kosina .name = "hiddev%d",
867e454cea2SKay Sievers .devnode = hiddev_devnode,
8686db3dfefSJiri Kosina .fops = &hiddev_fops,
8696db3dfefSJiri Kosina .minor_base = HIDDEV_MINOR_BASE,
8706db3dfefSJiri Kosina };
8716db3dfefSJiri Kosina
8726db3dfefSJiri Kosina /*
8736db3dfefSJiri Kosina * This is where hid.c calls us to connect a hid device to the hiddev driver
8746db3dfefSJiri Kosina */
hiddev_connect(struct hid_device * hid,unsigned int force)87593c10132SJiri Slaby int hiddev_connect(struct hid_device *hid, unsigned int force)
8766db3dfefSJiri Kosina {
8776db3dfefSJiri Kosina struct hiddev *hiddev;
8786db3dfefSJiri Kosina struct usbhid_device *usbhid = hid->driver_data;
8796db3dfefSJiri Kosina int retval;
8806db3dfefSJiri Kosina
88193c10132SJiri Slaby if (!force) {
88293c10132SJiri Slaby unsigned int i;
8836db3dfefSJiri Kosina for (i = 0; i < hid->maxcollection; i++)
8846db3dfefSJiri Kosina if (hid->collection[i].type ==
8856db3dfefSJiri Kosina HID_COLLECTION_APPLICATION &&
8866db3dfefSJiri Kosina !IS_INPUT_APPLICATION(hid->collection[i].usage))
8876db3dfefSJiri Kosina break;
8886db3dfefSJiri Kosina
88993c10132SJiri Slaby if (i == hid->maxcollection)
8909951bb25SJason Gerecke return -EINVAL;
89193c10132SJiri Slaby }
8926db3dfefSJiri Kosina
8936db3dfefSJiri Kosina if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
8949951bb25SJason Gerecke return -ENOMEM;
8956db3dfefSJiri Kosina
89607903407SOliver Neukum init_waitqueue_head(&hiddev->wait);
89707903407SOliver Neukum INIT_LIST_HEAD(&hiddev->list);
89807903407SOliver Neukum spin_lock_init(&hiddev->list_lock);
89907903407SOliver Neukum mutex_init(&hiddev->existancelock);
90076052749SJiri Kosina hid->hiddev = hiddev;
90107903407SOliver Neukum hiddev->hid = hid;
90207903407SOliver Neukum hiddev->exist = 1;
9036db3dfefSJiri Kosina retval = usb_register_dev(usbhid->intf, &hiddev_class);
9046db3dfefSJiri Kosina if (retval) {
9054291ee30SJoe Perches hid_err(hid, "Not able to get a minor for this device\n");
90676052749SJiri Kosina hid->hiddev = NULL;
9076db3dfefSJiri Kosina kfree(hiddev);
9089951bb25SJason Gerecke return retval;
90907903407SOliver Neukum }
9109143059fSBenjamin Tissoires
9119143059fSBenjamin Tissoires /*
9129143059fSBenjamin Tissoires * If HID_QUIRK_NO_INIT_REPORTS is set, make sure we don't initialize
9139143059fSBenjamin Tissoires * the reports.
9149143059fSBenjamin Tissoires */
9159143059fSBenjamin Tissoires hiddev->initialized = hid->quirks & HID_QUIRK_NO_INIT_REPORTS;
9169143059fSBenjamin Tissoires
917733aca90SJaejoong Kim hiddev->minor = usbhid->intf->minor;
918733aca90SJaejoong Kim
9196db3dfefSJiri Kosina return 0;
9206db3dfefSJiri Kosina }
9216db3dfefSJiri Kosina
9226db3dfefSJiri Kosina /*
9236db3dfefSJiri Kosina * This is where hid.c calls us to disconnect a hiddev device from the
9246db3dfefSJiri Kosina * corresponding hid device (usually because the usb device has disconnected)
9256db3dfefSJiri Kosina */
9266db3dfefSJiri Kosina static struct usb_class_driver hiddev_class;
hiddev_disconnect(struct hid_device * hid)9276db3dfefSJiri Kosina void hiddev_disconnect(struct hid_device *hid)
9286db3dfefSJiri Kosina {
9296db3dfefSJiri Kosina struct hiddev *hiddev = hid->hiddev;
9306db3dfefSJiri Kosina struct usbhid_device *usbhid = hid->driver_data;
9316db3dfefSJiri Kosina
932ba18311dSMing Lei usb_deregister_dev(usbhid->intf, &hiddev_class);
933ba18311dSMing Lei
93407903407SOliver Neukum mutex_lock(&hiddev->existancelock);
9356db3dfefSJiri Kosina hiddev->exist = 0;
9366db3dfefSJiri Kosina
9376db3dfefSJiri Kosina if (hiddev->open) {
938d36b7d4cSDmitry Torokhov hid_hw_close(hiddev->hid);
9396db3dfefSJiri Kosina wake_up_interruptible(&hiddev->wait);
9405c02c447Sdan.carpenter@oracle.com mutex_unlock(&hiddev->existancelock);
9416db3dfefSJiri Kosina } else {
9427f77897eSJiri Kosina mutex_unlock(&hiddev->existancelock);
9436db3dfefSJiri Kosina kfree(hiddev);
9446db3dfefSJiri Kosina }
9456db3dfefSJiri Kosina }
946