aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Ott <alan@signal11.us>2011-01-18 03:04:39 -0500
committerJiri Kosina <jkosina@suse.cz>2011-02-11 09:05:49 -0500
commitb4dbde9da8ece42bbe4c70c26bac3b28dd6a3ddb (patch)
treefde6ca709962906877c25e0910d67ca5c00beb74
parent0825411ade21a39ac63b3e011d092b1f95b5f3f5 (diff)
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>
-rw-r--r--drivers/hid/hidraw.c106
-rw-r--r--drivers/hid/usbhid/hid-core.c35
-rw-r--r--include/linux/hid.h3
-rw-r--r--include/linux/hidraw.h3
4 files changed, 141 insertions, 6 deletions
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:
102} 102}
103 103
104/* the first byte is expected to be a report number */ 104/* the first byte is expected to be a report number */
105static 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 */
106static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type)
106{ 107{
107 unsigned int minor = iminor(file->f_path.dentry->d_inode); 108 unsigned int minor = iminor(file->f_path.dentry->d_inode);
108 struct hid_device *dev; 109 struct hid_device *dev;
109 __u8 *buf; 110 __u8 *buf;
110 int ret = 0; 111 int ret = 0;
111 112
112 mutex_lock(&minors_lock);
113
114 if (!hidraw_table[minor]) { 113 if (!hidraw_table[minor]) {
115 ret = -ENODEV; 114 ret = -ENODEV;
116 goto out; 115 goto out;
@@ -148,14 +147,92 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t
148 goto out_free; 147 goto out_free;
149 } 148 }
150 149
151 ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT); 150 ret = dev->hid_output_raw_report(dev, buf, count, report_type);
152out_free: 151out_free:
153 kfree(buf); 152 kfree(buf);
154out: 153out:
154 return ret;
155}
156
157/* the first byte is expected to be a report number */
158static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
159{
160 ssize_t ret;
161 mutex_lock(&minors_lock);
162 ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
155 mutex_unlock(&minors_lock); 163 mutex_unlock(&minors_lock);
156 return ret; 164 return ret;
157} 165}
158 166
167
168/* This function performs a Get_Report transfer over the control endpoint
169 per section 7.2.1 of the HID specification, version 1.1. The first byte
170 of buffer is the report number to request, or 0x0 if the defice does not
171 use numbered reports. The report_type parameter can be HID_FEATURE_REPORT
172 or HID_INPUT_REPORT. This function is to be called with the minors_lock
173 mutex held. */
174static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type)
175{
176 unsigned int minor = iminor(file->f_path.dentry->d_inode);
177 struct hid_device *dev;
178 __u8 *buf;
179 int ret = 0, len;
180 unsigned char report_number;
181
182 dev = hidraw_table[minor]->hid;
183
184 if (!dev->hid_get_raw_report) {
185 ret = -ENODEV;
186 goto out;
187 }
188
189 if (count > HID_MAX_BUFFER_SIZE) {
190 printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
191 task_pid_nr(current));
192 ret = -EINVAL;
193 goto out;
194 }
195
196 if (count < 2) {
197 printk(KERN_WARNING "hidraw: pid %d passed too short report\n",
198 task_pid_nr(current));
199 ret = -EINVAL;
200 goto out;
201 }
202
203 buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
204 if (!buf) {
205 ret = -ENOMEM;
206 goto out;
207 }
208
209 /* Read the first byte from the user. This is the report number,
210 which is passed to dev->hid_get_raw_report(). */
211 if (copy_from_user(&report_number, buffer, 1)) {
212 ret = -EFAULT;
213 goto out_free;
214 }
215
216 ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type);
217
218 if (ret < 0)
219 goto out_free;
220
221 len = (ret < count) ? ret : count;
222
223 if (copy_to_user(buffer, buf, len)) {
224 ret = -EFAULT;
225 goto out_free;
226 }
227
228 ret = len;
229
230out_free:
231 kfree(buf);
232out:
233 return ret;
234}
235
159static unsigned int hidraw_poll(struct file *file, poll_table *wait) 236static unsigned int hidraw_poll(struct file *file, poll_table *wait)
160{ 237{
161 struct hidraw_list *list = file->private_data; 238 struct hidraw_list *list = file->private_data;
@@ -295,7 +372,24 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
295 default: 372 default:
296 { 373 {
297 struct hid_device *hid = dev->hid; 374 struct hid_device *hid = dev->hid;
298 if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) { 375 if (_IOC_TYPE(cmd) != 'H') {
376 ret = -EINVAL;
377 break;
378 }
379
380 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) {
381 int len = _IOC_SIZE(cmd);
382 ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT);
383 break;
384 }
385 if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) {
386 int len = _IOC_SIZE(cmd);
387 ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT);
388 break;
389 }
390
391 /* Begin Read-only ioctls. */
392 if (_IOC_DIR(cmd) != _IOC_READ) {
299 ret = -EINVAL; 393 ret = -EINVAL;
300 break; 394 break;
301 } 395 }
@@ -327,7 +421,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
327 -EFAULT : len; 421 -EFAULT : len;
328 break; 422 break;
329 } 423 }
330 } 424 }
331 425
332 ret = -ENOTTY; 426 ret = -ENOTTY;
333 } 427 }
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)
799 return 0; 799 return 0;
800} 800}
801 801
802static 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
802static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, 836static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count,
803 unsigned char report_type) 837 unsigned char report_type)
804{ 838{
@@ -1139,6 +1173,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
1139 1173
1140 usb_set_intfdata(intf, hid); 1174 usb_set_intfdata(intf, hid);
1141 hid->ll_driver = &usb_hid_driver; 1175 hid->ll_driver = &usb_hid_driver;
1176 hid->hid_get_raw_report = usbhid_get_raw_report;
1142 hid->hid_output_raw_report = usbhid_output_raw_report; 1177 hid->hid_output_raw_report = usbhid_output_raw_report;
1143 hid->ff_init = hid_pidff_init; 1178 hid->ff_init = hid_pidff_init;
1144#ifdef CONFIG_USB_HIDDEV 1179#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 */
504 struct hid_usage *, __s32); 504 struct hid_usage *, __s32);
505 void (*hiddev_report_event) (struct hid_device *, struct hid_report *); 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 /* handler for raw output data, used by hidraw */ 510 /* handler for raw output data, used by hidraw */
508 int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char); 511 int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char);
509 512
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 {
35#define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo) 35#define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo)
36#define HIDIOCGRAWNAME(len) _IOC(_IOC_READ, 'H', 0x04, len) 36#define HIDIOCGRAWNAME(len) _IOC(_IOC_READ, 'H', 0x04, len)
37#define HIDIOCGRAWPHYS(len) _IOC(_IOC_READ, 'H', 0x05, len) 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#define HIDRAW_FIRST_MINOR 0 42#define HIDRAW_FIRST_MINOR 0
40#define HIDRAW_MAX_DEVICES 64 43#define HIDRAW_MAX_DEVICES 64