ublink(4), led(4) and ledctl(1)

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

ublink(4), led(4) and ledctl(1)

Patrick Wildt-3
Hi,

I have a ThingM blink(1) USB RGB device that shows up as uhid(4).
The tooling is "interesting", especially with all those libusb and
HID libraries doing the abstraction.  This introduces ublink(4), a
dedicated kernel driver for that device.  There are two LEDs on the
device, which can be modified seperately.  The firmware is impress-
ive and provides features like playing sequences and adjusting how
long it should take to fade from one colour to another.  Obviously
this also increases the complexity of the tools involved to toggle
those RGB LEDs.  Thus, the driver is kept simple (for now).

In addition to providing a way to set RGB LEDs, we can also use it
as a watchdog.  If we don't "tickle" it in a specific timeframe it
will play a (unless otherwise programmed) random sequence, which for
instance can be used to see that the machine has paniced.  This has
been quite useful while debugging the USB stack, because once the
magic sequence started you know that you're in deep trouble.  All
other features are unimplemented.  Gamma correction would be nice
to have.

Since there is no abstraction layer for LEDs yet, this also intro-
duces led(4), which attaches to ublink(4), and provides /dev/ledX.

uhidev1 at uhub0 port 3 configuration 1 interface 0 "ThingM blink(1) mk2" rev 2.00/0.02 addr 2
uhidev1: iclass 3/0, 1 report id
ublink0 at uhidev1 reportid 1
led0 at ublink0: 2 LEDs

led(4) can be improved even further.  We can attach the ThinkPad
LEDs to it to control it.  There are also plenty of mouses that
have controllable RGBs. We could add "human readable descrip-
tions", for instance "upper side LED" or "docking station LED",
so that users can find out more easily which LED they have to
toggle.  A fading or blinking mechanism, including hardware off-
loading, can probably be added as well.

To be able to control those devices, there's ledctl(1), a simple
program that can turn on/off /dev/ledX devices and also set RGB
colours.

I only did the MAKEDEV stuff for amd64, and also there are no
manpages yet.  Also I haven't run it through make build yet,
sorry.  And I don't often do userland tools, so feel free to
let me know how to improve it.

Thanks,
Patrick

diff --git a/etc/MAKEDEV.common b/etc/MAKEDEV.common
index 6c48a46a74a..1ee654feb4b 100644
--- a/etc/MAKEDEV.common
+++ b/etc/MAKEDEV.common
@@ -519,6 +519,8 @@ __devitem(ipmi, ipmi*, IPMI BMC access)dnl
 _mkdev(ipmi, ipmi*, {-M ipmi$U c major_ipmi_c $U 600-})dnl
 __devitem(gpio, gpio*, General Purpose Input/Output)dnl
 _mcdev(gpio, gpio*, gpio, {-major_gpio_c-}, 600)dnl
+__devitem(led, led*, Generic LED devices)dnl
+_mcdev({-led-}, led*, {-led-}, {-major_led_c-}, 660)dnl
 __devitem(vmm, vmm, Virtual Machine Monitor)dnl
 _mkdev(vmm, vmm, {-M vmm c major_vmm_c 0 600-})dnl
 __devitem(pvbus, pvbus*, paravirtual device tree root)dnl
diff --git a/etc/etc.amd64/MAKEDEV b/etc/etc.amd64/MAKEDEV
index 0bf05aa2fb7..63920f63329 100644
--- a/etc/etc.amd64/MAKEDEV
+++ b/etc/etc.amd64/MAKEDEV
@@ -78,6 +78,7 @@
 # gpr* GPR400 smartcard reader
 # hotplug devices hot plugging
 # ipmi* IPMI BMC access
+# led* Generic LED devices
 # nvram NVRAM access
 # kcov Kernel code coverage tracing
 # pci* PCI bus devices
@@ -329,6 +330,10 @@ nvram)
  M nvram c 85 0 440 kmem
  ;;
 
+led*)
+ M led$U c 55 $U 660
+ ;;
+
 ipmi*)
  M ipmi$U c 96 $U 600
  ;;
diff --git a/etc/etc.amd64/MAKEDEV.md b/etc/etc.amd64/MAKEDEV.md
index 1aa4f673524..03f696e8c5a 100644
--- a/etc/etc.amd64/MAKEDEV.md
+++ b/etc/etc.amd64/MAKEDEV.md
@@ -76,6 +76,7 @@ _DEV(gpr, 80)
 _DEV(hotplug, 82)
 _DEV(ipmi, 96)
 dnl _DEV(joy, 26)
+_DEV(led, 55)
 _DEV(nvram, 85)
 _DEV(kcov, 19)
 _DEV(pci, 72)
diff --git a/share/man/man8/man8.amd64/MAKEDEV.8 b/share/man/man8/man8.amd64/MAKEDEV.8
index f43b5cc341f..a1c08a9738e 100644
--- a/share/man/man8/man8.amd64/MAKEDEV.8
+++ b/share/man/man8/man8.amd64/MAKEDEV.8
@@ -1,10 +1,10 @@
-.\" $OpenBSD: MAKEDEV.8,v 1.83 2018/08/19 11:51:04 anton Exp $
+.\" $OpenBSD$
 .\"
 .\" THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.
 .\" generated from:
 .\"
 .\" OpenBSD: etc.amd64/MAKEDEV.md,v 1.70 2018/08/19 11:42:33 anton Exp
-.\" OpenBSD: MAKEDEV.common,v 1.100 2018/08/19 11:42:33 anton Exp
+.\" OpenBSD: MAKEDEV.common,v 1.103 2019/06/11 14:48:56 jcs Exp
 .\" OpenBSD: MAKEDEV.man,v 1.9 2017/06/06 08:11:23 tb Exp
 .\" OpenBSD: MAKEDEV.mansub,v 1.2 2004/02/20 19:13:01 miod Exp
 .\"
@@ -23,7 +23,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: August 19 2018 $
+.Dd $Mdocdate: June 6 2017 $
 .Dt MAKEDEV 8 amd64
 .Os
 .Sh NAME
@@ -230,6 +230,9 @@ devices hot plugging, see
 .It Ar ipmi*
 IPMI BMC access, see
 .Xr ipmi 4 .
+.It Ar led*
+Generic LED devices, see
+.Xr led 4 .
 .It Ar nvram
 NVRAM access, see
 .Xr nvram 4 .
diff --git a/sys/arch/amd64/amd64/conf.c b/sys/arch/amd64/amd64/conf.c
index a8c10783f11..307287ea096 100644
--- a/sys/arch/amd64/amd64/conf.c
+++ b/sys/arch/amd64/amd64/conf.c
@@ -163,6 +163,7 @@ cdev_decl(nvram);
 cdev_decl(drm);
 #include "viocon.h"
 cdev_decl(viocon);
+#include "led.h"
 
 #include "wsdisplay.h"
 #include "wskbd.h"
@@ -247,7 +248,7 @@ struct cdevsw cdevsw[] =
  cdev_midi_init(NMIDI,midi), /* 52: MIDI I/O */
  cdev_notdef(), /* 53 was: sequencer I/O */
  cdev_notdef(), /* 54 was: RAIDframe disk driver */
- cdev_notdef(), /* 55: */
+ cdev_led_init(NLED,led), /* 55: led(4) */
  /* The following slots are reserved for isdn4bsd. */
  cdev_notdef(), /* 56: i4b main device */
  cdev_notdef(), /* 57: i4b control device */
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index 64d390905d9..1fd43d81507 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -280,6 +280,8 @@ ucom* at ucycom?
 uslhcom* at uhidev? # Silicon Labs CP2110 USB HID UART
 ucom* at uslhcom?
 uhid* at uhidev? # USB generic HID support
+ublink* at uhidev? # ThingM blink(1)
+led* at ublink?
 upd* at uhidev? # USB Power Devices sensors
 aue* at uhub? # ADMtek AN986 Pegasus Ethernet
 atu* at uhub? # Atmel AT76c50x based 802.11b
diff --git a/sys/conf/files b/sys/conf/files
index 0cef7842919..ab0db72178a 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -56,6 +56,11 @@ define i2c_bitbang
 # 1-Wire bus bit-banging
 define onewire_bitbang
 
+define led {}
+device led
+file dev/ic/led.c led needs-flag
+attach led at led
+
 # net device attributes - we have generic code for ether(net)
 define crypto
 define ether
diff --git a/sys/dev/ic/led.c b/sys/dev/ic/led.c
new file mode 100644
index 00000000000..3bc8c214394
--- /dev/null
+++ b/sys/dev/ic/led.c
@@ -0,0 +1,139 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2019 Patrick Wildt <[hidden email]>
+ *
+ * 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 DISCLAIMS 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/kernel.h>
+#include <sys/vnode.h>
+#include <sys/conf.h>
+
+#include <dev/ic/led.h>
+
+#ifdef LED_DEBUG
+#define DPRINTF(x) do { if (leddebug) printf x; } while (0)
+#define DPRINTFN(n,x) do { if (leddebug>(n)) printf x; } while (0)
+int leddebug = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct led_softc {
+ struct device sc_dev;
+ unsigned int sc_nled;
+
+ void *sc_cookie;
+ struct led_ctl *sc_ctl;
+};
+
+#define LEDUNIT(dev) (minor(dev))
+
+int led_match(struct device *, void *, void *);
+void led_attach(struct device *, struct device *, void *);
+int led_detach(struct device *, int);
+
+struct cfdriver led_cd = {
+ NULL, "led", DV_DULL
+};
+
+struct cfattach led_ca = {
+ sizeof(struct led_softc), led_match, led_attach, led_detach,
+};
+
+int
+led_match(struct device *parent, void *match, void *aux)
+{
+ return 1;
+}
+
+void
+led_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct led_softc *sc = (struct led_softc *)self;
+ struct led_attach_args *laa = (struct led_attach_args *)aux;
+
+ sc->sc_cookie = laa->cookie;
+ sc->sc_ctl = laa->ctl;
+ sc->sc_nled = laa->nled;
+
+ printf(": %u LEDs\n", sc->sc_nled);
+}
+
+int
+led_detach(struct device *self, int flags)
+{
+ int maj, mn;
+
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if (cdevsw[maj].d_open == ledopen)
+ break;
+
+ /* Nuke the vnodes for any open instances (calls close). */
+ mn = self->dv_unit;
+ vdevgone(maj, mn, mn, VCHR);
+
+ return 0;
+}
+
+int
+ledopen(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct led_softc *sc;
+
+ if (LEDUNIT(dev) >= led_cd.cd_ndevs)
+ return ENXIO;
+ sc = led_cd.cd_devs[LEDUNIT(dev)];
+ if (sc == NULL)
+ return ENXIO;
+
+ return 0;
+}
+
+int
+ledclose(dev_t dev, int flag, int mode, struct proc *p)
+{
+ return 0;
+}
+
+int
+ledioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
+{
+ struct led_softc *sc;
+ struct led_set *set;
+
+ if (LEDUNIT(dev) >= led_cd.cd_ndevs)
+ return ENXIO;
+ sc = led_cd.cd_devs[LEDUNIT(dev)];
+ if (sc == NULL)
+ return ENXIO;
+
+ switch (cmd) {
+ case LED_GET_NUM:
+ *(unsigned int *)addr = sc->sc_nled;
+ break;
+ case LED_SET:
+ set = (struct led_set *)addr;
+ if (set->led >= sc->sc_nled)
+ return ENXIO;
+ sc->sc_ctl->set_led(sc->sc_cookie, set->led, set->rgb);
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
diff --git a/sys/dev/ic/led.h b/sys/dev/ic/led.h
new file mode 100644
index 00000000000..d738692e35f
--- /dev/null
+++ b/sys/dev/ic/led.h
@@ -0,0 +1,36 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2019 Patrick Wildt <[hidden email]>
+ *
+ * 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 DISCLAIMS 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/ioctl.h>
+
+struct led_ctl {
+ void (*set_led)(void *, unsigned int led, uint32_t);
+};
+
+struct led_attach_args {
+ void *cookie;
+ struct led_ctl *ctl;
+ unsigned int nled;
+};
+
+struct led_set {
+ unsigned int led;
+ uint32_t rgb;
+};
+
+#define LED_GET_NUM _IOR ('L', 0, unsigned int)
+#define LED_SET _IOW ('L', 1, struct led_set)
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb
index 2a68642a9a9..12a6a087824 100644
--- a/sys/dev/usb/files.usb
+++ b/sys/dev/usb/files.usb
@@ -468,3 +468,8 @@ file dev/usb/uwacom.c uwacom
 
 attach bwfm at uhub with bwfm_usb: firmload
 file dev/usb/if_bwfm_usb.c bwfm_usb
+
+# ThingM blink(1)
+device ublink: led
+attach ublink at uhidbus
+file dev/usb/ublink.c ublink
diff --git a/sys/dev/usb/ublink.c b/sys/dev/usb/ublink.c
new file mode 100644
index 00000000000..4b749c6a88e
--- /dev/null
+++ b/sys/dev/usb/ublink.c
@@ -0,0 +1,169 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2019 Patrick Wildt <[hidden email]>
+ *
+ * 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 DISCLAIMS 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 <sys/timeout.h>
+
+#include <dev/ic/led.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/uhidev.h>
+
+#define BLINK_FADE_MS 300
+#define BLINK_WATCHDOG_MS 15000
+#define BLINK_TIMEOUT_MS 10000
+
+#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
+
+#ifdef UBLINK_DEBUG
+#define DPRINTF(x) printf x
+#else
+#define DPRINTF(x)
+#endif
+
+struct ublink_softc {
+ struct uhidev sc_hdev;
+ struct usbd_device *sc_udev;
+
+ struct timeout sc_wdog_to;
+};
+
+int ublink_match(struct device *, void *, void *);
+void ublink_attach(struct device *, struct device *, void *);
+int ublink_detach(struct device *, int);
+
+void ublink_watchdog(void *);
+
+void ublink_set_led(void *, unsigned int, uint32_t);
+
+struct led_ctl ublink_ctl = {
+ ublink_set_led
+};
+
+struct cfdriver ublink_cd = {
+ NULL, "ublink", DV_DULL
+};
+
+struct cfattach ublink_ca = {
+ sizeof(struct ublink_softc), ublink_match, ublink_attach, ublink_detach
+};
+
+struct usb_devno ublink_devs[] = {
+ { USB_VENDOR_THINGM, USB_PRODUCT_THINGM_BLINK1 },
+};
+
+int
+ublink_match(struct device *parent, void *match, void *aux)
+{
+ struct uhidev_attach_arg *uha = aux;
+
+ if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
+ return UMATCH_NONE;
+
+ if (usb_lookup(ublink_devs, uha->uaa->vendor,
+    uha->uaa->product) == NULL)
+ return UMATCH_NONE;
+
+ return UMATCH_VENDOR_PRODUCT;
+}
+
+void
+ublink_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct ublink_softc *sc = (struct ublink_softc *)self;
+ struct uhidev_attach_arg *uha = aux;
+ struct led_attach_args la;
+
+ sc->sc_udev = uha->parent->sc_udev;
+ sc->sc_hdev.sc_parent = uha->parent;
+ sc->sc_hdev.sc_report_id = uha->reportid;
+
+ printf("\n");
+
+ /* Turn on watchdog and repeatedly re-arm. */
+ timeout_set(&sc->sc_wdog_to, ublink_watchdog, sc);
+ timeout_add_msec(&sc->sc_wdog_to, BLINK_TIMEOUT_MS);
+
+ /* Attach led(4). */
+ la.cookie = self;
+ la.ctl = &ublink_ctl;
+ la.nled = 2;
+ config_found(self, &la, NULL);
+}
+
+int
+ublink_detach(struct device *self, int flags)
+{
+ struct ublink_softc *sc = (struct ublink_softc *)self;
+
+ timeout_del(&sc->sc_wdog_to);
+ return config_detach_children(self, flags);
+}
+
+void
+ublink_watchdog(void *self)
+{
+ struct ublink_softc *sc = self;
+ uint8_t buf[7];
+
+ if (usbd_is_dying(sc->sc_udev))
+ return;
+
+ /* "serverdown" */
+ buf[0] = 'D';
+ buf[1] = 1;
+ buf[2] = ((BLINK_WATCHDOG_MS / 10) >> 8);
+ buf[3] = ((BLINK_WATCHDOG_MS / 10) % 0xff);
+ buf[4] = 1;
+ buf[5] = 0;
+ buf[6] = 15;
+
+ uhidev_set_report_async(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
+     sc->sc_hdev.sc_report_id, buf, sizeof(buf));
+ timeout_add_msec(&sc->sc_wdog_to, BLINK_TIMEOUT_MS);
+}
+
+void
+ublink_set_led(void *self, unsigned int led, uint32_t rgb)
+{
+ struct ublink_softc *sc = self;
+ uint8_t buf[7];
+
+ if (usbd_is_dying(sc->sc_udev))
+ return;
+
+ if (led > 1)
+ return;
+
+ /* fade to RGB */
+ buf[0] = 'c';
+ buf[1] = (rgb >> 16) & 0xff;
+ buf[2] = (rgb >> 8) & 0xff;
+ buf[3] = rgb & 0xff;
+ buf[4] = ((BLINK_FADE_MS / 10) >> 8);
+ buf[5] = (BLINK_FADE_MS / 10) % 0xff;
+ buf[6] = led + 1;
+
+ uhidev_set_report_async(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
+     sc->sc_hdev.sc_report_id, buf, sizeof(buf));
+}
diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs
index c506a7bcc28..afd673b6b7e 100644
--- a/sys/dev/usb/usbdevs
+++ b/sys/dev/usb/usbdevs
@@ -630,6 +630,7 @@ vendor TRIPPLITE 0x2478 Tripp-Lite
 vendor ARUBA 0x2626 Aruba
 vendor XIAOMI 0x2717 Xiaomi
 vendor NHJ 0x2770 NHJ
+vendor THINGM 0x27b8 ThingM
 vendor ASUSTEK 0x2821 ASUSTeK Computer
 vendor PLANEX 0x2c02 Planex Communications
 vendor LINKINSTRUMENTS 0x3195 Link Instruments
@@ -4249,6 +4250,9 @@ product TI NEXII 0x5409 Nex II Digital
 product TI MSP430_JTAG 0xf430 MSP-FET430UIF JTAG
 product TI MSP430 0xf432 MSP-FET430UIF
 
+/* ThingM products */
+product THINGM BLINK1 0x01ed blink(1)
+
 /* Thrustmaster products */
 product THRUST FUSION_PAD 0xa0a3 Fusion Digital Gamepad
 
diff --git a/sys/dev/usb/usbdevs.h b/sys/dev/usb/usbdevs.h
index e520ee87431..b74272773b0 100644
--- a/sys/dev/usb/usbdevs.h
+++ b/sys/dev/usb/usbdevs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: usbdevs.h,v 1.714 2019/12/07 08:47:20 kevlo Exp $ */
+/* $OpenBSD$ */
 
 /*
  * THIS FILE IS AUTOMATICALLY GENERATED.  DO NOT EDIT.
@@ -637,6 +637,7 @@
 #define USB_VENDOR_ARUBA 0x2626 /* Aruba */
 #define USB_VENDOR_XIAOMI 0x2717 /* Xiaomi */
 #define USB_VENDOR_NHJ 0x2770 /* NHJ */
+#define USB_VENDOR_THINGM 0x27b8 /* ThingM */
 #define USB_VENDOR_ASUSTEK 0x2821 /* ASUSTeK Computer */
 #define USB_VENDOR_PLANEX 0x2c02 /* Planex Communications */
 #define USB_VENDOR_LINKINSTRUMENTS 0x3195 /* Link Instruments */
@@ -4256,6 +4257,9 @@
 #define USB_PRODUCT_TI_MSP430_JTAG 0xf430 /* MSP-FET430UIF JTAG */
 #define USB_PRODUCT_TI_MSP430 0xf432 /* MSP-FET430UIF */
 
+/* ThingM products */
+#define USB_PRODUCT_THINGM_BLINK1 0x01ed /* blink(1) */
+
 /* Thrustmaster products */
 #define USB_PRODUCT_THRUST_FUSION_PAD 0xa0a3 /* Fusion Digital Gamepad */
 
diff --git a/sys/dev/usb/usbdevs_data.h b/sys/dev/usb/usbdevs_data.h
index 21b61b04d5a..b2a46a8ef79 100644
--- a/sys/dev/usb/usbdevs_data.h
+++ b/sys/dev/usb/usbdevs_data.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: usbdevs_data.h,v 1.708 2019/12/07 08:47:20 kevlo Exp $ */
+/* $OpenBSD$ */
 
 /*
  * THIS FILE IS AUTOMATICALLY GENERATED.  DO NOT EDIT.
@@ -10901,6 +10901,10 @@ const struct usb_known_product usb_known_products[] = {
     USB_VENDOR_TI, USB_PRODUCT_TI_MSP430,
     "MSP-FET430UIF",
  },
+ {
+    USB_VENDOR_THINGM, USB_PRODUCT_THINGM_BLINK1,
+    "blink(1)",
+ },
  {
     USB_VENDOR_THRUST, USB_PRODUCT_THRUST_FUSION_PAD,
     "Fusion Digital Gamepad",
@@ -14041,6 +14045,10 @@ const struct usb_known_vendor usb_known_vendors[] = {
     USB_VENDOR_NHJ,
     "NHJ",
  },
+ {
+    USB_VENDOR_THINGM,
+    "ThingM",
+ },
  {
     USB_VENDOR_ASUSTEK,
     "ASUSTeK Computer",
diff --git a/sys/sys/conf.h b/sys/sys/conf.h
index 48118f7f670..9782ffab7be 100644
--- a/sys/sys/conf.h
+++ b/sys/sys/conf.h
@@ -432,6 +432,13 @@ extern struct cdevsw cdevsw[];
  (dev_type_stop((*))) enodev, 0, selfalse, \
  (dev_type_mmap((*))) enodev }
 
+/* open, close, ioctl */
+#define cdev_led_init(c,n) { \
+ dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \
+ (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \
+ (dev_type_stop((*))) enodev, 0, selfalse, \
+ (dev_type_mmap((*))) enodev }
+
 /* open, close, ioctl */
 #define       cdev_bio_init(c,n) { \
  dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \
@@ -579,6 +586,8 @@ cdev_decl(diskmap);
 
 cdev_decl(bpf);
 
+cdev_decl(led);
+
 cdev_decl(pf);
 
 cdev_decl(tun);
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index 35a3c00d69f..79b180eefcc 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -11,8 +11,8 @@ SUBDIR= apply arch at aucat audioctl awk banner \
  find fgen finger fmt fold from fstat ftp gencat getcap \
  getconf getent getopt gprof grep head hexdump htpasswd id indent \
  infocmp ipcrm ipcs \
- join jot kdump keynote ktrace lam last lastcomm ldap leave less lex \
- libtool lndir \
+ join jot kdump keynote ktrace lam last lastcomm ldap leave ledctl \
+ less lex libtool lndir \
  locale locate lock logger login logname look lorder \
  m4 mail make mandoc mesg mg \
  midicat mixerctl mkdep mklocale mktemp nc netstat \
diff --git a/usr.bin/ledctl/Makefile b/usr.bin/ledctl/Makefile
new file mode 100644
index 00000000000..2d19fa44551
--- /dev/null
+++ b/usr.bin/ledctl/Makefile
@@ -0,0 +1,8 @@
+# $OpenBSD$
+
+PROG= ledctl
+SRCS= ledctl.c
+
+NOMAN=
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ledctl/ledctl.c b/usr.bin/ledctl/ledctl.c
new file mode 100644
index 00000000000..90dc2a531e3
--- /dev/null
+++ b/usr.bin/ledctl/ledctl.c
@@ -0,0 +1,94 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2019 Patrick Wildt <[hidden email]>
+ *
+ * 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 DISCLAIMS 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/types.h>
+
+#include <dev/ic/led.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: %s -f device -n led [on|off|rgb]\n",
+    getprogname());
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *dev, *errstr;
+ unsigned int led = 0;
+ struct led_set set;
+ int ch, fd;
+ long rgb;
+ char *ep;
+
+ dev = "/dev/led0";
+ while ((ch = getopt(argc, argv, "f:n:")) != -1) {
+ switch (ch) {
+ case 'f':
+ dev = optarg;
+ break;
+ case 'n':
+ led = strtonum(optarg, 0, 64, &errstr);
+ if (errstr != NULL)
+ errx(1, "led index is %s: %s", errstr, optarg);
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (dev == NULL || argc != 1)
+ usage();
+
+ fd = open(dev, O_RDWR);
+ if (fd == -1)
+ err(1, "%s", dev);
+
+ memset(&set, 0, sizeof(set));
+ set.led = led;
+ if (strcmp(argv[0], "on") == 0) {
+ set.rgb = 0xffffff;
+ ioctl(fd, LED_SET, &set);
+ } else if (strcmp(argv[0], "off") == 0) {
+ set.rgb = 0x000000;
+ ioctl(fd, LED_SET, &led);
+ } else {
+ errno = 0;
+ rgb = strtol(argv[0], &ep, 16);
+ if (*argv[0] == '\0' || *ep != '\0')
+ errx(1, "led rgb is %s: %s", errstr, argv[0]);
+ if (errno == ERANGE && (rgb == LONG_MAX || rgb == LONG_MIN))
+ errx(1, "led rgb is %s: %s", errstr, argv[0]);
+ set.rgb = rgb;
+ ioctl(fd, LED_SET, &set);
+ }
+
+ exit(0);
+}

Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Theo de Raadt-2
+__devitem(led, led*, Generic LED devices)dnl
+_mcdev({-led-}, led*, {-led-}, {-major_led_c-}, 660)dnl

Mode 660 leads me to ask -- what is the group?

If you say wheel, that is related to the diff I sent recently.
wheel marks who can su to root.  Somehow it has accidentally
gained additional powers over time, which is a disaster because
we have people running JIT in browsers who gain those powers.

I believe we should start removing those powers one by one.

Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Patrick Wildt-3
On Fri, Dec 13, 2019 at 02:43:25PM -0700, Theo de Raadt wrote:
> +__devitem(led, led*, Generic LED devices)dnl
> +_mcdev({-led-}, led*, {-led-}, {-major_led_c-}, 660)dnl
>
> Mode 660 leads me to ask -- what is the group?

I don't know, I figured you could help me there.  I guess the
default can be 600 and then, like you wrote in the other mail,
people will have to chmod/chown it after every upgrade.

Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Theo de Raadt-2
Patrick Wildt <[hidden email]> wrote:

> On Fri, Dec 13, 2019 at 02:43:25PM -0700, Theo de Raadt wrote:
> > +__devitem(led, led*, Generic LED devices)dnl
> > +_mcdev({-led-}, led*, {-led-}, {-major_led_c-}, 660)dnl
> >
> > Mode 660 leads me to ask -- what is the group?
>
> I don't know, I figured you could help me there.  I guess the
> default can be 600 and then, like you wrote in the other mail,
> people will have to chmod/chown it after every upgrade.

Yeah.  Or utilize it in a wrapper.

Tough shit :)

Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Jonathan Gray-11
In reply to this post by Patrick Wildt-3
On Fri, Dec 13, 2019 at 10:34:59PM +0100, Patrick Wildt wrote:

> Hi,
>
> I have a ThingM blink(1) USB RGB device that shows up as uhid(4).
> The tooling is "interesting", especially with all those libusb and
> HID libraries doing the abstraction.  This introduces ublink(4), a
> dedicated kernel driver for that device.  There are two LEDs on the
> device, which can be modified seperately.  The firmware is impress-
> ive and provides features like playing sequences and adjusting how
> long it should take to fade from one colour to another.  Obviously
> this also increases the complexity of the tools involved to toggle
> those RGB LEDs.  Thus, the driver is kept simple (for now).
>
> In addition to providing a way to set RGB LEDs, we can also use it
> as a watchdog.  If we don't "tickle" it in a specific timeframe it
> will play a (unless otherwise programmed) random sequence, which for
> instance can be used to see that the machine has paniced.  This has
> been quite useful while debugging the USB stack, because once the
> magic sequence started you know that you're in deep trouble.  All
> other features are unimplemented.  Gamma correction would be nice
> to have.
>
> Since there is no abstraction layer for LEDs yet, this also intro-
> duces led(4), which attaches to ublink(4), and provides /dev/ledX.

There is the machdep.led_blink sysctl CPU_LED_BLINK implemented by
multiple architectures to blink based on load average.

See
man4.hppa/lcd.4:.Ar machdep.led_blink
man4.sparc64/auxio.4:.Ar machdep.led_blink
man4.sparc64/clkbrd.4:.Ar machdep.led_blink
man4.sparc64/fhc.4:.Ar machdep.led_blink
man4.sparc64/led.4:.Ar machdep.led_blink
man4.sparc64/ppm.4:.Ar machdep.led_blink

sys/arch/alpha/include/cpu.h: { "led_blink", CTLTYPE_INT } \
sys/arch/hppa/include/cpu.h: { "led_blink", CTLTYPE_INT }, \
sys/arch/landisk/include/cpu.h: { "led_blink", CTLTYPE_INT } \
sys/arch/sparc64/include/cpu.h: { "led_blink", CTLTYPE_INT }, \

Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Jan Klemkow
In reply to this post by Patrick Wildt-3
On Fri, Dec 13, 2019 at 10:34:59PM +0100, Patrick Wildt wrote:

Nice project.  If this makes it into the tree then I could rewrite [1].
[1]: https://github.com/younix/g403led

> And I don't often do userland tools, so feel free to
> let me know how to improve it.
> ...
> +int
> +main(int argc, char **argv)
> +{
> ...
> + exit(0);
> +}

I think its better to return 0 in main() instead of calling the libc
function exit(3).

> ... and also there are no manpages yet.

Below is an example for ledctl.8.

Thanks,
Jan

.\" $OpenBSD$
.\"
.\" Copyright (c) 2019 Jan Klemkow <[hidden email]>
.\"
.\" 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 DISCLAIMS 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.
.\"
.Dd $Mdocdate: December 14 2019 $
.Dt LEDCTL 8
.Os
.Sh NAME
.Nm ledctl
.Nd sets the colors of LEDs
.Sh SYNOPSIS
.Nm
.Op Fl f Ar device
.Op Fl n Ar led
.Op Ar on|off|rgb
.Sh DESCRIPTION
The
.Nm
utility turns LEDs
.Ar on ,
.Ar off
or set their color state.
The color value
.Ar rgb
has to be in hex format.
The parameters
.Ar on
and
.Ar off
are shortcuts for the color values 0xffffff and 0x000000.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl f Ar device
.Nm
uses
the device file
.Ar device .
Default is
.Pa /dev/led0 .
.It Fl n Ar led
A
.Xr led 4
device may consists of multiple LEDs.
Sets
.Ar led
to the index of the right LED.
Default is 0.
.El
.Sh FILES
.Pa /dev/led0
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
Sets the first LED of
.Pa /dev/led0
to red:
.Bd -literal -offset indent
$ ledctl 0xff0000
.Ed
.Pp
Sets the second LED of
.Pa /dev/led0
to green:
.Bd -literal -offset indent
$ ledctl -n 1 0x00ff00
.Ed
.Pp
Sets the first LED of
.Pa /dev/led2
to blue:
.Bd -literal -offset indent
$ ledctl -f /dev/led2 0x0000ff
.Ed
.\".Sh SEE ALSO
.\".Xr led 4
.Sh AUTHORS
The
.Nm
program was written by
.An Patrick Wildt Aq Mt [hidden email] .

signature.asc (499 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Mark Kettenis
In reply to this post by Jonathan Gray-11
> Date: Sat, 14 Dec 2019 10:20:52 +1100
> From: Jonathan Gray <[hidden email]>
>
> On Fri, Dec 13, 2019 at 10:34:59PM +0100, Patrick Wildt wrote:
> > Hi,
> >
> > I have a ThingM blink(1) USB RGB device that shows up as uhid(4).
> > The tooling is "interesting", especially with all those libusb and
> > HID libraries doing the abstraction.  This introduces ublink(4), a
> > dedicated kernel driver for that device.  There are two LEDs on the
> > device, which can be modified seperately.  The firmware is impress-
> > ive and provides features like playing sequences and adjusting how
> > long it should take to fade from one colour to another.  Obviously
> > this also increases the complexity of the tools involved to toggle
> > those RGB LEDs.  Thus, the driver is kept simple (for now).
> >
> > In addition to providing a way to set RGB LEDs, we can also use it
> > as a watchdog.  If we don't "tickle" it in a specific timeframe it
> > will play a (unless otherwise programmed) random sequence, which for
> > instance can be used to see that the machine has paniced.  This has
> > been quite useful while debugging the USB stack, because once the
> > magic sequence started you know that you're in deep trouble.  All
> > other features are unimplemented.  Gamma correction would be nice
> > to have.
> >
> > Since there is no abstraction layer for LEDs yet, this also intro-
> > duces led(4), which attaches to ublink(4), and provides /dev/ledX.
>
> There is the machdep.led_blink sysctl CPU_LED_BLINK implemented by
> multiple architectures to blink based on load average.
>
> See
> man4.hppa/lcd.4:.Ar machdep.led_blink
> man4.sparc64/auxio.4:.Ar machdep.led_blink
> man4.sparc64/clkbrd.4:.Ar machdep.led_blink
> man4.sparc64/fhc.4:.Ar machdep.led_blink
> man4.sparc64/led.4:.Ar machdep.led_blink
> man4.sparc64/ppm.4:.Ar machdep.led_blink
>
> sys/arch/alpha/include/cpu.h: { "led_blink", CTLTYPE_INT }
> sys/arch/hppa/include/cpu.h: { "led_blink", CTLTYPE_INT },
> sys/arch/landisk/include/cpu.h: { "led_blink", CTLTYPE_INT }
> sys/arch/sparc64/include/cpu.h: { "led_blink", CTLTYPE_INT },
>

Those machines all have a more-or-less dedicated LED for this.  

What could be done is to make it possible to assign this function to
arbitrary LEDs through ledctl(8) such that we can have this
functionality on other architectures as well.

As patrick@ is probably aware of, the device trees on various
armv7/arm64 machines have LED devices in them as well.  These are
mostly controlled through GPIOs.  It'd make sense if the framework
allowed to control these LEDs as well at some point.

Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Kevin Lo
In reply to this post by Patrick Wildt-3
On Fri, Dec 13, 2019 at 10:34:59PM +0100, Patrick Wildt wrote:

>
> Hi,
>
> I have a ThingM blink(1) USB RGB device that shows up as uhid(4).
> The tooling is "interesting", especially with all those libusb and
> HID libraries doing the abstraction.  This introduces ublink(4), a
> dedicated kernel driver for that device.  There are two LEDs on the
> device, which can be modified seperately.  The firmware is impress-
> ive and provides features like playing sequences and adjusting how
> long it should take to fade from one colour to another.  Obviously
> this also increases the complexity of the tools involved to toggle
> those RGB LEDs.  Thus, the driver is kept simple (for now).
>
> In addition to providing a way to set RGB LEDs, we can also use it
> as a watchdog.  If we don't "tickle" it in a specific timeframe it
> will play a (unless otherwise programmed) random sequence, which for
> instance can be used to see that the machine has paniced.  This has
> been quite useful while debugging the USB stack, because once the
> magic sequence started you know that you're in deep trouble.  All
> other features are unimplemented.  Gamma correction would be nice
> to have.
>
> Since there is no abstraction layer for LEDs yet, this also intro-
> duces led(4), which attaches to ublink(4), and provides /dev/ledX.
>
> uhidev1 at uhub0 port 3 configuration 1 interface 0 "ThingM blink(1) mk2" rev 2.00/0.02 addr 2
> uhidev1: iclass 3/0, 1 report id
> ublink0 at uhidev1 reportid 1
> led0 at ublink0: 2 LEDs
>
> led(4) can be improved even further.  We can attach the ThinkPad
> LEDs to it to control it.  There are also plenty of mouses that
> have controllable RGBs. We could add "human readable descrip-
> tions", for instance "upper side LED" or "docking station LED",
> so that users can find out more easily which LED they have to
> toggle.  A fading or blinking mechanism, including hardware off-
> loading, can probably be added as well.
>
> To be able to control those devices, there's ledctl(1), a simple
> program that can turn on/off /dev/ledX devices and also set RGB
> colours.
>
> I only did the MAKEDEV stuff for amd64, and also there are no
> manpages yet.  Also I haven't run it through make build yet,
> sorry.  And I don't often do userland tools, so feel free to
> let me know how to improve it.

I tested with my ThingM blink(1) mk2:

uhidev2 at uhub3 port 4 configuration 1 interface 0 "ThingM blink(1) mk2" rev 2.00/0.02 addr 4
uhidev2: iclass 3/0, 1 report id
ublink0 at uhidev2 reportid 1
led0 at ublink0: 2 LEDs

Turning the LED on or off, and set it to blue (ledctl ff), all work for me.

> Thanks,
> Patrick

        Kevin

Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Stuart Henderson
In reply to this post by Patrick Wildt-3
On 2019/12/13 22:34, Patrick Wildt wrote:
> uhidev1 at uhub0 port 3 configuration 1 interface 0 "ThingM blink(1) mk2" rev 2.00/0.02 addr 2
> uhidev1: iclass 3/0, 1 report id
> ublink0 at uhidev1 reportid 1
> led0 at ublink0: 2 LEDs

Does this attachment break the normal software used with these?
(in ports as misc/blink1)

Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Patrick Wildt-3
On Mon, Dec 16, 2019 at 01:57:41PM +0000, Stuart Henderson wrote:
> On 2019/12/13 22:34, Patrick Wildt wrote:
> > uhidev1 at uhub0 port 3 configuration 1 interface 0 "ThingM blink(1) mk2" rev 2.00/0.02 addr 2
> > uhidev1: iclass 3/0, 1 report id
> > ublink0 at uhidev1 reportid 1
> > led0 at ublink0: 2 LEDs
>
> Does this attachment break the normal software used with these?
> (in ports as misc/blink1)

Of course it does!  This means that you can use the device only with
led(4), and that you can use all of the features provided by led(4),
and nothing else.  If you want to run it insecurely, with some external
tool accessing your USB devices, you will *have to* explicitly disable
the kernel driver.

Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Patrick Wildt-3
In reply to this post by Patrick Wildt-3
On Fri, Dec 13, 2019 at 10:34:59PM +0100, Patrick Wildt wrote:

> Hi,
>
> I have a ThingM blink(1) USB RGB device that shows up as uhid(4).
> The tooling is "interesting", especially with all those libusb and
> HID libraries doing the abstraction.  This introduces ublink(4), a
> dedicated kernel driver for that device.  There are two LEDs on the
> device, which can be modified seperately.  The firmware is impress-
> ive and provides features like playing sequences and adjusting how
> long it should take to fade from one colour to another.  Obviously
> this also increases the complexity of the tools involved to toggle
> those RGB LEDs.  Thus, the driver is kept simple (for now).
>
> In addition to providing a way to set RGB LEDs, we can also use it
> as a watchdog.  If we don't "tickle" it in a specific timeframe it
> will play a (unless otherwise programmed) random sequence, which for
> instance can be used to see that the machine has paniced.  This has
> been quite useful while debugging the USB stack, because once the
> magic sequence started you know that you're in deep trouble.  All
> other features are unimplemented.  Gamma correction would be nice
> to have.
>
> Since there is no abstraction layer for LEDs yet, this also intro-
> duces led(4), which attaches to ublink(4), and provides /dev/ledX.
>
> uhidev1 at uhub0 port 3 configuration 1 interface 0 "ThingM blink(1) mk2" rev 2.00/0.02 addr 2
> uhidev1: iclass 3/0, 1 report id
> ublink0 at uhidev1 reportid 1
> led0 at ublink0: 2 LEDs
>
> led(4) can be improved even further.  We can attach the ThinkPad
> LEDs to it to control it.  There are also plenty of mouses that
> have controllable RGBs. We could add "human readable descrip-
> tions", for instance "upper side LED" or "docking station LED",
> so that users can find out more easily which LED they have to
> toggle.  A fading or blinking mechanism, including hardware off-
> loading, can probably be added as well.
>
> To be able to control those devices, there's ledctl(1), a simple
> program that can turn on/off /dev/ledX devices and also set RGB
> colours.
>
> I only did the MAKEDEV stuff for amd64, and also there are no
> manpages yet.  Also I haven't run it through make build yet,
> sorry.  And I don't often do userland tools, so feel free to
> let me know how to improve it.
>
> Thanks,
> Patrick

Updated diff, changes:

 * All the manpages!
 * /dev/led{0,1,2,...} are now by default 600.
 * Doesn't panic when you remove the stick, I think I
   might have already had that fixed in the first diff.
 * Some small improvements here and there, nothing special.

Thanks to jan for ledctl(1) manpage and review.  And thanks to
cheloha@!

Patrick

diff --git a/etc/MAKEDEV.common b/etc/MAKEDEV.common
index c187df748d6..dc67b54ec5a 100644
--- a/etc/MAKEDEV.common
+++ b/etc/MAKEDEV.common
@@ -521,6 +521,8 @@ __devitem(ipmi, ipmi*, IPMI BMC access)dnl
 _mkdev(ipmi, ipmi*, {-M ipmi$U c major_ipmi_c $U 600-})dnl
 __devitem(gpio, gpio*, General Purpose Input/Output)dnl
 _mcdev(gpio, gpio*, gpio, {-major_gpio_c-}, 600)dnl
+__devitem(led, led*, Generic LED devices)dnl
+_mcdev({-led-}, led*, {-led-}, {-major_led_c-}, 600)dnl
 __devitem(vmm, vmm, Virtual Machine Monitor)dnl
 _mkdev(vmm, vmm, {-M vmm c major_vmm_c 0 600-})dnl
 __devitem(pvbus, pvbus*, paravirtual device tree root)dnl
diff --git a/etc/etc.amd64/MAKEDEV b/etc/etc.amd64/MAKEDEV
index 543b0ec731b..50f7ac53acb 100644
--- a/etc/etc.amd64/MAKEDEV
+++ b/etc/etc.amd64/MAKEDEV
@@ -78,6 +78,7 @@
 # gpio* General Purpose Input/Output
 # hotplug devices hot plugging
 # ipmi* IPMI BMC access
+# led* Generic LED devices
 # nvram NVRAM access
 # kcov Kernel code coverage tracing
 # pci* PCI bus devices
@@ -329,6 +330,10 @@ nvram)
  M nvram c 85 0 440 kmem
  ;;
 
+led*)
+ M led$U c 55 $U 600
+ ;;
+
 ipmi*)
  M ipmi$U c 96 $U 600
  ;;
diff --git a/etc/etc.amd64/MAKEDEV.md b/etc/etc.amd64/MAKEDEV.md
index f46b52bd7d6..66e85a16da8 100644
--- a/etc/etc.amd64/MAKEDEV.md
+++ b/etc/etc.amd64/MAKEDEV.md
@@ -76,6 +76,7 @@ _DEV(gpio, 88)
 _DEV(hotplug, 82)
 _DEV(ipmi, 96)
 dnl _DEV(joy, 26)
+_DEV(led, 55)
 _DEV(nvram, 85)
 _DEV(kcov, 19)
 _DEV(pci, 72)
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 307bd44b66b..45ab7539c60 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -41,8 +41,8 @@ MAN= aac.4 abcrtc.4 ac97.4 acphy.4 acrtc.4 \
  ip.4 ip6.4 ipcomp.4 ipgphy.4 ipmi.4 ips.4 ipsec.4 ipw.4 \
  isa.4 isagpio.4 isapnp.4 islrtc.4 it.4 itherm.4 iwi.4 iwn.4 iwm.4 \
  ix.4 ixgb.4 ixl.4 jmb.4 jme.4 jmphy.4 \
- kate.4 kcov.4 km.4 ksmn.4 ksyms.4 kubsan.4 kue.4 lc.4 lge.4 lii.4 \
- lisa.4 lm.4 lmenv.4 lmn.4 lmtemp.4 lo.4 lpt.4 lxtphy.4 luphy.4 \
+ kate.4 kcov.4 km.4 ksmn.4 ksyms.4 kubsan.4 kue.4 lc.4 led.4 lge.4 \
+ lii.4 lisa.4 lm.4 lmenv.4 lmn.4 lmtemp.4 lo.4 lpt.4 lxtphy.4 luphy.4 \
  maestro.4 mainbus.4 malo.4 maxds.4 maxrtc.4 maxtmp.4 mbg.4 \
  mcprtc.4 mcx.4 midi.4 mii.4 mfi.4 \
  mfii.4 mlphy.4 moscom.4 mos.4 mpe.4 mpath.4 mpi.4 mpii.4 \
@@ -75,8 +75,8 @@ MAN= aac.4 abcrtc.4 ac97.4 acphy.4 acrtc.4 \
  tcic.4 tcp.4 termios.4 tht.4 ti.4 tipmic.4 tl.4 \
  tlphy.4 thmc.4 tpm.4 tpmr.4 tqphy.4 trm.4 trunk.4 tsl.4 tty.4 \
  tun.4 tap.4 twe.4 \
- txp.4 txphy.4 uaudio.4 uark.4 uath.4 ubcmtp.4 uberry.4 ubsa.4 \
- ubsec.4 ucom.4 uchcom.4 ucrcom.4 ucycom.4 ukspan.4 uslhcom.4 \
+ txp.4 txphy.4 uaudio.4 uark.4 uath.4 ubcmtp.4 uberry.4 ublink.4 \
+ ubsa.4 ubsec.4 ucom.4 uchcom.4 ucrcom.4 ucycom.4 ukspan.4 uslhcom.4 \
  udav.4 udcf.4 udl.4 udp.4 udsbr.4 \
  uftdi.4 ugen.4 ugl.4 ugold.4 uguru.4 uhci.4 uhid.4 uhidev.4 uipaq.4 \
  uk.4 ukbd.4 \
diff --git a/share/man/man4/led.4 b/share/man/man4/led.4
new file mode 100644
index 00000000000..fb06edee73a
--- /dev/null
+++ b/share/man/man4/led.4
@@ -0,0 +1,78 @@
+.\" $OpenBSD$
+.\"
+.\" Copyright (c) 2019 Patrick Wildt <[hidden email]>
+.\"
+.\" 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 DISCLAIMS 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.
+.\"
+.Dd $Mdocdate$
+.Dt LED 4
+.Os
+.Sh NAME
+.Nm led
+.Nd Generic LED device
+.Sh SYNOPSIS
+.Cd "led* at ublink?"
+.Sh DESCRIPTION
+The
+.Nm
+device attaches to the LED controller and provices a uniform
+programming interface to its LEDs.
+.Pp
+Each LED controller with an attached
+.Nm
+device has an associated device file under the
+.Pa /dev
+directory, e.g.\&
+.Pa /dev/led0 .
+Access from userland is performed through
+.Xr ioctl 2
+calls on these devices.
+.Sh IOCTL INTERFACE
+The following structures and constants are defined in the
+.In sys/gpio.h
+header file:
+.Bl -tag -width XXXX
+.It Dv LED_GET_NUM Fa "unsigned int"
+Returns the number of LEDs on this LED controller.
+.It Dv LED_SET Fa "struct led_set"
+Sets a single LED's state using the
+.Fa led_set
+structur:
+.Bd -literal
+struct led_set {
+ unsigned int led; /* total number of LEDs available */
+ uint32_t rgb; /* colour value to set */
+};
+.Ed
+.El
+.Sh FILES
+.Bl -tag -width "/dev/ledu" -compact
+.It /dev/led Ns Ar u
+LED device unit
+.Ar u
+file.
+.El
+.Sh SEE ALSO
+.Xr ledctl 1 ,
+.Xr ioctl 2
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Ox 6.7 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Patrick Wildt Aq Mt [hidden email] .
diff --git a/share/man/man4/ublink.4 b/share/man/man4/ublink.4
new file mode 100644
index 00000000000..3cbc33856d9
--- /dev/null
+++ b/share/man/man4/ublink.4
@@ -0,0 +1,50 @@
+.\" $OpenBSD$
+.\"
+.\" Copyright (c) 2019 Patrick Wildt <[hidden email]>
+.\"
+.\" 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 DISCLAIMS 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.
+.\"
+.Dd $Mdocdate$
+.Dt UBLINK 4
+.Os
+.Sh NAME
+.Nm ublink
+.Nd ThingM blink(1) USB RGB LED notification light
+.Sh SYNOPSIS
+.Cd "ublink* at uhidev?"
+.Cd "led* at ublink?"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the ThingM blink(1) notification light.
+It provides access to its two LEDs using the
+.Xr led 4
+device.
+.Pp
+Additionally it sets up a default watchdog.
+If the host does not respond in a given time, the LEDs will start
+playing a pre-programmed sequence.
+.Sh SEE ALSO
+.Xr led 4 ,
+.Xr uhidev 4
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Ox 6.7 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Patrick Wildt Aq Mt [hidden email] .
diff --git a/share/man/man4/uhidev.4 b/share/man/man4/uhidev.4
index 7dafba8fa22..4e93bf6e624 100644
--- a/share/man/man4/uhidev.4
+++ b/share/man/man4/uhidev.4
@@ -37,6 +37,7 @@
 .Sh SYNOPSIS
 .Cd "uhidev*  at uhub?"
 .Cd "ubcmtp*  at uhidev?"
+.Cd "ublink*  at uhidev?"
 .Cd "ucycom*  at uhidev?"
 .Cd "ugold*   at uhidev?"
 .Cd "uhid*    at uhidev?"
@@ -69,6 +70,7 @@ only dispatches data to them based on the report id.
 .Sh SEE ALSO
 .Xr intro 4 ,
 .Xr ubcmtp 4 ,
+.Xr ublink 4 ,
 .Xr ucycom 4 ,
 .Xr ugold 4 ,
 .Xr uhid 4 ,
diff --git a/share/man/man8/man8.amd64/MAKEDEV.8 b/share/man/man8/man8.amd64/MAKEDEV.8
index 02d448eed25..2836f490cf5 100644
--- a/share/man/man8/man8.amd64/MAKEDEV.8
+++ b/share/man/man8/man8.amd64/MAKEDEV.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: MAKEDEV.8,v 1.86 2019/12/17 13:18:05 reyk Exp $
+.\" $OpenBSD$
 .\"
 .\" THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.
 .\" generated from:
@@ -23,7 +23,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: December 17 2019 $
+.Dd $Mdocdate: June 6 2017 $
 .Dt MAKEDEV 8 amd64
 .Os
 .Sh NAME
@@ -230,6 +230,9 @@ devices hot plugging, see
 .It Ar ipmi*
 IPMI BMC access, see
 .Xr ipmi 4 .
+.It Ar led*
+Generic LED devices, see
+.Xr led 4 .
 .It Ar nvram
 NVRAM access, see
 .Xr nvram 4 .
diff --git a/sys/arch/amd64/amd64/conf.c b/sys/arch/amd64/amd64/conf.c
index 6330f6e442d..84b9d2c7ab3 100644
--- a/sys/arch/amd64/amd64/conf.c
+++ b/sys/arch/amd64/amd64/conf.c
@@ -164,6 +164,7 @@ cdev_decl(nvram);
 cdev_decl(drm);
 #include "viocon.h"
 cdev_decl(viocon);
+#include "led.h"
 
 #include "wsdisplay.h"
 #include "wskbd.h"
@@ -248,7 +249,7 @@ struct cdevsw cdevsw[] =
  cdev_midi_init(NMIDI,midi), /* 52: MIDI I/O */
  cdev_notdef(), /* 53 was: sequencer I/O */
  cdev_notdef(), /* 54 was: RAIDframe disk driver */
- cdev_notdef(), /* 55: */
+ cdev_led_init(NLED,led), /* 55: led(4) */
  /* The following slots are reserved for isdn4bsd. */
  cdev_notdef(), /* 56: i4b main device */
  cdev_notdef(), /* 57: i4b control device */
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index 17e88a9eb51..aae4a516aad 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -280,6 +280,8 @@ ucom* at ucycom?
 uslhcom* at uhidev? # Silicon Labs CP2110 USB HID UART
 ucom* at uslhcom?
 uhid* at uhidev? # USB generic HID support
+ublink* at uhidev? # ThingM blink(1)
+led* at ublink?
 fido* at uhidev? # FIDO/U2F security key support
 upd* at uhidev? # USB Power Devices sensors
 aue* at uhub? # ADMtek AN986 Pegasus Ethernet
diff --git a/sys/conf/files b/sys/conf/files
index 0cef7842919..ab0db72178a 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -56,6 +56,11 @@ define i2c_bitbang
 # 1-Wire bus bit-banging
 define onewire_bitbang
 
+define led {}
+device led
+file dev/ic/led.c led needs-flag
+attach led at led
+
 # net device attributes - we have generic code for ether(net)
 define crypto
 define ether
diff --git a/sys/dev/ic/led.c b/sys/dev/ic/led.c
new file mode 100644
index 00000000000..3bc8c214394
--- /dev/null
+++ b/sys/dev/ic/led.c
@@ -0,0 +1,139 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2019 Patrick Wildt <[hidden email]>
+ *
+ * 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 DISCLAIMS 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/kernel.h>
+#include <sys/vnode.h>
+#include <sys/conf.h>
+
+#include <dev/ic/led.h>
+
+#ifdef LED_DEBUG
+#define DPRINTF(x) do { if (leddebug) printf x; } while (0)
+#define DPRINTFN(n,x) do { if (leddebug>(n)) printf x; } while (0)
+int leddebug = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+struct led_softc {
+ struct device sc_dev;
+ unsigned int sc_nled;
+
+ void *sc_cookie;
+ struct led_ctl *sc_ctl;
+};
+
+#define LEDUNIT(dev) (minor(dev))
+
+int led_match(struct device *, void *, void *);
+void led_attach(struct device *, struct device *, void *);
+int led_detach(struct device *, int);
+
+struct cfdriver led_cd = {
+ NULL, "led", DV_DULL
+};
+
+struct cfattach led_ca = {
+ sizeof(struct led_softc), led_match, led_attach, led_detach,
+};
+
+int
+led_match(struct device *parent, void *match, void *aux)
+{
+ return 1;
+}
+
+void
+led_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct led_softc *sc = (struct led_softc *)self;
+ struct led_attach_args *laa = (struct led_attach_args *)aux;
+
+ sc->sc_cookie = laa->cookie;
+ sc->sc_ctl = laa->ctl;
+ sc->sc_nled = laa->nled;
+
+ printf(": %u LEDs\n", sc->sc_nled);
+}
+
+int
+led_detach(struct device *self, int flags)
+{
+ int maj, mn;
+
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if (cdevsw[maj].d_open == ledopen)
+ break;
+
+ /* Nuke the vnodes for any open instances (calls close). */
+ mn = self->dv_unit;
+ vdevgone(maj, mn, mn, VCHR);
+
+ return 0;
+}
+
+int
+ledopen(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct led_softc *sc;
+
+ if (LEDUNIT(dev) >= led_cd.cd_ndevs)
+ return ENXIO;
+ sc = led_cd.cd_devs[LEDUNIT(dev)];
+ if (sc == NULL)
+ return ENXIO;
+
+ return 0;
+}
+
+int
+ledclose(dev_t dev, int flag, int mode, struct proc *p)
+{
+ return 0;
+}
+
+int
+ledioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
+{
+ struct led_softc *sc;
+ struct led_set *set;
+
+ if (LEDUNIT(dev) >= led_cd.cd_ndevs)
+ return ENXIO;
+ sc = led_cd.cd_devs[LEDUNIT(dev)];
+ if (sc == NULL)
+ return ENXIO;
+
+ switch (cmd) {
+ case LED_GET_NUM:
+ *(unsigned int *)addr = sc->sc_nled;
+ break;
+ case LED_SET:
+ set = (struct led_set *)addr;
+ if (set->led >= sc->sc_nled)
+ return ENXIO;
+ sc->sc_ctl->set_led(sc->sc_cookie, set->led, set->rgb);
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
diff --git a/sys/dev/ic/led.h b/sys/dev/ic/led.h
new file mode 100644
index 00000000000..d738692e35f
--- /dev/null
+++ b/sys/dev/ic/led.h
@@ -0,0 +1,36 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2019 Patrick Wildt <[hidden email]>
+ *
+ * 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 DISCLAIMS 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/ioctl.h>
+
+struct led_ctl {
+ void (*set_led)(void *, unsigned int led, uint32_t);
+};
+
+struct led_attach_args {
+ void *cookie;
+ struct led_ctl *ctl;
+ unsigned int nled;
+};
+
+struct led_set {
+ unsigned int led;
+ uint32_t rgb;
+};
+
+#define LED_GET_NUM _IOR ('L', 0, unsigned int)
+#define LED_SET _IOW ('L', 1, struct led_set)
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb
index 0f697127e92..c3b7ac5a73a 100644
--- a/sys/dev/usb/files.usb
+++ b/sys/dev/usb/files.usb
@@ -473,3 +473,8 @@ file dev/usb/uwacom.c uwacom
 
 attach bwfm at uhub with bwfm_usb: firmload
 file dev/usb/if_bwfm_usb.c bwfm_usb
+
+# ThingM blink(1)
+device ublink: led
+attach ublink at uhidbus
+file dev/usb/ublink.c ublink
diff --git a/sys/dev/usb/ublink.c b/sys/dev/usb/ublink.c
new file mode 100644
index 00000000000..4129694a5fd
--- /dev/null
+++ b/sys/dev/usb/ublink.c
@@ -0,0 +1,169 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2019 Patrick Wildt <[hidden email]>
+ *
+ * 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 DISCLAIMS 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 <sys/timeout.h>
+
+#include <dev/ic/led.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/uhidev.h>
+
+#define BLINK_FADE_MS 300
+#define BLINK_WATCHDOG_MS 15000
+#define BLINK_TIMEOUT_MS 10000
+
+#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
+
+#ifdef UBLINK_DEBUG
+#define DPRINTF(x) printf x
+#else
+#define DPRINTF(x)
+#endif
+
+struct ublink_softc {
+ struct uhidev sc_hdev;
+ struct usbd_device *sc_udev;
+
+ struct timeout sc_wdog_to;
+};
+
+int ublink_match(struct device *, void *, void *);
+void ublink_attach(struct device *, struct device *, void *);
+int ublink_detach(struct device *, int);
+
+void ublink_watchdog(void *);
+
+void ublink_set_led(void *, unsigned int, uint32_t);
+
+struct led_ctl ublink_ctl = {
+ ublink_set_led
+};
+
+struct cfdriver ublink_cd = {
+ NULL, "ublink", DV_DULL
+};
+
+struct cfattach ublink_ca = {
+ sizeof(struct ublink_softc), ublink_match, ublink_attach, ublink_detach
+};
+
+struct usb_devno ublink_devs[] = {
+ { USB_VENDOR_THINGM, USB_PRODUCT_THINGM_BLINK1 },
+};
+
+int
+ublink_match(struct device *parent, void *match, void *aux)
+{
+ struct uhidev_attach_arg *uha = aux;
+
+ if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
+ return UMATCH_NONE;
+
+ if (usb_lookup(ublink_devs, uha->uaa->vendor,
+    uha->uaa->product) == NULL)
+ return UMATCH_NONE;
+
+ return UMATCH_VENDOR_PRODUCT;
+}
+
+void
+ublink_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct ublink_softc *sc = (struct ublink_softc *)self;
+ struct uhidev_attach_arg *uha = aux;
+ struct led_attach_args la;
+
+ sc->sc_udev = uha->parent->sc_udev;
+ sc->sc_hdev.sc_parent = uha->parent;
+ sc->sc_hdev.sc_report_id = uha->reportid;
+
+ printf("\n");
+
+ /* Turn on watchdog and repeatedly re-arm. */
+ timeout_set(&sc->sc_wdog_to, ublink_watchdog, sc);
+ timeout_add_msec(&sc->sc_wdog_to, BLINK_TIMEOUT_MS);
+
+ /* Attach led(4). */
+ la.cookie = self;
+ la.ctl = &ublink_ctl;
+ la.nled = 3;
+ config_found(self, &la, NULL);
+}
+
+int
+ublink_detach(struct device *self, int flags)
+{
+ struct ublink_softc *sc = (struct ublink_softc *)self;
+
+ timeout_del(&sc->sc_wdog_to);
+ return config_detach_children(self, flags);
+}
+
+void
+ublink_watchdog(void *self)
+{
+ struct ublink_softc *sc = self;
+ uint8_t buf[7];
+
+ if (usbd_is_dying(sc->sc_udev))
+ return;
+
+ /* "serverdown" */
+ buf[0] = 'D';
+ buf[1] = 1;
+ buf[2] = ((BLINK_WATCHDOG_MS / 10) >> 8);
+ buf[3] = ((BLINK_WATCHDOG_MS / 10) % 0xff);
+ buf[4] = 1;
+ buf[5] = 0;
+ buf[6] = 15;
+
+ uhidev_set_report_async(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
+     sc->sc_hdev.sc_report_id, buf, sizeof(buf));
+ timeout_add_msec(&sc->sc_wdog_to, BLINK_TIMEOUT_MS);
+}
+
+void
+ublink_set_led(void *self, unsigned int led, uint32_t rgb)
+{
+ struct ublink_softc *sc = self;
+ uint8_t buf[7];
+
+ if (usbd_is_dying(sc->sc_udev))
+ return;
+
+ if (led > 2)
+ return;
+
+ /* fade to RGB */
+ buf[0] = 'c';
+ buf[1] = (rgb >> 16) & 0xff;
+ buf[2] = (rgb >> 8) & 0xff;
+ buf[3] = rgb & 0xff;
+ buf[4] = ((BLINK_FADE_MS / 10) >> 8);
+ buf[5] = (BLINK_FADE_MS / 10) % 0xff;
+ buf[6] = led;
+
+ uhidev_set_report_async(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
+     sc->sc_hdev.sc_report_id, buf, sizeof(buf));
+}
diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs
index c506a7bcc28..afd673b6b7e 100644
--- a/sys/dev/usb/usbdevs
+++ b/sys/dev/usb/usbdevs
@@ -630,6 +630,7 @@ vendor TRIPPLITE 0x2478 Tripp-Lite
 vendor ARUBA 0x2626 Aruba
 vendor XIAOMI 0x2717 Xiaomi
 vendor NHJ 0x2770 NHJ
+vendor THINGM 0x27b8 ThingM
 vendor ASUSTEK 0x2821 ASUSTeK Computer
 vendor PLANEX 0x2c02 Planex Communications
 vendor LINKINSTRUMENTS 0x3195 Link Instruments
@@ -4249,6 +4250,9 @@ product TI NEXII 0x5409 Nex II Digital
 product TI MSP430_JTAG 0xf430 MSP-FET430UIF JTAG
 product TI MSP430 0xf432 MSP-FET430UIF
 
+/* ThingM products */
+product THINGM BLINK1 0x01ed blink(1)
+
 /* Thrustmaster products */
 product THRUST FUSION_PAD 0xa0a3 Fusion Digital Gamepad
 
diff --git a/sys/dev/usb/usbdevs.h b/sys/dev/usb/usbdevs.h
index e520ee87431..b74272773b0 100644
--- a/sys/dev/usb/usbdevs.h
+++ b/sys/dev/usb/usbdevs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: usbdevs.h,v 1.714 2019/12/07 08:47:20 kevlo Exp $ */
+/* $OpenBSD$ */
 
 /*
  * THIS FILE IS AUTOMATICALLY GENERATED.  DO NOT EDIT.
@@ -637,6 +637,7 @@
 #define USB_VENDOR_ARUBA 0x2626 /* Aruba */
 #define USB_VENDOR_XIAOMI 0x2717 /* Xiaomi */
 #define USB_VENDOR_NHJ 0x2770 /* NHJ */
+#define USB_VENDOR_THINGM 0x27b8 /* ThingM */
 #define USB_VENDOR_ASUSTEK 0x2821 /* ASUSTeK Computer */
 #define USB_VENDOR_PLANEX 0x2c02 /* Planex Communications */
 #define USB_VENDOR_LINKINSTRUMENTS 0x3195 /* Link Instruments */
@@ -4256,6 +4257,9 @@
 #define USB_PRODUCT_TI_MSP430_JTAG 0xf430 /* MSP-FET430UIF JTAG */
 #define USB_PRODUCT_TI_MSP430 0xf432 /* MSP-FET430UIF */
 
+/* ThingM products */
+#define USB_PRODUCT_THINGM_BLINK1 0x01ed /* blink(1) */
+
 /* Thrustmaster products */
 #define USB_PRODUCT_THRUST_FUSION_PAD 0xa0a3 /* Fusion Digital Gamepad */
 
diff --git a/sys/dev/usb/usbdevs_data.h b/sys/dev/usb/usbdevs_data.h
index 21b61b04d5a..b2a46a8ef79 100644
--- a/sys/dev/usb/usbdevs_data.h
+++ b/sys/dev/usb/usbdevs_data.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: usbdevs_data.h,v 1.708 2019/12/07 08:47:20 kevlo Exp $ */
+/* $OpenBSD$ */
 
 /*
  * THIS FILE IS AUTOMATICALLY GENERATED.  DO NOT EDIT.
@@ -10901,6 +10901,10 @@ const struct usb_known_product usb_known_products[] = {
     USB_VENDOR_TI, USB_PRODUCT_TI_MSP430,
     "MSP-FET430UIF",
  },
+ {
+    USB_VENDOR_THINGM, USB_PRODUCT_THINGM_BLINK1,
+    "blink(1)",
+ },
  {
     USB_VENDOR_THRUST, USB_PRODUCT_THRUST_FUSION_PAD,
     "Fusion Digital Gamepad",
@@ -14041,6 +14045,10 @@ const struct usb_known_vendor usb_known_vendors[] = {
     USB_VENDOR_NHJ,
     "NHJ",
  },
+ {
+    USB_VENDOR_THINGM,
+    "ThingM",
+ },
  {
     USB_VENDOR_ASUSTEK,
     "ASUSTeK Computer",
diff --git a/sys/sys/conf.h b/sys/sys/conf.h
index b43c8374fa5..bf5cac8a98e 100644
--- a/sys/sys/conf.h
+++ b/sys/sys/conf.h
@@ -439,6 +439,13 @@ extern struct cdevsw cdevsw[];
  (dev_type_stop((*))) enodev, 0, selfalse, \
  (dev_type_mmap((*))) enodev }
 
+/* open, close, ioctl */
+#define cdev_led_init(c,n) { \
+ dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \
+ (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \
+ (dev_type_stop((*))) enodev, 0, selfalse, \
+ (dev_type_mmap((*))) enodev }
+
 /* open, close, ioctl */
 #define       cdev_bio_init(c,n) { \
  dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \
@@ -586,6 +593,8 @@ cdev_decl(diskmap);
 
 cdev_decl(bpf);
 
+cdev_decl(led);
+
 cdev_decl(pf);
 
 cdev_decl(tun);
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index 35a3c00d69f..79b180eefcc 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -11,8 +11,8 @@ SUBDIR= apply arch at aucat audioctl awk banner \
  find fgen finger fmt fold from fstat ftp gencat getcap \
  getconf getent getopt gprof grep head hexdump htpasswd id indent \
  infocmp ipcrm ipcs \
- join jot kdump keynote ktrace lam last lastcomm ldap leave less lex \
- libtool lndir \
+ join jot kdump keynote ktrace lam last lastcomm ldap leave ledctl \
+ less lex libtool lndir \
  locale locate lock logger login logname look lorder \
  m4 mail make mandoc mesg mg \
  midicat mixerctl mkdep mklocale mktemp nc netstat \
diff --git a/usr.bin/ledctl/Makefile b/usr.bin/ledctl/Makefile
new file mode 100644
index 00000000000..35338c8219c
--- /dev/null
+++ b/usr.bin/ledctl/Makefile
@@ -0,0 +1,6 @@
+# $OpenBSD$
+
+PROG= ledctl
+SRCS= ledctl.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ledctl/ledctl.1 b/usr.bin/ledctl/ledctl.1
new file mode 100644
index 00000000000..300968310a4
--- /dev/null
+++ b/usr.bin/ledctl/ledctl.1
@@ -0,0 +1,93 @@
+.\" $OpenBSD$
+.\"
+.\" Copyright (c) 2019 Jan Klemkow <[hidden email]>
+.\"
+.\" 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 DISCLAIMS 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.
+.\"
+.Dd $Mdocdate$
+.Dt LEDCTL 1
+.Os
+.Sh NAME
+.Nm ledctl
+.Nd sets the colors of LEDs
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar device
+.Op Fl n Ar led
+.Op Ar on|off|rgb
+.Sh DESCRIPTION
+The
+.Nm
+utility turns LEDs
+.Ar on ,
+.Ar off
+or sets their color.
+The color value
+.Ar rgb
+has to be in hex format.
+The parameters
+.Ar on
+and
+.Ar off
+are shortcuts for the color values 0xffffff and 0x000000.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl f Ar device
+.Nm
+uses
+the device file
+.Ar device .
+Default is
+.Pa /dev/led0 .
+.It Fl n Ar led
+A
+.Xr led 4
+device may consists of multiple LEDs.
+Sets
+.Ar led
+to the index of the right LED.
+Default is 0.
+.El
+.Sh FILES
+.Pa /dev/led0
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+Sets the first LED of
+.Pa /dev/led0
+to red:
+.Bd -literal -offset indent
+$ ledctl 0xff0000
+.Ed
+.Pp
+Sets the second LED of
+.Pa /dev/led0
+to green:
+.Bd -literal -offset indent
+$ ledctl -n 1 0x00ff00
+.Ed
+.Pp
+Sets the first LED of
+.Pa /dev/led2
+to blue:
+.Bd -literal -offset indent
+$ ledctl -f /dev/led2 0x0000ff
+.Ed
+.\".Sh SEE ALSO
+.\".Xr led 4
+.Sh AUTHORS
+The
+.Nm
+program was written by
+.An Patrick Wildt Aq Mt [hidden email] .
diff --git a/usr.bin/ledctl/ledctl.c b/usr.bin/ledctl/ledctl.c
new file mode 100644
index 00000000000..e3275c42db0
--- /dev/null
+++ b/usr.bin/ledctl/ledctl.c
@@ -0,0 +1,101 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2019 Patrick Wildt <[hidden email]>
+ *
+ * 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 DISCLAIMS 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/types.h>
+
+#include <dev/ic/led.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-f device] [-n led] [on|off|rgb]\n",
+    getprogname());
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *dev, *errstr;
+ unsigned int led, nled;
+ struct led_set set;
+ int ch, fd;
+ long rgb;
+ char *ep;
+
+ led = 0;
+ dev = "/dev/led0";
+ while ((ch = getopt(argc, argv, "f:n:")) != -1) {
+ switch (ch) {
+ case 'f':
+ dev = optarg;
+ break;
+ case 'n':
+ led = strtonum(optarg, 0, 64, &errstr);
+ if (errstr != NULL)
+ errx(1, "led is %s: %s", errstr, optarg);
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (dev == NULL || argc != 1)
+ usage();
+
+ fd = open(dev, O_RDWR);
+ if (fd == -1)
+ err(1, "%s", dev);
+
+ if (ioctl(fd, LED_GET_NUM, &nled) == -1)
+ err(1, "ioctl LED_GET_NUM");
+
+ if (led >= nled)
+ errx(1, "bogus led: %u (max %u)", led, nled);
+
+ memset(&set, 0, sizeof(set));
+ set.led = led;
+ if (strcmp(argv[0], "on") == 0) {
+ set.rgb = 0xffffff;
+ } else if (strcmp(argv[0], "off") == 0) {
+ set.rgb = 0x000000;
+ } else {
+ errno = 0;
+ rgb = strtol(argv[0], &ep, 16);
+ if (*argv[0] == '\0' || *ep != '\0')
+ errx(1, "bogus color: %s", argv[0]);
+ if (errno == ERANGE && (rgb == LONG_MAX || rgb == LONG_MIN))
+ errx(1, "bogus color: %s", argv[0]);
+ set.rgb = rgb;
+ }
+
+ if (ioctl(fd, LED_SET, &set) == -1)
+ err(1, "ioctl LED_SET");
+
+ return 0;
+}

Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Stuart Henderson
While it's nice to have basic support in the kernel, for people using
these devices for sequences / controlling a chain of neopixels / etc
they're going to need a custom kernel with the driver disabled in order
to access it from userland.

For release users this either locks them out of using syspatch
completely (if they do this by building -stable with custom config -
syspatch won't run at all with -stable), or at least from using syspatch
for kernel fixes (if using config -ef) - so it would be really nice to
have a better way around this before adding more drivers taking over
from existing device support.

Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Theo de Raadt-2
Stuart Henderson <[hidden email]> wrote:

> While it's nice to have basic support in the kernel, for people using
> these devices for sequences / controlling a chain of neopixels / etc
> they're going to need a custom kernel with the driver disabled in order
> to access it from userland.
>
> For release users this either locks them out of using syspatch
> completely (if they do this by building -stable with custom config -
> syspatch won't run at all with -stable), or at least from using syspatch
> for kernel fixes (if using config -ef) - so it would be really nice to
> have a better way around this before adding more drivers taking over
> from existing device support.

I disagree.

This unbridled ugen/uhid/usb direct-access via libusb has got to stop.

"taking over existing device support" is backwards.  We don't expose
/dev/pci for X anymore, and we should not do it here either.


Reply | Threaded
Open this post in threaded view
|

Re: ublink(4), led(4) and ledctl(1)

Martin Pieuchot
In reply to this post by Stuart Henderson
On 19/12/19(Thu) 18:37, Stuart Henderson wrote:
> While it's nice to have basic support in the kernel, for people using
> these devices for sequences / controlling a chain of neopixels / etc
> they're going to need a custom kernel with the driver disabled in order
> to access it from userland.

Which features do you need that are currently missing in led(4)?