HID: roccat: fix actual/startup profile sysfs attribute in koneplus
[pandora-kernel.git] / drivers / hid / hid-roccat-koneplus.c
index 1608c8d..5b640a7 100644 (file)
 #include <linux/device.h>
 #include <linux/input.h>
 #include <linux/hid.h>
-#include <linux/usb.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/hid-roccat.h>
 #include "hid-ids.h"
-#include "hid-roccat.h"
+#include "hid-roccat-common.h"
 #include "hid-roccat-koneplus.h"
 
 static uint profile_numbers[5] = {0, 1, 2, 3, 4};
@@ -39,110 +39,63 @@ static void koneplus_profile_activated(struct koneplus_device *koneplus,
 static int koneplus_send_control(struct usb_device *usb_dev, uint value,
                enum koneplus_control_requests request)
 {
-       int len;
-       struct koneplus_control *control;
+       struct koneplus_control control;
 
        if ((request == KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
                        request == KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
                        value > 4)
                return -EINVAL;
 
-       control = kmalloc(sizeof(struct koneplus_control), GFP_KERNEL);
-       if (!control)
-               return -ENOMEM;
+       control.command = KONEPLUS_COMMAND_CONTROL;
+       control.value = value;
+       control.request = request;
 
-       control->command = KONEPLUS_COMMAND_CONTROL;
-       control->value = value;
-       control->request = request;
-
-       len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
-                       USB_REQ_SET_CONFIGURATION,
-                       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
-                       KONEPLUS_USB_COMMAND_CONTROL, 0, control,
-                       sizeof(struct koneplus_control),
-                       USB_CTRL_SET_TIMEOUT);
-
-       kfree(control);
-
-       if (len != sizeof(struct koneplus_control))
-               return len;
-
-       return 0;
-}
-
-static int koneplus_receive(struct usb_device *usb_dev, uint usb_command,
-               void *buf, uint size) {
-       int len;
-
-       len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
-                       USB_REQ_CLEAR_FEATURE,
-                       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-                       usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
-
-       return (len != size) ? -EIO : 0;
+       return roccat_common_send(usb_dev, KONEPLUS_USB_COMMAND_CONTROL,
+                       &control, sizeof(struct koneplus_control));
 }
 
 static int koneplus_receive_control_status(struct usb_device *usb_dev)
 {
        int retval;
-       struct koneplus_control *control;
-
-       control = kmalloc(sizeof(struct koneplus_control), GFP_KERNEL);
-       if (!control)
-               return -ENOMEM;
+       struct koneplus_control control;
 
        do {
-               retval = koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_CONTROL,
-                               control, sizeof(struct koneplus_control));
+               retval = roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_CONTROL,
+                               &control, sizeof(struct koneplus_control));
 
                /* check if we get a completely wrong answer */
                if (retval)
-                       goto out;
+                       return retval;
 
-               if (control->value == KONEPLUS_CONTROL_REQUEST_STATUS_OK) {
-                       retval = 0;
-                       goto out;
-               }
+               if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_OK)
+                       return 0;
 
                /* indicates that hardware needs some more time to complete action */
-               if (control->value == KONEPLUS_CONTROL_REQUEST_STATUS_WAIT) {
+               if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_WAIT) {
                        msleep(500); /* windows driver uses 1000 */
                        continue;
                }
 
                /* seems to be critical - replug necessary */
-               if (control->value == KONEPLUS_CONTROL_REQUEST_STATUS_OVERLOAD) {
-                       retval = -EINVAL;
-                       goto out;
-               }
-
-               dev_err(&usb_dev->dev, "koneplus_receive_control_status: "
-                               "unknown response value 0x%x\n", control->value);
-               retval = -EINVAL;
-               goto out;
+               if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_OVERLOAD)
+                       return -EINVAL;
 
+               hid_err(usb_dev, "koneplus_receive_control_status: "
+                               "unknown response value 0x%x\n", control.value);
+               return -EINVAL;
        } while (1);
-out:
-       kfree(control);
-       return retval;
 }
 
 static int koneplus_send(struct usb_device *usb_dev, uint command,
-               void *buf, uint size) {
-       int len;
-
-       len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
-                       USB_REQ_SET_CONFIGURATION,
-                       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
-                       command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
-
-       if (len != size)
-               return -EIO;
+               void const *buf, uint size)
+{
+       int retval;
 
-       if (koneplus_receive_control_status(usb_dev))
-               return -EIO;
+       retval = roccat_common_send(usb_dev, command, buf, size);
+       if (retval)
+               return retval;
 
-       return 0;
+       return koneplus_receive_control_status(usb_dev);
 }
 
 static int koneplus_select_profile(struct usb_device *usb_dev, uint number,
@@ -167,7 +120,7 @@ static int koneplus_select_profile(struct usb_device *usb_dev, uint number,
 static int koneplus_get_info(struct usb_device *usb_dev,
                struct koneplus_info *buf)
 {
-       return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_INFO,
+       return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_INFO,
                        buf, sizeof(struct koneplus_info));
 }
 
@@ -181,7 +134,7 @@ static int koneplus_get_profile_settings(struct usb_device *usb_dev,
        if (retval)
                return retval;
 
-       return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS,
+       return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS,
                        buf, sizeof(struct koneplus_profile_settings));
 }
 
@@ -189,7 +142,7 @@ static int koneplus_set_profile_settings(struct usb_device *usb_dev,
                struct koneplus_profile_settings const *settings)
 {
        return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS,
-                       (void *)settings, sizeof(struct koneplus_profile_settings));
+                       settings, sizeof(struct koneplus_profile_settings));
 }
 
 static int koneplus_get_profile_buttons(struct usb_device *usb_dev,
@@ -202,7 +155,7 @@ static int koneplus_get_profile_buttons(struct usb_device *usb_dev,
        if (retval)
                return retval;
 
-       return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS,
+       return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS,
                        buf, sizeof(struct koneplus_profile_buttons));
 }
 
@@ -210,40 +163,32 @@ static int koneplus_set_profile_buttons(struct usb_device *usb_dev,
                struct koneplus_profile_buttons const *buttons)
 {
        return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS,
-                       (void *)buttons, sizeof(struct koneplus_profile_buttons));
+                       buttons, sizeof(struct koneplus_profile_buttons));
 }
 
 /* retval is 0-4 on success, < 0 on error */
-static int koneplus_get_startup_profile(struct usb_device *usb_dev)
+static int koneplus_get_actual_profile(struct usb_device *usb_dev)
 {
-       struct koneplus_startup_profile *buf;
+       struct koneplus_actual_profile buf;
        int retval;
 
-       buf = kmalloc(sizeof(struct koneplus_startup_profile), GFP_KERNEL);
+       retval = roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_ACTUAL_PROFILE,
+                       &buf, sizeof(struct koneplus_actual_profile));
 
-       retval = koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE,
-                       buf, sizeof(struct koneplus_startup_profile));
-
-       if (retval)
-               goto out;
-
-       retval = buf->startup_profile;
-out:
-       kfree(buf);
-       return retval;
+       return retval ? retval : buf.actual_profile;
 }
 
-static int koneplus_set_startup_profile(struct usb_device *usb_dev,
-               int startup_profile)
+static int koneplus_set_actual_profile(struct usb_device *usb_dev,
+               int new_profile)
 {
-       struct koneplus_startup_profile buf;
+       struct koneplus_actual_profile buf;
 
-       buf.command = KONEPLUS_COMMAND_STARTUP_PROFILE;
-       buf.size = sizeof(struct koneplus_startup_profile);
-       buf.startup_profile = startup_profile;
+       buf.command = KONEPLUS_COMMAND_ACTUAL_PROFILE;
+       buf.size = sizeof(struct koneplus_actual_profile);
+       buf.actual_profile = new_profile;
 
-       return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE,
-                       (char *)&buf, sizeof(struct koneplus_profile_buttons));
+       return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_ACTUAL_PROFILE,
+                       &buf, sizeof(struct koneplus_actual_profile));
 }
 
 static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj,
@@ -256,11 +201,14 @@ static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj,
        struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
        int retval;
 
+       if (off >= real_size)
+               return 0;
+
        if (off != 0 || count != real_size)
                return -EINVAL;
 
        mutex_lock(&koneplus->koneplus_lock);
-       retval = koneplus_receive(usb_dev, command, buf, real_size);
+       retval = roccat_common_receive(usb_dev, command, buf, real_size);
        mutex_unlock(&koneplus->koneplus_lock);
 
        if (retval)
@@ -283,7 +231,7 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj,
                return -EINVAL;
 
        mutex_lock(&koneplus->koneplus_lock);
-       retval = koneplus_send(usb_dev, command, (void *)buf, real_size);
+       retval = koneplus_send(usb_dev, command, buf, real_size);
        mutex_unlock(&koneplus->koneplus_lock);
 
        if (retval)
@@ -347,7 +295,7 @@ static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp,
                count = sizeof(struct koneplus_profile_settings) - off;
 
        mutex_lock(&koneplus->koneplus_lock);
-       memcpy(buf, ((void const *)&koneplus->profile_settings[*(uint *)(attr->private)]) + off,
+       memcpy(buf, ((char const *)&koneplus->profile_settings[*(uint *)(attr->private)]) + off,
                        count);
        mutex_unlock(&koneplus->koneplus_lock);
 
@@ -406,7 +354,7 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp,
                count = sizeof(struct koneplus_profile_buttons) - off;
 
        mutex_lock(&koneplus->koneplus_lock);
-       memcpy(buf, ((void const *)&koneplus->profile_buttons[*(uint *)(attr->private)]) + off,
+       memcpy(buf, ((char const *)&koneplus->profile_buttons[*(uint *)(attr->private)]) + off,
                        count);
        mutex_unlock(&koneplus->koneplus_lock);
 
@@ -450,21 +398,22 @@ static ssize_t koneplus_sysfs_write_profile_buttons(struct file *fp,
        return sizeof(struct koneplus_profile_buttons);
 }
 
-static ssize_t koneplus_sysfs_show_startup_profile(struct device *dev,
+static ssize_t koneplus_sysfs_show_actual_profile(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
        struct koneplus_device *koneplus =
                        hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
-       return snprintf(buf, PAGE_SIZE, "%d\n", koneplus->startup_profile);
+       return snprintf(buf, PAGE_SIZE, "%d\n", koneplus->actual_profile);
 }
 
-static ssize_t koneplus_sysfs_set_startup_profile(struct device *dev,
+static ssize_t koneplus_sysfs_set_actual_profile(struct device *dev,
                struct device_attribute *attr, char const *buf, size_t size)
 {
        struct koneplus_device *koneplus;
        struct usb_device *usb_dev;
        unsigned long profile;
        int retval;
+       struct koneplus_roccat_report roccat_report;
 
        dev = dev->parent->parent;
        koneplus = hid_get_drvdata(dev_get_drvdata(dev));
@@ -475,20 +424,25 @@ static ssize_t koneplus_sysfs_set_startup_profile(struct device *dev,
                return retval;
 
        mutex_lock(&koneplus->koneplus_lock);
-       retval = koneplus_set_startup_profile(usb_dev, profile);
-       mutex_unlock(&koneplus->koneplus_lock);
-       if (retval)
+
+       retval = koneplus_set_actual_profile(usb_dev, profile);
+       if (retval) {
+               mutex_unlock(&koneplus->koneplus_lock);
                return retval;
+       }
 
-       return size;
-}
+       koneplus->actual_profile = profile;
 
-static ssize_t koneplus_sysfs_show_actual_profile(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct koneplus_device *koneplus =
-                       hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
-       return snprintf(buf, PAGE_SIZE, "%d\n", koneplus->actual_profile);
+       roccat_report.type = KONEPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE;
+       roccat_report.data1 = profile + 1;
+       roccat_report.data2 = 0;
+       roccat_report.profile = profile + 1;
+       roccat_report_event(koneplus->chrdev_minor,
+                       (uint8_t const *)&roccat_report);
+
+       mutex_unlock(&koneplus->koneplus_lock);
+
+       return size;
 }
 
 static ssize_t koneplus_sysfs_show_firmware_version(struct device *dev,
@@ -500,11 +454,12 @@ static ssize_t koneplus_sysfs_show_firmware_version(struct device *dev,
 }
 
 static struct device_attribute koneplus_attributes[] = {
+       __ATTR(actual_profile, 0660,
+                       koneplus_sysfs_show_actual_profile,
+                       koneplus_sysfs_set_actual_profile),
        __ATTR(startup_profile, 0660,
-                       koneplus_sysfs_show_startup_profile,
-                       koneplus_sysfs_set_startup_profile),
-       __ATTR(actual_profile, 0440,
-                       koneplus_sysfs_show_actual_profile, NULL),
+                       koneplus_sysfs_show_actual_profile,
+                       koneplus_sysfs_set_actual_profile),
        __ATTR(firmware_version, 0440,
                        koneplus_sysfs_show_firmware_version, NULL),
        __ATTR_NULL
@@ -512,7 +467,7 @@ static struct device_attribute koneplus_attributes[] = {
 
 static struct bin_attribute koneplus_bin_attributes[] = {
        {
-               .attr = { .name = "sensor", .mode = 0220 },
+               .attr = { .name = "sensor", .mode = 0660 },
                .size = sizeof(struct koneplus_sensor),
                .read = koneplus_sysfs_read_sensor,
                .write = koneplus_sysfs_write_sensor
@@ -609,13 +564,10 @@ static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev,
                struct koneplus_device *koneplus)
 {
        int retval, i;
-       static uint wait = 70; /* device will freeze with just 60 */
+       static uint wait = 200;
 
        mutex_init(&koneplus->koneplus_lock);
 
-       koneplus->startup_profile = koneplus_get_startup_profile(usb_dev);
-
-       msleep(wait);
        retval = koneplus_get_info(usb_dev, &koneplus->info);
        if (retval)
                return retval;
@@ -634,7 +586,11 @@ static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev,
                        return retval;
        }
 
-       koneplus_profile_activated(koneplus, koneplus->startup_profile);
+       msleep(wait);
+       retval = koneplus_get_actual_profile(usb_dev);
+       if (retval < 0)
+               return retval;
+       koneplus_profile_activated(koneplus, retval);
 
        return 0;
 }
@@ -651,21 +607,21 @@ static int koneplus_init_specials(struct hid_device *hdev)
 
                koneplus = kzalloc(sizeof(*koneplus), GFP_KERNEL);
                if (!koneplus) {
-                       dev_err(&hdev->dev, "can't alloc device descriptor\n");
+                       hid_err(hdev, "can't alloc device descriptor\n");
                        return -ENOMEM;
                }
                hid_set_drvdata(hdev, koneplus);
 
                retval = koneplus_init_koneplus_device_struct(usb_dev, koneplus);
                if (retval) {
-                       dev_err(&hdev->dev,
-                                       "couldn't init struct koneplus_device\n");
+                       hid_err(hdev, "couldn't init struct koneplus_device\n");
                        goto exit_free;
                }
 
-               retval = roccat_connect(koneplus_class, hdev);
+               retval = roccat_connect(koneplus_class, hdev,
+                               sizeof(struct koneplus_roccat_report));
                if (retval < 0) {
-                       dev_err(&hdev->dev, "couldn't init char dev\n");
+                       hid_err(hdev, "couldn't init char dev\n");
                } else {
                        koneplus->chrdev_minor = retval;
                        koneplus->roccat_claimed = 1;
@@ -701,19 +657,19 @@ static int koneplus_probe(struct hid_device *hdev,
 
        retval = hid_parse(hdev);
        if (retval) {
-               dev_err(&hdev->dev, "parse failed\n");
+               hid_err(hdev, "parse failed\n");
                goto exit;
        }
 
        retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
        if (retval) {
-               dev_err(&hdev->dev, "hw start failed\n");
+               hid_err(hdev, "hw start failed\n");
                goto exit;
        }
 
        retval = koneplus_init_specials(hdev);
        if (retval) {
-               dev_err(&hdev->dev, "couldn't install mouse\n");
+               hid_err(hdev, "couldn't install mouse\n");
                goto exit_stop;
        }
 
@@ -769,8 +725,7 @@ static void koneplus_report_to_chrdev(struct koneplus_device const *koneplus,
        roccat_report.data2 = button_report->data2;
        roccat_report.profile = koneplus->actual_profile + 1;
        roccat_report_event(koneplus->chrdev_minor,
-                       (uint8_t const *)&roccat_report,
-                       sizeof(struct koneplus_roccat_report));
+                       (uint8_t const *)&roccat_report);
 }
 
 static int koneplus_raw_event(struct hid_device *hdev,
@@ -825,8 +780,8 @@ static int __init koneplus_init(void)
 
 static void __exit koneplus_exit(void)
 {
-       class_destroy(koneplus_class);
        hid_unregister_driver(&koneplus_driver);
+       class_destroy(koneplus_class);
 }
 
 module_init(koneplus_init);