root/drivers/hid/hid-uclogic-params.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. uclogic_params_pen_inrange_to_str
  2. uclogic_params_get_str_desc
  3. uclogic_params_pen_cleanup
  4. uclogic_params_pen_init_v1
  5. uclogic_params_get_le24
  6. uclogic_params_pen_init_v2
  7. uclogic_params_frame_cleanup
  8. uclogic_params_frame_init_with_desc
  9. uclogic_params_frame_init_v1_buttonpad
  10. uclogic_params_cleanup
  11. uclogic_params_get_desc
  12. uclogic_params_init_invalid
  13. uclogic_params_init_with_opt_desc
  14. uclogic_params_init_with_pen_unused
  15. uclogic_params_huion_init
  16. uclogic_params_init

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  *  HID driver for UC-Logic devices not fully compliant with HID standard
   4  *  - tablet initialization and parameter retrieval
   5  *
   6  *  Copyright (c) 2018 Nikolai Kondrashov
   7  */
   8 
   9 /*
  10  * This program is free software; you can redistribute it and/or modify it
  11  * under the terms of the GNU General Public License as published by the Free
  12  * Software Foundation; either version 2 of the License, or (at your option)
  13  * any later version.
  14  */
  15 
  16 #include "hid-uclogic-params.h"
  17 #include "hid-uclogic-rdesc.h"
  18 #include "usbhid/usbhid.h"
  19 #include "hid-ids.h"
  20 #include <linux/ctype.h>
  21 #include <asm/unaligned.h>
  22 
  23 /**
  24  * Convert a pen in-range reporting type to a string.
  25  *
  26  * @inrange:    The in-range reporting type to convert.
  27  *
  28  * Returns:
  29  *      The string representing the type, or NULL if the type is unknown.
  30  */
  31 const char *uclogic_params_pen_inrange_to_str(
  32                         enum uclogic_params_pen_inrange inrange)
  33 {
  34         switch (inrange) {
  35         case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL:
  36                 return "normal";
  37         case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED:
  38                 return "inverted";
  39         case UCLOGIC_PARAMS_PEN_INRANGE_NONE:
  40                 return "none";
  41         default:
  42                 return NULL;
  43         }
  44 }
  45 
  46 /**
  47  * uclogic_params_get_str_desc - retrieve a string descriptor from a HID
  48  * device interface, putting it into a kmalloc-allocated buffer as is, without
  49  * character encoding conversion.
  50  *
  51  * @pbuf:       Location for the kmalloc-allocated buffer pointer containing
  52  *              the retrieved descriptor. Not modified in case of error.
  53  *              Can be NULL to have retrieved descriptor discarded.
  54  * @hdev:       The HID device of the tablet interface to retrieve the string
  55  *              descriptor from. Cannot be NULL.
  56  * @idx:        Index of the string descriptor to request from the device.
  57  * @len:        Length of the buffer to allocate and the data to retrieve.
  58  *
  59  * Returns:
  60  *      number of bytes retrieved (<= len),
  61  *      -EPIPE, if the descriptor was not found, or
  62  *      another negative errno code in case of other error.
  63  */
  64 static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,
  65                                         __u8 idx, size_t len)
  66 {
  67         int rc;
  68         struct usb_device *udev = hid_to_usb_dev(hdev);
  69         __u8 *buf = NULL;
  70 
  71         /* Check arguments */
  72         if (hdev == NULL) {
  73                 rc = -EINVAL;
  74                 goto cleanup;
  75         }
  76 
  77         buf = kmalloc(len, GFP_KERNEL);
  78         if (buf == NULL) {
  79                 rc = -ENOMEM;
  80                 goto cleanup;
  81         }
  82 
  83         rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
  84                                 USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
  85                                 (USB_DT_STRING << 8) + idx,
  86                                 0x0409, buf, len,
  87                                 USB_CTRL_GET_TIMEOUT);
  88         if (rc == -EPIPE) {
  89                 hid_dbg(hdev, "string descriptor #%hhu not found\n", idx);
  90                 goto cleanup;
  91         } else if (rc < 0) {
  92                 hid_err(hdev,
  93                         "failed retrieving string descriptor #%hhu: %d\n",
  94                         idx, rc);
  95                 goto cleanup;
  96         }
  97 
  98         if (pbuf != NULL) {
  99                 *pbuf = buf;
 100                 buf = NULL;
 101         }
 102 
 103 cleanup:
 104         kfree(buf);
 105         return rc;
 106 }
 107 
 108 /**
 109  * uclogic_params_pen_cleanup - free resources used by struct
 110  * uclogic_params_pen (tablet interface's pen input parameters).
 111  * Can be called repeatedly.
 112  *
 113  * @pen:        Pen input parameters to cleanup. Cannot be NULL.
 114  */
 115 static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen)
 116 {
 117         kfree(pen->desc_ptr);
 118         memset(pen, 0, sizeof(*pen));
 119 }
 120 
 121 /**
 122  * uclogic_params_pen_init_v1() - initialize tablet interface pen
 123  * input and retrieve its parameters from the device, using v1 protocol.
 124  *
 125  * @pen:        Pointer to the pen parameters to initialize (to be
 126  *              cleaned up with uclogic_params_pen_cleanup()). Not modified in
 127  *              case of error, or if parameters are not found. Cannot be NULL.
 128  * @pfound:     Location for a flag which is set to true if the parameters
 129  *              were found, and to false if not (e.g. device was
 130  *              incompatible). Not modified in case of error. Cannot be NULL.
 131  * @hdev:       The HID device of the tablet interface to initialize and get
 132  *              parameters from. Cannot be NULL.
 133  *
 134  * Returns:
 135  *      Zero, if successful. A negative errno code on error.
 136  */
 137 static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,
 138                                       bool *pfound,
 139                                       struct hid_device *hdev)
 140 {
 141         int rc;
 142         bool found = false;
 143         /* Buffer for (part of) the string descriptor */
 144         __u8 *buf = NULL;
 145         /* Minimum descriptor length required, maximum seen so far is 18 */
 146         const int len = 12;
 147         s32 resolution;
 148         /* Pen report descriptor template parameters */
 149         s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
 150         __u8 *desc_ptr = NULL;
 151 
 152         /* Check arguments */
 153         if (pen == NULL || pfound == NULL || hdev == NULL) {
 154                 rc = -EINVAL;
 155                 goto cleanup;
 156         }
 157 
 158         /*
 159          * Read string descriptor containing pen input parameters.
 160          * The specific string descriptor and data were discovered by sniffing
 161          * the Windows driver traffic.
 162          * NOTE: This enables fully-functional tablet mode.
 163          */
 164         rc = uclogic_params_get_str_desc(&buf, hdev, 100, len);
 165         if (rc == -EPIPE) {
 166                 hid_dbg(hdev,
 167                         "string descriptor with pen parameters not found, assuming not compatible\n");
 168                 goto finish;
 169         } else if (rc < 0) {
 170                 hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
 171                 goto cleanup;
 172         } else if (rc != len) {
 173                 hid_dbg(hdev,
 174                         "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
 175                         rc, len);
 176                 goto finish;
 177         }
 178 
 179         /*
 180          * Fill report descriptor parameters from the string descriptor
 181          */
 182         desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
 183                 get_unaligned_le16(buf + 2);
 184         desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
 185                 get_unaligned_le16(buf + 4);
 186         desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
 187                 get_unaligned_le16(buf + 8);
 188         resolution = get_unaligned_le16(buf + 10);
 189         if (resolution == 0) {
 190                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
 191                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
 192         } else {
 193                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
 194                         desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
 195                         resolution;
 196                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
 197                         desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
 198                         resolution;
 199         }
 200         kfree(buf);
 201         buf = NULL;
 202 
 203         /*
 204          * Generate pen report descriptor
 205          */
 206         desc_ptr = uclogic_rdesc_template_apply(
 207                                 uclogic_rdesc_pen_v1_template_arr,
 208                                 uclogic_rdesc_pen_v1_template_size,
 209                                 desc_params, ARRAY_SIZE(desc_params));
 210         if (desc_ptr == NULL) {
 211                 rc = -ENOMEM;
 212                 goto cleanup;
 213         }
 214 
 215         /*
 216          * Fill-in the parameters
 217          */
 218         memset(pen, 0, sizeof(*pen));
 219         pen->desc_ptr = desc_ptr;
 220         desc_ptr = NULL;
 221         pen->desc_size = uclogic_rdesc_pen_v1_template_size;
 222         pen->id = UCLOGIC_RDESC_PEN_V1_ID;
 223         pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;
 224         found = true;
 225 finish:
 226         *pfound = found;
 227         rc = 0;
 228 cleanup:
 229         kfree(desc_ptr);
 230         kfree(buf);
 231         return rc;
 232 }
 233 
 234 /**
 235  * uclogic_params_get_le24() - get a 24-bit little-endian number from a
 236  * buffer.
 237  *
 238  * @p:  The pointer to the number buffer.
 239  *
 240  * Returns:
 241  *      The retrieved number
 242  */
 243 static s32 uclogic_params_get_le24(const void *p)
 244 {
 245         const __u8 *b = p;
 246         return b[0] | (b[1] << 8UL) | (b[2] << 16UL);
 247 }
 248 
 249 /**
 250  * uclogic_params_pen_init_v2() - initialize tablet interface pen
 251  * input and retrieve its parameters from the device, using v2 protocol.
 252  *
 253  * @pen:        Pointer to the pen parameters to initialize (to be
 254  *              cleaned up with uclogic_params_pen_cleanup()). Not modified in
 255  *              case of error, or if parameters are not found. Cannot be NULL.
 256  * @pfound:     Location for a flag which is set to true if the parameters
 257  *              were found, and to false if not (e.g. device was
 258  *              incompatible). Not modified in case of error. Cannot be NULL.
 259  * @hdev:       The HID device of the tablet interface to initialize and get
 260  *              parameters from. Cannot be NULL.
 261  *
 262  * Returns:
 263  *      Zero, if successful. A negative errno code on error.
 264  */
 265 static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
 266                                         bool *pfound,
 267                                         struct hid_device *hdev)
 268 {
 269         int rc;
 270         bool found = false;
 271         /* Buffer for (part of) the string descriptor */
 272         __u8 *buf = NULL;
 273         /* Descriptor length required */
 274         const int len = 18;
 275         s32 resolution;
 276         /* Pen report descriptor template parameters */
 277         s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
 278         __u8 *desc_ptr = NULL;
 279 
 280         /* Check arguments */
 281         if (pen == NULL || pfound == NULL || hdev == NULL) {
 282                 rc = -EINVAL;
 283                 goto cleanup;
 284         }
 285 
 286         /*
 287          * Read string descriptor containing pen input parameters.
 288          * The specific string descriptor and data were discovered by sniffing
 289          * the Windows driver traffic.
 290          * NOTE: This enables fully-functional tablet mode.
 291          */
 292         rc = uclogic_params_get_str_desc(&buf, hdev, 200, len);
 293         if (rc == -EPIPE) {
 294                 hid_dbg(hdev,
 295                         "string descriptor with pen parameters not found, assuming not compatible\n");
 296                 goto finish;
 297         } else if (rc < 0) {
 298                 hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
 299                 goto cleanup;
 300         } else if (rc != len) {
 301                 hid_dbg(hdev,
 302                         "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
 303                         rc, len);
 304                 goto finish;
 305         } else {
 306                 size_t i;
 307                 /*
 308                  * Check it's not just a catch-all UTF-16LE-encoded ASCII
 309                  * string (such as the model name) some tablets put into all
 310                  * unknown string descriptors.
 311                  */
 312                 for (i = 2;
 313                      i < len &&
 314                         (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0);
 315                      i += 2);
 316                 if (i >= len) {
 317                         hid_dbg(hdev,
 318                                 "string descriptor with pen parameters seems to contain only text, assuming not compatible\n");
 319                         goto finish;
 320                 }
 321         }
 322 
 323         /*
 324          * Fill report descriptor parameters from the string descriptor
 325          */
 326         desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
 327                 uclogic_params_get_le24(buf + 2);
 328         desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
 329                 uclogic_params_get_le24(buf + 5);
 330         desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
 331                 get_unaligned_le16(buf + 8);
 332         resolution = get_unaligned_le16(buf + 10);
 333         if (resolution == 0) {
 334                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
 335                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
 336         } else {
 337                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
 338                         desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
 339                         resolution;
 340                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
 341                         desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
 342                         resolution;
 343         }
 344         kfree(buf);
 345         buf = NULL;
 346 
 347         /*
 348          * Generate pen report descriptor
 349          */
 350         desc_ptr = uclogic_rdesc_template_apply(
 351                                 uclogic_rdesc_pen_v2_template_arr,
 352                                 uclogic_rdesc_pen_v2_template_size,
 353                                 desc_params, ARRAY_SIZE(desc_params));
 354         if (desc_ptr == NULL) {
 355                 rc = -ENOMEM;
 356                 goto cleanup;
 357         }
 358 
 359         /*
 360          * Fill-in the parameters
 361          */
 362         memset(pen, 0, sizeof(*pen));
 363         pen->desc_ptr = desc_ptr;
 364         desc_ptr = NULL;
 365         pen->desc_size = uclogic_rdesc_pen_v2_template_size;
 366         pen->id = UCLOGIC_RDESC_PEN_V2_ID;
 367         pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE;
 368         pen->fragmented_hires = true;
 369         found = true;
 370 finish:
 371         *pfound = found;
 372         rc = 0;
 373 cleanup:
 374         kfree(desc_ptr);
 375         kfree(buf);
 376         return rc;
 377 }
 378 
 379 /**
 380  * uclogic_params_frame_cleanup - free resources used by struct
 381  * uclogic_params_frame (tablet interface's frame controls input parameters).
 382  * Can be called repeatedly.
 383  *
 384  * @frame:      Frame controls input parameters to cleanup. Cannot be NULL.
 385  */
 386 static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame)
 387 {
 388         kfree(frame->desc_ptr);
 389         memset(frame, 0, sizeof(*frame));
 390 }
 391 
 392 /**
 393  * uclogic_params_frame_init_with_desc() - initialize tablet's frame control
 394  * parameters with a static report descriptor.
 395  *
 396  * @frame:      Pointer to the frame parameters to initialize (to be cleaned
 397  *              up with uclogic_params_frame_cleanup()). Not modified in case
 398  *              of error. Cannot be NULL.
 399  * @desc_ptr:   Report descriptor pointer. Can be NULL, if desc_size is zero.
 400  * @desc_size:  Report descriptor size.
 401  * @id:         Report ID used for frame reports, if they should be tweaked,
 402  *              zero if not.
 403  *
 404  * Returns:
 405  *      Zero, if successful. A negative errno code on error.
 406  */
 407 static int uclogic_params_frame_init_with_desc(
 408                                         struct uclogic_params_frame *frame,
 409                                         const __u8 *desc_ptr,
 410                                         size_t desc_size,
 411                                         unsigned int id)
 412 {
 413         __u8 *copy_desc_ptr;
 414 
 415         if (frame == NULL || (desc_ptr == NULL && desc_size != 0))
 416                 return -EINVAL;
 417 
 418         copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
 419         if (copy_desc_ptr == NULL)
 420                 return -ENOMEM;
 421 
 422         memset(frame, 0, sizeof(*frame));
 423         frame->desc_ptr = copy_desc_ptr;
 424         frame->desc_size = desc_size;
 425         frame->id = id;
 426         return 0;
 427 }
 428 
 429 /**
 430  * uclogic_params_frame_init_v1_buttonpad() - initialize abstract buttonpad
 431  * on a v1 tablet interface.
 432  *
 433  * @frame:      Pointer to the frame parameters to initialize (to be cleaned
 434  *              up with uclogic_params_frame_cleanup()). Not modified in case
 435  *              of error, or if parameters are not found. Cannot be NULL.
 436  * @pfound:     Location for a flag which is set to true if the parameters
 437  *              were found, and to false if not (e.g. device was
 438  *              incompatible). Not modified in case of error. Cannot be NULL.
 439  * @hdev:       The HID device of the tablet interface to initialize and get
 440  *              parameters from. Cannot be NULL.
 441  *
 442  * Returns:
 443  *      Zero, if successful. A negative errno code on error.
 444  */
 445 static int uclogic_params_frame_init_v1_buttonpad(
 446                                         struct uclogic_params_frame *frame,
 447                                         bool *pfound,
 448                                         struct hid_device *hdev)
 449 {
 450         int rc;
 451         bool found = false;
 452         struct usb_device *usb_dev = hid_to_usb_dev(hdev);
 453         char *str_buf = NULL;
 454         const size_t str_len = 16;
 455 
 456         /* Check arguments */
 457         if (frame == NULL || pfound == NULL || hdev == NULL) {
 458                 rc = -EINVAL;
 459                 goto cleanup;
 460         }
 461 
 462         /*
 463          * Enable generic button mode
 464          */
 465         str_buf = kzalloc(str_len, GFP_KERNEL);
 466         if (str_buf == NULL) {
 467                 rc = -ENOMEM;
 468                 goto cleanup;
 469         }
 470 
 471         rc = usb_string(usb_dev, 123, str_buf, str_len);
 472         if (rc == -EPIPE) {
 473                 hid_dbg(hdev,
 474                         "generic button -enabling string descriptor not found\n");
 475         } else if (rc < 0) {
 476                 goto cleanup;
 477         } else if (strncmp(str_buf, "HK On", rc) != 0) {
 478                 hid_dbg(hdev,
 479                         "invalid response to enabling generic buttons: \"%s\"\n",
 480                         str_buf);
 481         } else {
 482                 hid_dbg(hdev, "generic buttons enabled\n");
 483                 rc = uclogic_params_frame_init_with_desc(
 484                                 frame,
 485                                 uclogic_rdesc_buttonpad_v1_arr,
 486                                 uclogic_rdesc_buttonpad_v1_size,
 487                                 UCLOGIC_RDESC_BUTTONPAD_V1_ID);
 488                 if (rc != 0)
 489                         goto cleanup;
 490                 found = true;
 491         }
 492 
 493         *pfound = found;
 494         rc = 0;
 495 cleanup:
 496         kfree(str_buf);
 497         return rc;
 498 }
 499 
 500 /**
 501  * uclogic_params_cleanup - free resources used by struct uclogic_params
 502  * (tablet interface's parameters).
 503  * Can be called repeatedly.
 504  *
 505  * @params:     Input parameters to cleanup. Cannot be NULL.
 506  */
 507 void uclogic_params_cleanup(struct uclogic_params *params)
 508 {
 509         if (!params->invalid) {
 510                 kfree(params->desc_ptr);
 511                 if (!params->pen_unused)
 512                         uclogic_params_pen_cleanup(&params->pen);
 513                 uclogic_params_frame_cleanup(&params->frame);
 514                 memset(params, 0, sizeof(*params));
 515         }
 516 }
 517 
 518 /**
 519  * Get a replacement report descriptor for a tablet's interface.
 520  *
 521  * @params:     The parameters of a tablet interface to get report
 522  *              descriptor for. Cannot be NULL.
 523  * @pdesc:      Location for the resulting, kmalloc-allocated report
 524  *              descriptor pointer, or for NULL, if there's no replacement
 525  *              report descriptor. Not modified in case of error. Cannot be
 526  *              NULL.
 527  * @psize:      Location for the resulting report descriptor size, not set if
 528  *              there's no replacement report descriptor. Not modified in case
 529  *              of error. Cannot be NULL.
 530  *
 531  * Returns:
 532  *      Zero, if successful.
 533  *      -EINVAL, if invalid arguments are supplied.
 534  *      -ENOMEM, if failed to allocate memory.
 535  */
 536 int uclogic_params_get_desc(const struct uclogic_params *params,
 537                                 __u8 **pdesc,
 538                                 unsigned int *psize)
 539 {
 540         bool common_present;
 541         bool pen_present;
 542         bool frame_present;
 543         unsigned int size;
 544         __u8 *desc = NULL;
 545 
 546         /* Check arguments */
 547         if (params == NULL || pdesc == NULL || psize == NULL)
 548                 return -EINVAL;
 549 
 550         size = 0;
 551 
 552         common_present = (params->desc_ptr != NULL);
 553         pen_present = (!params->pen_unused && params->pen.desc_ptr != NULL);
 554         frame_present = (params->frame.desc_ptr != NULL);
 555 
 556         if (common_present)
 557                 size += params->desc_size;
 558         if (pen_present)
 559                 size += params->pen.desc_size;
 560         if (frame_present)
 561                 size += params->frame.desc_size;
 562 
 563         if (common_present || pen_present || frame_present) {
 564                 __u8 *p;
 565 
 566                 desc = kmalloc(size, GFP_KERNEL);
 567                 if (desc == NULL)
 568                         return -ENOMEM;
 569                 p = desc;
 570 
 571                 if (common_present) {
 572                         memcpy(p, params->desc_ptr,
 573                                 params->desc_size);
 574                         p += params->desc_size;
 575                 }
 576                 if (pen_present) {
 577                         memcpy(p, params->pen.desc_ptr,
 578                                 params->pen.desc_size);
 579                         p += params->pen.desc_size;
 580                 }
 581                 if (frame_present) {
 582                         memcpy(p, params->frame.desc_ptr,
 583                                 params->frame.desc_size);
 584                         p += params->frame.desc_size;
 585                 }
 586 
 587                 WARN_ON(p != desc + size);
 588 
 589                 *psize = size;
 590         }
 591 
 592         *pdesc = desc;
 593         return 0;
 594 }
 595 
 596 /**
 597  * uclogic_params_init_invalid() - initialize tablet interface parameters,
 598  * specifying the interface is invalid.
 599  *
 600  * @params:             Parameters to initialize (to be cleaned with
 601  *                      uclogic_params_cleanup()). Cannot be NULL.
 602  */
 603 static void uclogic_params_init_invalid(struct uclogic_params *params)
 604 {
 605         params->invalid = true;
 606 }
 607 
 608 /**
 609  * uclogic_params_init_with_opt_desc() - initialize tablet interface
 610  * parameters with an optional replacement report descriptor. Only modify
 611  * report descriptor, if the original report descriptor matches the expected
 612  * size.
 613  *
 614  * @params:             Parameters to initialize (to be cleaned with
 615  *                      uclogic_params_cleanup()). Not modified in case of
 616  *                      error. Cannot be NULL.
 617  * @hdev:               The HID device of the tablet interface create the
 618  *                      parameters for. Cannot be NULL.
 619  * @orig_desc_size:     Expected size of the original report descriptor to
 620  *                      be replaced.
 621  * @desc_ptr:           Pointer to the replacement report descriptor.
 622  *                      Can be NULL, if desc_size is zero.
 623  * @desc_size:          Size of the replacement report descriptor.
 624  *
 625  * Returns:
 626  *      Zero, if successful. -EINVAL if an invalid argument was passed.
 627  *      -ENOMEM, if failed to allocate memory.
 628  */
 629 static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,
 630                                              struct hid_device *hdev,
 631                                              unsigned int orig_desc_size,
 632                                              __u8 *desc_ptr,
 633                                              unsigned int desc_size)
 634 {
 635         __u8 *desc_copy_ptr = NULL;
 636         unsigned int desc_copy_size;
 637         int rc;
 638 
 639         /* Check arguments */
 640         if (params == NULL || hdev == NULL ||
 641             (desc_ptr == NULL && desc_size != 0)) {
 642                 rc = -EINVAL;
 643                 goto cleanup;
 644         }
 645 
 646         /* Replace report descriptor, if it matches */
 647         if (hdev->dev_rsize == orig_desc_size) {
 648                 hid_dbg(hdev,
 649                         "device report descriptor matches the expected size, replacing\n");
 650                 desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
 651                 if (desc_copy_ptr == NULL) {
 652                         rc = -ENOMEM;
 653                         goto cleanup;
 654                 }
 655                 desc_copy_size = desc_size;
 656         } else {
 657                 hid_dbg(hdev,
 658                         "device report descriptor doesn't match the expected size (%u != %u), preserving\n",
 659                         hdev->dev_rsize, orig_desc_size);
 660                 desc_copy_ptr = NULL;
 661                 desc_copy_size = 0;
 662         }
 663 
 664         /* Output parameters */
 665         memset(params, 0, sizeof(*params));
 666         params->desc_ptr = desc_copy_ptr;
 667         desc_copy_ptr = NULL;
 668         params->desc_size = desc_copy_size;
 669 
 670         rc = 0;
 671 cleanup:
 672         kfree(desc_copy_ptr);
 673         return rc;
 674 }
 675 
 676 /**
 677  * uclogic_params_init_with_pen_unused() - initialize tablet interface
 678  * parameters preserving original reports and generic HID processing, but
 679  * disabling pen usage.
 680  *
 681  * @params:             Parameters to initialize (to be cleaned with
 682  *                      uclogic_params_cleanup()). Not modified in case of
 683  *                      error. Cannot be NULL.
 684  */
 685 static void uclogic_params_init_with_pen_unused(struct uclogic_params *params)
 686 {
 687         memset(params, 0, sizeof(*params));
 688         params->pen_unused = true;
 689 }
 690 
 691 /**
 692  * uclogic_params_init() - initialize a Huion tablet interface and discover
 693  * its parameters.
 694  *
 695  * @params:     Parameters to fill in (to be cleaned with
 696  *              uclogic_params_cleanup()). Not modified in case of error.
 697  *              Cannot be NULL.
 698  * @hdev:       The HID device of the tablet interface to initialize and get
 699  *              parameters from. Cannot be NULL.
 700  *
 701  * Returns:
 702  *      Zero, if successful. A negative errno code on error.
 703  */
 704 static int uclogic_params_huion_init(struct uclogic_params *params,
 705                                      struct hid_device *hdev)
 706 {
 707         int rc;
 708         struct usb_device *udev = hid_to_usb_dev(hdev);
 709         struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
 710         __u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
 711         bool found;
 712         /* The resulting parameters (noop) */
 713         struct uclogic_params p = {0, };
 714         static const char transition_ver[] = "HUION_T153_160607";
 715         char *ver_ptr = NULL;
 716         const size_t ver_len = sizeof(transition_ver) + 1;
 717 
 718         /* Check arguments */
 719         if (params == NULL || hdev == NULL) {
 720                 rc = -EINVAL;
 721                 goto cleanup;
 722         }
 723 
 724         /* If it's not a pen interface */
 725         if (bInterfaceNumber != 0) {
 726                 /* TODO: Consider marking the interface invalid */
 727                 uclogic_params_init_with_pen_unused(&p);
 728                 goto output;
 729         }
 730 
 731         /* Try to get firmware version */
 732         ver_ptr = kzalloc(ver_len, GFP_KERNEL);
 733         if (ver_ptr == NULL) {
 734                 rc = -ENOMEM;
 735                 goto cleanup;
 736         }
 737         rc = usb_string(udev, 201, ver_ptr, ver_len);
 738         if (rc == -EPIPE) {
 739                 *ver_ptr = '\0';
 740         } else if (rc < 0) {
 741                 hid_err(hdev,
 742                         "failed retrieving Huion firmware version: %d\n", rc);
 743                 goto cleanup;
 744         }
 745 
 746         /* If this is a transition firmware */
 747         if (strcmp(ver_ptr, transition_ver) == 0) {
 748                 hid_dbg(hdev,
 749                         "transition firmware detected, not probing pen v2 parameters\n");
 750         } else {
 751                 /* Try to probe v2 pen parameters */
 752                 rc = uclogic_params_pen_init_v2(&p.pen, &found, hdev);
 753                 if (rc != 0) {
 754                         hid_err(hdev,
 755                                 "failed probing pen v2 parameters: %d\n", rc);
 756                         goto cleanup;
 757                 } else if (found) {
 758                         hid_dbg(hdev, "pen v2 parameters found\n");
 759                         /* Create v2 buttonpad parameters */
 760                         rc = uclogic_params_frame_init_with_desc(
 761                                         &p.frame,
 762                                         uclogic_rdesc_buttonpad_v2_arr,
 763                                         uclogic_rdesc_buttonpad_v2_size,
 764                                         UCLOGIC_RDESC_BUTTONPAD_V2_ID);
 765                         if (rc != 0) {
 766                                 hid_err(hdev,
 767                                         "failed creating v2 buttonpad parameters: %d\n",
 768                                         rc);
 769                                 goto cleanup;
 770                         }
 771                         /* Set bitmask marking frame reports in pen reports */
 772                         p.pen_frame_flag = 0x20;
 773                         goto output;
 774                 }
 775                 hid_dbg(hdev, "pen v2 parameters not found\n");
 776         }
 777 
 778         /* Try to probe v1 pen parameters */
 779         rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
 780         if (rc != 0) {
 781                 hid_err(hdev,
 782                         "failed probing pen v1 parameters: %d\n", rc);
 783                 goto cleanup;
 784         } else if (found) {
 785                 hid_dbg(hdev, "pen v1 parameters found\n");
 786                 /* Try to probe v1 buttonpad */
 787                 rc = uclogic_params_frame_init_v1_buttonpad(
 788                                                 &p.frame,
 789                                                 &found, hdev);
 790                 if (rc != 0) {
 791                         hid_err(hdev, "v1 buttonpad probing failed: %d\n", rc);
 792                         goto cleanup;
 793                 }
 794                 hid_dbg(hdev, "buttonpad v1 parameters%s found\n",
 795                         (found ? "" : " not"));
 796                 if (found) {
 797                         /* Set bitmask marking frame reports */
 798                         p.pen_frame_flag = 0x20;
 799                 }
 800                 goto output;
 801         }
 802         hid_dbg(hdev, "pen v1 parameters not found\n");
 803 
 804         uclogic_params_init_invalid(&p);
 805 
 806 output:
 807         /* Output parameters */
 808         memcpy(params, &p, sizeof(*params));
 809         memset(&p, 0, sizeof(p));
 810         rc = 0;
 811 cleanup:
 812         kfree(ver_ptr);
 813         uclogic_params_cleanup(&p);
 814         return rc;
 815 }
 816 
 817 /**
 818  * uclogic_params_init() - initialize a tablet interface and discover its
 819  * parameters.
 820  *
 821  * @params:     Parameters to fill in (to be cleaned with
 822  *              uclogic_params_cleanup()). Not modified in case of error.
 823  *              Cannot be NULL.
 824  * @hdev:       The HID device of the tablet interface to initialize and get
 825  *              parameters from. Cannot be NULL. Must be using the USB low-level
 826  *              driver, i.e. be an actual USB tablet.
 827  *
 828  * Returns:
 829  *      Zero, if successful. A negative errno code on error.
 830  */
 831 int uclogic_params_init(struct uclogic_params *params,
 832                         struct hid_device *hdev)
 833 {
 834         int rc;
 835         struct usb_device *udev = hid_to_usb_dev(hdev);
 836         __u8  bNumInterfaces = udev->config->desc.bNumInterfaces;
 837         struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
 838         __u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
 839         bool found;
 840         /* The resulting parameters (noop) */
 841         struct uclogic_params p = {0, };
 842 
 843         /* Check arguments */
 844         if (params == NULL || hdev == NULL ||
 845             !hid_is_using_ll_driver(hdev, &usb_hid_driver)) {
 846                 rc = -EINVAL;
 847                 goto cleanup;
 848         }
 849 
 850         /*
 851          * Set replacement report descriptor if the original matches the
 852          * specified size. Otherwise keep interface unchanged.
 853          */
 854 #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \
 855         uclogic_params_init_with_opt_desc(                  \
 856                 &p, hdev,                                   \
 857                 UCLOGIC_RDESC_##_orig_desc_token##_SIZE,    \
 858                 uclogic_rdesc_##_new_desc_token##_arr,      \
 859                 uclogic_rdesc_##_new_desc_token##_size)
 860 
 861 #define VID_PID(_vid, _pid) \
 862         (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX))
 863 
 864         /*
 865          * Handle specific interfaces for specific tablets.
 866          *
 867          * Observe the following logic:
 868          *
 869          * If the interface is recognized as producing certain useful input:
 870          *      Mark interface as valid.
 871          *      Output interface parameters.
 872          * Else, if the interface is recognized as *not* producing any useful
 873          * input:
 874          *      Mark interface as invalid.
 875          * Else:
 876          *      Mark interface as valid.
 877          *      Output noop parameters.
 878          *
 879          * Rule of thumb: it is better to disable a broken interface than let
 880          *                it spew garbage input.
 881          */
 882 
 883         switch (VID_PID(hdev->vendor, hdev->product)) {
 884         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 885                      USB_DEVICE_ID_UCLOGIC_TABLET_PF1209):
 886                 rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed);
 887                 if (rc != 0)
 888                         goto cleanup;
 889                 break;
 890         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 891                      USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U):
 892                 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed);
 893                 if (rc != 0)
 894                         goto cleanup;
 895                 break;
 896         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 897                      USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U):
 898                 if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) {
 899                         if (bInterfaceNumber == 0) {
 900                                 /* Try to probe v1 pen parameters */
 901                                 rc = uclogic_params_pen_init_v1(&p.pen,
 902                                                                 &found, hdev);
 903                                 if (rc != 0) {
 904                                         hid_err(hdev,
 905                                                 "pen probing failed: %d\n",
 906                                                 rc);
 907                                         goto cleanup;
 908                                 }
 909                                 if (!found) {
 910                                         hid_warn(hdev,
 911                                                  "pen parameters not found");
 912                                 }
 913                         } else {
 914                                 uclogic_params_init_invalid(&p);
 915                         }
 916                 } else {
 917                         rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed);
 918                         if (rc != 0)
 919                                 goto cleanup;
 920                 }
 921                 break;
 922         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 923                      USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U):
 924                 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed);
 925                 if (rc != 0)
 926                         goto cleanup;
 927                 break;
 928         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 929                      USB_DEVICE_ID_UCLOGIC_TABLET_WP1062):
 930                 rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed);
 931                 if (rc != 0)
 932                         goto cleanup;
 933                 break;
 934         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 935                      USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850):
 936                 switch (bInterfaceNumber) {
 937                 case 0:
 938                         rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0);
 939                         if (rc != 0)
 940                                 goto cleanup;
 941                         break;
 942                 case 1:
 943                         rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1);
 944                         if (rc != 0)
 945                                 goto cleanup;
 946                         break;
 947                 case 2:
 948                         rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2);
 949                         if (rc != 0)
 950                                 goto cleanup;
 951                         break;
 952                 }
 953                 break;
 954         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 955                      USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60):
 956                 /*
 957                  * If it is not a three-interface version, which is known to
 958                  * respond to initialization.
 959                  */
 960                 if (bNumInterfaces != 3) {
 961                         switch (bInterfaceNumber) {
 962                         case 0:
 963                                 rc = WITH_OPT_DESC(TWHA60_ORIG0,
 964                                                         twha60_fixed0);
 965                                 if (rc != 0)
 966                                         goto cleanup;
 967                                 break;
 968                         case 1:
 969                                 rc = WITH_OPT_DESC(TWHA60_ORIG1,
 970                                                         twha60_fixed1);
 971                                 if (rc != 0)
 972                                         goto cleanup;
 973                                 break;
 974                         }
 975                         break;
 976                 }
 977                 /* FALL THROUGH */
 978         case VID_PID(USB_VENDOR_ID_HUION,
 979                      USB_DEVICE_ID_HUION_TABLET):
 980         case VID_PID(USB_VENDOR_ID_HUION,
 981                      USB_DEVICE_ID_HUION_HS64):
 982         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 983                      USB_DEVICE_ID_HUION_TABLET):
 984         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 985                      USB_DEVICE_ID_YIYNOVA_TABLET):
 986         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 987                      USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81):
 988         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 989                      USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3):
 990         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 991                      USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45):
 992         case VID_PID(USB_VENDOR_ID_UCLOGIC,
 993                      USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47):
 994                 rc = uclogic_params_huion_init(&p, hdev);
 995                 if (rc != 0)
 996                         goto cleanup;
 997                 break;
 998         case VID_PID(USB_VENDOR_ID_UGTIZER,
 999                      USB_DEVICE_ID_UGTIZER_TABLET_GP0610):
1000         case VID_PID(USB_VENDOR_ID_UGEE,
1001                      USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540):
1002         case VID_PID(USB_VENDOR_ID_UGEE,
1003                      USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640):
1004         case VID_PID(USB_VENDOR_ID_UGEE,
1005                      USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720):
1006                 /* If this is the pen interface */
1007                 if (bInterfaceNumber == 1) {
1008                         /* Probe v1 pen parameters */
1009                         rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1010                         if (rc != 0) {
1011                                 hid_err(hdev, "pen probing failed: %d\n", rc);
1012                                 goto cleanup;
1013                         }
1014                         if (!found) {
1015                                 hid_warn(hdev, "pen parameters not found");
1016                                 uclogic_params_init_invalid(&p);
1017                         }
1018                 } else {
1019                         /* TODO: Consider marking the interface invalid */
1020                         uclogic_params_init_with_pen_unused(&p);
1021                 }
1022                 break;
1023         case VID_PID(USB_VENDOR_ID_UGEE,
1024                      USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01):
1025                 /* If this is the pen and frame interface */
1026                 if (bInterfaceNumber == 1) {
1027                         /* Probe v1 pen parameters */
1028                         rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1029                         if (rc != 0) {
1030                                 hid_err(hdev, "pen probing failed: %d\n", rc);
1031                                 goto cleanup;
1032                         }
1033                         /* Initialize frame parameters */
1034                         rc = uclogic_params_frame_init_with_desc(
1035                                 &p.frame,
1036                                 uclogic_rdesc_xppen_deco01_frame_arr,
1037                                 uclogic_rdesc_xppen_deco01_frame_size,
1038                                 0);
1039                         if (rc != 0)
1040                                 goto cleanup;
1041                 } else {
1042                         /* TODO: Consider marking the interface invalid */
1043                         uclogic_params_init_with_pen_unused(&p);
1044                 }
1045                 break;
1046         case VID_PID(USB_VENDOR_ID_UGEE,
1047                      USB_DEVICE_ID_UGEE_TABLET_G5):
1048                 /* Ignore non-pen interfaces */
1049                 if (bInterfaceNumber != 1) {
1050                         uclogic_params_init_invalid(&p);
1051                         break;
1052                 }
1053 
1054                 rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1055                 if (rc != 0) {
1056                         hid_err(hdev, "pen probing failed: %d\n", rc);
1057                         goto cleanup;
1058                 } else if (found) {
1059                         rc = uclogic_params_frame_init_with_desc(
1060                                 &p.frame,
1061                                 uclogic_rdesc_ugee_g5_frame_arr,
1062                                 uclogic_rdesc_ugee_g5_frame_size,
1063                                 UCLOGIC_RDESC_UGEE_G5_FRAME_ID);
1064                         if (rc != 0) {
1065                                 hid_err(hdev,
1066                                         "failed creating buttonpad parameters: %d\n",
1067                                         rc);
1068                                 goto cleanup;
1069                         }
1070                         p.frame.re_lsb =
1071                                 UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB;
1072                         p.frame.dev_id_byte =
1073                                 UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE;
1074                 } else {
1075                         hid_warn(hdev, "pen parameters not found");
1076                         uclogic_params_init_invalid(&p);
1077                 }
1078 
1079                 break;
1080         case VID_PID(USB_VENDOR_ID_UGEE,
1081                      USB_DEVICE_ID_UGEE_TABLET_EX07S):
1082                 /* Ignore non-pen interfaces */
1083                 if (bInterfaceNumber != 1) {
1084                         uclogic_params_init_invalid(&p);
1085                         break;
1086                 }
1087 
1088                 rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1089                 if (rc != 0) {
1090                         hid_err(hdev, "pen probing failed: %d\n", rc);
1091                         goto cleanup;
1092                 } else if (found) {
1093                         rc = uclogic_params_frame_init_with_desc(
1094                                 &p.frame,
1095                                 uclogic_rdesc_ugee_ex07_buttonpad_arr,
1096                                 uclogic_rdesc_ugee_ex07_buttonpad_size,
1097                                 0);
1098                         if (rc != 0) {
1099                                 hid_err(hdev,
1100                                         "failed creating buttonpad parameters: %d\n",
1101                                         rc);
1102                                 goto cleanup;
1103                         }
1104                 } else {
1105                         hid_warn(hdev, "pen parameters not found");
1106                         uclogic_params_init_invalid(&p);
1107                 }
1108 
1109                 break;
1110         }
1111 
1112 #undef VID_PID
1113 #undef WITH_OPT_DESC
1114 
1115         /* Output parameters */
1116         memcpy(params, &p, sizeof(*params));
1117         memset(&p, 0, sizeof(p));
1118         rc = 0;
1119 cleanup:
1120         uclogic_params_cleanup(&p);
1121         return rc;
1122 }

/* [<][>][^][v][top][bottom][index][help] */