1*ff0c13d6SNikolai Kondrashov // SPDX-License-Identifier: GPL-2.0+ 2*ff0c13d6SNikolai Kondrashov /* 3*ff0c13d6SNikolai Kondrashov * HID driver for UC-Logic devices not fully compliant with HID standard 4*ff0c13d6SNikolai Kondrashov * 5*ff0c13d6SNikolai Kondrashov * Copyright (c) 2010-2014 Nikolai Kondrashov 6*ff0c13d6SNikolai Kondrashov * Copyright (c) 2013 Martin Rusko 7*ff0c13d6SNikolai Kondrashov */ 8*ff0c13d6SNikolai Kondrashov 9*ff0c13d6SNikolai Kondrashov /* 10*ff0c13d6SNikolai Kondrashov * This program is free software; you can redistribute it and/or modify it 11*ff0c13d6SNikolai Kondrashov * under the terms of the GNU General Public License as published by the Free 12*ff0c13d6SNikolai Kondrashov * Software Foundation; either version 2 of the License, or (at your option) 13*ff0c13d6SNikolai Kondrashov * any later version. 14*ff0c13d6SNikolai Kondrashov */ 15*ff0c13d6SNikolai Kondrashov 16*ff0c13d6SNikolai Kondrashov #include <linux/device.h> 17*ff0c13d6SNikolai Kondrashov #include <linux/hid.h> 18*ff0c13d6SNikolai Kondrashov #include <linux/module.h> 19*ff0c13d6SNikolai Kondrashov #include <linux/usb.h> 20*ff0c13d6SNikolai Kondrashov #include "usbhid/usbhid.h" 21*ff0c13d6SNikolai Kondrashov #include "hid-uclogic-rdesc.h" 22*ff0c13d6SNikolai Kondrashov 23*ff0c13d6SNikolai Kondrashov #include "hid-ids.h" 24*ff0c13d6SNikolai Kondrashov 25*ff0c13d6SNikolai Kondrashov /* Parameter indices */ 26*ff0c13d6SNikolai Kondrashov enum uclogic_prm { 27*ff0c13d6SNikolai Kondrashov UCLOGIC_PRM_X_LM = 1, 28*ff0c13d6SNikolai Kondrashov UCLOGIC_PRM_Y_LM = 2, 29*ff0c13d6SNikolai Kondrashov UCLOGIC_PRM_PRESSURE_LM = 4, 30*ff0c13d6SNikolai Kondrashov UCLOGIC_PRM_RESOLUTION = 5, 31*ff0c13d6SNikolai Kondrashov UCLOGIC_PRM_NUM 32*ff0c13d6SNikolai Kondrashov }; 33*ff0c13d6SNikolai Kondrashov 34*ff0c13d6SNikolai Kondrashov /* Driver data */ 35*ff0c13d6SNikolai Kondrashov struct uclogic_drvdata { 36*ff0c13d6SNikolai Kondrashov __u8 *rdesc; 37*ff0c13d6SNikolai Kondrashov unsigned int rsize; 38*ff0c13d6SNikolai Kondrashov bool invert_pen_inrange; 39*ff0c13d6SNikolai Kondrashov bool ignore_pen_usage; 40*ff0c13d6SNikolai Kondrashov bool has_virtual_pad_interface; 41*ff0c13d6SNikolai Kondrashov }; 42*ff0c13d6SNikolai Kondrashov 43*ff0c13d6SNikolai Kondrashov static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, 44*ff0c13d6SNikolai Kondrashov unsigned int *rsize) 45*ff0c13d6SNikolai Kondrashov { 46*ff0c13d6SNikolai Kondrashov struct usb_interface *iface = to_usb_interface(hdev->dev.parent); 47*ff0c13d6SNikolai Kondrashov __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; 48*ff0c13d6SNikolai Kondrashov struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 49*ff0c13d6SNikolai Kondrashov 50*ff0c13d6SNikolai Kondrashov if (drvdata->rdesc != NULL) { 51*ff0c13d6SNikolai Kondrashov rdesc = drvdata->rdesc; 52*ff0c13d6SNikolai Kondrashov *rsize = drvdata->rsize; 53*ff0c13d6SNikolai Kondrashov return rdesc; 54*ff0c13d6SNikolai Kondrashov } 55*ff0c13d6SNikolai Kondrashov 56*ff0c13d6SNikolai Kondrashov switch (hdev->product) { 57*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: 58*ff0c13d6SNikolai Kondrashov if (*rsize == UCLOGIC_RDESC_PF1209_ORIG_SIZE) { 59*ff0c13d6SNikolai Kondrashov rdesc = uclogic_rdesc_pf1209_fixed_arr; 60*ff0c13d6SNikolai Kondrashov *rsize = uclogic_rdesc_pf1209_fixed_size; 61*ff0c13d6SNikolai Kondrashov } 62*ff0c13d6SNikolai Kondrashov break; 63*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U: 64*ff0c13d6SNikolai Kondrashov if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { 65*ff0c13d6SNikolai Kondrashov rdesc = uclogic_rdesc_wp4030u_fixed_arr; 66*ff0c13d6SNikolai Kondrashov *rsize = uclogic_rdesc_wp4030u_fixed_size; 67*ff0c13d6SNikolai Kondrashov } 68*ff0c13d6SNikolai Kondrashov break; 69*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U: 70*ff0c13d6SNikolai Kondrashov if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { 71*ff0c13d6SNikolai Kondrashov rdesc = uclogic_rdesc_wp5540u_fixed_arr; 72*ff0c13d6SNikolai Kondrashov *rsize = uclogic_rdesc_wp5540u_fixed_size; 73*ff0c13d6SNikolai Kondrashov } 74*ff0c13d6SNikolai Kondrashov break; 75*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U: 76*ff0c13d6SNikolai Kondrashov if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { 77*ff0c13d6SNikolai Kondrashov rdesc = uclogic_rdesc_wp8060u_fixed_arr; 78*ff0c13d6SNikolai Kondrashov *rsize = uclogic_rdesc_wp8060u_fixed_size; 79*ff0c13d6SNikolai Kondrashov } 80*ff0c13d6SNikolai Kondrashov break; 81*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UCLOGIC_TABLET_WP1062: 82*ff0c13d6SNikolai Kondrashov if (*rsize == UCLOGIC_RDESC_WP1062_ORIG_SIZE) { 83*ff0c13d6SNikolai Kondrashov rdesc = uclogic_rdesc_wp1062_fixed_arr; 84*ff0c13d6SNikolai Kondrashov *rsize = uclogic_rdesc_wp1062_fixed_size; 85*ff0c13d6SNikolai Kondrashov } 86*ff0c13d6SNikolai Kondrashov break; 87*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850: 88*ff0c13d6SNikolai Kondrashov switch (iface_num) { 89*ff0c13d6SNikolai Kondrashov case 0: 90*ff0c13d6SNikolai Kondrashov if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG0_SIZE) { 91*ff0c13d6SNikolai Kondrashov rdesc = uclogic_rdesc_twhl850_fixed0_arr; 92*ff0c13d6SNikolai Kondrashov *rsize = uclogic_rdesc_twhl850_fixed0_size; 93*ff0c13d6SNikolai Kondrashov } 94*ff0c13d6SNikolai Kondrashov break; 95*ff0c13d6SNikolai Kondrashov case 1: 96*ff0c13d6SNikolai Kondrashov if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG1_SIZE) { 97*ff0c13d6SNikolai Kondrashov rdesc = uclogic_rdesc_twhl850_fixed1_arr; 98*ff0c13d6SNikolai Kondrashov *rsize = uclogic_rdesc_twhl850_fixed1_size; 99*ff0c13d6SNikolai Kondrashov } 100*ff0c13d6SNikolai Kondrashov break; 101*ff0c13d6SNikolai Kondrashov case 2: 102*ff0c13d6SNikolai Kondrashov if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG2_SIZE) { 103*ff0c13d6SNikolai Kondrashov rdesc = uclogic_rdesc_twhl850_fixed2_arr; 104*ff0c13d6SNikolai Kondrashov *rsize = uclogic_rdesc_twhl850_fixed2_size; 105*ff0c13d6SNikolai Kondrashov } 106*ff0c13d6SNikolai Kondrashov break; 107*ff0c13d6SNikolai Kondrashov } 108*ff0c13d6SNikolai Kondrashov break; 109*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: 110*ff0c13d6SNikolai Kondrashov switch (iface_num) { 111*ff0c13d6SNikolai Kondrashov case 0: 112*ff0c13d6SNikolai Kondrashov if (*rsize == UCLOGIC_RDESC_TWHA60_ORIG0_SIZE) { 113*ff0c13d6SNikolai Kondrashov rdesc = uclogic_rdesc_twha60_fixed0_arr; 114*ff0c13d6SNikolai Kondrashov *rsize = uclogic_rdesc_twha60_fixed0_size; 115*ff0c13d6SNikolai Kondrashov } 116*ff0c13d6SNikolai Kondrashov break; 117*ff0c13d6SNikolai Kondrashov case 1: 118*ff0c13d6SNikolai Kondrashov if (*rsize == UCLOGIC_RDESC_TWHA60_ORIG1_SIZE) { 119*ff0c13d6SNikolai Kondrashov rdesc = uclogic_rdesc_twha60_fixed1_arr; 120*ff0c13d6SNikolai Kondrashov *rsize = uclogic_rdesc_twha60_fixed1_size; 121*ff0c13d6SNikolai Kondrashov } 122*ff0c13d6SNikolai Kondrashov break; 123*ff0c13d6SNikolai Kondrashov } 124*ff0c13d6SNikolai Kondrashov break; 125*ff0c13d6SNikolai Kondrashov } 126*ff0c13d6SNikolai Kondrashov 127*ff0c13d6SNikolai Kondrashov return rdesc; 128*ff0c13d6SNikolai Kondrashov } 129*ff0c13d6SNikolai Kondrashov 130*ff0c13d6SNikolai Kondrashov static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, 131*ff0c13d6SNikolai Kondrashov struct hid_field *field, struct hid_usage *usage, 132*ff0c13d6SNikolai Kondrashov unsigned long **bit, int *max) 133*ff0c13d6SNikolai Kondrashov { 134*ff0c13d6SNikolai Kondrashov struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 135*ff0c13d6SNikolai Kondrashov 136*ff0c13d6SNikolai Kondrashov /* discard the unused pen interface */ 137*ff0c13d6SNikolai Kondrashov if ((drvdata->ignore_pen_usage) && 138*ff0c13d6SNikolai Kondrashov (field->application == HID_DG_PEN)) 139*ff0c13d6SNikolai Kondrashov return -1; 140*ff0c13d6SNikolai Kondrashov 141*ff0c13d6SNikolai Kondrashov /* let hid-core decide what to do */ 142*ff0c13d6SNikolai Kondrashov return 0; 143*ff0c13d6SNikolai Kondrashov } 144*ff0c13d6SNikolai Kondrashov 145*ff0c13d6SNikolai Kondrashov static int uclogic_input_configured(struct hid_device *hdev, 146*ff0c13d6SNikolai Kondrashov struct hid_input *hi) 147*ff0c13d6SNikolai Kondrashov { 148*ff0c13d6SNikolai Kondrashov char *name; 149*ff0c13d6SNikolai Kondrashov const char *suffix = NULL; 150*ff0c13d6SNikolai Kondrashov struct hid_field *field; 151*ff0c13d6SNikolai Kondrashov size_t len; 152*ff0c13d6SNikolai Kondrashov 153*ff0c13d6SNikolai Kondrashov /* no report associated (HID_QUIRK_MULTI_INPUT not set) */ 154*ff0c13d6SNikolai Kondrashov if (!hi->report) 155*ff0c13d6SNikolai Kondrashov return 0; 156*ff0c13d6SNikolai Kondrashov 157*ff0c13d6SNikolai Kondrashov field = hi->report->field[0]; 158*ff0c13d6SNikolai Kondrashov 159*ff0c13d6SNikolai Kondrashov switch (field->application) { 160*ff0c13d6SNikolai Kondrashov case HID_GD_KEYBOARD: 161*ff0c13d6SNikolai Kondrashov suffix = "Keyboard"; 162*ff0c13d6SNikolai Kondrashov break; 163*ff0c13d6SNikolai Kondrashov case HID_GD_MOUSE: 164*ff0c13d6SNikolai Kondrashov suffix = "Mouse"; 165*ff0c13d6SNikolai Kondrashov break; 166*ff0c13d6SNikolai Kondrashov case HID_GD_KEYPAD: 167*ff0c13d6SNikolai Kondrashov suffix = "Pad"; 168*ff0c13d6SNikolai Kondrashov break; 169*ff0c13d6SNikolai Kondrashov case HID_DG_PEN: 170*ff0c13d6SNikolai Kondrashov suffix = "Pen"; 171*ff0c13d6SNikolai Kondrashov break; 172*ff0c13d6SNikolai Kondrashov case HID_CP_CONSUMER_CONTROL: 173*ff0c13d6SNikolai Kondrashov suffix = "Consumer Control"; 174*ff0c13d6SNikolai Kondrashov break; 175*ff0c13d6SNikolai Kondrashov case HID_GD_SYSTEM_CONTROL: 176*ff0c13d6SNikolai Kondrashov suffix = "System Control"; 177*ff0c13d6SNikolai Kondrashov break; 178*ff0c13d6SNikolai Kondrashov } 179*ff0c13d6SNikolai Kondrashov 180*ff0c13d6SNikolai Kondrashov if (suffix) { 181*ff0c13d6SNikolai Kondrashov len = strlen(hdev->name) + 2 + strlen(suffix); 182*ff0c13d6SNikolai Kondrashov name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL); 183*ff0c13d6SNikolai Kondrashov if (name) { 184*ff0c13d6SNikolai Kondrashov snprintf(name, len, "%s %s", hdev->name, suffix); 185*ff0c13d6SNikolai Kondrashov hi->input->name = name; 186*ff0c13d6SNikolai Kondrashov } 187*ff0c13d6SNikolai Kondrashov } 188*ff0c13d6SNikolai Kondrashov 189*ff0c13d6SNikolai Kondrashov return 0; 190*ff0c13d6SNikolai Kondrashov } 191*ff0c13d6SNikolai Kondrashov 192*ff0c13d6SNikolai Kondrashov /** 193*ff0c13d6SNikolai Kondrashov * Enable fully-functional tablet mode and determine device parameters. 194*ff0c13d6SNikolai Kondrashov * 195*ff0c13d6SNikolai Kondrashov * @hdev: HID device 196*ff0c13d6SNikolai Kondrashov */ 197*ff0c13d6SNikolai Kondrashov static int uclogic_tablet_enable(struct hid_device *hdev) 198*ff0c13d6SNikolai Kondrashov { 199*ff0c13d6SNikolai Kondrashov int rc; 200*ff0c13d6SNikolai Kondrashov struct usb_device *usb_dev = hid_to_usb_dev(hdev); 201*ff0c13d6SNikolai Kondrashov struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 202*ff0c13d6SNikolai Kondrashov __le16 *buf = NULL; 203*ff0c13d6SNikolai Kondrashov size_t len; 204*ff0c13d6SNikolai Kondrashov s32 params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; 205*ff0c13d6SNikolai Kondrashov s32 resolution; 206*ff0c13d6SNikolai Kondrashov 207*ff0c13d6SNikolai Kondrashov /* 208*ff0c13d6SNikolai Kondrashov * Read string descriptor containing tablet parameters. The specific 209*ff0c13d6SNikolai Kondrashov * string descriptor and data were discovered by sniffing the Windows 210*ff0c13d6SNikolai Kondrashov * driver traffic. 211*ff0c13d6SNikolai Kondrashov * NOTE: This enables fully-functional tablet mode. 212*ff0c13d6SNikolai Kondrashov */ 213*ff0c13d6SNikolai Kondrashov len = UCLOGIC_PRM_NUM * sizeof(*buf); 214*ff0c13d6SNikolai Kondrashov buf = kmalloc(len, GFP_KERNEL); 215*ff0c13d6SNikolai Kondrashov if (buf == NULL) { 216*ff0c13d6SNikolai Kondrashov rc = -ENOMEM; 217*ff0c13d6SNikolai Kondrashov goto cleanup; 218*ff0c13d6SNikolai Kondrashov } 219*ff0c13d6SNikolai Kondrashov rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), 220*ff0c13d6SNikolai Kondrashov USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, 221*ff0c13d6SNikolai Kondrashov (USB_DT_STRING << 8) + 0x64, 222*ff0c13d6SNikolai Kondrashov 0x0409, buf, len, 223*ff0c13d6SNikolai Kondrashov USB_CTRL_GET_TIMEOUT); 224*ff0c13d6SNikolai Kondrashov if (rc == -EPIPE) { 225*ff0c13d6SNikolai Kondrashov hid_err(hdev, "device parameters not found\n"); 226*ff0c13d6SNikolai Kondrashov rc = -ENODEV; 227*ff0c13d6SNikolai Kondrashov goto cleanup; 228*ff0c13d6SNikolai Kondrashov } else if (rc < 0) { 229*ff0c13d6SNikolai Kondrashov hid_err(hdev, "failed to get device parameters: %d\n", rc); 230*ff0c13d6SNikolai Kondrashov rc = -ENODEV; 231*ff0c13d6SNikolai Kondrashov goto cleanup; 232*ff0c13d6SNikolai Kondrashov } else if (rc != len) { 233*ff0c13d6SNikolai Kondrashov hid_err(hdev, "invalid device parameters\n"); 234*ff0c13d6SNikolai Kondrashov rc = -ENODEV; 235*ff0c13d6SNikolai Kondrashov goto cleanup; 236*ff0c13d6SNikolai Kondrashov } 237*ff0c13d6SNikolai Kondrashov 238*ff0c13d6SNikolai Kondrashov /* Extract device parameters */ 239*ff0c13d6SNikolai Kondrashov params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 240*ff0c13d6SNikolai Kondrashov le16_to_cpu(buf[UCLOGIC_PRM_X_LM]); 241*ff0c13d6SNikolai Kondrashov params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 242*ff0c13d6SNikolai Kondrashov le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]); 243*ff0c13d6SNikolai Kondrashov params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 244*ff0c13d6SNikolai Kondrashov le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]); 245*ff0c13d6SNikolai Kondrashov resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]); 246*ff0c13d6SNikolai Kondrashov if (resolution == 0) { 247*ff0c13d6SNikolai Kondrashov params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 248*ff0c13d6SNikolai Kondrashov params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 249*ff0c13d6SNikolai Kondrashov } else { 250*ff0c13d6SNikolai Kondrashov params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 251*ff0c13d6SNikolai Kondrashov params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 252*ff0c13d6SNikolai Kondrashov 1000 / resolution; 253*ff0c13d6SNikolai Kondrashov params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 254*ff0c13d6SNikolai Kondrashov params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 255*ff0c13d6SNikolai Kondrashov 1000 / resolution; 256*ff0c13d6SNikolai Kondrashov } 257*ff0c13d6SNikolai Kondrashov 258*ff0c13d6SNikolai Kondrashov /* Format fixed report descriptor */ 259*ff0c13d6SNikolai Kondrashov drvdata->rdesc = uclogic_rdesc_template_apply( 260*ff0c13d6SNikolai Kondrashov uclogic_rdesc_pen_template_arr, 261*ff0c13d6SNikolai Kondrashov uclogic_rdesc_pen_template_size, 262*ff0c13d6SNikolai Kondrashov params, ARRAY_SIZE(params)); 263*ff0c13d6SNikolai Kondrashov if (drvdata->rdesc == NULL) { 264*ff0c13d6SNikolai Kondrashov rc = -ENOMEM; 265*ff0c13d6SNikolai Kondrashov goto cleanup; 266*ff0c13d6SNikolai Kondrashov } 267*ff0c13d6SNikolai Kondrashov drvdata->rsize = uclogic_rdesc_pen_template_size; 268*ff0c13d6SNikolai Kondrashov 269*ff0c13d6SNikolai Kondrashov rc = 0; 270*ff0c13d6SNikolai Kondrashov 271*ff0c13d6SNikolai Kondrashov cleanup: 272*ff0c13d6SNikolai Kondrashov kfree(buf); 273*ff0c13d6SNikolai Kondrashov return rc; 274*ff0c13d6SNikolai Kondrashov } 275*ff0c13d6SNikolai Kondrashov 276*ff0c13d6SNikolai Kondrashov /** 277*ff0c13d6SNikolai Kondrashov * Enable actual button mode. 278*ff0c13d6SNikolai Kondrashov * 279*ff0c13d6SNikolai Kondrashov * @hdev: HID device 280*ff0c13d6SNikolai Kondrashov */ 281*ff0c13d6SNikolai Kondrashov static int uclogic_button_enable(struct hid_device *hdev) 282*ff0c13d6SNikolai Kondrashov { 283*ff0c13d6SNikolai Kondrashov int rc; 284*ff0c13d6SNikolai Kondrashov struct usb_device *usb_dev = hid_to_usb_dev(hdev); 285*ff0c13d6SNikolai Kondrashov struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 286*ff0c13d6SNikolai Kondrashov char *str_buf; 287*ff0c13d6SNikolai Kondrashov size_t str_len = 16; 288*ff0c13d6SNikolai Kondrashov unsigned char *rdesc; 289*ff0c13d6SNikolai Kondrashov size_t rdesc_len; 290*ff0c13d6SNikolai Kondrashov 291*ff0c13d6SNikolai Kondrashov str_buf = kzalloc(str_len, GFP_KERNEL); 292*ff0c13d6SNikolai Kondrashov if (str_buf == NULL) { 293*ff0c13d6SNikolai Kondrashov rc = -ENOMEM; 294*ff0c13d6SNikolai Kondrashov goto cleanup; 295*ff0c13d6SNikolai Kondrashov } 296*ff0c13d6SNikolai Kondrashov 297*ff0c13d6SNikolai Kondrashov /* Enable abstract keyboard mode */ 298*ff0c13d6SNikolai Kondrashov rc = usb_string(usb_dev, 0x7b, str_buf, str_len); 299*ff0c13d6SNikolai Kondrashov if (rc == -EPIPE) { 300*ff0c13d6SNikolai Kondrashov hid_info(hdev, "button mode setting not found\n"); 301*ff0c13d6SNikolai Kondrashov rc = 0; 302*ff0c13d6SNikolai Kondrashov goto cleanup; 303*ff0c13d6SNikolai Kondrashov } else if (rc < 0) { 304*ff0c13d6SNikolai Kondrashov hid_err(hdev, "failed to enable abstract keyboard\n"); 305*ff0c13d6SNikolai Kondrashov goto cleanup; 306*ff0c13d6SNikolai Kondrashov } else if (strncmp(str_buf, "HK On", rc)) { 307*ff0c13d6SNikolai Kondrashov hid_info(hdev, "invalid answer when requesting buttons: '%s'\n", 308*ff0c13d6SNikolai Kondrashov str_buf); 309*ff0c13d6SNikolai Kondrashov rc = -EINVAL; 310*ff0c13d6SNikolai Kondrashov goto cleanup; 311*ff0c13d6SNikolai Kondrashov } 312*ff0c13d6SNikolai Kondrashov 313*ff0c13d6SNikolai Kondrashov /* Re-allocate fixed report descriptor */ 314*ff0c13d6SNikolai Kondrashov rdesc_len = drvdata->rsize + uclogic_rdesc_buttonpad_size; 315*ff0c13d6SNikolai Kondrashov rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL); 316*ff0c13d6SNikolai Kondrashov if (!rdesc) { 317*ff0c13d6SNikolai Kondrashov rc = -ENOMEM; 318*ff0c13d6SNikolai Kondrashov goto cleanup; 319*ff0c13d6SNikolai Kondrashov } 320*ff0c13d6SNikolai Kondrashov 321*ff0c13d6SNikolai Kondrashov memcpy(rdesc, drvdata->rdesc, drvdata->rsize); 322*ff0c13d6SNikolai Kondrashov 323*ff0c13d6SNikolai Kondrashov /* Append the buttonpad descriptor */ 324*ff0c13d6SNikolai Kondrashov memcpy(rdesc + drvdata->rsize, uclogic_rdesc_buttonpad_arr, 325*ff0c13d6SNikolai Kondrashov uclogic_rdesc_buttonpad_size); 326*ff0c13d6SNikolai Kondrashov 327*ff0c13d6SNikolai Kondrashov /* clean up old rdesc and use the new one */ 328*ff0c13d6SNikolai Kondrashov drvdata->rsize = rdesc_len; 329*ff0c13d6SNikolai Kondrashov devm_kfree(&hdev->dev, drvdata->rdesc); 330*ff0c13d6SNikolai Kondrashov drvdata->rdesc = rdesc; 331*ff0c13d6SNikolai Kondrashov 332*ff0c13d6SNikolai Kondrashov rc = 0; 333*ff0c13d6SNikolai Kondrashov 334*ff0c13d6SNikolai Kondrashov cleanup: 335*ff0c13d6SNikolai Kondrashov kfree(str_buf); 336*ff0c13d6SNikolai Kondrashov return rc; 337*ff0c13d6SNikolai Kondrashov } 338*ff0c13d6SNikolai Kondrashov 339*ff0c13d6SNikolai Kondrashov static int uclogic_probe(struct hid_device *hdev, 340*ff0c13d6SNikolai Kondrashov const struct hid_device_id *id) 341*ff0c13d6SNikolai Kondrashov { 342*ff0c13d6SNikolai Kondrashov int rc; 343*ff0c13d6SNikolai Kondrashov struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 344*ff0c13d6SNikolai Kondrashov struct usb_device *udev = hid_to_usb_dev(hdev); 345*ff0c13d6SNikolai Kondrashov struct uclogic_drvdata *drvdata; 346*ff0c13d6SNikolai Kondrashov 347*ff0c13d6SNikolai Kondrashov /* 348*ff0c13d6SNikolai Kondrashov * libinput requires the pad interface to be on a different node 349*ff0c13d6SNikolai Kondrashov * than the pen, so use QUIRK_MULTI_INPUT for all tablets. 350*ff0c13d6SNikolai Kondrashov */ 351*ff0c13d6SNikolai Kondrashov hdev->quirks |= HID_QUIRK_MULTI_INPUT; 352*ff0c13d6SNikolai Kondrashov 353*ff0c13d6SNikolai Kondrashov /* Allocate and assign driver data */ 354*ff0c13d6SNikolai Kondrashov drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); 355*ff0c13d6SNikolai Kondrashov if (drvdata == NULL) 356*ff0c13d6SNikolai Kondrashov return -ENOMEM; 357*ff0c13d6SNikolai Kondrashov 358*ff0c13d6SNikolai Kondrashov hid_set_drvdata(hdev, drvdata); 359*ff0c13d6SNikolai Kondrashov 360*ff0c13d6SNikolai Kondrashov switch (id->product) { 361*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_HUION_TABLET: 362*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_YIYNOVA_TABLET: 363*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81: 364*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3: 365*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45: 366*ff0c13d6SNikolai Kondrashov /* If this is the pen interface */ 367*ff0c13d6SNikolai Kondrashov if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { 368*ff0c13d6SNikolai Kondrashov rc = uclogic_tablet_enable(hdev); 369*ff0c13d6SNikolai Kondrashov if (rc) { 370*ff0c13d6SNikolai Kondrashov hid_err(hdev, "tablet enabling failed\n"); 371*ff0c13d6SNikolai Kondrashov return rc; 372*ff0c13d6SNikolai Kondrashov } 373*ff0c13d6SNikolai Kondrashov drvdata->invert_pen_inrange = true; 374*ff0c13d6SNikolai Kondrashov 375*ff0c13d6SNikolai Kondrashov rc = uclogic_button_enable(hdev); 376*ff0c13d6SNikolai Kondrashov drvdata->has_virtual_pad_interface = !rc; 377*ff0c13d6SNikolai Kondrashov } else { 378*ff0c13d6SNikolai Kondrashov drvdata->ignore_pen_usage = true; 379*ff0c13d6SNikolai Kondrashov } 380*ff0c13d6SNikolai Kondrashov break; 381*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UGTIZER_TABLET_GP0610: 382*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UGEE_TABLET_EX07S: 383*ff0c13d6SNikolai Kondrashov /* If this is the pen interface */ 384*ff0c13d6SNikolai Kondrashov if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { 385*ff0c13d6SNikolai Kondrashov rc = uclogic_tablet_enable(hdev); 386*ff0c13d6SNikolai Kondrashov if (rc) { 387*ff0c13d6SNikolai Kondrashov hid_err(hdev, "tablet enabling failed\n"); 388*ff0c13d6SNikolai Kondrashov return rc; 389*ff0c13d6SNikolai Kondrashov } 390*ff0c13d6SNikolai Kondrashov drvdata->invert_pen_inrange = true; 391*ff0c13d6SNikolai Kondrashov } else { 392*ff0c13d6SNikolai Kondrashov drvdata->ignore_pen_usage = true; 393*ff0c13d6SNikolai Kondrashov } 394*ff0c13d6SNikolai Kondrashov break; 395*ff0c13d6SNikolai Kondrashov case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: 396*ff0c13d6SNikolai Kondrashov /* 397*ff0c13d6SNikolai Kondrashov * If it is the three-interface version, which is known to 398*ff0c13d6SNikolai Kondrashov * respond to initialization. 399*ff0c13d6SNikolai Kondrashov */ 400*ff0c13d6SNikolai Kondrashov if (udev->config->desc.bNumInterfaces == 3) { 401*ff0c13d6SNikolai Kondrashov /* If it is the pen interface */ 402*ff0c13d6SNikolai Kondrashov if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { 403*ff0c13d6SNikolai Kondrashov rc = uclogic_tablet_enable(hdev); 404*ff0c13d6SNikolai Kondrashov if (rc) { 405*ff0c13d6SNikolai Kondrashov hid_err(hdev, "tablet enabling failed\n"); 406*ff0c13d6SNikolai Kondrashov return rc; 407*ff0c13d6SNikolai Kondrashov } 408*ff0c13d6SNikolai Kondrashov drvdata->invert_pen_inrange = true; 409*ff0c13d6SNikolai Kondrashov 410*ff0c13d6SNikolai Kondrashov rc = uclogic_button_enable(hdev); 411*ff0c13d6SNikolai Kondrashov drvdata->has_virtual_pad_interface = !rc; 412*ff0c13d6SNikolai Kondrashov } else { 413*ff0c13d6SNikolai Kondrashov drvdata->ignore_pen_usage = true; 414*ff0c13d6SNikolai Kondrashov } 415*ff0c13d6SNikolai Kondrashov } 416*ff0c13d6SNikolai Kondrashov break; 417*ff0c13d6SNikolai Kondrashov } 418*ff0c13d6SNikolai Kondrashov 419*ff0c13d6SNikolai Kondrashov rc = hid_parse(hdev); 420*ff0c13d6SNikolai Kondrashov if (rc) { 421*ff0c13d6SNikolai Kondrashov hid_err(hdev, "parse failed\n"); 422*ff0c13d6SNikolai Kondrashov return rc; 423*ff0c13d6SNikolai Kondrashov } 424*ff0c13d6SNikolai Kondrashov 425*ff0c13d6SNikolai Kondrashov rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 426*ff0c13d6SNikolai Kondrashov if (rc) { 427*ff0c13d6SNikolai Kondrashov hid_err(hdev, "hw start failed\n"); 428*ff0c13d6SNikolai Kondrashov return rc; 429*ff0c13d6SNikolai Kondrashov } 430*ff0c13d6SNikolai Kondrashov 431*ff0c13d6SNikolai Kondrashov return 0; 432*ff0c13d6SNikolai Kondrashov } 433*ff0c13d6SNikolai Kondrashov 434*ff0c13d6SNikolai Kondrashov static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, 435*ff0c13d6SNikolai Kondrashov u8 *data, int size) 436*ff0c13d6SNikolai Kondrashov { 437*ff0c13d6SNikolai Kondrashov struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 438*ff0c13d6SNikolai Kondrashov 439*ff0c13d6SNikolai Kondrashov if ((report->type == HID_INPUT_REPORT) && 440*ff0c13d6SNikolai Kondrashov (report->id == UCLOGIC_RDESC_PEN_ID) && 441*ff0c13d6SNikolai Kondrashov (size >= 2)) { 442*ff0c13d6SNikolai Kondrashov if (drvdata->has_virtual_pad_interface && (data[1] & 0x20)) 443*ff0c13d6SNikolai Kondrashov /* Change to virtual frame button report ID */ 444*ff0c13d6SNikolai Kondrashov data[0] = 0xf7; 445*ff0c13d6SNikolai Kondrashov else if (drvdata->invert_pen_inrange) 446*ff0c13d6SNikolai Kondrashov /* Invert the in-range bit */ 447*ff0c13d6SNikolai Kondrashov data[1] ^= 0x40; 448*ff0c13d6SNikolai Kondrashov } 449*ff0c13d6SNikolai Kondrashov 450*ff0c13d6SNikolai Kondrashov return 0; 451*ff0c13d6SNikolai Kondrashov } 452*ff0c13d6SNikolai Kondrashov 453*ff0c13d6SNikolai Kondrashov static const struct hid_device_id uclogic_devices[] = { 454*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 455*ff0c13d6SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, 456*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 457*ff0c13d6SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) }, 458*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 459*ff0c13d6SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) }, 460*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 461*ff0c13d6SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, 462*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 463*ff0c13d6SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, 464*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 465*ff0c13d6SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, 466*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 467*ff0c13d6SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, 468*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, 469*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, 470*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, 471*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, 472*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, 473*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, 474*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, 475*ff0c13d6SNikolai Kondrashov { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, 476*ff0c13d6SNikolai Kondrashov { } 477*ff0c13d6SNikolai Kondrashov }; 478*ff0c13d6SNikolai Kondrashov MODULE_DEVICE_TABLE(hid, uclogic_devices); 479*ff0c13d6SNikolai Kondrashov 480*ff0c13d6SNikolai Kondrashov static struct hid_driver uclogic_driver = { 481*ff0c13d6SNikolai Kondrashov .name = "uclogic", 482*ff0c13d6SNikolai Kondrashov .id_table = uclogic_devices, 483*ff0c13d6SNikolai Kondrashov .probe = uclogic_probe, 484*ff0c13d6SNikolai Kondrashov .report_fixup = uclogic_report_fixup, 485*ff0c13d6SNikolai Kondrashov .raw_event = uclogic_raw_event, 486*ff0c13d6SNikolai Kondrashov .input_mapping = uclogic_input_mapping, 487*ff0c13d6SNikolai Kondrashov .input_configured = uclogic_input_configured, 488*ff0c13d6SNikolai Kondrashov }; 489*ff0c13d6SNikolai Kondrashov module_hid_driver(uclogic_driver); 490*ff0c13d6SNikolai Kondrashov 491*ff0c13d6SNikolai Kondrashov MODULE_AUTHOR("Martin Rusko"); 492*ff0c13d6SNikolai Kondrashov MODULE_AUTHOR("Nikolai Kondrashov"); 493*ff0c13d6SNikolai Kondrashov MODULE_LICENSE("GPL"); 494