11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (c) 2001 Paul Stewart
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (c) 2001 Vojtech Pavlik
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  HID char devices, giving access to raw HID device events.
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Should you need to contact me, the author, you can do so either by
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * e-mail - mail your message to Paul Stewart <stewart@wetlogic.net>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/poll.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/input.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
34dde5845a529ff753364a6d1aea61180946270bfaJiri Kosina#include <linux/hid.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/hiddev.h>
36bb6c8d8fa9b5587eea18078ce0bcf6bb2905789fPhilip Langdale#include <linux/compat.h>
37dde5845a529ff753364a6d1aea61180946270bfaJiri Kosina#include "usbhid.h"
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_USB_DYNAMIC_MINORS
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define HIDDEV_MINOR_BASE	0
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define HIDDEV_MINORS		256
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define HIDDEV_MINOR_BASE	96
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define HIDDEV_MINORS		16
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
46affbb8c6e690be2196258e65f3cc92d55b18d9faJiri Kosina#define HIDDEV_BUFFER_SIZE	2048
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct hiddev {
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int exist;
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int open;
51079034073faf974973baa0256b029451f6e768adOliver Neukum	struct mutex existancelock;
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wait_queue_head_t wait;
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hid_device *hid;
54826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	struct list_head list;
55cdcb44e87bedcf5070eece61f89f9373a3810031Jiri Kosina	spinlock_t list_lock;
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct hiddev_list {
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int head;
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int tail;
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned flags;
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct fasync_struct *fasync;
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev *hiddev;
65826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	struct list_head node;
66079034073faf974973baa0256b029451f6e768adOliver Neukum	struct mutex thread_lock;
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Find a report, given the report's type and ID.  The ID can be specified
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * indirectly by REPORT_ID_FIRST (which returns the first report of the given
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * given type which follows old_id.
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct hid_report *
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldshiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
78826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
79826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK;
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hid_report_enum *report_enum;
81826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	struct hid_report *report;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct list_head *list;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
85826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	    rinfo->report_type > HID_REPORT_TYPE_MAX)
86826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov		return NULL;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	report_enum = hid->report_enum +
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(rinfo->report_type - HID_REPORT_TYPE_MIN);
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (flags) {
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0: /* Nothing to do -- report_id is already set correctly */
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HID_REPORT_ID_FIRST:
96826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov		if (list_empty(&report_enum->report_list))
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return NULL;
98826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov
99826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov		list = report_enum->report_list.next;
100826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov		report = list_entry(list, struct hid_report, list);
101826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov		rinfo->report_id = report->id;
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
10305f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HID_REPORT_ID_NEXT:
105826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov		report = report_enum->report_id_hash[rid];
106826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov		if (!report)
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return NULL;
108826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov
109826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov		list = report->list.next;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (list == &report_enum->report_list)
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return NULL;
112826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov
113826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov		report = list_entry(list, struct hid_report, list);
114826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov		rinfo->report_id = report->id;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
11605f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return report_enum->report_id_hash[rinfo->report_id];
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Perform an exhaustive search of the report table for a usage, given its
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * type and usage id.
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct hid_field *
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldshiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hid_report *report;
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hid_report_enum *report_enum;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hid_field *field;
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (uref->report_type < HID_REPORT_TYPE_MIN ||
137826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	    uref->report_type > HID_REPORT_TYPE_MAX)
138826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov		return NULL;
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	report_enum = hid->report_enum +
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(uref->report_type - HID_REPORT_TYPE_MIN);
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
143826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	list_for_each_entry(report, &report_enum->report_list, list) {
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < report->maxfield; i++) {
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			field = report->field[i];
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (j = 0; j < field->maxusage; j++) {
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (field->usage[j].hid == uref->usage_code) {
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					uref->report_id = report->id;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					uref->field_index = i;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					uref->usage_index = j;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					return field;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
155826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	}
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NULL;
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void hiddev_send_event(struct hid_device *hid,
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      struct hiddev_usage_ref *uref)
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev *hiddev = hid->hiddev;
164826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	struct hiddev_list *list;
165cdcb44e87bedcf5070eece61f89f9373a3810031Jiri Kosina	unsigned long flags;
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
167cdcb44e87bedcf5070eece61f89f9373a3810031Jiri Kosina	spin_lock_irqsave(&hiddev->list_lock, flags);
168826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	list_for_each_entry(list, &hiddev->list, node) {
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (uref->field_index != HID_FIELD_INDEX_NONE ||
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    (list->flags & HIDDEV_FLAG_REPORT) != 0) {
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			list->buffer[list->head] = *uref;
17205f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov			list->head = (list->head + 1) &
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				(HIDDEV_BUFFER_SIZE - 1);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kill_fasync(&list->fasync, SIGIO, POLL_IN);
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
177cdcb44e87bedcf5070eece61f89f9373a3810031Jiri Kosina	spin_unlock_irqrestore(&hiddev->list_lock, flags);
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wake_up_interruptible(&hiddev->wait);
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is where hid.c calls into hiddev to pass an event that occurred over
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the interrupt pipe
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
1877d12e780e003f93433d49ce78cfedf4b4c52adc5David Howells		      struct hid_usage *usage, __s32 value)
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned type = field->report_type;
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_usage_ref uref;
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19205f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov	uref.report_type =
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
19405f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov	  ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
195826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	   ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uref.report_id = field->report->id;
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uref.field_index = field->index;
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uref.usage_index = (usage - field->usage);
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uref.usage_code = usage->hid;
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uref.value = value;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hiddev_send_event(hid, &uref);
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
204229695e51efc4ed5e04ab471c82591d0f432909dJiri KosinaEXPORT_SYMBOL_GPL(hiddev_hid_event);
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid hiddev_report_event(struct hid_device *hid, struct hid_report *report)
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned type = report->type;
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_usage_ref uref;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(&uref, 0, sizeof(uref));
21205f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov	uref.report_type =
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
21405f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov	  ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
215826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	   ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uref.report_id = report->id;
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uref.field_index = HID_FIELD_INDEX_NONE;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hiddev_send_event(hid, &uref);
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
221aa8de2f038baec993f07ef66fb3e94481d1ec22bJiri Kosina
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * fasync file op
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int hiddev_fasync(int fd, struct file *file, int on)
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_list *list = file->private_data;
228826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov
22960aa49243d09afc873f082567d2e3c16634ced84Jonathan Corbet	return fasync_helper(fd, file, on, &list->fasync);
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * release file op
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int hiddev_release(struct inode * inode, struct file * file)
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_list *list = file->private_data;
239cdcb44e87bedcf5070eece61f89f9373a3810031Jiri Kosina	unsigned long flags;
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241cdcb44e87bedcf5070eece61f89f9373a3810031Jiri Kosina	spin_lock_irqsave(&list->hiddev->list_lock, flags);
242826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	list_del(&list->node);
243cdcb44e87bedcf5070eece61f89f9373a3810031Jiri Kosina	spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2456cb4b040795c555c7ab4b1ba29b0dba2b5a42bebJiri Kosina	mutex_lock(&list->hiddev->existancelock);
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!--list->hiddev->open) {
2470361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum		if (list->hiddev->exist) {
2484916b3a57fc94664677d439b911b8aaf86c7ec23Jiri Kosina			usbhid_close(list->hiddev->hid);
2490361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum			usbhid_put_power(list->hiddev->hid);
2500361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum		} else {
2515c699d7d3f94ee1dd934edea889b32f8279a4e65Dan Carpenter			mutex_unlock(&list->hiddev->existancelock);
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(list->hiddev);
2535c699d7d3f94ee1dd934edea889b32f8279a4e65Dan Carpenter			kfree(list);
2545c699d7d3f94ee1dd934edea889b32f8279a4e65Dan Carpenter			return 0;
2550361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum		}
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2586cb4b040795c555c7ab4b1ba29b0dba2b5a42bebJiri Kosina	mutex_unlock(&list->hiddev->existancelock);
2595c699d7d3f94ee1dd934edea889b32f8279a4e65Dan Carpenter	kfree(list);
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * open file op
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
267826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhovstatic int hiddev_open(struct inode *inode, struct file *file)
268826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov{
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_list *list;
270bd25f4dd6972755579d0ea50d1a5ace2e9b00d1aArnd Bergmann	struct usb_interface *intf;
2719c9e54a8df0be48aa359744f412377cc55c3b7d2Jiri Kosina	struct hid_device *hid;
272bd25f4dd6972755579d0ea50d1a5ace2e9b00d1aArnd Bergmann	struct hiddev *hiddev;
273bd25f4dd6972755579d0ea50d1a5ace2e9b00d1aArnd Bergmann	int res;
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2758fe294caf8c868edd9046251824a0af91991bf43Guillaume Chazarain	intf = usbhid_find_interface(iminor(inode));
276bd25f4dd6972755579d0ea50d1a5ace2e9b00d1aArnd Bergmann	if (!intf)
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
2789c9e54a8df0be48aa359744f412377cc55c3b7d2Jiri Kosina	hid = usb_get_intfdata(intf);
2799c9e54a8df0be48aa359744f412377cc55c3b7d2Jiri Kosina	hiddev = hid->hiddev;
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281bbdb7dafb5b5a3c0197cbabd5055d8e9c093e3eaOliver Neukum	if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
283079034073faf974973baa0256b029451f6e768adOliver Neukum	mutex_init(&list->thread_lock);
284bd25f4dd6972755579d0ea50d1a5ace2e9b00d1aArnd Bergmann	list->hiddev = hiddev;
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	file->private_data = list;
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
287079034073faf974973baa0256b029451f6e768adOliver Neukum	/*
288079034073faf974973baa0256b029451f6e768adOliver Neukum	 * no need for locking because the USB major number
289079034073faf974973baa0256b029451f6e768adOliver Neukum	 * is shared which usbcore guards against disconnect
290079034073faf974973baa0256b029451f6e768adOliver Neukum	 */
291079034073faf974973baa0256b029451f6e768adOliver Neukum	if (list->hiddev->exist) {
292079034073faf974973baa0256b029451f6e768adOliver Neukum		if (!list->hiddev->open++) {
293bd25f4dd6972755579d0ea50d1a5ace2e9b00d1aArnd Bergmann			res = usbhid_open(hiddev->hid);
294079034073faf974973baa0256b029451f6e768adOliver Neukum			if (res < 0) {
295079034073faf974973baa0256b029451f6e768adOliver Neukum				res = -EIO;
296079034073faf974973baa0256b029451f6e768adOliver Neukum				goto bail;
297079034073faf974973baa0256b029451f6e768adOliver Neukum			}
298079034073faf974973baa0256b029451f6e768adOliver Neukum		}
299079034073faf974973baa0256b029451f6e768adOliver Neukum	} else {
300079034073faf974973baa0256b029451f6e768adOliver Neukum		res = -ENODEV;
301079034073faf974973baa0256b029451f6e768adOliver Neukum		goto bail;
302079034073faf974973baa0256b029451f6e768adOliver Neukum	}
303079034073faf974973baa0256b029451f6e768adOliver Neukum
304079034073faf974973baa0256b029451f6e768adOliver Neukum	spin_lock_irq(&list->hiddev->list_lock);
305bd25f4dd6972755579d0ea50d1a5ace2e9b00d1aArnd Bergmann	list_add_tail(&list->node, &hiddev->list);
306079034073faf974973baa0256b029451f6e768adOliver Neukum	spin_unlock_irq(&list->hiddev->list_lock);
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3086cb4b040795c555c7ab4b1ba29b0dba2b5a42bebJiri Kosina	mutex_lock(&hiddev->existancelock);
3090361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum	if (!list->hiddev->open++)
3100361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum		if (list->hiddev->exist) {
311bd25f4dd6972755579d0ea50d1a5ace2e9b00d1aArnd Bergmann			struct hid_device *hid = hiddev->hid;
3120361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum			res = usbhid_get_power(hid);
3130361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum			if (res < 0) {
3140361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum				res = -EIO;
3156cb4b040795c555c7ab4b1ba29b0dba2b5a42bebJiri Kosina				goto bail_unlock;
3160361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum			}
3170361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum			usbhid_open(hid);
3180361a28d3f9a4315a100c7b37ba0b55cfe15fe07Oliver Neukum		}
3196cb4b040795c555c7ab4b1ba29b0dba2b5a42bebJiri Kosina	mutex_unlock(&hiddev->existancelock);
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3216cb4b040795c555c7ab4b1ba29b0dba2b5a42bebJiri Kosinabail_unlock:
3226cb4b040795c555c7ab4b1ba29b0dba2b5a42bebJiri Kosina	mutex_unlock(&hiddev->existancelock);
323079034073faf974973baa0256b029451f6e768adOliver Neukumbail:
324079034073faf974973baa0256b029451f6e768adOliver Neukum	file->private_data = NULL;
32548e7a3c95c9f98c2cb6f894820e3cc2d0448e92fJohannes Weiner	kfree(list);
326079034073faf974973baa0256b029451f6e768adOliver Neukum	return res;
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * "write" file op
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EINVAL;
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * "read" file op
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
34296fe2ab830d7dffee1b3d8abf27ced4d7d5765e7Johannes Weiner	DEFINE_WAIT(wait);
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_list *list = file->private_data;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int event_size;
345079034073faf974973baa0256b029451f6e768adOliver Neukum	int retval;
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count < event_size)
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
353079034073faf974973baa0256b029451f6e768adOliver Neukum	/* lock against other threads */
354079034073faf974973baa0256b029451f6e768adOliver Neukum	retval = mutex_lock_interruptible(&list->thread_lock);
355079034073faf974973baa0256b029451f6e768adOliver Neukum	if (retval)
356079034073faf974973baa0256b029451f6e768adOliver Neukum		return -ERESTARTSYS;
357079034073faf974973baa0256b029451f6e768adOliver Neukum
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (retval == 0) {
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (list->head == list->tail) {
360079034073faf974973baa0256b029451f6e768adOliver Neukum			prepare_to_wait(&list->hiddev->wait, &wait, TASK_INTERRUPTIBLE);
36105f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			while (list->head == list->tail) {
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (file->f_flags & O_NONBLOCK) {
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					retval = -EAGAIN;
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (signal_pending(current)) {
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					retval = -ERESTARTSYS;
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (!list->hiddev->exist) {
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					retval = -EIO;
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
37505f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov
376079034073faf974973baa0256b029451f6e768adOliver Neukum				/* let O_NONBLOCK tasks run */
377079034073faf974973baa0256b029451f6e768adOliver Neukum				mutex_unlock(&list->thread_lock);
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				schedule();
37906268b2a384ece73618c1ad7649d19905ab79806Peter Waechtler				if (mutex_lock_interruptible(&list->thread_lock)) {
38006268b2a384ece73618c1ad7649d19905ab79806Peter Waechtler					finish_wait(&list->hiddev->wait, &wait);
381079034073faf974973baa0256b029451f6e768adOliver Neukum					return -EINTR;
38206268b2a384ece73618c1ad7649d19905ab79806Peter Waechtler				}
38348d705522da4fa04bb0169a7ca3c9ab92e28b613Micon, David				set_current_state(TASK_INTERRUPTIBLE);
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
385079034073faf974973baa0256b029451f6e768adOliver Neukum			finish_wait(&list->hiddev->wait, &wait);
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
389079034073faf974973baa0256b029451f6e768adOliver Neukum		if (retval) {
390079034073faf974973baa0256b029451f6e768adOliver Neukum			mutex_unlock(&list->thread_lock);
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return retval;
392079034073faf974973baa0256b029451f6e768adOliver Neukum		}
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
39505f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov		while (list->head != list->tail &&
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       retval + event_size <= count) {
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
398079034073faf974973baa0256b029451f6e768adOliver Neukum				if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE) {
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					struct hiddev_event event;
400079034073faf974973baa0256b029451f6e768adOliver Neukum
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					event.hid = list->buffer[list->tail].usage_code;
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					event.value = list->buffer[list->tail].value;
403079034073faf974973baa0256b029451f6e768adOliver Neukum					if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) {
404079034073faf974973baa0256b029451f6e768adOliver Neukum						mutex_unlock(&list->thread_lock);
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						return -EFAULT;
406079034073faf974973baa0256b029451f6e768adOliver Neukum					}
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					retval += sizeof(struct hiddev_event);
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else {
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				    (list->flags & HIDDEV_FLAG_REPORT) != 0) {
412079034073faf974973baa0256b029451f6e768adOliver Neukum
413079034073faf974973baa0256b029451f6e768adOliver Neukum					if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) {
414079034073faf974973baa0256b029451f6e768adOliver Neukum						mutex_unlock(&list->thread_lock);
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						return -EFAULT;
416079034073faf974973baa0256b029451f6e768adOliver Neukum					}
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					retval += sizeof(struct hiddev_usage_ref);
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
424079034073faf974973baa0256b029451f6e768adOliver Neukum	mutex_unlock(&list->thread_lock);
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * "poll" file op
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * No kernel lock - fine
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int hiddev_poll(struct file *file, poll_table *wait)
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_list *list = file->private_data;
436826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	poll_wait(file, &list->hiddev->wait, wait);
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (list->head != list->tail)
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return POLLIN | POLLRDNORM;
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!list->hiddev->exist)
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return POLLERR | POLLHUP;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * "ioctl" file op
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
448cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvarestatic noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
449cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare{
450cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	struct hid_device *hid = hiddev->hid;
451cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	struct hiddev_report_info rinfo;
452cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	struct hiddev_usage_ref_multi *uref_multi = NULL;
453cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	struct hiddev_usage_ref *uref;
454cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	struct hid_report *report;
455cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	struct hid_field *field;
456cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	int i;
457cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
458cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
459cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	if (!uref_multi)
460cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		return -ENOMEM;
461cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	uref = &uref_multi->uref;
462cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
463cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		if (copy_from_user(uref_multi, user_arg,
464cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				   sizeof(*uref_multi)))
465cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			goto fault;
466cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	} else {
467cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		if (copy_from_user(uref, user_arg, sizeof(*uref)))
468cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			goto fault;
469cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	}
470cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
471cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	switch (cmd) {
472cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	case HIDIOCGUCODE:
473cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		rinfo.report_type = uref->report_type;
474cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		rinfo.report_id = uref->report_id;
475cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
476cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			goto inval;
477cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
478cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		if (uref->field_index >= report->maxfield)
479cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			goto inval;
480cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
481cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		field = report->field[uref->field_index];
482cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		if (uref->usage_index >= field->maxusage)
483cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			goto inval;
484cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
485cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		uref->usage_code = field->usage[uref->usage_index].hid;
486cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
487cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		if (copy_to_user(user_arg, uref, sizeof(*uref)))
488cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			goto fault;
489cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
490eb9910894d7857c273e049b297fd6251e5ecc43eJiri Slaby		goto goodreturn;
491cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
492cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	default:
493cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		if (cmd != HIDIOCGUSAGE &&
494cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		    cmd != HIDIOCGUSAGES &&
495cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		    uref->report_type == HID_REPORT_TYPE_INPUT)
496cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			goto inval;
497cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
498cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
499cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			field = hiddev_lookup_usage(hid, uref);
500cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			if (field == NULL)
501cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				goto inval;
502cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		} else {
503cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			rinfo.report_type = uref->report_type;
504cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			rinfo.report_id = uref->report_id;
505cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
506cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				goto inval;
507cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
508cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			if (uref->field_index >= report->maxfield)
509cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				goto inval;
510cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
511cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			field = report->field[uref->field_index];
512cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
513cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			if (cmd == HIDIOCGCOLLECTIONINDEX) {
514cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				if (uref->usage_index >= field->maxusage)
515cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare					goto inval;
516cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			} else if (uref->usage_index >= field->report_count)
517cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				goto inval;
518cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
519cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
520cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				 (uref_multi->num_values > HID_MAX_MULTI_USAGES ||
521cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				  uref->usage_index + uref_multi->num_values > field->report_count))
522cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				goto inval;
523ac065bf214bb6a7fb7536f2dde686d4694342801Dan Carpenter		}
524cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
525cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		switch (cmd) {
526cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		case HIDIOCGUSAGE:
527cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			uref->value = field->value[uref->usage_index];
528cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			if (copy_to_user(user_arg, uref, sizeof(*uref)))
529cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				goto fault;
530cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			goto goodreturn;
531cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
532cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		case HIDIOCSUSAGE:
533cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			field->value[uref->usage_index] = uref->value;
534cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			goto goodreturn;
535cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
536cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		case HIDIOCGCOLLECTIONINDEX:
5374859484b0957ddc7fe3e0fa349d98b0f1c7876bdJiri Slaby			i = field->usage[uref->usage_index].collection_index;
538cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			kfree(uref_multi);
5394859484b0957ddc7fe3e0fa349d98b0f1c7876bdJiri Slaby			return i;
540cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		case HIDIOCGUSAGES:
541cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			for (i = 0; i < uref_multi->num_values; i++)
542cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				uref_multi->values[i] =
543cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				    field->value[uref->usage_index + i];
544cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			if (copy_to_user(user_arg, uref_multi,
545cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare					 sizeof(*uref_multi)))
546cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				goto fault;
547cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			goto goodreturn;
548cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		case HIDIOCSUSAGES:
549cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			for (i = 0; i < uref_multi->num_values; i++)
550cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				field->value[uref->usage_index + i] =
551cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare				    uref_multi->values[i];
552cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare			goto goodreturn;
553cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		}
554cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
555cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvaregoodreturn:
556cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		kfree(uref_multi);
557cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		return 0;
558cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvarefault:
559cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		kfree(uref_multi);
560cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		return -EFAULT;
561cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvareinval:
562cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		kfree(uref_multi);
563cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		return -EINVAL;
564cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	}
565cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare}
566cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
567cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvarestatic noinline int hiddev_ioctl_string(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
568cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare{
569cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	struct hid_device *hid = hiddev->hid;
570cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	struct usb_device *dev = hid_to_usb_dev(hid);
571cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	int idx, len;
572cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	char *buf;
573cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
574cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	if (get_user(idx, (int __user *)user_arg))
575cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		return -EFAULT;
576cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
577cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
578cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		return -ENOMEM;
579cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
580cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
581cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		kfree(buf);
582cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		return -EINVAL;
583cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	}
584cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
585cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
586cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		kfree(buf);
587cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		return -EFAULT;
588cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	}
589cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
590cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	kfree(buf);
591cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
592cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare	return len;
593cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare}
594cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare
5957961df16819085b8a357720d89d0239036e6af2aAlan Coxstatic long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_list *list = file->private_data;
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev *hiddev = list->hiddev;
5991a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak	struct hid_device *hid;
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_collection_info cinfo;
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_report_info rinfo;
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_field_info finfo;
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev_devinfo dinfo;
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hid_report *report;
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hid_field *field;
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __user *user_arg = (void __user *)arg;
60733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak	int i, r = -EINVAL;
6081a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak
6097961df16819085b8a357720d89d0239036e6af2aAlan Cox	/* Called without BKL by compat methods so no BKL taken */
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
61133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak	mutex_lock(&hiddev->existancelock);
61233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak	if (!hiddev->exist) {
61333d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		r = -ENODEV;
61433d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		goto ret_unlock;
61533d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak	}
61633d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak
61733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak	hid = hiddev->hid;
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGVERSION:
62233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		r = put_user(HID_VERSION, (int __user *)arg) ?
62333d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			-EFAULT : 0;
62433d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		break;
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCAPPLICATION:
62733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (arg < 0 || arg >= hid->maxapplication)
62833d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < hid->maxcollection; i++)
63105f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov			if (hid->collection[i].type ==
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    HID_COLLECTION_APPLICATION && arg-- == 0)
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
63405f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov
63533d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (i < hid->maxcollection)
6361a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak			r = hid->collection[i].usage;
63733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		break;
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGDEVINFO:
64033d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		{
64133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			struct usb_device *dev = hid_to_usb_dev(hid);
64233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			struct usbhid_device *usbhid = hid->driver_data;
64333d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak
6449561f7faa45cb855b1ba83a4acf3f2ad3665e71fDan Carpenter			memset(&dinfo, 0, sizeof(dinfo));
6459561f7faa45cb855b1ba83a4acf3f2ad3665e71fDan Carpenter
64633d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			dinfo.bustype = BUS_USB;
64733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			dinfo.busnum = dev->bus->busnum;
64833d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			dinfo.devnum = dev->devnum;
64933d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			dinfo.ifnum = usbhid->ifnum;
65033d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor);
65133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
65233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
65333d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			dinfo.num_applications = hid->maxapplication;
65433d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak
65533d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			r = copy_to_user(user_arg, &dinfo, sizeof(dinfo)) ?
65633d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak				-EFAULT : 0;
65733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
6581a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak		}
6591a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGFLAG:
66133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		r = put_user(list->flags, (int __user *)arg) ?
66233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			-EFAULT : 0;
66333d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		break;
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCSFLAG:
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int newflags;
66833d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak
66933d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			if (get_user(newflags, (int __user *)arg)) {
67033d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak				r = -EFAULT;
67133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak				break;
67233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			}
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((newflags & ~HIDDEV_FLAGS) != 0 ||
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    ((newflags & HIDDEV_FLAG_REPORT) != 0 &&
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			     (newflags & HIDDEV_FLAG_UREF) == 0))
67733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak				break;
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			list->flags = newflags;
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
68133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			r = 0;
68233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGSTRING:
68633d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		r = hiddev_ioctl_string(hiddev, cmd, user_arg);
68733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		break;
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCINITREPORT:
6904916b3a57fc94664677d439b911b8aaf86c7ec23Jiri Kosina		usbhid_init_reports(hid);
69133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		r = 0;
69233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		break;
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGREPORT:
69533d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
69633d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			r = -EFAULT;
69733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
69833d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		}
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
70133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
7021a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak
7031a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak		report = hiddev_lookup_report(hid, &rinfo);
70433d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (report == NULL)
70533d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
7061a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak
7071a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak		usbhid_submit_report(hid, report, USB_DIR_IN);
7081a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak		usbhid_wait_io(hid);
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
71033d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		r = 0;
71133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		break;
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCSREPORT:
71433d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
71533d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			r = -EFAULT;
71633d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
71733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		}
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
72033d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
7211a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak
7221a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak		report = hiddev_lookup_report(hid, &rinfo);
72333d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (report == NULL)
72433d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
7251a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak
7261a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak		usbhid_submit_report(hid, report, USB_DIR_OUT);
7271a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak		usbhid_wait_io(hid);
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
72933d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		r = 0;
73033d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		break;
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGREPORTINFO:
73333d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) {
73433d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			r = -EFAULT;
73533d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
7361a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak		}
7371a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak
7381a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak		report = hiddev_lookup_report(hid, &rinfo);
73933d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (report == NULL)
74033d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rinfo.num_fields = report->maxfield;
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
74433d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		r = copy_to_user(user_arg, &rinfo, sizeof(rinfo)) ?
74533d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			-EFAULT : 0;
74633d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		break;
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGFIELDINFO:
74933d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (copy_from_user(&finfo, user_arg, sizeof(finfo))) {
75033d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			r = -EFAULT;
75133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
75233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		}
75333d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rinfo.report_type = finfo.report_type;
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rinfo.report_id = finfo.report_id;
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7571a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak		report = hiddev_lookup_report(hid, &rinfo);
75833d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (report == NULL)
75933d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
7601a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak
76133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (finfo.field_index >= report->maxfield)
76233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		field = report->field[finfo.field_index];
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memset(&finfo, 0, sizeof(finfo));
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.report_type = rinfo.report_type;
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.report_id = rinfo.report_id;
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.field_index = field->report_count - 1;
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.maxusage = field->maxusage;
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.flags = field->flags;
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.physical = field->physical;
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.logical = field->logical;
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.application = field->application;
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.logical_minimum = field->logical_minimum;
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.logical_maximum = field->logical_maximum;
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.physical_minimum = field->physical_minimum;
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.physical_maximum = field->physical_maximum;
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.unit_exponent = field->unit_exponent;
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		finfo.unit = field->unit;
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
78133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		r = copy_to_user(user_arg, &finfo, sizeof(finfo)) ?
78233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			-EFAULT : 0;
78333d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		break;
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGUCODE:
786cf2a299e48cbeb6c942e1f765b92ca6058355f68Jean Delvare		/* fall through */
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGUSAGE:
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCSUSAGE:
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGUSAGES:
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCSUSAGES:
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGCOLLECTIONINDEX:
79233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		r = hiddev_ioctl_usage(hiddev, cmd, user_arg);
79333d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		break;
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case HIDIOCGCOLLECTIONINFO:
79633d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) {
79733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			r = -EFAULT;
79833d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
7991a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak		}
8001a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak
80133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		if (cinfo.index >= hid->maxcollection)
80233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cinfo.type = hid->collection[cinfo.index].type;
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cinfo.usage = hid->collection[cinfo.index].usage;
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cinfo.level = hid->collection[cinfo.index].level;
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
80833d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		r = copy_to_user(user_arg, &cinfo, sizeof(cinfo)) ?
80933d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			-EFAULT : 0;
81033d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak		break;
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
81433d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
817dd2ed487fdd78b50549b2ca8418875c0d9f4a30eDaniel Mack			int len = strlen(hid->name) + 1;
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (len > _IOC_SIZE(cmd))
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 len = _IOC_SIZE(cmd);
8201a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak			r = copy_to_user(user_arg, hid->name, len) ?
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				-EFAULT : len;
82233d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
826dd2ed487fdd78b50549b2ca8418875c0d9f4a30eDaniel Mack			int len = strlen(hid->phys) + 1;
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (len > _IOC_SIZE(cmd))
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				len = _IOC_SIZE(cmd);
8291a8e8fab790ea7af81b8f964fdec706ad1ec2271Valentine Barshak			r = copy_to_user(user_arg, hid->phys, len) ?
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				-EFAULT : len;
83133d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak			break;
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
83433d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak
83533d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshakret_unlock:
83633d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak	mutex_unlock(&hiddev->existancelock);
83733d6eb570b1f3fe5ba93cef465c5be66535c2c9aValentine Barshak	return r;
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
840bb6c8d8fa9b5587eea18078ce0bcf6bb2905789fPhilip Langdale#ifdef CONFIG_COMPAT
841bb6c8d8fa9b5587eea18078ce0bcf6bb2905789fPhilip Langdalestatic long hiddev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
842bb6c8d8fa9b5587eea18078ce0bcf6bb2905789fPhilip Langdale{
84388af45bafdda8f892c9d45ce38d55fdf7e734513Jiri Kosina	return hiddev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
844bb6c8d8fa9b5587eea18078ce0bcf6bb2905789fPhilip Langdale}
845bb6c8d8fa9b5587eea18078ce0bcf6bb2905789fPhilip Langdale#endif
846bb6c8d8fa9b5587eea18078ce0bcf6bb2905789fPhilip Langdale
847066202dd48cf3296b6cc22b5fcf89aef33fa0efcLuiz Fernando N. Capitulinostatic const struct file_operations hiddev_fops = {
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner =	THIS_MODULE,
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read =		hiddev_read,
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =	hiddev_write,
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.poll =		hiddev_poll,
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =		hiddev_open,
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release =	hiddev_release,
8547961df16819085b8a357720d89d0239036e6af2aAlan Cox	.unlocked_ioctl =	hiddev_ioctl,
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.fasync =	hiddev_fasync,
856bb6c8d8fa9b5587eea18078ce0bcf6bb2905789fPhilip Langdale#ifdef CONFIG_COMPAT
857bb6c8d8fa9b5587eea18078ce0bcf6bb2905789fPhilip Langdale	.compat_ioctl	= hiddev_compat_ioctl,
858bb6c8d8fa9b5587eea18078ce0bcf6bb2905789fPhilip Langdale#endif
8596038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann	.llseek		= noop_llseek,
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8622c9ede55ecec58099b72e4bb8eab719f32f72c31Al Virostatic char *hiddev_devnode(struct device *dev, umode_t *mode)
863f7a386c5b8ff34cd84ae922603d1c6f9d234edeeKay Sievers{
864f7a386c5b8ff34cd84ae922603d1c6f9d234edeeKay Sievers	return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
865f7a386c5b8ff34cd84ae922603d1c6f9d234edeeKay Sievers}
866f7a386c5b8ff34cd84ae922603d1c6f9d234edeeKay Sievers
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_class_driver hiddev_class = {
868d6e5bcf4a7ebbe258124a931f1449338340a99b5Greg Kroah-Hartman	.name =		"hiddev%d",
869e454cea20bdcff10ee698d11b8882662a0153a47Kay Sievers	.devnode =	hiddev_devnode,
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.fops =		&hiddev_fops,
87105f091ab4c8c1f12f8dd38ee789489904fea327dDmitry Torokhov	.minor_base =	HIDDEV_MINOR_BASE,
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is where hid.c calls us to connect a hid device to the hiddev driver
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
87793c10132a7ac160df3175b53f7ee857625412165Jiri Slabyint hiddev_connect(struct hid_device *hid, unsigned int force)
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev *hiddev;
8804916b3a57fc94664677d439b911b8aaf86c7ec23Jiri Kosina	struct usbhid_device *usbhid = hid->driver_data;
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
88393c10132a7ac160df3175b53f7ee857625412165Jiri Slaby	if (!force) {
88493c10132a7ac160df3175b53f7ee857625412165Jiri Slaby		unsigned int i;
88593c10132a7ac160df3175b53f7ee857625412165Jiri Slaby		for (i = 0; i < hid->maxcollection; i++)
88693c10132a7ac160df3175b53f7ee857625412165Jiri Slaby			if (hid->collection[i].type ==
88793c10132a7ac160df3175b53f7ee857625412165Jiri Slaby			    HID_COLLECTION_APPLICATION &&
88893c10132a7ac160df3175b53f7ee857625412165Jiri Slaby			    !IS_INPUT_APPLICATION(hid->collection[i].usage))
88993c10132a7ac160df3175b53f7ee857625412165Jiri Slaby				break;
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
89193c10132a7ac160df3175b53f7ee857625412165Jiri Slaby		if (i == hid->maxcollection)
89293c10132a7ac160df3175b53f7ee857625412165Jiri Slaby			return -1;
89393c10132a7ac160df3175b53f7ee857625412165Jiri Slaby	}
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
895bbdb7dafb5b5a3c0197cbabd5055d8e9c093e3eaOliver Neukum	if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_waitqueue_head(&hiddev->wait);
899826d598242d9200ddee63fce96f03793fddee4fcDmitry Torokhov	INIT_LIST_HEAD(&hiddev->list);
900cdcb44e87bedcf5070eece61f89f9373a3810031Jiri Kosina	spin_lock_init(&hiddev->list_lock);
901079034073faf974973baa0256b029451f6e768adOliver Neukum	mutex_init(&hiddev->existancelock);
90276052749143d03006271cc0ce8205ad756917062Jiri Kosina	hid->hiddev = hiddev;
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hiddev->hid = hid;
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hiddev->exist = 1;
905079034073faf974973baa0256b029451f6e768adOliver Neukum	retval = usb_register_dev(usbhid->intf, &hiddev_class);
906079034073faf974973baa0256b029451f6e768adOliver Neukum	if (retval) {
9074291ee305e9bb0699504a66f0e2b7aefcf0512a5Joe Perches		hid_err(hid, "Not able to get a minor for this device\n");
90876052749143d03006271cc0ce8205ad756917062Jiri Kosina		hid->hiddev = NULL;
909079034073faf974973baa0256b029451f6e768adOliver Neukum		kfree(hiddev);
910079034073faf974973baa0256b029451f6e768adOliver Neukum		return -1;
911079034073faf974973baa0256b029451f6e768adOliver Neukum	}
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is where hid.c calls us to disconnect a hiddev device from the
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * corresponding hid device (usually because the usb device has disconnected)
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_class_driver hiddev_class;
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid hiddev_disconnect(struct hid_device *hid)
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct hiddev *hiddev = hid->hiddev;
9234916b3a57fc94664677d439b911b8aaf86c7ec23Jiri Kosina	struct usbhid_device *usbhid = hid->driver_data;
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
925ba18311dff7933ccb9c41bbbb1ad3d70840069b5Ming Lei	usb_deregister_dev(usbhid->intf, &hiddev_class);
926ba18311dff7933ccb9c41bbbb1ad3d70840069b5Ming Lei
927079034073faf974973baa0256b029451f6e768adOliver Neukum	mutex_lock(&hiddev->existancelock);
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hiddev->exist = 0;
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (hiddev->open) {
9317f77897ef2b6a5ee4eb8bc24fe8b1f3eab254328Jiri Kosina		mutex_unlock(&hiddev->existancelock);
9324916b3a57fc94664677d439b911b8aaf86c7ec23Jiri Kosina		usbhid_close(hiddev->hid);
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wake_up_interruptible(&hiddev->wait);
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
9357f77897ef2b6a5ee4eb8bc24fe8b1f3eab254328Jiri Kosina		mutex_unlock(&hiddev->existancelock);
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(hiddev);
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
939