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