1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * HID driver for ViewSonic devices not fully compliant with HID standard
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2017 Nikolai Kondrashov
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun * This program is free software; you can redistribute it and/or modify it
10*4882a593Smuzhiyun * under the terms of the GNU General Public License as published by the Free
11*4882a593Smuzhiyun * Software Foundation; either version 2 of the License, or (at your option)
12*4882a593Smuzhiyun * any later version.
13*4882a593Smuzhiyun */
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/device.h>
16*4882a593Smuzhiyun #include <linux/hid.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #include "hid-ids.h"
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun /* Size of the original descriptor of PD1011 signature pad */
22*4882a593Smuzhiyun #define PD1011_RDESC_ORIG_SIZE 408
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun /* Fixed report descriptor of PD1011 signature pad */
25*4882a593Smuzhiyun static __u8 pd1011_rdesc_fixed[] = {
26*4882a593Smuzhiyun 0x05, 0x0D, /* Usage Page (Digitizer), */
27*4882a593Smuzhiyun 0x09, 0x02, /* Usage (Pen), */
28*4882a593Smuzhiyun 0xA1, 0x01, /* Collection (Application), */
29*4882a593Smuzhiyun 0x85, 0x02, /* Report ID (2), */
30*4882a593Smuzhiyun 0x09, 0x20, /* Usage (Stylus), */
31*4882a593Smuzhiyun 0xA0, /* Collection (Physical), */
32*4882a593Smuzhiyun 0x75, 0x10, /* Report Size (16), */
33*4882a593Smuzhiyun 0x95, 0x01, /* Report Count (1), */
34*4882a593Smuzhiyun 0xA4, /* Push, */
35*4882a593Smuzhiyun 0x05, 0x01, /* Usage Page (Desktop), */
36*4882a593Smuzhiyun 0x65, 0x13, /* Unit (Inch), */
37*4882a593Smuzhiyun 0x55, 0xFD, /* Unit Exponent (-3), */
38*4882a593Smuzhiyun 0x34, /* Physical Minimum (0), */
39*4882a593Smuzhiyun 0x09, 0x30, /* Usage (X), */
40*4882a593Smuzhiyun 0x46, 0x5D, 0x21, /* Physical Maximum (8541), */
41*4882a593Smuzhiyun 0x27, 0x80, 0xA9,
42*4882a593Smuzhiyun 0x00, 0x00, /* Logical Maximum (43392), */
43*4882a593Smuzhiyun 0x81, 0x02, /* Input (Variable), */
44*4882a593Smuzhiyun 0x09, 0x31, /* Usage (Y), */
45*4882a593Smuzhiyun 0x46, 0xDA, 0x14, /* Physical Maximum (5338), */
46*4882a593Smuzhiyun 0x26, 0xF0, 0x69, /* Logical Maximum (27120), */
47*4882a593Smuzhiyun 0x81, 0x02, /* Input (Variable), */
48*4882a593Smuzhiyun 0xB4, /* Pop, */
49*4882a593Smuzhiyun 0x14, /* Logical Minimum (0), */
50*4882a593Smuzhiyun 0x25, 0x01, /* Logical Maximum (1), */
51*4882a593Smuzhiyun 0x75, 0x01, /* Report Size (1), */
52*4882a593Smuzhiyun 0x95, 0x01, /* Report Count (1), */
53*4882a593Smuzhiyun 0x81, 0x03, /* Input (Constant, Variable), */
54*4882a593Smuzhiyun 0x09, 0x32, /* Usage (In Range), */
55*4882a593Smuzhiyun 0x09, 0x42, /* Usage (Tip Switch), */
56*4882a593Smuzhiyun 0x95, 0x02, /* Report Count (2), */
57*4882a593Smuzhiyun 0x81, 0x02, /* Input (Variable), */
58*4882a593Smuzhiyun 0x95, 0x05, /* Report Count (5), */
59*4882a593Smuzhiyun 0x81, 0x03, /* Input (Constant, Variable), */
60*4882a593Smuzhiyun 0x75, 0x10, /* Report Size (16), */
61*4882a593Smuzhiyun 0x95, 0x01, /* Report Count (1), */
62*4882a593Smuzhiyun 0x09, 0x30, /* Usage (Tip Pressure), */
63*4882a593Smuzhiyun 0x15, 0x05, /* Logical Minimum (5), */
64*4882a593Smuzhiyun 0x26, 0xFF, 0x07, /* Logical Maximum (2047), */
65*4882a593Smuzhiyun 0x81, 0x02, /* Input (Variable), */
66*4882a593Smuzhiyun 0x75, 0x10, /* Report Size (16), */
67*4882a593Smuzhiyun 0x95, 0x01, /* Report Count (1), */
68*4882a593Smuzhiyun 0x81, 0x03, /* Input (Constant, Variable), */
69*4882a593Smuzhiyun 0xC0, /* End Collection, */
70*4882a593Smuzhiyun 0xC0 /* End Collection */
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun
viewsonic_report_fixup(struct hid_device * hdev,__u8 * rdesc,unsigned int * rsize)73*4882a593Smuzhiyun static __u8 *viewsonic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
74*4882a593Smuzhiyun unsigned int *rsize)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun switch (hdev->product) {
77*4882a593Smuzhiyun case USB_DEVICE_ID_VIEWSONIC_PD1011:
78*4882a593Smuzhiyun case USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011:
79*4882a593Smuzhiyun if (*rsize == PD1011_RDESC_ORIG_SIZE) {
80*4882a593Smuzhiyun rdesc = pd1011_rdesc_fixed;
81*4882a593Smuzhiyun *rsize = sizeof(pd1011_rdesc_fixed);
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun break;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun return rdesc;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun static const struct hid_device_id viewsonic_devices[] = {
90*4882a593Smuzhiyun { HID_USB_DEVICE(USB_VENDOR_ID_VIEWSONIC,
91*4882a593Smuzhiyun USB_DEVICE_ID_VIEWSONIC_PD1011) },
92*4882a593Smuzhiyun { HID_USB_DEVICE(USB_VENDOR_ID_SIGNOTEC,
93*4882a593Smuzhiyun USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011) },
94*4882a593Smuzhiyun { }
95*4882a593Smuzhiyun };
96*4882a593Smuzhiyun MODULE_DEVICE_TABLE(hid, viewsonic_devices);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun static struct hid_driver viewsonic_driver = {
99*4882a593Smuzhiyun .name = "viewsonic",
100*4882a593Smuzhiyun .id_table = viewsonic_devices,
101*4882a593Smuzhiyun .report_fixup = viewsonic_report_fixup,
102*4882a593Smuzhiyun };
103*4882a593Smuzhiyun module_hid_driver(viewsonic_driver);
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun MODULE_LICENSE("GPL");
106