1 /* 2 * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 /* 8 * Wacom ExpressKey Remote Driver 9 * 10 * HID driver for Wacom ExpressKey Remote (USB HID, vendor 0x056a). 11 * The device uses a vendor-specific HID usage page (0xFF0C) with 12 * proprietary report formats. Only a single remote is supported; 13 * multi-remote pairing (up to 5) is not implemented. 14 * 15 * Protocol decoded from USB traffic capture (HydraUSB3 analyser). 16 * Report structure cross-referenced with the HID report descriptor 17 * and publicly available Wacom protocol documentation. 18 * 19 * Report ID 0x10 (DEVICE_LIST): pairing status (ignored) 20 * Report ID 0x11 (REMOTE): button and touch ring events 21 * buf[0]: report ID (0x11) 22 * buf[3..5]: serial number (LE 24-bit) 23 * buf[7]: battery (bit 7 = charging, bits 0-6 = percent 0-100) 24 * buf[9]: buttons 0-7 25 * buf[10]: buttons 8-15 26 * buf[11]: bits 0-1 = buttons 16-17, bits 6-7 = touch ring mode 27 * buf[12]: touch ring (bit 7 = active, bits 0-6 = position 1-72, 28 * where raw value 1 maps to position 0 and 72 maps to 71) 29 */ 30 31 #include <sys/cdefs.h> 32 33 #include "opt_hid.h" 34 35 #include <sys/param.h> 36 #include <sys/bus.h> 37 #include <sys/kernel.h> 38 #include <sys/lock.h> 39 #include <sys/malloc.h> 40 #include <sys/module.h> 41 #include <sys/mutex.h> 42 #include <sys/sysctl.h> 43 44 #include <dev/evdev/input.h> 45 #include <dev/evdev/evdev.h> 46 47 #define HID_DEBUG_VAR hidwacom_debug 48 #include <dev/hid/hid.h> 49 #include <dev/hid/hidbus.h> 50 #include "usbdevs.h" 51 52 #ifdef HID_DEBUG 53 static int hidwacom_debug = 0; 54 55 static SYSCTL_NODE(_hw_hid, OID_AUTO, hidwacom, CTLFLAG_RW, 0, 56 "Wacom ExpressKey Remote"); 57 SYSCTL_INT(_hw_hid_hidwacom, OID_AUTO, debug, CTLFLAG_RWTUN, 58 &hidwacom_debug, 0, "Debug level"); 59 #endif 60 61 /* Report IDs */ 62 #define WACOM_REPORT_DEVICE_LIST 0x10 63 #define WACOM_REPORT_REMOTE 0x11 64 65 /* Minimum report length (report ID + 12 data bytes for ring at buf[12]) */ 66 #define WACOM_REMOTE_MIN_LEN 13 67 68 /* Touch ring modes (buf[11] bits 6-7) */ 69 #define WACOM_RING_MODE_MASK 0xc0 70 #define WACOM_RING_MODE_SHIFT 6 71 72 /* Touch ring */ 73 #define WACOM_RING_ACTIVE 0x80 74 #define WACOM_RING_POSITION_MASK 0x7f 75 #define WACOM_RING_MAX 71 76 77 /* Battery: bit 7 = charging flag, bits 0-6 = percent */ 78 #define WACOM_BATTERY_CHARGING 0x80 79 #define WACOM_BATTERY_PERCENT_MASK 0x7f 80 81 /* Pad device ID for ABS_MISC activity marker (matches Linux PAD_DEVICE_ID) */ 82 #define WACOM_PAD_DEVICE_ID 0x0f 83 84 #define HIDWACOM_DEV_NAME "Wacom ExpressKey Remote" 85 86 /* Button evdev keycodes */ 87 static const uint16_t hidwacom_buttons[] = { 88 BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, /* buf[9] */ 89 BTN_8, BTN_9, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* buf[10] */ 90 BTN_BASE, BTN_BASE2, /* buf[11] */ 91 }; 92 93 struct hidwacom_softc { 94 struct mtx sc_mtx; 95 struct evdev_dev *sc_evdev; 96 }; 97 98 static void 99 hidwacom_intr(void *context, void *data, hid_size_t len) 100 { 101 struct hidwacom_softc *sc = context; 102 uint8_t *buf = data; 103 uint8_t report_id; 104 uint8_t b9, b10, b11, ring, battery; 105 uint32_t serial; 106 int i, pos; 107 108 if (len < 1) 109 return; 110 111 report_id = buf[0]; 112 113 if (report_id == WACOM_REPORT_DEVICE_LIST) { 114 DPRINTFN(2, "device list report (pairing)\n"); 115 return; 116 } 117 118 if (report_id != WACOM_REPORT_REMOTE) { 119 DPRINTFN(1, "unknown report id: 0x%02x\n", report_id); 120 return; 121 } 122 123 if (len < WACOM_REMOTE_MIN_LEN) { 124 DPRINTFN(1, "short remote report: %zu\n", (size_t)len); 125 return; 126 } 127 128 mtx_lock(&sc->sc_mtx); 129 130 /* Serial number (LE 24-bit) */ 131 serial = buf[3] + ((uint32_t)buf[4] << 8) + ((uint32_t)buf[5] << 16); 132 133 /* Battery: percent in bits 0-6, charging flag in bit 7 */ 134 battery = buf[7] & WACOM_BATTERY_PERCENT_MASK; 135 136 /* Extract button bytes */ 137 b9 = buf[9]; 138 b10 = buf[10]; 139 b11 = buf[11]; 140 141 /* Buttons 0-7 from buf[9] */ 142 for (i = 0; i < 8; i++) 143 evdev_push_key(sc->sc_evdev, hidwacom_buttons[i], 144 (b9 >> i) & 1); 145 146 /* Buttons 8-15 from buf[10] */ 147 for (i = 0; i < 8; i++) 148 evdev_push_key(sc->sc_evdev, hidwacom_buttons[8 + i], 149 (b10 >> i) & 1); 150 151 /* Buttons 16-17 from buf[11] bits 0-1 */ 152 evdev_push_key(sc->sc_evdev, BTN_BASE, b11 & 0x01); 153 evdev_push_key(sc->sc_evdev, BTN_BASE2, b11 & 0x02); 154 155 /* Touch ring: raw position is 1-based (1..72); subtract 1 for 0..71 */ 156 ring = buf[12]; 157 if (ring & WACOM_RING_ACTIVE) { 158 pos = (ring & WACOM_RING_POSITION_MASK) - 1; 159 if (pos < 0) 160 pos = 0; 161 if (pos > WACOM_RING_MAX) 162 pos = WACOM_RING_MAX; 163 evdev_push_abs(sc->sc_evdev, ABS_WHEEL, pos); 164 evdev_push_abs(sc->sc_evdev, ABS_MISC, 165 (b11 >> WACOM_RING_MODE_SHIFT) & 0x03); 166 } else { 167 evdev_push_abs(sc->sc_evdev, ABS_WHEEL, 0); 168 evdev_push_abs(sc->sc_evdev, ABS_MISC, 0); 169 } 170 171 /* Battery percent */ 172 evdev_push_event(sc->sc_evdev, EV_MSC, MSC_PULSELED, battery); 173 174 /* Remote serial number */ 175 evdev_push_event(sc->sc_evdev, EV_MSC, MSC_SERIAL, serial); 176 177 evdev_sync(sc->sc_evdev); 178 179 mtx_unlock(&sc->sc_mtx); 180 } 181 182 static const struct hid_device_id hidwacom_devs[] = { 183 { HID_BVP(BUS_USB, USB_VENDOR_WACOM, 184 USB_PRODUCT_WACOM_EXPRESSKEY_REMOTE) }, 185 }; 186 187 static int 188 hidwacom_probe(device_t dev) 189 { 190 int error; 191 192 error = HIDBUS_LOOKUP_DRIVER_INFO(dev, hidwacom_devs); 193 if (error != 0) 194 return (error); 195 196 if (hidbus_get_index(dev) != 0) 197 return (ENXIO); 198 199 hidbus_set_desc(dev, HIDWACOM_DEV_NAME); 200 return (BUS_PROBE_DEFAULT); 201 } 202 203 static int 204 hidwacom_attach(device_t dev) 205 { 206 struct hidwacom_softc *sc = device_get_softc(dev); 207 const struct hid_device_info *hw; 208 int i, error; 209 210 hw = hid_get_device_info(dev); 211 mtx_init(&sc->sc_mtx, "hidwacom", NULL, MTX_DEF); 212 213 sc->sc_evdev = evdev_alloc(); 214 evdev_set_name(sc->sc_evdev, HIDWACOM_DEV_NAME); 215 evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev)); 216 evdev_set_id(sc->sc_evdev, hw->idBus, hw->idVendor, hw->idProduct, 217 hw->idVersion); 218 evdev_set_serial(sc->sc_evdev, hw->serial); 219 220 evdev_support_event(sc->sc_evdev, EV_SYN); 221 evdev_support_event(sc->sc_evdev, EV_KEY); 222 evdev_support_event(sc->sc_evdev, EV_ABS); 223 evdev_support_event(sc->sc_evdev, EV_MSC); 224 225 for (i = 0; i < (int)nitems(hidwacom_buttons); i++) 226 evdev_support_key(sc->sc_evdev, hidwacom_buttons[i]); 227 228 evdev_support_abs(sc->sc_evdev, ABS_WHEEL, 0, WACOM_RING_MAX, 229 0, 0, 0); 230 evdev_support_abs(sc->sc_evdev, ABS_MISC, 0, 3, 0, 0, 0); 231 evdev_support_msc(sc->sc_evdev, MSC_SERIAL); 232 evdev_support_msc(sc->sc_evdev, MSC_PULSELED); 233 234 error = evdev_register_mtx(sc->sc_evdev, &sc->sc_mtx); 235 if (error != 0) { 236 device_printf(dev, "evdev_register_mtx failed: %d\n", error); 237 goto fail; 238 } 239 240 hidbus_set_intr(dev, hidwacom_intr, sc); 241 242 error = hid_intr_start(dev); 243 if (error != 0) { 244 device_printf(dev, "hid_intr_start failed: %d\n", error); 245 hid_intr_stop(dev); 246 goto fail; 247 } 248 249 return (0); 250 251 fail: 252 if (sc->sc_evdev != NULL) 253 evdev_free(sc->sc_evdev); 254 mtx_destroy(&sc->sc_mtx); 255 return (error); 256 } 257 258 static int 259 hidwacom_detach(device_t dev) 260 { 261 struct hidwacom_softc *sc = device_get_softc(dev); 262 int error; 263 264 error = hid_intr_stop(dev); 265 if (error != 0) { 266 device_printf(dev, "hid_intr_stop failed: %d\n", error); 267 return (error); 268 } 269 evdev_free(sc->sc_evdev); 270 mtx_destroy(&sc->sc_mtx); 271 272 return (0); 273 } 274 275 static device_method_t hidwacom_methods[] = { 276 DEVMETHOD(device_probe, hidwacom_probe), 277 DEVMETHOD(device_attach, hidwacom_attach), 278 DEVMETHOD(device_detach, hidwacom_detach), 279 DEVMETHOD_END 280 }; 281 282 static driver_t hidwacom_driver = { 283 "hidwacom", 284 hidwacom_methods, 285 sizeof(struct hidwacom_softc) 286 }; 287 288 DRIVER_MODULE(hidwacom, hidbus, hidwacom_driver, NULL, NULL); 289 MODULE_DEPEND(hidwacom, hid, 1, 1, 1); 290 MODULE_DEPEND(hidwacom, hidbus, 1, 1, 1); 291 MODULE_DEPEND(hidwacom, evdev, 1, 1, 1); 292 MODULE_VERSION(hidwacom, 1); 293 HID_PNP_INFO(hidwacom_devs); 294