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>
This commit is contained in:
Alan Ott 2011-01-18 03:04:39 -05:00 committed by Jiri Kosina
parent 0825411ade
commit b4dbde9da8
4 changed files with 141 additions and 6 deletions

View file

@ -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;
}