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 <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 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 <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 = 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 <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 = 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); +}