linux/drivers/hid/hid-uclogic.c
<<
>>
Prefs
   1/*
   2 *  HID driver for UC-Logic devices not fully compliant with HID standard
   3 *
   4 *  Copyright (c) 2010-2014 Nikolai Kondrashov
   5 *  Copyright (c) 2013 Martin Rusko
   6 */
   7
   8/*
   9 * This program is free software; you can redistribute it and/or modify it
  10 * under the terms of the GNU General Public License as published by the Free
  11 * Software Foundation; either version 2 of the License, or (at your option)
  12 * any later version.
  13 */
  14
  15#include <linux/device.h>
  16#include <linux/hid.h>
  17#include <linux/module.h>
  18#include <linux/usb.h>
  19#include <asm/unaligned.h>
  20#include "usbhid/usbhid.h"
  21
  22#include "hid-ids.h"
  23
  24/* Size of the original descriptor of WPXXXXU tablets */
  25#define WPXXXXU_RDESC_ORIG_SIZE 212
  26
  27/* Fixed WP4030U report descriptor */
  28static __u8 wp4030u_rdesc_fixed[] = {
  29        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
  30        0x09, 0x02,         /*  Usage (Pen),                        */
  31        0xA1, 0x01,         /*  Collection (Application),           */
  32        0x85, 0x09,         /*      Report ID (9),                  */
  33        0x09, 0x20,         /*      Usage (Stylus),                 */
  34        0xA0,               /*      Collection (Physical),          */
  35        0x75, 0x01,         /*          Report Size (1),            */
  36        0x09, 0x42,         /*          Usage (Tip Switch),         */
  37        0x09, 0x44,         /*          Usage (Barrel Switch),      */
  38        0x09, 0x46,         /*          Usage (Tablet Pick),        */
  39        0x14,               /*          Logical Minimum (0),        */
  40        0x25, 0x01,         /*          Logical Maximum (1),        */
  41        0x95, 0x03,         /*          Report Count (3),           */
  42        0x81, 0x02,         /*          Input (Variable),           */
  43        0x95, 0x05,         /*          Report Count (5),           */
  44        0x81, 0x01,         /*          Input (Constant),           */
  45        0x75, 0x10,         /*          Report Size (16),           */
  46        0x95, 0x01,         /*          Report Count (1),           */
  47        0x14,               /*          Logical Minimum (0),        */
  48        0xA4,               /*          Push,                       */
  49        0x05, 0x01,         /*          Usage Page (Desktop),       */
  50        0x55, 0xFD,         /*          Unit Exponent (-3),         */
  51        0x65, 0x13,         /*          Unit (Inch),                */
  52        0x34,               /*          Physical Minimum (0),       */
  53        0x09, 0x30,         /*          Usage (X),                  */
  54        0x46, 0xA0, 0x0F,   /*          Physical Maximum (4000),    */
  55        0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
  56        0x81, 0x02,         /*          Input (Variable),           */
  57        0x09, 0x31,         /*          Usage (Y),                  */
  58        0x46, 0xB8, 0x0B,   /*          Physical Maximum (3000),    */
  59        0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
  60        0x81, 0x02,         /*          Input (Variable),           */
  61        0xB4,               /*          Pop,                        */
  62        0x09, 0x30,         /*          Usage (Tip Pressure),       */
  63        0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
  64        0x81, 0x02,         /*          Input (Variable),           */
  65        0xC0,               /*      End Collection,                 */
  66        0xC0                /*  End Collection                      */
  67};
  68
  69/* Fixed WP5540U report descriptor */
  70static __u8 wp5540u_rdesc_fixed[] = {
  71        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
  72        0x09, 0x02,         /*  Usage (Pen),                        */
  73        0xA1, 0x01,         /*  Collection (Application),           */
  74        0x85, 0x09,         /*      Report ID (9),                  */
  75        0x09, 0x20,         /*      Usage (Stylus),                 */
  76        0xA0,               /*      Collection (Physical),          */
  77        0x75, 0x01,         /*          Report Size (1),            */
  78        0x09, 0x42,         /*          Usage (Tip Switch),         */
  79        0x09, 0x44,         /*          Usage (Barrel Switch),      */
  80        0x09, 0x46,         /*          Usage (Tablet Pick),        */
  81        0x14,               /*          Logical Minimum (0),        */
  82        0x25, 0x01,         /*          Logical Maximum (1),        */
  83        0x95, 0x03,         /*          Report Count (3),           */
  84        0x81, 0x02,         /*          Input (Variable),           */
  85        0x95, 0x05,         /*          Report Count (5),           */
  86        0x81, 0x01,         /*          Input (Constant),           */
  87        0x75, 0x10,         /*          Report Size (16),           */
  88        0x95, 0x01,         /*          Report Count (1),           */
  89        0x14,               /*          Logical Minimum (0),        */
  90        0xA4,               /*          Push,                       */
  91        0x05, 0x01,         /*          Usage Page (Desktop),       */
  92        0x55, 0xFD,         /*          Unit Exponent (-3),         */
  93        0x65, 0x13,         /*          Unit (Inch),                */
  94        0x34,               /*          Physical Minimum (0),       */
  95        0x09, 0x30,         /*          Usage (X),                  */
  96        0x46, 0x7C, 0x15,   /*          Physical Maximum (5500),    */
  97        0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
  98        0x81, 0x02,         /*          Input (Variable),           */
  99        0x09, 0x31,         /*          Usage (Y),                  */
 100        0x46, 0xA0, 0x0F,   /*          Physical Maximum (4000),    */
 101        0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
 102        0x81, 0x02,         /*          Input (Variable),           */
 103        0xB4,               /*          Pop,                        */
 104        0x09, 0x30,         /*          Usage (Tip Pressure),       */
 105        0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
 106        0x81, 0x02,         /*          Input (Variable),           */
 107        0xC0,               /*      End Collection,                 */
 108        0xC0,               /*  End Collection,                     */
 109        0x05, 0x01,         /*  Usage Page (Desktop),               */
 110        0x09, 0x02,         /*  Usage (Mouse),                      */
 111        0xA1, 0x01,         /*  Collection (Application),           */
 112        0x85, 0x08,         /*      Report ID (8),                  */
 113        0x09, 0x01,         /*      Usage (Pointer),                */
 114        0xA0,               /*      Collection (Physical),          */
 115        0x75, 0x01,         /*          Report Size (1),            */
 116        0x05, 0x09,         /*          Usage Page (Button),        */
 117        0x19, 0x01,         /*          Usage Minimum (01h),        */
 118        0x29, 0x03,         /*          Usage Maximum (03h),        */
 119        0x14,               /*          Logical Minimum (0),        */
 120        0x25, 0x01,         /*          Logical Maximum (1),        */
 121        0x95, 0x03,         /*          Report Count (3),           */
 122        0x81, 0x02,         /*          Input (Variable),           */
 123        0x95, 0x05,         /*          Report Count (5),           */
 124        0x81, 0x01,         /*          Input (Constant),           */
 125        0x05, 0x01,         /*          Usage Page (Desktop),       */
 126        0x75, 0x08,         /*          Report Size (8),            */
 127        0x09, 0x30,         /*          Usage (X),                  */
 128        0x09, 0x31,         /*          Usage (Y),                  */
 129        0x15, 0x81,         /*          Logical Minimum (-127),     */
 130        0x25, 0x7F,         /*          Logical Maximum (127),      */
 131        0x95, 0x02,         /*          Report Count (2),           */
 132        0x81, 0x06,         /*          Input (Variable, Relative), */
 133        0x09, 0x38,         /*          Usage (Wheel),              */
 134        0x15, 0xFF,         /*          Logical Minimum (-1),       */
 135        0x25, 0x01,         /*          Logical Maximum (1),        */
 136        0x95, 0x01,         /*          Report Count (1),           */
 137        0x81, 0x06,         /*          Input (Variable, Relative), */
 138        0x81, 0x01,         /*          Input (Constant),           */
 139        0xC0,               /*      End Collection,                 */
 140        0xC0                /*  End Collection                      */
 141};
 142
 143/* Fixed WP8060U report descriptor */
 144static __u8 wp8060u_rdesc_fixed[] = {
 145        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
 146        0x09, 0x02,         /*  Usage (Pen),                        */
 147        0xA1, 0x01,         /*  Collection (Application),           */
 148        0x85, 0x09,         /*      Report ID (9),                  */
 149        0x09, 0x20,         /*      Usage (Stylus),                 */
 150        0xA0,               /*      Collection (Physical),          */
 151        0x75, 0x01,         /*          Report Size (1),            */
 152        0x09, 0x42,         /*          Usage (Tip Switch),         */
 153        0x09, 0x44,         /*          Usage (Barrel Switch),      */
 154        0x09, 0x46,         /*          Usage (Tablet Pick),        */
 155        0x14,               /*          Logical Minimum (0),        */
 156        0x25, 0x01,         /*          Logical Maximum (1),        */
 157        0x95, 0x03,         /*          Report Count (3),           */
 158        0x81, 0x02,         /*          Input (Variable),           */
 159        0x95, 0x05,         /*          Report Count (5),           */
 160        0x81, 0x01,         /*          Input (Constant),           */
 161        0x75, 0x10,         /*          Report Size (16),           */
 162        0x95, 0x01,         /*          Report Count (1),           */
 163        0x14,               /*          Logical Minimum (0),        */
 164        0xA4,               /*          Push,                       */
 165        0x05, 0x01,         /*          Usage Page (Desktop),       */
 166        0x55, 0xFD,         /*          Unit Exponent (-3),         */
 167        0x65, 0x13,         /*          Unit (Inch),                */
 168        0x34,               /*          Physical Minimum (0),       */
 169        0x09, 0x30,         /*          Usage (X),                  */
 170        0x46, 0x40, 0x1F,   /*          Physical Maximum (8000),    */
 171        0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
 172        0x81, 0x02,         /*          Input (Variable),           */
 173        0x09, 0x31,         /*          Usage (Y),                  */
 174        0x46, 0x70, 0x17,   /*          Physical Maximum (6000),    */
 175        0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
 176        0x81, 0x02,         /*          Input (Variable),           */
 177        0xB4,               /*          Pop,                        */
 178        0x09, 0x30,         /*          Usage (Tip Pressure),       */
 179        0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
 180        0x81, 0x02,         /*          Input (Variable),           */
 181        0xC0,               /*      End Collection,                 */
 182        0xC0,               /*  End Collection,                     */
 183        0x05, 0x01,         /*  Usage Page (Desktop),               */
 184        0x09, 0x02,         /*  Usage (Mouse),                      */
 185        0xA1, 0x01,         /*  Collection (Application),           */
 186        0x85, 0x08,         /*      Report ID (8),                  */
 187        0x09, 0x01,         /*      Usage (Pointer),                */
 188        0xA0,               /*      Collection (Physical),          */
 189        0x75, 0x01,         /*          Report Size (1),            */
 190        0x05, 0x09,         /*          Usage Page (Button),        */
 191        0x19, 0x01,         /*          Usage Minimum (01h),        */
 192        0x29, 0x03,         /*          Usage Maximum (03h),        */
 193        0x14,               /*          Logical Minimum (0),        */
 194        0x25, 0x01,         /*          Logical Maximum (1),        */
 195        0x95, 0x03,         /*          Report Count (3),           */
 196        0x81, 0x02,         /*          Input (Variable),           */
 197        0x95, 0x05,         /*          Report Count (5),           */
 198        0x81, 0x01,         /*          Input (Constant),           */
 199        0x05, 0x01,         /*          Usage Page (Desktop),       */
 200        0x75, 0x08,         /*          Report Size (8),            */
 201        0x09, 0x30,         /*          Usage (X),                  */
 202        0x09, 0x31,         /*          Usage (Y),                  */
 203        0x15, 0x81,         /*          Logical Minimum (-127),     */
 204        0x25, 0x7F,         /*          Logical Maximum (127),      */
 205        0x95, 0x02,         /*          Report Count (2),           */
 206        0x81, 0x06,         /*          Input (Variable, Relative), */
 207        0x09, 0x38,         /*          Usage (Wheel),              */
 208        0x15, 0xFF,         /*          Logical Minimum (-1),       */
 209        0x25, 0x01,         /*          Logical Maximum (1),        */
 210        0x95, 0x01,         /*          Report Count (1),           */
 211        0x81, 0x06,         /*          Input (Variable, Relative), */
 212        0x81, 0x01,         /*          Input (Constant),           */
 213        0xC0,               /*      End Collection,                 */
 214        0xC0                /*  End Collection                      */
 215};
 216
 217/* Size of the original descriptor of WP1062 tablet */
 218#define WP1062_RDESC_ORIG_SIZE  254
 219
 220/* Fixed WP1062 report descriptor */
 221static __u8 wp1062_rdesc_fixed[] = {
 222        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
 223        0x09, 0x02,         /*  Usage (Pen),                        */
 224        0xA1, 0x01,         /*  Collection (Application),           */
 225        0x85, 0x09,         /*      Report ID (9),                  */
 226        0x09, 0x20,         /*      Usage (Stylus),                 */
 227        0xA0,               /*      Collection (Physical),          */
 228        0x75, 0x01,         /*          Report Size (1),            */
 229        0x09, 0x42,         /*          Usage (Tip Switch),         */
 230        0x09, 0x44,         /*          Usage (Barrel Switch),      */
 231        0x09, 0x46,         /*          Usage (Tablet Pick),        */
 232        0x14,               /*          Logical Minimum (0),        */
 233        0x25, 0x01,         /*          Logical Maximum (1),        */
 234        0x95, 0x03,         /*          Report Count (3),           */
 235        0x81, 0x02,         /*          Input (Variable),           */
 236        0x95, 0x04,         /*          Report Count (4),           */
 237        0x81, 0x01,         /*          Input (Constant),           */
 238        0x09, 0x32,         /*          Usage (In Range),           */
 239        0x95, 0x01,         /*          Report Count (1),           */
 240        0x81, 0x02,         /*          Input (Variable),           */
 241        0x75, 0x10,         /*          Report Size (16),           */
 242        0x95, 0x01,         /*          Report Count (1),           */
 243        0x14,               /*          Logical Minimum (0),        */
 244        0xA4,               /*          Push,                       */
 245        0x05, 0x01,         /*          Usage Page (Desktop),       */
 246        0x55, 0xFD,         /*          Unit Exponent (-3),         */
 247        0x65, 0x13,         /*          Unit (Inch),                */
 248        0x34,               /*          Physical Minimum (0),       */
 249        0x09, 0x30,         /*          Usage (X),                  */
 250        0x46, 0x10, 0x27,   /*          Physical Maximum (10000),   */
 251        0x26, 0x20, 0x4E,   /*          Logical Maximum (20000),    */
 252        0x81, 0x02,         /*          Input (Variable),           */
 253        0x09, 0x31,         /*          Usage (Y),                  */
 254        0x46, 0xB7, 0x19,   /*          Physical Maximum (6583),    */
 255        0x26, 0x6E, 0x33,   /*          Logical Maximum (13166),    */
 256        0x81, 0x02,         /*          Input (Variable),           */
 257        0xB4,               /*          Pop,                        */
 258        0x09, 0x30,         /*          Usage (Tip Pressure),       */
 259        0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
 260        0x81, 0x02,         /*          Input (Variable),           */
 261        0xC0,               /*      End Collection,                 */
 262        0xC0                /*  End Collection                      */
 263};
 264
 265/* Size of the original descriptor of PF1209 tablet */
 266#define PF1209_RDESC_ORIG_SIZE  234
 267
 268/* Fixed PF1209 report descriptor */
 269static __u8 pf1209_rdesc_fixed[] = {
 270        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
 271        0x09, 0x02,         /*  Usage (Pen),                        */
 272        0xA1, 0x01,         /*  Collection (Application),           */
 273        0x85, 0x09,         /*      Report ID (9),                  */
 274        0x09, 0x20,         /*      Usage (Stylus),                 */
 275        0xA0,               /*      Collection (Physical),          */
 276        0x75, 0x01,         /*          Report Size (1),            */
 277        0x09, 0x42,         /*          Usage (Tip Switch),         */
 278        0x09, 0x44,         /*          Usage (Barrel Switch),      */
 279        0x09, 0x46,         /*          Usage (Tablet Pick),        */
 280        0x14,               /*          Logical Minimum (0),        */
 281        0x25, 0x01,         /*          Logical Maximum (1),        */
 282        0x95, 0x03,         /*          Report Count (3),           */
 283        0x81, 0x02,         /*          Input (Variable),           */
 284        0x95, 0x05,         /*          Report Count (5),           */
 285        0x81, 0x01,         /*          Input (Constant),           */
 286        0x75, 0x10,         /*          Report Size (16),           */
 287        0x95, 0x01,         /*          Report Count (1),           */
 288        0x14,               /*          Logical Minimum (0),        */
 289        0xA4,               /*          Push,                       */
 290        0x05, 0x01,         /*          Usage Page (Desktop),       */
 291        0x55, 0xFD,         /*          Unit Exponent (-3),         */
 292        0x65, 0x13,         /*          Unit (Inch),                */
 293        0x34,               /*          Physical Minimum (0),       */
 294        0x09, 0x30,         /*          Usage (X),                  */
 295        0x46, 0xE0, 0x2E,   /*          Physical Maximum (12000),   */
 296        0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
 297        0x81, 0x02,         /*          Input (Variable),           */
 298        0x09, 0x31,         /*          Usage (Y),                  */
 299        0x46, 0x28, 0x23,   /*          Physical Maximum (9000),    */
 300        0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
 301        0x81, 0x02,         /*          Input (Variable),           */
 302        0xB4,               /*          Pop,                        */
 303        0x09, 0x30,         /*          Usage (Tip Pressure),       */
 304        0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
 305        0x81, 0x02,         /*          Input (Variable),           */
 306        0xC0,               /*      End Collection,                 */
 307        0xC0,               /*  End Collection,                     */
 308        0x05, 0x01,         /*  Usage Page (Desktop),               */
 309        0x09, 0x02,         /*  Usage (Mouse),                      */
 310        0xA1, 0x01,         /*  Collection (Application),           */
 311        0x85, 0x08,         /*      Report ID (8),                  */
 312        0x09, 0x01,         /*      Usage (Pointer),                */
 313        0xA0,               /*      Collection (Physical),          */
 314        0x75, 0x01,         /*          Report Size (1),            */
 315        0x05, 0x09,         /*          Usage Page (Button),        */
 316        0x19, 0x01,         /*          Usage Minimum (01h),        */
 317        0x29, 0x03,         /*          Usage Maximum (03h),        */
 318        0x14,               /*          Logical Minimum (0),        */
 319        0x25, 0x01,         /*          Logical Maximum (1),        */
 320        0x95, 0x03,         /*          Report Count (3),           */
 321        0x81, 0x02,         /*          Input (Variable),           */
 322        0x95, 0x05,         /*          Report Count (5),           */
 323        0x81, 0x01,         /*          Input (Constant),           */
 324        0x05, 0x01,         /*          Usage Page (Desktop),       */
 325        0x75, 0x08,         /*          Report Size (8),            */
 326        0x09, 0x30,         /*          Usage (X),                  */
 327        0x09, 0x31,         /*          Usage (Y),                  */
 328        0x15, 0x81,         /*          Logical Minimum (-127),     */
 329        0x25, 0x7F,         /*          Logical Maximum (127),      */
 330        0x95, 0x02,         /*          Report Count (2),           */
 331        0x81, 0x06,         /*          Input (Variable, Relative), */
 332        0x09, 0x38,         /*          Usage (Wheel),              */
 333        0x15, 0xFF,         /*          Logical Minimum (-1),       */
 334        0x25, 0x01,         /*          Logical Maximum (1),        */
 335        0x95, 0x01,         /*          Report Count (1),           */
 336        0x81, 0x06,         /*          Input (Variable, Relative), */
 337        0x81, 0x01,         /*          Input (Constant),           */
 338        0xC0,               /*      End Collection,                 */
 339        0xC0                /*  End Collection                      */
 340};
 341
 342/* Size of the original descriptors of TWHL850 tablet */
 343#define TWHL850_RDESC_ORIG_SIZE0        182
 344#define TWHL850_RDESC_ORIG_SIZE1        161
 345#define TWHL850_RDESC_ORIG_SIZE2        92
 346
 347/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */
 348static __u8 twhl850_rdesc_fixed0[] = {
 349        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
 350        0x09, 0x02,         /*  Usage (Pen),                        */
 351        0xA1, 0x01,         /*  Collection (Application),           */
 352        0x85, 0x09,         /*      Report ID (9),                  */
 353        0x09, 0x20,         /*      Usage (Stylus),                 */
 354        0xA0,               /*      Collection (Physical),          */
 355        0x14,               /*          Logical Minimum (0),        */
 356        0x25, 0x01,         /*          Logical Maximum (1),        */
 357        0x75, 0x01,         /*          Report Size (1),            */
 358        0x95, 0x03,         /*          Report Count (3),           */
 359        0x09, 0x42,         /*          Usage (Tip Switch),         */
 360        0x09, 0x44,         /*          Usage (Barrel Switch),      */
 361        0x09, 0x46,         /*          Usage (Tablet Pick),        */
 362        0x81, 0x02,         /*          Input (Variable),           */
 363        0x81, 0x03,         /*          Input (Constant, Variable), */
 364        0x95, 0x01,         /*          Report Count (1),           */
 365        0x09, 0x32,         /*          Usage (In Range),           */
 366        0x81, 0x02,         /*          Input (Variable),           */
 367        0x81, 0x03,         /*          Input (Constant, Variable), */
 368        0x75, 0x10,         /*          Report Size (16),           */
 369        0xA4,               /*          Push,                       */
 370        0x05, 0x01,         /*          Usage Page (Desktop),       */
 371        0x65, 0x13,         /*          Unit (Inch),                */
 372        0x55, 0xFD,         /*          Unit Exponent (-3),         */
 373        0x34,               /*          Physical Minimum (0),       */
 374        0x09, 0x30,         /*          Usage (X),                  */
 375        0x46, 0x40, 0x1F,   /*          Physical Maximum (8000),    */
 376        0x26, 0x00, 0x7D,   /*          Logical Maximum (32000),    */
 377        0x81, 0x02,         /*          Input (Variable),           */
 378        0x09, 0x31,         /*          Usage (Y),                  */
 379        0x46, 0x88, 0x13,   /*          Physical Maximum (5000),    */
 380        0x26, 0x20, 0x4E,   /*          Logical Maximum (20000),    */
 381        0x81, 0x02,         /*          Input (Variable),           */
 382        0xB4,               /*          Pop,                        */
 383        0x09, 0x30,         /*          Usage (Tip Pressure),       */
 384        0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
 385        0x81, 0x02,         /*          Input (Variable),           */
 386        0xC0,               /*      End Collection,                 */
 387        0xC0                /*  End Collection                      */
 388};
 389
 390/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */
 391static __u8 twhl850_rdesc_fixed1[] = {
 392        0x05, 0x01,         /*  Usage Page (Desktop),               */
 393        0x09, 0x02,         /*  Usage (Mouse),                      */
 394        0xA1, 0x01,         /*  Collection (Application),           */
 395        0x85, 0x01,         /*      Report ID (1),                  */
 396        0x09, 0x01,         /*      Usage (Pointer),                */
 397        0xA0,               /*      Collection (Physical),          */
 398        0x05, 0x09,         /*          Usage Page (Button),        */
 399        0x75, 0x01,         /*          Report Size (1),            */
 400        0x95, 0x03,         /*          Report Count (3),           */
 401        0x19, 0x01,         /*          Usage Minimum (01h),        */
 402        0x29, 0x03,         /*          Usage Maximum (03h),        */
 403        0x14,               /*          Logical Minimum (0),        */
 404        0x25, 0x01,         /*          Logical Maximum (1),        */
 405        0x81, 0x02,         /*          Input (Variable),           */
 406        0x95, 0x05,         /*          Report Count (5),           */
 407        0x81, 0x03,         /*          Input (Constant, Variable), */
 408        0x05, 0x01,         /*          Usage Page (Desktop),       */
 409        0x09, 0x30,         /*          Usage (X),                  */
 410        0x09, 0x31,         /*          Usage (Y),                  */
 411        0x16, 0x00, 0x80,   /*          Logical Minimum (-32768),   */
 412        0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
 413        0x75, 0x10,         /*          Report Size (16),           */
 414        0x95, 0x02,         /*          Report Count (2),           */
 415        0x81, 0x06,         /*          Input (Variable, Relative), */
 416        0x09, 0x38,         /*          Usage (Wheel),              */
 417        0x15, 0xFF,         /*          Logical Minimum (-1),       */
 418        0x25, 0x01,         /*          Logical Maximum (1),        */
 419        0x95, 0x01,         /*          Report Count (1),           */
 420        0x75, 0x08,         /*          Report Size (8),            */
 421        0x81, 0x06,         /*          Input (Variable, Relative), */
 422        0x81, 0x03,         /*          Input (Constant, Variable), */
 423        0xC0,               /*      End Collection,                 */
 424        0xC0                /*  End Collection                      */
 425};
 426
 427/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */
 428static __u8 twhl850_rdesc_fixed2[] = {
 429        0x05, 0x01,         /*  Usage Page (Desktop),               */
 430        0x09, 0x06,         /*  Usage (Keyboard),                   */
 431        0xA1, 0x01,         /*  Collection (Application),           */
 432        0x85, 0x03,         /*      Report ID (3),                  */
 433        0x05, 0x07,         /*      Usage Page (Keyboard),          */
 434        0x14,               /*      Logical Minimum (0),            */
 435        0x19, 0xE0,         /*      Usage Minimum (KB Leftcontrol), */
 436        0x29, 0xE7,         /*      Usage Maximum (KB Right GUI),   */
 437        0x25, 0x01,         /*      Logical Maximum (1),            */
 438        0x75, 0x01,         /*      Report Size (1),                */
 439        0x95, 0x08,         /*      Report Count (8),               */
 440        0x81, 0x02,         /*      Input (Variable),               */
 441        0x18,               /*      Usage Minimum (None),           */
 442        0x29, 0xFF,         /*      Usage Maximum (FFh),            */
 443        0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
 444        0x75, 0x08,         /*      Report Size (8),                */
 445        0x95, 0x06,         /*      Report Count (6),               */
 446        0x80,               /*      Input,                          */
 447        0xC0                /*  End Collection                      */
 448};
 449
 450/* Size of the original descriptors of TWHA60 tablet */
 451#define TWHA60_RDESC_ORIG_SIZE0 254
 452#define TWHA60_RDESC_ORIG_SIZE1 139
 453
 454/* Fixed TWHA60 report descriptor, interface 0 (stylus) */
 455static __u8 twha60_rdesc_fixed0[] = {
 456        0x05, 0x0D,         /*  Usage Page (Digitizer),             */
 457        0x09, 0x02,         /*  Usage (Pen),                        */
 458        0xA1, 0x01,         /*  Collection (Application),           */
 459        0x85, 0x09,         /*      Report ID (9),                  */
 460        0x09, 0x20,         /*      Usage (Stylus),                 */
 461        0xA0,               /*      Collection (Physical),          */
 462        0x75, 0x01,         /*          Report Size (1),            */
 463        0x09, 0x42,         /*          Usage (Tip Switch),         */
 464        0x09, 0x44,         /*          Usage (Barrel Switch),      */
 465        0x09, 0x46,         /*          Usage (Tablet Pick),        */
 466        0x14,               /*          Logical Minimum (0),        */
 467        0x25, 0x01,         /*          Logical Maximum (1),        */
 468        0x95, 0x03,         /*          Report Count (3),           */
 469        0x81, 0x02,         /*          Input (Variable),           */
 470        0x95, 0x04,         /*          Report Count (4),           */
 471        0x81, 0x01,         /*          Input (Constant),           */
 472        0x09, 0x32,         /*          Usage (In Range),           */
 473        0x95, 0x01,         /*          Report Count (1),           */
 474        0x81, 0x02,         /*          Input (Variable),           */
 475        0x75, 0x10,         /*          Report Size (16),           */
 476        0x95, 0x01,         /*          Report Count (1),           */
 477        0x14,               /*          Logical Minimum (0),        */
 478        0xA4,               /*          Push,                       */
 479        0x05, 0x01,         /*          Usage Page (Desktop),       */
 480        0x55, 0xFD,         /*          Unit Exponent (-3),         */
 481        0x65, 0x13,         /*          Unit (Inch),                */
 482        0x34,               /*          Physical Minimum (0),       */
 483        0x09, 0x30,         /*          Usage (X),                  */
 484        0x46, 0x10, 0x27,   /*          Physical Maximum (10000),   */
 485        0x27, 0x3F, 0x9C,
 486                0x00, 0x00, /*          Logical Maximum (39999),    */
 487        0x81, 0x02,         /*          Input (Variable),           */
 488        0x09, 0x31,         /*          Usage (Y),                  */
 489        0x46, 0x6A, 0x18,   /*          Physical Maximum (6250),    */
 490        0x26, 0xA7, 0x61,   /*          Logical Maximum (24999),    */
 491        0x81, 0x02,         /*          Input (Variable),           */
 492        0xB4,               /*          Pop,                        */
 493        0x09, 0x30,         /*          Usage (Tip Pressure),       */
 494        0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
 495        0x81, 0x02,         /*          Input (Variable),           */
 496        0xC0,               /*      End Collection,                 */
 497        0xC0                /*  End Collection                      */
 498};
 499
 500/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */
 501static __u8 twha60_rdesc_fixed1[] = {
 502        0x05, 0x01, /*  Usage Page (Desktop),       */
 503        0x09, 0x06, /*  Usage (Keyboard),           */
 504        0xA1, 0x01, /*  Collection (Application),   */
 505        0x85, 0x05, /*      Report ID (5),          */
 506        0x05, 0x07, /*      Usage Page (Keyboard),  */
 507        0x14,       /*      Logical Minimum (0),    */
 508        0x25, 0x01, /*      Logical Maximum (1),    */
 509        0x75, 0x01, /*      Report Size (1),        */
 510        0x95, 0x08, /*      Report Count (8),       */
 511        0x81, 0x01, /*      Input (Constant),       */
 512        0x95, 0x0C, /*      Report Count (12),      */
 513        0x19, 0x3A, /*      Usage Minimum (KB F1),  */
 514        0x29, 0x45, /*      Usage Maximum (KB F12), */
 515        0x81, 0x02, /*      Input (Variable),       */
 516        0x95, 0x0C, /*      Report Count (12),      */
 517        0x19, 0x68, /*      Usage Minimum (KB F13), */
 518        0x29, 0x73, /*      Usage Maximum (KB F24), */
 519        0x81, 0x02, /*      Input (Variable),       */
 520        0x95, 0x08, /*      Report Count (8),       */
 521        0x81, 0x01, /*      Input (Constant),       */
 522        0xC0        /*  End Collection              */
 523};
 524
 525/* Report descriptor template placeholder head */
 526#define UCLOGIC_PH_HEAD 0xFE, 0xED, 0x1D
 527
 528/* Report descriptor template placeholder IDs */
 529enum uclogic_ph_id {
 530        UCLOGIC_PH_ID_X_LM,
 531        UCLOGIC_PH_ID_X_PM,
 532        UCLOGIC_PH_ID_Y_LM,
 533        UCLOGIC_PH_ID_Y_PM,
 534        UCLOGIC_PH_ID_PRESSURE_LM,
 535        UCLOGIC_PH_ID_NUM
 536};
 537
 538/* Report descriptor template placeholder */
 539#define UCLOGIC_PH(_ID) UCLOGIC_PH_HEAD, UCLOGIC_PH_ID_##_ID
 540#define UCLOGIC_PEN_REPORT_ID   0x07
 541
 542/* Fixed report descriptor template */
 543static const __u8 uclogic_tablet_rdesc_template[] = {
 544        0x05, 0x0D,             /*  Usage Page (Digitizer),                 */
 545        0x09, 0x02,             /*  Usage (Pen),                            */
 546        0xA1, 0x01,             /*  Collection (Application),               */
 547        0x85, 0x07,             /*      Report ID (7),                      */
 548        0x09, 0x20,             /*      Usage (Stylus),                     */
 549        0xA0,                   /*      Collection (Physical),              */
 550        0x14,                   /*          Logical Minimum (0),            */
 551        0x25, 0x01,             /*          Logical Maximum (1),            */
 552        0x75, 0x01,             /*          Report Size (1),                */
 553        0x09, 0x42,             /*          Usage (Tip Switch),             */
 554        0x09, 0x44,             /*          Usage (Barrel Switch),          */
 555        0x09, 0x46,             /*          Usage (Tablet Pick),            */
 556        0x95, 0x03,             /*          Report Count (3),               */
 557        0x81, 0x02,             /*          Input (Variable),               */
 558        0x95, 0x03,             /*          Report Count (3),               */
 559        0x81, 0x03,             /*          Input (Constant, Variable),     */
 560        0x09, 0x32,             /*          Usage (In Range),               */
 561        0x95, 0x01,             /*          Report Count (1),               */
 562        0x81, 0x02,             /*          Input (Variable),               */
 563        0x95, 0x01,             /*          Report Count (1),               */
 564        0x81, 0x03,             /*          Input (Constant, Variable),     */
 565        0x75, 0x10,             /*          Report Size (16),               */
 566        0x95, 0x01,             /*          Report Count (1),               */
 567        0xA4,                   /*          Push,                           */
 568        0x05, 0x01,             /*          Usage Page (Desktop),           */
 569        0x65, 0x13,             /*          Unit (Inch),                    */
 570        0x55, 0xFD,             /*          Unit Exponent (-3),             */
 571        0x34,                   /*          Physical Minimum (0),           */
 572        0x09, 0x30,             /*          Usage (X),                      */
 573        0x27, UCLOGIC_PH(X_LM), /*          Logical Maximum (PLACEHOLDER),  */
 574        0x47, UCLOGIC_PH(X_PM), /*          Physical Maximum (PLACEHOLDER), */
 575        0x81, 0x02,             /*          Input (Variable),               */
 576        0x09, 0x31,             /*          Usage (Y),                      */
 577        0x27, UCLOGIC_PH(Y_LM), /*          Logical Maximum (PLACEHOLDER),  */
 578        0x47, UCLOGIC_PH(Y_PM), /*          Physical Maximum (PLACEHOLDER), */
 579        0x81, 0x02,             /*          Input (Variable),               */
 580        0xB4,                   /*          Pop,                            */
 581        0x09, 0x30,             /*          Usage (Tip Pressure),           */
 582        0x27,
 583        UCLOGIC_PH(PRESSURE_LM),/*          Logical Maximum (PLACEHOLDER),  */
 584        0x81, 0x02,             /*          Input (Variable),               */
 585        0xC0,                   /*      End Collection,                     */
 586        0xC0                    /*  End Collection                          */
 587};
 588
 589/* Fixed virtual pad report descriptor */
 590static const __u8 uclogic_buttonpad_rdesc[] = {
 591        0x05, 0x01,             /*  Usage Page (Desktop),                   */
 592        0x09, 0x07,             /*  Usage (Keypad),                         */
 593        0xA1, 0x01,             /*  Collection (Application),               */
 594        0x85, 0xF7,             /*      Report ID (247),                    */
 595        0x05, 0x0D,             /*      Usage Page (Digitizers),            */
 596        0x09, 0x39,             /*      Usage (Tablet Function Keys),       */
 597        0xA0,                   /*      Collection (Physical),              */
 598        0x05, 0x09,             /*          Usage Page (Button),            */
 599        0x75, 0x01,             /*          Report Size (1),                */
 600        0x95, 0x18,             /*          Report Count (24),              */
 601        0x81, 0x03,             /*          Input (Constant, Variable),     */
 602        0x19, 0x01,             /*          Usage Minimum (01h),            */
 603        0x29, 0x08,             /*          Usage Maximum (08h),            */
 604        0x95, 0x08,             /*          Report Count (8),               */
 605        0x81, 0x02,             /*          Input (Variable),               */
 606        0xC0,                   /*      End Collection                      */
 607        0xC0                    /*  End Collection                          */
 608};
 609
 610/* Parameter indices */
 611enum uclogic_prm {
 612        UCLOGIC_PRM_X_LM        = 1,
 613        UCLOGIC_PRM_Y_LM        = 2,
 614        UCLOGIC_PRM_PRESSURE_LM = 4,
 615        UCLOGIC_PRM_RESOLUTION  = 5,
 616        UCLOGIC_PRM_NUM
 617};
 618
 619/* Driver data */
 620struct uclogic_drvdata {
 621        __u8 *rdesc;
 622        unsigned int rsize;
 623        bool invert_pen_inrange;
 624        bool ignore_pen_usage;
 625        bool has_virtual_pad_interface;
 626};
 627
 628static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 629                                        unsigned int *rsize)
 630{
 631        struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
 632        __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
 633        struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
 634
 635        if (drvdata->rdesc != NULL) {
 636                rdesc = drvdata->rdesc;
 637                *rsize = drvdata->rsize;
 638                return rdesc;
 639        }
 640
 641        switch (hdev->product) {
 642        case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209:
 643                if (*rsize == PF1209_RDESC_ORIG_SIZE) {
 644                        rdesc = pf1209_rdesc_fixed;
 645                        *rsize = sizeof(pf1209_rdesc_fixed);
 646                }
 647                break;
 648        case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U:
 649                if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
 650                        rdesc = wp4030u_rdesc_fixed;
 651                        *rsize = sizeof(wp4030u_rdesc_fixed);
 652                }
 653                break;
 654        case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U:
 655                if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
 656                        rdesc = wp5540u_rdesc_fixed;
 657                        *rsize = sizeof(wp5540u_rdesc_fixed);
 658                }
 659                break;
 660        case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U:
 661                if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
 662                        rdesc = wp8060u_rdesc_fixed;
 663                        *rsize = sizeof(wp8060u_rdesc_fixed);
 664                }
 665                break;
 666        case USB_DEVICE_ID_UCLOGIC_TABLET_WP1062:
 667                if (*rsize == WP1062_RDESC_ORIG_SIZE) {
 668                        rdesc = wp1062_rdesc_fixed;
 669                        *rsize = sizeof(wp1062_rdesc_fixed);
 670                }
 671                break;
 672        case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850:
 673                switch (iface_num) {
 674                case 0:
 675                        if (*rsize == TWHL850_RDESC_ORIG_SIZE0) {
 676                                rdesc = twhl850_rdesc_fixed0;
 677                                *rsize = sizeof(twhl850_rdesc_fixed0);
 678                        }
 679                        break;
 680                case 1:
 681                        if (*rsize == TWHL850_RDESC_ORIG_SIZE1) {
 682                                rdesc = twhl850_rdesc_fixed1;
 683                                *rsize = sizeof(twhl850_rdesc_fixed1);
 684                        }
 685                        break;
 686                case 2:
 687                        if (*rsize == TWHL850_RDESC_ORIG_SIZE2) {
 688                                rdesc = twhl850_rdesc_fixed2;
 689                                *rsize = sizeof(twhl850_rdesc_fixed2);
 690                        }
 691                        break;
 692                }
 693                break;
 694        case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60:
 695                switch (iface_num) {
 696                case 0:
 697                        if (*rsize == TWHA60_RDESC_ORIG_SIZE0) {
 698                                rdesc = twha60_rdesc_fixed0;
 699                                *rsize = sizeof(twha60_rdesc_fixed0);
 700                        }
 701                        break;
 702                case 1:
 703                        if (*rsize == TWHA60_RDESC_ORIG_SIZE1) {
 704                                rdesc = twha60_rdesc_fixed1;
 705                                *rsize = sizeof(twha60_rdesc_fixed1);
 706                        }
 707                        break;
 708                }
 709                break;
 710        }
 711
 712        return rdesc;
 713}
 714
 715static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 716                struct hid_field *field, struct hid_usage *usage,
 717                unsigned long **bit, int *max)
 718{
 719        struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
 720
 721        /* discard the unused pen interface */
 722        if ((drvdata->ignore_pen_usage) &&
 723            (field->application == HID_DG_PEN))
 724                return -1;
 725
 726        /* let hid-core decide what to do */
 727        return 0;
 728}
 729
 730static int uclogic_input_configured(struct hid_device *hdev,
 731                struct hid_input *hi)
 732{
 733        char *name;
 734        const char *suffix = NULL;
 735        struct hid_field *field;
 736        size_t len;
 737
 738        /* no report associated (HID_QUIRK_MULTI_INPUT not set) */
 739        if (!hi->report)
 740                return 0;
 741
 742        field = hi->report->field[0];
 743
 744        switch (field->application) {
 745        case HID_GD_KEYBOARD:
 746                suffix = "Keyboard";
 747                break;
 748        case HID_GD_MOUSE:
 749                suffix = "Mouse";
 750                break;
 751        case HID_GD_KEYPAD:
 752                suffix = "Pad";
 753                break;
 754        case HID_DG_PEN:
 755                suffix = "Pen";
 756                break;
 757        case HID_CP_CONSUMER_CONTROL:
 758                suffix = "Consumer Control";
 759                break;
 760        case HID_GD_SYSTEM_CONTROL:
 761                suffix = "System Control";
 762                break;
 763        }
 764
 765        if (suffix) {
 766                len = strlen(hdev->name) + 2 + strlen(suffix);
 767                name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL);
 768                if (name) {
 769                        snprintf(name, len, "%s %s", hdev->name, suffix);
 770                        hi->input->name = name;
 771                }
 772        }
 773
 774        return 0;
 775}
 776
 777/**
 778 * Enable fully-functional tablet mode and determine device parameters.
 779 *
 780 * @hdev:       HID device
 781 */
 782static int uclogic_tablet_enable(struct hid_device *hdev)
 783{
 784        int rc;
 785        struct usb_device *usb_dev = hid_to_usb_dev(hdev);
 786        struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
 787        __le16 *buf = NULL;
 788        size_t len;
 789        s32 params[UCLOGIC_PH_ID_NUM];
 790        s32 resolution;
 791        __u8 *p;
 792        s32 v;
 793
 794        /*
 795         * Read string descriptor containing tablet parameters. The specific
 796         * string descriptor and data were discovered by sniffing the Windows
 797         * driver traffic.
 798         * NOTE: This enables fully-functional tablet mode.
 799         */
 800        len = UCLOGIC_PRM_NUM * sizeof(*buf);
 801        buf = kmalloc(len, GFP_KERNEL);
 802        if (buf == NULL) {
 803                rc = -ENOMEM;
 804                goto cleanup;
 805        }
 806        rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
 807                                USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
 808                                (USB_DT_STRING << 8) + 0x64,
 809                                0x0409, buf, len,
 810                                USB_CTRL_GET_TIMEOUT);
 811        if (rc == -EPIPE) {
 812                hid_err(hdev, "device parameters not found\n");
 813                rc = -ENODEV;
 814                goto cleanup;
 815        } else if (rc < 0) {
 816                hid_err(hdev, "failed to get device parameters: %d\n", rc);
 817                rc = -ENODEV;
 818                goto cleanup;
 819        } else if (rc != len) {
 820                hid_err(hdev, "invalid device parameters\n");
 821                rc = -ENODEV;
 822                goto cleanup;
 823        }
 824
 825        /* Extract device parameters */
 826        params[UCLOGIC_PH_ID_X_LM] = le16_to_cpu(buf[UCLOGIC_PRM_X_LM]);
 827        params[UCLOGIC_PH_ID_Y_LM] = le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]);
 828        params[UCLOGIC_PH_ID_PRESSURE_LM] =
 829                le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]);
 830        resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]);
 831        if (resolution == 0) {
 832                params[UCLOGIC_PH_ID_X_PM] = 0;
 833                params[UCLOGIC_PH_ID_Y_PM] = 0;
 834        } else {
 835                params[UCLOGIC_PH_ID_X_PM] = params[UCLOGIC_PH_ID_X_LM] *
 836                                                1000 / resolution;
 837                params[UCLOGIC_PH_ID_Y_PM] = params[UCLOGIC_PH_ID_Y_LM] *
 838                                                1000 / resolution;
 839        }
 840
 841        /* Allocate fixed report descriptor */
 842        drvdata->rdesc = devm_kzalloc(&hdev->dev,
 843                                sizeof(uclogic_tablet_rdesc_template),
 844                                GFP_KERNEL);
 845        if (drvdata->rdesc == NULL) {
 846                rc = -ENOMEM;
 847                goto cleanup;
 848        }
 849        drvdata->rsize = sizeof(uclogic_tablet_rdesc_template);
 850
 851        /* Format fixed report descriptor */
 852        memcpy(drvdata->rdesc, uclogic_tablet_rdesc_template,
 853                drvdata->rsize);
 854        for (p = drvdata->rdesc;
 855             p <= drvdata->rdesc + drvdata->rsize - 4;) {
 856                if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D &&
 857                    p[3] < ARRAY_SIZE(params)) {
 858                        v = params[p[3]];
 859                        put_unaligned(cpu_to_le32(v), (s32 *)p);
 860                        p += 4;
 861                } else {
 862                        p++;
 863                }
 864        }
 865
 866        rc = 0;
 867
 868cleanup:
 869        kfree(buf);
 870        return rc;
 871}
 872
 873/**
 874 * Enable actual button mode.
 875 *
 876 * @hdev:       HID device
 877 */
 878static int uclogic_button_enable(struct hid_device *hdev)
 879{
 880        int rc;
 881        struct usb_device *usb_dev = hid_to_usb_dev(hdev);
 882        struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
 883        char *str_buf;
 884        size_t str_len = 16;
 885        unsigned char *rdesc;
 886        size_t rdesc_len;
 887
 888        str_buf = kzalloc(str_len, GFP_KERNEL);
 889        if (str_buf == NULL) {
 890                rc = -ENOMEM;
 891                goto cleanup;
 892        }
 893
 894        /* Enable abstract keyboard mode */
 895        rc = usb_string(usb_dev, 0x7b, str_buf, str_len);
 896        if (rc == -EPIPE) {
 897                hid_info(hdev, "button mode setting not found\n");
 898                rc = 0;
 899                goto cleanup;
 900        } else if (rc < 0) {
 901                hid_err(hdev, "failed to enable abstract keyboard\n");
 902                goto cleanup;
 903        } else if (strncmp(str_buf, "HK On", rc)) {
 904                hid_info(hdev, "invalid answer when requesting buttons: '%s'\n",
 905                        str_buf);
 906                rc = -EINVAL;
 907                goto cleanup;
 908        }
 909
 910        /* Re-allocate fixed report descriptor */
 911        rdesc_len = drvdata->rsize + sizeof(uclogic_buttonpad_rdesc);
 912        rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL);
 913        if (!rdesc) {
 914                rc = -ENOMEM;
 915                goto cleanup;
 916        }
 917
 918        memcpy(rdesc, drvdata->rdesc, drvdata->rsize);
 919
 920        /* Append the buttonpad descriptor */
 921        memcpy(rdesc + drvdata->rsize, uclogic_buttonpad_rdesc,
 922               sizeof(uclogic_buttonpad_rdesc));
 923
 924        /* clean up old rdesc and use the new one */
 925        drvdata->rsize = rdesc_len;
 926        devm_kfree(&hdev->dev, drvdata->rdesc);
 927        drvdata->rdesc = rdesc;
 928
 929        rc = 0;
 930
 931cleanup:
 932        kfree(str_buf);
 933        return rc;
 934}
 935
 936static int uclogic_probe(struct hid_device *hdev,
 937                const struct hid_device_id *id)
 938{
 939        int rc;
 940        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 941        struct usb_device *udev = hid_to_usb_dev(hdev);
 942        struct uclogic_drvdata *drvdata;
 943
 944        /*
 945         * libinput requires the pad interface to be on a different node
 946         * than the pen, so use QUIRK_MULTI_INPUT for all tablets.
 947         */
 948        hdev->quirks |= HID_QUIRK_MULTI_INPUT;
 949
 950        /* Allocate and assign driver data */
 951        drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
 952        if (drvdata == NULL)
 953                return -ENOMEM;
 954
 955        hid_set_drvdata(hdev, drvdata);
 956
 957        switch (id->product) {
 958        case USB_DEVICE_ID_HUION_TABLET:
 959        case USB_DEVICE_ID_YIYNOVA_TABLET:
 960        case USB_DEVICE_ID_UGEE_TABLET_81:
 961        case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3:
 962        case USB_DEVICE_ID_UGEE_TABLET_45:
 963                /* If this is the pen interface */
 964                if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
 965                        rc = uclogic_tablet_enable(hdev);
 966                        if (rc) {
 967                                hid_err(hdev, "tablet enabling failed\n");
 968                                return rc;
 969                        }
 970                        drvdata->invert_pen_inrange = true;
 971
 972                        rc = uclogic_button_enable(hdev);
 973                        drvdata->has_virtual_pad_interface = !rc;
 974                } else {
 975                        drvdata->ignore_pen_usage = true;
 976                }
 977                break;
 978        case USB_DEVICE_ID_UGTIZER_TABLET_GP0610:
 979        case USB_DEVICE_ID_UGEE_TABLET_EX07S:
 980                /* If this is the pen interface */
 981                if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
 982                        rc = uclogic_tablet_enable(hdev);
 983                        if (rc) {
 984                                hid_err(hdev, "tablet enabling failed\n");
 985                                return rc;
 986                        }
 987                        drvdata->invert_pen_inrange = true;
 988                } else {
 989                        drvdata->ignore_pen_usage = true;
 990                }
 991                break;
 992        case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60:
 993                /*
 994                 * If it is the three-interface version, which is known to
 995                 * respond to initialization.
 996                 */
 997                if (udev->config->desc.bNumInterfaces == 3) {
 998                        /* If it is the pen interface */
 999                        if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
1000                                rc = uclogic_tablet_enable(hdev);
1001                                if (rc) {
1002                                        hid_err(hdev, "tablet enabling failed\n");
1003                                        return rc;
1004                                }
1005                                drvdata->invert_pen_inrange = true;
1006
1007                                rc = uclogic_button_enable(hdev);
1008                                drvdata->has_virtual_pad_interface = !rc;
1009                        } else {
1010                                drvdata->ignore_pen_usage = true;
1011                        }
1012                }
1013                break;
1014        }
1015
1016        rc = hid_parse(hdev);
1017        if (rc) {
1018                hid_err(hdev, "parse failed\n");
1019                return rc;
1020        }
1021
1022        rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
1023        if (rc) {
1024                hid_err(hdev, "hw start failed\n");
1025                return rc;
1026        }
1027
1028        return 0;
1029}
1030
1031static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report,
1032                        u8 *data, int size)
1033{
1034        struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
1035
1036        if ((report->type == HID_INPUT_REPORT) &&
1037            (report->id == UCLOGIC_PEN_REPORT_ID) &&
1038            (size >= 2)) {
1039                if (drvdata->has_virtual_pad_interface && (data[1] & 0x20))
1040                        /* Change to virtual frame button report ID */
1041                        data[0] = 0xf7;
1042                else if (drvdata->invert_pen_inrange)
1043                        /* Invert the in-range bit */
1044                        data[1] ^= 0x40;
1045        }
1046
1047        return 0;
1048}
1049
1050static const struct hid_device_id uclogic_devices[] = {
1051        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
1052                                USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
1053        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
1054                                USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
1055        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
1056                                USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
1057        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
1058                                USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
1059        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
1060                                USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
1061        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
1062                                USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
1063        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
1064                                USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
1065        { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) },
1066        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) },
1067        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) },
1068        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) },
1069        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) },
1070        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },
1071        { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
1072        { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) },
1073        { }
1074};
1075MODULE_DEVICE_TABLE(hid, uclogic_devices);
1076
1077static struct hid_driver uclogic_driver = {
1078        .name = "uclogic",
1079        .id_table = uclogic_devices,
1080        .probe = uclogic_probe,
1081        .report_fixup = uclogic_report_fixup,
1082        .raw_event = uclogic_raw_event,
1083        .input_mapping = uclogic_input_mapping,
1084        .input_configured = uclogic_input_configured,
1085};
1086module_hid_driver(uclogic_driver);
1087
1088MODULE_AUTHOR("Martin Rusko");
1089MODULE_AUTHOR("Nikolai Kondrashov");
1090MODULE_LICENSE("GPL");
1091