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