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