diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index a5faad3e66ec..148670072757 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,1048 +1,1050 @@ # @(#)Makefile 8.1 (Berkeley) 6/18/93 .include MAN= aac.4 \ aacraid.4 \ acpi.4 \ ${_acpi_asus.4} \ ${_acpi_asus_wmi.4} \ ${_acpi_dock.4} \ ${_acpi_fujitsu.4} \ ${_acpi_hp.4} \ ${_acpi_ibm.4} \ ${_acpi_panasonic.4} \ ${_acpi_rapidstart.4} \ ${_acpi_sony.4} \ acpi_ged.4 \ acpi_thermal.4 \ acpi_battery.4 \ ${_acpi_toshiba.4} \ acpi_video.4 \ ${_acpi_wmi.4} \ ada.4 \ adm6996fc.4 \ ads111x.4 \ ae.4 \ ${_aesni.4} \ age.4 \ agp.4 \ ahc.4 \ ahci.4 \ ahd.4 \ ${_aibs.4} \ aio.4 \ alc.4 \ ale.4 \ alpm.4 \ altera_atse.4 \ altera_avgen.4 \ altera_jtag_uart.4 \ altera_sdcard.4 \ altq.4 \ amdpm.4 \ ${_amdsbwd.4} \ ${_amdsmb.4} \ ${_amdsmn.4} \ ${_amdtemp.4} \ ${_bxe.4} \ ${_aout.4} \ ${_apic.4} \ arcmsr.4 \ arswitch.4 \ ${_asmc.4} \ at45d.4 \ ata.4 \ ath.4 \ ath_hal.4 \ atkbd.4 \ atkbdc.4 \ + ${_atopcase.4} \ atp.4 \ ${_atf_test_case.4} \ ${_atrtc.4} \ ${_attimer.4} \ audit.4 \ auditpipe.4 \ aue.4 \ axe.4 \ axge.4 \ axp.4 \ bce.4 \ bcm5974.4 \ bcma.4 \ bfe.4 \ bge.4 \ ${_bhyve.4} \ bhnd.4 \ bhnd_chipc.4 \ bhnd_pmu.4 \ bhndb.4 \ bhndb_pci.4 \ blackhole.4 \ bnxt.4 \ boottrace.4 \ bpf.4 \ bridge.4 \ bwi.4 \ bwn.4 \ ${_bytgpio.4} \ capsicum.4 \ cardbus.4 \ carp.4 \ cas.4 \ cc_cdg.4 \ cc_chd.4 \ cc_cubic.4 \ cc_dctcp.4 \ cc_hd.4 \ cc_htcp.4 \ cc_newreno.4 \ cc_vegas.4 \ ${_ccd.4} \ ccr.4 \ cd.4 \ cdce.4 \ cdceem.4 \ cfi.4 \ cfumass.4 \ ${_cgem.4} \ ch.4 \ chromebook_platform.4 \ ${_chvgpio.4} \ ciss.4 \ ${_coretemp.4} \ cp2112.4 \ ${_cpuctl.4} \ cpufreq.4 \ crypto.4 \ ctl.4 \ cue.4 \ cxgb.4 \ cxgbe.4 \ cxgbev.4 \ cyapa.4 \ da.4 \ dc.4 \ dcons.4 \ dcons_crom.4 \ ddb.4 \ devctl.4 \ disc.4 \ disk.4 \ divert.4 \ ${_dpms.4} \ ds1307.4 \ ds3231.4 \ ${_dtrace_provs} \ dummynet.4 \ edsc.4 \ ehci.4 \ em.4 \ ena.4 \ enc.4 \ enic.4 \ epair.4 \ est.4 \ et.4 \ etherswitch.4 \ eventtimers.4 \ exca.4 \ e6060sw.4 \ fd.4 \ fdc.4 \ fdt.4 \ fdt_pinctrl.4 \ fdtbus.4 \ ffclock.4 \ filemon.4 \ firewire.4 \ ${_ftgpio.4} \ ${_ftwd.4} \ full.4 \ fwe.4 \ fwip.4 \ fwohci.4 \ fxp.4 \ gbde.4 \ gdb.4 \ gem.4 \ genet.4 \ genetlink.4 \ geom.4 \ geom_linux_lvm.4 \ geom_map.4 \ geom_uzip.4 \ gif.4 \ ${_gve.4} \ gpio.4 \ gpioiic.4 \ gpiokeys.4 \ gpioled.4 \ gpioths.4 \ gre.4 \ h_ertt.4 \ hconf.4 \ hcons.4 \ hgame.4 \ hidbus.4 \ hidquirk.4 \ hidraw.4 \ hifn.4 \ hkbd.4 \ hms.4 \ hmt.4 \ hpen.4 \ hpet.4 \ ${_hpt27xx.4} \ ${_hptiop.4} \ ${_hptmv.4} \ ${_hptnr.4} \ ${_hptrr.4} \ hsctrl.4 \ htu21.4 \ ${_hv_kvp.4} \ ${_hv_netvsc.4} \ ${_hv_storvsc.4} \ ${_hv_utils.4} \ ${_hv_vmbus.4} \ ${_hv_vss.4} \ hwpmc.4 \ ${_hwpstate_intel.4} \ iavf.4 \ ichsmb.4 \ ${_ichwd.4} \ icmp.4 \ icmp6.4 \ ida.4 \ ietp.4 \ if_ipsec.4 \ iflib.4 \ ifmib.4 \ ig4.4 \ igmp.4 \ iic.4 \ iic_gpiomux.4 \ iicbb.4 \ iicbus.4 \ iichid.4 \ iicmux.4 \ iicsmb.4 \ ${_igc.4} \ ${_imcsmb.4} \ inet.4 \ inet6.4 \ intpm.4 \ intro.4 \ ${_io.4} \ ${_ioat.4} \ ip.4 \ ip6.4 \ ipfirewall.4 \ ipheth.4 \ ${_ipmi.4} \ ips.4 \ ipsec.4 \ ipw.4 \ ipwfw.4 \ isci.4 \ isl.4 \ ismt.4 \ isp.4 \ ispfw.4 \ ${_itwd.4} \ iwi.4 \ iwifw.4 \ iwm.4 \ iwmfw.4 \ iwn.4 \ iwnfw.4 \ iwlwifi.4 \ iwlwififw.4 \ ixgbe.4 \ ixl.4 \ jedec_dimm.4 \ jme.4 \ kbdmux.4 \ kcov.4 \ keyboard.4 \ kld.4 \ ksyms.4 \ ksz8995ma.4 \ ktls.4 \ ktr.4 \ kue.4 \ ${_kvmclock.4} \ lagg.4 \ le.4 \ led.4 \ lge.4 \ ${_linux.4} \ liquidio.4 \ lm75.4 \ lo.4 \ lp.4 \ lpbb.4 \ lpt.4 \ ltc430x.4 \ mac.4 \ mac_biba.4 \ mac_bsdextended.4 \ mac_ddb.4 \ mac_ifoff.4 \ mac_ipacl.4 \ mac_lomac.4 \ mac_mls.4 \ mac_none.4 \ mac_ntpd.4 \ mac_partition.4 \ mac_portacl.4 \ mac_priority.4 \ mac_seeotheruids.4 \ mac_stub.4 \ mac_test.4 \ malo.4 \ max44009.4 \ md.4 \ mdio.4 \ me.4 \ mem.4 \ mfi.4 \ ${_mgb.4} \ miibus.4 \ mld.4 \ mlx.4 \ mlx4en.4 \ mlx5en.4 \ mmc.4 \ mmcsd.4 \ mod_cc.4 \ mos.4 \ mouse.4 \ mpi3mr.4 \ mpr.4 \ mps.4 \ mpt.4 \ mrsas.4 \ msk.4 \ mtio.4 \ multicast.4 \ muge.4 \ mvs.4 \ mwl.4 \ mwlfw.4 \ mx25l.4 \ mxge.4 \ my.4 \ net80211.4 \ netdump.4 \ netfpga10g_nf10bmac.4 \ netgdb.4 \ netgraph.4 \ netintro.4 \ netlink.4 \ netmap.4 \ ${_nfe.4} \ ${_nfsmb.4} \ ng_async.4 \ ng_bpf.4 \ ng_bridge.4 \ ng_btsocket.4 \ ng_car.4 \ ng_checksum.4 \ ng_cisco.4 \ ng_deflate.4 \ ng_device.4 \ nge.4 \ ng_echo.4 \ ng_eiface.4 \ ng_etf.4 \ ng_ether.4 \ ng_ether_echo.4 \ ng_frame_relay.4 \ ng_gif.4 \ ng_gif_demux.4 \ ng_hci.4 \ ng_hole.4 \ ng_hub.4 \ ng_iface.4 \ ng_ipfw.4 \ ng_ip_input.4 \ ng_ksocket.4 \ ng_l2cap.4 \ ng_l2tp.4 \ ng_lmi.4 \ ng_macfilter.4 \ ng_mppc.4 \ ng_nat.4 \ ng_netflow.4 \ ng_one2many.4 \ ng_patch.4 \ ng_pipe.4 \ ng_ppp.4 \ ng_pppoe.4 \ ng_pptpgre.4 \ ng_pred1.4 \ ng_rfc1490.4 \ ng_socket.4 \ ng_source.4 \ ng_split.4 \ ng_tag.4 \ ng_tcpmss.4 \ ng_tee.4 \ ng_tty.4 \ ng_ubt.4 \ ng_UI.4 \ ng_vjc.4 \ ng_vlan.4 \ ng_vlan_rotate.4 \ nmdm.4 \ ${_ntb.4} \ ${_ntb_hw_amd.4} \ ${_ntb_hw_intel.4} \ ${_ntb_hw_plx.4} \ ${_ntb_transport.4} \ ${_nda.4} \ ${_if_ntb.4} \ null.4 \ numa.4 \ ${_nvd.4} \ ${_nvdimm.4} \ ${_nvme.4} \ ${_nvram.4} \ oce.4 \ ocs_fc.4\ ohci.4 \ openfirm.4 \ orm.4 \ ${_ossl.4} \ ow.4 \ ow_temp.4 \ owc.4 \ ovpn.4 \ ${_padlock.4} \ pass.4 \ pca954x.4 \ pccard.4 \ pccbb.4 \ pcf.4 \ pcf8574.4 \ pcf8591.4 \ ${_pchtherm.4} \ pci.4 \ pcib.4 \ pcic.4 \ pcm.4 \ ${_pf.4} \ ${_pflog.4} \ ${_pfsync.4} \ pim.4 \ pms.4 \ polling.4 \ ppbus.4 \ ppc.4 \ ppi.4 \ procdesc.4 \ proto.4 \ ps4dshock.4 \ psm.4 \ pst.4 \ pt.4 \ ptnet.4 \ pts.4 \ pty.4 \ puc.4 \ pwmc.4 \ ${_qat.4} \ ${_qat_c2xxx.4} \ ${_qlxge.4} \ ${_qlxgb.4} \ ${_qlxgbe.4} \ ${_qlnxe.4} \ ral.4 \ random.4 \ rctl.4 \ re.4 \ rgephy.4 \ rights.4 \ rl.4 \ rndtest.4 \ route.4 \ rtnetlink.4 \ rtsx.4 \ rtw88.4 \ rtw88fw.4 \ rtwn.4 \ rtwnfw.4 \ rtwn_pci.4 \ rue.4 \ sa.4 \ safe.4 \ safexcel.4 \ sbp.4 \ sbp_targ.4 \ scc.4 \ sched_4bsd.4 \ sched_ule.4 \ screen.4 \ scsi.4 \ sctp.4 \ sdhci.4 \ sem.4 \ send.4 \ ses.4 \ ${_sfxge.4} \ sge.4 \ siba.4 \ siftr.4 \ siis.4 \ simplebus.4 \ sis.4 \ sk.4 \ ${_smartpqi.4} \ smb.4 \ smbios.4 \ smbus.4 \ smp.4 \ smsc.4 \ snd_als4000.4 \ snd_atiixp.4 \ snd_cmi.4 \ snd_cs4281.4 \ snd_csa.4 \ snd_emu10k1.4 \ snd_emu10kx.4 \ snd_envy24.4 \ snd_envy24ht.4 \ snd_es137x.4 \ snd_fm801.4 \ snd_hda.4 \ snd_hdspe.4 \ snd_ich.4 \ snd_maestro3.4 \ snd_neomagic.4 \ snd_solo.4 \ snd_spicds.4 \ snd_t4dwave.4 \ snd_uaudio.4 \ snd_via8233.4 \ snd_via82c686.4 \ snd_vibes.4 \ sndstat.4 \ snp.4 \ spigen.4 \ ${_spkr.4} \ splash.4 \ ste.4 \ stf.4 \ stge.4 \ ${_sume.4} \ ${_superio.4} \ sym.4 \ syncache.4 \ syncer.4 \ syscons.4 \ sysmouse.4 \ tap.4 \ targ.4 \ tcp.4 \ tcp_bbr.4 \ tcp_rack.4 \ tdfx.4 \ termios.4 \ textdump.4 \ ti.4 \ timecounters.4 \ ${_tpm.4} \ tslog.4 \ tty.4 \ tun.4 \ twe.4 \ tws.4 \ udp.4 \ udplite.4 \ ure.4 \ vale.4 \ vga.4 \ vge.4 \ viapm.4 \ ${_viawd.4} \ virtio.4 \ virtio_balloon.4 \ virtio_blk.4 \ virtio_console.4 \ virtio_gpu.4 \ virtio_random.4 \ virtio_scsi.4 \ ${_vmci.4} \ vkbd.4 \ vlan.4 \ vxlan.4 \ ${_vmd.4} \ ${_vmm.4} \ ${_vmx.4} \ vr.4 \ vt.4 \ vte.4 \ vtnet.4 \ watchdog.4 \ ${_wbwd.4} \ ${_wdatwd.4} \ wg.4 \ witness.4 \ wlan.4 \ wlan_acl.4 \ wlan_amrr.4 \ wlan_ccmp.4 \ wlan_tkip.4 \ wlan_wep.4 \ wlan_xauth.4 \ wmt.4 \ ${_wpi.4} \ wsp.4 \ xb360gp.4 \ ${_xen.4} \ xhci.4 \ xl.4 \ ${_xnb.4} \ xpt.4 \ zero.4 MLINKS= ads111x.4 ads1013.4 \ ads111x.4 ads1014.4 \ ads111x.4 ads1015.4 \ ads111x.4 ads1113.4 \ ads111x.4 ads1114.4 \ ads111x.4 ads1115.4 MLINKS+=ae.4 if_ae.4 MLINKS+=age.4 if_age.4 MLINKS+=agp.4 agpgart.4 MLINKS+=alc.4 if_alc.4 MLINKS+=ale.4 if_ale.4 MLINKS+=altera_atse.4 atse.4 MLINKS+=altera_sdcard.4 altera_sdcardc.4 MLINKS+=altq.4 ALTQ.4 MLINKS+=ath.4 if_ath.4 MLINKS+=aue.4 if_aue.4 MLINKS+=axe.4 if_axe.4 MLINKS+=bce.4 if_bce.4 MLINKS+=bfe.4 if_bfe.4 MLINKS+=bge.4 if_bge.4 MLINKS+=bnxt.4 if_bnxt.4 MLINKS+=bridge.4 if_bridge.4 MLINKS+=bwi.4 if_bwi.4 MLINKS+=bwn.4 if_bwn.4 MLINKS+=${_bxe.4} ${_if_bxe.4} MLINKS+=cas.4 if_cas.4 MLINKS+=cdce.4 if_cdce.4 MLINKS+=cfi.4 cfid.4 MLINKS+=crypto.4 cryptodev.4 MLINKS+=cue.4 if_cue.4 MLINKS+=cxgb.4 if_cxgb.4 MLINKS+=cxgbe.4 if_cxgbe.4 \ cxgbe.4 vcxgbe.4 \ cxgbe.4 if_vcxgbe.4 \ cxgbe.4 cxl.4 \ cxgbe.4 if_cxl.4 \ cxgbe.4 vcxl.4 \ cxgbe.4 if_vcxl.4 \ cxgbe.4 cc.4 \ cxgbe.4 if_cc.4 \ cxgbe.4 vcc.4 \ cxgbe.4 if_vcc.4 MLINKS+=cxgbev.4 if_cxgbev.4 \ cxgbev.4 cxlv.4 \ cxgbev.4 if_cxlv.4 \ cxgbev.4 ccv.4 \ cxgbev.4 if_ccv.4 MLINKS+=dc.4 if_dc.4 MLINKS+=disc.4 if_disc.4 MLINKS+=edsc.4 if_edsc.4 MLINKS+=em.4 if_em.4 \ em.4 igb.4 \ em.4 if_igb.4 \ em.4 lem.4 \ em.4 if_lem.4 MLINKS+=enc.4 if_enc.4 MLINKS+=epair.4 if_epair.4 MLINKS+=et.4 if_et.4 MLINKS+=fd.4 stderr.4 \ fd.4 stdin.4 \ fd.4 stdout.4 MLINKS+=fdt.4 FDT.4 MLINKS+=firewire.4 ieee1394.4 MLINKS+=fwe.4 if_fwe.4 MLINKS+=fwip.4 if_fwip.4 MLINKS+=fxp.4 if_fxp.4 MLINKS+=gem.4 if_gem.4 MLINKS+=genet.4 if_genet.4 MLINKS+=geom.4 GEOM.4 MLINKS+=gif.4 if_gif.4 MLINKS+=gpio.4 gpiobus.4 MLINKS+=gpioths.4 dht11.4 MLINKS+=gpioths.4 dht22.4 MLINKS+=gre.4 if_gre.4 MLINKS+=hpet.4 acpi_hpet.4 MLINKS+=${_hptrr.4} ${_rr232x.4} MLINKS+=${_attimer.4} ${_i8254.4} MLINKS+=ip.4 rawip.4 MLINKS+=ipfirewall.4 ipaccounting.4 \ ipfirewall.4 ipacct.4 \ ipfirewall.4 ipfw.4 MLINKS+=ipheth.4 if_ipheth.4 MLINKS+=ipw.4 if_ipw.4 MLINKS+=iwi.4 if_iwi.4 MLINKS+=iwm.4 if_iwm.4 MLINKS+=iwn.4 if_iwn.4 MLINKS+=ixgbe.4 ix.4 MLINKS+=ixgbe.4 if_ix.4 MLINKS+=ixgbe.4 if_ixgbe.4 MLINKS+=ixl.4 if_ixl.4 MLINKS+=iavf.4 if_iavf.4 MLINKS+=jme.4 if_jme.4 MLINKS+=kue.4 if_kue.4 MLINKS+=lagg.4 trunk.4 MLINKS+=lagg.4 if_lagg.4 MLINKS+=le.4 if_le.4 MLINKS+=lge.4 if_lge.4 MLINKS+=lo.4 loop.4 MLINKS+=lp.4 plip.4 MLINKS+=malo.4 if_malo.4 MLINKS+=md.4 vn.4 MLINKS+=mem.4 kmem.4 MLINKS+=mfi.4 mfi_linux.4 \ mfi.4 mfip.4 MLINKS+=mlx5en.4 mce.4 MLINKS+=mos.4 if_mos.4 MLINKS+=msk.4 if_msk.4 MLINKS+=mwl.4 if_mwl.4 MLINKS+=mxge.4 if_mxge.4 MLINKS+=my.4 if_my.4 MLINKS+=netfpga10g_nf10bmac.4 if_nf10bmac.4 MLINKS+=netintro.4 net.4 \ netintro.4 networking.4 MLINKS+=${_nfe.4} ${_if_nfe.4} MLINKS+=nge.4 if_nge.4 MLINKS+=openfirm.4 openfirmware.4 MLINKS+=ow.4 onewire.4 MLINKS+=pccbb.4 cbb.4 MLINKS+=pcm.4 snd.4 \ pcm.4 sound.4 MLINKS+=pms.4 pmspcv.4 MLINKS+=ptnet.4 if_ptnet.4 MLINKS+=ral.4 if_ral.4 MLINKS+=re.4 if_re.4 MLINKS+=rl.4 if_rl.4 MLINKS+=rtwn_pci.4 if_rtwn_pci.4 MLINKS+=rue.4 if_rue.4 MLINKS+=scsi.4 CAM.4 \ scsi.4 cam.4 \ scsi.4 scbus.4 \ scsi.4 SCSI.4 MLINKS+=sge.4 if_sge.4 MLINKS+=sis.4 if_sis.4 MLINKS+=sk.4 if_sk.4 MLINKS+=smp.4 SMP.4 MLINKS+=smsc.4 if_smsc.4 MLINKS+=snd_envy24.4 snd_ak452x.4 MLINKS+=${_spkr.4} ${_speaker.4} MLINKS+=splash.4 screensaver.4 MLINKS+=ste.4 if_ste.4 MLINKS+=stf.4 if_stf.4 MLINKS+=stge.4 if_stge.4 MLINKS+=syncache.4 syncookies.4 MLINKS+=syscons.4 sc.4 MLINKS+=tap.4 if_tap.4 \ tap.4 vmnet.4 \ tap.4 if_vmnet.4 MLINKS+=tdfx.4 tdfx_linux.4 MLINKS+=ti.4 if_ti.4 MLINKS+=tun.4 if_tun.4 MLINKS+=ure.4 if_ure.4 MLINKS+=vge.4 if_vge.4 MLINKS+=vlan.4 if_vlan.4 MLINKS+=vxlan.4 if_vxlan.4 MLINKS+=${_vmx.4} ${_if_vmx.4} MLINKS+=vr.4 if_vr.4 MLINKS+=vte.4 if_vte.4 MLINKS+=vtnet.4 if_vtnet.4 MLINKS+=watchdog.4 SW_WATCHDOG.4 MLINKS+=wg.4 if_wg.4 MLINKS+=wlan.4 wifi.4 MLINKS+=${_wpi.4} ${_if_wpi.4} MLINKS+=xl.4 if_xl.4 .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" _acpi_asus.4= acpi_asus.4 _acpi_asus_wmi.4= acpi_asus_wmi.4 _acpi_dock.4= acpi_dock.4 _acpi_fujitsu.4=acpi_fujitsu.4 _acpi_hp.4= acpi_hp.4 _acpi_ibm.4= acpi_ibm.4 _acpi_panasonic.4=acpi_panasonic.4 _acpi_rapidstart.4=acpi_rapidstart.4 _acpi_sony.4= acpi_sony.4 _acpi_toshiba.4=acpi_toshiba.4 _acpi_wmi.4= acpi_wmi.4 _aesni.4= aesni.4 _aout.4= aout.4 _apic.4= apic.4 _atrtc.4= atrtc.4 _attimer.4= attimer.4 _aibs.4= aibs.4 _amdsbwd.4= amdsbwd.4 _amdsmb.4= amdsmb.4 _amdsmn.4= amdsmn.4 _amdtemp.4= amdtemp.4 _asmc.4= asmc.4 +_atopcase.4= atopcase.4 _bxe.4= bxe.4 _bytgpio.4= bytgpio.4 _chvgpio.4= chvgpio.4 _coretemp.4= coretemp.4 _cpuctl.4= cpuctl.4 _dpms.4= dpms.4 _ftgpio.4= ftgpio.4 _ftwd.4= ftwd.4 _hpt27xx.4= hpt27xx.4 _hptiop.4= hptiop.4 _hptmv.4= hptmv.4 _hptnr.4= hptnr.4 _hptrr.4= hptrr.4 _hv_kvp.4= hv_kvp.4 _hv_netvsc.4= hv_netvsc.4 _hv_storvsc.4= hv_storvsc.4 _hv_utils.4= hv_utils.4 _hv_vmbus.4= hv_vmbus.4 _hv_vss.4= hv_vss.4 _hwpstate_intel.4= hwpstate_intel.4 _i8254.4= i8254.4 _ichwd.4= ichwd.4 _if_bxe.4= if_bxe.4 _if_nfe.4= if_nfe.4 _if_urtw.4= if_urtw.4 _if_vmx.4= if_vmx.4 _if_wpi.4= if_wpi.4 _igc.4= igc.4 _imcsmb.4= imcsmb.4 _io.4= io.4 _itwd.4= itwd.4 _kvmclock.4= kvmclock.4 _mgb.4= mgb.4 _nda.4= nda.4 _nfe.4= nfe.4 _nfsmb.4= nfsmb.4 _if_ntb.4= if_ntb.4 _ntb.4= ntb.4 _ntb_hw_amd.4= ntb_hw_amd.4 _ntb_hw_intel.4= ntb_hw_intel.4 _ntb_hw_plx.4= ntb_hw_plx.4 _ntb_transport.4=ntb_transport.4 _nvd.4= nvd.4 _nvme.4= nvme.4 _nvram.4= nvram.4 _padlock.4= padlock.4 _pchtherm.4= pchtherm.4 _qat.4= qat.4 _qat_c2xxx.4= qat_c2xxx.4 _rr232x.4= rr232x.4 _speaker.4= speaker.4 _spkr.4= spkr.4 _superio.4= superio.4 _tpm.4= tpm.4 _urtw.4= urtw.4 _viawd.4= viawd.4 _vmci.4= vmci.4 _vmd.4= vmd.4 _vmx.4= vmx.4 _wbwd.4= wbwd.4 _wdatwd.4= wdatwd.4 _wpi.4= wpi.4 _xen.4= xen.4 _xnb.4= xnb.4 .endif .if ${MACHINE_CPUARCH} == "amd64" _ioat.4= ioat.4 _nvdimm.4= nvdimm.4 _qlxge.4= qlxge.4 _qlxgb.4= qlxgb.4 _qlxgbe.4= qlxgbe.4 _qlnxe.4= qlnxe.4 _sfxge.4= sfxge.4 _smartpqi.4= smartpqi.4 _sume.4= sume.4 MLINKS+=qlxge.4 if_qlxge.4 MLINKS+=qlxgb.4 if_qlxgb.4 MLINKS+=qlxgbe.4 if_qlxgbe.4 MLINKS+=qlnxe.4 if_qlnxe.4 MLINKS+=sfxge.4 if_sfxge.4 MLINKS+=sume.4 if_sume.4 .if ${MK_BHYVE} != "no" _bhyve.4= bhyve.4 _vmm.4= vmm.4 .endif .endif .if ${MACHINE_CPUARCH} == "powerpc" _nvd.4= nvd.4 _nvme.4= nvme.4 .endif .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" || \ ${MACHINE_CPUARCH} == "aarch64" _gve.4= gve.4 _ipmi.4= ipmi.4 _linux.4= linux.4 _ossl.4= ossl.4 .endif .if ${MACHINE_CPUARCH} == "arm" || ${MACHINE_CPUARCH} == "aarch64" || \ ${MACHINE_CPUARCH} == "riscv" _cgem.4= cgem.4 MLINKS+=cgem.4 if_cgem.4 .endif .if empty(MAN_ARCH) || ${MAN_ARCH} == "all" __arches= ${:!/bin/sh -c "/bin/ls -d ${.CURDIR}/man4.*"!:E} .else __arches= ${MAN_ARCH} .endif .for __arch in ${__arches:O:u} .if exists(${.CURDIR}/man4.${__arch}) SUBDIR+= man4.${__arch} .endif .endfor .if ${MK_BLUETOOTH} != "no" MAN+= ng_bluetooth.4 .endif .if ${MK_CCD} != "no" _ccd.4= ccd.4 .endif .if ${MK_CDDL} != "no" _dtrace_provs= dtrace_audit.4 \ dtrace_io.4 \ dtrace_ip.4 \ dtrace_kinst.4 \ dtrace_lockstat.4 \ dtrace_proc.4 \ dtrace_sched.4 \ dtrace_sctp.4 \ dtrace_tcp.4 \ dtrace_udp.4 \ dtrace_udplite.4 MLINKS+= dtrace_audit.4 dtaudit.4 .endif .if ${MK_EFI} != "no" MAN+= efidev.4 MLINKS+= efidev.4 efirtc.4 .endif .if ${MK_ISCSI} != "no" MAN+= cfiscsi.4 MAN+= iscsi.4 MAN+= iser.4 .endif .if ${MK_OFED} != "no" MAN+= mlx4ib.4 MAN+= mlx5ib.4 .endif .if ${MK_MLX5TOOL} != "no" MAN+= mlx5io.4 .endif .if ${MK_TESTS} != "no" ATF= ${SRCTOP}/contrib/atf .PATH: ${ATF}/doc _atf_test_case.4= atf-test-case.4 .endif .if ${MK_PF} != "no" _pf.4= pf.4 _pflog.4= pflog.4 _pfsync.4= pfsync.4 .endif .if ${MK_USB} != "no" MAN+= \ otus.4 \ otusfw.4 \ rsu.4 \ rsufw.4 \ rtwn_usb.4 \ rum.4 \ run.4 \ runfw.4 \ u3g.4 \ uark.4 \ uart.4 \ uath.4 \ ubsa.4 \ ubser.4 \ ubtbcmfw.4 \ uchcom.4 \ ucom.4 \ ucycom.4 \ udav.4 \ udbp.4 \ udl.4 \ uep.4 \ ufoma.4 \ uftdi.4 \ ugen.4 \ ugold.4 \ uhci.4 \ uhid.4 \ uhso.4 \ uipaq.4 \ ukbd.4 \ uled.4 \ ulpt.4 \ umass.4 \ umcs.4 \ umct.4 \ umodem.4 \ umoscom.4 \ ums.4 \ unix.4 \ upgt.4 \ uplcom.4 \ ural.4 \ urio.4 \ urndis.4 \ ${_urtw.4} \ usb.4 \ usb_quirk.4 \ usb_template.4 \ usbhid.4 \ usfs.4 \ uslcom.4 \ uvisor.4 \ uvscom.4 \ zyd.4 MLINKS+=otus.4 if_otus.4 MLINKS+=rsu.4 if_rsu.4 MLINKS+=rtwn_usb.4 if_rtwn_usb.4 MLINKS+=rum.4 if_rum.4 MLINKS+=run.4 if_run.4 MLINKS+=u3g.4 u3gstub.4 MLINKS+=uath.4 if_uath.4 MLINKS+=udav.4 if_udav.4 MLINKS+=upgt.4 if_upgt.4 MLINKS+=ural.4 if_ural.4 MLINKS+=urndis.4 if_urndis.4 MLINKS+=${_urtw.4} ${_if_urtw.4} MLINKS+=zyd.4 if_zyd.4 .endif .include diff --git a/share/man/man4/atopcase.4 b/share/man/man4/atopcase.4 new file mode 100644 index 000000000000..fc93b7094466 --- /dev/null +++ b/share/man/man4/atopcase.4 @@ -0,0 +1,134 @@ +.\" Copyright (c) 2023 Vladimir Kondratyev +.\" +.\" 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. +.\" +.Dd August 17, 2023 +.Dt ATOPCASE 4 +.Os +.Sh NAME +.Nm atopcase +.Nd Apple HID-over-SPI transport driver +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device atopcase" +.Cd "device intelspi" +.Cd "device spibus" +.Cd "device hidbus" +.Cd "device hkbd" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +atopcase_load="YES" +hkbd_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for Human Interface Devices (HID) on +Serial Peripheral Interface (SPI) buses on Apple Intel Macs. +.Sh HARDWARE +The +.Nm +driver supports the following MacBooks produced in 2015-2018 years: +.Pp +.Bl -bullet -compact +.It +Macbook8,1 +.It +Macbook9,1 +.It +Macbook10,1 +.It +MacbookPro11,4 +.It +MacbookPro12,1 +.It +MacbookPro13,1 +.It +MacbookPro13,2 +.It +MacbookPro13,3 +.It +MacbookPro14,1 +.It +MacbookPro14,2 +.It +MacbookPro14,3 +.El +.Sh SYSCTL VARIABLES +The following variables are available as both +.Xr sysctl 8 +variables and +.Xr loader 8 +tunables: +.Bl -tag -width indent +.It Va hw.hid.atopcase.debug +Debug output level, where 0 is debugging disabled and larger values increase +debug message verbosity. +Default is 0. +.El +.Sh FILES +.Bl -tag -width ".Pa /dev/backlight/atopcase0" -compact +.It Pa /dev/backlight/atopcase0 +Keyboard +.Xr backlight 8 +device node. +.El +.Sh SEE ALSO +.Xr acpi 4 , +.Xr backlight 8 , +.Xr loader 8 , +.Xr loader.conf 5 . +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 14.0. +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was originally written by +.An Val Packett Aq Mt val@packett.cool +and marginally improved upon by +.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . +.Pp +This manual page was written by +.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . +.Sh BUGS +Device interrupts are not acknowledged on some hardware that results in +interrupt storm. +Installation of Darwin OSI in +.Xr acpi 4 +driver fixes the issue. +To install Darwin OSI add following lines to +.Xr loader.conf 5 : +.Bl -tag -width indent +.It Va hw.acpi.install_interface="Darwin" +.It Va hw.acpi.remove_interface="Windows 2009, Windows 2012" +.El diff --git a/sys/conf/files.x86 b/sys/conf/files.x86 index c3e67def1688..15781eea8fee 100644 --- a/sys/conf/files.x86 +++ b/sys/conf/files.x86 @@ -1,351 +1,353 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # # # This file contains all the x86 devices and such that are # common between i386 and amd64, but aren't applicable to # any other architecture we support. # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "${KEYMAP} -L ${ATKBD_DFLT_KEYMAP} | ${KEYMAP_FIX} > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" cddl/dev/fbt/x86/fbt_isa.c optional dtrace_fbt | dtraceall compile-with "${FBT_C}" cddl/dev/dtrace/x86/dis_tables.c optional dtrace_fbt | dtraceall compile-with "${DTRACE_C}" cddl/dev/dtrace/x86/instr_size.c optional dtrace_fbt | dtraceall compile-with "${DTRACE_C}" crypto/aesni/aesni.c optional aesni aesni_ghash.o optional aesni \ dependency "$S/crypto/aesni/aesni_ghash.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}" \ no-implicit-rule \ clean "aesni_ghash.o" aesni_ccm.o optional aesni \ dependency "$S/crypto/aesni/aesni_ccm.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}" \ no-implicit-rule \ clean "aesni_ccm.o" aesni_wrap.o optional aesni \ dependency "$S/crypto/aesni/aesni_wrap.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} -mmmx -msse -msse4 -maes ${.IMPSRC}" \ no-implicit-rule \ clean "aesni_wrap.o" intel_sha1.o optional aesni \ dependency "$S/crypto/aesni/intel_sha1.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} -mmmx -msse -msse4 -msha ${.IMPSRC}" \ no-implicit-rule \ clean "intel_sha1.o" intel_sha256.o optional aesni \ dependency "$S/crypto/aesni/intel_sha256.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} -mmmx -msse -msse4 -msha ${.IMPSRC}" \ no-implicit-rule \ clean "intel_sha256.o" crypto/openssl/ossl_x86.c optional ossl crypto/via/padlock.c optional padlock crypto/via/padlock_cipher.c optional padlock crypto/via/padlock_hash.c optional padlock dev/acpica/acpi_hpet.c optional acpi dev/acpica/acpi_if.m standard dev/acpica/acpi_pci.c optional acpi pci dev/acpica/acpi_pci_link.c optional acpi pci dev/acpica/acpi_pcib.c optional acpi pci dev/acpica/acpi_pcib_acpi.c optional acpi pci dev/acpica/acpi_pcib_pci.c optional acpi pci dev/acpica/acpi_pxm.c optional acpi dev/acpica/acpi_timer.c optional acpi dev/amdsbwd/amdsbwd.c optional amdsbwd dev/amdsmn/amdsmn.c optional amdsmn | amdtemp dev/amdtemp/amdtemp.c optional amdtemp dev/arcmsr/arcmsr.c optional arcmsr pci dev/asmc/asmc.c optional asmc isa dev/atkbdc/atkbd.c optional atkbd atkbdc dev/atkbdc/atkbd_atkbdc.c optional atkbd atkbdc dev/atkbdc/atkbdc.c optional atkbdc dev/atkbdc/atkbdc_isa.c optional atkbdc isa dev/atkbdc/atkbdc_subr.c optional atkbdc dev/atkbdc/psm.c optional psm atkbdc +dev/atopcase/atopcase.c optional atopcase acpi hid spibus +dev/atopcase/atopcase_acpi.c optional atopcase acpi hid spibus dev/bxe/bxe.c optional bxe pci dev/bxe/bxe_stats.c optional bxe pci dev/bxe/bxe_debug.c optional bxe pci dev/bxe/ecore_sp.c optional bxe pci dev/bxe/bxe_elink.c optional bxe pci dev/bxe/57710_init_values.c optional bxe pci dev/bxe/57711_init_values.c optional bxe pci dev/bxe/57712_init_values.c optional bxe pci dev/coretemp/coretemp.c optional coretemp dev/cpuctl/cpuctl.c optional cpuctl dev/dpms/dpms.c optional dpms dev/fb/fb.c optional fb | vga dev/fb/s3_pci.c optional s3pci dev/fb/vesa.c optional vga vesa dev/fb/vga.c optional vga dev/fdc/fdc.c optional fdc dev/fdc/fdc_acpi.c optional fdc dev/fdc/fdc_isa.c optional fdc isa dev/gpio/bytgpio.c optional bytgpio dev/gpio/chvgpio.c optional chvgpio dev/hpt27xx/hpt27xx_os_bsd.c optional hpt27xx dev/hpt27xx/hpt27xx_osm_bsd.c optional hpt27xx dev/hpt27xx/hpt27xx_config.c optional hpt27xx dev/hpt27xx/$M-elf.hpt27xx_lib.o optional hpt27xx dev/hptmv/entry.c optional hptmv dev/hptmv/mv.c optional hptmv dev/hptmv/gui_lib.c optional hptmv dev/hptmv/hptproc.c optional hptmv dev/hptmv/ioctl.c optional hptmv dev/hptmv/$M-elf.hptmvraid.o optional hptmv dev/hptnr/hptnr_os_bsd.c optional hptnr dev/hptnr/hptnr_osm_bsd.c optional hptnr dev/hptnr/hptnr_config.c optional hptnr dev/hptnr/$M-elf.hptnr_lib.o optional hptnr dev/hptrr/hptrr_os_bsd.c optional hptrr dev/hptrr/hptrr_osm_bsd.c optional hptrr dev/hptrr/hptrr_config.c optional hptrr dev/hptrr/$M-elf.hptrr_lib.o optional hptrr dev/hwpmc/hwpmc_amd.c optional hwpmc dev/hwpmc/hwpmc_intel.c optional hwpmc dev/hwpmc/hwpmc_core.c optional hwpmc dev/hwpmc/hwpmc_uncore.c optional hwpmc dev/hwpmc/hwpmc_tsc.c optional hwpmc dev/hwpmc/hwpmc_x86.c optional hwpmc dev/hyperv/hvsock/hv_sock.c optional hyperv dev/hyperv/input/hv_hid.c optional hyperv hvhid dev/hyperv/input/hv_kbd.c optional hyperv dev/hyperv/input/hv_kbdc.c optional hyperv dev/hyperv/pcib/vmbus_pcib.c optional hyperv pci dev/hyperv/netvsc/hn_nvs.c optional hyperv dev/hyperv/netvsc/hn_rndis.c optional hyperv dev/hyperv/netvsc/if_hn.c optional hyperv dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c optional hyperv dev/hyperv/utilities/hv_kvp.c optional hyperv dev/hyperv/utilities/hv_snapshot.c optional hyperv dev/hyperv/utilities/vmbus_heartbeat.c optional hyperv dev/hyperv/utilities/vmbus_ic.c optional hyperv dev/hyperv/utilities/vmbus_shutdown.c optional hyperv dev/hyperv/utilities/vmbus_timesync.c optional hyperv dev/hyperv/vmbus/hyperv.c optional hyperv dev/hyperv/vmbus/x86/hyperv_x86.c optional hyperv dev/hyperv/vmbus/x86/vmbus_x86.c optional hyperv dev/hyperv/vmbus/hyperv_busdma.c optional hyperv dev/hyperv/vmbus/vmbus.c optional hyperv pci dev/hyperv/vmbus/vmbus_br.c optional hyperv dev/hyperv/vmbus/vmbus_chan.c optional hyperv dev/hyperv/vmbus/vmbus_et.c optional hyperv dev/hyperv/vmbus/vmbus_if.m optional hyperv dev/hyperv/vmbus/vmbus_res.c optional hyperv dev/hyperv/vmbus/vmbus_xact.c optional hyperv dev/ichwd/ichwd.c optional ichwd dev/imcsmb/imcsmb.c optional imcsmb dev/imcsmb/imcsmb_pci.c optional imcsmb pci dev/intel/pchtherm.c optional pchtherm dev/intel/spi.c optional intelspi dev/intel/spi_pci.c optional intelspi pci dev/intel/spi_acpi.c optional intelspi acpi dev/io/iodev.c optional io dev/iommu/busdma_iommu.c optional acpi iommu pci dev/iommu/iommu_gas.c optional acpi iommu pci dev/ipmi/ipmi.c optional ipmi dev/ipmi/ipmi_acpi.c optional ipmi acpi dev/ipmi/ipmi_isa.c optional ipmi isa dev/ipmi/ipmi_bt.c optional ipmi dev/ipmi/ipmi_kcs.c optional ipmi dev/ipmi/ipmi_smic.c optional ipmi dev/ipmi/ipmi_smbus.c optional ipmi smbus dev/ipmi/ipmi_smbios.c optional ipmi dev/ipmi/ipmi_ssif.c optional ipmi smbus dev/ipmi/ipmi_pci.c optional ipmi pci dev/isci/isci.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/isci_controller.c optional isci dev/isci/isci_domain.c optional isci dev/isci/isci_interrupt.c optional isci dev/isci/isci_io_request.c optional isci dev/isci/isci_logger.c optional isci dev/isci/isci_oem_parameters.c optional isci dev/isci/isci_remote_device.c optional isci dev/isci/isci_sysctl.c optional isci dev/isci/isci_task_request.c optional isci dev/isci/isci_timer.c optional isci dev/isci/scil/sati.c optional isci dev/isci/scil/sati_abort_task_set.c optional isci dev/isci/scil/sati_atapi.c optional isci dev/isci/scil/sati_device.c optional isci dev/isci/scil/sati_inquiry.c optional isci dev/isci/scil/sati_log_sense.c optional isci dev/isci/scil/sati_lun_reset.c optional isci dev/isci/scil/sati_mode_pages.c optional isci dev/isci/scil/sati_mode_select.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/sati_mode_sense.c optional isci dev/isci/scil/sati_mode_sense_10.c optional isci dev/isci/scil/sati_mode_sense_6.c optional isci dev/isci/scil/sati_move.c optional isci dev/isci/scil/sati_passthrough.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/sati_read.c optional isci dev/isci/scil/sati_read_buffer.c optional isci dev/isci/scil/sati_read_capacity.c optional isci dev/isci/scil/sati_reassign_blocks.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/sati_report_luns.c optional isci dev/isci/scil/sati_request_sense.c optional isci dev/isci/scil/sati_start_stop_unit.c optional isci dev/isci/scil/sati_synchronize_cache.c optional isci dev/isci/scil/sati_test_unit_ready.c optional isci dev/isci/scil/sati_unmap.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/sati_util.c optional isci dev/isci/scil/sati_verify.c optional isci dev/isci/scil/sati_write.c optional isci dev/isci/scil/sati_write_and_verify.c optional isci dev/isci/scil/sati_write_buffer.c optional isci dev/isci/scil/sati_write_long.c optional isci dev/isci/scil/sci_abstract_list.c optional isci dev/isci/scil/sci_base_controller.c optional isci dev/isci/scil/sci_base_domain.c optional isci dev/isci/scil/sci_base_iterator.c optional isci dev/isci/scil/sci_base_library.c optional isci dev/isci/scil/sci_base_logger.c optional isci dev/isci/scil/sci_base_memory_descriptor_list.c optional isci dev/isci/scil/sci_base_memory_descriptor_list_decorator.c optional isci dev/isci/scil/sci_base_object.c optional isci dev/isci/scil/sci_base_observer.c optional isci dev/isci/scil/sci_base_phy.c optional isci dev/isci/scil/sci_base_port.c optional isci dev/isci/scil/sci_base_remote_device.c optional isci dev/isci/scil/sci_base_request.c optional isci dev/isci/scil/sci_base_state_machine.c optional isci dev/isci/scil/sci_base_state_machine_logger.c optional isci dev/isci/scil/sci_base_state_machine_observer.c optional isci dev/isci/scil/sci_base_subject.c optional isci dev/isci/scil/sci_util.c optional isci dev/isci/scil/scic_sds_controller.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/scic_sds_library.c optional isci dev/isci/scil/scic_sds_pci.c optional isci dev/isci/scil/scic_sds_phy.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/scic_sds_port.c optional isci dev/isci/scil/scic_sds_port_configuration_agent.c optional isci dev/isci/scil/scic_sds_remote_device.c optional isci dev/isci/scil/scic_sds_remote_node_context.c optional isci dev/isci/scil/scic_sds_remote_node_table.c optional isci dev/isci/scil/scic_sds_request.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/scic_sds_sgpio.c optional isci dev/isci/scil/scic_sds_smp_remote_device.c optional isci dev/isci/scil/scic_sds_smp_request.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/scic_sds_ssp_request.c optional isci dev/isci/scil/scic_sds_stp_packet_request.c optional isci dev/isci/scil/scic_sds_stp_remote_device.c optional isci dev/isci/scil/scic_sds_stp_request.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/scic_sds_unsolicited_frame_control.c optional isci dev/isci/scil/scif_sas_controller.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/scif_sas_controller_state_handlers.c optional isci dev/isci/scil/scif_sas_controller_states.c optional isci dev/isci/scil/scif_sas_domain.c optional isci dev/isci/scil/scif_sas_domain_state_handlers.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/scif_sas_domain_states.c optional isci dev/isci/scil/scif_sas_high_priority_request_queue.c optional isci dev/isci/scil/scif_sas_internal_io_request.c optional isci dev/isci/scil/scif_sas_io_request.c optional isci dev/isci/scil/scif_sas_io_request_state_handlers.c optional isci dev/isci/scil/scif_sas_io_request_states.c optional isci dev/isci/scil/scif_sas_library.c optional isci dev/isci/scil/scif_sas_remote_device.c optional isci dev/isci/scil/scif_sas_remote_device_ready_substate_handlers.c optional isci dev/isci/scil/scif_sas_remote_device_ready_substates.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/scif_sas_remote_device_starting_substate_handlers.c optional isci dev/isci/scil/scif_sas_remote_device_starting_substates.c optional isci dev/isci/scil/scif_sas_remote_device_state_handlers.c optional isci dev/isci/scil/scif_sas_remote_device_states.c optional isci dev/isci/scil/scif_sas_request.c optional isci dev/isci/scil/scif_sas_smp_activity_clear_affiliation.c optional isci dev/isci/scil/scif_sas_smp_io_request.c optional isci dev/isci/scil/scif_sas_smp_phy.c optional isci dev/isci/scil/scif_sas_smp_remote_device.c optional isci \ compile-with "${NORMAL_C} ${NO_WUNUSED_BUT_SET_VARIABLE}" dev/isci/scil/scif_sas_stp_io_request.c optional isci dev/isci/scil/scif_sas_stp_remote_device.c optional isci dev/isci/scil/scif_sas_stp_task_request.c optional isci dev/isci/scil/scif_sas_task_request.c optional isci dev/isci/scil/scif_sas_task_request_state_handlers.c optional isci dev/isci/scil/scif_sas_task_request_states.c optional isci dev/isci/scil/scif_sas_timer.c optional isci dev/itwd/itwd.c optional itwd dev/kvm_clock/kvm_clock.c optional kvm_clock dev/mana/gdma_main.c optional mana dev/mana/mana_en.c optional mana dev/mana/mana_sysctl.c optional mana dev/mana/shm_channel.c optional mana dev/mana/hw_channel.c optional mana dev/mana/gdma_util.c optional mana dev/qat_c2xxx/qat.c optional qat_c2xxx dev/qat_c2xxx/qat_ae.c optional qat_c2xxx dev/qat_c2xxx/qat_c2xxx.c optional qat_c2xxx dev/qat_c2xxx/qat_hw15.c optional qat_c2xxx dev/smbios/smbios_subr.c standard libkern/strcmp.c standard libkern/strncmp.c standard libkern/x86/crc32_sse42.c standard # # x86 shared code between IA32 and AMD64 architectures # x86/acpica/OsdEnvironment.c optional acpi x86/acpica/acpi_apm.c optional acpi x86/acpica/srat.c optional acpi x86/bios/vpd.c optional vpd x86/cpufreq/est.c optional cpufreq x86/cpufreq/hwpstate_amd.c optional cpufreq x86/cpufreq/hwpstate_intel.c optional cpufreq x86/cpufreq/p4tcc.c optional cpufreq x86/cpufreq/powernow.c optional cpufreq x86/iommu/intel_ctx.c optional acpi iommu pci x86/iommu/intel_drv.c optional acpi iommu pci x86/iommu/intel_fault.c optional acpi iommu pci x86/iommu/intel_idpgtbl.c optional acpi iommu pci x86/iommu/intel_intrmap.c optional acpi iommu pci x86/iommu/intel_qi.c optional acpi iommu pci x86/iommu/intel_quirks.c optional acpi iommu pci x86/iommu/intel_utils.c optional acpi iommu pci x86/isa/atrtc.c standard x86/isa/clock.c standard x86/isa/isa.c optional isa x86/isa/isa_dma.c optional isa x86/isa/nmi.c standard x86/isa/orm.c optional isa x86/pci/pci_bus.c optional pci x86/pci/qpi.c optional pci x86/x86/autoconf.c standard x86/x86/bus_machdep.c standard x86/x86/busdma_bounce.c standard x86/x86/busdma_machdep.c standard x86/x86/cpu_machdep.c standard x86/x86/dbreg.c optional ddb | gdb x86/x86/dump_machdep.c standard x86/x86/fdt_machdep.c optional fdt x86/x86/identcpu.c standard x86/x86/intr_machdep.c standard x86/x86/legacy.c standard x86/x86/mca.c standard x86/x86/x86_mem.c optional mem x86/x86/mp_x86.c optional smp x86/x86/nexus.c standard x86/x86/pvclock.c optional kvm_clock | xenhvm x86/x86/stack_machdep.c optional ddb | stack x86/x86/tsc.c standard x86/x86/ucode.c standard x86/x86/delay.c standard x86/xen/hvm.c optional xenhvm x86/xen/xen_apic.c optional xenhvm smp x86/xen/xen_arch_intr.c optional xenhvm diff --git a/sys/dev/atopcase/atopcase.c b/sys/dev/atopcase/atopcase.c new file mode 100644 index 000000000000..db1258b77a69 --- /dev/null +++ b/sys/dev/atopcase/atopcase.c @@ -0,0 +1,722 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2023 Val Packett + * Copyright (c) 2023 Vladimir Kondratyev + * + * 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. + */ + +#include "opt_hid.h" +#include "opt_spi.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define HID_DEBUG_VAR atopcase_debug +#include +#include + +#include +#include + +#include "spibus_if.h" + +#include "atopcase_reg.h" +#include "atopcase_var.h" + +#define ATOPCASE_IN_KDB() (SCHEDULER_STOPPED() || kdb_active) +#define ATOPCASE_IN_POLLING_MODE(sc) \ + (((sc)->sc_gpe_bit == 0 && ((sc)->sc_irq_ih == NULL)) || cold ||\ + ATOPCASE_IN_KDB()) +#define ATOPCASE_WAKEUP(sc, chan) do { \ + if (!ATOPCASE_IN_POLLING_MODE(sc)) { \ + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "wakeup: %p\n", chan); \ + wakeup(chan); \ + } \ +} while (0) +#define ATOPCASE_SPI_PAUSE() DELAY(100) +#define ATOPCASE_SPI_NO_SLEEP_FLAG(sc) \ + ((sc)->sc_irq_ih != NULL ? SPI_FLAG_NO_SLEEP : 0) + +/* Tunables */ +static SYSCTL_NODE(_hw_hid, OID_AUTO, atopcase, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + "Apple MacBook Topcase HID driver"); + +#ifdef HID_DEBUG +enum atopcase_log_level atopcase_debug = ATOPCASE_LLEVEL_DISABLED; + +SYSCTL_INT(_hw_hid_atopcase, OID_AUTO, debug, CTLFLAG_RWTUN, + &atopcase_debug, ATOPCASE_LLEVEL_DISABLED, "atopcase log level"); +#endif /* !HID_DEBUG */ + +static const uint8_t booted[] = { 0xa0, 0x80, 0x00, 0x00 }; +static const uint8_t status_ok[] = { 0xac, 0x27, 0x68, 0xd5 }; + +static inline struct atopcase_child * +atopcase_get_child_by_device(struct atopcase_softc *sc, uint8_t device) +{ + switch (device) { + case ATOPCASE_DEV_KBRD: + return (&sc->sc_kb); + case ATOPCASE_DEV_TPAD: + return (&sc->sc_tp); + default: + return (NULL); + } +} + +static int +atopcase_receive_status(struct atopcase_softc *sc) +{ + struct spi_command cmd = SPI_COMMAND_INITIALIZER; + uint8_t dummy_buffer[4] = { 0 }; + uint8_t status_buffer[4] = { 0 }; + int err; + + cmd.tx_cmd = dummy_buffer; + cmd.tx_cmd_sz = sizeof(dummy_buffer); + cmd.rx_cmd = status_buffer; + cmd.rx_cmd_sz = sizeof(status_buffer); + cmd.flags = ATOPCASE_SPI_NO_SLEEP_FLAG(sc); + + err = SPIBUS_TRANSFER(device_get_parent(sc->sc_dev), sc->sc_dev, &cmd); + ATOPCASE_SPI_PAUSE(); + if (err) { + device_printf(sc->sc_dev, "SPI error: %d\n", err); + return (err); + } + + DPRINTFN(ATOPCASE_LLEVEL_TRACE, "Status: %*D\n", 4, status_buffer, " "); + + if (memcmp(status_buffer, status_ok, sizeof(status_ok)) == 0) { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "Wrote command\n"); + ATOPCASE_WAKEUP(sc, sc->sc_dev); + } else { + device_printf(sc->sc_dev, "Failed to write command\n"); + return (EIO); + } + + return (0); +} + +static int +atopcase_process_message(struct atopcase_softc *sc, uint8_t device, void *msg, + uint16_t msg_len) +{ + struct atopcase_header *hdr = msg; + struct atopcase_child *ac; + void *payload; + uint16_t pl_len, crc; + + payload = (uint8_t *)msg + sizeof(*hdr); + pl_len = le16toh(hdr->len); + + if (pl_len + sizeof(*hdr) + sizeof(crc) != msg_len) { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, + "message with length overflow\n"); + return (EIO); + } + + crc = le16toh(*(uint16_t *)((uint8_t *)payload + pl_len)); + if (crc != crc16(0, msg, msg_len - sizeof(crc))) { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, + "message with failed checksum\n"); + return (EIO); + } + +#define CPOFF(dst, len, off) do { \ + unsigned _len = le16toh(len); \ + unsigned _off = le16toh(off); \ + if (pl_len >= _len + _off) { \ + memcpy(dst, (uint8_t*)payload + _off, MIN(_len, sizeof(dst)));\ + (dst)[MIN(_len, sizeof(dst) - 1)] = '\0'; \ + }} while (0); + + if ((ac = atopcase_get_child_by_device(sc, device)) != NULL + && hdr->type == ATOPCASE_MSG_TYPE_REPORT(device)) { + if (ac->open) + ac->intr_handler(ac->intr_ctx, payload, pl_len); + } else if (device == ATOPCASE_DEV_INFO + && hdr->type == ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_IFACE) + && (ac = atopcase_get_child_by_device(sc, hdr->type_arg)) != NULL) { + struct atopcase_iface_info_payload *iface = payload; + CPOFF(ac->name, iface->name_len, iface->name_off); + DPRINTF("Interface #%d name: %s\n", ac->device, ac->name); + } else if (device == ATOPCASE_DEV_INFO + && hdr->type == ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_DESCRIPTOR) + && (ac = atopcase_get_child_by_device(sc, hdr->type_arg)) != NULL) { + memcpy(ac->rdesc, payload, pl_len); + ac->rdesc_len = ac->hw.rdescsize = pl_len; + DPRINTF("%s HID report descriptor: %*D\n", ac->name, + (int) ac->hw.rdescsize, ac->rdesc, " "); + } else if (device == ATOPCASE_DEV_INFO + && hdr->type == ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_DEVICE) + && hdr->type_arg == ATOPCASE_INFO_DEVICE) { + struct atopcase_device_info_payload *dev = payload; + sc->sc_vid = le16toh(dev->vid); + sc->sc_pid = le16toh(dev->pid); + sc->sc_ver = le16toh(dev->ver); + CPOFF(sc->sc_vendor, dev->vendor_len, dev->vendor_off); + CPOFF(sc->sc_product, dev->product_len, dev->product_off); + CPOFF(sc->sc_serial, dev->serial_len, dev->serial_off); + if (bootverbose) { + device_printf(sc->sc_dev, "Device info descriptor:\n"); + printf(" Vendor: %s\n", sc->sc_vendor); + printf(" Product: %s\n", sc->sc_product); + printf(" Serial: %s\n", sc->sc_serial); + } + } + + return (0); +} + +int +atopcase_receive_packet(struct atopcase_softc *sc) +{ + struct atopcase_packet pkt = { 0 }; + struct spi_command cmd = SPI_COMMAND_INITIALIZER; + void *msg; + int err; + uint16_t length, remaining, offset, msg_len; + + bzero(&sc->sc_junk, sizeof(struct atopcase_packet)); + cmd.tx_cmd = &sc->sc_junk; + cmd.tx_cmd_sz = sizeof(struct atopcase_packet); + cmd.rx_cmd = &pkt; + cmd.rx_cmd_sz = sizeof(struct atopcase_packet); + cmd.flags = ATOPCASE_SPI_NO_SLEEP_FLAG(sc); + err = SPIBUS_TRANSFER(device_get_parent(sc->sc_dev), sc->sc_dev, &cmd); + ATOPCASE_SPI_PAUSE(); + if (err) { + device_printf(sc->sc_dev, "SPI error: %d\n", err); + return (err); + } + + DPRINTFN(ATOPCASE_LLEVEL_TRACE, "Response: %*D\n", 256, &pkt, " "); + + if (le16toh(pkt.checksum) != crc16(0, &pkt, sizeof(pkt) - 2)) { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "packet with failed checksum\n"); + return (EIO); + } + + /* + * When we poll and nothing has arrived we get a particular packet + * starting with '80 11 00 01' + */ + if (pkt.direction == ATOPCASE_DIR_NOTHING) { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "'Nothing' packet: %*D\n", 4, + &pkt, " "); + return (EAGAIN); + } + + if (pkt.direction != ATOPCASE_DIR_READ && + pkt.direction != ATOPCASE_DIR_WRITE) { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, + "unknown message direction 0x%x\n", pkt.direction); + return (EIO); + } + + length = le16toh(pkt.length); + remaining = le16toh(pkt.remaining); + offset = le16toh(pkt.offset); + + if (length > sizeof(pkt.data)) { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, + "packet with length overflow: %u\n", length); + return (EIO); + } + + if (pkt.direction == ATOPCASE_DIR_READ && + pkt.device == ATOPCASE_DEV_INFO && + length == sizeof(booted) && + memcmp(pkt.data, booted, length) == 0) { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "GPE boot packet\n"); + sc->sc_booted = true; + ATOPCASE_WAKEUP(sc, sc); + return (0); + } + + /* handle multi-packet messages */ + if (remaining != 0 || offset != 0) { + if (offset != sc->sc_msg_len) { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, + "Unexpected offset (got %u, expected %u)\n", + offset, sc->sc_msg_len); + sc->sc_msg_len = 0; + return (EIO); + } + + if ((size_t)remaining + length + offset > sizeof(sc->sc_msg)) { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, + "Message with length overflow: %zu\n", + (size_t)remaining + length + offset); + sc->sc_msg_len = 0; + return (EIO); + } + + memcpy(sc->sc_msg + offset, &pkt.data, length); + sc->sc_msg_len += length; + + if (remaining != 0) + return (0); + + msg = sc->sc_msg; + msg_len = sc->sc_msg_len; + } else { + msg = pkt.data; + msg_len = length; + } + sc->sc_msg_len = 0; + + err = atopcase_process_message(sc, pkt.device, msg, msg_len); + if (err == 0 && pkt.direction == ATOPCASE_DIR_WRITE) { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "Write ack\n"); + ATOPCASE_WAKEUP(sc, sc); + } + + return (err); +} + +static int +atopcase_send(struct atopcase_softc *sc, struct atopcase_packet *pkt) +{ + struct spi_command cmd = SPI_COMMAND_INITIALIZER; + int err, retries; + + cmd.tx_cmd = pkt; + cmd.tx_cmd_sz = sizeof(struct atopcase_packet); + cmd.rx_cmd = &sc->sc_junk; + cmd.rx_cmd_sz = sizeof(struct atopcase_packet); + cmd.flags = SPI_FLAG_KEEP_CS | ATOPCASE_SPI_NO_SLEEP_FLAG(sc); + + DPRINTFN(ATOPCASE_LLEVEL_TRACE, "Request: %*D\n", + (int)sizeof(struct atopcase_packet), cmd.tx_cmd, " "); + + if (!ATOPCASE_IN_POLLING_MODE(sc)) { + if (sc->sc_irq_ih != NULL) + mtx_lock(&sc->sc_mtx); + else + sx_xlock(&sc->sc_sx); + } + sc->sc_wait_for_status = true; + err = SPIBUS_TRANSFER(device_get_parent(sc->sc_dev), sc->sc_dev, &cmd); + ATOPCASE_SPI_PAUSE(); + if (!ATOPCASE_IN_POLLING_MODE(sc)) { + if (sc->sc_irq_ih != NULL) + mtx_unlock(&sc->sc_mtx); + else + sx_xunlock(&sc->sc_sx); + } + if (err != 0) { + device_printf(sc->sc_dev, "SPI error: %d\n", err); + goto exit; + } + + if (ATOPCASE_IN_POLLING_MODE(sc)) { + err = atopcase_receive_status(sc); + } else { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "wait for: %p\n", sc->sc_dev); + err = tsleep(sc->sc_dev, 0, "atcstat", hz / 10); + } + sc->sc_wait_for_status = false; + if (err != 0) { + DPRINTF("Write status read failed: %d\n", err); + goto exit; + } + + if (ATOPCASE_IN_POLLING_MODE(sc)) { + /* Backlight setting may require a lot of time */ + retries = 20; + while ((err = atopcase_receive_packet(sc)) == EAGAIN && + --retries != 0) + DELAY(1000); + } else { + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "wait for: %p\n", sc); + err = tsleep(sc, 0, "atcack", hz / 10); + } + if (err != 0) + DPRINTF("Write ack read failed: %d\n", err); + +exit: + if (err == EWOULDBLOCK) + err = EIO; + + return (err); +} + +static void +atopcase_create_message(struct atopcase_packet *pkt, uint8_t device, + uint16_t type, uint8_t type_arg, const void *payload, uint8_t len, + uint16_t resp_len) +{ + struct atopcase_header *hdr = (struct atopcase_header *)pkt->data; + uint16_t msg_checksum; + static uint8_t seq_no; + + KASSERT(len <= ATOPCASE_DATA_SIZE - sizeof(struct atopcase_header), + ("outgoing msg must be 1 packet")); + + bzero(pkt, sizeof(struct atopcase_packet)); + pkt->direction = ATOPCASE_DIR_WRITE; + pkt->device = device; + pkt->length = htole16(sizeof(*hdr) + len + 2); + + hdr->type = htole16(type); + hdr->type_arg = type_arg; + hdr->seq_no = seq_no++; + hdr->resp_len = htole16((resp_len == 0) ? len : resp_len); + hdr->len = htole16(len); + + memcpy(pkt->data + sizeof(*hdr), payload, len); + msg_checksum = htole16(crc16(0, pkt->data, pkt->length - 2)); + memcpy(pkt->data + sizeof(*hdr) + len, &msg_checksum, 2); + pkt->checksum = htole16(crc16(0, (uint8_t*)pkt, sizeof(*pkt) - 2)); + + return; +} + +static int +atopcase_request_desc(struct atopcase_softc *sc, uint16_t type, uint8_t device) +{ + atopcase_create_message( + &sc->sc_buf, ATOPCASE_DEV_INFO, type, device, NULL, 0, 0x200); + return (atopcase_send(sc, &sc->sc_buf)); +} + +int +atopcase_intr(struct atopcase_softc *sc) +{ + int err; + + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "Interrupt event\n"); + + if (sc->sc_wait_for_status) { + err = atopcase_receive_status(sc); + sc->sc_wait_for_status = false; + } else + err = atopcase_receive_packet(sc); + + return (err); +} + +static int +atopcase_add_child(struct atopcase_softc *sc, struct atopcase_child *ac, + uint8_t device) +{ + device_t hidbus; + int err = 0; + + ac->device = device; + + /* fill device info */ + strlcpy(ac->hw.name, "Apple MacBook", sizeof(ac->hw.name)); + ac->hw.idBus = BUS_SPI; + ac->hw.idVendor = sc->sc_vid; + ac->hw.idProduct = sc->sc_pid; + ac->hw.idVersion = sc->sc_ver; + strlcpy(ac->hw.idPnP, sc->sc_hid, sizeof(ac->hw.idPnP)); + strlcpy(ac->hw.serial, sc->sc_serial, sizeof(ac->hw.serial)); + /* + * HID write and set_report methods executed on Apple SPI topcase + * hardware do the same request on SPI layer. Set HQ_NOWRITE quirk to + * force hidmap to convert writes to set_reports. That makes HID bus + * write handler unnecessary and reduces code duplication. + */ + hid_add_dynamic_quirk(&ac->hw, HQ_NOWRITE); + + DPRINTF("Get the interface #%d descriptor\n", device); + err = atopcase_request_desc(sc, + ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_IFACE), device); + if (err) { + device_printf(sc->sc_dev, "can't receive iface descriptor\n"); + goto exit; + } + + DPRINTF("Get the \"%s\" HID report descriptor\n", ac->name); + err = atopcase_request_desc(sc, + ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_DESCRIPTOR), device); + if (err) { + device_printf(sc->sc_dev, "can't receive report descriptor\n"); + goto exit; + } + + hidbus = device_add_child(sc->sc_dev, "hidbus", -1); + if (hidbus == NULL) { + device_printf(sc->sc_dev, "can't add child\n"); + err = ENOMEM; + goto exit; + } + device_set_ivars(hidbus, &ac->hw); + ac->hidbus = hidbus; + +exit: + return (err); +} + +int +atopcase_init(struct atopcase_softc *sc) +{ + int err; + + /* Wait until we know we're getting reasonable responses */ + if(!sc->sc_booted && tsleep(sc, 0, "atcboot", hz / 20) != 0) { + device_printf(sc->sc_dev, "can't establish communication\n"); + err = EIO; + goto err; + } + + /* + * Management device may send a message on first boot after power off. + * Let interrupt handler to read and discard it. + */ + DELAY(2000); + + DPRINTF("Get the device descriptor\n"); + err = atopcase_request_desc(sc, + ATOPCASE_MSG_TYPE_INFO(ATOPCASE_INFO_DEVICE), + ATOPCASE_INFO_DEVICE); + if (err) { + device_printf(sc->sc_dev, "can't receive device descriptor\n"); + goto err; + } + + err = atopcase_add_child(sc, &sc->sc_kb, ATOPCASE_DEV_KBRD); + if (err != 0) + goto err; + err = atopcase_add_child(sc, &sc->sc_tp, ATOPCASE_DEV_TPAD); + if (err != 0) + goto err; + + /* TODO: skip on 2015 models where it's controlled by asmc */ + sc->sc_backlight = backlight_register("atopcase", sc->sc_dev); + if (!sc->sc_backlight) { + device_printf(sc->sc_dev, "can't register backlight\n"); + err = ENOMEM; + } + + if (sc->sc_tq != NULL) + taskqueue_enqueue_timeout(sc->sc_tq, &sc->sc_task, hz / 120); + + return (bus_generic_attach(sc->sc_dev)); + +err: + return (err); +} + +int +atopcase_destroy(struct atopcase_softc *sc) +{ + int err; + + err = device_delete_children(sc->sc_dev); + if (err) + return (err); + + if (sc->sc_backlight) + backlight_destroy(sc->sc_backlight); + + return (0); +} + +static struct atopcase_child * +atopcase_get_child_by_hidbus(device_t child) +{ + device_t parent = device_get_parent(child); + struct atopcase_softc *sc = device_get_softc(parent); + + if (child == sc->sc_kb.hidbus) + return (&sc->sc_kb); + if (child == sc->sc_tp.hidbus) + return (&sc->sc_tp); + panic("unknown child"); +} + +void +atopcase_intr_setup(device_t dev, device_t child, hid_intr_t intr, + void *context, struct hid_rdesc_info *rdesc) +{ + struct atopcase_child *ac = atopcase_get_child_by_hidbus(child); + + if (intr == NULL) + return; + + rdesc->rdsize = ATOPCASE_MSG_SIZE - sizeof(struct atopcase_header) - 2; + rdesc->grsize = 0; + rdesc->srsize = ATOPCASE_DATA_SIZE - sizeof(struct atopcase_header) - 2; + rdesc->wrsize = 0; + + ac->intr_handler = intr; + ac->intr_ctx = context; +} + +void +atopcase_intr_unsetup(device_t dev, device_t child) +{ +} + +int +atopcase_intr_start(device_t dev, device_t child) +{ + struct atopcase_softc *sc = device_get_softc(dev); + struct atopcase_child *ac = atopcase_get_child_by_hidbus(child); + + if (ATOPCASE_IN_POLLING_MODE(sc)) + sx_xlock(&sc->sc_write_sx); + else if (sc->sc_irq_ih != NULL) + mtx_lock(&sc->sc_mtx); + else + sx_xlock(&sc->sc_sx); + ac->open = true; + if (ATOPCASE_IN_POLLING_MODE(sc)) + sx_xunlock(&sc->sc_write_sx); + else if (sc->sc_irq_ih != NULL) + mtx_unlock(&sc->sc_mtx); + else + sx_xunlock(&sc->sc_sx); + + return (0); +} + +int +atopcase_intr_stop(device_t dev, device_t child) +{ + struct atopcase_softc *sc = device_get_softc(dev); + struct atopcase_child *ac = atopcase_get_child_by_hidbus(child); + + if (ATOPCASE_IN_POLLING_MODE(sc)) + sx_xlock(&sc->sc_write_sx); + else if (sc->sc_irq_ih != NULL) + mtx_lock(&sc->sc_mtx); + else + sx_xlock(&sc->sc_sx); + ac->open = false; + if (ATOPCASE_IN_POLLING_MODE(sc)) + sx_xunlock(&sc->sc_write_sx); + else if (sc->sc_irq_ih != NULL) + mtx_unlock(&sc->sc_mtx); + else + sx_xunlock(&sc->sc_sx); + + return (0); +} + +void +atopcase_intr_poll(device_t dev, device_t child) +{ + struct atopcase_softc *sc = device_get_softc(dev); + + (void)atopcase_receive_packet(sc); +} + +int +atopcase_get_rdesc(device_t dev, device_t child, void *buf, hid_size_t len) +{ + struct atopcase_child *ac = atopcase_get_child_by_hidbus(child); + + if (ac->rdesc_len != len) + return (ENXIO); + memcpy(buf, ac->rdesc, len); + + return (0); +} + +int +atopcase_set_report(device_t dev, device_t child, const void *buf, + hid_size_t len, uint8_t type __unused, uint8_t id) +{ + struct atopcase_softc *sc = device_get_softc(dev); + struct atopcase_child *ac = atopcase_get_child_by_hidbus(child); + int err; + + if (len >= ATOPCASE_DATA_SIZE - sizeof(struct atopcase_header) - 2) + return (EINVAL); + + DPRINTF("%s HID command SET_REPORT %d (len %d): %*D\n", + ac->name, id, len, len, buf, " "); + + if (!ATOPCASE_IN_KDB()) + sx_xlock(&sc->sc_write_sx); + atopcase_create_message(&sc->sc_buf, ac->device, + ATOPCASE_MSG_TYPE_SET_REPORT(ac->device, id), 0, buf, len, 0); + err = atopcase_send(sc, &sc->sc_buf); + if (!ATOPCASE_IN_KDB()) + sx_xunlock(&sc->sc_write_sx); + + return (err); +} + +int +atopcase_backlight_update_status(device_t dev, struct backlight_props *props) +{ + struct atopcase_softc *sc = device_get_softc(dev); + struct atopcase_bl_payload payload = { 0 }; + + payload.report_id = ATOPCASE_BKL_REPORT_ID; + payload.device = ATOPCASE_DEV_KBRD; + /* + * Hardware range is 32-255 for visible backlight, + * convert from percentages + */ + payload.level = (props->brightness == 0) ? 0 : + (32 + (223 * props->brightness / 100)); + payload.status = (payload.level > 0) ? 0x01F4 : 0x1; + + return (atopcase_set_report(dev, sc->sc_kb.hidbus, &payload, + sizeof(payload), HID_OUTPUT_REPORT, ATOPCASE_BKL_REPORT_ID)); +} + +int +atopcase_backlight_get_status(device_t dev, struct backlight_props *props) +{ + struct atopcase_softc *sc = device_get_softc(dev); + + props->brightness = sc->sc_backlight_level; + props->nlevels = 0; + + return (0); +} + +int +atopcase_backlight_get_info(device_t dev, struct backlight_info *info) +{ + info->type = BACKLIGHT_TYPE_KEYBOARD; + strlcpy(info->name, "Apple MacBook Keyboard", BACKLIGHTMAXNAMELENGTH); + + return (0); +} diff --git a/sys/dev/atopcase/atopcase_acpi.c b/sys/dev/atopcase/atopcase_acpi.c new file mode 100644 index 000000000000..a794163f1a39 --- /dev/null +++ b/sys/dev/atopcase/atopcase_acpi.c @@ -0,0 +1,458 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2023 Val Packett + * Copyright (c) 2023 Vladimir Kondratyev + * + * 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. + */ + +#include "opt_acpi.h" +#include "opt_hid.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#define HID_DEBUG_VAR atopcase_debug +#include +#include + +#include +#include + +#include "backlight_if.h" +#include "hid_if.h" + +#include "atopcase_reg.h" +#include "atopcase_var.h" + +/* + * XXX: The Linux driver only supports ACPI GPEs, but we only receive + * interrupts in this driver on a MacBookPro 12,1 and 14,1. This is because + * Linux responds to _OSI("Darwin") while we don't! + * + * ACPI GPE is enabled on FreeBSD by addition of following lines to + * /boot/loader.conf: + * hw.acpi.install_interface="Darwin" + * hw.acpi.remove_interface="Windows 2009, Windows 2012" + */ + +static const char *atopcase_ids[] = { "APP000D", NULL }; + +static device_probe_t atopcase_acpi_probe; +static device_attach_t atopcase_acpi_attach; +static device_detach_t atopcase_acpi_detach; +static device_suspend_t atopcase_acpi_suspend; +static device_resume_t atopcase_acpi_resume; + +static bool +acpi_is_atopcase(ACPI_HANDLE handle) +{ + const char **ids; + UINT32 sta; + + for (ids = atopcase_ids; *ids != NULL; ids++) { + if (acpi_MatchHid(handle, *ids)) + break; + } + if (*ids == NULL) + return (false); + + /* + * If no _STA method or if it failed, then assume that + * the device is present. + */ + if (ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) || + ACPI_DEVICE_PRESENT(sta)) + return (true); + + return (false); +} + +static int +atopcase_acpi_set_comm_enabled(struct atopcase_softc *sc, char *prop, + const bool on) +{ + ACPI_OBJECT argobj; + ACPI_OBJECT_LIST args; + + argobj.Type = ACPI_TYPE_INTEGER; + argobj.Integer.Value = on; + args.Count = 1; + args.Pointer = &argobj; + + if (ACPI_FAILURE( + AcpiEvaluateObject(sc->sc_handle, prop, &args, NULL))) + return (ENXIO); + + DELAY(100); + + return (0); +} + +static int +atopcase_acpi_test_comm_enabled(ACPI_HANDLE handle, char *prop, int *enabled) +{ + if (ACPI_FAILURE(acpi_GetInteger(handle, prop, enabled))) + return (ENXIO); + + return (0); +} + +static void +atopcase_acpi_task(void *ctx, int pending __unused) +{ + struct atopcase_softc *sc = ctx; + int err = EAGAIN; + + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "Timer event\n"); + + sx_xlock(&sc->sc_write_sx); + err = atopcase_receive_packet(sc); + sx_xunlock(&sc->sc_write_sx); + + /* Rearm timer */ + taskqueue_enqueue_timeout(sc->sc_tq, &sc->sc_task, + hz / (err == EAGAIN ? 10 : 120)); +} + +static void +atopcase_acpi_gpe_task(void *ctx) +{ + struct atopcase_softc *sc = ctx; + + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "GPE event\n"); + + sx_xlock(&sc->sc_sx); + (void)atopcase_intr(sc); + sx_xunlock(&sc->sc_sx); + + /* Rearm GPE */ + if (ACPI_FAILURE(AcpiFinishGpe(NULL, sc->sc_gpe_bit))) + device_printf(sc->sc_dev, "GPE rearm failed\n"); +} + +static UINT32 +atopcase_acpi_notify(ACPI_HANDLE h __unused, UINT32 notify __unused, void *ctx) +{ + AcpiOsExecute(OSL_GPE_HANDLER, atopcase_acpi_gpe_task, ctx); + return (0); +} + +static void +atopcase_acpi_intr(void *ctx) +{ + struct atopcase_softc *sc = ctx; + + DPRINTFN(ATOPCASE_LLEVEL_DEBUG, "Interrupt event\n"); + + mtx_lock(&sc->sc_mtx); + sc->sc_intr_cnt++; + (void)atopcase_intr(sc); + mtx_unlock(&sc->sc_mtx); +} + +static int +atopcase_acpi_probe(device_t dev) +{ + ACPI_HANDLE handle; + int usb_enabled; + + if (acpi_disabled("atopcase")) + return (ENXIO); + + handle = acpi_get_handle(dev); + if (handle == NULL) + return (ENXIO); + + if (!acpi_is_atopcase(handle)) + return (ENXIO); + + /* If USB interface exists and is enabled, use USB driver */ + if (atopcase_acpi_test_comm_enabled(handle, "UIST", &usb_enabled) == 0 + && usb_enabled != 0) + return (ENXIO); + + device_set_desc(dev, "Apple MacBook SPI Topcase"); + + return (BUS_PROBE_DEFAULT); +} + +static int +atopcase_acpi_attach(device_t dev) +{ + struct atopcase_softc *sc = device_get_softc(dev); + ACPI_DEVICE_INFO *device_info; + uint32_t cs_delay; + int spi_enabled, err; + + sc->sc_dev = dev; + sc->sc_handle = acpi_get_handle(dev); + + if (atopcase_acpi_test_comm_enabled(sc->sc_handle, "SIST", + &spi_enabled) != 0) { + device_printf(dev, "can't test SPI communication\n"); + return (ENXIO); + } + + /* Turn SPI off if enabled to force "booted" packet to appear */ + if (spi_enabled != 0 && + atopcase_acpi_set_comm_enabled(sc, "SIEN", false) != 0) { + device_printf(dev, "can't disable SPI communication\n"); + return (ENXIO); + } + + if (atopcase_acpi_set_comm_enabled(sc, "SIEN", true) != 0) { + device_printf(dev, "can't enable SPI communication\n"); + return (ENXIO); + } + + /* + * Apple encodes a CS delay in ACPI properties, but + * - they're encoded in a non-standard way that predates _DSD, and + * - they're only exported if you respond to _OSI(Darwin) which we don't + * - because that has more side effects than we're prepared to handle + * - Apple makes a Windows driver and Windows is not Darwin + * - so presumably that one uses hardcoded values too + */ + spibus_get_cs_delay(sc->sc_dev, &cs_delay); + if (cs_delay == 0) + spibus_set_cs_delay(sc->sc_dev, 10); + + /* Retrieve ACPI _HID */ + if (ACPI_FAILURE(AcpiGetObjectInfo(sc->sc_handle, &device_info))) + return (ENXIO); + if (device_info->Valid & ACPI_VALID_HID) + strlcpy(sc->sc_hid, device_info->HardwareId.String, + sizeof(sc->sc_hid)); + AcpiOsFree(device_info); + + sx_init(&sc->sc_write_sx, "atc_wr"); + sx_init(&sc->sc_sx, "atc_sx"); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + err = ENXIO; + sc->sc_irq_res = bus_alloc_resource_any(sc->sc_dev, + SYS_RES_IRQ, &sc->sc_irq_rid, RF_ACTIVE); + if (sc->sc_irq_res != NULL) { + if (bus_setup_intr(dev, sc->sc_irq_res, + INTR_TYPE_MISC | INTR_MPSAFE, NULL, + atopcase_acpi_intr, sc, &sc->sc_irq_ih) != 0) { + device_printf(dev, "can't setup interrupt handler\n"); + goto err; + } + device_printf(dev, "Using interrupts.\n"); + /* + * On some hardware interrupts are not acked by SPI read for + * unknown reasons that leads to interrupt storm due to level + * triggering. GPE does not suffer from this problem. + * + * TODO: Find out what Windows driver does to ack IRQ. + */ + pause("atopcase", hz / 5); + DPRINTF("interrupts asserted: %u\n", sc->sc_intr_cnt); + if (sc->sc_intr_cnt > 2 || sc->sc_intr_cnt == 0) { + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_ih); + sc->sc_irq_ih = NULL; + device_printf(dev, "Interrupt storm detected. " + "Falling back to polling\n"); + sc->sc_tq = taskqueue_create("atc_tq", M_WAITOK|M_ZERO, + taskqueue_thread_enqueue, &sc->sc_tq); + TIMEOUT_TASK_INIT(sc->sc_tq, &sc->sc_task, 0, + atopcase_acpi_task, sc); + taskqueue_start_threads(&sc->sc_tq, 1, PI_TTY, + "%s taskq", device_get_nameunit(dev)); + } + /* + * Interrupts does not work at all. It may happen if kernel + * erroneously detected stray irq at bus_teardown_intr() and + * completelly disabled it after than. + * Fetch "booted" packet manually to pass communication check. + */ + if (sc->sc_intr_cnt == 0) + atopcase_receive_packet(sc); + } else { + if (bootverbose) + device_printf(dev, "can't allocate IRQ resource\n"); + if (ACPI_FAILURE(acpi_GetInteger(sc->sc_handle, "_GPE", + &sc->sc_gpe_bit))) { + device_printf(dev, "can't allocate nor IRQ nor GPE\n"); + goto err; + } + if (ACPI_FAILURE(AcpiInstallGpeHandler(NULL, sc->sc_gpe_bit, + ACPI_GPE_LEVEL_TRIGGERED, atopcase_acpi_notify, sc))) { + device_printf(dev, "can't install ACPI GPE handler\n"); + goto err; + } + if (ACPI_FAILURE(AcpiEnableGpe(NULL, sc->sc_gpe_bit))) { + device_printf(dev, "can't enable ACPI notification\n"); + goto err; + } + device_printf(dev, "Using ACPI GPE.\n"); + if (bootverbose) + device_printf(dev, "GPE int %d\n", sc->sc_gpe_bit); + } + + err = atopcase_init(sc); + +err: + if (err != 0) + atopcase_acpi_detach(dev); + return (err); +} + +static int +atopcase_acpi_detach(device_t dev) +{ + struct atopcase_softc *sc = device_get_softc(dev); + int err; + + err = atopcase_destroy(sc); + if (err != 0) + return (err); + + if (sc->sc_irq_ih) + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_ih); + if (sc->sc_irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, + sc->sc_irq_rid, sc->sc_irq_res); + + if (sc->sc_tq != NULL) { + while (taskqueue_cancel_timeout(sc->sc_tq, &sc->sc_task, NULL)) + taskqueue_drain_timeout(sc->sc_tq, &sc->sc_task); + taskqueue_free(sc->sc_tq); + } + + if (sc->sc_gpe_bit != 0 && ACPI_FAILURE(AcpiRemoveGpeHandler(NULL, + sc->sc_gpe_bit, atopcase_acpi_notify))) + device_printf(dev, "can't remove ACPI GPE handler\n"); + + if (atopcase_acpi_set_comm_enabled(sc, "SIEN", false) != 0) + device_printf(dev, "can't disable SPI communication\n"); + + mtx_destroy(&sc->sc_mtx); + sx_destroy(&sc->sc_sx); + sx_destroy(&sc->sc_write_sx); + + return (0); +} + +static int +atopcase_acpi_suspend(device_t dev) +{ + struct atopcase_softc *sc = device_get_softc(dev); + int err; + + err = bus_generic_suspend(dev); + if (err) + return (err); + + if (sc->sc_gpe_bit != 0) + AcpiDisableGpe(NULL, sc->sc_gpe_bit); + + if (sc->sc_tq != NULL) + while (taskqueue_cancel_timeout(sc->sc_tq, &sc->sc_task, NULL)) + taskqueue_drain_timeout(sc->sc_tq, &sc->sc_task); + + if (atopcase_acpi_set_comm_enabled(sc, "SIEN", false) != 0) + device_printf(dev, "can't disable SPI communication\n"); + + return (0); +} + +static int +atopcase_acpi_resume(device_t dev) +{ + struct atopcase_softc *sc = device_get_softc(dev); + + if (sc->sc_gpe_bit != 0) + AcpiEnableGpe(NULL, sc->sc_gpe_bit); + + if (sc->sc_tq != NULL) + taskqueue_enqueue_timeout(sc->sc_tq, &sc->sc_task, hz / 120); + + if (atopcase_acpi_set_comm_enabled(sc, "SIEN", true) != 0) { + device_printf(dev, "can't enable SPI communication\n"); + return (ENXIO); + } + + return (bus_generic_resume(dev)); +} + +static device_method_t atopcase_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, atopcase_acpi_probe), + DEVMETHOD(device_attach, atopcase_acpi_attach), + DEVMETHOD(device_detach, atopcase_acpi_detach), + DEVMETHOD(device_suspend, atopcase_acpi_suspend), + DEVMETHOD(device_resume, atopcase_acpi_resume), + + /* HID interrupt interface */ + DEVMETHOD(hid_intr_setup, atopcase_intr_setup), + DEVMETHOD(hid_intr_unsetup, atopcase_intr_unsetup), + DEVMETHOD(hid_intr_start, atopcase_intr_start), + DEVMETHOD(hid_intr_stop, atopcase_intr_stop), + DEVMETHOD(hid_intr_poll, atopcase_intr_poll), + + /* HID interface */ + DEVMETHOD(hid_get_rdesc, atopcase_get_rdesc), + DEVMETHOD(hid_set_report, atopcase_set_report), + + /* Backlight interface */ + DEVMETHOD(backlight_update_status, atopcase_backlight_update_status), + DEVMETHOD(backlight_get_status, atopcase_backlight_get_status), + DEVMETHOD(backlight_get_info, atopcase_backlight_get_info), + + DEVMETHOD_END +}; + +static driver_t atopcase_driver = { + "atopcase", + atopcase_methods, + sizeof(struct atopcase_softc), +}; + +DRIVER_MODULE(atopcase, spibus, atopcase_driver, 0, 0); +MODULE_DEPEND(atopcase, acpi, 1, 1, 1); +MODULE_DEPEND(atopcase, backlight, 1, 1, 1); +MODULE_DEPEND(atopcase, hid, 1, 1, 1); +MODULE_DEPEND(atopcase, hidbus, 1, 1, 1); +MODULE_DEPEND(atopcase, spibus, 1, 1, 1); +MODULE_VERSION(atopcase, 1); +SPIBUS_ACPI_PNP_INFO(atopcase_ids); diff --git a/sys/dev/atopcase/atopcase_reg.h b/sys/dev/atopcase/atopcase_reg.h new file mode 100644 index 000000000000..9ea5a9d71299 --- /dev/null +++ b/sys/dev/atopcase/atopcase_reg.h @@ -0,0 +1,110 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2023 Val Packett + * Copyright (c) 2023 Vladimir Kondratyev + * + * 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 _ATOPCASE_REG_H_ +#define _ATOPCASE_REG_H_ + +#include + +#define ATOPCASE_PACKET_SIZE 256 +#define ATOPCASE_DATA_SIZE 246 +#define ATOPCASE_PKT_PER_MSG 2 +#define ATOPCASE_MSG_SIZE (ATOPCASE_DATA_SIZE * ATOPCASE_PKT_PER_MSG) + +/* Read == device-initiated, Write == host-initiated or reply to that */ +#define ATOPCASE_DIR_READ 0x20 +#define ATOPCASE_DIR_WRITE 0x40 +#define ATOPCASE_DIR_NOTHING 0x80 + +#define ATOPCASE_DEV_MGMT 0x00 +#define ATOPCASE_DEV_KBRD 0x01 +#define ATOPCASE_DEV_TPAD 0x02 +#define ATOPCASE_DEV_INFO 0xD0 + +#define ATOPCASE_BKL_REPORT_ID 0xB0 + +#define ATOPCASE_INFO_DEVICE 0x01 +#define ATOPCASE_INFO_IFACE 0x02 +#define ATOPCASE_INFO_DESCRIPTOR 0x10 + +#define ATOPCASE_MSG_TYPE_SET_REPORT(dev,rid) ((rid << 8) | 0x50 | dev) +#define ATOPCASE_MSG_TYPE_REPORT(dev) ((dev << 8) | 0x10) +#define ATOPCASE_MSG_TYPE_INFO(inf) ((inf << 8) | 0x20) + +struct atopcase_bl_payload { + uint8_t report_id; + uint8_t device; + uint16_t level; + uint16_t status; +} __packed; + +struct atopcase_device_info_payload { + uint16_t unknown[2]; + uint16_t num_devs; + uint16_t vid; + uint16_t pid; + uint16_t ver; + uint16_t vendor_off; + uint16_t vendor_len; + uint16_t product_off; + uint16_t product_len; + uint16_t serial_off; + uint16_t serial_len; +} __packed; + +struct atopcase_iface_info_payload { + uint8_t unknown0; + uint8_t iface_num; + uint8_t unknown1[3]; + uint8_t country_code; + uint16_t max_input_report_len; + uint16_t max_output_report_len; + uint16_t max_control_report_len; + uint16_t name_off; + uint16_t name_len; +} __packed; + +struct atopcase_header { + uint16_t type; + uint8_t type_arg; /* means "device" for ATOPCASE_MSG_TYPE_DESCRIPTOR */ + uint8_t seq_no; + uint16_t resp_len; + uint16_t len; +} __packed; + +struct atopcase_packet { + uint8_t direction; + uint8_t device; + uint16_t offset; + uint16_t remaining; + uint16_t length; + uint8_t data[ATOPCASE_DATA_SIZE]; + uint16_t checksum; +} __packed; + +#endif /* _ATOPCASE_REG_H_ */ diff --git a/sys/dev/atopcase/atopcase_var.h b/sys/dev/atopcase/atopcase_var.h new file mode 100644 index 000000000000..223ec4a1f450 --- /dev/null +++ b/sys/dev/atopcase/atopcase_var.h @@ -0,0 +1,142 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2023 Val Packett + * Copyright (c) 2023 Vladimir Kondratyev + * + * 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 _ATOPCASE_VAR_H_ +#define _ATOPCASE_VAR_H_ + +#include "opt_hid.h" + +#include +#include +#include + +#include +#include + +#include + +#include + +struct atopcase_child { + device_t hidbus; + + struct hid_device_info hw; + + uint8_t device; + uint8_t name[80]; + + uint8_t rdesc[ATOPCASE_MSG_SIZE]; + size_t rdesc_len; + + hid_intr_t *intr_handler; + void *intr_ctx; + + bool open; +}; + +struct atopcase_softc { + device_t sc_dev; + + ACPI_HANDLE sc_handle; + int sc_gpe_bit; + + int sc_irq_rid; + struct resource *sc_irq_res; + void *sc_irq_ih; + volatile unsigned int sc_intr_cnt; + + struct timeout_task sc_task; + struct taskqueue *sc_tq; + + bool sc_booted; + bool sc_wait_for_status; + + uint8_t sc_hid[HID_PNP_ID_SIZE]; + uint8_t sc_vendor[80]; + uint8_t sc_product[80]; + uint8_t sc_serial[80]; + uint16_t sc_vid; + uint16_t sc_pid; + uint16_t sc_ver; + + /* + * Writes are complex and async (i.e. 2 responses arrive via interrupt) + * and cannot be interleaved (no new writes until responses arrive). + * they are serialized with sc_write_sx lock. + */ + struct sx sc_write_sx; + /* + * SPI transfers must be separated by a small pause. As they can be + * initiated by both interrupts and users, do ATOPCASE_SPI_PAUSE() + * after each transfer and serialize them with sc_sx or sc_mtx locks + * depending on interupt source (GPE or PIC). Still use sc_write_sx + * lock while polling. + */ + struct sx sc_sx; + struct mtx sc_mtx; + + struct atopcase_child sc_kb; + struct atopcase_child sc_tp; + + struct cdev *sc_backlight; + uint32_t sc_backlight_level; + + uint16_t sc_msg_len; + uint8_t sc_msg[ATOPCASE_MSG_SIZE]; + struct atopcase_packet sc_buf; + struct atopcase_packet sc_junk; +}; + +#ifdef HID_DEBUG +enum atopcase_log_level { + ATOPCASE_LLEVEL_DISABLED = 0, + ATOPCASE_LLEVEL_INFO, + ATOPCASE_LLEVEL_DEBUG, /* for troubleshooting */ + ATOPCASE_LLEVEL_TRACE, /* log every packet */ +}; +extern enum atopcase_log_level atopcase_debug; +#endif + +int atopcase_receive_packet(struct atopcase_softc *); +int atopcase_init(struct atopcase_softc *); +int atopcase_destroy(struct atopcase_softc *sc); +int atopcase_intr(struct atopcase_softc *); +void atopcase_intr_setup(device_t, device_t, hid_intr_t, void *, + struct hid_rdesc_info *); +void atopcase_intr_unsetup(device_t, device_t); +int atopcase_intr_start(device_t, device_t); +int atopcase_intr_stop(device_t, device_t); +void atopcase_intr_poll(device_t, device_t); +int atopcase_get_rdesc(device_t, device_t, void *, hid_size_t); +int atopcase_set_report(device_t, device_t, const void *, hid_size_t, uint8_t, + uint8_t); +int atopcase_backlight_update_status(device_t, struct backlight_props *); +int atopcase_backlight_get_status(device_t, struct backlight_props *); +int atopcase_backlight_get_info(device_t, struct backlight_info *); + +#endif /* _ATOPCASE_VAR_H_ */ diff --git a/sys/dev/hid/hidbus.c b/sys/dev/hid/hidbus.c index 1f7cb615c70e..cddc6c961195 100644 --- a/sys/dev/hid/hidbus.c +++ b/sys/dev/hid/hidbus.c @@ -1,978 +1,979 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019-2020 Vladimir Kondratyev * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HID_DEBUG_VAR hid_debug #include #include #include #include "hid_if.h" #define INPUT_EPOCH global_epoch_preempt #define HID_RSIZE_MAX 1024 static hid_intr_t hidbus_intr; static device_probe_t hidbus_probe; static device_attach_t hidbus_attach; static device_detach_t hidbus_detach; struct hidbus_ivars { int32_t usage; uint8_t index; uint32_t flags; uintptr_t driver_info; /* for internal use */ struct mtx *mtx; /* child intr mtx */ hid_intr_t *intr_handler; /* executed under mtx*/ void *intr_ctx; unsigned int refcnt; /* protected by mtx */ struct epoch_context epoch_ctx; CK_STAILQ_ENTRY(hidbus_ivars) link; }; struct hidbus_softc { device_t dev; struct sx sx; struct mtx mtx; bool nowrite; struct hid_rdesc_info rdesc; bool overloaded; int nest; /* Child attach nesting lvl */ int nauto; /* Number of autochildren */ CK_STAILQ_HEAD(, hidbus_ivars) tlcs; }; static int hidbus_fill_rdesc_info(struct hid_rdesc_info *hri, const void *data, hid_size_t len) { int error = 0; hri->data = __DECONST(void *, data); hri->len = len; /* * If report descriptor is not available yet, set maximal * report sizes high enough to allow hidraw to work. */ hri->isize = len == 0 ? HID_RSIZE_MAX : hid_report_size_max(data, len, hid_input, &hri->iid); hri->osize = len == 0 ? HID_RSIZE_MAX : hid_report_size_max(data, len, hid_output, &hri->oid); hri->fsize = len == 0 ? HID_RSIZE_MAX : hid_report_size_max(data, len, hid_feature, &hri->fid); if (hri->isize > HID_RSIZE_MAX) { DPRINTF("input size is too large, %u bytes (truncating)\n", hri->isize); hri->isize = HID_RSIZE_MAX; error = EOVERFLOW; } if (hri->osize > HID_RSIZE_MAX) { DPRINTF("output size is too large, %u bytes (truncating)\n", hri->osize); hri->osize = HID_RSIZE_MAX; error = EOVERFLOW; } if (hri->fsize > HID_RSIZE_MAX) { DPRINTF("feature size is too large, %u bytes (truncating)\n", hri->fsize); hri->fsize = HID_RSIZE_MAX; error = EOVERFLOW; } return (error); } int hidbus_locate(const void *desc, hid_size_t size, int32_t u, enum hid_kind k, uint8_t tlc_index, uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id, struct hid_absinfo *ai) { struct hid_data *d; struct hid_item h; int i; d = hid_start_parse(desc, size, 1 << k); HIDBUS_FOREACH_ITEM(d, &h, tlc_index) { 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; if (ai != NULL && (h.flags&HIO_RELATIVE) == 0) *ai = (struct hid_absinfo) { .max = h.logical_maximum, .min = h.logical_minimum, .res = hid_item_resolution(&h), }; 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); } bool hidbus_is_collection(const void *desc, hid_size_t size, int32_t usage, uint8_t tlc_index) { struct hid_data *d; struct hid_item h; bool ret = false; d = hid_start_parse(desc, size, 0); HIDBUS_FOREACH_ITEM(d, &h, tlc_index) { if (h.kind == hid_collection && h.usage == usage) { ret = true; break; } } hid_end_parse(d); return (ret); } static device_t hidbus_add_child(device_t dev, u_int order, const char *name, int unit) { struct hidbus_softc *sc = device_get_softc(dev); struct hidbus_ivars *tlc; device_t child; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (child); tlc = malloc(sizeof(struct hidbus_ivars), M_DEVBUF, M_WAITOK | M_ZERO); tlc->mtx = &sc->mtx; device_set_ivars(child, tlc); sx_xlock(&sc->sx); CK_STAILQ_INSERT_TAIL(&sc->tlcs, tlc, link); sx_unlock(&sc->sx); return (child); } static int hidbus_enumerate_children(device_t dev, const void* data, hid_size_t len) { struct hidbus_softc *sc = device_get_softc(dev); struct hid_data *hd; struct hid_item hi; device_t child; uint8_t index = 0; if (data == NULL || len == 0) return (ENXIO); /* Add a child for each top level collection */ hd = hid_start_parse(data, len, 1 << hid_input); while (hid_get_item(hd, &hi)) { if (hi.kind != hid_collection || hi.collevel != 1) continue; child = BUS_ADD_CHILD(dev, 0, NULL, -1); if (child == NULL) { device_printf(dev, "Could not add HID device\n"); continue; } hidbus_set_index(child, index); hidbus_set_usage(child, hi.usage); hidbus_set_flags(child, HIDBUS_FLAG_AUTOCHILD); index++; DPRINTF("Add child TLC: 0x%04x:0x%04x\n", HID_GET_USAGE_PAGE(hi.usage), HID_GET_USAGE(hi.usage)); } hid_end_parse(hd); if (index == 0) return (ENXIO); sc->nauto = index; return (0); } static int hidbus_attach_children(device_t dev) { struct hidbus_softc *sc = device_get_softc(dev); int error; HID_INTR_SETUP(device_get_parent(dev), dev, hidbus_intr, sc, &sc->rdesc); error = hidbus_enumerate_children(dev, sc->rdesc.data, sc->rdesc.len); if (error != 0) DPRINTF("failed to enumerate children: error %d\n", error); /* * hidbus_attach_children() can recurse through device_identify-> * hid_set_report_descr() call sequence. Do not perform children * attach twice in that case. */ sc->nest++; bus_generic_probe(dev); sc->nest--; if (sc->nest != 0) return (0); if (hid_is_keyboard(sc->rdesc.data, sc->rdesc.len) != 0) error = bus_generic_attach(dev); else error = bus_delayed_attach_children(dev); if (error != 0) device_printf(dev, "failed to attach child: error %d\n", error); return (error); } static int hidbus_detach_children(device_t dev) { device_t *children, bus; bool is_bus; int i, error; error = 0; is_bus = device_get_devclass(dev) == devclass_find("hidbus"); bus = is_bus ? dev : device_get_parent(dev); KASSERT(device_get_devclass(bus) == devclass_find("hidbus"), ("Device is not hidbus or it's child")); if (is_bus) { /* If hidbus is passed, delete all children. */ bus_generic_detach(bus); device_delete_children(bus); } else { /* * If hidbus child is passed, delete all hidbus children * except caller. Deleting the caller may result in deadlock. */ error = device_get_children(bus, &children, &i); if (error != 0) return (error); while (i-- > 0) { if (children[i] == dev) continue; DPRINTF("Delete child. index=%d (%s)\n", hidbus_get_index(children[i]), device_get_nameunit(children[i])); error = device_delete_child(bus, children[i]); if (error) { DPRINTF("Failed deleting %s\n", device_get_nameunit(children[i])); break; } } free(children, M_TEMP); } HID_INTR_UNSETUP(device_get_parent(bus), bus); return (error); } static int hidbus_probe(device_t dev) { device_set_desc(dev, "HID bus"); /* Allow other subclasses to override this driver. */ return (BUS_PROBE_GENERIC); } static int hidbus_attach(device_t dev) { struct hidbus_softc *sc = device_get_softc(dev); struct hid_device_info *devinfo = device_get_ivars(dev); void *d_ptr = NULL; hid_size_t d_len; int error; sc->dev = dev; CK_STAILQ_INIT(&sc->tlcs); mtx_init(&sc->mtx, "hidbus ivar lock", NULL, MTX_DEF); sx_init(&sc->sx, "hidbus ivar list lock"); /* * Ignore error. It is possible for non-HID device e.g. XBox360 gamepad * to emulate HID through overloading of report descriptor. */ d_len = devinfo->rdescsize; if (d_len != 0) { d_ptr = malloc(d_len, M_DEVBUF, M_ZERO | M_WAITOK); error = hid_get_rdesc(dev, d_ptr, d_len); if (error != 0) { free(d_ptr, M_DEVBUF); d_len = 0; d_ptr = NULL; } } hidbus_fill_rdesc_info(&sc->rdesc, d_ptr, d_len); sc->nowrite = hid_test_quirk(devinfo, HQ_NOWRITE); error = hidbus_attach_children(dev); if (error != 0) { hidbus_detach(dev); return (ENXIO); } return (0); } static int hidbus_detach(device_t dev) { struct hidbus_softc *sc = device_get_softc(dev); hidbus_detach_children(dev); sx_destroy(&sc->sx); mtx_destroy(&sc->mtx); free(sc->rdesc.data, M_DEVBUF); return (0); } static void hidbus_child_detached(device_t bus, device_t child) { struct hidbus_softc *sc = device_get_softc(bus); struct hidbus_ivars *tlc = device_get_ivars(child); KASSERT(tlc->refcnt == 0, ("Child device is running")); tlc->mtx = &sc->mtx; tlc->intr_handler = NULL; tlc->flags &= ~HIDBUS_FLAG_CAN_POLL; } /* * Epoch callback indicating tlc is safe to destroy */ static void hidbus_ivar_dtor(epoch_context_t ctx) { struct hidbus_ivars *tlc; tlc = __containerof(ctx, struct hidbus_ivars, epoch_ctx); free(tlc, M_DEVBUF); } static void hidbus_child_deleted(device_t bus, device_t child) { struct hidbus_softc *sc = device_get_softc(bus); struct hidbus_ivars *tlc = device_get_ivars(child); sx_xlock(&sc->sx); KASSERT(tlc->refcnt == 0, ("Child device is running")); CK_STAILQ_REMOVE(&sc->tlcs, tlc, hidbus_ivars, link); sx_unlock(&sc->sx); epoch_call(INPUT_EPOCH, hidbus_ivar_dtor, &tlc->epoch_ctx); } static int hidbus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct hidbus_softc *sc = device_get_softc(bus); struct hidbus_ivars *tlc = device_get_ivars(child); switch (which) { case HIDBUS_IVAR_INDEX: *result = tlc->index; break; case HIDBUS_IVAR_USAGE: *result = tlc->usage; break; case HIDBUS_IVAR_FLAGS: *result = tlc->flags; break; case HIDBUS_IVAR_DRIVER_INFO: *result = tlc->driver_info; break; case HIDBUS_IVAR_LOCK: *result = (uintptr_t)(tlc->mtx == &sc->mtx ? NULL : tlc->mtx); break; default: return (EINVAL); } return (0); } static int hidbus_write_ivar(device_t bus, device_t child, int which, uintptr_t value) { struct hidbus_softc *sc = device_get_softc(bus); struct hidbus_ivars *tlc = device_get_ivars(child); switch (which) { case HIDBUS_IVAR_INDEX: tlc->index = value; break; case HIDBUS_IVAR_USAGE: tlc->usage = value; break; case HIDBUS_IVAR_FLAGS: tlc->flags = value; if ((value & HIDBUS_FLAG_CAN_POLL) != 0) HID_INTR_SETUP( device_get_parent(bus), bus, NULL, NULL, NULL); break; case HIDBUS_IVAR_DRIVER_INFO: tlc->driver_info = value; break; case HIDBUS_IVAR_LOCK: tlc->mtx = (struct mtx *)value == NULL ? &sc->mtx : (struct mtx *)value; break; default: return (EINVAL); } return (0); } /* Location hint for devctl(8) */ static int hidbus_child_location(device_t bus, device_t child, struct sbuf *sb) { struct hidbus_ivars *tlc = device_get_ivars(child); sbuf_printf(sb, "index=%hhu", tlc->index); return (0); } /* PnP information for devctl(8) */ static int hidbus_child_pnpinfo(device_t bus, device_t child, struct sbuf *sb) { struct hidbus_ivars *tlc = device_get_ivars(child); struct hid_device_info *devinfo = device_get_ivars(bus); sbuf_printf(sb, "page=0x%04x usage=0x%04x bus=0x%02hx " "vendor=0x%04hx product=0x%04hx version=0x%04hx%s%s", HID_GET_USAGE_PAGE(tlc->usage), HID_GET_USAGE(tlc->usage), devinfo->idBus, devinfo->idVendor, devinfo->idProduct, devinfo->idVersion, devinfo->idPnP[0] == '\0' ? "" : " _HID=", devinfo->idPnP[0] == '\0' ? "" : devinfo->idPnP); return (0); } void hidbus_set_desc(device_t child, const char *suffix) { device_t bus = device_get_parent(child); struct hidbus_softc *sc = device_get_softc(bus); struct hid_device_info *devinfo = device_get_ivars(bus); struct hidbus_ivars *tlc = device_get_ivars(child); char buf[80]; /* Do not add NULL suffix or if device name already contains it. */ if (suffix != NULL && strcasestr(devinfo->name, suffix) == NULL && (sc->nauto > 1 || (tlc->flags & HIDBUS_FLAG_AUTOCHILD) == 0)) { snprintf(buf, sizeof(buf), "%s %s", devinfo->name, suffix); device_set_desc_copy(child, buf); } else device_set_desc(child, devinfo->name); } device_t hidbus_find_child(device_t bus, int32_t usage) { device_t *children, child; int ccount, i; bus_topo_assert(); /* Get a list of all hidbus children */ if (device_get_children(bus, &children, &ccount) != 0) return (NULL); /* Scan through to find required TLC */ for (i = 0, child = NULL; i < ccount; i++) { if (hidbus_get_usage(children[i]) == usage) { child = children[i]; break; } } free(children, M_TEMP); return (child); } void hidbus_intr(void *context, void *buf, hid_size_t len) { struct hidbus_softc *sc = context; struct hidbus_ivars *tlc; struct epoch_tracker et; /* * Broadcast input report to all subscribers. * TODO: Add check for input report ID. * * Relock mutex on every TLC item as we can't hold any locks over whole * TLC list here due to LOR with open()/close() handlers. */ if (!HID_IN_POLLING_MODE()) epoch_enter_preempt(INPUT_EPOCH, &et); CK_STAILQ_FOREACH(tlc, &sc->tlcs, link) { if (tlc->refcnt == 0 || tlc->intr_handler == NULL) continue; if (HID_IN_POLLING_MODE()) { if ((tlc->flags & HIDBUS_FLAG_CAN_POLL) != 0) tlc->intr_handler(tlc->intr_ctx, buf, len); } else { mtx_lock(tlc->mtx); tlc->intr_handler(tlc->intr_ctx, buf, len); mtx_unlock(tlc->mtx); } } if (!HID_IN_POLLING_MODE()) epoch_exit_preempt(INPUT_EPOCH, &et); } void hidbus_set_intr(device_t child, hid_intr_t *handler, void *context) { struct hidbus_ivars *tlc = device_get_ivars(child); tlc->intr_handler = handler; tlc->intr_ctx = context; } static int hidbus_intr_start(device_t bus, device_t child) { MPASS(bus = device_get_parent(child)); struct hidbus_softc *sc = device_get_softc(bus); struct hidbus_ivars *ivar = device_get_ivars(child); struct hidbus_ivars *tlc; bool refcnted = false; int error; if (sx_xlock_sig(&sc->sx) != 0) return (EINTR); CK_STAILQ_FOREACH(tlc, &sc->tlcs, link) { refcnted |= (tlc->refcnt != 0); if (tlc == ivar) { mtx_lock(tlc->mtx); ++tlc->refcnt; mtx_unlock(tlc->mtx); } } error = refcnted ? 0 : hid_intr_start(bus); sx_unlock(&sc->sx); return (error); } static int hidbus_intr_stop(device_t bus, device_t child) { MPASS(bus = device_get_parent(child)); struct hidbus_softc *sc = device_get_softc(bus); struct hidbus_ivars *ivar = device_get_ivars(child); struct hidbus_ivars *tlc; bool refcnted = false; int error; if (sx_xlock_sig(&sc->sx) != 0) return (EINTR); CK_STAILQ_FOREACH(tlc, &sc->tlcs, link) { if (tlc == ivar) { mtx_lock(tlc->mtx); MPASS(tlc->refcnt != 0); --tlc->refcnt; mtx_unlock(tlc->mtx); } refcnted |= (tlc->refcnt != 0); } error = refcnted ? 0 : hid_intr_stop(bus); sx_unlock(&sc->sx); return (error); } static void hidbus_intr_poll(device_t bus, device_t child __unused) { hid_intr_poll(bus); } struct hid_rdesc_info * hidbus_get_rdesc_info(device_t child) { device_t bus = device_get_parent(child); struct hidbus_softc *sc = device_get_softc(bus); return (&sc->rdesc); } /* * HID interface. * * Hidbus as well as any hidbus child can be passed as first arg. */ /* Read cached report descriptor */ int hid_get_report_descr(device_t dev, void **data, hid_size_t *len) { device_t bus; struct hidbus_softc *sc; bus = device_get_devclass(dev) == devclass_find("hidbus") ? dev : device_get_parent(dev); sc = device_get_softc(bus); /* * Do not send request to a transport backend. * Use cached report descriptor instead of it. */ if (sc->rdesc.data == NULL || sc->rdesc.len == 0) return (ENXIO); if (data != NULL) *data = sc->rdesc.data; if (len != NULL) *len = sc->rdesc.len; return (0); } /* * Replace cached report descriptor with top level driver provided one. * * It deletes all hidbus children except caller and enumerates them again after * new descriptor has been registered. Currently it can not be called from * autoenumerated (by report's TLC) child device context as it results in child * duplication. To overcome this limitation hid_set_report_descr() should be * called from device_identify driver's handler with hidbus itself passed as * 'device_t dev' parameter. */ int hid_set_report_descr(device_t dev, const void *data, hid_size_t len) { struct hid_rdesc_info rdesc; device_t bus; struct hidbus_softc *sc; bool is_bus; int error; bus_topo_assert(); is_bus = device_get_devclass(dev) == devclass_find("hidbus"); bus = is_bus ? dev : device_get_parent(dev); sc = device_get_softc(bus); /* * Do not overload already overloaded report descriptor in * device_identify handler. It causes infinite recursion loop. */ if (is_bus && sc->overloaded) return(0); DPRINTFN(5, "len=%d\n", len); DPRINTFN(5, "data = %*D\n", len, data, " "); error = hidbus_fill_rdesc_info(&rdesc, data, len); if (error != 0) return (error); error = hidbus_detach_children(dev); if (error != 0) return(error); /* Make private copy to handle a case of dynamicaly allocated data. */ rdesc.data = malloc(len, M_DEVBUF, M_ZERO | M_WAITOK); bcopy(data, rdesc.data, len); sc->overloaded = true; free(sc->rdesc.data, M_DEVBUF); bcopy(&rdesc, &sc->rdesc, sizeof(struct hid_rdesc_info)); error = hidbus_attach_children(bus); return (error); } static int hidbus_get_rdesc(device_t dev, device_t child __unused, void *data, hid_size_t len) { return (hid_get_rdesc(dev, data, len)); } static int hidbus_read(device_t dev, device_t child __unused, void *data, hid_size_t maxlen, hid_size_t *actlen) { return (hid_read(dev, data, maxlen, actlen)); } static int hidbus_write(device_t dev, device_t child __unused, const void *data, hid_size_t len) { struct hidbus_softc *sc; uint8_t id; sc = device_get_softc(dev); /* * Output interrupt endpoint is often optional. If HID device * does not provide it, send reports via control pipe. */ if (sc->nowrite) { /* try to extract the ID byte */ id = (sc->rdesc.oid & (len > 0)) ? *(const uint8_t*)data : 0; return (hid_set_report(dev, data, len, HID_OUTPUT_REPORT, id)); } return (hid_write(dev, data, len)); } static int hidbus_get_report(device_t dev, device_t child __unused, void *data, hid_size_t maxlen, hid_size_t *actlen, uint8_t type, uint8_t id) { return (hid_get_report(dev, data, maxlen, actlen, type, id)); } static int hidbus_set_report(device_t dev, device_t child __unused, const void *data, hid_size_t len, uint8_t type, uint8_t id) { return (hid_set_report(dev, data, len, type, id)); } static int hidbus_set_idle(device_t dev, device_t child __unused, uint16_t duration, uint8_t id) { return (hid_set_idle(dev, duration, id)); } static int hidbus_set_protocol(device_t dev, device_t child __unused, uint16_t protocol) { return (hid_set_protocol(dev, protocol)); } static int hidbus_ioctl(device_t dev, device_t child __unused, unsigned long cmd, uintptr_t data) { return (hid_ioctl(dev, cmd, data)); } /*------------------------------------------------------------------------* * hidbus_lookup_id * * This functions takes an array of "struct hid_device_id" and tries * to match the entries with the information in "struct hid_device_info". * * Return values: * NULL: No match found. * Else: Pointer to matching entry. *------------------------------------------------------------------------*/ const struct hid_device_id * hidbus_lookup_id(device_t dev, const struct hid_device_id *id, int nitems_id) { const struct hid_device_id *id_end; const struct hid_device_info *info; int32_t usage; bool is_child; if (id == NULL) { goto done; } id_end = id + nitems_id; info = hid_get_device_info(dev); is_child = device_get_devclass(dev) != devclass_find("hidbus"); if (is_child) usage = hidbus_get_usage(dev); /* * Keep on matching array entries until we find a match or * until we reach the end of the matching array: */ for (; id != id_end; id++) { if (is_child && (id->match_flag_page) && (id->page != HID_GET_USAGE_PAGE(usage))) { continue; } if (is_child && (id->match_flag_usage) && (id->usage != HID_GET_USAGE(usage))) { continue; } if ((id->match_flag_bus) && (id->idBus != info->idBus)) { continue; } if ((id->match_flag_vendor) && (id->idVendor != info->idVendor)) { continue; } if ((id->match_flag_product) && (id->idProduct != info->idProduct)) { continue; } if ((id->match_flag_ver_lo) && (id->idVersion_lo > info->idVersion)) { continue; } if ((id->match_flag_ver_hi) && (id->idVersion_hi < info->idVersion)) { continue; } if (id->match_flag_pnp && strncmp(id->idPnP, info->idPnP, HID_PNP_ID_SIZE) != 0) { continue; } /* We found a match! */ return (id); } done: return (NULL); } /*------------------------------------------------------------------------* * hidbus_lookup_driver_info - factored out code * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ int hidbus_lookup_driver_info(device_t child, const struct hid_device_id *id, int nitems_id) { id = hidbus_lookup_id(child, id, nitems_id); if (id) { /* copy driver info */ hidbus_set_driver_info(child, id->driver_info); return (0); } return (ENXIO); } const struct hid_device_info * hid_get_device_info(device_t dev) { device_t bus; bus = device_get_devclass(dev) == devclass_find("hidbus") ? dev : device_get_parent(dev); return (device_get_ivars(bus)); } static device_method_t hidbus_methods[] = { /* device interface */ DEVMETHOD(device_probe, hidbus_probe), DEVMETHOD(device_attach, hidbus_attach), DEVMETHOD(device_detach, hidbus_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* bus interface */ DEVMETHOD(bus_add_child, hidbus_add_child), DEVMETHOD(bus_child_detached, hidbus_child_detached), DEVMETHOD(bus_child_deleted, hidbus_child_deleted), DEVMETHOD(bus_read_ivar, hidbus_read_ivar), DEVMETHOD(bus_write_ivar, hidbus_write_ivar), DEVMETHOD(bus_child_pnpinfo, hidbus_child_pnpinfo), DEVMETHOD(bus_child_location, hidbus_child_location), /* hid interface */ DEVMETHOD(hid_intr_start, hidbus_intr_start), DEVMETHOD(hid_intr_stop, hidbus_intr_stop), DEVMETHOD(hid_intr_poll, hidbus_intr_poll), DEVMETHOD(hid_get_rdesc, hidbus_get_rdesc), DEVMETHOD(hid_read, hidbus_read), DEVMETHOD(hid_write, hidbus_write), DEVMETHOD(hid_get_report, hidbus_get_report), DEVMETHOD(hid_set_report, hidbus_set_report), DEVMETHOD(hid_set_idle, hidbus_set_idle), DEVMETHOD(hid_set_protocol, hidbus_set_protocol), DEVMETHOD(hid_ioctl, hidbus_ioctl), DEVMETHOD_END }; driver_t hidbus_driver = { "hidbus", hidbus_methods, sizeof(struct hidbus_softc), }; MODULE_DEPEND(hidbus, hid, 1, 1, 1); MODULE_VERSION(hidbus, 1); +DRIVER_MODULE(hidbus, atopcase, hidbus_driver, 0, 0); DRIVER_MODULE(hidbus, hvhid, hidbus_driver, 0, 0); DRIVER_MODULE(hidbus, iichid, hidbus_driver, 0, 0); DRIVER_MODULE(hidbus, usbhid, hidbus_driver, 0, 0); diff --git a/sys/modules/spi/Makefile b/sys/modules/spi/Makefile index 9872a3a8899c..494ff6dc2dcf 100644 --- a/sys/modules/spi/Makefile +++ b/sys/modules/spi/Makefile @@ -1,8 +1,13 @@ SUBDIR = \ ../spigen \ at45d \ + ${_atopcase} \ mx25l \ spibus \ +.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" +_atopcase=atopcase +.endif + .include diff --git a/sys/modules/spi/atopcase/Makefile b/sys/modules/spi/atopcase/Makefile new file mode 100644 index 000000000000..a6acb14ce87a --- /dev/null +++ b/sys/modules/spi/atopcase/Makefile @@ -0,0 +1,7 @@ +.PATH: ${SRCTOP}/sys/dev/atopcase +KMOD= atopcase +SRCS= atopcase.c atopcase_acpi.c +SRCS+= acpi_if.h backlight_if.h bus_if.h device_if.h hid_if.h opt_acpi.h \ + opt_hid.h opt_spi.h spibus_if.h usbdevs.h + +.include