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> 4445b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32 4545b48cbcSBrooks Davis #include <sys/abi_compat.h> 4645b48cbcSBrooks Davis #endif 4794773907SVladimir Kondratyev #include <sys/bus.h> 4894773907SVladimir Kondratyev #include <sys/conf.h> 4994773907SVladimir Kondratyev #include <sys/fcntl.h> 5094773907SVladimir Kondratyev #include <sys/filio.h> 5194773907SVladimir Kondratyev #include <sys/ioccom.h> 5294773907SVladimir Kondratyev #include <sys/kernel.h> 5394773907SVladimir Kondratyev #include <sys/lock.h> 5494773907SVladimir Kondratyev #include <sys/malloc.h> 5594773907SVladimir Kondratyev #include <sys/module.h> 5694773907SVladimir Kondratyev #include <sys/mutex.h> 5794773907SVladimir Kondratyev #include <sys/poll.h> 5894773907SVladimir Kondratyev #include <sys/priv.h> 5994773907SVladimir Kondratyev #include <sys/proc.h> 6094773907SVladimir Kondratyev #include <sys/selinfo.h> 6194773907SVladimir Kondratyev #include <sys/sysctl.h> 6294773907SVladimir Kondratyev #include <sys/systm.h> 6394773907SVladimir Kondratyev #include <sys/tty.h> 6494773907SVladimir Kondratyev #include <sys/uio.h> 6594773907SVladimir Kondratyev 6694773907SVladimir Kondratyev #define HID_DEBUG_VAR hidraw_debug 6794773907SVladimir Kondratyev #include <dev/hid/hid.h> 6894773907SVladimir Kondratyev #include <dev/hid/hidbus.h> 6994773907SVladimir Kondratyev #include <dev/hid/hidraw.h> 7094773907SVladimir Kondratyev 7194773907SVladimir Kondratyev #ifdef HID_DEBUG 7294773907SVladimir Kondratyev static int hidraw_debug = 0; 7394773907SVladimir Kondratyev static SYSCTL_NODE(_hw_hid, OID_AUTO, hidraw, CTLFLAG_RW, 0, 7494773907SVladimir Kondratyev "HID raw interface"); 7594773907SVladimir Kondratyev SYSCTL_INT(_hw_hid_hidraw, OID_AUTO, debug, CTLFLAG_RWTUN, 7694773907SVladimir Kondratyev &hidraw_debug, 0, "Debug level"); 7794773907SVladimir Kondratyev #endif 7894773907SVladimir Kondratyev 7994773907SVladimir Kondratyev #define HIDRAW_INDEX 0xFF /* Arbitrary high value */ 8094773907SVladimir Kondratyev 8194773907SVladimir Kondratyev #define HIDRAW_LOCAL_BUFSIZE 64 /* Size of on-stack buffer. */ 8294773907SVladimir Kondratyev #define HIDRAW_LOCAL_ALLOC(local_buf, size) \ 8394773907SVladimir Kondratyev (sizeof(local_buf) > (size) ? (local_buf) : \ 8494773907SVladimir Kondratyev malloc((size), M_DEVBUF, M_ZERO | M_WAITOK)) 8594773907SVladimir Kondratyev #define HIDRAW_LOCAL_FREE(local_buf, buf) \ 8694773907SVladimir Kondratyev if ((local_buf) != (buf)) { \ 8794773907SVladimir Kondratyev free((buf), M_DEVBUF); \ 8894773907SVladimir Kondratyev } 8994773907SVladimir Kondratyev 9094773907SVladimir Kondratyev struct hidraw_softc { 9194773907SVladimir Kondratyev device_t sc_dev; /* base device */ 9294773907SVladimir Kondratyev 9394773907SVladimir Kondratyev struct mtx sc_mtx; /* hidbus private mutex */ 9494773907SVladimir Kondratyev 9594773907SVladimir Kondratyev struct hid_rdesc_info *sc_rdesc; 9694773907SVladimir Kondratyev const struct hid_device_info *sc_hw; 9794773907SVladimir Kondratyev 9894773907SVladimir Kondratyev uint8_t *sc_q; 9994773907SVladimir Kondratyev hid_size_t *sc_qlen; 10094773907SVladimir Kondratyev int sc_head; 10194773907SVladimir Kondratyev int sc_tail; 10294773907SVladimir Kondratyev int sc_sleepcnt; 10394773907SVladimir Kondratyev 10494773907SVladimir Kondratyev struct selinfo sc_rsel; 10594773907SVladimir Kondratyev struct proc *sc_async; /* process that wants SIGIO */ 10694773907SVladimir Kondratyev struct { /* driver state */ 10794773907SVladimir Kondratyev bool open:1; /* device is open */ 10894773907SVladimir Kondratyev bool aslp:1; /* waiting for device data in read() */ 10994773907SVladimir Kondratyev bool sel:1; /* waiting for device data in poll() */ 11094773907SVladimir Kondratyev bool quiet:1; /* Ignore input data */ 11194773907SVladimir Kondratyev bool immed:1; /* return read data immediately */ 11294773907SVladimir Kondratyev bool uhid:1; /* driver switched in to uhid mode */ 11394773907SVladimir Kondratyev bool lock:1; /* input queue sleepable lock */ 11494773907SVladimir Kondratyev bool flush:1; /* do not wait for data in read() */ 11594773907SVladimir Kondratyev } sc_state; 11694773907SVladimir Kondratyev int sc_fflags; /* access mode for open lifetime */ 11794773907SVladimir Kondratyev 11894773907SVladimir Kondratyev struct cdev *dev; 11994773907SVladimir Kondratyev }; 12094773907SVladimir Kondratyev 12145b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32 12245b48cbcSBrooks Davis struct hidraw_gen_descriptor32 { 12345b48cbcSBrooks Davis uint32_t hgd_data; /* void * */ 12445b48cbcSBrooks Davis uint16_t hgd_lang_id; 12545b48cbcSBrooks Davis uint16_t hgd_maxlen; 12645b48cbcSBrooks Davis uint16_t hgd_actlen; 12745b48cbcSBrooks Davis uint16_t hgd_offset; 12845b48cbcSBrooks Davis uint8_t hgd_config_index; 12945b48cbcSBrooks Davis uint8_t hgd_string_index; 13045b48cbcSBrooks Davis uint8_t hgd_iface_index; 13145b48cbcSBrooks Davis uint8_t hgd_altif_index; 13245b48cbcSBrooks Davis uint8_t hgd_endpt_index; 13345b48cbcSBrooks Davis uint8_t hgd_report_type; 13445b48cbcSBrooks Davis uint8_t reserved[8]; 13545b48cbcSBrooks Davis }; 13645b48cbcSBrooks Davis #define HIDRAW_GET_REPORT_DESC32 \ 13745b48cbcSBrooks Davis _IOC_NEWTYPE(HIDRAW_GET_REPORT_DESC, struct hidraw_gen_descriptor32) 13845b48cbcSBrooks Davis #define HIDRAW_GET_REPORT32 \ 13945b48cbcSBrooks Davis _IOC_NEWTYPE(HIDRAW_GET_REPORT, struct hidraw_gen_descriptor32) 14045b48cbcSBrooks Davis #define HIDRAW_SET_REPORT_DESC32 \ 14145b48cbcSBrooks Davis _IOC_NEWTYPE(HIDRAW_SET_REPORT_DESC, struct hidraw_gen_descriptor32) 14245b48cbcSBrooks Davis #define HIDRAW_SET_REPORT32 \ 14345b48cbcSBrooks Davis _IOC_NEWTYPE(HIDRAW_SET_REPORT, struct hidraw_gen_descriptor32) 14445b48cbcSBrooks Davis #endif 14545b48cbcSBrooks Davis 14694773907SVladimir Kondratyev static d_open_t hidraw_open; 14794773907SVladimir Kondratyev static d_read_t hidraw_read; 14894773907SVladimir Kondratyev static d_write_t hidraw_write; 14994773907SVladimir Kondratyev static d_ioctl_t hidraw_ioctl; 15094773907SVladimir Kondratyev static d_poll_t hidraw_poll; 15194773907SVladimir Kondratyev static d_kqfilter_t hidraw_kqfilter; 15294773907SVladimir Kondratyev 15394773907SVladimir Kondratyev static d_priv_dtor_t hidraw_dtor; 15494773907SVladimir Kondratyev 15594773907SVladimir Kondratyev static struct cdevsw hidraw_cdevsw = { 15694773907SVladimir Kondratyev .d_version = D_VERSION, 15794773907SVladimir Kondratyev .d_open = hidraw_open, 15894773907SVladimir Kondratyev .d_read = hidraw_read, 15994773907SVladimir Kondratyev .d_write = hidraw_write, 16094773907SVladimir Kondratyev .d_ioctl = hidraw_ioctl, 16194773907SVladimir Kondratyev .d_poll = hidraw_poll, 16294773907SVladimir Kondratyev .d_kqfilter = hidraw_kqfilter, 16394773907SVladimir Kondratyev .d_name = "hidraw", 16494773907SVladimir Kondratyev }; 16594773907SVladimir Kondratyev 16694773907SVladimir Kondratyev static hid_intr_t hidraw_intr; 16794773907SVladimir Kondratyev 16894773907SVladimir Kondratyev static device_identify_t hidraw_identify; 16994773907SVladimir Kondratyev static device_probe_t hidraw_probe; 17094773907SVladimir Kondratyev static device_attach_t hidraw_attach; 17194773907SVladimir Kondratyev static device_detach_t hidraw_detach; 17294773907SVladimir Kondratyev 17394773907SVladimir Kondratyev static int hidraw_kqread(struct knote *, long); 17494773907SVladimir Kondratyev static void hidraw_kqdetach(struct knote *); 17594773907SVladimir Kondratyev static void hidraw_notify(struct hidraw_softc *); 17694773907SVladimir Kondratyev 17794773907SVladimir Kondratyev static struct filterops hidraw_filterops_read = { 17894773907SVladimir Kondratyev .f_isfd = 1, 17994773907SVladimir Kondratyev .f_detach = hidraw_kqdetach, 18094773907SVladimir Kondratyev .f_event = hidraw_kqread, 18194773907SVladimir Kondratyev }; 18294773907SVladimir Kondratyev 18394773907SVladimir Kondratyev static void 18494773907SVladimir Kondratyev hidraw_identify(driver_t *driver, device_t parent) 18594773907SVladimir Kondratyev { 18694773907SVladimir Kondratyev device_t child; 18794773907SVladimir Kondratyev 18894773907SVladimir Kondratyev if (device_find_child(parent, "hidraw", -1) == NULL) { 18994773907SVladimir Kondratyev child = BUS_ADD_CHILD(parent, 0, "hidraw", 19094773907SVladimir Kondratyev device_get_unit(parent)); 19194773907SVladimir Kondratyev if (child != NULL) 19294773907SVladimir Kondratyev hidbus_set_index(child, HIDRAW_INDEX); 19394773907SVladimir Kondratyev } 19494773907SVladimir Kondratyev } 19594773907SVladimir Kondratyev 19694773907SVladimir Kondratyev static int 19794773907SVladimir Kondratyev hidraw_probe(device_t self) 19894773907SVladimir Kondratyev { 19994773907SVladimir Kondratyev 20094773907SVladimir Kondratyev if (hidbus_get_index(self) != HIDRAW_INDEX) 20194773907SVladimir Kondratyev return (ENXIO); 20294773907SVladimir Kondratyev 20394773907SVladimir Kondratyev hidbus_set_desc(self, "Raw HID Device"); 20494773907SVladimir Kondratyev 20594773907SVladimir Kondratyev return (BUS_PROBE_GENERIC); 20694773907SVladimir Kondratyev } 20794773907SVladimir Kondratyev 20894773907SVladimir Kondratyev static int 20994773907SVladimir Kondratyev hidraw_attach(device_t self) 21094773907SVladimir Kondratyev { 21194773907SVladimir Kondratyev struct hidraw_softc *sc = device_get_softc(self); 21294773907SVladimir Kondratyev struct make_dev_args mda; 21394773907SVladimir Kondratyev int error; 21494773907SVladimir Kondratyev 21594773907SVladimir Kondratyev sc->sc_dev = self; 21694773907SVladimir Kondratyev sc->sc_rdesc = hidbus_get_rdesc_info(self); 21794773907SVladimir Kondratyev sc->sc_hw = hid_get_device_info(self); 21894773907SVladimir Kondratyev 21994773907SVladimir Kondratyev /* Hidraw mode does not require report descriptor to work */ 22094773907SVladimir Kondratyev if (sc->sc_rdesc->data == NULL || sc->sc_rdesc->len == 0) 22194773907SVladimir Kondratyev device_printf(self, "no report descriptor\n"); 22294773907SVladimir Kondratyev 22394773907SVladimir Kondratyev mtx_init(&sc->sc_mtx, "hidraw lock", NULL, MTX_DEF); 22494773907SVladimir Kondratyev knlist_init_mtx(&sc->sc_rsel.si_note, &sc->sc_mtx); 22594773907SVladimir Kondratyev 22694773907SVladimir Kondratyev make_dev_args_init(&mda); 22794773907SVladimir Kondratyev mda.mda_flags = MAKEDEV_WAITOK; 22894773907SVladimir Kondratyev mda.mda_devsw = &hidraw_cdevsw; 22994773907SVladimir Kondratyev mda.mda_uid = UID_ROOT; 23094773907SVladimir Kondratyev mda.mda_gid = GID_OPERATOR; 23194773907SVladimir Kondratyev mda.mda_mode = 0600; 23294773907SVladimir Kondratyev mda.mda_si_drv1 = sc; 23394773907SVladimir Kondratyev 23494773907SVladimir Kondratyev error = make_dev_s(&mda, &sc->dev, "hidraw%d", device_get_unit(self)); 23594773907SVladimir Kondratyev if (error) { 23694773907SVladimir Kondratyev device_printf(self, "Can not create character device\n"); 23794773907SVladimir Kondratyev hidraw_detach(self); 23894773907SVladimir Kondratyev return (error); 23994773907SVladimir Kondratyev } 2409be6b22dSVladimir Kondratyev #ifdef HIDRAW_MAKE_UHID_ALIAS 2419be6b22dSVladimir Kondratyev (void)make_dev_alias(sc->dev, "uhid%d", device_get_unit(self)); 2429be6b22dSVladimir Kondratyev #endif 24394773907SVladimir Kondratyev 24494773907SVladimir Kondratyev hidbus_set_lock(self, &sc->sc_mtx); 24594773907SVladimir Kondratyev hidbus_set_intr(self, hidraw_intr, sc); 24694773907SVladimir Kondratyev 24794773907SVladimir Kondratyev return (0); 24894773907SVladimir Kondratyev } 24994773907SVladimir Kondratyev 25094773907SVladimir Kondratyev static int 25194773907SVladimir Kondratyev hidraw_detach(device_t self) 25294773907SVladimir Kondratyev { 25394773907SVladimir Kondratyev struct hidraw_softc *sc = device_get_softc(self); 25494773907SVladimir Kondratyev 25594773907SVladimir Kondratyev DPRINTF("sc=%p\n", sc); 25694773907SVladimir Kondratyev 25794773907SVladimir Kondratyev if (sc->dev != NULL) { 25894773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 25994773907SVladimir Kondratyev sc->dev->si_drv1 = NULL; 26094773907SVladimir Kondratyev /* Wake everyone */ 26194773907SVladimir Kondratyev hidraw_notify(sc); 26294773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 26394773907SVladimir Kondratyev destroy_dev(sc->dev); 26494773907SVladimir Kondratyev } 26594773907SVladimir Kondratyev 26694773907SVladimir Kondratyev knlist_clear(&sc->sc_rsel.si_note, 0); 26794773907SVladimir Kondratyev knlist_destroy(&sc->sc_rsel.si_note); 26894773907SVladimir Kondratyev seldrain(&sc->sc_rsel); 26994773907SVladimir Kondratyev mtx_destroy(&sc->sc_mtx); 27094773907SVladimir Kondratyev 27194773907SVladimir Kondratyev return (0); 27294773907SVladimir Kondratyev } 27394773907SVladimir Kondratyev 27494773907SVladimir Kondratyev void 27594773907SVladimir Kondratyev hidraw_intr(void *context, void *buf, hid_size_t len) 27694773907SVladimir Kondratyev { 27794773907SVladimir Kondratyev struct hidraw_softc *sc = context; 27894773907SVladimir Kondratyev int next; 27994773907SVladimir Kondratyev 28094773907SVladimir Kondratyev DPRINTFN(5, "len=%d\n", len); 28194773907SVladimir Kondratyev DPRINTFN(5, "data = %*D\n", len, buf, " "); 28294773907SVladimir Kondratyev 28394773907SVladimir Kondratyev next = (sc->sc_tail + 1) % HIDRAW_BUFFER_SIZE; 28494773907SVladimir Kondratyev if (sc->sc_state.quiet || next == sc->sc_head) 28594773907SVladimir Kondratyev return; 28694773907SVladimir Kondratyev 28794773907SVladimir Kondratyev bcopy(buf, sc->sc_q + sc->sc_tail * sc->sc_rdesc->rdsize, len); 28894773907SVladimir Kondratyev 28994773907SVladimir Kondratyev /* Make sure we don't process old data */ 29094773907SVladimir Kondratyev if (len < sc->sc_rdesc->rdsize) 29194773907SVladimir Kondratyev bzero(sc->sc_q + sc->sc_tail * sc->sc_rdesc->rdsize + len, 29294773907SVladimir Kondratyev sc->sc_rdesc->isize - len); 29394773907SVladimir Kondratyev 29494773907SVladimir Kondratyev sc->sc_qlen[sc->sc_tail] = len; 29594773907SVladimir Kondratyev sc->sc_tail = next; 29694773907SVladimir Kondratyev 29794773907SVladimir Kondratyev hidraw_notify(sc); 29894773907SVladimir Kondratyev } 29994773907SVladimir Kondratyev 30094773907SVladimir Kondratyev static inline int 30194773907SVladimir Kondratyev hidraw_lock_queue(struct hidraw_softc *sc, bool flush) 30294773907SVladimir Kondratyev { 30394773907SVladimir Kondratyev int error = 0; 30494773907SVladimir Kondratyev 30594773907SVladimir Kondratyev mtx_assert(&sc->sc_mtx, MA_OWNED); 30694773907SVladimir Kondratyev 30794773907SVladimir Kondratyev if (flush) 30894773907SVladimir Kondratyev sc->sc_state.flush = true; 30994773907SVladimir Kondratyev ++sc->sc_sleepcnt; 31094773907SVladimir Kondratyev while (sc->sc_state.lock && error == 0) { 31194773907SVladimir Kondratyev /* Flush is requested. Wakeup all readers and forbid sleeps */ 31294773907SVladimir Kondratyev if (flush && sc->sc_state.aslp) { 31394773907SVladimir Kondratyev sc->sc_state.aslp = false; 31494773907SVladimir Kondratyev DPRINTFN(5, "waking %p\n", &sc->sc_q); 31594773907SVladimir Kondratyev wakeup(&sc->sc_q); 31694773907SVladimir Kondratyev } 31794773907SVladimir Kondratyev error = mtx_sleep(&sc->sc_sleepcnt, &sc->sc_mtx, 31894773907SVladimir Kondratyev PZERO | PCATCH, "hidrawio", 0); 31994773907SVladimir Kondratyev } 32094773907SVladimir Kondratyev --sc->sc_sleepcnt; 32194773907SVladimir Kondratyev if (flush) 32294773907SVladimir Kondratyev sc->sc_state.flush = false; 32394773907SVladimir Kondratyev if (error == 0) 32494773907SVladimir Kondratyev sc->sc_state.lock = true; 32594773907SVladimir Kondratyev 32694773907SVladimir Kondratyev return (error); 32794773907SVladimir Kondratyev } 32894773907SVladimir Kondratyev 32994773907SVladimir Kondratyev static inline void 33094773907SVladimir Kondratyev hidraw_unlock_queue(struct hidraw_softc *sc) 33194773907SVladimir Kondratyev { 33294773907SVladimir Kondratyev 33394773907SVladimir Kondratyev mtx_assert(&sc->sc_mtx, MA_OWNED); 33494773907SVladimir Kondratyev KASSERT(sc->sc_state.lock, ("input buffer is not locked")); 33594773907SVladimir Kondratyev 33694773907SVladimir Kondratyev if (sc->sc_sleepcnt != 0) 33794773907SVladimir Kondratyev wakeup_one(&sc->sc_sleepcnt); 33894773907SVladimir Kondratyev sc->sc_state.lock = false; 33994773907SVladimir Kondratyev } 34094773907SVladimir Kondratyev 34194773907SVladimir Kondratyev static int 34294773907SVladimir Kondratyev hidraw_open(struct cdev *dev, int flag, int mode, struct thread *td) 34394773907SVladimir Kondratyev { 34494773907SVladimir Kondratyev struct hidraw_softc *sc; 34594773907SVladimir Kondratyev int error; 34694773907SVladimir Kondratyev 34794773907SVladimir Kondratyev sc = dev->si_drv1; 34894773907SVladimir Kondratyev if (sc == NULL) 34994773907SVladimir Kondratyev return (ENXIO); 35094773907SVladimir Kondratyev 35194773907SVladimir Kondratyev DPRINTF("sc=%p\n", sc); 35294773907SVladimir Kondratyev 35394773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 35494773907SVladimir Kondratyev if (sc->sc_state.open) { 35594773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 35694773907SVladimir Kondratyev return (EBUSY); 35794773907SVladimir Kondratyev } 35894773907SVladimir Kondratyev sc->sc_state.open = true; 35994773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 36094773907SVladimir Kondratyev 36194773907SVladimir Kondratyev error = devfs_set_cdevpriv(sc, hidraw_dtor); 36294773907SVladimir Kondratyev if (error != 0) { 36394773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 36494773907SVladimir Kondratyev sc->sc_state.open = false; 36594773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 36694773907SVladimir Kondratyev return (error); 36794773907SVladimir Kondratyev } 36894773907SVladimir Kondratyev 36994773907SVladimir Kondratyev sc->sc_q = malloc(sc->sc_rdesc->rdsize * HIDRAW_BUFFER_SIZE, M_DEVBUF, 37094773907SVladimir Kondratyev M_ZERO | M_WAITOK); 37194773907SVladimir Kondratyev sc->sc_qlen = malloc(sizeof(hid_size_t) * HIDRAW_BUFFER_SIZE, M_DEVBUF, 37294773907SVladimir Kondratyev M_ZERO | M_WAITOK); 37394773907SVladimir Kondratyev 37494773907SVladimir Kondratyev /* Set up interrupt pipe. */ 37594773907SVladimir Kondratyev sc->sc_state.immed = false; 37694773907SVladimir Kondratyev sc->sc_async = 0; 37794773907SVladimir Kondratyev sc->sc_state.uhid = false; /* hidraw mode is default */ 37894773907SVladimir Kondratyev sc->sc_state.quiet = false; 37994773907SVladimir Kondratyev sc->sc_head = sc->sc_tail = 0; 38094773907SVladimir Kondratyev sc->sc_fflags = flag; 38194773907SVladimir Kondratyev 38294773907SVladimir Kondratyev hidbus_intr_start(sc->sc_dev); 38394773907SVladimir Kondratyev 38494773907SVladimir Kondratyev return (0); 38594773907SVladimir Kondratyev } 38694773907SVladimir Kondratyev 38794773907SVladimir Kondratyev static void 38894773907SVladimir Kondratyev hidraw_dtor(void *data) 38994773907SVladimir Kondratyev { 39094773907SVladimir Kondratyev struct hidraw_softc *sc = data; 39194773907SVladimir Kondratyev 39294773907SVladimir Kondratyev DPRINTF("sc=%p\n", sc); 39394773907SVladimir Kondratyev 39494773907SVladimir Kondratyev /* Disable interrupts. */ 39594773907SVladimir Kondratyev hidbus_intr_stop(sc->sc_dev); 39694773907SVladimir Kondratyev 39794773907SVladimir Kondratyev sc->sc_tail = sc->sc_head = 0; 39894773907SVladimir Kondratyev sc->sc_async = 0; 39994773907SVladimir Kondratyev free(sc->sc_q, M_DEVBUF); 40094773907SVladimir Kondratyev free(sc->sc_qlen, M_DEVBUF); 40194773907SVladimir Kondratyev sc->sc_q = NULL; 40294773907SVladimir Kondratyev 40394773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 40494773907SVladimir Kondratyev sc->sc_state.open = false; 40594773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 40694773907SVladimir Kondratyev } 40794773907SVladimir Kondratyev 40894773907SVladimir Kondratyev static int 40994773907SVladimir Kondratyev hidraw_read(struct cdev *dev, struct uio *uio, int flag) 41094773907SVladimir Kondratyev { 41194773907SVladimir Kondratyev struct hidraw_softc *sc; 41294773907SVladimir Kondratyev size_t length; 41394773907SVladimir Kondratyev int error; 41494773907SVladimir Kondratyev 41594773907SVladimir Kondratyev DPRINTFN(1, "\n"); 41694773907SVladimir Kondratyev 41794773907SVladimir Kondratyev sc = dev->si_drv1; 41894773907SVladimir Kondratyev if (sc == NULL) 41994773907SVladimir Kondratyev return (EIO); 42094773907SVladimir Kondratyev 42194773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 42294773907SVladimir Kondratyev error = dev->si_drv1 == NULL ? EIO : hidraw_lock_queue(sc, false); 42394773907SVladimir Kondratyev if (error != 0) { 42494773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 42594773907SVladimir Kondratyev return (error); 42694773907SVladimir Kondratyev } 42794773907SVladimir Kondratyev 42894773907SVladimir Kondratyev if (sc->sc_state.immed) { 42994773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 43094773907SVladimir Kondratyev DPRINTFN(1, "immed\n"); 43194773907SVladimir Kondratyev 43294773907SVladimir Kondratyev error = hid_get_report(sc->sc_dev, sc->sc_q, 43394773907SVladimir Kondratyev sc->sc_rdesc->isize, NULL, HID_INPUT_REPORT, 43494773907SVladimir Kondratyev sc->sc_rdesc->iid); 43594773907SVladimir Kondratyev if (error == 0) 43694773907SVladimir Kondratyev error = uiomove(sc->sc_q, sc->sc_rdesc->isize, uio); 43794773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 43894773907SVladimir Kondratyev goto exit; 43994773907SVladimir Kondratyev } 44094773907SVladimir Kondratyev 44194773907SVladimir Kondratyev while (sc->sc_tail == sc->sc_head && !sc->sc_state.flush) { 44294773907SVladimir Kondratyev if (flag & O_NONBLOCK) { 44394773907SVladimir Kondratyev error = EWOULDBLOCK; 44494773907SVladimir Kondratyev goto exit; 44594773907SVladimir Kondratyev } 44694773907SVladimir Kondratyev sc->sc_state.aslp = true; 44794773907SVladimir Kondratyev DPRINTFN(5, "sleep on %p\n", &sc->sc_q); 44894773907SVladimir Kondratyev error = mtx_sleep(&sc->sc_q, &sc->sc_mtx, PZERO | PCATCH, 44994773907SVladimir Kondratyev "hidrawrd", 0); 45094773907SVladimir Kondratyev DPRINTFN(5, "woke, error=%d\n", error); 45194773907SVladimir Kondratyev if (dev->si_drv1 == NULL) 45294773907SVladimir Kondratyev error = EIO; 45394773907SVladimir Kondratyev if (error) { 45494773907SVladimir Kondratyev sc->sc_state.aslp = false; 45594773907SVladimir Kondratyev goto exit; 45694773907SVladimir Kondratyev } 45794773907SVladimir Kondratyev } 45894773907SVladimir Kondratyev 45994773907SVladimir Kondratyev while (sc->sc_tail != sc->sc_head && uio->uio_resid > 0) { 46094773907SVladimir Kondratyev length = min(uio->uio_resid, sc->sc_state.uhid ? 46194773907SVladimir Kondratyev sc->sc_rdesc->isize : sc->sc_qlen[sc->sc_head]); 46294773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 46394773907SVladimir Kondratyev 46494773907SVladimir Kondratyev /* Copy the data to the user process. */ 46594773907SVladimir Kondratyev DPRINTFN(5, "got %lu chars\n", (u_long)length); 46694773907SVladimir Kondratyev error = uiomove(sc->sc_q + sc->sc_head * sc->sc_rdesc->rdsize, 46794773907SVladimir Kondratyev length, uio); 46894773907SVladimir Kondratyev 46994773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 47094773907SVladimir Kondratyev if (error != 0) 47194773907SVladimir Kondratyev goto exit; 47294773907SVladimir Kondratyev /* Remove a small chunk from the input queue. */ 47394773907SVladimir Kondratyev sc->sc_head = (sc->sc_head + 1) % HIDRAW_BUFFER_SIZE; 47494773907SVladimir Kondratyev /* 47594773907SVladimir Kondratyev * In uhid mode transfer as many chunks as possible. Hidraw 47694773907SVladimir Kondratyev * packets are transferred one by one due to different length. 47794773907SVladimir Kondratyev */ 47894773907SVladimir Kondratyev if (!sc->sc_state.uhid) 47994773907SVladimir Kondratyev goto exit; 48094773907SVladimir Kondratyev } 48194773907SVladimir Kondratyev exit: 48294773907SVladimir Kondratyev hidraw_unlock_queue(sc); 48394773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 48494773907SVladimir Kondratyev 48594773907SVladimir Kondratyev return (error); 48694773907SVladimir Kondratyev } 48794773907SVladimir Kondratyev 48894773907SVladimir Kondratyev static int 48994773907SVladimir Kondratyev hidraw_write(struct cdev *dev, struct uio *uio, int flag) 49094773907SVladimir Kondratyev { 49194773907SVladimir Kondratyev uint8_t local_buf[HIDRAW_LOCAL_BUFSIZE], *buf; 49294773907SVladimir Kondratyev struct hidraw_softc *sc; 49394773907SVladimir Kondratyev int error; 49494773907SVladimir Kondratyev int size; 49594773907SVladimir Kondratyev size_t buf_offset; 49694773907SVladimir Kondratyev uint8_t id = 0; 49794773907SVladimir Kondratyev 49894773907SVladimir Kondratyev DPRINTFN(1, "\n"); 49994773907SVladimir Kondratyev 50094773907SVladimir Kondratyev sc = dev->si_drv1; 50194773907SVladimir Kondratyev if (sc == NULL) 50294773907SVladimir Kondratyev return (EIO); 50394773907SVladimir Kondratyev 50494773907SVladimir Kondratyev if (sc->sc_rdesc->osize == 0) 50594773907SVladimir Kondratyev return (EOPNOTSUPP); 50694773907SVladimir Kondratyev 50794773907SVladimir Kondratyev buf_offset = 0; 50894773907SVladimir Kondratyev if (sc->sc_state.uhid) { 50994773907SVladimir Kondratyev size = sc->sc_rdesc->osize; 51094773907SVladimir Kondratyev if (uio->uio_resid != size) 51194773907SVladimir Kondratyev return (EINVAL); 51294773907SVladimir Kondratyev } else { 51394773907SVladimir Kondratyev size = uio->uio_resid; 51494773907SVladimir Kondratyev if (size < 2) 51594773907SVladimir Kondratyev return (EINVAL); 51694773907SVladimir Kondratyev /* Strip leading 0 if the device doesnt use numbered reports */ 51794773907SVladimir Kondratyev error = uiomove(&id, 1, uio); 51894773907SVladimir Kondratyev if (error) 51994773907SVladimir Kondratyev return (error); 52094773907SVladimir Kondratyev if (id != 0) 52194773907SVladimir Kondratyev buf_offset++; 52294773907SVladimir Kondratyev else 52394773907SVladimir Kondratyev size--; 52494773907SVladimir Kondratyev /* Check if underlying driver could process this request */ 52594773907SVladimir Kondratyev if (size > sc->sc_rdesc->wrsize) 52694773907SVladimir Kondratyev return (ENOBUFS); 52794773907SVladimir Kondratyev } 52894773907SVladimir Kondratyev buf = HIDRAW_LOCAL_ALLOC(local_buf, size); 52994773907SVladimir Kondratyev buf[0] = id; 53094773907SVladimir Kondratyev error = uiomove(buf + buf_offset, uio->uio_resid, uio); 53194773907SVladimir Kondratyev if (error == 0) 53294773907SVladimir Kondratyev error = hid_write(sc->sc_dev, buf, size); 53394773907SVladimir Kondratyev HIDRAW_LOCAL_FREE(local_buf, buf); 53494773907SVladimir Kondratyev 53594773907SVladimir Kondratyev return (error); 53694773907SVladimir Kondratyev } 53794773907SVladimir Kondratyev 53845b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32 53945b48cbcSBrooks Davis static void 54045b48cbcSBrooks Davis update_hgd32(const struct hidraw_gen_descriptor *hgd, 54145b48cbcSBrooks Davis struct hidraw_gen_descriptor32 *hgd32) 54245b48cbcSBrooks Davis { 54345b48cbcSBrooks Davis /* Don't update hgd_data pointer */ 54445b48cbcSBrooks Davis CP(*hgd, *hgd32, hgd_lang_id); 54545b48cbcSBrooks Davis CP(*hgd, *hgd32, hgd_maxlen); 54645b48cbcSBrooks Davis CP(*hgd, *hgd32, hgd_actlen); 54745b48cbcSBrooks Davis CP(*hgd, *hgd32, hgd_offset); 54845b48cbcSBrooks Davis CP(*hgd, *hgd32, hgd_config_index); 54945b48cbcSBrooks Davis CP(*hgd, *hgd32, hgd_string_index); 55045b48cbcSBrooks Davis CP(*hgd, *hgd32, hgd_iface_index); 55145b48cbcSBrooks Davis CP(*hgd, *hgd32, hgd_altif_index); 55245b48cbcSBrooks Davis CP(*hgd, *hgd32, hgd_endpt_index); 55345b48cbcSBrooks Davis CP(*hgd, *hgd32, hgd_report_type); 55445b48cbcSBrooks Davis /* Don't update reserved */ 55545b48cbcSBrooks Davis } 55645b48cbcSBrooks Davis #endif 55745b48cbcSBrooks Davis 55894773907SVladimir Kondratyev static int 55994773907SVladimir Kondratyev hidraw_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, 56094773907SVladimir Kondratyev struct thread *td) 56194773907SVladimir Kondratyev { 56294773907SVladimir Kondratyev uint8_t local_buf[HIDRAW_LOCAL_BUFSIZE]; 56345b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32 56445b48cbcSBrooks Davis struct hidraw_gen_descriptor local_hgd; 56545b48cbcSBrooks Davis struct hidraw_gen_descriptor32 *hgd32 = NULL; 56645b48cbcSBrooks Davis #endif 56794773907SVladimir Kondratyev void *buf; 56894773907SVladimir Kondratyev struct hidraw_softc *sc; 56994773907SVladimir Kondratyev struct hidraw_gen_descriptor *hgd; 57094773907SVladimir Kondratyev struct hidraw_report_descriptor *hrd; 57194773907SVladimir Kondratyev struct hidraw_devinfo *hdi; 57294773907SVladimir Kondratyev uint32_t size; 57394773907SVladimir Kondratyev int id, len; 57494773907SVladimir Kondratyev int error = 0; 57594773907SVladimir Kondratyev 57694773907SVladimir Kondratyev DPRINTFN(2, "cmd=%lx\n", cmd); 57794773907SVladimir Kondratyev 57894773907SVladimir Kondratyev sc = dev->si_drv1; 57994773907SVladimir Kondratyev if (sc == NULL) 58094773907SVladimir Kondratyev return (EIO); 58194773907SVladimir Kondratyev 58245b48cbcSBrooks Davis hgd = (struct hidraw_gen_descriptor *)addr; 583*34077a8eSHans Petter Selasky 584*34077a8eSHans Petter Selasky #ifdef COMPAT_FREEBSD32 58545b48cbcSBrooks Davis switch (cmd) { 58645b48cbcSBrooks Davis case HIDRAW_GET_REPORT_DESC32: 58745b48cbcSBrooks Davis case HIDRAW_GET_REPORT32: 58845b48cbcSBrooks Davis case HIDRAW_SET_REPORT_DESC32: 58945b48cbcSBrooks Davis case HIDRAW_SET_REPORT32: 59045b48cbcSBrooks Davis cmd = _IOC_NEWTYPE(cmd, struct hidraw_gen_descriptor); 59145b48cbcSBrooks Davis hgd32 = (struct hidraw_gen_descriptor32 *)addr; 59245b48cbcSBrooks Davis hgd = &local_hgd; 59345b48cbcSBrooks Davis PTRIN_CP(*hgd32, *hgd, hgd_data); 59445b48cbcSBrooks Davis CP(*hgd32, *hgd, hgd_lang_id); 59545b48cbcSBrooks Davis CP(*hgd32, *hgd, hgd_maxlen); 59645b48cbcSBrooks Davis CP(*hgd32, *hgd, hgd_actlen); 59745b48cbcSBrooks Davis CP(*hgd32, *hgd, hgd_offset); 59845b48cbcSBrooks Davis CP(*hgd32, *hgd, hgd_config_index); 59945b48cbcSBrooks Davis CP(*hgd32, *hgd, hgd_string_index); 60045b48cbcSBrooks Davis CP(*hgd32, *hgd, hgd_iface_index); 60145b48cbcSBrooks Davis CP(*hgd32, *hgd, hgd_altif_index); 60245b48cbcSBrooks Davis CP(*hgd32, *hgd, hgd_endpt_index); 60345b48cbcSBrooks Davis CP(*hgd32, *hgd, hgd_report_type); 60445b48cbcSBrooks Davis /* Don't copy reserved */ 60545b48cbcSBrooks Davis break; 60645b48cbcSBrooks Davis } 60745b48cbcSBrooks Davis #endif 60845b48cbcSBrooks Davis 60994773907SVladimir Kondratyev /* fixed-length ioctls handling */ 61094773907SVladimir Kondratyev switch (cmd) { 61194773907SVladimir Kondratyev case FIONBIO: 61294773907SVladimir Kondratyev /* All handled in the upper FS layer. */ 61394773907SVladimir Kondratyev return (0); 61494773907SVladimir Kondratyev 61594773907SVladimir Kondratyev case FIOASYNC: 61694773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 61794773907SVladimir Kondratyev if (*(int *)addr) { 61894773907SVladimir Kondratyev if (sc->sc_async == NULL) { 61994773907SVladimir Kondratyev sc->sc_async = td->td_proc; 62094773907SVladimir Kondratyev DPRINTF("FIOASYNC %p\n", sc->sc_async); 62194773907SVladimir Kondratyev } else 62294773907SVladimir Kondratyev error = EBUSY; 62394773907SVladimir Kondratyev } else 62494773907SVladimir Kondratyev sc->sc_async = NULL; 62594773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 62694773907SVladimir Kondratyev return (error); 62794773907SVladimir Kondratyev 62894773907SVladimir Kondratyev /* XXX this is not the most general solution. */ 62994773907SVladimir Kondratyev case TIOCSPGRP: 63094773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 63194773907SVladimir Kondratyev if (sc->sc_async == NULL) 63294773907SVladimir Kondratyev error = EINVAL; 63394773907SVladimir Kondratyev else if (*(int *)addr != sc->sc_async->p_pgid) 63494773907SVladimir Kondratyev error = EPERM; 63594773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 63694773907SVladimir Kondratyev return (error); 63794773907SVladimir Kondratyev 63894773907SVladimir Kondratyev case HIDRAW_GET_REPORT_DESC: 63994773907SVladimir Kondratyev if (sc->sc_rdesc->data == NULL || sc->sc_rdesc->len == 0) 64094773907SVladimir Kondratyev return (EOPNOTSUPP); 64194773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 64294773907SVladimir Kondratyev sc->sc_state.uhid = true; 64394773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 64494773907SVladimir Kondratyev if (sc->sc_rdesc->len > hgd->hgd_maxlen) { 64594773907SVladimir Kondratyev size = hgd->hgd_maxlen; 64694773907SVladimir Kondratyev } else { 64794773907SVladimir Kondratyev size = sc->sc_rdesc->len; 64894773907SVladimir Kondratyev } 64994773907SVladimir Kondratyev hgd->hgd_actlen = size; 65045b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32 65145b48cbcSBrooks Davis if (hgd32 != NULL) 65245b48cbcSBrooks Davis update_hgd32(hgd, hgd32); 65345b48cbcSBrooks Davis #endif 65494773907SVladimir Kondratyev if (hgd->hgd_data == NULL) 65594773907SVladimir Kondratyev return (0); /* descriptor length only */ 65645b48cbcSBrooks Davis 65794773907SVladimir Kondratyev return (copyout(sc->sc_rdesc->data, hgd->hgd_data, size)); 65894773907SVladimir Kondratyev 65994773907SVladimir Kondratyev 66094773907SVladimir Kondratyev case HIDRAW_SET_REPORT_DESC: 66194773907SVladimir Kondratyev if (!(sc->sc_fflags & FWRITE)) 66294773907SVladimir Kondratyev return (EPERM); 66394773907SVladimir Kondratyev 66494773907SVladimir Kondratyev /* check privileges */ 66594773907SVladimir Kondratyev error = priv_check(curthread, PRIV_DRIVER); 66694773907SVladimir Kondratyev if (error) 66794773907SVladimir Kondratyev return (error); 66894773907SVladimir Kondratyev 66994773907SVladimir Kondratyev /* Stop interrupts and clear input report buffer */ 67094773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 67194773907SVladimir Kondratyev sc->sc_tail = sc->sc_head = 0; 67294773907SVladimir Kondratyev error = hidraw_lock_queue(sc, true); 67394773907SVladimir Kondratyev if (error == 0) 67494773907SVladimir Kondratyev sc->sc_state.quiet = true; 67594773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 67694773907SVladimir Kondratyev if (error != 0) 67794773907SVladimir Kondratyev return(error); 67894773907SVladimir Kondratyev 67994773907SVladimir Kondratyev buf = HIDRAW_LOCAL_ALLOC(local_buf, hgd->hgd_maxlen); 68094773907SVladimir Kondratyev copyin(hgd->hgd_data, buf, hgd->hgd_maxlen); 68194773907SVladimir Kondratyev /* Lock newbus around set_report_descr call */ 68294773907SVladimir Kondratyev mtx_lock(&Giant); 68394773907SVladimir Kondratyev error = hid_set_report_descr(sc->sc_dev, buf, hgd->hgd_maxlen); 68494773907SVladimir Kondratyev mtx_unlock(&Giant); 68594773907SVladimir Kondratyev HIDRAW_LOCAL_FREE(local_buf, buf); 68694773907SVladimir Kondratyev 68794773907SVladimir Kondratyev /* Realloc hidraw input queue */ 68894773907SVladimir Kondratyev if (error == 0) 68994773907SVladimir Kondratyev sc->sc_q = realloc(sc->sc_q, 69094773907SVladimir Kondratyev sc->sc_rdesc->rdsize * HIDRAW_BUFFER_SIZE, 69194773907SVladimir Kondratyev M_DEVBUF, M_ZERO | M_WAITOK); 69294773907SVladimir Kondratyev 69394773907SVladimir Kondratyev /* Start interrupts again */ 69494773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 69594773907SVladimir Kondratyev sc->sc_state.quiet = false; 69694773907SVladimir Kondratyev hidraw_unlock_queue(sc); 69794773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 69894773907SVladimir Kondratyev return (error); 69994773907SVladimir Kondratyev case HIDRAW_SET_IMMED: 70094773907SVladimir Kondratyev if (!(sc->sc_fflags & FREAD)) 70194773907SVladimir Kondratyev return (EPERM); 70294773907SVladimir Kondratyev if (*(int *)addr) { 70394773907SVladimir Kondratyev /* XXX should read into ibuf, but does it matter? */ 70494773907SVladimir Kondratyev size = sc->sc_rdesc->isize; 70594773907SVladimir Kondratyev buf = HIDRAW_LOCAL_ALLOC(local_buf, size); 70694773907SVladimir Kondratyev error = hid_get_report(sc->sc_dev, buf, size, NULL, 70794773907SVladimir Kondratyev HID_INPUT_REPORT, sc->sc_rdesc->iid); 70894773907SVladimir Kondratyev HIDRAW_LOCAL_FREE(local_buf, buf); 70994773907SVladimir Kondratyev if (error) 71094773907SVladimir Kondratyev return (EOPNOTSUPP); 71194773907SVladimir Kondratyev 71294773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 71394773907SVladimir Kondratyev sc->sc_state.immed = true; 71494773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 71594773907SVladimir Kondratyev } else { 71694773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 71794773907SVladimir Kondratyev sc->sc_state.immed = false; 71894773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 71994773907SVladimir Kondratyev } 72094773907SVladimir Kondratyev return (0); 72194773907SVladimir Kondratyev 72294773907SVladimir Kondratyev case HIDRAW_GET_REPORT: 72394773907SVladimir Kondratyev if (!(sc->sc_fflags & FREAD)) 72494773907SVladimir Kondratyev return (EPERM); 72594773907SVladimir Kondratyev switch (hgd->hgd_report_type) { 72694773907SVladimir Kondratyev case HID_INPUT_REPORT: 72794773907SVladimir Kondratyev size = sc->sc_rdesc->isize; 72894773907SVladimir Kondratyev id = sc->sc_rdesc->iid; 72994773907SVladimir Kondratyev break; 73094773907SVladimir Kondratyev case HID_OUTPUT_REPORT: 73194773907SVladimir Kondratyev size = sc->sc_rdesc->osize; 73294773907SVladimir Kondratyev id = sc->sc_rdesc->oid; 73394773907SVladimir Kondratyev break; 73494773907SVladimir Kondratyev case HID_FEATURE_REPORT: 73594773907SVladimir Kondratyev size = sc->sc_rdesc->fsize; 73694773907SVladimir Kondratyev id = sc->sc_rdesc->fid; 73794773907SVladimir Kondratyev break; 73894773907SVladimir Kondratyev default: 73994773907SVladimir Kondratyev return (EINVAL); 74094773907SVladimir Kondratyev } 74194773907SVladimir Kondratyev if (id != 0) 74294773907SVladimir Kondratyev copyin(hgd->hgd_data, &id, 1); 74394773907SVladimir Kondratyev size = MIN(hgd->hgd_maxlen, size); 74494773907SVladimir Kondratyev buf = HIDRAW_LOCAL_ALLOC(local_buf, size); 74594773907SVladimir Kondratyev error = hid_get_report(sc->sc_dev, buf, size, NULL, 74694773907SVladimir Kondratyev hgd->hgd_report_type, id); 74794773907SVladimir Kondratyev if (!error) 74894773907SVladimir Kondratyev error = copyout(buf, hgd->hgd_data, size); 74994773907SVladimir Kondratyev HIDRAW_LOCAL_FREE(local_buf, buf); 75045b48cbcSBrooks Davis #ifdef COMPAT_FREEBSD32 75145b48cbcSBrooks Davis /* 75245b48cbcSBrooks Davis * HIDRAW_GET_REPORT is declared _IOWR, but hgd is not written 75345b48cbcSBrooks Davis * so we don't call update_hgd32(). 75445b48cbcSBrooks Davis */ 75545b48cbcSBrooks Davis #endif 75694773907SVladimir Kondratyev return (error); 75794773907SVladimir Kondratyev 75894773907SVladimir Kondratyev case HIDRAW_SET_REPORT: 75994773907SVladimir Kondratyev if (!(sc->sc_fflags & FWRITE)) 76094773907SVladimir Kondratyev return (EPERM); 76194773907SVladimir Kondratyev switch (hgd->hgd_report_type) { 76294773907SVladimir Kondratyev case HID_INPUT_REPORT: 76394773907SVladimir Kondratyev size = sc->sc_rdesc->isize; 76494773907SVladimir Kondratyev id = sc->sc_rdesc->iid; 76594773907SVladimir Kondratyev break; 76694773907SVladimir Kondratyev case HID_OUTPUT_REPORT: 76794773907SVladimir Kondratyev size = sc->sc_rdesc->osize; 76894773907SVladimir Kondratyev id = sc->sc_rdesc->oid; 76994773907SVladimir Kondratyev break; 77094773907SVladimir Kondratyev case HID_FEATURE_REPORT: 77194773907SVladimir Kondratyev size = sc->sc_rdesc->fsize; 77294773907SVladimir Kondratyev id = sc->sc_rdesc->fid; 77394773907SVladimir Kondratyev break; 77494773907SVladimir Kondratyev default: 77594773907SVladimir Kondratyev return (EINVAL); 77694773907SVladimir Kondratyev } 77794773907SVladimir Kondratyev size = MIN(hgd->hgd_maxlen, size); 77894773907SVladimir Kondratyev buf = HIDRAW_LOCAL_ALLOC(local_buf, size); 77994773907SVladimir Kondratyev copyin(hgd->hgd_data, buf, size); 78094773907SVladimir Kondratyev if (id != 0) 78194773907SVladimir Kondratyev id = *(uint8_t *)buf; 78294773907SVladimir Kondratyev error = hid_set_report(sc->sc_dev, buf, size, 78394773907SVladimir Kondratyev hgd->hgd_report_type, id); 78494773907SVladimir Kondratyev HIDRAW_LOCAL_FREE(local_buf, buf); 78594773907SVladimir Kondratyev return (error); 78694773907SVladimir Kondratyev 78794773907SVladimir Kondratyev case HIDRAW_GET_REPORT_ID: 78894773907SVladimir Kondratyev *(int *)addr = 0; /* XXX: we only support reportid 0? */ 78994773907SVladimir Kondratyev return (0); 79094773907SVladimir Kondratyev 79194773907SVladimir Kondratyev case HIDIOCGRDESCSIZE: 792f988d7faSVladimir Kondratyev *(int *)addr = sc->sc_hw->rdescsize; 79394773907SVladimir Kondratyev return (0); 79494773907SVladimir Kondratyev 79594773907SVladimir Kondratyev case HIDIOCGRDESC: 79694773907SVladimir Kondratyev hrd = *(struct hidraw_report_descriptor **)addr; 79794773907SVladimir Kondratyev error = copyin(&hrd->size, &size, sizeof(uint32_t)); 79894773907SVladimir Kondratyev if (error) 79994773907SVladimir Kondratyev return (error); 80094773907SVladimir Kondratyev /* 80194773907SVladimir Kondratyev * HID_MAX_DESCRIPTOR_SIZE-1 is a limit of report descriptor 80294773907SVladimir Kondratyev * size in current Linux implementation. 80394773907SVladimir Kondratyev */ 80494773907SVladimir Kondratyev if (size >= HID_MAX_DESCRIPTOR_SIZE) 80594773907SVladimir Kondratyev return (EINVAL); 80694773907SVladimir Kondratyev buf = HIDRAW_LOCAL_ALLOC(local_buf, size); 80794773907SVladimir Kondratyev error = hid_get_rdesc(sc->sc_dev, buf, size); 80894773907SVladimir Kondratyev if (error == 0) { 80994773907SVladimir Kondratyev size = MIN(size, sc->sc_rdesc->len); 81094773907SVladimir Kondratyev error = copyout(buf, hrd->value, size); 81194773907SVladimir Kondratyev } 81294773907SVladimir Kondratyev HIDRAW_LOCAL_FREE(local_buf, buf); 81394773907SVladimir Kondratyev return (error); 81494773907SVladimir Kondratyev 81594773907SVladimir Kondratyev case HIDIOCGRAWINFO: 81694773907SVladimir Kondratyev hdi = (struct hidraw_devinfo *)addr; 81794773907SVladimir Kondratyev hdi->bustype = sc->sc_hw->idBus; 81894773907SVladimir Kondratyev hdi->vendor = sc->sc_hw->idVendor; 81994773907SVladimir Kondratyev hdi->product = sc->sc_hw->idProduct; 82094773907SVladimir Kondratyev return (0); 82194773907SVladimir Kondratyev } 82294773907SVladimir Kondratyev 82394773907SVladimir Kondratyev /* variable-length ioctls handling */ 82494773907SVladimir Kondratyev len = IOCPARM_LEN(cmd); 82594773907SVladimir Kondratyev switch (IOCBASECMD(cmd)) { 82694773907SVladimir Kondratyev case HIDIOCGRAWNAME(0): 82794773907SVladimir Kondratyev strlcpy(addr, sc->sc_hw->name, len); 82894773907SVladimir Kondratyev return (0); 82994773907SVladimir Kondratyev 83094773907SVladimir Kondratyev case HIDIOCGRAWPHYS(0): 83194773907SVladimir Kondratyev strlcpy(addr, device_get_nameunit(sc->sc_dev), len); 83294773907SVladimir Kondratyev return (0); 83394773907SVladimir Kondratyev 83494773907SVladimir Kondratyev case HIDIOCSFEATURE(0): 83594773907SVladimir Kondratyev if (!(sc->sc_fflags & FWRITE)) 83694773907SVladimir Kondratyev return (EPERM); 83794773907SVladimir Kondratyev if (len < 2) 83894773907SVladimir Kondratyev return (EINVAL); 83994773907SVladimir Kondratyev id = *(uint8_t *)addr; 84094773907SVladimir Kondratyev if (id == 0) { 84194773907SVladimir Kondratyev addr = (uint8_t *)addr + 1; 84294773907SVladimir Kondratyev len--; 84394773907SVladimir Kondratyev } 84494773907SVladimir Kondratyev return (hid_set_report(sc->sc_dev, addr, len, 84594773907SVladimir Kondratyev HID_FEATURE_REPORT, id)); 84694773907SVladimir Kondratyev 84794773907SVladimir Kondratyev case HIDIOCGFEATURE(0): 84894773907SVladimir Kondratyev if (!(sc->sc_fflags & FREAD)) 84994773907SVladimir Kondratyev return (EPERM); 85094773907SVladimir Kondratyev if (len < 2) 85194773907SVladimir Kondratyev return (EINVAL); 85294773907SVladimir Kondratyev id = *(uint8_t *)addr; 85394773907SVladimir Kondratyev if (id == 0) { 85494773907SVladimir Kondratyev addr = (uint8_t *)addr + 1; 85594773907SVladimir Kondratyev len--; 85694773907SVladimir Kondratyev } 85794773907SVladimir Kondratyev return (hid_get_report(sc->sc_dev, addr, len, NULL, 85894773907SVladimir Kondratyev HID_FEATURE_REPORT, id)); 85994773907SVladimir Kondratyev 86094773907SVladimir Kondratyev case HIDIOCGRAWUNIQ(0): 86194773907SVladimir Kondratyev strlcpy(addr, sc->sc_hw->serial, len); 86294773907SVladimir Kondratyev return (0); 86394773907SVladimir Kondratyev } 86494773907SVladimir Kondratyev 86594773907SVladimir Kondratyev return (EINVAL); 86694773907SVladimir Kondratyev } 86794773907SVladimir Kondratyev 86894773907SVladimir Kondratyev static int 86994773907SVladimir Kondratyev hidraw_poll(struct cdev *dev, int events, struct thread *td) 87094773907SVladimir Kondratyev { 87194773907SVladimir Kondratyev struct hidraw_softc *sc; 87294773907SVladimir Kondratyev int revents = 0; 87394773907SVladimir Kondratyev 87494773907SVladimir Kondratyev sc = dev->si_drv1; 87594773907SVladimir Kondratyev if (sc == NULL) 87694773907SVladimir Kondratyev return (POLLHUP); 87794773907SVladimir Kondratyev 87894773907SVladimir Kondratyev if (events & (POLLOUT | POLLWRNORM) && (sc->sc_fflags & FWRITE)) 87994773907SVladimir Kondratyev revents |= events & (POLLOUT | POLLWRNORM); 88094773907SVladimir Kondratyev if (events & (POLLIN | POLLRDNORM) && (sc->sc_fflags & FREAD)) { 88194773907SVladimir Kondratyev mtx_lock(&sc->sc_mtx); 88294773907SVladimir Kondratyev if (sc->sc_head != sc->sc_tail) 88394773907SVladimir Kondratyev revents |= events & (POLLIN | POLLRDNORM); 88494773907SVladimir Kondratyev else { 88594773907SVladimir Kondratyev sc->sc_state.sel = true; 88694773907SVladimir Kondratyev selrecord(td, &sc->sc_rsel); 88794773907SVladimir Kondratyev } 88894773907SVladimir Kondratyev mtx_unlock(&sc->sc_mtx); 88994773907SVladimir Kondratyev } 89094773907SVladimir Kondratyev 89194773907SVladimir Kondratyev return (revents); 89294773907SVladimir Kondratyev } 89394773907SVladimir Kondratyev 89494773907SVladimir Kondratyev static int 89594773907SVladimir Kondratyev hidraw_kqfilter(struct cdev *dev, struct knote *kn) 89694773907SVladimir Kondratyev { 89794773907SVladimir Kondratyev struct hidraw_softc *sc; 89894773907SVladimir Kondratyev 89994773907SVladimir Kondratyev sc = dev->si_drv1; 90094773907SVladimir Kondratyev if (sc == NULL) 90194773907SVladimir Kondratyev return (ENXIO); 90294773907SVladimir Kondratyev 90394773907SVladimir Kondratyev switch(kn->kn_filter) { 90494773907SVladimir Kondratyev case EVFILT_READ: 90594773907SVladimir Kondratyev if (sc->sc_fflags & FREAD) { 90694773907SVladimir Kondratyev kn->kn_fop = &hidraw_filterops_read; 90794773907SVladimir Kondratyev break; 90894773907SVladimir Kondratyev } 90994773907SVladimir Kondratyev /* FALLTHROUGH */ 91094773907SVladimir Kondratyev default: 91194773907SVladimir Kondratyev return(EINVAL); 91294773907SVladimir Kondratyev } 91394773907SVladimir Kondratyev kn->kn_hook = sc; 91494773907SVladimir Kondratyev 91594773907SVladimir Kondratyev knlist_add(&sc->sc_rsel.si_note, kn, 0); 91694773907SVladimir Kondratyev return (0); 91794773907SVladimir Kondratyev } 91894773907SVladimir Kondratyev 91994773907SVladimir Kondratyev static int 92094773907SVladimir Kondratyev hidraw_kqread(struct knote *kn, long hint) 92194773907SVladimir Kondratyev { 92294773907SVladimir Kondratyev struct hidraw_softc *sc; 92394773907SVladimir Kondratyev int ret; 92494773907SVladimir Kondratyev 92594773907SVladimir Kondratyev sc = kn->kn_hook; 92694773907SVladimir Kondratyev 92794773907SVladimir Kondratyev mtx_assert(&sc->sc_mtx, MA_OWNED); 92894773907SVladimir Kondratyev 92994773907SVladimir Kondratyev if (sc->dev->si_drv1 == NULL) { 93094773907SVladimir Kondratyev kn->kn_flags |= EV_EOF; 93194773907SVladimir Kondratyev ret = 1; 93294773907SVladimir Kondratyev } else 93394773907SVladimir Kondratyev ret = (sc->sc_head != sc->sc_tail) ? 1 : 0; 93494773907SVladimir Kondratyev 93594773907SVladimir Kondratyev return (ret); 93694773907SVladimir Kondratyev } 93794773907SVladimir Kondratyev 93894773907SVladimir Kondratyev static void 93994773907SVladimir Kondratyev hidraw_kqdetach(struct knote *kn) 94094773907SVladimir Kondratyev { 94194773907SVladimir Kondratyev struct hidraw_softc *sc; 94294773907SVladimir Kondratyev 94394773907SVladimir Kondratyev sc = kn->kn_hook; 94494773907SVladimir Kondratyev knlist_remove(&sc->sc_rsel.si_note, kn, 0); 94594773907SVladimir Kondratyev } 94694773907SVladimir Kondratyev 94794773907SVladimir Kondratyev static void 94894773907SVladimir Kondratyev hidraw_notify(struct hidraw_softc *sc) 94994773907SVladimir Kondratyev { 95094773907SVladimir Kondratyev 95194773907SVladimir Kondratyev mtx_assert(&sc->sc_mtx, MA_OWNED); 95294773907SVladimir Kondratyev 95394773907SVladimir Kondratyev if (sc->sc_state.aslp) { 95494773907SVladimir Kondratyev sc->sc_state.aslp = false; 95594773907SVladimir Kondratyev DPRINTFN(5, "waking %p\n", &sc->sc_q); 95694773907SVladimir Kondratyev wakeup(&sc->sc_q); 95794773907SVladimir Kondratyev } 95894773907SVladimir Kondratyev if (sc->sc_state.sel) { 95994773907SVladimir Kondratyev sc->sc_state.sel = false; 96094773907SVladimir Kondratyev selwakeuppri(&sc->sc_rsel, PZERO); 96194773907SVladimir Kondratyev } 96294773907SVladimir Kondratyev if (sc->sc_async != NULL) { 96394773907SVladimir Kondratyev DPRINTFN(3, "sending SIGIO %p\n", sc->sc_async); 96494773907SVladimir Kondratyev PROC_LOCK(sc->sc_async); 96594773907SVladimir Kondratyev kern_psignal(sc->sc_async, SIGIO); 96694773907SVladimir Kondratyev PROC_UNLOCK(sc->sc_async); 96794773907SVladimir Kondratyev } 96894773907SVladimir Kondratyev KNOTE_LOCKED(&sc->sc_rsel.si_note, 0); 96994773907SVladimir Kondratyev } 97094773907SVladimir Kondratyev 97194773907SVladimir Kondratyev static device_method_t hidraw_methods[] = { 97294773907SVladimir Kondratyev /* Device interface */ 97394773907SVladimir Kondratyev DEVMETHOD(device_identify, hidraw_identify), 97494773907SVladimir Kondratyev DEVMETHOD(device_probe, hidraw_probe), 97594773907SVladimir Kondratyev DEVMETHOD(device_attach, hidraw_attach), 97694773907SVladimir Kondratyev DEVMETHOD(device_detach, hidraw_detach), 97794773907SVladimir Kondratyev 97894773907SVladimir Kondratyev DEVMETHOD_END 97994773907SVladimir Kondratyev }; 98094773907SVladimir Kondratyev 98194773907SVladimir Kondratyev static driver_t hidraw_driver = { 98294773907SVladimir Kondratyev "hidraw", 98394773907SVladimir Kondratyev hidraw_methods, 98494773907SVladimir Kondratyev sizeof(struct hidraw_softc) 98594773907SVladimir Kondratyev }; 98694773907SVladimir Kondratyev 9879be6b22dSVladimir Kondratyev #ifndef HIDRAW_MAKE_UHID_ALIAS 9889be6b22dSVladimir Kondratyev devclass_t hidraw_devclass; 9899be6b22dSVladimir Kondratyev #endif 99094773907SVladimir Kondratyev 99194773907SVladimir Kondratyev DRIVER_MODULE(hidraw, hidbus, hidraw_driver, hidraw_devclass, NULL, 0); 99294773907SVladimir Kondratyev MODULE_DEPEND(hidraw, hidbus, 1, 1, 1); 99394773907SVladimir Kondratyev MODULE_DEPEND(hidraw, hid, 1, 1, 1); 99494773907SVladimir Kondratyev MODULE_DEPEND(hidraw, usb, 1, 1, 1); 99594773907SVladimir Kondratyev MODULE_VERSION(hidraw, 1); 996