162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2021 Hans de Goede <hdegoede@redhat.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Driver for the LetSketch / VSON WP9620N drawing tablet. 662306a36Sopenharmony_ci * This drawing tablet is also sold under other brand names such as Case U, 762306a36Sopenharmony_ci * presumably this driver will work for all of them. But it has only been 862306a36Sopenharmony_ci * tested with a LetSketch WP9620N model. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * These tablets also work without a special HID driver, but then only part 1162306a36Sopenharmony_ci * of the active area works and both the pad and stylus buttons are hardwired 1262306a36Sopenharmony_ci * to special key-combos. E.g. the 2 stylus buttons send right mouse clicks / 1362306a36Sopenharmony_ci * resp. "e" key presses. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * This device has 4 USB interfaces: 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Interface 0 EP 0x81 bootclass mouse, rdesc len 18, report id 0x08, 1862306a36Sopenharmony_ci * Application(ff00.0001) 1962306a36Sopenharmony_ci * This interface sends raw event input reports in a custom format, but only 2062306a36Sopenharmony_ci * after doing the special dance from letsketch_probe(). After enabling this 2162306a36Sopenharmony_ci * interface the other 3 interfaces are disabled. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Interface 1 EP 0x82 bootclass mouse, rdesc len 83, report id 0x0a, Tablet 2462306a36Sopenharmony_ci * This interface sends absolute events for the pen, including pressure, 2562306a36Sopenharmony_ci * but only for some part of the active area due to special "aspect ratio" 2662306a36Sopenharmony_ci * correction and only half by default since it assumes it will be used 2762306a36Sopenharmony_ci * with a phone in portraid mode, while using the tablet in landscape mode. 2862306a36Sopenharmony_ci * Also stylus + pad button events are not reported here. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * Interface 2 EP 0x83 bootclass keybd, rdesc len 64, report id none, Std Kbd 3162306a36Sopenharmony_ci * This interfaces send various hard-coded key-combos for the pad buttons 3262306a36Sopenharmony_ci * and "e" keypresses for the 2nd stylus button 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Interface 3 EP 0x84 bootclass mouse, rdesc len 75, report id 0x01, Std Mouse 3562306a36Sopenharmony_ci * This reports right-click mouse-button events for the 1st stylus button 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci#include <linux/device.h> 3862306a36Sopenharmony_ci#include <linux/input.h> 3962306a36Sopenharmony_ci#include <linux/hid.h> 4062306a36Sopenharmony_ci#include <linux/module.h> 4162306a36Sopenharmony_ci#include <linux/timer.h> 4262306a36Sopenharmony_ci#include <linux/usb.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include <asm/unaligned.h> 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#include "hid-ids.h" 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define LETSKETCH_RAW_IF 0 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define LETSKETCH_RAW_DATA_LEN 12 5162306a36Sopenharmony_ci#define LETSKETCH_RAW_REPORT_ID 8 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define LETSKETCH_PAD_BUTTONS 5 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define LETSKETCH_INFO_STR_IDX_BEGIN 0xc8 5662306a36Sopenharmony_ci#define LETSKETCH_INFO_STR_IDX_END 0xca 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define LETSKETCH_GET_STRING_RETRIES 5 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct letsketch_data { 6162306a36Sopenharmony_ci struct hid_device *hdev; 6262306a36Sopenharmony_ci struct input_dev *input_tablet; 6362306a36Sopenharmony_ci struct input_dev *input_tablet_pad; 6462306a36Sopenharmony_ci struct timer_list inrange_timer; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int letsketch_open(struct input_dev *dev) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct letsketch_data *data = input_get_drvdata(dev); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return hid_hw_open(data->hdev); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void letsketch_close(struct input_dev *dev) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct letsketch_data *data = input_get_drvdata(dev); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci hid_hw_close(data->hdev); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic struct input_dev *letsketch_alloc_input_dev(struct letsketch_data *data) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct input_dev *input; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci input = devm_input_allocate_device(&data->hdev->dev); 8662306a36Sopenharmony_ci if (!input) 8762306a36Sopenharmony_ci return NULL; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci input->id.bustype = data->hdev->bus; 9062306a36Sopenharmony_ci input->id.vendor = data->hdev->vendor; 9162306a36Sopenharmony_ci input->id.product = data->hdev->product; 9262306a36Sopenharmony_ci input->id.version = data->hdev->bus; 9362306a36Sopenharmony_ci input->phys = data->hdev->phys; 9462306a36Sopenharmony_ci input->uniq = data->hdev->uniq; 9562306a36Sopenharmony_ci input->open = letsketch_open; 9662306a36Sopenharmony_ci input->close = letsketch_close; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci input_set_drvdata(input, data); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return input; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int letsketch_setup_input_tablet(struct letsketch_data *data) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct input_dev *input; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci input = letsketch_alloc_input_dev(data); 10862306a36Sopenharmony_ci if (!input) 10962306a36Sopenharmony_ci return -ENOMEM; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci input_set_abs_params(input, ABS_X, 0, 50800, 0, 0); 11262306a36Sopenharmony_ci input_set_abs_params(input, ABS_Y, 0, 31750, 0, 0); 11362306a36Sopenharmony_ci input_set_abs_params(input, ABS_PRESSURE, 0, 8192, 0, 0); 11462306a36Sopenharmony_ci input_abs_set_res(input, ABS_X, 240); 11562306a36Sopenharmony_ci input_abs_set_res(input, ABS_Y, 225); 11662306a36Sopenharmony_ci input_set_capability(input, EV_KEY, BTN_TOUCH); 11762306a36Sopenharmony_ci input_set_capability(input, EV_KEY, BTN_TOOL_PEN); 11862306a36Sopenharmony_ci input_set_capability(input, EV_KEY, BTN_STYLUS); 11962306a36Sopenharmony_ci input_set_capability(input, EV_KEY, BTN_STYLUS2); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* All known brands selling this tablet use WP9620[N] as model name */ 12262306a36Sopenharmony_ci input->name = "WP9620 Tablet"; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci data->input_tablet = input; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return input_register_device(data->input_tablet); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int letsketch_setup_input_tablet_pad(struct letsketch_data *data) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct input_dev *input; 13262306a36Sopenharmony_ci int i; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci input = letsketch_alloc_input_dev(data); 13562306a36Sopenharmony_ci if (!input) 13662306a36Sopenharmony_ci return -ENOMEM; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci for (i = 0; i < LETSKETCH_PAD_BUTTONS; i++) 13962306a36Sopenharmony_ci input_set_capability(input, EV_KEY, BTN_0 + i); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * These are never send on the pad input_dev, but must be set 14362306a36Sopenharmony_ci * on the Pad to make udev / libwacom happy. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci input_set_abs_params(input, ABS_X, 0, 1, 0, 0); 14662306a36Sopenharmony_ci input_set_abs_params(input, ABS_Y, 0, 1, 0, 0); 14762306a36Sopenharmony_ci input_set_capability(input, EV_KEY, BTN_STYLUS); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci input->name = "WP9620 Pad"; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci data->input_tablet_pad = input; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return input_register_device(data->input_tablet_pad); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void letsketch_inrange_timeout(struct timer_list *t) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct letsketch_data *data = from_timer(data, t, inrange_timer); 15962306a36Sopenharmony_ci struct input_dev *input = data->input_tablet; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci input_report_key(input, BTN_TOOL_PEN, 0); 16262306a36Sopenharmony_ci input_sync(input); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int letsketch_raw_event(struct hid_device *hdev, 16662306a36Sopenharmony_ci struct hid_report *report, 16762306a36Sopenharmony_ci u8 *raw_data, int size) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct letsketch_data *data = hid_get_drvdata(hdev); 17062306a36Sopenharmony_ci struct input_dev *input; 17162306a36Sopenharmony_ci int i; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (size != LETSKETCH_RAW_DATA_LEN || raw_data[0] != LETSKETCH_RAW_REPORT_ID) 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci switch (raw_data[1] & 0xf0) { 17762306a36Sopenharmony_ci case 0x80: /* Pen data */ 17862306a36Sopenharmony_ci input = data->input_tablet; 17962306a36Sopenharmony_ci input_report_key(input, BTN_TOOL_PEN, 1); 18062306a36Sopenharmony_ci input_report_key(input, BTN_TOUCH, raw_data[1] & 0x01); 18162306a36Sopenharmony_ci input_report_key(input, BTN_STYLUS, raw_data[1] & 0x02); 18262306a36Sopenharmony_ci input_report_key(input, BTN_STYLUS2, raw_data[1] & 0x04); 18362306a36Sopenharmony_ci input_report_abs(input, ABS_X, 18462306a36Sopenharmony_ci get_unaligned_le16(raw_data + 2)); 18562306a36Sopenharmony_ci input_report_abs(input, ABS_Y, 18662306a36Sopenharmony_ci get_unaligned_le16(raw_data + 4)); 18762306a36Sopenharmony_ci input_report_abs(input, ABS_PRESSURE, 18862306a36Sopenharmony_ci get_unaligned_le16(raw_data + 6)); 18962306a36Sopenharmony_ci /* 19062306a36Sopenharmony_ci * There is no out of range event, so use a timer for this 19162306a36Sopenharmony_ci * when in range we get an event approx. every 8 ms. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci mod_timer(&data->inrange_timer, jiffies + msecs_to_jiffies(100)); 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci case 0xe0: /* Pad data */ 19662306a36Sopenharmony_ci input = data->input_tablet_pad; 19762306a36Sopenharmony_ci for (i = 0; i < LETSKETCH_PAD_BUTTONS; i++) 19862306a36Sopenharmony_ci input_report_key(input, BTN_0 + i, raw_data[4] == (i + 1)); 19962306a36Sopenharmony_ci break; 20062306a36Sopenharmony_ci default: 20162306a36Sopenharmony_ci hid_warn(data->hdev, "Warning unknown data header: 0x%02x\n", 20262306a36Sopenharmony_ci raw_data[0]); 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci input_sync(input); 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* 21162306a36Sopenharmony_ci * The tablets magic handshake to put it in raw mode relies on getting 21262306a36Sopenharmony_ci * string descriptors. But the firmware is buggy and does not like it if 21362306a36Sopenharmony_ci * we do this too fast. Even if we go slow sometimes the usb_string() call 21462306a36Sopenharmony_ci * fails. Ignore errors and retry it a couple of times if necessary. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic int letsketch_get_string(struct usb_device *udev, int index, char *buf, int size) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci int i, ret; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci for (i = 0; i < LETSKETCH_GET_STRING_RETRIES; i++) { 22162306a36Sopenharmony_ci usleep_range(5000, 7000); 22262306a36Sopenharmony_ci ret = usb_string(udev, index, buf, size); 22362306a36Sopenharmony_ci if (ret > 0) 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci dev_err(&udev->dev, "Max retries (%d) exceeded reading string descriptor %d\n", 22862306a36Sopenharmony_ci LETSKETCH_GET_STRING_RETRIES, index); 22962306a36Sopenharmony_ci return ret ? ret : -EIO; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int letsketch_probe(struct hid_device *hdev, const struct hid_device_id *id) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct device *dev = &hdev->dev; 23562306a36Sopenharmony_ci struct letsketch_data *data; 23662306a36Sopenharmony_ci struct usb_interface *intf; 23762306a36Sopenharmony_ci struct usb_device *udev; 23862306a36Sopenharmony_ci char buf[256]; 23962306a36Sopenharmony_ci int i, ret; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!hid_is_usb(hdev)) 24262306a36Sopenharmony_ci return -ENODEV; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci intf = to_usb_interface(hdev->dev.parent); 24562306a36Sopenharmony_ci if (intf->altsetting->desc.bInterfaceNumber != LETSKETCH_RAW_IF) 24662306a36Sopenharmony_ci return -ENODEV; /* Ignore the other interfaces */ 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci udev = interface_to_usbdev(intf); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * Instead of using a set-feature request, or even a custom USB ctrl 25262306a36Sopenharmony_ci * message the tablet needs this elaborate magic reading of USB 25362306a36Sopenharmony_ci * string descriptors to kick it into raw mode. This is what the 25462306a36Sopenharmony_ci * Windows drivers are seen doing in an USB trace under Windows. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci for (i = LETSKETCH_INFO_STR_IDX_BEGIN; i <= LETSKETCH_INFO_STR_IDX_END; i++) { 25762306a36Sopenharmony_ci ret = letsketch_get_string(udev, i, buf, sizeof(buf)); 25862306a36Sopenharmony_ci if (ret) 25962306a36Sopenharmony_ci return ret; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci hid_info(hdev, "Device info: %s\n", buf); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for (i = 1; i <= 250; i++) { 26562306a36Sopenharmony_ci ret = letsketch_get_string(udev, i, buf, sizeof(buf)); 26662306a36Sopenharmony_ci if (ret) 26762306a36Sopenharmony_ci return ret; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci ret = letsketch_get_string(udev, 0x64, buf, sizeof(buf)); 27162306a36Sopenharmony_ci if (ret) 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci ret = letsketch_get_string(udev, LETSKETCH_INFO_STR_IDX_BEGIN, buf, sizeof(buf)); 27562306a36Sopenharmony_ci if (ret) 27662306a36Sopenharmony_ci return ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* 27962306a36Sopenharmony_ci * The tablet should be in raw mode now, end with a final delay before 28062306a36Sopenharmony_ci * doing further IO to the device. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci usleep_range(5000, 7000); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ret = hid_parse(hdev); 28562306a36Sopenharmony_ci if (ret) 28662306a36Sopenharmony_ci return ret; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 28962306a36Sopenharmony_ci if (!data) 29062306a36Sopenharmony_ci return -ENOMEM; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci data->hdev = hdev; 29362306a36Sopenharmony_ci timer_setup(&data->inrange_timer, letsketch_inrange_timeout, 0); 29462306a36Sopenharmony_ci hid_set_drvdata(hdev, data); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ret = letsketch_setup_input_tablet(data); 29762306a36Sopenharmony_ci if (ret) 29862306a36Sopenharmony_ci return ret; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci ret = letsketch_setup_input_tablet_pad(data); 30162306a36Sopenharmony_ci if (ret) 30262306a36Sopenharmony_ci return ret; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return hid_hw_start(hdev, HID_CONNECT_HIDRAW); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic const struct hid_device_id letsketch_devices[] = { 30862306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_LETSKETCH, USB_DEVICE_ID_WP9620N) }, 30962306a36Sopenharmony_ci { } 31062306a36Sopenharmony_ci}; 31162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, letsketch_devices); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic struct hid_driver letsketch_driver = { 31462306a36Sopenharmony_ci .name = "letsketch", 31562306a36Sopenharmony_ci .id_table = letsketch_devices, 31662306a36Sopenharmony_ci .probe = letsketch_probe, 31762306a36Sopenharmony_ci .raw_event = letsketch_raw_event, 31862306a36Sopenharmony_ci}; 31962306a36Sopenharmony_cimodule_hid_driver(letsketch_driver); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 32262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 323