Download raw body.
ugaomon: Support for Gaomon S620
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 <andrew@kloet.net>
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/uhidev.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsmousevar.h>
+
+#include <dev/hid/hidmsvar.h>
+
+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
ugaomon: Support for Gaomon S620