Version:  2.0.40 2.2.26 2.4.37 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18

Linux/drivers/hid/hid-huion.c

  1 /*
  2  *  HID driver for Huion devices not fully compliant with HID standard
  3  *
  4  *  Copyright (c) 2013 Martin Rusko
  5  *  Copyright (c) 2014 Nikolai Kondrashov
  6  */
  7 
  8 /*
  9  * This program is free software; you can redistribute it and/or modify it
 10  * under the terms of the GNU General Public License as published by the Free
 11  * Software Foundation; either version 2 of the License, or (at your option)
 12  * any later version.
 13  */
 14 
 15 #include <linux/device.h>
 16 #include <linux/hid.h>
 17 #include <linux/module.h>
 18 #include <linux/usb.h>
 19 #include <asm/unaligned.h>
 20 #include "usbhid/usbhid.h"
 21 
 22 #include "hid-ids.h"
 23 
 24 /* Report descriptor template placeholder head */
 25 #define HUION_PH_HEAD   0xFE, 0xED, 0x1D
 26 
 27 /* Report descriptor template placeholder IDs */
 28 enum huion_ph_id {
 29         HUION_PH_ID_X_LM,
 30         HUION_PH_ID_X_PM,
 31         HUION_PH_ID_Y_LM,
 32         HUION_PH_ID_Y_PM,
 33         HUION_PH_ID_PRESSURE_LM,
 34         HUION_PH_ID_NUM
 35 };
 36 
 37 /* Report descriptor template placeholder */
 38 #define HUION_PH(_ID) HUION_PH_HEAD, HUION_PH_ID_##_ID
 39 
 40 /* Fixed report descriptor template */
 41 static const __u8 huion_tablet_rdesc_template[] = {
 42         0x05, 0x0D,             /*  Usage Page (Digitizer),                 */
 43         0x09, 0x02,             /*  Usage (Pen),                            */
 44         0xA1, 0x01,             /*  Collection (Application),               */
 45         0x85, 0x07,             /*      Report ID (7),                      */
 46         0x09, 0x20,             /*      Usage (Stylus),                     */
 47         0xA0,                   /*      Collection (Physical),              */
 48         0x14,                   /*          Logical Minimum (0),            */
 49         0x25, 0x01,             /*          Logical Maximum (1),            */
 50         0x75, 0x01,             /*          Report Size (1),                */
 51         0x09, 0x42,             /*          Usage (Tip Switch),             */
 52         0x09, 0x44,             /*          Usage (Barrel Switch),          */
 53         0x09, 0x46,             /*          Usage (Tablet Pick),            */
 54         0x95, 0x03,             /*          Report Count (3),               */
 55         0x81, 0x02,             /*          Input (Variable),               */
 56         0x95, 0x03,             /*          Report Count (3),               */
 57         0x81, 0x03,             /*          Input (Constant, Variable),     */
 58         0x09, 0x32,             /*          Usage (In Range),               */
 59         0x95, 0x01,             /*          Report Count (1),               */
 60         0x81, 0x02,             /*          Input (Variable),               */
 61         0x95, 0x01,             /*          Report Count (1),               */
 62         0x81, 0x03,             /*          Input (Constant, Variable),     */
 63         0x75, 0x10,             /*          Report Size (16),               */
 64         0x95, 0x01,             /*          Report Count (1),               */
 65         0xA4,                   /*          Push,                           */
 66         0x05, 0x01,             /*          Usage Page (Desktop),           */
 67         0x65, 0x13,             /*          Unit (Inch),                    */
 68         0x55, 0xFD,             /*          Unit Exponent (-3),             */
 69         0x34,                   /*          Physical Minimum (0),           */
 70         0x09, 0x30,             /*          Usage (X),                      */
 71         0x27, HUION_PH(X_LM),   /*          Logical Maximum (PLACEHOLDER),  */
 72         0x47, HUION_PH(X_PM),   /*          Physical Maximum (PLACEHOLDER), */
 73         0x81, 0x02,             /*          Input (Variable),               */
 74         0x09, 0x31,             /*          Usage (Y),                      */
 75         0x27, HUION_PH(Y_LM),   /*          Logical Maximum (PLACEHOLDER),  */
 76         0x47, HUION_PH(Y_PM),   /*          Physical Maximum (PLACEHOLDER), */
 77         0x81, 0x02,             /*          Input (Variable),               */
 78         0xB4,                   /*          Pop,                            */
 79         0x09, 0x30,             /*          Usage (Tip Pressure),           */
 80         0x27,
 81         HUION_PH(PRESSURE_LM),  /*          Logical Maximum (PLACEHOLDER),  */
 82         0x81, 0x02,             /*          Input (Variable),               */
 83         0xC0,                   /*      End Collection,                     */
 84         0xC0                    /*  End Collection                          */
 85 };
 86 
 87 /* Parameter indices */
 88 enum huion_prm {
 89         HUION_PRM_X_LM          = 1,
 90         HUION_PRM_Y_LM          = 2,
 91         HUION_PRM_PRESSURE_LM   = 4,
 92         HUION_PRM_RESOLUTION    = 5,
 93         HUION_PRM_NUM
 94 };
 95 
 96 /* Driver data */
 97 struct huion_drvdata {
 98         __u8 *rdesc;
 99         unsigned int rsize;
100 };
101 
102 static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc,
103                 unsigned int *rsize)
104 {
105         struct huion_drvdata *drvdata = hid_get_drvdata(hdev);
106         switch (hdev->product) {
107         case USB_DEVICE_ID_HUION_TABLET:
108                 if (drvdata->rdesc != NULL) {
109                         rdesc = drvdata->rdesc;
110                         *rsize = drvdata->rsize;
111                 }
112                 break;
113         }
114         return rdesc;
115 }
116 
117 /**
118  * Enable fully-functional tablet mode and determine device parameters.
119  *
120  * @hdev:       HID device
121  */
122 static int huion_tablet_enable(struct hid_device *hdev)
123 {
124         int rc;
125         struct usb_device *usb_dev = hid_to_usb_dev(hdev);
126         struct huion_drvdata *drvdata = hid_get_drvdata(hdev);
127         __le16 *buf = NULL;
128         size_t len;
129         s32 params[HUION_PH_ID_NUM];
130         s32 resolution;
131         __u8 *p;
132         s32 v;
133 
134         /*
135          * Read string descriptor containing tablet parameters. The specific
136          * string descriptor and data were discovered by sniffing the Windows
137          * driver traffic.
138          * NOTE: This enables fully-functional tablet mode.
139          */
140         len = HUION_PRM_NUM * sizeof(*buf);
141         buf = kmalloc(len, GFP_KERNEL);
142         if (buf == NULL) {
143                 hid_err(hdev, "failed to allocate parameter buffer\n");
144                 rc = -ENOMEM;
145                 goto cleanup;
146         }
147         rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
148                                 USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
149                                 (USB_DT_STRING << 8) + 0x64,
150                                 0x0409, buf, len,
151                                 USB_CTRL_GET_TIMEOUT);
152         if (rc == -EPIPE) {
153                 hid_err(hdev, "device parameters not found\n");
154                 rc = -ENODEV;
155                 goto cleanup;
156         } else if (rc < 0) {
157                 hid_err(hdev, "failed to get device parameters: %d\n", rc);
158                 rc = -ENODEV;
159                 goto cleanup;
160         } else if (rc != len) {
161                 hid_err(hdev, "invalid device parameters\n");
162                 rc = -ENODEV;
163                 goto cleanup;
164         }
165 
166         /* Extract device parameters */
167         params[HUION_PH_ID_X_LM] = le16_to_cpu(buf[HUION_PRM_X_LM]);
168         params[HUION_PH_ID_Y_LM] = le16_to_cpu(buf[HUION_PRM_Y_LM]);
169         params[HUION_PH_ID_PRESSURE_LM] =
170                 le16_to_cpu(buf[HUION_PRM_PRESSURE_LM]);
171         resolution = le16_to_cpu(buf[HUION_PRM_RESOLUTION]);
172         if (resolution == 0) {
173                 params[HUION_PH_ID_X_PM] = 0;
174                 params[HUION_PH_ID_Y_PM] = 0;
175         } else {
176                 params[HUION_PH_ID_X_PM] = params[HUION_PH_ID_X_LM] *
177                                                 1000 / resolution;
178                 params[HUION_PH_ID_Y_PM] = params[HUION_PH_ID_Y_LM] *
179                                                 1000 / resolution;
180         }
181 
182         /* Allocate fixed report descriptor */
183         drvdata->rdesc = devm_kmalloc(&hdev->dev,
184                                 sizeof(huion_tablet_rdesc_template),
185                                 GFP_KERNEL);
186         if (drvdata->rdesc == NULL) {
187                 hid_err(hdev, "failed to allocate fixed rdesc\n");
188                 rc = -ENOMEM;
189                 goto cleanup;
190         }
191         drvdata->rsize = sizeof(huion_tablet_rdesc_template);
192 
193         /* Format fixed report descriptor */
194         memcpy(drvdata->rdesc, huion_tablet_rdesc_template,
195                 drvdata->rsize);
196         for (p = drvdata->rdesc;
197              p <= drvdata->rdesc + drvdata->rsize - 4;) {
198                 if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D &&
199                     p[3] < sizeof(params)) {
200                         v = params[p[3]];
201                         put_unaligned(cpu_to_le32(v), (s32 *)p);
202                         p += 4;
203                 } else {
204                         p++;
205                 }
206         }
207 
208         rc = 0;
209 
210 cleanup:
211         kfree(buf);
212         return rc;
213 }
214 
215 static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id)
216 {
217         int rc;
218         struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
219         struct huion_drvdata *drvdata;
220 
221         /* Allocate and assign driver data */
222         drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
223         if (drvdata == NULL) {
224                 hid_err(hdev, "failed to allocate driver data\n");
225                 return -ENOMEM;
226         }
227         hid_set_drvdata(hdev, drvdata);
228 
229         switch (id->product) {
230         case USB_DEVICE_ID_HUION_TABLET:
231                 /* If this is the pen interface */
232                 if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
233                         rc = huion_tablet_enable(hdev);
234                         if (rc) {
235                                 hid_err(hdev, "tablet enabling failed\n");
236                                 return rc;
237                         }
238                 }
239                 break;
240         }
241 
242         rc = hid_parse(hdev);
243         if (rc) {
244                 hid_err(hdev, "parse failed\n");
245                 return rc;
246         }
247 
248         rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
249         if (rc) {
250                 hid_err(hdev, "hw start failed\n");
251                 return rc;
252         }
253 
254         return 0;
255 }
256 
257 static int huion_raw_event(struct hid_device *hdev, struct hid_report *report,
258                         u8 *data, int size)
259 {
260         struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
261 
262         /* If this is a pen input report */
263         if (intf->cur_altsetting->desc.bInterfaceNumber == 0 &&
264             report->type == HID_INPUT_REPORT &&
265             report->id == 0x07 && size >= 2)
266                 /* Invert the in-range bit */
267                 data[1] ^= 0x40;
268 
269         return 0;
270 }
271 
272 static const struct hid_device_id huion_devices[] = {
273         { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) },
274         { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) },
275         { }
276 };
277 MODULE_DEVICE_TABLE(hid, huion_devices);
278 
279 static struct hid_driver huion_driver = {
280         .name = "huion",
281         .id_table = huion_devices,
282         .probe = huion_probe,
283         .report_fixup = huion_report_fixup,
284         .raw_event = huion_raw_event,
285 };
286 module_hid_driver(huion_driver);
287 
288 MODULE_AUTHOR("Martin Rusko");
289 MODULE_DESCRIPTION("Huion HID driver");
290 MODULE_LICENSE("GPL");
291 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us