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