1
linux/drivers/hid/hid-roccat-pyra.c
Stefan Achatz 901e64dbdb HID: roccat: fix NULL pointer dereference, add range checks
On rare occassions raw events can be triggered before drvdata gets set up
which leads to NULL pointer dereferences. This was only observed with pyra on
2.6.39, but is fixed for all devices now to play it save.
kovaplus returned wrong actual values when profile change was initiated from host.
Added range checks for setting actual profile on all devices.

Signed-off-by: Stefan Achatz <erazor_de@users.sourceforge.net>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2011-06-13 13:06:23 +02:00

698 lines
19 KiB
C

/*
* Roccat Pyra driver for Linux
*
* Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
*/
/*
* 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.
*/
/*
* Roccat Pyra is a mobile gamer mouse which comes in wired and wireless
* variant. Wireless variant is not tested.
* Userland tools can be found at http://sourceforge.net/projects/roccat
*/
#include <linux/device.h>
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/hid-roccat.h>
#include "hid-ids.h"
#include "hid-roccat-common.h"
#include "hid-roccat-pyra.h"
static uint profile_numbers[5] = {0, 1, 2, 3, 4};
/* pyra_class is used for creating sysfs attributes via roccat char device */
static struct class *pyra_class;
static void profile_activated(struct pyra_device *pyra,
unsigned int new_profile)
{
pyra->actual_profile = new_profile;
pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi;
}
static int pyra_send_control(struct usb_device *usb_dev, int value,
enum pyra_control_requests request)
{
struct pyra_control control;
if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
(value < 0 || value > 4))
return -EINVAL;
control.command = PYRA_COMMAND_CONTROL;
control.value = value;
control.request = request;
return roccat_common_send(usb_dev, PYRA_COMMAND_CONTROL,
&control, sizeof(struct pyra_control));
}
static int pyra_receive_control_status(struct usb_device *usb_dev)
{
int retval;
struct pyra_control control;
do {
msleep(10);
retval = roccat_common_receive(usb_dev, PYRA_COMMAND_CONTROL,
&control, sizeof(struct pyra_control));
/* requested too early, try again */
} while (retval == -EPROTO);
if (!retval && control.command == PYRA_COMMAND_CONTROL &&
control.request == PYRA_CONTROL_REQUEST_STATUS &&
control.value == 1)
return 0;
else {
hid_err(usb_dev, "receive control status: unknown response 0x%x 0x%x\n",
control.request, control.value);
return retval ? retval : -EINVAL;
}
}
static int pyra_get_profile_settings(struct usb_device *usb_dev,
struct pyra_profile_settings *buf, int number)
{
int retval;
retval = pyra_send_control(usb_dev, number,
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
if (retval)
return retval;
return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
buf, sizeof(struct pyra_profile_settings));
}
static int pyra_get_profile_buttons(struct usb_device *usb_dev,
struct pyra_profile_buttons *buf, int number)
{
int retval;
retval = pyra_send_control(usb_dev, number,
PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
if (retval)
return retval;
return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS,
buf, sizeof(struct pyra_profile_buttons));
}
static int pyra_get_settings(struct usb_device *usb_dev,
struct pyra_settings *buf)
{
return roccat_common_receive(usb_dev, PYRA_COMMAND_SETTINGS,
buf, sizeof(struct pyra_settings));
}
static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf)
{
return roccat_common_receive(usb_dev, PYRA_COMMAND_INFO,
buf, sizeof(struct pyra_info));
}
static int pyra_send(struct usb_device *usb_dev, uint command,
void const *buf, uint size)
{
int retval;
retval = roccat_common_send(usb_dev, command, buf, size);
if (retval)
return retval;
return pyra_receive_control_status(usb_dev);
}
static int pyra_set_profile_settings(struct usb_device *usb_dev,
struct pyra_profile_settings const *settings)
{
return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, settings,
sizeof(struct pyra_profile_settings));
}
static int pyra_set_profile_buttons(struct usb_device *usb_dev,
struct pyra_profile_buttons const *buttons)
{
return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, buttons,
sizeof(struct pyra_profile_buttons));
}
static int pyra_set_settings(struct usb_device *usb_dev,
struct pyra_settings const *settings)
{
return pyra_send(usb_dev, PYRA_COMMAND_SETTINGS, settings,
sizeof(struct pyra_settings));
}
static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
if (off >= sizeof(struct pyra_profile_settings))
return 0;
if (off + count > sizeof(struct pyra_profile_settings))
count = sizeof(struct pyra_profile_settings) - off;
mutex_lock(&pyra->pyra_lock);
memcpy(buf, ((char const *)&pyra->profile_settings[*(uint *)(attr->private)]) + off,
count);
mutex_unlock(&pyra->pyra_lock);
return count;
}
static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
if (off >= sizeof(struct pyra_profile_buttons))
return 0;
if (off + count > sizeof(struct pyra_profile_buttons))
count = sizeof(struct pyra_profile_buttons) - off;
mutex_lock(&pyra->pyra_lock);
memcpy(buf, ((char const *)&pyra->profile_buttons[*(uint *)(attr->private)]) + off,
count);
mutex_unlock(&pyra->pyra_lock);
return count;
}
static ssize_t pyra_sysfs_write_profile_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval = 0;
int difference;
int profile_number;
struct pyra_profile_settings *profile_settings;
if (off != 0 || count != sizeof(struct pyra_profile_settings))
return -EINVAL;
profile_number = ((struct pyra_profile_settings const *)buf)->number;
profile_settings = &pyra->profile_settings[profile_number];
mutex_lock(&pyra->pyra_lock);
difference = memcmp(buf, profile_settings,
sizeof(struct pyra_profile_settings));
if (difference) {
retval = pyra_set_profile_settings(usb_dev,
(struct pyra_profile_settings const *)buf);
if (!retval)
memcpy(profile_settings, buf,
sizeof(struct pyra_profile_settings));
}
mutex_unlock(&pyra->pyra_lock);
if (retval)
return retval;
return sizeof(struct pyra_profile_settings);
}
static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval = 0;
int difference;
int profile_number;
struct pyra_profile_buttons *profile_buttons;
if (off != 0 || count != sizeof(struct pyra_profile_buttons))
return -EINVAL;
profile_number = ((struct pyra_profile_buttons const *)buf)->number;
profile_buttons = &pyra->profile_buttons[profile_number];
mutex_lock(&pyra->pyra_lock);
difference = memcmp(buf, profile_buttons,
sizeof(struct pyra_profile_buttons));
if (difference) {
retval = pyra_set_profile_buttons(usb_dev,
(struct pyra_profile_buttons const *)buf);
if (!retval)
memcpy(profile_buttons, buf,
sizeof(struct pyra_profile_buttons));
}
mutex_unlock(&pyra->pyra_lock);
if (retval)
return retval;
return sizeof(struct pyra_profile_buttons);
}
static ssize_t pyra_sysfs_read_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
if (off >= sizeof(struct pyra_settings))
return 0;
if (off + count > sizeof(struct pyra_settings))
count = sizeof(struct pyra_settings) - off;
mutex_lock(&pyra->pyra_lock);
memcpy(buf, ((char const *)&pyra->settings) + off, count);
mutex_unlock(&pyra->pyra_lock);
return count;
}
static ssize_t pyra_sysfs_write_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval = 0;
int difference;
if (off != 0 || count != sizeof(struct pyra_settings))
return -EINVAL;
mutex_lock(&pyra->pyra_lock);
difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings));
if (difference) {
retval = pyra_set_settings(usb_dev,
(struct pyra_settings const *)buf);
if (!retval)
memcpy(&pyra->settings, buf,
sizeof(struct pyra_settings));
}
mutex_unlock(&pyra->pyra_lock);
if (retval)
return retval;
profile_activated(pyra, pyra->settings.startup_profile);
return sizeof(struct pyra_settings);
}
static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pyra_device *pyra =
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi);
}
static ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pyra_device *pyra =
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile);
}
static ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pyra_device *pyra =
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version);
}
static ssize_t pyra_sysfs_show_startup_profile(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pyra_device *pyra =
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile);
}
static struct device_attribute pyra_attributes[] = {
__ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL),
__ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL),
__ATTR(firmware_version, 0440,
pyra_sysfs_show_firmware_version, NULL),
__ATTR(startup_profile, 0440,
pyra_sysfs_show_startup_profile, NULL),
__ATTR_NULL
};
static struct bin_attribute pyra_bin_attributes[] = {
{
.attr = { .name = "profile_settings", .mode = 0220 },
.size = sizeof(struct pyra_profile_settings),
.write = pyra_sysfs_write_profile_settings
},
{
.attr = { .name = "profile1_settings", .mode = 0440 },
.size = sizeof(struct pyra_profile_settings),
.read = pyra_sysfs_read_profilex_settings,
.private = &profile_numbers[0]
},
{
.attr = { .name = "profile2_settings", .mode = 0440 },
.size = sizeof(struct pyra_profile_settings),
.read = pyra_sysfs_read_profilex_settings,
.private = &profile_numbers[1]
},
{
.attr = { .name = "profile3_settings", .mode = 0440 },
.size = sizeof(struct pyra_profile_settings),
.read = pyra_sysfs_read_profilex_settings,
.private = &profile_numbers[2]
},
{
.attr = { .name = "profile4_settings", .mode = 0440 },
.size = sizeof(struct pyra_profile_settings),
.read = pyra_sysfs_read_profilex_settings,
.private = &profile_numbers[3]
},
{
.attr = { .name = "profile5_settings", .mode = 0440 },
.size = sizeof(struct pyra_profile_settings),
.read = pyra_sysfs_read_profilex_settings,
.private = &profile_numbers[4]
},
{
.attr = { .name = "profile_buttons", .mode = 0220 },
.size = sizeof(struct pyra_profile_buttons),
.write = pyra_sysfs_write_profile_buttons
},
{
.attr = { .name = "profile1_buttons", .mode = 0440 },
.size = sizeof(struct pyra_profile_buttons),
.read = pyra_sysfs_read_profilex_buttons,
.private = &profile_numbers[0]
},
{
.attr = { .name = "profile2_buttons", .mode = 0440 },
.size = sizeof(struct pyra_profile_buttons),
.read = pyra_sysfs_read_profilex_buttons,
.private = &profile_numbers[1]
},
{
.attr = { .name = "profile3_buttons", .mode = 0440 },
.size = sizeof(struct pyra_profile_buttons),
.read = pyra_sysfs_read_profilex_buttons,
.private = &profile_numbers[2]
},
{
.attr = { .name = "profile4_buttons", .mode = 0440 },
.size = sizeof(struct pyra_profile_buttons),
.read = pyra_sysfs_read_profilex_buttons,
.private = &profile_numbers[3]
},
{
.attr = { .name = "profile5_buttons", .mode = 0440 },
.size = sizeof(struct pyra_profile_buttons),
.read = pyra_sysfs_read_profilex_buttons,
.private = &profile_numbers[4]
},
{
.attr = { .name = "settings", .mode = 0660 },
.size = sizeof(struct pyra_settings),
.read = pyra_sysfs_read_settings,
.write = pyra_sysfs_write_settings
},
__ATTR_NULL
};
static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
struct pyra_device *pyra)
{
struct pyra_info info;
int retval, i;
mutex_init(&pyra->pyra_lock);
retval = pyra_get_info(usb_dev, &info);
if (retval)
return retval;
pyra->firmware_version = info.firmware_version;
retval = pyra_get_settings(usb_dev, &pyra->settings);
if (retval)
return retval;
for (i = 0; i < 5; ++i) {
retval = pyra_get_profile_settings(usb_dev,
&pyra->profile_settings[i], i);
if (retval)
return retval;
retval = pyra_get_profile_buttons(usb_dev,
&pyra->profile_buttons[i], i);
if (retval)
return retval;
}
profile_activated(pyra, pyra->settings.startup_profile);
return 0;
}
static int pyra_init_specials(struct hid_device *hdev)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct usb_device *usb_dev = interface_to_usbdev(intf);
struct pyra_device *pyra;
int retval;
if (intf->cur_altsetting->desc.bInterfaceProtocol
== USB_INTERFACE_PROTOCOL_MOUSE) {
pyra = kzalloc(sizeof(*pyra), GFP_KERNEL);
if (!pyra) {
hid_err(hdev, "can't alloc device descriptor\n");
return -ENOMEM;
}
hid_set_drvdata(hdev, pyra);
retval = pyra_init_pyra_device_struct(usb_dev, pyra);
if (retval) {
hid_err(hdev, "couldn't init struct pyra_device\n");
goto exit_free;
}
retval = roccat_connect(pyra_class, hdev,
sizeof(struct pyra_roccat_report));
if (retval < 0) {
hid_err(hdev, "couldn't init char dev\n");
} else {
pyra->chrdev_minor = retval;
pyra->roccat_claimed = 1;
}
} else {
hid_set_drvdata(hdev, NULL);
}
return 0;
exit_free:
kfree(pyra);
return retval;
}
static void pyra_remove_specials(struct hid_device *hdev)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct pyra_device *pyra;
if (intf->cur_altsetting->desc.bInterfaceProtocol
== USB_INTERFACE_PROTOCOL_MOUSE) {
pyra = hid_get_drvdata(hdev);
if (pyra->roccat_claimed)
roccat_disconnect(pyra->chrdev_minor);
kfree(hid_get_drvdata(hdev));
}
}
static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int retval;
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
goto exit;
}
retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (retval) {
hid_err(hdev, "hw start failed\n");
goto exit;
}
retval = pyra_init_specials(hdev);
if (retval) {
hid_err(hdev, "couldn't install mouse\n");
goto exit_stop;
}
return 0;
exit_stop:
hid_hw_stop(hdev);
exit:
return retval;
}
static void pyra_remove(struct hid_device *hdev)
{
pyra_remove_specials(hdev);
hid_hw_stop(hdev);
}
static void pyra_keep_values_up_to_date(struct pyra_device *pyra,
u8 const *data)
{
struct pyra_mouse_event_button const *button_event;
switch (data[0]) {
case PYRA_MOUSE_REPORT_NUMBER_BUTTON:
button_event = (struct pyra_mouse_event_button const *)data;
switch (button_event->type) {
case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
profile_activated(pyra, button_event->data1 - 1);
break;
case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
pyra->actual_cpi = button_event->data1;
break;
}
break;
}
}
static void pyra_report_to_chrdev(struct pyra_device const *pyra,
u8 const *data)
{
struct pyra_roccat_report roccat_report;
struct pyra_mouse_event_button const *button_event;
if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON)
return;
button_event = (struct pyra_mouse_event_button const *)data;
switch (button_event->type) {
case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
roccat_report.type = button_event->type;
roccat_report.value = button_event->data1;
roccat_report.key = 0;
roccat_report_event(pyra->chrdev_minor,
(uint8_t const *)&roccat_report);
break;
case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO:
case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT:
case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH:
if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) {
roccat_report.type = button_event->type;
roccat_report.key = button_event->data1;
/*
* pyra reports profile numbers with range 1-5.
* Keeping this behaviour.
*/
roccat_report.value = pyra->actual_profile + 1;
roccat_report_event(pyra->chrdev_minor,
(uint8_t const *)&roccat_report);
}
break;
}
}
static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct pyra_device *pyra = hid_get_drvdata(hdev);
if (intf->cur_altsetting->desc.bInterfaceProtocol
!= USB_INTERFACE_PROTOCOL_MOUSE)
return 0;
if (pyra == NULL)
return 0;
pyra_keep_values_up_to_date(pyra, data);
if (pyra->roccat_claimed)
pyra_report_to_chrdev(pyra, data);
return 0;
}
static const struct hid_device_id pyra_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
{ }
};
MODULE_DEVICE_TABLE(hid, pyra_devices);
static struct hid_driver pyra_driver = {
.name = "pyra",
.id_table = pyra_devices,
.probe = pyra_probe,
.remove = pyra_remove,
.raw_event = pyra_raw_event
};
static int __init pyra_init(void)
{
int retval;
/* class name has to be same as driver name */
pyra_class = class_create(THIS_MODULE, "pyra");
if (IS_ERR(pyra_class))
return PTR_ERR(pyra_class);
pyra_class->dev_attrs = pyra_attributes;
pyra_class->dev_bin_attrs = pyra_bin_attributes;
retval = hid_register_driver(&pyra_driver);
if (retval)
class_destroy(pyra_class);
return retval;
}
static void __exit pyra_exit(void)
{
hid_unregister_driver(&pyra_driver);
class_destroy(pyra_class);
}
module_init(pyra_init);
module_exit(pyra_exit);
MODULE_AUTHOR("Stefan Achatz");
MODULE_DESCRIPTION("USB Roccat Pyra driver");
MODULE_LICENSE("GPL v2");