16bd03b20SKevin Lo /*- 2*0642eac4SKevin Lo * Copyright (c) 2014, 2017 Kevin Lo 36bd03b20SKevin Lo * All rights reserved. 46bd03b20SKevin Lo * 56bd03b20SKevin Lo * Redistribution and use in source and binary forms, with or without 66bd03b20SKevin Lo * modification, are permitted provided that the following conditions 76bd03b20SKevin Lo * are met: 86bd03b20SKevin Lo * 1. Redistributions of source code must retain the above copyright 96bd03b20SKevin Lo * notice, this list of conditions, and the following disclaimer. 106bd03b20SKevin Lo * 2. Redistributions in binary form must reproduce the above copyright 116bd03b20SKevin Lo * notice, this list of conditions and the following disclaimer in the 126bd03b20SKevin Lo * documentation and/or other materials provided with the distribution. 136bd03b20SKevin Lo * 146bd03b20SKevin Lo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 156bd03b20SKevin Lo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 166bd03b20SKevin Lo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 176bd03b20SKevin Lo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 186bd03b20SKevin Lo * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 196bd03b20SKevin Lo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 206bd03b20SKevin Lo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 216bd03b20SKevin Lo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 226bd03b20SKevin Lo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 236bd03b20SKevin Lo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 246bd03b20SKevin Lo * SUCH DAMAGE. 256bd03b20SKevin Lo * 266bd03b20SKevin Lo */ 276bd03b20SKevin Lo 286bd03b20SKevin Lo #include <sys/cdefs.h> 296bd03b20SKevin Lo __FBSDID("$FreeBSD$"); 306bd03b20SKevin Lo 316bd03b20SKevin Lo #include <sys/stdint.h> 326bd03b20SKevin Lo #include <sys/stddef.h> 336bd03b20SKevin Lo #include <sys/param.h> 346bd03b20SKevin Lo #include <sys/queue.h> 356bd03b20SKevin Lo #include <sys/types.h> 366bd03b20SKevin Lo #include <sys/systm.h> 376bd03b20SKevin Lo #include <sys/kernel.h> 386bd03b20SKevin Lo #include <sys/bus.h> 396bd03b20SKevin Lo #include <sys/module.h> 406bd03b20SKevin Lo #include <sys/lock.h> 416bd03b20SKevin Lo #include <sys/mutex.h> 426bd03b20SKevin Lo #include <sys/condvar.h> 436bd03b20SKevin Lo #include <sys/sysctl.h> 446bd03b20SKevin Lo #include <sys/sx.h> 456bd03b20SKevin Lo #include <sys/unistd.h> 466bd03b20SKevin Lo #include <sys/callout.h> 476bd03b20SKevin Lo #include <sys/malloc.h> 486bd03b20SKevin Lo #include <sys/priv.h> 496bd03b20SKevin Lo #include <sys/conf.h> 506bd03b20SKevin Lo #include <sys/fcntl.h> 516bd03b20SKevin Lo 526bd03b20SKevin Lo #include <dev/usb/usb.h> 536bd03b20SKevin Lo #include <dev/usb/usbdi.h> 546bd03b20SKevin Lo #include <dev/usb/usbhid.h> 556bd03b20SKevin Lo #include "usbdevs.h" 566bd03b20SKevin Lo 576bd03b20SKevin Lo #define USB_DEBUG_VAR usb_debug 586bd03b20SKevin Lo #include <dev/usb/usb_debug.h> 596bd03b20SKevin Lo 606bd03b20SKevin Lo #include <dev/usb/uled_ioctl.h> 616bd03b20SKevin Lo 626bd03b20SKevin Lo struct uled_softc { 636bd03b20SKevin Lo struct usb_fifo_sc sc_fifo; 646bd03b20SKevin Lo struct mtx sc_mtx; 656bd03b20SKevin Lo 666bd03b20SKevin Lo struct usb_device *sc_udev; 676bd03b20SKevin Lo struct uled_color sc_color; 686bd03b20SKevin Lo 696bd03b20SKevin Lo uint8_t sc_state; 706bd03b20SKevin Lo #define ULED_ENABLED 0x01 71*0642eac4SKevin Lo 72*0642eac4SKevin Lo int sc_flags; 73*0642eac4SKevin Lo #define ULED_FLAG_BLINK1 0x0001 746bd03b20SKevin Lo }; 756bd03b20SKevin Lo 76*0642eac4SKevin Lo /* Initial commands. */ 77*0642eac4SKevin Lo static uint8_t blink1[] = { 0x1, 'v', 0, 0, 0, 0, 0, 0 }; 78*0642eac4SKevin Lo static uint8_t dl100b[] = { 0x1f, 0x2, 0, 0x5f, 0, 0, 0x1a, 0x3 }; 796bd03b20SKevin Lo 80*0642eac4SKevin Lo /* Prototypes. */ 816bd03b20SKevin Lo static device_probe_t uled_probe; 826bd03b20SKevin Lo static device_attach_t uled_attach; 836bd03b20SKevin Lo static device_detach_t uled_detach; 846bd03b20SKevin Lo 856bd03b20SKevin Lo static usb_fifo_open_t uled_open; 866bd03b20SKevin Lo static usb_fifo_close_t uled_close; 876bd03b20SKevin Lo static usb_fifo_ioctl_t uled_ioctl; 886bd03b20SKevin Lo 896bd03b20SKevin Lo static struct usb_fifo_methods uled_fifo_methods = { 906bd03b20SKevin Lo .f_open = &uled_open, 916bd03b20SKevin Lo .f_close = &uled_close, 926bd03b20SKevin Lo .f_ioctl = &uled_ioctl, 936bd03b20SKevin Lo .basename[0] = "uled", 946bd03b20SKevin Lo }; 956bd03b20SKevin Lo 966bd03b20SKevin Lo static usb_error_t uled_ctrl_msg(struct uled_softc *, uint8_t, uint8_t, 97*0642eac4SKevin Lo uint16_t, uint16_t, void *, uint16_t); 986bd03b20SKevin Lo static int uled_enable(struct uled_softc *); 996bd03b20SKevin Lo 1006bd03b20SKevin Lo static devclass_t uled_devclass; 1016bd03b20SKevin Lo 1026bd03b20SKevin Lo static device_method_t uled_methods[] = { 1036bd03b20SKevin Lo DEVMETHOD(device_probe, uled_probe), 1046bd03b20SKevin Lo DEVMETHOD(device_attach, uled_attach), 1056bd03b20SKevin Lo DEVMETHOD(device_detach, uled_detach), 1066bd03b20SKevin Lo 1076bd03b20SKevin Lo DEVMETHOD_END 1086bd03b20SKevin Lo }; 1096bd03b20SKevin Lo 1106bd03b20SKevin Lo static driver_t uled_driver = { 1116bd03b20SKevin Lo .name = "uled", 1126bd03b20SKevin Lo .methods = uled_methods, 1136bd03b20SKevin Lo .size = sizeof(struct uled_softc), 1146bd03b20SKevin Lo }; 1156bd03b20SKevin Lo 1166bd03b20SKevin Lo static const STRUCT_USB_HOST_ID uled_devs[] = { 117*0642eac4SKevin Lo #define ULED_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } 118*0642eac4SKevin Lo ULED_DEV(DREAMLINK, DL100B, 0), 119*0642eac4SKevin Lo ULED_DEV(THINGM, BLINK1, ULED_FLAG_BLINK1), 120*0642eac4SKevin Lo #undef ULED_DEV 1216bd03b20SKevin Lo }; 1226bd03b20SKevin Lo 123f809f280SWarner Losh DRIVER_MODULE(uled, uhub, uled_driver, uled_devclass, NULL, NULL); 124f809f280SWarner Losh MODULE_DEPEND(uled, usb, 1, 1, 1); 125f809f280SWarner Losh MODULE_VERSION(uled, 1); 126f809f280SWarner Losh USB_PNP_HOST_INFO(uled_devs); 127f809f280SWarner Losh 1286bd03b20SKevin Lo static int 1296bd03b20SKevin Lo uled_probe(device_t dev) 1306bd03b20SKevin Lo { 1316bd03b20SKevin Lo struct usb_attach_arg *uaa; 1326bd03b20SKevin Lo 1336bd03b20SKevin Lo uaa = device_get_ivars(dev); 1346bd03b20SKevin Lo if (uaa->usb_mode != USB_MODE_HOST) 1356bd03b20SKevin Lo return (ENXIO); 1366bd03b20SKevin Lo if (uaa->info.bInterfaceClass != UICLASS_HID) 1376bd03b20SKevin Lo return (ENXIO); 1386bd03b20SKevin Lo 1396bd03b20SKevin Lo return (usbd_lookup_id_by_uaa(uled_devs, sizeof(uled_devs), uaa)); 1406bd03b20SKevin Lo } 1416bd03b20SKevin Lo 1426bd03b20SKevin Lo static int 1436bd03b20SKevin Lo uled_attach(device_t dev) 1446bd03b20SKevin Lo { 1456bd03b20SKevin Lo struct usb_attach_arg *uaa; 1466bd03b20SKevin Lo struct uled_softc *sc; 1476bd03b20SKevin Lo int unit; 1486bd03b20SKevin Lo usb_error_t error; 1496bd03b20SKevin Lo 1506bd03b20SKevin Lo uaa = device_get_ivars(dev); 1516bd03b20SKevin Lo sc = device_get_softc(dev); 1526bd03b20SKevin Lo unit = device_get_unit(dev); 153*0642eac4SKevin Lo sc->sc_flags = USB_GET_DRIVER_INFO(uaa); 1546bd03b20SKevin Lo 1556bd03b20SKevin Lo device_set_usb_desc(dev); 1566bd03b20SKevin Lo mtx_init(&sc->sc_mtx, "uled lock", NULL, MTX_DEF | MTX_RECURSE); 1576bd03b20SKevin Lo 1586bd03b20SKevin Lo sc->sc_udev = uaa->device; 1596bd03b20SKevin Lo 1606bd03b20SKevin Lo error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx, 1616bd03b20SKevin Lo &uled_fifo_methods, &sc->sc_fifo, unit, -1, 1626bd03b20SKevin Lo uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); 1636bd03b20SKevin Lo if (error != 0) 1646bd03b20SKevin Lo goto detach; 1656bd03b20SKevin Lo 1666bd03b20SKevin Lo sc->sc_color.red = 0; 1676bd03b20SKevin Lo sc->sc_color.green = 0; 1686bd03b20SKevin Lo sc->sc_color.blue = 0; 1696bd03b20SKevin Lo 1706bd03b20SKevin Lo return (0); 1716bd03b20SKevin Lo 1726bd03b20SKevin Lo detach: 1736bd03b20SKevin Lo uled_detach(dev); 1746bd03b20SKevin Lo return (ENOMEM); 1756bd03b20SKevin Lo } 1766bd03b20SKevin Lo 1776bd03b20SKevin Lo static int 1786bd03b20SKevin Lo uled_detach(device_t dev) 1796bd03b20SKevin Lo { 1806bd03b20SKevin Lo struct uled_softc *sc; 1816bd03b20SKevin Lo 1826bd03b20SKevin Lo sc = device_get_softc(dev); 1836bd03b20SKevin Lo usb_fifo_detach(&sc->sc_fifo); 1846bd03b20SKevin Lo mtx_destroy(&sc->sc_mtx); 1856bd03b20SKevin Lo return (0); 1866bd03b20SKevin Lo } 1876bd03b20SKevin Lo 1886bd03b20SKevin Lo static usb_error_t 1896bd03b20SKevin Lo uled_ctrl_msg(struct uled_softc *sc, uint8_t rt, uint8_t reqno, 1906bd03b20SKevin Lo uint16_t value, uint16_t index, void *buf, uint16_t buflen) 1916bd03b20SKevin Lo { 1926bd03b20SKevin Lo struct usb_device_request req; 1936bd03b20SKevin Lo 1946bd03b20SKevin Lo req.bmRequestType = rt; 1956bd03b20SKevin Lo req.bRequest = reqno; 1966bd03b20SKevin Lo USETW(req.wValue, value); 1976bd03b20SKevin Lo USETW(req.wIndex, index); 1986bd03b20SKevin Lo USETW(req.wLength, buflen); 1996bd03b20SKevin Lo 2006bd03b20SKevin Lo return (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, buf, 2016bd03b20SKevin Lo 0, NULL, 2000)); 2026bd03b20SKevin Lo } 2036bd03b20SKevin Lo 2046bd03b20SKevin Lo static int 2056bd03b20SKevin Lo uled_enable(struct uled_softc *sc) 2066bd03b20SKevin Lo { 207*0642eac4SKevin Lo uint8_t *cmdbuf; 2086bd03b20SKevin Lo int error; 2096bd03b20SKevin Lo 210*0642eac4SKevin Lo cmdbuf = (sc->sc_flags & ULED_FLAG_BLINK1) ? blink1 : dl100b; 211*0642eac4SKevin Lo 2126bd03b20SKevin Lo sc->sc_state |= ULED_ENABLED; 2136bd03b20SKevin Lo mtx_lock(&sc->sc_mtx); 2146bd03b20SKevin Lo error = uled_ctrl_msg(sc, UT_WRITE_CLASS_INTERFACE, UR_SET_REPORT, 2156bd03b20SKevin Lo 0x200, 0, cmdbuf, sizeof(cmdbuf)); 2166bd03b20SKevin Lo mtx_unlock(&sc->sc_mtx); 2176bd03b20SKevin Lo return (error); 2186bd03b20SKevin Lo } 2196bd03b20SKevin Lo 2206bd03b20SKevin Lo static int 2216bd03b20SKevin Lo uled_open(struct usb_fifo *fifo, int fflags) 2226bd03b20SKevin Lo { 2236bd03b20SKevin Lo if (fflags & FREAD) { 2246bd03b20SKevin Lo struct uled_softc *sc; 2256bd03b20SKevin Lo int rc; 2266bd03b20SKevin Lo 2276bd03b20SKevin Lo sc = usb_fifo_softc(fifo); 2286bd03b20SKevin Lo if (sc->sc_state & ULED_ENABLED) 2296bd03b20SKevin Lo return (EBUSY); 2306bd03b20SKevin Lo if ((rc = uled_enable(sc)) != 0) 2316bd03b20SKevin Lo return (rc); 2326bd03b20SKevin Lo } 2336bd03b20SKevin Lo return (0); 2346bd03b20SKevin Lo } 2356bd03b20SKevin Lo 2366bd03b20SKevin Lo static void 2376bd03b20SKevin Lo uled_close(struct usb_fifo *fifo, int fflags) 2386bd03b20SKevin Lo { 2396bd03b20SKevin Lo if (fflags & FREAD) { 2406bd03b20SKevin Lo struct uled_softc *sc; 2416bd03b20SKevin Lo 2426bd03b20SKevin Lo sc = usb_fifo_softc(fifo); 2436bd03b20SKevin Lo sc->sc_state &= ~ULED_ENABLED; 2446bd03b20SKevin Lo } 2456bd03b20SKevin Lo } 2466bd03b20SKevin Lo 2476bd03b20SKevin Lo static int 2486bd03b20SKevin Lo uled_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) 2496bd03b20SKevin Lo { 2506bd03b20SKevin Lo struct uled_softc *sc; 2516bd03b20SKevin Lo struct uled_color color; 2526bd03b20SKevin Lo int error; 2536bd03b20SKevin Lo 2546bd03b20SKevin Lo sc = usb_fifo_softc(fifo); 2556bd03b20SKevin Lo error = 0; 2566bd03b20SKevin Lo 2576bd03b20SKevin Lo mtx_lock(&sc->sc_mtx); 2586bd03b20SKevin Lo 2596bd03b20SKevin Lo switch(cmd) { 2606bd03b20SKevin Lo case ULED_GET_COLOR: 2616bd03b20SKevin Lo *(struct uled_color *)addr = sc->sc_color; 2626bd03b20SKevin Lo break; 2636bd03b20SKevin Lo case ULED_SET_COLOR: 2646bd03b20SKevin Lo color = *(struct uled_color *)addr; 2656bd03b20SKevin Lo uint8_t buf[8]; 2666bd03b20SKevin Lo 2676bd03b20SKevin Lo sc->sc_color.red = color.red; 2686bd03b20SKevin Lo sc->sc_color.green = color.green; 2696bd03b20SKevin Lo sc->sc_color.blue = color.blue; 2706bd03b20SKevin Lo 271*0642eac4SKevin Lo if (sc->sc_flags & ULED_FLAG_BLINK1) { 272*0642eac4SKevin Lo buf[0] = 0x1; 273*0642eac4SKevin Lo buf[1] = 'n'; 274*0642eac4SKevin Lo buf[2] = color.red; 275*0642eac4SKevin Lo buf[3] = color.green; 276*0642eac4SKevin Lo buf[4] = color.blue; 277*0642eac4SKevin Lo buf[5] = buf[6] = buf[7] = 0; 278*0642eac4SKevin Lo } else { 2796bd03b20SKevin Lo buf[0] = color.red; 2806bd03b20SKevin Lo buf[1] = color.green; 2816bd03b20SKevin Lo buf[2] = color.blue; 2826bd03b20SKevin Lo buf[3] = buf[4] = buf[5] = 0; 2836bd03b20SKevin Lo buf[6] = 0x1a; 2846bd03b20SKevin Lo buf[7] = 0x05; 285*0642eac4SKevin Lo } 2866bd03b20SKevin Lo error = uled_ctrl_msg(sc, UT_WRITE_CLASS_INTERFACE, 2876bd03b20SKevin Lo UR_SET_REPORT, 0x200, 0, buf, sizeof(buf)); 2886bd03b20SKevin Lo break; 2896bd03b20SKevin Lo default: 2906bd03b20SKevin Lo error = ENOTTY; 2916bd03b20SKevin Lo break; 2926bd03b20SKevin Lo } 2936bd03b20SKevin Lo 2946bd03b20SKevin Lo mtx_unlock(&sc->sc_mtx); 2956bd03b20SKevin Lo return (error); 2966bd03b20SKevin Lo } 297