Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: Add Support for Setting and Getting Feature Reports from hidraw

Per the HID Specification, Feature reports must be sent and received on
the Configuration endpoint (EP 0) through the Set_Report/Get_Report
interfaces. This patch adds two ioctls to hidraw to set and get feature
reports to and from the device. Modifications were made to hidraw and
usbhid.

New hidraw ioctls:
HIDIOCSFEATURE - Perform a Set_Report transfer of a Feature report.
HIDIOCGFEATURE - Perform a Get_Report transfer of a Feature report.

Signed-off-by: Alan Ott <alan@signal11.us>
Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Alan Ott and committed by
Jiri Kosina
b4dbde9d 0825411a

+141 -6
+100 -6
drivers/hid/hidraw.c
··· 102 102 } 103 103 104 104 /* the first byte is expected to be a report number */ 105 - static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 105 + /* This function is to be called with the minors_lock mutex held */ 106 + static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type) 106 107 { 107 108 unsigned int minor = iminor(file->f_path.dentry->d_inode); 108 109 struct hid_device *dev; 109 110 __u8 *buf; 110 111 int ret = 0; 111 - 112 - mutex_lock(&minors_lock); 113 112 114 113 if (!hidraw_table[minor]) { 115 114 ret = -ENODEV; ··· 147 148 goto out_free; 148 149 } 149 150 150 - ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT); 151 + ret = dev->hid_output_raw_report(dev, buf, count, report_type); 151 152 out_free: 152 153 kfree(buf); 153 154 out: 155 + return ret; 156 + } 157 + 158 + /* the first byte is expected to be a report number */ 159 + static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 160 + { 161 + ssize_t ret; 162 + mutex_lock(&minors_lock); 163 + ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT); 154 164 mutex_unlock(&minors_lock); 165 + return ret; 166 + } 167 + 168 + 169 + /* This function performs a Get_Report transfer over the control endpoint 170 + per section 7.2.1 of the HID specification, version 1.1. The first byte 171 + of buffer is the report number to request, or 0x0 if the defice does not 172 + use numbered reports. The report_type parameter can be HID_FEATURE_REPORT 173 + or HID_INPUT_REPORT. This function is to be called with the minors_lock 174 + mutex held. */ 175 + static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type) 176 + { 177 + unsigned int minor = iminor(file->f_path.dentry->d_inode); 178 + struct hid_device *dev; 179 + __u8 *buf; 180 + int ret = 0, len; 181 + unsigned char report_number; 182 + 183 + dev = hidraw_table[minor]->hid; 184 + 185 + if (!dev->hid_get_raw_report) { 186 + ret = -ENODEV; 187 + goto out; 188 + } 189 + 190 + if (count > HID_MAX_BUFFER_SIZE) { 191 + printk(KERN_WARNING "hidraw: pid %d passed too large report\n", 192 + task_pid_nr(current)); 193 + ret = -EINVAL; 194 + goto out; 195 + } 196 + 197 + if (count < 2) { 198 + printk(KERN_WARNING "hidraw: pid %d passed too short report\n", 199 + task_pid_nr(current)); 200 + ret = -EINVAL; 201 + goto out; 202 + } 203 + 204 + buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); 205 + if (!buf) { 206 + ret = -ENOMEM; 207 + goto out; 208 + } 209 + 210 + /* Read the first byte from the user. This is the report number, 211 + which is passed to dev->hid_get_raw_report(). */ 212 + if (copy_from_user(&report_number, buffer, 1)) { 213 + ret = -EFAULT; 214 + goto out_free; 215 + } 216 + 217 + ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type); 218 + 219 + if (ret < 0) 220 + goto out_free; 221 + 222 + len = (ret < count) ? ret : count; 223 + 224 + if (copy_to_user(buffer, buf, len)) { 225 + ret = -EFAULT; 226 + goto out_free; 227 + } 228 + 229 + ret = len; 230 + 231 + out_free: 232 + kfree(buf); 233 + out: 155 234 return ret; 156 235 } 157 236 ··· 372 295 default: 373 296 { 374 297 struct hid_device *hid = dev->hid; 375 - if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) { 298 + if (_IOC_TYPE(cmd) != 'H') { 299 + ret = -EINVAL; 300 + break; 301 + } 302 + 303 + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) { 304 + int len = _IOC_SIZE(cmd); 305 + ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT); 306 + break; 307 + } 308 + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) { 309 + int len = _IOC_SIZE(cmd); 310 + ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT); 311 + break; 312 + } 313 + 314 + /* Begin Read-only ioctls. */ 315 + if (_IOC_DIR(cmd) != _IOC_READ) { 376 316 ret = -EINVAL; 377 317 break; 378 318 } ··· 421 327 -EFAULT : len; 422 328 break; 423 329 } 424 - } 330 + } 425 331 426 332 ret = -ENOTTY; 427 333 }
+35
drivers/hid/usbhid/hid-core.c
··· 799 799 return 0; 800 800 } 801 801 802 + static int usbhid_get_raw_report(struct hid_device *hid, 803 + unsigned char report_number, __u8 *buf, size_t count, 804 + unsigned char report_type) 805 + { 806 + struct usbhid_device *usbhid = hid->driver_data; 807 + struct usb_device *dev = hid_to_usb_dev(hid); 808 + struct usb_interface *intf = usbhid->intf; 809 + struct usb_host_interface *interface = intf->cur_altsetting; 810 + int skipped_report_id = 0; 811 + int ret; 812 + 813 + /* Byte 0 is the report number. Report data starts at byte 1.*/ 814 + buf[0] = report_number; 815 + if (report_number == 0x0) { 816 + /* Offset the return buffer by 1, so that the report ID 817 + will remain in byte 0. */ 818 + buf++; 819 + count--; 820 + skipped_report_id = 1; 821 + } 822 + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 823 + HID_REQ_GET_REPORT, 824 + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 825 + ((report_type + 1) << 8) | report_number, 826 + interface->desc.bInterfaceNumber, buf, count, 827 + USB_CTRL_SET_TIMEOUT); 828 + 829 + /* count also the report id */ 830 + if (ret > 0 && skipped_report_id) 831 + ret++; 832 + 833 + return ret; 834 + } 835 + 802 836 static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, 803 837 unsigned char report_type) 804 838 { ··· 1173 1139 1174 1140 usb_set_intfdata(intf, hid); 1175 1141 hid->ll_driver = &usb_hid_driver; 1142 + hid->hid_get_raw_report = usbhid_get_raw_report; 1176 1143 hid->hid_output_raw_report = usbhid_output_raw_report; 1177 1144 hid->ff_init = hid_pidff_init; 1178 1145 #ifdef CONFIG_USB_HIDDEV
+3
include/linux/hid.h
··· 504 504 struct hid_usage *, __s32); 505 505 void (*hiddev_report_event) (struct hid_device *, struct hid_report *); 506 506 507 + /* handler for raw input (Get_Report) data, used by hidraw */ 508 + int (*hid_get_raw_report) (struct hid_device *, unsigned char, __u8 *, size_t, unsigned char); 509 + 507 510 /* handler for raw output data, used by hidraw */ 508 511 int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char); 509 512
+3
include/linux/hidraw.h
··· 35 35 #define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo) 36 36 #define HIDIOCGRAWNAME(len) _IOC(_IOC_READ, 'H', 0x04, len) 37 37 #define HIDIOCGRAWPHYS(len) _IOC(_IOC_READ, 'H', 0x05, len) 38 + /* The first byte of SFEATURE and GFEATURE is the report number */ 39 + #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) 40 + #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) 38 41 39 42 #define HIDRAW_FIRST_MINOR 0 40 43 #define HIDRAW_MAX_DEVICES 64