| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865 |
- // 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 <linux/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_event_hooks - free resources used by the list of raw
- * event hooks.
- * Can be called repeatedly.
- *
- * @params: Input parameters to cleanup. Cannot be NULL.
- */
- static void uclogic_params_cleanup_event_hooks(struct uclogic_params *params)
- {
- struct uclogic_raw_event_hook *curr, *n;
- if (!params || !params->event_hooks)
- return;
- list_for_each_entry_safe(curr, n, ¶ms->event_hooks->list, list) {
- cancel_work_sync(&curr->work);
- list_del(&curr->list);
- kfree(curr->event);
- kfree(curr);
- }
- kfree(params->event_hooks);
- params->event_hooks = NULL;
- }
- /**
- * 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]);
- uclogic_params_cleanup_event_hooks(params);
- 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,
- const __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,
- const __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;
- }
- /* The firmware is used in userspace as unique identifier */
- strscpy(hdev->uniq, ver_ptr, sizeof(hdev->uniq));
- /* 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, const u8 *magic_arr,
- size_t 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_reconnect_work() - When a wireless tablet looses
- * connection to the USB dongle and reconnects, either because of its physical
- * distance or because it was switches off and on using the frame's switch,
- * uclogic_probe_interface() needs to be called again to enable the tablet.
- *
- * @work: The work that triggered this function.
- */
- static void uclogic_params_ugee_v2_reconnect_work(struct work_struct *work)
- {
- struct uclogic_raw_event_hook *event_hook;
- event_hook = container_of(work, struct uclogic_raw_event_hook, work);
- uclogic_probe_interface(event_hook->hdev, uclogic_ugee_v2_probe_arr,
- uclogic_ugee_v2_probe_size,
- uclogic_ugee_v2_probe_endpoint);
- }
- /**
- * uclogic_params_ugee_v2_init_event_hooks() - initialize the list of events
- * to be hooked for UGEE v2 devices.
- * @hdev: The HID device of the tablet interface to initialize and get
- * parameters from.
- * @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_event_hooks(struct hid_device *hdev,
- struct uclogic_params *p)
- {
- struct uclogic_raw_event_hook *event_hook;
- __u8 reconnect_event[] = {
- /* Event received on wireless tablet reconnection */
- 0x02, 0xF8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- if (!p)
- return -EINVAL;
- /* The reconnection event is only received if the tablet has battery */
- if (!uclogic_params_ugee_v2_has_battery(hdev))
- return 0;
- p->event_hooks = kzalloc(sizeof(*p->event_hooks), GFP_KERNEL);
- if (!p->event_hooks)
- return -ENOMEM;
- INIT_LIST_HEAD(&p->event_hooks->list);
- event_hook = kzalloc(sizeof(*event_hook), GFP_KERNEL);
- if (!event_hook)
- return -ENOMEM;
- INIT_WORK(&event_hook->work, uclogic_params_ugee_v2_reconnect_work);
- event_hook->hdev = hdev;
- event_hook->size = ARRAY_SIZE(reconnect_event);
- event_hook->event = kmemdup(reconnect_event, event_hook->size, GFP_KERNEL);
- if (!event_hook->event)
- return -ENOMEM;
- list_add_tail(&event_hook->list, &p->event_hooks->list);
- return 0;
- }
- /**
- * 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;
- /* 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, uclogic_ugee_v2_probe_arr,
- uclogic_ugee_v2_probe_size,
- uclogic_ugee_v2_probe_endpoint);
- 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;
- }
- }
- /* Create a list of raw events to be ignored */
- rc = uclogic_params_ugee_v2_init_event_hooks(hdev, &p);
- if (rc) {
- hid_err(hdev, "error initializing event hook list: %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
|