linux/drivers/hid/hid-wacom.c
<<
>>
Prefs
   1/*
   2 *  Bluetooth Wacom Tablet support
   3 *
   4 *  Copyright (c) 1999 Andreas Gal
   5 *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
   6 *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
   7 *  Copyright (c) 2006-2007 Jiri Kosina
   8 *  Copyright (c) 2007 Paul Walmsley
   9 *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
  10 *  Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
  11 *  Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
  12 */
  13
  14/*
  15 * This program is free software; you can redistribute it and/or modify it
  16 * under the terms of the GNU General Public License as published by the Free
  17 * Software Foundation; either version 2 of the License, or (at your option)
  18 * any later version.
  19 */
  20
  21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  22
  23#include <linux/device.h>
  24#include <linux/hid.h>
  25#include <linux/module.h>
  26#include <linux/slab.h>
  27#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
  28#include <linux/power_supply.h>
  29#endif
  30
  31#include "hid-ids.h"
  32
  33struct wacom_data {
  34        __u16 tool;
  35        unsigned char butstate;
  36        unsigned char high_speed;
  37#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
  38        int battery_capacity;
  39        struct power_supply battery;
  40        struct power_supply ac;
  41#endif
  42};
  43
  44#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
  45/*percent of battery capacity, 0 means AC online*/
  46static unsigned short batcap[8] = { 1, 15, 25, 35, 50, 70, 100, 0 };
  47
  48static enum power_supply_property wacom_battery_props[] = {
  49        POWER_SUPPLY_PROP_PRESENT,
  50        POWER_SUPPLY_PROP_CAPACITY
  51};
  52
  53static enum power_supply_property wacom_ac_props[] = {
  54        POWER_SUPPLY_PROP_PRESENT,
  55        POWER_SUPPLY_PROP_ONLINE
  56};
  57
  58static int wacom_battery_get_property(struct power_supply *psy,
  59                                enum power_supply_property psp,
  60                                union power_supply_propval *val)
  61{
  62        struct wacom_data *wdata = container_of(psy,
  63                                        struct wacom_data, battery);
  64        int power_state = batcap[wdata->battery_capacity];
  65        int ret = 0;
  66
  67        switch (psp) {
  68        case POWER_SUPPLY_PROP_PRESENT:
  69                val->intval = 1;
  70                break;
  71        case POWER_SUPPLY_PROP_CAPACITY:
  72                /* show 100% battery capacity when charging */
  73                if (power_state == 0)
  74                        val->intval = 100;
  75                else
  76                        val->intval = power_state;
  77                break;
  78        default:
  79                ret = -EINVAL;
  80                break;
  81        }
  82        return ret;
  83}
  84
  85static int wacom_ac_get_property(struct power_supply *psy,
  86                                enum power_supply_property psp,
  87                                union power_supply_propval *val)
  88{
  89        struct wacom_data *wdata = container_of(psy, struct wacom_data, ac);
  90        int power_state = batcap[wdata->battery_capacity];
  91        int ret = 0;
  92
  93        switch (psp) {
  94        case POWER_SUPPLY_PROP_PRESENT:
  95                /* fall through */
  96        case POWER_SUPPLY_PROP_ONLINE:
  97                if (power_state == 0)
  98                        val->intval = 1;
  99                else
 100                        val->intval = 0;
 101                break;
 102        default:
 103                ret = -EINVAL;
 104                break;
 105        }
 106        return ret;
 107}
 108#endif
 109
 110static void wacom_poke(struct hid_device *hdev, u8 speed)
 111{
 112        struct wacom_data *wdata = hid_get_drvdata(hdev);
 113        int limit, ret;
 114        char rep_data[2];
 115
 116        rep_data[0] = 0x03 ; rep_data[1] = 0x00;
 117        limit = 3;
 118        do {
 119                ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
 120                                HID_FEATURE_REPORT);
 121        } while (ret < 0 && limit-- > 0);
 122
 123        if (ret >= 0) {
 124                if (speed == 0)
 125                        rep_data[0] = 0x05;
 126                else
 127                        rep_data[0] = 0x06;
 128
 129                rep_data[1] = 0x00;
 130                limit = 3;
 131                do {
 132                        ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
 133                                        HID_FEATURE_REPORT);
 134                } while (ret < 0 && limit-- > 0);
 135
 136                if (ret >= 0) {
 137                        wdata->high_speed = speed;
 138                        return;
 139                }
 140        }
 141
 142        /*
 143         * Note that if the raw queries fail, it's not a hard failure and it
 144         * is safe to continue
 145         */
 146        hid_warn(hdev, "failed to poke device, command %d, err %d\n",
 147                 rep_data[0], ret);
 148        return;
 149}
 150
 151static ssize_t wacom_show_speed(struct device *dev,
 152                                struct device_attribute
 153                                *attr, char *buf)
 154{
 155        struct wacom_data *wdata = dev_get_drvdata(dev);
 156
 157        return snprintf(buf, PAGE_SIZE, "%i\n", wdata->high_speed);
 158}
 159
 160static ssize_t wacom_store_speed(struct device *dev,
 161                                struct device_attribute *attr,
 162                                const char *buf, size_t count)
 163{
 164        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 165        int new_speed;
 166
 167        if (sscanf(buf, "%1d", &new_speed ) != 1)
 168                return -EINVAL;
 169
 170        if (new_speed == 0 || new_speed == 1) {
 171                wacom_poke(hdev, new_speed);
 172                return strnlen(buf, PAGE_SIZE);
 173        } else
 174                return -EINVAL;
 175}
 176
 177static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
 178                wacom_show_speed, wacom_store_speed);
 179
 180static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
 181                u8 *raw_data, int size)
 182{
 183        struct wacom_data *wdata = hid_get_drvdata(hdev);
 184        struct hid_input *hidinput;
 185        struct input_dev *input;
 186        unsigned char *data = (unsigned char *) raw_data;
 187        int tool, x, y, rw;
 188
 189        if (!(hdev->claimed & HID_CLAIMED_INPUT))
 190                return 0;
 191
 192        tool = 0;
 193        hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
 194        input = hidinput->input;
 195
 196        /* Check if this is a tablet report */
 197        if (data[0] != 0x03)
 198                return 0;
 199
 200        /* Get X & Y positions */
 201        x = le16_to_cpu(*(__le16 *) &data[2]);
 202        y = le16_to_cpu(*(__le16 *) &data[4]);
 203
 204        /* Get current tool identifier */
 205        if (data[1] & 0x90) { /* If pen is in the in/active area */
 206                switch ((data[1] >> 5) & 3) {
 207                case 0: /* Pen */
 208                        tool = BTN_TOOL_PEN;
 209                        break;
 210
 211                case 1: /* Rubber */
 212                        tool = BTN_TOOL_RUBBER;
 213                        break;
 214
 215                case 2: /* Mouse with wheel */
 216                case 3: /* Mouse without wheel */
 217                        tool = BTN_TOOL_MOUSE;
 218                        break;
 219                }
 220
 221                /* Reset tool if out of active tablet area */
 222                if (!(data[1] & 0x10))
 223                        tool = 0;
 224        }
 225
 226        /* If tool changed, notify input subsystem */
 227        if (wdata->tool != tool) {
 228                if (wdata->tool) {
 229                        /* Completely reset old tool state */
 230                        if (wdata->tool == BTN_TOOL_MOUSE) {
 231                                input_report_key(input, BTN_LEFT, 0);
 232                                input_report_key(input, BTN_RIGHT, 0);
 233                                input_report_key(input, BTN_MIDDLE, 0);
 234                                input_report_abs(input, ABS_DISTANCE,
 235                                        input_abs_get_max(input, ABS_DISTANCE));
 236                        } else {
 237                                input_report_key(input, BTN_TOUCH, 0);
 238                                input_report_key(input, BTN_STYLUS, 0);
 239                                input_report_key(input, BTN_STYLUS2, 0);
 240                                input_report_abs(input, ABS_PRESSURE, 0);
 241                        }
 242                        input_report_key(input, wdata->tool, 0);
 243                        input_sync(input);
 244                }
 245                wdata->tool = tool;
 246                if (tool)
 247                        input_report_key(input, tool, 1);
 248        }
 249
 250        if (tool) {
 251                input_report_abs(input, ABS_X, x);
 252                input_report_abs(input, ABS_Y, y);
 253
 254                switch ((data[1] >> 5) & 3) {
 255                case 2: /* Mouse with wheel */
 256                        input_report_key(input, BTN_MIDDLE, data[1] & 0x04);
 257                        rw = (data[6] & 0x01) ? -1 :
 258                                (data[6] & 0x02) ? 1 : 0;
 259                        input_report_rel(input, REL_WHEEL, rw);
 260                        /* fall through */
 261
 262                case 3: /* Mouse without wheel */
 263                        input_report_key(input, BTN_LEFT, data[1] & 0x01);
 264                        input_report_key(input, BTN_RIGHT, data[1] & 0x02);
 265                        /* Compute distance between mouse and tablet */
 266                        rw = 44 - (data[6] >> 2);
 267                        if (rw < 0)
 268                                rw = 0;
 269                        else if (rw > 31)
 270                                rw = 31;
 271                        input_report_abs(input, ABS_DISTANCE, rw);
 272                        break;
 273
 274                default:
 275                        input_report_abs(input, ABS_PRESSURE,
 276                                        data[6] | (((__u16) (data[1] & 0x08)) << 5));
 277                        input_report_key(input, BTN_TOUCH, data[1] & 0x01);
 278                        input_report_key(input, BTN_STYLUS, data[1] & 0x02);
 279                        input_report_key(input, BTN_STYLUS2, (tool == BTN_TOOL_PEN) && data[1] & 0x04);
 280                        break;
 281                }
 282
 283                input_sync(input);
 284        }
 285
 286        /* Report the state of the two buttons at the top of the tablet
 287         * as two extra fingerpad keys (buttons 4 & 5). */
 288        rw = data[7] & 0x03;
 289        if (rw != wdata->butstate) {
 290                wdata->butstate = rw;
 291                input_report_key(input, BTN_0, rw & 0x02);
 292                input_report_key(input, BTN_1, rw & 0x01);
 293                input_report_key(input, BTN_TOOL_FINGER, 0xf0);
 294                input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
 295                input_sync(input);
 296        }
 297
 298#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
 299        /* Store current battery capacity */
 300        rw = (data[7] >> 2 & 0x07);
 301        if (rw != wdata->battery_capacity)
 302                wdata->battery_capacity = rw;
 303#endif
 304        return 1;
 305}
 306
 307static int wacom_probe(struct hid_device *hdev,
 308                const struct hid_device_id *id)
 309{
 310        struct hid_input *hidinput;
 311        struct input_dev *input;
 312        struct wacom_data *wdata;
 313        int ret;
 314
 315        wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
 316        if (wdata == NULL) {
 317                hid_err(hdev, "can't alloc wacom descriptor\n");
 318                return -ENOMEM;
 319        }
 320
 321        hid_set_drvdata(hdev, wdata);
 322
 323        /* Parse the HID report now */
 324        ret = hid_parse(hdev);
 325        if (ret) {
 326                hid_err(hdev, "parse failed\n");
 327                goto err_free;
 328        }
 329
 330        ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
 331        if (ret) {
 332                hid_err(hdev, "hw start failed\n");
 333                goto err_free;
 334        }
 335
 336        ret = device_create_file(&hdev->dev, &dev_attr_speed);
 337        if (ret)
 338                hid_warn(hdev,
 339                         "can't create sysfs speed attribute err: %d\n", ret);
 340
 341        /* Set Wacom mode 2 with high reporting speed */
 342        wacom_poke(hdev, 1);
 343
 344#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
 345        wdata->battery.properties = wacom_battery_props;
 346        wdata->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
 347        wdata->battery.get_property = wacom_battery_get_property;
 348        wdata->battery.name = "wacom_battery";
 349        wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
 350        wdata->battery.use_for_apm = 0;
 351
 352        ret = power_supply_register(&hdev->dev, &wdata->battery);
 353        if (ret) {
 354                hid_warn(hdev, "can't create sysfs battery attribute, err: %d\n",
 355                         ret);
 356                /*
 357                 * battery attribute is not critical for the tablet, but if it
 358                 * failed then there is no need to create ac attribute
 359                 */
 360                goto move_on;
 361        }
 362
 363        wdata->ac.properties = wacom_ac_props;
 364        wdata->ac.num_properties = ARRAY_SIZE(wacom_ac_props);
 365        wdata->ac.get_property = wacom_ac_get_property;
 366        wdata->ac.name = "wacom_ac";
 367        wdata->ac.type = POWER_SUPPLY_TYPE_MAINS;
 368        wdata->ac.use_for_apm = 0;
 369
 370        ret = power_supply_register(&hdev->dev, &wdata->ac);
 371        if (ret) {
 372                hid_warn(hdev,
 373                         "can't create ac battery attribute, err: %d\n", ret);
 374                /*
 375                 * ac attribute is not critical for the tablet, but if it
 376                 * failed then we don't want to battery attribute to exist
 377                 */
 378                power_supply_unregister(&wdata->battery);
 379        }
 380
 381move_on:
 382#endif
 383        hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
 384        input = hidinput->input;
 385
 386        /* Basics */
 387        input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
 388
 389        __set_bit(REL_WHEEL, input->relbit);
 390
 391        __set_bit(BTN_TOOL_PEN, input->keybit);
 392        __set_bit(BTN_TOUCH, input->keybit);
 393        __set_bit(BTN_STYLUS, input->keybit);
 394        __set_bit(BTN_STYLUS2, input->keybit);
 395        __set_bit(BTN_LEFT, input->keybit);
 396        __set_bit(BTN_RIGHT, input->keybit);
 397        __set_bit(BTN_MIDDLE, input->keybit);
 398
 399        /* Pad */
 400        input->evbit[0] |= BIT(EV_MSC);
 401
 402        __set_bit(MSC_SERIAL, input->mscbit);
 403
 404        __set_bit(BTN_0, input->keybit);
 405        __set_bit(BTN_1, input->keybit);
 406        __set_bit(BTN_TOOL_FINGER, input->keybit);
 407
 408        /* Distance, rubber and mouse */
 409        __set_bit(BTN_TOOL_RUBBER, input->keybit);
 410        __set_bit(BTN_TOOL_MOUSE, input->keybit);
 411
 412        input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
 413        input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
 414        input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
 415        input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
 416
 417        return 0;
 418
 419err_free:
 420        kfree(wdata);
 421        return ret;
 422}
 423
 424static void wacom_remove(struct hid_device *hdev)
 425{
 426#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
 427        struct wacom_data *wdata = hid_get_drvdata(hdev);
 428#endif
 429        hid_hw_stop(hdev);
 430
 431#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
 432        power_supply_unregister(&wdata->battery);
 433        power_supply_unregister(&wdata->ac);
 434#endif
 435        kfree(hid_get_drvdata(hdev));
 436}
 437
 438static const struct hid_device_id wacom_devices[] = {
 439        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
 440
 441        { }
 442};
 443MODULE_DEVICE_TABLE(hid, wacom_devices);
 444
 445static struct hid_driver wacom_driver = {
 446        .name = "wacom",
 447        .id_table = wacom_devices,
 448        .probe = wacom_probe,
 449        .remove = wacom_remove,
 450        .raw_event = wacom_raw_event,
 451};
 452
 453static int __init wacom_init(void)
 454{
 455        int ret;
 456
 457        ret = hid_register_driver(&wacom_driver);
 458        if (ret)
 459                pr_err("can't register wacom driver\n");
 460        return ret;
 461}
 462
 463static void __exit wacom_exit(void)
 464{
 465        hid_unregister_driver(&wacom_driver);
 466}
 467
 468module_init(wacom_init);
 469module_exit(wacom_exit);
 470MODULE_LICENSE("GPL");
 471
 472