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 <patr...@blueri.se> +.\" +.\" 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 patr...@blueri.se . 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 <patr...@blueri.se> +.\" +.\" 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 patr...@blueri.se . 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 <patr...@blueri.se> + * + * 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 <patr...@blueri.se> + * + * 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 <patr...@blueri.se> + * + * 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 <j...@openbsd.org> +.\" +.\" 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 patr...@blueri.se . 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 <patr...@blueri.se> + * + * 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; +}