--- zzzz-none-000/linux-3.10.107/drivers/hid/hidraw.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/hid/hidraw.c 2021-02-04 17:41:59.000000000 +0000 @@ -6,7 +6,7 @@ * to work on raw hid events as they want to, and avoids a need to * use a transport-specific userspace libhid/libusb libraries. * - * Copyright (c) 2007 Jiri Kosina + * Copyright (c) 2007-2014 Jiri Kosina */ /* @@ -104,8 +104,11 @@ return ret; } -/* The first byte is expected to be a report number. - * This function is to be called with the minors_lock mutex held */ +/* + * The first byte of the report buffer is expected to be a report number. + * + * This function is to be called with the minors_lock mutex held. + */ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type) { unsigned int minor = iminor(file_inode(file)); @@ -120,10 +123,6 @@ dev = hidraw_table[minor]->hid; - if (!dev->hid_output_raw_report) { - ret = -ENODEV; - goto out; - } if (count > HID_MAX_BUFFER_SIZE) { hid_warn(dev, "pid %d passed too large report\n", @@ -150,14 +149,27 @@ goto out_free; } - ret = dev->hid_output_raw_report(dev, buf, count, report_type); + if ((report_type == HID_OUTPUT_REPORT) && + !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) { + ret = hid_hw_output_report(dev, buf, count); + /* + * compatibility with old implementation of USB-HID and I2C-HID: + * if the device does not support receiving output reports, + * on an interrupt endpoint, fallback to SET_REPORT HID command. + */ + if (ret != -ENOSYS) + goto out_free; + } + + ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type, + HID_REQ_SET_REPORT); + out_free: kfree(buf); out: return ret; } -/* the first byte is expected to be a report number */ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { ssize_t ret; @@ -168,12 +180,15 @@ } -/* This function performs a Get_Report transfer over the control endpoint +/* + * This function performs a Get_Report transfer over the control endpoint * per section 7.2.1 of the HID specification, version 1.1. The first byte * of buffer is the report number to request, or 0x0 if the defice does not * use numbered reports. The report_type parameter can be HID_FEATURE_REPORT - * or HID_INPUT_REPORT. This function is to be called with the minors_lock - * mutex held. */ + * or HID_INPUT_REPORT. + * + * This function is to be called with the minors_lock mutex held. + */ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type) { unsigned int minor = iminor(file_inode(file)); @@ -184,7 +199,7 @@ dev = hidraw_table[minor]->hid; - if (!dev->hid_get_raw_report) { + if (!dev->ll_driver->raw_request) { ret = -ENODEV; goto out; } @@ -209,14 +224,17 @@ goto out; } - /* Read the first byte from the user. This is the report number, - * which is passed to dev->hid_get_raw_report(). */ + /* + * Read the first byte from the user. This is the report number, + * which is passed to hid_hw_raw_request(). + */ if (copy_from_user(&report_number, buffer, 1)) { ret = -EFAULT; goto out_free; } - ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type); + ret = hid_hw_raw_request(dev, report_number, buf, count, report_type, + HID_REQ_GET_REPORT); if (ret < 0) goto out_free; @@ -253,6 +271,7 @@ unsigned int minor = iminor(inode); struct hidraw *dev; struct hidraw_list *list; + unsigned long flags; int err = 0; if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { @@ -266,11 +285,6 @@ goto out_unlock; } - list->hidraw = hidraw_table[minor]; - mutex_init(&list->read_mutex); - list_add_tail(&list->node, &hidraw_table[minor]->list); - file->private_data = list; - dev = hidraw_table[minor]; if (!dev->open++) { err = hid_hw_power(dev->hid, PM_HINT_FULLON); @@ -283,9 +297,16 @@ if (err < 0) { hid_hw_power(dev->hid, PM_HINT_NORMAL); dev->open--; + goto out_unlock; } } + list->hidraw = hidraw_table[minor]; + mutex_init(&list->read_mutex); + spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); + list_add_tail(&list->node, &hidraw_table[minor]->list); + spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); + file->private_data = list; out_unlock: mutex_unlock(&minors_lock); out: @@ -305,18 +326,25 @@ static void drop_ref(struct hidraw *hidraw, int exists_bit) { if (exists_bit) { - hid_hw_close(hidraw->hid); hidraw->exist = 0; - if (hidraw->open) + if (hidraw->open) { + hid_hw_close(hidraw->hid); wake_up_interruptible(&hidraw->wait); + } + device_destroy(hidraw_class, + MKDEV(hidraw_major, hidraw->minor)); } else { --hidraw->open; } - - if (!hidraw->open && !hidraw->exist) { - device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); - hidraw_table[hidraw->minor] = NULL; - kfree(hidraw); + if (!hidraw->open) { + if (!hidraw->exist) { + hidraw_table[hidraw->minor] = NULL; + kfree(hidraw); + } else { + /* close device for last reader */ + hid_hw_power(hidraw->hid, PM_HINT_NORMAL); + hid_hw_close(hidraw->hid); + } } } @@ -324,10 +352,13 @@ { unsigned int minor = iminor(inode); struct hidraw_list *list = file->private_data; + unsigned long flags; mutex_lock(&minors_lock); + spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); list_del(&list->node); + spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); kfree(list); drop_ref(hidraw_table[minor], 0); @@ -456,7 +487,9 @@ struct hidraw *dev = hid->hidraw; struct hidraw_list *list; int ret = 0; + unsigned long flags; + spin_lock_irqsave(&dev->list_lock, flags); list_for_each_entry(list, &dev->list, node) { int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); @@ -471,6 +504,7 @@ list->head = new_head; kill_fasync(&list->fasync, SIGIO, POLL_IN); } + spin_unlock_irqrestore(&dev->list_lock, flags); wake_up_interruptible(&dev->wait); return ret; @@ -482,7 +516,7 @@ int minor, result; struct hidraw *dev; - /* we accept any HID device, no matter the applications */ + /* we accept any HID device, all applications */ dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL); if (!dev) @@ -517,8 +551,8 @@ goto out; } - mutex_unlock(&minors_lock); init_waitqueue_head(&dev->wait); + spin_lock_init(&dev->list_lock); INIT_LIST_HEAD(&dev->list); dev->hid = hid; @@ -527,6 +561,7 @@ dev->exist = 1; hid->hidraw = dev; + mutex_unlock(&minors_lock); out: return result;