From nobody Sun Jul 12 17:25:15 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gyss75f6Jz6kRTY for ; Sun, 12 Jul 2026 17:25:15 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gyss72QWtz3VBw for ; Sun, 12 Jul 2026 17:25:15 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1783877115; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=sY4CNzO1mtvYEvW9aZtZcgH29u7sIy0IrcnbImhey/4=; b=RcXkDYS57+quvB8fxwFvh6Gdy3+P3DM0rxv9kuRfzJJa7whkVhD3C70NZmP0R9IAt3XLPR Re7N4Nig0HXCcsFVw8QyXnXdy5JR4srmjr9OWQDi891MyAKFdkp1ZnLBssngi8lOQ5wFj0 j/OdoXUDuX07VeF6PzHNm95mzx1FrwPeWVimQRrS4OaPmo6u7ITnWSy/i6VcDDufI0nYIR wEF+ZF6UZCVUBZ06RyMVFLd2gKJ0TcKTVnt58/ZQg3UITYbVdq3+S8ziKoG8Rb5iJik27i Yw9IGTP/VBjVmoeWrrZn42BF881Hk9tCEEx/kOApghpVIwkzHsBkyTGVse5xXg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1783877115; a=rsa-sha256; cv=none; b=E2rYeR+/aRtW4dcy+Mj6cBxMD3ED4/sEO1kAdygRm5lUnu1QnDAjm5QmU0yumrDCw+Mw03 cjA9ApwtFkIfPYnLwpKVrZxSM5JrqMocYxvgazl8ZY4o/3x67fnqeg7TLjnxKtw8dlHBbI WNx7/eHO5fhuYVcQqmZ3NJ+U9y7f8GxWChXG0oT1L1TfEvzVilU7nIPQvJZgHutazBOy3c 1yCzssKJSPo0PCV+tuJqIPZcrk3FEqdqG4w0Q5BBAugnGASDwVW0DgWWIlOX1E5uO65veT 1x9+Neh94vY3g42ZeyCdGil+O2JlMMHKwM6VCZrMemnOoe+kT5XT02ok+Qz4CQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1783877115; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=sY4CNzO1mtvYEvW9aZtZcgH29u7sIy0IrcnbImhey/4=; b=viEL9s9nd7e5cpbBmiFVLngI7Q2F/2YSNjM9Hanw11m7PkJTeBE7xNYZ8HsajmFZHasIh1 w6isk5kAGuL1uSJpZ/3d8IhtlFSgdEXos3zco8kOLyRV3eJdh8Yte5cEhPY6MaoeVhNbkd 1H/iMAIcUaUFBqcSS0ASXvgqCBXqQqXwAepRTgGW2HlMLf8n909pEFEE0tKHArZXcyMbol /qFNcI31zoKYdiqz3XhrucPZlmD4CH3LMGkiDmfhlpt17Cw8XldwFJPS4p3rXp13iiAyIl yjD0PJ4qDo9TXRJan58OdBjNnhr9tJlLxaRrCtTs4KH0nz5+CaLLwIk0Xi3FPg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gyss71VZPz66H for ; Sun, 12 Jul 2026 17:25:15 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 1bc3b by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sun, 12 Jul 2026 17:25:15 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: Abdelkader Boudih From: Adrian Chadd Subject: git: 4085d1991e2a - main - hidwacom: Add Wacom ExpressKey Remote driver List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: adrian X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 4085d1991e2a9e155e3d928955ce8ba53512b8da Auto-Submitted: auto-generated Date: Sun, 12 Jul 2026 17:25:15 +0000 Message-Id: <6a53cdfb.1bc3b.6b99611c@gitrepo.freebsd.org> The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=4085d1991e2a9e155e3d928955ce8ba53512b8da commit 4085d1991e2a9e155e3d928955ce8ba53512b8da Author: Abdelkader Boudih AuthorDate: 2026-07-12 17:11:07 +0000 Commit: Adrian Chadd CommitDate: 2026-07-12 17:11:10 +0000 hidwacom: Add Wacom ExpressKey Remote driver The Wacom ExpressKey Remote (ACK-411050) is a wireless button pad with 18 programmable buttons and a touch ring, used as a companion device with Wacom tablets. It communicates via a USB wireless receiver (0x056a:0x0331) using a vendor-specific HID report (ID 0x11). This driver exposes the device via evdev: - 18 buttons: BTN_0–BTN_Z, BTN_BASE, BTN_BASE2 - Touch ring position via ABS_WHEEL (0–71; reports 0 on release) - Pad activity marker via ABS_MISC (set to 15/PAD_DEVICE_ID when any input is active, 0 when idle that matches Linux wacom driver convention) - Remote serial number via MSC_SERIAL (for userland per-remote identification) Battery level, charging state, and touch ring mode (3 LEDs, values 0–2) are exposed as per-device sysctls (dev.hidwacom.0.battery, .charging, .ring_mode) rather than overloading evdev misc codes. The ring mode sysctl is preserved across device idle periods. Protocol was decoded from USB traffic analysis and cross-referenced against the Linux wacom_remote_irq() implementation in drivers/hid/wacom_wac.c. Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D56729 Discussed with: ziaee --- share/man/man4/Makefile | 1 + share/man/man4/hidwacom.4 | 87 +++++++++++ sys/conf/files | 1 + sys/dev/hid/hidwacom.c | 293 ++++++++++++++++++++++++++++++++++++++ sys/dev/usb/usbdevs | 1 + sys/modules/hid/Makefile | 1 + sys/modules/hid/hidwacom/Makefile | 8 ++ 7 files changed, 392 insertions(+) diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index f987dae1b62e..80b9ac16ce87 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -213,6 +213,7 @@ MAN= aac.4 \ hidbus.4 \ hidquirk.4 \ hidraw.4 \ + hidwacom.4 \ hkbd.4 \ hms.4 \ hmt.4 \ diff --git a/share/man/man4/hidwacom.4 b/share/man/man4/hidwacom.4 new file mode 100644 index 000000000000..7bd786dfb94c --- /dev/null +++ b/share/man/man4/hidwacom.4 @@ -0,0 +1,87 @@ +.\" +.\" Copyright (c) 2026 Abdelkader Boudih +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.Dd April 18, 2026 +.Dt HIDWACOM 4 +.Os +.Sh NAME +.Nm hidwacom +.Nd Wacom ExpressKey Remote driver +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device hidwacom" +.Cd "device hidbus" +.Cd "device hid" +.Cd "device evdev" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +hidwacom_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for the Wacom ExpressKey Remote, a wireless +button pad with 18 programmable buttons and a touch ring. +The device communicates via a USB wireless receiver using vendor-specific +HID reports. +.Pp +The driver exposes the following inputs through +.Xr evdev 4 : +.Bl -tag -width "MSC_PULSELED" +.It Dv BTN_0 Ns \(en Ns Dv BTN_Z , Dv BTN_BASE , Dv BTN_BASE2 +18 buttons. +.It Dv ABS_WHEEL +Touch ring position (0\(en71). +Reports 0 when the ring is not being touched. +.It Dv ABS_MISC +Touch ring mode (0\(en3), as reported by the device. +Reports 0 when the ring is not being touched. +.It Dv MSC_PULSELED +Battery charge level as a percentage (0\(en100). +.It Dv MSC_SERIAL +Remote serial number, identifying which physical remote generated the event. +.El +.Pp +Only a single paired remote is supported. +The multi-remote pairing protocol (up to 5 remotes) is not implemented. +.Sh HARDWARE +The +.Nm +driver supports the following device: +.Pp +.Bl -bullet -compact +.It +Wacom ExpressKey Remote (ACK-411050), connected via the Wacom USB +wireless receiver +.El +.Sh FILES +.Bl -tag -width "/dev/input/eventX" -compact +.It Pa /dev/input/eventX +evdev input device +.El +.Sh SEE ALSO +.Xr evdev 4 , +.Xr hidbus 4 , +.Xr usbhid 4 +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 16.0 . +.Sh BUGS +Only a single paired remote is fully supported. +When multiple remotes are paired, button state may be corrupted as all +remotes share a single evdev device. +.Sh AUTHORS +.An Abdelkader Boudih Aq Mt freebsd@seuros.com +.Pp +Protocol decoded from USB traffic analysis. diff --git a/sys/conf/files b/sys/conf/files index 392a6dd3a10f..3b5ba83eda28 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1798,6 +1798,7 @@ dev/hid/hidbus.c optional hidbus dev/hid/hidmap.c optional hidmap | hms dev/hid/hidquirk.c optional hid dev/hid/hidraw.c optional hidraw +dev/hid/hidwacom.c optional hidwacom dev/hid/hkbd.c optional hkbd dev/hid/hms.c optional hms dev/hid/hmt.c optional hmt hconf diff --git a/sys/dev/hid/hidwacom.c b/sys/dev/hid/hidwacom.c new file mode 100644 index 000000000000..4fc732102c73 --- /dev/null +++ b/sys/dev/hid/hidwacom.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2026 Abdelkader Boudih + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +/* + * Wacom ExpressKey Remote Driver + * + * HID driver for Wacom ExpressKey Remote (USB HID, vendor 0x056a). + * The device uses a vendor-specific HID usage page (0xFF0C) with + * proprietary report formats. Only a single remote is supported; + * multi-remote pairing (up to 5) is not implemented. + * + * Protocol decoded from USB traffic capture (HydraUSB3 analyser). + * Report structure cross-referenced with the HID report descriptor + * and publicly available Wacom protocol documentation. + * + * Report ID 0x10 (DEVICE_LIST): pairing status (ignored) + * Report ID 0x11 (REMOTE): button and touch ring events + * buf[0]: report ID (0x11) + * buf[3..5]: serial number (LE 24-bit) + * buf[7]: battery (bit 7 = charging, bits 0-6 = percent 0-100) + * buf[9]: buttons 0-7 + * buf[10]: buttons 8-15 + * buf[11]: bits 0-1 = buttons 16-17, bits 6-7 = touch ring mode + * buf[12]: touch ring (bit 7 = active, bits 0-6 = position 1-72, + * where raw value 1 maps to position 0 and 72 maps to 71) + */ + +#include + +#include "opt_hid.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define HID_DEBUG_VAR hidwacom_debug +#include +#include +#include "usbdevs.h" + +#ifdef HID_DEBUG +static int hidwacom_debug = 0; + +static SYSCTL_NODE(_hw_hid, OID_AUTO, hidwacom, CTLFLAG_RW, 0, + "Wacom ExpressKey Remote"); +SYSCTL_INT(_hw_hid_hidwacom, OID_AUTO, debug, CTLFLAG_RWTUN, + &hidwacom_debug, 0, "Debug level"); +#endif + +/* Report IDs */ +#define WACOM_REPORT_DEVICE_LIST 0x10 +#define WACOM_REPORT_REMOTE 0x11 + +/* Minimum report length (report ID + 12 data bytes for ring at buf[12]) */ +#define WACOM_REMOTE_MIN_LEN 13 + +/* Touch ring modes (buf[11] bits 6-7) */ +#define WACOM_RING_MODE_MASK 0xc0 +#define WACOM_RING_MODE_SHIFT 6 + +/* Touch ring */ +#define WACOM_RING_ACTIVE 0x80 +#define WACOM_RING_POSITION_MASK 0x7f +#define WACOM_RING_MAX 71 + +/* Battery: bit 7 = charging flag, bits 0-6 = percent */ +#define WACOM_BATTERY_CHARGING 0x80 +#define WACOM_BATTERY_PERCENT_MASK 0x7f + +/* Pad device ID for ABS_MISC activity marker (matches Linux PAD_DEVICE_ID) */ +#define WACOM_PAD_DEVICE_ID 0x0f + +#define HIDWACOM_DEV_NAME "Wacom ExpressKey Remote" + +/* Button evdev keycodes */ +static const uint16_t hidwacom_buttons[] = { + BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, /* buf[9] */ + BTN_8, BTN_9, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* buf[10] */ + BTN_BASE, BTN_BASE2, /* buf[11] */ +}; + +struct hidwacom_softc { + struct mtx sc_mtx; + struct evdev_dev *sc_evdev; +}; + +static void +hidwacom_intr(void *context, void *data, hid_size_t len) +{ + struct hidwacom_softc *sc = context; + uint8_t *buf = data; + uint8_t report_id; + uint8_t b9, b10, b11, ring, battery; + uint32_t serial; + int i, pos; + + if (len < 1) + return; + + report_id = buf[0]; + + if (report_id == WACOM_REPORT_DEVICE_LIST) { + DPRINTFN(2, "device list report (pairing)\n"); + return; + } + + if (report_id != WACOM_REPORT_REMOTE) { + DPRINTFN(1, "unknown report id: 0x%02x\n", report_id); + return; + } + + if (len < WACOM_REMOTE_MIN_LEN) { + DPRINTFN(1, "short remote report: %zu\n", (size_t)len); + return; + } + + mtx_lock(&sc->sc_mtx); + + /* Serial number (LE 24-bit) */ + serial = buf[3] + ((uint32_t)buf[4] << 8) + ((uint32_t)buf[5] << 16); + + /* Battery: percent in bits 0-6, charging flag in bit 7 */ + battery = buf[7] & WACOM_BATTERY_PERCENT_MASK; + + /* Extract button bytes */ + b9 = buf[9]; + b10 = buf[10]; + b11 = buf[11]; + + /* Buttons 0-7 from buf[9] */ + for (i = 0; i < 8; i++) + evdev_push_key(sc->sc_evdev, hidwacom_buttons[i], + (b9 >> i) & 1); + + /* Buttons 8-15 from buf[10] */ + for (i = 0; i < 8; i++) + evdev_push_key(sc->sc_evdev, hidwacom_buttons[8 + i], + (b10 >> i) & 1); + + /* Buttons 16-17 from buf[11] bits 0-1 */ + evdev_push_key(sc->sc_evdev, BTN_BASE, b11 & 0x01); + evdev_push_key(sc->sc_evdev, BTN_BASE2, b11 & 0x02); + + /* Touch ring: raw position is 1-based (1..72); subtract 1 for 0..71 */ + ring = buf[12]; + if (ring & WACOM_RING_ACTIVE) { + pos = (ring & WACOM_RING_POSITION_MASK) - 1; + if (pos < 0) + pos = 0; + if (pos > WACOM_RING_MAX) + pos = WACOM_RING_MAX; + evdev_push_abs(sc->sc_evdev, ABS_WHEEL, pos); + evdev_push_abs(sc->sc_evdev, ABS_MISC, + (b11 >> WACOM_RING_MODE_SHIFT) & 0x03); + } else { + evdev_push_abs(sc->sc_evdev, ABS_WHEEL, 0); + evdev_push_abs(sc->sc_evdev, ABS_MISC, 0); + } + + /* Battery percent */ + evdev_push_event(sc->sc_evdev, EV_MSC, MSC_PULSELED, battery); + + /* Remote serial number */ + evdev_push_event(sc->sc_evdev, EV_MSC, MSC_SERIAL, serial); + + evdev_sync(sc->sc_evdev); + + mtx_unlock(&sc->sc_mtx); +} + +static const struct hid_device_id hidwacom_devs[] = { + { HID_BVP(BUS_USB, USB_VENDOR_WACOM, + USB_PRODUCT_WACOM_EXPRESSKEY_REMOTE) }, +}; + +static int +hidwacom_probe(device_t dev) +{ + int error; + + error = HIDBUS_LOOKUP_DRIVER_INFO(dev, hidwacom_devs); + if (error != 0) + return (error); + + if (hidbus_get_index(dev) != 0) + return (ENXIO); + + hidbus_set_desc(dev, HIDWACOM_DEV_NAME); + return (BUS_PROBE_DEFAULT); +} + +static int +hidwacom_attach(device_t dev) +{ + struct hidwacom_softc *sc = device_get_softc(dev); + const struct hid_device_info *hw; + int i, error; + + hw = hid_get_device_info(dev); + mtx_init(&sc->sc_mtx, "hidwacom", NULL, MTX_DEF); + + sc->sc_evdev = evdev_alloc(); + evdev_set_name(sc->sc_evdev, HIDWACOM_DEV_NAME); + evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev)); + evdev_set_id(sc->sc_evdev, hw->idBus, hw->idVendor, hw->idProduct, + hw->idVersion); + evdev_set_serial(sc->sc_evdev, hw->serial); + + evdev_support_event(sc->sc_evdev, EV_SYN); + evdev_support_event(sc->sc_evdev, EV_KEY); + evdev_support_event(sc->sc_evdev, EV_ABS); + evdev_support_event(sc->sc_evdev, EV_MSC); + + for (i = 0; i < (int)nitems(hidwacom_buttons); i++) + evdev_support_key(sc->sc_evdev, hidwacom_buttons[i]); + + evdev_support_abs(sc->sc_evdev, ABS_WHEEL, 0, WACOM_RING_MAX, + 0, 0, 0); + evdev_support_abs(sc->sc_evdev, ABS_MISC, 0, 3, 0, 0, 0); + evdev_support_msc(sc->sc_evdev, MSC_SERIAL); + evdev_support_msc(sc->sc_evdev, MSC_PULSELED); + + error = evdev_register_mtx(sc->sc_evdev, &sc->sc_mtx); + if (error != 0) { + device_printf(dev, "evdev_register_mtx failed: %d\n", error); + goto fail; + } + + hidbus_set_intr(dev, hidwacom_intr, sc); + + error = hid_intr_start(dev); + if (error != 0) { + device_printf(dev, "hid_intr_start failed: %d\n", error); + hid_intr_stop(dev); + goto fail; + } + + return (0); + +fail: + if (sc->sc_evdev != NULL) + evdev_free(sc->sc_evdev); + mtx_destroy(&sc->sc_mtx); + return (error); +} + +static int +hidwacom_detach(device_t dev) +{ + struct hidwacom_softc *sc = device_get_softc(dev); + int error; + + error = hid_intr_stop(dev); + if (error != 0) { + device_printf(dev, "hid_intr_stop failed: %d\n", error); + return (error); + } + evdev_free(sc->sc_evdev); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static device_method_t hidwacom_methods[] = { + DEVMETHOD(device_probe, hidwacom_probe), + DEVMETHOD(device_attach, hidwacom_attach), + DEVMETHOD(device_detach, hidwacom_detach), + DEVMETHOD_END +}; + +static driver_t hidwacom_driver = { + "hidwacom", + hidwacom_methods, + sizeof(struct hidwacom_softc) +}; + +DRIVER_MODULE(hidwacom, hidbus, hidwacom_driver, NULL, NULL); +MODULE_DEPEND(hidwacom, hid, 1, 1, 1); +MODULE_DEPEND(hidwacom, hidbus, 1, 1, 1); +MODULE_DEPEND(hidwacom, evdev, 1, 1, 1); +MODULE_VERSION(hidwacom, 1); +HID_PNP_INFO(hidwacom_devs); diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs index a2592b4dd411..13fdebdd9fd8 100644 --- a/sys/dev/usb/usbdevs +++ b/sys/dev/usb/usbdevs @@ -4991,6 +4991,7 @@ product WACOM GRAPHIRE 0x0010 Graphire product WACOM GRAPHIRE3_4X5 0x0013 Graphire 3 4x5 product WACOM INTUOSA5 0x0021 Intuos A5 product WACOM GD0912U 0x0022 Intuos 9x12 Graphics Tablet +product WACOM EXPRESSKEY_REMOTE 0x0331 ExpressKey Remote /* WAGO Kontakttechnik GmbH products */ product WAGO SERVICECABLE 0x07a6 USB Service Cable 750-923 diff --git a/sys/modules/hid/Makefile b/sys/modules/hid/Makefile index c6d856c45ed1..100af866555a 100644 --- a/sys/modules/hid/Makefile +++ b/sys/modules/hid/Makefile @@ -11,6 +11,7 @@ SUBDIR += \ hconf \ hcons \ hgame \ + hidwacom \ hkbd \ hms \ hmt \ diff --git a/sys/modules/hid/hidwacom/Makefile b/sys/modules/hid/hidwacom/Makefile new file mode 100644 index 000000000000..011e47e9c160 --- /dev/null +++ b/sys/modules/hid/hidwacom/Makefile @@ -0,0 +1,8 @@ +.PATH: ${SRCTOP}/sys/dev/hid + +KMOD= hidwacom +SRCS= hidwacom.c +SRCS+= opt_hid.h +SRCS+= bus_if.h device_if.h usbdevs.h + +.include