From: Andrew Kloet Subject: ugaomon: Support for Gaomon S620 To: tech@openbsd.org Date: Sat, 03 Oct 2026 03:02:31 -0400 Hello tech@ I wrote a driver for my Gaomon S620. Majorly inspired by uwacom(4) by Frank Groeneveld. Looking for evaluation. Andrew diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC index 24dcce200..8086d0c6a 100644 --- a/sys/arch/amd64/conf/GENERIC +++ b/sys/arch/amd64/conf/GENERIC @@ -301,6 +301,8 @@ uts* at uhub? # USB touchscreen wsmouse* at uts? mux 0 uwacom* at uhidev? # USB Wacom tablet wsmouse* at uwacom? mux 0 +ugaomon* at uhidev? # USB Gaomon tablet +wsmouse* at ugaomon? mux 0 ukbd* at uhidev? # USB keyboard wskbd* at ukbd? mux 1 ucycom* at uhidev? # Cypress serial diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb index 88fff1b7c..bb55fc560 100644 --- a/sys/dev/usb/files.usb +++ b/sys/dev/usb/files.usb @@ -485,6 +485,11 @@ device uwacom: hid, hidms, wsmousedev attach uwacom at uhidbus file dev/usb/uwacom.c uwacom +# Gaomon tablets +device ugaomon: hid, hidms, wsmousedev +attach ugaomon at uhidbus +file dev/usb/ugaomon.c ugaomon + attach bwfm at uhub with bwfm_usb: firmload file dev/usb/if_bwfm_usb.c bwfm_usb diff --git a/sys/dev/usb/ugaomon.c b/sys/dev/usb/ugaomon.c new file mode 100644 index 000000000..71fd106c0 --- /dev/null +++ b/sys/dev/usb/ugaomon.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2026 Andrew Kloet + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +struct ugaomon_softc { + struct uhidev sc_hdev; + struct hidms sc_ms; +}; + +struct cfdriver ugaomon_cd = { + NULL, "ugaomon", DV_DULL +}; + +const struct usb_devno ugaomon_devs[] = { + { USB_VENDOR_GAOMON, USB_PRODUCT_GAOMON_S620 }, +}; + +int ugaomon_match(struct device *, void *, void *); +void ugaomon_attach(struct device *, struct device *, void *); +int ugaomon_detach(struct device *, int); +void ugaomon_intr(struct uhidev *, void *, u_int); +int ugaomon_enable(void *); +void ugaomon_disable(void *); +int ugaomon_ioctl(void *, u_long, caddr_t, int, struct proc *); + +const struct cfattach ugaomon_ca = { + sizeof(struct ugaomon_softc), ugaomon_match, ugaomon_attach, ugaomon_detach +}; + +const struct wsmouse_accessops ugaomon_accessops = { + ugaomon_enable, + ugaomon_ioctl, + ugaomon_disable, +}; + +int +ugaomon_match(struct device *parent, void *match, void *aux) +{ + struct uhidev_attach_arg *uha = aux; + int size; + void *desc; + + if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) + return (UMATCH_NONE); + + if (usb_lookup(ugaomon_devs, uha->uaa->vendor, + uha->uaa->product) == NULL) + return (UMATCH_NONE); + + uhidev_get_report_desc(uha->parent, &desc, &size); + + if (hid_is_collection(desc, size, uha->reportid, + HID_USAGE2(HUP_DIGITIZERS, HUD_DIGITIZER)) || + hid_is_collection(desc, size, uha->reportid, + HID_USAGE2(HUP_DIGITIZERS, HUD_PEN))) + return (UMATCH_IFACECLASS); + if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), + uha->reportid, hid_input, NULL, NULL) && + !hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, HUD_PEN), + uha->reportid, hid_input, NULL, NULL)) + return (UMATCH_NONE); + + return (UMATCH_IFACECLASS); +} + +void +ugaomon_attach(struct device *parent, struct device *self, void *aux) +{ + struct ugaomon_softc *sc = (struct ugaomon_softc *)self; + struct hidms *ms = &sc->sc_ms; + struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; + struct usb_attach_arg *uaa = uha->uaa; + + sc->sc_hdev.sc_intr = ugaomon_intr; + sc->sc_hdev.sc_parent = uha->parent; + sc->sc_hdev.sc_udev = uaa->device; + sc->sc_hdev.sc_report_id = uha->reportid; + + usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); + + ms->sc_device = self; + ms->sc_rawmode = 1; + ms->sc_flags = HIDMS_ABSX | HIDMS_ABSY; + ms->sc_num_buttons = 3; + + ms->sc_tsscale.minx = 0; + ms->sc_tsscale.miny = 0; + + switch(uha->uaa->product) { + case USB_PRODUCT_GAOMON_S620: + ms->sc_tsscale.maxx = 33020; + ms->sc_tsscale.maxy = 20320; + break; + } + + hidms_attach(ms, &ugaomon_accessops); +} + +int +ugaomon_detach(struct device *self, int flags) +{ + struct ugaomon_softc *sc = (struct ugaomon_softc *)self; + struct hidms *ms = &sc->sc_ms; + + return hidms_detach(ms, flags); +} + +void +ugaomon_intr(struct uhidev *addr, void *buf, u_int len) +{ + struct ugaomon_softc *sc = (struct ugaomon_softc *)addr; + struct hidms *ms = &sc->sc_ms; + uint8_t *data = (uint8_t *)buf; + u_int32_t buttons = 0; + int status, x, y, pressure; + + if (ms->sc_enabled == 0) + return; + + if (len < 9) + return; + + status = data[0]; + x = data[1] | (data[2] << 8); + y = data[3] | (data[4] << 8); + pressure = data[5] | (data[6] << 8); + + if (status & 0x01) + buttons |= 1; + if (status == 0xc2 || status == 0xc3) + buttons |= 2; + else if (status == 0xc4 || status == 0xc5) + buttons |= 4; + + wsmouse_position(ms->sc_wsmousedev, x, y); + wsmouse_buttons(ms->sc_wsmousedev, buttons); + wsmouse_input_sync(ms->sc_wsmousedev); +} + +int +ugaomon_enable(void *v) +{ + struct ugaomon_softc *sc = v; + struct hidms *ms = &sc->sc_ms; + int rv; + + if (usbd_is_dying(sc->sc_hdev.sc_udev)) + return EIO; + + if ((rv = hidms_enable(ms)) != 0) + return rv; + + return uhidev_open(&sc->sc_hdev); +} + +void +ugaomon_disable(void *v) +{ + struct ugaomon_softc *sc = v; + struct hidms *ms = &sc->sc_ms; + + hidms_disable(ms); + uhidev_close(&sc->sc_hdev); +} + +int +ugaomon_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct ugaomon_softc *sc = v; + struct hidms *ms = &sc->sc_ms; + int rc; + + switch (cmd) { + case WSMOUSEIO_GTYPE: + *(u_int *)data = WSMOUSE_TYPE_TPANEL; + return 0; + } + + rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); + if (rc != -1) + return rc; + + return hidms_ioctl(ms, cmd, data, flag, p); +} diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs index 825aafb61..b8f3a0632 100644 --- a/sys/dev/usb/usbdevs +++ b/sys/dev/usb/usbdevs @@ -641,6 +641,7 @@ vendor ARDUINO 0x2341 Arduino SA vendor TPLINK 0x2357 TP-Link vendor WMR 0x2405 West Mountain Radio vendor TRIPPLITE 0x2478 Tripp-Lite +vendor GAOMON 0x256c Gaomon vendor HAILUCK 0x258a HAILUCK Co., Ltd vendor ARUBA 0x2626 Aruba vendor XIAOMI 0x2717 Xiaomi @@ -2159,6 +2160,9 @@ product FUJITSUCOMP VIRTETH 0xa4a2 Virtual Eth Device /* Fushicai */ product FUSHICAI USBTV007 0x3002 Fushicai Audio-Video Grabber +/* Gaomon */ +product GAOMON S620 0x006f S620 + /* Garmin International products */ product GARMIN GPSMAP60CSX 0x0003 GPSmap 60Csx product GARMIN IQUE3600 0x0004 Ique 3600