Merge branch 'next' into for-linus
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 27 Oct 2011 04:46:20 +0000 (21:46 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 27 Oct 2011 04:46:20 +0000 (21:46 -0700)
1  2 
drivers/input/keyboard/adp5588-keys.c
drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/input/touchscreen/wacom_w8001.c

@@@ -550,7 -550,7 +550,7 @@@ static int __devinit adp5588_probe(stru
        }
  
        error = request_irq(client->irq, adp5588_irq,
-                           IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+                           IRQF_TRIGGER_FALLING,
                            client->dev.driver->name, kpad);
        if (error) {
                dev_err(&client->dev, "irq %d busy?\n", client->irq);
@@@ -668,3 -668,4 +668,3 @@@ module_exit(adp5588_exit)
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
  MODULE_DESCRIPTION("ADP5588/87 Keypad driver");
 -MODULE_ALIAS("platform:adp5588-keys");
@@@ -48,27 -48,49 +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)
@@@ -229,6 -251,13 +251,6 @@@ static int wacom_parse_hid(struct usb_i
                                                        get_unaligned_le16(&report[i + 3]);
                                                i += 4;
                                        }
 -                              } else if (usage == WCM_DIGITIZER) {
 -                                      /* max pressure isn't reported
 -                                      features->pressure_max = (unsigned short)
 -                                                      (report[i+4] << 8  | report[i + 3]);
 -                                      */
 -                                      features->pressure_max = 255;
 -                                      i += 4;
                                }
                                break;
  
                                pen = 1;
                                i++;
                                break;
 -
 -                      case HID_USAGE_UNDEFINED:
 -                              if (usage == WCM_DESKTOP && finger) /* capacity */
 -                                      features->pressure_max =
 -                                              get_unaligned_le16(&report[i + 3]);
 -                              i += 4;
 -                              break;
                        }
                        break;
  
@@@ -319,23 -355,23 +341,23 @@@ static int wacom_query_tablet_data(stru
                        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 -490,275 +476,275 @@@ static void wacom_remove_shared_data(st
        }
  }
  
+ 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);
        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 -878,7 +864,7 @@@ static void wacom_disconnect(struct usb
  
        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 -901,16 +887,16 @@@ static int wacom_resume(struct usb_inte
  {
        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);
  
@@@ -800,26 -800,25 +800,26 @@@ static int wacom_bpt_touch(struct wacom
        int i;
  
        for (i = 0; i < 2; i++) {
 -              int p = data[9 * i + 2];
 -              bool touch = p && !wacom->shared->stylus_in_proximity;
 +              int offset = (data[1] & 0x80) ? (8 * i) : (9 * i);
 +              bool touch = data[offset + 3] & 0x80;
  
 -              input_mt_slot(input, i);
 -              input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
                /*
                 * Touch events need to be disabled while stylus is
                 * in proximity because user's hand is resting on touchpad
                 * and sending unwanted events.  User expects tablet buttons
                 * to continue working though.
                 */
 +              touch = touch && !wacom->shared->stylus_in_proximity;
 +
 +              input_mt_slot(input, i);
 +              input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
                if (touch) {
 -                      int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff;
 -                      int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff;
 +                      int x = get_unaligned_be16(&data[offset + 3]) & 0x7ff;
 +                      int y = get_unaligned_be16(&data[offset + 5]) & 0x7ff;
                        if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) {
                                x <<= 5;
                                y <<= 5;
                        }
 -                      input_report_abs(input, ABS_MT_PRESSURE, p);
                        input_report_abs(input, ABS_MT_POSITION_X, x);
                        input_report_abs(input, ABS_MT_POSITION_Y, y);
                }
@@@ -874,7 -873,15 +874,15 @@@ static int wacom_bpt_pen(struct wacom_w
                x = le16_to_cpup((__le16 *)&data[2]);
                y = le16_to_cpup((__le16 *)&data[4]);
                p = le16_to_cpup((__le16 *)&data[6]);
-               d = data[8];
+               /*
+                * Convert distance from out prox to distance from tablet.
+                * distance will be greater than distance_max once
+                * touching and applying pressure; do not report negative
+                * distance.
+                */
+               if (data[8] <= wacom->features.distance_max)
+                       d = wacom->features.distance_max - data[8];
                pen = data[1] & 0x01;
                btn1 = data[1] & 0x02;
                btn2 = data[1] & 0x04;
@@@ -1030,8 -1037,6 +1038,6 @@@ void wacom_setup_device_quirks(struct w
                features->y_max <<= 5;
                features->x_fuzz <<= 5;
                features->y_fuzz <<= 5;
-               features->pressure_max = 256;
-               features->pressure_fuzz = 16;
                features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES;
        }
  }
@@@ -1057,11 -1062,10 +1063,11 @@@ void wacom_setup_input_capabilities(str
                             features->x_fuzz, 0);
        input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
                             features->y_fuzz, 0);
 -      input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max,
 -                           features->pressure_fuzz, 0);
  
        if (features->device_type == BTN_TOOL_PEN) {
 +              input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max,
 +                           features->pressure_fuzz, 0);
 +
                /* penabled devices have fixed resolution for each model */
                input_abs_set_res(input_dev, ABS_X, features->x_resolution);
                input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
                __set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
                __set_bit(BTN_STYLUS, input_dev->keybit);
                __set_bit(BTN_STYLUS2, input_dev->keybit);
 +
 +              __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
                break;
  
        case WACOM_21UX2:
                for (i = 0; i < 8; i++)
                        __set_bit(BTN_0 + i, input_dev->keybit);
  
 -              if (wacom_wac->features.type != WACOM_21UX2) {
 -                      input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
 -                      input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
 -              }
 -
 +              input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
 +              input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
                input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
 +
 +              __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
 +
                wacom_setup_cintiq(wacom_wac);
                break;
  
                /* fall through */
  
        case INTUOS:
 +              __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
 +
                wacom_setup_intuos(wacom_wac);
                break;
  
  
                input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
                wacom_setup_intuos(wacom_wac);
 +
 +              __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
                break;
  
        case TABLETPC2FG:
        case TABLETPC:
                __clear_bit(ABS_MISC, input_dev->absbit);
  
 +              __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
 +
                if (features->device_type != BTN_TOOL_PEN)
                        break;  /* no need to process stylus stuff */
  
                /* fall through */
  
        case PL:
 -      case PTU:
        case DTU:
                __set_bit(BTN_TOOL_PEN, input_dev->keybit);
 +              __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
                __set_bit(BTN_STYLUS, input_dev->keybit);
                __set_bit(BTN_STYLUS2, input_dev->keybit);
 +
 +              __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
 +              break;
 +
 +      case PTU:
 +              __set_bit(BTN_STYLUS2, input_dev->keybit);
                /* fall through */
  
        case PENPARTNER:
 +              __set_bit(BTN_TOOL_PEN, input_dev->keybit);
                __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
 +              __set_bit(BTN_STYLUS, input_dev->keybit);
 +
 +              __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
                break;
  
        case BAMBOO_PT:
                __clear_bit(ABS_MISC, input_dev->absbit);
  
 +              __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
 +
                if (features->device_type == BTN_TOOL_DOUBLETAP) {
                        __set_bit(BTN_LEFT, input_dev->keybit);
                        __set_bit(BTN_FORWARD, input_dev->keybit);
                        input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
                                             0, features->y_max,
                                             features->y_fuzz, 0);
-                       input_set_abs_params(input_dev, ABS_MT_PRESSURE,
-                                            0, features->pressure_max,
-                                            features->pressure_fuzz, 0);
                } else if (features->device_type == BTN_TOOL_PEN) {
                        __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
                        __set_bit(BTN_TOOL_PEN, input_dev->keybit);
                        __set_bit(BTN_STYLUS, input_dev->keybit);
                        __set_bit(BTN_STYLUS2, input_dev->keybit);
+                       input_set_abs_params(input_dev, ABS_DISTANCE, 0,
+                                             features->distance_max,
+                                             0, 0);
                }
                break;
        }
@@@ -1469,37 -1453,37 +1475,37 @@@ static const struct wacom_features waco
          31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0xD0 =
        { "Wacom Bamboo 2FG",     WACOM_PKGLEN_BBFUN,     14720,  9200, 1023,
-         63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+         31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0xD1 =
        { "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN,     14720,  9200, 1023,
-         63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+         31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0xD2 =
        { "Wacom Bamboo Craft",   WACOM_PKGLEN_BBFUN,     14720,  9200, 1023,
-         63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+         31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0xD3 =
-       { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN,     21648, 13530, 1023,
-         63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+       { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN,     21648, 13700, 1023,
+         31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0xD4 =
        { "Wacom Bamboo Pen",     WACOM_PKGLEN_BBFUN,     14720,  9200, 1023,
-         63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+         31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0xD5 =
-       { "Wacom Bamboo Pen 6x8",     WACOM_PKGLEN_BBFUN, 21648, 13530, 1023,
-         63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+       { "Wacom Bamboo Pen 6x8",     WACOM_PKGLEN_BBFUN, 21648, 13700, 1023,
+         31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0xD6 =
        { "Wacom BambooPT 2FG 4x5", WACOM_PKGLEN_BBFUN,   14720,  9200, 1023,
-         63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+         31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0xD7 =
        { "Wacom BambooPT 2FG Small", WACOM_PKGLEN_BBFUN, 14720,  9200, 1023,
-         63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+         31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0xD8 =
-       { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN,   21648, 13530, 1023,
-         63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+       { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN,   21648, 13700, 1023,
+         31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0xDA =
        { "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN,  14720,  9200, 1023,
-         63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+         31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static struct wacom_features wacom_features_0xDB =
-       { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN,  21648, 13530, 1023,
-         63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+       { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN,  21648, 13700, 1023,
+         31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0x6004 =
        { "ISD-V4",               WACOM_PKGLEN_GRAPHIRE,  12800,  8000,  255,
          0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@@ -367,6 -367,20 +367,20 @@@ static int w8001_command(struct w8001 *
        return rc;
  }
  
+ static int w8001_open(struct input_dev *dev)
+ {
+       struct w8001 *w8001 = input_get_drvdata(dev);
+       return w8001_command(w8001, W8001_CMD_START, false);
+ }
+ static void w8001_close(struct input_dev *dev)
+ {
+       struct w8001 *w8001 = input_get_drvdata(dev);
+       w8001_command(w8001, W8001_CMD_STOP, false);
+ }
  static int w8001_setup(struct w8001 *w8001)
  {
        struct input_dev *dev = w8001->dev;
        dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
        strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
  
 +      __set_bit(INPUT_PROP_DIRECT, dev->propbit);
 +
        /* penabled? */
        error = w8001_command(w8001, W8001_CMD_QUERY, true);
        if (!error) {
  
        strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
  
-       return w8001_command(w8001, W8001_CMD_START, false);
+       return 0;
  }
  
  /*
@@@ -487,12 -499,12 +501,12 @@@ static void w8001_disconnect(struct ser
  {
        struct w8001 *w8001 = serio_get_drvdata(serio);
  
-       input_get_device(w8001->dev);
-       input_unregister_device(w8001->dev);
        serio_close(serio);
-       serio_set_drvdata(serio, NULL);
-       input_put_device(w8001->dev);
+       input_unregister_device(w8001->dev);
        kfree(w8001);
+       serio_set_drvdata(serio, NULL);
  }
  
  /*
@@@ -536,6 -548,11 +550,11 @@@ static int w8001_connect(struct serio *
        input_dev->id.version = 0x0100;
        input_dev->dev.parent = &serio->dev;
  
+       input_dev->open = w8001_open;
+       input_dev->close = w8001_close;
+       input_set_drvdata(input_dev, w8001);
        err = input_register_device(w8001->dev);
        if (err)
                goto fail3;