xref: /freebsd/sys/dev/hid/hidraw.c (revision 947739079604f5f9fea92d783c9ef43dce52e462)
1*94773907SVladimir Kondratyev /*-
2*94773907SVladimir Kondratyev  * SPDX-License-Identifier: BSD-2-Clause-NetBSD
3*94773907SVladimir Kondratyev  *
4*94773907SVladimir Kondratyev  * Copyright (c) 1998 The NetBSD Foundation, Inc.
5*94773907SVladimir Kondratyev  * All rights reserved.
6*94773907SVladimir Kondratyev  * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
7*94773907SVladimir Kondratyev  *
8*94773907SVladimir Kondratyev  * This code is derived from software contributed to The NetBSD Foundation
9*94773907SVladimir Kondratyev  * by Lennart Augustsson (lennart@augustsson.net) at
10*94773907SVladimir Kondratyev  * Carlstedt Research & Technology.
11*94773907SVladimir Kondratyev  *
12*94773907SVladimir Kondratyev  * Redistribution and use in source and binary forms, with or without
13*94773907SVladimir Kondratyev  * modification, are permitted provided that the following conditions
14*94773907SVladimir Kondratyev  * are met:
15*94773907SVladimir Kondratyev  * 1. Redistributions of source code must retain the above copyright
16*94773907SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer.
17*94773907SVladimir Kondratyev  * 2. Redistributions in binary form must reproduce the above copyright
18*94773907SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer in the
19*94773907SVladimir Kondratyev  *    documentation and/or other materials provided with the distribution.
20*94773907SVladimir Kondratyev  *
21*94773907SVladimir Kondratyev  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22*94773907SVladimir Kondratyev  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23*94773907SVladimir Kondratyev  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24*94773907SVladimir Kondratyev  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25*94773907SVladimir Kondratyev  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26*94773907SVladimir Kondratyev  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27*94773907SVladimir Kondratyev  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28*94773907SVladimir Kondratyev  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29*94773907SVladimir Kondratyev  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30*94773907SVladimir Kondratyev  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31*94773907SVladimir Kondratyev  * POSSIBILITY OF SUCH DAMAGE.
32*94773907SVladimir Kondratyev  */
33*94773907SVladimir Kondratyev 
34*94773907SVladimir Kondratyev /*
35*94773907SVladimir Kondratyev  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
36*94773907SVladimir Kondratyev  */
37*94773907SVladimir Kondratyev 
38*94773907SVladimir Kondratyev #include <sys/cdefs.h>
39*94773907SVladimir Kondratyev __FBSDID("$FreeBSD$");
40*94773907SVladimir Kondratyev 
41*94773907SVladimir Kondratyev #include <sys/param.h>
42*94773907SVladimir Kondratyev #include <sys/bus.h>
43*94773907SVladimir Kondratyev #include <sys/conf.h>
44*94773907SVladimir Kondratyev #include <sys/fcntl.h>
45*94773907SVladimir Kondratyev #include <sys/filio.h>
46*94773907SVladimir Kondratyev #include <sys/ioccom.h>
47*94773907SVladimir Kondratyev #include <sys/kernel.h>
48*94773907SVladimir Kondratyev #include <sys/lock.h>
49*94773907SVladimir Kondratyev #include <sys/malloc.h>
50*94773907SVladimir Kondratyev #include <sys/module.h>
51*94773907SVladimir Kondratyev #include <sys/mutex.h>
52*94773907SVladimir Kondratyev #include <sys/poll.h>
53*94773907SVladimir Kondratyev #include <sys/priv.h>
54*94773907SVladimir Kondratyev #include <sys/proc.h>
55*94773907SVladimir Kondratyev #include <sys/selinfo.h>
56*94773907SVladimir Kondratyev #include <sys/sysctl.h>
57*94773907SVladimir Kondratyev #include <sys/systm.h>
58*94773907SVladimir Kondratyev #include <sys/tty.h>
59*94773907SVladimir Kondratyev #include <sys/uio.h>
60*94773907SVladimir Kondratyev 
61*94773907SVladimir Kondratyev #define HID_DEBUG_VAR	hidraw_debug
62*94773907SVladimir Kondratyev #include <dev/hid/hid.h>
63*94773907SVladimir Kondratyev #include <dev/hid/hidbus.h>
64*94773907SVladimir Kondratyev #include <dev/hid/hidraw.h>
65*94773907SVladimir Kondratyev 
66*94773907SVladimir Kondratyev #ifdef HID_DEBUG
67*94773907SVladimir Kondratyev static int hidraw_debug = 0;
68*94773907SVladimir Kondratyev static SYSCTL_NODE(_hw_hid, OID_AUTO, hidraw, CTLFLAG_RW, 0,
69*94773907SVladimir Kondratyev     "HID raw interface");
70*94773907SVladimir Kondratyev SYSCTL_INT(_hw_hid_hidraw, OID_AUTO, debug, CTLFLAG_RWTUN,
71*94773907SVladimir Kondratyev     &hidraw_debug, 0, "Debug level");
72*94773907SVladimir Kondratyev #endif
73*94773907SVladimir Kondratyev 
74*94773907SVladimir Kondratyev #define	HIDRAW_INDEX		0xFF	/* Arbitrary high value */
75*94773907SVladimir Kondratyev 
76*94773907SVladimir Kondratyev #define	HIDRAW_LOCAL_BUFSIZE	64	/* Size of on-stack buffer. */
77*94773907SVladimir Kondratyev #define	HIDRAW_LOCAL_ALLOC(local_buf, size)		\
78*94773907SVladimir Kondratyev 	(sizeof(local_buf) > (size) ? (local_buf) :	\
79*94773907SVladimir Kondratyev 	    malloc((size), M_DEVBUF, M_ZERO | M_WAITOK))
80*94773907SVladimir Kondratyev #define	HIDRAW_LOCAL_FREE(local_buf, buf)		\
81*94773907SVladimir Kondratyev 	if ((local_buf) != (buf)) {			\
82*94773907SVladimir Kondratyev 		free((buf), M_DEVBUF);			\
83*94773907SVladimir Kondratyev 	}
84*94773907SVladimir Kondratyev 
85*94773907SVladimir Kondratyev struct hidraw_softc {
86*94773907SVladimir Kondratyev 	device_t sc_dev;		/* base device */
87*94773907SVladimir Kondratyev 
88*94773907SVladimir Kondratyev 	struct mtx sc_mtx;		/* hidbus private mutex */
89*94773907SVladimir Kondratyev 
90*94773907SVladimir Kondratyev 	struct hid_rdesc_info *sc_rdesc;
91*94773907SVladimir Kondratyev 	const struct hid_device_info *sc_hw;
92*94773907SVladimir Kondratyev 
93*94773907SVladimir Kondratyev 	uint8_t *sc_q;
94*94773907SVladimir Kondratyev 	hid_size_t *sc_qlen;
95*94773907SVladimir Kondratyev 	int sc_head;
96*94773907SVladimir Kondratyev 	int sc_tail;
97*94773907SVladimir Kondratyev 	int sc_sleepcnt;
98*94773907SVladimir Kondratyev 
99*94773907SVladimir Kondratyev 	struct selinfo sc_rsel;
100*94773907SVladimir Kondratyev 	struct proc *sc_async;	/* process that wants SIGIO */
101*94773907SVladimir Kondratyev 	struct {			/* driver state */
102*94773907SVladimir Kondratyev 		bool	open:1;		/* device is open */
103*94773907SVladimir Kondratyev 		bool	aslp:1;		/* waiting for device data in read() */
104*94773907SVladimir Kondratyev 		bool	sel:1;		/* waiting for device data in poll() */
105*94773907SVladimir Kondratyev 		bool	quiet:1;	/* Ignore input data */
106*94773907SVladimir Kondratyev 		bool	immed:1;	/* return read data immediately */
107*94773907SVladimir Kondratyev 		bool	uhid:1;		/* driver switched in to uhid mode */
108*94773907SVladimir Kondratyev 		bool	lock:1;		/* input queue sleepable lock */
109*94773907SVladimir Kondratyev 		bool	flush:1;	/* do not wait for data in read() */
110*94773907SVladimir Kondratyev 	} sc_state;
111*94773907SVladimir Kondratyev 	int sc_fflags;			/* access mode for open lifetime */
112*94773907SVladimir Kondratyev 
113*94773907SVladimir Kondratyev 	struct cdev *dev;
114*94773907SVladimir Kondratyev };
115*94773907SVladimir Kondratyev 
116*94773907SVladimir Kondratyev static d_open_t		hidraw_open;
117*94773907SVladimir Kondratyev static d_read_t		hidraw_read;
118*94773907SVladimir Kondratyev static d_write_t	hidraw_write;
119*94773907SVladimir Kondratyev static d_ioctl_t	hidraw_ioctl;
120*94773907SVladimir Kondratyev static d_poll_t		hidraw_poll;
121*94773907SVladimir Kondratyev static d_kqfilter_t	hidraw_kqfilter;
122*94773907SVladimir Kondratyev 
123*94773907SVladimir Kondratyev static d_priv_dtor_t	hidraw_dtor;
124*94773907SVladimir Kondratyev 
125*94773907SVladimir Kondratyev static struct cdevsw hidraw_cdevsw = {
126*94773907SVladimir Kondratyev 	.d_version =	D_VERSION,
127*94773907SVladimir Kondratyev 	.d_open =	hidraw_open,
128*94773907SVladimir Kondratyev 	.d_read =	hidraw_read,
129*94773907SVladimir Kondratyev 	.d_write =	hidraw_write,
130*94773907SVladimir Kondratyev 	.d_ioctl =	hidraw_ioctl,
131*94773907SVladimir Kondratyev 	.d_poll =	hidraw_poll,
132*94773907SVladimir Kondratyev 	.d_kqfilter =	hidraw_kqfilter,
133*94773907SVladimir Kondratyev 	.d_name =	"hidraw",
134*94773907SVladimir Kondratyev };
135*94773907SVladimir Kondratyev 
136*94773907SVladimir Kondratyev static hid_intr_t	hidraw_intr;
137*94773907SVladimir Kondratyev 
138*94773907SVladimir Kondratyev static device_identify_t hidraw_identify;
139*94773907SVladimir Kondratyev static device_probe_t	hidraw_probe;
140*94773907SVladimir Kondratyev static device_attach_t	hidraw_attach;
141*94773907SVladimir Kondratyev static device_detach_t	hidraw_detach;
142*94773907SVladimir Kondratyev 
143*94773907SVladimir Kondratyev static int		hidraw_kqread(struct knote *, long);
144*94773907SVladimir Kondratyev static void		hidraw_kqdetach(struct knote *);
145*94773907SVladimir Kondratyev static void		hidraw_notify(struct hidraw_softc *);
146*94773907SVladimir Kondratyev 
147*94773907SVladimir Kondratyev static struct filterops hidraw_filterops_read = {
148*94773907SVladimir Kondratyev 	.f_isfd =	1,
149*94773907SVladimir Kondratyev 	.f_detach =	hidraw_kqdetach,
150*94773907SVladimir Kondratyev 	.f_event =	hidraw_kqread,
151*94773907SVladimir Kondratyev };
152*94773907SVladimir Kondratyev 
153*94773907SVladimir Kondratyev static void
154*94773907SVladimir Kondratyev hidraw_identify(driver_t *driver, device_t parent)
155*94773907SVladimir Kondratyev {
156*94773907SVladimir Kondratyev 	device_t child;
157*94773907SVladimir Kondratyev 
158*94773907SVladimir Kondratyev 	if (device_find_child(parent, "hidraw", -1) == NULL) {
159*94773907SVladimir Kondratyev 		child = BUS_ADD_CHILD(parent, 0, "hidraw",
160*94773907SVladimir Kondratyev 		    device_get_unit(parent));
161*94773907SVladimir Kondratyev 		if (child != NULL)
162*94773907SVladimir Kondratyev 			hidbus_set_index(child, HIDRAW_INDEX);
163*94773907SVladimir Kondratyev 	}
164*94773907SVladimir Kondratyev }
165*94773907SVladimir Kondratyev 
166*94773907SVladimir Kondratyev static int
167*94773907SVladimir Kondratyev hidraw_probe(device_t self)
168*94773907SVladimir Kondratyev {
169*94773907SVladimir Kondratyev 
170*94773907SVladimir Kondratyev 	if (hidbus_get_index(self) != HIDRAW_INDEX)
171*94773907SVladimir Kondratyev 		return (ENXIO);
172*94773907SVladimir Kondratyev 
173*94773907SVladimir Kondratyev 	hidbus_set_desc(self, "Raw HID Device");
174*94773907SVladimir Kondratyev 
175*94773907SVladimir Kondratyev 	return (BUS_PROBE_GENERIC);
176*94773907SVladimir Kondratyev }
177*94773907SVladimir Kondratyev 
178*94773907SVladimir Kondratyev static int
179*94773907SVladimir Kondratyev hidraw_attach(device_t self)
180*94773907SVladimir Kondratyev {
181*94773907SVladimir Kondratyev 	struct hidraw_softc *sc = device_get_softc(self);
182*94773907SVladimir Kondratyev 	struct make_dev_args mda;
183*94773907SVladimir Kondratyev 	int error;
184*94773907SVladimir Kondratyev 
185*94773907SVladimir Kondratyev 	sc->sc_dev = self;
186*94773907SVladimir Kondratyev 	sc->sc_rdesc = hidbus_get_rdesc_info(self);
187*94773907SVladimir Kondratyev 	sc->sc_hw = hid_get_device_info(self);
188*94773907SVladimir Kondratyev 
189*94773907SVladimir Kondratyev 	/* Hidraw mode does not require report descriptor to work */
190*94773907SVladimir Kondratyev 	if (sc->sc_rdesc->data == NULL || sc->sc_rdesc->len == 0)
191*94773907SVladimir Kondratyev 		device_printf(self, "no report descriptor\n");
192*94773907SVladimir Kondratyev 
193*94773907SVladimir Kondratyev 	mtx_init(&sc->sc_mtx, "hidraw lock", NULL, MTX_DEF);
194*94773907SVladimir Kondratyev 	knlist_init_mtx(&sc->sc_rsel.si_note, &sc->sc_mtx);
195*94773907SVladimir Kondratyev 
196*94773907SVladimir Kondratyev 	make_dev_args_init(&mda);
197*94773907SVladimir Kondratyev 	mda.mda_flags = MAKEDEV_WAITOK;
198*94773907SVladimir Kondratyev 	mda.mda_devsw = &hidraw_cdevsw;
199*94773907SVladimir Kondratyev 	mda.mda_uid = UID_ROOT;
200*94773907SVladimir Kondratyev 	mda.mda_gid = GID_OPERATOR;
201*94773907SVladimir Kondratyev 	mda.mda_mode = 0600;
202*94773907SVladimir Kondratyev 	mda.mda_si_drv1 = sc;
203*94773907SVladimir Kondratyev 
204*94773907SVladimir Kondratyev 	error = make_dev_s(&mda, &sc->dev, "hidraw%d", device_get_unit(self));
205*94773907SVladimir Kondratyev 	if (error) {
206*94773907SVladimir Kondratyev 		device_printf(self, "Can not create character device\n");
207*94773907SVladimir Kondratyev 		hidraw_detach(self);
208*94773907SVladimir Kondratyev 		return (error);
209*94773907SVladimir Kondratyev 	}
210*94773907SVladimir Kondratyev 
211*94773907SVladimir Kondratyev 	hidbus_set_lock(self, &sc->sc_mtx);
212*94773907SVladimir Kondratyev 	hidbus_set_intr(self, hidraw_intr, sc);
213*94773907SVladimir Kondratyev 
214*94773907SVladimir Kondratyev 	return (0);
215*94773907SVladimir Kondratyev }
216*94773907SVladimir Kondratyev 
217*94773907SVladimir Kondratyev static int
218*94773907SVladimir Kondratyev hidraw_detach(device_t self)
219*94773907SVladimir Kondratyev {
220*94773907SVladimir Kondratyev 	struct hidraw_softc *sc = device_get_softc(self);
221*94773907SVladimir Kondratyev 
222*94773907SVladimir Kondratyev 	DPRINTF("sc=%p\n", sc);
223*94773907SVladimir Kondratyev 
224*94773907SVladimir Kondratyev 	if (sc->dev != NULL) {
225*94773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
226*94773907SVladimir Kondratyev 		sc->dev->si_drv1 = NULL;
227*94773907SVladimir Kondratyev 		/* Wake everyone */
228*94773907SVladimir Kondratyev 		hidraw_notify(sc);
229*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
230*94773907SVladimir Kondratyev 		destroy_dev(sc->dev);
231*94773907SVladimir Kondratyev 	}
232*94773907SVladimir Kondratyev 
233*94773907SVladimir Kondratyev 	knlist_clear(&sc->sc_rsel.si_note, 0);
234*94773907SVladimir Kondratyev 	knlist_destroy(&sc->sc_rsel.si_note);
235*94773907SVladimir Kondratyev 	seldrain(&sc->sc_rsel);
236*94773907SVladimir Kondratyev 	mtx_destroy(&sc->sc_mtx);
237*94773907SVladimir Kondratyev 
238*94773907SVladimir Kondratyev 	return (0);
239*94773907SVladimir Kondratyev }
240*94773907SVladimir Kondratyev 
241*94773907SVladimir Kondratyev void
242*94773907SVladimir Kondratyev hidraw_intr(void *context, void *buf, hid_size_t len)
243*94773907SVladimir Kondratyev {
244*94773907SVladimir Kondratyev 	struct hidraw_softc *sc = context;
245*94773907SVladimir Kondratyev 	int next;
246*94773907SVladimir Kondratyev 
247*94773907SVladimir Kondratyev 	DPRINTFN(5, "len=%d\n", len);
248*94773907SVladimir Kondratyev 	DPRINTFN(5, "data = %*D\n", len, buf, " ");
249*94773907SVladimir Kondratyev 
250*94773907SVladimir Kondratyev 	next = (sc->sc_tail + 1) % HIDRAW_BUFFER_SIZE;
251*94773907SVladimir Kondratyev 	if (sc->sc_state.quiet || next == sc->sc_head)
252*94773907SVladimir Kondratyev 		return;
253*94773907SVladimir Kondratyev 
254*94773907SVladimir Kondratyev 	bcopy(buf, sc->sc_q + sc->sc_tail * sc->sc_rdesc->rdsize, len);
255*94773907SVladimir Kondratyev 
256*94773907SVladimir Kondratyev 	/* Make sure we don't process old data */
257*94773907SVladimir Kondratyev 	if (len < sc->sc_rdesc->rdsize)
258*94773907SVladimir Kondratyev 		bzero(sc->sc_q + sc->sc_tail * sc->sc_rdesc->rdsize + len,
259*94773907SVladimir Kondratyev 		    sc->sc_rdesc->isize - len);
260*94773907SVladimir Kondratyev 
261*94773907SVladimir Kondratyev 	sc->sc_qlen[sc->sc_tail] = len;
262*94773907SVladimir Kondratyev 	sc->sc_tail = next;
263*94773907SVladimir Kondratyev 
264*94773907SVladimir Kondratyev 	hidraw_notify(sc);
265*94773907SVladimir Kondratyev }
266*94773907SVladimir Kondratyev 
267*94773907SVladimir Kondratyev static inline int
268*94773907SVladimir Kondratyev hidraw_lock_queue(struct hidraw_softc *sc, bool flush)
269*94773907SVladimir Kondratyev {
270*94773907SVladimir Kondratyev 	int error = 0;
271*94773907SVladimir Kondratyev 
272*94773907SVladimir Kondratyev 	mtx_assert(&sc->sc_mtx, MA_OWNED);
273*94773907SVladimir Kondratyev 
274*94773907SVladimir Kondratyev 	if (flush)
275*94773907SVladimir Kondratyev 		sc->sc_state.flush = true;
276*94773907SVladimir Kondratyev 	++sc->sc_sleepcnt;
277*94773907SVladimir Kondratyev 	while (sc->sc_state.lock && error == 0) {
278*94773907SVladimir Kondratyev 		/* Flush is requested. Wakeup all readers and forbid sleeps */
279*94773907SVladimir Kondratyev 		if (flush && sc->sc_state.aslp) {
280*94773907SVladimir Kondratyev 			sc->sc_state.aslp = false;
281*94773907SVladimir Kondratyev 			DPRINTFN(5, "waking %p\n", &sc->sc_q);
282*94773907SVladimir Kondratyev 			wakeup(&sc->sc_q);
283*94773907SVladimir Kondratyev 		}
284*94773907SVladimir Kondratyev 		error = mtx_sleep(&sc->sc_sleepcnt, &sc->sc_mtx,
285*94773907SVladimir Kondratyev 		    PZERO | PCATCH, "hidrawio", 0);
286*94773907SVladimir Kondratyev 	}
287*94773907SVladimir Kondratyev 	--sc->sc_sleepcnt;
288*94773907SVladimir Kondratyev 	if (flush)
289*94773907SVladimir Kondratyev 		sc->sc_state.flush = false;
290*94773907SVladimir Kondratyev 	if (error == 0)
291*94773907SVladimir Kondratyev 		sc->sc_state.lock = true;
292*94773907SVladimir Kondratyev 
293*94773907SVladimir Kondratyev 	return (error);
294*94773907SVladimir Kondratyev }
295*94773907SVladimir Kondratyev 
296*94773907SVladimir Kondratyev static inline void
297*94773907SVladimir Kondratyev hidraw_unlock_queue(struct hidraw_softc *sc)
298*94773907SVladimir Kondratyev {
299*94773907SVladimir Kondratyev 
300*94773907SVladimir Kondratyev 	mtx_assert(&sc->sc_mtx, MA_OWNED);
301*94773907SVladimir Kondratyev 	KASSERT(sc->sc_state.lock, ("input buffer is not locked"));
302*94773907SVladimir Kondratyev 
303*94773907SVladimir Kondratyev 	if (sc->sc_sleepcnt != 0)
304*94773907SVladimir Kondratyev 		wakeup_one(&sc->sc_sleepcnt);
305*94773907SVladimir Kondratyev 	sc->sc_state.lock = false;
306*94773907SVladimir Kondratyev }
307*94773907SVladimir Kondratyev 
308*94773907SVladimir Kondratyev static int
309*94773907SVladimir Kondratyev hidraw_open(struct cdev *dev, int flag, int mode, struct thread *td)
310*94773907SVladimir Kondratyev {
311*94773907SVladimir Kondratyev 	struct hidraw_softc *sc;
312*94773907SVladimir Kondratyev 	int error;
313*94773907SVladimir Kondratyev 
314*94773907SVladimir Kondratyev 	sc = dev->si_drv1;
315*94773907SVladimir Kondratyev 	if (sc == NULL)
316*94773907SVladimir Kondratyev 		return (ENXIO);
317*94773907SVladimir Kondratyev 
318*94773907SVladimir Kondratyev 	DPRINTF("sc=%p\n", sc);
319*94773907SVladimir Kondratyev 
320*94773907SVladimir Kondratyev 	mtx_lock(&sc->sc_mtx);
321*94773907SVladimir Kondratyev 	if (sc->sc_state.open) {
322*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
323*94773907SVladimir Kondratyev 		return (EBUSY);
324*94773907SVladimir Kondratyev 	}
325*94773907SVladimir Kondratyev 	sc->sc_state.open = true;
326*94773907SVladimir Kondratyev 	mtx_unlock(&sc->sc_mtx);
327*94773907SVladimir Kondratyev 
328*94773907SVladimir Kondratyev 	error = devfs_set_cdevpriv(sc, hidraw_dtor);
329*94773907SVladimir Kondratyev 	if (error != 0) {
330*94773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
331*94773907SVladimir Kondratyev 		sc->sc_state.open = false;
332*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
333*94773907SVladimir Kondratyev 		return (error);
334*94773907SVladimir Kondratyev 	}
335*94773907SVladimir Kondratyev 
336*94773907SVladimir Kondratyev 	sc->sc_q = malloc(sc->sc_rdesc->rdsize * HIDRAW_BUFFER_SIZE, M_DEVBUF,
337*94773907SVladimir Kondratyev 	    M_ZERO | M_WAITOK);
338*94773907SVladimir Kondratyev 	sc->sc_qlen = malloc(sizeof(hid_size_t) * HIDRAW_BUFFER_SIZE, M_DEVBUF,
339*94773907SVladimir Kondratyev 	    M_ZERO | M_WAITOK);
340*94773907SVladimir Kondratyev 
341*94773907SVladimir Kondratyev 	/* Set up interrupt pipe. */
342*94773907SVladimir Kondratyev 	sc->sc_state.immed = false;
343*94773907SVladimir Kondratyev 	sc->sc_async = 0;
344*94773907SVladimir Kondratyev 	sc->sc_state.uhid = false;	/* hidraw mode is default */
345*94773907SVladimir Kondratyev 	sc->sc_state.quiet = false;
346*94773907SVladimir Kondratyev 	sc->sc_head = sc->sc_tail = 0;
347*94773907SVladimir Kondratyev 	sc->sc_fflags = flag;
348*94773907SVladimir Kondratyev 
349*94773907SVladimir Kondratyev 	hidbus_intr_start(sc->sc_dev);
350*94773907SVladimir Kondratyev 
351*94773907SVladimir Kondratyev 	return (0);
352*94773907SVladimir Kondratyev }
353*94773907SVladimir Kondratyev 
354*94773907SVladimir Kondratyev static void
355*94773907SVladimir Kondratyev hidraw_dtor(void *data)
356*94773907SVladimir Kondratyev {
357*94773907SVladimir Kondratyev 	struct hidraw_softc *sc = data;
358*94773907SVladimir Kondratyev 
359*94773907SVladimir Kondratyev 	DPRINTF("sc=%p\n", sc);
360*94773907SVladimir Kondratyev 
361*94773907SVladimir Kondratyev 	/* Disable interrupts. */
362*94773907SVladimir Kondratyev 	hidbus_intr_stop(sc->sc_dev);
363*94773907SVladimir Kondratyev 
364*94773907SVladimir Kondratyev 	sc->sc_tail = sc->sc_head = 0;
365*94773907SVladimir Kondratyev 	sc->sc_async = 0;
366*94773907SVladimir Kondratyev 	free(sc->sc_q, M_DEVBUF);
367*94773907SVladimir Kondratyev 	free(sc->sc_qlen, M_DEVBUF);
368*94773907SVladimir Kondratyev 	sc->sc_q = NULL;
369*94773907SVladimir Kondratyev 
370*94773907SVladimir Kondratyev 	mtx_lock(&sc->sc_mtx);
371*94773907SVladimir Kondratyev 	sc->sc_state.open = false;
372*94773907SVladimir Kondratyev 	mtx_unlock(&sc->sc_mtx);
373*94773907SVladimir Kondratyev }
374*94773907SVladimir Kondratyev 
375*94773907SVladimir Kondratyev static int
376*94773907SVladimir Kondratyev hidraw_read(struct cdev *dev, struct uio *uio, int flag)
377*94773907SVladimir Kondratyev {
378*94773907SVladimir Kondratyev 	struct hidraw_softc *sc;
379*94773907SVladimir Kondratyev 	size_t length;
380*94773907SVladimir Kondratyev 	int error;
381*94773907SVladimir Kondratyev 
382*94773907SVladimir Kondratyev 	DPRINTFN(1, "\n");
383*94773907SVladimir Kondratyev 
384*94773907SVladimir Kondratyev 	sc = dev->si_drv1;
385*94773907SVladimir Kondratyev 	if (sc == NULL)
386*94773907SVladimir Kondratyev 		return (EIO);
387*94773907SVladimir Kondratyev 
388*94773907SVladimir Kondratyev 	mtx_lock(&sc->sc_mtx);
389*94773907SVladimir Kondratyev 	error = dev->si_drv1 == NULL ? EIO : hidraw_lock_queue(sc, false);
390*94773907SVladimir Kondratyev 	if (error != 0) {
391*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
392*94773907SVladimir Kondratyev 		return (error);
393*94773907SVladimir Kondratyev 	}
394*94773907SVladimir Kondratyev 
395*94773907SVladimir Kondratyev 	if (sc->sc_state.immed) {
396*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
397*94773907SVladimir Kondratyev 		DPRINTFN(1, "immed\n");
398*94773907SVladimir Kondratyev 
399*94773907SVladimir Kondratyev 		error = hid_get_report(sc->sc_dev, sc->sc_q,
400*94773907SVladimir Kondratyev 		    sc->sc_rdesc->isize, NULL, HID_INPUT_REPORT,
401*94773907SVladimir Kondratyev 		    sc->sc_rdesc->iid);
402*94773907SVladimir Kondratyev 		if (error == 0)
403*94773907SVladimir Kondratyev 			error = uiomove(sc->sc_q, sc->sc_rdesc->isize, uio);
404*94773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
405*94773907SVladimir Kondratyev 		goto exit;
406*94773907SVladimir Kondratyev 	}
407*94773907SVladimir Kondratyev 
408*94773907SVladimir Kondratyev 	while (sc->sc_tail == sc->sc_head && !sc->sc_state.flush) {
409*94773907SVladimir Kondratyev 		if (flag & O_NONBLOCK) {
410*94773907SVladimir Kondratyev 			error = EWOULDBLOCK;
411*94773907SVladimir Kondratyev 			goto exit;
412*94773907SVladimir Kondratyev 		}
413*94773907SVladimir Kondratyev 		sc->sc_state.aslp = true;
414*94773907SVladimir Kondratyev 		DPRINTFN(5, "sleep on %p\n", &sc->sc_q);
415*94773907SVladimir Kondratyev 		error = mtx_sleep(&sc->sc_q, &sc->sc_mtx, PZERO | PCATCH,
416*94773907SVladimir Kondratyev 		    "hidrawrd", 0);
417*94773907SVladimir Kondratyev 		DPRINTFN(5, "woke, error=%d\n", error);
418*94773907SVladimir Kondratyev 		if (dev->si_drv1 == NULL)
419*94773907SVladimir Kondratyev 			error = EIO;
420*94773907SVladimir Kondratyev 		if (error) {
421*94773907SVladimir Kondratyev 			sc->sc_state.aslp = false;
422*94773907SVladimir Kondratyev 			goto exit;
423*94773907SVladimir Kondratyev 		}
424*94773907SVladimir Kondratyev 	}
425*94773907SVladimir Kondratyev 
426*94773907SVladimir Kondratyev 	while (sc->sc_tail != sc->sc_head && uio->uio_resid > 0) {
427*94773907SVladimir Kondratyev 		length = min(uio->uio_resid, sc->sc_state.uhid ?
428*94773907SVladimir Kondratyev 		    sc->sc_rdesc->isize : sc->sc_qlen[sc->sc_head]);
429*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
430*94773907SVladimir Kondratyev 
431*94773907SVladimir Kondratyev 		/* Copy the data to the user process. */
432*94773907SVladimir Kondratyev 		DPRINTFN(5, "got %lu chars\n", (u_long)length);
433*94773907SVladimir Kondratyev 		error = uiomove(sc->sc_q + sc->sc_head * sc->sc_rdesc->rdsize,
434*94773907SVladimir Kondratyev 		    length, uio);
435*94773907SVladimir Kondratyev 
436*94773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
437*94773907SVladimir Kondratyev 		if (error != 0)
438*94773907SVladimir Kondratyev 			goto exit;
439*94773907SVladimir Kondratyev 		/* Remove a small chunk from the input queue. */
440*94773907SVladimir Kondratyev 		sc->sc_head = (sc->sc_head + 1) % HIDRAW_BUFFER_SIZE;
441*94773907SVladimir Kondratyev 		/*
442*94773907SVladimir Kondratyev 		 * In uhid mode transfer as many chunks as possible. Hidraw
443*94773907SVladimir Kondratyev 		 * packets are transferred one by one due to different length.
444*94773907SVladimir Kondratyev 		 */
445*94773907SVladimir Kondratyev 		if (!sc->sc_state.uhid)
446*94773907SVladimir Kondratyev 			goto exit;
447*94773907SVladimir Kondratyev 	}
448*94773907SVladimir Kondratyev exit:
449*94773907SVladimir Kondratyev 	hidraw_unlock_queue(sc);
450*94773907SVladimir Kondratyev 	mtx_unlock(&sc->sc_mtx);
451*94773907SVladimir Kondratyev 
452*94773907SVladimir Kondratyev 	return (error);
453*94773907SVladimir Kondratyev }
454*94773907SVladimir Kondratyev 
455*94773907SVladimir Kondratyev static int
456*94773907SVladimir Kondratyev hidraw_write(struct cdev *dev, struct uio *uio, int flag)
457*94773907SVladimir Kondratyev {
458*94773907SVladimir Kondratyev 	uint8_t local_buf[HIDRAW_LOCAL_BUFSIZE], *buf;
459*94773907SVladimir Kondratyev 	struct hidraw_softc *sc;
460*94773907SVladimir Kondratyev 	int error;
461*94773907SVladimir Kondratyev 	int size;
462*94773907SVladimir Kondratyev 	size_t buf_offset;
463*94773907SVladimir Kondratyev 	uint8_t id = 0;
464*94773907SVladimir Kondratyev 
465*94773907SVladimir Kondratyev 	DPRINTFN(1, "\n");
466*94773907SVladimir Kondratyev 
467*94773907SVladimir Kondratyev 	sc = dev->si_drv1;
468*94773907SVladimir Kondratyev 	if (sc == NULL)
469*94773907SVladimir Kondratyev 		return (EIO);
470*94773907SVladimir Kondratyev 
471*94773907SVladimir Kondratyev 	if (sc->sc_rdesc->osize == 0)
472*94773907SVladimir Kondratyev 		return (EOPNOTSUPP);
473*94773907SVladimir Kondratyev 
474*94773907SVladimir Kondratyev 	buf_offset = 0;
475*94773907SVladimir Kondratyev 	if (sc->sc_state.uhid) {
476*94773907SVladimir Kondratyev 		size = sc->sc_rdesc->osize;
477*94773907SVladimir Kondratyev 		if (uio->uio_resid != size)
478*94773907SVladimir Kondratyev 			return (EINVAL);
479*94773907SVladimir Kondratyev 	} else {
480*94773907SVladimir Kondratyev 		size = uio->uio_resid;
481*94773907SVladimir Kondratyev 		if (size < 2)
482*94773907SVladimir Kondratyev 			return (EINVAL);
483*94773907SVladimir Kondratyev 		/* Strip leading 0 if the device doesnt use numbered reports */
484*94773907SVladimir Kondratyev 		error = uiomove(&id, 1, uio);
485*94773907SVladimir Kondratyev 		if (error)
486*94773907SVladimir Kondratyev 			return (error);
487*94773907SVladimir Kondratyev 		if (id != 0)
488*94773907SVladimir Kondratyev 			buf_offset++;
489*94773907SVladimir Kondratyev 		else
490*94773907SVladimir Kondratyev 			size--;
491*94773907SVladimir Kondratyev 		/* Check if underlying driver could process this request */
492*94773907SVladimir Kondratyev 		if (size > sc->sc_rdesc->wrsize)
493*94773907SVladimir Kondratyev 			return (ENOBUFS);
494*94773907SVladimir Kondratyev 	}
495*94773907SVladimir Kondratyev 	buf = HIDRAW_LOCAL_ALLOC(local_buf, size);
496*94773907SVladimir Kondratyev 	buf[0] = id;
497*94773907SVladimir Kondratyev 	error = uiomove(buf + buf_offset, uio->uio_resid, uio);
498*94773907SVladimir Kondratyev 	if (error == 0)
499*94773907SVladimir Kondratyev 		error = hid_write(sc->sc_dev, buf, size);
500*94773907SVladimir Kondratyev 	HIDRAW_LOCAL_FREE(local_buf, buf);
501*94773907SVladimir Kondratyev 
502*94773907SVladimir Kondratyev 	return (error);
503*94773907SVladimir Kondratyev }
504*94773907SVladimir Kondratyev 
505*94773907SVladimir Kondratyev static int
506*94773907SVladimir Kondratyev hidraw_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
507*94773907SVladimir Kondratyev     struct thread *td)
508*94773907SVladimir Kondratyev {
509*94773907SVladimir Kondratyev 	uint8_t local_buf[HIDRAW_LOCAL_BUFSIZE];
510*94773907SVladimir Kondratyev 	void *buf;
511*94773907SVladimir Kondratyev 	struct hidraw_softc *sc;
512*94773907SVladimir Kondratyev 	struct hidraw_gen_descriptor *hgd;
513*94773907SVladimir Kondratyev 	struct hidraw_report_descriptor *hrd;
514*94773907SVladimir Kondratyev 	struct hidraw_devinfo *hdi;
515*94773907SVladimir Kondratyev 	uint32_t size;
516*94773907SVladimir Kondratyev 	int id, len;
517*94773907SVladimir Kondratyev 	int error = 0;
518*94773907SVladimir Kondratyev 
519*94773907SVladimir Kondratyev 	DPRINTFN(2, "cmd=%lx\n", cmd);
520*94773907SVladimir Kondratyev 
521*94773907SVladimir Kondratyev 	sc = dev->si_drv1;
522*94773907SVladimir Kondratyev 	if (sc == NULL)
523*94773907SVladimir Kondratyev 		return (EIO);
524*94773907SVladimir Kondratyev 
525*94773907SVladimir Kondratyev 	/* fixed-length ioctls handling */
526*94773907SVladimir Kondratyev 	switch (cmd) {
527*94773907SVladimir Kondratyev 	case FIONBIO:
528*94773907SVladimir Kondratyev 		/* All handled in the upper FS layer. */
529*94773907SVladimir Kondratyev 		return (0);
530*94773907SVladimir Kondratyev 
531*94773907SVladimir Kondratyev 	case FIOASYNC:
532*94773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
533*94773907SVladimir Kondratyev 		if (*(int *)addr) {
534*94773907SVladimir Kondratyev 			if (sc->sc_async == NULL) {
535*94773907SVladimir Kondratyev 				sc->sc_async = td->td_proc;
536*94773907SVladimir Kondratyev 				DPRINTF("FIOASYNC %p\n", sc->sc_async);
537*94773907SVladimir Kondratyev 			} else
538*94773907SVladimir Kondratyev 				error = EBUSY;
539*94773907SVladimir Kondratyev 		} else
540*94773907SVladimir Kondratyev 			sc->sc_async = NULL;
541*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
542*94773907SVladimir Kondratyev 		return (error);
543*94773907SVladimir Kondratyev 
544*94773907SVladimir Kondratyev 	/* XXX this is not the most general solution. */
545*94773907SVladimir Kondratyev 	case TIOCSPGRP:
546*94773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
547*94773907SVladimir Kondratyev 		if (sc->sc_async == NULL)
548*94773907SVladimir Kondratyev 			error = EINVAL;
549*94773907SVladimir Kondratyev 		else if (*(int *)addr != sc->sc_async->p_pgid)
550*94773907SVladimir Kondratyev 			error = EPERM;
551*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
552*94773907SVladimir Kondratyev 		return (error);
553*94773907SVladimir Kondratyev 
554*94773907SVladimir Kondratyev 	case HIDRAW_GET_REPORT_DESC:
555*94773907SVladimir Kondratyev 		if (sc->sc_rdesc->data == NULL || sc->sc_rdesc->len == 0)
556*94773907SVladimir Kondratyev 			return (EOPNOTSUPP);
557*94773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
558*94773907SVladimir Kondratyev 		sc->sc_state.uhid = true;
559*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
560*94773907SVladimir Kondratyev 		hgd = (struct hidraw_gen_descriptor *)addr;
561*94773907SVladimir Kondratyev 		if (sc->sc_rdesc->len > hgd->hgd_maxlen) {
562*94773907SVladimir Kondratyev 			size = hgd->hgd_maxlen;
563*94773907SVladimir Kondratyev 		} else {
564*94773907SVladimir Kondratyev 			size = sc->sc_rdesc->len;
565*94773907SVladimir Kondratyev 		}
566*94773907SVladimir Kondratyev 		hgd->hgd_actlen = size;
567*94773907SVladimir Kondratyev 		if (hgd->hgd_data == NULL)
568*94773907SVladimir Kondratyev 			return (0);		/* descriptor length only */
569*94773907SVladimir Kondratyev 		return (copyout(sc->sc_rdesc->data, hgd->hgd_data, size));
570*94773907SVladimir Kondratyev 
571*94773907SVladimir Kondratyev 
572*94773907SVladimir Kondratyev 	case HIDRAW_SET_REPORT_DESC:
573*94773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FWRITE))
574*94773907SVladimir Kondratyev 			return (EPERM);
575*94773907SVladimir Kondratyev 
576*94773907SVladimir Kondratyev 		/* check privileges */
577*94773907SVladimir Kondratyev 		error = priv_check(curthread, PRIV_DRIVER);
578*94773907SVladimir Kondratyev 		if (error)
579*94773907SVladimir Kondratyev 			return (error);
580*94773907SVladimir Kondratyev 
581*94773907SVladimir Kondratyev 		/* Stop interrupts and clear input report buffer */
582*94773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
583*94773907SVladimir Kondratyev 		sc->sc_tail = sc->sc_head = 0;
584*94773907SVladimir Kondratyev 		error = hidraw_lock_queue(sc, true);
585*94773907SVladimir Kondratyev 		if (error == 0)
586*94773907SVladimir Kondratyev 			sc->sc_state.quiet = true;
587*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
588*94773907SVladimir Kondratyev 		if (error != 0)
589*94773907SVladimir Kondratyev 			return(error);
590*94773907SVladimir Kondratyev 
591*94773907SVladimir Kondratyev 		hgd = (struct hidraw_gen_descriptor *)addr;
592*94773907SVladimir Kondratyev 		buf = HIDRAW_LOCAL_ALLOC(local_buf, hgd->hgd_maxlen);
593*94773907SVladimir Kondratyev 		copyin(hgd->hgd_data, buf, hgd->hgd_maxlen);
594*94773907SVladimir Kondratyev 		/* Lock newbus around set_report_descr call */
595*94773907SVladimir Kondratyev 		mtx_lock(&Giant);
596*94773907SVladimir Kondratyev 		error = hid_set_report_descr(sc->sc_dev, buf, hgd->hgd_maxlen);
597*94773907SVladimir Kondratyev 		mtx_unlock(&Giant);
598*94773907SVladimir Kondratyev 		HIDRAW_LOCAL_FREE(local_buf, buf);
599*94773907SVladimir Kondratyev 
600*94773907SVladimir Kondratyev 		/* Realloc hidraw input queue */
601*94773907SVladimir Kondratyev 		if (error == 0)
602*94773907SVladimir Kondratyev 			sc->sc_q = realloc(sc->sc_q,
603*94773907SVladimir Kondratyev 			    sc->sc_rdesc->rdsize * HIDRAW_BUFFER_SIZE,
604*94773907SVladimir Kondratyev 			    M_DEVBUF, M_ZERO | M_WAITOK);
605*94773907SVladimir Kondratyev 
606*94773907SVladimir Kondratyev 		/* Start interrupts again */
607*94773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
608*94773907SVladimir Kondratyev 		sc->sc_state.quiet = false;
609*94773907SVladimir Kondratyev 		hidraw_unlock_queue(sc);
610*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
611*94773907SVladimir Kondratyev 		return (error);
612*94773907SVladimir Kondratyev 	case HIDRAW_SET_IMMED:
613*94773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FREAD))
614*94773907SVladimir Kondratyev 			return (EPERM);
615*94773907SVladimir Kondratyev 		if (*(int *)addr) {
616*94773907SVladimir Kondratyev 			/* XXX should read into ibuf, but does it matter? */
617*94773907SVladimir Kondratyev 			size = sc->sc_rdesc->isize;
618*94773907SVladimir Kondratyev 			buf = HIDRAW_LOCAL_ALLOC(local_buf, size);
619*94773907SVladimir Kondratyev 			error = hid_get_report(sc->sc_dev, buf, size, NULL,
620*94773907SVladimir Kondratyev 			    HID_INPUT_REPORT, sc->sc_rdesc->iid);
621*94773907SVladimir Kondratyev 			HIDRAW_LOCAL_FREE(local_buf, buf);
622*94773907SVladimir Kondratyev 			if (error)
623*94773907SVladimir Kondratyev 				return (EOPNOTSUPP);
624*94773907SVladimir Kondratyev 
625*94773907SVladimir Kondratyev 			mtx_lock(&sc->sc_mtx);
626*94773907SVladimir Kondratyev 			sc->sc_state.immed = true;
627*94773907SVladimir Kondratyev 			mtx_unlock(&sc->sc_mtx);
628*94773907SVladimir Kondratyev 		} else {
629*94773907SVladimir Kondratyev 			mtx_lock(&sc->sc_mtx);
630*94773907SVladimir Kondratyev 			sc->sc_state.immed = false;
631*94773907SVladimir Kondratyev 			mtx_unlock(&sc->sc_mtx);
632*94773907SVladimir Kondratyev 		}
633*94773907SVladimir Kondratyev 		return (0);
634*94773907SVladimir Kondratyev 
635*94773907SVladimir Kondratyev 	case HIDRAW_GET_REPORT:
636*94773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FREAD))
637*94773907SVladimir Kondratyev 			return (EPERM);
638*94773907SVladimir Kondratyev 		hgd = (struct hidraw_gen_descriptor *)addr;
639*94773907SVladimir Kondratyev 		switch (hgd->hgd_report_type) {
640*94773907SVladimir Kondratyev 		case HID_INPUT_REPORT:
641*94773907SVladimir Kondratyev 			size = sc->sc_rdesc->isize;
642*94773907SVladimir Kondratyev 			id = sc->sc_rdesc->iid;
643*94773907SVladimir Kondratyev 			break;
644*94773907SVladimir Kondratyev 		case HID_OUTPUT_REPORT:
645*94773907SVladimir Kondratyev 			size = sc->sc_rdesc->osize;
646*94773907SVladimir Kondratyev 			id = sc->sc_rdesc->oid;
647*94773907SVladimir Kondratyev 			break;
648*94773907SVladimir Kondratyev 		case HID_FEATURE_REPORT:
649*94773907SVladimir Kondratyev 			size = sc->sc_rdesc->fsize;
650*94773907SVladimir Kondratyev 			id = sc->sc_rdesc->fid;
651*94773907SVladimir Kondratyev 			break;
652*94773907SVladimir Kondratyev 		default:
653*94773907SVladimir Kondratyev 			return (EINVAL);
654*94773907SVladimir Kondratyev 		}
655*94773907SVladimir Kondratyev 		if (id != 0)
656*94773907SVladimir Kondratyev 			copyin(hgd->hgd_data, &id, 1);
657*94773907SVladimir Kondratyev 		size = MIN(hgd->hgd_maxlen, size);
658*94773907SVladimir Kondratyev 		buf = HIDRAW_LOCAL_ALLOC(local_buf, size);
659*94773907SVladimir Kondratyev 		error = hid_get_report(sc->sc_dev, buf, size, NULL,
660*94773907SVladimir Kondratyev 		    hgd->hgd_report_type, id);
661*94773907SVladimir Kondratyev 		if (!error)
662*94773907SVladimir Kondratyev 			error = copyout(buf, hgd->hgd_data, size);
663*94773907SVladimir Kondratyev 		HIDRAW_LOCAL_FREE(local_buf, buf);
664*94773907SVladimir Kondratyev 		return (error);
665*94773907SVladimir Kondratyev 
666*94773907SVladimir Kondratyev 	case HIDRAW_SET_REPORT:
667*94773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FWRITE))
668*94773907SVladimir Kondratyev 			return (EPERM);
669*94773907SVladimir Kondratyev 		hgd = (struct hidraw_gen_descriptor *)addr;
670*94773907SVladimir Kondratyev 		switch (hgd->hgd_report_type) {
671*94773907SVladimir Kondratyev 		case HID_INPUT_REPORT:
672*94773907SVladimir Kondratyev 			size = sc->sc_rdesc->isize;
673*94773907SVladimir Kondratyev 			id = sc->sc_rdesc->iid;
674*94773907SVladimir Kondratyev 			break;
675*94773907SVladimir Kondratyev 		case HID_OUTPUT_REPORT:
676*94773907SVladimir Kondratyev 			size = sc->sc_rdesc->osize;
677*94773907SVladimir Kondratyev 			id = sc->sc_rdesc->oid;
678*94773907SVladimir Kondratyev 			break;
679*94773907SVladimir Kondratyev 		case HID_FEATURE_REPORT:
680*94773907SVladimir Kondratyev 			size = sc->sc_rdesc->fsize;
681*94773907SVladimir Kondratyev 			id = sc->sc_rdesc->fid;
682*94773907SVladimir Kondratyev 			break;
683*94773907SVladimir Kondratyev 		default:
684*94773907SVladimir Kondratyev 			return (EINVAL);
685*94773907SVladimir Kondratyev 		}
686*94773907SVladimir Kondratyev 		size = MIN(hgd->hgd_maxlen, size);
687*94773907SVladimir Kondratyev 		buf = HIDRAW_LOCAL_ALLOC(local_buf, size);
688*94773907SVladimir Kondratyev 		copyin(hgd->hgd_data, buf, size);
689*94773907SVladimir Kondratyev 		if (id != 0)
690*94773907SVladimir Kondratyev 			id = *(uint8_t *)buf;
691*94773907SVladimir Kondratyev 		error = hid_set_report(sc->sc_dev, buf, size,
692*94773907SVladimir Kondratyev 		    hgd->hgd_report_type, id);
693*94773907SVladimir Kondratyev 		HIDRAW_LOCAL_FREE(local_buf, buf);
694*94773907SVladimir Kondratyev 		return (error);
695*94773907SVladimir Kondratyev 
696*94773907SVladimir Kondratyev 	case HIDRAW_GET_REPORT_ID:
697*94773907SVladimir Kondratyev 		*(int *)addr = 0;	/* XXX: we only support reportid 0? */
698*94773907SVladimir Kondratyev 		return (0);
699*94773907SVladimir Kondratyev 
700*94773907SVladimir Kondratyev 	case HIDIOCGRDESCSIZE:
701*94773907SVladimir Kondratyev 		*(int *)addr = sc->sc_rdesc->len;
702*94773907SVladimir Kondratyev 		return (0);
703*94773907SVladimir Kondratyev 
704*94773907SVladimir Kondratyev 	case HIDIOCGRDESC:
705*94773907SVladimir Kondratyev 		hrd = *(struct hidraw_report_descriptor **)addr;
706*94773907SVladimir Kondratyev 		error = copyin(&hrd->size, &size, sizeof(uint32_t));
707*94773907SVladimir Kondratyev 		if (error)
708*94773907SVladimir Kondratyev 			return (error);
709*94773907SVladimir Kondratyev 		/*
710*94773907SVladimir Kondratyev 		 * HID_MAX_DESCRIPTOR_SIZE-1 is a limit of report descriptor
711*94773907SVladimir Kondratyev 		 * size in current Linux implementation.
712*94773907SVladimir Kondratyev 		 */
713*94773907SVladimir Kondratyev 		if (size >= HID_MAX_DESCRIPTOR_SIZE)
714*94773907SVladimir Kondratyev 			return (EINVAL);
715*94773907SVladimir Kondratyev 		buf = HIDRAW_LOCAL_ALLOC(local_buf, size);
716*94773907SVladimir Kondratyev 		error = hid_get_rdesc(sc->sc_dev, buf, size);
717*94773907SVladimir Kondratyev 		if (error == 0) {
718*94773907SVladimir Kondratyev 			size = MIN(size, sc->sc_rdesc->len);
719*94773907SVladimir Kondratyev 			error = copyout(buf, hrd->value, size);
720*94773907SVladimir Kondratyev 		}
721*94773907SVladimir Kondratyev 		HIDRAW_LOCAL_FREE(local_buf, buf);
722*94773907SVladimir Kondratyev 		return (error);
723*94773907SVladimir Kondratyev 
724*94773907SVladimir Kondratyev 	case HIDIOCGRAWINFO:
725*94773907SVladimir Kondratyev 		hdi = (struct hidraw_devinfo *)addr;
726*94773907SVladimir Kondratyev 		hdi->bustype = sc->sc_hw->idBus;
727*94773907SVladimir Kondratyev 		hdi->vendor = sc->sc_hw->idVendor;
728*94773907SVladimir Kondratyev 		hdi->product = sc->sc_hw->idProduct;
729*94773907SVladimir Kondratyev 		return (0);
730*94773907SVladimir Kondratyev 	}
731*94773907SVladimir Kondratyev 
732*94773907SVladimir Kondratyev 	/* variable-length ioctls handling */
733*94773907SVladimir Kondratyev 	len = IOCPARM_LEN(cmd);
734*94773907SVladimir Kondratyev 	switch (IOCBASECMD(cmd)) {
735*94773907SVladimir Kondratyev 	case HIDIOCGRAWNAME(0):
736*94773907SVladimir Kondratyev 		strlcpy(addr, sc->sc_hw->name, len);
737*94773907SVladimir Kondratyev 		return (0);
738*94773907SVladimir Kondratyev 
739*94773907SVladimir Kondratyev 	case HIDIOCGRAWPHYS(0):
740*94773907SVladimir Kondratyev 		strlcpy(addr, device_get_nameunit(sc->sc_dev), len);
741*94773907SVladimir Kondratyev 		return (0);
742*94773907SVladimir Kondratyev 
743*94773907SVladimir Kondratyev 	case HIDIOCSFEATURE(0):
744*94773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FWRITE))
745*94773907SVladimir Kondratyev 			return (EPERM);
746*94773907SVladimir Kondratyev 		if (len < 2)
747*94773907SVladimir Kondratyev 			return (EINVAL);
748*94773907SVladimir Kondratyev 		id = *(uint8_t *)addr;
749*94773907SVladimir Kondratyev 		if (id == 0) {
750*94773907SVladimir Kondratyev 			addr = (uint8_t *)addr + 1;
751*94773907SVladimir Kondratyev 			len--;
752*94773907SVladimir Kondratyev 		}
753*94773907SVladimir Kondratyev 		return (hid_set_report(sc->sc_dev, addr, len,
754*94773907SVladimir Kondratyev 		    HID_FEATURE_REPORT, id));
755*94773907SVladimir Kondratyev 
756*94773907SVladimir Kondratyev 	case HIDIOCGFEATURE(0):
757*94773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FREAD))
758*94773907SVladimir Kondratyev 			return (EPERM);
759*94773907SVladimir Kondratyev 		if (len < 2)
760*94773907SVladimir Kondratyev 			return (EINVAL);
761*94773907SVladimir Kondratyev 		id = *(uint8_t *)addr;
762*94773907SVladimir Kondratyev 		if (id == 0) {
763*94773907SVladimir Kondratyev 			addr = (uint8_t *)addr + 1;
764*94773907SVladimir Kondratyev 			len--;
765*94773907SVladimir Kondratyev 		}
766*94773907SVladimir Kondratyev 		return (hid_get_report(sc->sc_dev, addr, len, NULL,
767*94773907SVladimir Kondratyev 		    HID_FEATURE_REPORT, id));
768*94773907SVladimir Kondratyev 
769*94773907SVladimir Kondratyev 	case HIDIOCGRAWUNIQ(0):
770*94773907SVladimir Kondratyev 		strlcpy(addr, sc->sc_hw->serial, len);
771*94773907SVladimir Kondratyev 		return (0);
772*94773907SVladimir Kondratyev 	}
773*94773907SVladimir Kondratyev 
774*94773907SVladimir Kondratyev 	return (EINVAL);
775*94773907SVladimir Kondratyev }
776*94773907SVladimir Kondratyev 
777*94773907SVladimir Kondratyev static int
778*94773907SVladimir Kondratyev hidraw_poll(struct cdev *dev, int events, struct thread *td)
779*94773907SVladimir Kondratyev {
780*94773907SVladimir Kondratyev 	struct hidraw_softc *sc;
781*94773907SVladimir Kondratyev 	int revents = 0;
782*94773907SVladimir Kondratyev 
783*94773907SVladimir Kondratyev 	sc = dev->si_drv1;
784*94773907SVladimir Kondratyev 	if (sc == NULL)
785*94773907SVladimir Kondratyev 		return (POLLHUP);
786*94773907SVladimir Kondratyev 
787*94773907SVladimir Kondratyev 	if (events & (POLLOUT | POLLWRNORM) && (sc->sc_fflags & FWRITE))
788*94773907SVladimir Kondratyev 		revents |= events & (POLLOUT | POLLWRNORM);
789*94773907SVladimir Kondratyev 	if (events & (POLLIN | POLLRDNORM) && (sc->sc_fflags & FREAD)) {
790*94773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
791*94773907SVladimir Kondratyev 		if (sc->sc_head != sc->sc_tail)
792*94773907SVladimir Kondratyev 			revents |= events & (POLLIN | POLLRDNORM);
793*94773907SVladimir Kondratyev 		else {
794*94773907SVladimir Kondratyev 			sc->sc_state.sel = true;
795*94773907SVladimir Kondratyev 			selrecord(td, &sc->sc_rsel);
796*94773907SVladimir Kondratyev 		}
797*94773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
798*94773907SVladimir Kondratyev 	}
799*94773907SVladimir Kondratyev 
800*94773907SVladimir Kondratyev 	return (revents);
801*94773907SVladimir Kondratyev }
802*94773907SVladimir Kondratyev 
803*94773907SVladimir Kondratyev static int
804*94773907SVladimir Kondratyev hidraw_kqfilter(struct cdev *dev, struct knote *kn)
805*94773907SVladimir Kondratyev {
806*94773907SVladimir Kondratyev 	struct hidraw_softc *sc;
807*94773907SVladimir Kondratyev 
808*94773907SVladimir Kondratyev 	sc = dev->si_drv1;
809*94773907SVladimir Kondratyev 	if (sc == NULL)
810*94773907SVladimir Kondratyev 		return (ENXIO);
811*94773907SVladimir Kondratyev 
812*94773907SVladimir Kondratyev 	switch(kn->kn_filter) {
813*94773907SVladimir Kondratyev 	case EVFILT_READ:
814*94773907SVladimir Kondratyev 		if (sc->sc_fflags & FREAD) {
815*94773907SVladimir Kondratyev 			kn->kn_fop = &hidraw_filterops_read;
816*94773907SVladimir Kondratyev 			break;
817*94773907SVladimir Kondratyev 		}
818*94773907SVladimir Kondratyev 		/* FALLTHROUGH */
819*94773907SVladimir Kondratyev 	default:
820*94773907SVladimir Kondratyev 		return(EINVAL);
821*94773907SVladimir Kondratyev 	}
822*94773907SVladimir Kondratyev 	kn->kn_hook = sc;
823*94773907SVladimir Kondratyev 
824*94773907SVladimir Kondratyev 	knlist_add(&sc->sc_rsel.si_note, kn, 0);
825*94773907SVladimir Kondratyev 	return (0);
826*94773907SVladimir Kondratyev }
827*94773907SVladimir Kondratyev 
828*94773907SVladimir Kondratyev static int
829*94773907SVladimir Kondratyev hidraw_kqread(struct knote *kn, long hint)
830*94773907SVladimir Kondratyev {
831*94773907SVladimir Kondratyev 	struct hidraw_softc *sc;
832*94773907SVladimir Kondratyev 	int ret;
833*94773907SVladimir Kondratyev 
834*94773907SVladimir Kondratyev 	sc = kn->kn_hook;
835*94773907SVladimir Kondratyev 
836*94773907SVladimir Kondratyev 	mtx_assert(&sc->sc_mtx, MA_OWNED);
837*94773907SVladimir Kondratyev 
838*94773907SVladimir Kondratyev 	if (sc->dev->si_drv1 == NULL) {
839*94773907SVladimir Kondratyev 		kn->kn_flags |= EV_EOF;
840*94773907SVladimir Kondratyev 		ret = 1;
841*94773907SVladimir Kondratyev 	} else
842*94773907SVladimir Kondratyev 		ret = (sc->sc_head != sc->sc_tail) ? 1 : 0;
843*94773907SVladimir Kondratyev 
844*94773907SVladimir Kondratyev 	return (ret);
845*94773907SVladimir Kondratyev }
846*94773907SVladimir Kondratyev 
847*94773907SVladimir Kondratyev static void
848*94773907SVladimir Kondratyev hidraw_kqdetach(struct knote *kn)
849*94773907SVladimir Kondratyev {
850*94773907SVladimir Kondratyev 	struct hidraw_softc *sc;
851*94773907SVladimir Kondratyev 
852*94773907SVladimir Kondratyev 	sc = kn->kn_hook;
853*94773907SVladimir Kondratyev 	knlist_remove(&sc->sc_rsel.si_note, kn, 0);
854*94773907SVladimir Kondratyev }
855*94773907SVladimir Kondratyev 
856*94773907SVladimir Kondratyev static void
857*94773907SVladimir Kondratyev hidraw_notify(struct hidraw_softc *sc)
858*94773907SVladimir Kondratyev {
859*94773907SVladimir Kondratyev 
860*94773907SVladimir Kondratyev 	mtx_assert(&sc->sc_mtx, MA_OWNED);
861*94773907SVladimir Kondratyev 
862*94773907SVladimir Kondratyev 	if (sc->sc_state.aslp) {
863*94773907SVladimir Kondratyev 		sc->sc_state.aslp = false;
864*94773907SVladimir Kondratyev 		DPRINTFN(5, "waking %p\n", &sc->sc_q);
865*94773907SVladimir Kondratyev 		wakeup(&sc->sc_q);
866*94773907SVladimir Kondratyev 	}
867*94773907SVladimir Kondratyev 	if (sc->sc_state.sel) {
868*94773907SVladimir Kondratyev 		sc->sc_state.sel = false;
869*94773907SVladimir Kondratyev 		selwakeuppri(&sc->sc_rsel, PZERO);
870*94773907SVladimir Kondratyev 	}
871*94773907SVladimir Kondratyev 	if (sc->sc_async != NULL) {
872*94773907SVladimir Kondratyev 		DPRINTFN(3, "sending SIGIO %p\n", sc->sc_async);
873*94773907SVladimir Kondratyev 		PROC_LOCK(sc->sc_async);
874*94773907SVladimir Kondratyev 		kern_psignal(sc->sc_async, SIGIO);
875*94773907SVladimir Kondratyev 		PROC_UNLOCK(sc->sc_async);
876*94773907SVladimir Kondratyev 	}
877*94773907SVladimir Kondratyev 	KNOTE_LOCKED(&sc->sc_rsel.si_note, 0);
878*94773907SVladimir Kondratyev }
879*94773907SVladimir Kondratyev 
880*94773907SVladimir Kondratyev static device_method_t hidraw_methods[] = {
881*94773907SVladimir Kondratyev 	/* Device interface */
882*94773907SVladimir Kondratyev 	DEVMETHOD(device_identify,	hidraw_identify),
883*94773907SVladimir Kondratyev 	DEVMETHOD(device_probe,		hidraw_probe),
884*94773907SVladimir Kondratyev 	DEVMETHOD(device_attach,	hidraw_attach),
885*94773907SVladimir Kondratyev 	DEVMETHOD(device_detach,	hidraw_detach),
886*94773907SVladimir Kondratyev 
887*94773907SVladimir Kondratyev 	DEVMETHOD_END
888*94773907SVladimir Kondratyev };
889*94773907SVladimir Kondratyev 
890*94773907SVladimir Kondratyev static driver_t hidraw_driver = {
891*94773907SVladimir Kondratyev 	"hidraw",
892*94773907SVladimir Kondratyev 	hidraw_methods,
893*94773907SVladimir Kondratyev 	sizeof(struct hidraw_softc)
894*94773907SVladimir Kondratyev };
895*94773907SVladimir Kondratyev 
896*94773907SVladimir Kondratyev static devclass_t hidraw_devclass;
897*94773907SVladimir Kondratyev 
898*94773907SVladimir Kondratyev DRIVER_MODULE(hidraw, hidbus, hidraw_driver, hidraw_devclass, NULL, 0);
899*94773907SVladimir Kondratyev MODULE_DEPEND(hidraw, hidbus, 1, 1, 1);
900*94773907SVladimir Kondratyev MODULE_DEPEND(hidraw, hid, 1, 1, 1);
901*94773907SVladimir Kondratyev MODULE_DEPEND(hidraw, usb, 1, 1, 1);
902*94773907SVladimir Kondratyev MODULE_VERSION(hidraw, 1);
903