if (device->driver->report_fixup)
device->driver->report_fixup(device, start, size);
---- device->rdesc = kmalloc(size, GFP_KERNEL);
++++ device->rdesc = kmemdup(start, size, GFP_KERNEL);
if (device->rdesc == NULL)
return -ENOMEM;
---- memcpy(device->rdesc, start, size);
device->rsize = size;
parser = vmalloc(sizeof(struct hid_parser));
unsigned count = field->report_count;
unsigned offset = field->report_offset;
unsigned size = field->report_size;
---- unsigned bitsused = offset + count * size;
unsigned n;
---- /* make sure the unused bits in the last byte are zeros */
---- if (count > 0 && size > 0 && (bitsused % 8) != 0)
---- data[(bitsused-1)/8] &= (1 << (bitsused % 8)) - 1;
----
for (n = 0; n < count; n++) {
if (field->logical_minimum < 0) /* signed values */
implement(data, offset + n * size, size, s32ton(field->value[n], size));
if (report->id > 0)
*data++ = report->id;
++++ memset(data, 0, ((report->size - 1) >> 3) + 1);
for (n = 0; n < report->maxfield; n++)
hid_output_field(report->field[n], data);
}
if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
hid->hiddev_report_event(hid, report);
--- if (hid->claimed & HID_CLAIMED_HIDRAW) {
--- /* numbered reports need to be passed with the report num */
--- if (report_enum->numbered)
--- hidraw_report_event(hid, data - 1, size + 1);
--- else
--- hidraw_report_event(hid, data, size);
--- }
+++ if (hid->claimed & HID_CLAIMED_HIDRAW)
+++ hidraw_report_event(hid, data, size);
for (a = 0; a < report->maxfield; a++)
hid_input_field(hid, report->field[a], cdata, interrupt);
buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC);
--- - if (!buf) {
--- - report = hid_get_report(report_enum, data);
+++ + if (!buf)
goto nomem;
--- - }
--- -
--- - snprintf(buf, HID_DEBUG_BUFSIZE - 1,
--- - "\nreport (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
--- - hid_debug_event(hid, buf);
--- -
--- - report = hid_get_report(report_enum, data);
--- - if (!report) {
--- - kfree(buf);
--- - return -1;
--- - }
/* dump the report */
snprintf(buf, HID_DEBUG_BUFSIZE - 1,
--- - "report %d (size %u) = ", report->id, size);
+++ + "\nreport (size %u) (%snumbered) = ", size, report_enum->numbered ? "" : "un");
hid_debug_event(hid, buf);
+++ +
for (i = 0; i < size; i++) {
snprintf(buf, HID_DEBUG_BUFSIZE - 1,
" %02x", data[i]);
hid_debug_event(hid, buf);
}
hid_debug_event(hid, "\n");
--- -
kfree(buf);
nomem:
+++ + report = hid_get_report(report_enum, data);
+++ +
+++ + if (!report)
+++ + return -1;
+++ +
if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
ret = hdrv->raw_event(hid, report, data, size);
if (ret != 0)
unsigned int i;
int len;
++++ if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE)
++++ connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV);
if (hdev->bus != BUS_USB)
connect_mask &= ~HID_CONNECT_HIDDEV;
if (hid_hiddev(hdev))
/* a list of devices for which there is a specialized driver on HID bus */
static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
++++ { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
++++ { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) },
+++ { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
++++ { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb653) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
++++ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
++++ { HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
{ }
/* we need to kill them here, otherwise they will stay allocated to
* wait for coming driver */
---- if (hid_ignore(hdev))
++++ if (!(hdev->quirks & HID_QUIRK_NO_IGNORE) && hid_ignore(hdev))
return -ENODEV;
/* XXX hack, any other cleaner solution after the driver core
dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus,
hdev->vendor, hdev->product, atomic_inc_return(&id));
++++ hid_debug_register(hdev, dev_name(&hdev->dev));
ret = device_add(&hdev->dev);
if (!ret)
hdev->status |= HID_STAT_ADDED;
----
---- hid_debug_register(hdev, dev_name(&hdev->dev));
++++ else
++++ hid_debug_unregister(hdev);
return ret;
}
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/major.h>
++ #include <linux/slab.h>
#include <linux/hid.h>
#include <linux/mutex.h>
#include <linux/sched.h>
static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
unsigned int minor = iminor(file->f_path.dentry->d_inode);
-- -- /* FIXME: What stops hidraw_table going NULL */
-- -- struct hid_device *dev = hidraw_table[minor]->hid;
++ ++ struct hid_device *dev;
__u8 *buf;
int ret = 0;
-- -- if (!dev->hid_output_raw_report)
-- -- return -ENODEV;
++ ++ mutex_lock(&minors_lock);
++ ++ dev = hidraw_table[minor]->hid;
++ ++
++ ++ if (!dev->hid_output_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));
-- -- return -EINVAL;
++ ++ ret = -EINVAL;
++ ++ goto out;
}
if (count < 2) {
printk(KERN_WARNING "hidraw: pid %d passed too short report\n",
task_pid_nr(current));
-- -- return -EINVAL;
++ ++ ret = -EINVAL;
++ ++ goto out;
}
buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
-- -- if (!buf)
-- -- return -ENOMEM;
++ ++ if (!buf) {
++ ++ ret = -ENOMEM;
++ ++ goto out;
++ ++ }
if (copy_from_user(buf, buffer, count)) {
ret = -EFAULT;
-- -- goto out;
++ ++ goto out_free;
}
ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT);
-- --out:
++ ++out_free:
kfree(buf);
++ ++out:
++ ++ mutex_unlock(&minors_lock);
return ret;
}
goto out;
}
-- -- lock_kernel();
mutex_lock(&minors_lock);
if (!hidraw_table[minor]) {
-- -- printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
-- -- minor);
kfree(list);
err = -ENODEV;
goto out_unlock;
out_unlock:
mutex_unlock(&minors_lock);
-- -- unlock_kernel();
out:
return err;
struct hidraw *dev;
struct hidraw_list *list = file->private_data;
-- -- if (!hidraw_table[minor]) {
-- -- printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
-- -- minor);
++ ++ if (!hidraw_table[minor])
return -ENODEV;
-- -- }
list_del(&list->node);
dev = hidraw_table[minor];
struct inode *inode = file->f_path.dentry->d_inode;
unsigned int minor = iminor(inode);
long ret = 0;
-- -- /* FIXME: What stops hidraw_table going NULL */
-- -- struct hidraw *dev = hidraw_table[minor];
++ ++ struct hidraw *dev;
void __user *user_arg = (void __user*) arg;
-- -- lock_kernel();
++ ++ mutex_lock(&minors_lock);
++ ++ dev = hidraw_table[minor];
++ ++
switch (cmd) {
case HIDIOCGRDESCSIZE:
if (put_user(dev->hid->rsize, (int __user *)arg))
-EFAULT : len;
break;
}
---- }
++++ }
ret = -ENOTTY;
}
-- -- unlock_kernel();
++ ++ mutex_unlock(&minors_lock);
return ret;
}
struct usb_host_interface *interface = intf->cur_altsetting;
int ret;
---- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
---- HID_REQ_SET_REPORT,
---- USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
---- ((report_type + 1) << 8) | *buf,
---- interface->desc.bInterfaceNumber, buf + 1, count - 1,
---- USB_CTRL_SET_TIMEOUT);
----
---- /* count also the report id */
---- if (ret > 0)
---- ret++;
++++ if (usbhid->urbout) {
++++ int actual_length;
++++ int skipped_report_id = 0;
++++ if (buf[0] == 0x0) {
++++ /* Don't send the Report ID */
++++ buf++;
++++ count--;
++++ skipped_report_id = 1;
++++ }
++++ ret = usb_interrupt_msg(dev, usbhid->urbout->pipe,
++++ buf, count, &actual_length,
++++ USB_CTRL_SET_TIMEOUT);
++++ /* return the number of bytes transferred */
++++ if (ret == 0) {
++++ ret = actual_length;
++++ /* count also the report id */
++++ if (skipped_report_id)
++++ ret++;
++++ }
++++ } else {
++++ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
++++ HID_REQ_SET_REPORT,
++++ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
++++ ((report_type + 1) << 8) | *buf,
++++ interface->desc.bInterfaceNumber, buf + 1, count - 1,
++++ USB_CTRL_SET_TIMEOUT);
++++ /* count also the report id */
++++ if (ret > 0)
++++ ret++;
++++ }
return ret;
}
}
}
--- init_waitqueue_head(&usbhid->wait);
--- INIT_WORK(&usbhid->reset_work, hid_reset);
--- INIT_WORK(&usbhid->restart_work, __usbhid_restart_queues);
--- setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
---
--- spin_lock_init(&usbhid->lock);
---
usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
if (!usbhid->urbctrl) {
ret = -ENOMEM;
/* Some keyboards don't work until their LEDs have been set.
* Since BIOSes do set the LEDs, it must be safe for any device
* that supports the keyboard boot protocol.
++++ * In addition, enable remote wakeup by default for all keyboard
++++ * devices supporting the boot protocol.
*/
if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT &&
interface->desc.bInterfaceProtocol ==
---- USB_INTERFACE_PROTOCOL_KEYBOARD)
++++ USB_INTERFACE_PROTOCOL_KEYBOARD) {
usbhid_set_leds(hid);
----
++++ device_set_wakeup_enable(&dev->dev, 1);
++++ }
return 0;
fail:
hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
hid->product = le16_to_cpu(dev->descriptor.idProduct);
hid->name[0] = 0;
++++ hid->quirks = usbhid_lookup_quirk(hid->vendor, hid->product);
if (intf->cur_altsetting->desc.bInterfaceProtocol ==
USB_INTERFACE_PROTOCOL_MOUSE)
hid->type = HID_TYPE_USBMOUSE;
usbhid->intf = intf;
usbhid->ifnum = interface->desc.bInterfaceNumber;
+++ init_waitqueue_head(&usbhid->wait);
+++ INIT_WORK(&usbhid->reset_work, hid_reset);
+++ INIT_WORK(&usbhid->restart_work, __usbhid_restart_queues);
+++ setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
+++ spin_lock_init(&usbhid->lock);
+++
ret = hid_add_device(hid);
if (ret) {
if (ret != -ENODEV)
{
set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
++++ if (hid->driver && hid->driver->suspend) {
++++ status = hid->driver->suspend(hid, message);
++++ if (status < 0)
++++ return status;
++++ }
} else {
usbhid_mark_busy(usbhid);
spin_unlock_irq(&usbhid->lock);
}
} else {
++++ if (hid->driver && hid->driver->suspend) {
++++ status = hid->driver->suspend(hid, message);
++++ if (status < 0)
++++ return status;
++++ }
spin_lock_irq(&usbhid->lock);
set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
hid_io_error(hid);
usbhid_restart_queues(usbhid);
++++ if (status >= 0 && hid->driver && hid->driver->resume) {
++++ int ret = hid->driver->resume(hid);
++++ if (ret < 0)
++++ status = ret;
++++ }
dev_dbg(&intf->dev, "resume status %d\n", status);
return 0;
}
{
struct hid_device *hid = usb_get_intfdata(intf);
struct usbhid_device *usbhid = hid->driver_data;
++++ int status;
clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
---- return hid_post_reset(intf);
++++ status = hid_post_reset(intf);
++++ if (status >= 0 && hid->driver && hid->driver->reset_resume) {
++++ int ret = hid->driver->reset_resume(hid);
++++ if (ret < 0)
++++ status = ret;
++++ }
++++ return status;
}
#endif /* CONFIG_PM */
#define HID_QUIRK_NOTOUCH 0x00000002
#define HID_QUIRK_IGNORE 0x00000004
#define HID_QUIRK_NOGET 0x00000008
++++#define HID_QUIRK_HIDDEV_FORCE 0x00000010
#define HID_QUIRK_BADPAD 0x00000020
#define HID_QUIRK_MULTI_INPUT 0x00000040
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000
++++#define HID_QUIRK_NO_IGNORE 0x40000000
/*
* This is the global environment of the parser. This information is
* @report_fixup: called before report descriptor parsing (NULL means nop)
* @input_mapping: invoked on input registering before mapping an usage
* @input_mapped: invoked on input registering after mapping an usage
++++ * @suspend: invoked on suspend (NULL means nop)
++++ * @resume: invoked on resume if device was not reset (NULL means nop)
++++ * @reset_resume: invoked on resume if device was reset (NULL means nop)
*
* raw_event and event should return 0 on no action performed, 1 when no
* further processing should be done and negative on error
int (*input_mapped)(struct hid_device *hdev,
struct hid_input *hidinput, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max);
++++ #ifdef CONFIG_PM
++++ int (*suspend)(struct hid_device *hdev, pm_message_t message);
++++ int (*resume)(struct hid_device *hdev);
++++ int (*reset_resume)(struct hid_device *hdev);
++++ #endif
/* private: */
struct device_driver driver;
};