18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * HID raw devices, giving access to raw HID events. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * In comparison to hiddev, this device does not process the 68c2ecf20Sopenharmony_ci * hid events at all (no parsing, no lookups). This lets applications 78c2ecf20Sopenharmony_ci * to work on raw hid events as they want to, and avoids a need to 88c2ecf20Sopenharmony_ci * use a transport-specific userspace libhid/libusb libraries. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (c) 2007-2014 Jiri Kosina 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/fs.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/errno.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/cdev.h> 228c2ecf20Sopenharmony_ci#include <linux/poll.h> 238c2ecf20Sopenharmony_ci#include <linux/device.h> 248c2ecf20Sopenharmony_ci#include <linux/major.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/hid.h> 278c2ecf20Sopenharmony_ci#include <linux/mutex.h> 288c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 298c2ecf20Sopenharmony_ci#include <linux/string.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/hidraw.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int hidraw_major; 348c2ecf20Sopenharmony_cistatic struct cdev hidraw_cdev; 358c2ecf20Sopenharmony_cistatic struct class *hidraw_class; 368c2ecf20Sopenharmony_cistatic struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; 378c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(minors_lock); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct hidraw_list *list = file->private_data; 428c2ecf20Sopenharmony_ci int ret = 0, len; 438c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci mutex_lock(&list->read_mutex); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci while (ret == 0) { 488c2ecf20Sopenharmony_ci if (list->head == list->tail) { 498c2ecf20Sopenharmony_ci add_wait_queue(&list->hidraw->wait, &wait); 508c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci while (list->head == list->tail) { 538c2ecf20Sopenharmony_ci if (signal_pending(current)) { 548c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci if (!list->hidraw->exist) { 588c2ecf20Sopenharmony_ci ret = -EIO; 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 628c2ecf20Sopenharmony_ci ret = -EAGAIN; 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* allow O_NONBLOCK to work well from other threads */ 678c2ecf20Sopenharmony_ci mutex_unlock(&list->read_mutex); 688c2ecf20Sopenharmony_ci schedule(); 698c2ecf20Sopenharmony_ci mutex_lock(&list->read_mutex); 708c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 748c2ecf20Sopenharmony_ci remove_wait_queue(&list->hidraw->wait, &wait); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (ret) 788c2ecf20Sopenharmony_ci goto out; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci len = list->buffer[list->tail].len > count ? 818c2ecf20Sopenharmony_ci count : list->buffer[list->tail].len; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (list->buffer[list->tail].value) { 848c2ecf20Sopenharmony_ci if (copy_to_user(buffer, list->buffer[list->tail].value, len)) { 858c2ecf20Sopenharmony_ci ret = -EFAULT; 868c2ecf20Sopenharmony_ci goto out; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci ret = len; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci kfree(list->buffer[list->tail].value); 928c2ecf20Sopenharmony_ci list->buffer[list->tail].value = NULL; 938c2ecf20Sopenharmony_ci list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ciout: 968c2ecf20Sopenharmony_ci mutex_unlock(&list->read_mutex); 978c2ecf20Sopenharmony_ci return ret; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * The first byte of the report buffer is expected to be a report number. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_cistatic ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci unsigned int minor = iminor(file_inode(file)); 1068c2ecf20Sopenharmony_ci struct hid_device *dev; 1078c2ecf20Sopenharmony_ci __u8 *buf; 1088c2ecf20Sopenharmony_ci int ret = 0; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci lockdep_assert_held(&minors_lock); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { 1138c2ecf20Sopenharmony_ci ret = -ENODEV; 1148c2ecf20Sopenharmony_ci goto out; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci dev = hidraw_table[minor]->hid; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (count > HID_MAX_BUFFER_SIZE) { 1208c2ecf20Sopenharmony_ci hid_warn(dev, "pid %d passed too large report\n", 1218c2ecf20Sopenharmony_ci task_pid_nr(current)); 1228c2ecf20Sopenharmony_ci ret = -EINVAL; 1238c2ecf20Sopenharmony_ci goto out; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (count < 2) { 1278c2ecf20Sopenharmony_ci hid_warn(dev, "pid %d passed too short report\n", 1288c2ecf20Sopenharmony_ci task_pid_nr(current)); 1298c2ecf20Sopenharmony_ci ret = -EINVAL; 1308c2ecf20Sopenharmony_ci goto out; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci buf = memdup_user(buffer, count); 1348c2ecf20Sopenharmony_ci if (IS_ERR(buf)) { 1358c2ecf20Sopenharmony_ci ret = PTR_ERR(buf); 1368c2ecf20Sopenharmony_ci goto out; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if ((report_type == HID_OUTPUT_REPORT) && 1408c2ecf20Sopenharmony_ci !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) { 1418c2ecf20Sopenharmony_ci ret = hid_hw_output_report(dev, buf, count); 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * compatibility with old implementation of USB-HID and I2C-HID: 1448c2ecf20Sopenharmony_ci * if the device does not support receiving output reports, 1458c2ecf20Sopenharmony_ci * on an interrupt endpoint, fallback to SET_REPORT HID command. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci if (ret != -ENOSYS) 1488c2ecf20Sopenharmony_ci goto out_free; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type, 1528c2ecf20Sopenharmony_ci HID_REQ_SET_REPORT); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ciout_free: 1558c2ecf20Sopenharmony_ci kfree(buf); 1568c2ecf20Sopenharmony_ciout: 1578c2ecf20Sopenharmony_ci return ret; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci ssize_t ret; 1638c2ecf20Sopenharmony_ci mutex_lock(&minors_lock); 1648c2ecf20Sopenharmony_ci ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT); 1658c2ecf20Sopenharmony_ci mutex_unlock(&minors_lock); 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/* 1718c2ecf20Sopenharmony_ci * This function performs a Get_Report transfer over the control endpoint 1728c2ecf20Sopenharmony_ci * per section 7.2.1 of the HID specification, version 1.1. The first byte 1738c2ecf20Sopenharmony_ci * of buffer is the report number to request, or 0x0 if the defice does not 1748c2ecf20Sopenharmony_ci * use numbered reports. The report_type parameter can be HID_FEATURE_REPORT 1758c2ecf20Sopenharmony_ci * or HID_INPUT_REPORT. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cistatic ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci unsigned int minor = iminor(file_inode(file)); 1808c2ecf20Sopenharmony_ci struct hid_device *dev; 1818c2ecf20Sopenharmony_ci __u8 *buf; 1828c2ecf20Sopenharmony_ci int ret = 0, len; 1838c2ecf20Sopenharmony_ci unsigned char report_number; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci lockdep_assert_held(&minors_lock); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { 1888c2ecf20Sopenharmony_ci ret = -ENODEV; 1898c2ecf20Sopenharmony_ci goto out; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci dev = hidraw_table[minor]->hid; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (!dev->ll_driver->raw_request) { 1958c2ecf20Sopenharmony_ci ret = -ENODEV; 1968c2ecf20Sopenharmony_ci goto out; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (count > HID_MAX_BUFFER_SIZE) { 2008c2ecf20Sopenharmony_ci hid_warn(dev, "pid %d passed too large report\n", 2018c2ecf20Sopenharmony_ci task_pid_nr(current)); 2028c2ecf20Sopenharmony_ci ret = -EINVAL; 2038c2ecf20Sopenharmony_ci goto out; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (count < 2) { 2078c2ecf20Sopenharmony_ci hid_warn(dev, "pid %d passed too short report\n", 2088c2ecf20Sopenharmony_ci task_pid_nr(current)); 2098c2ecf20Sopenharmony_ci ret = -EINVAL; 2108c2ecf20Sopenharmony_ci goto out; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci buf = kmalloc(count, GFP_KERNEL); 2148c2ecf20Sopenharmony_ci if (!buf) { 2158c2ecf20Sopenharmony_ci ret = -ENOMEM; 2168c2ecf20Sopenharmony_ci goto out; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* 2208c2ecf20Sopenharmony_ci * Read the first byte from the user. This is the report number, 2218c2ecf20Sopenharmony_ci * which is passed to hid_hw_raw_request(). 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci if (copy_from_user(&report_number, buffer, 1)) { 2248c2ecf20Sopenharmony_ci ret = -EFAULT; 2258c2ecf20Sopenharmony_ci goto out_free; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci ret = hid_hw_raw_request(dev, report_number, buf, count, report_type, 2298c2ecf20Sopenharmony_ci HID_REQ_GET_REPORT); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (ret < 0) 2328c2ecf20Sopenharmony_ci goto out_free; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci len = (ret < count) ? ret : count; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (copy_to_user(buffer, buf, len)) { 2378c2ecf20Sopenharmony_ci ret = -EFAULT; 2388c2ecf20Sopenharmony_ci goto out_free; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ret = len; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ciout_free: 2448c2ecf20Sopenharmony_ci kfree(buf); 2458c2ecf20Sopenharmony_ciout: 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic __poll_t hidraw_poll(struct file *file, poll_table *wait) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct hidraw_list *list = file->private_data; 2528c2ecf20Sopenharmony_ci __poll_t mask = EPOLLOUT | EPOLLWRNORM; /* hidraw is always writable */ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci poll_wait(file, &list->hidraw->wait, wait); 2558c2ecf20Sopenharmony_ci if (list->head != list->tail) 2568c2ecf20Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 2578c2ecf20Sopenharmony_ci if (!list->hidraw->exist) 2588c2ecf20Sopenharmony_ci mask |= EPOLLERR | EPOLLHUP; 2598c2ecf20Sopenharmony_ci return mask; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int hidraw_open(struct inode *inode, struct file *file) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci unsigned int minor = iminor(inode); 2658c2ecf20Sopenharmony_ci struct hidraw *dev; 2668c2ecf20Sopenharmony_ci struct hidraw_list *list; 2678c2ecf20Sopenharmony_ci unsigned long flags; 2688c2ecf20Sopenharmony_ci int err = 0; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { 2718c2ecf20Sopenharmony_ci err = -ENOMEM; 2728c2ecf20Sopenharmony_ci goto out; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci mutex_lock(&minors_lock); 2768c2ecf20Sopenharmony_ci if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { 2778c2ecf20Sopenharmony_ci err = -ENODEV; 2788c2ecf20Sopenharmony_ci goto out_unlock; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci dev = hidraw_table[minor]; 2828c2ecf20Sopenharmony_ci if (!dev->open++) { 2838c2ecf20Sopenharmony_ci err = hid_hw_power(dev->hid, PM_HINT_FULLON); 2848c2ecf20Sopenharmony_ci if (err < 0) { 2858c2ecf20Sopenharmony_ci dev->open--; 2868c2ecf20Sopenharmony_ci goto out_unlock; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci err = hid_hw_open(dev->hid); 2908c2ecf20Sopenharmony_ci if (err < 0) { 2918c2ecf20Sopenharmony_ci hid_hw_power(dev->hid, PM_HINT_NORMAL); 2928c2ecf20Sopenharmony_ci dev->open--; 2938c2ecf20Sopenharmony_ci goto out_unlock; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci list->hidraw = hidraw_table[minor]; 2988c2ecf20Sopenharmony_ci mutex_init(&list->read_mutex); 2998c2ecf20Sopenharmony_ci spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); 3008c2ecf20Sopenharmony_ci list_add_tail(&list->node, &hidraw_table[minor]->list); 3018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); 3028c2ecf20Sopenharmony_ci file->private_data = list; 3038c2ecf20Sopenharmony_ciout_unlock: 3048c2ecf20Sopenharmony_ci mutex_unlock(&minors_lock); 3058c2ecf20Sopenharmony_ciout: 3068c2ecf20Sopenharmony_ci if (err < 0) 3078c2ecf20Sopenharmony_ci kfree(list); 3088c2ecf20Sopenharmony_ci return err; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int hidraw_fasync(int fd, struct file *file, int on) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct hidraw_list *list = file->private_data; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return fasync_helper(fd, file, on, &list->fasync); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic void drop_ref(struct hidraw *hidraw, int exists_bit) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci if (exists_bit) { 3228c2ecf20Sopenharmony_ci hidraw->exist = 0; 3238c2ecf20Sopenharmony_ci if (hidraw->open) { 3248c2ecf20Sopenharmony_ci hid_hw_close(hidraw->hid); 3258c2ecf20Sopenharmony_ci wake_up_interruptible(&hidraw->wait); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci device_destroy(hidraw_class, 3288c2ecf20Sopenharmony_ci MKDEV(hidraw_major, hidraw->minor)); 3298c2ecf20Sopenharmony_ci } else { 3308c2ecf20Sopenharmony_ci --hidraw->open; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci if (!hidraw->open) { 3338c2ecf20Sopenharmony_ci if (!hidraw->exist) { 3348c2ecf20Sopenharmony_ci hidraw_table[hidraw->minor] = NULL; 3358c2ecf20Sopenharmony_ci kfree(hidraw); 3368c2ecf20Sopenharmony_ci } else { 3378c2ecf20Sopenharmony_ci /* close device for last reader */ 3388c2ecf20Sopenharmony_ci hid_hw_close(hidraw->hid); 3398c2ecf20Sopenharmony_ci hid_hw_power(hidraw->hid, PM_HINT_NORMAL); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int hidraw_release(struct inode * inode, struct file * file) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci unsigned int minor = iminor(inode); 3478c2ecf20Sopenharmony_ci struct hidraw_list *list = file->private_data; 3488c2ecf20Sopenharmony_ci unsigned long flags; 3498c2ecf20Sopenharmony_ci int i; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci mutex_lock(&minors_lock); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); 3548c2ecf20Sopenharmony_ci for (i = list->tail; i < list->head; i++) 3558c2ecf20Sopenharmony_ci kfree(list->buffer[i].value); 3568c2ecf20Sopenharmony_ci list_del(&list->node); 3578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); 3588c2ecf20Sopenharmony_ci kfree(list); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci drop_ref(hidraw_table[minor], 0); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci mutex_unlock(&minors_lock); 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic long hidraw_ioctl(struct file *file, unsigned int cmd, 3678c2ecf20Sopenharmony_ci unsigned long arg) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 3708c2ecf20Sopenharmony_ci unsigned int minor = iminor(inode); 3718c2ecf20Sopenharmony_ci long ret = 0; 3728c2ecf20Sopenharmony_ci struct hidraw *dev; 3738c2ecf20Sopenharmony_ci void __user *user_arg = (void __user*) arg; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci mutex_lock(&minors_lock); 3768c2ecf20Sopenharmony_ci dev = hidraw_table[minor]; 3778c2ecf20Sopenharmony_ci if (!dev || !dev->exist) { 3788c2ecf20Sopenharmony_ci ret = -ENODEV; 3798c2ecf20Sopenharmony_ci goto out; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci switch (cmd) { 3838c2ecf20Sopenharmony_ci case HIDIOCGRDESCSIZE: 3848c2ecf20Sopenharmony_ci if (put_user(dev->hid->rsize, (int __user *)arg)) 3858c2ecf20Sopenharmony_ci ret = -EFAULT; 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci case HIDIOCGRDESC: 3898c2ecf20Sopenharmony_ci { 3908c2ecf20Sopenharmony_ci __u32 len; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (get_user(len, (int __user *)arg)) 3938c2ecf20Sopenharmony_ci ret = -EFAULT; 3948c2ecf20Sopenharmony_ci else if (len > HID_MAX_DESCRIPTOR_SIZE - 1) 3958c2ecf20Sopenharmony_ci ret = -EINVAL; 3968c2ecf20Sopenharmony_ci else if (copy_to_user(user_arg + offsetof( 3978c2ecf20Sopenharmony_ci struct hidraw_report_descriptor, 3988c2ecf20Sopenharmony_ci value[0]), 3998c2ecf20Sopenharmony_ci dev->hid->rdesc, 4008c2ecf20Sopenharmony_ci min(dev->hid->rsize, len))) 4018c2ecf20Sopenharmony_ci ret = -EFAULT; 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci case HIDIOCGRAWINFO: 4058c2ecf20Sopenharmony_ci { 4068c2ecf20Sopenharmony_ci struct hidraw_devinfo dinfo; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci dinfo.bustype = dev->hid->bus; 4098c2ecf20Sopenharmony_ci dinfo.vendor = dev->hid->vendor; 4108c2ecf20Sopenharmony_ci dinfo.product = dev->hid->product; 4118c2ecf20Sopenharmony_ci if (copy_to_user(user_arg, &dinfo, sizeof(dinfo))) 4128c2ecf20Sopenharmony_ci ret = -EFAULT; 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci default: 4168c2ecf20Sopenharmony_ci { 4178c2ecf20Sopenharmony_ci struct hid_device *hid = dev->hid; 4188c2ecf20Sopenharmony_ci if (_IOC_TYPE(cmd) != 'H') { 4198c2ecf20Sopenharmony_ci ret = -EINVAL; 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) { 4248c2ecf20Sopenharmony_ci int len = _IOC_SIZE(cmd); 4258c2ecf20Sopenharmony_ci ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT); 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) { 4298c2ecf20Sopenharmony_ci int len = _IOC_SIZE(cmd); 4308c2ecf20Sopenharmony_ci ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT); 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Begin Read-only ioctls. */ 4358c2ecf20Sopenharmony_ci if (_IOC_DIR(cmd) != _IOC_READ) { 4368c2ecf20Sopenharmony_ci ret = -EINVAL; 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) { 4418c2ecf20Sopenharmony_ci int len = strlen(hid->name) + 1; 4428c2ecf20Sopenharmony_ci if (len > _IOC_SIZE(cmd)) 4438c2ecf20Sopenharmony_ci len = _IOC_SIZE(cmd); 4448c2ecf20Sopenharmony_ci ret = copy_to_user(user_arg, hid->name, len) ? 4458c2ecf20Sopenharmony_ci -EFAULT : len; 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) { 4508c2ecf20Sopenharmony_ci int len = strlen(hid->phys) + 1; 4518c2ecf20Sopenharmony_ci if (len > _IOC_SIZE(cmd)) 4528c2ecf20Sopenharmony_ci len = _IOC_SIZE(cmd); 4538c2ecf20Sopenharmony_ci ret = copy_to_user(user_arg, hid->phys, len) ? 4548c2ecf20Sopenharmony_ci -EFAULT : len; 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWUNIQ(0))) { 4598c2ecf20Sopenharmony_ci int len = strlen(hid->uniq) + 1; 4608c2ecf20Sopenharmony_ci if (len > _IOC_SIZE(cmd)) 4618c2ecf20Sopenharmony_ci len = _IOC_SIZE(cmd); 4628c2ecf20Sopenharmony_ci ret = copy_to_user(user_arg, hid->uniq, len) ? 4638c2ecf20Sopenharmony_ci -EFAULT : len; 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci ret = -ENOTTY; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ciout: 4718c2ecf20Sopenharmony_ci mutex_unlock(&minors_lock); 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic const struct file_operations hidraw_ops = { 4768c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4778c2ecf20Sopenharmony_ci .read = hidraw_read, 4788c2ecf20Sopenharmony_ci .write = hidraw_write, 4798c2ecf20Sopenharmony_ci .poll = hidraw_poll, 4808c2ecf20Sopenharmony_ci .open = hidraw_open, 4818c2ecf20Sopenharmony_ci .release = hidraw_release, 4828c2ecf20Sopenharmony_ci .unlocked_ioctl = hidraw_ioctl, 4838c2ecf20Sopenharmony_ci .fasync = hidraw_fasync, 4848c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 4858c2ecf20Sopenharmony_ci .llseek = noop_llseek, 4868c2ecf20Sopenharmony_ci}; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ciint hidraw_report_event(struct hid_device *hid, u8 *data, int len) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct hidraw *dev = hid->hidraw; 4918c2ecf20Sopenharmony_ci struct hidraw_list *list; 4928c2ecf20Sopenharmony_ci int ret = 0; 4938c2ecf20Sopenharmony_ci unsigned long flags; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->list_lock, flags); 4968c2ecf20Sopenharmony_ci list_for_each_entry(list, &dev->list, node) { 4978c2ecf20Sopenharmony_ci int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (new_head == list->tail) 5008c2ecf20Sopenharmony_ci continue; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) { 5038c2ecf20Sopenharmony_ci ret = -ENOMEM; 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci list->buffer[list->head].len = len; 5078c2ecf20Sopenharmony_ci list->head = new_head; 5088c2ecf20Sopenharmony_ci kill_fasync(&list->fasync, SIGIO, POLL_IN); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->list_lock, flags); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci wake_up_interruptible(&dev->wait); 5138c2ecf20Sopenharmony_ci return ret; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hidraw_report_event); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ciint hidraw_connect(struct hid_device *hid) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci int minor, result; 5208c2ecf20Sopenharmony_ci struct hidraw *dev; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* we accept any HID device, all applications */ 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL); 5258c2ecf20Sopenharmony_ci if (!dev) 5268c2ecf20Sopenharmony_ci return -ENOMEM; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci result = -EINVAL; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci mutex_lock(&minors_lock); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) { 5338c2ecf20Sopenharmony_ci if (hidraw_table[minor]) 5348c2ecf20Sopenharmony_ci continue; 5358c2ecf20Sopenharmony_ci hidraw_table[minor] = dev; 5368c2ecf20Sopenharmony_ci result = 0; 5378c2ecf20Sopenharmony_ci break; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (result) { 5418c2ecf20Sopenharmony_ci mutex_unlock(&minors_lock); 5428c2ecf20Sopenharmony_ci kfree(dev); 5438c2ecf20Sopenharmony_ci goto out; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci dev->dev = device_create(hidraw_class, &hid->dev, MKDEV(hidraw_major, minor), 5478c2ecf20Sopenharmony_ci NULL, "%s%d", "hidraw", minor); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (IS_ERR(dev->dev)) { 5508c2ecf20Sopenharmony_ci hidraw_table[minor] = NULL; 5518c2ecf20Sopenharmony_ci mutex_unlock(&minors_lock); 5528c2ecf20Sopenharmony_ci result = PTR_ERR(dev->dev); 5538c2ecf20Sopenharmony_ci kfree(dev); 5548c2ecf20Sopenharmony_ci goto out; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci init_waitqueue_head(&dev->wait); 5588c2ecf20Sopenharmony_ci spin_lock_init(&dev->list_lock); 5598c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->list); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci dev->hid = hid; 5628c2ecf20Sopenharmony_ci dev->minor = minor; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci dev->exist = 1; 5658c2ecf20Sopenharmony_ci hid->hidraw = dev; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci mutex_unlock(&minors_lock); 5688c2ecf20Sopenharmony_ciout: 5698c2ecf20Sopenharmony_ci return result; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hidraw_connect); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_civoid hidraw_disconnect(struct hid_device *hid) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct hidraw *hidraw = hid->hidraw; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci mutex_lock(&minors_lock); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci drop_ref(hidraw, 1); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci mutex_unlock(&minors_lock); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hidraw_disconnect); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ciint __init hidraw_init(void) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci int result; 5898c2ecf20Sopenharmony_ci dev_t dev_id; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR, 5928c2ecf20Sopenharmony_ci HIDRAW_MAX_DEVICES, "hidraw"); 5938c2ecf20Sopenharmony_ci if (result < 0) { 5948c2ecf20Sopenharmony_ci pr_warn("can't get major number\n"); 5958c2ecf20Sopenharmony_ci goto out; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci hidraw_major = MAJOR(dev_id); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci hidraw_class = class_create(THIS_MODULE, "hidraw"); 6018c2ecf20Sopenharmony_ci if (IS_ERR(hidraw_class)) { 6028c2ecf20Sopenharmony_ci result = PTR_ERR(hidraw_class); 6038c2ecf20Sopenharmony_ci goto error_cdev; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci cdev_init(&hidraw_cdev, &hidraw_ops); 6078c2ecf20Sopenharmony_ci result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES); 6088c2ecf20Sopenharmony_ci if (result < 0) 6098c2ecf20Sopenharmony_ci goto error_class; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci pr_info("raw HID events driver (C) Jiri Kosina\n"); 6128c2ecf20Sopenharmony_ciout: 6138c2ecf20Sopenharmony_ci return result; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cierror_class: 6168c2ecf20Sopenharmony_ci class_destroy(hidraw_class); 6178c2ecf20Sopenharmony_cierror_cdev: 6188c2ecf20Sopenharmony_ci unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); 6198c2ecf20Sopenharmony_ci goto out; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_civoid hidraw_exit(void) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci dev_t dev_id = MKDEV(hidraw_major, 0); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci cdev_del(&hidraw_cdev); 6278c2ecf20Sopenharmony_ci class_destroy(hidraw_class); 6288c2ecf20Sopenharmony_ci unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci} 631