xref: /freebsd/sys/dev/hid/hidraw.c (revision f988d7fa050e4886cdeb9483a039e75d58c31883)
194773907SVladimir Kondratyev /*-
294773907SVladimir Kondratyev  * SPDX-License-Identifier: BSD-2-Clause-NetBSD
394773907SVladimir Kondratyev  *
494773907SVladimir Kondratyev  * Copyright (c) 1998 The NetBSD Foundation, Inc.
594773907SVladimir Kondratyev  * All rights reserved.
694773907SVladimir Kondratyev  * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
794773907SVladimir Kondratyev  *
894773907SVladimir Kondratyev  * This code is derived from software contributed to The NetBSD Foundation
994773907SVladimir Kondratyev  * by Lennart Augustsson (lennart@augustsson.net) at
1094773907SVladimir Kondratyev  * Carlstedt Research & Technology.
1194773907SVladimir Kondratyev  *
1294773907SVladimir Kondratyev  * Redistribution and use in source and binary forms, with or without
1394773907SVladimir Kondratyev  * modification, are permitted provided that the following conditions
1494773907SVladimir Kondratyev  * are met:
1594773907SVladimir Kondratyev  * 1. Redistributions of source code must retain the above copyright
1694773907SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer.
1794773907SVladimir Kondratyev  * 2. Redistributions in binary form must reproduce the above copyright
1894773907SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer in the
1994773907SVladimir Kondratyev  *    documentation and/or other materials provided with the distribution.
2094773907SVladimir Kondratyev  *
2194773907SVladimir Kondratyev  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2294773907SVladimir Kondratyev  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2394773907SVladimir Kondratyev  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2494773907SVladimir Kondratyev  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2594773907SVladimir Kondratyev  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2694773907SVladimir Kondratyev  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2794773907SVladimir Kondratyev  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2894773907SVladimir Kondratyev  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2994773907SVladimir Kondratyev  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3094773907SVladimir Kondratyev  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3194773907SVladimir Kondratyev  * POSSIBILITY OF SUCH DAMAGE.
3294773907SVladimir Kondratyev  */
3394773907SVladimir Kondratyev 
3494773907SVladimir Kondratyev /*
3594773907SVladimir Kondratyev  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
3694773907SVladimir Kondratyev  */
3794773907SVladimir Kondratyev 
3894773907SVladimir Kondratyev #include <sys/cdefs.h>
3994773907SVladimir Kondratyev __FBSDID("$FreeBSD$");
4094773907SVladimir Kondratyev 
419be6b22dSVladimir Kondratyev #include "opt_hid.h"
429be6b22dSVladimir Kondratyev 
4394773907SVladimir Kondratyev #include <sys/param.h>
4494773907SVladimir Kondratyev #include <sys/bus.h>
4594773907SVladimir Kondratyev #include <sys/conf.h>
4694773907SVladimir Kondratyev #include <sys/fcntl.h>
4794773907SVladimir Kondratyev #include <sys/filio.h>
4894773907SVladimir Kondratyev #include <sys/ioccom.h>
4994773907SVladimir Kondratyev #include <sys/kernel.h>
5094773907SVladimir Kondratyev #include <sys/lock.h>
5194773907SVladimir Kondratyev #include <sys/malloc.h>
5294773907SVladimir Kondratyev #include <sys/module.h>
5394773907SVladimir Kondratyev #include <sys/mutex.h>
5494773907SVladimir Kondratyev #include <sys/poll.h>
5594773907SVladimir Kondratyev #include <sys/priv.h>
5694773907SVladimir Kondratyev #include <sys/proc.h>
5794773907SVladimir Kondratyev #include <sys/selinfo.h>
5894773907SVladimir Kondratyev #include <sys/sysctl.h>
5994773907SVladimir Kondratyev #include <sys/systm.h>
6094773907SVladimir Kondratyev #include <sys/tty.h>
6194773907SVladimir Kondratyev #include <sys/uio.h>
6294773907SVladimir Kondratyev 
6394773907SVladimir Kondratyev #define HID_DEBUG_VAR	hidraw_debug
6494773907SVladimir Kondratyev #include <dev/hid/hid.h>
6594773907SVladimir Kondratyev #include <dev/hid/hidbus.h>
6694773907SVladimir Kondratyev #include <dev/hid/hidraw.h>
6794773907SVladimir Kondratyev 
6894773907SVladimir Kondratyev #ifdef HID_DEBUG
6994773907SVladimir Kondratyev static int hidraw_debug = 0;
7094773907SVladimir Kondratyev static SYSCTL_NODE(_hw_hid, OID_AUTO, hidraw, CTLFLAG_RW, 0,
7194773907SVladimir Kondratyev     "HID raw interface");
7294773907SVladimir Kondratyev SYSCTL_INT(_hw_hid_hidraw, OID_AUTO, debug, CTLFLAG_RWTUN,
7394773907SVladimir Kondratyev     &hidraw_debug, 0, "Debug level");
7494773907SVladimir Kondratyev #endif
7594773907SVladimir Kondratyev 
7694773907SVladimir Kondratyev #define	HIDRAW_INDEX		0xFF	/* Arbitrary high value */
7794773907SVladimir Kondratyev 
7894773907SVladimir Kondratyev #define	HIDRAW_LOCAL_BUFSIZE	64	/* Size of on-stack buffer. */
7994773907SVladimir Kondratyev #define	HIDRAW_LOCAL_ALLOC(local_buf, size)		\
8094773907SVladimir Kondratyev 	(sizeof(local_buf) > (size) ? (local_buf) :	\
8194773907SVladimir Kondratyev 	    malloc((size), M_DEVBUF, M_ZERO | M_WAITOK))
8294773907SVladimir Kondratyev #define	HIDRAW_LOCAL_FREE(local_buf, buf)		\
8394773907SVladimir Kondratyev 	if ((local_buf) != (buf)) {			\
8494773907SVladimir Kondratyev 		free((buf), M_DEVBUF);			\
8594773907SVladimir Kondratyev 	}
8694773907SVladimir Kondratyev 
8794773907SVladimir Kondratyev struct hidraw_softc {
8894773907SVladimir Kondratyev 	device_t sc_dev;		/* base device */
8994773907SVladimir Kondratyev 
9094773907SVladimir Kondratyev 	struct mtx sc_mtx;		/* hidbus private mutex */
9194773907SVladimir Kondratyev 
9294773907SVladimir Kondratyev 	struct hid_rdesc_info *sc_rdesc;
9394773907SVladimir Kondratyev 	const struct hid_device_info *sc_hw;
9494773907SVladimir Kondratyev 
9594773907SVladimir Kondratyev 	uint8_t *sc_q;
9694773907SVladimir Kondratyev 	hid_size_t *sc_qlen;
9794773907SVladimir Kondratyev 	int sc_head;
9894773907SVladimir Kondratyev 	int sc_tail;
9994773907SVladimir Kondratyev 	int sc_sleepcnt;
10094773907SVladimir Kondratyev 
10194773907SVladimir Kondratyev 	struct selinfo sc_rsel;
10294773907SVladimir Kondratyev 	struct proc *sc_async;	/* process that wants SIGIO */
10394773907SVladimir Kondratyev 	struct {			/* driver state */
10494773907SVladimir Kondratyev 		bool	open:1;		/* device is open */
10594773907SVladimir Kondratyev 		bool	aslp:1;		/* waiting for device data in read() */
10694773907SVladimir Kondratyev 		bool	sel:1;		/* waiting for device data in poll() */
10794773907SVladimir Kondratyev 		bool	quiet:1;	/* Ignore input data */
10894773907SVladimir Kondratyev 		bool	immed:1;	/* return read data immediately */
10994773907SVladimir Kondratyev 		bool	uhid:1;		/* driver switched in to uhid mode */
11094773907SVladimir Kondratyev 		bool	lock:1;		/* input queue sleepable lock */
11194773907SVladimir Kondratyev 		bool	flush:1;	/* do not wait for data in read() */
11294773907SVladimir Kondratyev 	} sc_state;
11394773907SVladimir Kondratyev 	int sc_fflags;			/* access mode for open lifetime */
11494773907SVladimir Kondratyev 
11594773907SVladimir Kondratyev 	struct cdev *dev;
11694773907SVladimir Kondratyev };
11794773907SVladimir Kondratyev 
11894773907SVladimir Kondratyev static d_open_t		hidraw_open;
11994773907SVladimir Kondratyev static d_read_t		hidraw_read;
12094773907SVladimir Kondratyev static d_write_t	hidraw_write;
12194773907SVladimir Kondratyev static d_ioctl_t	hidraw_ioctl;
12294773907SVladimir Kondratyev static d_poll_t		hidraw_poll;
12394773907SVladimir Kondratyev static d_kqfilter_t	hidraw_kqfilter;
12494773907SVladimir Kondratyev 
12594773907SVladimir Kondratyev static d_priv_dtor_t	hidraw_dtor;
12694773907SVladimir Kondratyev 
12794773907SVladimir Kondratyev static struct cdevsw hidraw_cdevsw = {
12894773907SVladimir Kondratyev 	.d_version =	D_VERSION,
12994773907SVladimir Kondratyev 	.d_open =	hidraw_open,
13094773907SVladimir Kondratyev 	.d_read =	hidraw_read,
13194773907SVladimir Kondratyev 	.d_write =	hidraw_write,
13294773907SVladimir Kondratyev 	.d_ioctl =	hidraw_ioctl,
13394773907SVladimir Kondratyev 	.d_poll =	hidraw_poll,
13494773907SVladimir Kondratyev 	.d_kqfilter =	hidraw_kqfilter,
13594773907SVladimir Kondratyev 	.d_name =	"hidraw",
13694773907SVladimir Kondratyev };
13794773907SVladimir Kondratyev 
13894773907SVladimir Kondratyev static hid_intr_t	hidraw_intr;
13994773907SVladimir Kondratyev 
14094773907SVladimir Kondratyev static device_identify_t hidraw_identify;
14194773907SVladimir Kondratyev static device_probe_t	hidraw_probe;
14294773907SVladimir Kondratyev static device_attach_t	hidraw_attach;
14394773907SVladimir Kondratyev static device_detach_t	hidraw_detach;
14494773907SVladimir Kondratyev 
14594773907SVladimir Kondratyev static int		hidraw_kqread(struct knote *, long);
14694773907SVladimir Kondratyev static void		hidraw_kqdetach(struct knote *);
14794773907SVladimir Kondratyev static void		hidraw_notify(struct hidraw_softc *);
14894773907SVladimir Kondratyev 
14994773907SVladimir Kondratyev static struct filterops hidraw_filterops_read = {
15094773907SVladimir Kondratyev 	.f_isfd =	1,
15194773907SVladimir Kondratyev 	.f_detach =	hidraw_kqdetach,
15294773907SVladimir Kondratyev 	.f_event =	hidraw_kqread,
15394773907SVladimir Kondratyev };
15494773907SVladimir Kondratyev 
15594773907SVladimir Kondratyev static void
15694773907SVladimir Kondratyev hidraw_identify(driver_t *driver, device_t parent)
15794773907SVladimir Kondratyev {
15894773907SVladimir Kondratyev 	device_t child;
15994773907SVladimir Kondratyev 
16094773907SVladimir Kondratyev 	if (device_find_child(parent, "hidraw", -1) == NULL) {
16194773907SVladimir Kondratyev 		child = BUS_ADD_CHILD(parent, 0, "hidraw",
16294773907SVladimir Kondratyev 		    device_get_unit(parent));
16394773907SVladimir Kondratyev 		if (child != NULL)
16494773907SVladimir Kondratyev 			hidbus_set_index(child, HIDRAW_INDEX);
16594773907SVladimir Kondratyev 	}
16694773907SVladimir Kondratyev }
16794773907SVladimir Kondratyev 
16894773907SVladimir Kondratyev static int
16994773907SVladimir Kondratyev hidraw_probe(device_t self)
17094773907SVladimir Kondratyev {
17194773907SVladimir Kondratyev 
17294773907SVladimir Kondratyev 	if (hidbus_get_index(self) != HIDRAW_INDEX)
17394773907SVladimir Kondratyev 		return (ENXIO);
17494773907SVladimir Kondratyev 
17594773907SVladimir Kondratyev 	hidbus_set_desc(self, "Raw HID Device");
17694773907SVladimir Kondratyev 
17794773907SVladimir Kondratyev 	return (BUS_PROBE_GENERIC);
17894773907SVladimir Kondratyev }
17994773907SVladimir Kondratyev 
18094773907SVladimir Kondratyev static int
18194773907SVladimir Kondratyev hidraw_attach(device_t self)
18294773907SVladimir Kondratyev {
18394773907SVladimir Kondratyev 	struct hidraw_softc *sc = device_get_softc(self);
18494773907SVladimir Kondratyev 	struct make_dev_args mda;
18594773907SVladimir Kondratyev 	int error;
18694773907SVladimir Kondratyev 
18794773907SVladimir Kondratyev 	sc->sc_dev = self;
18894773907SVladimir Kondratyev 	sc->sc_rdesc = hidbus_get_rdesc_info(self);
18994773907SVladimir Kondratyev 	sc->sc_hw = hid_get_device_info(self);
19094773907SVladimir Kondratyev 
19194773907SVladimir Kondratyev 	/* Hidraw mode does not require report descriptor to work */
19294773907SVladimir Kondratyev 	if (sc->sc_rdesc->data == NULL || sc->sc_rdesc->len == 0)
19394773907SVladimir Kondratyev 		device_printf(self, "no report descriptor\n");
19494773907SVladimir Kondratyev 
19594773907SVladimir Kondratyev 	mtx_init(&sc->sc_mtx, "hidraw lock", NULL, MTX_DEF);
19694773907SVladimir Kondratyev 	knlist_init_mtx(&sc->sc_rsel.si_note, &sc->sc_mtx);
19794773907SVladimir Kondratyev 
19894773907SVladimir Kondratyev 	make_dev_args_init(&mda);
19994773907SVladimir Kondratyev 	mda.mda_flags = MAKEDEV_WAITOK;
20094773907SVladimir Kondratyev 	mda.mda_devsw = &hidraw_cdevsw;
20194773907SVladimir Kondratyev 	mda.mda_uid = UID_ROOT;
20294773907SVladimir Kondratyev 	mda.mda_gid = GID_OPERATOR;
20394773907SVladimir Kondratyev 	mda.mda_mode = 0600;
20494773907SVladimir Kondratyev 	mda.mda_si_drv1 = sc;
20594773907SVladimir Kondratyev 
20694773907SVladimir Kondratyev 	error = make_dev_s(&mda, &sc->dev, "hidraw%d", device_get_unit(self));
20794773907SVladimir Kondratyev 	if (error) {
20894773907SVladimir Kondratyev 		device_printf(self, "Can not create character device\n");
20994773907SVladimir Kondratyev 		hidraw_detach(self);
21094773907SVladimir Kondratyev 		return (error);
21194773907SVladimir Kondratyev 	}
2129be6b22dSVladimir Kondratyev #ifdef HIDRAW_MAKE_UHID_ALIAS
2139be6b22dSVladimir Kondratyev 	(void)make_dev_alias(sc->dev, "uhid%d", device_get_unit(self));
2149be6b22dSVladimir Kondratyev #endif
21594773907SVladimir Kondratyev 
21694773907SVladimir Kondratyev 	hidbus_set_lock(self, &sc->sc_mtx);
21794773907SVladimir Kondratyev 	hidbus_set_intr(self, hidraw_intr, sc);
21894773907SVladimir Kondratyev 
21994773907SVladimir Kondratyev 	return (0);
22094773907SVladimir Kondratyev }
22194773907SVladimir Kondratyev 
22294773907SVladimir Kondratyev static int
22394773907SVladimir Kondratyev hidraw_detach(device_t self)
22494773907SVladimir Kondratyev {
22594773907SVladimir Kondratyev 	struct hidraw_softc *sc = device_get_softc(self);
22694773907SVladimir Kondratyev 
22794773907SVladimir Kondratyev 	DPRINTF("sc=%p\n", sc);
22894773907SVladimir Kondratyev 
22994773907SVladimir Kondratyev 	if (sc->dev != NULL) {
23094773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
23194773907SVladimir Kondratyev 		sc->dev->si_drv1 = NULL;
23294773907SVladimir Kondratyev 		/* Wake everyone */
23394773907SVladimir Kondratyev 		hidraw_notify(sc);
23494773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
23594773907SVladimir Kondratyev 		destroy_dev(sc->dev);
23694773907SVladimir Kondratyev 	}
23794773907SVladimir Kondratyev 
23894773907SVladimir Kondratyev 	knlist_clear(&sc->sc_rsel.si_note, 0);
23994773907SVladimir Kondratyev 	knlist_destroy(&sc->sc_rsel.si_note);
24094773907SVladimir Kondratyev 	seldrain(&sc->sc_rsel);
24194773907SVladimir Kondratyev 	mtx_destroy(&sc->sc_mtx);
24294773907SVladimir Kondratyev 
24394773907SVladimir Kondratyev 	return (0);
24494773907SVladimir Kondratyev }
24594773907SVladimir Kondratyev 
24694773907SVladimir Kondratyev void
24794773907SVladimir Kondratyev hidraw_intr(void *context, void *buf, hid_size_t len)
24894773907SVladimir Kondratyev {
24994773907SVladimir Kondratyev 	struct hidraw_softc *sc = context;
25094773907SVladimir Kondratyev 	int next;
25194773907SVladimir Kondratyev 
25294773907SVladimir Kondratyev 	DPRINTFN(5, "len=%d\n", len);
25394773907SVladimir Kondratyev 	DPRINTFN(5, "data = %*D\n", len, buf, " ");
25494773907SVladimir Kondratyev 
25594773907SVladimir Kondratyev 	next = (sc->sc_tail + 1) % HIDRAW_BUFFER_SIZE;
25694773907SVladimir Kondratyev 	if (sc->sc_state.quiet || next == sc->sc_head)
25794773907SVladimir Kondratyev 		return;
25894773907SVladimir Kondratyev 
25994773907SVladimir Kondratyev 	bcopy(buf, sc->sc_q + sc->sc_tail * sc->sc_rdesc->rdsize, len);
26094773907SVladimir Kondratyev 
26194773907SVladimir Kondratyev 	/* Make sure we don't process old data */
26294773907SVladimir Kondratyev 	if (len < sc->sc_rdesc->rdsize)
26394773907SVladimir Kondratyev 		bzero(sc->sc_q + sc->sc_tail * sc->sc_rdesc->rdsize + len,
26494773907SVladimir Kondratyev 		    sc->sc_rdesc->isize - len);
26594773907SVladimir Kondratyev 
26694773907SVladimir Kondratyev 	sc->sc_qlen[sc->sc_tail] = len;
26794773907SVladimir Kondratyev 	sc->sc_tail = next;
26894773907SVladimir Kondratyev 
26994773907SVladimir Kondratyev 	hidraw_notify(sc);
27094773907SVladimir Kondratyev }
27194773907SVladimir Kondratyev 
27294773907SVladimir Kondratyev static inline int
27394773907SVladimir Kondratyev hidraw_lock_queue(struct hidraw_softc *sc, bool flush)
27494773907SVladimir Kondratyev {
27594773907SVladimir Kondratyev 	int error = 0;
27694773907SVladimir Kondratyev 
27794773907SVladimir Kondratyev 	mtx_assert(&sc->sc_mtx, MA_OWNED);
27894773907SVladimir Kondratyev 
27994773907SVladimir Kondratyev 	if (flush)
28094773907SVladimir Kondratyev 		sc->sc_state.flush = true;
28194773907SVladimir Kondratyev 	++sc->sc_sleepcnt;
28294773907SVladimir Kondratyev 	while (sc->sc_state.lock && error == 0) {
28394773907SVladimir Kondratyev 		/* Flush is requested. Wakeup all readers and forbid sleeps */
28494773907SVladimir Kondratyev 		if (flush && sc->sc_state.aslp) {
28594773907SVladimir Kondratyev 			sc->sc_state.aslp = false;
28694773907SVladimir Kondratyev 			DPRINTFN(5, "waking %p\n", &sc->sc_q);
28794773907SVladimir Kondratyev 			wakeup(&sc->sc_q);
28894773907SVladimir Kondratyev 		}
28994773907SVladimir Kondratyev 		error = mtx_sleep(&sc->sc_sleepcnt, &sc->sc_mtx,
29094773907SVladimir Kondratyev 		    PZERO | PCATCH, "hidrawio", 0);
29194773907SVladimir Kondratyev 	}
29294773907SVladimir Kondratyev 	--sc->sc_sleepcnt;
29394773907SVladimir Kondratyev 	if (flush)
29494773907SVladimir Kondratyev 		sc->sc_state.flush = false;
29594773907SVladimir Kondratyev 	if (error == 0)
29694773907SVladimir Kondratyev 		sc->sc_state.lock = true;
29794773907SVladimir Kondratyev 
29894773907SVladimir Kondratyev 	return (error);
29994773907SVladimir Kondratyev }
30094773907SVladimir Kondratyev 
30194773907SVladimir Kondratyev static inline void
30294773907SVladimir Kondratyev hidraw_unlock_queue(struct hidraw_softc *sc)
30394773907SVladimir Kondratyev {
30494773907SVladimir Kondratyev 
30594773907SVladimir Kondratyev 	mtx_assert(&sc->sc_mtx, MA_OWNED);
30694773907SVladimir Kondratyev 	KASSERT(sc->sc_state.lock, ("input buffer is not locked"));
30794773907SVladimir Kondratyev 
30894773907SVladimir Kondratyev 	if (sc->sc_sleepcnt != 0)
30994773907SVladimir Kondratyev 		wakeup_one(&sc->sc_sleepcnt);
31094773907SVladimir Kondratyev 	sc->sc_state.lock = false;
31194773907SVladimir Kondratyev }
31294773907SVladimir Kondratyev 
31394773907SVladimir Kondratyev static int
31494773907SVladimir Kondratyev hidraw_open(struct cdev *dev, int flag, int mode, struct thread *td)
31594773907SVladimir Kondratyev {
31694773907SVladimir Kondratyev 	struct hidraw_softc *sc;
31794773907SVladimir Kondratyev 	int error;
31894773907SVladimir Kondratyev 
31994773907SVladimir Kondratyev 	sc = dev->si_drv1;
32094773907SVladimir Kondratyev 	if (sc == NULL)
32194773907SVladimir Kondratyev 		return (ENXIO);
32294773907SVladimir Kondratyev 
32394773907SVladimir Kondratyev 	DPRINTF("sc=%p\n", sc);
32494773907SVladimir Kondratyev 
32594773907SVladimir Kondratyev 	mtx_lock(&sc->sc_mtx);
32694773907SVladimir Kondratyev 	if (sc->sc_state.open) {
32794773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
32894773907SVladimir Kondratyev 		return (EBUSY);
32994773907SVladimir Kondratyev 	}
33094773907SVladimir Kondratyev 	sc->sc_state.open = true;
33194773907SVladimir Kondratyev 	mtx_unlock(&sc->sc_mtx);
33294773907SVladimir Kondratyev 
33394773907SVladimir Kondratyev 	error = devfs_set_cdevpriv(sc, hidraw_dtor);
33494773907SVladimir Kondratyev 	if (error != 0) {
33594773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
33694773907SVladimir Kondratyev 		sc->sc_state.open = false;
33794773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
33894773907SVladimir Kondratyev 		return (error);
33994773907SVladimir Kondratyev 	}
34094773907SVladimir Kondratyev 
34194773907SVladimir Kondratyev 	sc->sc_q = malloc(sc->sc_rdesc->rdsize * HIDRAW_BUFFER_SIZE, M_DEVBUF,
34294773907SVladimir Kondratyev 	    M_ZERO | M_WAITOK);
34394773907SVladimir Kondratyev 	sc->sc_qlen = malloc(sizeof(hid_size_t) * HIDRAW_BUFFER_SIZE, M_DEVBUF,
34494773907SVladimir Kondratyev 	    M_ZERO | M_WAITOK);
34594773907SVladimir Kondratyev 
34694773907SVladimir Kondratyev 	/* Set up interrupt pipe. */
34794773907SVladimir Kondratyev 	sc->sc_state.immed = false;
34894773907SVladimir Kondratyev 	sc->sc_async = 0;
34994773907SVladimir Kondratyev 	sc->sc_state.uhid = false;	/* hidraw mode is default */
35094773907SVladimir Kondratyev 	sc->sc_state.quiet = false;
35194773907SVladimir Kondratyev 	sc->sc_head = sc->sc_tail = 0;
35294773907SVladimir Kondratyev 	sc->sc_fflags = flag;
35394773907SVladimir Kondratyev 
35494773907SVladimir Kondratyev 	hidbus_intr_start(sc->sc_dev);
35594773907SVladimir Kondratyev 
35694773907SVladimir Kondratyev 	return (0);
35794773907SVladimir Kondratyev }
35894773907SVladimir Kondratyev 
35994773907SVladimir Kondratyev static void
36094773907SVladimir Kondratyev hidraw_dtor(void *data)
36194773907SVladimir Kondratyev {
36294773907SVladimir Kondratyev 	struct hidraw_softc *sc = data;
36394773907SVladimir Kondratyev 
36494773907SVladimir Kondratyev 	DPRINTF("sc=%p\n", sc);
36594773907SVladimir Kondratyev 
36694773907SVladimir Kondratyev 	/* Disable interrupts. */
36794773907SVladimir Kondratyev 	hidbus_intr_stop(sc->sc_dev);
36894773907SVladimir Kondratyev 
36994773907SVladimir Kondratyev 	sc->sc_tail = sc->sc_head = 0;
37094773907SVladimir Kondratyev 	sc->sc_async = 0;
37194773907SVladimir Kondratyev 	free(sc->sc_q, M_DEVBUF);
37294773907SVladimir Kondratyev 	free(sc->sc_qlen, M_DEVBUF);
37394773907SVladimir Kondratyev 	sc->sc_q = NULL;
37494773907SVladimir Kondratyev 
37594773907SVladimir Kondratyev 	mtx_lock(&sc->sc_mtx);
37694773907SVladimir Kondratyev 	sc->sc_state.open = false;
37794773907SVladimir Kondratyev 	mtx_unlock(&sc->sc_mtx);
37894773907SVladimir Kondratyev }
37994773907SVladimir Kondratyev 
38094773907SVladimir Kondratyev static int
38194773907SVladimir Kondratyev hidraw_read(struct cdev *dev, struct uio *uio, int flag)
38294773907SVladimir Kondratyev {
38394773907SVladimir Kondratyev 	struct hidraw_softc *sc;
38494773907SVladimir Kondratyev 	size_t length;
38594773907SVladimir Kondratyev 	int error;
38694773907SVladimir Kondratyev 
38794773907SVladimir Kondratyev 	DPRINTFN(1, "\n");
38894773907SVladimir Kondratyev 
38994773907SVladimir Kondratyev 	sc = dev->si_drv1;
39094773907SVladimir Kondratyev 	if (sc == NULL)
39194773907SVladimir Kondratyev 		return (EIO);
39294773907SVladimir Kondratyev 
39394773907SVladimir Kondratyev 	mtx_lock(&sc->sc_mtx);
39494773907SVladimir Kondratyev 	error = dev->si_drv1 == NULL ? EIO : hidraw_lock_queue(sc, false);
39594773907SVladimir Kondratyev 	if (error != 0) {
39694773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
39794773907SVladimir Kondratyev 		return (error);
39894773907SVladimir Kondratyev 	}
39994773907SVladimir Kondratyev 
40094773907SVladimir Kondratyev 	if (sc->sc_state.immed) {
40194773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
40294773907SVladimir Kondratyev 		DPRINTFN(1, "immed\n");
40394773907SVladimir Kondratyev 
40494773907SVladimir Kondratyev 		error = hid_get_report(sc->sc_dev, sc->sc_q,
40594773907SVladimir Kondratyev 		    sc->sc_rdesc->isize, NULL, HID_INPUT_REPORT,
40694773907SVladimir Kondratyev 		    sc->sc_rdesc->iid);
40794773907SVladimir Kondratyev 		if (error == 0)
40894773907SVladimir Kondratyev 			error = uiomove(sc->sc_q, sc->sc_rdesc->isize, uio);
40994773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
41094773907SVladimir Kondratyev 		goto exit;
41194773907SVladimir Kondratyev 	}
41294773907SVladimir Kondratyev 
41394773907SVladimir Kondratyev 	while (sc->sc_tail == sc->sc_head && !sc->sc_state.flush) {
41494773907SVladimir Kondratyev 		if (flag & O_NONBLOCK) {
41594773907SVladimir Kondratyev 			error = EWOULDBLOCK;
41694773907SVladimir Kondratyev 			goto exit;
41794773907SVladimir Kondratyev 		}
41894773907SVladimir Kondratyev 		sc->sc_state.aslp = true;
41994773907SVladimir Kondratyev 		DPRINTFN(5, "sleep on %p\n", &sc->sc_q);
42094773907SVladimir Kondratyev 		error = mtx_sleep(&sc->sc_q, &sc->sc_mtx, PZERO | PCATCH,
42194773907SVladimir Kondratyev 		    "hidrawrd", 0);
42294773907SVladimir Kondratyev 		DPRINTFN(5, "woke, error=%d\n", error);
42394773907SVladimir Kondratyev 		if (dev->si_drv1 == NULL)
42494773907SVladimir Kondratyev 			error = EIO;
42594773907SVladimir Kondratyev 		if (error) {
42694773907SVladimir Kondratyev 			sc->sc_state.aslp = false;
42794773907SVladimir Kondratyev 			goto exit;
42894773907SVladimir Kondratyev 		}
42994773907SVladimir Kondratyev 	}
43094773907SVladimir Kondratyev 
43194773907SVladimir Kondratyev 	while (sc->sc_tail != sc->sc_head && uio->uio_resid > 0) {
43294773907SVladimir Kondratyev 		length = min(uio->uio_resid, sc->sc_state.uhid ?
43394773907SVladimir Kondratyev 		    sc->sc_rdesc->isize : sc->sc_qlen[sc->sc_head]);
43494773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
43594773907SVladimir Kondratyev 
43694773907SVladimir Kondratyev 		/* Copy the data to the user process. */
43794773907SVladimir Kondratyev 		DPRINTFN(5, "got %lu chars\n", (u_long)length);
43894773907SVladimir Kondratyev 		error = uiomove(sc->sc_q + sc->sc_head * sc->sc_rdesc->rdsize,
43994773907SVladimir Kondratyev 		    length, uio);
44094773907SVladimir Kondratyev 
44194773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
44294773907SVladimir Kondratyev 		if (error != 0)
44394773907SVladimir Kondratyev 			goto exit;
44494773907SVladimir Kondratyev 		/* Remove a small chunk from the input queue. */
44594773907SVladimir Kondratyev 		sc->sc_head = (sc->sc_head + 1) % HIDRAW_BUFFER_SIZE;
44694773907SVladimir Kondratyev 		/*
44794773907SVladimir Kondratyev 		 * In uhid mode transfer as many chunks as possible. Hidraw
44894773907SVladimir Kondratyev 		 * packets are transferred one by one due to different length.
44994773907SVladimir Kondratyev 		 */
45094773907SVladimir Kondratyev 		if (!sc->sc_state.uhid)
45194773907SVladimir Kondratyev 			goto exit;
45294773907SVladimir Kondratyev 	}
45394773907SVladimir Kondratyev exit:
45494773907SVladimir Kondratyev 	hidraw_unlock_queue(sc);
45594773907SVladimir Kondratyev 	mtx_unlock(&sc->sc_mtx);
45694773907SVladimir Kondratyev 
45794773907SVladimir Kondratyev 	return (error);
45894773907SVladimir Kondratyev }
45994773907SVladimir Kondratyev 
46094773907SVladimir Kondratyev static int
46194773907SVladimir Kondratyev hidraw_write(struct cdev *dev, struct uio *uio, int flag)
46294773907SVladimir Kondratyev {
46394773907SVladimir Kondratyev 	uint8_t local_buf[HIDRAW_LOCAL_BUFSIZE], *buf;
46494773907SVladimir Kondratyev 	struct hidraw_softc *sc;
46594773907SVladimir Kondratyev 	int error;
46694773907SVladimir Kondratyev 	int size;
46794773907SVladimir Kondratyev 	size_t buf_offset;
46894773907SVladimir Kondratyev 	uint8_t id = 0;
46994773907SVladimir Kondratyev 
47094773907SVladimir Kondratyev 	DPRINTFN(1, "\n");
47194773907SVladimir Kondratyev 
47294773907SVladimir Kondratyev 	sc = dev->si_drv1;
47394773907SVladimir Kondratyev 	if (sc == NULL)
47494773907SVladimir Kondratyev 		return (EIO);
47594773907SVladimir Kondratyev 
47694773907SVladimir Kondratyev 	if (sc->sc_rdesc->osize == 0)
47794773907SVladimir Kondratyev 		return (EOPNOTSUPP);
47894773907SVladimir Kondratyev 
47994773907SVladimir Kondratyev 	buf_offset = 0;
48094773907SVladimir Kondratyev 	if (sc->sc_state.uhid) {
48194773907SVladimir Kondratyev 		size = sc->sc_rdesc->osize;
48294773907SVladimir Kondratyev 		if (uio->uio_resid != size)
48394773907SVladimir Kondratyev 			return (EINVAL);
48494773907SVladimir Kondratyev 	} else {
48594773907SVladimir Kondratyev 		size = uio->uio_resid;
48694773907SVladimir Kondratyev 		if (size < 2)
48794773907SVladimir Kondratyev 			return (EINVAL);
48894773907SVladimir Kondratyev 		/* Strip leading 0 if the device doesnt use numbered reports */
48994773907SVladimir Kondratyev 		error = uiomove(&id, 1, uio);
49094773907SVladimir Kondratyev 		if (error)
49194773907SVladimir Kondratyev 			return (error);
49294773907SVladimir Kondratyev 		if (id != 0)
49394773907SVladimir Kondratyev 			buf_offset++;
49494773907SVladimir Kondratyev 		else
49594773907SVladimir Kondratyev 			size--;
49694773907SVladimir Kondratyev 		/* Check if underlying driver could process this request */
49794773907SVladimir Kondratyev 		if (size > sc->sc_rdesc->wrsize)
49894773907SVladimir Kondratyev 			return (ENOBUFS);
49994773907SVladimir Kondratyev 	}
50094773907SVladimir Kondratyev 	buf = HIDRAW_LOCAL_ALLOC(local_buf, size);
50194773907SVladimir Kondratyev 	buf[0] = id;
50294773907SVladimir Kondratyev 	error = uiomove(buf + buf_offset, uio->uio_resid, uio);
50394773907SVladimir Kondratyev 	if (error == 0)
50494773907SVladimir Kondratyev 		error = hid_write(sc->sc_dev, buf, size);
50594773907SVladimir Kondratyev 	HIDRAW_LOCAL_FREE(local_buf, buf);
50694773907SVladimir Kondratyev 
50794773907SVladimir Kondratyev 	return (error);
50894773907SVladimir Kondratyev }
50994773907SVladimir Kondratyev 
51094773907SVladimir Kondratyev static int
51194773907SVladimir Kondratyev hidraw_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
51294773907SVladimir Kondratyev     struct thread *td)
51394773907SVladimir Kondratyev {
51494773907SVladimir Kondratyev 	uint8_t local_buf[HIDRAW_LOCAL_BUFSIZE];
51594773907SVladimir Kondratyev 	void *buf;
51694773907SVladimir Kondratyev 	struct hidraw_softc *sc;
51794773907SVladimir Kondratyev 	struct hidraw_gen_descriptor *hgd;
51894773907SVladimir Kondratyev 	struct hidraw_report_descriptor *hrd;
51994773907SVladimir Kondratyev 	struct hidraw_devinfo *hdi;
52094773907SVladimir Kondratyev 	uint32_t size;
52194773907SVladimir Kondratyev 	int id, len;
52294773907SVladimir Kondratyev 	int error = 0;
52394773907SVladimir Kondratyev 
52494773907SVladimir Kondratyev 	DPRINTFN(2, "cmd=%lx\n", cmd);
52594773907SVladimir Kondratyev 
52694773907SVladimir Kondratyev 	sc = dev->si_drv1;
52794773907SVladimir Kondratyev 	if (sc == NULL)
52894773907SVladimir Kondratyev 		return (EIO);
52994773907SVladimir Kondratyev 
53094773907SVladimir Kondratyev 	/* fixed-length ioctls handling */
53194773907SVladimir Kondratyev 	switch (cmd) {
53294773907SVladimir Kondratyev 	case FIONBIO:
53394773907SVladimir Kondratyev 		/* All handled in the upper FS layer. */
53494773907SVladimir Kondratyev 		return (0);
53594773907SVladimir Kondratyev 
53694773907SVladimir Kondratyev 	case FIOASYNC:
53794773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
53894773907SVladimir Kondratyev 		if (*(int *)addr) {
53994773907SVladimir Kondratyev 			if (sc->sc_async == NULL) {
54094773907SVladimir Kondratyev 				sc->sc_async = td->td_proc;
54194773907SVladimir Kondratyev 				DPRINTF("FIOASYNC %p\n", sc->sc_async);
54294773907SVladimir Kondratyev 			} else
54394773907SVladimir Kondratyev 				error = EBUSY;
54494773907SVladimir Kondratyev 		} else
54594773907SVladimir Kondratyev 			sc->sc_async = NULL;
54694773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
54794773907SVladimir Kondratyev 		return (error);
54894773907SVladimir Kondratyev 
54994773907SVladimir Kondratyev 	/* XXX this is not the most general solution. */
55094773907SVladimir Kondratyev 	case TIOCSPGRP:
55194773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
55294773907SVladimir Kondratyev 		if (sc->sc_async == NULL)
55394773907SVladimir Kondratyev 			error = EINVAL;
55494773907SVladimir Kondratyev 		else if (*(int *)addr != sc->sc_async->p_pgid)
55594773907SVladimir Kondratyev 			error = EPERM;
55694773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
55794773907SVladimir Kondratyev 		return (error);
55894773907SVladimir Kondratyev 
55994773907SVladimir Kondratyev 	case HIDRAW_GET_REPORT_DESC:
56094773907SVladimir Kondratyev 		if (sc->sc_rdesc->data == NULL || sc->sc_rdesc->len == 0)
56194773907SVladimir Kondratyev 			return (EOPNOTSUPP);
56294773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
56394773907SVladimir Kondratyev 		sc->sc_state.uhid = true;
56494773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
56594773907SVladimir Kondratyev 		hgd = (struct hidraw_gen_descriptor *)addr;
56694773907SVladimir Kondratyev 		if (sc->sc_rdesc->len > hgd->hgd_maxlen) {
56794773907SVladimir Kondratyev 			size = hgd->hgd_maxlen;
56894773907SVladimir Kondratyev 		} else {
56994773907SVladimir Kondratyev 			size = sc->sc_rdesc->len;
57094773907SVladimir Kondratyev 		}
57194773907SVladimir Kondratyev 		hgd->hgd_actlen = size;
57294773907SVladimir Kondratyev 		if (hgd->hgd_data == NULL)
57394773907SVladimir Kondratyev 			return (0);		/* descriptor length only */
57494773907SVladimir Kondratyev 		return (copyout(sc->sc_rdesc->data, hgd->hgd_data, size));
57594773907SVladimir Kondratyev 
57694773907SVladimir Kondratyev 
57794773907SVladimir Kondratyev 	case HIDRAW_SET_REPORT_DESC:
57894773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FWRITE))
57994773907SVladimir Kondratyev 			return (EPERM);
58094773907SVladimir Kondratyev 
58194773907SVladimir Kondratyev 		/* check privileges */
58294773907SVladimir Kondratyev 		error = priv_check(curthread, PRIV_DRIVER);
58394773907SVladimir Kondratyev 		if (error)
58494773907SVladimir Kondratyev 			return (error);
58594773907SVladimir Kondratyev 
58694773907SVladimir Kondratyev 		/* Stop interrupts and clear input report buffer */
58794773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
58894773907SVladimir Kondratyev 		sc->sc_tail = sc->sc_head = 0;
58994773907SVladimir Kondratyev 		error = hidraw_lock_queue(sc, true);
59094773907SVladimir Kondratyev 		if (error == 0)
59194773907SVladimir Kondratyev 			sc->sc_state.quiet = true;
59294773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
59394773907SVladimir Kondratyev 		if (error != 0)
59494773907SVladimir Kondratyev 			return(error);
59594773907SVladimir Kondratyev 
59694773907SVladimir Kondratyev 		hgd = (struct hidraw_gen_descriptor *)addr;
59794773907SVladimir Kondratyev 		buf = HIDRAW_LOCAL_ALLOC(local_buf, hgd->hgd_maxlen);
59894773907SVladimir Kondratyev 		copyin(hgd->hgd_data, buf, hgd->hgd_maxlen);
59994773907SVladimir Kondratyev 		/* Lock newbus around set_report_descr call */
60094773907SVladimir Kondratyev 		mtx_lock(&Giant);
60194773907SVladimir Kondratyev 		error = hid_set_report_descr(sc->sc_dev, buf, hgd->hgd_maxlen);
60294773907SVladimir Kondratyev 		mtx_unlock(&Giant);
60394773907SVladimir Kondratyev 		HIDRAW_LOCAL_FREE(local_buf, buf);
60494773907SVladimir Kondratyev 
60594773907SVladimir Kondratyev 		/* Realloc hidraw input queue */
60694773907SVladimir Kondratyev 		if (error == 0)
60794773907SVladimir Kondratyev 			sc->sc_q = realloc(sc->sc_q,
60894773907SVladimir Kondratyev 			    sc->sc_rdesc->rdsize * HIDRAW_BUFFER_SIZE,
60994773907SVladimir Kondratyev 			    M_DEVBUF, M_ZERO | M_WAITOK);
61094773907SVladimir Kondratyev 
61194773907SVladimir Kondratyev 		/* Start interrupts again */
61294773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
61394773907SVladimir Kondratyev 		sc->sc_state.quiet = false;
61494773907SVladimir Kondratyev 		hidraw_unlock_queue(sc);
61594773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
61694773907SVladimir Kondratyev 		return (error);
61794773907SVladimir Kondratyev 	case HIDRAW_SET_IMMED:
61894773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FREAD))
61994773907SVladimir Kondratyev 			return (EPERM);
62094773907SVladimir Kondratyev 		if (*(int *)addr) {
62194773907SVladimir Kondratyev 			/* XXX should read into ibuf, but does it matter? */
62294773907SVladimir Kondratyev 			size = sc->sc_rdesc->isize;
62394773907SVladimir Kondratyev 			buf = HIDRAW_LOCAL_ALLOC(local_buf, size);
62494773907SVladimir Kondratyev 			error = hid_get_report(sc->sc_dev, buf, size, NULL,
62594773907SVladimir Kondratyev 			    HID_INPUT_REPORT, sc->sc_rdesc->iid);
62694773907SVladimir Kondratyev 			HIDRAW_LOCAL_FREE(local_buf, buf);
62794773907SVladimir Kondratyev 			if (error)
62894773907SVladimir Kondratyev 				return (EOPNOTSUPP);
62994773907SVladimir Kondratyev 
63094773907SVladimir Kondratyev 			mtx_lock(&sc->sc_mtx);
63194773907SVladimir Kondratyev 			sc->sc_state.immed = true;
63294773907SVladimir Kondratyev 			mtx_unlock(&sc->sc_mtx);
63394773907SVladimir Kondratyev 		} else {
63494773907SVladimir Kondratyev 			mtx_lock(&sc->sc_mtx);
63594773907SVladimir Kondratyev 			sc->sc_state.immed = false;
63694773907SVladimir Kondratyev 			mtx_unlock(&sc->sc_mtx);
63794773907SVladimir Kondratyev 		}
63894773907SVladimir Kondratyev 		return (0);
63994773907SVladimir Kondratyev 
64094773907SVladimir Kondratyev 	case HIDRAW_GET_REPORT:
64194773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FREAD))
64294773907SVladimir Kondratyev 			return (EPERM);
64394773907SVladimir Kondratyev 		hgd = (struct hidraw_gen_descriptor *)addr;
64494773907SVladimir Kondratyev 		switch (hgd->hgd_report_type) {
64594773907SVladimir Kondratyev 		case HID_INPUT_REPORT:
64694773907SVladimir Kondratyev 			size = sc->sc_rdesc->isize;
64794773907SVladimir Kondratyev 			id = sc->sc_rdesc->iid;
64894773907SVladimir Kondratyev 			break;
64994773907SVladimir Kondratyev 		case HID_OUTPUT_REPORT:
65094773907SVladimir Kondratyev 			size = sc->sc_rdesc->osize;
65194773907SVladimir Kondratyev 			id = sc->sc_rdesc->oid;
65294773907SVladimir Kondratyev 			break;
65394773907SVladimir Kondratyev 		case HID_FEATURE_REPORT:
65494773907SVladimir Kondratyev 			size = sc->sc_rdesc->fsize;
65594773907SVladimir Kondratyev 			id = sc->sc_rdesc->fid;
65694773907SVladimir Kondratyev 			break;
65794773907SVladimir Kondratyev 		default:
65894773907SVladimir Kondratyev 			return (EINVAL);
65994773907SVladimir Kondratyev 		}
66094773907SVladimir Kondratyev 		if (id != 0)
66194773907SVladimir Kondratyev 			copyin(hgd->hgd_data, &id, 1);
66294773907SVladimir Kondratyev 		size = MIN(hgd->hgd_maxlen, size);
66394773907SVladimir Kondratyev 		buf = HIDRAW_LOCAL_ALLOC(local_buf, size);
66494773907SVladimir Kondratyev 		error = hid_get_report(sc->sc_dev, buf, size, NULL,
66594773907SVladimir Kondratyev 		    hgd->hgd_report_type, id);
66694773907SVladimir Kondratyev 		if (!error)
66794773907SVladimir Kondratyev 			error = copyout(buf, hgd->hgd_data, size);
66894773907SVladimir Kondratyev 		HIDRAW_LOCAL_FREE(local_buf, buf);
66994773907SVladimir Kondratyev 		return (error);
67094773907SVladimir Kondratyev 
67194773907SVladimir Kondratyev 	case HIDRAW_SET_REPORT:
67294773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FWRITE))
67394773907SVladimir Kondratyev 			return (EPERM);
67494773907SVladimir Kondratyev 		hgd = (struct hidraw_gen_descriptor *)addr;
67594773907SVladimir Kondratyev 		switch (hgd->hgd_report_type) {
67694773907SVladimir Kondratyev 		case HID_INPUT_REPORT:
67794773907SVladimir Kondratyev 			size = sc->sc_rdesc->isize;
67894773907SVladimir Kondratyev 			id = sc->sc_rdesc->iid;
67994773907SVladimir Kondratyev 			break;
68094773907SVladimir Kondratyev 		case HID_OUTPUT_REPORT:
68194773907SVladimir Kondratyev 			size = sc->sc_rdesc->osize;
68294773907SVladimir Kondratyev 			id = sc->sc_rdesc->oid;
68394773907SVladimir Kondratyev 			break;
68494773907SVladimir Kondratyev 		case HID_FEATURE_REPORT:
68594773907SVladimir Kondratyev 			size = sc->sc_rdesc->fsize;
68694773907SVladimir Kondratyev 			id = sc->sc_rdesc->fid;
68794773907SVladimir Kondratyev 			break;
68894773907SVladimir Kondratyev 		default:
68994773907SVladimir Kondratyev 			return (EINVAL);
69094773907SVladimir Kondratyev 		}
69194773907SVladimir Kondratyev 		size = MIN(hgd->hgd_maxlen, size);
69294773907SVladimir Kondratyev 		buf = HIDRAW_LOCAL_ALLOC(local_buf, size);
69394773907SVladimir Kondratyev 		copyin(hgd->hgd_data, buf, size);
69494773907SVladimir Kondratyev 		if (id != 0)
69594773907SVladimir Kondratyev 			id = *(uint8_t *)buf;
69694773907SVladimir Kondratyev 		error = hid_set_report(sc->sc_dev, buf, size,
69794773907SVladimir Kondratyev 		    hgd->hgd_report_type, id);
69894773907SVladimir Kondratyev 		HIDRAW_LOCAL_FREE(local_buf, buf);
69994773907SVladimir Kondratyev 		return (error);
70094773907SVladimir Kondratyev 
70194773907SVladimir Kondratyev 	case HIDRAW_GET_REPORT_ID:
70294773907SVladimir Kondratyev 		*(int *)addr = 0;	/* XXX: we only support reportid 0? */
70394773907SVladimir Kondratyev 		return (0);
70494773907SVladimir Kondratyev 
70594773907SVladimir Kondratyev 	case HIDIOCGRDESCSIZE:
706*f988d7faSVladimir Kondratyev 		*(int *)addr = sc->sc_hw->rdescsize;
70794773907SVladimir Kondratyev 		return (0);
70894773907SVladimir Kondratyev 
70994773907SVladimir Kondratyev 	case HIDIOCGRDESC:
71094773907SVladimir Kondratyev 		hrd = *(struct hidraw_report_descriptor **)addr;
71194773907SVladimir Kondratyev 		error = copyin(&hrd->size, &size, sizeof(uint32_t));
71294773907SVladimir Kondratyev 		if (error)
71394773907SVladimir Kondratyev 			return (error);
71494773907SVladimir Kondratyev 		/*
71594773907SVladimir Kondratyev 		 * HID_MAX_DESCRIPTOR_SIZE-1 is a limit of report descriptor
71694773907SVladimir Kondratyev 		 * size in current Linux implementation.
71794773907SVladimir Kondratyev 		 */
71894773907SVladimir Kondratyev 		if (size >= HID_MAX_DESCRIPTOR_SIZE)
71994773907SVladimir Kondratyev 			return (EINVAL);
72094773907SVladimir Kondratyev 		buf = HIDRAW_LOCAL_ALLOC(local_buf, size);
72194773907SVladimir Kondratyev 		error = hid_get_rdesc(sc->sc_dev, buf, size);
72294773907SVladimir Kondratyev 		if (error == 0) {
72394773907SVladimir Kondratyev 			size = MIN(size, sc->sc_rdesc->len);
72494773907SVladimir Kondratyev 			error = copyout(buf, hrd->value, size);
72594773907SVladimir Kondratyev 		}
72694773907SVladimir Kondratyev 		HIDRAW_LOCAL_FREE(local_buf, buf);
72794773907SVladimir Kondratyev 		return (error);
72894773907SVladimir Kondratyev 
72994773907SVladimir Kondratyev 	case HIDIOCGRAWINFO:
73094773907SVladimir Kondratyev 		hdi = (struct hidraw_devinfo *)addr;
73194773907SVladimir Kondratyev 		hdi->bustype = sc->sc_hw->idBus;
73294773907SVladimir Kondratyev 		hdi->vendor = sc->sc_hw->idVendor;
73394773907SVladimir Kondratyev 		hdi->product = sc->sc_hw->idProduct;
73494773907SVladimir Kondratyev 		return (0);
73594773907SVladimir Kondratyev 	}
73694773907SVladimir Kondratyev 
73794773907SVladimir Kondratyev 	/* variable-length ioctls handling */
73894773907SVladimir Kondratyev 	len = IOCPARM_LEN(cmd);
73994773907SVladimir Kondratyev 	switch (IOCBASECMD(cmd)) {
74094773907SVladimir Kondratyev 	case HIDIOCGRAWNAME(0):
74194773907SVladimir Kondratyev 		strlcpy(addr, sc->sc_hw->name, len);
74294773907SVladimir Kondratyev 		return (0);
74394773907SVladimir Kondratyev 
74494773907SVladimir Kondratyev 	case HIDIOCGRAWPHYS(0):
74594773907SVladimir Kondratyev 		strlcpy(addr, device_get_nameunit(sc->sc_dev), len);
74694773907SVladimir Kondratyev 		return (0);
74794773907SVladimir Kondratyev 
74894773907SVladimir Kondratyev 	case HIDIOCSFEATURE(0):
74994773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FWRITE))
75094773907SVladimir Kondratyev 			return (EPERM);
75194773907SVladimir Kondratyev 		if (len < 2)
75294773907SVladimir Kondratyev 			return (EINVAL);
75394773907SVladimir Kondratyev 		id = *(uint8_t *)addr;
75494773907SVladimir Kondratyev 		if (id == 0) {
75594773907SVladimir Kondratyev 			addr = (uint8_t *)addr + 1;
75694773907SVladimir Kondratyev 			len--;
75794773907SVladimir Kondratyev 		}
75894773907SVladimir Kondratyev 		return (hid_set_report(sc->sc_dev, addr, len,
75994773907SVladimir Kondratyev 		    HID_FEATURE_REPORT, id));
76094773907SVladimir Kondratyev 
76194773907SVladimir Kondratyev 	case HIDIOCGFEATURE(0):
76294773907SVladimir Kondratyev 		if (!(sc->sc_fflags & FREAD))
76394773907SVladimir Kondratyev 			return (EPERM);
76494773907SVladimir Kondratyev 		if (len < 2)
76594773907SVladimir Kondratyev 			return (EINVAL);
76694773907SVladimir Kondratyev 		id = *(uint8_t *)addr;
76794773907SVladimir Kondratyev 		if (id == 0) {
76894773907SVladimir Kondratyev 			addr = (uint8_t *)addr + 1;
76994773907SVladimir Kondratyev 			len--;
77094773907SVladimir Kondratyev 		}
77194773907SVladimir Kondratyev 		return (hid_get_report(sc->sc_dev, addr, len, NULL,
77294773907SVladimir Kondratyev 		    HID_FEATURE_REPORT, id));
77394773907SVladimir Kondratyev 
77494773907SVladimir Kondratyev 	case HIDIOCGRAWUNIQ(0):
77594773907SVladimir Kondratyev 		strlcpy(addr, sc->sc_hw->serial, len);
77694773907SVladimir Kondratyev 		return (0);
77794773907SVladimir Kondratyev 	}
77894773907SVladimir Kondratyev 
77994773907SVladimir Kondratyev 	return (EINVAL);
78094773907SVladimir Kondratyev }
78194773907SVladimir Kondratyev 
78294773907SVladimir Kondratyev static int
78394773907SVladimir Kondratyev hidraw_poll(struct cdev *dev, int events, struct thread *td)
78494773907SVladimir Kondratyev {
78594773907SVladimir Kondratyev 	struct hidraw_softc *sc;
78694773907SVladimir Kondratyev 	int revents = 0;
78794773907SVladimir Kondratyev 
78894773907SVladimir Kondratyev 	sc = dev->si_drv1;
78994773907SVladimir Kondratyev 	if (sc == NULL)
79094773907SVladimir Kondratyev 		return (POLLHUP);
79194773907SVladimir Kondratyev 
79294773907SVladimir Kondratyev 	if (events & (POLLOUT | POLLWRNORM) && (sc->sc_fflags & FWRITE))
79394773907SVladimir Kondratyev 		revents |= events & (POLLOUT | POLLWRNORM);
79494773907SVladimir Kondratyev 	if (events & (POLLIN | POLLRDNORM) && (sc->sc_fflags & FREAD)) {
79594773907SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
79694773907SVladimir Kondratyev 		if (sc->sc_head != sc->sc_tail)
79794773907SVladimir Kondratyev 			revents |= events & (POLLIN | POLLRDNORM);
79894773907SVladimir Kondratyev 		else {
79994773907SVladimir Kondratyev 			sc->sc_state.sel = true;
80094773907SVladimir Kondratyev 			selrecord(td, &sc->sc_rsel);
80194773907SVladimir Kondratyev 		}
80294773907SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
80394773907SVladimir Kondratyev 	}
80494773907SVladimir Kondratyev 
80594773907SVladimir Kondratyev 	return (revents);
80694773907SVladimir Kondratyev }
80794773907SVladimir Kondratyev 
80894773907SVladimir Kondratyev static int
80994773907SVladimir Kondratyev hidraw_kqfilter(struct cdev *dev, struct knote *kn)
81094773907SVladimir Kondratyev {
81194773907SVladimir Kondratyev 	struct hidraw_softc *sc;
81294773907SVladimir Kondratyev 
81394773907SVladimir Kondratyev 	sc = dev->si_drv1;
81494773907SVladimir Kondratyev 	if (sc == NULL)
81594773907SVladimir Kondratyev 		return (ENXIO);
81694773907SVladimir Kondratyev 
81794773907SVladimir Kondratyev 	switch(kn->kn_filter) {
81894773907SVladimir Kondratyev 	case EVFILT_READ:
81994773907SVladimir Kondratyev 		if (sc->sc_fflags & FREAD) {
82094773907SVladimir Kondratyev 			kn->kn_fop = &hidraw_filterops_read;
82194773907SVladimir Kondratyev 			break;
82294773907SVladimir Kondratyev 		}
82394773907SVladimir Kondratyev 		/* FALLTHROUGH */
82494773907SVladimir Kondratyev 	default:
82594773907SVladimir Kondratyev 		return(EINVAL);
82694773907SVladimir Kondratyev 	}
82794773907SVladimir Kondratyev 	kn->kn_hook = sc;
82894773907SVladimir Kondratyev 
82994773907SVladimir Kondratyev 	knlist_add(&sc->sc_rsel.si_note, kn, 0);
83094773907SVladimir Kondratyev 	return (0);
83194773907SVladimir Kondratyev }
83294773907SVladimir Kondratyev 
83394773907SVladimir Kondratyev static int
83494773907SVladimir Kondratyev hidraw_kqread(struct knote *kn, long hint)
83594773907SVladimir Kondratyev {
83694773907SVladimir Kondratyev 	struct hidraw_softc *sc;
83794773907SVladimir Kondratyev 	int ret;
83894773907SVladimir Kondratyev 
83994773907SVladimir Kondratyev 	sc = kn->kn_hook;
84094773907SVladimir Kondratyev 
84194773907SVladimir Kondratyev 	mtx_assert(&sc->sc_mtx, MA_OWNED);
84294773907SVladimir Kondratyev 
84394773907SVladimir Kondratyev 	if (sc->dev->si_drv1 == NULL) {
84494773907SVladimir Kondratyev 		kn->kn_flags |= EV_EOF;
84594773907SVladimir Kondratyev 		ret = 1;
84694773907SVladimir Kondratyev 	} else
84794773907SVladimir Kondratyev 		ret = (sc->sc_head != sc->sc_tail) ? 1 : 0;
84894773907SVladimir Kondratyev 
84994773907SVladimir Kondratyev 	return (ret);
85094773907SVladimir Kondratyev }
85194773907SVladimir Kondratyev 
85294773907SVladimir Kondratyev static void
85394773907SVladimir Kondratyev hidraw_kqdetach(struct knote *kn)
85494773907SVladimir Kondratyev {
85594773907SVladimir Kondratyev 	struct hidraw_softc *sc;
85694773907SVladimir Kondratyev 
85794773907SVladimir Kondratyev 	sc = kn->kn_hook;
85894773907SVladimir Kondratyev 	knlist_remove(&sc->sc_rsel.si_note, kn, 0);
85994773907SVladimir Kondratyev }
86094773907SVladimir Kondratyev 
86194773907SVladimir Kondratyev static void
86294773907SVladimir Kondratyev hidraw_notify(struct hidraw_softc *sc)
86394773907SVladimir Kondratyev {
86494773907SVladimir Kondratyev 
86594773907SVladimir Kondratyev 	mtx_assert(&sc->sc_mtx, MA_OWNED);
86694773907SVladimir Kondratyev 
86794773907SVladimir Kondratyev 	if (sc->sc_state.aslp) {
86894773907SVladimir Kondratyev 		sc->sc_state.aslp = false;
86994773907SVladimir Kondratyev 		DPRINTFN(5, "waking %p\n", &sc->sc_q);
87094773907SVladimir Kondratyev 		wakeup(&sc->sc_q);
87194773907SVladimir Kondratyev 	}
87294773907SVladimir Kondratyev 	if (sc->sc_state.sel) {
87394773907SVladimir Kondratyev 		sc->sc_state.sel = false;
87494773907SVladimir Kondratyev 		selwakeuppri(&sc->sc_rsel, PZERO);
87594773907SVladimir Kondratyev 	}
87694773907SVladimir Kondratyev 	if (sc->sc_async != NULL) {
87794773907SVladimir Kondratyev 		DPRINTFN(3, "sending SIGIO %p\n", sc->sc_async);
87894773907SVladimir Kondratyev 		PROC_LOCK(sc->sc_async);
87994773907SVladimir Kondratyev 		kern_psignal(sc->sc_async, SIGIO);
88094773907SVladimir Kondratyev 		PROC_UNLOCK(sc->sc_async);
88194773907SVladimir Kondratyev 	}
88294773907SVladimir Kondratyev 	KNOTE_LOCKED(&sc->sc_rsel.si_note, 0);
88394773907SVladimir Kondratyev }
88494773907SVladimir Kondratyev 
88594773907SVladimir Kondratyev static device_method_t hidraw_methods[] = {
88694773907SVladimir Kondratyev 	/* Device interface */
88794773907SVladimir Kondratyev 	DEVMETHOD(device_identify,	hidraw_identify),
88894773907SVladimir Kondratyev 	DEVMETHOD(device_probe,		hidraw_probe),
88994773907SVladimir Kondratyev 	DEVMETHOD(device_attach,	hidraw_attach),
89094773907SVladimir Kondratyev 	DEVMETHOD(device_detach,	hidraw_detach),
89194773907SVladimir Kondratyev 
89294773907SVladimir Kondratyev 	DEVMETHOD_END
89394773907SVladimir Kondratyev };
89494773907SVladimir Kondratyev 
89594773907SVladimir Kondratyev static driver_t hidraw_driver = {
89694773907SVladimir Kondratyev 	"hidraw",
89794773907SVladimir Kondratyev 	hidraw_methods,
89894773907SVladimir Kondratyev 	sizeof(struct hidraw_softc)
89994773907SVladimir Kondratyev };
90094773907SVladimir Kondratyev 
9019be6b22dSVladimir Kondratyev #ifndef HIDRAW_MAKE_UHID_ALIAS
9029be6b22dSVladimir Kondratyev devclass_t hidraw_devclass;
9039be6b22dSVladimir Kondratyev #endif
90494773907SVladimir Kondratyev 
90594773907SVladimir Kondratyev DRIVER_MODULE(hidraw, hidbus, hidraw_driver, hidraw_devclass, NULL, 0);
90694773907SVladimir Kondratyev MODULE_DEPEND(hidraw, hidbus, 1, 1, 1);
90794773907SVladimir Kondratyev MODULE_DEPEND(hidraw, hid, 1, 1, 1);
90894773907SVladimir Kondratyev MODULE_DEPEND(hidraw, usb, 1, 1, 1);
90994773907SVladimir Kondratyev MODULE_VERSION(hidraw, 1);
910