xref: /openbmc/linux/drivers/hid/usbhid/hiddev.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
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