1271159Skevlo/*- 2271159Skevlo * Copyright (c) 2014 Kevin Lo 3271159Skevlo * All rights reserved. 4271159Skevlo * 5271159Skevlo * Redistribution and use in source and binary forms, with or without 6271159Skevlo * modification, are permitted provided that the following conditions 7271159Skevlo * are met: 8271159Skevlo * 1. Redistributions of source code must retain the above copyright 9271159Skevlo * notice, this list of conditions, and the following disclaimer. 10271159Skevlo * 2. Redistributions in binary form must reproduce the above copyright 11271159Skevlo * notice, this list of conditions and the following disclaimer in the 12271159Skevlo * documentation and/or other materials provided with the distribution. 13271159Skevlo * 14271159Skevlo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15271159Skevlo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16271159Skevlo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17271159Skevlo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 18271159Skevlo * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19271159Skevlo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20271159Skevlo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21271159Skevlo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22271159Skevlo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23271159Skevlo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24271159Skevlo * SUCH DAMAGE. 25271159Skevlo * 26271159Skevlo */ 27271159Skevlo 28271159Skevlo#include <sys/cdefs.h> 29271159Skevlo__FBSDID("$FreeBSD$"); 30271159Skevlo 31271159Skevlo#include <sys/stdint.h> 32271159Skevlo#include <sys/stddef.h> 33271159Skevlo#include <sys/param.h> 34271159Skevlo#include <sys/queue.h> 35271159Skevlo#include <sys/types.h> 36271159Skevlo#include <sys/systm.h> 37271159Skevlo#include <sys/kernel.h> 38271159Skevlo#include <sys/bus.h> 39271159Skevlo#include <sys/module.h> 40271159Skevlo#include <sys/lock.h> 41271159Skevlo#include <sys/mutex.h> 42271159Skevlo#include <sys/condvar.h> 43271159Skevlo#include <sys/sysctl.h> 44271159Skevlo#include <sys/sx.h> 45271159Skevlo#include <sys/unistd.h> 46271159Skevlo#include <sys/callout.h> 47271159Skevlo#include <sys/malloc.h> 48271159Skevlo#include <sys/priv.h> 49271159Skevlo#include <sys/conf.h> 50271159Skevlo#include <sys/fcntl.h> 51271159Skevlo 52271159Skevlo#include <dev/usb/usb.h> 53271159Skevlo#include <dev/usb/usbdi.h> 54271159Skevlo#include <dev/usb/usbhid.h> 55271159Skevlo#include "usbdevs.h" 56271159Skevlo 57271159Skevlo#define USB_DEBUG_VAR usb_debug 58271159Skevlo#include <dev/usb/usb_debug.h> 59271159Skevlo 60271159Skevlo#include <dev/usb/uled_ioctl.h> 61271159Skevlo 62271159Skevlostruct uled_softc { 63271159Skevlo struct usb_fifo_sc sc_fifo; 64271159Skevlo struct mtx sc_mtx; 65271159Skevlo 66271159Skevlo struct usb_device *sc_udev; 67271159Skevlo struct uled_color sc_color; 68271159Skevlo 69271159Skevlo uint8_t sc_state; 70271159Skevlo#define ULED_ENABLED 0x01 71271159Skevlo}; 72271159Skevlo 73271159Skevlo/* prototypes */ 74271159Skevlo 75271159Skevlostatic device_probe_t uled_probe; 76271159Skevlostatic device_attach_t uled_attach; 77271159Skevlostatic device_detach_t uled_detach; 78271159Skevlo 79271159Skevlostatic usb_fifo_open_t uled_open; 80271159Skevlostatic usb_fifo_close_t uled_close; 81271159Skevlostatic usb_fifo_ioctl_t uled_ioctl; 82271159Skevlo 83271159Skevlostatic struct usb_fifo_methods uled_fifo_methods = { 84271159Skevlo .f_open = &uled_open, 85271159Skevlo .f_close = &uled_close, 86271159Skevlo .f_ioctl = &uled_ioctl, 87271159Skevlo .basename[0] = "uled", 88271159Skevlo}; 89271159Skevlo 90271159Skevlostatic usb_error_t uled_ctrl_msg(struct uled_softc *, uint8_t, uint8_t, 91271159Skevlo uint16_t, uint16_t, void *buf, uint16_t); 92271159Skevlostatic int uled_enable(struct uled_softc *); 93271159Skevlo 94271159Skevlostatic devclass_t uled_devclass; 95271159Skevlo 96271159Skevlostatic device_method_t uled_methods[] = { 97271159Skevlo DEVMETHOD(device_probe, uled_probe), 98271159Skevlo DEVMETHOD(device_attach, uled_attach), 99271159Skevlo DEVMETHOD(device_detach, uled_detach), 100271159Skevlo 101271159Skevlo DEVMETHOD_END 102271159Skevlo}; 103271159Skevlo 104271159Skevlostatic driver_t uled_driver = { 105271159Skevlo .name = "uled", 106271159Skevlo .methods = uled_methods, 107271159Skevlo .size = sizeof(struct uled_softc), 108271159Skevlo}; 109271159Skevlo 110292080Simpstatic const STRUCT_USB_HOST_ID uled_devs[] = { 111292080Simp {USB_VPI(USB_VENDOR_DREAMLINK, USB_PRODUCT_DREAMLINK_DL100B, 0)}, 112292080Simp}; 113292080Simp 114271159SkevloDRIVER_MODULE(uled, uhub, uled_driver, uled_devclass, NULL, NULL); 115271159SkevloMODULE_DEPEND(uled, usb, 1, 1, 1); 116271159SkevloMODULE_VERSION(uled, 1); 117292080SimpUSB_PNP_HOST_INFO(uled_devs); 118271159Skevlo 119271159Skevlostatic int 120271159Skevlouled_probe(device_t dev) 121271159Skevlo{ 122271159Skevlo struct usb_attach_arg *uaa; 123271159Skevlo 124271159Skevlo uaa = device_get_ivars(dev); 125271159Skevlo if (uaa->usb_mode != USB_MODE_HOST) 126271159Skevlo return (ENXIO); 127271159Skevlo if (uaa->info.bInterfaceClass != UICLASS_HID) 128271159Skevlo return (ENXIO); 129271159Skevlo 130271159Skevlo return (usbd_lookup_id_by_uaa(uled_devs, sizeof(uled_devs), uaa)); 131271159Skevlo} 132271159Skevlo 133271159Skevlostatic int 134271159Skevlouled_attach(device_t dev) 135271159Skevlo{ 136271159Skevlo struct usb_attach_arg *uaa; 137271159Skevlo struct uled_softc *sc; 138271159Skevlo int unit; 139271159Skevlo usb_error_t error; 140271159Skevlo 141271159Skevlo uaa = device_get_ivars(dev); 142271159Skevlo sc = device_get_softc(dev); 143271159Skevlo unit = device_get_unit(dev); 144271159Skevlo 145271159Skevlo device_set_usb_desc(dev); 146271159Skevlo mtx_init(&sc->sc_mtx, "uled lock", NULL, MTX_DEF | MTX_RECURSE); 147271159Skevlo 148271159Skevlo sc->sc_udev = uaa->device; 149271159Skevlo 150271159Skevlo error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx, 151271159Skevlo &uled_fifo_methods, &sc->sc_fifo, unit, -1, 152271159Skevlo uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); 153271159Skevlo if (error != 0) 154271159Skevlo goto detach; 155271159Skevlo 156271159Skevlo sc->sc_color.red = 0; 157271159Skevlo sc->sc_color.green = 0; 158271159Skevlo sc->sc_color.blue = 0; 159271159Skevlo 160271159Skevlo return (0); 161271159Skevlo 162271159Skevlodetach: 163271159Skevlo uled_detach(dev); 164271159Skevlo return (ENOMEM); 165271159Skevlo} 166271159Skevlo 167271159Skevlostatic int 168271159Skevlouled_detach(device_t dev) 169271159Skevlo{ 170271159Skevlo struct uled_softc *sc; 171271159Skevlo 172271159Skevlo sc = device_get_softc(dev); 173271159Skevlo usb_fifo_detach(&sc->sc_fifo); 174271159Skevlo mtx_destroy(&sc->sc_mtx); 175271159Skevlo return (0); 176271159Skevlo} 177271159Skevlo 178271159Skevlostatic usb_error_t 179271159Skevlouled_ctrl_msg(struct uled_softc *sc, uint8_t rt, uint8_t reqno, 180271159Skevlo uint16_t value, uint16_t index, void *buf, uint16_t buflen) 181271159Skevlo{ 182271159Skevlo struct usb_device_request req; 183271159Skevlo 184271159Skevlo req.bmRequestType = rt; 185271159Skevlo req.bRequest = reqno; 186271159Skevlo USETW(req.wValue, value); 187271159Skevlo USETW(req.wIndex, index); 188271159Skevlo USETW(req.wLength, buflen); 189271159Skevlo 190271159Skevlo return (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, buf, 191271159Skevlo 0, NULL, 2000)); 192271159Skevlo} 193271159Skevlo 194271159Skevlostatic int 195271159Skevlouled_enable(struct uled_softc *sc) 196271159Skevlo{ 197271159Skevlo static uint8_t cmdbuf[] = { 0x1f, 0x02, 0x00, 0x5f, 0x00, 0x00, 0x1a, 198271159Skevlo 0x03 }; 199271159Skevlo int error; 200271159Skevlo 201271159Skevlo sc->sc_state |= ULED_ENABLED; 202271159Skevlo mtx_lock(&sc->sc_mtx); 203271159Skevlo error = uled_ctrl_msg(sc, UT_WRITE_CLASS_INTERFACE, UR_SET_REPORT, 204271159Skevlo 0x200, 0, cmdbuf, sizeof(cmdbuf)); 205271159Skevlo mtx_unlock(&sc->sc_mtx); 206271159Skevlo return (error); 207271159Skevlo} 208271159Skevlo 209271159Skevlostatic int 210271159Skevlouled_open(struct usb_fifo *fifo, int fflags) 211271159Skevlo{ 212271159Skevlo if (fflags & FREAD) { 213271159Skevlo struct uled_softc *sc; 214271159Skevlo int rc; 215271159Skevlo 216271159Skevlo sc = usb_fifo_softc(fifo); 217271159Skevlo if (sc->sc_state & ULED_ENABLED) 218271159Skevlo return (EBUSY); 219271159Skevlo if ((rc = uled_enable(sc)) != 0) 220271159Skevlo return (rc); 221271159Skevlo } 222271159Skevlo return (0); 223271159Skevlo} 224271159Skevlo 225271159Skevlostatic void 226271159Skevlouled_close(struct usb_fifo *fifo, int fflags) 227271159Skevlo{ 228271159Skevlo if (fflags & FREAD) { 229271159Skevlo struct uled_softc *sc; 230271159Skevlo 231271159Skevlo sc = usb_fifo_softc(fifo); 232271159Skevlo sc->sc_state &= ~ULED_ENABLED; 233271159Skevlo } 234271159Skevlo} 235271680Sbrooks 236271159Skevlostatic int 237271159Skevlouled_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) 238271159Skevlo{ 239271159Skevlo struct uled_softc *sc; 240271159Skevlo struct uled_color color; 241271159Skevlo int error; 242271159Skevlo 243271159Skevlo sc = usb_fifo_softc(fifo); 244271159Skevlo error = 0; 245271159Skevlo 246271159Skevlo mtx_lock(&sc->sc_mtx); 247271159Skevlo 248271159Skevlo switch(cmd) { 249271159Skevlo case ULED_GET_COLOR: 250271159Skevlo *(struct uled_color *)addr = sc->sc_color; 251271159Skevlo break; 252271159Skevlo case ULED_SET_COLOR: 253271159Skevlo color = *(struct uled_color *)addr; 254271159Skevlo uint8_t buf[8]; 255271159Skevlo 256271159Skevlo sc->sc_color.red = color.red; 257271159Skevlo sc->sc_color.green = color.green; 258271159Skevlo sc->sc_color.blue = color.blue; 259271159Skevlo 260271159Skevlo buf[0] = color.red; 261271159Skevlo buf[1] = color.green; 262271159Skevlo buf[2] = color.blue; 263271159Skevlo buf[3] = buf[4] = buf[5] = 0; 264271159Skevlo buf[6] = 0x1a; 265271159Skevlo buf[7] = 0x05; 266271159Skevlo error = uled_ctrl_msg(sc, UT_WRITE_CLASS_INTERFACE, 267271159Skevlo UR_SET_REPORT, 0x200, 0, buf, sizeof(buf)); 268271159Skevlo break; 269271159Skevlo default: 270271159Skevlo error = ENOTTY; 271271159Skevlo break; 272271159Skevlo } 273271159Skevlo 274271159Skevlo mtx_unlock(&sc->sc_mtx); 275271159Skevlo return (error); 276271159Skevlo} 277