The branch main has been updated by wulf:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=947739079604f5f9fea92d783c9ef43dce52e462

commit 947739079604f5f9fea92d783c9ef43dce52e462
Author:     Vladimir Kondratyev <w...@freebsd.org>
AuthorDate: 2020-10-14 00:52:47 +0000
Commit:     Vladimir Kondratyev <w...@freebsd.org>
CommitDate: 2021-01-07 23:18:43 +0000

    hid: Import hidraw(4) - driver for access to raw HID device data
    
    This driver provides raw access to HID devices through uhid(4)-compatible
    interface and is based on pre-8.x uhid(4) code. Unlike uhid(4) it does
    not take devices in to monopoly ownership and allows parallel access
    from other drivers.
    
    hidraw supports Linux's hidraw-compatible interface as well.
    
    Reviewed by:    hselasky
    Differential revision:  https://reviews.freebsd.org/D27992
---
 include/Makefile                |   6 +-
 share/man/man4/Makefile         |   1 +
 share/man/man4/hidraw.4         | 308 ++++++++++++++
 sys/conf/files                  |   1 +
 sys/dev/hid/hidraw.c            | 902 ++++++++++++++++++++++++++++++++++++++++
 sys/dev/hid/hidraw.h            |  97 +++++
 sys/dev/usb/usb_ioctl.h         |   2 +-
 sys/modules/hid/Makefile        |   3 +-
 sys/modules/hid/hidraw/Makefile |   9 +
 9 files changed, 1325 insertions(+), 4 deletions(-)

diff --git a/include/Makefile b/include/Makefile
index 7e19f66edf71..59f62f1d4897 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -95,7 +95,8 @@ EVDEV=                input.h \
 EVDEVDIR=      ${INCLUDEDIR}/dev/evdev
 
 .PATH: ${SRCTOP}/sys/dev/hid
-HID=           hid.h
+HID=           hid.h \
+               hidraw.h
 HIDDIR=                ${INCLUDEDIR}/dev/hid
 
 .PATH: ${SRCTOP}/sys/dev/hyperv/include ${SRCTOP}/sys/dev/hyperv/utilities
@@ -340,7 +341,8 @@ symlinks: .PHONY .META
        ${INSTALL_SYMLINK} ${TAG_ARGS:D${TAG_ARGS},dev} \
            $$(printf '../../../../sys/dev/evdev/%s ' input.h 
input-event-codes.h uinput.h) \
            ${SDESTDIR}${INCLUDEDIR}/dev/evdev;
-       ${INSTALL_SYMLINK} ${TAG_ARGS:D${TAG_ARGS},dev} 
../../../../sys/dev/hid/hid.h \
+       ${INSTALL_SYMLINK} ${TAG_ARGS:D${TAG_ARGS},dev} \
+           $$(printf '../../../../sys/dev/hid/%s ' hid.h hidraw.h) \
            ${SDESTDIR}${INCLUDEDIR}/dev/hid; \
        ${INSTALL_SYMLINK} ${TAG_ARGS:D${TAG_ARGS},dev} 
../../../../sys/dev/hyperv/include/hyperv.h \
            ${SDESTDIR}${INCLUDEDIR}/dev/hyperv; \
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 4fd816804062..02d1bca75a3f 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -182,6 +182,7 @@ MAN=        aac.4 \
        hconf.4 \
        hidbus.4 \
        hidquirk.4 \
+       hidraw.4 \
        hifn.4 \
        hkbd.4 \
        hmt.4 \
diff --git a/share/man/man4/hidraw.4 b/share/man/man4/hidraw.4
new file mode 100644
index 000000000000..7012089e354b
--- /dev/null
+++ b/share/man/man4/hidraw.4
@@ -0,0 +1,308 @@
+.\" $NetBSD: uhid.4,v 1.13 2001/12/29 14:41:59 augustss Exp $
+.\"
+.\" Copyright (c) 1999, 2001 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Lennart Augustsson.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 1, 2018
+.Dt HIDRAW 4
+.Os
+.Sh NAME
+.Nm hidraw
+.Nd Raw Access to HID devices
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following line in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device hidraw"
+.Cd "device hid"
+.Cd "device hidbus"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+hidraw_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides a raw interface to Human Interface Devices (HIDs).
+The reports are sent to and received from the device unmodified.
+.Pp
+The device handles 2 sets of
+.Xr ioctl 2
+calls:
+.Pp
+.Fx
+.Xr uhid 4
+\-compatible calls:
+.Bl -tag -width indent
+.It Dv HID_GET_REPORT_ID Pq Vt int
+Get the report identifier used by this HID report.
+.It Dv HID_GET_REPORT_DESC Pq Vt "struct hidraw_gen_descriptor"
+Get the HID report descriptor.
+Copies a maximum of
+.Va hgd_maxlen
+bytes of the report descriptor data into the memory
+specified by
+.Va hgd_data .
+Upon return
+.Va hgd_actlen
+is set to the number of bytes copied.
+Using
+this descriptor the exact layout and meaning of data to/from
+the device can be found.
+The report descriptor is delivered
+without any processing.
+.Bd -literal
+struct hidraw_gen_descriptor {
+       void   *hgd_data;
+       uint16_t hgd_maxlen;
+       uint16_t hgd_actlen;
+       uint8_t hgd_report_type;
+       ...
+};
+.Ed
+.It Dv HID_SET_IMMED Pq Vt int
+Sets the device in a mode where each
+.Xr read 2
+will return the current value of the input report.
+Normally
+a
+.Xr read 2
+will only return the data that the device reports on its
+interrupt pipe.
+This call may fail if the device does not support
+this feature.
+.It Dv HID_GET_REPORT Pq Vt "struct hidraw_gen_descriptor"
+Get a report from the device without waiting for data on
+the interrupt pipe.
+Copies a maximum of
+.Va hgd_maxlen
+bytes of the report data into the memory specified by
+.Va hgd_data .
+Upon return
+.Va hgd_actlen
+is set to the number of bytes copied.
+The
+.Va hgd_report_type
+field indicates which report is requested.
+It should be
+.Dv HID_INPUT_REPORT ,
+.Dv HID_OUTPUT_REPORT ,
+or
+.Dv HID_FEATURE_REPORT .
+On a device which uses numbered reports, the first byte of the returned data
+is the report number.
+The report data begins from the second byte.
+For devices which do not use numbered reports, the report data begins at the
+first byte.
+This call may fail if the device does not support this feature.
+.It Dv HID_SET_REPORT Pq Vt "struct hidraw_gen_descriptor"
+Set a report in the device.
+The
+.Va hgd_report_type
+field indicates which report is to be set.
+It should be
+.Dv HID_INPUT_REPORT ,
+.Dv HID_OUTPUT_REPORT ,
+or
+.Dv HID_FEATURE_REPORT .
+The value of the report is specified by the
+.Va hgd_data
+and the
+.Va hgd_maxlen
+fields.
+On a device which uses numbered reports, the first byte of data to be sent is
+the report number.
+The report data begins from the second byte.
+For devices which do not use numbered reports, the report data begins at the
+first byte.
+This call may fail if the device does not support this feature.
+.El
+.Pp
+Linux
+.Nm
+\-compatible calls:
+.Bl -tag -width indent
+.It Dv HIDIOCGRDESCSIZE Pq Vt int
+Get the HID report descriptor size.
+Returns the size of the device report descriptor to use with
+.Dv HIDIOCGRDESC
+.Xr ioctl 2 .
+.It Dv HIDIOCGRDESC Pq Vt "struct hidraw_report_descriptor"
+Get the HID report descriptor.
+Copies a maximum of
+.Va size
+bytes of the report descriptor data into the memory
+specified by
+.Va value .
+.Bd -literal
+struct hidraw_report_descriptor {
+       uint32_t        size;
+       uint8_t         value[HID_MAX_DESCRIPTOR_SIZE];
+};
+.Ed
+.It Dv HIDIOCGRAWINFO Pq Vt "struct hidraw_devinfo"
+Get structure containing the bus type, the vendor ID (VID), and product ID
+(PID) of the device. The bus type can be one of:
+.Dv BUS_USB
+or
+.Dv BUS_I2C
+which are defined in dev/evdev/input.h.
+.Bd -literal
+struct hidraw_devinfo {
+       uint32_t        bustype;
+       int16_t         vendor;
+       int16_t         product;
+};
+.Ed
+.It Dv HIDIOCGRAWNAME(len) Pq Vt "char[] buf"
+Get device description.
+Copies a maximum of
+.Va len
+bytes of the device description previously set with
+.Xr device_set_desc 9
+procedure into the memory
+specified by
+.Va buf .
+.It Dv HIDIOCGRAWPHYS(len) Pq Vt "char[] buf"
+Get the newbus path to the device.
+.\For Bluetooth devices, it returns the hardware (MAC) address of the device.
+Copies a maximum of
+.Va len
+bytes of the newbus device path
+into the memory
+specified by
+.Va buf .
+.It Dv HIDIOCGFEATURE(len) Pq Vt "void[] buf"
+Get a feature report from the device.
+Copies a maximum of
+.Va len
+bytes of the feature report data into the memory specified by
+.Va buf .
+The first byte of the supplied buffer should be set to the report
+number of the requested report.
+For devices which do not use numbered reports, set the first byte to 0.
+The report will be returned starting at the first byte of the buffer
+(ie: the report number is not returned).
+This call may fail if the device does not support this feature.
+.It Dv HIDIOCSFEATURE(len) Pq Vt "void[] buf"
+Set a feature Report in the device.
+The value of the report is specified by the
+.Va buf
+and the
+.Va len
+parameters.
+Set the first byte of the supplied buffer to the report number.
+For devices which do not use numbered reports, set the first byte to 0.
+The report data begins in the second byte.
+Make sure to set len accordingly, to one more than the length of the report
+(to account for the report number).
+This call may fail if the device does not support this feature.
+.El
+.Pp
+Use
+.Xr read 2
+to get data from the device.
+Data should be read in chunks of the
+size prescribed by the report descriptor.
+On a device which uses numbered reports, the first byte of the returned data
+is the report number.
+The report data begins from the second byte.
+For devices which do not use numbered reports, the report data begins at the
+first byte.
+.Pp
+Use
+.Xr write 2
+to send data to the device.
+Data should be written in chunks of the
+size prescribed by the report descriptor.
+The first byte of the buffer passed to
+.Xr write 2
+should be set to the report number.
+If the device does not use numbered reports, there are 2 operation modes:
+.Nm
+mode and
+.Xr uhid 4
+mode.
+In the
+.Nm
+mode, the first byte should be set to 0 and the report data itself should
+begin at the second byte.
+In the
+.Xr uhid 4
+mode, the report data should begin at the first byte.
+The modes can be switched with issuing one of
+.Dv HIDIOCGRDESC
+or
+.Dv HID_GET_REPORT_DESC
+.Xr ioctl 2
+accordingly.
+Default mode is
+.Nm .
+.Sh SYSCTL VARIABLES
+The following variables are available as both
+.Xr sysctl 8
+variables and
+.Xr loader 8
+tunables:
+.Bl -tag -width indent
+.It Va hw.hid.hidraw.debug
+Debug output level, where 0 is debugging disabled and larger values increase
+debug message verbosity.
+Default is 0.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /dev/hidraw?"
+.It Pa /dev/hidraw?
+.El
+.Sh SEE ALSO
+.Xr usbhidctl 1 ,
+.Xr hid 4 ,
+.Xr hidbus 4 ,
+.Xr uhid 4
+.Sh HISTORY
+The
+.Xr uhid 4
+driver
+appeared in
+.Nx 1.4 .
+.Nm
+protocol support was added in
+.Fx 13
+by
+.An Vladimir Kondratyev Aq Mt w...@freebsd.org .
+This manual page was adopted from
+.Nx
+by
+.An Tom Rhodes Aq Mt trho...@freebsd.org
+in April 2002.
diff --git a/sys/conf/files b/sys/conf/files
index 71d78778d500..cea809deb039 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1820,6 +1820,7 @@ dev/hid/hid.c                     optional hid
 dev/hid/hid_if.m               optional hid
 dev/hid/hidbus.c               optional hidbus
 dev/hid/hidquirk.c             optional hid
+dev/hid/hidraw.c               optional hidraw
 dev/hid/hkbd.c                 optional hkbd
 dev/hid/hmt.c                  optional hmt hconf
 dev/hifn/hifn7751.c            optional hifn
diff --git a/sys/dev/hid/hidraw.c b/sys/dev/hid/hidraw.c
new file mode 100644
index 000000000000..1da8cb202bd9
--- /dev/null
+++ b/sys/dev/hid/hidraw.c
@@ -0,0 +1,902 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-NetBSD
+ *
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ * Copyright (c) 2020 Vladimir Kondratyev <w...@freebsd.org>
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lenn...@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/ioccom.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/poll.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/selinfo.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/tty.h>
+#include <sys/uio.h>
+
+#define HID_DEBUG_VAR  hidraw_debug
+#include <dev/hid/hid.h>
+#include <dev/hid/hidbus.h>
+#include <dev/hid/hidraw.h>
+
+#ifdef HID_DEBUG
+static int hidraw_debug = 0;
+static SYSCTL_NODE(_hw_hid, OID_AUTO, hidraw, CTLFLAG_RW, 0,
+    "HID raw interface");
+SYSCTL_INT(_hw_hid_hidraw, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &hidraw_debug, 0, "Debug level");
+#endif
+
+#define        HIDRAW_INDEX            0xFF    /* Arbitrary high value */
+
+#define        HIDRAW_LOCAL_BUFSIZE    64      /* Size of on-stack buffer. */
+#define        HIDRAW_LOCAL_ALLOC(local_buf, size)             \
+       (sizeof(local_buf) > (size) ? (local_buf) :     \
+           malloc((size), M_DEVBUF, M_ZERO | M_WAITOK))
+#define        HIDRAW_LOCAL_FREE(local_buf, buf)               \
+       if ((local_buf) != (buf)) {                     \
+               free((buf), M_DEVBUF);                  \
+       }
+
+struct hidraw_softc {
+       device_t sc_dev;                /* base device */
+
+       struct mtx sc_mtx;              /* hidbus private mutex */
+
+       struct hid_rdesc_info *sc_rdesc;
+       const struct hid_device_info *sc_hw;
+
+       uint8_t *sc_q;
+       hid_size_t *sc_qlen;
+       int sc_head;
+       int sc_tail;
+       int sc_sleepcnt;
+
+       struct selinfo sc_rsel;
+       struct proc *sc_async;  /* process that wants SIGIO */
+       struct {                        /* driver state */
+               bool    open:1;         /* device is open */
+               bool    aslp:1;         /* waiting for device data in read() */
+               bool    sel:1;          /* waiting for device data in poll() */
+               bool    quiet:1;        /* Ignore input data */
+               bool    immed:1;        /* return read data immediately */
+               bool    uhid:1;         /* driver switched in to uhid mode */
+               bool    lock:1;         /* input queue sleepable lock */
+               bool    flush:1;        /* do not wait for data in read() */
+       } sc_state;
+       int sc_fflags;                  /* access mode for open lifetime */
+
+       struct cdev *dev;
+};
+
+static d_open_t                hidraw_open;
+static d_read_t                hidraw_read;
+static d_write_t       hidraw_write;
+static d_ioctl_t       hidraw_ioctl;
+static d_poll_t                hidraw_poll;
+static d_kqfilter_t    hidraw_kqfilter;
+
+static d_priv_dtor_t   hidraw_dtor;
+
+static struct cdevsw hidraw_cdevsw = {
+       .d_version =    D_VERSION,
+       .d_open =       hidraw_open,
+       .d_read =       hidraw_read,
+       .d_write =      hidraw_write,
+       .d_ioctl =      hidraw_ioctl,
+       .d_poll =       hidraw_poll,
+       .d_kqfilter =   hidraw_kqfilter,
+       .d_name =       "hidraw",
+};
+
+static hid_intr_t      hidraw_intr;
+
+static device_identify_t hidraw_identify;
+static device_probe_t  hidraw_probe;
+static device_attach_t hidraw_attach;
+static device_detach_t hidraw_detach;
+
+static int             hidraw_kqread(struct knote *, long);
+static void            hidraw_kqdetach(struct knote *);
+static void            hidraw_notify(struct hidraw_softc *);
+
+static struct filterops hidraw_filterops_read = {
+       .f_isfd =       1,
+       .f_detach =     hidraw_kqdetach,
+       .f_event =      hidraw_kqread,
+};
+
+static void
+hidraw_identify(driver_t *driver, device_t parent)
+{
+       device_t child;
+
+       if (device_find_child(parent, "hidraw", -1) == NULL) {
+               child = BUS_ADD_CHILD(parent, 0, "hidraw",
+                   device_get_unit(parent));
+               if (child != NULL)
+                       hidbus_set_index(child, HIDRAW_INDEX);
+       }
+}
+
+static int
+hidraw_probe(device_t self)
+{
+
+       if (hidbus_get_index(self) != HIDRAW_INDEX)
+               return (ENXIO);
+
+       hidbus_set_desc(self, "Raw HID Device");
+
+       return (BUS_PROBE_GENERIC);
+}
+
+static int
+hidraw_attach(device_t self)
+{
+       struct hidraw_softc *sc = device_get_softc(self);
+       struct make_dev_args mda;
+       int error;
+
+       sc->sc_dev = self;
+       sc->sc_rdesc = hidbus_get_rdesc_info(self);
+       sc->sc_hw = hid_get_device_info(self);
+
+       /* Hidraw mode does not require report descriptor to work */
+       if (sc->sc_rdesc->data == NULL || sc->sc_rdesc->len == 0)
+               device_printf(self, "no report descriptor\n");
+
+       mtx_init(&sc->sc_mtx, "hidraw lock", NULL, MTX_DEF);
+       knlist_init_mtx(&sc->sc_rsel.si_note, &sc->sc_mtx);
+
+       make_dev_args_init(&mda);
+       mda.mda_flags = MAKEDEV_WAITOK;
+       mda.mda_devsw = &hidraw_cdevsw;
+       mda.mda_uid = UID_ROOT;
+       mda.mda_gid = GID_OPERATOR;
+       mda.mda_mode = 0600;
+       mda.mda_si_drv1 = sc;
+
+       error = make_dev_s(&mda, &sc->dev, "hidraw%d", device_get_unit(self));
+       if (error) {
+               device_printf(self, "Can not create character device\n");
+               hidraw_detach(self);
+               return (error);
+       }
+
+       hidbus_set_lock(self, &sc->sc_mtx);
+       hidbus_set_intr(self, hidraw_intr, sc);
+
+       return (0);
+}
+
+static int
+hidraw_detach(device_t self)
+{
+       struct hidraw_softc *sc = device_get_softc(self);
+
+       DPRINTF("sc=%p\n", sc);
+
+       if (sc->dev != NULL) {
+               mtx_lock(&sc->sc_mtx);
+               sc->dev->si_drv1 = NULL;
+               /* Wake everyone */
+               hidraw_notify(sc);
+               mtx_unlock(&sc->sc_mtx);
+               destroy_dev(sc->dev);
+       }
+
+       knlist_clear(&sc->sc_rsel.si_note, 0);
+       knlist_destroy(&sc->sc_rsel.si_note);
+       seldrain(&sc->sc_rsel);
+       mtx_destroy(&sc->sc_mtx);
+
+       return (0);
+}
+
+void
+hidraw_intr(void *context, void *buf, hid_size_t len)
+{
+       struct hidraw_softc *sc = context;
+       int next;
+
+       DPRINTFN(5, "len=%d\n", len);
+       DPRINTFN(5, "data = %*D\n", len, buf, " ");
+
+       next = (sc->sc_tail + 1) % HIDRAW_BUFFER_SIZE;
+       if (sc->sc_state.quiet || next == sc->sc_head)
+               return;
+
+       bcopy(buf, sc->sc_q + sc->sc_tail * sc->sc_rdesc->rdsize, len);
+
+       /* Make sure we don't process old data */
+       if (len < sc->sc_rdesc->rdsize)
+               bzero(sc->sc_q + sc->sc_tail * sc->sc_rdesc->rdsize + len,
+                   sc->sc_rdesc->isize - len);
+
+       sc->sc_qlen[sc->sc_tail] = len;
+       sc->sc_tail = next;
+
+       hidraw_notify(sc);
+}
+
+static inline int
+hidraw_lock_queue(struct hidraw_softc *sc, bool flush)
+{
+       int error = 0;
+
+       mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+       if (flush)
+               sc->sc_state.flush = true;
+       ++sc->sc_sleepcnt;
+       while (sc->sc_state.lock && error == 0) {
+               /* Flush is requested. Wakeup all readers and forbid sleeps */
+               if (flush && sc->sc_state.aslp) {
+                       sc->sc_state.aslp = false;
+                       DPRINTFN(5, "waking %p\n", &sc->sc_q);
+                       wakeup(&sc->sc_q);
+               }
+               error = mtx_sleep(&sc->sc_sleepcnt, &sc->sc_mtx,
+                   PZERO | PCATCH, "hidrawio", 0);
+       }
+       --sc->sc_sleepcnt;
+       if (flush)
+               sc->sc_state.flush = false;
+       if (error == 0)
+               sc->sc_state.lock = true;
+
+       return (error);
+}
+
+static inline void
+hidraw_unlock_queue(struct hidraw_softc *sc)
+{
+
+       mtx_assert(&sc->sc_mtx, MA_OWNED);
+       KASSERT(sc->sc_state.lock, ("input buffer is not locked"));
+
+       if (sc->sc_sleepcnt != 0)
+               wakeup_one(&sc->sc_sleepcnt);
+       sc->sc_state.lock = false;
+}
+
+static int
+hidraw_open(struct cdev *dev, int flag, int mode, struct thread *td)
+{
+       struct hidraw_softc *sc;
+       int error;
+
+       sc = dev->si_drv1;
+       if (sc == NULL)
+               return (ENXIO);
+
+       DPRINTF("sc=%p\n", sc);
+
+       mtx_lock(&sc->sc_mtx);
+       if (sc->sc_state.open) {
+               mtx_unlock(&sc->sc_mtx);
+               return (EBUSY);
+       }
+       sc->sc_state.open = true;
+       mtx_unlock(&sc->sc_mtx);
+
+       error = devfs_set_cdevpriv(sc, hidraw_dtor);
+       if (error != 0) {
+               mtx_lock(&sc->sc_mtx);
+               sc->sc_state.open = false;
+               mtx_unlock(&sc->sc_mtx);
+               return (error);
+       }
+
+       sc->sc_q = malloc(sc->sc_rdesc->rdsize * HIDRAW_BUFFER_SIZE, M_DEVBUF,
+           M_ZERO | M_WAITOK);
+       sc->sc_qlen = malloc(sizeof(hid_size_t) * HIDRAW_BUFFER_SIZE, M_DEVBUF,
+           M_ZERO | M_WAITOK);
+
+       /* Set up interrupt pipe. */
+       sc->sc_state.immed = false;
+       sc->sc_async = 0;
+       sc->sc_state.uhid = false;      /* hidraw mode is default */
+       sc->sc_state.quiet = false;
+       sc->sc_head = sc->sc_tail = 0;
+       sc->sc_fflags = flag;
+
+       hidbus_intr_start(sc->sc_dev);
+
+       return (0);
+}
+
+static void
+hidraw_dtor(void *data)
+{
+       struct hidraw_softc *sc = data;
+
+       DPRINTF("sc=%p\n", sc);
+
+       /* Disable interrupts. */
+       hidbus_intr_stop(sc->sc_dev);
+
+       sc->sc_tail = sc->sc_head = 0;
+       sc->sc_async = 0;
+       free(sc->sc_q, M_DEVBUF);
+       free(sc->sc_qlen, M_DEVBUF);
+       sc->sc_q = NULL;
+
+       mtx_lock(&sc->sc_mtx);
+       sc->sc_state.open = false;
+       mtx_unlock(&sc->sc_mtx);
+}
+
+static int
+hidraw_read(struct cdev *dev, struct uio *uio, int flag)
+{
+       struct hidraw_softc *sc;
+       size_t length;
+       int error;
+
+       DPRINTFN(1, "\n");
+
+       sc = dev->si_drv1;
+       if (sc == NULL)
+               return (EIO);
+
+       mtx_lock(&sc->sc_mtx);
+       error = dev->si_drv1 == NULL ? EIO : hidraw_lock_queue(sc, false);
+       if (error != 0) {
+               mtx_unlock(&sc->sc_mtx);
+               return (error);
+       }
+
+       if (sc->sc_state.immed) {
+               mtx_unlock(&sc->sc_mtx);
+               DPRINTFN(1, "immed\n");
+
+               error = hid_get_report(sc->sc_dev, sc->sc_q,
+                   sc->sc_rdesc->isize, NULL, HID_INPUT_REPORT,
+                   sc->sc_rdesc->iid);
+               if (error == 0)
+                       error = uiomove(sc->sc_q, sc->sc_rdesc->isize, uio);
+               mtx_lock(&sc->sc_mtx);
+               goto exit;
+       }
+
+       while (sc->sc_tail == sc->sc_head && !sc->sc_state.flush) {
+               if (flag & O_NONBLOCK) {
+                       error = EWOULDBLOCK;
+                       goto exit;
+               }
+               sc->sc_state.aslp = true;
+               DPRINTFN(5, "sleep on %p\n", &sc->sc_q);
+               error = mtx_sleep(&sc->sc_q, &sc->sc_mtx, PZERO | PCATCH,
+                   "hidrawrd", 0);
+               DPRINTFN(5, "woke, error=%d\n", error);
+               if (dev->si_drv1 == NULL)
+                       error = EIO;
+               if (error) {
+                       sc->sc_state.aslp = false;
+                       goto exit;
+               }
+       }
+
+       while (sc->sc_tail != sc->sc_head && uio->uio_resid > 0) {
+               length = min(uio->uio_resid, sc->sc_state.uhid ?
+                   sc->sc_rdesc->isize : sc->sc_qlen[sc->sc_head]);
+               mtx_unlock(&sc->sc_mtx);
+
+               /* Copy the data to the user process. */
+               DPRINTFN(5, "got %lu chars\n", (u_long)length);
+               error = uiomove(sc->sc_q + sc->sc_head * sc->sc_rdesc->rdsize,
+                   length, uio);
+
+               mtx_lock(&sc->sc_mtx);
+               if (error != 0)
+                       goto exit;
+               /* Remove a small chunk from the input queue. */
+               sc->sc_head = (sc->sc_head + 1) % HIDRAW_BUFFER_SIZE;
+               /*
+                * In uhid mode transfer as many chunks as possible. Hidraw
+                * packets are transferred one by one due to different length.
+                */
+               if (!sc->sc_state.uhid)
+                       goto exit;
+       }
+exit:
+       hidraw_unlock_queue(sc);
+       mtx_unlock(&sc->sc_mtx);
+
+       return (error);
+}
+
+static int
+hidraw_write(struct cdev *dev, struct uio *uio, int flag)
+{
+       uint8_t local_buf[HIDRAW_LOCAL_BUFSIZE], *buf;
+       struct hidraw_softc *sc;
+       int error;
+       int size;
+       size_t buf_offset;
+       uint8_t id = 0;
+
+       DPRINTFN(1, "\n");
+
+       sc = dev->si_drv1;
+       if (sc == NULL)
+               return (EIO);
+
+       if (sc->sc_rdesc->osize == 0)
+               return (EOPNOTSUPP);
+
+       buf_offset = 0;
+       if (sc->sc_state.uhid) {
+               size = sc->sc_rdesc->osize;
+               if (uio->uio_resid != size)
+                       return (EINVAL);
+       } else {
+               size = uio->uio_resid;
+               if (size < 2)
+                       return (EINVAL);
+               /* Strip leading 0 if the device doesnt use numbered reports */
+               error = uiomove(&id, 1, uio);
+               if (error)
+                       return (error);
+               if (id != 0)
+                       buf_offset++;
+               else
+                       size--;
+               /* Check if underlying driver could process this request */
+               if (size > sc->sc_rdesc->wrsize)
+                       return (ENOBUFS);
+       }
+       buf = HIDRAW_LOCAL_ALLOC(local_buf, size);
+       buf[0] = id;
+       error = uiomove(buf + buf_offset, uio->uio_resid, uio);
+       if (error == 0)
+               error = hid_write(sc->sc_dev, buf, size);
+       HIDRAW_LOCAL_FREE(local_buf, buf);
+
+       return (error);
+}
+
+static int
+hidraw_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
+    struct thread *td)
+{
+       uint8_t local_buf[HIDRAW_LOCAL_BUFSIZE];
+       void *buf;
+       struct hidraw_softc *sc;
+       struct hidraw_gen_descriptor *hgd;
+       struct hidraw_report_descriptor *hrd;
+       struct hidraw_devinfo *hdi;
+       uint32_t size;
+       int id, len;
+       int error = 0;
+
+       DPRINTFN(2, "cmd=%lx\n", cmd);
+
+       sc = dev->si_drv1;
+       if (sc == NULL)
+               return (EIO);
+
+       /* fixed-length ioctls handling */
+       switch (cmd) {
+       case FIONBIO:
+               /* All handled in the upper FS layer. */
+               return (0);
+
+       case FIOASYNC:
+               mtx_lock(&sc->sc_mtx);
+               if (*(int *)addr) {
+                       if (sc->sc_async == NULL) {
+                               sc->sc_async = td->td_proc;
+                               DPRINTF("FIOASYNC %p\n", sc->sc_async);
+                       } else
+                               error = EBUSY;
+               } else
+                       sc->sc_async = NULL;
+               mtx_unlock(&sc->sc_mtx);
+               return (error);
+
+       /* XXX this is not the most general solution. */
+       case TIOCSPGRP:
+               mtx_lock(&sc->sc_mtx);
+               if (sc->sc_async == NULL)
+                       error = EINVAL;
+               else if (*(int *)addr != sc->sc_async->p_pgid)
+                       error = EPERM;
+               mtx_unlock(&sc->sc_mtx);
+               return (error);
+
+       case HIDRAW_GET_REPORT_DESC:
+               if (sc->sc_rdesc->data == NULL || sc->sc_rdesc->len == 0)
+                       return (EOPNOTSUPP);
+               mtx_lock(&sc->sc_mtx);
+               sc->sc_state.uhid = true;
+               mtx_unlock(&sc->sc_mtx);
+               hgd = (struct hidraw_gen_descriptor *)addr;
+               if (sc->sc_rdesc->len > hgd->hgd_maxlen) {
+                       size = hgd->hgd_maxlen;
+               } else {
+                       size = sc->sc_rdesc->len;
+               }
+               hgd->hgd_actlen = size;
+               if (hgd->hgd_data == NULL)
+                       return (0);             /* descriptor length only */
+               return (copyout(sc->sc_rdesc->data, hgd->hgd_data, size));
+
+
+       case HIDRAW_SET_REPORT_DESC:
+               if (!(sc->sc_fflags & FWRITE))
+                       return (EPERM);
+
+               /* check privileges */
+               error = priv_check(curthread, PRIV_DRIVER);
+               if (error)
+                       return (error);
+
+               /* Stop interrupts and clear input report buffer */
+               mtx_lock(&sc->sc_mtx);
+               sc->sc_tail = sc->sc_head = 0;
+               error = hidraw_lock_queue(sc, true);
+               if (error == 0)
+                       sc->sc_state.quiet = true;
*** 461 LINES SKIPPED ***
_______________________________________________
dev-commits-src-main@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-main
To unsubscribe, send any mail to "dev-commits-src-main-unsubscr...@freebsd.org"

Reply via email to