Merge branch 'x86-geode-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / input / tablet / wacom_sys.c
index 958b4eb..1c1b7b4 100644 (file)
@@ -48,27 +48,49 @@ struct hid_descriptor {
 /* defines to get/set USB message */
 #define USB_REQ_GET_REPORT     0x01
 #define USB_REQ_SET_REPORT     0x09
+
 #define WAC_HID_FEATURE_REPORT 0x03
 #define WAC_MSG_RETRIES                5
 
-static int usb_get_report(struct usb_interface *intf, unsigned char type,
-                               unsigned char id, void *buf, int size)
+#define WAC_CMD_LED_CONTROL    0x20
+#define WAC_CMD_ICON_START     0x21
+#define WAC_CMD_ICON_XFER      0x23
+#define WAC_CMD_RETRIES                10
+
+static int wacom_get_report(struct usb_interface *intf, u8 type, u8 id,
+                           void *buf, size_t size, unsigned int retries)
 {
-       return usb_control_msg(interface_to_usbdev(intf),
-               usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
-               USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-               (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
-               buf, size, 100);
+       struct usb_device *dev = interface_to_usbdev(intf);
+       int retval;
+
+       do {
+               retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                               USB_REQ_GET_REPORT,
+                               USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                               (type << 8) + id,
+                               intf->altsetting[0].desc.bInterfaceNumber,
+                               buf, size, 100);
+       } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+
+       return retval;
 }
 
-static int usb_set_report(struct usb_interface *intf, unsigned char type,
-                               unsigned char id, void *buf, int size)
+static int wacom_set_report(struct usb_interface *intf, u8 type, u8 id,
+                           void *buf, size_t size, unsigned int retries)
 {
-       return usb_control_msg(interface_to_usbdev(intf),
-               usb_sndctrlpipe(interface_to_usbdev(intf), 0),
-                USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-                (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
-               buf, size, 1000);
+       struct usb_device *dev = interface_to_usbdev(intf);
+       int retval;
+
+       do {
+               retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                               USB_REQ_SET_REPORT,
+                               USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                               (type << 8) + id,
+                               intf->altsetting[0].desc.bInterfaceNumber,
+                               buf, size, 1000);
+       } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+
+       return retval;
 }
 
 static void wacom_sys_irq(struct urb *urb)
@@ -319,23 +341,23 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
                        rep_data[2] = 0;
                        rep_data[3] = 0;
                        report_id = 3;
-                       error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
-                               report_id, rep_data, 4);
+                       error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
+                                                report_id, rep_data, 4, 1);
                        if (error >= 0)
-                               error = usb_get_report(intf,
-                                       WAC_HID_FEATURE_REPORT, report_id,
-                                       rep_data, 4);
+                               error = wacom_get_report(intf,
+                                               WAC_HID_FEATURE_REPORT,
+                                               report_id, rep_data, 4, 1);
                } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES);
        } else if (features->type != TABLETPC) {
                do {
                        rep_data[0] = 2;
                        rep_data[1] = 2;
-                       error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
-                               report_id, rep_data, 2);
+                       error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
+                                                report_id, rep_data, 2, 1);
                        if (error >= 0)
-                               error = usb_get_report(intf,
-                                       WAC_HID_FEATURE_REPORT, report_id,
-                                       rep_data, 2);
+                               error = wacom_get_report(intf,
+                                               WAC_HID_FEATURE_REPORT,
+                                               report_id, rep_data, 2, 1);
                } while ((error < 0 || rep_data[1] != 2) && limit++ < WAC_MSG_RETRIES);
        }
 
@@ -454,6 +476,275 @@ static void wacom_remove_shared_data(struct wacom_wac *wacom)
        }
 }
 
+static int wacom_led_control(struct wacom *wacom)
+{
+       unsigned char *buf;
+       int retval, led = 0;
+
+       buf = kzalloc(9, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       if (wacom->wacom_wac.features.type == WACOM_21UX2)
+               led = (wacom->led.select[1] << 4) | 0x40;
+
+       led |=  wacom->led.select[0] | 0x4;
+
+       buf[0] = WAC_CMD_LED_CONTROL;
+       buf[1] = led;
+       buf[2] = wacom->led.llv;
+       buf[3] = wacom->led.hlv;
+       buf[4] = wacom->led.img_lum;
+
+       retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL,
+                                 buf, 9, WAC_CMD_RETRIES);
+       kfree(buf);
+
+       return retval;
+}
+
+static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img)
+{
+       unsigned char *buf;
+       int i, retval;
+
+       buf = kzalloc(259, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       /* Send 'start' command */
+       buf[0] = WAC_CMD_ICON_START;
+       buf[1] = 1;
+       retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START,
+                                 buf, 2, WAC_CMD_RETRIES);
+       if (retval < 0)
+               goto out;
+
+       buf[0] = WAC_CMD_ICON_XFER;
+       buf[1] = button_id & 0x07;
+       for (i = 0; i < 4; i++) {
+               buf[2] = i;
+               memcpy(buf + 3, img + i * 256, 256);
+
+               retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_XFER,
+                                         buf, 259, WAC_CMD_RETRIES);
+               if (retval < 0)
+                       break;
+       }
+
+       /* Send 'stop' */
+       buf[0] = WAC_CMD_ICON_START;
+       buf[1] = 0;
+       wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START,
+                        buf, 2, WAC_CMD_RETRIES);
+
+out:
+       kfree(buf);
+       return retval;
+}
+
+static ssize_t wacom_led_select_store(struct device *dev, int set_id,
+                                     const char *buf, size_t count)
+{
+       struct wacom *wacom = dev_get_drvdata(dev);
+       unsigned int id;
+       int err;
+
+       err = kstrtouint(buf, 10, &id);
+       if (err)
+               return err;
+
+       mutex_lock(&wacom->lock);
+
+       wacom->led.select[set_id] = id & 0x3;
+       err = wacom_led_control(wacom);
+
+       mutex_unlock(&wacom->lock);
+
+       return err < 0 ? err : count;
+}
+
+#define DEVICE_LED_SELECT_ATTR(SET_ID)                                 \
+static ssize_t wacom_led##SET_ID##_select_store(struct device *dev,    \
+       struct device_attribute *attr, const char *buf, size_t count)   \
+{                                                                      \
+       return wacom_led_select_store(dev, SET_ID, buf, count);         \
+}                                                                      \
+static ssize_t wacom_led##SET_ID##_select_show(struct device *dev,     \
+       struct device_attribute *attr, char *buf)                       \
+{                                                                      \
+       struct wacom *wacom = dev_get_drvdata(dev);                     \
+       return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]);     \
+}                                                                      \
+static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR,     \
+                   wacom_led##SET_ID##_select_show,                    \
+                   wacom_led##SET_ID##_select_store)
+
+DEVICE_LED_SELECT_ATTR(0);
+DEVICE_LED_SELECT_ATTR(1);
+
+static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest,
+                                    const char *buf, size_t count)
+{
+       unsigned int value;
+       int err;
+
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       mutex_lock(&wacom->lock);
+
+       *dest = value & 0x7f;
+       err = wacom_led_control(wacom);
+
+       mutex_unlock(&wacom->lock);
+
+       return err < 0 ? err : count;
+}
+
+#define DEVICE_LUMINANCE_ATTR(name, field)                             \
+static ssize_t wacom_##name##_luminance_store(struct device *dev,      \
+       struct device_attribute *attr, const char *buf, size_t count)   \
+{                                                                      \
+       struct wacom *wacom = dev_get_drvdata(dev);                     \
+                                                                       \
+       return wacom_luminance_store(wacom, &wacom->led.field,          \
+                                    buf, count);                       \
+}                                                                      \
+static DEVICE_ATTR(name##_luminance, S_IWUSR,                          \
+                  NULL, wacom_##name##_luminance_store)
+
+DEVICE_LUMINANCE_ATTR(status0, llv);
+DEVICE_LUMINANCE_ATTR(status1, hlv);
+DEVICE_LUMINANCE_ATTR(buttons, img_lum);
+
+static ssize_t wacom_button_image_store(struct device *dev, int button_id,
+                                       const char *buf, size_t count)
+{
+       struct wacom *wacom = dev_get_drvdata(dev);
+       int err;
+
+       if (count != 1024)
+               return -EINVAL;
+
+       mutex_lock(&wacom->lock);
+
+       err = wacom_led_putimage(wacom, button_id, buf);
+
+       mutex_unlock(&wacom->lock);
+
+       return err < 0 ? err : count;
+}
+
+#define DEVICE_BTNIMG_ATTR(BUTTON_ID)                                  \
+static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev,     \
+       struct device_attribute *attr, const char *buf, size_t count)   \
+{                                                                      \
+       return wacom_button_image_store(dev, BUTTON_ID, buf, count);    \
+}                                                                      \
+static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR,                        \
+                  NULL, wacom_btnimg##BUTTON_ID##_store)
+
+DEVICE_BTNIMG_ATTR(0);
+DEVICE_BTNIMG_ATTR(1);
+DEVICE_BTNIMG_ATTR(2);
+DEVICE_BTNIMG_ATTR(3);
+DEVICE_BTNIMG_ATTR(4);
+DEVICE_BTNIMG_ATTR(5);
+DEVICE_BTNIMG_ATTR(6);
+DEVICE_BTNIMG_ATTR(7);
+
+static struct attribute *cintiq_led_attrs[] = {
+       &dev_attr_status_led0_select.attr,
+       &dev_attr_status_led1_select.attr,
+       NULL
+};
+
+static struct attribute_group cintiq_led_attr_group = {
+       .name = "wacom_led",
+       .attrs = cintiq_led_attrs,
+};
+
+static struct attribute *intuos4_led_attrs[] = {
+       &dev_attr_status0_luminance.attr,
+       &dev_attr_status1_luminance.attr,
+       &dev_attr_status_led0_select.attr,
+       &dev_attr_buttons_luminance.attr,
+       &dev_attr_button0_rawimg.attr,
+       &dev_attr_button1_rawimg.attr,
+       &dev_attr_button2_rawimg.attr,
+       &dev_attr_button3_rawimg.attr,
+       &dev_attr_button4_rawimg.attr,
+       &dev_attr_button5_rawimg.attr,
+       &dev_attr_button6_rawimg.attr,
+       &dev_attr_button7_rawimg.attr,
+       NULL
+};
+
+static struct attribute_group intuos4_led_attr_group = {
+       .name = "wacom_led",
+       .attrs = intuos4_led_attrs,
+};
+
+static int wacom_initialize_leds(struct wacom *wacom)
+{
+       int error;
+
+       /* Initialize default values */
+       switch (wacom->wacom_wac.features.type) {
+       case INTUOS4:
+       case INTUOS4L:
+               wacom->led.select[0] = 0;
+               wacom->led.select[1] = 0;
+               wacom->led.llv = 10;
+               wacom->led.hlv = 20;
+               wacom->led.img_lum = 10;
+               error = sysfs_create_group(&wacom->intf->dev.kobj,
+                                          &intuos4_led_attr_group);
+               break;
+
+       case WACOM_21UX2:
+               wacom->led.select[0] = 0;
+               wacom->led.select[1] = 0;
+               wacom->led.llv = 0;
+               wacom->led.hlv = 0;
+               wacom->led.img_lum = 0;
+
+               error = sysfs_create_group(&wacom->intf->dev.kobj,
+                                          &cintiq_led_attr_group);
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (error) {
+               dev_err(&wacom->intf->dev,
+                       "cannot create sysfs group err: %d\n", error);
+               return error;
+       }
+       wacom_led_control(wacom);
+
+       return 0;
+}
+
+static void wacom_destroy_leds(struct wacom *wacom)
+{
+       switch (wacom->wacom_wac.features.type) {
+       case INTUOS4:
+       case INTUOS4L:
+               sysfs_remove_group(&wacom->intf->dev.kobj,
+                                  &intuos4_led_attr_group);
+               break;
+
+       case WACOM_21UX2:
+               sysfs_remove_group(&wacom->intf->dev.kobj,
+                                  &cintiq_led_attr_group);
+               break;
+       }
+}
+
 static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
        struct usb_device *dev = interface_to_usbdev(intf);
@@ -542,16 +833,21 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        wacom->irq->transfer_dma = wacom->data_dma;
        wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
-       error = input_register_device(input_dev);
+       error = wacom_initialize_leds(wacom);
        if (error)
                goto fail4;
 
+       error = input_register_device(input_dev);
+       if (error)
+               goto fail5;
+
        /* Note that if query fails it is not a hard failure */
        wacom_query_tablet_data(intf, features);
 
        usb_set_intfdata(intf, wacom);
        return 0;
 
+ fail5: wacom_destroy_leds(wacom);
  fail4:        wacom_remove_shared_data(wacom_wac);
  fail3:        usb_free_urb(wacom->irq);
  fail2:        usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);
@@ -568,6 +864,7 @@ static void wacom_disconnect(struct usb_interface *intf)
 
        usb_kill_urb(wacom->irq);
        input_unregister_device(wacom->wacom_wac.input);
+       wacom_destroy_leds(wacom);
        usb_free_urb(wacom->irq);
        usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
                        wacom->wacom_wac.data, wacom->data_dma);
@@ -590,17 +887,16 @@ static int wacom_resume(struct usb_interface *intf)
 {
        struct wacom *wacom = usb_get_intfdata(intf);
        struct wacom_features *features = &wacom->wacom_wac.features;
-       int rv;
+       int rv = 0;
 
        mutex_lock(&wacom->lock);
 
        /* switch to wacom mode first */
        wacom_query_tablet_data(intf, features);
+       wacom_led_control(wacom);
 
-       if (wacom->open)
-               rv = usb_submit_urb(wacom->irq, GFP_NOIO);
-       else
-               rv = 0;
+       if (wacom->open && usb_submit_urb(wacom->irq, GFP_NOIO) < 0)
+               rv = -EIO;
 
        mutex_unlock(&wacom->lock);