summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/misc/uled.c
diff options
context:
space:
mode:
authorkevlo <kevlo@FreeBSD.org>2014-09-05 11:25:58 +0000
committerkevlo <kevlo@FreeBSD.org>2014-09-05 11:25:58 +0000
commitdadcc9c0d2fe98f8127c9e5737a457b4e7cc3d8a (patch)
tree0bf7cd8dd10c27f1a8db7f06fcbcc8c12df6063c /sys/dev/usb/misc/uled.c
parentfe5ebc01ac813633bd578410ab0c3fd0123b2928 (diff)
downloadFreeBSD-src-dadcc9c0d2fe98f8127c9e5737a457b4e7cc3d8a.zip
FreeBSD-src-dadcc9c0d2fe98f8127c9e5737a457b4e7cc3d8a.tar.gz
The USB LED driver for the Dream Cheeky WebMail Notifier.
Reviewed by: hselasky
Diffstat (limited to 'sys/dev/usb/misc/uled.c')
-rw-r--r--sys/dev/usb/misc/uled.c275
1 files changed, 275 insertions, 0 deletions
diff --git a/sys/dev/usb/misc/uled.c b/sys/dev/usb/misc/uled.c
new file mode 100644
index 0000000..9053067
--- /dev/null
+++ b/sys/dev/usb/misc/uled.c
@@ -0,0 +1,275 @@
+/*-
+ * Copyright (c) 2014 Kevin Lo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbhid.h>
+#include "usbdevs.h"
+
+#define USB_DEBUG_VAR usb_debug
+#include <dev/usb/usb_debug.h>
+
+#include <dev/usb/uled_ioctl.h>
+
+struct uled_softc {
+ struct usb_fifo_sc sc_fifo;
+ struct mtx sc_mtx;
+
+ struct usb_device *sc_udev;
+ struct uled_color sc_color;
+
+ uint8_t sc_state;
+#define ULED_ENABLED 0x01
+};
+
+/* prototypes */
+
+static device_probe_t uled_probe;
+static device_attach_t uled_attach;
+static device_detach_t uled_detach;
+
+static usb_fifo_open_t uled_open;
+static usb_fifo_close_t uled_close;
+static usb_fifo_ioctl_t uled_ioctl;
+
+static struct usb_fifo_methods uled_fifo_methods = {
+ .f_open = &uled_open,
+ .f_close = &uled_close,
+ .f_ioctl = &uled_ioctl,
+ .basename[0] = "uled",
+};
+
+static usb_error_t uled_ctrl_msg(struct uled_softc *, uint8_t, uint8_t,
+ uint16_t, uint16_t, void *buf, uint16_t);
+static int uled_enable(struct uled_softc *);
+
+static devclass_t uled_devclass;
+
+static device_method_t uled_methods[] = {
+ DEVMETHOD(device_probe, uled_probe),
+ DEVMETHOD(device_attach, uled_attach),
+ DEVMETHOD(device_detach, uled_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t uled_driver = {
+ .name = "uled",
+ .methods = uled_methods,
+ .size = sizeof(struct uled_softc),
+};
+
+DRIVER_MODULE(uled, uhub, uled_driver, uled_devclass, NULL, NULL);
+MODULE_DEPEND(uled, usb, 1, 1, 1);
+MODULE_VERSION(uled, 1);
+
+static const STRUCT_USB_HOST_ID uled_devs[] = {
+ {USB_VPI(USB_VENDOR_DREAMLINK, USB_PRODUCT_DREAMLINK_DL100B, 0)},
+};
+
+static int
+uled_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa;
+
+ uaa = device_get_ivars(dev);
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bInterfaceClass != UICLASS_HID)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(uled_devs, sizeof(uled_devs), uaa));
+}
+
+static int
+uled_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa;
+ struct uled_softc *sc;
+ int unit;
+ usb_error_t error;
+
+ uaa = device_get_ivars(dev);
+ sc = device_get_softc(dev);
+ unit = device_get_unit(dev);
+
+ device_set_usb_desc(dev);
+ mtx_init(&sc->sc_mtx, "uled lock", NULL, MTX_DEF | MTX_RECURSE);
+
+ sc->sc_udev = uaa->device;
+
+ error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &uled_fifo_methods, &sc->sc_fifo, unit, -1,
+ uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644);
+ if (error != 0)
+ goto detach;
+
+ sc->sc_color.red = 0;
+ sc->sc_color.green = 0;
+ sc->sc_color.blue = 0;
+
+ return (0);
+
+detach:
+ uled_detach(dev);
+ return (ENOMEM);
+}
+
+static int
+uled_detach(device_t dev)
+{
+ struct uled_softc *sc;
+
+ sc = device_get_softc(dev);
+ usb_fifo_detach(&sc->sc_fifo);
+ mtx_destroy(&sc->sc_mtx);
+ return (0);
+}
+
+static usb_error_t
+uled_ctrl_msg(struct uled_softc *sc, uint8_t rt, uint8_t reqno,
+ uint16_t value, uint16_t index, void *buf, uint16_t buflen)
+{
+ struct usb_device_request req;
+
+ req.bmRequestType = rt;
+ req.bRequest = reqno;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, buflen);
+
+ return (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, buf,
+ 0, NULL, 2000));
+}
+
+static int
+uled_enable(struct uled_softc *sc)
+{
+ static uint8_t cmdbuf[] = { 0x1f, 0x02, 0x00, 0x5f, 0x00, 0x00, 0x1a,
+ 0x03 };
+ int error;
+
+ sc->sc_state |= ULED_ENABLED;
+ mtx_lock(&sc->sc_mtx);
+ error = uled_ctrl_msg(sc, UT_WRITE_CLASS_INTERFACE, UR_SET_REPORT,
+ 0x200, 0, cmdbuf, sizeof(cmdbuf));
+ mtx_unlock(&sc->sc_mtx);
+ return (error);
+}
+
+static int
+uled_open(struct usb_fifo *fifo, int fflags)
+{
+ if (fflags & FREAD) {
+ struct uled_softc *sc;
+ int rc;
+
+ sc = usb_fifo_softc(fifo);
+ if (sc->sc_state & ULED_ENABLED)
+ return (EBUSY);
+ if ((rc = uled_enable(sc)) != 0)
+ return (rc);
+ }
+ return (0);
+}
+
+static void
+uled_close(struct usb_fifo *fifo, int fflags)
+{
+ if (fflags & FREAD) {
+ struct uled_softc *sc;
+
+ sc = usb_fifo_softc(fifo);
+ sc->sc_state &= ~ULED_ENABLED;
+ }
+}
+
+static int
+uled_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
+{
+ struct uled_softc *sc;
+ struct uled_color color;
+ int error;
+
+ sc = usb_fifo_softc(fifo);
+ error = 0;
+
+ mtx_lock(&sc->sc_mtx);
+
+ switch(cmd) {
+ case ULED_GET_COLOR:
+ *(struct uled_color *)addr = sc->sc_color;
+ break;
+ case ULED_SET_COLOR:
+ color = *(struct uled_color *)addr;
+ uint8_t buf[8];
+
+ sc->sc_color.red = color.red;
+ sc->sc_color.green = color.green;
+ sc->sc_color.blue = color.blue;
+
+ buf[0] = color.red;
+ buf[1] = color.green;
+ buf[2] = color.blue;
+ buf[3] = buf[4] = buf[5] = 0;
+ buf[6] = 0x1a;
+ buf[7] = 0x05;
+ error = uled_ctrl_msg(sc, UT_WRITE_CLASS_INTERFACE,
+ UR_SET_REPORT, 0x200, 0, buf, sizeof(buf));
+ break;
+ default:
+ error = ENOTTY;
+ break;
+ }
+
+ mtx_unlock(&sc->sc_mtx);
+ return (error);
+}
OpenPOWER on IntegriCloud