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
hidraw_identify(driver_t * driver,device_t parent)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
hidraw_probe(device_t self)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
hidraw_attach(device_t self)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
hidraw_detach(device_t self)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
hidraw_intr(void * context,void * buf,hid_size_t len)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
hidraw_lock_queue(struct hidraw_softc * sc,bool flush)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
hidraw_unlock_queue(struct hidraw_softc * sc)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
hidraw_open(struct cdev * dev,int flag,int mode,struct thread * td)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
hidraw_dtor(void * data)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
hidraw_read(struct cdev * dev,struct uio * uio,int flag)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
hidraw_write(struct cdev * dev,struct uio * uio,int flag)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
update_hgd32(const struct hidraw_gen_descriptor * hgd,struct hidraw_gen_descriptor32 * hgd32)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
hidraw_ioctl(struct cdev * dev,u_long cmd,caddr_t addr,int flag,struct thread * td)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
hidraw_poll(struct cdev * dev,int events,struct thread * td)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
hidraw_kqfilter(struct cdev * dev,struct knote * kn)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
hidraw_kqread(struct knote * kn,long hint)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
hidraw_kqdetach(struct knote * kn)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
hidraw_notify(struct hidraw_softc * sc)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