From 8211e46004518c977f70f2661da961d5ba617399 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 30 Jan 2011 13:38:25 +0100 Subject: HID: roccat: Add ioctl command to retreive report size from chardev Roccat chardev was reworked to support only a defined report size per device and this can be retreived by an ioctl now to enable future changes in report definitions. Header was moved/renamed from drivers/hid to include/linux for accessibility. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- include/linux/roccat.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 include/linux/roccat.h (limited to 'include') diff --git a/include/linux/roccat.h b/include/linux/roccat.h new file mode 100644 index 000000000000..24e1ca01f9a0 --- /dev/null +++ b/include/linux/roccat.h @@ -0,0 +1,29 @@ +#ifndef __HID_ROCCAT_H +#define __HID_ROCCAT_H + +/* + * Copyright (c) 2010 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include + +#define ROCCATIOCGREPSIZE _IOR('H', 0xf1, int) + +#ifdef __KERNEL__ + +int roccat_connect(struct class *klass, struct hid_device *hid, + int report_size); +void roccat_disconnect(int minor); +int roccat_report_event(int minor, u8 const *data); + +#endif + +#endif -- cgit v1.2.3 From 5dc0c9835fb96c75c8dbf657393764bd0abbac04 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Thu, 3 Feb 2011 16:14:43 +0100 Subject: HID: roccat: Rename header roccat.h -> hid-roccat.h It was desired that the header roccat.h should be named hid-roccat.h Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- Documentation/ioctl/ioctl-number.txt | 2 +- drivers/hid/hid-roccat-arvo.c | 2 +- drivers/hid/hid-roccat-kone.c | 2 +- drivers/hid/hid-roccat-koneplus.c | 2 +- drivers/hid/hid-roccat-kovaplus.c | 2 +- drivers/hid/hid-roccat-pyra.c | 2 +- drivers/hid/hid-roccat.c | 2 +- include/linux/hid-roccat.h | 29 +++++++++++++++++++++++++++++ include/linux/roccat.h | 29 ----------------------------- 9 files changed, 36 insertions(+), 36 deletions(-) create mode 100644 include/linux/hid-roccat.h delete mode 100644 include/linux/roccat.h (limited to 'include') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index b0f1fa0770d1..e68543f767d5 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -133,7 +133,7 @@ Code Seq#(hex) Include File Comments 'H' C0-DF net/bluetooth/hidp/hidp.h conflict! 'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! 'H' C0-DF net/bluetooth/bnep/bnep.h conflict! -'H' F1 linux/roccat.h +'H' F1 linux/hid-roccat.h 'I' all linux/isdn.h conflict! 'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict! 'I' 40-4F linux/mISDNif.h conflict! diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index 75f532f4d4cc..2307471d96dc 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-arvo.h" diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index d7497964a5b3..a57838d15267 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-kone.h" diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index ac2010732495..a8e2117756ac 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-koneplus.h" diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 4eeb62fb4e75..984be2f8967e 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-kovaplus.h" diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 02a72433a712..160f481344f6 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-pyra.h" diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index bbe294c0dd9b..5666e7587b18 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #define ROCCAT_FIRST_MINOR 0 #define ROCCAT_MAX_DEVICES 8 diff --git a/include/linux/hid-roccat.h b/include/linux/hid-roccat.h new file mode 100644 index 000000000000..24e1ca01f9a0 --- /dev/null +++ b/include/linux/hid-roccat.h @@ -0,0 +1,29 @@ +#ifndef __HID_ROCCAT_H +#define __HID_ROCCAT_H + +/* + * Copyright (c) 2010 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include + +#define ROCCATIOCGREPSIZE _IOR('H', 0xf1, int) + +#ifdef __KERNEL__ + +int roccat_connect(struct class *klass, struct hid_device *hid, + int report_size); +void roccat_disconnect(int minor); +int roccat_report_event(int minor, u8 const *data); + +#endif + +#endif diff --git a/include/linux/roccat.h b/include/linux/roccat.h deleted file mode 100644 index 24e1ca01f9a0..000000000000 --- a/include/linux/roccat.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __HID_ROCCAT_H -#define __HID_ROCCAT_H - -/* - * Copyright (c) 2010 Stefan Achatz - */ - -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - */ - -#include -#include - -#define ROCCATIOCGREPSIZE _IOR('H', 0xf1, int) - -#ifdef __KERNEL__ - -int roccat_connect(struct class *klass, struct hid_device *hid, - int report_size); -void roccat_disconnect(int minor); -int roccat_report_event(int minor, u8 const *data); - -#endif - -#endif -- cgit v1.2.3 From b4dbde9da8ece42bbe4c70c26bac3b28dd6a3ddb Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Tue, 18 Jan 2011 03:04:39 -0500 Subject: 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 Signed-off-by: Antonio Ospite Signed-off-by: Jiri Kosina --- drivers/hid/hidraw.c | 106 +++++++++++++++++++++++++++++++++++++++--- drivers/hid/usbhid/hid-core.c | 35 ++++++++++++++ include/linux/hid.h | 3 ++ include/linux/hidraw.h | 3 ++ 4 files changed, 141 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 468e87b53ed2..8f06044a3e3e 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -102,15 +102,14 @@ out: } /* 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) +/* 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->f_path.dentry->d_inode); struct hid_device *dev; __u8 *buf; int ret = 0; - mutex_lock(&minors_lock); - if (!hidraw_table[minor]) { ret = -ENODEV; goto out; @@ -148,14 +147,92 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t goto out_free; } - ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT); + ret = dev->hid_output_raw_report(dev, buf, count, report_type); 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; + mutex_lock(&minors_lock); + ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT); mutex_unlock(&minors_lock); return ret; } + +/* 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. */ +static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type) +{ + unsigned int minor = iminor(file->f_path.dentry->d_inode); + struct hid_device *dev; + __u8 *buf; + int ret = 0, len; + unsigned char report_number; + + dev = hidraw_table[minor]->hid; + + if (!dev->hid_get_raw_report) { + ret = -ENODEV; + goto out; + } + + if (count > HID_MAX_BUFFER_SIZE) { + printk(KERN_WARNING "hidraw: pid %d passed too large report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + if (count < 2) { + printk(KERN_WARNING "hidraw: pid %d passed too short report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + /* Read the first byte from the user. This is the report number, + which is passed to dev->hid_get_raw_report(). */ + 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); + + if (ret < 0) + goto out_free; + + len = (ret < count) ? ret : count; + + if (copy_to_user(buffer, buf, len)) { + ret = -EFAULT; + goto out_free; + } + + ret = len; + +out_free: + kfree(buf); +out: + return ret; +} + static unsigned int hidraw_poll(struct file *file, poll_table *wait) { struct hidraw_list *list = file->private_data; @@ -295,7 +372,24 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, default: { struct hid_device *hid = dev->hid; - if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) { + if (_IOC_TYPE(cmd) != 'H') { + ret = -EINVAL; + break; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) { + int len = _IOC_SIZE(cmd); + ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) { + int len = _IOC_SIZE(cmd); + ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + + /* Begin Read-only ioctls. */ + if (_IOC_DIR(cmd) != _IOC_READ) { ret = -EINVAL; break; } @@ -327,7 +421,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, -EFAULT : len; break; } - } + } ret = -ENOTTY; } diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index b336dd84036f..38c261a40c74 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -799,6 +799,40 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) return 0; } +static int usbhid_get_raw_report(struct hid_device *hid, + unsigned char report_number, __u8 *buf, size_t count, + unsigned char report_type) +{ + struct usbhid_device *usbhid = hid->driver_data; + struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_interface *intf = usbhid->intf; + struct usb_host_interface *interface = intf->cur_altsetting; + int skipped_report_id = 0; + int ret; + + /* Byte 0 is the report number. Report data starts at byte 1.*/ + buf[0] = report_number; + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + buf++; + count--; + skipped_report_id = 1; + } + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ((report_type + 1) << 8) | report_number, + interface->desc.bInterfaceNumber, buf, count, + USB_CTRL_SET_TIMEOUT); + + /* count also the report id */ + if (ret > 0 && skipped_report_id) + ret++; + + return ret; +} + static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { @@ -1139,6 +1173,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * usb_set_intfdata(intf, hid); hid->ll_driver = &usb_hid_driver; + hid->hid_get_raw_report = usbhid_get_raw_report; hid->hid_output_raw_report = usbhid_output_raw_report; hid->ff_init = hid_pidff_init; #ifdef CONFIG_USB_HIDDEV diff --git a/include/linux/hid.h b/include/linux/hid.h index d91c25e253c8..e8ee0a93bda3 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -504,6 +504,9 @@ struct hid_device { /* device report descriptor */ struct hid_usage *, __s32); void (*hiddev_report_event) (struct hid_device *, struct hid_report *); + /* handler for raw input (Get_Report) data, used by hidraw */ + int (*hid_get_raw_report) (struct hid_device *, unsigned char, __u8 *, size_t, unsigned char); + /* handler for raw output data, used by hidraw */ int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char); diff --git a/include/linux/hidraw.h b/include/linux/hidraw.h index dd8d69269176..4b88e697c4e9 100644 --- a/include/linux/hidraw.h +++ b/include/linux/hidraw.h @@ -35,6 +35,9 @@ struct hidraw_devinfo { #define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo) #define HIDIOCGRAWNAME(len) _IOC(_IOC_READ, 'H', 0x04, len) #define HIDIOCGRAWPHYS(len) _IOC(_IOC_READ, 'H', 0x05, len) +/* The first byte of SFEATURE and GFEATURE is the report number */ +#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) +#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) #define HIDRAW_FIRST_MINOR 0 #define HIDRAW_MAX_DEVICES 64 -- cgit v1.2.3 From f635bd11c8d332d917fb9a4cad3071b2357d5b2a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Thu, 24 Feb 2011 19:30:59 +0100 Subject: HID: Do not create input devices for feature reports When the multi input quirk is set, there is a new input device created for every feature report. Since the idea is to present features per hid device, not per input device, revert back to the original report loop and change the feature_mapping() callback to not take the input device as argument. Signed-off-by: Henrik Rydberg Tested-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 30 +++++++++++++++++++++--------- drivers/hid/hid-multitouch.c | 2 +- include/linux/hid.h | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f552bfad32c..ebcc02a1c1c9 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -290,14 +290,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel goto ignore; } - if (field->report_type == HID_FEATURE_REPORT) { - if (device->driver->feature_mapping) { - device->driver->feature_mapping(device, hidinput, field, - usage); - } - goto ignore; - } - if (device->driver->input_mapping) { int ret = device->driver->input_mapping(device, hidinput, field, usage, &bit, &max); @@ -835,6 +827,24 @@ static void hidinput_close(struct input_dev *dev) hid_hw_close(hid); } +static void report_features(struct hid_device *hid) +{ + struct hid_driver *drv = hid->driver; + struct hid_report_enum *rep_enum; + struct hid_report *rep; + int i, j; + + if (!drv->feature_mapping) + return; + + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) + for (i = 0; i < rep->maxfield; i++) + for (j = 0; j < rep->field[i]->maxusage; j++) + drv->feature_mapping(hid, rep->field[i], + rep->field[i]->usage + j); +} + /* * Register the input device; print a message. * Configure the input layer interface @@ -863,7 +873,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) return -1; } - for (k = HID_INPUT_REPORT; k <= HID_FEATURE_REPORT; k++) { + report_features(hid); + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { if (k == HID_OUTPUT_REPORT && hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) continue; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 07d3183fdde5..2bbc9545f5cc 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -122,7 +122,7 @@ struct mt_class mt_classes[] = { { } }; -static void mt_feature_mapping(struct hid_device *hdev, struct hid_input *hi, +static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { if (usage->hid == HID_DG_INPUTMODE) { diff --git a/include/linux/hid.h b/include/linux/hid.h index d91c25e253c8..fc5faf60f6df 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -638,7 +638,7 @@ struct hid_driver { struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max); void (*feature_mapping)(struct hid_device *hdev, - struct hid_input *hidinput, struct hid_field *field, + struct hid_field *field, struct hid_usage *usage); #ifdef CONFIG_PM int (*suspend)(struct hid_device *hdev, pm_message_t message); -- cgit v1.2.3