diff --git a/sys/conf/options b/sys/conf/options index 789def58cb48..e69ee92d1136 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -1,1022 +1,1023 @@ # $FreeBSD$ # # On the handling of kernel options # # All kernel options should be listed in NOTES, with suitable # descriptions. Negative options (options that make some code not # compile) should be commented out; LINT (generated from NOTES) should # compile as much code as possible. Try to structure option-using # code so that a single option only switch code on, or only switch # code off, to make it possible to have a full compile-test. If # necessary, you can check for COMPILING_LINT to get maximum code # coverage. # # All new options shall also be listed in either "conf/options" or # "conf/options.". Options that affect a single source-file # .[c|s] should be directed into "opt_.h", while options # that affect multiple files should either go in "opt_global.h" if # this is a kernel-wide option (used just about everywhere), or in # "opt_.h" if it affects only some files. # Note that the effect of listing only an option without a # header-file-name in conf/options (and cousins) is that the last # convention is followed. # # This handling scheme is not yet fully implemented. # # # Format of this file: # Option name filename # # If filename is missing, the default is # opt_.h AAC_DEBUG opt_aac.h AACRAID_DEBUG opt_aacraid.h AHC_ALLOW_MEMIO opt_aic7xxx.h AHC_TMODE_ENABLE opt_aic7xxx.h AHC_DUMP_EEPROM opt_aic7xxx.h AHC_DEBUG opt_aic7xxx.h AHC_DEBUG_OPTS opt_aic7xxx.h AHC_REG_PRETTY_PRINT opt_aic7xxx.h AHD_DEBUG opt_aic79xx.h AHD_DEBUG_OPTS opt_aic79xx.h AHD_TMODE_ENABLE opt_aic79xx.h AHD_REG_PRETTY_PRINT opt_aic79xx.h TWA_DEBUG opt_twa.h # Debugging options. ALT_BREAK_TO_DEBUGGER opt_kdb.h BREAK_TO_DEBUGGER opt_kdb.h BUF_TRACKING opt_global.h DDB DDB_BUFR_SIZE opt_ddb.h DDB_CAPTURE_DEFAULTBUFSIZE opt_ddb.h DDB_CAPTURE_MAXBUFSIZE opt_ddb.h DDB_CTF opt_ddb.h DDB_NUMSYM opt_ddb.h EARLY_PRINTF opt_global.h FULL_BUF_TRACKING opt_global.h GDB KDB opt_global.h KDB_TRACE opt_kdb.h KDB_UNATTENDED opt_kdb.h KLD_DEBUG opt_kld.h NUM_CORE_FILES opt_global.h QUEUE_MACRO_DEBUG_TRACE opt_global.h QUEUE_MACRO_DEBUG_TRASH opt_global.h SYSCTL_DEBUG opt_sysctl.h TEXTDUMP_PREFERRED opt_ddb.h TEXTDUMP_VERBOSE opt_ddb.h TSLOG opt_global.h TSLOGSIZE opt_global.h # Miscellaneous options. ALQ ALTERA_SDCARD_FAST_SIM opt_altera_sdcard.h ATSE_CFI_HACK opt_cfi.h AUDIT opt_global.h BOOTHOWTO opt_global.h BOOTVERBOSE opt_global.h CALLOUT_PROFILING CAPABILITIES opt_capsicum.h CAPABILITY_MODE opt_capsicum.h COMPAT_43 opt_global.h COMPAT_43TTY opt_global.h COMPAT_FREEBSD4 opt_global.h COMPAT_FREEBSD5 opt_global.h COMPAT_FREEBSD6 opt_global.h COMPAT_FREEBSD7 opt_global.h COMPAT_FREEBSD9 opt_global.h COMPAT_FREEBSD10 opt_global.h COMPAT_FREEBSD11 opt_global.h COMPAT_FREEBSD12 opt_global.h COMPAT_CLOUDABI32 opt_dontuse.h COMPAT_CLOUDABI64 opt_dontuse.h COMPAT_LINUXKPI opt_dontuse.h _COMPAT_LINUX32 opt_compat.h # XXX: make sure opt_compat.h exists COMPILING_LINT opt_global.h CY_PCI_FASTINTR DEADLKRES opt_watchdog.h EXPERIMENTAL opt_global.h EXT_RESOURCES opt_global.h DIRECTIO FILEMON opt_dontuse.h FFCLOCK FULL_PREEMPTION opt_sched.h GZIO opt_gzio.h IMAGACT_BINMISC opt_dontuse.h IPI_PREEMPTION opt_sched.h GEOM_BDE opt_geom.h GEOM_CACHE opt_geom.h GEOM_CONCAT opt_geom.h GEOM_ELI opt_geom.h GEOM_GATE opt_geom.h GEOM_JOURNAL opt_geom.h GEOM_LABEL opt_geom.h GEOM_LABEL_GPT opt_geom.h GEOM_LINUX_LVM opt_geom.h GEOM_MAP opt_geom.h GEOM_MIRROR opt_geom.h GEOM_MOUNTVER opt_geom.h GEOM_MULTIPATH opt_geom.h GEOM_NOP opt_geom.h GEOM_PART_APM opt_geom.h GEOM_PART_BSD opt_geom.h GEOM_PART_BSD64 opt_geom.h GEOM_PART_EBR opt_geom.h GEOM_PART_GPT opt_geom.h GEOM_PART_LDM opt_geom.h GEOM_PART_MBR opt_geom.h GEOM_PART_VTOC8 opt_geom.h GEOM_RAID opt_geom.h GEOM_RAID3 opt_geom.h GEOM_SHSEC opt_geom.h GEOM_STRIPE opt_geom.h GEOM_UZIP opt_geom.h GEOM_UZIP_DEBUG opt_geom.h GEOM_VINUM opt_geom.h GEOM_VIRSTOR opt_geom.h GEOM_ZERO opt_geom.h IFLIB opt_iflib.h KDTRACE_HOOKS opt_global.h KDTRACE_FRAME opt_kdtrace.h KN_HASHSIZE opt_kqueue.h KSTACK_MAX_PAGES KSTACK_PAGES KSTACK_USAGE_PROF KTRACE KTRACE_REQUEST_POOL opt_ktrace.h LIBICONV MAC opt_global.h MAC_BIBA opt_dontuse.h MAC_BSDEXTENDED opt_dontuse.h MAC_IFOFF opt_dontuse.h MAC_LOMAC opt_dontuse.h MAC_MLS opt_dontuse.h MAC_NONE opt_dontuse.h MAC_NTPD opt_dontuse.h MAC_PARTITION opt_dontuse.h MAC_PORTACL opt_dontuse.h MAC_SEEOTHERUIDS opt_dontuse.h MAC_STATIC opt_mac.h MAC_STUB opt_dontuse.h MAC_TEST opt_dontuse.h MAC_VERIEXEC opt_dontuse.h MAC_VERIEXEC_SHA1 opt_dontuse.h MAC_VERIEXEC_SHA256 opt_dontuse.h MAC_VERIEXEC_SHA384 opt_dontuse.h MAC_VERIEXEC_SHA512 opt_dontuse.h MD_ROOT opt_md.h MD_ROOT_FSTYPE opt_md.h MD_ROOT_READONLY opt_md.h MD_ROOT_SIZE opt_md.h MD_ROOT_MEM opt_md.h MFI_DEBUG opt_mfi.h MFI_DECODE_LOG opt_mfi.h MPROF_BUFFERS opt_mprof.h MPROF_HASH_SIZE opt_mprof.h NEW_PCIB opt_global.h NO_ADAPTIVE_MUTEXES opt_adaptive_mutexes.h NO_ADAPTIVE_RWLOCKS NO_ADAPTIVE_SX NO_OBSOLETE_CODE opt_global.h NO_SYSCTL_DESCR opt_global.h NSWBUF_MIN opt_param.h MBUF_PACKET_ZONE_DISABLE opt_global.h PANIC_REBOOT_WAIT_TIME opt_panic.h PCI_HP opt_pci.h PCI_IOV opt_global.h PPC_DEBUG opt_ppc.h PPC_PROBE_CHIPSET opt_ppc.h PPS_SYNC opt_ntp.h PREEMPTION opt_sched.h QUOTA SCHED_4BSD opt_sched.h SCHED_STATS opt_sched.h SCHED_ULE opt_sched.h SLEEPQUEUE_PROFILING SLHCI_DEBUG opt_slhci.h STACK opt_stack.h SUIDDIR MSGMNB opt_sysvipc.h MSGMNI opt_sysvipc.h MSGSEG opt_sysvipc.h MSGSSZ opt_sysvipc.h MSGTQL opt_sysvipc.h SEMMNI opt_sysvipc.h SEMMNS opt_sysvipc.h SEMMNU opt_sysvipc.h SEMMSL opt_sysvipc.h SEMOPM opt_sysvipc.h SEMUME opt_sysvipc.h SHMALL opt_sysvipc.h SHMMAX opt_sysvipc.h SHMMAXPGS opt_sysvipc.h SHMMIN opt_sysvipc.h SHMMNI opt_sysvipc.h SHMSEG opt_sysvipc.h SYSVMSG opt_sysvipc.h SYSVSEM opt_sysvipc.h SYSVSHM opt_sysvipc.h SW_WATCHDOG opt_watchdog.h TCPHPTS opt_inet.h TURNSTILE_PROFILING UMTX_PROFILING UMTX_CHAINS opt_global.h VERBOSE_SYSINIT ZSTDIO opt_zstdio.h # Sanitizers COVERAGE opt_global.h KCOV KCSAN opt_global.h KUBSAN opt_global.h # POSIX kernel options P1003_1B_MQUEUE opt_posix.h P1003_1B_SEMAPHORES opt_posix.h _KPOSIX_PRIORITY_SCHEDULING opt_posix.h # Do we want the config file compiled into the kernel? INCLUDE_CONFIG_FILE opt_config.h # Options for static filesystems. These should only be used at config # time, since the corresponding lkms cannot work if there are any static # dependencies. Unusability is enforced by hiding the defines for the # options in a never-included header. AUTOFS opt_dontuse.h CD9660 opt_dontuse.h EXT2FS opt_dontuse.h FDESCFS opt_dontuse.h FFS opt_dontuse.h FUSEFS opt_dontuse.h MSDOSFS opt_dontuse.h NULLFS opt_dontuse.h PROCFS opt_dontuse.h PSEUDOFS opt_dontuse.h SMBFS opt_dontuse.h TMPFS opt_dontuse.h UDF opt_dontuse.h UNIONFS opt_dontuse.h ZFS opt_dontuse.h # Pseudofs debugging PSEUDOFS_TRACE opt_pseudofs.h # In-kernel GSS-API KGSSAPI opt_kgssapi.h KGSSAPI_DEBUG opt_kgssapi.h # These static filesystems have one slightly bogus static dependency in # sys/i386/i386/autoconf.c. If any of these filesystems are # statically compiled into the kernel, code for mounting them as root # filesystems will be enabled - but look below. # NFSCL - client # NFSD - server NFSCL opt_nfs.h NFSD opt_nfs.h # filesystems and libiconv bridge CD9660_ICONV opt_dontuse.h MSDOSFS_ICONV opt_dontuse.h UDF_ICONV opt_dontuse.h # If you are following the conditions in the copyright, # you can enable soft-updates which will speed up a lot of thigs # and make the system safer from crashes at the same time. # otherwise a STUB module will be compiled in. SOFTUPDATES opt_ffs.h # On small, embedded systems, it can be useful to turn off support for # snapshots. It saves about 30-40k for a feature that would be lightly # used, if it is used at all. NO_FFS_SNAPSHOT opt_ffs.h # Enabling this option turns on support for Access Control Lists in UFS, # which can be used to support high security configurations. Depends on # UFS_EXTATTR. UFS_ACL opt_ufs.h # Enabling this option turns on support for extended attributes in UFS-based # filesystems, which can be used to support high security configurations # as well as new filesystem features. UFS_EXTATTR opt_ufs.h UFS_EXTATTR_AUTOSTART opt_ufs.h # Enable fast hash lookups for large directories on UFS-based filesystems. UFS_DIRHASH opt_ufs.h # Enable gjournal-based UFS journal. UFS_GJOURNAL opt_ufs.h # The below sentence is not in English, and neither is this one. # We plan to remove the static dependences above, with a # _ROOT option to control if it usable as root. This list # allows these options to be present in config files already (though # they won't make any difference yet). NFS_ROOT opt_nfsroot.h # SMB/CIFS requester NETSMB opt_netsmb.h # Enable debugnet(4) networking support. DEBUGNET opt_global.h # Enable netdump(4) client support. NETDUMP opt_global.h # Enable netgdb(4) support. NETGDB opt_global.h # Options used only in subr_param.c. HZ opt_param.h MAXFILES opt_param.h NBUF opt_param.h NSFBUFS opt_param.h VM_BCACHE_SIZE_MAX opt_param.h VM_SWZONE_SIZE_MAX opt_param.h MAXUSERS DFLDSIZ opt_param.h MAXDSIZ opt_param.h MAXSSIZ opt_param.h # Generic SCSI options. CAM_MAX_HIGHPOWER opt_cam.h CAMDEBUG opt_cam.h CAM_DEBUG_COMPILE opt_cam.h CAM_DEBUG_DELAY opt_cam.h CAM_DEBUG_BUS opt_cam.h CAM_DEBUG_TARGET opt_cam.h CAM_DEBUG_LUN opt_cam.h CAM_DEBUG_FLAGS opt_cam.h CAM_BOOT_DELAY opt_cam.h CAM_IOSCHED_DYNAMIC opt_cam.h CAM_IO_STATS opt_cam.h CAM_TEST_FAILURE opt_cam.h SCSI_DELAY opt_scsi.h SCSI_NO_SENSE_STRINGS opt_scsi.h SCSI_NO_OP_STRINGS opt_scsi.h # Options used only in cam/ata/ata_da.c ATA_STATIC_ID opt_ada.h # Options used only in cam/scsi/scsi_cd.c CHANGER_MIN_BUSY_SECONDS opt_cd.h CHANGER_MAX_BUSY_SECONDS opt_cd.h # Options used only in cam/scsi/scsi_da.c DA_TRACK_REFS opt_da.h # Options used only in cam/scsi/scsi_sa.c. SA_IO_TIMEOUT opt_sa.h SA_SPACE_TIMEOUT opt_sa.h SA_REWIND_TIMEOUT opt_sa.h SA_ERASE_TIMEOUT opt_sa.h SA_1FM_AT_EOD opt_sa.h # Options used only in cam/scsi/scsi_pt.c SCSI_PT_DEFAULT_TIMEOUT opt_pt.h # Options used only in cam/scsi/scsi_ses.c SES_ENABLE_PASSTHROUGH opt_ses.h # Options used in dev/sym/ (Symbios SCSI driver). SYM_SETUP_SCSI_DIFF opt_sym.h #-HVD support for 825a, 875, 885 # disabled:0 (default), enabled:1 SYM_SETUP_PCI_PARITY opt_sym.h #-PCI parity checking # disabled:0, enabled:1 (default) SYM_SETUP_MAX_LUN opt_sym.h #-Number of LUNs supported # default:8, range:[1..64] # Options used only in dev/isp/* ISP_TARGET_MODE opt_isp.h ISP_FW_CRASH_DUMP opt_isp.h ISP_DEFAULT_ROLES opt_isp.h ISP_INTERNAL_TARGET opt_isp.h ISP_FCTAPE_OFF opt_isp.h # Options used only in dev/iscsi ISCSI_INITIATOR_DEBUG opt_iscsi_initiator.h # Net stuff. ACCEPT_FILTER_DATA ACCEPT_FILTER_DNS ACCEPT_FILTER_HTTP ALTQ opt_global.h ALTQ_CBQ opt_altq.h ALTQ_CDNR opt_altq.h ALTQ_CODEL opt_altq.h ALTQ_DEBUG opt_altq.h ALTQ_HFSC opt_altq.h ALTQ_FAIRQ opt_altq.h ALTQ_NOPCC opt_altq.h ALTQ_PRIQ opt_altq.h ALTQ_RED opt_altq.h ALTQ_RIO opt_altq.h BOOTP opt_bootp.h BOOTP_BLOCKSIZE opt_bootp.h BOOTP_COMPAT opt_bootp.h BOOTP_NFSROOT opt_bootp.h BOOTP_NFSV3 opt_bootp.h BOOTP_WIRED_TO opt_bootp.h DEVICE_POLLING DUMMYNET opt_ipdn.h RATELIMIT opt_ratelimit.h RATELIMIT_DEBUG opt_ratelimit.h INET opt_inet.h INET6 opt_inet6.h STATS opt_global.h IPDIVERT IPFILTER opt_ipfilter.h IPFILTER_DEFAULT_BLOCK opt_ipfilter.h IPFILTER_LOG opt_ipfilter.h IPFILTER_LOOKUP opt_ipfilter.h IPFIREWALL opt_ipfw.h IPFIREWALL_DEFAULT_TO_ACCEPT opt_ipfw.h IPFIREWALL_NAT opt_ipfw.h IPFIREWALL_NAT64 opt_ipfw.h IPFIREWALL_NPTV6 opt_ipfw.h IPFIREWALL_VERBOSE opt_ipfw.h IPFIREWALL_VERBOSE_LIMIT opt_ipfw.h IPFIREWALL_PMOD opt_ipfw.h IPSEC opt_ipsec.h IPSEC_DEBUG opt_ipsec.h IPSEC_SUPPORT opt_ipsec.h IPSTEALTH KERN_TLS KRPC LIBALIAS LIBMCHAIN MBUF_PROFILING MBUF_STRESS_TEST MROUTING opt_mrouting.h NFSLOCKD PCBGROUP opt_pcbgroup.h PF_DEFAULT_TO_DROP opt_pf.h ROUTE_MPATH opt_route.h ROUTETABLES opt_route.h FIB_ALGO opt_route.h RSS opt_rss.h SLIP_IFF_OPTS opt_slip.h TCPDEBUG TCPPCAP opt_global.h SIFTR TCP_BLACKBOX opt_global.h TCP_HHOOK opt_inet.h TCP_OFFLOAD opt_inet.h # Enable code to dispatch TCP offloading TCP_RFC7413 opt_inet.h TCP_RFC7413_MAX_KEYS opt_inet.h TCP_RFC7413_MAX_PSKS opt_inet.h TCP_SIGNATURE opt_ipsec.h VLAN_ARRAY opt_vlan.h XDR XBONEHACK # # SCTP # SCTP opt_sctp.h SCTP_SUPPORT opt_sctp.h SCTP_DEBUG opt_sctp.h # Enable debug printfs SCTP_LOCK_LOGGING opt_sctp.h # Log to KTR lock activity SCTP_MBUF_LOGGING opt_sctp.h # Log to KTR general mbuf aloc/free SCTP_MBCNT_LOGGING opt_sctp.h # Log to KTR mbcnt activity SCTP_PACKET_LOGGING opt_sctp.h # Log to a packet buffer last N packets SCTP_LTRACE_CHUNKS opt_sctp.h # Log to KTR chunks processed SCTP_LTRACE_ERRORS opt_sctp.h # Log to KTR error returns. SCTP_USE_PERCPU_STAT opt_sctp.h # Use per cpu stats. SCTP_MCORE_INPUT opt_sctp.h # Have multiple input threads for input mbufs SCTP_LOCAL_TRACE_BUF opt_sctp.h # Use tracebuffer exported via sysctl SCTP_DETAILED_STR_STATS opt_sctp.h # Use per PR-SCTP policy stream stats # # # # Netgraph(4). Use option NETGRAPH to enable the base netgraph code. # Each netgraph node type can be either be compiled into the kernel # or loaded dynamically. To get the former, include the corresponding # option below. Each type has its own man page, e.g. ng_async(4). NETGRAPH NETGRAPH_DEBUG opt_netgraph.h NETGRAPH_ASYNC opt_netgraph.h NETGRAPH_ATMLLC opt_netgraph.h NETGRAPH_ATM_ATMPIF opt_netgraph.h NETGRAPH_BLUETOOTH opt_netgraph.h NETGRAPH_BLUETOOTH_BT3C opt_netgraph.h NETGRAPH_BLUETOOTH_H4 opt_netgraph.h NETGRAPH_BLUETOOTH_HCI opt_netgraph.h NETGRAPH_BLUETOOTH_L2CAP opt_netgraph.h NETGRAPH_BLUETOOTH_SOCKET opt_netgraph.h NETGRAPH_BLUETOOTH_UBT opt_netgraph.h NETGRAPH_BLUETOOTH_UBTBCMFW opt_netgraph.h NETGRAPH_BPF opt_netgraph.h NETGRAPH_BRIDGE opt_netgraph.h NETGRAPH_CAR opt_netgraph.h NETGRAPH_CHECKSUM opt_netgraph.h NETGRAPH_CISCO opt_netgraph.h NETGRAPH_DEFLATE opt_netgraph.h NETGRAPH_DEVICE opt_netgraph.h NETGRAPH_ECHO opt_netgraph.h NETGRAPH_EIFACE opt_netgraph.h NETGRAPH_ETHER opt_netgraph.h NETGRAPH_ETHER_ECHO opt_netgraph.h NETGRAPH_FEC opt_netgraph.h NETGRAPH_FRAME_RELAY opt_netgraph.h NETGRAPH_GIF opt_netgraph.h NETGRAPH_GIF_DEMUX opt_netgraph.h NETGRAPH_HOLE opt_netgraph.h NETGRAPH_IFACE opt_netgraph.h NETGRAPH_IP_INPUT opt_netgraph.h NETGRAPH_IPFW opt_netgraph.h NETGRAPH_KSOCKET opt_netgraph.h NETGRAPH_L2TP opt_netgraph.h NETGRAPH_LMI opt_netgraph.h NETGRAPH_MPPC_COMPRESSION opt_netgraph.h NETGRAPH_MPPC_ENCRYPTION opt_netgraph.h NETGRAPH_NAT opt_netgraph.h NETGRAPH_NETFLOW opt_netgraph.h NETGRAPH_ONE2MANY opt_netgraph.h NETGRAPH_PATCH opt_netgraph.h NETGRAPH_PIPE opt_netgraph.h NETGRAPH_PPP opt_netgraph.h NETGRAPH_PPPOE opt_netgraph.h NETGRAPH_PPTPGRE opt_netgraph.h NETGRAPH_PRED1 opt_netgraph.h NETGRAPH_RFC1490 opt_netgraph.h NETGRAPH_SOCKET opt_netgraph.h NETGRAPH_SPLIT opt_netgraph.h NETGRAPH_SPPP opt_netgraph.h NETGRAPH_TAG opt_netgraph.h NETGRAPH_TCPMSS opt_netgraph.h NETGRAPH_TEE opt_netgraph.h NETGRAPH_TTY opt_netgraph.h NETGRAPH_UI opt_netgraph.h NETGRAPH_VJC opt_netgraph.h NETGRAPH_VLAN opt_netgraph.h # NgATM options NGATM_ATM opt_netgraph.h NGATM_ATMBASE opt_netgraph.h NGATM_SSCOP opt_netgraph.h NGATM_SSCFU opt_netgraph.h NGATM_UNI opt_netgraph.h NGATM_CCATM opt_netgraph.h # DRM options DRM_DEBUG opt_drm.h TI_SF_BUF_JUMBO opt_ti.h TI_JUMBO_HDRSPLIT opt_ti.h # Misc debug flags. Most of these should probably be replaced with # 'DEBUG', and then let people recompile just the interesting modules # with 'make CC="cc -DDEBUG"'. CLUSTERDEBUG opt_debug_cluster.h DEBUG_1284 opt_ppb_1284.h LPT_DEBUG opt_lpt.h PLIP_DEBUG opt_plip.h LOCKF_DEBUG opt_debug_lockf.h SI_DEBUG opt_debug_si.h IFMEDIA_DEBUG opt_ifmedia.h # Fb options FB_DEBUG opt_fb.h FB_INSTALL_CDEV opt_fb.h # ppbus related options PERIPH_1284 opt_ppb_1284.h DONTPROBE_1284 opt_ppb_1284.h # smbus related options ENABLE_ALART opt_intpm.h # These cause changes all over the kernel BLKDEV_IOSIZE opt_global.h BURN_BRIDGES opt_global.h DEBUG opt_global.h DEBUG_LOCKS opt_global.h DEBUG_VFS_LOCKS opt_global.h DFLTPHYS opt_global.h DIAGNOSTIC opt_global.h INVARIANT_SUPPORT opt_global.h INVARIANTS opt_global.h KASSERT_PANIC_OPTIONAL opt_global.h MAXCPU opt_global.h MAXMEMDOM opt_global.h MAXPHYS opt_maxphys.h MCLSHIFT opt_global.h MUTEX_NOINLINE opt_global.h LOCK_PROFILING opt_global.h LOCK_PROFILING_FAST opt_global.h MSIZE opt_global.h REGRESSION opt_global.h RWLOCK_NOINLINE opt_global.h SX_NOINLINE opt_global.h VFS_BIO_DEBUG opt_global.h # These are VM related options VM_KMEM_SIZE opt_vm.h VM_KMEM_SIZE_SCALE opt_vm.h VM_KMEM_SIZE_MAX opt_vm.h VM_NRESERVLEVEL opt_vm.h VM_LEVEL_0_ORDER opt_vm.h NO_SWAPPING opt_vm.h MALLOC_MAKE_FAILURES opt_vm.h MALLOC_PROFILE opt_vm.h MALLOC_DEBUG_MAXZONES opt_vm.h # The MemGuard replacement allocator used for tamper-after-free detection DEBUG_MEMGUARD opt_vm.h # The RedZone malloc(9) protection DEBUG_REDZONE opt_vm.h # Standard SMP options EARLY_AP_STARTUP opt_global.h SMP opt_global.h NUMA opt_global.h # Size of the kernel message buffer MSGBUF_SIZE opt_msgbuf.h # NFS options NFS_MINATTRTIMO opt_nfs.h NFS_MAXATTRTIMO opt_nfs.h NFS_MINDIRATTRTIMO opt_nfs.h NFS_MAXDIRATTRTIMO opt_nfs.h NFS_DEBUG opt_nfs.h # TMPFS options TMPFS_PAGES_MINRESERVED opt_tmpfs.h # Options for uart(4) UART_PPS_ON_CTS opt_uart.h UART_POLL_FREQ opt_uart.h UART_DEV_TOLERANCE_PCT opt_uart.h # options for bus/device framework BUS_DEBUG opt_bus.h # options for USB support USB_DEBUG opt_usb.h USB_HOST_ALIGN opt_usb.h USB_REQ_DEBUG opt_usb.h USB_TEMPLATE opt_usb.h USB_VERBOSE opt_usb.h USB_DMA_SINGLE_ALLOC opt_usb.h USB_EHCI_BIG_ENDIAN_DESC opt_usb.h U3G_DEBUG opt_u3g.h UKBD_DFLT_KEYMAP opt_ukbd.h UPLCOM_INTR_INTERVAL opt_uplcom.h UVSCOM_DEFAULT_OPKTSIZE opt_uvscom.h UVSCOM_INTR_INTERVAL opt_uvscom.h USBHID_ENABLED opt_usb.h # options for the Realtek rtwn driver RTWN_DEBUG opt_rtwn.h RTWN_WITHOUT_UCODE opt_rtwn.h # Embedded system options INIT_PATH ROOTDEVNAME FDC_DEBUG opt_fdc.h PCFCLOCK_VERBOSE opt_pcfclock.h PCFCLOCK_MAX_RETRIES opt_pcfclock.h KTR opt_global.h KTR_ALQ opt_ktr.h KTR_MASK opt_ktr.h KTR_CPUMASK opt_ktr.h KTR_COMPILE opt_global.h KTR_BOOT_ENTRIES opt_global.h KTR_ENTRIES opt_global.h KTR_VERBOSE opt_ktr.h WITNESS opt_global.h WITNESS_KDB opt_witness.h WITNESS_NO_VNODE opt_witness.h WITNESS_SKIPSPIN opt_witness.h WITNESS_COUNT opt_witness.h OPENSOLARIS_WITNESS opt_global.h EPOCH_TRACE opt_global.h # options for ACPI support ACPI_DEBUG opt_acpi.h ACPI_MAX_TASKS opt_acpi.h ACPI_MAX_THREADS opt_acpi.h DEV_ACPI opt_acpi.h # options for IOMMU support IOMMU opt_iommu.h # ISA support DEV_ISA opt_isa.h ISAPNP opt_isa.h # various 'device presence' options. DEV_BPF opt_bpf.h DEV_CARP opt_carp.h DEV_NETMAP opt_global.h DEV_PCI opt_pci.h DEV_PF opt_pf.h DEV_PFLOG opt_pf.h DEV_PFSYNC opt_pf.h DEV_SPLASH opt_splash.h DEV_VLAN opt_vlan.h # ed driver ED_HPP opt_ed.h ED_3C503 opt_ed.h ED_SIC opt_ed.h # bce driver BCE_DEBUG opt_bce.h BCE_NVRAM_WRITE_SUPPORT opt_bce.h SOCKBUF_DEBUG opt_global.h # options for hifn driver HIFN_DEBUG opt_hifn.h HIFN_RNDTEST opt_hifn.h # options for safenet driver SAFE_DEBUG opt_safe.h SAFE_NO_RNG opt_safe.h SAFE_RNDTEST opt_safe.h # syscons/vt options MAXCONS opt_syscons.h SC_ALT_MOUSE_IMAGE opt_syscons.h SC_CUT_SPACES2TABS opt_syscons.h SC_CUT_SEPCHARS opt_syscons.h SC_DEBUG_LEVEL opt_syscons.h SC_DFLT_FONT opt_syscons.h SC_DFLT_TERM opt_syscons.h SC_DISABLE_KDBKEY opt_syscons.h SC_DISABLE_REBOOT opt_syscons.h SC_HISTORY_SIZE opt_syscons.h SC_KERNEL_CONS_ATTR opt_syscons.h SC_KERNEL_CONS_ATTRS opt_syscons.h SC_KERNEL_CONS_REV_ATTR opt_syscons.h SC_MOUSE_CHAR opt_syscons.h SC_NO_CUTPASTE opt_syscons.h SC_NO_FONT_LOADING opt_syscons.h SC_NO_HISTORY opt_syscons.h SC_NO_MODE_CHANGE opt_syscons.h SC_NO_SUSPEND_VTYSWITCH opt_syscons.h SC_NO_SYSMOUSE opt_syscons.h SC_NO_TERM_DUMB opt_syscons.h SC_NO_TERM_SC opt_syscons.h SC_NO_TERM_TEKEN opt_syscons.h SC_NORM_ATTR opt_syscons.h SC_NORM_REV_ATTR opt_syscons.h SC_PIXEL_MODE opt_syscons.h SC_RENDER_DEBUG opt_syscons.h SC_TWOBUTTON_MOUSE opt_syscons.h VT_ALT_TO_ESC_HACK opt_syscons.h VT_FB_MAX_WIDTH opt_syscons.h VT_FB_MAX_HEIGHT opt_syscons.h VT_MAXWINDOWS opt_syscons.h VT_TWOBUTTON_MOUSE opt_syscons.h DEV_SC opt_syscons.h DEV_VT opt_syscons.h # teken terminal emulator options TEKEN_CONS25 opt_teken.h TEKEN_UTF8 opt_teken.h TERMINAL_KERN_ATTR opt_teken.h TERMINAL_NORM_ATTR opt_teken.h # options for printf PRINTF_BUFR_SIZE opt_printf.h BOOT_TAG opt_printf.h BOOT_TAG_SZ opt_printf.h # kbd options KBD_DISABLE_KEYMAP_LOAD opt_kbd.h KBD_INSTALL_CDEV opt_kbd.h KBD_MAXRETRY opt_kbd.h KBD_MAXWAIT opt_kbd.h KBD_RESETDELAY opt_kbd.h KBDIO_DEBUG opt_kbd.h KBDMUX_DFLT_KEYMAP opt_kbdmux.h # options for the Atheros driver ATH_DEBUG opt_ath.h ATH_TXBUF opt_ath.h ATH_RXBUF opt_ath.h ATH_DIAGAPI opt_ath.h ATH_TX99_DIAG opt_ath.h ATH_ENABLE_11N opt_ath.h ATH_ENABLE_DFS opt_ath.h ATH_EEPROM_FIRMWARE opt_ath.h ATH_ENABLE_RADIOTAP_VENDOR_EXT opt_ath.h ATH_DEBUG_ALQ opt_ath.h ATH_KTR_INTR_DEBUG opt_ath.h # options for the Atheros hal # XXX For now, this breaks non-AR9130 chipsets, so only use it # XXX when actually targeting AR9130. AH_SUPPORT_AR9130 opt_ah.h # This is required for AR933x SoC support AH_SUPPORT_AR9330 opt_ah.h AH_SUPPORT_AR9340 opt_ah.h AH_SUPPORT_QCA9530 opt_ah.h AH_SUPPORT_QCA9550 opt_ah.h AH_DEBUG opt_ah.h AH_ASSERT opt_ah.h AH_DEBUG_ALQ opt_ah.h AH_REGOPS_FUNC opt_ah.h AH_WRITE_REGDOMAIN opt_ah.h AH_DEBUG_COUNTRY opt_ah.h AH_WRITE_EEPROM opt_ah.h AH_PRIVATE_DIAG opt_ah.h AH_NEED_DESC_SWAP opt_ah.h AH_USE_INIPDGAIN opt_ah.h AH_MAXCHAN opt_ah.h AH_RXCFG_SDMAMW_4BYTES opt_ah.h AH_INTERRUPT_DEBUGGING opt_ah.h # AR5416 and later interrupt mitigation # XXX do not use this for AR9130 AH_AR5416_INTERRUPT_MITIGATION opt_ah.h # options for the Altera mSGDMA driver (altera_msgdma) ALTERA_MSGDMA_DESC_STD opt_altera_msgdma.h ALTERA_MSGDMA_DESC_EXT opt_altera_msgdma.h ALTERA_MSGDMA_DESC_PF_STD opt_altera_msgdma.h ALTERA_MSGDMA_DESC_PF_EXT opt_altera_msgdma.h # options for the Broadcom BCM43xx driver (bwi) BWI_DEBUG opt_bwi.h BWI_DEBUG_VERBOSE opt_bwi.h # options for the Brodacom BCM43xx driver (bwn) BWN_DEBUG opt_bwn.h BWN_GPL_PHY opt_bwn.h BWN_USE_SIBA opt_bwn.h # Options for the SIBA driver SIBA_DEBUG opt_siba.h # options for the Marvell 8335 wireless driver MALO_DEBUG opt_malo.h MALO_TXBUF opt_malo.h MALO_RXBUF opt_malo.h # options for the Marvell wireless driver MWL_DEBUG opt_mwl.h MWL_TXBUF opt_mwl.h MWL_RXBUF opt_mwl.h MWL_DIAGAPI opt_mwl.h MWL_AGGR_SIZE opt_mwl.h MWL_TX_NODROP opt_mwl.h # Options for the Marvell NETA driver MVNETA_MULTIQUEUE opt_mvneta.h MVNETA_KTR opt_mvneta.h # Options for the Intel 802.11ac wireless driver IWM_DEBUG opt_iwm.h # Options for the Intel 802.11n wireless driver IWN_DEBUG opt_iwn.h # Options for the Intel 3945ABG wireless driver WPI_DEBUG opt_wpi.h # dcons options DCONS_BUF_SIZE opt_dcons.h DCONS_POLL_HZ opt_dcons.h DCONS_FORCE_CONSOLE opt_dcons.h DCONS_FORCE_GDB opt_dcons.h # HWPMC options HWPMC_DEBUG opt_global.h HWPMC_HOOKS HWPMC_MIPS_BACKTRACE opt_hwpmc_hooks.h # 802.11 support layer IEEE80211_DEBUG opt_wlan.h IEEE80211_DEBUG_REFCNT opt_wlan.h IEEE80211_SUPPORT_MESH opt_wlan.h IEEE80211_SUPPORT_SUPERG opt_wlan.h IEEE80211_SUPPORT_TDMA opt_wlan.h IEEE80211_ALQ opt_wlan.h IEEE80211_DFS_DEBUG opt_wlan.h # 802.11 TDMA support TDMA_SLOTLEN_DEFAULT opt_tdma.h TDMA_SLOTCNT_DEFAULT opt_tdma.h TDMA_BINTVAL_DEFAULT opt_tdma.h TDMA_TXRATE_11B_DEFAULT opt_tdma.h TDMA_TXRATE_11G_DEFAULT opt_tdma.h TDMA_TXRATE_11A_DEFAULT opt_tdma.h TDMA_TXRATE_TURBO_DEFAULT opt_tdma.h TDMA_TXRATE_HALF_DEFAULT opt_tdma.h TDMA_TXRATE_QUARTER_DEFAULT opt_tdma.h TDMA_TXRATE_11NA_DEFAULT opt_tdma.h TDMA_TXRATE_11NG_DEFAULT opt_tdma.h # VideoMode PICKMODE_DEBUG opt_videomode.h # Network stack virtualization options VIMAGE opt_global.h VNET_DEBUG opt_global.h # Common Flash Interface (CFI) options CFI_SUPPORT_STRATAFLASH opt_cfi.h CFI_ARMEDANDDANGEROUS opt_cfi.h CFI_HARDWAREBYTESWAP opt_cfi.h # Sound options SND_DEBUG opt_snd.h SND_DIAGNOSTIC opt_snd.h SND_FEEDER_MULTIFORMAT opt_snd.h SND_FEEDER_FULL_MULTIFORMAT opt_snd.h SND_FEEDER_RATE_HP opt_snd.h SND_PCM_64 opt_snd.h SND_OLDSTEREO opt_snd.h X86BIOS # Flattened device tree options FDT opt_platform.h FDT_DTB_STATIC opt_platform.h # OFED Infiniband stack OFED opt_ofed.h OFED_DEBUG_INIT opt_ofed.h SDP opt_ofed.h SDP_DEBUG opt_ofed.h IPOIB opt_ofed.h IPOIB_DEBUG opt_ofed.h IPOIB_CM opt_ofed.h # Resource Accounting RACCT opt_global.h RACCT_DEFAULT_TO_DISABLED opt_global.h # Resource Limits RCTL opt_global.h # Random number generator(s) # Alternative RNG algorithm. RANDOM_FENESTRASX opt_global.h # With this, no entropy processor is loaded, but the entropy # harvesting infrastructure is present. This means an entropy # processor may be loaded as a module. RANDOM_LOADABLE opt_global.h # This turns on high-rate and potentially expensive harvesting in # the uma slab allocator. RANDOM_ENABLE_UMA opt_global.h RANDOM_ENABLE_ETHER opt_global.h # This options turns TPM into entropy source. TPM_HARVEST opt_tpm.h # BHND(4) driver BHND_LOGLEVEL opt_global.h # GPIO and child devices GPIO_SPI_DEBUG opt_gpio.h # SPI devices SPIGEN_LEGACY_CDEVNAME opt_spi.h # etherswitch(4) driver RTL8366_SOFT_RESET opt_etherswitch.h # evdev protocol support EVDEV_SUPPORT opt_evdev.h EVDEV_DEBUG opt_evdev.h UINPUT_DEBUG opt_evdev.h # Hyper-V network driver HN_DEBUG opt_hn.h # CAM-based MMC stack MMCCAM # Encrypted kernel crash dumps EKCD opt_ekcd.h # NVME options NVME_USE_NVD opt_nvme.h # amdsbwd options AMDSBWD_DEBUG opt_amdsbwd.h # gcov support GCOV opt_global.h LINDEBUGFS # options for HID support HID_DEBUG opt_hid.h IICHID_DEBUG opt_hid.h IICHID_SAMPLING opt_hid.h HKBD_DFLT_KEYMAP opt_hkbd.h +HIDRAW_MAKE_UHID_ALIAS opt_hid.h diff --git a/sys/dev/hid/hid.c b/sys/dev/hid/hid.c index 5a91692e1038..699bfa4a8bb3 100644 --- a/sys/dev/hid/hid.c +++ b/sys/dev/hid/hid.c @@ -1,1075 +1,1079 @@ /* $FreeBSD$ */ /* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD * * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@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. */ #include "opt_hid.h" #include #include #include #include #include #include #include #define HID_DEBUG_VAR hid_debug #include #include #include "hid_if.h" /* * Define this unconditionally in case a kernel module is loaded that * has been compiled with debugging options. */ int hid_debug = 0; SYSCTL_NODE(_hw, OID_AUTO, hid, CTLFLAG_RW, 0, "HID debugging"); SYSCTL_INT(_hw_hid, OID_AUTO, debug, CTLFLAG_RWTUN, &hid_debug, 0, "Debug level"); +#ifdef HIDRAW_MAKE_UHID_ALIAS +devclass_t hidraw_devclass; +#endif + static void hid_clear_local(struct hid_item *); static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize); static hid_test_quirk_t hid_test_quirk_w; hid_test_quirk_t *hid_test_quirk_p = &hid_test_quirk_w; #define MAXUSAGE 64 #define MAXPUSH 4 #define MAXID 16 #define MAXLOCCNT 2048 struct hid_pos_data { int32_t rid; uint32_t pos; }; struct hid_data { const uint8_t *start; const uint8_t *end; const uint8_t *p; struct hid_item cur[MAXPUSH]; struct hid_pos_data last_pos[MAXID]; int32_t usages_min[MAXUSAGE]; int32_t usages_max[MAXUSAGE]; int32_t usage_last; /* last seen usage */ uint32_t loc_size; /* last seen size */ uint32_t loc_count; /* last seen count */ uint32_t ncount; /* end usage item count */ uint32_t icount; /* current usage item count */ uint8_t kindset; /* we have 5 kinds so 8 bits are enough */ uint8_t pushlevel; /* current pushlevel */ uint8_t nusage; /* end "usages_min/max" index */ uint8_t iusage; /* current "usages_min/max" index */ uint8_t ousage; /* current "usages_min/max" offset */ uint8_t susage; /* usage set flags */ }; /*------------------------------------------------------------------------* * hid_clear_local *------------------------------------------------------------------------*/ static void hid_clear_local(struct hid_item *c) { c->loc.count = 0; c->loc.size = 0; c->nusages = 0; memset(c->usages, 0, sizeof(c->usages)); c->usage_minimum = 0; c->usage_maximum = 0; c->designator_index = 0; c->designator_minimum = 0; c->designator_maximum = 0; c->string_index = 0; c->string_minimum = 0; c->string_maximum = 0; c->set_delimiter = 0; } static void hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) { uint8_t i; /* check for same report ID - optimise */ if (c->report_ID == next_rID) return; /* save current position for current rID */ if (c->report_ID == 0) { i = 0; } else { for (i = 1; i != MAXID; i++) { if (s->last_pos[i].rid == c->report_ID) break; if (s->last_pos[i].rid == 0) break; } } if (i != MAXID) { s->last_pos[i].rid = c->report_ID; s->last_pos[i].pos = c->loc.pos; } /* store next report ID */ c->report_ID = next_rID; /* lookup last position for next rID */ if (next_rID == 0) { i = 0; } else { for (i = 1; i != MAXID; i++) { if (s->last_pos[i].rid == next_rID) break; if (s->last_pos[i].rid == 0) break; } } if (i != MAXID) { s->last_pos[i].rid = next_rID; c->loc.pos = s->last_pos[i].pos; } else { DPRINTF("Out of RID entries, position is set to zero!\n"); c->loc.pos = 0; } } /*------------------------------------------------------------------------* * hid_start_parse *------------------------------------------------------------------------*/ struct hid_data * hid_start_parse(const void *d, hid_size_t len, int kindset) { struct hid_data *s; if ((kindset-1) & kindset) { DPRINTFN(0, "Only one bit can be " "set in the kindset\n"); return (NULL); } s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO); s->start = s->p = d; s->end = ((const uint8_t *)d) + len; s->kindset = kindset; return (s); } /*------------------------------------------------------------------------* * hid_end_parse *------------------------------------------------------------------------*/ void hid_end_parse(struct hid_data *s) { if (s == NULL) return; free(s, M_TEMP); } /*------------------------------------------------------------------------* * get byte from HID descriptor *------------------------------------------------------------------------*/ static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize) { const uint8_t *ptr; uint8_t retval; ptr = s->p; /* check if end is reached */ if (ptr == s->end) return (0); /* read out a byte */ retval = *ptr; /* check if data pointer can be advanced by "wSize" bytes */ if ((s->end - ptr) < wSize) ptr = s->end; else ptr += wSize; /* update pointer */ s->p = ptr; return (retval); } /*------------------------------------------------------------------------* * hid_get_item *------------------------------------------------------------------------*/ int hid_get_item(struct hid_data *s, struct hid_item *h) { struct hid_item *c; unsigned int bTag, bType, bSize; uint32_t oldpos; int32_t mask; int32_t dval; if (s == NULL) return (0); c = &s->cur[s->pushlevel]; top: /* check if there is an array of items */ if (s->icount < s->ncount) { /* get current usage */ if (s->iusage < s->nusage) { dval = s->usages_min[s->iusage] + s->ousage; c->usage = dval; s->usage_last = dval; if (dval == s->usages_max[s->iusage]) { s->iusage ++; s->ousage = 0; } else { s->ousage ++; } } else { DPRINTFN(1, "Using last usage\n"); dval = s->usage_last; } c->nusages = 1; /* array type HID item may have multiple usages */ while ((c->flags & HIO_VARIABLE) == 0 && s->ousage == 0 && s->iusage < s->nusage && c->nusages < HID_ITEM_MAXUSAGE) c->usages[c->nusages++] = s->usages_min[s->iusage++]; if ((c->flags & HIO_VARIABLE) == 0 && s->ousage == 0 && s->iusage < s->nusage) DPRINTFN(0, "HID_ITEM_MAXUSAGE should be increased " "up to %hhu to parse the HID report descriptor\n", s->nusage); s->icount ++; /* * Only copy HID item, increment position and return * if correct kindset! */ if (s->kindset & (1 << c->kind)) { *h = *c; DPRINTFN(1, "%u,%u,%u\n", h->loc.pos, h->loc.size, h->loc.count); c->loc.pos += c->loc.size * c->loc.count; return (1); } } /* reset state variables */ s->icount = 0; s->ncount = 0; s->iusage = 0; s->nusage = 0; s->susage = 0; s->ousage = 0; hid_clear_local(c); /* get next item */ while (s->p != s->end) { bSize = hid_get_byte(s, 1); if (bSize == 0xfe) { /* long item */ bSize = hid_get_byte(s, 1); bSize |= hid_get_byte(s, 1) << 8; bTag = hid_get_byte(s, 1); bType = 0xff; /* XXX what should it be */ } else { /* short item */ bTag = bSize >> 4; bType = (bSize >> 2) & 3; bSize &= 3; if (bSize == 3) bSize = 4; } switch (bSize) { case 0: dval = 0; mask = 0; break; case 1: dval = (int8_t)hid_get_byte(s, 1); mask = 0xFF; break; case 2: dval = hid_get_byte(s, 1); dval |= hid_get_byte(s, 1) << 8; dval = (int16_t)dval; mask = 0xFFFF; break; case 4: dval = hid_get_byte(s, 1); dval |= hid_get_byte(s, 1) << 8; dval |= hid_get_byte(s, 1) << 16; dval |= hid_get_byte(s, 1) << 24; mask = 0xFFFFFFFF; break; default: dval = hid_get_byte(s, bSize); DPRINTFN(0, "bad length %u (data=0x%02x)\n", bSize, dval); continue; } switch (bType) { case 0: /* Main */ switch (bTag) { case 8: /* Input */ c->kind = hid_input; ret: c->flags = dval; c->loc.count = s->loc_count; c->loc.size = s->loc_size; if (c->flags & HIO_VARIABLE) { /* range check usage count */ if (c->loc.count > MAXLOCCNT) { DPRINTFN(0, "Number of " "items(%u) truncated to %u\n", (unsigned)(c->loc.count), MAXLOCCNT); s->ncount = MAXLOCCNT; } else s->ncount = c->loc.count; /* * The "top" loop will return * one and one item: */ c->loc.count = 1; } else { s->ncount = 1; } goto top; case 9: /* Output */ c->kind = hid_output; goto ret; case 10: /* Collection */ c->kind = hid_collection; c->collection = dval; c->collevel++; c->usage = s->usage_last; c->nusages = 1; *h = *c; return (1); case 11: /* Feature */ c->kind = hid_feature; goto ret; case 12: /* End collection */ c->kind = hid_endcollection; if (c->collevel == 0) { DPRINTFN(0, "invalid end collection\n"); return (0); } c->collevel--; *h = *c; return (1); default: DPRINTFN(0, "Main bTag=%d\n", bTag); break; } break; case 1: /* Global */ switch (bTag) { case 0: c->_usage_page = dval << 16; break; case 1: c->logical_minimum = dval; break; case 2: c->logical_maximum = dval; break; case 3: c->physical_minimum = dval; break; case 4: c->physical_maximum = dval; break; case 5: c->unit_exponent = dval; break; case 6: c->unit = dval; break; case 7: /* mask because value is unsigned */ s->loc_size = dval & mask; break; case 8: hid_switch_rid(s, c, dval & mask); break; case 9: /* mask because value is unsigned */ s->loc_count = dval & mask; break; case 10: /* Push */ /* stop parsing, if invalid push level */ if ((s->pushlevel + 1) >= MAXPUSH) { DPRINTFN(0, "Cannot push item @ %d\n", s->pushlevel); return (0); } s->pushlevel ++; s->cur[s->pushlevel] = *c; /* store size and count */ c->loc.size = s->loc_size; c->loc.count = s->loc_count; /* update current item pointer */ c = &s->cur[s->pushlevel]; break; case 11: /* Pop */ /* stop parsing, if invalid push level */ if (s->pushlevel == 0) { DPRINTFN(0, "Cannot pop item @ 0\n"); return (0); } s->pushlevel --; /* preserve position */ oldpos = c->loc.pos; c = &s->cur[s->pushlevel]; /* restore size and count */ s->loc_size = c->loc.size; s->loc_count = c->loc.count; /* set default item location */ c->loc.pos = oldpos; c->loc.size = 0; c->loc.count = 0; break; default: DPRINTFN(0, "Global bTag=%d\n", bTag); break; } break; case 2: /* Local */ switch (bTag) { case 0: if (bSize != 4) dval = (dval & mask) | c->_usage_page; /* set last usage, in case of a collection */ s->usage_last = dval; if (s->nusage < MAXUSAGE) { s->usages_min[s->nusage] = dval; s->usages_max[s->nusage] = dval; s->nusage ++; } else { DPRINTFN(0, "max usage reached\n"); } /* clear any pending usage sets */ s->susage = 0; break; case 1: s->susage |= 1; if (bSize != 4) dval = (dval & mask) | c->_usage_page; c->usage_minimum = dval; goto check_set; case 2: s->susage |= 2; if (bSize != 4) dval = (dval & mask) | c->_usage_page; c->usage_maximum = dval; check_set: if (s->susage != 3) break; /* sanity check */ if ((s->nusage < MAXUSAGE) && (c->usage_minimum <= c->usage_maximum)) { /* add usage range */ s->usages_min[s->nusage] = c->usage_minimum; s->usages_max[s->nusage] = c->usage_maximum; s->nusage ++; } else { DPRINTFN(0, "Usage set dropped\n"); } s->susage = 0; break; case 3: c->designator_index = dval; break; case 4: c->designator_minimum = dval; break; case 5: c->designator_maximum = dval; break; case 7: c->string_index = dval; break; case 8: c->string_minimum = dval; break; case 9: c->string_maximum = dval; break; case 10: c->set_delimiter = dval; break; default: DPRINTFN(0, "Local bTag=%d\n", bTag); break; } break; default: DPRINTFN(0, "default bType=%d\n", bType); break; } } return (0); } /*------------------------------------------------------------------------* * hid_report_size *------------------------------------------------------------------------*/ int hid_report_size(const void *buf, hid_size_t len, enum hid_kind k, uint8_t id) { struct hid_data *d; struct hid_item h; uint32_t temp; uint32_t hpos; uint32_t lpos; int report_id = 0; hpos = 0; lpos = 0xFFFFFFFF; for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { if (h.kind == k && h.report_ID == id) { /* compute minimum */ if (lpos > h.loc.pos) lpos = h.loc.pos; /* compute end position */ temp = h.loc.pos + (h.loc.size * h.loc.count); /* compute maximum */ if (hpos < temp) hpos = temp; if (h.report_ID != 0) report_id = 1; } } hid_end_parse(d); /* safety check - can happen in case of currupt descriptors */ if (lpos > hpos) temp = 0; else temp = hpos - lpos; /* return length in bytes rounded up */ return ((temp + 7) / 8 + report_id); } int hid_report_size_max(const void *buf, hid_size_t len, enum hid_kind k, uint8_t *id) { struct hid_data *d; struct hid_item h; uint32_t temp; uint32_t hpos; uint32_t lpos; uint8_t any_id; any_id = 0; hpos = 0; lpos = 0xFFFFFFFF; for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { if (h.kind == k) { /* check for ID-byte presence */ if ((h.report_ID != 0) && !any_id) { if (id != NULL) *id = h.report_ID; any_id = 1; } /* compute minimum */ if (lpos > h.loc.pos) lpos = h.loc.pos; /* compute end position */ temp = h.loc.pos + (h.loc.size * h.loc.count); /* compute maximum */ if (hpos < temp) hpos = temp; } } hid_end_parse(d); /* safety check - can happen in case of currupt descriptors */ if (lpos > hpos) temp = 0; else temp = hpos - lpos; /* check for ID byte */ if (any_id) temp += 8; else if (id != NULL) *id = 0; /* return length in bytes rounded up */ return ((temp + 7) / 8); } /*------------------------------------------------------------------------* * hid_locate *------------------------------------------------------------------------*/ int hid_locate(const void *desc, hid_size_t size, int32_t u, enum hid_kind k, uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) { struct hid_data *d; struct hid_item h; int i; for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) { for (i = 0; i < h.nusages; i++) { if (h.kind == k && h.usages[i] == u) { if (index--) break; if (loc != NULL) *loc = h.loc; if (flags != NULL) *flags = h.flags; if (id != NULL) *id = h.report_ID; hid_end_parse(d); return (1); } } } if (loc != NULL) loc->size = 0; if (flags != NULL) *flags = 0; if (id != NULL) *id = 0; hid_end_parse(d); return (0); } /*------------------------------------------------------------------------* * hid_get_data *------------------------------------------------------------------------*/ static uint32_t hid_get_data_sub(const uint8_t *buf, hid_size_t len, struct hid_location *loc, int is_signed) { uint32_t hpos = loc->pos; uint32_t hsize = loc->size; uint32_t data; uint32_t rpos; uint8_t n; DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize); /* Range check and limit */ if (hsize == 0) return (0); if (hsize > 32) hsize = 32; /* Get data in a safe way */ data = 0; rpos = (hpos / 8); n = (hsize + 7) / 8; rpos += n; while (n--) { rpos--; if (rpos < len) data |= buf[rpos] << (8 * n); } /* Correctly shift down data */ data = (data >> (hpos % 8)); n = 32 - hsize; /* Mask and sign extend in one */ if (is_signed != 0) data = (int32_t)((int32_t)data << n) >> n; else data = (uint32_t)((uint32_t)data << n) >> n; DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", loc->pos, loc->size, (long)data); return (data); } int32_t hid_get_data(const uint8_t *buf, hid_size_t len, struct hid_location *loc) { return (hid_get_data_sub(buf, len, loc, 1)); } uint32_t hid_get_udata(const uint8_t *buf, hid_size_t len, struct hid_location *loc) { return (hid_get_data_sub(buf, len, loc, 0)); } /*------------------------------------------------------------------------* * hid_put_data *------------------------------------------------------------------------*/ void hid_put_udata(uint8_t *buf, hid_size_t len, struct hid_location *loc, unsigned int value) { uint32_t hpos = loc->pos; uint32_t hsize = loc->size; uint64_t data; uint64_t mask; uint32_t rpos; uint8_t n; DPRINTFN(11, "hid_put_data: loc %d/%d = %u\n", hpos, hsize, value); /* Range check and limit */ if (hsize == 0) return; if (hsize > 32) hsize = 32; /* Put data in a safe way */ rpos = (hpos / 8); n = (hsize + 7) / 8; data = ((uint64_t)value) << (hpos % 8); mask = ((1ULL << hsize) - 1ULL) << (hpos % 8); rpos += n; while (n--) { rpos--; if (rpos < len) { buf[rpos] &= ~(mask >> (8 * n)); buf[rpos] |= (data >> (8 * n)); } } } /*------------------------------------------------------------------------* * hid_is_collection *------------------------------------------------------------------------*/ int hid_is_collection(const void *desc, hid_size_t size, int32_t usage) { struct hid_data *hd; struct hid_item hi; int err; hd = hid_start_parse(desc, size, hid_input); if (hd == NULL) return (0); while ((err = hid_get_item(hd, &hi))) { if (hi.kind == hid_collection && hi.usage == usage) break; } hid_end_parse(hd); return (err); } /*------------------------------------------------------------------------* * calculate HID item resolution. unit/mm for distances, unit/rad for angles *------------------------------------------------------------------------*/ int32_t hid_item_resolution(struct hid_item *hi) { /* * hid unit scaling table according to HID Usage Table Review * Request 39 Tbl 17 http://www.usb.org/developers/hidpage/HUTRR39b.pdf */ static const int64_t scale[0x10][2] = { [0x00] = { 1, 1 }, [0x01] = { 1, 10 }, [0x02] = { 1, 100 }, [0x03] = { 1, 1000 }, [0x04] = { 1, 10000 }, [0x05] = { 1, 100000 }, [0x06] = { 1, 1000000 }, [0x07] = { 1, 10000000 }, [0x08] = { 100000000, 1 }, [0x09] = { 10000000, 1 }, [0x0A] = { 1000000, 1 }, [0x0B] = { 100000, 1 }, [0x0C] = { 10000, 1 }, [0x0D] = { 1000, 1 }, [0x0E] = { 100, 1 }, [0x0F] = { 10, 1 }, }; int64_t logical_size; int64_t physical_size; int64_t multiplier; int64_t divisor; int64_t resolution; switch (hi->unit) { case HUM_CENTIMETER: multiplier = 1; divisor = 10; break; case HUM_INCH: multiplier = 10; divisor = 254; break; case HUM_RADIAN: multiplier = 1; divisor = 1; break; case HUM_DEGREE: multiplier = 573; divisor = 10; break; default: return (0); } if ((hi->logical_maximum <= hi->logical_minimum) || (hi->physical_maximum <= hi->physical_minimum) || (hi->unit_exponent < 0) || (hi->unit_exponent >= nitems(scale))) return (0); logical_size = (int64_t)hi->logical_maximum - (int64_t)hi->logical_minimum; physical_size = (int64_t)hi->physical_maximum - (int64_t)hi->physical_minimum; /* Round to ceiling */ resolution = logical_size * multiplier * scale[hi->unit_exponent][0] / (physical_size * divisor * scale[hi->unit_exponent][1]); if (resolution > INT32_MAX) return (0); return (resolution); } /*------------------------------------------------------------------------* * hid_is_mouse * * This function will decide if a USB descriptor belongs to a USB mouse. * * Return values: * Zero: Not a USB mouse. * Else: Is a USB mouse. *------------------------------------------------------------------------*/ int hid_is_mouse(const void *d_ptr, uint16_t d_len) { struct hid_data *hd; struct hid_item hi; int mdepth; int found; hd = hid_start_parse(d_ptr, d_len, 1 << hid_input); if (hd == NULL) return (0); mdepth = 0; found = 0; while (hid_get_item(hd, &hi)) { switch (hi.kind) { case hid_collection: if (mdepth != 0) mdepth++; else if (hi.collection == 1 && hi.usage == HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) mdepth++; break; case hid_endcollection: if (mdepth != 0) mdepth--; break; case hid_input: if (mdepth == 0) break; if (hi.usage == HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) && (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) found++; if (hi.usage == HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) && (hi.flags & (HIO_CONST|HIO_RELATIVE)) == HIO_RELATIVE) found++; break; default: break; } } hid_end_parse(hd); return (found); } /*------------------------------------------------------------------------* * hid_is_keyboard * * This function will decide if a USB descriptor belongs to a USB keyboard. * * Return values: * Zero: Not a USB keyboard. * Else: Is a USB keyboard. *------------------------------------------------------------------------*/ int hid_is_keyboard(const void *d_ptr, uint16_t d_len) { if (hid_is_collection(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) return (1); return (0); } /*------------------------------------------------------------------------* * hid_test_quirk - test a device for a given quirk * * Return values: * false: The HID device does not have the given quirk. * true: The HID device has the given quirk. *------------------------------------------------------------------------*/ bool hid_test_quirk(const struct hid_device_info *dev_info, uint16_t quirk) { bool found; uint8_t x; if (quirk == HQ_NONE) return (false); /* search the automatic per device quirks first */ for (x = 0; x != HID_MAX_AUTO_QUIRK; x++) { if (dev_info->autoQuirk[x] == quirk) return (true); } /* search global quirk table, if any */ found = (hid_test_quirk_p) (dev_info, quirk); return (found); } static bool hid_test_quirk_w(const struct hid_device_info *dev_info, uint16_t quirk) { return (false); /* no match */ } int hid_add_dynamic_quirk(struct hid_device_info *dev_info, uint16_t quirk) { uint8_t x; for (x = 0; x != HID_MAX_AUTO_QUIRK; x++) { if (dev_info->autoQuirk[x] == 0 || dev_info->autoQuirk[x] == quirk) { dev_info->autoQuirk[x] = quirk; return (0); /* success */ } } return (ENOSPC); } void hid_quirk_unload(void *arg) { /* reset function pointer */ hid_test_quirk_p = &hid_test_quirk_w; #ifdef NOT_YET hidquirk_ioctl_p = &hidquirk_ioctl_w; #endif /* wait for CPU to exit the loaded functions, if any */ /* XXX this is a tradeoff */ pause("WAIT", hz); } int hid_get_rdesc(device_t dev, void *data, hid_size_t len) { return (HID_GET_RDESC(device_get_parent(dev), data, len)); } int hid_read(device_t dev, void *data, hid_size_t maxlen, hid_size_t *actlen) { return (HID_READ(device_get_parent(dev), data, maxlen, actlen)); } int hid_write(device_t dev, const void *data, hid_size_t len) { return (HID_WRITE(device_get_parent(dev), data, len)); } int hid_get_report(device_t dev, void *data, hid_size_t maxlen, hid_size_t *actlen, uint8_t type, uint8_t id) { return (HID_GET_REPORT(device_get_parent(dev), data, maxlen, actlen, type, id)); } int hid_set_report(device_t dev, const void *data, hid_size_t len, uint8_t type, uint8_t id) { return (HID_SET_REPORT(device_get_parent(dev), data, len, type, id)); } int hid_set_idle(device_t dev, uint16_t duration, uint8_t id) { return (HID_SET_IDLE(device_get_parent(dev), duration, id)); } int hid_set_protocol(device_t dev, uint16_t protocol) { return (HID_SET_PROTOCOL(device_get_parent(dev), protocol)); } MODULE_VERSION(hid, 1); diff --git a/sys/dev/hid/hid.h b/sys/dev/hid/hid.h index cea33f7b4af8..e88cbba04d9a 100644 --- a/sys/dev/hid/hid.h +++ b/sys/dev/hid/hid.h @@ -1,344 +1,347 @@ /* $FreeBSD$ */ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD * * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. All rights reserved. * * 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 AUTHOR 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 AUTHOR 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. */ #ifndef _HID_HID_H_ #define _HID_HID_H_ /* Usage pages */ #define HUP_UNDEFINED 0x0000 #define HUP_GENERIC_DESKTOP 0x0001 #define HUP_SIMULATION 0x0002 #define HUP_VR_CONTROLS 0x0003 #define HUP_SPORTS_CONTROLS 0x0004 #define HUP_GAMING_CONTROLS 0x0005 #define HUP_KEYBOARD 0x0007 #define HUP_LEDS 0x0008 #define HUP_BUTTON 0x0009 #define HUP_ORDINALS 0x000a #define HUP_TELEPHONY 0x000b #define HUP_CONSUMER 0x000c #define HUP_DIGITIZERS 0x000d #define HUP_PHYSICAL_IFACE 0x000e #define HUP_UNICODE 0x0010 #define HUP_ALPHANUM_DISPLAY 0x0014 #define HUP_MONITOR 0x0080 #define HUP_MONITOR_ENUM_VAL 0x0081 #define HUP_VESA_VC 0x0082 #define HUP_VESA_CMD 0x0083 #define HUP_POWER 0x0084 #define HUP_BATTERY_SYSTEM 0x0085 #define HUP_BARCODE_SCANNER 0x008b #define HUP_SCALE 0x008c #define HUP_CAMERA_CONTROL 0x0090 #define HUP_ARCADE 0x0091 #define HUP_MICROSOFT 0xff00 /* Usages, generic desktop */ #define HUG_POINTER 0x0001 #define HUG_MOUSE 0x0002 #define HUG_JOYSTICK 0x0004 #define HUG_GAME_PAD 0x0005 #define HUG_KEYBOARD 0x0006 #define HUG_KEYPAD 0x0007 #define HUG_MULTIAXIS_CNTROLLER 0x0008 #define HUG_X 0x0030 #define HUG_Y 0x0031 #define HUG_Z 0x0032 #define HUG_RX 0x0033 #define HUG_RY 0x0034 #define HUG_RZ 0x0035 #define HUG_SLIDER 0x0036 #define HUG_DIAL 0x0037 #define HUG_WHEEL 0x0038 #define HUG_HAT_SWITCH 0x0039 #define HUG_COUNTED_BUFFER 0x003a #define HUG_BYTE_COUNT 0x003b #define HUG_MOTION_WAKEUP 0x003c #define HUG_VX 0x0040 #define HUG_VY 0x0041 #define HUG_VZ 0x0042 #define HUG_VBRX 0x0043 #define HUG_VBRY 0x0044 #define HUG_VBRZ 0x0045 #define HUG_VNO 0x0046 #define HUG_TWHEEL 0x0048 /* M$ Wireless Intellimouse Wheel */ #define HUG_SYSTEM_CONTROL 0x0080 #define HUG_SYSTEM_POWER_DOWN 0x0081 #define HUG_SYSTEM_SLEEP 0x0082 #define HUG_SYSTEM_WAKEUP 0x0083 #define HUG_SYSTEM_CONTEXT_MENU 0x0084 #define HUG_SYSTEM_MAIN_MENU 0x0085 #define HUG_SYSTEM_APP_MENU 0x0086 #define HUG_SYSTEM_MENU_HELP 0x0087 #define HUG_SYSTEM_MENU_EXIT 0x0088 #define HUG_SYSTEM_MENU_SELECT 0x0089 #define HUG_SYSTEM_MENU_RIGHT 0x008a #define HUG_SYSTEM_MENU_LEFT 0x008b #define HUG_SYSTEM_MENU_UP 0x008c #define HUG_SYSTEM_MENU_DOWN 0x008d #define HUG_SYSTEM_POWER_UP 0x008e #define HUG_SYSTEM_RESTART 0x008f #define HUG_D_PAD_UP 0x0090 #define HUG_D_PAD_DOWN 0x0091 #define HUG_D_PAD_RIGHT 0x0092 #define HUG_D_PAD_LEFT 0x0093 #define HUG_APPLE_EJECT 0x00b8 /* Usages Digitizers */ #define HUD_UNDEFINED 0x0000 #define HUD_DIGITIZER 0x0001 #define HUD_PEN 0x0002 #define HUD_TOUCHSCREEN 0x0004 #define HUD_TOUCHPAD 0x0005 #define HUD_CONFIG 0x000e #define HUD_FINGER 0x0022 #define HUD_TIP_PRESSURE 0x0030 #define HUD_BARREL_PRESSURE 0x0031 #define HUD_IN_RANGE 0x0032 #define HUD_TOUCH 0x0033 #define HUD_UNTOUCH 0x0034 #define HUD_TAP 0x0035 #define HUD_QUALITY 0x0036 #define HUD_DATA_VALID 0x0037 #define HUD_TRANSDUCER_INDEX 0x0038 #define HUD_TABLET_FKEYS 0x0039 #define HUD_PROGRAM_CHANGE_KEYS 0x003a #define HUD_BATTERY_STRENGTH 0x003b #define HUD_INVERT 0x003c #define HUD_X_TILT 0x003d #define HUD_Y_TILT 0x003e #define HUD_AZIMUTH 0x003f #define HUD_ALTITUDE 0x0040 #define HUD_TWIST 0x0041 #define HUD_TIP_SWITCH 0x0042 #define HUD_SEC_TIP_SWITCH 0x0043 #define HUD_BARREL_SWITCH 0x0044 #define HUD_ERASER 0x0045 #define HUD_TABLET_PICK 0x0046 #define HUD_CONFIDENCE 0x0047 #define HUD_WIDTH 0x0048 #define HUD_HEIGHT 0x0049 #define HUD_CONTACTID 0x0051 #define HUD_INPUT_MODE 0x0052 #define HUD_DEVICE_INDEX 0x0053 #define HUD_CONTACTCOUNT 0x0054 #define HUD_CONTACT_MAX 0x0055 #define HUD_SCAN_TIME 0x0056 #define HUD_SURFACE_SWITCH 0x0057 #define HUD_BUTTONS_SWITCH 0x0058 #define HUD_BUTTON_TYPE 0x0059 #define HUD_SEC_BARREL_SWITCH 0x005a #define HUD_LATENCY_MODE 0x0060 /* Usages, Consumer */ #define HUC_CONTROL 0x0001 #define HUC_HEADPHONE 0x0005 #define HUC_AC_PAN 0x0238 #define HID_USAGE2(p,u) (((p) << 16) | (u)) #define HID_GET_USAGE(u) ((u) & 0xffff) #define HID_GET_USAGE_PAGE(u) (((u) >> 16) & 0xffff) #define HID_INPUT_REPORT 0x01 #define HID_OUTPUT_REPORT 0x02 #define HID_FEATURE_REPORT 0x03 /* Bits in the input/output/feature items */ #define HIO_CONST 0x001 #define HIO_VARIABLE 0x002 #define HIO_RELATIVE 0x004 #define HIO_WRAP 0x008 #define HIO_NONLINEAR 0x010 #define HIO_NOPREF 0x020 #define HIO_NULLSTATE 0x040 #define HIO_VOLATILE 0x080 #define HIO_BUFBYTES 0x100 /* Units of Measure */ #define HUM_CENTIMETER 0x11 #define HUM_RADIAN 0x12 #define HUM_INCH 0x13 #define HUM_DEGREE 0x14 #if defined(_KERNEL) || defined(_STANDALONE) #define HID_ITEM_MAXUSAGE 4 #define HID_MAX_AUTO_QUIRK 8 /* maximum number of dynamic quirks */ #define HID_PNP_ID_SIZE 20 /* includes null terminator */ +/* Share unit number pool between uhid and hidraw */ +extern devclass_t hidraw_devclass; + /* Declare global HID debug variable. */ extern int hid_debug; /* Check if HID debugging is enabled. */ #ifdef HID_DEBUG_VAR #ifdef HID_DEBUG #define DPRINTFN(n,fmt,...) do { \ if ((HID_DEBUG_VAR) >= (n)) { \ printf("%s: " fmt, \ __FUNCTION__ ,##__VA_ARGS__); \ } \ } while (0) #define DPRINTF(...) DPRINTFN(1, __VA_ARGS__) #else #define DPRINTF(...) do { } while (0) #define DPRINTFN(...) do { } while (0) #endif #endif /* Declare parent SYSCTL HID node. */ #ifdef SYSCTL_DECL SYSCTL_DECL(_hw_hid); #endif typedef uint32_t hid_size_t; #define HID_IN_POLLING_MODE() (SCHEDULER_STOPPED() || kdb_active) enum hid_kind { hid_input, hid_output, hid_feature, hid_collection, hid_endcollection }; struct hid_location { uint32_t size; uint32_t count; uint32_t pos; }; struct hid_item { /* Global */ int32_t _usage_page; int32_t logical_minimum; int32_t logical_maximum; int32_t physical_minimum; int32_t physical_maximum; int32_t unit_exponent; int32_t unit; int32_t report_ID; /* Local */ int nusages; union { int32_t usage; int32_t usages[HID_ITEM_MAXUSAGE]; }; int32_t usage_minimum; int32_t usage_maximum; int32_t designator_index; int32_t designator_minimum; int32_t designator_maximum; int32_t string_index; int32_t string_minimum; int32_t string_maximum; int32_t set_delimiter; /* Misc */ int32_t collection; int collevel; enum hid_kind kind; uint32_t flags; /* Location */ struct hid_location loc; }; struct hid_absinfo { int32_t min; int32_t max; int32_t res; }; struct hid_device_info { char name[80]; char serial[80]; char idPnP[HID_PNP_ID_SIZE]; uint16_t idBus; uint16_t idVendor; uint16_t idProduct; uint16_t idVersion; hid_size_t rdescsize; /* Report descriptor size */ uint8_t autoQuirk[HID_MAX_AUTO_QUIRK]; }; struct hid_rdesc_info { void *data; hid_size_t len; hid_size_t isize; hid_size_t osize; hid_size_t fsize; uint8_t iid; uint8_t oid; uint8_t fid; /* Max sizes for HID requests supported by transport backend */ hid_size_t rdsize; hid_size_t wrsize; hid_size_t grsize; hid_size_t srsize; }; typedef void hid_intr_t(void *context, void *data, hid_size_t len); typedef bool hid_test_quirk_t(const struct hid_device_info *dev_info, uint16_t quirk); extern hid_test_quirk_t *hid_test_quirk_p; /* prototypes from "usb_hid.c" */ struct hid_data *hid_start_parse(const void *d, hid_size_t len, int kindset); void hid_end_parse(struct hid_data *s); int hid_get_item(struct hid_data *s, struct hid_item *h); int hid_report_size(const void *buf, hid_size_t len, enum hid_kind k, uint8_t id); int hid_report_size_max(const void *buf, hid_size_t len, enum hid_kind k, uint8_t *id); int hid_locate(const void *desc, hid_size_t size, int32_t usage, enum hid_kind kind, uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id); int32_t hid_get_data(const uint8_t *buf, hid_size_t len, struct hid_location *loc); uint32_t hid_get_udata(const uint8_t *buf, hid_size_t len, struct hid_location *loc); void hid_put_udata(uint8_t *buf, hid_size_t len, struct hid_location *loc, unsigned int value); int hid_is_collection(const void *desc, hid_size_t size, int32_t usage); int32_t hid_item_resolution(struct hid_item *hi); int hid_is_mouse(const void *d_ptr, uint16_t d_len); int hid_is_keyboard(const void *d_ptr, uint16_t d_len); bool hid_test_quirk(const struct hid_device_info *dev_info, uint16_t quirk); int hid_add_dynamic_quirk(struct hid_device_info *dev_info, uint16_t quirk); void hid_quirk_unload(void *arg); int hid_get_rdesc(device_t, void *, hid_size_t); int hid_read(device_t, void *, hid_size_t, hid_size_t *); int hid_write(device_t, const void *, hid_size_t); int hid_get_report(device_t, void *, hid_size_t, hid_size_t *, uint8_t, uint8_t); int hid_set_report(device_t, const void *, hid_size_t, uint8_t, uint8_t); int hid_set_idle(device_t, uint16_t, uint8_t); int hid_set_protocol(device_t, uint16_t); #endif /* _KERNEL || _STANDALONE */ #endif /* _HID_HID_H_ */ diff --git a/sys/dev/hid/hidraw.c b/sys/dev/hid/hidraw.c index 1da8cb202bd9..6cc67ff14d23 100644 --- a/sys/dev/hid/hidraw.c +++ b/sys/dev/hid/hidraw.c @@ -1,902 +1,909 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD * * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * Copyright (c) 2020 Vladimir Kondratyev * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@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 __FBSDID("$FreeBSD$"); +#include "opt_hid.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HID_DEBUG_VAR hidraw_debug #include #include #include #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); } +#ifdef HIDRAW_MAKE_UHID_ALIAS + (void)make_dev_alias(sc->dev, "uhid%d", device_get_unit(self)); +#endif 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; mtx_unlock(&sc->sc_mtx); if (error != 0) return(error); hgd = (struct hidraw_gen_descriptor *)addr; buf = HIDRAW_LOCAL_ALLOC(local_buf, hgd->hgd_maxlen); copyin(hgd->hgd_data, buf, hgd->hgd_maxlen); /* Lock newbus around set_report_descr call */ mtx_lock(&Giant); error = hid_set_report_descr(sc->sc_dev, buf, hgd->hgd_maxlen); mtx_unlock(&Giant); HIDRAW_LOCAL_FREE(local_buf, buf); /* Realloc hidraw input queue */ if (error == 0) sc->sc_q = realloc(sc->sc_q, sc->sc_rdesc->rdsize * HIDRAW_BUFFER_SIZE, M_DEVBUF, M_ZERO | M_WAITOK); /* Start interrupts again */ mtx_lock(&sc->sc_mtx); sc->sc_state.quiet = false; hidraw_unlock_queue(sc); mtx_unlock(&sc->sc_mtx); return (error); case HIDRAW_SET_IMMED: if (!(sc->sc_fflags & FREAD)) return (EPERM); if (*(int *)addr) { /* XXX should read into ibuf, but does it matter? */ size = sc->sc_rdesc->isize; buf = HIDRAW_LOCAL_ALLOC(local_buf, size); error = hid_get_report(sc->sc_dev, buf, size, NULL, HID_INPUT_REPORT, sc->sc_rdesc->iid); HIDRAW_LOCAL_FREE(local_buf, buf); if (error) return (EOPNOTSUPP); mtx_lock(&sc->sc_mtx); sc->sc_state.immed = true; mtx_unlock(&sc->sc_mtx); } else { mtx_lock(&sc->sc_mtx); sc->sc_state.immed = false; mtx_unlock(&sc->sc_mtx); } return (0); case HIDRAW_GET_REPORT: if (!(sc->sc_fflags & FREAD)) return (EPERM); hgd = (struct hidraw_gen_descriptor *)addr; switch (hgd->hgd_report_type) { case HID_INPUT_REPORT: size = sc->sc_rdesc->isize; id = sc->sc_rdesc->iid; break; case HID_OUTPUT_REPORT: size = sc->sc_rdesc->osize; id = sc->sc_rdesc->oid; break; case HID_FEATURE_REPORT: size = sc->sc_rdesc->fsize; id = sc->sc_rdesc->fid; break; default: return (EINVAL); } if (id != 0) copyin(hgd->hgd_data, &id, 1); size = MIN(hgd->hgd_maxlen, size); buf = HIDRAW_LOCAL_ALLOC(local_buf, size); error = hid_get_report(sc->sc_dev, buf, size, NULL, hgd->hgd_report_type, id); if (!error) error = copyout(buf, hgd->hgd_data, size); HIDRAW_LOCAL_FREE(local_buf, buf); return (error); case HIDRAW_SET_REPORT: if (!(sc->sc_fflags & FWRITE)) return (EPERM); hgd = (struct hidraw_gen_descriptor *)addr; switch (hgd->hgd_report_type) { case HID_INPUT_REPORT: size = sc->sc_rdesc->isize; id = sc->sc_rdesc->iid; break; case HID_OUTPUT_REPORT: size = sc->sc_rdesc->osize; id = sc->sc_rdesc->oid; break; case HID_FEATURE_REPORT: size = sc->sc_rdesc->fsize; id = sc->sc_rdesc->fid; break; default: return (EINVAL); } size = MIN(hgd->hgd_maxlen, size); buf = HIDRAW_LOCAL_ALLOC(local_buf, size); copyin(hgd->hgd_data, buf, size); if (id != 0) id = *(uint8_t *)buf; error = hid_set_report(sc->sc_dev, buf, size, hgd->hgd_report_type, id); HIDRAW_LOCAL_FREE(local_buf, buf); return (error); case HIDRAW_GET_REPORT_ID: *(int *)addr = 0; /* XXX: we only support reportid 0? */ return (0); case HIDIOCGRDESCSIZE: *(int *)addr = sc->sc_rdesc->len; return (0); case HIDIOCGRDESC: hrd = *(struct hidraw_report_descriptor **)addr; error = copyin(&hrd->size, &size, sizeof(uint32_t)); if (error) return (error); /* * HID_MAX_DESCRIPTOR_SIZE-1 is a limit of report descriptor * size in current Linux implementation. */ if (size >= HID_MAX_DESCRIPTOR_SIZE) return (EINVAL); buf = HIDRAW_LOCAL_ALLOC(local_buf, size); error = hid_get_rdesc(sc->sc_dev, buf, size); if (error == 0) { size = MIN(size, sc->sc_rdesc->len); error = copyout(buf, hrd->value, size); } HIDRAW_LOCAL_FREE(local_buf, buf); return (error); case HIDIOCGRAWINFO: hdi = (struct hidraw_devinfo *)addr; hdi->bustype = sc->sc_hw->idBus; hdi->vendor = sc->sc_hw->idVendor; hdi->product = sc->sc_hw->idProduct; return (0); } /* variable-length ioctls handling */ len = IOCPARM_LEN(cmd); switch (IOCBASECMD(cmd)) { case HIDIOCGRAWNAME(0): strlcpy(addr, sc->sc_hw->name, len); return (0); case HIDIOCGRAWPHYS(0): strlcpy(addr, device_get_nameunit(sc->sc_dev), len); return (0); case HIDIOCSFEATURE(0): if (!(sc->sc_fflags & FWRITE)) return (EPERM); if (len < 2) return (EINVAL); id = *(uint8_t *)addr; if (id == 0) { addr = (uint8_t *)addr + 1; len--; } return (hid_set_report(sc->sc_dev, addr, len, HID_FEATURE_REPORT, id)); case HIDIOCGFEATURE(0): if (!(sc->sc_fflags & FREAD)) return (EPERM); if (len < 2) return (EINVAL); id = *(uint8_t *)addr; if (id == 0) { addr = (uint8_t *)addr + 1; len--; } return (hid_get_report(sc->sc_dev, addr, len, NULL, HID_FEATURE_REPORT, id)); case HIDIOCGRAWUNIQ(0): strlcpy(addr, sc->sc_hw->serial, len); return (0); } return (EINVAL); } static int hidraw_poll(struct cdev *dev, int events, struct thread *td) { struct hidraw_softc *sc; int revents = 0; sc = dev->si_drv1; if (sc == NULL) return (POLLHUP); if (events & (POLLOUT | POLLWRNORM) && (sc->sc_fflags & FWRITE)) revents |= events & (POLLOUT | POLLWRNORM); if (events & (POLLIN | POLLRDNORM) && (sc->sc_fflags & FREAD)) { mtx_lock(&sc->sc_mtx); if (sc->sc_head != sc->sc_tail) revents |= events & (POLLIN | POLLRDNORM); else { sc->sc_state.sel = true; selrecord(td, &sc->sc_rsel); } mtx_unlock(&sc->sc_mtx); } return (revents); } static int hidraw_kqfilter(struct cdev *dev, struct knote *kn) { struct hidraw_softc *sc; sc = dev->si_drv1; if (sc == NULL) return (ENXIO); switch(kn->kn_filter) { case EVFILT_READ: if (sc->sc_fflags & FREAD) { kn->kn_fop = &hidraw_filterops_read; break; } /* FALLTHROUGH */ default: return(EINVAL); } kn->kn_hook = sc; knlist_add(&sc->sc_rsel.si_note, kn, 0); return (0); } static int hidraw_kqread(struct knote *kn, long hint) { struct hidraw_softc *sc; int ret; sc = kn->kn_hook; mtx_assert(&sc->sc_mtx, MA_OWNED); if (sc->dev->si_drv1 == NULL) { kn->kn_flags |= EV_EOF; ret = 1; } else ret = (sc->sc_head != sc->sc_tail) ? 1 : 0; return (ret); } static void hidraw_kqdetach(struct knote *kn) { struct hidraw_softc *sc; sc = kn->kn_hook; knlist_remove(&sc->sc_rsel.si_note, kn, 0); } static void hidraw_notify(struct hidraw_softc *sc) { mtx_assert(&sc->sc_mtx, MA_OWNED); if (sc->sc_state.aslp) { sc->sc_state.aslp = false; DPRINTFN(5, "waking %p\n", &sc->sc_q); wakeup(&sc->sc_q); } if (sc->sc_state.sel) { sc->sc_state.sel = false; selwakeuppri(&sc->sc_rsel, PZERO); } if (sc->sc_async != NULL) { DPRINTFN(3, "sending SIGIO %p\n", sc->sc_async); PROC_LOCK(sc->sc_async); kern_psignal(sc->sc_async, SIGIO); PROC_UNLOCK(sc->sc_async); } KNOTE_LOCKED(&sc->sc_rsel.si_note, 0); } static device_method_t hidraw_methods[] = { /* Device interface */ DEVMETHOD(device_identify, hidraw_identify), DEVMETHOD(device_probe, hidraw_probe), DEVMETHOD(device_attach, hidraw_attach), DEVMETHOD(device_detach, hidraw_detach), DEVMETHOD_END }; static driver_t hidraw_driver = { "hidraw", hidraw_methods, sizeof(struct hidraw_softc) }; -static devclass_t hidraw_devclass; +#ifndef HIDRAW_MAKE_UHID_ALIAS +devclass_t hidraw_devclass; +#endif DRIVER_MODULE(hidraw, hidbus, hidraw_driver, hidraw_devclass, NULL, 0); MODULE_DEPEND(hidraw, hidbus, 1, 1, 1); MODULE_DEPEND(hidraw, hid, 1, 1, 1); MODULE_DEPEND(hidraw, usb, 1, 1, 1); MODULE_VERSION(hidraw, 1); diff --git a/sys/dev/usb/input/uhid.c b/sys/dev/usb/input/uhid.c index dd463dd7de8c..e5eae534a6d0 100644 --- a/sys/dev/usb/input/uhid.c +++ b/sys/dev/usb/input/uhid.c @@ -1,916 +1,928 @@ /* $NetBSD: uhid.c,v 1.46 2001/11/13 06:24:55 lukem Exp $ */ /* Also already merged from NetBSD: * $NetBSD: uhid.c,v 1.54 2002/09/23 05:51:21 simonb Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD * * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@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 "opt_hid.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include #include #include #define USB_DEBUG_VAR uhid_debug #include #include #include #ifdef USB_DEBUG static int uhid_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, uhid, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB uhid"); SYSCTL_INT(_hw_usb_uhid, OID_AUTO, debug, CTLFLAG_RWTUN, &uhid_debug, 0, "Debug level"); #endif #define UHID_BSIZE 1024 /* bytes, buffer size */ #define UHID_FRAME_NUM 50 /* bytes, frame number */ enum { UHID_INTR_DT_WR, UHID_INTR_DT_RD, UHID_CTRL_DT_WR, UHID_CTRL_DT_RD, UHID_N_TRANSFER, }; struct uhid_softc { struct usb_fifo_sc sc_fifo; struct mtx sc_mtx; struct usb_xfer *sc_xfer[UHID_N_TRANSFER]; struct usb_device *sc_udev; void *sc_repdesc_ptr; uint32_t sc_isize; uint32_t sc_osize; uint32_t sc_fsize; uint16_t sc_repdesc_size; uint8_t sc_iface_no; uint8_t sc_iface_index; uint8_t sc_iid; uint8_t sc_oid; uint8_t sc_fid; uint8_t sc_flags; #define UHID_FLAG_IMMED 0x01 /* set if read should be immediate */ #define UHID_FLAG_STATIC_DESC 0x04 /* set if report descriptors are * static */ }; static const uint8_t uhid_xb360gp_report_descr[] = {UHID_XB360GP_REPORT_DESCR()}; static const uint8_t uhid_graphire_report_descr[] = {UHID_GRAPHIRE_REPORT_DESCR()}; static const uint8_t uhid_graphire3_4x5_report_descr[] = {UHID_GRAPHIRE3_4X5_REPORT_DESCR()}; /* prototypes */ static device_probe_t uhid_probe; static device_attach_t uhid_attach; static device_detach_t uhid_detach; static usb_callback_t uhid_intr_write_callback; static usb_callback_t uhid_intr_read_callback; static usb_callback_t uhid_write_callback; static usb_callback_t uhid_read_callback; static usb_fifo_cmd_t uhid_start_read; static usb_fifo_cmd_t uhid_stop_read; static usb_fifo_cmd_t uhid_start_write; static usb_fifo_cmd_t uhid_stop_write; static usb_fifo_open_t uhid_open; static usb_fifo_close_t uhid_close; static usb_fifo_ioctl_t uhid_ioctl; static usb_fifo_ioctl_t uhid_ioctl_post; static struct usb_fifo_methods uhid_fifo_methods = { .f_open = &uhid_open, .f_close = &uhid_close, .f_ioctl = &uhid_ioctl, .f_ioctl_post = &uhid_ioctl_post, .f_start_read = &uhid_start_read, .f_stop_read = &uhid_stop_read, .f_start_write = &uhid_start_write, .f_stop_write = &uhid_stop_write, .basename[0] = "uhid", }; static void uhid_intr_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct uhid_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: tr_setup: pc = usbd_xfer_get_frame(xfer, 0); if (usb_fifo_get_data(sc->sc_fifo.fp[USB_FIFO_TX], pc, 0, usbd_xfer_max_len(xfer), &actlen, 0)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void uhid_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct uhid_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTF("transferred!\n"); pc = usbd_xfer_get_frame(xfer, 0); /* * If the ID byte is non zero we allow descriptors * having multiple sizes: */ if ((actlen >= (int)sc->sc_isize) || ((actlen > 0) && (sc->sc_iid != 0))) { /* limit report length to the maximum */ if (actlen > (int)sc->sc_isize) actlen = sc->sc_isize; usb_fifo_put_data(sc->sc_fifo.fp[USB_FIFO_RX], pc, 0, actlen, 1); } else { /* ignore it */ DPRINTF("ignored transfer, %d bytes\n", actlen); } case USB_ST_SETUP: re_submit: if (usb_fifo_put_bytes_max( sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { usbd_xfer_set_frame_len(xfer, 0, sc->sc_isize); usbd_transfer_submit(xfer); } return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto re_submit; } return; } } static void uhid_fill_set_report(struct usb_device_request *req, uint8_t iface_no, uint8_t type, uint8_t id, uint16_t size) { req->bmRequestType = UT_WRITE_CLASS_INTERFACE; req->bRequest = UR_SET_REPORT; USETW2(req->wValue, type, id); req->wIndex[0] = iface_no; req->wIndex[1] = 0; USETW(req->wLength, size); } static void uhid_fill_get_report(struct usb_device_request *req, uint8_t iface_no, uint8_t type, uint8_t id, uint16_t size) { req->bmRequestType = UT_READ_CLASS_INTERFACE; req->bRequest = UR_GET_REPORT; USETW2(req->wValue, type, id); req->wIndex[0] = iface_no; req->wIndex[1] = 0; USETW(req->wLength, size); } static void uhid_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct uhid_softc *sc = usbd_xfer_softc(xfer); struct usb_device_request req; struct usb_page_cache *pc; uint32_t size = sc->sc_osize; uint32_t actlen; uint8_t id; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: /* try to extract the ID byte */ if (sc->sc_oid) { pc = usbd_xfer_get_frame(xfer, 0); if (usb_fifo_get_data(sc->sc_fifo.fp[USB_FIFO_TX], pc, 0, 1, &actlen, 0)) { if (actlen != 1) { goto tr_error; } usbd_copy_out(pc, 0, &id, 1); } else { return; } if (size) { size--; } } else { id = 0; } pc = usbd_xfer_get_frame(xfer, 1); if (usb_fifo_get_data(sc->sc_fifo.fp[USB_FIFO_TX], pc, 0, UHID_BSIZE, &actlen, 1)) { if (actlen != size) { goto tr_error; } uhid_fill_set_report (&req, sc->sc_iface_no, UHID_OUTPUT_REPORT, id, size); pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &req, sizeof(req)); usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); usbd_xfer_set_frame_len(xfer, 1, size); usbd_xfer_set_frames(xfer, size ? 2 : 1); usbd_transfer_submit(xfer); } return; default: tr_error: /* bomb out */ usb_fifo_get_data_error(sc->sc_fifo.fp[USB_FIFO_TX]); return; } } static void uhid_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct uhid_softc *sc = usbd_xfer_softc(xfer); struct usb_device_request req; struct usb_page_cache *pc; pc = usbd_xfer_get_frame(xfer, 0); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: usb_fifo_put_data(sc->sc_fifo.fp[USB_FIFO_RX], pc, sizeof(req), sc->sc_isize, 1); return; case USB_ST_SETUP: if (usb_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) > 0) { uhid_fill_get_report (&req, sc->sc_iface_no, UHID_INPUT_REPORT, sc->sc_iid, sc->sc_isize); usbd_copy_in(pc, 0, &req, sizeof(req)); usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); usbd_xfer_set_frame_len(xfer, 1, sc->sc_isize); usbd_xfer_set_frames(xfer, sc->sc_isize ? 2 : 1); usbd_transfer_submit(xfer); } return; default: /* Error */ /* bomb out */ usb_fifo_put_data_error(sc->sc_fifo.fp[USB_FIFO_RX]); return; } } static const struct usb_config uhid_config[UHID_N_TRANSFER] = { [UHID_INTR_DT_WR] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .flags = {.pipe_bof = 1,.no_pipe_ok = 1, }, .bufsize = UHID_BSIZE, .callback = &uhid_intr_write_callback, }, [UHID_INTR_DT_RD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = UHID_BSIZE, .callback = &uhid_intr_read_callback, }, [UHID_CTRL_DT_WR] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request) + UHID_BSIZE, .callback = &uhid_write_callback, .timeout = 1000, /* 1 second */ }, [UHID_CTRL_DT_RD] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request) + UHID_BSIZE, .callback = &uhid_read_callback, .timeout = 1000, /* 1 second */ }, }; static void uhid_start_read(struct usb_fifo *fifo) { struct uhid_softc *sc = usb_fifo_softc(fifo); if (sc->sc_flags & UHID_FLAG_IMMED) { usbd_transfer_start(sc->sc_xfer[UHID_CTRL_DT_RD]); } else { usbd_transfer_start(sc->sc_xfer[UHID_INTR_DT_RD]); } } static void uhid_stop_read(struct usb_fifo *fifo) { struct uhid_softc *sc = usb_fifo_softc(fifo); usbd_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_RD]); usbd_transfer_stop(sc->sc_xfer[UHID_INTR_DT_RD]); } static void uhid_start_write(struct usb_fifo *fifo) { struct uhid_softc *sc = usb_fifo_softc(fifo); if ((sc->sc_flags & UHID_FLAG_IMMED) || sc->sc_xfer[UHID_INTR_DT_WR] == NULL) { usbd_transfer_start(sc->sc_xfer[UHID_CTRL_DT_WR]); } else { usbd_transfer_start(sc->sc_xfer[UHID_INTR_DT_WR]); } } static void uhid_stop_write(struct usb_fifo *fifo) { struct uhid_softc *sc = usb_fifo_softc(fifo); usbd_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_WR]); usbd_transfer_stop(sc->sc_xfer[UHID_INTR_DT_WR]); } static int uhid_get_report(struct uhid_softc *sc, uint8_t type, uint8_t id, void *kern_data, void *user_data, uint16_t len) { int err; uint8_t free_data = 0; if (kern_data == NULL) { kern_data = malloc(len, M_USBDEV, M_WAITOK); free_data = 1; } err = usbd_req_get_report(sc->sc_udev, NULL, kern_data, len, sc->sc_iface_index, type, id); if (err) { err = ENXIO; goto done; } if (user_data) { /* dummy buffer */ err = copyout(kern_data, user_data, len); if (err) { goto done; } } done: if (free_data) { free(kern_data, M_USBDEV); } return (err); } static int uhid_set_report(struct uhid_softc *sc, uint8_t type, uint8_t id, void *kern_data, void *user_data, uint16_t len) { int err; uint8_t free_data = 0; if (kern_data == NULL) { kern_data = malloc(len, M_USBDEV, M_WAITOK); free_data = 1; err = copyin(user_data, kern_data, len); if (err) { goto done; } } err = usbd_req_set_report(sc->sc_udev, NULL, kern_data, len, sc->sc_iface_index, type, id); if (err) { err = ENXIO; goto done; } done: if (free_data) { free(kern_data, M_USBDEV); } return (err); } static int uhid_open(struct usb_fifo *fifo, int fflags) { struct uhid_softc *sc = usb_fifo_softc(fifo); /* * The buffers are one byte larger than maximum so that one * can detect too large read/writes and short transfers: */ if (fflags & FREAD) { /* reset flags */ mtx_lock(&sc->sc_mtx); sc->sc_flags &= ~UHID_FLAG_IMMED; mtx_unlock(&sc->sc_mtx); if (usb_fifo_alloc_buffer(fifo, sc->sc_isize + 1, UHID_FRAME_NUM)) { return (ENOMEM); } } if (fflags & FWRITE) { if (usb_fifo_alloc_buffer(fifo, sc->sc_osize + 1, UHID_FRAME_NUM)) { return (ENOMEM); } } return (0); } static void uhid_close(struct usb_fifo *fifo, int fflags) { if (fflags & (FREAD | FWRITE)) { usb_fifo_free_buffer(fifo); } } static int uhid_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) { struct uhid_softc *sc = usb_fifo_softc(fifo); struct usb_gen_descriptor *ugd; uint32_t size; int error = 0; uint8_t id; switch (cmd) { case USB_GET_REPORT_DESC: ugd = addr; if (sc->sc_repdesc_size > ugd->ugd_maxlen) { size = ugd->ugd_maxlen; } else { size = sc->sc_repdesc_size; } ugd->ugd_actlen = size; if (ugd->ugd_data == NULL) break; /* descriptor length only */ error = copyout(sc->sc_repdesc_ptr, ugd->ugd_data, size); break; case USB_SET_IMMED: if (!(fflags & FREAD)) { error = EPERM; break; } if (*(int *)addr) { /* do a test read */ error = uhid_get_report(sc, UHID_INPUT_REPORT, sc->sc_iid, NULL, NULL, sc->sc_isize); if (error) { break; } mtx_lock(&sc->sc_mtx); sc->sc_flags |= UHID_FLAG_IMMED; mtx_unlock(&sc->sc_mtx); } else { mtx_lock(&sc->sc_mtx); sc->sc_flags &= ~UHID_FLAG_IMMED; mtx_unlock(&sc->sc_mtx); } break; case USB_GET_REPORT: if (!(fflags & FREAD)) { error = EPERM; break; } ugd = addr; switch (ugd->ugd_report_type) { case UHID_INPUT_REPORT: size = sc->sc_isize; id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; id = sc->sc_fid; break; default: return (EINVAL); } if (id != 0) copyin(ugd->ugd_data, &id, 1); error = uhid_get_report(sc, ugd->ugd_report_type, id, NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size)); break; case USB_SET_REPORT: if (!(fflags & FWRITE)) { error = EPERM; break; } ugd = addr; switch (ugd->ugd_report_type) { case UHID_INPUT_REPORT: size = sc->sc_isize; id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; id = sc->sc_fid; break; default: return (EINVAL); } if (id != 0) copyin(ugd->ugd_data, &id, 1); error = uhid_set_report(sc, ugd->ugd_report_type, id, NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size)); break; case USB_GET_REPORT_ID: *(int *)addr = 0; /* XXX: we only support reportid 0? */ break; default: error = ENOIOCTL; break; } return (error); } static int uhid_ioctl_post(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) { int error; switch (cmd) { case USB_GET_DEVICEINFO: error = ugen_fill_deviceinfo(fifo, addr); break; default: error = EINVAL; break; } return (error); } static const STRUCT_USB_HOST_ID uhid_devs[] = { /* generic HID class */ {USB_IFACE_CLASS(UICLASS_HID),}, /* the Xbox 360 gamepad doesn't use the HID class */ {USB_IFACE_CLASS(UICLASS_VENDOR), USB_IFACE_SUBCLASS(UISUBCLASS_XBOX360_CONTROLLER), USB_IFACE_PROTOCOL(UIPROTO_XBOX360_GAMEPAD),}, }; static int uhid_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); int error; void *buf; uint16_t len; DPRINTFN(11, "\n"); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); error = usbd_lookup_id_by_uaa(uhid_devs, sizeof(uhid_devs), uaa); if (error) return (error); if (usb_test_quirk(uaa, UQ_HID_IGNORE)) return (ENXIO); /* * Don't attach to mouse and keyboard devices, hence then no * "nomatch" event is generated and then ums and ukbd won't * attach properly when loaded. */ if ((uaa->info.bInterfaceClass == UICLASS_HID) && (uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) && (((uaa->info.bInterfaceProtocol == UIPROTO_BOOT_KEYBOARD) && !usb_test_quirk(uaa, UQ_KBD_IGNORE)) || ((uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) && !usb_test_quirk(uaa, UQ_UMS_IGNORE)))) return (ENXIO); /* Check for mandatory multitouch usages to give wmt(4) a chance */ if (!usb_test_quirk(uaa, UQ_WMT_IGNORE)) { error = usbd_req_get_hid_desc(uaa->device, NULL, &buf, &len, M_USBDEV, uaa->info.bIfaceIndex); /* Let HID decscriptor-less devices to be handled at attach */ if (!error) { if (hid_locate(buf, len, HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACT_MAX), hid_feature, 0, NULL, NULL, NULL) && hid_locate(buf, len, HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTID), hid_input, 0, NULL, NULL, NULL)) { free(buf, M_USBDEV); return (ENXIO); } free(buf, M_USBDEV); } } return (BUS_PROBE_GENERIC); } static int uhid_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct uhid_softc *sc = device_get_softc(dev); int unit = device_get_unit(dev); int error = 0; DPRINTFN(10, "sc=%p\n", sc); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "uhid lock", NULL, MTX_DEF | MTX_RECURSE); sc->sc_udev = uaa->device; sc->sc_iface_no = uaa->info.bIfaceNum; sc->sc_iface_index = uaa->info.bIfaceIndex; error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, uhid_config, UHID_N_TRANSFER, sc, &sc->sc_mtx); if (error) { DPRINTF("error=%s\n", usbd_errstr(error)); goto detach; } if (uaa->info.idVendor == USB_VENDOR_WACOM) { /* the report descriptor for the Wacom Graphire is broken */ if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE) { sc->sc_repdesc_size = sizeof(uhid_graphire_report_descr); sc->sc_repdesc_ptr = __DECONST(void *, &uhid_graphire_report_descr); sc->sc_flags |= UHID_FLAG_STATIC_DESC; } else if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) { static uint8_t reportbuf[] = {2, 2, 2}; /* * The Graphire3 needs 0x0202 to be written to * feature report ID 2 before it'll start * returning digitizer data. */ error = usbd_req_set_report(uaa->device, NULL, reportbuf, sizeof(reportbuf), uaa->info.bIfaceIndex, UHID_FEATURE_REPORT, 2); if (error) { DPRINTF("set report failed, error=%s (ignored)\n", usbd_errstr(error)); } sc->sc_repdesc_size = sizeof(uhid_graphire3_4x5_report_descr); sc->sc_repdesc_ptr = __DECONST(void *, &uhid_graphire3_4x5_report_descr); sc->sc_flags |= UHID_FLAG_STATIC_DESC; } } else if ((uaa->info.bInterfaceClass == UICLASS_VENDOR) && (uaa->info.bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER) && (uaa->info.bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)) { static const uint8_t reportbuf[3] = {1, 3, 0}; /* * Turn off the four LEDs on the gamepad which * are blinking by default: */ error = usbd_req_set_report(uaa->device, NULL, __DECONST(void *, reportbuf), sizeof(reportbuf), uaa->info.bIfaceIndex, UHID_OUTPUT_REPORT, 0); if (error) { DPRINTF("set output report failed, error=%s (ignored)\n", usbd_errstr(error)); } /* the Xbox 360 gamepad has no report descriptor */ sc->sc_repdesc_size = sizeof(uhid_xb360gp_report_descr); sc->sc_repdesc_ptr = __DECONST(void *, &uhid_xb360gp_report_descr); sc->sc_flags |= UHID_FLAG_STATIC_DESC; } if (sc->sc_repdesc_ptr == NULL) { error = usbd_req_get_hid_desc(uaa->device, NULL, &sc->sc_repdesc_ptr, &sc->sc_repdesc_size, M_USBDEV, uaa->info.bIfaceIndex); if (error) { device_printf(dev, "no report descriptor\n"); goto detach; } } error = usbd_req_set_idle(uaa->device, NULL, uaa->info.bIfaceIndex, 0, 0); if (error) { DPRINTF("set idle failed, error=%s (ignored)\n", usbd_errstr(error)); } sc->sc_isize = hid_report_size_max (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_input, &sc->sc_iid); sc->sc_osize = hid_report_size_max (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_output, &sc->sc_oid); sc->sc_fsize = hid_report_size_max (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_feature, &sc->sc_fid); if (sc->sc_isize > UHID_BSIZE) { DPRINTF("input size is too large, " "%d bytes (truncating)\n", sc->sc_isize); sc->sc_isize = UHID_BSIZE; } if (sc->sc_osize > UHID_BSIZE) { DPRINTF("output size is too large, " "%d bytes (truncating)\n", sc->sc_osize); sc->sc_osize = UHID_BSIZE; } if (sc->sc_fsize > UHID_BSIZE) { DPRINTF("feature size is too large, " "%d bytes (truncating)\n", sc->sc_fsize); sc->sc_fsize = UHID_BSIZE; } error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx, &uhid_fifo_methods, &sc->sc_fifo, unit, -1, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); if (error) { goto detach; } return (0); /* success */ detach: uhid_detach(dev); return (ENOMEM); } static int uhid_detach(device_t dev) { struct uhid_softc *sc = device_get_softc(dev); usb_fifo_detach(&sc->sc_fifo); usbd_transfer_unsetup(sc->sc_xfer, UHID_N_TRANSFER); if (sc->sc_repdesc_ptr) { if (!(sc->sc_flags & UHID_FLAG_STATIC_DESC)) { free(sc->sc_repdesc_ptr, M_USBDEV); } } mtx_destroy(&sc->sc_mtx); return (0); } +#ifndef HIDRAW_MAKE_UHID_ALIAS static devclass_t uhid_devclass; +#endif static device_method_t uhid_methods[] = { DEVMETHOD(device_probe, uhid_probe), DEVMETHOD(device_attach, uhid_attach), DEVMETHOD(device_detach, uhid_detach), DEVMETHOD_END }; static driver_t uhid_driver = { +#ifdef HIDRAW_MAKE_UHID_ALIAS + .name = "hidraw", +#else .name = "uhid", +#endif .methods = uhid_methods, .size = sizeof(struct uhid_softc), }; +#ifdef HIDRAW_MAKE_UHID_ALIAS +DRIVER_MODULE(uhid, uhub, uhid_driver, hidraw_devclass, NULL, 0); +#else DRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass, NULL, 0); +#endif MODULE_DEPEND(uhid, usb, 1, 1, 1); MODULE_DEPEND(uhid, hid, 1, 1, 1); MODULE_VERSION(uhid, 1); #ifndef USBHID_ENABLED USB_PNP_HOST_INFO(uhid_devs); #endif diff --git a/sys/modules/hid/hidraw/Makefile b/sys/modules/hid/hidraw/Makefile index e6305bc5bdb6..fdf9c31c9f3a 100644 --- a/sys/modules/hid/hidraw/Makefile +++ b/sys/modules/hid/hidraw/Makefile @@ -1,9 +1,9 @@ # $FreeBSD$ .PATH: ${SRCTOP}/sys/dev/hid KMOD= hidraw SRCS= hidraw.c -SRCS+= bus_if.h device_if.h +SRCS+= opt_hid.h bus_if.h device_if.h .include diff --git a/sys/modules/usb/uhid/Makefile b/sys/modules/usb/uhid/Makefile index f1fa4f660b36..1ae7b012903f 100644 --- a/sys/modules/usb/uhid/Makefile +++ b/sys/modules/usb/uhid/Makefile @@ -1,36 +1,36 @@ # # $FreeBSD$ # # Copyright (c) 2008 Hans Petter Selasky. All rights reserved. # # 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 AUTHOR 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 AUTHOR 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. # S= ${SRCTOP}/sys .PATH: $S/dev/usb/input KMOD= uhid -SRCS= opt_bus.h opt_usb.h device_if.h bus_if.h usb_if.h vnode_if.h usbdevs.h \ - uhid.c +SRCS= opt_bus.h opt_hid.h opt_usb.h device_if.h bus_if.h usb_if.h \ + vnode_if.h usbdevs.h uhid.c .include