123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * HID driver for UC-Logic devices not fully compliant with HID standard
- * - tablet initialization and parameter retrieval
- *
- * Copyright (c) 2018 Nikolai Kondrashov
- */
- /*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- */
- #include "hid-uclogic-params.h"
- #include "hid-uclogic-rdesc.h"
- #include "usbhid/usbhid.h"
- #include "hid-ids.h"
- #include <linux/ctype.h>
- #include <linux/string.h>
- #include <asm/unaligned.h>
- /**
- * uclogic_params_pen_inrange_to_str() - Convert a pen in-range reporting type
- * to a string.
- * @inrange: The in-range reporting type to convert.
- *
- * Return:
- * * The string representing the type, or
- * * %NULL if the type is unknown.
- */
- static const char *uclogic_params_pen_inrange_to_str(
- enum uclogic_params_pen_inrange inrange)
- {
- switch (inrange) {
- case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL:
- return "normal";
- case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED:
- return "inverted";
- case UCLOGIC_PARAMS_PEN_INRANGE_NONE:
- return "none";
- default:
- return NULL;
- }
- }
- /**
- * uclogic_params_pen_hid_dbg() - Dump tablet interface pen parameters
- * @hdev: The HID device the pen parameters describe.
- * @pen: The pen parameters to dump.
- *
- * Dump tablet interface pen parameters with hid_dbg(). The dump is indented
- * with a tab.
- */
- static void uclogic_params_pen_hid_dbg(const struct hid_device *hdev,
- const struct uclogic_params_pen *pen)
- {
- size_t i;
- hid_dbg(hdev, "\t.usage_invalid = %s\n",
- (pen->usage_invalid ? "true" : "false"));
- hid_dbg(hdev, "\t.desc_ptr = %p\n", pen->desc_ptr);
- hid_dbg(hdev, "\t.desc_size = %u\n", pen->desc_size);
- hid_dbg(hdev, "\t.id = %u\n", pen->id);
- hid_dbg(hdev, "\t.subreport_list = {\n");
- for (i = 0; i < ARRAY_SIZE(pen->subreport_list); i++) {
- hid_dbg(hdev, "\t\t{0x%02hhx, %hhu}%s\n",
- pen->subreport_list[i].value,
- pen->subreport_list[i].id,
- i < (ARRAY_SIZE(pen->subreport_list) - 1) ? "," : "");
- }
- hid_dbg(hdev, "\t}\n");
- hid_dbg(hdev, "\t.inrange = %s\n",
- uclogic_params_pen_inrange_to_str(pen->inrange));
- hid_dbg(hdev, "\t.fragmented_hires = %s\n",
- (pen->fragmented_hires ? "true" : "false"));
- hid_dbg(hdev, "\t.tilt_y_flipped = %s\n",
- (pen->tilt_y_flipped ? "true" : "false"));
- }
- /**
- * uclogic_params_frame_hid_dbg() - Dump tablet interface frame parameters
- * @hdev: The HID device the pen parameters describe.
- * @frame: The frame parameters to dump.
- *
- * Dump tablet interface frame parameters with hid_dbg(). The dump is
- * indented with two tabs.
- */
- static void uclogic_params_frame_hid_dbg(
- const struct hid_device *hdev,
- const struct uclogic_params_frame *frame)
- {
- hid_dbg(hdev, "\t\t.desc_ptr = %p\n", frame->desc_ptr);
- hid_dbg(hdev, "\t\t.desc_size = %u\n", frame->desc_size);
- hid_dbg(hdev, "\t\t.id = %u\n", frame->id);
- hid_dbg(hdev, "\t\t.suffix = %s\n", frame->suffix);
- hid_dbg(hdev, "\t\t.re_lsb = %u\n", frame->re_lsb);
- hid_dbg(hdev, "\t\t.dev_id_byte = %u\n", frame->dev_id_byte);
- hid_dbg(hdev, "\t\t.touch_byte = %u\n", frame->touch_byte);
- hid_dbg(hdev, "\t\t.touch_max = %hhd\n", frame->touch_max);
- hid_dbg(hdev, "\t\t.touch_flip_at = %hhd\n",
- frame->touch_flip_at);
- hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n",
- frame->bitmap_dial_byte);
- }
- /**
- * uclogic_params_hid_dbg() - Dump tablet interface parameters
- * @hdev: The HID device the parameters describe.
- * @params: The parameters to dump.
- *
- * Dump tablet interface parameters with hid_dbg().
- */
- void uclogic_params_hid_dbg(const struct hid_device *hdev,
- const struct uclogic_params *params)
- {
- size_t i;
- hid_dbg(hdev, ".invalid = %s\n",
- params->invalid ? "true" : "false");
- hid_dbg(hdev, ".desc_ptr = %p\n", params->desc_ptr);
- hid_dbg(hdev, ".desc_size = %u\n", params->desc_size);
- hid_dbg(hdev, ".pen = {\n");
- uclogic_params_pen_hid_dbg(hdev, ¶ms->pen);
- hid_dbg(hdev, "\t}\n");
- hid_dbg(hdev, ".frame_list = {\n");
- for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
- hid_dbg(hdev, "\t{\n");
- uclogic_params_frame_hid_dbg(hdev, ¶ms->frame_list[i]);
- hid_dbg(hdev, "\t}%s\n",
- i < (ARRAY_SIZE(params->frame_list) - 1) ? "," : "");
- }
- hid_dbg(hdev, "}\n");
- }
- /**
- * uclogic_params_get_str_desc - retrieve a string descriptor from a HID
- * device interface, putting it into a kmalloc-allocated buffer as is, without
- * character encoding conversion.
- *
- * @pbuf: Location for the kmalloc-allocated buffer pointer containing
- * the retrieved descriptor. Not modified in case of error.
- * Can be NULL to have retrieved descriptor discarded.
- * @hdev: The HID device of the tablet interface to retrieve the string
- * descriptor from. Cannot be NULL.
- * @idx: Index of the string descriptor to request from the device.
- * @len: Length of the buffer to allocate and the data to retrieve.
- *
- * Returns:
- * number of bytes retrieved (<= len),
- * -EPIPE, if the descriptor was not found, or
- * another negative errno code in case of other error.
- */
- static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,
- __u8 idx, size_t len)
- {
- int rc;
- struct usb_device *udev;
- __u8 *buf = NULL;
- /* Check arguments */
- if (hdev == NULL) {
- rc = -EINVAL;
- goto cleanup;
- }
- udev = hid_to_usb_dev(hdev);
- buf = kmalloc(len, GFP_KERNEL);
- if (buf == NULL) {
- rc = -ENOMEM;
- goto cleanup;
- }
- rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
- USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
- (USB_DT_STRING << 8) + idx,
- 0x0409, buf, len,
- USB_CTRL_GET_TIMEOUT);
- if (rc == -EPIPE) {
- hid_dbg(hdev, "string descriptor #%hhu not found\n", idx);
- goto cleanup;
- } else if (rc < 0) {
- hid_err(hdev,
- "failed retrieving string descriptor #%u: %d\n",
- idx, rc);
- goto cleanup;
- }
- if (pbuf != NULL) {
- *pbuf = buf;
- buf = NULL;
- }
- cleanup:
- kfree(buf);
- return rc;
- }
- /**
- * uclogic_params_pen_cleanup - free resources used by struct
- * uclogic_params_pen (tablet interface's pen input parameters).
- * Can be called repeatedly.
- *
- * @pen: Pen input parameters to cleanup. Cannot be NULL.
- */
- static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen)
- {
- kfree(pen->desc_ptr);
- memset(pen, 0, sizeof(*pen));
- }
- /**
- * uclogic_params_pen_init_v1() - initialize tablet interface pen
- * input and retrieve its parameters from the device, using v1 protocol.
- *
- * @pen: Pointer to the pen parameters to initialize (to be
- * cleaned up with uclogic_params_pen_cleanup()). Not modified in
- * case of error, or if parameters are not found. Cannot be NULL.
- * @pfound: Location for a flag which is set to true if the parameters
- * were found, and to false if not (e.g. device was
- * incompatible). Not modified in case of error. Cannot be NULL.
- * @hdev: The HID device of the tablet interface to initialize and get
- * parameters from. Cannot be NULL.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,
- bool *pfound,
- struct hid_device *hdev)
- {
- int rc;
- bool found = false;
- /* Buffer for (part of) the string descriptor */
- __u8 *buf = NULL;
- /* Minimum descriptor length required, maximum seen so far is 18 */
- const int len = 12;
- s32 resolution;
- /* Pen report descriptor template parameters */
- s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
- __u8 *desc_ptr = NULL;
- /* Check arguments */
- if (pen == NULL || pfound == NULL || hdev == NULL) {
- rc = -EINVAL;
- goto cleanup;
- }
- /*
- * Read string descriptor containing pen input parameters.
- * The specific string descriptor and data were discovered by sniffing
- * the Windows driver traffic.
- * NOTE: This enables fully-functional tablet mode.
- */
- rc = uclogic_params_get_str_desc(&buf, hdev, 100, len);
- if (rc == -EPIPE) {
- hid_dbg(hdev,
- "string descriptor with pen parameters not found, assuming not compatible\n");
- goto finish;
- } else if (rc < 0) {
- hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
- goto cleanup;
- } else if (rc != len) {
- hid_dbg(hdev,
- "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
- rc, len);
- goto finish;
- }
- /*
- * Fill report descriptor parameters from the string descriptor
- */
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
- get_unaligned_le16(buf + 2);
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
- get_unaligned_le16(buf + 4);
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
- get_unaligned_le16(buf + 8);
- resolution = get_unaligned_le16(buf + 10);
- if (resolution == 0) {
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
- } else {
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
- resolution;
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
- resolution;
- }
- kfree(buf);
- buf = NULL;
- /*
- * Generate pen report descriptor
- */
- desc_ptr = uclogic_rdesc_template_apply(
- uclogic_rdesc_v1_pen_template_arr,
- uclogic_rdesc_v1_pen_template_size,
- desc_params, ARRAY_SIZE(desc_params));
- if (desc_ptr == NULL) {
- rc = -ENOMEM;
- goto cleanup;
- }
- /*
- * Fill-in the parameters
- */
- memset(pen, 0, sizeof(*pen));
- pen->desc_ptr = desc_ptr;
- desc_ptr = NULL;
- pen->desc_size = uclogic_rdesc_v1_pen_template_size;
- pen->id = UCLOGIC_RDESC_V1_PEN_ID;
- pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;
- found = true;
- finish:
- *pfound = found;
- rc = 0;
- cleanup:
- kfree(desc_ptr);
- kfree(buf);
- return rc;
- }
- /**
- * uclogic_params_get_le24() - get a 24-bit little-endian number from a
- * buffer.
- *
- * @p: The pointer to the number buffer.
- *
- * Returns:
- * The retrieved number
- */
- static s32 uclogic_params_get_le24(const void *p)
- {
- const __u8 *b = p;
- return b[0] | (b[1] << 8UL) | (b[2] << 16UL);
- }
- /**
- * uclogic_params_pen_init_v2() - initialize tablet interface pen
- * input and retrieve its parameters from the device, using v2 protocol.
- *
- * @pen: Pointer to the pen parameters to initialize (to be
- * cleaned up with uclogic_params_pen_cleanup()). Not
- * modified in case of error, or if parameters are not
- * found. Cannot be NULL.
- * @pfound: Location for a flag which is set to true if the
- * parameters were found, and to false if not (e.g.
- * device was incompatible). Not modified in case of
- * error. Cannot be NULL.
- * @pparams_ptr: Location for a kmalloc'ed pointer to the retrieved raw
- * parameters, which could be used to identify the tablet
- * to some extent. Should be freed with kfree after use.
- * NULL, if not needed. Not modified in case of error.
- * Only set if *pfound is set to true.
- * @pparams_len: Location for the length of the retrieved raw
- * parameters. NULL, if not needed. Not modified in case
- * of error. Only set if *pfound is set to true.
- * @hdev: The HID device of the tablet interface to initialize
- * and get parameters from. Cannot be NULL.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
- bool *pfound,
- __u8 **pparams_ptr,
- size_t *pparams_len,
- struct hid_device *hdev)
- {
- int rc;
- bool found = false;
- /* Buffer for (part of) the parameter string descriptor */
- __u8 *buf = NULL;
- /* Parameter string descriptor required length */
- const int params_len_min = 18;
- /* Parameter string descriptor accepted length */
- const int params_len_max = 32;
- /* Parameter string descriptor received length */
- int params_len;
- size_t i;
- s32 resolution;
- /* Pen report descriptor template parameters */
- s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
- __u8 *desc_ptr = NULL;
- /* Check arguments */
- if (pen == NULL || pfound == NULL || hdev == NULL) {
- rc = -EINVAL;
- goto cleanup;
- }
- /*
- * Read string descriptor containing pen input parameters.
- * The specific string descriptor and data were discovered by sniffing
- * the Windows driver traffic.
- * NOTE: This enables fully-functional tablet mode.
- */
- rc = uclogic_params_get_str_desc(&buf, hdev, 200, params_len_max);
- if (rc == -EPIPE) {
- hid_dbg(hdev,
- "string descriptor with pen parameters not found, assuming not compatible\n");
- goto finish;
- } else if (rc < 0) {
- hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
- goto cleanup;
- } else if (rc < params_len_min) {
- hid_dbg(hdev,
- "string descriptor with pen parameters is too short (got %d, expected at least %d), assuming not compatible\n",
- rc, params_len_min);
- goto finish;
- }
- params_len = rc;
- /*
- * Check it's not just a catch-all UTF-16LE-encoded ASCII
- * string (such as the model name) some tablets put into all
- * unknown string descriptors.
- */
- for (i = 2;
- i < params_len &&
- (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0);
- i += 2);
- if (i >= params_len) {
- hid_dbg(hdev,
- "string descriptor with pen parameters seems to contain only text, assuming not compatible\n");
- goto finish;
- }
- /*
- * Fill report descriptor parameters from the string descriptor
- */
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
- uclogic_params_get_le24(buf + 2);
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
- uclogic_params_get_le24(buf + 5);
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
- get_unaligned_le16(buf + 8);
- resolution = get_unaligned_le16(buf + 10);
- if (resolution == 0) {
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
- } else {
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
- resolution;
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
- resolution;
- }
- /*
- * Generate pen report descriptor
- */
- desc_ptr = uclogic_rdesc_template_apply(
- uclogic_rdesc_v2_pen_template_arr,
- uclogic_rdesc_v2_pen_template_size,
- desc_params, ARRAY_SIZE(desc_params));
- if (desc_ptr == NULL) {
- rc = -ENOMEM;
- goto cleanup;
- }
- /*
- * Fill-in the parameters
- */
- memset(pen, 0, sizeof(*pen));
- pen->desc_ptr = desc_ptr;
- desc_ptr = NULL;
- pen->desc_size = uclogic_rdesc_v2_pen_template_size;
- pen->id = UCLOGIC_RDESC_V2_PEN_ID;
- pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE;
- pen->fragmented_hires = true;
- pen->tilt_y_flipped = true;
- found = true;
- if (pparams_ptr != NULL) {
- *pparams_ptr = buf;
- buf = NULL;
- }
- if (pparams_len != NULL)
- *pparams_len = params_len;
- finish:
- *pfound = found;
- rc = 0;
- cleanup:
- kfree(desc_ptr);
- kfree(buf);
- return rc;
- }
- /**
- * uclogic_params_frame_cleanup - free resources used by struct
- * uclogic_params_frame (tablet interface's frame controls input parameters).
- * Can be called repeatedly.
- *
- * @frame: Frame controls input parameters to cleanup. Cannot be NULL.
- */
- static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame)
- {
- kfree(frame->desc_ptr);
- memset(frame, 0, sizeof(*frame));
- }
- /**
- * uclogic_params_frame_init_with_desc() - initialize tablet's frame control
- * parameters with a static report descriptor.
- *
- * @frame: Pointer to the frame parameters to initialize (to be cleaned
- * up with uclogic_params_frame_cleanup()). Not modified in case
- * of error. Cannot be NULL.
- * @desc_ptr: Report descriptor pointer. Can be NULL, if desc_size is zero.
- * @desc_size: Report descriptor size.
- * @id: Report ID used for frame reports, if they should be tweaked,
- * zero if not.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_params_frame_init_with_desc(
- struct uclogic_params_frame *frame,
- const __u8 *desc_ptr,
- size_t desc_size,
- unsigned int id)
- {
- __u8 *copy_desc_ptr;
- if (frame == NULL || (desc_ptr == NULL && desc_size != 0))
- return -EINVAL;
- copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
- if (copy_desc_ptr == NULL)
- return -ENOMEM;
- memset(frame, 0, sizeof(*frame));
- frame->desc_ptr = copy_desc_ptr;
- frame->desc_size = desc_size;
- frame->id = id;
- return 0;
- }
- /**
- * uclogic_params_frame_init_v1() - initialize v1 tablet interface frame
- * controls.
- *
- * @frame: Pointer to the frame parameters to initialize (to be cleaned
- * up with uclogic_params_frame_cleanup()). Not modified in case
- * of error, or if parameters are not found. Cannot be NULL.
- * @pfound: Location for a flag which is set to true if the parameters
- * were found, and to false if not (e.g. device was
- * incompatible). Not modified in case of error. Cannot be NULL.
- * @hdev: The HID device of the tablet interface to initialize and get
- * parameters from. Cannot be NULL.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame,
- bool *pfound,
- struct hid_device *hdev)
- {
- int rc;
- bool found = false;
- struct usb_device *usb_dev;
- char *str_buf = NULL;
- const size_t str_len = 16;
- /* Check arguments */
- if (frame == NULL || pfound == NULL || hdev == NULL) {
- rc = -EINVAL;
- goto cleanup;
- }
- usb_dev = hid_to_usb_dev(hdev);
- /*
- * Enable generic button mode
- */
- str_buf = kzalloc(str_len, GFP_KERNEL);
- if (str_buf == NULL) {
- rc = -ENOMEM;
- goto cleanup;
- }
- rc = usb_string(usb_dev, 123, str_buf, str_len);
- if (rc == -EPIPE) {
- hid_dbg(hdev,
- "generic button -enabling string descriptor not found\n");
- } else if (rc < 0) {
- goto cleanup;
- } else if (strncmp(str_buf, "HK On", rc) != 0) {
- hid_dbg(hdev,
- "invalid response to enabling generic buttons: \"%s\"\n",
- str_buf);
- } else {
- hid_dbg(hdev, "generic buttons enabled\n");
- rc = uclogic_params_frame_init_with_desc(
- frame,
- uclogic_rdesc_v1_frame_arr,
- uclogic_rdesc_v1_frame_size,
- UCLOGIC_RDESC_V1_FRAME_ID);
- if (rc != 0)
- goto cleanup;
- found = true;
- }
- *pfound = found;
- rc = 0;
- cleanup:
- kfree(str_buf);
- return rc;
- }
- /**
- * uclogic_params_cleanup - free resources used by struct uclogic_params
- * (tablet interface's parameters).
- * Can be called repeatedly.
- *
- * @params: Input parameters to cleanup. Cannot be NULL.
- */
- void uclogic_params_cleanup(struct uclogic_params *params)
- {
- if (!params->invalid) {
- size_t i;
- kfree(params->desc_ptr);
- uclogic_params_pen_cleanup(¶ms->pen);
- for (i = 0; i < ARRAY_SIZE(params->frame_list); i++)
- uclogic_params_frame_cleanup(¶ms->frame_list[i]);
- memset(params, 0, sizeof(*params));
- }
- }
- /**
- * uclogic_params_get_desc() - Get a replacement report descriptor for a
- * tablet's interface.
- *
- * @params: The parameters of a tablet interface to get report
- * descriptor for. Cannot be NULL.
- * @pdesc: Location for the resulting, kmalloc-allocated report
- * descriptor pointer, or for NULL, if there's no replacement
- * report descriptor. Not modified in case of error. Cannot be
- * NULL.
- * @psize: Location for the resulting report descriptor size, not set if
- * there's no replacement report descriptor. Not modified in case
- * of error. Cannot be NULL.
- *
- * Returns:
- * Zero, if successful.
- * -EINVAL, if invalid arguments are supplied.
- * -ENOMEM, if failed to allocate memory.
- */
- int uclogic_params_get_desc(const struct uclogic_params *params,
- __u8 **pdesc,
- unsigned int *psize)
- {
- int rc = -ENOMEM;
- bool present = false;
- unsigned int size = 0;
- __u8 *desc = NULL;
- size_t i;
- /* Check arguments */
- if (params == NULL || pdesc == NULL || psize == NULL)
- return -EINVAL;
- /* Concatenate descriptors */
- #define ADD_DESC(_desc_ptr, _desc_size) \
- do { \
- unsigned int new_size; \
- __u8 *new_desc; \
- if ((_desc_ptr) == NULL) { \
- break; \
- } \
- new_size = size + (_desc_size); \
- new_desc = krealloc(desc, new_size, GFP_KERNEL); \
- if (new_desc == NULL) { \
- goto cleanup; \
- } \
- memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \
- desc = new_desc; \
- size = new_size; \
- present = true; \
- } while (0)
- ADD_DESC(params->desc_ptr, params->desc_size);
- ADD_DESC(params->pen.desc_ptr, params->pen.desc_size);
- for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
- ADD_DESC(params->frame_list[i].desc_ptr,
- params->frame_list[i].desc_size);
- }
- #undef ADD_DESC
- if (present) {
- *pdesc = desc;
- *psize = size;
- desc = NULL;
- }
- rc = 0;
- cleanup:
- kfree(desc);
- return rc;
- }
- /**
- * uclogic_params_init_invalid() - initialize tablet interface parameters,
- * specifying the interface is invalid.
- *
- * @params: Parameters to initialize (to be cleaned with
- * uclogic_params_cleanup()). Cannot be NULL.
- */
- static void uclogic_params_init_invalid(struct uclogic_params *params)
- {
- params->invalid = true;
- }
- /**
- * uclogic_params_init_with_opt_desc() - initialize tablet interface
- * parameters with an optional replacement report descriptor. Only modify
- * report descriptor, if the original report descriptor matches the expected
- * size.
- *
- * @params: Parameters to initialize (to be cleaned with
- * uclogic_params_cleanup()). Not modified in case of
- * error. Cannot be NULL.
- * @hdev: The HID device of the tablet interface create the
- * parameters for. Cannot be NULL.
- * @orig_desc_size: Expected size of the original report descriptor to
- * be replaced.
- * @desc_ptr: Pointer to the replacement report descriptor.
- * Can be NULL, if desc_size is zero.
- * @desc_size: Size of the replacement report descriptor.
- *
- * Returns:
- * Zero, if successful. -EINVAL if an invalid argument was passed.
- * -ENOMEM, if failed to allocate memory.
- */
- static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,
- struct hid_device *hdev,
- unsigned int orig_desc_size,
- __u8 *desc_ptr,
- unsigned int desc_size)
- {
- __u8 *desc_copy_ptr = NULL;
- unsigned int desc_copy_size;
- int rc;
- /* Check arguments */
- if (params == NULL || hdev == NULL ||
- (desc_ptr == NULL && desc_size != 0)) {
- rc = -EINVAL;
- goto cleanup;
- }
- /* Replace report descriptor, if it matches */
- if (hdev->dev_rsize == orig_desc_size) {
- hid_dbg(hdev,
- "device report descriptor matches the expected size, replacing\n");
- desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
- if (desc_copy_ptr == NULL) {
- rc = -ENOMEM;
- goto cleanup;
- }
- desc_copy_size = desc_size;
- } else {
- hid_dbg(hdev,
- "device report descriptor doesn't match the expected size (%u != %u), preserving\n",
- hdev->dev_rsize, orig_desc_size);
- desc_copy_ptr = NULL;
- desc_copy_size = 0;
- }
- /* Output parameters */
- memset(params, 0, sizeof(*params));
- params->desc_ptr = desc_copy_ptr;
- desc_copy_ptr = NULL;
- params->desc_size = desc_copy_size;
- rc = 0;
- cleanup:
- kfree(desc_copy_ptr);
- return rc;
- }
- /**
- * uclogic_params_huion_init() - initialize a Huion tablet interface and discover
- * its parameters.
- *
- * @params: Parameters to fill in (to be cleaned with
- * uclogic_params_cleanup()). Not modified in case of error.
- * Cannot be NULL.
- * @hdev: The HID device of the tablet interface to initialize and get
- * parameters from. Cannot be NULL.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_params_huion_init(struct uclogic_params *params,
- struct hid_device *hdev)
- {
- int rc;
- struct usb_device *udev;
- struct usb_interface *iface;
- __u8 bInterfaceNumber;
- bool found;
- /* The resulting parameters (noop) */
- struct uclogic_params p = {0, };
- static const char transition_ver[] = "HUION_T153_160607";
- char *ver_ptr = NULL;
- const size_t ver_len = sizeof(transition_ver) + 1;
- __u8 *params_ptr = NULL;
- size_t params_len = 0;
- /* Parameters string descriptor of a model with touch ring (HS610) */
- const __u8 touch_ring_model_params_buf[] = {
- 0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00,
- 0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01,
- 0x04, 0x3C, 0x3E
- };
- /* Check arguments */
- if (params == NULL || hdev == NULL) {
- rc = -EINVAL;
- goto cleanup;
- }
- udev = hid_to_usb_dev(hdev);
- iface = to_usb_interface(hdev->dev.parent);
- bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
- /* If it's a custom keyboard interface */
- if (bInterfaceNumber == 1) {
- /* Keep everything intact, but mark pen usage invalid */
- p.pen.usage_invalid = true;
- goto output;
- /* Else, if it's not a pen interface */
- } else if (bInterfaceNumber != 0) {
- uclogic_params_init_invalid(&p);
- goto output;
- }
- /* Try to get firmware version */
- ver_ptr = kzalloc(ver_len, GFP_KERNEL);
- if (ver_ptr == NULL) {
- rc = -ENOMEM;
- goto cleanup;
- }
- rc = usb_string(udev, 201, ver_ptr, ver_len);
- if (rc == -EPIPE) {
- *ver_ptr = '\0';
- } else if (rc < 0) {
- hid_err(hdev,
- "failed retrieving Huion firmware version: %d\n", rc);
- goto cleanup;
- }
- /* If this is a transition firmware */
- if (strcmp(ver_ptr, transition_ver) == 0) {
- hid_dbg(hdev,
- "transition firmware detected, not probing pen v2 parameters\n");
- } else {
- /* Try to probe v2 pen parameters */
- rc = uclogic_params_pen_init_v2(&p.pen, &found,
- ¶ms_ptr, ¶ms_len,
- hdev);
- if (rc != 0) {
- hid_err(hdev,
- "failed probing pen v2 parameters: %d\n", rc);
- goto cleanup;
- } else if (found) {
- hid_dbg(hdev, "pen v2 parameters found\n");
- /* Create v2 frame button parameters */
- rc = uclogic_params_frame_init_with_desc(
- &p.frame_list[0],
- uclogic_rdesc_v2_frame_buttons_arr,
- uclogic_rdesc_v2_frame_buttons_size,
- UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID);
- if (rc != 0) {
- hid_err(hdev,
- "failed creating v2 frame button parameters: %d\n",
- rc);
- goto cleanup;
- }
- /* Link from pen sub-report */
- p.pen.subreport_list[0].value = 0xe0;
- p.pen.subreport_list[0].id =
- UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID;
- /* If this is the model with touch ring */
- if (params_ptr != NULL &&
- params_len == sizeof(touch_ring_model_params_buf) &&
- memcmp(params_ptr, touch_ring_model_params_buf,
- params_len) == 0) {
- /* Create touch ring parameters */
- rc = uclogic_params_frame_init_with_desc(
- &p.frame_list[1],
- uclogic_rdesc_v2_frame_touch_ring_arr,
- uclogic_rdesc_v2_frame_touch_ring_size,
- UCLOGIC_RDESC_V2_FRAME_TOUCH_ID);
- if (rc != 0) {
- hid_err(hdev,
- "failed creating v2 frame touch ring parameters: %d\n",
- rc);
- goto cleanup;
- }
- p.frame_list[1].suffix = "Touch Ring";
- p.frame_list[1].dev_id_byte =
- UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE;
- p.frame_list[1].touch_byte = 5;
- p.frame_list[1].touch_max = 12;
- p.frame_list[1].touch_flip_at = 7;
- } else {
- /* Create touch strip parameters */
- rc = uclogic_params_frame_init_with_desc(
- &p.frame_list[1],
- uclogic_rdesc_v2_frame_touch_strip_arr,
- uclogic_rdesc_v2_frame_touch_strip_size,
- UCLOGIC_RDESC_V2_FRAME_TOUCH_ID);
- if (rc != 0) {
- hid_err(hdev,
- "failed creating v2 frame touch strip parameters: %d\n",
- rc);
- goto cleanup;
- }
- p.frame_list[1].suffix = "Touch Strip";
- p.frame_list[1].dev_id_byte =
- UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE;
- p.frame_list[1].touch_byte = 5;
- p.frame_list[1].touch_max = 8;
- }
- /* Link from pen sub-report */
- p.pen.subreport_list[1].value = 0xf0;
- p.pen.subreport_list[1].id =
- UCLOGIC_RDESC_V2_FRAME_TOUCH_ID;
- /* Create v2 frame dial parameters */
- rc = uclogic_params_frame_init_with_desc(
- &p.frame_list[2],
- uclogic_rdesc_v2_frame_dial_arr,
- uclogic_rdesc_v2_frame_dial_size,
- UCLOGIC_RDESC_V2_FRAME_DIAL_ID);
- if (rc != 0) {
- hid_err(hdev,
- "failed creating v2 frame dial parameters: %d\n",
- rc);
- goto cleanup;
- }
- p.frame_list[2].suffix = "Dial";
- p.frame_list[2].dev_id_byte =
- UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE;
- p.frame_list[2].bitmap_dial_byte = 5;
- /* Link from pen sub-report */
- p.pen.subreport_list[2].value = 0xf1;
- p.pen.subreport_list[2].id =
- UCLOGIC_RDESC_V2_FRAME_DIAL_ID;
- goto output;
- }
- hid_dbg(hdev, "pen v2 parameters not found\n");
- }
- /* Try to probe v1 pen parameters */
- rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
- if (rc != 0) {
- hid_err(hdev,
- "failed probing pen v1 parameters: %d\n", rc);
- goto cleanup;
- } else if (found) {
- hid_dbg(hdev, "pen v1 parameters found\n");
- /* Try to probe v1 frame */
- rc = uclogic_params_frame_init_v1(&p.frame_list[0],
- &found, hdev);
- if (rc != 0) {
- hid_err(hdev, "v1 frame probing failed: %d\n", rc);
- goto cleanup;
- }
- hid_dbg(hdev, "frame v1 parameters%s found\n",
- (found ? "" : " not"));
- if (found) {
- /* Link frame button subreports from pen reports */
- p.pen.subreport_list[0].value = 0xe0;
- p.pen.subreport_list[0].id =
- UCLOGIC_RDESC_V1_FRAME_ID;
- }
- goto output;
- }
- hid_dbg(hdev, "pen v1 parameters not found\n");
- uclogic_params_init_invalid(&p);
- output:
- /* Output parameters */
- memcpy(params, &p, sizeof(*params));
- memset(&p, 0, sizeof(p));
- rc = 0;
- cleanup:
- kfree(params_ptr);
- kfree(ver_ptr);
- uclogic_params_cleanup(&p);
- return rc;
- }
- /**
- * uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or
- * the XP-PEN Deco Mini 7, need to be initialized by sending them magic data.
- *
- * @hdev: The HID device of the tablet interface to initialize and get
- * parameters from. Cannot be NULL.
- * @magic_arr: The magic data that should be sent to probe the interface.
- * Cannot be NULL.
- * @magic_size: Size of the magic data.
- * @endpoint: Endpoint where the magic data should be sent.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_probe_interface(struct hid_device *hdev, u8 *magic_arr,
- int magic_size, int endpoint)
- {
- struct usb_device *udev;
- unsigned int pipe = 0;
- int sent;
- u8 *buf = NULL;
- int rc = 0;
- if (!hdev || !magic_arr) {
- rc = -EINVAL;
- goto cleanup;
- }
- buf = kmemdup(magic_arr, magic_size, GFP_KERNEL);
- if (!buf) {
- rc = -ENOMEM;
- goto cleanup;
- }
- udev = hid_to_usb_dev(hdev);
- pipe = usb_sndintpipe(udev, endpoint);
- rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000);
- if (rc || sent != magic_size) {
- hid_err(hdev, "Interface probing failed: %d\n", rc);
- rc = -1;
- goto cleanup;
- }
- rc = 0;
- cleanup:
- kfree(buf);
- return rc;
- }
- /**
- * uclogic_params_parse_ugee_v2_desc - parse the string descriptor containing
- * pen and frame parameters returned by UGEE v2 devices.
- *
- * @str_desc: String descriptor, cannot be NULL.
- * @str_desc_size: Size of the string descriptor.
- * @desc_params: Output description params list.
- * @desc_params_size: Size of the output description params list.
- * @frame_type: Output frame type.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_params_parse_ugee_v2_desc(const __u8 *str_desc,
- size_t str_desc_size,
- s32 *desc_params,
- size_t desc_params_size,
- enum uclogic_params_frame_type *frame_type)
- {
- s32 pen_x_lm, pen_y_lm;
- s32 pen_x_pm, pen_y_pm;
- s32 pen_pressure_lm;
- s32 frame_num_buttons;
- s32 resolution;
- /* Minimum descriptor length required, maximum seen so far is 14 */
- const int min_str_desc_size = 12;
- if (!str_desc || str_desc_size < min_str_desc_size)
- return -EINVAL;
- if (desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)
- return -EINVAL;
- pen_x_lm = get_unaligned_le16(str_desc + 2);
- pen_y_lm = get_unaligned_le16(str_desc + 4);
- frame_num_buttons = str_desc[6];
- *frame_type = str_desc[7];
- pen_pressure_lm = get_unaligned_le16(str_desc + 8);
- resolution = get_unaligned_le16(str_desc + 10);
- if (resolution == 0) {
- pen_x_pm = 0;
- pen_y_pm = 0;
- } else {
- pen_x_pm = pen_x_lm * 1000 / resolution;
- pen_y_pm = pen_y_lm * 1000 / resolution;
- }
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = pen_x_lm;
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = pen_x_pm;
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = pen_y_lm;
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = pen_y_pm;
- desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = pen_pressure_lm;
- desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = frame_num_buttons;
- return 0;
- }
- /**
- * uclogic_params_ugee_v2_init_frame_buttons() - initialize a UGEE v2 frame with
- * buttons.
- * @p: Parameters to fill in, cannot be NULL.
- * @desc_params: Device description params list.
- * @desc_params_size: Size of the description params list.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_params_ugee_v2_init_frame_buttons(struct uclogic_params *p,
- const s32 *desc_params,
- size_t desc_params_size)
- {
- __u8 *rdesc_frame = NULL;
- int rc = 0;
- if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)
- return -EINVAL;
- rdesc_frame = uclogic_rdesc_template_apply(
- uclogic_rdesc_ugee_v2_frame_btn_template_arr,
- uclogic_rdesc_ugee_v2_frame_btn_template_size,
- desc_params, UCLOGIC_RDESC_PH_ID_NUM);
- if (!rdesc_frame)
- return -ENOMEM;
- rc = uclogic_params_frame_init_with_desc(&p->frame_list[0],
- rdesc_frame,
- uclogic_rdesc_ugee_v2_frame_btn_template_size,
- UCLOGIC_RDESC_V1_FRAME_ID);
- kfree(rdesc_frame);
- return rc;
- }
- /**
- * uclogic_params_ugee_v2_init_frame_dial() - initialize a UGEE v2 frame with a
- * bitmap dial.
- * @p: Parameters to fill in, cannot be NULL.
- * @desc_params: Device description params list.
- * @desc_params_size: Size of the description params list.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_params_ugee_v2_init_frame_dial(struct uclogic_params *p,
- const s32 *desc_params,
- size_t desc_params_size)
- {
- __u8 *rdesc_frame = NULL;
- int rc = 0;
- if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)
- return -EINVAL;
- rdesc_frame = uclogic_rdesc_template_apply(
- uclogic_rdesc_ugee_v2_frame_dial_template_arr,
- uclogic_rdesc_ugee_v2_frame_dial_template_size,
- desc_params, UCLOGIC_RDESC_PH_ID_NUM);
- if (!rdesc_frame)
- return -ENOMEM;
- rc = uclogic_params_frame_init_with_desc(&p->frame_list[0],
- rdesc_frame,
- uclogic_rdesc_ugee_v2_frame_dial_template_size,
- UCLOGIC_RDESC_V1_FRAME_ID);
- kfree(rdesc_frame);
- if (rc)
- return rc;
- p->frame_list[0].bitmap_dial_byte = 7;
- return 0;
- }
- /**
- * uclogic_params_ugee_v2_init_frame_mouse() - initialize a UGEE v2 frame with a
- * mouse.
- * @p: Parameters to fill in, cannot be NULL.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_params_ugee_v2_init_frame_mouse(struct uclogic_params *p)
- {
- int rc = 0;
- if (!p)
- return -EINVAL;
- rc = uclogic_params_frame_init_with_desc(&p->frame_list[1],
- uclogic_rdesc_ugee_v2_frame_mouse_template_arr,
- uclogic_rdesc_ugee_v2_frame_mouse_template_size,
- UCLOGIC_RDESC_V1_FRAME_ID);
- return rc;
- }
- /**
- * uclogic_params_ugee_v2_has_battery() - check whether a UGEE v2 device has
- * battery or not.
- * @hdev: The HID device of the tablet interface.
- *
- * Returns:
- * True if the device has battery, false otherwise.
- */
- static bool uclogic_params_ugee_v2_has_battery(struct hid_device *hdev)
- {
- struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
- if (drvdata->quirks & UCLOGIC_BATTERY_QUIRK)
- return true;
- /* The XP-PEN Deco LW vendor, product and version are identical to the
- * Deco L. The only difference reported by their firmware is the product
- * name. Add a quirk to support battery reporting on the wireless
- * version.
- */
- if (hdev->vendor == USB_VENDOR_ID_UGEE &&
- hdev->product == USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) {
- struct usb_device *udev = hid_to_usb_dev(hdev);
- if (strstarts(udev->product, "Deco LW"))
- return true;
- }
- return false;
- }
- /**
- * uclogic_params_ugee_v2_init_battery() - initialize UGEE v2 battery reporting.
- * @hdev: The HID device of the tablet interface, cannot be NULL.
- * @p: Parameters to fill in, cannot be NULL.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_params_ugee_v2_init_battery(struct hid_device *hdev,
- struct uclogic_params *p)
- {
- int rc = 0;
- if (!hdev || !p)
- return -EINVAL;
- /* Some tablets contain invalid characters in hdev->uniq, throwing a
- * "hwmon: '<name>' is not a valid name attribute, please fix" error.
- * Use the device vendor and product IDs instead.
- */
- snprintf(hdev->uniq, sizeof(hdev->uniq), "%x-%x", hdev->vendor,
- hdev->product);
- rc = uclogic_params_frame_init_with_desc(&p->frame_list[1],
- uclogic_rdesc_ugee_v2_battery_template_arr,
- uclogic_rdesc_ugee_v2_battery_template_size,
- UCLOGIC_RDESC_UGEE_V2_BATTERY_ID);
- if (rc)
- return rc;
- p->frame_list[1].suffix = "Battery";
- p->pen.subreport_list[1].value = 0xf2;
- p->pen.subreport_list[1].id = UCLOGIC_RDESC_UGEE_V2_BATTERY_ID;
- return rc;
- }
- /**
- * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by
- * discovering their parameters.
- *
- * These tables, internally designed as v2 to differentiate them from older
- * models, expect a payload of magic data in orther to be switched to the fully
- * functional mode and expose their parameters in a similar way to the
- * information present in uclogic_params_pen_init_v1() but with some
- * differences.
- *
- * @params: Parameters to fill in (to be cleaned with
- * uclogic_params_cleanup()). Not modified in case of error.
- * Cannot be NULL.
- * @hdev: The HID device of the tablet interface to initialize and get
- * parameters from. Cannot be NULL.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
- struct hid_device *hdev)
- {
- int rc = 0;
- struct uclogic_drvdata *drvdata;
- struct usb_interface *iface;
- __u8 bInterfaceNumber;
- const int str_desc_len = 12;
- __u8 *str_desc = NULL;
- __u8 *rdesc_pen = NULL;
- s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
- enum uclogic_params_frame_type frame_type;
- __u8 magic_arr[] = {
- 0x02, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- /* The resulting parameters (noop) */
- struct uclogic_params p = {0, };
- if (!params || !hdev) {
- rc = -EINVAL;
- goto cleanup;
- }
- drvdata = hid_get_drvdata(hdev);
- iface = to_usb_interface(hdev->dev.parent);
- bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
- if (bInterfaceNumber == 0) {
- rc = uclogic_params_ugee_v2_init_frame_mouse(&p);
- if (rc)
- goto cleanup;
- goto output;
- }
- if (bInterfaceNumber != 2) {
- uclogic_params_init_invalid(&p);
- goto output;
- }
- /*
- * Initialize the interface by sending magic data.
- * The specific data was discovered by sniffing the Windows driver
- * traffic.
- */
- rc = uclogic_probe_interface(hdev, magic_arr, sizeof(magic_arr), 0x03);
- if (rc) {
- uclogic_params_init_invalid(&p);
- goto output;
- }
- /*
- * Read the string descriptor containing pen and frame parameters.
- * The specific string descriptor and data were discovered by sniffing
- * the Windows driver traffic.
- */
- rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len);
- if (rc != str_desc_len) {
- hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc);
- uclogic_params_init_invalid(&p);
- goto output;
- }
- rc = uclogic_params_parse_ugee_v2_desc(str_desc, str_desc_len,
- desc_params,
- ARRAY_SIZE(desc_params),
- &frame_type);
- if (rc)
- goto cleanup;
- kfree(str_desc);
- str_desc = NULL;
- /* Initialize the pen interface */
- rdesc_pen = uclogic_rdesc_template_apply(
- uclogic_rdesc_ugee_v2_pen_template_arr,
- uclogic_rdesc_ugee_v2_pen_template_size,
- desc_params, ARRAY_SIZE(desc_params));
- if (!rdesc_pen) {
- rc = -ENOMEM;
- goto cleanup;
- }
- p.pen.desc_ptr = rdesc_pen;
- p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size;
- p.pen.id = 0x02;
- p.pen.subreport_list[0].value = 0xf0;
- p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID;
- /* Initialize the frame interface */
- if (drvdata->quirks & UCLOGIC_MOUSE_FRAME_QUIRK)
- frame_type = UCLOGIC_PARAMS_FRAME_MOUSE;
- switch (frame_type) {
- case UCLOGIC_PARAMS_FRAME_DIAL:
- case UCLOGIC_PARAMS_FRAME_MOUSE:
- rc = uclogic_params_ugee_v2_init_frame_dial(&p, desc_params,
- ARRAY_SIZE(desc_params));
- break;
- case UCLOGIC_PARAMS_FRAME_BUTTONS:
- default:
- rc = uclogic_params_ugee_v2_init_frame_buttons(&p, desc_params,
- ARRAY_SIZE(desc_params));
- break;
- }
- if (rc)
- goto cleanup;
- /* Initialize the battery interface*/
- if (uclogic_params_ugee_v2_has_battery(hdev)) {
- rc = uclogic_params_ugee_v2_init_battery(hdev, &p);
- if (rc) {
- hid_err(hdev, "error initializing battery: %d\n", rc);
- goto cleanup;
- }
- }
- output:
- /* Output parameters */
- memcpy(params, &p, sizeof(*params));
- memset(&p, 0, sizeof(p));
- rc = 0;
- cleanup:
- kfree(str_desc);
- uclogic_params_cleanup(&p);
- return rc;
- }
- /**
- * uclogic_params_init() - initialize a tablet interface and discover its
- * parameters.
- *
- * @params: Parameters to fill in (to be cleaned with
- * uclogic_params_cleanup()). Not modified in case of error.
- * Cannot be NULL.
- * @hdev: The HID device of the tablet interface to initialize and get
- * parameters from. Cannot be NULL. Must be using the USB low-level
- * driver, i.e. be an actual USB tablet.
- *
- * Returns:
- * Zero, if successful. A negative errno code on error.
- */
- int uclogic_params_init(struct uclogic_params *params,
- struct hid_device *hdev)
- {
- int rc;
- struct usb_device *udev;
- __u8 bNumInterfaces;
- struct usb_interface *iface;
- __u8 bInterfaceNumber;
- bool found;
- /* The resulting parameters (noop) */
- struct uclogic_params p = {0, };
- /* Check arguments */
- if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) {
- rc = -EINVAL;
- goto cleanup;
- }
- udev = hid_to_usb_dev(hdev);
- bNumInterfaces = udev->config->desc.bNumInterfaces;
- iface = to_usb_interface(hdev->dev.parent);
- bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
- /*
- * Set replacement report descriptor if the original matches the
- * specified size. Otherwise keep interface unchanged.
- */
- #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \
- uclogic_params_init_with_opt_desc( \
- &p, hdev, \
- UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \
- uclogic_rdesc_##_new_desc_token##_arr, \
- uclogic_rdesc_##_new_desc_token##_size)
- #define VID_PID(_vid, _pid) \
- (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX))
- /*
- * Handle specific interfaces for specific tablets.
- *
- * Observe the following logic:
- *
- * If the interface is recognized as producing certain useful input:
- * Mark interface as valid.
- * Output interface parameters.
- * Else, if the interface is recognized as *not* producing any useful
- * input:
- * Mark interface as invalid.
- * Else:
- * Mark interface as valid.
- * Output noop parameters.
- *
- * Rule of thumb: it is better to disable a broken interface than let
- * it spew garbage input.
- */
- switch (VID_PID(hdev->vendor, hdev->product)) {
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_UCLOGIC_TABLET_PF1209):
- rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed);
- if (rc != 0)
- goto cleanup;
- break;
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U):
- rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed);
- if (rc != 0)
- goto cleanup;
- break;
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U):
- if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) {
- if (bInterfaceNumber == 0) {
- /* Try to probe v1 pen parameters */
- rc = uclogic_params_pen_init_v1(&p.pen,
- &found, hdev);
- if (rc != 0) {
- hid_err(hdev,
- "pen probing failed: %d\n",
- rc);
- goto cleanup;
- }
- if (!found) {
- hid_warn(hdev,
- "pen parameters not found");
- }
- } else {
- uclogic_params_init_invalid(&p);
- }
- } else {
- rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed);
- if (rc != 0)
- goto cleanup;
- }
- break;
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U):
- rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed);
- if (rc != 0)
- goto cleanup;
- break;
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_UCLOGIC_TABLET_WP1062):
- rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed);
- if (rc != 0)
- goto cleanup;
- break;
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850):
- switch (bInterfaceNumber) {
- case 0:
- rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0);
- if (rc != 0)
- goto cleanup;
- break;
- case 1:
- rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1);
- if (rc != 0)
- goto cleanup;
- break;
- case 2:
- rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2);
- if (rc != 0)
- goto cleanup;
- break;
- }
- break;
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60):
- /*
- * If it is not a three-interface version, which is known to
- * respond to initialization.
- */
- if (bNumInterfaces != 3) {
- switch (bInterfaceNumber) {
- case 0:
- rc = WITH_OPT_DESC(TWHA60_ORIG0,
- twha60_fixed0);
- if (rc != 0)
- goto cleanup;
- break;
- case 1:
- rc = WITH_OPT_DESC(TWHA60_ORIG1,
- twha60_fixed1);
- if (rc != 0)
- goto cleanup;
- break;
- }
- break;
- }
- fallthrough;
- case VID_PID(USB_VENDOR_ID_HUION,
- USB_DEVICE_ID_HUION_TABLET):
- case VID_PID(USB_VENDOR_ID_HUION,
- USB_DEVICE_ID_HUION_TABLET2):
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_HUION_TABLET):
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_YIYNOVA_TABLET):
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81):
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3):
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45):
- case VID_PID(USB_VENDOR_ID_UCLOGIC,
- USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47):
- rc = uclogic_params_huion_init(&p, hdev);
- if (rc != 0)
- goto cleanup;
- break;
- case VID_PID(USB_VENDOR_ID_UGTIZER,
- USB_DEVICE_ID_UGTIZER_TABLET_GP0610):
- case VID_PID(USB_VENDOR_ID_UGTIZER,
- USB_DEVICE_ID_UGTIZER_TABLET_GT5040):
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540):
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640):
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06):
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720):
- /* If this is the pen interface */
- if (bInterfaceNumber == 1) {
- /* Probe v1 pen parameters */
- rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
- if (rc != 0) {
- hid_err(hdev, "pen probing failed: %d\n", rc);
- goto cleanup;
- }
- if (!found) {
- hid_warn(hdev, "pen parameters not found");
- uclogic_params_init_invalid(&p);
- }
- } else {
- uclogic_params_init_invalid(&p);
- }
- break;
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01):
- /* If this is the pen and frame interface */
- if (bInterfaceNumber == 1) {
- /* Probe v1 pen parameters */
- rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
- if (rc != 0) {
- hid_err(hdev, "pen probing failed: %d\n", rc);
- goto cleanup;
- }
- /* Initialize frame parameters */
- rc = uclogic_params_frame_init_with_desc(
- &p.frame_list[0],
- uclogic_rdesc_xppen_deco01_frame_arr,
- uclogic_rdesc_xppen_deco01_frame_size,
- 0);
- if (rc != 0)
- goto cleanup;
- } else {
- uclogic_params_init_invalid(&p);
- }
- break;
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_PARBLO_A610_PRO):
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2):
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L):
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW):
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S):
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW):
- rc = uclogic_params_ugee_v2_init(&p, hdev);
- if (rc != 0)
- goto cleanup;
- break;
- case VID_PID(USB_VENDOR_ID_TRUST,
- USB_DEVICE_ID_TRUST_PANORA_TABLET):
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_TABLET_G5):
- /* Ignore non-pen interfaces */
- if (bInterfaceNumber != 1) {
- uclogic_params_init_invalid(&p);
- break;
- }
- rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
- if (rc != 0) {
- hid_err(hdev, "pen probing failed: %d\n", rc);
- goto cleanup;
- } else if (found) {
- rc = uclogic_params_frame_init_with_desc(
- &p.frame_list[0],
- uclogic_rdesc_ugee_g5_frame_arr,
- uclogic_rdesc_ugee_g5_frame_size,
- UCLOGIC_RDESC_UGEE_G5_FRAME_ID);
- if (rc != 0) {
- hid_err(hdev,
- "failed creating frame parameters: %d\n",
- rc);
- goto cleanup;
- }
- p.frame_list[0].re_lsb =
- UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB;
- p.frame_list[0].dev_id_byte =
- UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE;
- } else {
- hid_warn(hdev, "pen parameters not found");
- uclogic_params_init_invalid(&p);
- }
- break;
- case VID_PID(USB_VENDOR_ID_UGEE,
- USB_DEVICE_ID_UGEE_TABLET_EX07S):
- /* Ignore non-pen interfaces */
- if (bInterfaceNumber != 1) {
- uclogic_params_init_invalid(&p);
- break;
- }
- rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
- if (rc != 0) {
- hid_err(hdev, "pen probing failed: %d\n", rc);
- goto cleanup;
- } else if (found) {
- rc = uclogic_params_frame_init_with_desc(
- &p.frame_list[0],
- uclogic_rdesc_ugee_ex07_frame_arr,
- uclogic_rdesc_ugee_ex07_frame_size,
- 0);
- if (rc != 0) {
- hid_err(hdev,
- "failed creating frame parameters: %d\n",
- rc);
- goto cleanup;
- }
- } else {
- hid_warn(hdev, "pen parameters not found");
- uclogic_params_init_invalid(&p);
- }
- break;
- }
- #undef VID_PID
- #undef WITH_OPT_DESC
- /* Output parameters */
- memcpy(params, &p, sizeof(*params));
- memset(&p, 0, sizeof(p));
- rc = 0;
- cleanup:
- uclogic_params_cleanup(&p);
- return rc;
- }
- #ifdef CONFIG_HID_KUNIT_TEST
- #include "hid-uclogic-params-test.c"
- #endif
|