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