18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2001 Paul Stewart 48c2ecf20Sopenharmony_ci * Copyright (c) 2001 Vojtech Pavlik 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * HID char devices, giving access to raw HID device events. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Should you need to contact me, the author, you can do so either by 128c2ecf20Sopenharmony_ci * e-mail - mail your message to Paul Stewart <stewart@wetlogic.net> 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/poll.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/input.h> 218c2ecf20Sopenharmony_ci#include <linux/usb.h> 228c2ecf20Sopenharmony_ci#include <linux/hid.h> 238c2ecf20Sopenharmony_ci#include <linux/hiddev.h> 248c2ecf20Sopenharmony_ci#include <linux/compat.h> 258c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 268c2ecf20Sopenharmony_ci#include <linux/nospec.h> 278c2ecf20Sopenharmony_ci#include "usbhid.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_DYNAMIC_MINORS 308c2ecf20Sopenharmony_ci#define HIDDEV_MINOR_BASE 0 318c2ecf20Sopenharmony_ci#define HIDDEV_MINORS 256 328c2ecf20Sopenharmony_ci#else 338c2ecf20Sopenharmony_ci#define HIDDEV_MINOR_BASE 96 348c2ecf20Sopenharmony_ci#define HIDDEV_MINORS 16 358c2ecf20Sopenharmony_ci#endif 368c2ecf20Sopenharmony_ci#define HIDDEV_BUFFER_SIZE 2048 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct hiddev_list { 398c2ecf20Sopenharmony_ci struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE]; 408c2ecf20Sopenharmony_ci int head; 418c2ecf20Sopenharmony_ci int tail; 428c2ecf20Sopenharmony_ci unsigned flags; 438c2ecf20Sopenharmony_ci struct fasync_struct *fasync; 448c2ecf20Sopenharmony_ci struct hiddev *hiddev; 458c2ecf20Sopenharmony_ci struct list_head node; 468c2ecf20Sopenharmony_ci struct mutex thread_lock; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* 508c2ecf20Sopenharmony_ci * Find a report, given the report's type and ID. The ID can be specified 518c2ecf20Sopenharmony_ci * indirectly by REPORT_ID_FIRST (which returns the first report of the given 528c2ecf20Sopenharmony_ci * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the 538c2ecf20Sopenharmony_ci * given type which follows old_id. 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_cistatic struct hid_report * 568c2ecf20Sopenharmony_cihiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK; 598c2ecf20Sopenharmony_ci unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK; 608c2ecf20Sopenharmony_ci struct hid_report_enum *report_enum; 618c2ecf20Sopenharmony_ci struct hid_report *report; 628c2ecf20Sopenharmony_ci struct list_head *list; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (rinfo->report_type < HID_REPORT_TYPE_MIN || 658c2ecf20Sopenharmony_ci rinfo->report_type > HID_REPORT_TYPE_MAX) 668c2ecf20Sopenharmony_ci return NULL; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci report_enum = hid->report_enum + 698c2ecf20Sopenharmony_ci (rinfo->report_type - HID_REPORT_TYPE_MIN); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci switch (flags) { 728c2ecf20Sopenharmony_ci case 0: /* Nothing to do -- report_id is already set correctly */ 738c2ecf20Sopenharmony_ci break; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci case HID_REPORT_ID_FIRST: 768c2ecf20Sopenharmony_ci if (list_empty(&report_enum->report_list)) 778c2ecf20Sopenharmony_ci return NULL; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci list = report_enum->report_list.next; 808c2ecf20Sopenharmony_ci report = list_entry(list, struct hid_report, list); 818c2ecf20Sopenharmony_ci rinfo->report_id = report->id; 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci case HID_REPORT_ID_NEXT: 858c2ecf20Sopenharmony_ci report = report_enum->report_id_hash[rid]; 868c2ecf20Sopenharmony_ci if (!report) 878c2ecf20Sopenharmony_ci return NULL; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci list = report->list.next; 908c2ecf20Sopenharmony_ci if (list == &report_enum->report_list) 918c2ecf20Sopenharmony_ci return NULL; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci report = list_entry(list, struct hid_report, list); 948c2ecf20Sopenharmony_ci rinfo->report_id = report->id; 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci default: 988c2ecf20Sopenharmony_ci return NULL; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return report_enum->report_id_hash[rinfo->report_id]; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * Perform an exhaustive search of the report table for a usage, given its 1068c2ecf20Sopenharmony_ci * type and usage id. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistatic struct hid_field * 1098c2ecf20Sopenharmony_cihiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci int i, j; 1128c2ecf20Sopenharmony_ci struct hid_report *report; 1138c2ecf20Sopenharmony_ci struct hid_report_enum *report_enum; 1148c2ecf20Sopenharmony_ci struct hid_field *field; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (uref->report_type < HID_REPORT_TYPE_MIN || 1178c2ecf20Sopenharmony_ci uref->report_type > HID_REPORT_TYPE_MAX) 1188c2ecf20Sopenharmony_ci return NULL; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci report_enum = hid->report_enum + 1218c2ecf20Sopenharmony_ci (uref->report_type - HID_REPORT_TYPE_MIN); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci list_for_each_entry(report, &report_enum->report_list, list) { 1248c2ecf20Sopenharmony_ci for (i = 0; i < report->maxfield; i++) { 1258c2ecf20Sopenharmony_ci field = report->field[i]; 1268c2ecf20Sopenharmony_ci for (j = 0; j < field->maxusage; j++) { 1278c2ecf20Sopenharmony_ci if (field->usage[j].hid == uref->usage_code) { 1288c2ecf20Sopenharmony_ci uref->report_id = report->id; 1298c2ecf20Sopenharmony_ci uref->field_index = i; 1308c2ecf20Sopenharmony_ci uref->usage_index = j; 1318c2ecf20Sopenharmony_ci return field; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return NULL; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void hiddev_send_event(struct hid_device *hid, 1418c2ecf20Sopenharmony_ci struct hiddev_usage_ref *uref) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct hiddev *hiddev = hid->hiddev; 1448c2ecf20Sopenharmony_ci struct hiddev_list *list; 1458c2ecf20Sopenharmony_ci unsigned long flags; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci spin_lock_irqsave(&hiddev->list_lock, flags); 1488c2ecf20Sopenharmony_ci list_for_each_entry(list, &hiddev->list, node) { 1498c2ecf20Sopenharmony_ci if (uref->field_index != HID_FIELD_INDEX_NONE || 1508c2ecf20Sopenharmony_ci (list->flags & HIDDEV_FLAG_REPORT) != 0) { 1518c2ecf20Sopenharmony_ci list->buffer[list->head] = *uref; 1528c2ecf20Sopenharmony_ci list->head = (list->head + 1) & 1538c2ecf20Sopenharmony_ci (HIDDEV_BUFFER_SIZE - 1); 1548c2ecf20Sopenharmony_ci kill_fasync(&list->fasync, SIGIO, POLL_IN); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hiddev->list_lock, flags); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci wake_up_interruptible(&hiddev->wait); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* 1638c2ecf20Sopenharmony_ci * This is where hid.c calls into hiddev to pass an event that occurred over 1648c2ecf20Sopenharmony_ci * the interrupt pipe 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_civoid hiddev_hid_event(struct hid_device *hid, struct hid_field *field, 1678c2ecf20Sopenharmony_ci struct hid_usage *usage, __s32 value) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci unsigned type = field->report_type; 1708c2ecf20Sopenharmony_ci struct hiddev_usage_ref uref; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci uref.report_type = 1738c2ecf20Sopenharmony_ci (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : 1748c2ecf20Sopenharmony_ci ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : 1758c2ecf20Sopenharmony_ci ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); 1768c2ecf20Sopenharmony_ci uref.report_id = field->report->id; 1778c2ecf20Sopenharmony_ci uref.field_index = field->index; 1788c2ecf20Sopenharmony_ci uref.usage_index = (usage - field->usage); 1798c2ecf20Sopenharmony_ci uref.usage_code = usage->hid; 1808c2ecf20Sopenharmony_ci uref.value = value; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci hiddev_send_event(hid, &uref); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hiddev_hid_event); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_civoid hiddev_report_event(struct hid_device *hid, struct hid_report *report) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci unsigned type = report->type; 1898c2ecf20Sopenharmony_ci struct hiddev_usage_ref uref; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci memset(&uref, 0, sizeof(uref)); 1928c2ecf20Sopenharmony_ci uref.report_type = 1938c2ecf20Sopenharmony_ci (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : 1948c2ecf20Sopenharmony_ci ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : 1958c2ecf20Sopenharmony_ci ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); 1968c2ecf20Sopenharmony_ci uref.report_id = report->id; 1978c2ecf20Sopenharmony_ci uref.field_index = HID_FIELD_INDEX_NONE; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci hiddev_send_event(hid, &uref); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* 2038c2ecf20Sopenharmony_ci * fasync file op 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_cistatic int hiddev_fasync(int fd, struct file *file, int on) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct hiddev_list *list = file->private_data; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return fasync_helper(fd, file, on, &list->fasync); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* 2148c2ecf20Sopenharmony_ci * release file op 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cistatic int hiddev_release(struct inode * inode, struct file * file) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct hiddev_list *list = file->private_data; 2198c2ecf20Sopenharmony_ci unsigned long flags; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci spin_lock_irqsave(&list->hiddev->list_lock, flags); 2228c2ecf20Sopenharmony_ci list_del(&list->node); 2238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&list->hiddev->list_lock, flags); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci mutex_lock(&list->hiddev->existancelock); 2268c2ecf20Sopenharmony_ci if (!--list->hiddev->open) { 2278c2ecf20Sopenharmony_ci if (list->hiddev->exist) { 2288c2ecf20Sopenharmony_ci hid_hw_close(list->hiddev->hid); 2298c2ecf20Sopenharmony_ci hid_hw_power(list->hiddev->hid, PM_HINT_NORMAL); 2308c2ecf20Sopenharmony_ci } else { 2318c2ecf20Sopenharmony_ci mutex_unlock(&list->hiddev->existancelock); 2328c2ecf20Sopenharmony_ci kfree(list->hiddev); 2338c2ecf20Sopenharmony_ci vfree(list); 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci mutex_unlock(&list->hiddev->existancelock); 2398c2ecf20Sopenharmony_ci vfree(list); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int __hiddev_open(struct hiddev *hiddev, struct file *file) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct hiddev_list *list; 2478c2ecf20Sopenharmony_ci int error; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci lockdep_assert_held(&hiddev->existancelock); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci list = vzalloc(sizeof(*list)); 2528c2ecf20Sopenharmony_ci if (!list) 2538c2ecf20Sopenharmony_ci return -ENOMEM; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci mutex_init(&list->thread_lock); 2568c2ecf20Sopenharmony_ci list->hiddev = hiddev; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (!hiddev->open++) { 2598c2ecf20Sopenharmony_ci error = hid_hw_power(hiddev->hid, PM_HINT_FULLON); 2608c2ecf20Sopenharmony_ci if (error < 0) 2618c2ecf20Sopenharmony_ci goto err_drop_count; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci error = hid_hw_open(hiddev->hid); 2648c2ecf20Sopenharmony_ci if (error < 0) 2658c2ecf20Sopenharmony_ci goto err_normal_power; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci spin_lock_irq(&hiddev->list_lock); 2698c2ecf20Sopenharmony_ci list_add_tail(&list->node, &hiddev->list); 2708c2ecf20Sopenharmony_ci spin_unlock_irq(&hiddev->list_lock); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci file->private_data = list; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cierr_normal_power: 2778c2ecf20Sopenharmony_ci hid_hw_power(hiddev->hid, PM_HINT_NORMAL); 2788c2ecf20Sopenharmony_cierr_drop_count: 2798c2ecf20Sopenharmony_ci hiddev->open--; 2808c2ecf20Sopenharmony_ci vfree(list); 2818c2ecf20Sopenharmony_ci return error; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* 2858c2ecf20Sopenharmony_ci * open file op 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_cistatic int hiddev_open(struct inode *inode, struct file *file) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct usb_interface *intf; 2908c2ecf20Sopenharmony_ci struct hid_device *hid; 2918c2ecf20Sopenharmony_ci struct hiddev *hiddev; 2928c2ecf20Sopenharmony_ci int res; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci intf = usbhid_find_interface(iminor(inode)); 2958c2ecf20Sopenharmony_ci if (!intf) 2968c2ecf20Sopenharmony_ci return -ENODEV; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci hid = usb_get_intfdata(intf); 2998c2ecf20Sopenharmony_ci hiddev = hid->hiddev; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci mutex_lock(&hiddev->existancelock); 3028c2ecf20Sopenharmony_ci res = hiddev->exist ? __hiddev_open(hiddev, file) : -ENODEV; 3038c2ecf20Sopenharmony_ci mutex_unlock(&hiddev->existancelock); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return res; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci/* 3098c2ecf20Sopenharmony_ci * "write" file op 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_cistatic ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci return -EINVAL; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci/* 3178c2ecf20Sopenharmony_ci * "read" file op 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_cistatic ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 3228c2ecf20Sopenharmony_ci struct hiddev_list *list = file->private_data; 3238c2ecf20Sopenharmony_ci int event_size; 3248c2ecf20Sopenharmony_ci int retval; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ? 3278c2ecf20Sopenharmony_ci sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (count < event_size) 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* lock against other threads */ 3338c2ecf20Sopenharmony_ci retval = mutex_lock_interruptible(&list->thread_lock); 3348c2ecf20Sopenharmony_ci if (retval) 3358c2ecf20Sopenharmony_ci return -ERESTARTSYS; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci while (retval == 0) { 3388c2ecf20Sopenharmony_ci if (list->head == list->tail) { 3398c2ecf20Sopenharmony_ci prepare_to_wait(&list->hiddev->wait, &wait, TASK_INTERRUPTIBLE); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci while (list->head == list->tail) { 3428c2ecf20Sopenharmony_ci if (signal_pending(current)) { 3438c2ecf20Sopenharmony_ci retval = -ERESTARTSYS; 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci if (!list->hiddev->exist) { 3478c2ecf20Sopenharmony_ci retval = -EIO; 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 3518c2ecf20Sopenharmony_ci retval = -EAGAIN; 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* let O_NONBLOCK tasks run */ 3568c2ecf20Sopenharmony_ci mutex_unlock(&list->thread_lock); 3578c2ecf20Sopenharmony_ci schedule(); 3588c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&list->thread_lock)) { 3598c2ecf20Sopenharmony_ci finish_wait(&list->hiddev->wait, &wait); 3608c2ecf20Sopenharmony_ci return -EINTR; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci finish_wait(&list->hiddev->wait, &wait); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (retval) { 3698c2ecf20Sopenharmony_ci mutex_unlock(&list->thread_lock); 3708c2ecf20Sopenharmony_ci return retval; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci while (list->head != list->tail && 3758c2ecf20Sopenharmony_ci retval + event_size <= count) { 3768c2ecf20Sopenharmony_ci if ((list->flags & HIDDEV_FLAG_UREF) == 0) { 3778c2ecf20Sopenharmony_ci if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE) { 3788c2ecf20Sopenharmony_ci struct hiddev_event event; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci event.hid = list->buffer[list->tail].usage_code; 3818c2ecf20Sopenharmony_ci event.value = list->buffer[list->tail].value; 3828c2ecf20Sopenharmony_ci if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) { 3838c2ecf20Sopenharmony_ci mutex_unlock(&list->thread_lock); 3848c2ecf20Sopenharmony_ci return -EFAULT; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci retval += sizeof(struct hiddev_event); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci } else { 3898c2ecf20Sopenharmony_ci if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE || 3908c2ecf20Sopenharmony_ci (list->flags & HIDDEV_FLAG_REPORT) != 0) { 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) { 3938c2ecf20Sopenharmony_ci mutex_unlock(&list->thread_lock); 3948c2ecf20Sopenharmony_ci return -EFAULT; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci retval += sizeof(struct hiddev_usage_ref); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1); 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci mutex_unlock(&list->thread_lock); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return retval; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci/* 4098c2ecf20Sopenharmony_ci * "poll" file op 4108c2ecf20Sopenharmony_ci * No kernel lock - fine 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_cistatic __poll_t hiddev_poll(struct file *file, poll_table *wait) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct hiddev_list *list = file->private_data; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci poll_wait(file, &list->hiddev->wait, wait); 4178c2ecf20Sopenharmony_ci if (list->head != list->tail) 4188c2ecf20Sopenharmony_ci return EPOLLIN | EPOLLRDNORM | EPOLLOUT; 4198c2ecf20Sopenharmony_ci if (!list->hiddev->exist) 4208c2ecf20Sopenharmony_ci return EPOLLERR | EPOLLHUP; 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci/* 4258c2ecf20Sopenharmony_ci * "ioctl" file op 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_cistatic noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct hid_device *hid = hiddev->hid; 4308c2ecf20Sopenharmony_ci struct hiddev_report_info rinfo; 4318c2ecf20Sopenharmony_ci struct hiddev_usage_ref_multi *uref_multi = NULL; 4328c2ecf20Sopenharmony_ci struct hiddev_usage_ref *uref; 4338c2ecf20Sopenharmony_ci struct hid_report *report; 4348c2ecf20Sopenharmony_ci struct hid_field *field; 4358c2ecf20Sopenharmony_ci int i; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); 4388c2ecf20Sopenharmony_ci if (!uref_multi) 4398c2ecf20Sopenharmony_ci return -ENOMEM; 4408c2ecf20Sopenharmony_ci uref = &uref_multi->uref; 4418c2ecf20Sopenharmony_ci if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { 4428c2ecf20Sopenharmony_ci if (copy_from_user(uref_multi, user_arg, 4438c2ecf20Sopenharmony_ci sizeof(*uref_multi))) 4448c2ecf20Sopenharmony_ci goto fault; 4458c2ecf20Sopenharmony_ci } else { 4468c2ecf20Sopenharmony_ci if (copy_from_user(uref, user_arg, sizeof(*uref))) 4478c2ecf20Sopenharmony_ci goto fault; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci switch (cmd) { 4518c2ecf20Sopenharmony_ci case HIDIOCGUCODE: 4528c2ecf20Sopenharmony_ci rinfo.report_type = uref->report_type; 4538c2ecf20Sopenharmony_ci rinfo.report_id = uref->report_id; 4548c2ecf20Sopenharmony_ci if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) 4558c2ecf20Sopenharmony_ci goto inval; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (uref->field_index >= report->maxfield) 4588c2ecf20Sopenharmony_ci goto inval; 4598c2ecf20Sopenharmony_ci uref->field_index = array_index_nospec(uref->field_index, 4608c2ecf20Sopenharmony_ci report->maxfield); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci field = report->field[uref->field_index]; 4638c2ecf20Sopenharmony_ci if (uref->usage_index >= field->maxusage) 4648c2ecf20Sopenharmony_ci goto inval; 4658c2ecf20Sopenharmony_ci uref->usage_index = array_index_nospec(uref->usage_index, 4668c2ecf20Sopenharmony_ci field->maxusage); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci uref->usage_code = field->usage[uref->usage_index].hid; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (copy_to_user(user_arg, uref, sizeof(*uref))) 4718c2ecf20Sopenharmony_ci goto fault; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci goto goodreturn; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci default: 4768c2ecf20Sopenharmony_ci if (cmd != HIDIOCGUSAGE && 4778c2ecf20Sopenharmony_ci cmd != HIDIOCGUSAGES && 4788c2ecf20Sopenharmony_ci uref->report_type == HID_REPORT_TYPE_INPUT) 4798c2ecf20Sopenharmony_ci goto inval; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (uref->report_id == HID_REPORT_ID_UNKNOWN) { 4828c2ecf20Sopenharmony_ci field = hiddev_lookup_usage(hid, uref); 4838c2ecf20Sopenharmony_ci if (field == NULL) 4848c2ecf20Sopenharmony_ci goto inval; 4858c2ecf20Sopenharmony_ci } else { 4868c2ecf20Sopenharmony_ci rinfo.report_type = uref->report_type; 4878c2ecf20Sopenharmony_ci rinfo.report_id = uref->report_id; 4888c2ecf20Sopenharmony_ci if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) 4898c2ecf20Sopenharmony_ci goto inval; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (uref->field_index >= report->maxfield) 4928c2ecf20Sopenharmony_ci goto inval; 4938c2ecf20Sopenharmony_ci uref->field_index = array_index_nospec(uref->field_index, 4948c2ecf20Sopenharmony_ci report->maxfield); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci field = report->field[uref->field_index]; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (cmd == HIDIOCGCOLLECTIONINDEX) { 4998c2ecf20Sopenharmony_ci if (uref->usage_index >= field->maxusage) 5008c2ecf20Sopenharmony_ci goto inval; 5018c2ecf20Sopenharmony_ci uref->usage_index = 5028c2ecf20Sopenharmony_ci array_index_nospec(uref->usage_index, 5038c2ecf20Sopenharmony_ci field->maxusage); 5048c2ecf20Sopenharmony_ci } else if (uref->usage_index >= field->report_count) 5058c2ecf20Sopenharmony_ci goto inval; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { 5098c2ecf20Sopenharmony_ci if (uref_multi->num_values > HID_MAX_MULTI_USAGES || 5108c2ecf20Sopenharmony_ci uref->usage_index + uref_multi->num_values > 5118c2ecf20Sopenharmony_ci field->report_count) 5128c2ecf20Sopenharmony_ci goto inval; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci uref->usage_index = 5158c2ecf20Sopenharmony_ci array_index_nospec(uref->usage_index, 5168c2ecf20Sopenharmony_ci field->report_count - 5178c2ecf20Sopenharmony_ci uref_multi->num_values); 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci switch (cmd) { 5218c2ecf20Sopenharmony_ci case HIDIOCGUSAGE: 5228c2ecf20Sopenharmony_ci if (uref->usage_index >= field->report_count) 5238c2ecf20Sopenharmony_ci goto inval; 5248c2ecf20Sopenharmony_ci uref->value = field->value[uref->usage_index]; 5258c2ecf20Sopenharmony_ci if (copy_to_user(user_arg, uref, sizeof(*uref))) 5268c2ecf20Sopenharmony_ci goto fault; 5278c2ecf20Sopenharmony_ci goto goodreturn; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci case HIDIOCSUSAGE: 5308c2ecf20Sopenharmony_ci if (uref->usage_index >= field->report_count) 5318c2ecf20Sopenharmony_ci goto inval; 5328c2ecf20Sopenharmony_ci field->value[uref->usage_index] = uref->value; 5338c2ecf20Sopenharmony_ci goto goodreturn; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci case HIDIOCGCOLLECTIONINDEX: 5368c2ecf20Sopenharmony_ci i = field->usage[uref->usage_index].collection_index; 5378c2ecf20Sopenharmony_ci kfree(uref_multi); 5388c2ecf20Sopenharmony_ci return i; 5398c2ecf20Sopenharmony_ci case HIDIOCGUSAGES: 5408c2ecf20Sopenharmony_ci for (i = 0; i < uref_multi->num_values; i++) 5418c2ecf20Sopenharmony_ci uref_multi->values[i] = 5428c2ecf20Sopenharmony_ci field->value[uref->usage_index + i]; 5438c2ecf20Sopenharmony_ci if (copy_to_user(user_arg, uref_multi, 5448c2ecf20Sopenharmony_ci sizeof(*uref_multi))) 5458c2ecf20Sopenharmony_ci goto fault; 5468c2ecf20Sopenharmony_ci goto goodreturn; 5478c2ecf20Sopenharmony_ci case HIDIOCSUSAGES: 5488c2ecf20Sopenharmony_ci for (i = 0; i < uref_multi->num_values; i++) 5498c2ecf20Sopenharmony_ci field->value[uref->usage_index + i] = 5508c2ecf20Sopenharmony_ci uref_multi->values[i]; 5518c2ecf20Sopenharmony_ci goto goodreturn; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cigoodreturn: 5558c2ecf20Sopenharmony_ci kfree(uref_multi); 5568c2ecf20Sopenharmony_ci return 0; 5578c2ecf20Sopenharmony_cifault: 5588c2ecf20Sopenharmony_ci kfree(uref_multi); 5598c2ecf20Sopenharmony_ci return -EFAULT; 5608c2ecf20Sopenharmony_ciinval: 5618c2ecf20Sopenharmony_ci kfree(uref_multi); 5628c2ecf20Sopenharmony_ci return -EINVAL; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic noinline int hiddev_ioctl_string(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci struct hid_device *hid = hiddev->hid; 5698c2ecf20Sopenharmony_ci struct usb_device *dev = hid_to_usb_dev(hid); 5708c2ecf20Sopenharmony_ci int idx, len; 5718c2ecf20Sopenharmony_ci char *buf; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (get_user(idx, (int __user *)user_arg)) 5748c2ecf20Sopenharmony_ci return -EFAULT; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL) 5778c2ecf20Sopenharmony_ci return -ENOMEM; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) { 5808c2ecf20Sopenharmony_ci kfree(buf); 5818c2ecf20Sopenharmony_ci return -EINVAL; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (copy_to_user(user_arg+sizeof(int), buf, len+1)) { 5858c2ecf20Sopenharmony_ci kfree(buf); 5868c2ecf20Sopenharmony_ci return -EFAULT; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci kfree(buf); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return len; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct hiddev_list *list = file->private_data; 5978c2ecf20Sopenharmony_ci struct hiddev *hiddev = list->hiddev; 5988c2ecf20Sopenharmony_ci struct hid_device *hid; 5998c2ecf20Sopenharmony_ci struct hiddev_collection_info cinfo; 6008c2ecf20Sopenharmony_ci struct hiddev_report_info rinfo; 6018c2ecf20Sopenharmony_ci struct hiddev_field_info finfo; 6028c2ecf20Sopenharmony_ci struct hiddev_devinfo dinfo; 6038c2ecf20Sopenharmony_ci struct hid_report *report; 6048c2ecf20Sopenharmony_ci struct hid_field *field; 6058c2ecf20Sopenharmony_ci void __user *user_arg = (void __user *)arg; 6068c2ecf20Sopenharmony_ci int i, r = -EINVAL; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* Called without BKL by compat methods so no BKL taken */ 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci mutex_lock(&hiddev->existancelock); 6118c2ecf20Sopenharmony_ci if (!hiddev->exist) { 6128c2ecf20Sopenharmony_ci r = -ENODEV; 6138c2ecf20Sopenharmony_ci goto ret_unlock; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci hid = hiddev->hid; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci switch (cmd) { 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci case HIDIOCGVERSION: 6218c2ecf20Sopenharmony_ci r = put_user(HID_VERSION, (int __user *)arg) ? 6228c2ecf20Sopenharmony_ci -EFAULT : 0; 6238c2ecf20Sopenharmony_ci break; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci case HIDIOCAPPLICATION: 6268c2ecf20Sopenharmony_ci if (arg >= hid->maxapplication) 6278c2ecf20Sopenharmony_ci break; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci for (i = 0; i < hid->maxcollection; i++) 6308c2ecf20Sopenharmony_ci if (hid->collection[i].type == 6318c2ecf20Sopenharmony_ci HID_COLLECTION_APPLICATION && arg-- == 0) 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (i < hid->maxcollection) 6358c2ecf20Sopenharmony_ci r = hid->collection[i].usage; 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci case HIDIOCGDEVINFO: 6398c2ecf20Sopenharmony_ci { 6408c2ecf20Sopenharmony_ci struct usb_device *dev = hid_to_usb_dev(hid); 6418c2ecf20Sopenharmony_ci struct usbhid_device *usbhid = hid->driver_data; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci memset(&dinfo, 0, sizeof(dinfo)); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci dinfo.bustype = BUS_USB; 6468c2ecf20Sopenharmony_ci dinfo.busnum = dev->bus->busnum; 6478c2ecf20Sopenharmony_ci dinfo.devnum = dev->devnum; 6488c2ecf20Sopenharmony_ci dinfo.ifnum = usbhid->ifnum; 6498c2ecf20Sopenharmony_ci dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor); 6508c2ecf20Sopenharmony_ci dinfo.product = le16_to_cpu(dev->descriptor.idProduct); 6518c2ecf20Sopenharmony_ci dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice); 6528c2ecf20Sopenharmony_ci dinfo.num_applications = hid->maxapplication; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci r = copy_to_user(user_arg, &dinfo, sizeof(dinfo)) ? 6558c2ecf20Sopenharmony_ci -EFAULT : 0; 6568c2ecf20Sopenharmony_ci break; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci case HIDIOCGFLAG: 6608c2ecf20Sopenharmony_ci r = put_user(list->flags, (int __user *)arg) ? 6618c2ecf20Sopenharmony_ci -EFAULT : 0; 6628c2ecf20Sopenharmony_ci break; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci case HIDIOCSFLAG: 6658c2ecf20Sopenharmony_ci { 6668c2ecf20Sopenharmony_ci int newflags; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (get_user(newflags, (int __user *)arg)) { 6698c2ecf20Sopenharmony_ci r = -EFAULT; 6708c2ecf20Sopenharmony_ci break; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if ((newflags & ~HIDDEV_FLAGS) != 0 || 6748c2ecf20Sopenharmony_ci ((newflags & HIDDEV_FLAG_REPORT) != 0 && 6758c2ecf20Sopenharmony_ci (newflags & HIDDEV_FLAG_UREF) == 0)) 6768c2ecf20Sopenharmony_ci break; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci list->flags = newflags; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci r = 0; 6818c2ecf20Sopenharmony_ci break; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci case HIDIOCGSTRING: 6858c2ecf20Sopenharmony_ci r = hiddev_ioctl_string(hiddev, cmd, user_arg); 6868c2ecf20Sopenharmony_ci break; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci case HIDIOCINITREPORT: 6898c2ecf20Sopenharmony_ci usbhid_init_reports(hid); 6908c2ecf20Sopenharmony_ci hiddev->initialized = true; 6918c2ecf20Sopenharmony_ci r = 0; 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci case HIDIOCGREPORT: 6958c2ecf20Sopenharmony_ci if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) { 6968c2ecf20Sopenharmony_ci r = -EFAULT; 6978c2ecf20Sopenharmony_ci break; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT) 7018c2ecf20Sopenharmony_ci break; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci report = hiddev_lookup_report(hid, &rinfo); 7048c2ecf20Sopenharmony_ci if (report == NULL) 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci hid_hw_request(hid, report, HID_REQ_GET_REPORT); 7088c2ecf20Sopenharmony_ci hid_hw_wait(hid); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci r = 0; 7118c2ecf20Sopenharmony_ci break; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci case HIDIOCSREPORT: 7148c2ecf20Sopenharmony_ci if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) { 7158c2ecf20Sopenharmony_ci r = -EFAULT; 7168c2ecf20Sopenharmony_ci break; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci if (rinfo.report_type == HID_REPORT_TYPE_INPUT) 7208c2ecf20Sopenharmony_ci break; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci report = hiddev_lookup_report(hid, &rinfo); 7238c2ecf20Sopenharmony_ci if (report == NULL) 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci hid_hw_request(hid, report, HID_REQ_SET_REPORT); 7278c2ecf20Sopenharmony_ci hid_hw_wait(hid); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci r = 0; 7308c2ecf20Sopenharmony_ci break; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci case HIDIOCGREPORTINFO: 7338c2ecf20Sopenharmony_ci if (copy_from_user(&rinfo, user_arg, sizeof(rinfo))) { 7348c2ecf20Sopenharmony_ci r = -EFAULT; 7358c2ecf20Sopenharmony_ci break; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci report = hiddev_lookup_report(hid, &rinfo); 7398c2ecf20Sopenharmony_ci if (report == NULL) 7408c2ecf20Sopenharmony_ci break; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci rinfo.num_fields = report->maxfield; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci r = copy_to_user(user_arg, &rinfo, sizeof(rinfo)) ? 7458c2ecf20Sopenharmony_ci -EFAULT : 0; 7468c2ecf20Sopenharmony_ci break; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci case HIDIOCGFIELDINFO: 7498c2ecf20Sopenharmony_ci if (copy_from_user(&finfo, user_arg, sizeof(finfo))) { 7508c2ecf20Sopenharmony_ci r = -EFAULT; 7518c2ecf20Sopenharmony_ci break; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci rinfo.report_type = finfo.report_type; 7558c2ecf20Sopenharmony_ci rinfo.report_id = finfo.report_id; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci report = hiddev_lookup_report(hid, &rinfo); 7588c2ecf20Sopenharmony_ci if (report == NULL) 7598c2ecf20Sopenharmony_ci break; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci if (finfo.field_index >= report->maxfield) 7628c2ecf20Sopenharmony_ci break; 7638c2ecf20Sopenharmony_ci finfo.field_index = array_index_nospec(finfo.field_index, 7648c2ecf20Sopenharmony_ci report->maxfield); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci field = report->field[finfo.field_index]; 7678c2ecf20Sopenharmony_ci memset(&finfo, 0, sizeof(finfo)); 7688c2ecf20Sopenharmony_ci finfo.report_type = rinfo.report_type; 7698c2ecf20Sopenharmony_ci finfo.report_id = rinfo.report_id; 7708c2ecf20Sopenharmony_ci finfo.field_index = field->report_count - 1; 7718c2ecf20Sopenharmony_ci finfo.maxusage = field->maxusage; 7728c2ecf20Sopenharmony_ci finfo.flags = field->flags; 7738c2ecf20Sopenharmony_ci finfo.physical = field->physical; 7748c2ecf20Sopenharmony_ci finfo.logical = field->logical; 7758c2ecf20Sopenharmony_ci finfo.application = field->application; 7768c2ecf20Sopenharmony_ci finfo.logical_minimum = field->logical_minimum; 7778c2ecf20Sopenharmony_ci finfo.logical_maximum = field->logical_maximum; 7788c2ecf20Sopenharmony_ci finfo.physical_minimum = field->physical_minimum; 7798c2ecf20Sopenharmony_ci finfo.physical_maximum = field->physical_maximum; 7808c2ecf20Sopenharmony_ci finfo.unit_exponent = field->unit_exponent; 7818c2ecf20Sopenharmony_ci finfo.unit = field->unit; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci r = copy_to_user(user_arg, &finfo, sizeof(finfo)) ? 7848c2ecf20Sopenharmony_ci -EFAULT : 0; 7858c2ecf20Sopenharmony_ci break; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci case HIDIOCGUCODE: 7888c2ecf20Sopenharmony_ci case HIDIOCGUSAGE: 7898c2ecf20Sopenharmony_ci case HIDIOCSUSAGE: 7908c2ecf20Sopenharmony_ci case HIDIOCGUSAGES: 7918c2ecf20Sopenharmony_ci case HIDIOCSUSAGES: 7928c2ecf20Sopenharmony_ci case HIDIOCGCOLLECTIONINDEX: 7938c2ecf20Sopenharmony_ci if (!hiddev->initialized) { 7948c2ecf20Sopenharmony_ci usbhid_init_reports(hid); 7958c2ecf20Sopenharmony_ci hiddev->initialized = true; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci r = hiddev_ioctl_usage(hiddev, cmd, user_arg); 7988c2ecf20Sopenharmony_ci break; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci case HIDIOCGCOLLECTIONINFO: 8018c2ecf20Sopenharmony_ci if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) { 8028c2ecf20Sopenharmony_ci r = -EFAULT; 8038c2ecf20Sopenharmony_ci break; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (cinfo.index >= hid->maxcollection) 8078c2ecf20Sopenharmony_ci break; 8088c2ecf20Sopenharmony_ci cinfo.index = array_index_nospec(cinfo.index, 8098c2ecf20Sopenharmony_ci hid->maxcollection); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci cinfo.type = hid->collection[cinfo.index].type; 8128c2ecf20Sopenharmony_ci cinfo.usage = hid->collection[cinfo.index].usage; 8138c2ecf20Sopenharmony_ci cinfo.level = hid->collection[cinfo.index].level; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci r = copy_to_user(user_arg, &cinfo, sizeof(cinfo)) ? 8168c2ecf20Sopenharmony_ci -EFAULT : 0; 8178c2ecf20Sopenharmony_ci break; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci default: 8208c2ecf20Sopenharmony_ci if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) 8218c2ecf20Sopenharmony_ci break; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) { 8248c2ecf20Sopenharmony_ci int len = strlen(hid->name) + 1; 8258c2ecf20Sopenharmony_ci if (len > _IOC_SIZE(cmd)) 8268c2ecf20Sopenharmony_ci len = _IOC_SIZE(cmd); 8278c2ecf20Sopenharmony_ci r = copy_to_user(user_arg, hid->name, len) ? 8288c2ecf20Sopenharmony_ci -EFAULT : len; 8298c2ecf20Sopenharmony_ci break; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) { 8338c2ecf20Sopenharmony_ci int len = strlen(hid->phys) + 1; 8348c2ecf20Sopenharmony_ci if (len > _IOC_SIZE(cmd)) 8358c2ecf20Sopenharmony_ci len = _IOC_SIZE(cmd); 8368c2ecf20Sopenharmony_ci r = copy_to_user(user_arg, hid->phys, len) ? 8378c2ecf20Sopenharmony_ci -EFAULT : len; 8388c2ecf20Sopenharmony_ci break; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ciret_unlock: 8438c2ecf20Sopenharmony_ci mutex_unlock(&hiddev->existancelock); 8448c2ecf20Sopenharmony_ci return r; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic const struct file_operations hiddev_fops = { 8488c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 8498c2ecf20Sopenharmony_ci .read = hiddev_read, 8508c2ecf20Sopenharmony_ci .write = hiddev_write, 8518c2ecf20Sopenharmony_ci .poll = hiddev_poll, 8528c2ecf20Sopenharmony_ci .open = hiddev_open, 8538c2ecf20Sopenharmony_ci .release = hiddev_release, 8548c2ecf20Sopenharmony_ci .unlocked_ioctl = hiddev_ioctl, 8558c2ecf20Sopenharmony_ci .fasync = hiddev_fasync, 8568c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 8578c2ecf20Sopenharmony_ci .llseek = noop_llseek, 8588c2ecf20Sopenharmony_ci}; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic char *hiddev_devnode(struct device *dev, umode_t *mode) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic struct usb_class_driver hiddev_class = { 8668c2ecf20Sopenharmony_ci .name = "hiddev%d", 8678c2ecf20Sopenharmony_ci .devnode = hiddev_devnode, 8688c2ecf20Sopenharmony_ci .fops = &hiddev_fops, 8698c2ecf20Sopenharmony_ci .minor_base = HIDDEV_MINOR_BASE, 8708c2ecf20Sopenharmony_ci}; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci/* 8738c2ecf20Sopenharmony_ci * This is where hid.c calls us to connect a hid device to the hiddev driver 8748c2ecf20Sopenharmony_ci */ 8758c2ecf20Sopenharmony_ciint hiddev_connect(struct hid_device *hid, unsigned int force) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci struct hiddev *hiddev; 8788c2ecf20Sopenharmony_ci struct usbhid_device *usbhid = hid->driver_data; 8798c2ecf20Sopenharmony_ci int retval; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci if (!force) { 8828c2ecf20Sopenharmony_ci unsigned int i; 8838c2ecf20Sopenharmony_ci for (i = 0; i < hid->maxcollection; i++) 8848c2ecf20Sopenharmony_ci if (hid->collection[i].type == 8858c2ecf20Sopenharmony_ci HID_COLLECTION_APPLICATION && 8868c2ecf20Sopenharmony_ci !IS_INPUT_APPLICATION(hid->collection[i].usage)) 8878c2ecf20Sopenharmony_ci break; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (i == hid->maxcollection) 8908c2ecf20Sopenharmony_ci return -1; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) 8948c2ecf20Sopenharmony_ci return -1; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci init_waitqueue_head(&hiddev->wait); 8978c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&hiddev->list); 8988c2ecf20Sopenharmony_ci spin_lock_init(&hiddev->list_lock); 8998c2ecf20Sopenharmony_ci mutex_init(&hiddev->existancelock); 9008c2ecf20Sopenharmony_ci hid->hiddev = hiddev; 9018c2ecf20Sopenharmony_ci hiddev->hid = hid; 9028c2ecf20Sopenharmony_ci hiddev->exist = 1; 9038c2ecf20Sopenharmony_ci retval = usb_register_dev(usbhid->intf, &hiddev_class); 9048c2ecf20Sopenharmony_ci if (retval) { 9058c2ecf20Sopenharmony_ci hid_err(hid, "Not able to get a minor for this device\n"); 9068c2ecf20Sopenharmony_ci hid->hiddev = NULL; 9078c2ecf20Sopenharmony_ci kfree(hiddev); 9088c2ecf20Sopenharmony_ci return -1; 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci /* 9128c2ecf20Sopenharmony_ci * If HID_QUIRK_NO_INIT_REPORTS is set, make sure we don't initialize 9138c2ecf20Sopenharmony_ci * the reports. 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci hiddev->initialized = hid->quirks & HID_QUIRK_NO_INIT_REPORTS; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci hiddev->minor = usbhid->intf->minor; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci return 0; 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci/* 9238c2ecf20Sopenharmony_ci * This is where hid.c calls us to disconnect a hiddev device from the 9248c2ecf20Sopenharmony_ci * corresponding hid device (usually because the usb device has disconnected) 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_cistatic struct usb_class_driver hiddev_class; 9278c2ecf20Sopenharmony_civoid hiddev_disconnect(struct hid_device *hid) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct hiddev *hiddev = hid->hiddev; 9308c2ecf20Sopenharmony_ci struct usbhid_device *usbhid = hid->driver_data; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci usb_deregister_dev(usbhid->intf, &hiddev_class); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci mutex_lock(&hiddev->existancelock); 9358c2ecf20Sopenharmony_ci hiddev->exist = 0; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (hiddev->open) { 9388c2ecf20Sopenharmony_ci hid_hw_close(hiddev->hid); 9398c2ecf20Sopenharmony_ci wake_up_interruptible(&hiddev->wait); 9408c2ecf20Sopenharmony_ci mutex_unlock(&hiddev->existancelock); 9418c2ecf20Sopenharmony_ci } else { 9428c2ecf20Sopenharmony_ci mutex_unlock(&hiddev->existancelock); 9438c2ecf20Sopenharmony_ci kfree(hiddev); 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci} 946