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