11.1Sjmcneill/*	$NetBSD: uhid_common.c,v 1.1 2025/12/07 10:05:10 jmcneill Exp $	*/
21.1Sjmcneill
31.1Sjmcneill/*
41.1Sjmcneill * Copyright (c) 1998, 2004, 2008, 2012 The NetBSD Foundation, Inc.
51.1Sjmcneill * All rights reserved.
61.1Sjmcneill *
71.1Sjmcneill * This code is derived from software contributed to The NetBSD Foundation
81.1Sjmcneill * by Lennart Augustsson (lennart@augustsson.net) at
91.1Sjmcneill * Carlstedt Research & Technology and Matthew R. Green (mrg@eterna23.net).
101.1Sjmcneill *
111.1Sjmcneill * Redistribution and use in source and binary forms, with or without
121.1Sjmcneill * modification, are permitted provided that the following conditions
131.1Sjmcneill * are met:
141.1Sjmcneill * 1. Redistributions of source code must retain the above copyright
151.1Sjmcneill *    notice, this list of conditions and the following disclaimer.
161.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
171.1Sjmcneill *    notice, this list of conditions and the following disclaimer in the
181.1Sjmcneill *    documentation and/or other materials provided with the distribution.
191.1Sjmcneill *
201.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
211.1Sjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
221.1Sjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
231.1Sjmcneill * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
241.1Sjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
251.1Sjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
261.1Sjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
271.1Sjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
281.1Sjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
291.1Sjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
301.1Sjmcneill * POSSIBILITY OF SUCH DAMAGE.
311.1Sjmcneill */
321.1Sjmcneill
331.1Sjmcneill/*
341.1Sjmcneill * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
351.1Sjmcneill */
361.1Sjmcneill
371.1Sjmcneill#include <sys/cdefs.h>
381.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: uhid_common.c,v 1.1 2025/12/07 10:05:10 jmcneill Exp $");
391.1Sjmcneill
401.1Sjmcneill#ifdef _KERNEL_OPT
411.1Sjmcneill#include "opt_hid.h"
421.1Sjmcneill#endif
431.1Sjmcneill
441.1Sjmcneill#include <sys/param.h>
451.1Sjmcneill#include <sys/types.h>
461.1Sjmcneill
471.1Sjmcneill#include <sys/atomic.h>
481.1Sjmcneill#include <sys/conf.h>
491.1Sjmcneill#include <sys/device.h>
501.1Sjmcneill#include <sys/file.h>
511.1Sjmcneill#include <sys/intr.h>
521.1Sjmcneill#include <sys/ioctl.h>
531.1Sjmcneill#include <sys/kernel.h>
541.1Sjmcneill#include <sys/kmem.h>
551.1Sjmcneill#include <sys/poll.h>
561.1Sjmcneill#include <sys/proc.h>
571.1Sjmcneill#include <sys/select.h>
581.1Sjmcneill#include <sys/signalvar.h>
591.1Sjmcneill#include <sys/systm.h>
601.1Sjmcneill#include <sys/tty.h>
611.1Sjmcneill#include <sys/vnode.h>
621.1Sjmcneill
631.1Sjmcneill#include <dev/hid/hid.h>
641.1Sjmcneill#include <dev/hid/hidev.h>
651.1Sjmcneill#include <dev/hid/uhid.h>
661.1Sjmcneill
671.1Sjmcneill#include <dev/usb/usbhid.h>
681.1Sjmcneill
691.1Sjmcneill#include "ioconf.h"
701.1Sjmcneill
711.1Sjmcneill#ifdef UHID_DEBUG
721.1Sjmcneill#define DPRINTF(x)	if (uhiddebug) printf x
731.1Sjmcneill#define DPRINTFN(n,x)	if (uhiddebug>(n)) printf x
741.1Sjmcneillint	uhiddebug = 0;
751.1Sjmcneill#else
761.1Sjmcneill#define DPRINTF(x)
771.1Sjmcneill#define DPRINTFN(n,x)
781.1Sjmcneill#endif
791.1Sjmcneill
801.1Sjmcneill#define	UHIDUNIT(dev)	(minor(dev))
811.1Sjmcneill#define	UHID_CHUNK	128	/* chunk size for read */
821.1Sjmcneill#define	UHID_BSIZE	1020	/* buffer size */
831.1Sjmcneill
841.1Sjmcneillstatic dev_type_open(uhidopen);
851.1Sjmcneillstatic dev_type_cancel(uhidcancel);
861.1Sjmcneillstatic dev_type_close(uhidclose);
871.1Sjmcneillstatic dev_type_read(uhidread);
881.1Sjmcneillstatic dev_type_write(uhidwrite);
891.1Sjmcneillstatic dev_type_ioctl(uhidioctl);
901.1Sjmcneillstatic dev_type_poll(uhidpoll);
911.1Sjmcneillstatic dev_type_kqfilter(uhidkqfilter);
921.1Sjmcneill
931.1Sjmcneillconst struct cdevsw uhid_cdevsw = {
941.1Sjmcneill	.d_open = uhidopen,
951.1Sjmcneill	.d_cancel = uhidcancel,
961.1Sjmcneill	.d_close = uhidclose,
971.1Sjmcneill	.d_read = uhidread,
981.1Sjmcneill	.d_write = uhidwrite,
991.1Sjmcneill	.d_ioctl = uhidioctl,
1001.1Sjmcneill	.d_stop = nostop,
1011.1Sjmcneill	.d_tty = notty,
1021.1Sjmcneill	.d_poll = uhidpoll,
1031.1Sjmcneill	.d_mmap = nommap,
1041.1Sjmcneill	.d_kqfilter = uhidkqfilter,
1051.1Sjmcneill	.d_discard = nodiscard,
1061.1Sjmcneill	.d_cfdriver = &uhid_cd,
1071.1Sjmcneill	.d_devtounit = dev_minor_unit,
1081.1Sjmcneill	.d_flag = D_OTHER
1091.1Sjmcneill};
1101.1Sjmcneill
1111.1Sjmcneillstatic void uhid_intr(void *, void *, u_int);
1121.1Sjmcneill
1131.1Sjmcneillvoid
1141.1Sjmcneilluhid_attach_common(struct uhid_softc *sc)
1151.1Sjmcneill{
1161.1Sjmcneill	int size, repid;
1171.1Sjmcneill	void *desc;
1181.1Sjmcneill
1191.1Sjmcneill	selinit(&sc->sc_rsel);
1201.1Sjmcneill
1211.1Sjmcneill	hidev_get_report_desc(sc->sc_hidev, &desc, &size);
1221.1Sjmcneill	repid = sc->sc_report_id;
1231.1Sjmcneill	sc->sc_isize = hid_report_size(desc, size, hid_input,   repid);
1241.1Sjmcneill	sc->sc_osize = hid_report_size(desc, size, hid_output,  repid);
1251.1Sjmcneill	sc->sc_fsize = hid_report_size(desc, size, hid_feature, repid);
1261.1Sjmcneill	sc->sc_raw =  hid_is_collection(desc, size, repid,
1271.1Sjmcneill	    HID_USAGE2(HUP_FIDO, HUF_U2FHID));
1281.1Sjmcneill
1291.1Sjmcneill	aprint_naive("\n");
1301.1Sjmcneill	aprint_normal(": input=%d, output=%d, feature=%d\n",
1311.1Sjmcneill	       sc->sc_isize, sc->sc_osize, sc->sc_fsize);
1321.1Sjmcneill
1331.1Sjmcneill	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB);
1341.1Sjmcneill	cv_init(&sc->sc_cv, "uhidrea");
1351.1Sjmcneill
1361.1Sjmcneill	if (!pmf_device_register(sc->sc_dev, NULL, NULL)) {
1371.1Sjmcneill		aprint_error_dev(sc->sc_dev,
1381.1Sjmcneill		    "couldn't establish power handler\n");
1391.1Sjmcneill	}
1401.1Sjmcneill}
1411.1Sjmcneill
1421.1Sjmcneillint
1431.1Sjmcneilluhid_detach_common(struct uhid_softc *sc)
1441.1Sjmcneill{
1451.1Sjmcneill	int maj, mn;
1461.1Sjmcneill
1471.1Sjmcneill	DPRINTF(("uhid_detach: sc=%p\n", sc));
1481.1Sjmcneill
1491.1Sjmcneill	pmf_device_deregister(sc->sc_dev);
1501.1Sjmcneill
1511.1Sjmcneill	/* locate the major number */
1521.1Sjmcneill	maj = cdevsw_lookup_major(&uhid_cdevsw);
1531.1Sjmcneill
1541.1Sjmcneill	/* Nuke the vnodes for any open instances (calls close). */
1551.1Sjmcneill	mn = device_unit(sc->sc_dev);
1561.1Sjmcneill	vdevgone(maj, mn, mn, VCHR);
1571.1Sjmcneill
1581.1Sjmcneill	KASSERTMSG(sc->sc_open == UHID_CLOSED, "open=%d", (int)sc->sc_open);
1591.1Sjmcneill
1601.1Sjmcneill	cv_destroy(&sc->sc_cv);
1611.1Sjmcneill	mutex_destroy(&sc->sc_lock);
1621.1Sjmcneill	seldestroy(&sc->sc_rsel);
1631.1Sjmcneill
1641.1Sjmcneill	return 0;
1651.1Sjmcneill}
1661.1Sjmcneill
1671.1Sjmcneillstatic void
1681.1Sjmcneilluhid_intr(void *cookie, void *data, u_int len)
1691.1Sjmcneill{
1701.1Sjmcneill	struct uhid_softc *sc = cookie;
1711.1Sjmcneill
1721.1Sjmcneill#ifdef UHID_DEBUG
1731.1Sjmcneill	if (uhiddebug > 5) {
1741.1Sjmcneill		uint32_t i;
1751.1Sjmcneill
1761.1Sjmcneill		DPRINTF(("uhid_intr: data ="));
1771.1Sjmcneill		for (i = 0; i < len; i++)
1781.1Sjmcneill			DPRINTF((" %02x", ((u_char *)data)[i]));
1791.1Sjmcneill		DPRINTF(("\n"));
1801.1Sjmcneill	}
1811.1Sjmcneill#endif
1821.1Sjmcneill
1831.1Sjmcneill	mutex_enter(&sc->sc_lock);
1841.1Sjmcneill	(void)b_to_q(data, len, &sc->sc_q);
1851.1Sjmcneill
1861.1Sjmcneill	DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q));
1871.1Sjmcneill	cv_broadcast(&sc->sc_cv);
1881.1Sjmcneill	selnotify(&sc->sc_rsel, 0, NOTE_SUBMIT);
1891.1Sjmcneill	if (atomic_load_relaxed(&sc->sc_async) != NULL) {
1901.1Sjmcneill		mutex_enter(&proc_lock);
1911.1Sjmcneill		if (sc->sc_async != NULL) {
1921.1Sjmcneill			DPRINTFN(3, ("uhid_intr: sending SIGIO to %jd\n",
1931.1Sjmcneill				(intmax_t)sc->sc_async->p_pid));
1941.1Sjmcneill			psignal(sc->sc_async, SIGIO);
1951.1Sjmcneill		}
1961.1Sjmcneill		mutex_exit(&proc_lock);
1971.1Sjmcneill	}
1981.1Sjmcneill	mutex_exit(&sc->sc_lock);
1991.1Sjmcneill}
2001.1Sjmcneill
2011.1Sjmcneillstatic int
2021.1Sjmcneilluhidopen(dev_t dev, int flag, int mode, struct lwp *l)
2031.1Sjmcneill{
2041.1Sjmcneill	struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev));
2051.1Sjmcneill	int error;
2061.1Sjmcneill
2071.1Sjmcneill	DPRINTF(("uhidopen: sc=%p\n", sc));
2081.1Sjmcneill	if (sc == NULL)
2091.1Sjmcneill		return ENXIO;
2101.1Sjmcneill
2111.1Sjmcneill	/*
2121.1Sjmcneill	 * Try to open.  If closing in progress, give up.  If already
2131.1Sjmcneill	 * open (or opening), fail -- opens are exclusive.
2141.1Sjmcneill	 */
2151.1Sjmcneill	mutex_enter(&sc->sc_lock);
2161.1Sjmcneill	if (sc->sc_open != UHID_CLOSED || sc->sc_closing) {
2171.1Sjmcneill		mutex_exit(&sc->sc_lock);
2181.1Sjmcneill		return EBUSY;
2191.1Sjmcneill	}
2201.1Sjmcneill	sc->sc_open = UHID_OPENING;
2211.1Sjmcneill	atomic_store_relaxed(&sc->sc_state, 0);
2221.1Sjmcneill	mutex_exit(&sc->sc_lock);
2231.1Sjmcneill
2241.1Sjmcneill	/* uhid interrupts aren't enabled yet, so setup sc_q now */
2251.1Sjmcneill	if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) {
2261.1Sjmcneill		error = ENOMEM;
2271.1Sjmcneill		goto fail0;
2281.1Sjmcneill	}
2291.1Sjmcneill
2301.1Sjmcneill	/* Allocate an output buffer if needed.  */
2311.1Sjmcneill	if (sc->sc_osize > 0)
2321.1Sjmcneill		sc->sc_obuf = kmem_alloc(sc->sc_osize, KM_SLEEP);
2331.1Sjmcneill	else
2341.1Sjmcneill		sc->sc_obuf = NULL;
2351.1Sjmcneill
2361.1Sjmcneill	/* Paranoia: reset SIGIO before enabling interrupts.  */
2371.1Sjmcneill	mutex_enter(&proc_lock);
2381.1Sjmcneill	atomic_store_relaxed(&sc->sc_async, NULL);
2391.1Sjmcneill	mutex_exit(&proc_lock);
2401.1Sjmcneill
2411.1Sjmcneill	/* Open the hidev -- after this point we can get interrupts.  */
2421.1Sjmcneill	error = hidev_open(sc->sc_hidev, &uhid_intr, sc);
2431.1Sjmcneill	if (error)
2441.1Sjmcneill		goto fail1;
2451.1Sjmcneill
2461.1Sjmcneill	/* We are open for business.  */
2471.1Sjmcneill	mutex_enter(&sc->sc_lock);
2481.1Sjmcneill	sc->sc_open = UHID_OPEN;
2491.1Sjmcneill	mutex_exit(&sc->sc_lock);
2501.1Sjmcneill
2511.1Sjmcneill	return 0;
2521.1Sjmcneill
2531.1Sjmcneillfail1:	selnotify(&sc->sc_rsel, POLLHUP, 0);
2541.1Sjmcneill	mutex_enter(&proc_lock);
2551.1Sjmcneill	atomic_store_relaxed(&sc->sc_async, NULL);
2561.1Sjmcneill	mutex_exit(&proc_lock);
2571.1Sjmcneill	if (sc->sc_osize > 0) {
2581.1Sjmcneill		kmem_free(sc->sc_obuf, sc->sc_osize);
2591.1Sjmcneill		sc->sc_obuf = NULL;
2601.1Sjmcneill	}
2611.1Sjmcneill	clfree(&sc->sc_q);
2621.1Sjmcneillfail0:	mutex_enter(&sc->sc_lock);
2631.1Sjmcneill	KASSERT(sc->sc_open == UHID_OPENING);
2641.1Sjmcneill	sc->sc_open = UHID_CLOSED;
2651.1Sjmcneill	atomic_store_relaxed(&sc->sc_state, 0);
2661.1Sjmcneill	mutex_exit(&sc->sc_lock);
2671.1Sjmcneill	return error;
2681.1Sjmcneill}
2691.1Sjmcneill
2701.1Sjmcneillstatic int
2711.1Sjmcneilluhidcancel(dev_t dev, int flag, int mode, struct lwp *l)
2721.1Sjmcneill{
2731.1Sjmcneill	struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev));
2741.1Sjmcneill
2751.1Sjmcneill	DPRINTF(("uhidcancel: sc=%p\n", sc));
2761.1Sjmcneill	if (sc == NULL)
2771.1Sjmcneill		return 0;
2781.1Sjmcneill
2791.1Sjmcneill	/*
2801.1Sjmcneill	 * Mark it closing, wake any waiters, and suspend output.
2811.1Sjmcneill	 * After this point, no new xfers can be submitted.
2821.1Sjmcneill	 */
2831.1Sjmcneill	mutex_enter(&sc->sc_lock);
2841.1Sjmcneill	sc->sc_closing = true;
2851.1Sjmcneill	cv_broadcast(&sc->sc_cv);
2861.1Sjmcneill	mutex_exit(&sc->sc_lock);
2871.1Sjmcneill
2881.1Sjmcneill	hidev_stop(sc->sc_hidev);
2891.1Sjmcneill
2901.1Sjmcneill	return 0;
2911.1Sjmcneill}
2921.1Sjmcneill
2931.1Sjmcneillstatic int
2941.1Sjmcneilluhidclose(dev_t dev, int flag, int mode, struct lwp *l)
2951.1Sjmcneill{
2961.1Sjmcneill	struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev));
2971.1Sjmcneill
2981.1Sjmcneill	DPRINTF(("uhidclose: sc=%p\n", sc));
2991.1Sjmcneill	if (sc == NULL)
3001.1Sjmcneill		return 0;
3011.1Sjmcneill
3021.1Sjmcneill	mutex_enter(&sc->sc_lock);
3031.1Sjmcneill	KASSERT(sc->sc_closing);
3041.1Sjmcneill	KASSERTMSG(sc->sc_open == UHID_OPEN || sc->sc_open == UHID_CLOSED,
3051.1Sjmcneill	    "sc_open=%d", sc->sc_open);
3061.1Sjmcneill	if (sc->sc_open == UHID_CLOSED)
3071.1Sjmcneill		goto out;
3081.1Sjmcneill
3091.1Sjmcneill	/* Release the lock while we free things.  */
3101.1Sjmcneill	mutex_exit(&sc->sc_lock);
3111.1Sjmcneill
3121.1Sjmcneill	/* Prevent further interrupts.  */
3131.1Sjmcneill	hidev_close(sc->sc_hidev);
3141.1Sjmcneill
3151.1Sjmcneill	/* Hang up all select/poll.  */
3161.1Sjmcneill	selnotify(&sc->sc_rsel, POLLHUP, 0);
3171.1Sjmcneill
3181.1Sjmcneill	/* Reset SIGIO.  */
3191.1Sjmcneill	mutex_enter(&proc_lock);
3201.1Sjmcneill	atomic_store_relaxed(&sc->sc_async, NULL);
3211.1Sjmcneill	mutex_exit(&proc_lock);
3221.1Sjmcneill
3231.1Sjmcneill	/* Free the buffer and queue.  */
3241.1Sjmcneill	if (sc->sc_osize > 0) {
3251.1Sjmcneill		kmem_free(sc->sc_obuf, sc->sc_osize);
3261.1Sjmcneill		sc->sc_obuf = NULL;
3271.1Sjmcneill	}
3281.1Sjmcneill	clfree(&sc->sc_q);
3291.1Sjmcneill
3301.1Sjmcneill	mutex_enter(&sc->sc_lock);
3311.1Sjmcneill
3321.1Sjmcneill	/* All set.  We are now closed.  */
3331.1Sjmcneill	sc->sc_open = UHID_CLOSED;
3341.1Sjmcneillout:	KASSERT(sc->sc_open == UHID_CLOSED);
3351.1Sjmcneill	sc->sc_closing = false;
3361.1Sjmcneill	atomic_store_relaxed(&sc->sc_state, 0);
3371.1Sjmcneill	mutex_exit(&sc->sc_lock);
3381.1Sjmcneill
3391.1Sjmcneill	return 0;
3401.1Sjmcneill}
3411.1Sjmcneill
3421.1Sjmcneillstatic int
3431.1Sjmcneilluhidread(dev_t dev, struct uio *uio, int flag)
3441.1Sjmcneill{
3451.1Sjmcneill	struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev));
3461.1Sjmcneill	int error = 0;
3471.1Sjmcneill	int extra;
3481.1Sjmcneill	size_t length;
3491.1Sjmcneill	u_char buffer[UHID_CHUNK];
3501.1Sjmcneill	usbd_status err;
3511.1Sjmcneill
3521.1Sjmcneill	DPRINTFN(1, ("uhidread\n"));
3531.1Sjmcneill	if (atomic_load_relaxed(&sc->sc_state) & UHID_IMMED) {
3541.1Sjmcneill		DPRINTFN(1, ("uhidread immed\n"));
3551.1Sjmcneill		extra = sc->sc_report_id != 0;
3561.1Sjmcneill		if (sc->sc_isize + extra > sizeof(buffer))
3571.1Sjmcneill			return ENOBUFS;
3581.1Sjmcneill		err = hidev_get_report(sc->sc_hidev, UHID_INPUT_REPORT,
3591.1Sjmcneill				       buffer, sc->sc_isize + extra);
3601.1Sjmcneill		if (err)
3611.1Sjmcneill			return EIO;
3621.1Sjmcneill		return uiomove(buffer+extra, sc->sc_isize, uio);
3631.1Sjmcneill	}
3641.1Sjmcneill
3651.1Sjmcneill	mutex_enter(&sc->sc_lock);
3661.1Sjmcneill	while (sc->sc_q.c_cc == 0) {
3671.1Sjmcneill		if (flag & IO_NDELAY) {
3681.1Sjmcneill			mutex_exit(&sc->sc_lock);
3691.1Sjmcneill			return EWOULDBLOCK;
3701.1Sjmcneill		}
3711.1Sjmcneill		if (sc->sc_closing) {
3721.1Sjmcneill			mutex_exit(&sc->sc_lock);
3731.1Sjmcneill			return EIO;
3741.1Sjmcneill		}
3751.1Sjmcneill		DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q));
3761.1Sjmcneill		error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock);
3771.1Sjmcneill		DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
3781.1Sjmcneill		if (error) {
3791.1Sjmcneill			break;
3801.1Sjmcneill		}
3811.1Sjmcneill	}
3821.1Sjmcneill
3831.1Sjmcneill	/* Transfer as many chunks as possible. */
3841.1Sjmcneill	while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) {
3851.1Sjmcneill		length = uimin(sc->sc_q.c_cc, uio->uio_resid);
3861.1Sjmcneill		if (length > sizeof(buffer))
3871.1Sjmcneill			length = sizeof(buffer);
3881.1Sjmcneill
3891.1Sjmcneill		/* Remove a small chunk from the input queue. */
3901.1Sjmcneill		(void) q_to_b(&sc->sc_q, buffer, length);
3911.1Sjmcneill		DPRINTFN(5, ("uhidread: got %lu chars\n", (u_long)length));
3921.1Sjmcneill
3931.1Sjmcneill		/* Copy the data to the user process. */
3941.1Sjmcneill		mutex_exit(&sc->sc_lock);
3951.1Sjmcneill		if ((error = uiomove(buffer, length, uio)) != 0)
3961.1Sjmcneill			return error;
3971.1Sjmcneill		mutex_enter(&sc->sc_lock);
3981.1Sjmcneill	}
3991.1Sjmcneill
4001.1Sjmcneill	mutex_exit(&sc->sc_lock);
4011.1Sjmcneill	return error;
4021.1Sjmcneill}
4031.1Sjmcneill
4041.1Sjmcneillstatic int
4051.1Sjmcneilluhidwrite(dev_t dev, struct uio *uio, int flag)
4061.1Sjmcneill{
4071.1Sjmcneill	struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev));
4081.1Sjmcneill	int error;
4091.1Sjmcneill	int size;
4101.1Sjmcneill	usbd_status err;
4111.1Sjmcneill
4121.1Sjmcneill	DPRINTFN(1, ("uhidwrite\n"));
4131.1Sjmcneill
4141.1Sjmcneill	size = sc->sc_osize;
4151.1Sjmcneill	if (uio->uio_resid != size || size == 0)
4161.1Sjmcneill		return EINVAL;
4171.1Sjmcneill	error = uiomove(sc->sc_obuf, size, uio);
4181.1Sjmcneill#ifdef UHID_DEBUG
4191.1Sjmcneill	if (uhiddebug > 5) {
4201.1Sjmcneill		uint32_t i;
4211.1Sjmcneill
4221.1Sjmcneill		DPRINTF(("%s: outdata[%d] =", device_xname(sc->sc_dev),
4231.1Sjmcneill		    error));
4241.1Sjmcneill		for (i = 0; i < size; i++)
4251.1Sjmcneill			DPRINTF((" %02x", sc->sc_obuf[i]));
4261.1Sjmcneill		DPRINTF(("\n"));
4271.1Sjmcneill	}
4281.1Sjmcneill#endif
4291.1Sjmcneill	if (!error) {
4301.1Sjmcneill		if (sc->sc_raw)
4311.1Sjmcneill			err = hidev_write(sc->sc_hidev, sc->sc_obuf, size);
4321.1Sjmcneill		else
4331.1Sjmcneill			err = hidev_set_report(sc->sc_hidev,
4341.1Sjmcneill			    UHID_OUTPUT_REPORT, sc->sc_obuf, size);
4351.1Sjmcneill		if (err) {
4361.1Sjmcneill			DPRINTF(("%s: err = %d\n",
4371.1Sjmcneill			    device_xname(sc->sc_dev), err));
4381.1Sjmcneill			error = EIO;
4391.1Sjmcneill		}
4401.1Sjmcneill	}
4411.1Sjmcneill
4421.1Sjmcneill	return error;
4431.1Sjmcneill}
4441.1Sjmcneill
4451.1Sjmcneillstatic int
4461.1Sjmcneilluhidioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
4471.1Sjmcneill{
4481.1Sjmcneill	struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev));
4491.1Sjmcneill	struct usb_ctl_report_desc *rd;
4501.1Sjmcneill	struct usb_ctl_report *re;
4511.1Sjmcneill	u_char buffer[UHID_CHUNK];
4521.1Sjmcneill	int size, extra;
4531.1Sjmcneill	usbd_status err;
4541.1Sjmcneill	void *desc;
4551.1Sjmcneill
4561.1Sjmcneill	DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
4571.1Sjmcneill
4581.1Sjmcneill	switch (cmd) {
4591.1Sjmcneill	case FIONBIO:
4601.1Sjmcneill		/* All handled in the upper FS layer. */
4611.1Sjmcneill		break;
4621.1Sjmcneill
4631.1Sjmcneill	case FIOASYNC:
4641.1Sjmcneill		mutex_enter(&proc_lock);
4651.1Sjmcneill		if (*(int *)addr) {
4661.1Sjmcneill			if (sc->sc_async != NULL) {
4671.1Sjmcneill				mutex_exit(&proc_lock);
4681.1Sjmcneill				return EBUSY;
4691.1Sjmcneill			}
4701.1Sjmcneill			atomic_store_relaxed(&sc->sc_async, l->l_proc);
4711.1Sjmcneill			DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", l->l_proc));
4721.1Sjmcneill		} else
4731.1Sjmcneill			atomic_store_relaxed(&sc->sc_async, NULL);
4741.1Sjmcneill		mutex_exit(&proc_lock);
4751.1Sjmcneill		break;
4761.1Sjmcneill
4771.1Sjmcneill	/* XXX this is not the most general solution. */
4781.1Sjmcneill	case TIOCSPGRP:
4791.1Sjmcneill		mutex_enter(&proc_lock);
4801.1Sjmcneill		if (sc->sc_async == NULL) {
4811.1Sjmcneill			mutex_exit(&proc_lock);
4821.1Sjmcneill			return EINVAL;
4831.1Sjmcneill		}
4841.1Sjmcneill		if (*(int *)addr != sc->sc_async->p_pgid) {
4851.1Sjmcneill			mutex_exit(&proc_lock);
4861.1Sjmcneill			return EPERM;
4871.1Sjmcneill		}
4881.1Sjmcneill		mutex_exit(&proc_lock);
4891.1Sjmcneill		break;
4901.1Sjmcneill
4911.1Sjmcneill	case FIOSETOWN:
4921.1Sjmcneill		mutex_enter(&proc_lock);
4931.1Sjmcneill		if (sc->sc_async == NULL) {
4941.1Sjmcneill			mutex_exit(&proc_lock);
4951.1Sjmcneill			return EINVAL;
4961.1Sjmcneill		}
4971.1Sjmcneill		if (-*(int *)addr != sc->sc_async->p_pgid
4981.1Sjmcneill		    && *(int *)addr != sc->sc_async->p_pid) {
4991.1Sjmcneill			mutex_exit(&proc_lock);
5001.1Sjmcneill			return EPERM;
5011.1Sjmcneill		}
5021.1Sjmcneill		mutex_exit(&proc_lock);
5031.1Sjmcneill		break;
5041.1Sjmcneill
5051.1Sjmcneill	case USB_HID_GET_RAW:
5061.1Sjmcneill		*(int *)addr = sc->sc_raw;
5071.1Sjmcneill		break;
5081.1Sjmcneill
5091.1Sjmcneill	case USB_HID_SET_RAW:
5101.1Sjmcneill		sc->sc_raw = *(int *)addr;
5111.1Sjmcneill		break;
5121.1Sjmcneill
5131.1Sjmcneill	case USB_GET_REPORT_DESC:
5141.1Sjmcneill		hidev_get_report_desc(sc->sc_hidev, &desc, &size);
5151.1Sjmcneill		rd = (struct usb_ctl_report_desc *)addr;
5161.1Sjmcneill		size = uimin(size, sizeof(rd->ucrd_data));
5171.1Sjmcneill		rd->ucrd_size = size;
5181.1Sjmcneill		memcpy(rd->ucrd_data, desc, size);
5191.1Sjmcneill		break;
5201.1Sjmcneill
5211.1Sjmcneill	case USB_SET_IMMED:
5221.1Sjmcneill		if (*(int *)addr) {
5231.1Sjmcneill			extra = sc->sc_report_id != 0;
5241.1Sjmcneill			if (sc->sc_isize + extra > sizeof(buffer))
5251.1Sjmcneill				return ENOBUFS;
5261.1Sjmcneill			err = hidev_get_report(sc->sc_hidev, UHID_INPUT_REPORT,
5271.1Sjmcneill					       buffer, sc->sc_isize + extra);
5281.1Sjmcneill			if (err)
5291.1Sjmcneill				return EOPNOTSUPP;
5301.1Sjmcneill
5311.1Sjmcneill			atomic_or_32(&sc->sc_state, UHID_IMMED);
5321.1Sjmcneill		} else
5331.1Sjmcneill			atomic_and_32(&sc->sc_state, ~UHID_IMMED);
5341.1Sjmcneill		break;
5351.1Sjmcneill
5361.1Sjmcneill	case USB_GET_REPORT:
5371.1Sjmcneill		re = (struct usb_ctl_report *)addr;
5381.1Sjmcneill		switch (re->ucr_report) {
5391.1Sjmcneill		case UHID_INPUT_REPORT:
5401.1Sjmcneill			size = sc->sc_isize;
5411.1Sjmcneill			break;
5421.1Sjmcneill		case UHID_OUTPUT_REPORT:
5431.1Sjmcneill			size = sc->sc_osize;
5441.1Sjmcneill			break;
5451.1Sjmcneill		case UHID_FEATURE_REPORT:
5461.1Sjmcneill			size = sc->sc_fsize;
5471.1Sjmcneill			break;
5481.1Sjmcneill		default:
5491.1Sjmcneill			return EINVAL;
5501.1Sjmcneill		}
5511.1Sjmcneill		extra = sc->sc_report_id != 0;
5521.1Sjmcneill		if (size + extra > sizeof(re->ucr_data))
5531.1Sjmcneill			return ENOBUFS;
5541.1Sjmcneill		err = hidev_get_report(sc->sc_hidev, re->ucr_report,
5551.1Sjmcneill		    re->ucr_data, size + extra);
5561.1Sjmcneill		if (extra)
5571.1Sjmcneill			memmove(re->ucr_data, re->ucr_data+1, size);
5581.1Sjmcneill		if (err)
5591.1Sjmcneill			return EIO;
5601.1Sjmcneill		break;
5611.1Sjmcneill
5621.1Sjmcneill	case USB_SET_REPORT:
5631.1Sjmcneill		re = (struct usb_ctl_report *)addr;
5641.1Sjmcneill		switch (re->ucr_report) {
5651.1Sjmcneill		case UHID_INPUT_REPORT:
5661.1Sjmcneill			size = sc->sc_isize;
5671.1Sjmcneill			break;
5681.1Sjmcneill		case UHID_OUTPUT_REPORT:
5691.1Sjmcneill			size = sc->sc_osize;
5701.1Sjmcneill			break;
5711.1Sjmcneill		case UHID_FEATURE_REPORT:
5721.1Sjmcneill			size = sc->sc_fsize;
5731.1Sjmcneill			break;
5741.1Sjmcneill		default:
5751.1Sjmcneill			return EINVAL;
5761.1Sjmcneill		}
5771.1Sjmcneill		if (size > sizeof(re->ucr_data))
5781.1Sjmcneill			return ENOBUFS;
5791.1Sjmcneill		err = hidev_set_report(sc->sc_hidev, re->ucr_report,
5801.1Sjmcneill		    re->ucr_data, size);
5811.1Sjmcneill		if (err)
5821.1Sjmcneill			return EIO;
5831.1Sjmcneill		break;
5841.1Sjmcneill
5851.1Sjmcneill	case USB_GET_REPORT_ID:
5861.1Sjmcneill		*(int *)addr = sc->sc_report_id;
5871.1Sjmcneill		break;
5881.1Sjmcneill
5891.1Sjmcneill	default:
5901.1Sjmcneill		if (sc->sc_ioctl != NULL) {
5911.1Sjmcneill			return sc->sc_ioctl(sc, cmd, addr, flag, l);
5921.1Sjmcneill		}
5931.1Sjmcneill		return EINVAL;
5941.1Sjmcneill	}
5951.1Sjmcneill	return 0;
5961.1Sjmcneill}
5971.1Sjmcneill
5981.1Sjmcneillstatic int
5991.1Sjmcneilluhidpoll(dev_t dev, int events, struct lwp *l)
6001.1Sjmcneill{
6011.1Sjmcneill	struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev));
6021.1Sjmcneill	int revents = 0;
6031.1Sjmcneill
6041.1Sjmcneill	mutex_enter(&sc->sc_lock);
6051.1Sjmcneill	if (events & (POLLOUT | POLLWRNORM))
6061.1Sjmcneill		revents |= events & (POLLOUT | POLLWRNORM);
6071.1Sjmcneill	if (events & (POLLIN | POLLRDNORM)) {
6081.1Sjmcneill		if (sc->sc_q.c_cc > 0)
6091.1Sjmcneill			revents |= events & (POLLIN | POLLRDNORM);
6101.1Sjmcneill		else
6111.1Sjmcneill			selrecord(l, &sc->sc_rsel);
6121.1Sjmcneill	}
6131.1Sjmcneill	mutex_exit(&sc->sc_lock);
6141.1Sjmcneill
6151.1Sjmcneill	return revents;
6161.1Sjmcneill}
6171.1Sjmcneill
6181.1Sjmcneillstatic void
6191.1Sjmcneillfilt_uhidrdetach(struct knote *kn)
6201.1Sjmcneill{
6211.1Sjmcneill	struct uhid_softc *sc = kn->kn_hook;
6221.1Sjmcneill
6231.1Sjmcneill	mutex_enter(&sc->sc_lock);
6241.1Sjmcneill	selremove_knote(&sc->sc_rsel, kn);
6251.1Sjmcneill	mutex_exit(&sc->sc_lock);
6261.1Sjmcneill}
6271.1Sjmcneill
6281.1Sjmcneillstatic int
6291.1Sjmcneillfilt_uhidread(struct knote *kn, long hint)
6301.1Sjmcneill{
6311.1Sjmcneill	struct uhid_softc *sc = kn->kn_hook;
6321.1Sjmcneill
6331.1Sjmcneill	if (hint == NOTE_SUBMIT)
6341.1Sjmcneill		KASSERT(mutex_owned(&sc->sc_lock));
6351.1Sjmcneill	else
6361.1Sjmcneill		mutex_enter(&sc->sc_lock);
6371.1Sjmcneill
6381.1Sjmcneill	kn->kn_data = sc->sc_q.c_cc;
6391.1Sjmcneill
6401.1Sjmcneill	if (hint == NOTE_SUBMIT)
6411.1Sjmcneill		KASSERT(mutex_owned(&sc->sc_lock));
6421.1Sjmcneill	else
6431.1Sjmcneill		mutex_exit(&sc->sc_lock);
6441.1Sjmcneill
6451.1Sjmcneill	return kn->kn_data > 0;
6461.1Sjmcneill}
6471.1Sjmcneill
6481.1Sjmcneillstatic const struct filterops uhidread_filtops = {
6491.1Sjmcneill	.f_flags = FILTEROP_ISFD,
6501.1Sjmcneill	.f_attach = NULL,
6511.1Sjmcneill	.f_detach = filt_uhidrdetach,
6521.1Sjmcneill	.f_event = filt_uhidread,
6531.1Sjmcneill};
6541.1Sjmcneill
6551.1Sjmcneillstatic int
6561.1Sjmcneilluhidkqfilter(dev_t dev, struct knote *kn)
6571.1Sjmcneill{
6581.1Sjmcneill	struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev));
6591.1Sjmcneill
6601.1Sjmcneill	switch (kn->kn_filter) {
6611.1Sjmcneill	case EVFILT_READ:
6621.1Sjmcneill		kn->kn_fop = &uhidread_filtops;
6631.1Sjmcneill		kn->kn_hook = sc;
6641.1Sjmcneill		mutex_enter(&sc->sc_lock);
6651.1Sjmcneill		selrecord_knote(&sc->sc_rsel, kn);
6661.1Sjmcneill		mutex_exit(&sc->sc_lock);
6671.1Sjmcneill		return 0;
6681.1Sjmcneill
6691.1Sjmcneill	case EVFILT_WRITE:
6701.1Sjmcneill		kn->kn_fop = &seltrue_filtops;
6711.1Sjmcneill		return 0;
6721.1Sjmcneill
6731.1Sjmcneill	default:
6741.1Sjmcneill		return EINVAL;
6751.1Sjmcneill	}
6761.1Sjmcneill}
677