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 * 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 */
  31const 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 */
  64static 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 #%u: %d\n",
  94                        idx, rc);
  95                goto cleanup;
  96        }
  97
  98        if (pbuf != NULL) {
  99                *pbuf = buf;
 100                buf = NULL;
 101        }
 102
 103cleanup:
 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 */
 115static 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 */
 137static 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;
 225finish:
 226        *pfound = found;
 227        rc = 0;
 228cleanup:
 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 */
 243static 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 */
 265static 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;
 370finish:
 371        *pfound = found;
 372        rc = 0;
 373cleanup:
 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 */
 386static 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 */
 407static 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 */
 445static 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;
 495cleanup:
 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 */
 507void 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 */
 536int 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 */
 603static 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 */
 629static 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;
 671cleanup:
 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 */
 685static 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 */
 704static 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
 806output:
 807        /* Output parameters */
 808        memcpy(params, &p, sizeof(*params));
 809        memset(&p, 0, sizeof(p));
 810        rc = 0;
 811cleanup:
 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 */
 831int 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                fallthrough;
 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_UGTIZER,
1001                     USB_DEVICE_ID_UGTIZER_TABLET_GT5040):
1002        case VID_PID(USB_VENDOR_ID_UGEE,
1003                     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540):
1004        case VID_PID(USB_VENDOR_ID_UGEE,
1005                     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640):
1006        case VID_PID(USB_VENDOR_ID_UGEE,
1007                     USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720):
1008                /* If this is the pen interface */
1009                if (bInterfaceNumber == 1) {
1010                        /* Probe v1 pen parameters */
1011                        rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1012                        if (rc != 0) {
1013                                hid_err(hdev, "pen probing failed: %d\n", rc);
1014                                goto cleanup;
1015                        }
1016                        if (!found) {
1017                                hid_warn(hdev, "pen parameters not found");
1018                                uclogic_params_init_invalid(&p);
1019                        }
1020                } else {
1021                        /* TODO: Consider marking the interface invalid */
1022                        uclogic_params_init_with_pen_unused(&p);
1023                }
1024                break;
1025        case VID_PID(USB_VENDOR_ID_UGEE,
1026                     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01):
1027                /* If this is the pen and frame interface */
1028                if (bInterfaceNumber == 1) {
1029                        /* Probe v1 pen parameters */
1030                        rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1031                        if (rc != 0) {
1032                                hid_err(hdev, "pen probing failed: %d\n", rc);
1033                                goto cleanup;
1034                        }
1035                        /* Initialize frame parameters */
1036                        rc = uclogic_params_frame_init_with_desc(
1037                                &p.frame,
1038                                uclogic_rdesc_xppen_deco01_frame_arr,
1039                                uclogic_rdesc_xppen_deco01_frame_size,
1040                                0);
1041                        if (rc != 0)
1042                                goto cleanup;
1043                } else {
1044                        /* TODO: Consider marking the interface invalid */
1045                        uclogic_params_init_with_pen_unused(&p);
1046                }
1047                break;
1048        case VID_PID(USB_VENDOR_ID_UGEE,
1049                     USB_DEVICE_ID_UGEE_TABLET_G5):
1050                /* Ignore non-pen interfaces */
1051                if (bInterfaceNumber != 1) {
1052                        uclogic_params_init_invalid(&p);
1053                        break;
1054                }
1055
1056                rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1057                if (rc != 0) {
1058                        hid_err(hdev, "pen probing failed: %d\n", rc);
1059                        goto cleanup;
1060                } else if (found) {
1061                        rc = uclogic_params_frame_init_with_desc(
1062                                &p.frame,
1063                                uclogic_rdesc_ugee_g5_frame_arr,
1064                                uclogic_rdesc_ugee_g5_frame_size,
1065                                UCLOGIC_RDESC_UGEE_G5_FRAME_ID);
1066                        if (rc != 0) {
1067                                hid_err(hdev,
1068                                        "failed creating buttonpad parameters: %d\n",
1069                                        rc);
1070                                goto cleanup;
1071                        }
1072                        p.frame.re_lsb =
1073                                UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB;
1074                        p.frame.dev_id_byte =
1075                                UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE;
1076                } else {
1077                        hid_warn(hdev, "pen parameters not found");
1078                        uclogic_params_init_invalid(&p);
1079                }
1080
1081                break;
1082        case VID_PID(USB_VENDOR_ID_UGEE,
1083                     USB_DEVICE_ID_UGEE_TABLET_EX07S):
1084                /* Ignore non-pen interfaces */
1085                if (bInterfaceNumber != 1) {
1086                        uclogic_params_init_invalid(&p);
1087                        break;
1088                }
1089
1090                rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1091                if (rc != 0) {
1092                        hid_err(hdev, "pen probing failed: %d\n", rc);
1093                        goto cleanup;
1094                } else if (found) {
1095                        rc = uclogic_params_frame_init_with_desc(
1096                                &p.frame,
1097                                uclogic_rdesc_ugee_ex07_buttonpad_arr,
1098                                uclogic_rdesc_ugee_ex07_buttonpad_size,
1099                                0);
1100                        if (rc != 0) {
1101                                hid_err(hdev,
1102                                        "failed creating buttonpad parameters: %d\n",
1103                                        rc);
1104                                goto cleanup;
1105                        }
1106                } else {
1107                        hid_warn(hdev, "pen parameters not found");
1108                        uclogic_params_init_invalid(&p);
1109                }
1110
1111                break;
1112        }
1113
1114#undef VID_PID
1115#undef WITH_OPT_DESC
1116
1117        /* Output parameters */
1118        memcpy(params, &p, sizeof(*params));
1119        memset(&p, 0, sizeof(p));
1120        rc = 0;
1121cleanup:
1122        uclogic_params_cleanup(&p);
1123        return rc;
1124}
1125