Index: head/sys/conf/files =================================================================== --- head/sys/conf/files (revision 170160) +++ head/sys/conf/files (revision 170161) @@ -1,2204 +1,2206 @@ # $FreeBSD$ # # 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. # acpi_quirks.h optional acpi \ dependency "$S/tools/acpi_quirks2h.awk $S/dev/acpica/acpi_quirks" \ compile-with "${AWK} -f $S/tools/acpi_quirks2h.awk $S/dev/acpica/acpi_quirks" \ no-obj no-implicit-rule before-depend \ clean "acpi_quirks.h" aicasm optional ahc | ahd \ dependency "$S/dev/aic7xxx/aicasm/*.[chyl]" \ compile-with "CC='${CC}' ${MAKE} -f $S/dev/aic7xxx/aicasm/Makefile MAKESRCPATH=$S/dev/aic7xxx/aicasm" \ no-obj no-implicit-rule \ clean "aicasm* y.tab.h" aic7xxx_seq.h optional ahc \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic7xxx_seq.h -r aic7xxx_reg.h -p aic7xxx_reg_print.c -i $S/dev/aic7xxx/aic7xxx_osm.h $S/dev/aic7xxx/aic7xxx.seq" \ no-obj no-implicit-rule before-depend local \ clean "aic7xxx_seq.h" \ dependency "$S/dev/aic7xxx/aic7xxx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic7xxx_reg.h optional ahc \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic7xxx_seq.h -r aic7xxx_reg.h -p aic7xxx_reg_print.c -i $S/dev/aic7xxx/aic7xxx_osm.h $S/dev/aic7xxx/aic7xxx.seq" \ no-obj no-implicit-rule before-depend local \ clean "aic7xxx_reg.h" \ dependency "$S/dev/aic7xxx/aic7xxx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic7xxx_reg_print.c optional ahc \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic7xxx_seq.h -r aic7xxx_reg.h -p aic7xxx_reg_print.c -i $S/dev/aic7xxx/aic7xxx_osm.h $S/dev/aic7xxx/aic7xxx.seq" \ no-obj no-implicit-rule local \ clean "aic7xxx_reg_print.c" \ dependency "$S/dev/aic7xxx/aic7xxx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic7xxx_reg_print.o optional ahc ahc_reg_pretty_print \ compile-with "${NORMAL_C}" \ no-implicit-rule local aic79xx_seq.h optional ahd pci \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic79xx_seq.h -r aic79xx_reg.h -p aic79xx_reg_print.c -i $S/dev/aic7xxx/aic79xx_osm.h $S/dev/aic7xxx/aic79xx.seq" \ no-obj no-implicit-rule before-depend local \ clean "aic79xx_seq.h" \ dependency "$S/dev/aic7xxx/aic79xx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic79xx_reg.h optional ahd pci \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic79xx_seq.h -r aic79xx_reg.h -p aic79xx_reg_print.c -i $S/dev/aic7xxx/aic79xx_osm.h $S/dev/aic7xxx/aic79xx.seq" \ no-obj no-implicit-rule before-depend local \ clean "aic79xx_reg.h" \ dependency "$S/dev/aic7xxx/aic79xx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic79xx_reg_print.c optional ahd pci \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic79xx_seq.h -r aic79xx_reg.h -p aic79xx_reg_print.c -i $S/dev/aic7xxx/aic79xx_osm.h $S/dev/aic7xxx/aic79xx.seq" \ no-obj no-implicit-rule local \ clean "aic79xx_reg_print.c" \ dependency "$S/dev/aic7xxx/aic79xx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic79xx_reg_print.o optional ahd pci ahd_reg_pretty_print \ compile-with "${NORMAL_C}" \ no-implicit-rule local emu10k1-alsa%diked.h optional snd_emu10k1 | snd_emu10kx \ dependency "$S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/emu10k1-alsa.h" \ compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/emu10k1-alsa.h emu10k1-alsa%diked.h" \ no-obj no-implicit-rule before-depend \ clean "emu10k1-alsa%diked.h" p16v-alsa%diked.h optional snd_emu10kx pci \ dependency "$S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p16v-alsa.h" \ compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p16v-alsa.h p16v-alsa%diked.h" \ no-obj no-implicit-rule before-depend \ clean "p16v-alsa%diked.h" p17v-alsa%diked.h optional snd_emu10kx pci \ dependency "$S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p17v-alsa.h" \ compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p17v-alsa.h p17v-alsa%diked.h" \ no-obj no-implicit-rule before-depend \ clean "p17v-alsa%diked.h" miidevs.h optional miibus | mii \ dependency "$S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ compile-with "${AWK} -f $S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ no-obj no-implicit-rule before-depend \ clean "miidevs.h" pccarddevs.h standard \ dependency "$S/tools/pccarddevs2h.awk $S/dev/pccard/pccarddevs" \ compile-with "${AWK} -f $S/tools/pccarddevs2h.awk $S/dev/pccard/pccarddevs" \ no-obj no-implicit-rule before-depend \ clean "pccarddevs.h" usbdevs.h optional usb \ dependency "$S/tools/usbdevs2h.awk $S/dev/usb/usbdevs" \ compile-with "${AWK} -f $S/tools/usbdevs2h.awk $S/dev/usb/usbdevs -h" \ no-obj no-implicit-rule before-depend \ clean "usbdevs.h" usbdevs_data.h optional usb \ dependency "$S/tools/usbdevs2h.awk $S/dev/usb/usbdevs" \ compile-with "${AWK} -f $S/tools/usbdevs2h.awk $S/dev/usb/usbdevs -d" \ no-obj no-implicit-rule before-depend \ clean "usbdevs_data.h" cam/cam.c optional scbus cam/cam_periph.c optional scbus cam/cam_queue.c optional scbus cam/cam_sim.c optional scbus cam/cam_xpt.c optional scbus cam/scsi/scsi_all.c optional scbus cam/scsi/scsi_cd.c optional cd cam/scsi/scsi_ch.c optional ch cam/scsi/scsi_da.c optional da cam/scsi/scsi_low.c optional ct | ncv | nsp | stg cam/scsi/scsi_low_pisa.c optional ct | ncv | nsp | stg cam/scsi/scsi_pass.c optional pass cam/scsi/scsi_pt.c optional pt cam/scsi/scsi_sa.c optional sa cam/scsi/scsi_ses.c optional ses cam/scsi/scsi_sg.c optional sg cam/scsi/scsi_targ_bh.c optional targbh cam/scsi/scsi_target.c optional targ coda/coda_fbsd.c optional vcoda coda/coda_namecache.c optional vcoda coda/coda_psdev.c optional vcoda coda/coda_subr.c optional vcoda coda/coda_venus.c optional vcoda coda/coda_vfsops.c optional vcoda coda/coda_vnops.c optional vcoda contrib/altq/altq/altq_cbq.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/altq/altq/altq_cdnr.c optional altq contrib/altq/altq/altq_hfsc.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/altq/altq/altq_priq.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/altq/altq/altq_red.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/altq/altq/altq_rio.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/altq/altq/altq_rmclass.c optional altq contrib/altq/altq/altq_subr.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/dev/acpica/dbcmds.c optional acpi acpi_debug contrib/dev/acpica/dbdisply.c optional acpi acpi_debug contrib/dev/acpica/dbexec.c optional acpi acpi_debug contrib/dev/acpica/dbfileio.c optional acpi acpi_debug contrib/dev/acpica/dbhistry.c optional acpi acpi_debug contrib/dev/acpica/dbinput.c optional acpi acpi_debug contrib/dev/acpica/dbstats.c optional acpi acpi_debug contrib/dev/acpica/dbutils.c optional acpi acpi_debug contrib/dev/acpica/dbxface.c optional acpi acpi_debug contrib/dev/acpica/dmbuffer.c optional acpi acpi_debug contrib/dev/acpica/dmnames.c optional acpi acpi_debug contrib/dev/acpica/dmopcode.c optional acpi acpi_debug contrib/dev/acpica/dmobject.c optional acpi acpi_debug contrib/dev/acpica/dmresrc.c optional acpi acpi_debug contrib/dev/acpica/dmresrcl.c optional acpi acpi_debug contrib/dev/acpica/dmresrcs.c optional acpi acpi_debug contrib/dev/acpica/dmutils.c optional acpi acpi_debug contrib/dev/acpica/dmwalk.c optional acpi acpi_debug contrib/dev/acpica/dsfield.c optional acpi contrib/dev/acpica/dsinit.c optional acpi contrib/dev/acpica/dsmethod.c optional acpi contrib/dev/acpica/dsmthdat.c optional acpi contrib/dev/acpica/dsobject.c optional acpi contrib/dev/acpica/dsopcode.c optional acpi contrib/dev/acpica/dsutils.c optional acpi contrib/dev/acpica/dswexec.c optional acpi contrib/dev/acpica/dswload.c optional acpi contrib/dev/acpica/dswscope.c optional acpi contrib/dev/acpica/dswstate.c optional acpi contrib/dev/acpica/evevent.c optional acpi contrib/dev/acpica/evgpe.c optional acpi contrib/dev/acpica/evgpeblk.c optional acpi contrib/dev/acpica/evmisc.c optional acpi contrib/dev/acpica/evregion.c optional acpi contrib/dev/acpica/evrgnini.c optional acpi contrib/dev/acpica/evsci.c optional acpi contrib/dev/acpica/evxface.c optional acpi contrib/dev/acpica/evxfevnt.c optional acpi contrib/dev/acpica/evxfregn.c optional acpi contrib/dev/acpica/exconfig.c optional acpi contrib/dev/acpica/exconvrt.c optional acpi contrib/dev/acpica/excreate.c optional acpi contrib/dev/acpica/exdump.c optional acpi contrib/dev/acpica/exfield.c optional acpi contrib/dev/acpica/exfldio.c optional acpi contrib/dev/acpica/exmisc.c optional acpi contrib/dev/acpica/exmutex.c optional acpi contrib/dev/acpica/exnames.c optional acpi contrib/dev/acpica/exoparg1.c optional acpi contrib/dev/acpica/exoparg2.c optional acpi contrib/dev/acpica/exoparg3.c optional acpi contrib/dev/acpica/exoparg6.c optional acpi contrib/dev/acpica/exprep.c optional acpi contrib/dev/acpica/exregion.c optional acpi contrib/dev/acpica/exresnte.c optional acpi contrib/dev/acpica/exresolv.c optional acpi contrib/dev/acpica/exresop.c optional acpi contrib/dev/acpica/exstore.c optional acpi contrib/dev/acpica/exstoren.c optional acpi contrib/dev/acpica/exstorob.c optional acpi contrib/dev/acpica/exsystem.c optional acpi contrib/dev/acpica/exutils.c optional acpi contrib/dev/acpica/hwacpi.c optional acpi contrib/dev/acpica/hwgpe.c optional acpi contrib/dev/acpica/hwregs.c optional acpi contrib/dev/acpica/hwsleep.c optional acpi contrib/dev/acpica/hwtimer.c optional acpi contrib/dev/acpica/nsaccess.c optional acpi contrib/dev/acpica/nsalloc.c optional acpi contrib/dev/acpica/nsdump.c optional acpi contrib/dev/acpica/nseval.c optional acpi contrib/dev/acpica/nsinit.c optional acpi contrib/dev/acpica/nsload.c optional acpi contrib/dev/acpica/nsnames.c optional acpi contrib/dev/acpica/nsobject.c optional acpi contrib/dev/acpica/nsparse.c optional acpi contrib/dev/acpica/nssearch.c optional acpi contrib/dev/acpica/nsutils.c optional acpi contrib/dev/acpica/nswalk.c optional acpi contrib/dev/acpica/nsxfeval.c optional acpi contrib/dev/acpica/nsxfname.c optional acpi contrib/dev/acpica/nsxfobj.c optional acpi contrib/dev/acpica/psargs.c optional acpi contrib/dev/acpica/psloop.c optional acpi contrib/dev/acpica/psopcode.c optional acpi contrib/dev/acpica/psparse.c optional acpi contrib/dev/acpica/psscope.c optional acpi contrib/dev/acpica/pstree.c optional acpi contrib/dev/acpica/psutils.c optional acpi contrib/dev/acpica/pswalk.c optional acpi contrib/dev/acpica/psxface.c optional acpi contrib/dev/acpica/rsaddr.c optional acpi contrib/dev/acpica/rscalc.c optional acpi contrib/dev/acpica/rscreate.c optional acpi contrib/dev/acpica/rsdump.c optional acpi contrib/dev/acpica/rsinfo.c optional acpi contrib/dev/acpica/rsio.c optional acpi contrib/dev/acpica/rsirq.c optional acpi contrib/dev/acpica/rslist.c optional acpi contrib/dev/acpica/rsmemory.c optional acpi contrib/dev/acpica/rsmisc.c optional acpi contrib/dev/acpica/rsutils.c optional acpi contrib/dev/acpica/rsxface.c optional acpi contrib/dev/acpica/tbfadt.c optional acpi contrib/dev/acpica/tbfind.c optional acpi contrib/dev/acpica/tbinstal.c optional acpi contrib/dev/acpica/tbutils.c optional acpi contrib/dev/acpica/tbxface.c optional acpi contrib/dev/acpica/tbxfroot.c optional acpi contrib/dev/acpica/utalloc.c optional acpi contrib/dev/acpica/utcache.c optional acpi contrib/dev/acpica/utclib.c optional acpi contrib/dev/acpica/utcopy.c optional acpi contrib/dev/acpica/utdebug.c optional acpi contrib/dev/acpica/utdelete.c optional acpi contrib/dev/acpica/uteval.c optional acpi contrib/dev/acpica/utglobal.c optional acpi contrib/dev/acpica/utinit.c optional acpi contrib/dev/acpica/utmath.c optional acpi contrib/dev/acpica/utmisc.c optional acpi contrib/dev/acpica/utmutex.c optional acpi contrib/dev/acpica/utobject.c optional acpi contrib/dev/acpica/utresrc.c optional acpi contrib/dev/acpica/utstate.c optional acpi contrib/dev/acpica/utxface.c optional acpi contrib/ipfilter/netinet/fil.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_auth.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_fil_freebsd.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_frag.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_log.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_nat.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_proxy.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_state.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_lookup.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_pool.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_htable.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_sync.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/mlfk_ipl.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ngatm/netnatm/api/cc_conn.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_data.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_dump.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_port.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_sig.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_user.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/unisap.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/misc/straddr.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/misc/unimsg_common.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/msg/traffic.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/msg/uni_ie.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/msg/uni_msg.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/saal/saal_sscfu.c optional ngatm_sscfu \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/saal/saal_sscop.c optional ngatm_sscop \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_call.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_coord.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_party.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_print.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_reset.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_uni.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_unimsgcpy.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_verify.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/pf/net/if_pflog.c optional pflog \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/if_pfsync.c optional pfsync \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_if.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_subr.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_ioctl.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_norm.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_table.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_osfp.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/netinet/in4_cksum.c optional pf inet crypto/blowfish/bf_ecb.c optional ipsec ipsec_esp crypto/blowfish/bf_skey.c optional crypto | ipsec ipsec_esp crypto/camellia/camellia.c optional crypto | ipsec ipsec_esp crypto/camellia/camellia-api.c optional crypto | ipsec ipsec_esp crypto/des/des_ecb.c optional crypto | ipsec ipsec_esp | netsmb crypto/des/des_setkey.c optional crypto | ipsec ipsec_esp | netsmb crypto/rc4/rc4.c optional netgraph_mppc_encryption crypto/rijndael/rijndael-alg-fst.c optional crypto | geom_bde | \ ipsec | random | wlan_ccmp crypto/rijndael/rijndael-api-fst.c optional geom_bde | random crypto/rijndael/rijndael-api.c optional crypto | ipsec | wlan_ccmp crypto/sha1.c optional carp | crypto | ipsec | \ netgraph_mppc_encryption | sctp crypto/sha2/sha2.c optional crypto | geom_bde | ipsec | random | \ sctp ddb/db_access.c optional ddb ddb/db_break.c optional ddb ddb/db_command.c optional ddb ddb/db_examine.c optional ddb ddb/db_expr.c optional ddb ddb/db_input.c optional ddb ddb/db_lex.c optional ddb ddb/db_main.c optional ddb ddb/db_output.c optional ddb ddb/db_print.c optional ddb ddb/db_ps.c optional ddb ddb/db_run.c optional ddb ddb/db_sym.c optional ddb ddb/db_thread.c optional ddb ddb/db_variables.c optional ddb ddb/db_watch.c optional ddb ddb/db_write_cmd.c optional ddb #dev/dpt/dpt_control.c optional dpt dev/aac/aac.c optional aac dev/aac/aac_cam.c optional aacp aac dev/aac/aac_debug.c optional aac dev/aac/aac_disk.c optional aac dev/aac/aac_linux.c optional aac compat_linux dev/aac/aac_pci.c optional aac pci dev/acpi_support/acpi_aiboost.c optional acpi_aiboost acpi dev/acpi_support/acpi_asus.c optional acpi_asus acpi dev/acpi_support/acpi_fujitsu.c optional acpi_fujitsu acpi dev/acpi_support/acpi_ibm.c optional acpi_ibm acpi dev/acpi_support/acpi_panasonic.c optional acpi_panasonic acpi dev/acpi_support/acpi_sony.c optional acpi_sony acpi dev/acpi_support/acpi_toshiba.c optional acpi_toshiba acpi dev/acpica/Osd/OsdDebug.c optional acpi dev/acpica/Osd/OsdHardware.c optional acpi dev/acpica/Osd/OsdInterrupt.c optional acpi dev/acpica/Osd/OsdMemory.c optional acpi dev/acpica/Osd/OsdSchedule.c optional acpi dev/acpica/Osd/OsdStream.c optional acpi dev/acpica/Osd/OsdSynch.c optional acpi dev/acpica/Osd/OsdTable.c optional acpi dev/acpica/acpi.c optional acpi dev/acpica/acpi_acad.c optional acpi dev/acpica/acpi_battery.c optional acpi dev/acpica/acpi_button.c optional acpi dev/acpica/acpi_cmbat.c optional acpi dev/acpica/acpi_cpu.c optional acpi dev/acpica/acpi_ec.c optional acpi dev/acpica/acpi_hpet.c optional acpi dev/acpica/acpi_isab.c optional acpi isa dev/acpica/acpi_lid.c optional acpi dev/acpica/acpi_package.c optional acpi 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_perf.c optional acpi dev/acpica/acpi_powerres.c optional acpi dev/acpica/acpi_quirk.c optional acpi dev/acpica/acpi_resource.c optional acpi dev/acpica/acpi_smbat.c optional acpi dev/acpica/acpi_thermal.c optional acpi dev/acpica/acpi_throttle.c optional acpi dev/acpica/acpi_timer.c optional acpi dev/acpica/acpi_video.c optional acpi_video acpi dev/acpica/acpi_dock.c optional acpi_dock acpi dev/adlink/adlink.c optional adlink dev/advansys/adv_eisa.c optional adv eisa dev/advansys/adv_pci.c optional adv pci dev/advansys/advansys.c optional adv dev/advansys/advlib.c optional adv dev/advansys/advmcode.c optional adv dev/advansys/adw_pci.c optional adw pci dev/advansys/adwcam.c optional adw dev/advansys/adwlib.c optional adw dev/advansys/adwmcode.c optional adw dev/aha/aha.c optional aha dev/aha/aha_isa.c optional aha isa dev/aha/aha_mca.c optional aha mca dev/ahb/ahb.c optional ahb eisa dev/aic/aic.c optional aic dev/aic/aic_pccard.c optional aic pccard dev/aic7xxx/ahc_eisa.c optional ahc eisa dev/aic7xxx/ahc_isa.c optional ahc isa dev/aic7xxx/ahc_pci.c optional ahc pci dev/aic7xxx/ahd_pci.c optional ahd pci dev/aic7xxx/aic7770.c optional ahc dev/aic7xxx/aic79xx.c optional ahd pci dev/aic7xxx/aic79xx_osm.c optional ahd pci dev/aic7xxx/aic79xx_pci.c optional ahd pci dev/aic7xxx/aic7xxx.c optional ahc dev/aic7xxx/aic7xxx_93cx6.c optional ahc dev/aic7xxx/aic7xxx_osm.c optional ahc dev/aic7xxx/aic7xxx_pci.c optional ahc pci dev/amd/amd.c optional amd dev/amr/amr.c optional amr dev/amr/amr_cam.c optional amr dev/amr/amr_disk.c optional amr dev/amr/amr_linux.c optional amr compat_linux dev/amr/amr_pci.c optional amr pci dev/an/if_an.c optional an dev/an/if_an_isa.c optional an isa dev/an/if_an_pccard.c optional an pccard dev/an/if_an_pci.c optional an pci dev/asr/asr.c optional asr pci dev/ata/ata_if.m optional ata dev/ata/ata-all.c optional ata dev/ata/ata-card.c optional ata pccard dev/ata/ata-cbus.c optional ata pc98 dev/ata/ata-chipset.c optional ata pci dev/ata/ata-disk.c optional atadisk dev/ata/ata-dma.c optional ata pci dev/ata/ata-isa.c optional ata isa dev/ata/ata-lowlevel.c optional ata dev/ata/ata-pci.c optional ata pci dev/ata/ata-queue.c optional ata dev/ata/ata-raid.c optional ataraid dev/ata/ata-usb.c optional atausb dev/ata/atapi-cam.c optional atapicam dev/ata/atapi-cd.c optional atapicd dev/ata/atapi-fd.c optional atapifd dev/ata/atapi-tape.c optional atapist dev/ath/ah_osdep.c optional ath_hal \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_rate/amrr/amrr.c optional ath_rate_amrr dev/ath/ath_rate/onoe/onoe.c optional ath_rate_onoe \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_rate/sample/sample.c optional ath_rate_sample \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/if_ath.c optional ath \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/if_ath_pci.c optional ath pci \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/awi/am79c930.c optional awi dev/awi/awi.c optional awi dev/awi/if_awi_pccard.c optional awi pccard dev/bce/if_bce.c optional bce dev/bfe/if_bfe.c optional bfe dev/bge/if_bge.c optional bge dev/bktr/bktr_audio.c optional bktr pci dev/bktr/bktr_card.c optional bktr pci dev/bktr/bktr_core.c optional bktr pci dev/bktr/bktr_i2c.c optional bktr pci smbus dev/bktr/bktr_os.c optional bktr pci dev/bktr/bktr_tuner.c optional bktr pci dev/bktr/msp34xx.c optional bktr pci dev/buslogic/bt.c optional bt dev/buslogic/bt_eisa.c optional bt eisa dev/buslogic/bt_isa.c optional bt isa dev/buslogic/bt_mca.c optional bt mca dev/buslogic/bt_pci.c optional bt pci dev/cardbus/cardbus.c optional cardbus dev/cardbus/cardbus_cis.c optional cardbus dev/cardbus/cardbus_device.c optional cardbus dev/ciss/ciss.c optional ciss dev/cm/smc90cx6.c optional cm dev/cnw/if_cnw.c optional cnw pccard dev/cpufreq/ichss.c optional cpufreq dev/cs/if_cs.c optional cs dev/cs/if_cs_isa.c optional cs isa dev/cs/if_cs_pccard.c optional cs pccard dev/cxgb/cxgb_main.c optional cxgb pci dev/cxgb/cxgb_offload.c optional cxgb pci dev/cxgb/cxgb_l2t.c optional cxgb pci dev/cxgb/cxgb_lro.c optional cxgb pci dev/cxgb/cxgb_sge.c optional cxgb pci dev/cxgb/common/cxgb_mc5.c optional cxgb pci dev/cxgb/common/cxgb_vsc8211.c optional cxgb pci dev/cxgb/common/cxgb_ael1002.c optional cxgb pci dev/cxgb/common/cxgb_mv88e1xxx.c optional cxgb pci dev/cxgb/common/cxgb_xgmac.c optional cxgb pci dev/cxgb/common/cxgb_t3_hw.c optional cxgb pci dev/cxgb/sys/uipc_mvec.c optional cxgb pci dev/cy/cy.c optional cy dev/cy/cy_isa.c optional cy isa dev/cy/cy_pci.c optional cy pci dev/dc/if_dc.c optional dc pci dev/dc/dcphy.c optional dc pci dev/dc/pnphy.c optional dc pci dev/dcons/dcons.c optional dcons dev/dcons/dcons_crom.c optional dcons_crom dev/dcons/dcons_os.c optional dcons dev/de/if_de.c optional de pci dev/digi/CX.c optional digi_CX dev/digi/CX_PCI.c optional digi_CX_PCI dev/digi/EPCX.c optional digi_EPCX dev/digi/EPCX_PCI.c optional digi_EPCX_PCI dev/digi/Xe.c optional digi_Xe dev/digi/Xem.c optional digi_Xem dev/digi/Xr.c optional digi_Xr dev/digi/digi.c optional digi dev/digi/digi_isa.c optional digi isa dev/digi/digi_pci.c optional digi pci dev/dpt/dpt_eisa.c optional dpt eisa dev/dpt/dpt_pci.c optional dpt pci dev/dpt/dpt_scsi.c optional dpt dev/drm/ati_pcigart.c optional drm dev/drm/drm_agpsupport.c optional drm dev/drm/drm_auth.c optional drm dev/drm/drm_bufs.c optional drm dev/drm/drm_context.c optional drm dev/drm/drm_dma.c optional drm dev/drm/drm_drawable.c optional drm dev/drm/drm_drv.c optional drm dev/drm/drm_fops.c optional drm dev/drm/drm_ioctl.c optional drm dev/drm/drm_irq.c optional drm dev/drm/drm_lock.c optional drm dev/drm/drm_memory.c optional drm dev/drm/drm_pci.c optional drm dev/drm/drm_scatter.c optional drm dev/drm/drm_sysctl.c optional drm dev/drm/drm_vm.c optional drm dev/drm/i915_dma.c optional i915drm dev/drm/i915_drv.c optional i915drm dev/drm/i915_irq.c optional i915drm dev/drm/i915_mem.c optional i915drm dev/drm/mach64_dma.c optional mach64drm dev/drm/mach64_drv.c optional mach64drm dev/drm/mach64_irq.c optional mach64drm dev/drm/mach64_state.c optional mach64drm dev/drm/mga_dma.c optional mgadrm dev/drm/mga_drv.c optional mgadrm dev/drm/mga_irq.c optional mgadrm dev/drm/mga_state.c optional mgadrm \ compile-with "${NORMAL_C} -finline-limit=13500" dev/drm/mga_warp.c optional mgadrm dev/drm/r128_cce.c optional r128drm dev/drm/r128_drv.c optional r128drm dev/drm/r128_irq.c optional r128drm dev/drm/r128_state.c optional r128drm \ compile-with "${NORMAL_C} -finline-limit=13500" dev/drm/r300_cmdbuf.c optional radeondrm dev/drm/radeon_cp.c optional radeondrm dev/drm/radeon_drv.c optional radeondrm dev/drm/radeon_irq.c optional radeondrm dev/drm/radeon_mem.c optional radeondrm dev/drm/radeon_state.c optional radeondrm dev/drm/savage_bci.c optional savagedrm dev/drm/savage_drv.c optional savagedrm dev/drm/savage_state.c optional savagedrm dev/drm/sis_drv.c optional sisdrm dev/drm/sis_ds.c optional sisdrm dev/drm/sis_mm.c optional sisdrm dev/drm/tdfx_drv.c optional tdfxdrm dev/ed/if_ed.c optional ed dev/ed/if_ed_novell.c optional ed dev/ed/if_ed_rtl80x9.c optional ed dev/ed/if_ed_pccard.c optional ed pccard dev/ed/if_ed_pci.c optional ed pci dev/eisa/eisa_if.m standard dev/eisa/eisaconf.c optional eisa dev/em/if_em.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_80003es2lan.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_82540.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_82541.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_82542.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_82543.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_82571.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_82575.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_api.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_ich8lan.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_mac.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_manage.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_nvm.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/em/e1000_phy.c optional em \ compile-with "${NORMAL_C} -I$S/dev/em" dev/en/if_en_pci.c optional en pci dev/en/midway.c optional en dev/ep/if_ep.c optional ep dev/ep/if_ep_eisa.c optional ep eisa dev/ep/if_ep_isa.c optional ep isa dev/ep/if_ep_mca.c optional ep mca dev/ep/if_ep_pccard.c optional ep pccard dev/esp/ncr53c9x.c optional esp dev/ex/if_ex.c optional ex dev/ex/if_ex_isa.c optional ex isa dev/ex/if_ex_pccard.c optional ex pccard dev/exca/exca.c optional cbb dev/fatm/if_fatm.c optional fatm pci dev/fb/splash.c optional splash dev/fe/if_fe.c optional fe dev/fe/if_fe_pccard.c optional fe pccard dev/firewire/firewire.c optional firewire dev/firewire/fwcrom.c optional firewire dev/firewire/fwdev.c optional firewire dev/firewire/fwdma.c optional firewire dev/firewire/fwmem.c optional firewire dev/firewire/fwohci.c optional firewire dev/firewire/fwohci_pci.c optional firewire pci dev/firewire/if_fwe.c optional fwe dev/firewire/if_fwip.c optional fwip dev/firewire/sbp.c optional sbp dev/firewire/sbp_targ.c optional sbp_targ dev/flash/at45d.c optional at45d dev/fxp/if_fxp.c optional fxp dev/gem/if_gem.c optional gem dev/gem/if_gem_pci.c optional gem pci dev/harp/if_harp.c optional harp pci dev/hatm/if_hatm.c optional hatm pci dev/hatm/if_hatm_intr.c optional hatm pci dev/hatm/if_hatm_ioctl.c optional hatm pci dev/hatm/if_hatm_rx.c optional hatm pci dev/hatm/if_hatm_tx.c optional hatm pci dev/hfa/fore_buffer.c optional hfa dev/hfa/fore_command.c optional hfa dev/hfa/fore_globals.c optional hfa dev/hfa/fore_if.c optional hfa dev/hfa/fore_init.c optional hfa dev/hfa/fore_intr.c optional hfa dev/hfa/fore_output.c optional hfa dev/hfa/fore_receive.c optional hfa dev/hfa/fore_stats.c optional hfa dev/hfa/fore_timer.c optional hfa dev/hfa/fore_transmit.c optional hfa dev/hfa/fore_vcm.c optional hfa #dev/hfa/hfa_eisa.c optional hfa eisa dev/hfa/hfa_freebsd.c optional hfa dev/hfa/hfa_pci.c optional hfa pci #dev/hfa/hfa_sbus.c optional hfa sbus dev/hifn/hifn7751.c optional hifn dev/hme/if_hme.c optional hme dev/hme/if_hme_pci.c optional hme pci dev/hme/if_hme_sbus.c optional hme sbus dev/hptiop/hptiop.c optional hptiop cam dev/hwpmc/hwpmc_logging.c optional hwpmc dev/hwpmc/hwpmc_mod.c optional hwpmc dev/ichsmb/ichsmb.c optional ichsmb dev/ichsmb/ichsmb_pci.c optional ichsmb pci dev/ida/ida.c optional ida dev/ida/ida_disk.c optional ida dev/ida/ida_eisa.c optional ida eisa dev/ida/ida_pci.c optional ida pci dev/ie/if_ie.c optional ie isa nowerror dev/ie/if_ie_isa.c optional ie isa dev/ieee488/ibfoo.c optional pcii | tnt4882 dev/ieee488/pcii.c optional pcii dev/ieee488/tnt4882.c optional tnt4882 dev/ieee488/upd7210.c optional pcii | tnt4882 dev/iicbus/ad7418.c optional ad7418 dev/iicbus/ds1672.c optional ds1672 dev/iicbus/icee.c optional icee dev/iicbus/if_ic.c optional ic dev/iicbus/iic.c optional iic dev/iicbus/iicbb.c optional iicbb dev/iicbus/iicbb_if.m optional iicbb dev/iicbus/iicbus.c optional iicbus dev/iicbus/iicbus_if.m optional iicbus dev/iicbus/iiconf.c optional iicbus dev/iicbus/iicsmb.c optional iicsmb \ dependency "iicbus_if.h" dev/iir/iir.c optional iir dev/iir/iir_ctrl.c optional iir dev/iir/iir_pci.c optional iir pci dev/ips/ips.c optional ips dev/ips/ips_commands.c optional ips dev/ips/ips_disk.c optional ips dev/ips/ips_ioctl.c optional ips dev/ips/ips_pci.c optional ips pci dev/ipw/if_ipw.c optional ipw dev/isp/isp.c optional isp dev/isp/isp_freebsd.c optional isp dev/isp/isp_library.c optional isp dev/isp/isp_pci.c optional isp pci dev/isp/isp_sbus.c optional isp sbus dev/isp/isp_target.c optional isp dev/ispfw/ispfw.c optional ispfw dev/iwi/if_iwi.c optional iwi dev/ixgb/if_ixgb.c optional ixgb dev/ixgb/ixgb_ee.c optional ixgb dev/ixgb/ixgb_hw.c optional ixgb dev/joy/joy.c optional joy dev/joy/joy_isa.c optional joy isa dev/joy/joy_pccard.c optional joy pccard dev/kbdmux/kbdmux.c optional kbdmux dev/le/am7990.c optional le dev/le/am79900.c optional le dev/le/if_le_pci.c optional le pci dev/le/lance.c optional le dev/led/led.c standard dev/lge/if_lge.c optional lge dev/lmc/if_lmc.c optional lmc dev/mc146818/mc146818.c optional mc146818 dev/mca/mca_bus.c optional mca dev/mcd/mcd.c optional mcd isa nowerror dev/mcd/mcd_isa.c optional mcd isa nowerror dev/md/md.c optional md dev/mem/memdev.c optional mem dev/mfi/mfi.c optional mfi dev/mfi/mfi_debug.c optional mfi dev/mfi/mfi_pci.c optional mfi pci dev/mfi/mfi_disk.c optional mfi dev/mfi/mfi_linux.c optional mfi compat_linux dev/mfi/mfi_cam.c optional mfip scbus dev/mii/acphy.c optional miibus | acphy dev/mii/amphy.c optional miibus | amphy dev/mii/bmtphy.c optional miibus | bmtphy dev/mii/brgphy.c optional miibus | brgphy dev/mii/ciphy.c optional miibus | ciphy dev/mii/e1000phy.c optional miibus | e1000phy # XXX only xl cards? dev/mii/exphy.c optional miibus | exphy dev/mii/gentbi.c optional miibus | gentbi # XXX only fxp cards? dev/mii/inphy.c optional miibus | inphy dev/mii/ip1000phy.c optional miibus | ip1000phy dev/mii/lxtphy.c optional miibus | lxtphy dev/mii/mii.c optional miibus | mii dev/mii/mii_physubr.c optional miibus | mii dev/mii/miibus_if.m optional miibus | mii dev/mii/mlphy.c optional miibus | mlphy dev/mii/nsgphy.c optional miibus | nsgphy dev/mii/nsphy.c optional miibus | nsphy dev/mii/pnaphy.c optional miibus | pnaphy dev/mii/qsphy.c optional miibus | qsphy dev/mii/rgephy.c optional miibus | rgephy dev/mii/rlphy.c optional miibus | rlphy dev/mii/rlswitch.c optional rlswitch # XXX rue only? dev/mii/ruephy.c optional miibus | ruephy dev/mii/tdkphy.c optional miibus | tdkphy dev/mii/tlphy.c optional miibus | tlphy dev/mii/ukphy.c optional miibus | mii dev/mii/ukphy_subr.c optional miibus | mii dev/mii/xmphy.c optional miibus | xmphy dev/mk48txx/mk48txx.c optional mk48txx dev/mlx/mlx.c optional mlx dev/mlx/mlx_disk.c optional mlx dev/mlx/mlx_pci.c optional mlx pci dev/mly/mly.c optional mly dev/mmc/mmc.c optional mmc dev/mmc/mmcbr_if.m standard dev/mmc/mmcbus_if.m standard dev/mmc/mmcsd.c optional mmcsd dev/mpt/mpt.c optional mpt dev/mpt/mpt_cam.c optional mpt dev/mpt/mpt_debug.c optional mpt dev/mpt/mpt_pci.c optional mpt pci dev/mpt/mpt_raid.c optional mpt dev/msk/if_msk.c optional msk dev/my/if_my.c optional my dev/ncv/ncr53c500.c optional ncv dev/ncv/ncr53c500_pccard.c optional ncv pccard dev/nge/if_nge.c optional nge dev/nmdm/nmdm.c optional nmdm dev/nsp/nsp.c optional nsp dev/nsp/nsp_pccard.c optional nsp pccard dev/null/null.c standard dev/patm/if_patm.c optional patm pci dev/patm/if_patm_attach.c optional patm pci dev/patm/if_patm_intr.c optional patm pci dev/patm/if_patm_ioctl.c optional patm pci dev/patm/if_patm_rtables.c optional patm pci dev/patm/if_patm_rx.c optional patm pci dev/patm/if_patm_tx.c optional patm pci dev/pbio/pbio.c optional pbio isa dev/pccard/card_if.m standard dev/pccard/pccard.c optional pccard dev/pccard/pccard_cis.c optional pccard dev/pccard/pccard_cis_quirks.c optional pccard dev/pccard/pccard_device.c optional pccard dev/pccard/power_if.m standard dev/pccbb/pccbb.c optional cbb dev/pccbb/pccbb_isa.c optional cbb isa dev/pccbb/pccbb_pci.c optional cbb pci dev/pcf/pcf.c optional pcf dev/pci/eisa_pci.c optional pci eisa dev/pci/fixup_pci.c optional pci dev/pci/hostb_pci.c optional pci dev/pci/ignore_pci.c optional pci dev/pci/isa_pci.c optional pci isa dev/pci/pci.c optional pci dev/pci/pci_if.m standard dev/pci/pci_pci.c optional pci dev/pci/pci_user.c optional pci dev/pci/pcib_if.m standard dev/pci/vga_pci.c optional pci dev/pdq/if_fea.c optional fea eisa dev/pdq/if_fpa.c optional fpa pci dev/pdq/pdq.c optional nowerror fea eisa | fpa pci dev/pdq/pdq_ifsubr.c optional nowerror fea eisa | fpa pci dev/ppbus/if_plip.c optional plip dev/ppbus/immio.c optional vpo dev/ppbus/lpbb.c optional lpbb dev/ppbus/lpt.c optional lpt dev/ppbus/pcfclock.c optional pcfclock dev/ppbus/ppb_1284.c optional ppbus dev/ppbus/ppb_base.c optional ppbus dev/ppbus/ppb_msq.c optional ppbus dev/ppbus/ppbconf.c optional ppbus dev/ppbus/ppbus_if.m optional ppbus dev/ppbus/ppi.c optional ppi dev/ppbus/pps.c optional pps dev/ppbus/vpo.c optional vpo dev/ppbus/vpoio.c optional vpo dev/ppc/ppc.c optional ppc dev/ppc/ppc_acpi.c optional ppc acpi dev/ppc/ppc_isa.c optional ppc isa dev/ppc/ppc_pci.c optional ppc pci dev/ppc/ppc_puc.c optional ppc puc dev/pst/pst-iop.c optional pst dev/pst/pst-pci.c optional pst pci dev/pst/pst-raid.c optional pst dev/puc/puc.c optional puc dev/puc/puc_cfg.c optional puc dev/puc/puc_pccard.c optional puc pccard dev/puc/puc_pci.c optional puc pci dev/puc/pucdata.c optional puc pci dev/ral/rt2560.c optional ral dev/ral/rt2661.c optional ral dev/ral/if_ralrate.c optional ral dev/ral/if_ral_pci.c optional ral pci dev/random/harvest.c standard dev/random/hash.c optional random dev/random/probe.c optional random dev/random/randomdev.c optional random dev/random/randomdev_soft.c optional random dev/random/yarrow.c optional random dev/ray/if_ray.c optional ray pccard dev/rc/rc.c optional rc dev/re/if_re.c optional re dev/rndtest/rndtest.c optional rndtest dev/rp/rp.c optional rp dev/rp/rp_isa.c optional rp isa dev/rp/rp_pci.c optional rp pci dev/safe/safe.c optional safe dev/sbsh/if_sbsh.c optional sbsh dev/scc/scc_if.m optional scc dev/scc/scc_bfe_ebus.c optional scc ebus dev/scc/scc_bfe_sbus.c optional scc fhc | scc sbus dev/scc/scc_core.c optional scc dev/scc/scc_dev_sab82532.c optional scc dev/scc/scc_dev_z8530.c optional scc dev/scd/scd.c optional scd isa dev/scd/scd_isa.c optional scd isa dev/si/si.c optional si dev/si/si2_z280.c optional si dev/si/si3_t225.c optional si dev/si/si_eisa.c optional si eisa dev/si/si_isa.c optional si isa dev/si/si_pci.c optional si pci dev/sk/if_sk.c optional sk pci dev/smbus/smb.c optional smb dev/smbus/smbconf.c optional smbus dev/smbus/smbus.c optional smbus dev/smbus/smbus_if.m optional smbus dev/sn/if_sn.c optional sn dev/sn/if_sn_isa.c optional sn isa dev/sn/if_sn_pccard.c optional sn pccard dev/snp/snp.c optional snp +dev/sound/clone.c optional sound +dev/sound/unit.c optional sound dev/sound/isa/ad1816.c optional snd_ad1816 isa dev/sound/isa/ess.c optional snd_ess isa dev/sound/isa/gusc.c optional snd_gusc isa dev/sound/isa/mss.c optional snd_mss isa dev/sound/isa/sb16.c optional snd_sb16 isa dev/sound/isa/sb8.c optional snd_sb8 isa dev/sound/isa/sbc.c optional snd_sbc isa dev/sound/isa/sndbuf_dma.c optional sound isa dev/sound/pci/als4000.c optional snd_als4000 pci dev/sound/pci/atiixp.c optional snd_atiixp pci #dev/sound/pci/au88x0.c optional snd_au88x0 pci dev/sound/pci/cmi.c optional snd_cmi pci dev/sound/pci/cs4281.c optional snd_cs4281 pci dev/sound/pci/csa.c optional snd_csa pci \ warning "kernel contains GPL contaminated csaimg.h header" dev/sound/pci/csapcm.c optional snd_csa pci dev/sound/pci/ds1.c optional snd_ds1 pci dev/sound/pci/emu10k1.c optional snd_emu10k1 pci \ dependency "emu10k1-alsa%diked.h" \ warning "kernel contains GPL contaminated emu10k1 headers" dev/sound/pci/emu10kx.c optional snd_emu10kx pci \ dependency "emu10k1-alsa%diked.h" \ dependency "p16v-alsa%diked.h" \ dependency "p17v-alsa%diked.h" \ warning "kernel contains GPL contaminated emu10kx headers" dev/sound/pci/emu10kx-pcm.c optional snd_emu10kx pci \ dependency "emu10k1-alsa%diked.h" \ dependency "p16v-alsa%diked.h" \ dependency "p17v-alsa%diked.h" \ warning "kernel contains GPL contaminated emu10kx headers" dev/sound/pci/emu10kx-midi.c optional snd_emu10kx pci \ dependency "emu10k1-alsa%diked.h" \ warning "kernel contains GPL contaminated emu10kx headers" dev/sound/pci/envy24.c optional snd_envy24 pci dev/sound/pci/envy24ht.c optional snd_envy24ht pci dev/sound/pci/es137x.c optional snd_es137x pci dev/sound/pci/fm801.c optional snd_fm801 pci dev/sound/pci/ich.c optional snd_ich pci dev/sound/pci/maestro.c optional snd_maestro pci dev/sound/pci/maestro3.c optional snd_maestro3 pci \ warning "kernel contains GPL contaminated maestro3 headers" dev/sound/pci/neomagic.c optional snd_neomagic pci dev/sound/pci/solo.c optional snd_solo pci dev/sound/pci/spicds.c optional snd_spicds pci dev/sound/pci/t4dwave.c optional snd_t4dwave pci dev/sound/pci/via8233.c optional snd_via8233 pci dev/sound/pci/via82c686.c optional snd_via82c686 pci dev/sound/pci/vibes.c optional snd_vibes pci dev/sound/pci/hda/hdac.c optional snd_hda pci dev/sound/pcm/ac97.c optional sound dev/sound/pcm/ac97_if.m optional sound dev/sound/pcm/ac97_patch.c optional sound dev/sound/pcm/buffer.c optional sound dev/sound/pcm/channel.c optional sound dev/sound/pcm/channel_if.m optional sound dev/sound/pcm/dsp.c optional sound dev/sound/pcm/fake.c optional sound dev/sound/pcm/feeder.c optional sound dev/sound/pcm/feeder_fmt.c optional sound dev/sound/pcm/feeder_if.m optional sound dev/sound/pcm/feeder_rate.c optional sound dev/sound/pcm/feeder_volume.c optional sound dev/sound/pcm/mixer.c optional sound dev/sound/pcm/mixer_if.m optional sound dev/sound/pcm/sndstat.c optional sound dev/sound/pcm/sound.c optional sound dev/sound/pcm/vchan.c optional sound #dev/sound/usb/upcm.c optional snd_upcm usb dev/sound/usb/uaudio.c optional snd_uaudio usb dev/sound/usb/uaudio_pcm.c optional snd_uaudio usb dev/sound/midi/midi.c optional sound dev/sound/midi/mpu401.c optional sound dev/sound/midi/mpu_if.m optional sound dev/sound/midi/mpufoi_if.m optional sound dev/sound/midi/sequencer.c optional sound dev/sound/midi/synth_if.m optional sound dev/spibus/spibus.c optional spibus \ dependency "spibus_if.h" dev/spibus/spibus_if.m optional spibus dev/sr/if_sr.c optional sr dev/sr/if_sr_pci.c optional sr pci dev/stg/tmc18c30.c optional stg dev/stg/tmc18c30_isa.c optional stg isa dev/stg/tmc18c30_pccard.c optional stg pccard dev/stg/tmc18c30_pci.c optional stg pci dev/stg/tmc18c30_subr.c optional stg dev/stge/if_stge.c optional stge dev/streams/streams.c optional streams dev/sym/sym_hipd.c optional sym \ dependency "$S/dev/sym/sym_{conf,defs}.h" dev/syscons/blank/blank_saver.c optional blank_saver dev/syscons/daemon/daemon_saver.c optional daemon_saver dev/syscons/dragon/dragon_saver.c optional dragon_saver dev/syscons/fade/fade_saver.c optional fade_saver dev/syscons/fire/fire_saver.c optional fire_saver dev/syscons/green/green_saver.c optional green_saver dev/syscons/logo/logo.c optional logo_saver dev/syscons/logo/logo_saver.c optional logo_saver dev/syscons/rain/rain_saver.c optional rain_saver dev/syscons/schistory.c optional sc dev/syscons/scmouse.c optional sc dev/syscons/scterm-dumb.c optional sc dev/syscons/scterm.c optional sc dev/syscons/scvidctl.c optional sc dev/syscons/snake/snake_saver.c optional snake_saver dev/syscons/star/star_saver.c optional star_saver dev/syscons/syscons.c optional sc dev/syscons/sysmouse.c optional sc dev/syscons/warp/warp_saver.c optional warp_saver dev/tdfx/tdfx_linux.c optional tdfx_linux tdfx compat_linux dev/tdfx/tdfx_pci.c optional tdfx pci dev/ti/if_ti.c optional ti pci dev/trm/trm.c optional trm dev/twa/tw_cl_init.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twa/tw_cl_intr.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twa/tw_cl_io.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twa/tw_cl_misc.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twa/tw_osl_cam.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twa/tw_osl_freebsd.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twe/twe.c optional twe dev/twe/twe_freebsd.c optional twe dev/tx/if_tx.c optional tx dev/txp/if_txp.c optional txp dev/uart/uart_bus_acpi.c optional uart acpi #dev/uart/uart_bus_cbus.c optional uart cbus dev/uart/uart_bus_ebus.c optional uart ebus dev/uart/uart_bus_isa.c optional uart isa dev/uart/uart_bus_pccard.c optional uart pccard dev/uart/uart_bus_pci.c optional uart pci dev/uart/uart_bus_puc.c optional uart puc dev/uart/uart_bus_scc.c optional uart scc dev/uart/uart_core.c optional uart dev/uart/uart_dbg.c optional uart gdb dev/uart/uart_dev_ns8250.c optional uart uart_ns8250 dev/uart/uart_dev_sab82532.c optional uart uart_sab82532 dev/uart/uart_dev_sab82532.c optional uart scc dev/uart/uart_dev_z8530.c optional uart uart_z8530 dev/uart/uart_dev_z8530.c optional uart scc dev/uart/uart_if.m optional uart dev/uart/uart_subr.c optional uart dev/uart/uart_tty.c optional uart dev/ubsec/ubsec.c optional ubsec # # USB support dev/usb/ehci.c optional ehci dev/usb/ehci_pci.c optional ehci pci dev/usb/hid.c optional usb dev/usb/if_aue.c optional aue dev/usb/if_axe.c optional axe dev/usb/if_cdce.c optional cdce dev/usb/if_cue.c optional cue dev/usb/if_kue.c optional kue dev/usb/if_ural.c optional ural dev/usb/if_rue.c optional rue dev/usb/if_rum.c optional rum dev/usb/if_udav.c optional udav dev/usb/ohci.c optional ohci dev/usb/ohci_pci.c optional ohci pci dev/usb/sl811hs.c optional slhci dev/usb/slhci_pccard.c optional slhci pccard dev/usb/uark.c optional uark dev/usb/ubsa.c optional ubsa dev/usb/ubser.c optional ubser dev/usb/ucom.c optional ucom dev/usb/ucycom.c optional ucycom dev/usb/udbp.c optional udbp dev/usb/ufoma.c optional ufoma dev/usb/ufm.c optional ufm dev/usb/uftdi.c optional uftdi dev/usb/ugen.c optional ugen dev/usb/uhci.c optional uhci dev/usb/uhci_pci.c optional uhci pci dev/usb/uhid.c optional uhid dev/usb/uhub.c optional usb dev/usb/uipaq.c optional uipaq dev/usb/ukbd.c optional ukbd dev/usb/ulpt.c optional ulpt dev/usb/umass.c optional umass dev/usb/umct.c optional umct dev/usb/umodem.c optional umodem dev/usb/ums.c optional ums dev/usb/uplcom.c optional uplcom dev/usb/urio.c optional urio dev/usb/usb.c optional usb dev/usb/usb_ethersubr.c optional usb dev/usb/usb_if.m optional usb dev/usb/usb_mem.c optional usb dev/usb/usb_quirks.c optional usb dev/usb/usb_subr.c optional usb dev/usb/usbdi.c optional usb dev/usb/usbdi_util.c optional usb dev/usb/uscanner.c optional uscanner dev/usb/uvisor.c optional uvisor dev/usb/uvscom.c optional uvscom dev/utopia/idtphy.c optional utopia dev/utopia/suni.c optional utopia dev/utopia/utopia.c optional utopia dev/vge/if_vge.c optional vge dev/vkbd/vkbd.c optional vkbd dev/vx/if_vx.c optional vx dev/vx/if_vx_eisa.c optional vx eisa dev/vx/if_vx_pci.c optional vx pci dev/watchdog/watchdog.c standard dev/wds/wd7000.c optional wds isa dev/wi/if_wi.c optional wi dev/wi/if_wi_pccard.c optional wi pccard dev/wi/if_wi_pci.c optional wi pci dev/wl/if_wl.c optional wl isa dev/xe/if_xe.c optional xe dev/xe/if_xe_pccard.c optional xe pccard fs/deadfs/dead_vnops.c standard fs/devfs/devfs_devs.c standard fs/devfs/devfs_rule.c standard fs/devfs/devfs_vfsops.c standard fs/devfs/devfs_vnops.c standard fs/fdescfs/fdesc_vfsops.c optional fdescfs fs/fdescfs/fdesc_vnops.c optional fdescfs fs/fifofs/fifo_vnops.c standard fs/hpfs/hpfs_alsubr.c optional hpfs fs/hpfs/hpfs_lookup.c optional hpfs fs/hpfs/hpfs_subr.c optional hpfs fs/hpfs/hpfs_vfsops.c optional hpfs fs/hpfs/hpfs_vnops.c optional hpfs fs/msdosfs/msdosfs_conv.c optional msdosfs fs/msdosfs/msdosfs_denode.c optional msdosfs fs/msdosfs/msdosfs_fat.c optional msdosfs fs/msdosfs/msdosfs_fileno.c optional msdosfs fs/msdosfs/msdosfs_iconv.c optional msdosfs_iconv fs/msdosfs/msdosfs_lookup.c optional msdosfs fs/msdosfs/msdosfs_vfsops.c optional msdosfs fs/msdosfs/msdosfs_vnops.c optional msdosfs fs/ntfs/ntfs_compr.c optional ntfs fs/ntfs/ntfs_iconv.c optional ntfs_iconv fs/ntfs/ntfs_ihash.c optional ntfs fs/ntfs/ntfs_subr.c optional ntfs fs/ntfs/ntfs_vfsops.c optional ntfs fs/ntfs/ntfs_vnops.c optional ntfs fs/nullfs/null_subr.c optional nullfs fs/nullfs/null_vfsops.c optional nullfs fs/nullfs/null_vnops.c optional nullfs fs/nwfs/nwfs_io.c optional nwfs fs/nwfs/nwfs_ioctl.c optional nwfs fs/nwfs/nwfs_node.c optional nwfs fs/nwfs/nwfs_subr.c optional nwfs fs/nwfs/nwfs_vfsops.c optional nwfs fs/nwfs/nwfs_vnops.c optional nwfs fs/portalfs/portal_vfsops.c optional portalfs fs/portalfs/portal_vnops.c optional portalfs fs/procfs/procfs.c optional procfs fs/procfs/procfs_ctl.c optional procfs fs/procfs/procfs_dbregs.c optional procfs fs/procfs/procfs_fpregs.c optional procfs fs/procfs/procfs_ioctl.c optional procfs fs/procfs/procfs_map.c optional procfs fs/procfs/procfs_mem.c optional procfs fs/procfs/procfs_note.c optional procfs fs/procfs/procfs_regs.c optional procfs fs/procfs/procfs_rlimit.c optional procfs fs/procfs/procfs_status.c optional procfs fs/procfs/procfs_type.c optional procfs fs/pseudofs/pseudofs.c optional pseudofs fs/pseudofs/pseudofs_fileno.c optional pseudofs fs/pseudofs/pseudofs_vncache.c optional pseudofs fs/pseudofs/pseudofs_vnops.c optional pseudofs fs/smbfs/smbfs_io.c optional smbfs fs/smbfs/smbfs_node.c optional smbfs fs/smbfs/smbfs_smb.c optional smbfs fs/smbfs/smbfs_subr.c optional smbfs fs/smbfs/smbfs_vfsops.c optional smbfs fs/smbfs/smbfs_vnops.c optional smbfs fs/udf/osta.c optional udf fs/udf/udf_iconv.c optional udf_iconv fs/udf/udf_vfsops.c optional udf fs/udf/udf_vnops.c optional udf fs/umapfs/umap_subr.c optional umapfs fs/umapfs/umap_vfsops.c optional umapfs fs/umapfs/umap_vnops.c optional umapfs fs/unionfs/union_subr.c optional unionfs fs/unionfs/union_vfsops.c optional unionfs fs/unionfs/union_vnops.c optional unionfs gdb/gdb_cons.c optional gdb gdb/gdb_main.c optional gdb gdb/gdb_packet.c optional gdb geom/bde/g_bde.c optional geom_bde geom/bde/g_bde_crypt.c optional geom_bde geom/bde/g_bde_lock.c optional geom_bde geom/bde/g_bde_work.c optional geom_bde geom/cache/g_cache.c optional geom_cache geom/concat/g_concat.c optional geom_concat geom/eli/g_eli.c optional geom_eli geom/eli/g_eli_crypto.c optional geom_eli geom/eli/g_eli_ctl.c optional geom_eli geom/eli/g_eli_integrity.c optional geom_eli geom/eli/g_eli_key.c optional geom_eli geom/eli/g_eli_privacy.c optional geom_eli geom/eli/pkcs5v2.c optional geom_eli geom/gate/g_gate.c optional geom_gate geom/geom_aes.c optional geom_aes geom/geom_bsd.c optional geom_bsd geom/geom_bsd_enc.c optional geom_bsd geom/geom_ccd.c optional ccd | geom_ccd geom/geom_ctl.c standard geom/geom_dev.c standard geom/geom_disk.c standard geom/geom_dump.c standard geom/geom_event.c standard geom/geom_fox.c optional geom_fox geom/geom_io.c standard geom/geom_kern.c standard geom/geom_mbr.c optional geom_mbr geom/geom_mbr_enc.c optional geom_mbr geom/geom_pc98.c optional geom_pc98 geom/geom_pc98_enc.c optional geom_pc98 geom/geom_slice.c standard geom/geom_subr.c standard geom/geom_sunlabel.c optional geom_sunlabel geom/geom_sunlabel_enc.c optional geom_sunlabel geom/geom_vfs.c standard geom/geom_vol_ffs.c optional geom_vol geom/journal/g_journal.c optional geom_journal geom/journal/g_journal_ufs.c optional geom_journal geom/label/g_label.c optional geom_label geom/label/g_label_ext2fs.c optional geom_label geom/label/g_label_iso9660.c optional geom_label geom/label/g_label_msdosfs.c optional geom_label geom/label/g_label_ntfs.c optional geom_label geom/label/g_label_reiserfs.c optional geom_label geom/label/g_label_ufs.c optional geom_label geom/mirror/g_mirror.c optional geom_mirror geom/mirror/g_mirror_ctl.c optional geom_mirror geom/multipath/g_multipath.c optional geom_multipath geom/nop/g_nop.c optional geom_nop geom/part/g_part.c standard geom/part/g_part_if.m standard geom/part/g_part_apm.c optional geom_part_apm geom/part/g_part_gpt.c optional geom_part_gpt geom/raid3/g_raid3.c optional geom_raid3 geom/raid3/g_raid3_ctl.c optional geom_raid3 geom/shsec/g_shsec.c optional geom_shsec geom/stripe/g_stripe.c optional geom_stripe geom/uzip/g_uzip.c optional geom_uzip geom/zero/g_zero.c optional geom_zero gnu/fs/ext2fs/ext2_alloc.c optional ext2fs \ warning "kernel contains GPL contaminated ext2fs filesystem" gnu/fs/ext2fs/ext2_balloc.c optional ext2fs gnu/fs/ext2fs/ext2_bmap.c optional ext2fs gnu/fs/ext2fs/ext2_inode.c optional ext2fs gnu/fs/ext2fs/ext2_inode_cnv.c optional ext2fs gnu/fs/ext2fs/ext2_linux_balloc.c optional ext2fs gnu/fs/ext2fs/ext2_linux_ialloc.c optional ext2fs gnu/fs/ext2fs/ext2_lookup.c optional ext2fs gnu/fs/ext2fs/ext2_subr.c optional ext2fs gnu/fs/ext2fs/ext2_vfsops.c optional ext2fs gnu/fs/ext2fs/ext2_vnops.c optional ext2fs gnu/fs/reiserfs/reiserfs_hashes.c optional reiserfs \ warning "kernel contains GPL contaminated ReiserFS filesystem" gnu/fs/reiserfs/reiserfs_inode.c optional reiserfs gnu/fs/reiserfs/reiserfs_item_ops.c optional reiserfs gnu/fs/reiserfs/reiserfs_namei.c optional reiserfs gnu/fs/reiserfs/reiserfs_prints.c optional reiserfs gnu/fs/reiserfs/reiserfs_stree.c optional reiserfs gnu/fs/reiserfs/reiserfs_vfsops.c optional reiserfs gnu/fs/reiserfs/reiserfs_vnops.c optional reiserfs # # isdn4bsd device drivers # i4b/driver/i4b_trace.c optional i4btrc i4b/driver/i4b_rbch.c optional i4brbch i4b/driver/i4b_tel.c optional i4btel i4b/driver/i4b_ipr.c optional i4bipr net/slcompress.c optional i4bipr | i4bisppp i4b/driver/i4b_ctl.c optional i4bctl i4b/driver/i4b_ing.c optional i4bing i4b/driver/i4b_isppp.c optional i4bisppp # # isdn4bsd CAPI driver # i4b/capi/capi_l4if.c optional i4bcapi i4b/capi/capi_llif.c optional i4bcapi i4b/capi/capi_msgs.c optional i4bcapi # # isdn4bsd AVM B1/T1 CAPI driver # i4b/capi/iavc/iavc_pci.c optional iavc i4bcapi pci i4b/capi/iavc/iavc_isa.c optional iavc i4bcapi isa i4b/capi/iavc/iavc_lli.c optional iavc i4bcapi i4b/capi/iavc/iavc_card.c optional iavc i4bcapi # # isdn4bsd support # i4b/layer2/i4b_mbuf.c optional i4btrc # # isdn4bsd Q.921 handler # i4b/layer2/i4b_l2.c optional i4bq921 i4b/layer2/i4b_l2fsm.c optional i4bq921 i4b/layer2/i4b_uframe.c optional i4bq921 i4b/layer2/i4b_tei.c optional i4bq921 i4b/layer2/i4b_sframe.c optional i4bq921 i4b/layer2/i4b_iframe.c optional i4bq921 i4b/layer2/i4b_l2timer.c optional i4bq921 i4b/layer2/i4b_util.c optional i4bq921 i4b/layer2/i4b_lme.c optional i4bq921 # # isdn4bsd Q.931 handler # i4b/layer3/i4b_q931.c optional i4bq931 i4b/layer3/i4b_l3fsm.c optional i4bq931 i4b/layer3/i4b_l3timer.c optional i4bq931 i4b/layer3/i4b_l2if.c optional i4bq931 i4b/layer3/i4b_l4if.c optional i4bq931 i4b/layer3/i4b_q932fac.c optional i4bq931 # # isdn4bsd control device driver, interface to isdnd # i4b/layer4/i4b_i4bdrv.c optional i4b i4b/layer4/i4b_l4.c optional i4b i4b/layer4/i4b_l4mgmt.c optional i4b i4b/layer4/i4b_l4timer.c optional i4b # isa/isa_if.m standard isa/isa_common.c optional isa isa/isahint.c optional isa isa/orm.c optional isa isa/pnp.c optional isa isapnp isa/pnpparse.c optional isa isapnp fs/cd9660/cd9660_bmap.c optional cd9660 fs/cd9660/cd9660_lookup.c optional cd9660 fs/cd9660/cd9660_node.c optional cd9660 fs/cd9660/cd9660_rrip.c optional cd9660 fs/cd9660/cd9660_util.c optional cd9660 fs/cd9660/cd9660_vfsops.c optional cd9660 fs/cd9660/cd9660_vnops.c optional cd9660 fs/cd9660/cd9660_iconv.c optional cd9660_iconv kern/bus_if.m standard kern/clock_if.m optional genclock kern/cpufreq_if.m standard kern/device_if.m standard kern/imgact_elf.c standard kern/imgact_shell.c standard kern/inflate.c optional gzip kern/init_main.c standard kern/init_sysent.c standard kern/ksched.c optional _kposix_priority_scheduling kern/kern_acct.c standard kern/kern_alq.c optional alq kern/kern_clock.c standard kern/kern_condvar.c standard kern/kern_conf.c standard kern/kern_cpu.c standard kern/kern_context.c standard kern/kern_descrip.c standard kern/kern_environment.c standard kern/kern_event.c standard kern/kern_exec.c standard kern/kern_exit.c standard kern/kern_fork.c standard kern/kern_idle.c standard kern/kern_intr.c standard kern/kern_jail.c standard kern/kern_kse.c standard kern/kern_kthread.c standard kern/kern_ktr.c optional ktr kern/kern_ktrace.c standard kern/kern_linker.c standard kern/kern_lock.c standard kern/kern_lockf.c standard kern/kern_malloc.c standard kern/kern_mbuf.c standard kern/kern_mib.c standard kern/kern_module.c standard kern/kern_mtxpool.c standard kern/kern_mutex.c standard kern/kern_ntptime.c standard kern/kern_physio.c standard kern/kern_pmc.c standard kern/kern_poll.c optional device_polling kern/kern_priv.c standard kern/kern_proc.c standard kern/kern_prot.c standard kern/kern_resource.c standard kern/kern_rwlock.c standard kern/kern_sema.c standard kern/kern_shutdown.c standard kern/kern_sig.c standard kern/kern_subr.c standard kern/kern_sx.c standard kern/kern_synch.c standard kern/kern_syscalls.c standard kern/kern_sysctl.c standard kern/kern_tc.c standard kern/kern_thr.c standard kern/kern_thread.c standard kern/kern_time.c standard kern/kern_timeout.c standard kern/kern_umtx.c standard kern/kern_uuid.c standard kern/kern_xxx.c standard kern/link_elf.c standard kern/linker_if.m standard kern/md4c.c optional netsmb kern/md5c.c standard kern/p1003_1b.c standard kern/posix4_mib.c standard kern/sched_4bsd.c optional sched_4bsd kern/sched_core.c optional sched_core kern/sched_ule.c optional sched_ule kern/serdev_if.m standard kern/subr_acl_posix1e.c standard kern/subr_autoconf.c standard kern/subr_blist.c standard kern/subr_bus.c standard kern/subr_clock.c standard kern/subr_devstat.c standard kern/subr_disk.c standard kern/subr_eventhandler.c standard kern/subr_fattime.c standard kern/subr_firmware.c optional firmware kern/subr_hints.c standard kern/subr_kdb.c standard kern/subr_kobj.c standard kern/subr_lock.c standard kern/subr_log.c standard kern/subr_mbpool.c optional libmbpool kern/subr_mchain.c optional libmchain kern/subr_module.c standard kern/subr_msgbuf.c standard kern/subr_param.c standard kern/subr_pcpu.c standard kern/subr_power.c standard kern/subr_prf.c standard kern/subr_prof.c standard kern/subr_rman.c standard kern/subr_rtc.c optional genclock kern/subr_sbuf.c standard kern/subr_scanf.c standard kern/subr_sleepqueue.c standard kern/subr_smp.c standard kern/subr_stack.c optional ddb kern/subr_taskqueue.c standard kern/subr_trap.c standard kern/subr_turnstile.c standard kern/subr_unit.c standard kern/subr_witness.c optional witness kern/sys_generic.c standard kern/sys_pipe.c standard kern/sys_process.c standard kern/sys_socket.c standard kern/syscalls.c optional witness | invariants kern/sysv_ipc.c standard kern/sysv_msg.c optional sysvmsg kern/sysv_sem.c optional sysvsem kern/sysv_shm.c optional sysvshm kern/tty.c standard kern/tty_compat.c optional compat_43tty kern/tty_conf.c standard kern/tty_cons.c standard kern/tty_pty.c optional pty kern/tty_pts.c optional pty kern/tty_subr.c standard kern/tty_tty.c standard kern/uipc_accf.c optional inet kern/uipc_cow.c optional zero_copy_sockets kern/uipc_debug.c optional ddb kern/uipc_domain.c standard kern/uipc_mbuf.c standard kern/uipc_mbuf2.c standard kern/uipc_mqueue.c optional p1003_1b_mqueue kern/uipc_sem.c optional p1003_1b_semaphores kern/uipc_sockbuf.c standard kern/uipc_socket.c standard kern/uipc_syscalls.c standard kern/uipc_usrreq.c standard kern/vfs_acl.c standard kern/vfs_aio.c optional vfs_aio kern/vfs_bio.c standard kern/vfs_cache.c standard kern/vfs_cluster.c standard kern/vfs_default.c standard kern/vfs_export.c standard kern/vfs_extattr.c standard kern/vfs_hash.c standard kern/vfs_init.c standard kern/vfs_lookup.c standard kern/vfs_mount.c standard kern/vfs_subr.c standard kern/vfs_syscalls.c standard kern/vfs_vnops.c standard # # These files in libkern/ are those needed by all architectures. Some # of the files in libkern/ are only needed on some architectures, e.g., # libkern/divdi3.c is needed by i386 but not alpha. Also, some of these # routines may be optimized for a particular platform. In either case, # the file should be moved to conf/files. from here. # libkern/arc4random.c standard libkern/bcd.c standard libkern/bsearch.c standard libkern/crc32.c standard libkern/fnmatch.c standard libkern/gets.c standard libkern/iconv.c optional libiconv libkern/iconv_converter_if.m optional libiconv libkern/iconv_xlat.c optional libiconv libkern/iconv_xlat16.c optional libiconv libkern/index.c standard libkern/inet_ntoa.c standard libkern/mcount.c optional profiling-routine libkern/qsort.c standard libkern/qsort_r.c standard libkern/random.c standard libkern/rindex.c standard libkern/scanc.c standard libkern/skpc.c standard libkern/strcasecmp.c standard libkern/strcat.c standard libkern/strcmp.c standard libkern/strcpy.c standard libkern/strdup.c standard libkern/strlcat.c standard libkern/strlcpy.c standard libkern/strlen.c standard libkern/strncmp.c standard libkern/strncpy.c standard libkern/strsep.c standard libkern/strspn.c standard libkern/strstr.c standard libkern/strtol.c standard libkern/strtoq.c standard libkern/strtoul.c standard libkern/strtouq.c standard libkern/strvalid.c standard net/bpf.c standard net/bpf_jitter.c optional bpf_jitter net/bpf_filter.c optional bpf | netgraph_bpf net/bridgestp.c optional if_bridge net/bsd_comp.c optional ppp_bsdcomp net/ieee8023ad_lacp.c optional lagg net/if.c standard net/if_arcsubr.c optional arcnet net/if_atmsubr.c optional atm net/if_bridge.c optional if_bridge net/if_clone.c standard net/if_disc.c optional disc net/if_edsc.c optional edsc net/if_ef.c optional ef net/if_enc.c optional enc net/if_ethersubr.c optional ether net/if_faith.c optional faith net/if_fddisubr.c optional fddi net/if_fwsubr.c optional fwip net/if_gif.c optional gif net/if_gre.c optional gre net/if_iso88025subr.c optional token net/if_lagg.c optional lagg net/if_loop.c optional loop net/if_media.c standard net/if_mib.c standard net/if_ppp.c optional ppp net/if_sl.c optional sl net/if_spppfr.c optional i4bisppp | sppp | netgraph_sppp net/if_spppsubr.c optional i4bisppp | sppp | netgraph_sppp net/if_stf.c optional stf net/if_tun.c optional tun net/if_tap.c optional tap net/if_vlan.c optional vlan net/mppcc.c optional netgraph_mppc_compression net/mppcd.c optional netgraph_mppc_compression net/netisr.c standard net/ppp_deflate.c optional ppp_deflate net/ppp_tty.c optional ppp net/pfil.c optional ether | inet net/radix.c standard net/raw_cb.c standard net/raw_usrreq.c standard net/route.c standard net/rtsock.c standard net/slcompress.c optional netgraph_vjc | ppp | sl | sppp | \ netgraph_sppp net/zlib.c optional crypto | geom_uzip | ipsec | \ ppp_deflate | netgraph_deflate net80211/ieee80211.c optional wlan net80211/ieee80211_acl.c optional wlan_acl net80211/ieee80211_amrr.c optional wlan_amrr net80211/ieee80211_crypto.c optional wlan net80211/ieee80211_crypto_ccmp.c optional wlan_ccmp net80211/ieee80211_crypto_none.c optional wlan net80211/ieee80211_crypto_tkip.c optional wlan_tkip net80211/ieee80211_crypto_wep.c optional wlan_wep net80211/ieee80211_freebsd.c optional wlan net80211/ieee80211_input.c optional wlan net80211/ieee80211_ioctl.c optional wlan net80211/ieee80211_node.c optional wlan net80211/ieee80211_output.c optional wlan net80211/ieee80211_proto.c optional wlan net80211/ieee80211_xauth.c optional wlan_xauth netatalk/aarp.c optional netatalk netatalk/at_control.c optional netatalk netatalk/at_proto.c optional netatalk netatalk/at_rmx.c optional netatalkdebug netatalk/ddp_input.c optional netatalk netatalk/ddp_output.c optional netatalk netatalk/ddp_pcb.c optional netatalk netatalk/ddp_usrreq.c optional netatalk netatm/atm_aal5.c optional atm_core netatm/atm_cm.c optional atm_core netatm/atm_device.c optional atm_core netatm/atm_if.c optional atm_core netatm/atm_proto.c optional atm_core netatm/atm_signal.c optional atm_core netatm/atm_socket.c optional atm_core netatm/atm_subr.c optional atm_core netatm/atm_usrreq.c optional atm_core netatm/ipatm/ipatm_event.c optional atm_ip atm_core netatm/ipatm/ipatm_if.c optional atm_ip atm_core netatm/ipatm/ipatm_input.c optional atm_ip atm_core netatm/ipatm/ipatm_load.c optional atm_ip atm_core netatm/ipatm/ipatm_output.c optional atm_ip atm_core netatm/ipatm/ipatm_usrreq.c optional atm_ip atm_core netatm/ipatm/ipatm_vcm.c optional atm_ip atm_core netatm/sigpvc/sigpvc_if.c optional atm_sigpvc atm_core netatm/sigpvc/sigpvc_subr.c optional atm_sigpvc atm_core netatm/spans/spans_arp.c optional atm_spans atm_core \ dependency "spans_xdr.h" netatm/spans/spans_cls.c optional atm_spans atm_core netatm/spans/spans_if.c optional atm_spans atm_core netatm/spans/spans_kxdr.c optional atm_spans atm_core netatm/spans/spans_msg.c optional atm_spans atm_core netatm/spans/spans_print.c optional atm_spans atm_core netatm/spans/spans_proto.c optional atm_spans atm_core netatm/spans/spans_subr.c optional atm_spans atm_core netatm/spans/spans_util.c optional atm_spans atm_core spans_xdr.h optional atm_spans atm_core \ before-depend \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "rpcgen -h -C $S/netatm/spans/spans_xdr.x | grep -v rpc/rpc.h > spans_xdr.h" \ clean "spans_xdr.h" \ no-obj no-implicit-rule spans_xdr.c optional atm_spans atm_core \ before-depend \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "rpcgen -c -C $S/netatm/spans/spans_xdr.x | grep -v rpc/rpc.h > spans_xdr.c" \ clean "spans_xdr.c" \ no-obj no-implicit-rule local spans_xdr.o optional atm_spans atm_core \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "${NORMAL_C}" \ no-implicit-rule local netatm/uni/q2110_sigaa.c optional atm_uni atm_core netatm/uni/q2110_sigcpcs.c optional atm_uni atm_core netatm/uni/q2110_subr.c optional atm_uni atm_core netatm/uni/qsaal1_sigaa.c optional atm_uni atm_core netatm/uni/qsaal1_sigcpcs.c optional atm_uni atm_core netatm/uni/qsaal1_subr.c optional atm_uni atm_core netatm/uni/sscf_uni.c optional atm_uni atm_core netatm/uni/sscf_uni_lower.c optional atm_uni atm_core netatm/uni/sscf_uni_upper.c optional atm_uni atm_core netatm/uni/sscop.c optional atm_uni atm_core netatm/uni/sscop_lower.c optional atm_uni atm_core netatm/uni/sscop_pdu.c optional atm_uni atm_core netatm/uni/sscop_sigaa.c optional atm_uni atm_core netatm/uni/sscop_sigcpcs.c optional atm_uni atm_core netatm/uni/sscop_subr.c optional atm_uni atm_core netatm/uni/sscop_timer.c optional atm_uni atm_core netatm/uni/sscop_upper.c optional atm_uni atm_core netatm/uni/uni_load.c optional atm_uni atm_core netatm/uni/uniarp.c optional atm_uni atm_core netatm/uni/uniarp_cache.c optional atm_uni atm_core netatm/uni/uniarp_input.c optional atm_uni atm_core netatm/uni/uniarp_output.c optional atm_uni atm_core netatm/uni/uniarp_timer.c optional atm_uni atm_core netatm/uni/uniarp_vcm.c optional atm_uni atm_core netatm/uni/uniip.c optional atm_uni atm_core netatm/uni/unisig_decode.c optional atm_uni atm_core netatm/uni/unisig_encode.c optional atm_uni atm_core netatm/uni/unisig_if.c optional atm_uni atm_core netatm/uni/unisig_mbuf.c optional atm_uni atm_core netatm/uni/unisig_msg.c optional atm_uni atm_core netatm/uni/unisig_print.c optional atm_uni atm_core netatm/uni/unisig_proto.c optional atm_uni atm_core netatm/uni/unisig_sigmgr_state.c optional atm_uni atm_core netatm/uni/unisig_subr.c optional atm_uni atm_core netatm/uni/unisig_util.c optional atm_uni atm_core netatm/uni/unisig_vc_state.c optional atm_uni atm_core netgraph/atm/atmpif/ng_atmpif.c optional netgraph_atm_atmpif netgraph/atm/atmpif/ng_atmpif_harp.c optional netgraph_atm_atmpif netgraph/atm/ccatm/ng_ccatm.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" netgraph/atm/ng_atm.c optional ngatm_atm netgraph/atm/ngatmbase.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" netgraph/atm/sscfu/ng_sscfu.c optional ngatm_sscfu \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" netgraph/atm/sscop/ng_sscop.c optional ngatm_sscop \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" netgraph/atm/uni/ng_uni.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" netgraph/bluetooth/common/ng_bluetooth.c optional netgraph_bluetooth netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c optional netgraph_bluetooth_bt3c netgraph/bluetooth/drivers/h4/ng_h4.c optional netgraph_bluetooth_h4 netgraph/bluetooth/drivers/ubt/ng_ubt.c optional netgraph_bluetooth_ubt netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c optional netgraph_bluetooth_ubtbcmfw netgraph/bluetooth/hci/ng_hci_cmds.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_evnt.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_main.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_misc.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_ulpi.c optional netgraph_bluetooth_hci netgraph/bluetooth/l2cap/ng_l2cap_cmds.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_evnt.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_llpi.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_main.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_misc.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_ulpi.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/socket/ng_btsocket.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_hci_raw.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_l2cap.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_l2cap_raw.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_rfcomm.c optional netgraph_bluetooth_socket netgraph/netflow/netflow.c optional netgraph_netflow netgraph/netflow/ng_netflow.c optional netgraph_netflow netgraph/ng_UI.c optional netgraph_UI netgraph/ng_async.c optional netgraph_async netgraph/ng_atmllc.c optional netgraph_atmllc netgraph/ng_base.c optional netgraph netgraph/ng_bpf.c optional netgraph_bpf netgraph/ng_bridge.c optional netgraph_bridge netgraph/ng_car.c optional netgraph_car netgraph/ng_cisco.c optional netgraph_cisco netgraph/ng_deflate.c optional netgraph_deflate netgraph/ng_device.c optional netgraph_device netgraph/ng_echo.c optional netgraph_echo netgraph/ng_eiface.c optional netgraph_eiface netgraph/ng_ether.c optional netgraph_ether netgraph/ng_fec.c optional netgraph_fec netgraph/ng_frame_relay.c optional netgraph_frame_relay netgraph/ng_gif.c optional netgraph_gif netgraph/ng_gif_demux.c optional netgraph_gif_demux netgraph/ng_hole.c optional netgraph_hole netgraph/ng_iface.c optional netgraph_iface netgraph/ng_ip_input.c optional netgraph_ip_input netgraph/ng_ipfw.c optional netgraph_ipfw netgraph/ng_ksocket.c optional netgraph_ksocket netgraph/ng_l2tp.c optional netgraph_l2tp netgraph/ng_lmi.c optional netgraph_lmi netgraph/ng_mppc.c optional netgraph_mppc_compression | \ netgraph_mppc_encryption netgraph/ng_nat.c optional netgraph_nat netgraph/ng_one2many.c optional netgraph_one2many netgraph/ng_parse.c optional netgraph netgraph/ng_ppp.c optional netgraph_ppp netgraph/ng_pppoe.c optional netgraph_pppoe netgraph/ng_pptpgre.c optional netgraph_pptpgre netgraph/ng_pred1.c optional netgraph_pred1 netgraph/ng_rfc1490.c optional netgraph_rfc1490 netgraph/ng_socket.c optional netgraph_socket netgraph/ng_split.c optional netgraph_split netgraph/ng_sppp.c optional netgraph_sppp netgraph/ng_tag.c optional netgraph_tag netgraph/ng_tcpmss.c optional netgraph_tcpmss netgraph/ng_tee.c optional netgraph_tee netgraph/ng_tty.c optional netgraph_tty netgraph/ng_vjc.c optional netgraph_vjc netinet/accf_data.c optional accept_filter_data netinet/accf_http.c optional accept_filter_http netinet/if_atm.c optional atm netinet/if_ether.c optional ether netinet/igmp.c optional inet netinet/in.c optional inet netinet/ip_carp.c optional carp netinet/in_gif.c optional gif inet netinet/ip_gre.c optional gre inet netinet/ip_id.c optional inet netinet/in_pcb.c optional inet netinet/in_proto.c optional inet \ compile-with "${NORMAL_C} -I$S/contrib/pf" netinet/in_rmx.c optional inet netinet/ip_divert.c optional ipdivert netinet/ip_dummynet.c optional dummynet netinet/ip_ecn.c optional inet | inet6 netinet/ip_encap.c optional inet | inet6 netinet/ip_fastfwd.c optional inet netinet/ip_fw2.c optional ipfirewall netinet/ip_fw_pfil.c optional ipfirewall netinet/ip_icmp.c optional inet netinet/ip_input.c optional inet netinet/ip_ipsec.c optional ipsec netinet/ip_ipsec.c optional fast_ipsec netinet/ip_mroute.c optional mrouting inet | mrouting inet6 netinet/ip_options.c optional inet netinet/ip_output.c optional inet netinet/raw_ip.c optional inet netinet/sctp_asconf.c optional inet inet6 sctp netinet/sctp_auth.c optional inet inet6 sctp netinet/sctp_bsd_addr.c optional inet inet6 sctp netinet/sctp_crc32.c optional inet inet6 sctp netinet/sctp_indata.c optional inet inet6 sctp netinet/sctp_input.c optional inet inet6 sctp netinet/sctp_output.c optional inet inet6 sctp netinet/sctp_pcb.c optional inet inet6 sctp netinet/sctp_peeloff.c optional inet inet6 sctp netinet/sctp_sysctl.c optional inet inet6 sctp netinet/sctp_timer.c optional inet inet6 sctp netinet/sctp_usrreq.c optional inet inet6 sctp netinet/sctputil.c optional inet inet6 sctp netinet/tcp_debug.c optional tcpdebug netinet/tcp_hostcache.c optional inet netinet/tcp_input.c optional inet netinet/tcp_output.c optional inet netinet/tcp_reass.c optional inet netinet/tcp_sack.c optional inet netinet/tcp_subr.c optional inet netinet/tcp_syncache.c optional inet netinet/tcp_timer.c optional inet netinet/tcp_timewait.c optional inet netinet/tcp_usrreq.c optional inet netinet/udp_usrreq.c optional inet netinet/libalias/alias.c optional libalias | netgraph_nat netinet/libalias/alias_db.c optional libalias | netgraph_nat netinet/libalias/alias_mod.c optional libalias | netgraph_nat netinet/libalias/alias_proxy.c optional libalias | netgraph_nat netinet/libalias/alias_util.c optional libalias | netgraph_nat netinet6/ah_aesxcbcmac.c optional ipsec netinet6/ah_core.c optional ipsec netinet6/ah_input.c optional ipsec netinet6/ah_output.c optional ipsec netinet6/dest6.c optional inet6 netinet6/esp_aesctr.c optional ipsec ipsec_esp netinet6/esp_core.c optional ipsec ipsec_esp netinet6/esp_input.c optional ipsec ipsec_esp netinet6/esp_output.c optional ipsec ipsec_esp netinet6/esp_rijndael.c optional ipsec ipsec_esp netinet6/esp_camellia.c optional ipsec ipsec_esp netinet6/frag6.c optional inet6 netinet6/icmp6.c optional inet6 netinet6/in6.c optional inet6 netinet6/in6_cksum.c optional inet6 netinet6/in6_gif.c optional gif inet6 netinet6/in6_ifattach.c optional inet6 netinet6/in6_pcb.c optional inet6 netinet6/in6_proto.c optional inet6 netinet6/in6_rmx.c optional inet6 netinet6/in6_src.c optional inet6 netinet6/ip6_forward.c optional inet6 netinet6/ip6_id.c optional inet6 netinet6/ip6_input.c optional inet6 netinet6/ip6_mroute.c optional mrouting inet6 netinet6/ip6_output.c optional inet6 netinet6/ipcomp_core.c optional ipsec netinet6/ipcomp_input.c optional ipsec netinet6/ipcomp_output.c optional ipsec netinet6/ipsec.c optional ipsec netinet6/mld6.c optional inet6 netinet6/nd6.c optional inet6 netinet6/nd6_nbr.c optional inet6 netinet6/nd6_rtr.c optional inet6 netinet6/raw_ip6.c optional inet6 netinet6/route6.c optional inet6 netinet6/scope6.c optional inet6 netinet6/sctp6_usrreq.c optional inet6 sctp netinet6/udp6_output.c optional inet6 netinet6/udp6_usrreq.c optional inet6 netipsec/ipsec.c optional fast_ipsec netipsec/ipsec_input.c optional fast_ipsec netipsec/ipsec_mbuf.c optional fast_ipsec netipsec/ipsec_output.c optional fast_ipsec netipsec/key.c optional fast_ipsec netipsec/key_debug.c optional fast_ipsec netipsec/keysock.c optional fast_ipsec netipsec/xform_ah.c optional fast_ipsec netipsec/xform_esp.c optional fast_ipsec netipsec/xform_ipcomp.c optional fast_ipsec netipsec/xform_ipip.c optional fast_ipsec netipsec/xform_tcp.c optional fast_ipsec tcp_signature netipx/ipx.c optional ipx netipx/ipx_cksum.c optional ipx netipx/ipx_input.c optional ipx netipx/ipx_ip.c optional ipx ipxip netipx/ipx_outputfl.c optional ipx netipx/ipx_pcb.c optional ipx netipx/ipx_proto.c optional ipx netipx/ipx_usrreq.c optional ipx netipx/spx_debug.c optional ipx netipx/spx_usrreq.c optional ipx netkey/key.c optional ipsec netkey/key_debug.c optional ipsec netkey/keydb.c optional ipsec netkey/keysock.c optional ipsec netnatm/natm.c optional natm netnatm/natm_pcb.c optional natm netnatm/natm_proto.c optional natm netncp/ncp_conn.c optional ncp netncp/ncp_crypt.c optional ncp netncp/ncp_login.c optional ncp netncp/ncp_mod.c optional ncp netncp/ncp_ncp.c optional ncp netncp/ncp_nls.c optional ncp netncp/ncp_rq.c optional ncp netncp/ncp_sock.c optional ncp netncp/ncp_subr.c optional ncp netsmb/smb_conn.c optional netsmb netsmb/smb_crypt.c optional netsmb netsmb/smb_dev.c optional netsmb netsmb/smb_iod.c optional netsmb netsmb/smb_rq.c optional netsmb netsmb/smb_smb.c optional netsmb netsmb/smb_subr.c optional netsmb netsmb/smb_trantcp.c optional netsmb netsmb/smb_usr.c optional netsmb nfs/nfs_common.c optional nfsclient | nfsserver nfs4client/nfs4_dev.c optional nfsclient nfs4client/nfs4_idmap.c optional nfsclient nfs4client/nfs4_socket.c optional nfsclient nfs4client/nfs4_subs.c optional nfsclient nfs4client/nfs4_vfs_subs.c optional nfsclient nfs4client/nfs4_vfsops.c optional nfsclient nfs4client/nfs4_vn_subs.c optional nfsclient nfs4client/nfs4_vnops.c optional nfsclient nfsclient/bootp_subr.c optional bootp nfsclient nfsclient/krpc_subr.c optional bootp nfsclient nfsclient/nfs_bio.c optional nfsclient nfsclient/nfs_diskless.c optional nfsclient nfs_root nfsclient/nfs_node.c optional nfsclient nfsclient/nfs_socket.c optional nfsclient nfsclient/nfs_subs.c optional nfsclient nfsclient/nfs_nfsiod.c optional nfsclient nfsclient/nfs_vfsops.c optional nfsclient nfsclient/nfs_vnops.c optional nfsclient nfsclient/nfs_lock.c optional nfsclient nfsserver/nfs_serv.c optional nfsserver nfsserver/nfs_srvsock.c optional nfsserver nfsserver/nfs_srvcache.c optional nfsserver nfsserver/nfs_srvsubs.c optional nfsserver nfsserver/nfs_syscalls.c optional nfsserver # crypto support opencrypto/cast.c optional crypto | ipsec ipsec_esp opencrypto/criov.c optional crypto opencrypto/crypto.c optional crypto opencrypto/cryptodev.c optional cryptodev opencrypto/cryptodev_if.m optional crypto opencrypto/cryptosoft.c optional crypto opencrypto/deflate.c optional crypto opencrypto/rmd160.c optional crypto | ipsec opencrypto/skipjack.c optional crypto opencrypto/xform.c optional crypto pci/agp.c optional agp pci pci/agp_if.m optional agp pci pci/alpm.c optional alpm pci pci/amdpm.c optional amdpm pci | nfpm pci pci/amdsmb.c optional amdsmb pci pci/if_mn.c optional mn pci pci/if_pcn.c optional pcn pci pci/if_rl.c optional rl pci pci/if_sf.c optional sf pci pci/if_sis.c optional sis pci pci/if_ste.c optional ste pci pci/if_tl.c optional tl pci pci/if_vr.c optional vr pci pci/if_wb.c optional wb pci pci/if_xl.c optional xl pci pci/intpm.c optional intpm pci pci/ncr.c optional ncr pci pci/nfsmb.c optional nfsmb pci pci/viapm.c optional viapm pci pci/xrpu.c optional xrpu pci rpc/rpcclnt.c optional nfsclient security/audit/audit.c optional audit security/audit/audit_arg.c optional audit security/audit/audit_bsm.c optional audit security/audit/audit_bsm_klib.c optional audit security/audit/audit_bsm_token.c optional audit security/audit/audit_pipe.c optional audit security/audit/audit_syscalls.c standard security/audit/audit_trigger.c optional audit security/audit/audit_worker.c optional audit security/mac/mac_audit.c optional mac audit security/mac/mac_framework.c optional mac security/mac/mac_inet.c optional mac inet security/mac/mac_label.c optional mac security/mac/mac_net.c optional mac security/mac/mac_pipe.c optional mac security/mac/mac_posix_sem.c optional mac security/mac/mac_priv.c optional mac security/mac/mac_process.c optional mac security/mac/mac_socket.c optional mac security/mac/mac_syscalls.c standard security/mac/mac_system.c optional mac security/mac/mac_sysv_msg.c optional mac security/mac/mac_sysv_sem.c optional mac security/mac/mac_sysv_shm.c optional mac security/mac/mac_vfs.c optional mac security/mac_biba/mac_biba.c optional mac_biba security/mac_bsdextended/mac_bsdextended.c optional mac_bsdextended security/mac_ifoff/mac_ifoff.c optional mac_ifoff security/mac_lomac/mac_lomac.c optional mac_lomac security/mac_mls/mac_mls.c optional mac_mls security/mac_none/mac_none.c optional mac_none security/mac_partition/mac_partition.c optional mac_partition security/mac_portacl/mac_portacl.c optional mac_portacl security/mac_seeotheruids/mac_seeotheruids.c optional mac_seeotheruids security/mac_stub/mac_stub.c optional mac_stub security/mac_test/mac_test.c optional mac_test ufs/ffs/ffs_alloc.c optional ffs ufs/ffs/ffs_balloc.c optional ffs ufs/ffs/ffs_inode.c optional ffs ufs/ffs/ffs_snapshot.c optional ffs ufs/ffs/ffs_softdep.c optional ffs ufs/ffs/ffs_subr.c optional ffs ufs/ffs/ffs_tables.c optional ffs ufs/ffs/ffs_vfsops.c optional ffs ufs/ffs/ffs_vnops.c optional ffs ufs/ffs/ffs_rawread.c optional directio ufs/ufs/ufs_acl.c optional ffs ufs/ufs/ufs_bmap.c optional ffs ufs/ufs/ufs_dirhash.c optional ffs ufs/ufs/ufs_extattr.c optional ffs ufs/ufs/ufs_gjournal.c optional ffs ufs/ufs/ufs_inode.c optional ffs ufs/ufs/ufs_lookup.c optional ffs ufs/ufs/ufs_quota.c optional ffs ufs/ufs/ufs_vfsops.c optional ffs ufs/ufs/ufs_vnops.c optional ffs vm/default_pager.c standard vm/device_pager.c standard vm/phys_pager.c standard vm/redzone.c optional DEBUG_REDZONE vm/swap_pager.c standard vm/uma_core.c standard vm/uma_dbg.c standard vm/vm_contig.c standard vm/memguard.c optional DEBUG_MEMGUARD vm/vm_fault.c standard vm/vm_glue.c standard vm/vm_init.c standard vm/vm_kern.c standard vm/vm_map.c standard vm/vm_meter.c standard vm/vm_mmap.c standard vm/vm_object.c standard vm/vm_page.c standard vm/vm_pageout.c standard vm/vm_pageq.c standard vm/vm_pager.c standard vm/vm_unix.c standard vm/vm_zeroidle.c standard vm/vnode_pager.c standard # gnu/fs/xfs/xfs_alloc.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" \ warning "kernel contains GPL contaminated xfs filesystem" gnu/fs/xfs/xfs_alloc_btree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_bit.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_bmap.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_bmap_btree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_btree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_buf_item.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_da_btree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_block.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_data.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_leaf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_node.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_sf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_trace.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir_leaf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_error.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_extfree_item.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_fsops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_ialloc.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_ialloc_btree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_inode.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_inode_item.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_iocore.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_itable.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dfrag.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_log.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_log_recover.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_mount.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_rename.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans_ail.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans_buf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans_extfree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans_inode.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans_item.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_utils.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_vfsops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_vnodeops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_rw.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_attr_leaf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_attr.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dmops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_qmops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_iget.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_freebsd_iget.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_mountops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_vnops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_frw.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_buf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_globals.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_dmistubs.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_super.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_stats.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_vfs.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_vnode.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_sysctl.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_fs_subr.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_ioctl.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/support/debug.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/support/ktrace.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/support/mrlock.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/support/uuid.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/support/kmem.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_iomap.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_behavior.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" Index: head/sys/dev/sound/pcm/buffer.c =================================================================== --- head/sys/dev/sound/pcm/buffer.c (revision 170160) +++ head/sys/dev/sound/pcm/buffer.c (revision 170161) @@ -1,751 +1,751 @@ /*- * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD$"); struct snd_dbuf * sndbuf_create(device_t dev, char *drv, char *desc, struct pcm_channel *channel) { struct snd_dbuf *b; b = malloc(sizeof(*b), M_DEVBUF, M_WAITOK | M_ZERO); snprintf(b->name, SNDBUF_NAMELEN, "%s:%s", drv, desc); b->dev = dev; b->channel = channel; return b; } void sndbuf_destroy(struct snd_dbuf *b) { sndbuf_free(b); free(b, M_DEVBUF); } bus_addr_t sndbuf_getbufaddr(struct snd_dbuf *buf) { return (buf->buf_addr); } static void sndbuf_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct snd_dbuf *b = (struct snd_dbuf *)arg; if (bootverbose) { device_printf(b->dev, "sndbuf_setmap %lx, %lx; ", (u_long)segs[0].ds_addr, (u_long)segs[0].ds_len); printf("%p -> %lx\n", b->buf, (u_long)segs[0].ds_addr); } if (error == 0) b->buf_addr = segs[0].ds_addr; else b->buf_addr = 0; } /* * Allocate memory for DMA buffer. If the device does not use DMA transfers, * the driver can call malloc(9) and sndbuf_setup() itself. */ int sndbuf_alloc(struct snd_dbuf *b, bus_dma_tag_t dmatag, int dmaflags, unsigned int size) { int ret; b->dmatag = dmatag; b->dmaflags = dmaflags | BUS_DMA_NOWAIT; b->maxsize = size; b->bufsize = b->maxsize; b->buf_addr = 0; b->flags |= SNDBUF_F_MANAGED; if (bus_dmamem_alloc(b->dmatag, (void **)&b->buf, b->dmaflags, &b->dmamap)) { sndbuf_free(b); return (ENOMEM); } if (bus_dmamap_load(b->dmatag, b->dmamap, b->buf, b->maxsize, sndbuf_setmap, b, 0) != 0 || b->buf_addr == 0) { sndbuf_free(b); return (ENOMEM); } ret = sndbuf_resize(b, 2, b->maxsize / 2); if (ret != 0) sndbuf_free(b); return (ret); } int sndbuf_setup(struct snd_dbuf *b, void *buf, unsigned int size) { b->flags &= ~SNDBUF_F_MANAGED; if (buf) b->flags |= SNDBUF_F_MANAGED; b->buf = buf; b->maxsize = size; b->bufsize = b->maxsize; return sndbuf_resize(b, 2, b->maxsize / 2); } void sndbuf_free(struct snd_dbuf *b) { if (b->tmpbuf) free(b->tmpbuf, M_DEVBUF); if (b->shadbuf) free(b->shadbuf, M_DEVBUF); if (b->buf) { if (b->flags & SNDBUF_F_MANAGED) { if (b->dmamap) bus_dmamap_unload(b->dmatag, b->dmamap); if (b->dmatag) bus_dmamem_free(b->dmatag, b->buf, b->dmamap); } else free(b->buf, M_DEVBUF); } b->tmpbuf = NULL; b->shadbuf = NULL; b->buf = NULL; b->sl = 0; b->dmatag = NULL; b->dmamap = NULL; } int sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) { u_int8_t *tmpbuf, *f2; chn_lock(b->channel); if (b->maxsize == 0) goto out; if (blkcnt == 0) blkcnt = b->blkcnt; if (blksz == 0) blksz = b->blksz; if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz > b->maxsize)) { chn_unlock(b->channel); return EINVAL; } if (blkcnt == b->blkcnt && blksz == b->blksz) goto out; chn_unlock(b->channel); tmpbuf = malloc(blkcnt * blksz, M_DEVBUF, M_NOWAIT); if (tmpbuf == NULL) return ENOMEM; chn_lock(b->channel); b->blkcnt = blkcnt; b->blksz = blksz; b->bufsize = blkcnt * blksz; f2 = b->tmpbuf; b->tmpbuf = tmpbuf; sndbuf_reset(b); chn_unlock(b->channel); free(f2, M_DEVBUF); return 0; out: chn_unlock(b->channel); return 0; } int sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) { u_int8_t *buf, *tmpbuf, *f1, *f2; u_int8_t *shadbuf, *f3; unsigned int bufsize; int ret; if (blkcnt < 2 || blksz < 16) return EINVAL; bufsize = blksz * blkcnt; chn_unlock(b->channel); buf = malloc(bufsize, M_DEVBUF, M_WAITOK); tmpbuf = malloc(bufsize, M_DEVBUF, M_WAITOK); shadbuf = malloc(bufsize, M_DEVBUF, M_WAITOK); chn_lock(b->channel); b->blkcnt = blkcnt; b->blksz = blksz; b->bufsize = bufsize; b->maxsize = bufsize; f1 = b->buf; f2 = b->tmpbuf; b->buf = buf; b->tmpbuf = tmpbuf; f3 = b->shadbuf; b->shadbuf = shadbuf; b->sl = bufsize; sndbuf_reset(b); chn_unlock(b->channel); if (f1) free(f1, M_DEVBUF); if (f2) free(f2, M_DEVBUF); if (f3) free(f3, M_DEVBUF); ret = 0; chn_lock(b->channel); return ret; } /** * @brief Zero out space in buffer free area * * This function clears a chunk of @c length bytes in the buffer free area * (i.e., where the next write will be placed). * * @param b buffer context * @param length number of bytes to blank */ void sndbuf_clear(struct snd_dbuf *b, unsigned int length) { int i; u_char data, *p; if (length == 0) return; if (length > b->bufsize) length = b->bufsize; data = sndbuf_zerodata(b->fmt); i = sndbuf_getfreeptr(b); p = sndbuf_getbuf(b); while (length > 0) { p[i] = data; length--; i++; if (i >= b->bufsize) i = 0; } } /** * @brief Zap buffer contents, resetting "ready area" fields * * @param b buffer context */ void sndbuf_fillsilence(struct snd_dbuf *b) { if (b->bufsize > 0) memset(sndbuf_getbuf(b), sndbuf_zerodata(b->fmt), b->bufsize); b->rp = 0; b->rl = b->bufsize; } /** * @brief Reset buffer w/o flushing statistics * * This function just zeroes out buffer contents and sets the "ready length" * to zero. This was originally to facilitate minimal playback interruption * (i.e., dropped samples) in SNDCTL_DSP_SILENCE/SKIP ioctls. * * @param b buffer context */ void sndbuf_softreset(struct snd_dbuf *b) { b->rl = 0; if (b->buf && b->bufsize > 0) sndbuf_clear(b, b->bufsize); } void sndbuf_reset(struct snd_dbuf *b) { b->hp = 0; b->rp = 0; b->rl = 0; b->dl = 0; b->prev_total = 0; b->total = 0; b->xrun = 0; if (b->buf && b->bufsize > 0) sndbuf_clear(b, b->bufsize); sndbuf_clearshadow(b); } u_int32_t sndbuf_getfmt(struct snd_dbuf *b) { return b->fmt; } int sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt) { b->fmt = fmt; b->bps = 1; b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0; if (b->fmt & AFMT_16BIT) b->bps <<= 1; else if (b->fmt & AFMT_24BIT) b->bps *= 3; else if (b->fmt & AFMT_32BIT) b->bps <<= 2; return 0; } unsigned int sndbuf_getspd(struct snd_dbuf *b) { return b->spd; } void sndbuf_setspd(struct snd_dbuf *b, unsigned int spd) { b->spd = spd; } unsigned int sndbuf_getalign(struct snd_dbuf *b) { static int align[] = {0, 1, 1, 2, 2, 2, 2, 3}; return align[b->bps - 1]; } unsigned int sndbuf_getblkcnt(struct snd_dbuf *b) { return b->blkcnt; } void sndbuf_setblkcnt(struct snd_dbuf *b, unsigned int blkcnt) { b->blkcnt = blkcnt; } unsigned int sndbuf_getblksz(struct snd_dbuf *b) { return b->blksz; } void sndbuf_setblksz(struct snd_dbuf *b, unsigned int blksz) { b->blksz = blksz; } unsigned int sndbuf_getbps(struct snd_dbuf *b) { return b->bps; } void * sndbuf_getbuf(struct snd_dbuf *b) { return b->buf; } void * sndbuf_getbufofs(struct snd_dbuf *b, unsigned int ofs) { KASSERT(ofs < b->bufsize, ("%s: ofs invalid %d", __func__, ofs)); return b->buf + ofs; } unsigned int sndbuf_getsize(struct snd_dbuf *b) { return b->bufsize; } unsigned int sndbuf_getmaxsize(struct snd_dbuf *b) { return b->maxsize; } unsigned int sndbuf_runsz(struct snd_dbuf *b) { return b->dl; } void sndbuf_setrun(struct snd_dbuf *b, int go) { b->dl = go? b->blksz : 0; } struct selinfo * sndbuf_getsel(struct snd_dbuf *b) { return &b->sel; } /************************************************************/ unsigned int sndbuf_getxrun(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); return b->xrun; } void sndbuf_setxrun(struct snd_dbuf *b, unsigned int xrun) { SNDBUF_LOCKASSERT(b); b->xrun = xrun; } unsigned int sndbuf_gethwptr(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); return b->hp; } void sndbuf_sethwptr(struct snd_dbuf *b, unsigned int ptr) { SNDBUF_LOCKASSERT(b); b->hp = ptr; } unsigned int sndbuf_getready(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl)); return b->rl; } unsigned int sndbuf_getreadyptr(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp)); return b->rp; } unsigned int sndbuf_getfree(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl)); return b->bufsize - b->rl; } unsigned int sndbuf_getfreeptr(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); KASSERT((b->rp >= 0) && (b->rp <= b->bufsize), ("%s: b->rp invalid %d", __func__, b->rp)); KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl)); return (b->rp + b->rl) % b->bufsize; } unsigned int sndbuf_getblocks(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); return b->total / b->blksz; } unsigned int sndbuf_getprevblocks(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); return b->prev_total / b->blksz; } unsigned int sndbuf_gettotal(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); return b->total; } void sndbuf_updateprevtotal(struct snd_dbuf *b) { SNDBUF_LOCKASSERT(b); b->prev_total = b->total; } unsigned int snd_xbytes(unsigned int v, unsigned int from, unsigned int to) { unsigned int w, x, y; if (from == to) return v; if (from == 0 || to == 0 || v == 0) return 0; x = from; y = to; while (y != 0) { w = x % y; x = y; y = w; } from /= x; to /= x; return (unsigned int)(((u_int64_t)v * to) / from); } unsigned int sndbuf_xbytes(unsigned int v, struct snd_dbuf *from, struct snd_dbuf *to) { if (from == NULL || to == NULL || v == 0) return 0; return snd_xbytes(v, sndbuf_getbps(from) * sndbuf_getspd(from), sndbuf_getbps(to) * sndbuf_getspd(to)); } u_int8_t sndbuf_zerodata(u_int32_t fmt) { if (fmt & AFMT_SIGNED) return (0x00); else if (fmt & AFMT_MU_LAW) return (0x7f); else if (fmt & AFMT_A_LAW) return (0x55); return (0x80); } /************************************************************/ /** * @brief Acquire buffer space to extend ready area * * This function extends the ready area length by @c count bytes, and may * optionally copy samples from another location stored in @c from. The * counter @c snd_dbuf::total is also incremented by @c count bytes. * * @param b audio buffer * @param from sample source (optional) * @param count number of bytes to acquire * * @retval 0 Unconditional */ int sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count) { int l; KASSERT(count <= sndbuf_getfree(b), ("%s: count %d > free %d", __func__, count, sndbuf_getfree(b))); KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl)); b->total += count; if (from != NULL) { while (count > 0) { l = min(count, sndbuf_getsize(b) - sndbuf_getfreeptr(b)); bcopy(from, sndbuf_getbufofs(b, sndbuf_getfreeptr(b)), l); from += l; b->rl += l; count -= l; } } else b->rl += count; KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __func__, b->rl, count)); return 0; } /** * @brief Dispose samples from channel buffer, increasing size of ready area * * This function discards samples from the supplied buffer by advancing the * ready area start pointer and decrementing the ready area length. If * @c to is not NULL, then the discard samples will be copied to the location * it points to. * * @param b PCM channel sound buffer * @param to destination buffer (optional) * @param count number of bytes to discard * * @returns 0 unconditionally */ int sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count) { int l; KASSERT(count <= sndbuf_getready(b), ("%s: count %d > ready %d", __func__, count, sndbuf_getready(b))); KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl)); if (to != NULL) { while (count > 0) { l = min(count, sndbuf_getsize(b) - sndbuf_getreadyptr(b)); bcopy(sndbuf_getbufofs(b, sndbuf_getreadyptr(b)), to, l); to += l; b->rl -= l; b->rp = (b->rp + l) % b->bufsize; count -= l; } } else { b->rl -= count; b->rp = (b->rp + count) % b->bufsize; } KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d, count %d", __func__, b->rl, count)); return 0; } /* count is number of bytes we want added to destination buffer */ int sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count) { unsigned int cnt; KASSERT(count > 0, ("can't feed 0 bytes")); if (sndbuf_getfree(to) < count) return EINVAL; do { cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from); - if (cnt) + if (cnt) { sndbuf_acquire(to, to->tmpbuf, cnt); - /* the root feeder has called sndbuf_dispose(from, , bytes fetched) */ - count -= cnt; + count -= cnt; + } } while (count && cnt); return 0; } /************************************************************/ void sndbuf_dump(struct snd_dbuf *b, char *s, u_int32_t what) { printf("%s: [", s); if (what & 0x01) printf(" bufsize: %d, maxsize: %d", b->bufsize, b->maxsize); if (what & 0x02) printf(" dl: %d, rp: %d, rl: %d, hp: %d", b->dl, b->rp, b->rl, b->hp); if (what & 0x04) printf(" total: %d, prev_total: %d, xrun: %d", b->total, b->prev_total, b->xrun); if (what & 0x08) printf(" fmt: 0x%x, spd: %d", b->fmt, b->spd); if (what & 0x10) printf(" blksz: %d, blkcnt: %d, flags: 0x%x", b->blksz, b->blkcnt, b->flags); printf(" ]\n"); } /************************************************************/ u_int32_t sndbuf_getflags(struct snd_dbuf *b) { return b->flags; } void sndbuf_setflags(struct snd_dbuf *b, u_int32_t flags, int on) { b->flags &= ~flags; if (on) b->flags |= flags; } /** * @brief Clear the shadow buffer by filling with samples equal to zero. * * @param b buffer to clear */ void sndbuf_clearshadow(struct snd_dbuf *b) { KASSERT(b != NULL, ("b is a null pointer")); KASSERT(b->sl >= 0, ("illegal shadow length")); if ((b->shadbuf != NULL) && (b->sl > 0)) memset(b->shadbuf, sndbuf_zerodata(b->fmt), b->sl); } #ifdef OSSV4_EXPERIMENT /** * @brief Return peak value from samples in buffer ready area. * * Peak ranges from 0-32767. If channel is monaural, most significant 16 * bits will be zero. For now, only expects to work with 1-2 channel * buffers. * * @note Currently only operates with linear PCM formats. * * @param b buffer to analyze * @param lpeak pointer to store left peak value * @param rpeak pointer to store right peak value */ void sndbuf_getpeaks(struct snd_dbuf *b, int *lp, int *rp) { u_int32_t lpeak, rpeak; lpeak = 0; rpeak = 0; /** * @todo fill this in later */ } #endif Index: head/sys/dev/sound/pcm/channel.c =================================================================== --- head/sys/dev/sound/pcm/channel.c (revision 170160) +++ head/sys/dev/sound/pcm/channel.c (revision 170161) @@ -1,2180 +1,2253 @@ /*- * Copyright (c) 1999 Cameron Grant * Portions Copyright by Luigi Rizzo - 1997-99 * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_isa.h" #include #include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD$"); #define MIN_CHUNK_SIZE 256 /* for uiomove etc. */ #if 0 #define DMA_ALIGN_THRESHOLD 4 #define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1)) #endif #define CHN_STARTED(c) ((c)->flags & CHN_F_TRIGGERED) #define CHN_STOPPED(c) (!CHN_STARTED(c)) #define CHN_DIRSTR(c) (((c)->direction == PCMDIR_PLAY) ? \ "PCMDIR_PLAY" : "PCMDIR_REC") #define BUF_PARENT(c, b) \ (((c) != NULL && (c)->parentchannel != NULL && \ (c)->parentchannel->bufhard != NULL) ? \ (c)->parentchannel->bufhard : (b)) #define CHN_TIMEOUT 5 #define CHN_TIMEOUT_MIN 1 #define CHN_TIMEOUT_MAX 10 /* #define DEB(x) x */ int report_soft_formats = 1; SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW, &report_soft_formats, 1, "report software-emulated formats"); int chn_latency = CHN_LATENCY_DEFAULT; TUNABLE_INT("hw.snd.latency", &chn_latency); static int sysctl_hw_snd_latency(SYSCTL_HANDLER_ARGS) { int err, val; val = chn_latency; err = sysctl_handle_int(oidp, &val, sizeof(val), req); if (err != 0 || req->newptr == NULL) return err; if (val < CHN_LATENCY_MIN || val > CHN_LATENCY_MAX) err = EINVAL; else chn_latency = val; return err; } SYSCTL_PROC(_hw_snd, OID_AUTO, latency, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(int), sysctl_hw_snd_latency, "I", "buffering latency (0=low ... 10=high)"); int chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT; TUNABLE_INT("hw.snd.latency_profile", &chn_latency_profile); static int sysctl_hw_snd_latency_profile(SYSCTL_HANDLER_ARGS) { int err, val; val = chn_latency_profile; err = sysctl_handle_int(oidp, &val, sizeof(val), req); if (err != 0 || req->newptr == NULL) return err; if (val < CHN_LATENCY_PROFILE_MIN || val > CHN_LATENCY_PROFILE_MAX) err = EINVAL; else chn_latency_profile = val; return err; } SYSCTL_PROC(_hw_snd, OID_AUTO, latency_profile, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(int), sysctl_hw_snd_latency_profile, "I", "buffering latency profile (0=aggresive 1=safe)"); static int chn_timeout = CHN_TIMEOUT; TUNABLE_INT("hw.snd.timeout", &chn_timeout); #ifdef SND_DEBUG static int sysctl_hw_snd_timeout(SYSCTL_HANDLER_ARGS) { int err, val; val = chn_timeout; err = sysctl_handle_int(oidp, &val, sizeof(val), req); if (err != 0 || req->newptr == NULL) return err; if (val < CHN_TIMEOUT_MIN || val > CHN_TIMEOUT_MAX) err = EINVAL; else chn_timeout = val; return err; } SYSCTL_PROC(_hw_snd, OID_AUTO, timeout, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(int), sysctl_hw_snd_timeout, "I", "interrupt timeout (1 - 10) seconds"); #endif static int chn_usefrags = 0; TUNABLE_INT("hw.snd.usefrags", &chn_usefrags); static int chn_syncdelay = -1; TUNABLE_INT("hw.snd.syncdelay", &chn_syncdelay); #ifdef SND_DEBUG SYSCTL_INT(_hw_snd, OID_AUTO, usefrags, CTLFLAG_RW, &chn_usefrags, 1, "prefer setfragments() over setblocksize()"); SYSCTL_INT(_hw_snd, OID_AUTO, syncdelay, CTLFLAG_RW, &chn_syncdelay, 1, "append (0-1000) millisecond trailing buffer delay on each sync"); #endif /** * @brief Channel sync group lock * * Clients should acquire this lock @b without holding any channel locks * before touching syncgroups or the main syncgroup list. */ struct mtx snd_pcm_syncgroups_mtx; MTX_SYSINIT(pcm_syncgroup, &snd_pcm_syncgroups_mtx, "PCM channel sync group lock", MTX_DEF); /** * @brief syncgroups' master list * * Each time a channel syncgroup is created, it's added to this list. This * list should only be accessed with @sa snd_pcm_syncgroups_mtx held. * * See SNDCTL_DSP_SYNCGROUP for more information. */ struct pcm_synclist snd_pcm_syncgroups = SLIST_HEAD_INITIALIZER(head); static int chn_buildfeeder(struct pcm_channel *c); static void chn_lockinit(struct pcm_channel *c, int dir) { switch(dir) { case PCMDIR_PLAY: c->lock = snd_mtxcreate(c->name, "pcm play channel"); break; + case PCMDIR_PLAY_VIRTUAL: + c->lock = snd_mtxcreate(c->name, "pcm virtual play channel"); + break; case PCMDIR_REC: c->lock = snd_mtxcreate(c->name, "pcm record channel"); break; - case PCMDIR_VIRTUAL: - c->lock = snd_mtxcreate(c->name, "pcm virtual play channel"); + case PCMDIR_REC_VIRTUAL: + c->lock = snd_mtxcreate(c->name, "pcm virtual record channel"); break; case 0: c->lock = snd_mtxcreate(c->name, "pcm fake channel"); break; } cv_init(&c->cv, c->name); } static void chn_lockdestroy(struct pcm_channel *c) { snd_mtxfree(c->lock); cv_destroy(&c->cv); } /** * @brief Determine channel is ready for I/O * * @retval 1 = ready for I/O * @retval 0 = not ready for I/O */ static int chn_polltrigger(struct pcm_channel *c) { struct snd_dbuf *bs = c->bufsoft; unsigned amt, lim; CHN_LOCKASSERT(c); if (c->flags & CHN_F_MAPPED) { if (sndbuf_getprevblocks(bs) == 0) return 1; else return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0; } else { amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); #if 0 lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1; #endif lim = c->lw; return (amt >= lim) ? 1 : 0; } return 0; } static int chn_pollreset(struct pcm_channel *c) { struct snd_dbuf *bs = c->bufsoft; CHN_LOCKASSERT(c); sndbuf_updateprevtotal(bs); return 1; } static void chn_wakeup(struct pcm_channel *c) { struct snd_dbuf *bs = c->bufsoft; - struct pcmchan_children *pce; + struct pcm_channel *ch; CHN_LOCKASSERT(c); - if (SLIST_EMPTY(&c->children)) { + if (CHN_EMPTY(c, children)) { if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c)) selwakeuppri(sndbuf_getsel(bs), PRIBIO); - wakeup_one(bs); + } else if (CHN_EMPTY(c, children.busy)) { + CHN_FOREACH(ch, c, children) { + CHN_LOCK(ch); + chn_wakeup(ch); + CHN_UNLOCK(ch); + } } else { - SLIST_FOREACH(pce, &c->children, link) { - CHN_LOCK(pce->channel); - chn_wakeup(pce->channel); - CHN_UNLOCK(pce->channel); + CHN_FOREACH(ch, c, children.busy) { + CHN_LOCK(ch); + chn_wakeup(ch); + CHN_UNLOCK(ch); } } + if (c->flags & CHN_F_SLEEPING) + wakeup_one(bs); } static int chn_sleep(struct pcm_channel *c, char *str, int timeout) { struct snd_dbuf *bs = c->bufsoft; int ret; CHN_LOCKASSERT(c); + + c->flags |= CHN_F_SLEEPING; #ifdef USING_MUTEX ret = msleep(bs, c->lock, PRIBIO | PCATCH, str, timeout); #else ret = tsleep(bs, PRIBIO | PCATCH, str, timeout); #endif + c->flags &= ~CHN_F_SLEEPING; return ret; } /* * chn_dmaupdate() tracks the status of a dma transfer, * updating pointers. */ static unsigned int chn_dmaupdate(struct pcm_channel *c) { struct snd_dbuf *b = c->bufhard; unsigned int delta, old, hwptr, amt; KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0")); CHN_LOCKASSERT(c); old = sndbuf_gethwptr(b); hwptr = chn_getptr(c); delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b); sndbuf_sethwptr(b, hwptr); DEB( if (delta >= ((sndbuf_getsize(b) * 15) / 16)) { if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING))) device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr); } ); if (c->direction == PCMDIR_PLAY) { amt = min(delta, sndbuf_getready(b)); amt -= amt % sndbuf_getbps(b); if (amt > 0) sndbuf_dispose(b, NULL, amt); } else { amt = min(delta, sndbuf_getfree(b)); amt -= amt % sndbuf_getbps(b); if (amt > 0) sndbuf_acquire(b, NULL, amt); } if (snd_verbose > 3 && CHN_STARTED(c) && delta == 0) { device_printf(c->dev, "WARNING: %s DMA completion " "too fast/slow ! hwptr=%u, old=%u " "delta=%u amt=%u ready=%u free=%u\n", CHN_DIRSTR(c), hwptr, old, delta, amt, sndbuf_getready(b), sndbuf_getfree(b)); } return delta; } void chn_wrupdate(struct pcm_channel *c) { int ret; CHN_LOCKASSERT(c); KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel")); if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || CHN_STOPPED(c)) return; chn_dmaupdate(c); ret = chn_wrfeed(c); /* tell the driver we've updated the primary buffer */ chn_trigger(c, PCMTRIG_EMLDMAWR); DEB(if (ret) printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);) } int chn_wrfeed(struct pcm_channel *c) { struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; unsigned int ret, amt; CHN_LOCKASSERT(c); #if 0 DEB( if (c->flags & CHN_F_CLOSING) { sndbuf_dump(b, "b", 0x02); sndbuf_dump(bs, "bs", 0x02); }) #endif if ((c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_CLOSING)) sndbuf_acquire(bs, NULL, sndbuf_getfree(bs)); amt = sndbuf_getfree(b); DEB(if (amt > sndbuf_getsize(bs) && sndbuf_getbps(bs) >= sndbuf_getbps(b)) { printf("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name, amt, sndbuf_getsize(bs), c->flags); }); ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC; /* * Possible xruns. There should be no empty space left in buffer. */ if (sndbuf_getfree(b) > 0) c->xruns++; if (sndbuf_getfree(b) < amt) chn_wakeup(c); return ret; } static void chn_wrintr(struct pcm_channel *c) { int ret; CHN_LOCKASSERT(c); /* update pointers in primary buffer */ chn_dmaupdate(c); /* ...and feed from secondary to primary */ ret = chn_wrfeed(c); /* tell the driver we've updated the primary buffer */ chn_trigger(c, PCMTRIG_EMLDMAWR); DEB(if (ret) printf("chn_wrintr: chn_wrfeed returned %d\n", ret);) } /* * user write routine - uiomove data into secondary buffer, trigger if necessary * if blocking, sleep, rinse and repeat. * * called externally, so must handle locking */ int chn_write(struct pcm_channel *c, struct uio *buf) { struct snd_dbuf *bs = c->bufsoft; void *off; int ret, timeout, sz, t, p; CHN_LOCKASSERT(c); ret = 0; timeout = chn_timeout * hz; while (ret == 0 && buf->uio_resid > 0) { sz = min(buf->uio_resid, sndbuf_getfree(bs)); if (sz > 0) { /* * The following assumes that the free space in * the buffer can never be less around the * unlock-uiomove-lock sequence. */ while (ret == 0 && sz > 0) { p = sndbuf_getfreeptr(bs); t = min(sz, sndbuf_getsize(bs) - p); off = sndbuf_getbufofs(bs, p); CHN_UNLOCK(c); ret = uiomove(off, t, buf); CHN_LOCK(c); sz -= t; sndbuf_acquire(bs, NULL, t); } ret = 0; if (CHN_STOPPED(c)) chn_start(c, 0); } else if (c->flags & (CHN_F_NBIO | CHN_F_NOTRIGGER)) { /** * @todo Evaluate whether EAGAIN is truly desirable. * 4Front drivers behave like this, but I'm * not sure if it at all violates the "write * should be allowed to block" model. * * The idea is that, while set with CHN_F_NOTRIGGER, * a channel isn't playing, *but* without this we * end up with "interrupt timeout / channel dead". */ ret = EAGAIN; } else { ret = chn_sleep(c, "pcmwr", timeout); if (ret == EAGAIN) { ret = EINVAL; c->flags |= CHN_F_DEAD; printf("%s: play interrupt timeout, " "channel dead\n", c->name); } else if (ret == ERESTART || ret == EINTR) c->flags |= CHN_F_ABORTING; } } return ret; } #if 0 static int chn_rddump(struct pcm_channel *c, unsigned int cnt) { struct snd_dbuf *b = c->bufhard; CHN_LOCKASSERT(c); #if 0 static u_int32_t kk = 0; printf("%u: dumping %d bytes\n", ++kk, cnt); #endif c->xruns++; sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt); return sndbuf_dispose(b, NULL, cnt); } #endif /* * Feed new data from the read buffer. Can be called in the bottom half. */ int chn_rdfeed(struct pcm_channel *c) { struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; unsigned int ret, amt; CHN_LOCKASSERT(c); DEB( if (c->flags & CHN_F_CLOSING) { sndbuf_dump(b, "b", 0x02); sndbuf_dump(bs, "bs", 0x02); }) #if 0 amt = sndbuf_getready(b); if (sndbuf_getfree(bs) < amt) { c->xruns++; amt = sndbuf_getfree(bs); } #endif amt = sndbuf_getfree(bs); - ret = (amt > 0) ? sndbuf_feed(b, bs, c, c->feeder, amt) : 0; + ret = (amt > 0) ? sndbuf_feed(b, bs, c, c->feeder, amt) : ENOSPC; amt = sndbuf_getready(b); if (amt > 0) { c->xruns++; sndbuf_dispose(b, NULL, amt); } if (sndbuf_getready(bs) > 0) chn_wakeup(c); return ret; } void chn_rdupdate(struct pcm_channel *c) { int ret; CHN_LOCKASSERT(c); KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel")); - if ((c->flags & CHN_F_MAPPED) || CHN_STOPPED(c)) + if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || CHN_STOPPED(c)) return; chn_trigger(c, PCMTRIG_EMLDMARD); chn_dmaupdate(c); ret = chn_rdfeed(c); DEB(if (ret) printf("chn_rdfeed: %d\n", ret);) } /* read interrupt routine. Must be called with interrupts blocked. */ static void chn_rdintr(struct pcm_channel *c) { int ret; CHN_LOCKASSERT(c); /* tell the driver to update the primary buffer if non-dma */ chn_trigger(c, PCMTRIG_EMLDMARD); /* update pointers in primary buffer */ chn_dmaupdate(c); /* ...and feed from primary to secondary */ ret = chn_rdfeed(c); } /* * user read routine - trigger if necessary, uiomove data from secondary buffer * if blocking, sleep, rinse and repeat. * * called externally, so must handle locking */ int chn_read(struct pcm_channel *c, struct uio *buf) { struct snd_dbuf *bs = c->bufsoft; void *off; int ret, timeout, sz, t, p; CHN_LOCKASSERT(c); if (CHN_STOPPED(c)) chn_start(c, 0); ret = 0; timeout = chn_timeout * hz; while (ret == 0 && buf->uio_resid > 0) { sz = min(buf->uio_resid, sndbuf_getready(bs)); if (sz > 0) { /* * The following assumes that the free space in * the buffer can never be less around the * unlock-uiomove-lock sequence. */ while (ret == 0 && sz > 0) { p = sndbuf_getreadyptr(bs); t = min(sz, sndbuf_getsize(bs) - p); off = sndbuf_getbufofs(bs, p); CHN_UNLOCK(c); ret = uiomove(off, t, buf); CHN_LOCK(c); sz -= t; sndbuf_dispose(bs, NULL, t); } ret = 0; } else if (c->flags & (CHN_F_NBIO | CHN_F_NOTRIGGER)) ret = EAGAIN; else { ret = chn_sleep(c, "pcmrd", timeout); if (ret == EAGAIN) { ret = EINVAL; c->flags |= CHN_F_DEAD; printf("%s: record interrupt timeout, " "channel dead\n", c->name); } else if (ret == ERESTART || ret == EINTR) c->flags |= CHN_F_ABORTING; } } return ret; } void chn_intr(struct pcm_channel *c) { CHN_LOCK(c); c->interrupts++; if (c->direction == PCMDIR_PLAY) chn_wrintr(c); else chn_rdintr(c); CHN_UNLOCK(c); } u_int32_t chn_start(struct pcm_channel *c, int force) { u_int32_t i, j; struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; CHN_LOCKASSERT(c); /* if we're running, or if we're prevented from triggering, bail */ if (CHN_STARTED(c) || ((c->flags & CHN_F_NOTRIGGER) && !force)) return EINVAL; if (force) { i = 1; j = 0; } else { if (c->direction == PCMDIR_REC) { i = sndbuf_getfree(bs); j = (i > 0) ? 1 : sndbuf_getready(b); } else { if (sndbuf_getfree(bs) == 0) { i = 1; j = 0; } else { struct snd_dbuf *pb; pb = BUF_PARENT(c, b); i = sndbuf_xbytes(sndbuf_getready(bs), bs, pb); j = sndbuf_getbps(pb); } } - if (snd_verbose > 3 && SLIST_EMPTY(&c->children)) + if (snd_verbose > 3 && CHN_EMPTY(c, children)) printf("%s: %s (%s) threshold i=%d j=%d\n", __func__, CHN_DIRSTR(c), (c->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware", i, j); } if (i >= j) { c->flags |= CHN_F_TRIGGERED; sndbuf_setrun(b, 1); c->feedcount = (c->flags & CHN_F_CLOSING) ? 2 : 0; c->interrupts = 0; c->xruns = 0; if (c->direction == PCMDIR_PLAY && c->parentchannel == NULL) { sndbuf_fillsilence(b); if (snd_verbose > 3) printf("%s: %s starting! (%s) (ready=%d " "force=%d i=%d j=%d intrtimeout=%u " "latency=%dms)\n", __func__, (c->flags & CHN_F_HAS_VCHAN) ? "VCHAN" : "HW", (c->flags & CHN_F_CLOSING) ? "closing" : "running", sndbuf_getready(b), force, i, j, c->timeout, (sndbuf_getsize(b) * 1000) / (sndbuf_getbps(b) * sndbuf_getspd(b))); } chn_trigger(c, PCMTRIG_START); return 0; } return 0; } void chn_resetbuf(struct pcm_channel *c) { struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; c->blocks = 0; sndbuf_reset(b); sndbuf_reset(bs); } /* * chn_sync waits until the space in the given channel goes above * a threshold. The threshold is checked against fl or rl respectively. * Assume that the condition can become true, do not check here... */ int chn_sync(struct pcm_channel *c, int threshold) { struct snd_dbuf *b, *bs; int ret, count, hcount, minflush, resid, residp, syncdelay, blksz; u_int32_t cflag; CHN_LOCKASSERT(c); bs = c->bufsoft; if ((c->flags & (CHN_F_DEAD | CHN_F_ABORTING)) || (threshold < 1 && sndbuf_getready(bs) < 1)) return 0; if (c->direction != PCMDIR_PLAY) return EINVAL; /* if we haven't yet started and nothing is buffered, else start*/ if (CHN_STOPPED(c)) { if (threshold > 0 || sndbuf_getready(bs) > 0) { ret = chn_start(c, 1); if (ret) return ret; } else return 0; } b = BUF_PARENT(c, c->bufhard); minflush = threshold + sndbuf_xbytes(sndbuf_getready(b), b, bs); syncdelay = chn_syncdelay; if (syncdelay < 0 && (threshold > 0 || sndbuf_getready(bs) > 0)) minflush += sndbuf_xbytes(sndbuf_getsize(b), b, bs); /* * Append (0-1000) millisecond trailing buffer (if needed) * for slower / high latency hardwares (notably USB audio) * to avoid audible truncation. */ if (syncdelay > 0) minflush += (sndbuf_getbps(bs) * sndbuf_getspd(bs) * ((syncdelay > 1000) ? 1000 : syncdelay)) / 1000; minflush -= minflush % sndbuf_getbps(bs); if (minflush > 0) { threshold = min(minflush, sndbuf_getfree(bs)); sndbuf_clear(bs, threshold); sndbuf_acquire(bs, NULL, threshold); minflush -= threshold; } resid = sndbuf_getready(bs); residp = resid; blksz = sndbuf_getblksz(b); if (blksz < 1) { printf("%s: WARNING: blksz < 1 ! maxsize=%d [%d/%d/%d]\n", __func__, sndbuf_getmaxsize(b), sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b)); if (sndbuf_getblkcnt(b) > 0) blksz = sndbuf_getsize(b) / sndbuf_getblkcnt(b); if (blksz < 1) blksz = 1; } count = sndbuf_xbytes(minflush + resid, bs, b) / blksz; hcount = count; ret = 0; if (snd_verbose > 3) printf("%s: [begin] timeout=%d count=%d " "minflush=%d resid=%d\n", __func__, c->timeout, count, minflush, resid); cflag = c->flags & CHN_F_CLOSING; c->flags |= CHN_F_CLOSING; while (count > 0 && (resid > 0 || minflush > 0)) { ret = chn_sleep(c, "pcmsyn", c->timeout); if (ret == ERESTART || ret == EINTR) { c->flags |= CHN_F_ABORTING; break; } if (ret == 0 || ret == EAGAIN) { resid = sndbuf_getready(bs); if (resid == residp) { --count; if (snd_verbose > 3) printf("%s: [stalled] timeout=%d " "count=%d hcount=%d " "resid=%d minflush=%d\n", __func__, c->timeout, count, hcount, resid, minflush); } else if (resid < residp && count < hcount) { ++count; if (snd_verbose > 3) printf("%s: [resume] timeout=%d " "count=%d hcount=%d " "resid=%d minflush=%d\n", __func__, c->timeout, count, hcount, resid, minflush); } if (minflush > 0 && sndbuf_getfree(bs) > 0) { threshold = min(minflush, sndbuf_getfree(bs)); sndbuf_clear(bs, threshold); sndbuf_acquire(bs, NULL, threshold); resid = sndbuf_getready(bs); minflush -= threshold; } residp = resid; } } c->flags &= ~CHN_F_CLOSING; c->flags |= cflag; if (snd_verbose > 3) printf("%s: timeout=%d count=%d hcount=%d resid=%d residp=%d " "minflush=%d ret=%d\n", __func__, c->timeout, count, hcount, resid, residp, minflush, ret); return 0; } /* called externally, handle locking */ int chn_poll(struct pcm_channel *c, int ev, struct thread *td) { struct snd_dbuf *bs = c->bufsoft; int ret; CHN_LOCKASSERT(c); if (!(c->flags & (CHN_F_MAPPED | CHN_F_TRIGGERED))) chn_start(c, 1); ret = 0; if (chn_polltrigger(c) && chn_pollreset(c)) ret = ev; else selrecord(td, sndbuf_getsel(bs)); return ret; } /* * chn_abort terminates a running dma transfer. it may sleep up to 200ms. * it returns the number of bytes that have not been transferred. * * called from: dsp_close, dsp_ioctl, with channel locked */ int chn_abort(struct pcm_channel *c) { int missing = 0; struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; CHN_LOCKASSERT(c); if (CHN_STOPPED(c)) return 0; c->flags |= CHN_F_ABORTING; c->flags &= ~CHN_F_TRIGGERED; /* kill the channel */ chn_trigger(c, PCMTRIG_ABORT); sndbuf_setrun(b, 0); if (!(c->flags & CHN_F_VIRTUAL)) chn_dmaupdate(c); missing = sndbuf_getready(bs); c->flags &= ~CHN_F_ABORTING; return missing; } /* * this routine tries to flush the dma transfer. It is called * on a close of a playback channel. * first, if there is data in the buffer, but the dma has not yet * begun, we need to start it. * next, we wait for the play buffer to drain * finally, we stop the dma. * * called from: dsp_close, not valid for record channels. */ int chn_flush(struct pcm_channel *c) { struct snd_dbuf *b = c->bufhard; CHN_LOCKASSERT(c); KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel")); DEB(printf("chn_flush: c->flags 0x%08x\n", c->flags)); c->flags |= CHN_F_CLOSING; chn_sync(c, 0); c->flags &= ~CHN_F_TRIGGERED; /* kill the channel */ chn_trigger(c, PCMTRIG_ABORT); sndbuf_setrun(b, 0); c->flags &= ~CHN_F_CLOSING; return 0; } int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist) { int i; for (i = 0; fmtlist[i]; i++) if (fmt == fmtlist[i]) return 1; return 0; } static struct afmtstr_table default_afmtstr_table[] = { { "alaw", AFMT_A_LAW }, { "mulaw", AFMT_MU_LAW }, { "u8", AFMT_U8 }, { "s8", AFMT_S8 }, { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE }, { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE }, { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE }, { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE }, { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE }, { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE }, { NULL, 0 }, }; int afmtstr_swap_sign(char *s) { if (s == NULL || strlen(s) < 2) /* full length of "s8" */ return 0; if (*s == 's') *s = 'u'; else if (*s == 'u') *s = 's'; else return 0; return 1; } int afmtstr_swap_endian(char *s) { if (s == NULL || strlen(s) < 5) /* full length of "s16le" */ return 0; if (s[3] == 'l') s[3] = 'b'; else if (s[3] == 'b') s[3] = 'l'; else return 0; return 1; } u_int32_t afmtstr2afmt(struct afmtstr_table *tbl, const char *s, int stereo) { size_t fsz, sz; sz = (s == NULL) ? 0 : strlen(s); if (sz > 1) { if (tbl == NULL) tbl = default_afmtstr_table; for (; tbl->fmtstr != NULL; tbl++) { fsz = strlen(tbl->fmtstr); if (sz < fsz) continue; if (strncmp(s, tbl->fmtstr, fsz) != 0) continue; if (fsz == sz) return tbl->format | ((stereo) ? AFMT_STEREO : 0); if ((sz - fsz) < 2 || s[fsz] != ':') break; /* * For now, just handle mono/stereo. */ if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 'm' || s[fsz + 1] == '1')) || strcmp(s + fsz + 1, "mono") == 0) return tbl->format; if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 's' || s[fsz + 1] == '2')) || strcmp(s + fsz + 1, "stereo") == 0) return tbl->format | AFMT_STEREO; break; } } return 0; } u_int32_t afmt2afmtstr(struct afmtstr_table *tbl, u_int32_t afmt, char *dst, size_t len, int type, int stereo) { u_int32_t fmt = 0; char *fmtstr = NULL, *tag = ""; if (tbl == NULL) tbl = default_afmtstr_table; for (; tbl->format != 0; tbl++) { if (tbl->format == 0) break; if ((afmt & ~AFMT_STEREO) != tbl->format) continue; fmt = afmt; fmtstr = tbl->fmtstr; break; } if (fmt != 0 && fmtstr != NULL && dst != NULL && len > 0) { strlcpy(dst, fmtstr, len); switch (type) { case AFMTSTR_SIMPLE: tag = (fmt & AFMT_STEREO) ? ":s" : ":m"; break; case AFMTSTR_NUM: tag = (fmt & AFMT_STEREO) ? ":2" : ":1"; break; case AFMTSTR_FULL: tag = (fmt & AFMT_STEREO) ? ":stereo" : ":mono"; break; case AFMTSTR_NONE: default: break; } if (strlen(tag) > 0 && ((stereo && !(fmt & AFMT_STEREO)) || \ (!stereo && (fmt & AFMT_STEREO)))) strlcat(dst, tag, len); } return fmt; } int chn_reset(struct pcm_channel *c, u_int32_t fmt) { int hwspd, r; CHN_LOCKASSERT(c); c->feedcount = 0; c->flags &= CHN_F_RESET; c->interrupts = 0; c->timeout = 1; c->xruns = 0; r = CHANNEL_RESET(c->methods, c->devinfo); if (fmt != 0) { #if 0 hwspd = DSP_DEFAULT_SPEED; /* only do this on a record channel until feederbuilder works */ if (c->direction == PCMDIR_REC) RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); c->speed = hwspd; #endif hwspd = chn_getcaps(c)->minspeed; c->speed = hwspd; if (r == 0) r = chn_setformat(c, fmt); if (r == 0) r = chn_setspeed(c, hwspd); #if 0 if (r == 0) r = chn_setvolume(c, 100, 100); #endif } if (r == 0) r = chn_setlatency(c, chn_latency); if (r == 0) { chn_resetbuf(c); r = CHANNEL_RESETDONE(c->methods, c->devinfo); } return r; } int chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction) { struct feeder_class *fc; struct snd_dbuf *b, *bs; int ret; if (chn_timeout < CHN_TIMEOUT_MIN || chn_timeout > CHN_TIMEOUT_MAX) chn_timeout = CHN_TIMEOUT; chn_lockinit(c, dir); b = NULL; bs = NULL; + CHN_INIT(c, children); + CHN_INIT(c, children.busy); c->devinfo = NULL; c->feeder = NULL; c->latency = -1; c->timeout = 1; ret = ENOMEM; b = sndbuf_create(c->dev, c->name, "primary", c); if (b == NULL) goto out; bs = sndbuf_create(c->dev, c->name, "secondary", c); if (bs == NULL) goto out; CHN_LOCK(c); ret = EINVAL; fc = feeder_getclass(NULL); if (fc == NULL) goto out; if (chn_addfeeder(c, fc, NULL)) goto out; /* * XXX - sndbuf_setup() & sndbuf_resize() expect to be called * with the channel unlocked because they are also called * from driver methods that don't know about locking */ CHN_UNLOCK(c); sndbuf_setup(bs, NULL, 0); CHN_LOCK(c); c->bufhard = b; c->bufsoft = bs; c->flags = 0; c->feederflags = 0; c->sm = NULL; ret = ENODEV; CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */ c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction); CHN_LOCK(c); if (c->devinfo == NULL) goto out; ret = ENOMEM; if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) goto out; ret = chn_setdir(c, direction); if (ret) goto out; ret = sndbuf_setfmt(b, AFMT_U8); if (ret) goto out; ret = sndbuf_setfmt(bs, AFMT_U8); if (ret) goto out; /** * @todo Should this be moved somewhere else? The primary buffer * is allocated by the driver or via DMA map setup, and tmpbuf * seems to only come into existence in sndbuf_resize(). */ if (c->direction == PCMDIR_PLAY) { bs->sl = sndbuf_getmaxsize(bs); bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_NOWAIT); if (bs->shadbuf == NULL) { ret = ENOMEM; goto out; } } out: CHN_UNLOCK(c); if (ret) { if (c->devinfo) { if (CHANNEL_FREE(c->methods, c->devinfo)) sndbuf_free(b); } if (bs) sndbuf_destroy(bs); if (b) sndbuf_destroy(b); c->flags |= CHN_F_DEAD; chn_lockdestroy(c); return ret; } return 0; } int chn_kill(struct pcm_channel *c) { struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; - if (CHN_STARTED(c)) + if (CHN_STARTED(c)) { + CHN_LOCK(c); chn_trigger(c, PCMTRIG_ABORT); - while (chn_removefeeder(c) == 0); + CHN_UNLOCK(c); + } + while (chn_removefeeder(c) == 0) + ; if (CHANNEL_FREE(c->methods, c->devinfo)) sndbuf_free(b); c->flags |= CHN_F_DEAD; sndbuf_destroy(bs); sndbuf_destroy(b); chn_lockdestroy(c); return 0; } int chn_setdir(struct pcm_channel *c, int dir) { #ifdef DEV_ISA struct snd_dbuf *b = c->bufhard; #endif int r; CHN_LOCKASSERT(c); c->direction = dir; r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction); #ifdef DEV_ISA if (!r && SND_DMA(b)) sndbuf_dmasetdir(b, c->direction); #endif return r; } int chn_setvolume(struct pcm_channel *c, int left, int right) { CHN_LOCKASSERT(c); /* should add a feeder for volume changing if channel returns -1 */ if (left > 100) left = 100; if (left < 0) left = 0; if (right > 100) right = 100; if (right < 0) right = 0; c->volume = left | (right << 8); return 0; } static u_int32_t round_pow2(u_int32_t v) { u_int32_t ret; if (v < 2) v = 2; ret = 0; while (v >> ret) ret++; ret = 1 << (ret - 1); while (ret < v) ret <<= 1; return ret; } static u_int32_t round_blksz(u_int32_t v, int round) { u_int32_t ret, tmp; if (round < 1) round = 1; ret = min(round_pow2(v), CHN_2NDBUFMAXSIZE >> 1); if (ret > v && (ret >> 1) > 0 && (ret >> 1) >= ((v * 3) >> 2)) ret >>= 1; tmp = ret - (ret % round); while (tmp < 16 || tmp < round) { ret <<= 1; tmp = ret - (ret % round); } return ret; } /* * 4Front call it DSP Policy, while we call it "Latency Profile". The idea * is to keep 2nd buffer short so that it doesn't cause long queue during * buffer transfer. * * Latency reference table for 48khz stereo 16bit: (PLAY) * * +---------+------------+-----------+------------+ * | Latency | Blockcount | Blocksize | Buffersize | * +---------+------------+-----------+------------+ * | 0 | 2 | 64 | 128 | * +---------+------------+-----------+------------+ * | 1 | 4 | 128 | 512 | * +---------+------------+-----------+------------+ * | 2 | 8 | 512 | 4096 | * +---------+------------+-----------+------------+ * | 3 | 16 | 512 | 8192 | * +---------+------------+-----------+------------+ * | 4 | 32 | 512 | 16384 | * +---------+------------+-----------+------------+ * | 5 | 32 | 1024 | 32768 | * +---------+------------+-----------+------------+ * | 6 | 16 | 2048 | 32768 | * +---------+------------+-----------+------------+ * | 7 | 8 | 4096 | 32768 | * +---------+------------+-----------+------------+ * | 8 | 4 | 8192 | 32768 | * +---------+------------+-----------+------------+ * | 9 | 2 | 16384 | 32768 | * +---------+------------+-----------+------------+ * | 10 | 2 | 32768 | 65536 | * +---------+------------+-----------+------------+ * * Recording need a different reference table. All we care is * gobbling up everything within reasonable buffering threshold. * * Latency reference table for 48khz stereo 16bit: (REC) * * +---------+------------+-----------+------------+ * | Latency | Blockcount | Blocksize | Buffersize | * +---------+------------+-----------+------------+ * | 0 | 512 | 32 | 16384 | * +---------+------------+-----------+------------+ * | 1 | 256 | 64 | 16384 | * +---------+------------+-----------+------------+ * | 2 | 128 | 128 | 16384 | * +---------+------------+-----------+------------+ * | 3 | 64 | 256 | 16384 | * +---------+------------+-----------+------------+ * | 4 | 32 | 512 | 16384 | * +---------+------------+-----------+------------+ * | 5 | 32 | 1024 | 32768 | * +---------+------------+-----------+------------+ * | 6 | 16 | 2048 | 32768 | * +---------+------------+-----------+------------+ * | 7 | 8 | 4096 | 32768 | * +---------+------------+-----------+------------+ * | 8 | 4 | 8192 | 32768 | * +---------+------------+-----------+------------+ * | 9 | 2 | 16384 | 32768 | * +---------+------------+-----------+------------+ * | 10 | 2 | 32768 | 65536 | * +---------+------------+-----------+------------+ * * Calculations for other data rate are entirely based on these reference * tables. For normal operation, Latency 5 seems give the best, well * balanced performance for typical workload. Anything below 5 will * eat up CPU to keep up with increasing context switches because of * shorter buffer space and usually require the application to handle it * aggresively through possibly real time programming technique. * */ #define CHN_LATENCY_PBLKCNT_REF \ {{1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1}, \ {1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1}} #define CHN_LATENCY_PBUFSZ_REF \ {{7, 9, 12, 13, 14, 15, 15, 15, 15, 15, 16}, \ {11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 17}} #define CHN_LATENCY_RBLKCNT_REF \ {{9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1}, \ {9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1}} #define CHN_LATENCY_RBUFSZ_REF \ {{14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16}, \ {15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17}} #define CHN_LATENCY_DATA_REF 192000 /* 48khz stereo 16bit ~ 48000 x 2 x 2 */ static int chn_calclatency(int dir, int latency, int bps, u_int32_t datarate, u_int32_t max, int *rblksz, int *rblkcnt) { static int pblkcnts[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] = CHN_LATENCY_PBLKCNT_REF; static int pbufszs[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] = CHN_LATENCY_PBUFSZ_REF; static int rblkcnts[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] = CHN_LATENCY_RBLKCNT_REF; static int rbufszs[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] = CHN_LATENCY_RBUFSZ_REF; u_int32_t bufsz; int lprofile, blksz, blkcnt; if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX || bps < 1 || datarate < 1 || !(dir == PCMDIR_PLAY || dir == PCMDIR_REC)) { if (rblksz != NULL) *rblksz = CHN_2NDBUFMAXSIZE >> 1; if (rblkcnt != NULL) *rblkcnt = 2; printf("%s: FAILED dir=%d latency=%d bps=%d " "datarate=%u max=%u\n", __func__, dir, latency, bps, datarate, max); return CHN_2NDBUFMAXSIZE; } lprofile = chn_latency_profile; if (dir == PCMDIR_PLAY) { blkcnt = pblkcnts[lprofile][latency]; bufsz = pbufszs[lprofile][latency]; } else { blkcnt = rblkcnts[lprofile][latency]; bufsz = rbufszs[lprofile][latency]; } bufsz = round_pow2(snd_xbytes(1 << bufsz, CHN_LATENCY_DATA_REF, datarate)); if (bufsz > max) bufsz = max; blksz = round_blksz(bufsz >> blkcnt, bps); if (rblksz != NULL) *rblksz = blksz; if (rblkcnt != NULL) *rblkcnt = 1 << blkcnt; return blksz << blkcnt; } static int chn_resizebuf(struct pcm_channel *c, int latency, int blkcnt, int blksz) { struct snd_dbuf *b, *bs, *pb; int sblksz, sblkcnt, hblksz, hblkcnt, limit = 1; int ret; CHN_LOCKASSERT(c); if ((c->flags & (CHN_F_MAPPED | CHN_F_TRIGGERED)) || !(c->direction == PCMDIR_PLAY || c->direction == PCMDIR_REC)) return EINVAL; if (latency == -1) { c->latency = -1; latency = chn_latency; } else if (latency == -2) { latency = c->latency; if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX) latency = chn_latency; } else if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX) return EINVAL; else { c->latency = latency; limit = 0; } bs = c->bufsoft; b = c->bufhard; if (!(blksz == 0 || blkcnt == -1) && (blksz < 16 || blksz < sndbuf_getbps(bs) || blkcnt < 2 || (blksz * blkcnt) > CHN_2NDBUFMAXSIZE)) return EINVAL; chn_calclatency(c->direction, latency, sndbuf_getbps(bs), sndbuf_getbps(bs) * sndbuf_getspd(bs), CHN_2NDBUFMAXSIZE, &sblksz, &sblkcnt); if (blksz == 0 || blkcnt == -1) { if (blkcnt == -1) c->flags &= ~CHN_F_HAS_SIZE; if (c->flags & CHN_F_HAS_SIZE) { blksz = sndbuf_getblksz(bs); blkcnt = sndbuf_getblkcnt(bs); } } else c->flags |= CHN_F_HAS_SIZE; if (c->flags & CHN_F_HAS_SIZE) { /* * The application has requested their own blksz/blkcnt. * Just obey with it, and let them toast alone. We can * clamp it to the nearest latency profile, but that would * defeat the purpose of having custom control. The least * we can do is round it to the nearest ^2 and align it. */ sblksz = round_blksz(blksz, sndbuf_getbps(bs)); sblkcnt = round_pow2(blkcnt); limit = 0; } if (c->parentchannel != NULL) { pb = BUF_PARENT(c, NULL); CHN_UNLOCK(c); chn_notify(c->parentchannel, CHN_N_BLOCKSIZE); CHN_LOCK(c); limit = (limit != 0 && pb != NULL) ? sndbuf_xbytes(sndbuf_getsize(pb), pb, bs) : 0; c->timeout = c->parentchannel->timeout; } else { hblkcnt = 2; if (c->flags & CHN_F_HAS_SIZE) { hblksz = round_blksz(sndbuf_xbytes(sblksz, bs, b), sndbuf_getbps(b)); hblkcnt = round_pow2(sndbuf_getblkcnt(bs)); } else chn_calclatency(c->direction, latency, sndbuf_getbps(b), sndbuf_getbps(b) * sndbuf_getspd(b), CHN_2NDBUFMAXSIZE, &hblksz, &hblkcnt); if ((hblksz << 1) > sndbuf_getmaxsize(b)) hblksz = round_blksz(sndbuf_getmaxsize(b) >> 1, sndbuf_getbps(b)); while ((hblksz * hblkcnt) > sndbuf_getmaxsize(b)) { if (hblkcnt < 4) hblksz >>= 1; else hblkcnt >>= 1; } hblksz -= hblksz % sndbuf_getbps(b); #if 0 hblksz = sndbuf_getmaxsize(b) >> 1; hblksz -= hblksz % sndbuf_getbps(b); hblkcnt = 2; #endif CHN_UNLOCK(c); if (chn_usefrags == 0 || CHANNEL_SETFRAGMENTS(c->methods, c->devinfo, hblksz, hblkcnt) < 1) sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, hblksz)); CHN_LOCK(c); - if (!SLIST_EMPTY(&c->children)) { + if (!CHN_EMPTY(c, children)) { sblksz = round_blksz( sndbuf_xbytes(sndbuf_getsize(b) >> 1, b, bs), sndbuf_getbps(bs)); sblkcnt = 2; limit = 0; } else if (limit != 0) limit = sndbuf_xbytes(sndbuf_getsize(b), b, bs); /* * Interrupt timeout */ c->timeout = ((u_int64_t)hz * sndbuf_getsize(b)) / ((u_int64_t)sndbuf_getspd(b) * sndbuf_getbps(b)); if (c->timeout < 1) c->timeout = 1; } if (limit > CHN_2NDBUFMAXSIZE) limit = CHN_2NDBUFMAXSIZE; #if 0 while (limit > 0 && (sblksz * sblkcnt) > limit) { if (sblkcnt < 4) break; sblkcnt >>= 1; } #endif while ((sblksz * sblkcnt) < limit) sblkcnt <<= 1; while ((sblksz * sblkcnt) > CHN_2NDBUFMAXSIZE) { if (sblkcnt < 4) sblksz >>= 1; else sblkcnt >>= 1; } sblksz -= sblksz % sndbuf_getbps(bs); if (sndbuf_getblkcnt(bs) != sblkcnt || sndbuf_getblksz(bs) != sblksz || sndbuf_getsize(bs) != (sblkcnt * sblksz)) { ret = sndbuf_remalloc(bs, sblkcnt, sblksz); if (ret != 0) { printf("%s: Failed: %d %d\n", __func__, sblkcnt, sblksz); return ret; } } /* * OSSv4 docs: "By default OSS will set the low water level equal * to the fragment size which is optimal in most cases." */ c->lw = sndbuf_getblksz(bs); chn_resetbuf(c); if (snd_verbose > 3) printf("%s: %s (%s) timeout=%u " "b[%d/%d/%d] bs[%d/%d/%d] limit=%d\n", __func__, CHN_DIRSTR(c), (c->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware", c->timeout, sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b), sndbuf_getsize(bs), sndbuf_getblksz(bs), sndbuf_getblkcnt(bs), limit); return 0; } int chn_setlatency(struct pcm_channel *c, int latency) { CHN_LOCKASSERT(c); /* Destroy blksz/blkcnt, enforce latency profile. */ return chn_resizebuf(c, latency, -1, 0); } int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz) { CHN_LOCKASSERT(c); /* Destroy latency profile, enforce blksz/blkcnt */ return chn_resizebuf(c, -1, blkcnt, blksz); } static int chn_tryspeed(struct pcm_channel *c, int speed) { struct pcm_feeder *f; struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; struct snd_dbuf *x; int r, delta; CHN_LOCKASSERT(c); DEB(printf("setspeed, channel %s\n", c->name)); DEB(printf("want speed %d, ", speed)); if (speed <= 0) return EINVAL; if (CHN_STOPPED(c)) { r = 0; c->speed = speed; sndbuf_setspd(bs, speed); RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); DEB(printf("try speed %d, ", speed)); sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed)); DEB(printf("got speed %d\n", sndbuf_getspd(b))); delta = sndbuf_getspd(b) - sndbuf_getspd(bs); if (delta < 0) delta = -delta; c->feederflags &= ~(1 << FEEDER_RATE); /* * Used to be 500. It was too big! */ if (delta > feeder_rate_round) c->feederflags |= 1 << FEEDER_RATE; else sndbuf_setspd(bs, sndbuf_getspd(b)); r = chn_buildfeeder(c); DEB(printf("r = %d\n", r)); if (r) goto out; if (!(c->feederflags & (1 << FEEDER_RATE))) goto out; r = EINVAL; f = chn_findfeeder(c, FEEDER_RATE); DEB(printf("feedrate = %p\n", f)); if (f == NULL) goto out; x = (c->direction == PCMDIR_REC)? b : bs; r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x)); DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r)); if (r) goto out; x = (c->direction == PCMDIR_REC)? bs : b; r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x)); DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r)); out: if (!r) r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b)); if (!r) sndbuf_setfmt(bs, c->format); if (!r) r = chn_resizebuf(c, -2, 0, 0); DEB(printf("setspeed done, r = %d\n", r)); return r; } else return EINVAL; } int chn_setspeed(struct pcm_channel *c, int speed) { int r, oldspeed = c->speed; r = chn_tryspeed(c, speed); if (r) { if (snd_verbose > 3) printf("Failed to set speed %d falling back to %d\n", speed, oldspeed); r = chn_tryspeed(c, oldspeed); } return r; } static int chn_tryformat(struct pcm_channel *c, u_int32_t fmt) { struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; int r; CHN_LOCKASSERT(c); if (CHN_STOPPED(c)) { DEB(printf("want format %d\n", fmt)); c->format = fmt; r = chn_buildfeeder(c); if (r == 0) { sndbuf_setfmt(bs, c->format); chn_resetbuf(c); r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b)); if (r == 0) r = chn_tryspeed(c, c->speed); } return r; } else return EINVAL; } int chn_setformat(struct pcm_channel *c, u_int32_t fmt) { u_int32_t oldfmt = c->format; int r; r = chn_tryformat(c, fmt); if (r) { if (snd_verbose > 3) printf("Format change 0x%08x failed, reverting to 0x%08x\n", fmt, oldfmt); chn_tryformat(c, oldfmt); } return r; } int chn_trigger(struct pcm_channel *c, int go) { #ifdef DEV_ISA struct snd_dbuf *b = c->bufhard; #endif + struct snddev_info *d = c->parentsnddev; int ret; CHN_LOCKASSERT(c); #ifdef DEV_ISA if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)) sndbuf_dmabounce(b); #endif + if ((go == PCMTRIG_START || go == PCMTRIG_STOP || + go == PCMTRIG_ABORT) && go == c->trigger) + return 0; + ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go); + if (ret == 0) { + switch (go) { + case PCMTRIG_START: + if (snd_verbose > 3) + device_printf(c->dev, + "%s() %s: calling go=0x%08x , " + "prev=0x%08x\n", __func__, c->name, go, + c->trigger); + if (c->trigger != PCMTRIG_START) { + c->trigger = go; + CHN_UNLOCK(c); + pcm_lock(d); + CHN_INSERT_HEAD(d, c, channels.pcm.busy); + pcm_unlock(d); + CHN_LOCK(c); + } + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + if (snd_verbose > 3) + device_printf(c->dev, + "%s() %s: calling go=0x%08x , " + "prev=0x%08x\n", __func__, c->name, go, + c->trigger); + if (c->trigger == PCMTRIG_START) { + c->trigger = go; + CHN_UNLOCK(c); + pcm_lock(d); + CHN_REMOVE(d, c, channels.pcm.busy); + pcm_unlock(d); + CHN_LOCK(c); + } + break; + default: + break; + } + } + return ret; } /** * @brief Queries sound driver for sample-aligned hardware buffer pointer index * * This function obtains the hardware pointer location, then aligns it to * the current bytes-per-sample value before returning. (E.g., a channel * running in 16 bit stereo mode would require 4 bytes per sample, so a * hwptr value ranging from 32-35 would be returned as 32.) * * @param c PCM channel context * @returns sample-aligned hardware buffer pointer index */ int chn_getptr(struct pcm_channel *c) { #if 0 int hwptr; int a = (1 << c->align) - 1; CHN_LOCKASSERT(c); hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; /* don't allow unaligned values in the hwa ptr */ #if 1 hwptr &= ~a ; /* Apply channel align mask */ #endif hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */ return hwptr; #endif int hwptr; CHN_LOCKASSERT(c); hwptr = (CHN_STARTED(c)) ? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; return (hwptr - (hwptr % sndbuf_getbps(c->bufhard))); } struct pcmchan_caps * chn_getcaps(struct pcm_channel *c) { CHN_LOCKASSERT(c); return CHANNEL_GETCAPS(c->methods, c->devinfo); } u_int32_t chn_getformats(struct pcm_channel *c) { u_int32_t *fmtlist, fmts; int i; fmtlist = chn_getcaps(c)->fmtlist; fmts = 0; for (i = 0; fmtlist[i]; i++) fmts |= fmtlist[i]; /* report software-supported formats */ if (report_soft_formats) fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE| AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE| AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE| AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8; return fmts; } static int chn_buildfeeder(struct pcm_channel *c) { struct feeder_class *fc; struct pcm_feederdesc desc; u_int32_t tmp[2], type, flags, hwfmt, *fmtlist; int err; char fmtstr[AFMTSTR_MAXSZ]; CHN_LOCKASSERT(c); while (chn_removefeeder(c) == 0) ; KASSERT((c->feeder == NULL), ("feeder chain not empty")); c->align = sndbuf_getalign(c->bufsoft); - if (SLIST_EMPTY(&c->children)) { + if (CHN_EMPTY(c, children) || c->direction == PCMDIR_REC) { + /* + * Virtual rec need this. + */ fc = feeder_getclass(NULL); KASSERT(fc != NULL, ("can't find root feeder")); err = chn_addfeeder(c, fc, NULL); if (err) { DEB(printf("can't add root feeder, err %d\n", err)); return err; } c->feeder->desc->out = c->format; - } else { + } else if (c->direction == PCMDIR_PLAY) { if (c->flags & CHN_F_HAS_VCHAN) { desc.type = FEEDER_MIXER; desc.in = c->format; } else { DEB(printf("can't decide which feeder type to use!\n")); return EOPNOTSUPP; } desc.out = c->format; desc.flags = 0; fc = feeder_getclass(&desc); if (fc == NULL) { DEB(printf("can't find vchan feeder\n")); return EOPNOTSUPP; } err = chn_addfeeder(c, fc, &desc); if (err) { DEB(printf("can't add vchan feeder, err %d\n", err)); return err; } - } + } else + return EOPNOTSUPP; + c->feederflags &= ~(1 << FEEDER_VOLUME); if (c->direction == PCMDIR_PLAY && !(c->flags & CHN_F_VIRTUAL) && c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTPCMVOL) && c->parentsnddev->mixer_dev) c->feederflags |= 1 << FEEDER_VOLUME; if (!(c->flags & CHN_F_VIRTUAL) && c->parentsnddev && ((c->direction == PCMDIR_PLAY && (c->parentsnddev->flags & SD_F_PSWAPLR)) || (c->direction == PCMDIR_REC && (c->parentsnddev->flags & SD_F_RSWAPLR)))) c->feederflags |= 1 << FEEDER_SWAPLR; flags = c->feederflags; fmtlist = chn_getcaps(c)->fmtlist; DEB(printf("feederflags %x\n", flags)); for (type = FEEDER_RATE; type < FEEDER_LAST; type++) { if (flags & (1 << type)) { desc.type = type; desc.in = 0; desc.out = 0; desc.flags = 0; DEB(printf("find feeder type %d, ", type)); if (type == FEEDER_VOLUME || type == FEEDER_RATE) { if (c->feeder->desc->out & AFMT_32BIT) strlcpy(fmtstr,"s32le", sizeof(fmtstr)); else if (c->feeder->desc->out & AFMT_24BIT) strlcpy(fmtstr, "s24le", sizeof(fmtstr)); else { /* * 8bit doesn't provide enough headroom * for proper processing without * creating too much noises. Force to * 16bit instead. */ strlcpy(fmtstr, "s16le", sizeof(fmtstr)); } if (!(c->feeder->desc->out & AFMT_8BIT) && c->feeder->desc->out & AFMT_BIGENDIAN) afmtstr_swap_endian(fmtstr); if (!(c->feeder->desc->out & (AFMT_A_LAW | AFMT_MU_LAW)) && !(c->feeder->desc->out & AFMT_SIGNED)) afmtstr_swap_sign(fmtstr); desc.in = afmtstr2afmt(NULL, fmtstr, AFMTSTR_MONO_RETURN); if (desc.in == 0) desc.in = AFMT_S16_LE; /* feeder_volume need stereo processing */ if (type == FEEDER_VOLUME || c->feeder->desc->out & AFMT_STEREO) desc.in |= AFMT_STEREO; desc.out = desc.in; } else if (type == FEEDER_SWAPLR) { desc.in = c->feeder->desc->out; desc.in |= AFMT_STEREO; desc.out = desc.in; } fc = feeder_getclass(&desc); DEB(printf("got %p\n", fc)); if (fc == NULL) { DEB(printf("can't find required feeder type %d\n", type)); return EOPNOTSUPP; } if (desc.in == 0 || desc.out == 0) desc = *fc->desc; DEB(printf("build fmtchain from 0x%08x to 0x%08x: ", c->feeder->desc->out, fc->desc->in)); tmp[0] = desc.in; tmp[1] = 0; if (chn_fmtchain(c, tmp) == 0) { DEB(printf("failed\n")); return ENODEV; } DEB(printf("ok\n")); err = chn_addfeeder(c, fc, &desc); if (err) { DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err)); return err; } DEB(printf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out)); } } if (c->direction == PCMDIR_REC) { tmp[0] = c->format; tmp[1] = 0; hwfmt = chn_fmtchain(c, tmp); } else hwfmt = chn_fmtchain(c, fmtlist); if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) { DEB(printf("Invalid hardware format: 0x%08x\n", hwfmt)); return ENODEV; + } else if (c->direction == PCMDIR_REC && !CHN_EMPTY(c, children)) { + /* + * Kind of awkward. This whole "MIXER" concept need a + * rethinking, I guess :) . Recording is the inverse + * of Playback, which is why we push mixer vchan down here. + */ + if (c->flags & CHN_F_HAS_VCHAN) { + desc.type = FEEDER_MIXER; + desc.in = c->format; + } else + return EOPNOTSUPP; + desc.out = c->format; + desc.flags = 0; + fc = feeder_getclass(&desc); + if (fc == NULL) + return EOPNOTSUPP; + + err = chn_addfeeder(c, fc, &desc); + if (err != 0) + return err; } sndbuf_setfmt(c->bufhard, hwfmt); if ((flags & (1 << FEEDER_VOLUME))) { u_int32_t parent = SOUND_MIXER_NONE; int vol, left, right; vol = 100 | (100 << 8); CHN_UNLOCK(c); /* * XXX This is ugly! The way mixer subs being so secretive * about its own internals force us to use this silly * monkey trick. */ if (mixer_ioctl(c->parentsnddev->mixer_dev, MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&vol, -1, NULL) != 0) device_printf(c->dev, "Soft PCM Volume: Failed to read default value\n"); left = vol & 0x7f; right = (vol >> 8) & 0x7f; if (c->parentsnddev != NULL && c->parentsnddev->mixer_dev != NULL && c->parentsnddev->mixer_dev->si_drv1 != NULL) parent = mix_getparent( c->parentsnddev->mixer_dev->si_drv1, SOUND_MIXER_PCM); if (parent != SOUND_MIXER_NONE) { vol = 100 | (100 << 8); if (mixer_ioctl(c->parentsnddev->mixer_dev, MIXER_READ(parent), (caddr_t)&vol, -1, NULL) != 0) device_printf(c->dev, "Soft Volume: Failed to read parent default value\n"); left = (left * (vol & 0x7f)) / 100; right = (right * ((vol >> 8) & 0x7f)) / 100; } CHN_LOCK(c); chn_setvolume(c, left, right); } return 0; } int chn_notify(struct pcm_channel *c, u_int32_t flags) { - struct pcmchan_children *pce; - struct pcm_channel *child; int run; CHN_LOCK(c); - if (SLIST_EMPTY(&c->children)) { + if (CHN_EMPTY(c, children)) { CHN_UNLOCK(c); return ENODEV; } run = (CHN_STARTED(c)) ? 1 : 0; /* * if the hwchan is running, we can't change its rate, format or * blocksize */ if (run) flags &= CHN_N_VOLUME | CHN_N_TRIGGER; if (flags & CHN_N_RATE) { /* * we could do something here, like scan children and decide on * the most appropriate rate to mix at, but we don't for now */ } if (flags & CHN_N_FORMAT) { /* * we could do something here, like scan children and decide on * the most appropriate mixer feeder to use, but we don't for now */ } if (flags & CHN_N_VOLUME) { /* * we could do something here but we don't for now */ } if (flags & CHN_N_BLOCKSIZE) { /* * Set to default latency profile */ chn_setlatency(c, chn_latency); } if (flags & CHN_N_TRIGGER) { int nrun; - /* - * scan the children, and figure out if any are running - * if so, we need to be running, otherwise we need to be stopped - * if we aren't in our target sstate, move to it - */ - nrun = 0; - SLIST_FOREACH(pce, &c->children, link) { - child = pce->channel; - CHN_LOCK(child); - nrun = CHN_STARTED(child); - CHN_UNLOCK(child); - if (nrun) - break; - } + + nrun = CHN_EMPTY(c, children.busy) ? 0 : 1; if (nrun && !run) chn_start(c, 1); if (!nrun && run) chn_abort(c); } CHN_UNLOCK(c); return 0; } /** * @brief Fetch array of supported discrete sample rates * * Wrapper for CHANNEL_GETRATES. Please see channel_if.m:getrates() for * detailed information. * * @note If the operation isn't supported, this function will just return 0 * (no rates in the array), and *rates will be set to NULL. Callers * should examine rates @b only if this function returns non-zero. * * @param c pcm channel to examine * @param rates pointer to array of integers; rate table will be recorded here * * @return number of rates in the array pointed to be @c rates */ int chn_getrates(struct pcm_channel *c, int **rates) { KASSERT(rates != NULL, ("rates is null")); CHN_LOCKASSERT(c); return CHANNEL_GETRATES(c->methods, c->devinfo, rates); } /** * @brief Remove channel from a sync group, if there is one. * * This function is initially intended for the following conditions: * - Starting a syncgroup (@c SNDCTL_DSP_SYNCSTART ioctl) * - Closing a device. (A channel can't be destroyed if it's still in use.) * * @note Before calling this function, the syncgroup list mutex must be * held. (Consider pcm_channel::sm protected by the SG list mutex * whether @c c is locked or not.) * * @param c channel device to be started or closed * @returns If this channel was the only member of a group, the group ID * is returned to the caller so that the caller can release it * via free_unr() after giving up the syncgroup lock. Else it * returns 0. */ int chn_syncdestroy(struct pcm_channel *c) { struct pcmchan_syncmember *sm; struct pcmchan_syncgroup *sg; int sg_id; sg_id = 0; PCM_SG_LOCKASSERT(MA_OWNED); if (c->sm != NULL) { sm = c->sm; sg = sm->parent; c->sm = NULL; KASSERT(sg != NULL, ("syncmember has null parent")); SLIST_REMOVE(&sg->members, sm, pcmchan_syncmember, link); free(sm, M_DEVBUF); if (SLIST_EMPTY(&sg->members)) { SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link); sg_id = sg->id; free(sg, M_DEVBUF); } } return sg_id; } void chn_lock(struct pcm_channel *c) { CHN_LOCK(c); } void chn_unlock(struct pcm_channel *c) { CHN_UNLOCK(c); } #ifdef OSSV4_EXPERIMENT int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak) { CHN_LOCKASSERT(c); return CHANNEL_GETPEAKS(c->methods, c->devinfo, lpeak, rpeak); } #endif Index: head/sys/dev/sound/pcm/channel.h =================================================================== --- head/sys/dev/sound/pcm/channel.h (revision 170160) +++ head/sys/dev/sound/pcm/channel.h (revision 170161) @@ -1,270 +1,345 @@ /*- * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ -struct pcmchan_children { - SLIST_ENTRY(pcmchan_children) link; - struct pcm_channel *channel; -}; - struct pcmchan_caps { u_int32_t minspeed, maxspeed; u_int32_t *fmtlist; u_int32_t caps; }; /* Forward declarations */ struct pcm_channel; struct pcmchan_syncgroup; struct pcmchan_syncmember; extern struct mtx snd_pcm_syncgroups_mtx; extern SLIST_HEAD(pcm_synclist, pcmchan_syncgroup) snd_pcm_syncgroups; #define PCM_SG_LOCK() mtx_lock(&snd_pcm_syncgroups_mtx) #define PCM_SG_TRYLOCK() mtx_trylock(&snd_pcm_syncgroups_mtx) #define PCM_SG_UNLOCK() mtx_unlock(&snd_pcm_syncgroups_mtx) #define PCM_SG_LOCKASSERT(arg) mtx_assert(&snd_pcm_syncgroups_mtx, arg) /** * @brief Specifies an audio device sync group */ struct pcmchan_syncgroup { SLIST_ENTRY(pcmchan_syncgroup) link; SLIST_HEAD(, pcmchan_syncmember) members; int id; /**< Group identifier; set to address of group. */ }; /** * @brief Specifies a container for members of a sync group */ struct pcmchan_syncmember { SLIST_ENTRY(pcmchan_syncmember) link; struct pcmchan_syncgroup *parent; /**< group head */ struct pcm_channel *ch; }; #define CHN_NAMELEN 32 struct pcm_channel { kobj_t methods; - int num; pid_t pid; int refcount; struct pcm_feeder *feeder; u_int32_t align; int volume; int latency; u_int32_t speed; u_int32_t format; u_int32_t flags; u_int32_t feederflags; u_int32_t blocks; int direction; unsigned int interrupts, xruns, feedcount; unsigned int timeout; struct snd_dbuf *bufhard, *bufsoft; struct snddev_info *parentsnddev; struct pcm_channel *parentchannel; void *devinfo; device_t dev; + int unit; char name[CHN_NAMELEN]; struct mtx *lock; + int trigger; /** * Increment,decrement this around operations that temporarily yield * lock. */ unsigned int inprog; /** * Special channel operations should examine @c inprog after acquiring * lock. If zero, operations may continue. Else, thread should * wait on this cv for previous operation to finish. */ struct cv cv; /** * Low water mark for select()/poll(). * * This is initialized to the channel's fragment size, and will be * overwritten if a new fragment size is set. Users may alter this * value directly with the @c SNDCTL_DSP_LOW_WATER ioctl. */ unsigned int lw; /** * If part of a sync group, this will point to the syncmember * container. */ struct pcmchan_syncmember *sm; #ifdef OSSV4_EXPERIMENT u_int16_t lpeak, rpeak; /**< Peak value from 0-32767. */ #endif - SLIST_HEAD(, pcmchan_children) children; + + struct { + SLIST_HEAD(, pcm_channel) head; + SLIST_ENTRY(pcm_channel) link; + struct { + SLIST_HEAD(, pcm_channel) head; + SLIST_ENTRY(pcm_channel) link; + } busy; + } children; + + struct { + struct { + SLIST_ENTRY(pcm_channel) link; + struct { + SLIST_ENTRY(pcm_channel) link; + } busy; + } pcm; + } channels; + + void *data1, *data2; }; +#define CHN_HEAD(x, y) &(x)->y.head +#define CHN_INIT(x, y) SLIST_INIT(CHN_HEAD(x, y)) +#define CHN_LINK(y) y.link +#define CHN_EMPTY(x, y) SLIST_EMPTY(CHN_HEAD(x, y)) +#define CHN_FIRST(x, y) SLIST_FIRST(CHN_HEAD(x, y)) + +#define CHN_FOREACH(x, y, z) \ + SLIST_FOREACH(x, CHN_HEAD(y, z), CHN_LINK(z)) + +#define CHN_FOREACH_SAFE(w, x, y, z) \ + SLIST_FOREACH_SAFE(w, CHN_HEAD(x, z), CHN_LINK(z), y) + +#define CHN_INSERT_HEAD(x, y, z) \ + SLIST_INSERT_HEAD(CHN_HEAD(x, z), y, CHN_LINK(z)) + +#define CHN_INSERT_AFTER(x, y, z) \ + SLIST_INSERT_AFTER(x, y, CHN_LINK(z)) + +#define CHN_REMOVE(x, y, z) \ + SLIST_REMOVE(CHN_HEAD(x, z), y, pcm_channel, CHN_LINK(z)) + +#define CHN_INSERT_HEAD_SAFE(x, y, z) do { \ + struct pcm_channel *t = NULL; \ + CHN_FOREACH(t, x, z) { \ + if (t == y) \ + break; \ + } \ + if (t != y) { \ + CHN_INSERT_HEAD(x, y, z); \ + } \ +} while(0) + +#define CHN_INSERT_AFTER_SAFE(w, x, y, z) do { \ + struct pcm_channel *t = NULL; \ + CHN_FOREACH(t, w, z) { \ + if (t == y) \ + break; \ + } \ + if (t != y) { \ + CHN_INSERT_AFTER(x, y, z); \ + } \ +} while(0) + +#define CHN_REMOVE_SAFE(x, y, z) do { \ + struct pcm_channel *t = NULL; \ + CHN_FOREACH(t, x, z) { \ + if (t == y) \ + break; \ + } \ + if (t == y) { \ + CHN_REMOVE(x, y, z); \ + } \ +} while(0) + +#define CHN_UNIT(x) (snd_unit2u((x)->unit)) +#define CHN_DEV(x) (snd_unit2d((x)->unit)) +#define CHN_CHAN(x) (snd_unit2c((x)->unit)) + #include "channel_if.h" int chn_reinit(struct pcm_channel *c); int chn_write(struct pcm_channel *c, struct uio *buf); int chn_read(struct pcm_channel *c, struct uio *buf); u_int32_t chn_start(struct pcm_channel *c, int force); int chn_sync(struct pcm_channel *c, int threshold); int chn_flush(struct pcm_channel *c); int chn_poll(struct pcm_channel *c, int ev, struct thread *td); int chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction); int chn_kill(struct pcm_channel *c); int chn_setdir(struct pcm_channel *c, int dir); int chn_reset(struct pcm_channel *c, u_int32_t fmt); int chn_setvolume(struct pcm_channel *c, int left, int right); int chn_setspeed(struct pcm_channel *c, int speed); int chn_setformat(struct pcm_channel *c, u_int32_t fmt); int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz); int chn_setlatency(struct pcm_channel *c, int latency); int chn_trigger(struct pcm_channel *c, int go); int chn_getptr(struct pcm_channel *c); struct pcmchan_caps *chn_getcaps(struct pcm_channel *c); u_int32_t chn_getformats(struct pcm_channel *c); void chn_resetbuf(struct pcm_channel *c); void chn_intr(struct pcm_channel *c); int chn_wrfeed(struct pcm_channel *c); int chn_rdfeed(struct pcm_channel *c); int chn_abort(struct pcm_channel *c); void chn_wrupdate(struct pcm_channel *c); void chn_rdupdate(struct pcm_channel *c); int chn_notify(struct pcm_channel *c, u_int32_t flags); void chn_lock(struct pcm_channel *c); void chn_unlock(struct pcm_channel *c); int chn_getrates(struct pcm_channel *c, int **rates); int chn_syncdestroy(struct pcm_channel *c); #ifdef OSSV4_EXPERIMENT int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak); #endif #ifdef USING_MUTEX #define CHN_LOCK(c) mtx_lock((struct mtx *)((c)->lock)) #define CHN_UNLOCK(c) mtx_unlock((struct mtx *)((c)->lock)) #define CHN_TRYLOCK(c) mtx_trylock((struct mtx *)((c)->lock)) #define CHN_LOCKASSERT(c) mtx_assert((struct mtx *)((c)->lock), MA_OWNED) #else #define CHN_LOCK(c) #define CHN_UNLOCK(c) #define CHN_TRYLOCK(c) #define CHN_LOCKASSERT(c) #endif int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist); #define AFMTSTR_NONE 0 /* "s16le" */ #define AFMTSTR_SIMPLE 1 /* "s16le:s" */ #define AFMTSTR_NUM 2 /* "s16le:2" */ #define AFMTSTR_FULL 3 /* "s16le:stereo" */ #define AFMTSTR_MAXSZ 13 /* include null terminator */ #define AFMTSTR_MONO_RETURN 0 #define AFMTSTR_STEREO_RETURN 1 struct afmtstr_table { char *fmtstr; u_int32_t format; }; int afmtstr_swap_sign(char *); int afmtstr_swap_endian(char *); u_int32_t afmtstr2afmt(struct afmtstr_table *, const char *, int); u_int32_t afmt2afmtstr(struct afmtstr_table *, u_int32_t, char *, size_t, int, int); extern int chn_latency; extern int chn_latency_profile; extern int report_soft_formats; -#define PCMDIR_VIRTUAL 2 -#define PCMDIR_PLAY 1 -#define PCMDIR_REC -1 +#define PCMDIR_PLAY 1 +#define PCMDIR_PLAY_VIRTUAL 2 +#define PCMDIR_REC -1 +#define PCMDIR_REC_VIRTUAL -2 #define PCMTRIG_START 1 #define PCMTRIG_EMLDMAWR 2 #define PCMTRIG_EMLDMARD 3 #define PCMTRIG_STOP 0 #define PCMTRIG_ABORT -1 #define CHN_F_CLOSING 0x00000004 /* a pending close */ #define CHN_F_ABORTING 0x00000008 /* a pending abort */ #define CHN_F_RUNNING 0x00000010 /* dma is running */ #define CHN_F_TRIGGERED 0x00000020 #define CHN_F_NOTRIGGER 0x00000040 +#define CHN_F_SLEEPING 0x00000080 #define CHN_F_BUSY 0x00001000 /* has been opened */ #define CHN_F_HAS_SIZE 0x00002000 /* user set block size */ #define CHN_F_NBIO 0x00004000 /* do non-blocking i/o */ #define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */ #define CHN_F_DEAD 0x00020000 #define CHN_F_BADSETTING 0x00040000 #define CHN_F_SETBLOCKSIZE 0x00080000 #define CHN_F_HAS_VCHAN 0x00100000 #define CHN_F_VIRTUAL 0x10000000 /* not backed by hardware */ #define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | \ CHN_F_HAS_VCHAN | CHN_F_VIRTUAL) #define CHN_N_RATE 0x00000001 #define CHN_N_FORMAT 0x00000002 #define CHN_N_VOLUME 0x00000004 #define CHN_N_BLOCKSIZE 0x00000008 #define CHN_N_TRIGGER 0x00000010 #define CHN_LATENCY_MIN 0 #define CHN_LATENCY_MAX 10 #define CHN_LATENCY_DEFAULT 5 #define CHN_POLICY_MIN CHN_LATENCY_MIN #define CHN_POLICY_MAX CHN_LATENCY_MAX #define CHN_POLICY_DEFAULT CHN_LATENCY_DEFAULT #define CHN_LATENCY_PROFILE_MIN 0 #define CHN_LATENCY_PROFILE_MAX 1 #define CHN_LATENCY_PROFILE_DEFAULT CHN_LATENCY_PROFILE_MAX /* * This should be large enough to hold all pcm data between * tsleeps in chn_{read,write} at the highest sample rate. * (which is usually 48kHz * 16bit * stereo = 192000 bytes/sec) */ #define CHN_2NDBUFBLKSIZE (2 * 1024) /* The total number of blocks per secondary bufhard. */ #define CHN_2NDBUFBLKNUM (32) /* The size of a whole secondary bufhard. */ #define CHN_2NDBUFMAXSIZE (131072) #define CHANNEL_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj)) Index: head/sys/dev/sound/pcm/dsp.c =================================================================== --- head/sys/dev/sound/pcm/dsp.c (revision 170160) +++ head/sys/dev/sound/pcm/dsp.c (revision 170161) @@ -1,2360 +1,2632 @@ /*- * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#include - #include +#include SND_DECLARE_FILE("$FreeBSD$"); +struct dsp_cdevinfo { + struct pcm_channel *rdch, *wrch; +}; + +#define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch) +#define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch) + +#define PCMDEV_ACQUIRE(x) do { \ + if ((x)->si_drv1 == NULL) \ + (x)->si_drv1 = x; \ +} while(0) + +#define PCMDEV_RELEASE(x) do { \ + if ((x)->si_drv1 == x) \ + (x)->si_drv1 = NULL; \ +} while(0) + #define OLDPCM_IOCTL static d_open_t dsp_open; static d_close_t dsp_close; static d_read_t dsp_read; static d_write_t dsp_write; static d_ioctl_t dsp_ioctl; static d_poll_t dsp_poll; static d_mmap_t dsp_mmap; struct cdevsw dsp_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = dsp_open, .d_close = dsp_close, .d_read = dsp_read, .d_write = dsp_write, .d_ioctl = dsp_ioctl, .d_poll = dsp_poll, .d_mmap = dsp_mmap, .d_name = "dsp", }; #ifdef USING_DEVFS -static eventhandler_tag dsp_ehtag; +static eventhandler_tag dsp_ehtag = NULL; +static int dsp_umax = -1; +static int dsp_cmax = -1; #endif static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group); static int dsp_oss_syncstart(int sg_id); static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy); #ifdef OSSV4_EXPERIMENT static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled); static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); static int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); static int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song); static int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song); static int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name); #endif static struct snddev_info * dsp_get_info(struct cdev *dev) { - struct snddev_info *d; - int unit; - - unit = PCMUNIT(dev); - if (unit >= devclass_get_maxunit(pcm_devclass)) - return NULL; - d = devclass_get_softc(pcm_devclass, unit); - - return d; + return (devclass_get_softc(pcm_devclass, PCMUNIT(dev))); } static u_int32_t dsp_get_flags(struct cdev *dev) { device_t bdev; - int unit; - unit = PCMUNIT(dev); - if (unit >= devclass_get_maxunit(pcm_devclass)) - return 0xffffffff; - bdev = devclass_get_device(pcm_devclass, unit); + bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev)); - return pcm_getflags(bdev); + return ((bdev != NULL) ? pcm_getflags(bdev) : 0xffffffff); } static void dsp_set_flags(struct cdev *dev, u_int32_t flags) { device_t bdev; - int unit; - unit = PCMUNIT(dev); - if (unit >= devclass_get_maxunit(pcm_devclass)) - return; - bdev = devclass_get_device(pcm_devclass, unit); + bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev)); - pcm_setflags(bdev, flags); + if (bdev != NULL) + pcm_setflags(bdev, flags); } /* * return the channels associated with an open device instance. * set the priority if the device is simplex and one direction (only) is * specified. * lock channels specified. */ static int getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio) { struct snddev_info *d; u_int32_t flags; - flags = dsp_get_flags(dev); d = dsp_get_info(dev); + if (d == NULL) + return -1; pcm_inprog(d, 1); pcm_lock(d); + flags = dsp_get_flags(dev); KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \ ("getchns: read and write both prioritised")); if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) { flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR); dsp_set_flags(dev, flags); } - *rdch = dev->si_drv1; - *wrch = dev->si_drv2; + *rdch = PCM_RDCH(dev); + *wrch = PCM_WRCH(dev); if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) { if (prio) { if (*rdch && flags & SD_F_PRIO_WR) { - dev->si_drv1 = NULL; + PCM_RDCH(dev) = NULL; *rdch = pcm_getfakechan(d); } else if (*wrch && flags & SD_F_PRIO_RD) { - dev->si_drv2 = NULL; + PCM_WRCH(dev) = NULL; *wrch = pcm_getfakechan(d); } } pcm_getfakechan(d)->flags |= CHN_F_BUSY; } pcm_unlock(d); if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD)) CHN_LOCK(*rdch); if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR)) CHN_LOCK(*wrch); return 0; } /* unlock specified channels */ static void relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio) { struct snddev_info *d; d = dsp_get_info(dev); + if (d == NULL) + return; if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR)) CHN_UNLOCK(wrch); if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD)) CHN_UNLOCK(rdch); pcm_inprog(d, -1); } +static void +dsp_cdevinfo_alloc(struct cdev *dev, + struct pcm_channel *rdch, struct pcm_channel *wrch) +{ + KASSERT(dev != NULL && dev->si_drv1 == dev && rdch != wrch, + ("bogus %s(), what are you trying to accomplish here?", __func__)); + + dev->si_drv1 = malloc(sizeof(struct dsp_cdevinfo), M_DEVBUF, + M_WAITOK | M_ZERO); + PCM_RDCH(dev) = rdch; + PCM_WRCH(dev) = wrch; +} + +static void +dsp_cdevinfo_free(struct cdev *dev) +{ + KASSERT(dev != NULL && dev->si_drv1 != NULL && + PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL, + ("bogus %s(), what are you trying to accomplish here?", __func__)); + + free(dev->si_drv1, M_DEVBUF); + dev->si_drv1 = NULL; +} + +/* duplex / simplex cdev type */ +enum { + DSP_CDEV_TYPE_RDONLY, /* simplex read-only (record) */ + DSP_CDEV_TYPE_WRONLY, /* simplex write-only (play) */ + DSP_CDEV_TYPE_RDWR, /* duplex read, write, or both */ +}; + +#define DSP_F_VALID(x) ((x) & (FREAD | FWRITE)) +#define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE)) +#define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x)) +#define DSP_F_READ(x) ((x) & FREAD) +#define DSP_F_WRITE(x) ((x) & FWRITE) + +static const struct { + int type; + char *name; + char *sep; + int use_sep; + int hw; + int max; + uint32_t fmt, spd; + int query; +} dsp_cdevs[] = { + { SND_DEV_DSP, "dsp", ".", 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_AUDIO, "audio", ".", 0, 0, 0, + AFMT_MU_LAW, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP16, "dspW", ".", 0, 0, 0, + AFMT_S16_LE, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSPHW_PLAY, "dsp", ".p", 1, 1, SND_MAXHWCHAN, + AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, + { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", 1, 1, SND_MAXVCHANS, + AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, + { SND_DEV_DSPHW_REC, "dsp", ".r", 1, 1, SND_MAXHWCHAN, + AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, + { SND_DEV_DSPHW_VREC, "dsp", ".vr", 1, 1, SND_MAXVCHANS, + AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, +}; + static int dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) { struct pcm_channel *rdch, *wrch; struct snddev_info *d; - u_int32_t fmt; - int devtype; - int error; - int chnum; + uint32_t fmt, spd; + int i, error, devtype; + int wdevunit, rdevunit; + /* Kind of impossible.. */ if (i_dev == NULL || td == NULL) return ENODEV; - if ((flags & (FREAD | FWRITE)) == 0) - return EINVAL; - + /* This too.. */ d = dsp_get_info(i_dev); - devtype = PCMDEV(i_dev); - chnum = -1; + if (d == NULL) + return EBADF; - /* decide default format */ - switch (devtype) { - case SND_DEV_DSP16: - fmt = AFMT_S16_LE; - break; + /* Lock snddev so nobody else can monkey with it. */ + pcm_lock(d); - case SND_DEV_DSP: - fmt = AFMT_U8; - break; + /* + * Try to acquire cloned device before someone else pick it. + * ENODEV means this is not a cloned droids. + */ + error = snd_clone_acquire(i_dev); + if (!(error == 0 || error == ENODEV)) { + pcm_unlock(d); + return error; + } - case SND_DEV_AUDIO: - fmt = AFMT_MU_LAW; - break; + if (!DSP_F_VALID(flags)) + error = EINVAL; + else if (i_dev->si_drv1 != NULL) + error = EBUSY; + else if (DSP_F_DUPLEX(flags) && + (dsp_get_flags(i_dev) & SD_F_SIMPLEX)) + error = ENOTSUP; + else + error = 0; - case SND_DEV_NORESET: - fmt = 0; - break; - - case SND_DEV_DSPHW: - /* - * HW *specific* access without channel numbering confusion - * caused by "first come first served" by dsp_clone(). - */ - fmt = AFMT_U8; - chnum = PCMCHAN(i_dev); - break; - - default: - panic("impossible devtype %d", devtype); + if (error != 0) { + (void)snd_clone_release(i_dev); + pcm_unlock(d); + return error; } - /* lock snddev so nobody else can monkey with it */ - pcm_lock(d); + /* + * Fake busy state by pointing si_drv1 to something else since + * we have to give up locking somewhere during setup process. + */ + PCMDEV_ACQUIRE(i_dev); - rdch = i_dev->si_drv1; - wrch = i_dev->si_drv2; + devtype = PCMDEV(i_dev); + wdevunit = -1; + rdevunit = -1; + fmt = 0; + spd = 0; - if (rdch || wrch || ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && - (flags & (FREAD | FWRITE)) == (FREAD | FWRITE))) { - /* simplex or not, better safe than sorry. */ - pcm_unlock(d); - return EBUSY; + for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { + if (devtype != dsp_cdevs[i].type) + continue; + if (DSP_F_SIMPLEX(flags) && + ((dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY && + DSP_F_READ(flags)) || + (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY && + DSP_F_WRITE(flags)))) { + /* + * simplex, opposite direction? Please be gone.. + */ + (void)snd_clone_release(i_dev); + PCMDEV_RELEASE(i_dev); + pcm_unlock(d); + return ENOTSUP; + } + if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) + wdevunit = dev2unit(i_dev); + else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) + rdevunit = dev2unit(i_dev); + fmt = dsp_cdevs[i].fmt; + spd = dsp_cdevs[i].spd; + break; } + /* No matching devtype? */ + if (fmt == 0 || spd == 0) + panic("impossible devtype %d", devtype); + + rdch = NULL; + wrch = NULL; + /* * if we get here, the open request is valid- either: * * we were previously not open * * we were open for play xor record and the opener wants * the non-open direction */ - if (flags & FREAD) { + if (DSP_F_READ(flags)) { /* open for read */ pcm_unlock(d); - error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, chnum); - if (error != 0 && error != EBUSY && chnum != -1 && (flags & FWRITE)) - error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, -1); + error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, + rdevunit); - if (error == 0 && (chn_reset(rdch, fmt) || - (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED)))) + if (error == 0 && (chn_reset(rdch, fmt) != 0 || + (chn_setspeed(rdch, spd) != 0))) error = ENODEV; if (error != 0) { - if (rdch) + if (rdch != NULL) pcm_chnrelease(rdch); + pcm_lock(d); + (void)snd_clone_release(i_dev); + PCMDEV_RELEASE(i_dev); + pcm_unlock(d); return error; } if (flags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO; pcm_chnref(rdch, 1); CHN_UNLOCK(rdch); pcm_lock(d); } - if (flags & FWRITE) { - /* open for write */ - pcm_unlock(d); - error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, chnum); - if (error != 0 && error != EBUSY && chnum != -1 && (flags & FREAD)) - error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, -1); + if (DSP_F_WRITE(flags)) { + /* open for write */ + pcm_unlock(d); + error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, + wdevunit); - if (error == 0 && (chn_reset(wrch, fmt) || - (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED)))) - error = ENODEV; + if (error == 0 && (chn_reset(wrch, fmt) != 0 || + (chn_setspeed(wrch, spd) != 0))) + error = ENODEV; - if (error != 0) { - if (wrch) - pcm_chnrelease(wrch); - if (rdch) { - /* - * Lock, deref and release previously created record channel - */ - CHN_LOCK(rdch); - pcm_chnref(rdch, -1); - pcm_chnrelease(rdch); + if (error != 0) { + if (wrch != NULL) + pcm_chnrelease(wrch); + if (rdch != NULL) { + /* + * Lock, deref and release previously + * created record channel + */ + CHN_LOCK(rdch); + pcm_chnref(rdch, -1); + pcm_chnrelease(rdch); + } + pcm_lock(d); + (void)snd_clone_release(i_dev); + PCMDEV_RELEASE(i_dev); + pcm_unlock(d); + return error; } - return error; - } - - if (flags & O_NONBLOCK) - wrch->flags |= CHN_F_NBIO; - pcm_chnref(wrch, 1); - CHN_UNLOCK(wrch); - pcm_lock(d); + if (flags & O_NONBLOCK) + wrch->flags |= CHN_F_NBIO; + pcm_chnref(wrch, 1); + CHN_UNLOCK(wrch); + pcm_lock(d); } - i_dev->si_drv1 = rdch; - i_dev->si_drv2 = wrch; + /* + * Increase clone refcount for its automatic garbage collector. + */ + (void)snd_clone_ref(i_dev); pcm_unlock(d); + + /* + * We're done. Allocate and point si_drv1 to a real + * allocated structure. + */ + dsp_cdevinfo_alloc(i_dev, rdch, wrch); + return 0; } static int dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) { struct pcm_channel *rdch, *wrch; struct snddev_info *d; int refs, sg_ids[2]; d = dsp_get_info(i_dev); + if (d == NULL) + return EBADF; pcm_lock(d); - rdch = i_dev->si_drv1; - wrch = i_dev->si_drv2; - pcm_unlock(d); + rdch = PCM_RDCH(i_dev); + wrch = PCM_WRCH(i_dev); /* * Free_unr() may sleep, so store released syncgroup IDs until after * all locks are released. */ sg_ids[0] = sg_ids[1] = 0; if (rdch || wrch) { refs = 0; + pcm_unlock(d); if (rdch) { /* * The channel itself need not be locked because: * a) Adding a channel to a syncgroup happens only in dsp_ioctl(), * which cannot run concurrently to dsp_close(). * b) The syncmember pointer (sm) is protected by the global * syncgroup list lock. * c) A channel can't just disappear, invalidating pointers, * unless it's closed/dereferenced first. */ PCM_SG_LOCK(); sg_ids[0] = chn_syncdestroy(rdch); PCM_SG_UNLOCK(); CHN_LOCK(rdch); refs += pcm_chnref(rdch, -1); chn_abort(rdch); /* won't sleep */ rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); chn_reset(rdch, 0); pcm_chnrelease(rdch); } if (wrch) { /* * Please see block above. */ PCM_SG_LOCK(); sg_ids[1] = chn_syncdestroy(wrch); PCM_SG_UNLOCK(); CHN_LOCK(wrch); refs += pcm_chnref(wrch, -1); chn_flush(wrch); /* may sleep */ wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); chn_reset(wrch, 0); pcm_chnrelease(wrch); } pcm_lock(d); if (rdch) - i_dev->si_drv1 = NULL; + PCM_RDCH(i_dev) = NULL; if (wrch) - i_dev->si_drv2 = NULL; + PCM_WRCH(i_dev) = NULL; /* * If there are no more references, release the channels. */ - if (refs == 0 && i_dev->si_drv1 == NULL && - i_dev->si_drv2 == NULL) { + if (refs == 0 && PCM_RDCH(i_dev) == NULL && + PCM_WRCH(i_dev) == NULL) { if (pcm_getfakechan(d)) pcm_getfakechan(d)->flags = 0; /* What is this?!? */ dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT); + dsp_cdevinfo_free(i_dev); + /* + * Release clone busy state and unref it + * so the automatic garbage collector will + * get the hint and do the remaining cleanup + * process. + */ + (void)snd_clone_release(i_dev); + (void)snd_clone_unref(i_dev); } - pcm_unlock(d); } + pcm_unlock(d); if (sg_ids[0]) free_unr(pcmsg_unrhdr, sg_ids[0]); if (sg_ids[1]) free_unr(pcmsg_unrhdr, sg_ids[1]); return 0; } static int dsp_read(struct cdev *i_dev, struct uio *buf, int flag) { struct pcm_channel *rdch, *wrch; int ret; getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD); - KASSERT(rdch, ("dsp_read: nonexistant channel")); - KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel")); + if (rdch == NULL || !(rdch->flags & CHN_F_BUSY)) + return EBADF; if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); return EINVAL; } if (!(rdch->flags & CHN_F_RUNNING)) rdch->flags |= CHN_F_RUNNING; ret = chn_read(rdch, buf); relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); return ret; } static int dsp_write(struct cdev *i_dev, struct uio *buf, int flag) { struct pcm_channel *rdch, *wrch; int ret; getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR); - KASSERT(wrch, ("dsp_write: nonexistant channel")); - KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel")); + if (wrch == NULL || !(wrch->flags & CHN_F_BUSY)) + return EBADF; if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); return EINVAL; } if (!(wrch->flags & CHN_F_RUNNING)) wrch->flags |= CHN_F_RUNNING; /* * Chn_write() must give up channel lock in order to copy bytes from * userland, so up the "in progress" counter to make sure someone * else doesn't come along and muss up the buffer. */ ++wrch->inprog; ret = chn_write(wrch, buf); --wrch->inprog; cv_signal(&wrch->cv); relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); return ret; } static int dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) { struct pcm_channel *chn, *rdch, *wrch; struct snddev_info *d; int kill; int ret = 0, *arg_i = (int *)arg, tmp; int xcmd; xcmd = 0; /* * this is an evil hack to allow broken apps to perform mixer ioctls * on dsp devices. */ d = dsp_get_info(i_dev); + if (d == NULL) + return EBADF; if (IOCGROUP(cmd) == 'M') { /* * This is at least, a bug to bug compatible with OSS. */ if (d->mixer_dev != NULL) return mixer_ioctl(d->mixer_dev, cmd, arg, -1, td); else return EBADF; } /* * Certain ioctls may be made on any type of device (audio, mixer, * and MIDI). Handle those special cases here. */ if (IOCGROUP(cmd) == 'X') { switch(cmd) { case SNDCTL_SYSINFO: sound_oss_sysinfo((oss_sysinfo *)arg); break; case SNDCTL_AUDIOINFO: ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg); break; case SNDCTL_MIXERINFO: ret = mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg); break; default: ret = EINVAL; } return ret; } getchns(i_dev, &rdch, &wrch, 0); kill = 0; if (wrch && (wrch->flags & CHN_F_DEAD)) kill |= 1; if (rdch && (rdch->flags & CHN_F_DEAD)) kill |= 2; if (kill == 3) { relchns(i_dev, rdch, wrch, 0); return EINVAL; } if (kill & 1) wrch = NULL; if (kill & 2) rdch = NULL; - + switch(cmd) { #ifdef OLDPCM_IOCTL /* * we start with the new ioctl interface. */ case AIONWRITE: /* how many bytes can write ? */ if (wrch) { CHN_LOCK(wrch); /* if (wrch && wrch->bufhard.dl) while (chn_wrfeed(wrch) == 0); */ *arg_i = sndbuf_getfree(wrch->bufsoft); CHN_UNLOCK(wrch); } else { *arg_i = 0; ret = EINVAL; } break; case AIOSSIZE: /* set the current blocksize */ { struct snd_size *p = (struct snd_size *)arg; p->play_size = 0; p->rec_size = 0; if (wrch) { CHN_LOCK(wrch); chn_setblocksize(wrch, 2, p->play_size); p->play_size = sndbuf_getblksz(wrch->bufsoft); CHN_UNLOCK(wrch); } if (rdch) { CHN_LOCK(rdch); chn_setblocksize(rdch, 2, p->rec_size); p->rec_size = sndbuf_getblksz(rdch->bufsoft); CHN_UNLOCK(rdch); } } break; case AIOGSIZE: /* get the current blocksize */ { struct snd_size *p = (struct snd_size *)arg; if (wrch) { CHN_LOCK(wrch); p->play_size = sndbuf_getblksz(wrch->bufsoft); CHN_UNLOCK(wrch); } if (rdch) { CHN_LOCK(rdch); p->rec_size = sndbuf_getblksz(rdch->bufsoft); CHN_UNLOCK(rdch); } } break; case AIOSFMT: case AIOGFMT: { snd_chan_param *p = (snd_chan_param *)arg; if (cmd == AIOSFMT && ((p->play_format != 0 && p->play_rate == 0) || (p->rec_format != 0 && p->rec_rate == 0))) { ret = EINVAL; break; } if (wrch) { CHN_LOCK(wrch); if (cmd == AIOSFMT && p->play_format != 0) { chn_setformat(wrch, p->play_format); chn_setspeed(wrch, p->play_rate); } p->play_rate = wrch->speed; p->play_format = wrch->format; CHN_UNLOCK(wrch); } else { p->play_rate = 0; p->play_format = 0; } if (rdch) { CHN_LOCK(rdch); if (cmd == AIOSFMT && p->rec_format != 0) { chn_setformat(rdch, p->rec_format); chn_setspeed(rdch, p->rec_rate); } p->rec_rate = rdch->speed; p->rec_format = rdch->format; CHN_UNLOCK(rdch); } else { p->rec_rate = 0; p->rec_format = 0; } } break; case AIOGCAP: /* get capabilities */ { snd_capabilities *p = (snd_capabilities *)arg; struct pcmchan_caps *pcaps = NULL, *rcaps = NULL; struct cdev *pdev; if (rdch) { CHN_LOCK(rdch); rcaps = chn_getcaps(rdch); } if (wrch) { CHN_LOCK(wrch); pcaps = chn_getcaps(wrch); } p->rate_min = max(rcaps? rcaps->minspeed : 0, pcaps? pcaps->minspeed : 0); p->rate_max = min(rcaps? rcaps->maxspeed : 1000000, pcaps? pcaps->maxspeed : 1000000); p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000, wrch? sndbuf_getsize(wrch->bufsoft) : 1000000); /* XXX bad on sb16 */ p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) & (wrch? chn_getformats(wrch) : 0xffffffff); if (rdch && wrch) p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX; pdev = d->mixer_dev; p->mixers = 1; /* default: one mixer */ p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0; p->left = p->right = 100; if (rdch) CHN_UNLOCK(rdch); if (wrch) CHN_UNLOCK(wrch); } break; case AIOSTOP: if (*arg_i == AIOSYNC_PLAY && wrch) { CHN_LOCK(wrch); *arg_i = chn_abort(wrch); CHN_UNLOCK(wrch); } else if (*arg_i == AIOSYNC_CAPTURE && rdch) { CHN_LOCK(rdch); *arg_i = chn_abort(rdch); CHN_UNLOCK(rdch); } else { printf("AIOSTOP: bad channel 0x%x\n", *arg_i); *arg_i = 0; } break; case AIOSYNC: printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n", ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos); break; #endif /* * here follow the standard ioctls (filio.h etc.) */ case FIONREAD: /* get # bytes to read */ if (rdch) { CHN_LOCK(rdch); /* if (rdch && rdch->bufhard.dl) while (chn_rdfeed(rdch) == 0); */ *arg_i = sndbuf_getready(rdch->bufsoft); CHN_UNLOCK(rdch); } else { *arg_i = 0; ret = EINVAL; } break; case FIOASYNC: /*set/clear async i/o */ DEB( printf("FIOASYNC\n") ; ) break; case SNDCTL_DSP_NONBLOCK: /* set non-blocking i/o */ case FIONBIO: /* set/clear non-blocking i/o */ if (rdch) { CHN_LOCK(rdch); if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i) rdch->flags |= CHN_F_NBIO; else rdch->flags &= ~CHN_F_NBIO; CHN_UNLOCK(rdch); } if (wrch) { CHN_LOCK(wrch); if (cmd == SNDCTL_DSP_NONBLOCK || *arg_i) wrch->flags |= CHN_F_NBIO; else wrch->flags &= ~CHN_F_NBIO; CHN_UNLOCK(wrch); } break; /* * Finally, here is the linux-compatible ioctl interface */ #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) case THE_REAL_SNDCTL_DSP_GETBLKSIZE: case SNDCTL_DSP_GETBLKSIZE: chn = wrch ? wrch : rdch; if (chn) { CHN_LOCK(chn); *arg_i = sndbuf_getblksz(chn->bufsoft); CHN_UNLOCK(chn); } else { *arg_i = 0; ret = EINVAL; } break ; case SNDCTL_DSP_SETBLKSIZE: RANGE(*arg_i, 16, 65536); if (wrch) { CHN_LOCK(wrch); chn_setblocksize(wrch, 2, *arg_i); CHN_UNLOCK(wrch); } if (rdch) { CHN_LOCK(rdch); chn_setblocksize(rdch, 2, *arg_i); CHN_UNLOCK(rdch); } break; case SNDCTL_DSP_RESET: DEB(printf("dsp reset\n")); if (wrch) { CHN_LOCK(wrch); chn_abort(wrch); chn_resetbuf(wrch); CHN_UNLOCK(wrch); } if (rdch) { CHN_LOCK(rdch); chn_abort(rdch); chn_resetbuf(rdch); CHN_UNLOCK(rdch); } break; case SNDCTL_DSP_SYNC: DEB(printf("dsp sync\n")); /* chn_sync may sleep */ if (wrch) { CHN_LOCK(wrch); chn_sync(wrch, 0); CHN_UNLOCK(wrch); } break; case SNDCTL_DSP_SPEED: /* chn_setspeed may sleep */ tmp = 0; if (wrch) { CHN_LOCK(wrch); ret = chn_setspeed(wrch, *arg_i); tmp = wrch->speed; CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); ret = chn_setspeed(rdch, *arg_i); if (tmp == 0) tmp = rdch->speed; CHN_UNLOCK(rdch); } *arg_i = tmp; break; case SOUND_PCM_READ_RATE: chn = wrch ? wrch : rdch; if (chn) { CHN_LOCK(chn); *arg_i = chn->speed; CHN_UNLOCK(chn); } else { *arg_i = 0; ret = EINVAL; } break; case SNDCTL_DSP_STEREO: tmp = -1; *arg_i = (*arg_i)? AFMT_STEREO : 0; if (wrch) { CHN_LOCK(wrch); ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); tmp = (wrch->format & AFMT_STEREO)? 1 : 0; CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); if (tmp == -1) tmp = (rdch->format & AFMT_STEREO)? 1 : 0; CHN_UNLOCK(rdch); } *arg_i = tmp; break; case SOUND_PCM_WRITE_CHANNELS: /* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */ if (*arg_i != 0) { tmp = 0; *arg_i = (*arg_i != 1)? AFMT_STEREO : 0; if (wrch) { CHN_LOCK(wrch); ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); tmp = (wrch->format & AFMT_STEREO)? 2 : 1; CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); if (tmp == 0) tmp = (rdch->format & AFMT_STEREO)? 2 : 1; CHN_UNLOCK(rdch); } *arg_i = tmp; } else { chn = wrch ? wrch : rdch; CHN_LOCK(chn); *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; CHN_UNLOCK(chn); } break; case SOUND_PCM_READ_CHANNELS: chn = wrch ? wrch : rdch; if (chn) { CHN_LOCK(chn); *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; CHN_UNLOCK(chn); } else { *arg_i = 0; ret = EINVAL; } break; case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */ chn = wrch ? wrch : rdch; if (chn) { CHN_LOCK(chn); *arg_i = chn_getformats(chn); CHN_UNLOCK(chn); } else { *arg_i = 0; ret = EINVAL; } break ; case SNDCTL_DSP_SETFMT: /* sets _one_ format */ if ((*arg_i != AFMT_QUERY)) { tmp = 0; if (wrch) { CHN_LOCK(wrch); ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO)); tmp = wrch->format & ~AFMT_STEREO; CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO)); if (tmp == 0) tmp = rdch->format & ~AFMT_STEREO; CHN_UNLOCK(rdch); } *arg_i = tmp; } else { chn = wrch ? wrch : rdch; CHN_LOCK(chn); *arg_i = chn->format & ~AFMT_STEREO; CHN_UNLOCK(chn); } break; case SNDCTL_DSP_SETFRAGMENT: DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg)); { u_int32_t fragln = (*arg_i) & 0x0000ffff; u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16; u_int32_t fragsz; u_int32_t r_maxfrags, r_fragsz; RANGE(fragln, 4, 16); fragsz = 1 << fragln; if (maxfrags == 0) maxfrags = CHN_2NDBUFMAXSIZE / fragsz; if (maxfrags < 2) maxfrags = 2; if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE) maxfrags = CHN_2NDBUFMAXSIZE / fragsz; DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz)); if (rdch) { CHN_LOCK(rdch); ret = chn_setblocksize(rdch, maxfrags, fragsz); r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft); r_fragsz = sndbuf_getblksz(rdch->bufsoft); CHN_UNLOCK(rdch); } else { r_maxfrags = maxfrags; r_fragsz = fragsz; } if (wrch && ret == 0) { CHN_LOCK(wrch); ret = chn_setblocksize(wrch, maxfrags, fragsz); maxfrags = sndbuf_getblkcnt(wrch->bufsoft); fragsz = sndbuf_getblksz(wrch->bufsoft); CHN_UNLOCK(wrch); } else { /* use whatever came from the read channel */ maxfrags = r_maxfrags; fragsz = r_fragsz; } fragln = 0; while (fragsz > 1) { fragln++; fragsz >>= 1; } *arg_i = (maxfrags << 16) | fragln; } break; case SNDCTL_DSP_GETISPACE: /* return the size of data available in the input queue */ { audio_buf_info *a = (audio_buf_info *)arg; if (rdch) { struct snd_dbuf *bs = rdch->bufsoft; CHN_LOCK(rdch); a->bytes = sndbuf_getready(bs); a->fragments = a->bytes / sndbuf_getblksz(bs); a->fragstotal = sndbuf_getblkcnt(bs); a->fragsize = sndbuf_getblksz(bs); CHN_UNLOCK(rdch); } } break; case SNDCTL_DSP_GETOSPACE: /* return space available in the output queue */ { audio_buf_info *a = (audio_buf_info *)arg; if (wrch) { struct snd_dbuf *bs = wrch->bufsoft; CHN_LOCK(wrch); /* XXX abusive DMA update: chn_wrupdate(wrch); */ a->bytes = sndbuf_getfree(bs); a->fragments = a->bytes / sndbuf_getblksz(bs); a->fragstotal = sndbuf_getblkcnt(bs); a->fragsize = sndbuf_getblksz(bs); CHN_UNLOCK(wrch); } } break; case SNDCTL_DSP_GETIPTR: { count_info *a = (count_info *)arg; if (rdch) { struct snd_dbuf *bs = rdch->bufsoft; CHN_LOCK(rdch); /* XXX abusive DMA update: chn_rdupdate(rdch); */ a->bytes = sndbuf_gettotal(bs); a->blocks = sndbuf_getblocks(bs) - rdch->blocks; a->ptr = sndbuf_getreadyptr(bs); rdch->blocks = sndbuf_getblocks(bs); CHN_UNLOCK(rdch); } else ret = EINVAL; } break; case SNDCTL_DSP_GETOPTR: { count_info *a = (count_info *)arg; if (wrch) { struct snd_dbuf *bs = wrch->bufsoft; CHN_LOCK(wrch); /* XXX abusive DMA update: chn_wrupdate(wrch); */ a->bytes = sndbuf_gettotal(bs); a->blocks = sndbuf_getblocks(bs) - wrch->blocks; a->ptr = sndbuf_getreadyptr(bs); wrch->blocks = sndbuf_getblocks(bs); CHN_UNLOCK(wrch); } else ret = EINVAL; } break; case SNDCTL_DSP_GETCAPS: *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER; if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX)) *arg_i |= DSP_CAP_DUPLEX; break; case SOUND_PCM_READ_BITS: chn = wrch ? wrch : rdch; if (chn) { CHN_LOCK(chn); if (chn->format & AFMT_8BIT) *arg_i = 8; else if (chn->format & AFMT_16BIT) *arg_i = 16; else if (chn->format & AFMT_24BIT) *arg_i = 24; else if (chn->format & AFMT_32BIT) *arg_i = 32; else ret = EINVAL; CHN_UNLOCK(chn); } else { *arg_i = 0; ret = EINVAL; } break; case SNDCTL_DSP_SETTRIGGER: if (rdch) { CHN_LOCK(rdch); rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER); if (*arg_i & PCM_ENABLE_INPUT) chn_start(rdch, 1); else rdch->flags |= CHN_F_NOTRIGGER; CHN_UNLOCK(rdch); } if (wrch) { CHN_LOCK(wrch); wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER); if (*arg_i & PCM_ENABLE_OUTPUT) chn_start(wrch, 1); else wrch->flags |= CHN_F_NOTRIGGER; CHN_UNLOCK(wrch); } break; case SNDCTL_DSP_GETTRIGGER: *arg_i = 0; if (wrch) { CHN_LOCK(wrch); if (wrch->flags & CHN_F_TRIGGERED) *arg_i |= PCM_ENABLE_OUTPUT; CHN_UNLOCK(wrch); } if (rdch) { CHN_LOCK(rdch); if (rdch->flags & CHN_F_TRIGGERED) *arg_i |= PCM_ENABLE_INPUT; CHN_UNLOCK(rdch); } break; case SNDCTL_DSP_GETODELAY: if (wrch) { struct snd_dbuf *bs = wrch->bufsoft; CHN_LOCK(wrch); /* XXX abusive DMA update: chn_wrupdate(wrch); */ *arg_i = sndbuf_getready(bs); CHN_UNLOCK(wrch); } else ret = EINVAL; break; case SNDCTL_DSP_POST: if (wrch) { CHN_LOCK(wrch); wrch->flags &= ~CHN_F_NOTRIGGER; chn_start(wrch, 1); CHN_UNLOCK(wrch); } break; case SNDCTL_DSP_SETDUPLEX: /* * switch to full-duplex mode if card is in half-duplex * mode and is able to work in full-duplex mode */ if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX)) dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX); break; /* * The following four ioctls are simple wrappers around mixer_ioctl * with no further processing. xcmd is short for "translated * command". */ case SNDCTL_DSP_GETRECVOL: if (xcmd == 0) xcmd = SOUND_MIXER_READ_RECLEV; /* FALLTHROUGH */ case SNDCTL_DSP_SETRECVOL: if (xcmd == 0) xcmd = SOUND_MIXER_WRITE_RECLEV; /* FALLTHROUGH */ case SNDCTL_DSP_GETPLAYVOL: if (xcmd == 0) xcmd = SOUND_MIXER_READ_PCM; /* FALLTHROUGH */ case SNDCTL_DSP_SETPLAYVOL: if (xcmd == 0) xcmd = SOUND_MIXER_WRITE_PCM; if (d->mixer_dev != NULL) ret = mixer_ioctl(d->mixer_dev, xcmd, arg, -1, td); else ret = ENOTSUP; break; case SNDCTL_DSP_GET_RECSRC_NAMES: case SNDCTL_DSP_GET_RECSRC: case SNDCTL_DSP_SET_RECSRC: if (d->mixer_dev != NULL) ret = mixer_ioctl(d->mixer_dev, cmd, arg, -1, td); else ret = ENOTSUP; break; /* * The following 3 ioctls aren't very useful at the moment. For * now, only a single channel is associated with a cdev (/dev/dspN * instance), so there's only a single output routing to use (i.e., * the wrch bound to this cdev). */ case SNDCTL_DSP_GET_PLAYTGT_NAMES: { oss_mixer_enuminfo *ei; ei = (oss_mixer_enuminfo *)arg; ei->dev = 0; ei->ctrl = 0; ei->version = 0; /* static for now */ ei->strindex[0] = 0; if (wrch != NULL) { ei->nvalues = 1; strlcpy(ei->strings, wrch->name, sizeof(ei->strings)); } else { ei->nvalues = 0; ei->strings[0] = '\0'; } } break; case SNDCTL_DSP_GET_PLAYTGT: case SNDCTL_DSP_SET_PLAYTGT: /* yes, they are the same for now */ /* * Re: SET_PLAYTGT * OSSv4: "The value that was accepted by the device will * be returned back in the variable pointed by the * argument." */ if (wrch != NULL) *arg_i = 0; else ret = EINVAL; break; case SNDCTL_DSP_SILENCE: /* * Flush the software (pre-feed) buffer, but try to minimize playback * interruption. (I.e., record unplayed samples with intent to * restore by SNDCTL_DSP_SKIP.) Intended for application "pause" * functionality. */ if (wrch == NULL) ret = EINVAL; else { struct snd_dbuf *bs; CHN_LOCK(wrch); while (wrch->inprog != 0) cv_wait(&wrch->cv, wrch->lock); bs = wrch->bufsoft; if ((bs->shadbuf != NULL) && (sndbuf_getready(bs) > 0)) { bs->sl = sndbuf_getready(bs); sndbuf_dispose(bs, bs->shadbuf, sndbuf_getready(bs)); sndbuf_fillsilence(bs); chn_start(wrch, 0); } CHN_UNLOCK(wrch); } break; case SNDCTL_DSP_SKIP: /* * OSSv4 docs: "This ioctl call discards all unplayed samples in the * playback buffer by moving the current write position immediately * before the point where the device is currently reading the samples." */ if (wrch == NULL) ret = EINVAL; else { struct snd_dbuf *bs; CHN_LOCK(wrch); while (wrch->inprog != 0) cv_wait(&wrch->cv, wrch->lock); bs = wrch->bufsoft; if ((bs->shadbuf != NULL) && (bs->sl > 0)) { sndbuf_softreset(bs); sndbuf_acquire(bs, bs->shadbuf, bs->sl); bs->sl = 0; chn_start(wrch, 0); } CHN_UNLOCK(wrch); } break; case SNDCTL_DSP_CURRENT_OPTR: case SNDCTL_DSP_CURRENT_IPTR: /** * @note Changing formats resets the buffer counters, which differs * from the 4Front drivers. However, I don't expect this to be * much of a problem. * * @note In a test where @c CURRENT_OPTR is called immediately after write * returns, this driver is about 32K samples behind whereas * 4Front's is about 8K samples behind. Should determine source * of discrepancy, even if only out of curiosity. * * @todo Actually test SNDCTL_DSP_CURRENT_IPTR. */ chn = (cmd == SNDCTL_DSP_CURRENT_OPTR) ? wrch : rdch; if (chn == NULL) ret = EINVAL; else { struct snd_dbuf *bs; /* int tmp; */ oss_count_t *oc = (oss_count_t *)arg; CHN_LOCK(chn); bs = chn->bufsoft; #if 0 tmp = (sndbuf_getsize(b) + chn_getptr(chn) - sndbuf_gethwptr(b)) % sndbuf_getsize(b); oc->samples = (sndbuf_gettotal(b) + tmp) / sndbuf_getbps(b); oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getbps(b); #else oc->samples = sndbuf_gettotal(bs) / sndbuf_getbps(bs); oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getbps(bs); #endif CHN_UNLOCK(chn); } break; case SNDCTL_DSP_HALT_OUTPUT: case SNDCTL_DSP_HALT_INPUT: chn = (cmd == SNDCTL_DSP_HALT_OUTPUT) ? wrch : rdch; if (chn == NULL) ret = EINVAL; else { CHN_LOCK(chn); chn_abort(chn); CHN_UNLOCK(chn); } break; case SNDCTL_DSP_LOW_WATER: /* * Set the number of bytes required to attract attention by * select/poll. */ if (wrch != NULL) { CHN_LOCK(wrch); wrch->lw = (*arg_i > 1) ? *arg_i : 1; CHN_UNLOCK(wrch); } if (rdch != NULL) { CHN_LOCK(rdch); rdch->lw = (*arg_i > 1) ? *arg_i : 1; CHN_UNLOCK(rdch); } break; case SNDCTL_DSP_GETERROR: /* * OSSv4 docs: "All errors and counters will automatically be * cleared to zeroes after the call so each call will return only * the errors that occurred after the previous invocation. ... The * play_underruns and rec_overrun fields are the only usefull fields * returned by OSS 4.0." */ { audio_errinfo *ei = (audio_errinfo *)arg; bzero((void *)ei, sizeof(*ei)); if (wrch != NULL) { CHN_LOCK(wrch); ei->play_underruns = wrch->xruns; wrch->xruns = 0; CHN_UNLOCK(wrch); } if (rdch != NULL) { CHN_LOCK(rdch); ei->rec_overruns = rdch->xruns; rdch->xruns = 0; CHN_UNLOCK(rdch); } } break; case SNDCTL_DSP_SYNCGROUP: ret = dsp_oss_syncgroup(wrch, rdch, (oss_syncgroup *)arg); break; case SNDCTL_DSP_SYNCSTART: ret = dsp_oss_syncstart(*arg_i); break; case SNDCTL_DSP_POLICY: ret = dsp_oss_policy(wrch, rdch, *arg_i); break; #ifdef OSSV4_EXPERIMENT /* * XXX The following ioctls are not yet supported and just return * EINVAL. */ case SNDCTL_DSP_GETOPEAKS: case SNDCTL_DSP_GETIPEAKS: chn = (cmd == SNDCTL_DSP_GETOPEAKS) ? wrch : rdch; if (chn == NULL) ret = EINVAL; else { oss_peaks_t *op = (oss_peaks_t *)arg; int lpeak, rpeak; CHN_LOCK(chn); ret = chn_getpeaks(chn, &lpeak, &rpeak); if (ret == -1) ret = EINVAL; else { (*op)[0] = lpeak; (*op)[1] = rpeak; } CHN_UNLOCK(chn); } break; case SNDCTL_DSP_COOKEDMODE: ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); break; case SNDCTL_DSP_GET_CHNORDER: ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg); break; case SNDCTL_DSP_SET_CHNORDER: ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg); break; case SNDCTL_GETLABEL: ret = dsp_oss_getlabel(wrch, rdch, (oss_label_t *)arg); break; case SNDCTL_SETLABEL: ret = dsp_oss_setlabel(wrch, rdch, (oss_label_t *)arg); break; case SNDCTL_GETSONG: ret = dsp_oss_getsong(wrch, rdch, (oss_longname_t *)arg); break; case SNDCTL_SETSONG: ret = dsp_oss_setsong(wrch, rdch, (oss_longname_t *)arg); break; case SNDCTL_SETNAME: ret = dsp_oss_setname(wrch, rdch, (oss_longname_t *)arg); break; #if 0 /** * @note The SNDCTL_CARDINFO ioctl was omitted per 4Front developer * documentation. "The usability of this call is very limited. It's * provided only for completeness of the API. OSS API doesn't have * any concept of card. Any information returned by this ioctl calld * is reserved exclusively for the utility programs included in the * OSS package. Applications should not try to use for this * information in any ways." */ case SNDCTL_CARDINFO: ret = EINVAL; break; /** * @note The S/PDIF interface ioctls, @c SNDCTL_DSP_READCTL and * @c SNDCTL_DSP_WRITECTL have been omitted at the suggestion of * 4Front Technologies. */ case SNDCTL_DSP_READCTL: case SNDCTL_DSP_WRITECTL: ret = EINVAL; break; #endif /* !0 (explicitly omitted ioctls) */ #endif /* !OSSV4_EXPERIMENT */ case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: /* undocumented */ case SNDCTL_DSP_SUBDIVIDE: case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: /* dunno what these do, don't sound important */ default: DEB(printf("default ioctl fn 0x%08lx fail\n", cmd)); ret = EINVAL; break; } relchns(i_dev, rdch, wrch, 0); return ret; } static int dsp_poll(struct cdev *i_dev, int events, struct thread *td) { struct pcm_channel *wrch = NULL, *rdch = NULL; int ret, e; ret = 0; getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); if (wrch) { e = (events & (POLLOUT | POLLWRNORM)); if (e) ret |= chn_poll(wrch, e, td); } if (rdch) { e = (events & (POLLIN | POLLRDNORM)); if (e) ret |= chn_poll(rdch, e, td); } relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); return ret; } static int dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) { struct pcm_channel *wrch = NULL, *rdch = NULL, *c; if (nprot & PROT_EXEC) return -1; getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); #if 0 /* * XXX the linux api uses the nprot to select read/write buffer * our vm system doesn't allow this, so force write buffer */ if (wrch && (nprot & PROT_WRITE)) { c = wrch; } else if (rdch && (nprot & PROT_READ)) { c = rdch; } else { return -1; } #else c = wrch; #endif if (c == NULL) { relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); return -1; } if (offset >= sndbuf_getsize(c->bufsoft)) { relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); return -1; } if (!(c->flags & CHN_F_MAPPED)) c->flags |= CHN_F_MAPPED; *paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset)); relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); return 0; } #ifdef USING_DEVFS -/* - * Clone logic is this: - * x E X = {dsp, dspW, audio} - * x -> x${sysctl("hw.snd.unit")} - * xN-> - * for i N = 1 to channels of device N - * if xN.i isn't busy, return its dev_t - */ +/* So much for dev_stdclone() */ +static int +dsp_stdclone(char *name, char *namep, char *sep, int use_sep, int *u, int *c) +{ + size_t len; + + len = strlen(namep); + + if (bcmp(name, namep, len) != 0) + return (ENODEV); + + name += len; + + if (isdigit(*name) == 0) + return (ENODEV); + + len = strlen(sep); + + if (*name == '0' && !(name[1] == '\0' || bcmp(name + 1, sep, len) == 0)) + return (ENODEV); + + for (*u = 0; isdigit(*name) != 0; name++) { + *u *= 10; + *u += *name - '0'; + if (*u > dsp_umax) + return (ENODEV); + } + + if (*name == '\0') + return ((use_sep == 0) ? 0 : ENODEV); + + if (bcmp(name, sep, len) != 0 || isdigit(name[len]) == 0) + return (ENODEV); + + name += len; + + if (*name == '0' && name[1] != '\0') + return (ENODEV); + + for (*c = 0; isdigit(*name) != 0; name++) { + *c *= 10; + *c += *name - '0'; + if (*c > dsp_cmax) + return (ENODEV); + } + + if (*name != '\0') + return (ENODEV); + + return (0); +} + static void -dsp_clone(void *arg, struct ucred *cred, char *name, int namelen, - struct cdev **dev) +dsp_clone(void *arg, +#if __FreeBSD_version >= 600034 + struct ucred *cred, +#endif + char *name, int namelen, struct cdev **dev) { - struct cdev *pdev; - struct snddev_info *pcm_dev; - struct snddev_channel *pcm_chan; - int i, unit, devtype; - static int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO}; - static char *devnames[3] = {"dsp", "dspW", "audio"}; + struct snddev_info *d; + struct snd_clone_entry *ce; + struct pcm_channel *c; + int i, unit, udcmask, cunit, devtype, devhw, devcmax, tumax; + char *devname, *devsep; + KASSERT(dsp_umax >= 0 && dsp_cmax >= 0, ("Uninitialized unit!")); + if (*dev != NULL) return; - if (pcm_devclass == NULL) - return; - devtype = 0; unit = -1; - for (i = 0; (i < 3) && (unit == -1); i++) { - devtype = devtypes[i]; - if (strcmp(name, devnames[i]) == 0) { + cunit = -1; + devtype = -1; + devhw = 0; + devcmax = -1; + tumax = -1; + devname = NULL; + devsep = NULL; + + for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])) && + unit == -1; i++) { + devtype = dsp_cdevs[i].type; + devname = dsp_cdevs[i].name; + devsep = dsp_cdevs[i].sep; + devhw = dsp_cdevs[i].hw; + devcmax = dsp_cdevs[i].max - 1; + if (strcmp(name, devname) == 0) unit = snd_unit; - } else { - if (dev_stdclone(name, NULL, devnames[i], &unit) != 1) - unit = -1; + else if (dsp_stdclone(name, devname, devsep, + dsp_cdevs[i].use_sep, &unit, &cunit) != 0) { + unit = -1; + cunit = -1; } } - if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass)) + + d = devclass_get_softc(pcm_devclass, unit); + if (d == NULL || d->clones == NULL) return; - pcm_dev = devclass_get_softc(pcm_devclass, unit); - - if (pcm_dev == NULL) + pcm_lock(d); + if (snd_clone_disabled(d->clones)) { + pcm_unlock(d); return; + } - SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) { + udcmask = snd_u2unit(unit) | snd_d2unit(devtype); - switch(devtype) { - case SND_DEV_DSP: - pdev = pcm_chan->dsp_devt; - break; - case SND_DEV_DSP16: - pdev = pcm_chan->dspW_devt; - break; - case SND_DEV_AUDIO: - pdev = pcm_chan->audio_devt; - break; - default: - panic("Unknown devtype %d", devtype); - } - - if ((pdev != NULL) && (pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) { - *dev = pdev; - dev_ref(*dev); + if (devhw != 0) { + KASSERT(devcmax <= dsp_cmax, + ("overflow: devcmax=%d, dsp_cmax=%d", devcmax, dsp_cmax)); + if (cunit > devcmax) { + pcm_unlock(d); return; } + udcmask |= snd_c2unit(cunit); + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + if (c->unit != udcmask) { + CHN_UNLOCK(c); + continue; + } + CHN_UNLOCK(c); + udcmask &= ~snd_c2unit(cunit); + /* + * Temporarily increase clone maxunit to overcome + * vchan flexibility. + * + * # sysctl dev.pcm.0.play.vchans=256 + * dev.pcm.0.play.vchans: 1 -> 256 + * # cat /dev/zero > /dev/dsp0.vp255 & + * [1] 17296 + * # sysctl dev.pcm.0.play.vchans=0 + * dev.pcm.0.play.vchans: 256 -> 1 + * # fg + * [1] + running cat /dev/zero > /dev/dsp0.vp255 + * ^C + * # cat /dev/zero > /dev/dsp0.vp255 + * zsh: operation not supported: /dev/dsp0.vp255 + */ + tumax = snd_clone_getmaxunit(d->clones); + if (cunit > tumax) + snd_clone_setmaxunit(d->clones, cunit); + else + tumax = -1; + goto dsp_clone_alloc; + } + /* + * Ok, so we're requesting unallocated vchan, but still + * within maximum vchan limit. + */ + if (((devtype == SND_DEV_DSPHW_VPLAY && d->pvchancount > 0) || + (devtype == SND_DEV_DSPHW_VREC && d->rvchancount > 0)) && + cunit < snd_maxautovchans) { + udcmask &= ~snd_c2unit(cunit); + tumax = snd_clone_getmaxunit(d->clones); + if (cunit > tumax) + snd_clone_setmaxunit(d->clones, cunit); + else + tumax = -1; + goto dsp_clone_alloc; + } + pcm_unlock(d); + return; } + +dsp_clone_alloc: + ce = snd_clone_alloc(d->clones, dev, &cunit, udcmask); + if (tumax != -1) + snd_clone_setmaxunit(d->clones, tumax); + if (ce != NULL) { + udcmask |= snd_c2unit(cunit); + pcm_unlock(d); + *dev = make_dev(&dsp_cdevsw, unit2minor(udcmask), + UID_ROOT, GID_WHEEL, 0666, "%s%d%s%d", + devname, unit, devsep, cunit); + pcm_lock(d); + snd_clone_register(ce, *dev); + } + pcm_unlock(d); + + if (*dev != NULL) + dev_ref(*dev); } static void dsp_sysinit(void *p) { + if (dsp_ehtag != NULL) + return; + /* initialize unit numbering */ + snd_unit_init(); + dsp_umax = PCMMAXUNIT; + dsp_cmax = PCMMAXCHAN; dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000); } static void dsp_sysuninit(void *p) { - if (dsp_ehtag != NULL) - EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag); + if (dsp_ehtag == NULL) + return; + EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag); + dsp_ehtag = NULL; } SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL); SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL); #endif +char * +dsp_unit2name(char *buf, size_t len, int unit) +{ + int i, dtype; + + KASSERT(buf != NULL && len != 0, ("bogus buf=%p len=%u", buf, len)); + + dtype = snd_unit2d(unit); + + for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { + if (dtype != dsp_cdevs[i].type) + continue; + snprintf(buf, len, "%s%d%s%d", dsp_cdevs[i].name, + snd_unit2u(unit), dsp_cdevs[i].sep, snd_unit2c(unit)); + return (buf); + } + + return (NULL); +} + /** * @brief Handler for SNDCTL_AUDIOINFO. * * Gathers information about the audio device specified in ai->dev. If * ai->dev == -1, then this function gathers information about the current * device. If the call comes in on a non-audio device and ai->dev == -1, * return EINVAL. * * This routine is supposed to go practically straight to the hardware, * getting capabilities directly from the sound card driver, side-stepping * the intermediate channel interface. * * Note, however, that the usefulness of this command is significantly * decreased when requesting info about any device other than the one serving * the request. While each snddev_channel refers to a specific device node, * the converse is *not* true. Currently, when a sound device node is opened, * the sound subsystem scans for an available audio channel (or channels, if * opened in read+write) and then assigns them to the si_drv[12] private * data fields. As a result, any information returned linking a channel to * a specific character device isn't necessarily accurate. * * @note * Calling threads must not hold any snddev_info or pcm_channel locks. * * @param dev device on which the ioctl was issued * @param ai ioctl request data container * * @retval 0 success * @retval EINVAL ai->dev specifies an invalid device * * @todo Verify correctness of Doxygen tags. ;) */ int dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai) { - struct snddev_channel *sce; struct pcmchan_caps *caps; struct pcm_channel *ch; struct snddev_info *d; - struct cdev *t_cdev; uint32_t fmts; int i, nchan, ret, *rates, minch, maxch; + char *devname, buf[CHN_NAMELEN]; /* * If probing the device that received the ioctl, make sure it's a * DSP device. (Users may use this ioctl with /dev/mixer and * /dev/midi.) */ if ((ai->dev == -1) && (i_dev->si_devsw != &dsp_cdevsw)) return EINVAL; ch = NULL; - t_cdev = NULL; + devname = NULL; nchan = 0; ret = 0; - + bzero(buf, sizeof(buf)); + /* * Search for the requested audio device (channel). Start by * iterating over pcm devices. */ for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { d = devclass_get_softc(pcm_devclass, i); if (d == NULL) continue; /* See the note in function docblock */ mtx_assert(d->lock, MA_NOTOWNED); pcm_inprog(d, 1); pcm_lock(d); - SLIST_FOREACH(sce, &d->channels, link) { - ch = sce->channel; + CHN_FOREACH(ch, d, channels.pcm) { mtx_assert(ch->lock, MA_NOTOWNED); CHN_LOCK(ch); if (ai->dev == -1) { - if ((ch == i_dev->si_drv1) || /* record ch */ - (ch == i_dev->si_drv2)) { /* playback ch */ - t_cdev = i_dev; + if ((ch == PCM_RDCH(i_dev)) || /* record ch */ + (ch == PCM_WRCH(i_dev))) { /* playback ch */ + devname = i_dev->si_name; goto dspfound; } } else if (ai->dev == nchan) { - t_cdev = sce->dsp_devt; + devname = dsp_unit2name(buf, sizeof(buf), + ch->unit); goto dspfound; } CHN_UNLOCK(ch); + /* + * XXX I really doubt if this is correct. + */ ++nchan; } pcm_unlock(d); pcm_inprog(d, -1); } /* Exhausted the search -- nothing is locked, so return. */ return EINVAL; dspfound: /* Should've found the device, but something isn't right */ - if (t_cdev == NULL) { + if (devname == NULL) { ret = EINVAL; goto out; } /* * At this point, the following synchronization stuff has happened: * - a specific PCM device is locked and its "in progress * operations" counter has been incremented, so be sure to unlock * and decrement when exiting; * - a specific audio channel has been locked, so be sure to unlock * when exiting; */ caps = chn_getcaps(ch); /* * With all handles collected, zero out the user's container and * begin filling in its fields. */ bzero((void *)ai, sizeof(oss_audioinfo)); ai->dev = nchan; strlcpy(ai->name, ch->name, sizeof(ai->name)); if ((ch->flags & CHN_F_BUSY) == 0) ai->busy = 0; else ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ; /** * @note * @c cmd - OSSv4 docs: "Only supported under Linux at this moment." * Cop-out, I know, but I'll save running around in the process * table for later. Is there a risk of leaking information? */ ai->pid = ch->pid; - + /* * These flags stolen from SNDCTL_DSP_GETCAPS handler. Note, however, * that a single channel operates in only one direction, so * DSP_CAP_DUPLEX is out. */ /** * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep these in * pcmchan::caps? */ ai->caps = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER; /* * Collect formats supported @b natively by the device. Also * determine min/max channels. (I.e., mono, stereo, or both?) * * If any channel is stereo, maxch = 2; * if all channels are stereo, minch = 2, too; * if any channel is mono, minch = 1; * and if all channels are mono, maxch = 1. */ minch = 0; maxch = 0; fmts = 0; for (i = 0; caps->fmtlist[i]; i++) { fmts |= caps->fmtlist[i]; if (caps->fmtlist[i] & AFMT_STEREO) { minch = (minch == 0) ? 2 : minch; maxch = 2; } else { minch = 1; maxch = (maxch == 0) ? 1 : maxch; } } if (ch->direction == PCMDIR_PLAY) ai->oformats = fmts; else ai->iformats = fmts; /** * @note * @c magic - OSSv4 docs: "Reserved for internal use by OSS." * * @par * @c card_number - OSSv4 docs: "Number of the sound card where this * device belongs or -1 if this information is not available. * Applications should normally not use this field for any * purpose." */ ai->card_number = -1; /** * @todo @c song_name - depends first on SNDCTL_[GS]ETSONG * @todo @c label - depends on SNDCTL_[GS]ETLABEL * @todo @c port_number - routing information? */ ai->port_number = -1; ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1; /** * @note * @c real_device - OSSv4 docs: "Obsolete." */ ai->real_device = -1; - strlcpy(ai->devnode, t_cdev->si_name, sizeof(ai->devnode)); + strlcpy(ai->devnode, devname, sizeof(ai->devnode)); ai->enabled = device_is_attached(d->dev) ? 1 : 0; /** * @note * @c flags - OSSv4 docs: "Reserved for future use." * * @note * @c binding - OSSv4 docs: "Reserved for future use." * * @todo @c handle - haven't decided how to generate this yet; bus, * vendor, device IDs? */ ai->min_rate = caps->minspeed; ai->max_rate = caps->maxspeed; ai->min_channels = minch; ai->max_channels = maxch; ai->nrates = chn_getrates(ch, &rates); if (ai->nrates > OSS_MAX_SAMPLE_RATES) ai->nrates = OSS_MAX_SAMPLE_RATES; for (i = 0; i < ai->nrates; i++) ai->rates[i] = rates[i]; out: CHN_UNLOCK(ch); pcm_unlock(d); pcm_inprog(d, -1); return ret; } /** * @brief Assigns a PCM channel to a sync group. * * Sync groups are used to enable audio operations on multiple devices * simultaneously. They may be used with any number of devices and may * span across applications. Devices are added to groups with * the SNDCTL_DSP_SYNCGROUP ioctl, and operations are triggered with the * SNDCTL_DSP_SYNCSTART ioctl. * * If the @c id field of the @c group parameter is set to zero, then a new * sync group is created. Otherwise, wrch and rdch (if set) are added to * the group specified. * * @todo As far as memory allocation, should we assume that things are * okay and allocate with M_WAITOK before acquiring channel locks, * freeing later if not? * * @param wrch output channel associated w/ device (if any) * @param rdch input channel associated w/ device (if any) * @param group Sync group parameters * * @retval 0 success * @retval non-zero error to be propagated upstream */ static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group) { struct pcmchan_syncmember *smrd, *smwr; struct pcmchan_syncgroup *sg; int ret, sg_ids[3]; smrd = NULL; smwr = NULL; sg = NULL; ret = 0; /* * Free_unr() may sleep, so store released syncgroup IDs until after * all locks are released. */ sg_ids[0] = sg_ids[1] = sg_ids[2] = 0; PCM_SG_LOCK(); /* * - Insert channel(s) into group's member list. * - Set CHN_F_NOTRIGGER on channel(s). * - Stop channel(s). */ /* * If device's channels are already mapped to a group, unmap them. */ if (wrch) { CHN_LOCK(wrch); sg_ids[0] = chn_syncdestroy(wrch); } if (rdch) { CHN_LOCK(rdch); sg_ids[1] = chn_syncdestroy(rdch); } /* * Verify that mode matches character device properites. * - Bail if PCM_ENABLE_OUTPUT && wrch == NULL. * - Bail if PCM_ENABLE_INPUT && rdch == NULL. */ if (((wrch == NULL) && (group->mode & PCM_ENABLE_OUTPUT)) || ((rdch == NULL) && (group->mode & PCM_ENABLE_INPUT))) { ret = EINVAL; goto out; } /* * An id of zero indicates the user wants to create a new * syncgroup. */ if (group->id == 0) { sg = (struct pcmchan_syncgroup *)malloc(sizeof(*sg), M_DEVBUF, M_NOWAIT); if (sg != NULL) { SLIST_INIT(&sg->members); sg->id = alloc_unr(pcmsg_unrhdr); group->id = sg->id; SLIST_INSERT_HEAD(&snd_pcm_syncgroups, sg, link); } else ret = ENOMEM; } else { SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) { if (sg->id == group->id) break; } if (sg == NULL) ret = EINVAL; } /* Couldn't create or find a syncgroup. Fail. */ if (sg == NULL) goto out; /* * Allocate a syncmember, assign it and a channel together, and * insert into syncgroup. */ if (group->mode & PCM_ENABLE_INPUT) { smrd = (struct pcmchan_syncmember *)malloc(sizeof(*smrd), M_DEVBUF, M_NOWAIT); if (smrd == NULL) { ret = ENOMEM; goto out; } SLIST_INSERT_HEAD(&sg->members, smrd, link); smrd->parent = sg; smrd->ch = rdch; chn_abort(rdch); rdch->flags |= CHN_F_NOTRIGGER; rdch->sm = smrd; } if (group->mode & PCM_ENABLE_OUTPUT) { smwr = (struct pcmchan_syncmember *)malloc(sizeof(*smwr), M_DEVBUF, M_NOWAIT); if (smwr == NULL) { ret = ENOMEM; goto out; } SLIST_INSERT_HEAD(&sg->members, smwr, link); smwr->parent = sg; smwr->ch = wrch; chn_abort(wrch); wrch->flags |= CHN_F_NOTRIGGER; wrch->sm = smwr; } out: if (ret != 0) { if (smrd != NULL) free(smrd, M_DEVBUF); if ((sg != NULL) && SLIST_EMPTY(&sg->members)) { sg_ids[2] = sg->id; SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link); free(sg, M_DEVBUF); } if (wrch) wrch->sm = NULL; if (rdch) rdch->sm = NULL; } if (wrch) CHN_UNLOCK(wrch); if (rdch) CHN_UNLOCK(rdch); PCM_SG_UNLOCK(); if (sg_ids[0]) free_unr(pcmsg_unrhdr, sg_ids[0]); if (sg_ids[1]) free_unr(pcmsg_unrhdr, sg_ids[1]); if (sg_ids[2]) free_unr(pcmsg_unrhdr, sg_ids[2]); return ret; } /** * @brief Launch a sync group into action * * Sync groups are established via SNDCTL_DSP_SYNCGROUP. This function * iterates over all members, triggering them along the way. * * @note Caller must not hold any channel locks. * * @param sg_id sync group identifier * * @retval 0 success * @retval non-zero error worthy of propagating upstream to user */ static int dsp_oss_syncstart(int sg_id) { struct pcmchan_syncmember *sm, *sm_tmp; struct pcmchan_syncgroup *sg; struct pcm_channel *c; int ret, needlocks; - + /* Get the synclists lock */ PCM_SG_LOCK(); do { ret = 0; needlocks = 0; /* Search for syncgroup by ID */ SLIST_FOREACH(sg, &snd_pcm_syncgroups, link) { if (sg->id == sg_id) break; } /* Return EINVAL if not found */ if (sg == NULL) { ret = EINVAL; break; } /* Any removals resulting in an empty group should've handled this */ KASSERT(!SLIST_EMPTY(&sg->members), ("found empty syncgroup")); /* * Attempt to lock all member channels - if any are already * locked, unlock those acquired, sleep for a bit, and try * again. */ SLIST_FOREACH(sm, &sg->members, link) { if (CHN_TRYLOCK(sm->ch) == 0) { int timo = hz * 5/1000; if (timo < 1) timo = 1; /* Release all locked channels so far, retry */ SLIST_FOREACH(sm_tmp, &sg->members, link) { /* sm is the member already locked */ if (sm == sm_tmp) break; CHN_UNLOCK(sm_tmp->ch); } /** @todo Is PRIBIO correct/ */ ret = msleep(sm, &snd_pcm_syncgroups_mtx, PRIBIO | PCATCH, "pcmsgrp", timo); if (ret == EINTR || ret == ERESTART) break; needlocks = 1; ret = 0; /* Assumes ret == EAGAIN... */ } } } while (needlocks && ret == 0); /* Proceed only if no errors encountered. */ if (ret == 0) { /* Launch channels */ while((sm = SLIST_FIRST(&sg->members)) != NULL) { SLIST_REMOVE_HEAD(&sg->members, link); c = sm->ch; c->sm = NULL; chn_start(c, 1); c->flags &= ~CHN_F_NOTRIGGER; CHN_UNLOCK(c); free(sm, M_DEVBUF); } SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link); free(sg, M_DEVBUF); } PCM_SG_UNLOCK(); /* * Free_unr() may sleep, so be sure to give up the syncgroup lock * first. */ if (ret == 0) free_unr(pcmsg_unrhdr, sg_id); return ret; } /** * @brief Handler for SNDCTL_DSP_POLICY * * The SNDCTL_DSP_POLICY ioctl is a simpler interface to control fragment * size and count like with SNDCTL_DSP_SETFRAGMENT. Instead of the user * specifying those two parameters, s/he simply selects a number from 0..10 * which corresponds to a buffer size. Smaller numbers request smaller * buffers with lower latencies (at greater overhead from more frequent * interrupts), while greater numbers behave in the opposite manner. * * The 4Front spec states that a value of 5 should be the default. However, * this implementation deviates slightly by using a linear scale without * consulting drivers. I.e., even though drivers may have different default * buffer sizes, a policy argument of 5 will have the same result across * all drivers. * * See http://manuals.opensound.com/developer/SNDCTL_DSP_POLICY.html for * more information. * * @todo When SNDCTL_DSP_COOKEDMODE is supported, it'll be necessary to * work with hardware drivers directly. * * @note PCM channel arguments must not be locked by caller. * * @param wrch Pointer to opened playback channel (optional; may be NULL) * @param rdch " recording channel (optional; may be NULL) * @param policy Integer from [0:10] * * @retval 0 constant (for now) */ static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy) { int ret; if (policy < CHN_POLICY_MIN || policy > CHN_POLICY_MAX) return EIO; /* Default: success */ ret = 0; if (rdch) { CHN_LOCK(rdch); ret = chn_setlatency(rdch, policy); CHN_UNLOCK(rdch); } if (wrch && ret == 0) { CHN_LOCK(wrch); ret = chn_setlatency(wrch, policy); CHN_UNLOCK(wrch); } if (ret) ret = EIO; return ret; } #ifdef OSSV4_EXPERIMENT /** * @brief Enable or disable "cooked" mode * * This is a handler for @c SNDCTL_DSP_COOKEDMODE. When in cooked mode, which * is the default, the sound system handles rate and format conversions * automatically (ex: user writing 11025Hz/8 bit/unsigned but card only * operates with 44100Hz/16bit/signed samples). * * Disabling cooked mode is intended for applications wanting to mmap() * a sound card's buffer space directly, bypassing the FreeBSD 2-stage * feeder architecture, presumably to gain as much control over audio * hardware as possible. * * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_COOKEDMODE.html * for more details. * * @note Currently, this function is just a stub that always returns EINVAL. * * @todo Figure out how to and actually implement this. * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param enabled 0 = raw mode, 1 = cooked mode * * @retval EINVAL Operation not yet supported. */ static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled) { return EINVAL; } /** * @brief Retrieve channel interleaving order * * This is the handler for @c SNDCTL_DSP_GET_CHNORDER. * * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_GET_CHNORDER.html * for more details. * * @note As the ioctl definition is still under construction, FreeBSD * does not currently support SNDCTL_DSP_GET_CHNORDER. * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param map channel map (result will be stored there) * * @retval EINVAL Operation not yet supported. */ static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) { return EINVAL; } /** * @brief Specify channel interleaving order * * This is the handler for @c SNDCTL_DSP_SET_CHNORDER. * * @note As the ioctl definition is still under construction, FreeBSD * does not currently support @c SNDCTL_DSP_SET_CHNORDER. * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param map channel map * * @retval EINVAL Operation not yet supported. */ static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) { return EINVAL; } /** * @brief Retrieve an audio device's label * * This is a handler for the @c SNDCTL_GETLABEL ioctl. * * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html * for more details. * * From Hannu@4Front: "For example ossxmix (just like some HW mixer * consoles) can show variable "labels" for certain controls. By default * the application name (say quake) is shown as the label but * applications may change the labels themselves." * * @note As the ioctl definition is still under construction, FreeBSD * does not currently support @c SNDCTL_GETLABEL. * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param label label gets copied here * * @retval EINVAL Operation not yet supported. */ static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label) { return EINVAL; } /** * @brief Specify an audio device's label * * This is a handler for the @c SNDCTL_SETLABEL ioctl. Please see the * comments for @c dsp_oss_getlabel immediately above. * * See @c http://manuals.opensound.com/developer/SNDCTL_GETLABEL.html * for more details. * * @note As the ioctl definition is still under construction, FreeBSD * does not currently support SNDCTL_SETLABEL. * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param label label gets copied from here * * @retval EINVAL Operation not yet supported. */ static int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label) { return EINVAL; } /** * @brief Retrieve name of currently played song * * This is a handler for the @c SNDCTL_GETSONG ioctl. Audio players could * tell the system the name of the currently playing song, which would be * visible in @c /dev/sndstat. * * See @c http://manuals.opensound.com/developer/SNDCTL_GETSONG.html * for more details. * * @note As the ioctl definition is still under construction, FreeBSD * does not currently support SNDCTL_GETSONG. * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param song song name gets copied here * * @retval EINVAL Operation not yet supported. */ static int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song) { return EINVAL; } /** * @brief Retrieve name of currently played song * * This is a handler for the @c SNDCTL_SETSONG ioctl. Audio players could * tell the system the name of the currently playing song, which would be * visible in @c /dev/sndstat. * * See @c http://manuals.opensound.com/developer/SNDCTL_SETSONG.html * for more details. * * @note As the ioctl definition is still under construction, FreeBSD * does not currently support SNDCTL_SETSONG. * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param song song name gets copied from here * * @retval EINVAL Operation not yet supported. */ static int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song) { return EINVAL; } /** * @brief Rename a device * * This is a handler for the @c SNDCTL_SETNAME ioctl. * * See @c http://manuals.opensound.com/developer/SNDCTL_SETNAME.html for * more details. * * From Hannu@4Front: "This call is used to change the device name * reported in /dev/sndstat and ossinfo. So instead of using some generic * 'OSS loopback audio (MIDI) driver' the device may be given a meaningfull * name depending on the current context (for example 'OSS virtual wave table * synth' or 'VoIP link to London')." * * @note As the ioctl definition is still under construction, FreeBSD * does not currently support SNDCTL_SETNAME. * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param name new device name gets copied from here * * @retval EINVAL Operation not yet supported. */ static int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name) { return EINVAL; } #endif /* !OSSV4_EXPERIMENT */ Index: head/sys/dev/sound/pcm/dsp.h =================================================================== --- head/sys/dev/sound/pcm/dsp.h (revision 170160) +++ head/sys/dev/sound/pcm/dsp.h (revision 170161) @@ -1,36 +1,37 @@ /*- * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _PCMDSP_H_ #define _PCMDSP_H_ extern struct cdevsw dsp_cdevsw; +char *dsp_unit2name(char *, size_t, int); int dsp_oss_audioinfo(struct cdev *, oss_audioinfo *); #endif /* !_PCMDSP_H_ */ Index: head/sys/dev/sound/pcm/feeder.c =================================================================== --- head/sys/dev/sound/pcm/feeder.c (revision 170160) +++ head/sys/dev/sound/pcm/feeder.c (revision 170161) @@ -1,915 +1,917 @@ /*- * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD$"); MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder"); #define MAXFEEDERS 256 #undef FEEDER_DEBUG int feeder_buffersize = FEEDBUFSZ; TUNABLE_INT("hw.snd.feeder_buffersize", &feeder_buffersize); #ifdef SND_DEBUG static int sysctl_hw_snd_feeder_buffersize(SYSCTL_HANDLER_ARGS) { int i, err, val; val = feeder_buffersize; err = sysctl_handle_int(oidp, &val, sizeof(val), req); if (err != 0 || req->newptr == NULL) return err; if (val < FEEDBUFSZ_MIN || val > FEEDBUFSZ_MAX) return EINVAL; i = 0; while (val >> i) i++; i = 1 << i; if (i > val && (i >> 1) > 0 && (i >> 1) >= ((val * 3) >> 2)) i >>= 1; feeder_buffersize = i; return err; } SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_buffersize, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(int), sysctl_hw_snd_feeder_buffersize, "I", "feeder buffer size"); #else SYSCTL_INT(_hw_snd, OID_AUTO, feeder_buffersize, CTLFLAG_RD, &feeder_buffersize, FEEDBUFSZ, "feeder buffer size"); #endif struct feedertab_entry { SLIST_ENTRY(feedertab_entry) link; struct feeder_class *feederclass; struct pcm_feederdesc *desc; int idx; }; static SLIST_HEAD(, feedertab_entry) feedertab; /*****************************************************************************/ void feeder_register(void *p) { static int feedercnt = 0; struct feeder_class *fc = p; struct feedertab_entry *fte; int i; if (feedercnt == 0) { KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name)); SLIST_INIT(&feedertab); fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); if (fte == NULL) { printf("can't allocate memory for root feeder: %s\n", fc->name); return; } fte->feederclass = fc; fte->desc = NULL; fte->idx = feedercnt; SLIST_INSERT_HEAD(&feedertab, fte, link); feedercnt++; /* initialize global variables */ - if (snd_verbose < 0 || snd_verbose > 3) + if (snd_verbose < 0 || snd_verbose > 4) snd_verbose = 1; - if (snd_unit < 0 || snd_unit > PCMMAXDEV) + /* initialize unit numbering */ + snd_unit_init(); + if (snd_unit < 0 || snd_unit > PCMMAXUNIT) snd_unit = 0; if (snd_maxautovchans < 0 || snd_maxautovchans > SND_MAXVCHANS) snd_maxautovchans = 0; if (chn_latency < CHN_LATENCY_MIN || chn_latency > CHN_LATENCY_MAX) chn_latency = CHN_LATENCY_DEFAULT; if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN || chn_latency_profile > CHN_LATENCY_PROFILE_MAX) chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT; if (feeder_buffersize < FEEDBUFSZ_MIN || feeder_buffersize > FEEDBUFSZ_MAX) feeder_buffersize = FEEDBUFSZ; if (feeder_rate_min < FEEDRATE_MIN || feeder_rate_max < FEEDRATE_MIN || feeder_rate_min > FEEDRATE_MAX || feeder_rate_max > FEEDRATE_MAX || !(feeder_rate_min < feeder_rate_max)) { feeder_rate_min = FEEDRATE_RATEMIN; feeder_rate_max = FEEDRATE_RATEMAX; } if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN || feeder_rate_round > FEEDRATE_ROUNDHZ_MAX) feeder_rate_round = FEEDRATE_ROUNDHZ; if (bootverbose) printf("%s: snd_unit=%d snd_maxautovchans=%d " "latency=%d feeder_buffersize=%d " "feeder_rate_min=%d feeder_rate_max=%d " "feeder_rate_round=%d\n", __func__, snd_unit, snd_maxautovchans, chn_latency, feeder_buffersize, feeder_rate_min, feeder_rate_max, feeder_rate_round); /* we've got our root feeder so don't veto pcm loading anymore */ pcm_veto_load = 0; return; } KASSERT(fc->desc != NULL, ("feeder '%s' has no descriptor", fc->name)); /* beyond this point failure is non-fatal but may result in some translations being unavailable */ i = 0; while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) { /* printf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */ fte = malloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); if (fte == NULL) { printf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); return; } fte->feederclass = fc; fte->desc = &fc->desc[i]; fte->idx = feedercnt; fte->desc->idx = feedercnt; SLIST_INSERT_HEAD(&feedertab, fte, link); i++; } feedercnt++; if (feedercnt >= MAXFEEDERS) printf("MAXFEEDERS (%d >= %d) exceeded\n", feedercnt, MAXFEEDERS); } static void feeder_unregisterall(void *p) { struct feedertab_entry *fte, *next; next = SLIST_FIRST(&feedertab); while (next != NULL) { fte = next; next = SLIST_NEXT(fte, link); free(fte, M_FEEDER); } } static int cmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m) { return ((n->type == m->type) && ((n->in == 0) || (n->in == m->in)) && ((n->out == 0) || (n->out == m->out)) && (n->flags == m->flags)); } static void feeder_destroy(struct pcm_feeder *f) { FEEDER_FREE(f); kobj_delete((kobj_t)f, M_FEEDER); } static struct pcm_feeder * feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) { struct pcm_feeder *f; int err; f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO); if (f == NULL) return NULL; f->align = fc->align; f->data = fc->data; f->source = NULL; f->parent = NULL; f->class = fc; f->desc = &(f->desc_static); if (desc) { *(f->desc) = *desc; } else { f->desc->type = FEEDER_ROOT; f->desc->in = 0; f->desc->out = 0; f->desc->flags = 0; f->desc->idx = 0; } err = FEEDER_INIT(f); if (err) { printf("feeder_init(%p) on %s returned %d\n", f, fc->name, err); feeder_destroy(f); return NULL; } return f; } struct feeder_class * feeder_getclass(struct pcm_feederdesc *desc) { struct feedertab_entry *fte; SLIST_FOREACH(fte, &feedertab, link) { if ((desc == NULL) && (fte->desc == NULL)) return fte->feederclass; if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc)) return fte->feederclass; } return NULL; } int chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc) { struct pcm_feeder *nf; nf = feeder_create(fc, desc); if (nf == NULL) return ENOSPC; nf->source = c->feeder; /* XXX we should use the lowest common denominator for align */ if (nf->align > 0) c->align += nf->align; else if (nf->align < 0 && c->align < -nf->align) c->align = -nf->align; if (c->feeder != NULL) c->feeder->parent = nf; c->feeder = nf; return 0; } int chn_removefeeder(struct pcm_channel *c) { struct pcm_feeder *f; if (c->feeder == NULL) return -1; f = c->feeder; c->feeder = c->feeder->source; feeder_destroy(f); return 0; } struct pcm_feeder * chn_findfeeder(struct pcm_channel *c, u_int32_t type) { struct pcm_feeder *f; f = c->feeder; while (f != NULL) { if (f->desc->type == type) return f; f = f->source; } return NULL; } static int chainok(struct pcm_feeder *test, struct pcm_feeder *stop) { u_int32_t visited[MAXFEEDERS / 32]; u_int32_t idx, mask; bzero(visited, sizeof(visited)); while (test && (test != stop)) { idx = test->desc->idx; if (idx < 0) panic("bad idx %d", idx); if (idx >= MAXFEEDERS) panic("bad idx %d", idx); mask = 1 << (idx & 31); idx >>= 5; if (visited[idx] & mask) return 0; visited[idx] |= mask; test = test->source; } return 1; } /* - * See feeder_fmtchain() for the mumbo-jumbo ridiculous explaination + * See feeder_fmtchain() for the mumbo-jumbo ridiculous explanation * of what the heck is this FMT_Q_* */ #define FMT_Q_UP 1 #define FMT_Q_DOWN 2 #define FMT_Q_EQ 3 #define FMT_Q_MULTI 4 /* * 14bit format scoring * -------------------- * * 13 12 11 10 9 8 2 1 0 offset * +---+---+---+---+---+---+-------------+---+---+ * | X | X | X | X | X | X | X X X X X X | X | X | * +---+---+---+---+---+---+-------------+---+---+ * | | | | | | | | | * | | | | | | | | +--> signed? * | | | | | | | | * | | | | | | | +------> bigendian? * | | | | | | | * | | | | | | +---------------> total channels * | | | | | | * | | | | | +------------------------> AFMT_A_LAW * | | | | | * | | | | +----------------------------> AFMT_MU_LAW * | | | | * | | | +--------------------------------> AFMT_8BIT * | | | * | | +------------------------------------> AFMT_16BIT * | | * | +----------------------------------------> AFMT_24BIT * | * +--------------------------------------------> AFMT_32BIT */ #define score_signeq(s1, s2) (((s1) & 0x1) == ((s2) & 0x1)) #define score_endianeq(s1, s2) (((s1) & 0x2) == ((s2) & 0x2)) #define score_cheq(s1, s2) (((s1) & 0xfc) == ((s2) & 0xfc)) #define score_val(s1) ((s1) & 0x3f00) #define score_cse(s1) ((s1) & 0x7f) u_int32_t chn_fmtscore(u_int32_t fmt) { u_int32_t ret; ret = 0; if (fmt & AFMT_SIGNED) ret |= 1 << 0; if (fmt & AFMT_BIGENDIAN) ret |= 1 << 1; if (fmt & AFMT_STEREO) ret |= (2 & 0x3f) << 2; else ret |= (1 & 0x3f) << 2; if (fmt & AFMT_A_LAW) ret |= 1 << 8; else if (fmt & AFMT_MU_LAW) ret |= 1 << 9; else if (fmt & AFMT_8BIT) ret |= 1 << 10; else if (fmt & AFMT_16BIT) ret |= 1 << 11; else if (fmt & AFMT_24BIT) ret |= 1 << 12; else if (fmt & AFMT_32BIT) ret |= 1 << 13; return ret; } static u_int32_t chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq) { u_int32_t best, score, score2, oldscore; int i; if (fmt == 0 || fmts == NULL || fmts[0] == 0) return 0; if (fmtvalid(fmt, fmts)) return fmt; best = 0; score = chn_fmtscore(fmt); oldscore = 0; for (i = 0; fmts[i] != 0; i++) { score2 = chn_fmtscore(fmts[i]); if (cheq && !score_cheq(score, score2)) continue; if (oldscore == 0 || (score_val(score2) == score_val(score)) || (score_val(score2) == score_val(oldscore)) || (score_val(score2) > score_val(oldscore) && score_val(score2) < score_val(score)) || (score_val(score2) < score_val(oldscore) && score_val(score2) > score_val(score)) || (score_val(oldscore) < score_val(score) && score_val(score2) > score_val(oldscore))) { if (score_val(oldscore) != score_val(score2) || score_cse(score) == score_cse(score2) || ((score_cse(oldscore) != score_cse(score) && !score_endianeq(score, oldscore) && (score_endianeq(score, score2) || (!score_signeq(score, oldscore) && score_signeq(score, score2)))))) { best = fmts[i]; oldscore = score2; } } } return best; } u_int32_t chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts) { return chn_fmtbestfunc(fmt, fmts, 0); } u_int32_t chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts) { return chn_fmtbestfunc(fmt, fmts, 1); } u_int32_t chn_fmtbest(u_int32_t fmt, u_int32_t *fmts) { u_int32_t best1, best2; u_int32_t score, score1, score2; if (fmtvalid(fmt, fmts)) return fmt; best1 = chn_fmtbeststereo(fmt, fmts); best2 = chn_fmtbestbit(fmt, fmts); if (best1 != 0 && best2 != 0 && best1 != best2) { if (fmt & AFMT_STEREO) return best1; else { score = score_val(chn_fmtscore(fmt)); score1 = score_val(chn_fmtscore(best1)); score2 = score_val(chn_fmtscore(best2)); if (score1 == score2 || score1 == score) return best1; else if (score2 == score) return best2; else if (score1 > score2) return best1; return best2; } } else if (best2 == 0) return best1; else return best2; } static struct pcm_feeder * feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) { struct feedertab_entry *fte, *ftebest; struct pcm_feeder *try, *ret; uint32_t fl, qout, qsrc, qdst; int qtype; if (to == NULL || to[0] == 0) return NULL; DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out)); if (fmtvalid(source->desc->out, to)) { DEB(printf("got it\n")); return source; } if (maxdepth < 0) return NULL; /* * WARNING: THIS IS _NOT_ FOR THE FAINT HEART * Disclaimer: I don't expect anybody could understand this * without deep logical and mathematical analysis * involving various unnamed probability theorem. * * This "Best Fit Random Chain Selection" (BLEHBLEHWHATEVER) algorithm * is **extremely** difficult to digest especially when applied to * large sets / numbers of random chains (feeders), each with * unique characteristic providing different sets of in/out format. * * Basically, our FEEDER_FMT (see feeder_fmt.c) chains characteristic: * 1) Format chains * 1.1 "8bit to any, not to 8bit" * 1.1.1 sign can remain consistent, e.g: u8 -> u16[le|be] * 1.1.2 sign can be changed, e.g: u8 -> s16[le|be] * 1.1.3 endian can be changed, e.g: u8 -> u16[le|be] * 1.1.4 both can be changed, e.g: u8 -> [u|s]16[le|be] * 1.2 "Any to 8bit, not from 8bit" * 1.2.1 sign can remain consistent, e.g: s16le -> s8 * 1.2.2 sign can be changed, e.g: s16le -> u8 * 1.2.3 source endian can be anything e.g: s16[le|be] -> s8 * 1.2.4 source endian / sign can be anything e.g: [u|s]16[le|be] -> u8 * 1.3 "Any to any where BOTH input and output either 8bit or non-8bit" * 1.3.1 endian MUST remain consistent * 1.3.2 sign CAN be changed * 1.4 "Long jump" is allowed, e.g: from 16bit to 32bit, excluding * 16bit to 24bit . * 2) Channel chains (mono <-> stereo) * 2.1 Both endian and sign MUST remain consistent * 3) Endian chains (big endian <-> little endian) * 3.1 Channels and sign MUST remain consistent * 4) Sign chains (signed <-> unsigned) * 4.1 Channels and endian MUST remain consistent * * .. and the mother of all chaining rules: * * Rules 0: Source and destination MUST not contain multiple selections. * (qtype != FMT_Q_MULTI) * * First of all, our caller ( chn_fmtchain() ) will reduce the possible * multiple from/to formats to a single best format using chn_fmtbest(). * Then, using chn_fmtscore(), we determine the chaining characteristic. * Our main goal is to narrow it down until it reach FMT_Q_EQ chaining * type while still adhering above chaining rules. * * The need for this complicated chaining procedures is inevitable, * since currently we have more than 200 different types of FEEDER_FMT * doing various unique format conversion. Without this (the old way), * it is possible to generate broken chain since it doesn't do any * sanity checking to ensure that the output format is "properly aligned" * with the direction of conversion (quality up/down/equal). * * Conversion: s24le to s32le * Possible chain: 1) s24le -> s32le (correct, optimized) * 2) s24le -> s16le -> s32le * (since we have feeder_24to16 and feeder_16to32) * +-- obviously broken! * * Using scoring mechanisme, this will ensure that the chaining * process do the right thing, or at least, give the best chain * possible without causing quality (the 'Q') degradation. */ qdst = chn_fmtscore(to[0]); qsrc = chn_fmtscore(source->desc->out); #define score_q(s1) score_val(s1) #define score_8bit(s1) ((s1) & 0x700) #define score_non8bit(s1) (!score_8bit(s1)) #define score_across8bit(s1, s2) ((score_8bit(s1) && score_non8bit(s2)) || \ (score_8bit(s2) && score_non8bit(s1))) #define FMT_CHAIN_Q_UP(s1, s2) (score_q(s1) < score_q(s2)) #define FMT_CHAIN_Q_DOWN(s1, s2) (score_q(s1) > score_q(s2)) #define FMT_CHAIN_Q_EQ(s1, s2) (score_q(s1) == score_q(s2)) #define FMT_Q_DOWN_FLAGS(s1, s2) (0x1 | (score_across8bit(s1, s2) ? \ 0x2 : 0x0)) #define FMT_Q_UP_FLAGS(s1, s2) FMT_Q_DOWN_FLAGS(s1, s2) #define FMT_Q_EQ_FLAGS(s1, s2) (0x3ffc | \ ((score_cheq(s1, s2) && \ score_endianeq(s1, s2)) ? \ 0x1 : 0x0) | \ ((score_cheq(s1, s2) && \ score_signeq(s1, s2)) ? \ 0x2 : 0x0)) /* Determine chaining direction and set matching flag */ fl = 0x3fff; if (to[1] != 0) { qtype = FMT_Q_MULTI; printf("%s: WARNING: FMT_Q_MULTI chaining. Expect the unexpected.\n", __func__); } else if (FMT_CHAIN_Q_DOWN(qsrc, qdst)) { qtype = FMT_Q_DOWN; fl = FMT_Q_DOWN_FLAGS(qsrc, qdst); } else if (FMT_CHAIN_Q_UP(qsrc, qdst)) { qtype = FMT_Q_UP; fl = FMT_Q_UP_FLAGS(qsrc, qdst); } else { qtype = FMT_Q_EQ; fl = FMT_Q_EQ_FLAGS(qsrc, qdst); } ftebest = NULL; SLIST_FOREACH(fte, &feedertab, link) { if (fte->desc == NULL) continue; if (fte->desc->type != FEEDER_FMT) continue; qout = chn_fmtscore(fte->desc->out); #define FMT_Q_MULTI_VALIDATE(qt) ((qt) == FMT_Q_MULTI) #define FMT_Q_FL_MATCH(qfl, s1, s2) (((s1) & (qfl)) == ((s2) & (qfl))) #define FMT_Q_UP_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_UP && \ score_q(s3) >= score_q(s1) && \ score_q(s3) <= score_q(s2)) #define FMT_Q_DOWN_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_DOWN && \ score_q(s3) <= score_q(s1) && \ score_q(s3) >= score_q(s2)) #define FMT_Q_EQ_VALIDATE(qt, s1, s2) ((qt) == FMT_Q_EQ && \ score_q(s1) == score_q(s2)) if (fte->desc->in == source->desc->out && (FMT_Q_MULTI_VALIDATE(qtype) || (FMT_Q_FL_MATCH(fl, qout, qdst) && (FMT_Q_UP_VALIDATE(qtype, qsrc, qdst, qout) || FMT_Q_DOWN_VALIDATE(qtype, qsrc, qdst, qout) || FMT_Q_EQ_VALIDATE(qtype, qdst, qout))))) { try = feeder_create(fte->feederclass, fte->desc); if (try) { try->source = source; ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; if (ret != NULL) return ret; feeder_destroy(try); } } else if (fte->desc->in == source->desc->out) { /* XXX quality must be considered! */ if (ftebest == NULL) ftebest = fte; } } if (ftebest != NULL) { try = feeder_create(ftebest->feederclass, ftebest->desc); if (try) { try->source = source; ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; if (ret != NULL) return ret; feeder_destroy(try); } } /* printf("giving up %s...\n", source->class->name); */ return NULL; } u_int32_t chn_fmtchain(struct pcm_channel *c, u_int32_t *to) { struct pcm_feeder *try, *del, *stop; u_int32_t tmpfrom[2], tmpto[2], best, *from; int i, max, bestmax; KASSERT(c != NULL, ("c == NULL")); KASSERT(c->feeder != NULL, ("c->feeder == NULL")); KASSERT(to != NULL, ("to == NULL")); KASSERT(to[0] != 0, ("to[0] == 0")); if (c == NULL || c->feeder == NULL || to == NULL || to[0] == 0) return 0; stop = c->feeder; best = 0; if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { from = chn_getcaps(c)->fmtlist; if (from[1] != 0) { best = chn_fmtbest(to[0], from); if (best != 0) { tmpfrom[0] = best; tmpfrom[1] = 0; from = tmpfrom; } } } else { tmpfrom[0] = c->feeder->desc->out; tmpfrom[1] = 0; from = tmpfrom; if (to[1] != 0) { best = chn_fmtbest(from[0], to); if (best != 0) { tmpto[0] = best; tmpto[1] = 0; to = tmpto; } } } #define FEEDER_FMTCHAIN_MAXDEPTH 8 try = NULL; if (to[0] != 0 && from[0] != 0 && to[1] == 0 && from[1] == 0) { max = 0; best = from[0]; c->feeder->desc->out = best; do { try = feeder_fmtchain(to, c->feeder, stop, max); DEB(if (try != NULL) { printf("%s: 0x%08x -> 0x%08x (maxdepth: %d)\n", __func__, from[0], to[0], max); }); } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); } else { printf("%s: Using the old-way format chaining!\n", __func__); i = 0; best = 0; bestmax = 100; while (from[i] != 0) { c->feeder->desc->out = from[i]; try = NULL; max = 0; do { try = feeder_fmtchain(to, c->feeder, stop, max); } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); if (try != NULL && max < bestmax) { bestmax = max; best = from[i]; } while (try != NULL && try != stop) { del = try; try = try->source; feeder_destroy(del); } i++; } if (best == 0) return 0; c->feeder->desc->out = best; try = feeder_fmtchain(to, c->feeder, stop, bestmax); } if (try == NULL) return 0; c->feeder = try; c->align = 0; #ifdef FEEDER_DEBUG printf("\n\nchain: "); #endif while (try && (try != stop)) { #ifdef FEEDER_DEBUG printf("%s [%d]", try->class->name, try->desc->idx); if (try->source) printf(" -> "); #endif if (try->source) try->source->parent = try; if (try->align > 0) c->align += try->align; else if (try->align < 0 && c->align < -try->align) c->align = -try->align; try = try->source; } #ifdef FEEDER_DEBUG printf("%s [%d]\n", try->class->name, try->desc->idx); #endif if (c->direction == PCMDIR_REC) { try = c->feeder; while (try != NULL) { if (try->desc->type == FEEDER_ROOT) return try->desc->out; try = try->source; } return best; } else return c->feeder->desc->out; } void feeder_printchain(struct pcm_feeder *head) { struct pcm_feeder *f; printf("feeder chain (head @%p)\n", head); f = head; while (f != NULL) { printf("%s/%d @ %p\n", f->class->name, f->desc->idx, f); f = f->source; } printf("[end]\n\n"); } /*****************************************************************************/ static int feed_root(struct pcm_feeder *feeder, struct pcm_channel *ch, u_int8_t *buffer, u_int32_t count, void *source) { struct snd_dbuf *src = source; int l, offset; KASSERT(count > 0, ("feed_root: count == 0")); /* count &= ~((1 << ch->align) - 1); */ KASSERT(count > 0, ("feed_root: aligned count == 0 (align = %d)", ch->align)); if (++ch->feedcount == 0) ch->feedcount = 2; l = min(count, sndbuf_getready(src)); /* When recording only return as much data as available */ if (ch->direction == PCMDIR_REC) { sndbuf_dispose(src, buffer, l); return l; } offset = count - l; if (offset > 0) { if (snd_verbose > 3) printf("%s: (%s) %spending %d bytes " "(count=%d l=%d feed=%d)\n", __func__, (ch->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware", (ch->feedcount == 1) ? "pre" : "ap", offset, count, l, ch->feedcount); if (ch->feedcount == 1) { memset(buffer, sndbuf_zerodata(sndbuf_getfmt(src)), offset); if (l > 0) sndbuf_dispose(src, buffer + offset, l); else ch->feedcount--; } else { if (l > 0) sndbuf_dispose(src, buffer, l); #if 1 memset(buffer + l, sndbuf_zerodata(sndbuf_getfmt(src)), offset); if (!(ch->flags & CHN_F_CLOSING)) ch->xruns++; #else if (l < 1 || (ch->flags & CHN_F_CLOSING)) { memset(buffer + l, sndbuf_zerodata(sndbuf_getfmt(src)), offset); if (!(ch->flags & CHN_F_CLOSING)) ch->xruns++; } else { int cp, tgt; tgt = l; while (offset > 0) { cp = min(l, offset); memcpy(buffer + tgt, buffer, cp); offset -= cp; tgt += cp; } ch->xruns++; } #endif } } else if (l > 0) sndbuf_dispose(src, buffer, l); return count; } static kobj_method_t feeder_root_methods[] = { KOBJMETHOD(feeder_feed, feed_root), { 0, 0 } }; static struct feeder_class feeder_root_class = { .name = "feeder_root", .methods = feeder_root_methods, .size = sizeof(struct pcm_feeder), .align = 0, .desc = NULL, .data = NULL, }; SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class); SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL); Index: head/sys/dev/sound/pcm/mixer.c =================================================================== --- head/sys/dev/sound/pcm/mixer.c (revision 170160) +++ head/sys/dev/sound/pcm/mixer.c (revision 170161) @@ -1,1032 +1,1049 @@ /*- * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "mixer_if.h" SND_DECLARE_FILE("$FreeBSD$"); MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); #define MIXER_NAMELEN 16 struct snd_mixer { KOBJ_FIELDS; const char *type; void *devinfo; int busy; int hwvol_muted; int hwvol_mixer; int hwvol_step; device_t dev; u_int32_t hwvol_mute_level; u_int32_t devs; u_int32_t recdevs; u_int32_t recsrc; u_int16_t level[32]; u_int8_t parent[32]; u_int32_t child[32]; u_int8_t realdev[32]; char name[MIXER_NAMELEN]; struct mtx *lock; oss_mixer_enuminfo enuminfo; /** * Counter is incremented when applications change any of this * mixer's controls. A change in value indicates that persistent * mixer applications should update their displays. */ int modify_counter; }; static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_VOLUME] = 75, [SOUND_MIXER_BASS] = 50, [SOUND_MIXER_TREBLE] = 50, [SOUND_MIXER_SYNTH] = 75, [SOUND_MIXER_PCM] = 75, [SOUND_MIXER_SPEAKER] = 75, [SOUND_MIXER_LINE] = 75, [SOUND_MIXER_MIC] = 0, [SOUND_MIXER_CD] = 75, [SOUND_MIXER_IGAIN] = 0, [SOUND_MIXER_LINE1] = 75, [SOUND_MIXER_VIDEO] = 75, [SOUND_MIXER_RECLEV] = 0, [SOUND_MIXER_OGAIN] = 50, [SOUND_MIXER_MONITOR] = 75, }; static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; static d_open_t mixer_open; static d_close_t mixer_close; static struct cdevsw mixer_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = mixer_open, .d_close = mixer_close, .d_ioctl = mixer_ioctl, .d_name = "mixer", }; /** * Keeps a count of mixer devices; used only by OSSv4 SNDCTL_SYSINFO ioctl. */ int mixer_count = 0; #ifdef USING_DEVFS -static eventhandler_tag mixer_ehtag; +static eventhandler_tag mixer_ehtag = NULL; #endif static struct cdev * mixer_get_devt(device_t dev) { struct snddev_info *snddev; snddev = device_get_softc(dev); return snddev->mixer_dev; } #ifdef SND_DYNSYSCTL static int mixer_lookup(char *devname) { int i; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (strncmp(devname, snd_mixernames[i], strlen(snd_mixernames[i])) == 0) return i; return -1; } #endif static int mixer_set_softpcmvol(struct snd_mixer *mixer, struct snddev_info *d, unsigned left, unsigned right) { - struct snddev_channel *sce; - struct pcm_channel *ch; -#ifdef USING_MUTEX - int locked = (mixer->lock && mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0; + struct pcm_channel *c; + int locked; + locked = (mixer->lock != NULL && + mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0; if (locked) snd_mtxunlock(mixer->lock); -#endif - SLIST_FOREACH(sce, &d->channels, link) { - ch = sce->channel; - CHN_LOCK(ch); - if (ch->direction == PCMDIR_PLAY && - (ch->feederflags & (1 << FEEDER_VOLUME))) - chn_setvolume(ch, left, right); - CHN_UNLOCK(ch); + + if (CHN_EMPTY(d, channels.pcm.busy)) { + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + if (c->direction == PCMDIR_PLAY && + (c->feederflags & (1 << FEEDER_VOLUME))) + chn_setvolume(c, left, right); + CHN_UNLOCK(c); + } + } else { + CHN_FOREACH(c, d, channels.pcm.busy) { + CHN_LOCK(c); + if (c->direction == PCMDIR_PLAY && + (c->feederflags & (1 << FEEDER_VOLUME))) + chn_setvolume(c, left, right); + CHN_UNLOCK(c); + } } -#ifdef USING_MUTEX + if (locked) snd_mtxlock(mixer->lock); -#endif + return 0; } static int mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev) { struct snddev_info *d; unsigned l, r, tl, tr; u_int32_t parent = SOUND_MIXER_NONE, child = 0; u_int32_t realdev; int i; if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || (0 == (m->devs & (1 << dev)))) return -1; l = min((lev & 0x00ff), 100); r = min(((lev & 0xff00) >> 8), 100); realdev = m->realdev[dev]; d = device_get_softc(m->dev); if (d == NULL) return -1; /* TODO: recursive handling */ parent = m->parent[dev]; if (parent >= SOUND_MIXER_NRDEVICES) parent = SOUND_MIXER_NONE; if (parent == SOUND_MIXER_NONE) child = m->child[dev]; if (parent != SOUND_MIXER_NONE) { tl = (l * (m->level[parent] & 0x00ff)) / 100; tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100; if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) mixer_set_softpcmvol(m, d, tl, tr); else if (realdev != SOUND_MIXER_NONE && MIXER_SET(m, realdev, tl, tr) < 0) return -1; } else if (child != 0) { for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (!(child & (1 << i)) || m->parent[i] != dev) continue; realdev = m->realdev[i]; tl = (l * (m->level[i] & 0x00ff)) / 100; tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) mixer_set_softpcmvol(m, d, tl, tr); else if (realdev != SOUND_MIXER_NONE) MIXER_SET(m, realdev, tl, tr); } realdev = m->realdev[dev]; if (realdev != SOUND_MIXER_NONE && MIXER_SET(m, realdev, l, r) < 0) return -1; } else { if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) mixer_set_softpcmvol(m, d, l, r); else if (realdev != SOUND_MIXER_NONE && MIXER_SET(m, realdev, l, r) < 0) return -1; } m->level[dev] = l | (r << 8); return 0; } static int mixer_get(struct snd_mixer *mixer, int dev) { if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) return mixer->level[dev]; else return -1; } static int mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) { src &= mixer->recdevs; if (src == 0) src = SOUND_MASK_MIC; mixer->recsrc = MIXER_SETRECSRC(mixer, src); return 0; } static int mixer_getrecsrc(struct snd_mixer *mixer) { return mixer->recsrc; } /** * @brief Retrieve the route number of the current recording device * * OSSv4 assigns routing numbers to recording devices, unlike the previous * API which relied on a fixed table of device numbers and names. This * function returns the routing number of the device currently selected * for recording. * * For now, this function is kind of a goofy compatibility stub atop the * existing sound system. (For example, in theory, the old sound system * allows multiple recording devices to be specified via a bitmask.) * * @param m mixer context container thing * * @retval 0 success * @retval EIDRM no recording device found (generally not possible) * @todo Ask about error code */ static int mixer_get_recroute(struct snd_mixer *m, int *route) { int i, cnt; cnt = 0; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { /** @todo can user set a multi-device mask? (== or &?) */ if ((1 << i) == m->recsrc) break; if ((1 << i) & m->recdevs) ++cnt; } if (i == SOUND_MIXER_NRDEVICES) return EIDRM; *route = cnt; return 0; } /** * @brief Select a device for recording * * This function sets a recording source based on a recording device's * routing number. Said number is translated to an old school recdev * mask and passed over mixer_setrecsrc. * * @param m mixer context container thing * * @retval 0 success(?) * @retval EINVAL User specified an invalid device number * @retval otherwise error from mixer_setrecsrc */ static int mixer_set_recroute(struct snd_mixer *m, int route) { int i, cnt, ret; ret = 0; cnt = 0; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if ((1 << i) & m->recdevs) { if (route == cnt) break; ++cnt; } } if (i == SOUND_MIXER_NRDEVICES) ret = EINVAL; else ret = mixer_setrecsrc(m, (1 << i)); return ret; } void mix_setdevs(struct snd_mixer *m, u_int32_t v) { struct snddev_info *d; int i; if (m == NULL) return; d = device_get_softc(m->dev); if (d != NULL && (d->flags & SD_F_SOFTPCMVOL)) v |= SOUND_MASK_PCM; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (m->parent[i] < SOUND_MIXER_NRDEVICES) v |= 1 << m->parent[i]; v |= m->child[i]; } m->devs = v; } /** * @brief Record mask of available recording devices * * Calling functions are responsible for defining the mask of available * recording devices. This function records that value in a structure * used by the rest of the mixer code. * * This function also populates a structure used by the SNDCTL_DSP_*RECSRC* * family of ioctls that are part of OSSV4. All recording device labels * are concatenated in ascending order corresponding to their routing * numbers. (Ex: a system might have 0 => 'vol', 1 => 'cd', 2 => 'line', * etc.) For now, these labels are just the standard recording device * names (cd, line1, etc.), but will eventually be fully dynamic and user * controlled. * * @param m mixer device context container thing * @param v mask of recording devices */ void mix_setrecdevs(struct snd_mixer *m, u_int32_t v) { oss_mixer_enuminfo *ei; char *loc; int i, nvalues, nwrote, nleft, ncopied; ei = &m->enuminfo; nvalues = 0; nwrote = 0; nleft = sizeof(ei->strings); loc = ei->strings; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if ((1 << i) & v) { ei->strindex[nvalues] = nwrote; ncopied = strlcpy(loc, snd_mixernames[i], nleft) + 1; /* strlcpy retval doesn't include terminator */ nwrote += ncopied; nleft -= ncopied; nvalues++; /* * XXX I don't think this should ever be possible. * Even with a move to dynamic device/channel names, * each label is limited to ~16 characters, so that'd * take a LOT to fill this buffer. */ if ((nleft <= 0) || (nvalues >= OSS_ENUM_MAXVALUE)) { device_printf(m->dev, "mix_setrecdevs: Not enough room to store device names--please file a bug report.\n"); device_printf(m->dev, "mix_setrecdevs: Please include details about your sound hardware, OS version, etc.\n"); break; } loc = &ei->strings[nwrote]; } } /* * NB: The SNDCTL_DSP_GET_RECSRC_NAMES ioctl ignores the dev * and ctrl fields. */ ei->nvalues = nvalues; m->recdevs = v; } void mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs) { u_int32_t mask = 0; int i; if (m == NULL || parent >= SOUND_MIXER_NRDEVICES) return; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (i == parent) continue; if (childs & (1 << i)) { mask |= 1 << i; if (m->parent[i] < SOUND_MIXER_NRDEVICES) m->child[m->parent[i]] &= ~(1 << i); m->parent[i] = parent; m->child[i] = 0; } } mask &= ~(1 << parent); m->child[parent] = mask; } void mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev) { if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES)) return; m->realdev[dev] = realdev; } u_int32_t mix_getparent(struct snd_mixer *m, u_int32_t dev) { if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) return SOUND_MIXER_NONE; return m->parent[dev]; } u_int32_t mix_getchild(struct snd_mixer *m, u_int32_t dev) { if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) return 0; return m->child[dev]; } u_int32_t mix_getdevs(struct snd_mixer *m) { return m->devs; } u_int32_t mix_getrecdevs(struct snd_mixer *m) { return m->recdevs; } void * mix_getdevinfo(struct snd_mixer *m) { return m->devinfo; } int mixer_init(device_t dev, kobj_class_t cls, void *devinfo) { struct snddev_info *snddev; struct snd_mixer *m; u_int16_t v; struct cdev *pdev; - int i, unit, val; + int i, unit, devunit, val; m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev)); m->lock = snd_mtxcreate(m->name, "pcm mixer"); m->type = cls->name; m->devinfo = devinfo; m->busy = 0; m->dev = dev; for (i = 0; i < 32; i++) { m->parent[i] = SOUND_MIXER_NONE; m->child[i] = 0; m->realdev[i] = i; } if (MIXER_INIT(m)) goto bad; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { v = snd_mixerdefaults[i]; if (resource_int_value(device_get_name(dev), device_get_unit(dev), snd_mixernames[i], &val) == 0) { if (val >= 0 && val <= 100) { v = (u_int16_t) val; } } mixer_set(m, i, v | (v << 8)); } mixer_setrecsrc(m, SOUND_MASK_MIC); unit = device_get_unit(dev); - pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), + devunit = snd_mkunit(unit, SND_DEV_CTL, 0); + pdev = make_dev(&mixer_cdevsw, unit2minor(devunit), UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); pdev->si_drv1 = m; snddev = device_get_softc(dev); snddev->mixer_dev = pdev; ++mixer_count; if (bootverbose) { for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (!(m->devs & (1 << i))) continue; if (m->realdev[i] != i) { device_printf(dev, "Mixer \"%s\" -> \"%s\":", snd_mixernames[i], (m->realdev[i] < SOUND_MIXER_NRDEVICES) ? snd_mixernames[m->realdev[i]] : "none"); } else { device_printf(dev, "Mixer \"%s\":", snd_mixernames[i]); } if (m->parent[i] < SOUND_MIXER_NRDEVICES) printf(" parent=\"%s\"", snd_mixernames[m->parent[i]]); if (m->child[i] != 0) printf(" child=0x%08x", m->child[i]); printf("\n"); } if (snddev->flags & SD_F_SOFTPCMVOL) device_printf(dev, "Soft PCM mixer ENABLED\n"); } return 0; bad: snd_mtxlock(m->lock); snd_mtxfree(m->lock); kobj_delete((kobj_t)m, M_MIXER); return -1; } int mixer_uninit(device_t dev) { int i; struct snddev_info *d; struct snd_mixer *m; struct cdev *pdev; d = device_get_softc(dev); pdev = mixer_get_devt(dev); if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL) return EBADF; m = pdev->si_drv1; snd_mtxlock(m->lock); if (m->busy) { snd_mtxunlock(m->lock); return EBUSY; } pdev->si_drv1 = NULL; destroy_dev(pdev); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) mixer_set(m, i, 0); mixer_setrecsrc(m, SOUND_MASK_MIC); MIXER_UNINIT(m); snd_mtxfree(m->lock); kobj_delete((kobj_t)m, M_MIXER); d->mixer_dev = NULL; --mixer_count; return 0; } int mixer_reinit(device_t dev) { struct snd_mixer *m; struct cdev *pdev; int i; pdev = mixer_get_devt(dev); m = pdev->si_drv1; snd_mtxlock(m->lock); i = MIXER_REINIT(m); if (i) { snd_mtxunlock(m->lock); return i; } for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) mixer_set(m, i, m->level[i]); mixer_setrecsrc(m, m->recsrc); snd_mtxunlock(m->lock); return 0; } #ifdef SND_DYNSYSCTL static int sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) { char devname[32]; int error, dev; struct snd_mixer *m; m = oidp->oid_arg1; snd_mtxlock(m->lock); strlcpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); snd_mtxunlock(m->lock); error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); snd_mtxlock(m->lock); if (error == 0 && req->newptr != NULL) { dev = mixer_lookup(devname); if (dev == -1) { snd_mtxunlock(m->lock); return EINVAL; } else if (dev != m->hwvol_mixer) { m->hwvol_mixer = dev; m->hwvol_muted = 0; } } snd_mtxunlock(m->lock); return error; } #endif int mixer_hwvol_init(device_t dev) { struct snd_mixer *m; struct cdev *pdev; pdev = mixer_get_devt(dev); m = pdev->si_drv1; m->hwvol_mixer = SOUND_MIXER_VOLUME; m->hwvol_step = 5; #ifdef SND_DYNSYSCTL SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, sysctl_hw_snd_hwvol_mixer, "A", ""); #endif return 0; } void mixer_hwvol_mute(device_t dev) { struct snd_mixer *m; struct cdev *pdev; pdev = mixer_get_devt(dev); m = pdev->si_drv1; snd_mtxlock(m->lock); if (m->hwvol_muted) { m->hwvol_muted = 0; mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); } else { m->hwvol_muted++; m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); mixer_set(m, m->hwvol_mixer, 0); } snd_mtxunlock(m->lock); } void mixer_hwvol_step(device_t dev, int left_step, int right_step) { struct snd_mixer *m; int level, left, right; struct cdev *pdev; pdev = mixer_get_devt(dev); m = pdev->si_drv1; snd_mtxlock(m->lock); if (m->hwvol_muted) { m->hwvol_muted = 0; level = m->hwvol_mute_level; } else level = mixer_get(m, m->hwvol_mixer); if (level != -1) { left = level & 0xff; right = level >> 8; left += left_step * m->hwvol_step; if (left < 0) left = 0; right += right_step * m->hwvol_step; if (right < 0) right = 0; mixer_set(m, m->hwvol_mixer, left | right << 8); } snd_mtxunlock(m->lock); } /* ----------------------------------------------------------------------- */ static int mixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td) { struct snd_mixer *m; m = i_dev->si_drv1; snd_mtxlock(m->lock); m->busy = 1; snd_mtxunlock(m->lock); return 0; } static int mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td) { struct snd_mixer *m; m = i_dev->si_drv1; snd_mtxlock(m->lock); if (!m->busy) { snd_mtxunlock(m->lock); return EBADF; } m->busy = 0; snd_mtxunlock(m->lock); return 0; } int mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) { struct snd_mixer *m; int ret, *arg_i = (int *)arg; int v = -1, j = cmd & 0xff; m = i_dev->si_drv1; if (m == NULL) return EBADF; snd_mtxlock(m->lock); if (mode != -1 && !m->busy) { snd_mtxunlock(m->lock); return EBADF; } if (cmd == SNDCTL_MIXERINFO) { snd_mtxunlock(m->lock); return mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg); } if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { if (j == SOUND_MIXER_RECSRC) ret = mixer_setrecsrc(m, *arg_i); else ret = mixer_set(m, j, *arg_i); snd_mtxunlock(m->lock); return (ret == 0)? 0 : ENXIO; } if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { switch (j) { case SOUND_MIXER_DEVMASK: case SOUND_MIXER_CAPS: case SOUND_MIXER_STEREODEVS: v = mix_getdevs(m); break; case SOUND_MIXER_RECMASK: v = mix_getrecdevs(m); break; case SOUND_MIXER_RECSRC: v = mixer_getrecsrc(m); break; default: v = mixer_get(m, j); } *arg_i = v; snd_mtxunlock(m->lock); return (v != -1)? 0 : ENXIO; } ret = 0; switch (cmd) { /** @todo Double check return values, error codes. */ case SNDCTL_SYSINFO: sound_oss_sysinfo((oss_sysinfo *)arg); break; case SNDCTL_AUDIOINFO: ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg); break; case SNDCTL_DSP_GET_RECSRC_NAMES: bcopy((void *)&m->enuminfo, arg, sizeof(oss_mixer_enuminfo)); break; case SNDCTL_DSP_GET_RECSRC: ret = mixer_get_recroute(m, arg_i); break; case SNDCTL_DSP_SET_RECSRC: ret = mixer_set_recroute(m, *arg_i); break; case OSS_GETVERSION: *arg_i = SOUND_VERSION; break; default: ret = ENXIO; } snd_mtxunlock(m->lock); return ret; } #ifdef USING_DEVFS static void -mixer_clone(void *arg, struct ucred *cred, char *name, int namelen, - struct cdev **dev) +mixer_clone(void *arg, +#if __FreeBSD_version >= 600034 + struct ucred *cred, +#endif + char *name, int namelen, struct cdev **dev) { - struct snddev_info *sd; + struct snddev_info *d; if (*dev != NULL) return; if (strcmp(name, "mixer") == 0) { - sd = devclass_get_softc(pcm_devclass, snd_unit); - if (sd != NULL && sd->mixer_dev != NULL) { - *dev = sd->mixer_dev; + d = devclass_get_softc(pcm_devclass, snd_unit); + if (d != NULL && d->mixer_dev != NULL) { + *dev = d->mixer_dev; dev_ref(*dev); } } } static void mixer_sysinit(void *p) { + if (mixer_ehtag != NULL) + return; mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000); } static void mixer_sysuninit(void *p) { - if (mixer_ehtag != NULL) - EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag); + if (mixer_ehtag == NULL) + return; + EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag); + mixer_ehtag = NULL; } SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL); SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL); #endif /** * @brief Handler for SNDCTL_MIXERINFO * * This function searches for a mixer based on the numeric ID stored * in oss_miserinfo::dev. If set to -1, then information about the * current mixer handling the request is provided. Note, however, that * this ioctl may be made with any sound device (audio, mixer, midi). * * @note Caller must not hold any PCM device, channel, or mixer locks. * * See http://manuals.opensound.com/developer/SNDCTL_MIXERINFO.html for * more information. * * @param i_dev character device on which the ioctl arrived * @param arg user argument (oss_mixerinfo *) * * @retval EINVAL oss_mixerinfo::dev specified a bad value * @retval 0 success */ int mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi) { struct snddev_info *d; struct snd_mixer *m; struct cdev *t_cdev; int nmix, ret, pcmunit, i; /* * If probing the device handling the ioctl, make sure it's a mixer * device. (This ioctl is valid on audio, mixer, and midi devices.) */ if ((mi->dev == -1) && (i_dev->si_devsw != &mixer_cdevsw)) return EINVAL; d = NULL; m = NULL; t_cdev = NULL; nmix = 0; ret = 0; pcmunit = -1; /* pcmX */ /* * There's a 1:1 relationship between mixers and PCM devices, so * begin by iterating over PCM devices and search for our mixer. */ for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { d = devclass_get_softc(pcm_devclass, i); if (d == NULL) continue; /* See the note in function docblock. */ mtx_assert(d->lock, MA_NOTOWNED); pcm_inprog(d, 1); pcm_lock(d); if (d->mixer_dev != NULL) { if (((mi->dev == -1) && (d->mixer_dev == i_dev)) || (mi->dev == nmix)) { t_cdev = d->mixer_dev; pcmunit = i; break; } ++nmix; } pcm_unlock(d); pcm_inprog(d, -1); } /* * If t_cdev is NULL, then search was exhausted and device wasn't * found. No locks are held, so just return. */ if (t_cdev == NULL) return EINVAL; m = t_cdev->si_drv1; mtx_lock(m->lock); /* * At this point, the following synchronization stuff has happened: * - a specific PCM device is locked and its "in progress * operations" counter has been incremented, so be sure to unlock * and decrement when exiting; * - a specific mixer device has been locked, so be sure to unlock * when existing. */ bzero((void *)mi, sizeof(*mi)); mi->dev = nmix; snprintf(mi->id, sizeof(mi->id), "mixer%d", dev2unit(t_cdev)); strlcpy(mi->name, m->name, sizeof(mi->name)); mi->modify_counter = m->modify_counter; mi->card_number = pcmunit; /* * Currently, FreeBSD assumes 1:1 relationship between a pcm and * mixer devices, so this is hardcoded to 0. */ mi->port_number = 0; /** * @todo Fill in @sa oss_mixerinfo::mixerhandle. * @note From 4Front: "mixerhandle is an arbitrary string that * identifies the mixer better than the device number * (mixerinfo.dev). Device numbers may change depending on * the order the drivers are loaded. However the handle * should remain the same provided that the sound card is * not moved to another PCI slot." */ /** * @note * @sa oss_mixerinfo::magic is a reserved field. * * @par * From 4Front: "magic is usually 0. However some devices may have * dedicated setup utilities and the magic field may contain an * unique driver specific value (managed by [4Front])." */ mi->enabled = device_is_attached(m->dev) ? 1 : 0; /** * The only flag for @sa oss_mixerinfo::caps is currently * MIXER_CAP_VIRTUAL, which I'm not sure we really worry about. */ /** * Mixer extensions currently aren't supported, so leave * @sa oss_mixerinfo::nrext blank for now. */ /** * @todo Fill in @sa oss_mixerinfo::priority (requires touching * drivers?) * @note The priority field is for mixer applets to determine which * mixer should be the default, with 0 being least preferred and 10 * being most preferred. From 4Front: "OSS drivers like ICH use * higher values (10) because such chips are known to be used only * on motherboards. Drivers for high end pro devices use 0 because * they will never be the default mixer. Other devices use values 1 * to 9 depending on the estimated probability of being the default * device. * * XXX Described by Hannu@4Front, but not found in soundcard.h. strlcpy(mi->devnode, t_cdev->si_name, sizeof(mi->devnode)); mi->legacy_device = pcmunit; */ mtx_unlock(m->lock); pcm_unlock(d); pcm_inprog(d, -1); return ret; } Index: head/sys/dev/sound/pcm/sndstat.c =================================================================== --- head/sys/dev/sound/pcm/sndstat.c (revision 170160) +++ head/sys/dev/sound/pcm/sndstat.c (revision 170161) @@ -1,396 +1,442 @@ /*- * Copyright (c) 2001 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #ifdef USING_MUTEX #include #endif SND_DECLARE_FILE("$FreeBSD$"); #define SS_TYPE_MODULE 0 #define SS_TYPE_FIRST 1 #define SS_TYPE_PCM 1 #define SS_TYPE_MIDI 2 #define SS_TYPE_SEQUENCER 3 #define SS_TYPE_LAST 3 static d_open_t sndstat_open; static d_close_t sndstat_close; static d_read_t sndstat_read; static struct cdevsw sndstat_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = sndstat_open, .d_close = sndstat_close, .d_read = sndstat_read, .d_name = "sndstat", }; struct sndstat_entry { SLIST_ENTRY(sndstat_entry) link; device_t dev; char *str; sndstat_handler handler; int type, unit; }; #ifdef USING_MUTEX static struct mtx sndstat_lock; #endif static struct sbuf sndstat_sbuf; -static struct cdev *sndstat_dev = 0; -static int sndstat_isopen = 0; -static int sndstat_bufptr; +static struct cdev *sndstat_dev = NULL; +static int sndstat_bufptr = -1; static int sndstat_maxunit = -1; static int sndstat_files = 0; +#define SNDSTAT_PID(x) ((pid_t)((intptr_t)((x)->si_drv1))) +#define SNDSTAT_PID_SET(x, y) (x)->si_drv1 = (void *)((intptr_t)(y)) +#define SNDSTAT_FLUSH() do { \ + if (sndstat_bufptr != -1) { \ + sbuf_delete(&sndstat_sbuf); \ + sndstat_bufptr = -1; \ + } \ +} while(0) + static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none); -#ifdef SND_DEBUG -SYSCTL_INT(_hw_snd, OID_AUTO, sndstat_isopen, CTLFLAG_RW, - &sndstat_isopen, 1, "sndstat emergency exit"); -#endif - int snd_verbose = 1; #ifdef USING_MUTEX TUNABLE_INT("hw.snd.verbose", &snd_verbose); #else TUNABLE_INT_DECL("hw.snd.verbose", 1, snd_verbose); #endif +#ifdef SND_DEBUG +static int +sysctl_hw_snd_sndstat_pid(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + if (sndstat_dev == NULL) + return (EINVAL); + + mtx_lock(&sndstat_lock); + val = (int)SNDSTAT_PID(sndstat_dev); + mtx_unlock(&sndstat_lock); + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + if (err == 0 && req->newptr != NULL && val == 0) { + mtx_lock(&sndstat_lock); + SNDSTAT_FLUSH(); + SNDSTAT_PID_SET(sndstat_dev, 0); + mtx_unlock(&sndstat_lock); + } + return (err); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, sndstat_pid, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_sndstat_pid, "I", "sndstat busy pid"); +#endif + static int sndstat_prepare(struct sbuf *s); static int sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS) { int error, verbose; verbose = snd_verbose; error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req); if (error == 0 && req->newptr != NULL) { mtx_lock(&sndstat_lock); if (verbose < 0 || verbose > 4) error = EINVAL; else snd_verbose = verbose; mtx_unlock(&sndstat_lock); } return error; } SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(int), sysctl_hw_sndverbose, "I", "verbosity level"); static int sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td) { int error; + if (sndstat_dev == NULL || i_dev != sndstat_dev) + return EBADF; + mtx_lock(&sndstat_lock); - if (sndstat_isopen) { + if (SNDSTAT_PID(i_dev) != 0) { mtx_unlock(&sndstat_lock); return EBUSY; } - sndstat_isopen = 1; + SNDSTAT_PID_SET(i_dev, td->td_proc->p_pid); mtx_unlock(&sndstat_lock); if (sbuf_new(&sndstat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) { error = ENXIO; goto out; } sndstat_bufptr = 0; error = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM; out: if (error) { mtx_lock(&sndstat_lock); - sndstat_isopen = 0; + SNDSTAT_FLUSH(); + SNDSTAT_PID_SET(i_dev, 0); mtx_unlock(&sndstat_lock); } return (error); } static int sndstat_close(struct cdev *i_dev, int flags, int mode, struct thread *td) { + if (sndstat_dev == NULL || i_dev != sndstat_dev) + return EBADF; + mtx_lock(&sndstat_lock); - if (!sndstat_isopen) { + if (SNDSTAT_PID(i_dev) == 0) { mtx_unlock(&sndstat_lock); return EBADF; } - mtx_unlock(&sndstat_lock); - sbuf_delete(&sndstat_sbuf); + SNDSTAT_FLUSH(); + SNDSTAT_PID_SET(i_dev, 0); - mtx_lock(&sndstat_lock); - sndstat_isopen = 0; mtx_unlock(&sndstat_lock); return 0; } static int sndstat_read(struct cdev *i_dev, struct uio *buf, int flag) { int l, err; + if (sndstat_dev == NULL || i_dev != sndstat_dev) + return EBADF; + mtx_lock(&sndstat_lock); - if (!sndstat_isopen) { + if (SNDSTAT_PID(i_dev) != buf->uio_td->td_proc->p_pid || + sndstat_bufptr == -1) { mtx_unlock(&sndstat_lock); return EBADF; } mtx_unlock(&sndstat_lock); l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr); err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0; sndstat_bufptr += l; return err; } /************************************************************************/ static struct sndstat_entry * sndstat_find(int type, int unit) { struct sndstat_entry *ent; SLIST_FOREACH(ent, &sndstat_devlist, link) { if (ent->type == type && ent->unit == unit) return ent; } return NULL; } int -sndstat_acquire(void) +sndstat_acquire(struct thread *td) { + if (sndstat_dev == NULL) + return EBADF; + mtx_lock(&sndstat_lock); - if (sndstat_isopen) { + if (SNDSTAT_PID(sndstat_dev) != 0) { mtx_unlock(&sndstat_lock); return EBUSY; } - sndstat_isopen = 1; + SNDSTAT_PID_SET(sndstat_dev, td->td_proc->p_pid); mtx_unlock(&sndstat_lock); return 0; } int -sndstat_release(void) +sndstat_release(struct thread *td) { + if (sndstat_dev == NULL) + return EBADF; + mtx_lock(&sndstat_lock); - if (!sndstat_isopen) { + if (SNDSTAT_PID(sndstat_dev) != td->td_proc->p_pid) { mtx_unlock(&sndstat_lock); return EBADF; } - sndstat_isopen = 0; + SNDSTAT_PID_SET(sndstat_dev, 0); mtx_unlock(&sndstat_lock); return 0; } int sndstat_register(device_t dev, char *str, sndstat_handler handler) { struct sndstat_entry *ent; const char *devtype; int type, unit; if (dev) { unit = device_get_unit(dev); devtype = device_get_name(dev); if (!strcmp(devtype, "pcm")) type = SS_TYPE_PCM; else if (!strcmp(devtype, "midi")) type = SS_TYPE_MIDI; else if (!strcmp(devtype, "sequencer")) type = SS_TYPE_SEQUENCER; else return EINVAL; } else { type = SS_TYPE_MODULE; unit = -1; } ent = malloc(sizeof *ent, M_DEVBUF, M_WAITOK | M_ZERO); ent->dev = dev; ent->str = str; ent->type = type; ent->unit = unit; ent->handler = handler; mtx_lock(&sndstat_lock); SLIST_INSERT_HEAD(&sndstat_devlist, ent, link); if (type == SS_TYPE_MODULE) sndstat_files++; sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit; mtx_unlock(&sndstat_lock); return 0; } int sndstat_registerfile(char *str) { return sndstat_register(NULL, str, NULL); } int sndstat_unregister(device_t dev) { struct sndstat_entry *ent; mtx_lock(&sndstat_lock); SLIST_FOREACH(ent, &sndstat_devlist, link) { if (ent->dev == dev) { SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); mtx_unlock(&sndstat_lock); free(ent, M_DEVBUF); return 0; } } mtx_unlock(&sndstat_lock); return ENXIO; } int sndstat_unregisterfile(char *str) { struct sndstat_entry *ent; mtx_lock(&sndstat_lock); SLIST_FOREACH(ent, &sndstat_devlist, link) { if (ent->dev == NULL && ent->str == str) { SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); sndstat_files--; mtx_unlock(&sndstat_lock); free(ent, M_DEVBUF); return 0; } } mtx_unlock(&sndstat_lock); return ENXIO; } /************************************************************************/ static int sndstat_prepare(struct sbuf *s) { struct sndstat_entry *ent; int i, j; sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit)\n", (unsigned int)sizeof(intpcm_t) << 3); if (SLIST_EMPTY(&sndstat_devlist)) { sbuf_printf(s, "No devices installed.\n"); sbuf_finish(s); return sbuf_len(s); } sbuf_printf(s, "Installed devices:\n"); for (i = 0; i <= sndstat_maxunit; i++) { for (j = SS_TYPE_FIRST; j <= SS_TYPE_LAST; j++) { ent = sndstat_find(j, i); if (!ent) continue; sbuf_printf(s, "%s:", device_get_nameunit(ent->dev)); sbuf_printf(s, " <%s>", device_get_desc(ent->dev)); sbuf_printf(s, " %s", ent->str); if (ent->handler) ent->handler(s, ent->dev, snd_verbose); else sbuf_printf(s, " [no handler]"); sbuf_printf(s, "\n"); } } if (snd_verbose >= 3 && sndstat_files > 0) { sbuf_printf(s, "\nFile Versions:\n"); SLIST_FOREACH(ent, &sndstat_devlist, link) { if (ent->dev == NULL && ent->str != NULL) sbuf_printf(s, "%s\n", ent->str); } } sbuf_finish(s); return sbuf_len(s); } static int sndstat_init(void) { + if (sndstat_dev != NULL) + return EINVAL; mtx_init(&sndstat_lock, "sndstat", "sndstat lock", MTX_DEF); - sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, UID_ROOT, GID_WHEEL, 0444, "sndstat"); - - return (sndstat_dev != 0)? 0 : ENXIO; + sndstat_dev = make_dev(&sndstat_cdevsw, SND_DEV_STATUS, + UID_ROOT, GID_WHEEL, 0444, "sndstat"); + return 0; } static int sndstat_uninit(void) { + if (sndstat_dev == NULL) + return EINVAL; + mtx_lock(&sndstat_lock); - if (sndstat_isopen) { + if (SNDSTAT_PID(sndstat_dev) != curthread->td_proc->p_pid) { mtx_unlock(&sndstat_lock); return EBUSY; } - sndstat_isopen = 1; + SNDSTAT_FLUSH(); + mtx_unlock(&sndstat_lock); - if (sndstat_dev) - destroy_dev(sndstat_dev); - sndstat_dev = 0; + destroy_dev(sndstat_dev); + sndstat_dev = NULL; mtx_destroy(&sndstat_lock); return 0; } static void sndstat_sysinit(void *p) { sndstat_init(); } static void sndstat_sysuninit(void *p) { int error; error = sndstat_uninit(); KASSERT(error == 0, ("%s: error = %d", __func__, error)); } SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL); SYSUNINIT(sndstat_sysuninit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysuninit, NULL); - - Index: head/sys/dev/sound/pcm/sound.c =================================================================== --- head/sys/dev/sound/pcm/sound.c (revision 170160) +++ head/sys/dev/sound/pcm/sound.c (revision 170161) @@ -1,1259 +1,1433 @@ /*- * Copyright (c) 1999 Cameron Grant * (C) 1997 Luigi Rizzo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include +#include #include #include #include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD$"); devclass_t pcm_devclass; int pcm_veto_load = 1; #ifdef USING_DEVFS int snd_unit = 0; TUNABLE_INT("hw.snd.default_unit", &snd_unit); #endif -int snd_maxautovchans = 4; +int snd_maxautovchans = 16; /* XXX: a tunable implies that we may need more than one sound channel before the system can change a sysctl (/etc/sysctl.conf), do we really need this? */ TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); +/* + * XXX I've had enough with people not telling proper version/arch + * while reporting problems, not after 387397913213th questions/requests. + */ +static const char snd_driver_version[] = + __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; +SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, + 0, "Driver version/arch"); + /** * @brief Unit number allocator for syncgroup IDs */ struct unrhdr *pcmsg_unrhdr = NULL; static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); void * snd_mtxcreate(const char *desc, const char *type) { #ifdef USING_MUTEX struct mtx *m; m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); mtx_init(m, desc, type, MTX_DEF); return m; #else return (void *)0xcafebabe; #endif } void snd_mtxfree(void *m) { #ifdef USING_MUTEX struct mtx *mtx = m; /* mtx_assert(mtx, MA_OWNED); */ mtx_destroy(mtx); free(mtx, M_DEVBUF); #endif } void snd_mtxassert(void *m) { #ifdef USING_MUTEX #ifdef INVARIANTS struct mtx *mtx = m; mtx_assert(mtx, MA_OWNED); #endif #endif } /* void snd_mtxlock(void *m) { #ifdef USING_MUTEX struct mtx *mtx = m; mtx_lock(mtx); #endif } void snd_mtxunlock(void *m) { #ifdef USING_MUTEX struct mtx *mtx = m; mtx_unlock(mtx); #endif } */ int snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) { #ifdef USING_MUTEX flags &= INTR_MPSAFE; flags |= INTR_TYPE_AV; #else flags = INTR_TYPE_AV; #endif return bus_setup_intr(dev, res, flags, #if __FreeBSD_version >= 700031 NULL, #endif hand, param, cookiep); } #ifndef PCM_DEBUG_MTX void pcm_lock(struct snddev_info *d) { snd_mtxlock(d->lock); } void pcm_unlock(struct snddev_info *d) { snd_mtxunlock(d->lock); } #endif struct pcm_channel * pcm_getfakechan(struct snddev_info *d) { return d->fakechan; } +static void +pcm_clonereset(struct snddev_info *d) +{ + int cmax; + + snd_mtxassert(d->lock); + + cmax = d->playcount + d->reccount - 1; + if (d->pvchancount > 0) + cmax += MAX(d->pvchancount, snd_maxautovchans) - 1; + if (d->rvchancount > 0) + cmax += MAX(d->rvchancount, snd_maxautovchans) - 1; + if (cmax > PCMMAXCLONE) + cmax = PCMMAXCLONE; + (void)snd_clone_gc(d->clones); + (void)snd_clone_setmaxunit(d->clones, cmax); +} + static int -pcm_setvchans(struct snddev_info *d, int newcnt) +pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) { - struct snddev_channel *sce = NULL; - struct pcm_channel *c = NULL; - int err = 0, vcnt, dcnt, i; + struct pcm_channel *c, *ch, *nch; + int err, vcnt; + err = 0; + pcm_inprog(d, 1); - if (d->playcount < 1) { + if ((direction == PCMDIR_PLAY && d->playcount < 1) || + (direction == PCMDIR_REC && d->reccount < 1)) { err = ENODEV; - goto setvchans_out; + goto pcm_setvchans_out; } if (!(d->flags & SD_F_AUTOVCHAN)) { err = EINVAL; - goto setvchans_out; + goto pcm_setvchans_out; } - vcnt = d->vchancount; - dcnt = d->playcount + d->reccount; - - if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) { + if (newcnt < 0 || newcnt > SND_MAXVCHANS) { err = E2BIG; - goto setvchans_out; + goto pcm_setvchans_out; } - dcnt += vcnt; + if (direction == PCMDIR_PLAY) + vcnt = d->pvchancount; + else if (direction == PCMDIR_REC) + vcnt = d->rvchancount; + else { + err = EINVAL; + goto pcm_setvchans_out; + } if (newcnt > vcnt) { + KASSERT(num == -1 || + (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), + ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", + num, newcnt, vcnt)); /* add new vchans - find a parent channel first */ - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; + CHN_FOREACH(c, d, channels.pcm) { CHN_LOCK(c); - if (c->direction == PCMDIR_PLAY && - ((c->flags & CHN_F_HAS_VCHAN) || - (vcnt == 0 && - !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) - goto addok; + if (c->direction == direction && + ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && + !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) + goto pcm_setvchans_addok; CHN_UNLOCK(c); } err = EBUSY; - goto setvchans_out; -addok: + goto pcm_setvchans_out; +pcm_setvchans_addok: c->flags |= CHN_F_BUSY; while (err == 0 && newcnt > vcnt) { - if (dcnt > PCMMAXCHAN) { - device_printf(d->dev, "%s: Maximum channel reached.\n", __func__); - break; - } - err = vchan_create(c); - if (err == 0) { + err = vchan_create(c, num); + if (err == 0) vcnt++; - dcnt++; - } else if (err == E2BIG && newcnt > vcnt) - device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err); + else if (err == E2BIG && newcnt > vcnt) + device_printf(d->dev, + "%s: err=%d Maximum channel reached.\n", + __func__, err); } if (vcnt == 0) c->flags &= ~CHN_F_BUSY; CHN_UNLOCK(c); + pcm_lock(d); + pcm_clonereset(d); + pcm_unlock(d); } else if (newcnt < vcnt) { -#define ORPHAN_CDEVT(cdevt) \ - ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \ - (cdevt)->si_drv2 == NULL)) - while (err == 0 && newcnt < vcnt) { - i = 0; - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - CHN_LOCK(c); - if (c->direction == PCMDIR_PLAY && - (c->flags & CHN_F_VIRTUAL) && - (i++ == newcnt)) { - if (!(c->flags & CHN_F_BUSY) && - ORPHAN_CDEVT(sce->dsp_devt) && - ORPHAN_CDEVT(sce->dspW_devt) && - ORPHAN_CDEVT(sce->audio_devt) && - ORPHAN_CDEVT(sce->dspHW_devt)) - goto remok; - /* - * Either we're busy, or our cdev - * has been stolen by dsp_clone(). - * Skip, and increase newcnt. - */ - if (!(c->flags & CHN_F_BUSY)) - device_printf(d->dev, - "%s: <%s> somebody steal my cdev!\n", - __func__, c->name); - newcnt++; - } + KASSERT(num == -1, + ("bogus vchan_destroy() request num=%d", num)); + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + if (c->direction != direction || + CHN_EMPTY(c, children) || + !(c->flags & CHN_F_HAS_VCHAN)) { CHN_UNLOCK(c); + continue; } - if (vcnt != newcnt) - err = EBUSY; - break; -remok: + CHN_FOREACH_SAFE(ch, c, nch, children) { + CHN_LOCK(ch); + if (!(ch->flags & CHN_F_BUSY)) { + CHN_UNLOCK(ch); + CHN_UNLOCK(c); + err = vchan_destroy(ch); + CHN_LOCK(c); + if (err == 0) + vcnt--; + } else + CHN_UNLOCK(ch); + if (vcnt == newcnt) { + err = 0; + break; + } + } CHN_UNLOCK(c); - err = vchan_destroy(c); - if (err == 0) - vcnt--; - else - device_printf(d->dev, - "%s: WARNING: vchan_destroy() failed!", - __func__); + break; } + pcm_lock(d); + pcm_clonereset(d); + pcm_unlock(d); } -setvchans_out: +pcm_setvchans_out: pcm_inprog(d, -1); return err; } /* return error status and a locked channel */ int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, - pid_t pid, int chnum) + pid_t pid, int devunit) { struct pcm_channel *c; - struct snddev_channel *sce; - int err; + int err, vchancount; + KASSERT(d != NULL && ch != NULL && (devunit == -1 || + !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && + (direction == PCMDIR_PLAY || direction == PCMDIR_REC), + ("%s() invalid d=%p ch=%p direction=%d pid=%d devunit=%d", + __func__, d, ch, direction, pid, devunit)); + + /* Double check again. */ + if (devunit != -1) { + switch (snd_unit2d(devunit)) { + case SND_DEV_DSPHW_PLAY: + case SND_DEV_DSPHW_VPLAY: + if (direction != PCMDIR_PLAY) + return (EOPNOTSUPP); + break; + case SND_DEV_DSPHW_REC: + case SND_DEV_DSPHW_VREC: + if (direction != PCMDIR_REC) + return (EOPNOTSUPP); + break; + default: + if (!(direction == PCMDIR_PLAY || + direction == PCMDIR_REC)) + return (EOPNOTSUPP); + break; + } + } + retry_chnalloc: err = ENODEV; /* scan for a free channel */ - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; + CHN_FOREACH(c, d, channels.pcm) { CHN_LOCK(c); - if (c->direction == direction && !(c->flags & CHN_F_BUSY)) { - if (chnum < 0 || sce->chan_num == chnum) { - c->flags |= CHN_F_BUSY; - c->pid = pid; - *ch = c; - return 0; - } - } - if (sce->chan_num == chnum) { + if (c->direction == direction && !(c->flags & CHN_F_BUSY) && + (devunit == -1 || devunit == -2 || c->unit == devunit)) { + c->flags |= CHN_F_BUSY; + c->pid = pid; + *ch = c; + return (0); + } else if (c->unit == devunit) { if (c->direction != direction) err = EOPNOTSUPP; else if (c->flags & CHN_F_BUSY) err = EBUSY; else err = EINVAL; CHN_UNLOCK(c); - return err; - } else if (c->direction == direction && (c->flags & CHN_F_BUSY)) + return (err); + } else if ((devunit == -1 || devunit == -2) && + c->direction == direction && (c->flags & CHN_F_BUSY)) err = EBUSY; CHN_UNLOCK(c); } /* no channel available */ - if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 && - d->vchancount < snd_maxautovchans && - d->devcount <= PCMMAXCHAN) { - err = pcm_setvchans(d, d->vchancount + 1); + if (devunit == -1 || (devunit != -2 && + (snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || + snd_unit2d(devunit) == SND_DEV_DSPHW_VREC))) { + if (direction == PCMDIR_PLAY) + vchancount = d->pvchancount; + else + vchancount = d->rvchancount; + if (!(vchancount > 0 && vchancount < snd_maxautovchans) && + (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) + return (err); + err = pcm_setvchans(d, direction, vchancount + 1, + (devunit == -1) ? -1 : snd_unit2c(devunit)); if (err == 0) { - chnum = -2; + if (devunit == -1) + devunit = -2; goto retry_chnalloc; } } - return err; + return (err); } /* release a locked channel and unlock it */ int pcm_chnrelease(struct pcm_channel *c) { CHN_LOCKASSERT(c); c->flags &= ~CHN_F_BUSY; c->pid = -1; CHN_UNLOCK(c); return 0; } int pcm_chnref(struct pcm_channel *c, int ref) { int r; CHN_LOCKASSERT(c); c->refcount += ref; r = c->refcount; return r; } int pcm_inprog(struct snddev_info *d, int delta) { int r; if (delta == 0) return d->inprog; /* backtrace(); */ pcm_lock(d); d->inprog += delta; r = d->inprog; pcm_unlock(d); return r; } static void pcm_setmaxautovchans(struct snddev_info *d, int num) { - if (num > 0 && d->vchancount == 0) - pcm_setvchans(d, 1); - else if (num == 0 && d->vchancount > 0) - pcm_setvchans(d, 0); + if (num < 0) + return; + + if (num >= 0 && d->pvchancount > num) + (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); + else if (num > 0 && d->pvchancount == 0) + (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); + + if (num >= 0 && d->rvchancount > num) + (void)pcm_setvchans(d, PCMDIR_REC, num, -1); + else if (num > 0 && d->rvchancount == 0) + (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); + + pcm_lock(d); + pcm_clonereset(d); + pcm_unlock(d); } #ifdef USING_DEVFS static int sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; int error, unit; unit = snd_unit; error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); if (error == 0 && req->newptr != NULL) { - if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) - return EINVAL; d = devclass_get_softc(pcm_devclass, unit); - if (d == NULL || SLIST_EMPTY(&d->channels)) + if (d == NULL || CHN_EMPTY(d, channels.pcm)) return EINVAL; snd_unit = unit; } return (error); } /* XXX: do we need a way to let the user change the default unit? */ SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device"); #endif static int sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; int i, v, error; v = snd_maxautovchans; error = sysctl_handle_int(oidp, &v, sizeof(v), req); if (error == 0 && req->newptr != NULL) { - if (v < 0 || v > PCMMAXCHAN) - return E2BIG; - if (pcm_devclass != NULL && v != snd_maxautovchans) { - for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { - d = devclass_get_softc(pcm_devclass, i); - if (!d) - continue; - pcm_setmaxautovchans(d, v); - } - } + if (v < 0) + v = 0; + if (v > SND_MAXVCHANS) + v = SND_MAXVCHANS; snd_maxautovchans = v; + for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { + d = devclass_get_softc(pcm_devclass, i); + if (d == NULL) + continue; + pcm_setmaxautovchans(d, v); + } } return (error); } SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); struct pcm_channel * -pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) +pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) { - struct snddev_channel *sce; - struct pcm_channel *ch, *c; - char *dirs; - uint32_t flsearch = 0; - int direction, err, rpnum, *pnum; + struct pcm_channel *ch; + int direction, err, rpnum, *pnum, max; + int udc, device, chan; + char *dirs, *devname, buf[CHN_NAMELEN]; + KASSERT(num >= -1, ("invalid num=%d", num)); + + pcm_lock(d); + switch(dir) { case PCMDIR_PLAY: dirs = "play"; direction = PCMDIR_PLAY; pnum = &d->playcount; + device = SND_DEV_DSPHW_PLAY; + max = SND_MAXHWCHAN; break; - + case PCMDIR_PLAY_VIRTUAL: + dirs = "virtual"; + direction = PCMDIR_PLAY; + pnum = &d->pvchancount; + device = SND_DEV_DSPHW_VPLAY; + max = SND_MAXVCHANS; + break; case PCMDIR_REC: dirs = "record"; direction = PCMDIR_REC; pnum = &d->reccount; + device = SND_DEV_DSPHW_REC; + max = SND_MAXHWCHAN; break; - - case PCMDIR_VIRTUAL: + case PCMDIR_REC_VIRTUAL: dirs = "virtual"; - direction = PCMDIR_PLAY; - pnum = &d->vchancount; - flsearch = CHN_F_VIRTUAL; + direction = PCMDIR_REC; + pnum = &d->rvchancount; + device = SND_DEV_DSPHW_VREC; + max = SND_MAXVCHANS; break; - default: + pcm_unlock(d); return NULL; } - ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); - ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); + chan = (num == -1) ? 0 : num; - snd_mtxlock(d->lock); - ch->num = 0; - rpnum = 0; - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - if (direction != c->direction || - (c->flags & CHN_F_VIRTUAL) != flsearch) - continue; - if (ch->num == c->num) - ch->num++; - else { -#if 0 - device_printf(d->dev, - "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n", - __func__, dirs, ch->num, c->num); -#endif - goto retry_num_search; - } - rpnum++; + if (*pnum >= max || chan >= max) { + pcm_unlock(d); + return NULL; } - goto retry_num_search_out; -retry_num_search: + rpnum = 0; - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - if (direction != c->direction || - (c->flags & CHN_F_VIRTUAL) != flsearch) + + CHN_FOREACH(ch, d, channels.pcm) { + if (CHN_DEV(ch) != device) continue; - if (ch->num == c->num) { - ch->num++; - goto retry_num_search; + if (chan == CHN_CHAN(ch)) { + if (num != -1) { + device_printf(d->dev, + "channel num=%d allocated!\n", chan); + pcm_unlock(d); + return NULL; + } + chan++; + if (chan >= max) { + device_printf(d->dev, + "chan=%d > %d\n", chan, max); + pcm_unlock(d); + return NULL; + } } rpnum++; } -retry_num_search_out: + if (*pnum != rpnum) { device_printf(d->dev, - "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n", - __func__, dirs, *pnum, rpnum); - *pnum = rpnum; + "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", + __func__, dirs, *pnum, rpnum); + pcm_unlock(d); + return NULL; } + + udc = snd_mkunit(device_get_unit(d->dev), device, chan); + devname = dsp_unit2name(buf, sizeof(buf), udc); + + if (devname == NULL) { + device_printf(d->dev, + "Failed to query device name udc=0x%08x\n", udc); + pcm_unlock(d); + return NULL; + } + (*pnum)++; - snd_mtxunlock(d->lock); + pcm_unlock(d); + ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); + ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); + ch->unit = udc; ch->pid = -1; ch->parentsnddev = d; ch->parentchannel = parent; ch->dev = d->dev; - snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); + ch->trigger = PCMTRIG_STOP; + snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", + device_get_nameunit(ch->dev), dirs, devname); err = chn_init(ch, devinfo, dir, direction); if (err) { - device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); + device_printf(d->dev, "chn_init(%s) failed: err = %d\n", + ch->name, err); kobj_delete(ch->methods, M_DEVBUF); free(ch, M_DEVBUF); - snd_mtxlock(d->lock); + pcm_lock(d); (*pnum)--; - snd_mtxunlock(d->lock); + pcm_unlock(d); return NULL; } return ch; } int pcm_chn_destroy(struct pcm_channel *ch) { struct snddev_info *d; int err; d = ch->parentsnddev; err = chn_kill(ch); if (err) { device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); return err; } kobj_delete(ch->methods, M_DEVBUF); free(ch, M_DEVBUF); return 0; } int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) { - struct snddev_channel *sce, *tmp, *after; - unsigned rdevcount; - int device = device_get_unit(d->dev); - size_t namelen; - char dtype; + struct pcm_channel *tmp, *after; + int num; - /* - * Note it's confusing nomenclature. - * dev_t - * device -> pcm_device - * unit -> pcm_channel - * channel -> snddev_channel - * device_t - * unit -> pcm_device - */ + pcm_lock(d); - sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); + KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || + ch->direction == PCMDIR_REC), ("Invalid pcm channel")); - snd_mtxlock(d->lock); - sce->channel = ch; - sce->chan_num = 0; - rdevcount = 0; after = NULL; - SLIST_FOREACH(tmp, &d->channels, link) { - if (sce->chan_num == tmp->chan_num) - sce->chan_num++; - else { -#if 0 - device_printf(d->dev, - "%s: cdev numbering screwed (Expect: %d, Got: %d)\n", - __func__, sce->chan_num, tmp->chan_num); -#endif - goto retry_chan_num_search; - } - after = tmp; - rdevcount++; - } - goto retry_chan_num_search_out; -retry_chan_num_search: + tmp = NULL; + num = 0; + /* - * Look for possible channel numbering collision. This may not - * be optimized, but it will ensure that no collision occured. - * Can be considered cheap since none of the locking/unlocking - * operations involved. + * Look for possible device collision. */ - rdevcount = 0; - after = NULL; - SLIST_FOREACH(tmp, &d->channels, link) { - if (sce->chan_num == tmp->chan_num) { - sce->chan_num++; - goto retry_chan_num_search; + CHN_FOREACH(tmp, d, channels.pcm) { + if (tmp->unit == ch->unit) { + device_printf(d->dev, "%s(): Device collision " + "old=%p new=%p devunit=0x%08x\n", + __func__, tmp, ch, ch->unit); + pcm_unlock(d); + return ENODEV; } - if (sce->chan_num > tmp->chan_num) + if (CHN_DEV(tmp) < CHN_DEV(ch)) { + if (num == 0) + after = tmp; + continue; + } else if (CHN_DEV(tmp) > CHN_DEV(ch)) + break; + num++; + if (CHN_CHAN(tmp) < CHN_CHAN(ch)) after = tmp; - rdevcount++; + else if (CHN_CHAN(tmp) > CHN_CHAN(ch)) + break; } -retry_chan_num_search_out: - /* - * Don't overflow PCMMKMINOR / PCMMAXCHAN. - */ - if (sce->chan_num > PCMMAXCHAN) { - snd_mtxunlock(d->lock); - device_printf(d->dev, - "%s: WARNING: sce->chan_num overflow! (%d)\n", - __func__, sce->chan_num); - free(sce, M_DEVBUF); - return E2BIG; - } - if (d->devcount != rdevcount) { - device_printf(d->dev, - "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n", - __func__, d->devcount, rdevcount); - d->devcount = rdevcount; - } - d->devcount++; - if (after == NULL) { - SLIST_INSERT_HEAD(&d->channels, sce, link); + + if (after != NULL) { + CHN_INSERT_AFTER(after, ch, channels.pcm); } else { - SLIST_INSERT_AFTER(after, sce, link); + CHN_INSERT_HEAD(d, ch, channels.pcm); } -#if 0 - if (1) { - int cnum = 0; - SLIST_FOREACH(tmp, &d->channels, link) { - if (cnum != tmp->chan_num) - device_printf(d->dev, - "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n", - __func__, cnum, tmp->chan_num); - cnum++; - } - } -#endif - if (ch->flags & CHN_F_VIRTUAL) - dtype = 'v'; - else if (ch->direction == PCMDIR_PLAY) - dtype = 'p'; - else if (ch->direction == PCMDIR_REC) - dtype = 'r'; - else - dtype = 'u'; /* we're screwed */ + d->devcount++; + pcm_unlock(d); - namelen = strlen(ch->name); - if ((CHN_NAMELEN - namelen) > 11) { /* ":dspXX.TYYY" */ - snprintf(ch->name + namelen, - CHN_NAMELEN - namelen, ":dsp%d.%c%d", - device, dtype, ch->num); - } - snd_mtxunlock(d->lock); - - /* - * I will revisit these someday, and nuke it mercilessly.. - */ - sce->dsp_devt = make_dev(&dsp_cdevsw, - PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), - UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", - device, sce->chan_num); - - sce->dspW_devt = make_dev(&dsp_cdevsw, - PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), - UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", - device, sce->chan_num); - - sce->audio_devt = make_dev(&dsp_cdevsw, - PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), - UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", - device, sce->chan_num); - - /* Except this. */ - sce->dspHW_devt = make_dev(&dsp_cdevsw, - PCMMKMINOR(device, SND_DEV_DSPHW, sce->chan_num), - UID_ROOT, GID_WHEEL, 0666, "dsp%d.%c%d", - device, dtype, ch->num); - return 0; } int pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) { - struct snddev_channel *sce; -#if 0 - int ourlock; + struct pcm_channel *tmp; - ourlock = 0; - if (!mtx_owned(d->lock)) { - snd_mtxlock(d->lock); - ourlock = 1; - } -#endif + tmp = NULL; - SLIST_FOREACH(sce, &d->channels, link) { - if (sce->channel == ch) - goto gotit; + CHN_FOREACH(tmp, d, channels.pcm) { + if (tmp == ch) + break; } -#if 0 - if (ourlock) - snd_mtxunlock(d->lock); -#endif - return EINVAL; -gotit: - SLIST_REMOVE(&d->channels, sce, snddev_channel, link); - if (ch->flags & CHN_F_VIRTUAL) - d->vchancount--; - else if (ch->direction == PCMDIR_REC) - d->reccount--; - else + if (tmp != ch) + return EINVAL; + + CHN_REMOVE(d, ch, channels.pcm); + switch (CHN_DEV(ch)) { + case SND_DEV_DSPHW_PLAY: d->playcount--; + break; + case SND_DEV_DSPHW_VPLAY: + d->pvchancount--; + break; + case SND_DEV_DSPHW_REC: + d->reccount--; + break; + case SND_DEV_DSPHW_VREC: + d->rvchancount--; + break; + default: + break; + } -#if 0 - if (ourlock) - snd_mtxunlock(d->lock); -#endif - free(sce, M_DEVBUF); - return 0; } int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) { struct snddev_info *d = device_get_softc(dev); struct pcm_channel *ch; int err; - ch = pcm_chn_create(d, NULL, cls, dir, devinfo); + ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); if (!ch) { device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); return ENODEV; } err = pcm_chn_add(d, ch); if (err) { device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); pcm_chn_destroy(ch); return err; } return err; } static int pcm_killchan(device_t dev) { struct snddev_info *d = device_get_softc(dev); - struct snddev_channel *sce; struct pcm_channel *ch; int error = 0; - sce = SLIST_FIRST(&d->channels); - ch = sce->channel; + ch = CHN_FIRST(d, channels.pcm); - error = pcm_chn_remove(d, sce->channel); + error = pcm_chn_remove(d, ch); if (error) return (error); return (pcm_chn_destroy(ch)); } int pcm_setstatus(device_t dev, char *str) { struct snddev_info *d = device_get_softc(dev); - snd_mtxlock(d->lock); + pcm_setmaxautovchans(d, snd_maxautovchans); + + pcm_lock(d); + strlcpy(d->status, str, SND_STATUSLEN); - snd_mtxunlock(d->lock); - if (snd_maxautovchans > 0) - pcm_setvchans(d, 1); + + /* Last stage, enable cloning. */ + if (d->clones != NULL) { + (void)snd_clone_enable(d->clones); + } + + pcm_unlock(d); + return 0; } uint32_t pcm_getflags(device_t dev) { struct snddev_info *d = device_get_softc(dev); return d->flags; } void pcm_setflags(device_t dev, uint32_t val) { struct snddev_info *d = device_get_softc(dev); d->flags = val; } void * pcm_getdevinfo(device_t dev) { struct snddev_info *d = device_get_softc(dev); return d->devinfo; } unsigned int pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) { struct snddev_info *d = device_get_softc(dev); int sz, x; sz = 0; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { x = sz; RANGE(sz, minbufsz, maxbufsz); if (x != sz) device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); x = minbufsz; while (x < sz) x <<= 1; if (x > sz) x >>= 1; if (x != sz) { device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); sz = x; } } else { sz = deflt; } d->bufsz = sz; return sz; } +#if defined(SND_DYNSYSCTL) && defined(SND_DEBUG) +static int +sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + uint32_t flags; + int err; + + d = oidp->oid_arg1; + if (d == NULL || d->clones == NULL) + return (ENODEV); + + pcm_lock(d); + flags = snd_clone_getflags(d->clones); + pcm_unlock(d); + err = sysctl_handle_int(oidp, (int *)(&flags), sizeof(flags), req); + + if (err == 0 && req->newptr != NULL) { + if ((flags & ~SND_CLONE_MASK)) + err = EINVAL; + else { + pcm_lock(d); + (void)snd_clone_setflags(d->clones, flags); + pcm_unlock(d); + } + } + + return (err); +} + +static int +sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + int err, deadline; + + d = oidp->oid_arg1; + if (d == NULL || d->clones == NULL) + return (ENODEV); + + pcm_lock(d); + deadline = snd_clone_getdeadline(d->clones); + pcm_unlock(d); + err = sysctl_handle_int(oidp, &deadline, sizeof(deadline), req); + + if (err == 0 && req->newptr != NULL) { + if (deadline < 0) + err = EINVAL; + else { + pcm_lock(d); + (void)snd_clone_setdeadline(d->clones, deadline); + pcm_unlock(d); + } + } + + return (err); +} + +static int +sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + int err, val; + + d = oidp->oid_arg1; + if (d == NULL || d->clones == NULL) + return (ENODEV); + + val = 0; + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + + if (err == 0 && req->newptr != NULL && val != 0) { + pcm_lock(d); + (void)snd_clone_gc(d->clones); + pcm_unlock(d); + } + + return (err); +} + +static int +sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + int i, err, val; + + val = 0; + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + + if (err == 0 && req->newptr != NULL && val != 0) { + for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { + d = devclass_get_softc(pcm_devclass, i); + if (d == NULL || d->clones == NULL) + continue; + pcm_lock(d); + (void)snd_clone_gc(d->clones); + pcm_unlock(d); + } + } + + return (err); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_clone_gc, "I", + "global clone garbage collector"); +#endif + int pcm_register(device_t dev, void *devinfo, int numplay, int numrec) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d; if (pcm_veto_load) { device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); return EINVAL; } + if (device_get_unit(dev) > PCMMAXUNIT) { + device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", + device_get_unit(dev), PCMMAXUNIT); + device_printf(dev, + "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); + return ENODEV; + } + + d = device_get_softc(dev); d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); #if 0 /* * d->flags should be cleared by the allocator of the softc. * We cannot clear this field here because several devices set * this flag before calling pcm_register(). */ d->flags = 0; #endif d->dev = dev; d->devinfo = devinfo; d->devcount = 0; d->reccount = 0; d->playcount = 0; - d->vchancount = 0; + d->pvchancount = 0; + d->rvchancount = 0; + d->pvchanrate = 0; + d->pvchanformat = 0; + d->rvchanrate = 0; + d->rvchanformat = 0; d->inprog = 0; - SLIST_INIT(&d->channels); + /* + * Create clone manager, disabled by default. Cloning will be + * enabled during final stage of driver iniialization through + * pcm_setstatus(). + */ + d->clones = snd_clone_create( +#ifdef SND_DIAGNOSTIC + d->lock, +#endif + SND_U_MASK | SND_D_MASK, PCMMAXCLONE, SND_CLONE_DEADLINE_DEFAULT, + SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF | + SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED); + if (bootverbose != 0 || snd_verbose > 3) { + pcm_lock(d); + device_printf(dev, + "clone manager: deadline=%dms flags=0x%08x\n", + snd_clone_getdeadline(d->clones), + snd_clone_getflags(d->clones)); + pcm_unlock(d); + } + + CHN_INIT(d, channels.pcm); + CHN_INIT(d, channels.pcm.busy); + if ((numplay == 0 || numrec == 0) && numplay != numrec) d->flags |= SD_F_SIMPLEX; d->fakechan = fkchan_setup(dev); chn_init(d->fakechan, NULL, 0, 0); #ifdef SND_DYNSYSCTL + sysctl_ctx_init(&d->play_sysctl_ctx); + d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", + CTLFLAG_RD, 0, "playback channels node"); + sysctl_ctx_init(&d->rec_sysctl_ctx); + d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", + CTLFLAG_RD, 0, "record channels node"); /* XXX: an user should be able to set this with a control tool, the sysadmin then needs min+max sysctls for this */ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); +#ifdef SND_DEBUG + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d), + sysctl_dev_pcm_clone_flags, "IU", + "clone flags"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), + sysctl_dev_pcm_clone_deadline, "I", + "clone expiration deadline (ms)"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), + sysctl_dev_pcm_clone_gc, "I", + "clone garbage collector"); #endif - if (numplay > 0) { +#endif + if (numplay > 0 || numrec > 0) { d->flags |= SD_F_AUTOVCHAN; vchan_initsys(dev); } sndstat_register(dev, d->status, sndstat_prepare_pcm); return 0; } int pcm_unregister(device_t dev) { struct snddev_info *d = device_get_softc(dev); - struct snddev_channel *sce; - struct pcmchan_children *pce; struct pcm_channel *ch; + struct thread *td; + int i; - if (sndstat_acquire() != 0) { + td = curthread; + + if (sndstat_acquire(td) != 0) { device_printf(dev, "unregister: sndstat busy\n"); return EBUSY; } - snd_mtxlock(d->lock); + pcm_lock(d); if (d->inprog) { device_printf(dev, "unregister: operation in progress\n"); - snd_mtxunlock(d->lock); - sndstat_release(); + pcm_unlock(d); + sndstat_release(td); return EBUSY; } - SLIST_FOREACH(sce, &d->channels, link) { - ch = sce->channel; + CHN_FOREACH(ch, d, channels.pcm) { if (ch->refcount > 0) { device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); - snd_mtxunlock(d->lock); - sndstat_release(); + pcm_unlock(d); + sndstat_release(td); return EBUSY; } } + if (d->clones != NULL) { + if (snd_clone_busy(d->clones) != 0) { + device_printf(dev, "unregister: clone busy\n"); + pcm_unlock(d); + sndstat_release(td); + return EBUSY; + } else + (void)snd_clone_disable(d->clones); + } + if (mixer_uninit(dev) == EBUSY) { device_printf(dev, "unregister: mixer busy\n"); - snd_mtxunlock(d->lock); - sndstat_release(); + if (d->clones != NULL) + (void)snd_clone_enable(d->clones); + pcm_unlock(d); + sndstat_release(td); return EBUSY; } - SLIST_FOREACH(sce, &d->channels, link) { - if (sce->dsp_devt) { - destroy_dev(sce->dsp_devt); - sce->dsp_devt = NULL; - } - if (sce->dspW_devt) { - destroy_dev(sce->dspW_devt); - sce->dspW_devt = NULL; - } - if (sce->audio_devt) { - destroy_dev(sce->audio_devt); - sce->audio_devt = NULL; - } - if (sce->dspHW_devt) { - destroy_dev(sce->dspHW_devt); - sce->dspHW_devt = NULL; - } - d->devcount--; - ch = sce->channel; - if (ch == NULL) - continue; - pce = SLIST_FIRST(&ch->children); - while (pce != NULL) { -#if 0 - device_printf(d->dev, "<%s> removing <%s>\n", - ch->name, (pce->channel != NULL) ? - pce->channel->name : "unknown"); -#endif - SLIST_REMOVE(&ch->children, pce, pcmchan_children, link); - free(pce, M_DEVBUF); - pce = SLIST_FIRST(&ch->children); - } + if (d->clones != NULL) { + snd_clone_destroy(d->clones); + d->clones = NULL; } -#ifdef SND_DYNSYSCTL -#if 0 - d->sysctl_tree_top = NULL; - sysctl_ctx_free(&d->sysctl_tree); -#endif -#endif + d->devcount = 0; -#if 0 - SLIST_FOREACH(sce, &d->channels, link) { - ch = sce->channel; - if (ch == NULL) - continue; - if (!SLIST_EMPTY(&ch->children)) - device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n", - __func__, ch->name); +#ifdef SND_DYNSYSCTL + if (d->play_sysctl_tree != NULL) { + sysctl_ctx_free(&d->play_sysctl_ctx); + d->play_sysctl_tree = NULL; } + if (d->rec_sysctl_tree != NULL) { + sysctl_ctx_free(&d->rec_sysctl_ctx); + d->rec_sysctl_tree = NULL; + } #endif - while (!SLIST_EMPTY(&d->channels)) + + while (!CHN_EMPTY(d, channels.pcm)) pcm_killchan(dev); chn_kill(d->fakechan); fkchan_kill(d->fakechan); -#if 0 - device_printf(d->dev, "%s: devcount=%u, playcount=%u, " - "reccount=%u, vchancount=%u\n", - __func__, d->devcount, d->playcount, d->reccount, - d->vchancount); -#endif - snd_mtxunlock(d->lock); + pcm_unlock(d); snd_mtxfree(d->lock); sndstat_unregister(dev); - sndstat_release(); + sndstat_release(td); + + if (snd_unit == device_get_unit(dev)) { + /* + * Reassign default unit to the next available dev. + */ + for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { + if (device_get_unit(dev) == i || + devclass_get_softc(pcm_devclass, i) == NULL) + continue; + snd_unit = i; + break; + } + } + return 0; } /************************************************************************/ static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) { struct snddev_info *d; - struct snddev_channel *sce; struct pcm_channel *c; struct pcm_feeder *f; - int pc, rc, vc; if (verbose < 1) return 0; d = device_get_softc(dev); if (!d) return ENXIO; - snd_mtxlock(d->lock); - if (!SLIST_EMPTY(&d->channels)) { - pc = rc = vc = 0; - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - if (c->direction == PCMDIR_PLAY) { - if (c->flags & CHN_F_VIRTUAL) - vc++; - else - pc++; - } else - rc++; - } - sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, + pcm_lock(d); + if (!CHN_EMPTY(d, channels.pcm)) { + sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", + d->playcount, d->pvchancount, + d->reccount, d->rvchancount, (d->flags & SD_F_SIMPLEX)? "" : " duplex", #ifdef USING_DEVFS (device_get_unit(dev) == snd_unit)? " default" : "" #else "" #endif ); if (verbose <= 1) { - snd_mtxunlock(d->lock); + pcm_unlock(d); return 0; } - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; + CHN_FOREACH(c, d, channels.pcm) { KASSERT(c->bufhard != NULL && c->bufsoft != NULL, ("hosed pcm channel setup")); sbuf_printf(s, "\n\t"); /* it would be better to indent child channels */ sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); sbuf_printf(s, "spd %d", c->speed); if (c->speed != sndbuf_getspd(c->bufhard)) sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); sbuf_printf(s, ", fmt 0x%08x", c->format); if (c->format != sndbuf_getfmt(c->bufhard)) sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); if (c->pid != -1) sbuf_printf(s, ", pid %d", c->pid); sbuf_printf(s, "\n\t"); sbuf_printf(s, "interrupts %d, ", c->interrupts); if (c->direction == PCMDIR_REC) sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), sndbuf_getblkcnt(c->bufhard), sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), sndbuf_getblkcnt(c->bufsoft)); else sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), sndbuf_getblkcnt(c->bufhard), sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), sndbuf_getblkcnt(c->bufsoft)); sbuf_printf(s, "\n\t"); sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); sbuf_printf(s, " -> "); f = c->feeder; while (f->source != NULL) f = f->source; while (f != NULL) { sbuf_printf(s, "%s", f->class->name); if (f->desc->type == FEEDER_FMT) sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); if (f->desc->type == FEEDER_RATE) sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || f->desc->type == FEEDER_VOLUME) sbuf_printf(s, "(0x%08x)", f->desc->out); sbuf_printf(s, " -> "); f = f->parent; } sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); } } else sbuf_printf(s, " (mixer only)"); - snd_mtxunlock(d->lock); + pcm_unlock(d); return 0; } /************************************************************************/ #ifdef SND_DYNSYSCTL int sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; + int direction, vchancount; int err, newcnt; - d = oidp->oid_arg1; + d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); + if (d == NULL) + return EINVAL; - newcnt = d->vchancount; + switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { + case VCHAN_PLAY: + if (d->playcount < 1) + return ENODEV; + direction = PCMDIR_PLAY; + vchancount = d->pvchancount; + break; + case VCHAN_REC: + if (d->reccount < 1) + return ENODEV; + direction = PCMDIR_REC; + vchancount = d->rvchancount; + break; + default: + return EINVAL; + break; + } + + newcnt = vchancount; err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); - if (err == 0 && req->newptr != NULL && d->vchancount != newcnt) - err = pcm_setvchans(d, newcnt); + if (err == 0 && req->newptr != NULL && vchancount != newcnt) { + if (newcnt < 0) + newcnt = 0; + if (newcnt > SND_MAXVCHANS) + newcnt = SND_MAXVCHANS; + err = pcm_setvchans(d, direction, newcnt, -1); + } return err; } #endif /************************************************************************/ /** * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. * * @param si Pointer to oss_sysinfo struct where information about the * sound subsystem will be written/copied. * * This routine returns information about the sound system, such as the * current OSS version, number of audio, MIDI, and mixer drivers, etc. * Also includes a bitmask showing which of the above types of devices * are open (busy). * * @note * Calling threads must not hold any snddev_info or pcm_channel locks. * * @author Ryan Beasley */ void sound_oss_sysinfo(oss_sysinfo *si) { static char si_product[] = "FreeBSD native OSS ABI"; static char si_version[] = __XSTRING(__FreeBSD_version); static int intnbits = sizeof(int) * 8; /* Better suited as macro? Must pester a C guru. */ - struct snddev_channel *sce; struct snddev_info *d; struct pcm_channel *c; int i, j, ncards; ncards = 0; strlcpy(si->product, si_product, sizeof(si->product)); strlcpy(si->version, si_version, sizeof(si->version)); si->versionnum = SOUND_VERSION; /* * Iterate over PCM devices and their channels, gathering up data * for the numaudios, ncards, and openedaudio fields. */ si->numaudios = 0; bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); if (pcm_devclass != NULL) { j = 0; for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { d = devclass_get_softc(pcm_devclass, i); if (!d) continue; /* See note in function's docblock */ mtx_assert(d->lock, MA_NOTOWNED); /* Increment device's "operations in progress" */ pcm_inprog(d, 1); pcm_lock(d); si->numaudios += d->devcount; ++ncards; - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; + CHN_FOREACH(c, d, channels.pcm) { mtx_assert(c->lock, MA_NOTOWNED); CHN_LOCK(c); if (c->flags & CHN_F_BUSY) si->openedaudio[j / intnbits] |= (1 << (j % intnbits)); CHN_UNLOCK(c); j++; } pcm_unlock(d); pcm_inprog(d, -1); } } si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ /** * @todo Collect num{midis,timers}. * * Need access to sound/midi/midi.c::midistat_lock in order * to safely touch midi_devices and get a head count of, well, * MIDI devices. midistat_lock is a global static (i.e., local to * midi.c), but midi_devices is a regular global; should the mutex * be publicized, or is there another way to get this information? * * NB: MIDI/sequencer stuff is currently on hold. */ si->nummidis = 0; si->numtimers = 0; si->nummixers = mixer_count; si->numcards = ncards; /* OSSv4 docs: Intended only for test apps; API doesn't really have much of a concept of cards. Shouldn't be used by applications. */ /** * @todo Fill in "busy devices" fields. * * si->openedmidi = " MIDI devices */ bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); /* * Si->filler is a reserved array, but according to docs each * element should be set to -1. */ for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) si->filler[i] = -1; } /************************************************************************/ static int sound_modevent(module_t mod, int type, void *data) { int ret; #if 0 return (midi_modevent(mod, type, data)); #else ret = 0; switch(type) { case MOD_LOAD: pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); break; case MOD_UNLOAD: case MOD_SHUTDOWN: + ret = sndstat_acquire(curthread); + if (ret != 0) + break; if (pcmsg_unrhdr != NULL) { delete_unrhdr(pcmsg_unrhdr); pcmsg_unrhdr = NULL; } break; default: ret = EOPNOTSUPP; } return ret; #endif } DEV_MODULE(sound, sound_modevent, NULL); MODULE_VERSION(sound, SOUND_MODVER); Index: head/sys/dev/sound/pcm/sound.h =================================================================== --- head/sys/dev/sound/pcm/sound.h (revision 170160) +++ head/sys/dev/sound/pcm/sound.h (revision 170161) @@ -1,597 +1,603 @@ /*- * Copyright (c) 1999 Cameron Grant * Copyright by Hannu Savolainen 1995 * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * first, include kernel header files. */ #ifndef _OS_H_ #define _OS_H_ #ifdef _KERNEL #include #include #include #include #include #include #include #include #include /* for DATA_SET */ #include #include #include #include #include #include #include #include #if __FreeBSD_version < 500000 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #undef USING_MUTEX #undef USING_DEVFS #if __FreeBSD_version > 500000 #include #include #include #define USING_MUTEX #define USING_DEVFS #else #define INTR_TYPE_AV INTR_TYPE_TTY #define INTR_MPSAFE 0 #endif #define SND_DYNSYSCTL struct pcm_channel; struct pcm_feeder; struct snd_dbuf; struct snd_mixer; #include #include #include #include #include +#include +#include #define PCM_SOFTC_SIZE 512 #define SND_STATUSLEN 64 #define SOUND_MODVER 2 #define SOUND_MINVER SOUND_MODVER #define SOUND_PREFVER SOUND_MODVER #define SOUND_MAXVER SOUND_MODVER /* * We're abusing the fact that MAXMINOR still have enough room - * for our bit twiddling and nobody ever need 2048 unique soundcards, - * 32 unique device types and 256 unique cloneable devices for the - * next 100 years... or until the NextPCM. - * - * MAXMINOR 0xffff00ff - * | | - * | +--- PCMMAXCHAN - * | - * +-------- ((PCMMAXUNIT << 5) | PCMMAXDEV) << 16 + * for our bit twiddling and nobody ever need 512 unique soundcards, + * 32 unique device types and 1024 unique cloneable devices for the + * next 100 years... */ -#define PCMMAXCHAN 0xff -#define PCMMAXDEV 0x1f -#define PCMMAXUNIT 0x7ff -#define PCMMINOR(x) minor(x) -#define PCMCHAN(x) (PCMMINOR(x) & PCMMAXCHAN) -#define PCMUNIT(x) ((PCMMINOR(x) >> 21) & PCMMAXUNIT) -#define PCMDEV(x) ((PCMMINOR(x) >> 16) & PCMMAXDEV) -#define PCMMKMINOR(u, d, c) ((((u) & PCMMAXUNIT) << 21) | \ - (((d) & PCMMAXDEV) << 16) | ((c) & PCMMAXCHAN)) +#define PCMMAXUNIT (snd_max_u()) +#define PCMMAXDEV (snd_max_d()) +#define PCMMAXCHAN (snd_max_c()) +#define PCMMAXCLONE PCMMAXCHAN + +#define PCMUNIT(x) (snd_unit2u(dev2unit(x))) +#define PCMDEV(x) (snd_unit2d(dev2unit(x))) +#define PCMCHAN(x) (snd_unit2c(dev2unit(x))) + +/* + * By design, limit possible channels for each direction. + */ +#define SND_MAXHWCHAN 256 +#define SND_MAXVCHANS SND_MAXHWCHAN + #define SD_F_SIMPLEX 0x00000001 #define SD_F_AUTOVCHAN 0x00000002 #define SD_F_SOFTPCMVOL 0x00000004 #define SD_F_PSWAPLR 0x00000008 #define SD_F_RSWAPLR 0x00000010 +#define SD_F_DYING 0x00000020 +#define SD_F_SUICIDE 0x00000040 #define SD_F_PRIO_RD 0x10000000 #define SD_F_PRIO_WR 0x20000000 #define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR) #define SD_F_DIR_SET 0x40000000 #define SD_F_TRANSIENT 0xf0000000 /* many variables should be reduced to a range. Here define a macro */ #define RANGE(var, low, high) (var) = \ (((var)<(low))? (low) : ((var)>(high))? (high) : (var)) #define DSP_BUFFSIZE (8192) /* * Macros for reading/writing PCM sample / int values from bytes array. * Since every process is done using signed integer (and to make our life * less miserable), unsigned sample will be converted to its signed * counterpart and restored during writing back. To avoid overflow, * we truncate 32bit (and only 32bit) samples down to 24bit (see below * for the reason), unless PCM_USE_64BIT_ARITH is defined. */ /* * Automatically turn on 64bit arithmetic on suitable archs * (amd64 64bit, ia64, etc..) for wider 32bit samples / integer processing. */ #if LONG_BIT >= 64 #undef PCM_USE_64BIT_ARITH #define PCM_USE_64BIT_ARITH 1 #else #if 0 #undef PCM_USE_64BIT_ARITH #define PCM_USE_64BIT_ARITH 1 #endif #endif #ifdef PCM_USE_64BIT_ARITH typedef int64_t intpcm_t; #else typedef int32_t intpcm_t; #endif /* 32bit fixed point shift */ #define PCM_FXSHIFT 8 #define PCM_S8_MAX 0x7f #define PCM_S8_MIN -0x80 #define PCM_S16_MAX 0x7fff #define PCM_S16_MIN -0x8000 #define PCM_S24_MAX 0x7fffff #define PCM_S24_MIN -0x800000 #ifdef PCM_USE_64BIT_ARITH #if LONG_BIT >= 64 #define PCM_S32_MAX 0x7fffffffL #define PCM_S32_MIN -0x80000000L #else #define PCM_S32_MAX 0x7fffffffLL #define PCM_S32_MIN -0x80000000LL #endif #else #define PCM_S32_MAX 0x7fffffff #define PCM_S32_MIN (-0x7fffffff - 1) #endif /* Bytes-per-sample definition */ #define PCM_8_BPS 1 #define PCM_16_BPS 2 #define PCM_24_BPS 3 #define PCM_32_BPS 4 #if BYTE_ORDER == LITTLE_ENDIAN #define PCM_READ_S16_LE(b8) *((int16_t *)(b8)) #define _PCM_READ_S32_LE(b8) *((int32_t *)(b8)) #define PCM_READ_S16_BE(b8) \ ((int32_t)((b8)[1] | ((int8_t)((b8)[0])) << 8)) #define _PCM_READ_S32_BE(b8) \ ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \ ((int8_t)((b8)[0])) << 24)) #define PCM_WRITE_S16_LE(b8, val) *((int16_t *)(b8)) = (val) #define _PCM_WRITE_S32_LE(b8, val) *((int32_t *)(b8)) = (val) #define PCM_WRITE_S16_BE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[1] = val; \ b8[0] = val >> 8; \ } while(0) #define _PCM_WRITE_S32_BE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[3] = val; \ b8[2] = val >> 8; \ b8[1] = val >> 16; \ b8[0] = val >> 24; \ } while(0) #define PCM_READ_U16_LE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) #define _PCM_READ_U32_LE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) #define PCM_READ_U16_BE(b8) \ ((int32_t)((b8)[1] | ((int8_t)((b8)[0] ^ 0x80)) << 8)) #define _PCM_READ_U32_BE(b8) \ ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \ ((int8_t)((b8)[0] ^ 0x80)) << 24)) #define PCM_WRITE_U16_LE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000 #define _PCM_WRITE_U32_LE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000 #define PCM_WRITE_U16_BE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[1] = val; \ b8[0] = (val >> 8) ^ 0x80; \ } while(0) #define _PCM_WRITE_U32_BE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[3] = val; \ b8[2] = val >> 8; \ b8[1] = val >> 16; \ b8[0] = (val >> 24) ^ 0x80; \ } while(0) #else /* !LITTLE_ENDIAN */ #define PCM_READ_S16_LE(b8) \ ((int32_t)((b8)[0] | ((int8_t)((b8)[1])) << 8)) #define _PCM_READ_S32_LE(b8) \ ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \ ((int8_t)((b8)[3])) << 24)) #define PCM_READ_S16_BE(b8) *((int16_t *)(b8)) #define _PCM_READ_S32_BE(b8) *((int32_t *)(b8)) #define PCM_WRITE_S16_LE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[0] = val; \ b8[1] = val >> 8; \ } while(0) #define _PCM_WRITE_S32_LE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[0] = val; \ b8[1] = val >> 8; \ b8[2] = val >> 16; \ b8[3] = val >> 24; \ } while(0) #define PCM_WRITE_S16_BE(b8, val) *((int16_t *)(b8)) = (val) #define _PCM_WRITE_S32_BE(b8, val) *((int32_t *)(b8)) = (val) #define PCM_READ_U16_LE(b8) \ ((int32_t)((b8)[0] | ((int8_t)((b8)[1] ^ 0x80)) << 8)) #define _PCM_READ_U32_LE(b8) \ ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \ ((int8_t)((b8)[3] ^ 0x80)) << 24)) #define PCM_READ_U16_BE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) #define _PCM_READ_U32_BE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) #define PCM_WRITE_U16_LE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[0] = val; \ b8[1] = (val >> 8) ^ 0x80; \ } while(0) #define _PCM_WRITE_U32_LE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[0] = val; \ b8[1] = val >> 8; \ b8[2] = val >> 16; \ b8[3] = (val >> 24) ^ 0x80; \ } while(0) #define PCM_WRITE_U16_BE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000 #define _PCM_WRITE_U32_BE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000 #endif #define PCM_READ_S24_LE(b8) \ ((int32_t)((b8)[0] | (b8)[1] << 8 | ((int8_t)((b8)[2])) << 16)) #define PCM_READ_S24_BE(b8) \ ((int32_t)((b8)[2] | (b8)[1] << 8 | ((int8_t)((b8)[0])) << 16)) #define PCM_WRITE_S24_LE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[0] = val; \ b8[1] = val >> 8; \ b8[2] = val >> 16; \ } while(0) #define PCM_WRITE_S24_BE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[2] = val; \ b8[1] = val >> 8; \ b8[0] = val >> 16; \ } while(0) #define PCM_READ_U24_LE(b8) \ ((int32_t)((b8)[0] | (b8)[1] << 8 | \ ((int8_t)((b8)[2] ^ 0x80)) << 16)) #define PCM_READ_U24_BE(b8) \ ((int32_t)((b8)[2] | (b8)[1] << 8 | \ ((int8_t)((b8)[0] ^ 0x80)) << 16)) #define PCM_WRITE_U24_LE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[0] = val; \ b8[1] = val >> 8; \ b8[2] = (val >> 16) ^ 0x80; \ } while(0) #define PCM_WRITE_U24_BE(bb8, vval) do { \ int32_t val = (vval); \ uint8_t *b8 = (bb8); \ b8[2] = val; \ b8[1] = val >> 8; \ b8[0] = (val >> 16) ^ 0x80; \ } while(0) #ifdef PCM_USE_64BIT_ARITH #define PCM_READ_S32_LE(b8) _PCM_READ_S32_LE(b8) #define PCM_READ_S32_BE(b8) _PCM_READ_S32_BE(b8) #define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) #define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) #define PCM_READ_U32_LE(b8) _PCM_READ_U32_LE(b8) #define PCM_READ_U32_BE(b8) _PCM_READ_U32_BE(b8) #define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) #define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) #else /* !PCM_USE_64BIT_ARITH */ /* * 24bit integer ?!? This is quite unfortunate, eh? Get the fact straight: * Dynamic range for: * 1) Human =~ 140db * 2) 16bit = 96db (close enough) * 3) 24bit = 144db (perfect) * 4) 32bit = 196db (way too much) * 5) Bugs Bunny = Gazillion!@%$Erbzzztt-EINVAL db * Since we're not Bugs Bunny ..uh..err.. avoiding 64bit arithmetic, 24bit * is pretty much sufficient for our signed integer processing. */ #define PCM_READ_S32_LE(b8) (_PCM_READ_S32_LE(b8) >> PCM_FXSHIFT) #define PCM_READ_S32_BE(b8) (_PCM_READ_S32_BE(b8) >> PCM_FXSHIFT) #define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, (val) << PCM_FXSHIFT) #define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, (val) << PCM_FXSHIFT) #define PCM_READ_U32_LE(b8) (_PCM_READ_U32_LE(b8) >> PCM_FXSHIFT) #define PCM_READ_U32_BE(b8) (_PCM_READ_U32_BE(b8) >> PCM_FXSHIFT) #define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, (val) << PCM_FXSHIFT) #define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, (val) << PCM_FXSHIFT) #endif /* * 8bit sample is pretty much useless since it doesn't provide * sufficient dynamic range throughout our filtering process. * For the sake of completeness, declare it anyway. */ #define PCM_READ_S8(b8) *((int8_t *)(b8)) #define PCM_READ_S8_NE(b8) PCM_READ_S8(b8) #define PCM_READ_U8(b8) ((int8_t)(*((uint8_t *)(b8)) ^ 0x80)) #define PCM_READ_U8_NE(b8) PCM_READ_U8(b8) #define PCM_WRITE_S8(b8, val) *((int8_t *)(b8)) = (val) #define PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val) #define PCM_WRITE_U8(b8, val) *((uint8_t *)(b8)) = (val) ^ 0x80 #define PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val) #define PCM_CLAMP_S8(val) \ (((val) > PCM_S8_MAX) ? PCM_S8_MAX : \ (((val) < PCM_S8_MIN) ? PCM_S8_MIN : (val))) #define PCM_CLAMP_S16(val) \ (((val) > PCM_S16_MAX) ? PCM_S16_MAX : \ (((val) < PCM_S16_MIN) ? PCM_S16_MIN : (val))) #define PCM_CLAMP_S24(val) \ (((val) > PCM_S24_MAX) ? PCM_S24_MAX : \ (((val) < PCM_S24_MIN) ? PCM_S24_MIN : (val))) #ifdef PCM_USE_64BIT_ARITH #define PCM_CLAMP_S32(val) \ (((val) > PCM_S32_MAX) ? PCM_S32_MAX : \ (((val) < PCM_S32_MIN) ? PCM_S32_MIN : (val))) #else #define PCM_CLAMP_S32(val) \ (((val) > PCM_S24_MAX) ? PCM_S32_MAX : \ (((val) < PCM_S24_MIN) ? PCM_S32_MIN : \ ((val) << PCM_FXSHIFT))) #endif #define PCM_CLAMP_U8(val) PCM_CLAMP_S8(val) #define PCM_CLAMP_U16(val) PCM_CLAMP_S16(val) #define PCM_CLAMP_U24(val) PCM_CLAMP_S24(val) #define PCM_CLAMP_U32(val) PCM_CLAMP_S32(val) /* make figuring out what a format is easier. got AFMT_STEREO already */ #define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE) #define AFMT_24BIT (AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_BE) #define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE) #define AFMT_8BIT (AFMT_MU_LAW | AFMT_A_LAW | AFMT_U8 | AFMT_S8) #define AFMT_SIGNED (AFMT_S32_LE | AFMT_S32_BE | AFMT_S24_LE | AFMT_S24_BE | \ AFMT_S16_LE | AFMT_S16_BE | AFMT_S8) #define AFMT_BIGENDIAN (AFMT_S32_BE | AFMT_U32_BE | AFMT_S24_BE | AFMT_U24_BE | \ AFMT_S16_BE | AFMT_U16_BE) struct pcm_channel *fkchan_setup(device_t dev); int fkchan_kill(struct pcm_channel *c); -/* XXX Flawed definition. I'll fix it someday. */ -#define SND_MAXVCHANS PCMMAXCHAN - /* * Minor numbers for the sound driver. * * Unfortunately Creative called the codec chip of SB as a DSP. For this * reason the /dev/dsp is reserved for digitized audio use. There is a * device for true DSP processors but it will be called something else. * In v3.0 it's /dev/sndproc but this could be a temporary solution. */ #define SND_DEV_CTL 0 /* Control port /dev/mixer */ #define SND_DEV_SEQ 1 /* Sequencer /dev/sequencer */ #define SND_DEV_MIDIN 2 /* Raw midi access */ #define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ #define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ #define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ #define SND_DEV_STATUS 6 /* /dev/sndstat */ /* #7 not in use now. */ #define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ #define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ #define SND_DEV_PSS SND_DEV_SNDPROC /* ? */ #define SND_DEV_NORESET 10 -#define SND_DEV_DSPHW 11 /* specific channel request */ +#define SND_DEV_DSPHW_PLAY 11 /* specific playback channel */ +#define SND_DEV_DSPHW_VPLAY 12 /* specific virtual playback channel */ +#define SND_DEV_DSPHW_REC 13 /* specific record channel */ +#define SND_DEV_DSPHW_VREC 14 /* specific virtual record channel */ + #define DSP_DEFAULT_SPEED 8000 #define ON 1 #define OFF 0 extern int pcm_veto_load; extern int snd_unit; extern int snd_maxautovchans; extern int snd_verbose; extern devclass_t pcm_devclass; extern struct unrhdr *pcmsg_unrhdr; /* * some macros for debugging purposes * DDB/DEB to enable/disable debugging stuff * BVDDB to enable debugging when bootverbose */ #define BVDDB(x) if (bootverbose) x #ifndef DEB #define DEB(x) #endif SYSCTL_DECL(_hw_snd); struct pcm_channel *pcm_getfakechan(struct snddev_info *d); -int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, pid_t pid, int chnum); +int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, pid_t pid, int devunit); int pcm_chnrelease(struct pcm_channel *c); int pcm_chnref(struct pcm_channel *c, int ref); int pcm_inprog(struct snddev_info *d, int delta); -struct pcm_channel *pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo); +struct pcm_channel *pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo); int pcm_chn_destroy(struct pcm_channel *ch); int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch); int pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch); int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo); unsigned int pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz); int pcm_register(device_t dev, void *devinfo, int numplay, int numrec); int pcm_unregister(device_t dev); int pcm_setstatus(device_t dev, char *str); u_int32_t pcm_getflags(device_t dev); void pcm_setflags(device_t dev, u_int32_t val); void *pcm_getdevinfo(device_t dev); int snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep); void *snd_mtxcreate(const char *desc, const char *type); void snd_mtxfree(void *m); void snd_mtxassert(void *m); #define snd_mtxlock(m) mtx_lock(m) #define snd_mtxunlock(m) mtx_unlock(m) int sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS); typedef int (*sndstat_handler)(struct sbuf *s, device_t dev, int verbose); -int sndstat_acquire(void); -int sndstat_release(void); +int sndstat_acquire(struct thread *td); +int sndstat_release(struct thread *td); int sndstat_register(device_t dev, char *str, sndstat_handler handler); int sndstat_registerfile(char *str); int sndstat_unregister(device_t dev); int sndstat_unregisterfile(char *str); #define SND_DECLARE_FILE(version) \ _SND_DECLARE_FILE(__LINE__, version) #define _SND_DECLARE_FILE(uniq, version) \ __SND_DECLARE_FILE(uniq, version) #define __SND_DECLARE_FILE(uniq, version) \ static char sndstat_vinfo[] = version; \ SYSINIT(sdf_ ## uniq, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_registerfile, sndstat_vinfo); \ SYSUNINIT(sdf_ ## uniq, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sndstat_unregisterfile, sndstat_vinfo); /* usage of flags in device config entry (config file) */ #define DV_F_DRQ_MASK 0x00000007 /* mask for secondary drq */ #define DV_F_DUAL_DMA 0x00000010 /* set to use secondary dma channel */ /* ought to be made obsolete but still used by mss */ #define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */ #define DV_F_DEV_SHIFT 8 /* force device type/class */ #define PCM_DEBUG_MTX /* * this is rather kludgey- we need to duplicate these struct def'ns from sound.c * so that the macro versions of pcm_{,un}lock can dereference them. * we also have to do this now makedev() has gone away. */ -struct snddev_channel { - SLIST_ENTRY(snddev_channel) link; - struct pcm_channel *channel; - int chan_num; - struct cdev *dsp_devt; - struct cdev *dspW_devt; - struct cdev *audio_devt; - struct cdev *dspHW_devt; -}; - struct snddev_info { - SLIST_HEAD(, snddev_channel) channels; + struct { + struct { + SLIST_HEAD(, pcm_channel) head; + struct { + SLIST_HEAD(, pcm_channel) head; + } busy; + } pcm; + } channels; + struct snd_clone *clones; struct pcm_channel *fakechan; - unsigned devcount, playcount, reccount, vchancount; + unsigned devcount, playcount, reccount, pvchancount, rvchancount ; unsigned flags; int inprog; unsigned int bufsz; void *devinfo; device_t dev; char status[SND_STATUSLEN]; struct mtx *lock; struct cdev *mixer_dev; - + uint32_t pvchanrate, pvchanformat; + uint32_t rvchanrate, rvchanformat; + struct sysctl_ctx_list play_sysctl_ctx, rec_sysctl_ctx; + struct sysctl_oid *play_sysctl_tree, *rec_sysctl_tree; }; void sound_oss_sysinfo(oss_sysinfo *); #ifdef PCM_DEBUG_MTX #define pcm_lock(d) mtx_lock(((struct snddev_info *)(d))->lock) #define pcm_unlock(d) mtx_unlock(((struct snddev_info *)(d))->lock) #else void pcm_lock(struct snddev_info *d); void pcm_unlock(struct snddev_info *d); #endif #ifdef KLD_MODULE #define PCM_KLDSTRING(a) ("kld " # a) #else #define PCM_KLDSTRING(a) "" #endif #endif /* _KERNEL */ #endif /* _OS_H_ */ Index: head/sys/dev/sound/pcm/vchan.c =================================================================== --- head/sys/dev/sound/pcm/vchan.c (revision 170160) +++ head/sys/dev/sound/pcm/vchan.c (revision 170161) @@ -1,933 +1,1027 @@ /*- * Copyright (c) 2001 Cameron Grant * Copyright (c) 2006 Ariff Abdullah * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Almost entirely rewritten to add multi-format/channels mixing support. * */ #include #include #include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD$"); MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder"); -/* - * Default speed / format - */ -#define VCHAN_DEFAULT_SPEED 48000 -#define VCHAN_DEFAULT_AFMT (AFMT_S16_LE | AFMT_STEREO) -#define VCHAN_DEFAULT_STRFMT "s16le" - typedef uint32_t (*feed_vchan_mixer)(uint8_t *, uint8_t *, uint32_t); struct vchinfo { - uint32_t spd, fmt, fmts[2], blksz, bps, run; - struct pcm_channel *channel, *parent; + struct pcm_channel *channel; struct pcmchan_caps caps; + uint32_t fmtlist[2]; + int trigger; }; /* support everything (mono / stereo), except a-law / mu-law */ static struct afmtstr_table vchan_supported_fmts[] = { { "u8", AFMT_U8 }, { "s8", AFMT_S8 }, { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE }, { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE }, { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE }, { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE }, { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE }, { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE }, { NULL, 0 }, }; /* alias table, shorter. */ static const struct { char *alias, *fmtstr; } vchan_fmtstralias[] = { { "8", "u8" }, { "16", "s16le" }, { "24", "s24le" }, { "32", "s32le" }, { NULL, NULL }, }; #define vchan_valid_format(fmt) \ afmt2afmtstr(vchan_supported_fmts, fmt, NULL, 0, 0, \ AFMTSTR_STEREO_RETURN) #define vchan_valid_strformat(strfmt) \ afmtstr2afmt(vchan_supported_fmts, strfmt, AFMTSTR_STEREO_RETURN); /* * Need specialized WRITE macros since 32bit might involved saturation * if calculation is done within 32bit arithmetic. */ #define VCHAN_PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val) #define VCHAN_PCM_WRITE_S16_LE(b8, val) PCM_WRITE_S16_LE(b8, val) #define VCHAN_PCM_WRITE_S24_LE(b8, val) PCM_WRITE_S24_LE(b8, val) #define VCHAN_PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) #define VCHAN_PCM_WRITE_S16_BE(b8, val) PCM_WRITE_S16_BE(b8, val) #define VCHAN_PCM_WRITE_S24_BE(b8, val) PCM_WRITE_S24_BE(b8, val) #define VCHAN_PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) #define VCHAN_PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val) #define VCHAN_PCM_WRITE_U16_LE(b8, val) PCM_WRITE_U16_LE(b8, val) #define VCHAN_PCM_WRITE_U24_LE(b8, val) PCM_WRITE_U24_LE(b8, val) #define VCHAN_PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) #define VCHAN_PCM_WRITE_U16_BE(b8, val) PCM_WRITE_U16_BE(b8, val) #define VCHAN_PCM_WRITE_U24_BE(b8, val) PCM_WRITE_U24_BE(b8, val) #define VCHAN_PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) #define FEEDER_VCHAN_MIX(FMTBIT, VCHAN_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ static uint32_t \ feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(uint8_t *to, uint8_t *tmp, \ uint32_t count) \ { \ int32_t x, y; \ VCHAN_INTCAST z; \ int i; \ \ i = count; \ tmp += i; \ to += i; \ \ do { \ tmp -= PCM_##FMTBIT##_BPS; \ to -= PCM_##FMTBIT##_BPS; \ i -= PCM_##FMTBIT##_BPS; \ x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(tmp); \ y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(to); \ z = (VCHAN_INTCAST)x + y; \ x = PCM_CLAMP_##SIGN##FMTBIT(z); \ VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x); \ } while (i != 0); \ \ - return count; \ + return (count); \ } FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne) FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le) FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le) FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le) FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be) FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be) FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be) FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne) FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le) FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le) FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le) FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be) FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be) FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be) struct feed_vchan_info { uint32_t format; int bps; feed_vchan_mixer mix; }; static struct feed_vchan_info feed_vchan_info_tbl[] = { { AFMT_S8, PCM_8_BPS, feed_vchan_mix_s8ne }, { AFMT_S16_LE, PCM_16_BPS, feed_vchan_mix_s16le }, { AFMT_S24_LE, PCM_24_BPS, feed_vchan_mix_s24le }, { AFMT_S32_LE, PCM_32_BPS, feed_vchan_mix_s32le }, { AFMT_S16_BE, PCM_16_BPS, feed_vchan_mix_s16be }, { AFMT_S24_BE, PCM_24_BPS, feed_vchan_mix_s24be }, { AFMT_S32_BE, PCM_32_BPS, feed_vchan_mix_s32be }, { AFMT_U8, PCM_8_BPS, feed_vchan_mix_u8ne }, { AFMT_U16_LE, PCM_16_BPS, feed_vchan_mix_u16le }, { AFMT_U24_LE, PCM_24_BPS, feed_vchan_mix_u24le }, { AFMT_U32_LE, PCM_32_BPS, feed_vchan_mix_u32le }, { AFMT_U16_BE, PCM_16_BPS, feed_vchan_mix_u16be }, { AFMT_U24_BE, PCM_24_BPS, feed_vchan_mix_u24be }, { AFMT_U32_BE, PCM_32_BPS, feed_vchan_mix_u32be }, }; #define FVCHAN_DATA(i, c) ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf))) #define FVCHAN_INFOIDX(m) (((m) >> 4) & 0x1f) #define FVCHAN_CHANNELS(m) ((m) & 0xf) static int feed_vchan_init(struct pcm_feeder *f) { int i, channels; if (f->desc->out != f->desc->in) - return EINVAL; + return (EINVAL); channels = (f->desc->out & AFMT_STEREO) ? 2 : 1; for (i = 0; i < sizeof(feed_vchan_info_tbl) / sizeof(feed_vchan_info_tbl[0]); i++) { if ((f->desc->out & ~AFMT_STEREO) == feed_vchan_info_tbl[i].format) { f->data = (void *)FVCHAN_DATA(i, channels); - return 0; + return (0); } } - return -1; + return (-1); } +static __inline int +feed_vchan_rec(struct pcm_channel *c) +{ + struct pcm_channel *ch; + struct snd_dbuf *b, *bs; + int cnt, rdy; + + /* + * Reset ready and moving pointer. We're not using bufsoft + * anywhere since its sole purpose is to become the primary + * distributor for the recorded buffer and also as an interrupt + * threshold progress indicator. + */ + b = c->bufsoft; + b->rp = 0; + b->rl = 0; + cnt = sndbuf_getsize(b); + + do { + cnt = FEEDER_FEED(c->feeder->source, c, b->tmpbuf, cnt, + c->bufhard); + if (cnt != 0) { + sndbuf_acquire(b, b->tmpbuf, cnt); + cnt = sndbuf_getfree(b); + } + } while (cnt != 0); + + /* Not enough data */ + if (b->rl < sndbuf_getbps(b)) { + b->rl = 0; + return (0); + } + + /* + * Keep track of ready and moving pointer since we will use + * bufsoft over and over again, pretending nothing has happened. + */ + rdy = b->rl; + + CHN_FOREACH(ch, c, children.busy) { + CHN_LOCK(ch); + bs = ch->bufsoft; + cnt = sndbuf_getfree(bs); + if (!(ch->flags & CHN_F_TRIGGERED) || + cnt < sndbuf_getbps(bs)) { + CHN_UNLOCK(ch); + continue; + } + do { + cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf, cnt, b); + if (cnt != 0) { + sndbuf_acquire(bs, bs->tmpbuf, cnt); + cnt = sndbuf_getfree(bs); + } + } while (cnt != 0); + /* + * Not entirely flushed out... + */ + if (b->rl != 0) + ch->xruns++; + CHN_UNLOCK(ch); + /* + * Rewind buffer position for next virtual channel. + */ + b->rp = 0; + b->rl = rdy; + } + + /* + * Set ready pointer to indicate that our children are ready + * to be woken up, also as an interrupt threshold progress + * indicator. + */ + b->rl = 1; + + /* + * Return 0 to bail out early from sndbuf_feed() loop. + * No need to increase feedcount counter since part of this + * feeder chains already include feed_root(). + */ + return (0); +} + static int feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) + uint32_t count, void *source) { struct feed_vchan_info *info; struct snd_dbuf *src = source; - struct pcmchan_children *cce; struct pcm_channel *ch; uint32_t cnt, mcnt, rcnt, sz; uint8_t *tmp; + if (c->direction == PCMDIR_REC) + return (feed_vchan_rec(c)); + sz = sndbuf_getsize(src); if (sz < count) count = sz; info = &feed_vchan_info_tbl[FVCHAN_INFOIDX((intptr_t)f->data)]; sz = info->bps * FVCHAN_CHANNELS((intptr_t)f->data); count -= count % sz; if (count < sz) - return 0; + return (0); /* * we are going to use our source as a temporary buffer since it's * got no other purpose. we obtain our data by traversing the channel * list of children and calling vchan_mix_* to mix count bytes from * each into our destination buffer, b */ tmp = sndbuf_getbuf(src); rcnt = 0; mcnt = 0; - SLIST_FOREACH(cce, &c->children, link) { - ch = cce->channel; + CHN_FOREACH(ch, c, children.busy) { CHN_LOCK(ch); if (!(ch->flags & CHN_F_TRIGGERED)) { CHN_UNLOCK(ch); continue; } if ((ch->flags & CHN_F_MAPPED) && !(ch->flags & CHN_F_CLOSING)) sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft)); if (rcnt == 0) { rcnt = FEEDER_FEED(ch->feeder, ch, b, count, ch->bufsoft); rcnt -= rcnt % sz; mcnt = count - rcnt; } else { cnt = FEEDER_FEED(ch->feeder, ch, tmp, count, ch->bufsoft); cnt -= cnt % sz; if (cnt != 0) { if (mcnt != 0) { memset(b + rcnt, sndbuf_zerodata(f->desc->out), mcnt); mcnt = 0; } cnt = info->mix(b, tmp, cnt); if (cnt > rcnt) rcnt = cnt; } } CHN_UNLOCK(ch); } if (++c->feedcount == 0) c->feedcount = 2; - return rcnt; + return (rcnt); } static struct pcm_feederdesc feeder_vchan_desc[] = { {FEEDER_MIXER, AFMT_S8, AFMT_S8, 0}, {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0}, {FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0}, {FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0}, {FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0}, {FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0}, {FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0}, {FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_U8, AFMT_U8, 0}, {FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0}, {FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0}, {FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0}, {FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0}, {FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0}, {FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0}, {FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, {FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, {0, 0, 0, 0}, }; static kobj_method_t feeder_vchan_methods[] = { KOBJMETHOD(feeder_init, feed_vchan_init), KOBJMETHOD(feeder_feed, feed_vchan), {0, 0} }; FEEDER_DECLARE(feeder_vchan, 2, NULL); /************************************************************/ static void * -vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) { struct vchinfo *ch; - struct pcm_channel *parent = devinfo; - KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction")); + KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC, + ("vchan_init: bad direction")); + KASSERT(c != NULL && c->parentchannel != NULL, + ("vchan_init: bad channels")); + ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); - ch->parent = parent; ch->channel = c; - ch->fmt = AFMT_U8; - ch->spd = DSP_DEFAULT_SPEED; - ch->blksz = 2048; + ch->trigger = PCMTRIG_STOP; c->flags |= CHN_F_VIRTUAL; - return ch; + return (ch); } static int vchan_free(kobj_t obj, void *data) { free(data, M_DEVBUF); - return 0; + + return (0); } static int vchan_setformat(kobj_t obj, void *data, uint32_t format) { struct vchinfo *ch = data; - struct pcm_channel *parent = ch->parent; - struct pcm_channel *channel = ch->channel; - ch->fmt = format; - ch->bps = 1; - ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0; - if (ch->fmt & AFMT_16BIT) - ch->bps <<= 1; - else if (ch->fmt & AFMT_24BIT) - ch->bps *= 3; - else if (ch->fmt & AFMT_32BIT) - ch->bps <<= 2; - CHN_UNLOCK(channel); - chn_notify(parent, CHN_N_FORMAT); - CHN_LOCK(channel); - sndbuf_setfmt(channel->bufsoft, format); - return 0; + if (fmtvalid(format, ch->fmtlist) == 0) + return (-1); + + return (0); } static int vchan_setspeed(kobj_t obj, void *data, uint32_t speed) { struct vchinfo *ch = data; - struct pcm_channel *parent = ch->parent; - struct pcm_channel *channel = ch->channel; + struct pcm_channel *p = ch->channel->parentchannel; - ch->spd = speed; - CHN_UNLOCK(channel); - CHN_LOCK(parent); - speed = sndbuf_getspd(parent->bufsoft); - CHN_UNLOCK(parent); - CHN_LOCK(channel); - return speed; + return (sndbuf_getspd(p->bufsoft)); } static int -vchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) +vchan_trigger(kobj_t obj, void *data, int go) { struct vchinfo *ch = data; - struct pcm_channel *channel = ch->channel; - struct pcm_channel *parent = ch->parent; - /* struct pcm_channel *channel = ch->channel; */ - int prate, crate; + struct pcm_channel *c, *p; + int otrigger; - ch->blksz = blocksize; - /* CHN_UNLOCK(channel); */ - sndbuf_setblksz(channel->bufhard, blocksize); - chn_notify(parent, CHN_N_BLOCKSIZE); - CHN_LOCK(parent); - /* CHN_LOCK(channel); */ + if (!(go == PCMTRIG_START || go == PCMTRIG_STOP || + go == PCMTRIG_ABORT) || go == ch->trigger) + return (0); - crate = ch->spd * ch->bps; - prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft); - blocksize = sndbuf_getblksz(parent->bufsoft); - CHN_UNLOCK(parent); - blocksize *= prate; - blocksize /= crate; - blocksize += ch->bps; - prate = 0; - while (blocksize >> prate) - prate++; - blocksize = 1 << (prate - 1); - blocksize -= blocksize % ch->bps; - /* XXX screwed !@#$ */ - if (blocksize < ch->bps) - blocksize = 4096 - (4096 % ch->bps); + c = ch->channel; + p = c->parentchannel; + otrigger = ch->trigger; + ch->trigger = go; - return blocksize; -} + CHN_UNLOCK(c); + CHN_LOCK(p); -static int -vchan_trigger(kobj_t obj, void *data, int go) -{ - struct vchinfo *ch = data; - struct pcm_channel *parent = ch->parent; - struct pcm_channel *channel = ch->channel; + switch (go) { + case PCMTRIG_START: + if (otrigger != PCMTRIG_START) { + CHN_INSERT_HEAD(p, c, children.busy); + } + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + if (otrigger == PCMTRIG_START) { + CHN_REMOVE(p, c, children.busy); + } + break; + default: + break; + } - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) - return 0; + CHN_UNLOCK(p); + chn_notify(p, CHN_N_TRIGGER); + CHN_LOCK(c); - ch->run = (go == PCMTRIG_START)? 1 : 0; - CHN_UNLOCK(channel); - chn_notify(parent, CHN_N_TRIGGER); - CHN_LOCK(channel); - - return 0; + return (0); } static struct pcmchan_caps * vchan_getcaps(kobj_t obj, void *data) { struct vchinfo *ch = data; + struct pcm_channel *c, *p; uint32_t fmt; - ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft); + c = ch->channel; + p = c->parentchannel; + ch->caps.minspeed = sndbuf_getspd(p->bufsoft); ch->caps.maxspeed = ch->caps.minspeed; ch->caps.caps = 0; - ch->fmts[1] = 0; - fmt = sndbuf_getfmt(ch->parent->bufsoft); + ch->fmtlist[1] = 0; + fmt = sndbuf_getfmt(p->bufsoft); if (fmt != vchan_valid_format(fmt)) { - device_printf(ch->parent->dev, + device_printf(c->dev, "%s: WARNING: invalid vchan format! (0x%08x)\n", __func__, fmt); fmt = VCHAN_DEFAULT_AFMT; } - ch->fmts[0] = fmt; - ch->caps.fmtlist = ch->fmts; + ch->fmtlist[0] = fmt; + ch->caps.fmtlist = ch->fmtlist; - return &ch->caps; + return (&ch->caps); } static kobj_method_t vchan_methods[] = { KOBJMETHOD(channel_init, vchan_init), KOBJMETHOD(channel_free, vchan_free), KOBJMETHOD(channel_setformat, vchan_setformat), KOBJMETHOD(channel_setspeed, vchan_setspeed), - KOBJMETHOD(channel_setblocksize, vchan_setblocksize), KOBJMETHOD(channel_trigger, vchan_trigger), KOBJMETHOD(channel_getcaps, vchan_getcaps), {0, 0} }; CHANNEL_DECLARE(vchan); /* * On the fly vchan rate settings */ #ifdef SND_DYNSYSCTL static int sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; - struct snddev_channel *sce; - struct pcm_channel *c, *ch = NULL, *fake; + struct pcm_channel *c, *ch = NULL; struct pcmchan_caps *caps; + int vchancount, *vchanrate; + int direction; int err = 0; int newspd = 0; - d = oidp->oid_arg1; - if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) - return EINVAL; + d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); + if (d == NULL || !(d->flags & SD_F_AUTOVCHAN)) + return (EINVAL); + + switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { + case VCHAN_PLAY: + direction = PCMDIR_PLAY; + vchancount = d->pvchancount; + vchanrate = &d->pvchanrate; + break; + case VCHAN_REC: + direction = PCMDIR_REC; + vchancount = d->rvchancount; + vchanrate = &d->rvchanrate; + break; + default: + return (EINVAL); + break; + } + + if (vchancount < 1) + return (EINVAL); if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { pcm_inprog(d, -1); - return EINPROGRESS; + return (EINPROGRESS); } - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; + CHN_FOREACH(c, d, channels.pcm) { CHN_LOCK(c); - if (c->direction == PCMDIR_PLAY) { + if (c->direction == direction) { if (c->flags & CHN_F_VIRTUAL) { /* Sanity check */ if (ch != NULL && ch != c->parentchannel) { CHN_UNLOCK(c); pcm_inprog(d, -1); - return EINVAL; + return (EINVAL); } if (req->newptr != NULL && (c->flags & CHN_F_BUSY)) { CHN_UNLOCK(c); pcm_inprog(d, -1); - return EBUSY; + return (EBUSY); } } else if (c->flags & CHN_F_HAS_VCHAN) { /* No way!! */ if (ch != NULL) { CHN_UNLOCK(c); pcm_inprog(d, -1); - return EINVAL; + return (EINVAL); } ch = c; newspd = ch->speed; } } CHN_UNLOCK(c); } if (ch == NULL) { pcm_inprog(d, -1); - return EINVAL; + return (EINVAL); } err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req); if (err == 0 && req->newptr != NULL) { if (newspd < 1 || newspd < feeder_rate_min || - newspd > feeder_rate_max) { + newspd > feeder_rate_max) { pcm_inprog(d, -1); - return EINVAL; + return (EINVAL); } CHN_LOCK(ch); if (feeder_rate_round) { caps = chn_getcaps(ch); if (caps == NULL || newspd < caps->minspeed || - newspd > caps->maxspeed) { + newspd > caps->maxspeed) { CHN_UNLOCK(ch); pcm_inprog(d, -1); - return EINVAL; + return (EINVAL); } } if (newspd != ch->speed) { err = chn_setspeed(ch, newspd); /* * Try to avoid FEEDER_RATE on parent channel if the * requested value is not supported by the hardware. */ if (!err && feeder_rate_round && - (ch->feederflags & (1 << FEEDER_RATE))) { + (ch->feederflags & (1 << FEEDER_RATE))) { newspd = sndbuf_getspd(ch->bufhard); err = chn_setspeed(ch, newspd); } CHN_UNLOCK(ch); if (err == 0) { - fake = pcm_getfakechan(d); - if (fake != NULL) { - CHN_LOCK(fake); - fake->speed = newspd; - CHN_UNLOCK(fake); - } + pcm_lock(d); + *vchanrate = newspd; + pcm_unlock(d); } } else CHN_UNLOCK(ch); } pcm_inprog(d, -1); - return err; + return (err); } static int sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; - struct snddev_channel *sce; - struct pcm_channel *c, *ch = NULL, *fake; + struct pcm_channel *c, *ch = NULL; uint32_t newfmt, spd; - char fmtstr[AFMTSTR_MAXSZ]; + int vchancount, *vchanformat; + int direction; int err = 0, i; + char fmtstr[AFMTSTR_MAXSZ]; - d = oidp->oid_arg1; - if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) - return EINVAL; + d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); + if (d == NULL || !(d->flags & SD_F_AUTOVCHAN)) + return (EINVAL); + + switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { + case VCHAN_PLAY: + direction = PCMDIR_PLAY; + vchancount = d->pvchancount; + vchanformat = &d->pvchanformat; + break; + case VCHAN_REC: + direction = PCMDIR_REC; + vchancount = d->rvchancount; + vchanformat = &d->rvchanformat; + break; + default: + return (EINVAL); + break; + } + + if (vchancount < 1) + return (EINVAL); if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { pcm_inprog(d, -1); - return EINPROGRESS; + return (EINPROGRESS); } - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; + CHN_FOREACH(c, d, channels.pcm) { CHN_LOCK(c); - if (c->direction == PCMDIR_PLAY) { + if (c->direction == direction) { if (c->flags & CHN_F_VIRTUAL) { /* Sanity check */ if (ch != NULL && ch != c->parentchannel) { CHN_UNLOCK(c); pcm_inprog(d, -1); - return EINVAL; + return (EINVAL); } if (req->newptr != NULL && (c->flags & CHN_F_BUSY)) { CHN_UNLOCK(c); pcm_inprog(d, -1); - return EBUSY; + return (EBUSY); } } else if (c->flags & CHN_F_HAS_VCHAN) { /* No way!! */ if (ch != NULL) { CHN_UNLOCK(c); pcm_inprog(d, -1); - return EINVAL; + return (EINVAL); } ch = c; - if (ch->format != afmt2afmtstr(vchan_supported_fmts, - ch->format, fmtstr, sizeof(fmtstr), - AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) { - strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, sizeof(fmtstr)); + if (ch->format != + afmt2afmtstr(vchan_supported_fmts, + ch->format, fmtstr, sizeof(fmtstr), + AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) { + strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, + sizeof(fmtstr)); } } } CHN_UNLOCK(c); } if (ch == NULL) { pcm_inprog(d, -1); - return EINVAL; + return (EINVAL); } err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); if (err == 0 && req->newptr != NULL) { for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) { if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) { - strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, sizeof(fmtstr)); + strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, + sizeof(fmtstr)); break; } } newfmt = vchan_valid_strformat(fmtstr); if (newfmt == 0) { pcm_inprog(d, -1); - return EINVAL; + return (EINVAL); } CHN_LOCK(ch); if (newfmt != ch->format) { /* Get channel speed, before chn_reset() screw it. */ spd = ch->speed; err = chn_reset(ch, newfmt); if (err == 0) err = chn_setspeed(ch, spd); CHN_UNLOCK(ch); if (err == 0) { - fake = pcm_getfakechan(d); - if (fake != NULL) { - CHN_LOCK(fake); - fake->format = newfmt; - CHN_UNLOCK(fake); - } + pcm_lock(d); + *vchanformat = newfmt; + pcm_unlock(d); } } else CHN_UNLOCK(ch); } pcm_inprog(d, -1); - return err; + return (err); } #endif /* virtual channel interface */ +#define VCHAN_FMT_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ + "play.vchanformat" : "rec.vchanformat" +#define VCHAN_SPD_HINT(x) ((x) == PCMDIR_PLAY_VIRTUAL) ? \ + "play.vchanrate" : "rec.vchanrate" + int -vchan_create(struct pcm_channel *parent) +vchan_create(struct pcm_channel *parent, int num) { struct snddev_info *d = parent->parentsnddev; - struct pcmchan_children *pce; - struct pcm_channel *child, *fake; + struct pcm_channel *ch, *tmp, *after; struct pcmchan_caps *parent_caps; - uint32_t vchanfmt = 0; - int err, first, speed = 0, r; + uint32_t vchanfmt; + int err, first, speed, r; + int direction; if (!(parent->flags & CHN_F_BUSY)) - return EBUSY; + return (EBUSY); - + if (parent->direction == PCMDIR_PLAY) { + direction = PCMDIR_PLAY_VIRTUAL; + vchanfmt = d->pvchanformat; + speed = d->pvchanrate; + } else if (parent->direction == PCMDIR_REC) { + direction = PCMDIR_REC_VIRTUAL; + vchanfmt = d->rvchanformat; + speed = d->rvchanrate; + } else + return (EINVAL); CHN_UNLOCK(parent); - pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); - /* create a new playback channel */ - child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); - if (!child) { - free(pce, M_DEVBUF); + ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent); + if (ch == NULL) { CHN_LOCK(parent); - return ENODEV; + return (ENODEV); } - pce->channel = child; /* add us to our grandparent's channel list */ - /* - * XXX maybe we shouldn't always add the dev_t - */ - err = pcm_chn_add(d, child); + err = pcm_chn_add(d, ch); if (err) { - pcm_chn_destroy(child); - free(pce, M_DEVBUF); + pcm_chn_destroy(ch); CHN_LOCK(parent); - return err; + return (err); } CHN_LOCK(parent); /* add us to our parent channel's children */ - first = SLIST_EMPTY(&parent->children); - SLIST_INSERT_HEAD(&parent->children, pce, link); + first = CHN_EMPTY(parent, children); + after = NULL; + CHN_FOREACH(tmp, parent, children) { + if (CHN_CHAN(tmp) > CHN_CHAN(ch)) + after = tmp; + else if (CHN_CHAN(tmp) < CHN_CHAN(ch)) + break; + } + if (after != NULL) { + CHN_INSERT_AFTER(after, ch, children); + } else { + CHN_INSERT_HEAD(parent, ch, children); + } parent->flags |= CHN_F_HAS_VCHAN; if (first) { parent_caps = chn_getcaps(parent); if (parent_caps == NULL) err = EINVAL; - fake = pcm_getfakechan(d); - - if (!err && fake != NULL) { - /* - * Avoid querying kernel hint, use saved value - * from fake channel. - */ - CHN_UNLOCK(parent); - CHN_LOCK(fake); - speed = fake->speed; - vchanfmt = fake->format; - CHN_UNLOCK(fake); - CHN_LOCK(parent); - } - if (!err) { if (vchanfmt == 0) { const char *vfmt; CHN_UNLOCK(parent); - r = resource_string_value(device_get_name(parent->dev), - device_get_unit(parent->dev), - "vchanformat", &vfmt); + r = resource_string_value( + device_get_name(parent->dev), + device_get_unit(parent->dev), + VCHAN_FMT_HINT(direction), + &vfmt); CHN_LOCK(parent); if (r != 0) vfmt = NULL; if (vfmt != NULL) { vchanfmt = vchan_valid_strformat(vfmt); for (r = 0; vchanfmt == 0 && - vchan_fmtstralias[r].alias != NULL; - r++) { + vchan_fmtstralias[r].alias != NULL; + r++) { if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) { vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr); break; } } } if (vchanfmt == 0) vchanfmt = VCHAN_DEFAULT_AFMT; } err = chn_reset(parent, vchanfmt); } if (!err) { /* * This is very sad. Few soundcards advertised as being * able to do (insanely) higher/lower speed, but in * reality, they simply can't. At least, we give user chance * to set sane value via kernel hints or sysctl. */ if (speed < 1) { CHN_UNLOCK(parent); - r = resource_int_value(device_get_name(parent->dev), - device_get_unit(parent->dev), - "vchanrate", &speed); + r = resource_int_value( + device_get_name(parent->dev), + device_get_unit(parent->dev), + VCHAN_SPD_HINT(direction), + &speed); CHN_LOCK(parent); if (r != 0) { /* - * No saved value from fake channel, - * no hint, NOTHING. + * No saved value, no hint, NOTHING. * * Workaround for sb16 running * poorly at 45k / 49k. */ switch (parent_caps->maxspeed) { case 45000: case 49000: speed = 44100; break; default: speed = VCHAN_DEFAULT_SPEED; if (speed > parent_caps->maxspeed) speed = parent_caps->maxspeed; break; } if (speed < parent_caps->minspeed) speed = parent_caps->minspeed; } } if (feeder_rate_round) { /* * Limit speed based on driver caps. * This is supposed to help fixed rate, non-VRA * AC97 cards, but.. (see below) */ if (speed < parent_caps->minspeed) speed = parent_caps->minspeed; if (speed > parent_caps->maxspeed) speed = parent_caps->maxspeed; } /* * We still need to limit the speed between * feeder_rate_min <-> feeder_rate_max. This is * just an escape goat if all of the above failed * miserably. */ if (speed < feeder_rate_min) speed = feeder_rate_min; if (speed > feeder_rate_max) speed = feeder_rate_max; err = chn_setspeed(parent, speed); /* * Try to avoid FEEDER_RATE on parent channel if the * requested value is not supported by the hardware. */ if (!err && feeder_rate_round && - (parent->feederflags & (1 << FEEDER_RATE))) { + (parent->feederflags & (1 << FEEDER_RATE))) { speed = sndbuf_getspd(parent->bufhard); err = chn_setspeed(parent, speed); } - if (!err && fake != NULL) { + if (!err) { /* - * Save new value to fake channel. + * Save new value. */ CHN_UNLOCK(parent); - CHN_LOCK(fake); - fake->speed = speed; - fake->format = vchanfmt; - CHN_UNLOCK(fake); + pcm_lock(d); + if (direction == PCMDIR_PLAY_VIRTUAL) { + d->pvchanformat = vchanfmt; + d->pvchanrate = speed; + } else { + d->rvchanformat = vchanfmt; + d->rvchanrate = speed; + } + pcm_unlock(d); CHN_LOCK(parent); } } if (err) { - SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); + CHN_REMOVE(parent, ch, children); parent->flags &= ~CHN_F_HAS_VCHAN; CHN_UNLOCK(parent); - free(pce, M_DEVBUF); - if (pcm_chn_remove(d, child) == 0) - pcm_chn_destroy(child); + pcm_lock(d); + if (pcm_chn_remove(d, ch) == 0) { + pcm_unlock(d); + pcm_chn_destroy(ch); + } else + pcm_unlock(d); CHN_LOCK(parent); - return err; + return (err); } } - return 0; + return (0); } int vchan_destroy(struct pcm_channel *c) { struct pcm_channel *parent = c->parentchannel; struct snddev_info *d = parent->parentsnddev; - struct pcmchan_children *pce; - struct snddev_channel *sce; uint32_t spd; int err; CHN_LOCK(parent); if (!(parent->flags & CHN_F_BUSY)) { CHN_UNLOCK(parent); - return EBUSY; + return (EBUSY); } - if (SLIST_EMPTY(&parent->children)) { + if (CHN_EMPTY(parent, children)) { CHN_UNLOCK(parent); - return EINVAL; + return (EINVAL); } /* remove us from our parent's children list */ - SLIST_FOREACH(pce, &parent->children, link) { - if (pce->channel == c) - goto gotch; - } - CHN_UNLOCK(parent); - return EINVAL; -gotch: - SLIST_FOREACH(sce, &d->channels, link) { - if (sce->channel == c) { - if (sce->dsp_devt) { - destroy_dev(sce->dsp_devt); - sce->dsp_devt = NULL; - } - if (sce->dspW_devt) { - destroy_dev(sce->dspW_devt); - sce->dspW_devt = NULL; - } - if (sce->audio_devt) { - destroy_dev(sce->audio_devt); - sce->audio_devt = NULL; - } - if (sce->dspHW_devt) { - destroy_dev(sce->dspHW_devt); - sce->dspHW_devt = NULL; - } - d->devcount--; - break; - } - } - SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); - free(pce, M_DEVBUF); + CHN_REMOVE(parent, c, children); - if (SLIST_EMPTY(&parent->children)) { + if (CHN_EMPTY(parent, children)) { parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); spd = parent->speed; if (chn_reset(parent, parent->format) == 0) chn_setspeed(parent, spd); } + CHN_UNLOCK(parent); + /* remove us from our grandparent's channel list */ + pcm_lock(d); err = pcm_chn_remove(d, c); + pcm_unlock(d); - CHN_UNLOCK(parent); /* destroy ourselves */ if (!err) err = pcm_chn_destroy(c); - return err; + return (err); } int vchan_initsys(device_t dev) { #ifdef SND_DYNSYSCTL struct snddev_info *d; + int unit; + unit = device_get_unit(dev); d = device_get_softc(dev); - SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), - OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), + + /* Play */ + SYSCTL_ADD_PROC(&d->play_sysctl_ctx, + SYSCTL_CHILDREN(d->play_sysctl_tree), + OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, + VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); - SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), - OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), + SYSCTL_ADD_PROC(&d->play_sysctl_ctx, + SYSCTL_CHILDREN(d->play_sysctl_tree), + OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, + VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate"); - SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), - OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d), + SYSCTL_ADD_PROC(&d->play_sysctl_ctx, + SYSCTL_CHILDREN(d->play_sysctl_tree), + OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, + VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, sysctl_hw_snd_vchanformat, "A", "virtual channel format"); + /* Rec */ + SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, + SYSCTL_CHILDREN(d->rec_sysctl_tree), + OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, + VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, + sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); + SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, + SYSCTL_CHILDREN(d->rec_sysctl_tree), + OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, + VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, + sysctl_hw_snd_vchanrate, "I", "virtual channel base speed/rate"); + SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, + SYSCTL_CHILDREN(d->rec_sysctl_tree), + OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, + VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, + sysctl_hw_snd_vchanformat, "A", "virtual channel format"); #endif - return 0; + return (0); } Index: head/sys/dev/sound/pcm/vchan.h =================================================================== --- head/sys/dev/sound/pcm/vchan.h (revision 170160) +++ head/sys/dev/sound/pcm/vchan.h (revision 170161) @@ -1,33 +1,52 @@ /*- * Copyright (c) 2001 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ -int vchan_create(struct pcm_channel *parent); +int vchan_create(struct pcm_channel *parent, int num); int vchan_destroy(struct pcm_channel *c); int vchan_initsys(device_t dev); +/* + * Default speed / format + */ +#define VCHAN_DEFAULT_SPEED 48000 +#define VCHAN_DEFAULT_AFMT (AFMT_S16_LE | AFMT_STEREO) +#define VCHAN_DEFAULT_STRFMT "s16le" +#define VCHAN_PLAY 0 +#define VCHAN_REC 1 + +/* + * Offset by +/- 1 so we can distinguish bogus pointer. + */ +#define VCHAN_SYSCTL_DATA(x, y) \ + ((void *)((intptr_t)(((((x) + 1) & 0xfff) << 2) | \ + (((VCHAN_##y) + 1) & 0x3)))) + +#define VCHAN_SYSCTL_DATA_SIZE sizeof(void *) +#define VCHAN_SYSCTL_UNIT(x) ((int)(((intptr_t)(x) >> 2) & 0xfff) - 1) +#define VCHAN_SYSCTL_DIR(x) ((int)((intptr_t)(x) & 0x3) - 1) Index: head/sys/dev/sound/usb/uaudio.c =================================================================== --- head/sys/dev/sound/usb/uaudio.c (revision 170160) +++ head/sys/dev/sound/usb/uaudio.c (revision 170161) @@ -1,4675 +1,4662 @@ /* $NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf * http://www.usb.org/developers/devclass_docs/frmts10.pdf * http://www.usb.org/developers/devclass_docs/termt10.pdf */ #include #if defined(__NetBSD__) || defined(__OpenBSD__) __KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $"); #endif /* * Also merged: * $NetBSD: uaudio.c,v 1.94 2005/01/15 15:19:53 kent Exp $ * $NetBSD: uaudio.c,v 1.95 2005/01/16 06:02:19 dsainty Exp $ * $NetBSD: uaudio.c,v 1.96 2005/01/16 12:46:00 kent Exp $ * $NetBSD: uaudio.c,v 1.97 2005/02/24 08:19:38 martin Exp $ * $NetBSD: uaudio.c,v 1.102 2006/04/14 17:00:55 christos Exp $ * $NetBSD: uaudio.c,v 1.103 2006/05/11 19:09:25 mrg Exp $ * $NetBSD: uaudio.c,v 1.105 2006/10/04 16:00:15 christos Exp $ */ #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #endif #include #include #include /* for bootverbose */ #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #elif defined(__FreeBSD__) #include #include #include #endif #include #if defined(__FreeBSD__) #include #include #endif #if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #include #include #include #elif defined(__FreeBSD__) #include /* XXXXX */ #include #include "feeder_if.h" #endif #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #elif defined(__FreeBSD__) #include #include #endif #if defined(__NetBSD__) || defined(__OpenBSD__) /* #define UAUDIO_DEBUG */ #else /* #define USB_DEBUG */ #endif /* #define UAUDIO_MULTIPLE_ENDPOINTS */ #ifdef USB_DEBUG #define DPRINTF(x) do { if (uaudiodebug) logprintf x; } while (0) #define DPRINTFN(n,x) do { if (uaudiodebug>(n)) logprintf x; } while (0) int uaudiodebug = 0; #if defined(__FreeBSD__) SYSCTL_NODE(_hw_usb, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio"); SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, debug, CTLFLAG_RW, &uaudiodebug, 0, "uaudio debug level"); #endif #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UAUDIO_NCHANBUFS 6 /* number of outstanding request */ #if defined(__NetBSD__) || defined(__OpenBSD__) #define UAUDIO_NFRAMES 10 /* ms of sound in each request */ #elif defined(__FreeBSD__) #define UAUDIO_NFRAMES 20 /* ms of sound in each request */ #endif #define MIX_MAX_CHAN 8 struct mixerctl { uint16_t wValue[MIX_MAX_CHAN]; /* using nchan */ uint16_t wIndex; uint8_t nchan; uint8_t type; #define MIX_ON_OFF 1 #define MIX_SIGNED_16 2 #define MIX_UNSIGNED_16 3 #define MIX_SIGNED_8 4 #define MIX_SELECTOR 5 #define MIX_SIZE(n) ((n) == MIX_SIGNED_16 || (n) == MIX_UNSIGNED_16 ? 2 : 1) #define MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16) int minval, maxval; u_int delta; u_int mul; #if defined(__FreeBSD__) /* XXXXX */ unsigned ctl; #define MAX_SELECTOR_INPUT_PIN 256 uint8_t slctrtype[MAX_SELECTOR_INPUT_PIN]; #endif uint8_t class; #if !defined(__FreeBSD__) char ctlname[MAX_AUDIO_DEV_LEN]; char *ctlunit; #endif }; #define MAKE(h,l) (((h) << 8) | (l)) struct as_info { uint8_t alt; uint8_t encoding; uint8_t attributes; /* Copy of bmAttributes of * usb_audio_streaming_endpoint_descriptor */ usbd_interface_handle ifaceh; const usb_interface_descriptor_t *idesc; const usb_endpoint_descriptor_audio_t *edesc; const usb_endpoint_descriptor_audio_t *edesc1; const struct usb_audio_streaming_type1_descriptor *asf1desc; int sc_busy; /* currently used */ }; struct chan { #if defined(__NetBSD__) || defined(__OpenBSD__) void (*intr)(void *); /* DMA completion intr handler */ void *arg; /* arg for intr() */ #else struct pcm_channel *pcm_ch; #endif usbd_pipe_handle pipe; usbd_pipe_handle sync_pipe; u_int sample_size; u_int sample_rate; u_int bytes_per_frame; u_int fraction; /* fraction/1000 is the extra samples/frame */ u_int residue; /* accumulates the fractional samples */ u_char *start; /* upper layer buffer start */ u_char *end; /* upper layer buffer end */ u_char *cur; /* current position in upper layer buffer */ int blksize; /* chunk size to report up */ int transferred; /* transferred bytes not reported up */ int altidx; /* currently used altidx */ int curchanbuf; struct chanbuf { struct chan *chan; usbd_xfer_handle xfer; u_char *buffer; u_int16_t sizes[UAUDIO_NFRAMES]; u_int16_t offsets[UAUDIO_NFRAMES]; u_int16_t size; } chanbufs[UAUDIO_NCHANBUFS]; struct uaudio_softc *sc; /* our softc */ #if defined(__FreeBSD__) u_int32_t format; int precision; int channels; #endif }; struct uaudio_softc { device_t sc_dev; /* base device */ usbd_device_handle sc_udev; /* USB device */ int sc_ac_iface; /* Audio Control interface */ usbd_interface_handle sc_ac_ifaceh; struct chan sc_playchan; /* play channel */ struct chan sc_recchan; /* record channel */ int sc_nullalt; int sc_audio_rev; struct as_info *sc_alts; /* alternate settings */ int sc_nalts; /* # of alternate settings */ int sc_altflags; #define HAS_8 0x01 #define HAS_16 0x02 #define HAS_8U 0x04 #define HAS_ALAW 0x08 #define HAS_MULAW 0x10 #define UA_NOFRAC 0x20 /* don't do sample rate adjustment */ #define HAS_24 0x40 #define HAS_32 0x80 int sc_mode; /* play/record capability */ struct mixerctl *sc_ctls; /* mixer controls */ int sc_nctls; /* # of mixer controls */ device_t sc_audiodev; char sc_dying; #if defined(__FreeBSD__) struct sbuf uaudio_sndstat; int uaudio_sndstat_flag; int async; #endif int sc_vendor; int sc_product; int sc_release; }; struct terminal_list { int size; uint16_t terminals[1]; }; #define TERMINAL_LIST_SIZE(N) (offsetof(struct terminal_list, terminals) \ + sizeof(uint16_t) * (N)) struct io_terminal { union { const usb_descriptor_t *desc; const struct usb_audio_input_terminal *it; const struct usb_audio_output_terminal *ot; const struct usb_audio_mixer_unit *mu; const struct usb_audio_selector_unit *su; const struct usb_audio_feature_unit *fu; const struct usb_audio_processing_unit *pu; const struct usb_audio_extension_unit *eu; } d; int inputs_size; struct terminal_list **inputs; /* list of source input terminals */ struct terminal_list *output; /* list of destination output terminals */ int direct; /* directly connected to an output terminal */ }; #define UAC_OUTPUT 0 #define UAC_INPUT 1 #define UAC_EQUAL 2 #define UAC_RECORD 3 #define UAC_NCLASSES 4 #ifdef USB_DEBUG #if defined(__FreeBSD__) #define AudioCinputs "inputs" #define AudioCoutputs "outputs" #define AudioCrecord "record" #define AudioCequalization "equalization" #endif static const char *uac_names[] = { AudioCoutputs, AudioCinputs, AudioCequalization, AudioCrecord, }; #endif static usbd_status uaudio_identify_ac (struct uaudio_softc *, const usb_config_descriptor_t *); static usbd_status uaudio_identify_as (struct uaudio_softc *, const usb_config_descriptor_t *); static usbd_status uaudio_process_as (struct uaudio_softc *, const char *, int *, int, const usb_interface_descriptor_t *); static void uaudio_add_alt(struct uaudio_softc *, const struct as_info *); static const usb_interface_descriptor_t *uaudio_find_iface (const char *, int, int *, int); static void uaudio_mixer_add_ctl(struct uaudio_softc *, struct mixerctl *); #if defined(__NetBSD__) || defined(__OpenBSD__) static char *uaudio_id_name (struct uaudio_softc *, const struct io_terminal *, int); #endif #ifdef USB_DEBUG static void uaudio_dump_cluster(const struct usb_audio_cluster *); #endif static struct usb_audio_cluster uaudio_get_cluster (int, const struct io_terminal *); static void uaudio_add_input (struct uaudio_softc *, const struct io_terminal *, int); static void uaudio_add_output (struct uaudio_softc *, const struct io_terminal *, int); static void uaudio_add_mixer (struct uaudio_softc *, const struct io_terminal *, int); static void uaudio_add_selector (struct uaudio_softc *, const struct io_terminal *, int); #ifdef USB_DEBUG static const char *uaudio_get_terminal_name(int); #endif static int uaudio_determine_class (const struct io_terminal *, struct mixerctl *); #if defined(__FreeBSD__) static const int uaudio_feature_name(const struct io_terminal *, struct mixerctl *); #else static const char *uaudio_feature_name (const struct io_terminal *, struct mixerctl *); #endif static void uaudio_add_feature (struct uaudio_softc *, const struct io_terminal *, int); static void uaudio_add_processing_updown (struct uaudio_softc *, const struct io_terminal *, int); static void uaudio_add_processing (struct uaudio_softc *, const struct io_terminal *, int); static void uaudio_add_extension (struct uaudio_softc *, const struct io_terminal *, int); static struct terminal_list *uaudio_merge_terminal_list (const struct io_terminal *); static struct terminal_list *uaudio_io_terminaltype (int, struct io_terminal *, int); static usbd_status uaudio_identify (struct uaudio_softc *, const usb_config_descriptor_t *); static int uaudio_signext(int, int); #if defined(__NetBSD__) || defined(__OpenBSD__) static int uaudio_value2bsd(struct mixerctl *, int); #endif static int uaudio_bsd2value(struct mixerctl *, int); static int uaudio_get(struct uaudio_softc *, int, int, int, int, int); #if defined(__NetBSD__) || defined(__OpenBSD__) static int uaudio_ctl_get (struct uaudio_softc *, int, struct mixerctl *, int); #endif static void uaudio_set (struct uaudio_softc *, int, int, int, int, int, int); static void uaudio_ctl_set (struct uaudio_softc *, int, struct mixerctl *, int, int); static usbd_status uaudio_set_speed(struct uaudio_softc *, int, u_int); static usbd_status uaudio_chan_open(struct uaudio_softc *, struct chan *); static void uaudio_chan_close(struct uaudio_softc *, struct chan *); static usbd_status uaudio_chan_alloc_buffers (struct uaudio_softc *, struct chan *); static void uaudio_chan_free_buffers(struct uaudio_softc *, struct chan *); #if defined(__NetBSD__) || defined(__OpenBSD__) static void uaudio_chan_init (struct chan *, int, const struct audio_params *, int); static void uaudio_chan_set_param(struct chan *, u_char *, u_char *, int); #endif static void uaudio_chan_ptransfer(struct chan *); static void uaudio_chan_pintr (usbd_xfer_handle, usbd_private_handle, usbd_status); static void uaudio_chan_rtransfer(struct chan *); static void uaudio_chan_rintr (usbd_xfer_handle, usbd_private_handle, usbd_status); #if defined(__NetBSD__) || defined(__OpenBSD__) static int uaudio_open(void *, int); static void uaudio_close(void *); static int uaudio_drain(void *); static int uaudio_query_encoding(void *, struct audio_encoding *); static void uaudio_get_minmax_rates (int, const struct as_info *, const struct audio_params *, int, u_long *, u_long *); static int uaudio_match_alt_sub (int, const struct as_info *, const struct audio_params *, int, u_long); static int uaudio_match_alt_chan (int, const struct as_info *, struct audio_params *, int); static int uaudio_match_alt (int, const struct as_info *, struct audio_params *, int); static int uaudio_set_params (void *, int, int, struct audio_params *, struct audio_params *); static int uaudio_round_blocksize(void *, int); static int uaudio_trigger_output (void *, void *, void *, int, void (*)(void *), void *, struct audio_params *); static int uaudio_trigger_input (void *, void *, void *, int, void (*)(void *), void *, struct audio_params *); static int uaudio_halt_in_dma(void *); static int uaudio_halt_out_dma(void *); static int uaudio_getdev(void *, struct audio_device *); static int uaudio_mixer_set_port(void *, mixer_ctrl_t *); static int uaudio_mixer_get_port(void *, mixer_ctrl_t *); static int uaudio_query_devinfo(void *, mixer_devinfo_t *); static int uaudio_get_props(void *); static const struct audio_hw_if uaudio_hw_if = { uaudio_open, uaudio_close, uaudio_drain, uaudio_query_encoding, uaudio_set_params, uaudio_round_blocksize, NULL, NULL, NULL, NULL, NULL, uaudio_halt_out_dma, uaudio_halt_in_dma, NULL, uaudio_getdev, NULL, uaudio_mixer_set_port, uaudio_mixer_get_port, uaudio_query_devinfo, NULL, NULL, NULL, NULL, uaudio_get_props, uaudio_trigger_output, uaudio_trigger_input, NULL, }; static struct audio_device uaudio_device = { "USB audio", "", "uaudio" }; #elif defined(__FreeBSD__) static int audio_attach_mi(device_t); static int uaudio_init_params(struct uaudio_softc * sc, struct chan *ch, int mode); static int uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); /* for NetBSD compatibirity */ #define AUMODE_PLAY 0x01 #define AUMODE_RECORD 0x02 #define AUDIO_PROP_FULLDUPLEX 0x01 #define AUDIO_ENCODING_ULAW 1 #define AUDIO_ENCODING_ALAW 2 #define AUDIO_ENCODING_SLINEAR_LE 6 #define AUDIO_ENCODING_SLINEAR_BE 7 #define AUDIO_ENCODING_ULINEAR_LE 8 #define AUDIO_ENCODING_ULINEAR_BE 9 #endif /* FreeBSD */ #if defined(__NetBSD__) || defined(__OpenBSD__) USB_DECLARE_DRIVER(uaudio); #elif defined(__FreeBSD__) USB_DECLARE_DRIVER_INIT(uaudio, DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(bus_print_child, bus_generic_print_child) ); #endif USB_MATCH(uaudio) { USB_MATCH_START(uaudio, uaa); usb_interface_descriptor_t *id; if (uaa->iface == NULL) return UMATCH_NONE; id = usbd_get_interface_descriptor(uaa->iface); /* Trigger on the control interface. */ if (id == NULL || id->bInterfaceClass != UICLASS_AUDIO || id->bInterfaceSubClass != UISUBCLASS_AUDIOCONTROL || (usbd_get_quirks(uaa->device)->uq_flags & UQ_BAD_AUDIO)) return UMATCH_NONE; return UMATCH_IFACECLASS_IFACESUBCLASS; } USB_ATTACH(uaudio) { USB_ATTACH_START(uaudio, sc, uaa); usb_interface_descriptor_t *id; usb_config_descriptor_t *cdesc; char devinfo[1024]; usbd_status err; int i, j, found; #if defined(__FreeBSD__) usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; #else usbd_devinfo(uaa->device, 0, devinfo, sizeof(devinfo)); printf(": %s\n", devinfo); #endif sc->sc_udev = uaa->device; sc->sc_vendor = uaa->vendor; sc->sc_product = uaa->product; sc->sc_release = uaa->release; #if defined(__FreeBSD__) if (resource_int_value(device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev), "async", &i) == 0 && i != 0) sc->async = 1; else sc->async = 0; #endif cdesc = usbd_get_config_descriptor(sc->sc_udev); if (cdesc == NULL) { printf("%s: failed to get configuration descriptor\n", device_get_nameunit(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } err = uaudio_identify(sc, cdesc); if (err) { printf("%s: audio descriptors make no sense, error=%d\n", device_get_nameunit(sc->sc_dev), err); USB_ATTACH_ERROR_RETURN; } sc->sc_ac_ifaceh = uaa->iface; /* Pick up the AS interface. */ for (i = 0; i < uaa->nifaces; i++) { if (uaa->ifaces[i] == NULL) continue; id = usbd_get_interface_descriptor(uaa->ifaces[i]); if (id == NULL) continue; found = 0; for (j = 0; j < sc->sc_nalts; j++) { if (id->bInterfaceNumber == sc->sc_alts[j].idesc->bInterfaceNumber) { sc->sc_alts[j].ifaceh = uaa->ifaces[i]; found = 1; } } if (found) uaa->ifaces[i] = NULL; } for (j = 0; j < sc->sc_nalts; j++) { if (sc->sc_alts[j].ifaceh == NULL) { printf("%s: alt %d missing AS interface(s)\n", device_get_nameunit(sc->sc_dev), j); USB_ATTACH_ERROR_RETURN; } } printf("%s: audio rev %d.%02x\n", device_get_nameunit(sc->sc_dev), sc->sc_audio_rev >> 8, sc->sc_audio_rev & 0xff); sc->sc_playchan.sc = sc->sc_recchan.sc = sc; sc->sc_playchan.altidx = -1; sc->sc_recchan.altidx = -1; if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_FRAC) sc->sc_altflags |= UA_NOFRAC; #ifndef USB_DEBUG if (bootverbose) #endif printf("%s: %d mixer controls\n", device_get_nameunit(sc->sc_dev), sc->sc_nctls); #if !defined(__FreeBSD__) usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, USBDEV(sc->sc_dev)); #endif DPRINTF(("uaudio_attach: doing audio_attach_mi\n")); #if defined(__OpenBSD__) audio_attach_mi(&uaudio_hw_if, sc, &sc->sc_dev); #elif defined(__NetBSD__) sc->sc_audiodev = audio_attach_mi(&uaudio_hw_if, sc, &sc->sc_dev); #elif defined(__FreeBSD__) sc->sc_dying = 0; if (audio_attach_mi(sc->sc_dev)) { printf("audio_attach_mi failed\n"); USB_ATTACH_ERROR_RETURN; } #endif #if defined(__FreeBSD__) SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->sc_dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), OID_AUTO, "async", CTLFLAG_RW, &sc->async, 0, "Asynchronous USB request"); #endif USB_ATTACH_SUCCESS_RETURN; } #if defined(__NetBSD__) || defined(__OpenBSD__) int uaudio_activate(device_t self, enum devact act) { struct uaudio_softc *sc; int rv; sc = (struct uaudio_softc *)self; rv = 0; switch (act) { case DVACT_ACTIVATE: return EOPNOTSUPP; case DVACT_DEACTIVATE: if (sc->sc_audiodev != NULL) rv = config_deactivate(sc->sc_audiodev); sc->sc_dying = 1; break; } return rv; } #endif #if defined(__NetBSD__) || defined(__OpenBSD__) int uaudio_detach(device_t self, int flags) { struct uaudio_softc *sc; int rv; sc = (struct uaudio_softc *)self; rv = 0; /* Wait for outstanding requests to complete. */ usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES); if (sc->sc_audiodev != NULL) rv = config_detach(sc->sc_audiodev, flags); usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, USBDEV(sc->sc_dev)); return rv; } #elif defined(__FreeBSD__) USB_DETACH(uaudio) { struct sndcard_func *func; device_t *devlist = NULL; int err, i, devcount; USB_DETACH_START(uaudio, sc); sbuf_delete(&(sc->uaudio_sndstat)); sc->uaudio_sndstat_flag = 0; sc->sc_dying = 1; #if 0 /* XXX */ /* Wait for outstanding requests to complete. */ usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES); #endif err = bus_generic_detach(sc->sc_dev); if (err == 0) { device_get_children(sc->sc_dev, &devlist, &devcount); for (i = 0; devlist != NULL && i < devcount; i++) { func = device_get_ivars(devlist[i]); if (func != NULL && func->func == SCF_PCM && func->varinfo == NULL) { device_set_ivars(devlist[i], NULL); free(func, M_DEVBUF); device_delete_child(sc->sc_dev, devlist[i]); } } if (devlist != NULL) free(devlist, M_TEMP); } return err; } #endif #if defined(__NetBSD__) || defined(__OpenBSD__) static int uaudio_query_encoding(void *addr, struct audio_encoding *fp) { struct uaudio_softc *sc; int flags; int idx; sc = addr; flags = sc->sc_altflags; if (sc->sc_dying) return EIO; if (sc->sc_nalts == 0 || flags == 0) return ENXIO; idx = fp->index; switch (idx) { case 0: strlcpy(fp->name, AudioEulinear, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_ULINEAR; fp->precision = 8; fp->flags = flags&HAS_8U ? 0 : AUDIO_ENCODINGFLAG_EMULATED; return (0); case 1: strlcpy(fp->name, AudioEmulaw, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_ULAW; fp->precision = 8; fp->flags = flags&HAS_MULAW ? 0 : AUDIO_ENCODINGFLAG_EMULATED; return (0); case 2: strlcpy(fp->name, AudioEalaw, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_ALAW; fp->precision = 8; fp->flags = flags&HAS_ALAW ? 0 : AUDIO_ENCODINGFLAG_EMULATED; return (0); case 3: strlcpy(fp->name, AudioEslinear, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_SLINEAR; fp->precision = 8; fp->flags = flags&HAS_8 ? 0 : AUDIO_ENCODINGFLAG_EMULATED; return (0); case 4: strlcpy(fp->name, AudioEslinear_le, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_SLINEAR_LE; fp->precision = 16; fp->flags = 0; return (0); case 5: strlcpy(fp->name, AudioEulinear_le, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_ULINEAR_LE; fp->precision = 16; fp->flags = AUDIO_ENCODINGFLAG_EMULATED; return (0); case 6: strlcpy(fp->name, AudioEslinear_be, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_SLINEAR_BE; fp->precision = 16; fp->flags = AUDIO_ENCODINGFLAG_EMULATED; return (0); case 7: strlcpy(fp->name, AudioEulinear_be, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_ULINEAR_BE; fp->precision = 16; fp->flags = AUDIO_ENCODINGFLAG_EMULATED; return (0); default: return (EINVAL); } } #endif static const usb_interface_descriptor_t * uaudio_find_iface(const char *buf, int size, int *offsp, int subtype) { const usb_interface_descriptor_t *d; while (*offsp < size) { d = (const void *)(buf + *offsp); *offsp += d->bLength; if (d->bDescriptorType == UDESC_INTERFACE && d->bInterfaceClass == UICLASS_AUDIO && d->bInterfaceSubClass == subtype) return d; } return NULL; } static void uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc) { int res; size_t len; struct mixerctl *nmc; #if defined(__FreeBSD__) if (mc->class < UAC_NCLASSES) { DPRINTF(("%s: adding %s.%d\n", __func__, uac_names[mc->class], mc->ctl)); } else { DPRINTF(("%s: adding %d\n", __func__, mc->ctl)); } #else if (mc->class < UAC_NCLASSES) { DPRINTF(("%s: adding %s.%s\n", __func__, uac_names[mc->class], mc->ctlname)); } else { DPRINTF(("%s: adding %s\n", __func__, mc->ctlname)); } #endif len = sizeof(*mc) * (sc->sc_nctls + 1); nmc = malloc(len, M_USBDEV, M_NOWAIT); if (nmc == NULL) { printf("uaudio_mixer_add_ctl: no memory\n"); return; } /* Copy old data, if there was any */ if (sc->sc_nctls != 0) { memcpy(nmc, sc->sc_ctls, sizeof(*mc) * (sc->sc_nctls)); free(sc->sc_ctls, M_USBDEV); } sc->sc_ctls = nmc; mc->delta = 0; if (mc->type == MIX_ON_OFF) { mc->minval = 0; mc->maxval = 1; } else if (mc->type == MIX_SELECTOR) { ; } else { /* Determine min and max values. */ mc->minval = uaudio_signext(mc->type, uaudio_get(sc, GET_MIN, UT_READ_CLASS_INTERFACE, mc->wValue[0], mc->wIndex, MIX_SIZE(mc->type))); mc->maxval = 1 + uaudio_signext(mc->type, uaudio_get(sc, GET_MAX, UT_READ_CLASS_INTERFACE, mc->wValue[0], mc->wIndex, MIX_SIZE(mc->type))); mc->mul = mc->maxval - mc->minval; if (mc->mul == 0) mc->mul = 1; res = uaudio_get(sc, GET_RES, UT_READ_CLASS_INTERFACE, mc->wValue[0], mc->wIndex, MIX_SIZE(mc->type)); if (res > 0) mc->delta = (res * 255 + mc->mul/2) / mc->mul; } sc->sc_ctls[sc->sc_nctls++] = *mc; #ifdef USB_DEBUG if (uaudiodebug > 2) { int i; DPRINTF(("uaudio_mixer_add_ctl: wValue=%04x",mc->wValue[0])); for (i = 1; i < mc->nchan; i++) DPRINTF((",%04x", mc->wValue[i])); #if defined(__FreeBSD__) DPRINTF((" wIndex=%04x type=%d ctl='%d' " "min=%d max=%d\n", mc->wIndex, mc->type, mc->ctl, mc->minval, mc->maxval)); #else DPRINTF((" wIndex=%04x type=%d name='%s' unit='%s' " "min=%d max=%d\n", mc->wIndex, mc->type, mc->ctlname, mc->ctlunit, mc->minval, mc->maxval)); #endif } #endif } #if defined(__NetBSD__) || defined(__OpenBSD__) static char * uaudio_id_name(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { static char buf[32]; snprintf(buf, sizeof(buf), "i%d", id); return buf; } #endif #ifdef USB_DEBUG static void uaudio_dump_cluster(const struct usb_audio_cluster *cl) { static const char *channel_names[16] = { "LEFT", "RIGHT", "CENTER", "LFE", "LEFT_SURROUND", "RIGHT_SURROUND", "LEFT_CENTER", "RIGHT_CENTER", "SURROUND", "LEFT_SIDE", "RIGHT_SIDE", "TOP", "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15", }; int cc, i, first; cc = UGETW(cl->wChannelConfig); logprintf("cluster: bNrChannels=%u wChannelConfig=0x%.4x", cl->bNrChannels, cc); first = TRUE; for (i = 0; cc != 0; i++) { if (cc & 1) { logprintf("%c%s", first ? '<' : ',', channel_names[i]); first = FALSE; } cc = cc >> 1; } logprintf("> iChannelNames=%u", cl->iChannelNames); } #endif static struct usb_audio_cluster uaudio_get_cluster(int id, const struct io_terminal *iot) { struct usb_audio_cluster r; const usb_descriptor_t *dp; int i; for (i = 0; i < 25; i++) { /* avoid infinite loops */ dp = iot[id].d.desc; if (dp == 0) goto bad; switch (dp->bDescriptorSubtype) { case UDESCSUB_AC_INPUT: r.bNrChannels = iot[id].d.it->bNrChannels; USETW(r.wChannelConfig, UGETW(iot[id].d.it->wChannelConfig)); r.iChannelNames = iot[id].d.it->iChannelNames; return r; case UDESCSUB_AC_OUTPUT: id = iot[id].d.ot->bSourceId; break; case UDESCSUB_AC_MIXER: r = *(const struct usb_audio_cluster *) &iot[id].d.mu->baSourceId[iot[id].d.mu->bNrInPins]; return r; case UDESCSUB_AC_SELECTOR: /* XXX This is not really right */ id = iot[id].d.su->baSourceId[0]; break; case UDESCSUB_AC_FEATURE: id = iot[id].d.fu->bSourceId; break; case UDESCSUB_AC_PROCESSING: r = *(const struct usb_audio_cluster *) &iot[id].d.pu->baSourceId[iot[id].d.pu->bNrInPins]; return r; case UDESCSUB_AC_EXTENSION: r = *(const struct usb_audio_cluster *) &iot[id].d.eu->baSourceId[iot[id].d.eu->bNrInPins]; return r; default: goto bad; } } bad: printf("uaudio_get_cluster: bad data\n"); memset(&r, 0, sizeof r); return r; } static void uaudio_add_input(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { #ifdef USB_DEBUG const struct usb_audio_input_terminal *d = iot[id].d.it; DPRINTFN(2,("uaudio_add_input: bTerminalId=%d wTerminalType=0x%04x " "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d " "iChannelNames=%d iTerminal=%d\n", d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal, d->bNrChannels, UGETW(d->wChannelConfig), d->iChannelNames, d->iTerminal)); #endif } static void uaudio_add_output(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { #ifdef USB_DEBUG const struct usb_audio_output_terminal *d; d = iot[id].d.ot; DPRINTFN(2,("uaudio_add_output: bTerminalId=%d wTerminalType=0x%04x " "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n", d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal, d->bSourceId, d->iTerminal)); #endif } static void uaudio_add_mixer(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { const struct usb_audio_mixer_unit *d = iot[id].d.mu; const struct usb_audio_mixer_unit_1 *d1; int c, chs, ichs, ochs, i, o, bno, p, mo, mc, k; const uByte *bm; struct mixerctl mix; DPRINTFN(2,("uaudio_add_mixer: bUnitId=%d bNrInPins=%d\n", d->bUnitId, d->bNrInPins)); /* Compute the number of input channels */ ichs = 0; for (i = 0; i < d->bNrInPins; i++) ichs += uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels; /* and the number of output channels */ d1 = (const struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins]; ochs = d1->bNrChannels; DPRINTFN(2,("uaudio_add_mixer: ichs=%d ochs=%d\n", ichs, ochs)); bm = d1->bmControls; mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); uaudio_determine_class(&iot[id], &mix); mix.type = MIX_SIGNED_16; #if !defined(__FreeBSD__) /* XXXXX */ mix.ctlunit = AudioNvolume; #endif #define BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1) for (p = i = 0; i < d->bNrInPins; i++) { chs = uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels; mc = 0; for (c = 0; c < chs; c++) { mo = 0; for (o = 0; o < ochs; o++) { bno = (p + c) * ochs + o; if (BIT(bno)) mo++; } if (mo == 1) mc++; } if (mc == chs && chs <= MIX_MAX_CHAN) { k = 0; for (c = 0; c < chs; c++) for (o = 0; o < ochs; o++) { bno = (p + c) * ochs + o; if (BIT(bno)) mix.wValue[k++] = MAKE(p+c+1, o+1); } #if !defined(__FreeBSD__) snprintf(mix.ctlname, sizeof(mix.ctlname), "mix%d-%s", d->bUnitId, uaudio_id_name(sc, iot, d->baSourceId[i])); #endif mix.nchan = chs; uaudio_mixer_add_ctl(sc, &mix); } else { /* XXX */ } #undef BIT p += chs; } } static void uaudio_add_selector(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { const struct usb_audio_selector_unit *d; struct mixerctl mix; #if !defined(__FreeBSD__) int i, wp; #else int i; struct mixerctl dummy; #endif d = iot[id].d.su; DPRINTFN(2,("uaudio_add_selector: bUnitId=%d bNrInPins=%d\n", d->bUnitId, d->bNrInPins)); mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); mix.wValue[0] = MAKE(0, 0); uaudio_determine_class(&iot[id], &mix); mix.nchan = 1; mix.type = MIX_SELECTOR; #if defined(__FreeBSD__) mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ mix.minval = 1; mix.maxval = d->bNrInPins; mix.mul = mix.maxval - mix.minval; for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) { mix.slctrtype[i] = SOUND_MIXER_NRDEVICES; } for (i = mix.minval; i <= mix.maxval; i++) { mix.slctrtype[i - 1] = uaudio_feature_name(&iot[d->baSourceId[i - 1]], &dummy); } #else mix.ctlunit = ""; mix.minval = 1; mix.maxval = d->bNrInPins; mix.mul = mix.maxval - mix.minval; wp = snprintf(mix.ctlname, MAX_AUDIO_DEV_LEN, "sel%d-", d->bUnitId); for (i = 1; i <= d->bNrInPins; i++) { wp += snprintf(mix.ctlname + wp, MAX_AUDIO_DEV_LEN - wp, "i%d", d->baSourceId[i - 1]); if (wp > MAX_AUDIO_DEV_LEN - 1) break; } #endif uaudio_mixer_add_ctl(sc, &mix); } #ifdef USB_DEBUG static const char * uaudio_get_terminal_name(int terminal_type) { static char buf[100]; switch (terminal_type) { /* USB terminal types */ case UAT_UNDEFINED: return "UAT_UNDEFINED"; case UAT_STREAM: return "UAT_STREAM"; case UAT_VENDOR: return "UAT_VENDOR"; /* input terminal types */ case UATI_UNDEFINED: return "UATI_UNDEFINED"; case UATI_MICROPHONE: return "UATI_MICROPHONE"; case UATI_DESKMICROPHONE: return "UATI_DESKMICROPHONE"; case UATI_PERSONALMICROPHONE: return "UATI_PERSONALMICROPHONE"; case UATI_OMNIMICROPHONE: return "UATI_OMNIMICROPHONE"; case UATI_MICROPHONEARRAY: return "UATI_MICROPHONEARRAY"; case UATI_PROCMICROPHONEARR: return "UATI_PROCMICROPHONEARR"; /* output terminal types */ case UATO_UNDEFINED: return "UATO_UNDEFINED"; case UATO_SPEAKER: return "UATO_SPEAKER"; case UATO_HEADPHONES: return "UATO_HEADPHONES"; case UATO_DISPLAYAUDIO: return "UATO_DISPLAYAUDIO"; case UATO_DESKTOPSPEAKER: return "UATO_DESKTOPSPEAKER"; case UATO_ROOMSPEAKER: return "UATO_ROOMSPEAKER"; case UATO_COMMSPEAKER: return "UATO_COMMSPEAKER"; case UATO_SUBWOOFER: return "UATO_SUBWOOFER"; /* bidir terminal types */ case UATB_UNDEFINED: return "UATB_UNDEFINED"; case UATB_HANDSET: return "UATB_HANDSET"; case UATB_HEADSET: return "UATB_HEADSET"; case UATB_SPEAKERPHONE: return "UATB_SPEAKERPHONE"; case UATB_SPEAKERPHONEESUP: return "UATB_SPEAKERPHONEESUP"; case UATB_SPEAKERPHONEECANC: return "UATB_SPEAKERPHONEECANC"; /* telephony terminal types */ case UATT_UNDEFINED: return "UATT_UNDEFINED"; case UATT_PHONELINE: return "UATT_PHONELINE"; case UATT_TELEPHONE: return "UATT_TELEPHONE"; case UATT_DOWNLINEPHONE: return "UATT_DOWNLINEPHONE"; /* external terminal types */ case UATE_UNDEFINED: return "UATE_UNDEFINED"; case UATE_ANALOGCONN: return "UATE_ANALOGCONN"; case UATE_LINECONN: return "UATE_LINECONN"; case UATE_LEGACYCONN: return "UATE_LEGACYCONN"; case UATE_DIGITALAUIFC: return "UATE_DIGITALAUIFC"; case UATE_SPDIF: return "UATE_SPDIF"; case UATE_1394DA: return "UATE_1394DA"; case UATE_1394DV: return "UATE_1394DV"; /* embedded function terminal types */ case UATF_UNDEFINED: return "UATF_UNDEFINED"; case UATF_CALIBNOISE: return "UATF_CALIBNOISE"; case UATF_EQUNOISE: return "UATF_EQUNOISE"; case UATF_CDPLAYER: return "UATF_CDPLAYER"; case UATF_DAT: return "UATF_DAT"; case UATF_DCC: return "UATF_DCC"; case UATF_MINIDISK: return "UATF_MINIDISK"; case UATF_ANALOGTAPE: return "UATF_ANALOGTAPE"; case UATF_PHONOGRAPH: return "UATF_PHONOGRAPH"; case UATF_VCRAUDIO: return "UATF_VCRAUDIO"; case UATF_VIDEODISCAUDIO: return "UATF_VIDEODISCAUDIO"; case UATF_DVDAUDIO: return "UATF_DVDAUDIO"; case UATF_TVTUNERAUDIO: return "UATF_TVTUNERAUDIO"; case UATF_SATELLITE: return "UATF_SATELLITE"; case UATF_CABLETUNER: return "UATF_CABLETUNER"; case UATF_DSS: return "UATF_DSS"; case UATF_RADIORECV: return "UATF_RADIORECV"; case UATF_RADIOXMIT: return "UATF_RADIOXMIT"; case UATF_MULTITRACK: return "UATF_MULTITRACK"; case UATF_SYNTHESIZER: return "UATF_SYNTHESIZER"; default: snprintf(buf, sizeof(buf), "unknown type (0x%.4x)", terminal_type); return buf; } } #endif static int uaudio_determine_class(const struct io_terminal *iot, struct mixerctl *mix) { int terminal_type; if (iot == NULL || iot->output == NULL) { mix->class = UAC_OUTPUT; return 0; } terminal_type = 0; if (iot->output->size == 1) terminal_type = iot->output->terminals[0]; /* * If the only output terminal is USB, * the class is UAC_RECORD. */ if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) { mix->class = UAC_RECORD; if (iot->inputs_size == 1 && iot->inputs[0] != NULL && iot->inputs[0]->size == 1) return iot->inputs[0]->terminals[0]; else return 0; } /* * If the ultimate destination of the unit is just one output * terminal and the unit is connected to the output terminal * directly, the class is UAC_OUTPUT. */ if (terminal_type != 0 && iot->direct) { mix->class = UAC_OUTPUT; return terminal_type; } /* * If the unit is connected to just one input terminal, * the class is UAC_INPUT. */ if (iot->inputs_size == 1 && iot->inputs[0] != NULL && iot->inputs[0]->size == 1) { mix->class = UAC_INPUT; return iot->inputs[0]->terminals[0]; } /* * Otherwise, the class is UAC_OUTPUT. */ mix->class = UAC_OUTPUT; return terminal_type; } #if defined(__FreeBSD__) const int uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix) { int terminal_type; terminal_type = uaudio_determine_class(iot, mix); if (mix->class == UAC_RECORD && terminal_type == 0) return SOUND_MIXER_IMIX; DPRINTF(("%s: terminal_type=%s\n", __func__, uaudio_get_terminal_name(terminal_type))); switch (terminal_type) { case UAT_STREAM: return SOUND_MIXER_PCM; case UATI_MICROPHONE: case UATI_DESKMICROPHONE: case UATI_PERSONALMICROPHONE: case UATI_OMNIMICROPHONE: case UATI_MICROPHONEARRAY: case UATI_PROCMICROPHONEARR: return SOUND_MIXER_MIC; case UATO_SPEAKER: case UATO_DESKTOPSPEAKER: case UATO_ROOMSPEAKER: case UATO_COMMSPEAKER: return SOUND_MIXER_SPEAKER; case UATE_ANALOGCONN: case UATE_LINECONN: case UATE_LEGACYCONN: return SOUND_MIXER_LINE; case UATE_DIGITALAUIFC: case UATE_SPDIF: case UATE_1394DA: case UATE_1394DV: return SOUND_MIXER_ALTPCM; case UATF_CDPLAYER: return SOUND_MIXER_CD; case UATF_SYNTHESIZER: return SOUND_MIXER_SYNTH; case UATF_VIDEODISCAUDIO: case UATF_DVDAUDIO: case UATF_TVTUNERAUDIO: return SOUND_MIXER_VIDEO; /* telephony terminal types */ case UATT_UNDEFINED: case UATT_PHONELINE: case UATT_TELEPHONE: case UATT_DOWNLINEPHONE: return SOUND_MIXER_PHONEIN; /* return SOUND_MIXER_PHONEOUT;*/ case UATF_RADIORECV: case UATF_RADIOXMIT: return SOUND_MIXER_RADIO; case UAT_UNDEFINED: case UAT_VENDOR: case UATI_UNDEFINED: /* output terminal types */ case UATO_UNDEFINED: case UATO_DISPLAYAUDIO: case UATO_SUBWOOFER: case UATO_HEADPHONES: /* bidir terminal types */ case UATB_UNDEFINED: case UATB_HANDSET: case UATB_HEADSET: case UATB_SPEAKERPHONE: case UATB_SPEAKERPHONEESUP: case UATB_SPEAKERPHONEECANC: /* external terminal types */ case UATE_UNDEFINED: /* embedded function terminal types */ case UATF_UNDEFINED: case UATF_CALIBNOISE: case UATF_EQUNOISE: case UATF_DAT: case UATF_DCC: case UATF_MINIDISK: case UATF_ANALOGTAPE: case UATF_PHONOGRAPH: case UATF_VCRAUDIO: case UATF_SATELLITE: case UATF_CABLETUNER: case UATF_DSS: case UATF_MULTITRACK: case 0xffff: default: DPRINTF(("%s: 'master' for 0x%.4x\n", __func__, terminal_type)); return SOUND_MIXER_VOLUME; } return SOUND_MIXER_VOLUME; } #else static const char * uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix) { int terminal_type; terminal_type = uaudio_determine_class(iot, mix); if (mix->class == UAC_RECORD && terminal_type == 0) return AudioNmixerout; DPRINTF(("%s: terminal_type=%s\n", __func__, uaudio_get_terminal_name(terminal_type))); switch (terminal_type) { case UAT_STREAM: return AudioNdac; case UATI_MICROPHONE: case UATI_DESKMICROPHONE: case UATI_PERSONALMICROPHONE: case UATI_OMNIMICROPHONE: case UATI_MICROPHONEARRAY: case UATI_PROCMICROPHONEARR: return AudioNmicrophone; case UATO_SPEAKER: case UATO_DESKTOPSPEAKER: case UATO_ROOMSPEAKER: case UATO_COMMSPEAKER: return AudioNspeaker; case UATO_HEADPHONES: return AudioNheadphone; case UATO_SUBWOOFER: return AudioNlfe; /* telephony terminal types */ case UATT_UNDEFINED: case UATT_PHONELINE: case UATT_TELEPHONE: case UATT_DOWNLINEPHONE: return "phone"; case UATE_ANALOGCONN: case UATE_LINECONN: case UATE_LEGACYCONN: return AudioNline; case UATE_DIGITALAUIFC: case UATE_SPDIF: case UATE_1394DA: case UATE_1394DV: return AudioNaux; case UATF_CDPLAYER: return AudioNcd; case UATF_SYNTHESIZER: return AudioNfmsynth; case UATF_VIDEODISCAUDIO: case UATF_DVDAUDIO: case UATF_TVTUNERAUDIO: return AudioNvideo; case UAT_UNDEFINED: case UAT_VENDOR: case UATI_UNDEFINED: /* output terminal types */ case UATO_UNDEFINED: case UATO_DISPLAYAUDIO: /* bidir terminal types */ case UATB_UNDEFINED: case UATB_HANDSET: case UATB_HEADSET: case UATB_SPEAKERPHONE: case UATB_SPEAKERPHONEESUP: case UATB_SPEAKERPHONEECANC: /* external terminal types */ case UATE_UNDEFINED: /* embedded function terminal types */ case UATF_UNDEFINED: case UATF_CALIBNOISE: case UATF_EQUNOISE: case UATF_DAT: case UATF_DCC: case UATF_MINIDISK: case UATF_ANALOGTAPE: case UATF_PHONOGRAPH: case UATF_VCRAUDIO: case UATF_SATELLITE: case UATF_CABLETUNER: case UATF_DSS: case UATF_RADIORECV: case UATF_RADIOXMIT: case UATF_MULTITRACK: case 0xffff: default: DPRINTF(("%s: 'master' for 0x%.4x\n", __func__, terminal_type)); return AudioNmaster; } return AudioNmaster; } #endif static void uaudio_add_feature(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { const struct usb_audio_feature_unit *d; const uByte *ctls; int ctlsize; int nchan; u_int fumask, mmask, cmask; struct mixerctl mix; int chan, ctl, i, unit; #if defined(__FreeBSD__) int mixernumber; #else const char *mixername; #endif #define GET(i) (ctls[(i)*ctlsize] | \ (ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0)) d = iot[id].d.fu; ctls = d->bmaControls; ctlsize = d->bControlSize; nchan = (d->bLength - 7) / ctlsize; mmask = GET(0); /* Figure out what we can control */ for (cmask = 0, chan = 1; chan < nchan; chan++) { DPRINTFN(9,("uaudio_add_feature: chan=%d mask=%x\n", chan, GET(chan))); cmask |= GET(chan); } #if !defined(__FreeBSD__) DPRINTFN(1,("uaudio_add_feature: bUnitId=%d, " "%d channels, mmask=0x%04x, cmask=0x%04x\n", d->bUnitId, nchan, mmask, cmask)); #endif if (nchan > MIX_MAX_CHAN) nchan = MIX_MAX_CHAN; unit = d->bUnitId; mix.wIndex = MAKE(unit, sc->sc_ac_iface); for (ctl = MUTE_CONTROL; ctl < LOUDNESS_CONTROL; ctl++) { fumask = FU_MASK(ctl); DPRINTFN(4,("uaudio_add_feature: ctl=%d fumask=0x%04x\n", ctl, fumask)); if (mmask & fumask) { mix.nchan = 1; mix.wValue[0] = MAKE(ctl, 0); } else if (cmask & fumask) { mix.nchan = nchan - 1; for (i = 1; i < nchan; i++) { if (GET(i) & fumask) mix.wValue[i-1] = MAKE(ctl, i); else mix.wValue[i-1] = -1; } } else { continue; } #undef GET #if defined(__FreeBSD__) mixernumber = uaudio_feature_name(&iot[id], &mix); #else mixername = uaudio_feature_name(&iot[id], &mix); #endif switch (ctl) { case MUTE_CONTROL: mix.type = MIX_ON_OFF; #if defined(__FreeBSD__) mix.ctl = SOUND_MIXER_NRDEVICES; #else mix.ctlunit = ""; snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s", mixername, AudioNmute); #endif break; case VOLUME_CONTROL: mix.type = MIX_SIGNED_16; #if defined(__FreeBSD__) mix.ctl = mixernumber; #else mix.ctlunit = AudioNvolume; strlcpy(mix.ctlname, mixername, sizeof(mix.ctlname)); #endif break; case BASS_CONTROL: mix.type = MIX_SIGNED_8; #if defined(__FreeBSD__) mix.ctl = SOUND_MIXER_BASS; #else mix.ctlunit = AudioNbass; snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s", mixername, AudioNbass); #endif break; case MID_CONTROL: mix.type = MIX_SIGNED_8; #if defined(__FreeBSD__) mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ #else mix.ctlunit = AudioNmid; snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s", mixername, AudioNmid); #endif break; case TREBLE_CONTROL: mix.type = MIX_SIGNED_8; #if defined(__FreeBSD__) mix.ctl = SOUND_MIXER_TREBLE; #else mix.ctlunit = AudioNtreble; snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s", mixername, AudioNtreble); #endif break; case GRAPHIC_EQUALIZER_CONTROL: continue; /* XXX don't add anything */ break; case AGC_CONTROL: mix.type = MIX_ON_OFF; #if defined(__FreeBSD__) mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ #else mix.ctlunit = ""; snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s", mixername, AudioNagc); #endif break; case DELAY_CONTROL: mix.type = MIX_UNSIGNED_16; #if defined(__FreeBSD__) mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ #else mix.ctlunit = "4 ms"; snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s", mixername, AudioNdelay); #endif break; case BASS_BOOST_CONTROL: mix.type = MIX_ON_OFF; #if defined(__FreeBSD__) mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ #else mix.ctlunit = ""; snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s", mixername, AudioNbassboost); #endif break; case LOUDNESS_CONTROL: mix.type = MIX_ON_OFF; #if defined(__FreeBSD__) mix.ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ #else mix.ctlunit = ""; snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s", mixername, AudioNloudness); #endif break; } uaudio_mixer_add_ctl(sc, &mix); } } static void uaudio_add_processing_updown(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { const struct usb_audio_processing_unit *d; const struct usb_audio_processing_unit_1 *d1; const struct usb_audio_processing_unit_updown *ud; struct mixerctl mix; int i; d = iot[id].d.pu; d1 = (const struct usb_audio_processing_unit_1 *) &d->baSourceId[d->bNrInPins]; ud = (const struct usb_audio_processing_unit_updown *) &d1->bmControls[d1->bControlSize]; DPRINTFN(2,("uaudio_add_processing_updown: bUnitId=%d bNrModes=%d\n", d->bUnitId, ud->bNrModes)); if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) { DPRINTF(("uaudio_add_processing_updown: no mode select\n")); return; } mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); mix.nchan = 1; mix.wValue[0] = MAKE(UD_MODE_SELECT_CONTROL, 0); uaudio_determine_class(&iot[id], &mix); mix.type = MIX_ON_OFF; /* XXX */ #if !defined(__FreeBSD__) mix.ctlunit = ""; snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d-mode", d->bUnitId); #endif for (i = 0; i < ud->bNrModes; i++) { DPRINTFN(2,("uaudio_add_processing_updown: i=%d bm=0x%x\n", i, UGETW(ud->waModes[i]))); /* XXX */ } uaudio_mixer_add_ctl(sc, &mix); } static void uaudio_add_processing(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { const struct usb_audio_processing_unit *d; const struct usb_audio_processing_unit_1 *d1; int ptype; struct mixerctl mix; d = iot[id].d.pu; d1 = (const struct usb_audio_processing_unit_1 *) &d->baSourceId[d->bNrInPins]; ptype = UGETW(d->wProcessType); DPRINTFN(2,("uaudio_add_processing: wProcessType=%d bUnitId=%d " "bNrInPins=%d\n", ptype, d->bUnitId, d->bNrInPins)); if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) { mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); mix.nchan = 1; mix.wValue[0] = MAKE(XX_ENABLE_CONTROL, 0); uaudio_determine_class(&iot[id], &mix); mix.type = MIX_ON_OFF; #if !defined(__FreeBSD__) mix.ctlunit = ""; snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d.%d-enable", d->bUnitId, ptype); #endif uaudio_mixer_add_ctl(sc, &mix); } switch(ptype) { case UPDOWNMIX_PROCESS: uaudio_add_processing_updown(sc, iot, id); break; case DOLBY_PROLOGIC_PROCESS: case P3D_STEREO_EXTENDER_PROCESS: case REVERBATION_PROCESS: case CHORUS_PROCESS: case DYN_RANGE_COMP_PROCESS: default: #ifdef USB_DEBUG printf("uaudio_add_processing: unit %d, type=%d not impl.\n", d->bUnitId, ptype); #endif break; } } static void uaudio_add_extension(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { const struct usb_audio_extension_unit *d; const struct usb_audio_extension_unit_1 *d1; struct mixerctl mix; d = iot[id].d.eu; d1 = (const struct usb_audio_extension_unit_1 *) &d->baSourceId[d->bNrInPins]; DPRINTFN(2,("uaudio_add_extension: bUnitId=%d bNrInPins=%d\n", d->bUnitId, d->bNrInPins)); if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_XU) return; if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) { mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); mix.nchan = 1; mix.wValue[0] = MAKE(UA_EXT_ENABLE, 0); uaudio_determine_class(&iot[id], &mix); mix.type = MIX_ON_OFF; #if !defined(__FreeBSD__) mix.ctlunit = ""; snprintf(mix.ctlname, sizeof(mix.ctlname), "ext%d-enable", d->bUnitId); #endif uaudio_mixer_add_ctl(sc, &mix); } } static struct terminal_list* uaudio_merge_terminal_list(const struct io_terminal *iot) { struct terminal_list *tml; uint16_t *ptm; int i, len; len = 0; if (iot->inputs == NULL) return NULL; for (i = 0; i < iot->inputs_size; i++) { if (iot->inputs[i] != NULL) len += iot->inputs[i]->size; } tml = malloc(TERMINAL_LIST_SIZE(len), M_TEMP, M_NOWAIT); if (tml == NULL) { printf("uaudio_merge_terminal_list: no memory\n"); return NULL; } tml->size = 0; ptm = tml->terminals; for (i = 0; i < iot->inputs_size; i++) { if (iot->inputs[i] == NULL) continue; if (iot->inputs[i]->size > len) break; memcpy(ptm, iot->inputs[i]->terminals, iot->inputs[i]->size * sizeof(uint16_t)); tml->size += iot->inputs[i]->size; ptm += iot->inputs[i]->size; len -= iot->inputs[i]->size; } return tml; } static struct terminal_list * uaudio_io_terminaltype(int outtype, struct io_terminal *iot, int id) { struct terminal_list *tml; struct io_terminal *it; int src_id, i; it = &iot[id]; if (it->output != NULL) { /* already has outtype? */ for (i = 0; i < it->output->size; i++) if (it->output->terminals[i] == outtype) return uaudio_merge_terminal_list(it); tml = malloc(TERMINAL_LIST_SIZE(it->output->size + 1), M_TEMP, M_NOWAIT); if (tml == NULL) { printf("uaudio_io_terminaltype: no memory\n"); return uaudio_merge_terminal_list(it); } memcpy(tml, it->output, TERMINAL_LIST_SIZE(it->output->size)); tml->terminals[it->output->size] = outtype; tml->size++; free(it->output, M_TEMP); it->output = tml; if (it->inputs != NULL) { for (i = 0; i < it->inputs_size; i++) if (it->inputs[i] != NULL) free(it->inputs[i], M_TEMP); free(it->inputs, M_TEMP); } it->inputs_size = 0; it->inputs = NULL; } else { /* end `iot[id] != NULL' */ it->inputs_size = 0; it->inputs = NULL; it->output = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT); if (it->output == NULL) { printf("uaudio_io_terminaltype: no memory\n"); return NULL; } it->output->terminals[0] = outtype; it->output->size = 1; it->direct = FALSE; } switch (it->d.desc->bDescriptorSubtype) { case UDESCSUB_AC_INPUT: it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT); if (it->inputs == NULL) { printf("uaudio_io_terminaltype: no memory\n"); return NULL; } tml = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT); if (tml == NULL) { printf("uaudio_io_terminaltype: no memory\n"); free(it->inputs, M_TEMP); it->inputs = NULL; return NULL; } it->inputs[0] = tml; tml->terminals[0] = UGETW(it->d.it->wTerminalType); tml->size = 1; it->inputs_size = 1; return uaudio_merge_terminal_list(it); case UDESCSUB_AC_FEATURE: src_id = it->d.fu->bSourceId; it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT); if (it->inputs == NULL) { printf("uaudio_io_terminaltype: no memory\n"); return uaudio_io_terminaltype(outtype, iot, src_id); } it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id); it->inputs_size = 1; return uaudio_merge_terminal_list(it); case UDESCSUB_AC_OUTPUT: it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT); if (it->inputs == NULL) { printf("uaudio_io_terminaltype: no memory\n"); return NULL; } src_id = it->d.ot->bSourceId; it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id); it->inputs_size = 1; iot[src_id].direct = TRUE; return NULL; case UDESCSUB_AC_MIXER: it->inputs_size = 0; it->inputs = malloc(sizeof(struct terminal_list *) * it->d.mu->bNrInPins, M_TEMP, M_NOWAIT); if (it->inputs == NULL) { printf("uaudio_io_terminaltype: no memory\n"); return NULL; } for (i = 0; i < it->d.mu->bNrInPins; i++) { src_id = it->d.mu->baSourceId[i]; it->inputs[i] = uaudio_io_terminaltype(outtype, iot, src_id); it->inputs_size++; } return uaudio_merge_terminal_list(it); case UDESCSUB_AC_SELECTOR: it->inputs_size = 0; it->inputs = malloc(sizeof(struct terminal_list *) * it->d.su->bNrInPins, M_TEMP, M_NOWAIT); if (it->inputs == NULL) { printf("uaudio_io_terminaltype: no memory\n"); return NULL; } for (i = 0; i < it->d.su->bNrInPins; i++) { src_id = it->d.su->baSourceId[i]; it->inputs[i] = uaudio_io_terminaltype(outtype, iot, src_id); it->inputs_size++; } return uaudio_merge_terminal_list(it); case UDESCSUB_AC_PROCESSING: it->inputs_size = 0; it->inputs = malloc(sizeof(struct terminal_list *) * it->d.pu->bNrInPins, M_TEMP, M_NOWAIT); if (it->inputs == NULL) { printf("uaudio_io_terminaltype: no memory\n"); return NULL; } for (i = 0; i < it->d.pu->bNrInPins; i++) { src_id = it->d.pu->baSourceId[i]; it->inputs[i] = uaudio_io_terminaltype(outtype, iot, src_id); it->inputs_size++; } return uaudio_merge_terminal_list(it); case UDESCSUB_AC_EXTENSION: it->inputs_size = 0; it->inputs = malloc(sizeof(struct terminal_list *) * it->d.eu->bNrInPins, M_TEMP, M_NOWAIT); if (it->inputs == NULL) { printf("uaudio_io_terminaltype: no memory\n"); return NULL; } for (i = 0; i < it->d.eu->bNrInPins; i++) { src_id = it->d.eu->baSourceId[i]; it->inputs[i] = uaudio_io_terminaltype(outtype, iot, src_id); it->inputs_size++; } return uaudio_merge_terminal_list(it); case UDESCSUB_AC_HEADER: default: return NULL; } } static usbd_status uaudio_identify(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc) { usbd_status err; err = uaudio_identify_ac(sc, cdesc); if (err) return err; return uaudio_identify_as(sc, cdesc); } static void uaudio_add_alt(struct uaudio_softc *sc, const struct as_info *ai) { size_t len; struct as_info *nai; len = sizeof(*ai) * (sc->sc_nalts + 1); nai = malloc(len, M_USBDEV, M_NOWAIT); if (nai == NULL) { printf("uaudio_add_alt: no memory\n"); return; } /* Copy old data, if there was any */ if (sc->sc_nalts != 0) { memcpy(nai, sc->sc_alts, sizeof(*ai) * (sc->sc_nalts)); free(sc->sc_alts, M_USBDEV); } sc->sc_alts = nai; DPRINTFN(2,("uaudio_add_alt: adding alt=%d, enc=%d\n", ai->alt, ai->encoding)); sc->sc_alts[sc->sc_nalts++] = *ai; } static usbd_status uaudio_process_as(struct uaudio_softc *sc, const char *buf, int *offsp, int size, const usb_interface_descriptor_t *id) #define offs (*offsp) { const struct usb_audio_streaming_interface_descriptor *asid; const struct usb_audio_streaming_type1_descriptor *asf1d; const usb_endpoint_descriptor_audio_t *ed; const usb_endpoint_descriptor_audio_t *epdesc1; const struct usb_audio_streaming_endpoint_descriptor *sed; int format, chan, prec, enc; int dir, type, sync; struct as_info ai; const char *format_str; asid = (const void *)(buf + offs); if (asid->bDescriptorType != UDESC_CS_INTERFACE || asid->bDescriptorSubtype != AS_GENERAL) return USBD_INVAL; DPRINTF(("uaudio_process_as: asid: bTerminakLink=%d wFormatTag=%d\n", asid->bTerminalLink, UGETW(asid->wFormatTag))); offs += asid->bLength; if (offs > size) return USBD_INVAL; asf1d = (const void *)(buf + offs); if (asf1d->bDescriptorType != UDESC_CS_INTERFACE || asf1d->bDescriptorSubtype != FORMAT_TYPE) return USBD_INVAL; offs += asf1d->bLength; if (offs > size) return USBD_INVAL; if (asf1d->bFormatType != FORMAT_TYPE_I) { printf("%s: ignored setting with type %d format\n", device_get_nameunit(sc->sc_dev), UGETW(asid->wFormatTag)); return USBD_NORMAL_COMPLETION; } ed = (const void *)(buf + offs); if (ed->bDescriptorType != UDESC_ENDPOINT) return USBD_INVAL; DPRINTF(("uaudio_process_as: endpoint[0] bLength=%d bDescriptorType=%d " "bEndpointAddress=%d bmAttributes=0x%x wMaxPacketSize=%d " "bInterval=%d bRefresh=%d bSynchAddress=%d\n", ed->bLength, ed->bDescriptorType, ed->bEndpointAddress, ed->bmAttributes, UGETW(ed->wMaxPacketSize), ed->bInterval, ed->bRefresh, ed->bSynchAddress)); offs += ed->bLength; if (offs > size) return USBD_INVAL; if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS) return USBD_INVAL; dir = UE_GET_DIR(ed->bEndpointAddress); type = UE_GET_ISO_TYPE(ed->bmAttributes); if ((usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_INP_ASYNC) && dir == UE_DIR_IN && type == UE_ISO_ADAPT) type = UE_ISO_ASYNC; /* We can't handle endpoints that need a sync pipe yet. */ sync = FALSE; if (dir == UE_DIR_IN && type == UE_ISO_ADAPT) { sync = TRUE; #ifndef UAUDIO_MULTIPLE_ENDPOINTS printf("%s: ignored input endpoint of type adaptive\n", device_get_nameunit(sc->sc_dev)); return USBD_NORMAL_COMPLETION; #endif } if (dir != UE_DIR_IN && type == UE_ISO_ASYNC) { sync = TRUE; #ifndef UAUDIO_MULTIPLE_ENDPOINTS printf("%s: ignored output endpoint of type async\n", device_get_nameunit(sc->sc_dev)); return USBD_NORMAL_COMPLETION; #endif } sed = (const void *)(buf + offs); if (sed->bDescriptorType != UDESC_CS_ENDPOINT || sed->bDescriptorSubtype != AS_GENERAL) return USBD_INVAL; DPRINTF((" streadming_endpoint: offset=%d bLength=%d\n", offs, sed->bLength)); offs += sed->bLength; if (offs > size) return USBD_INVAL; #ifdef UAUDIO_MULTIPLE_ENDPOINTS if (sync && id->bNumEndpoints <= 1) { printf("%s: a sync-pipe endpoint but no other endpoint\n", device_get_nameunit(sc->sc_dev)); return USBD_INVAL; } #endif if (!sync && id->bNumEndpoints > 1) { printf("%s: non sync-pipe endpoint but multiple endpoints\n", device_get_nameunit(sc->sc_dev)); return USBD_INVAL; } epdesc1 = NULL; if (id->bNumEndpoints > 1) { epdesc1 = (const void*)(buf + offs); if (epdesc1->bDescriptorType != UDESC_ENDPOINT) return USBD_INVAL; DPRINTF(("uaudio_process_as: endpoint[1] bLength=%d " "bDescriptorType=%d bEndpointAddress=%d " "bmAttributes=0x%x wMaxPacketSize=%d bInterval=%d " "bRefresh=%d bSynchAddress=%d\n", epdesc1->bLength, epdesc1->bDescriptorType, epdesc1->bEndpointAddress, epdesc1->bmAttributes, UGETW(epdesc1->wMaxPacketSize), epdesc1->bInterval, epdesc1->bRefresh, epdesc1->bSynchAddress)); offs += epdesc1->bLength; if (offs > size) return USBD_INVAL; if (epdesc1->bSynchAddress != 0) { printf("%s: invalid endpoint: bSynchAddress=0\n", device_get_nameunit(sc->sc_dev)); return USBD_INVAL; } if (UE_GET_XFERTYPE(epdesc1->bmAttributes) != UE_ISOCHRONOUS) { printf("%s: invalid endpoint: bmAttributes=0x%x\n", device_get_nameunit(sc->sc_dev), epdesc1->bmAttributes); return USBD_INVAL; } if (epdesc1->bEndpointAddress != ed->bSynchAddress) { printf("%s: invalid endpoint addresses: " "ep[0]->bSynchAddress=0x%x " "ep[1]->bEndpointAddress=0x%x\n", device_get_nameunit(sc->sc_dev), ed->bSynchAddress, epdesc1->bEndpointAddress); return USBD_INVAL; } /* UE_GET_ADDR(epdesc1->bEndpointAddress), and epdesc1->bRefresh */ } format = UGETW(asid->wFormatTag); chan = asf1d->bNrChannels; prec = asf1d->bBitResolution; if (prec != 8 && prec != 16 && prec != 24 && prec != 32) { printf("%s: ignored setting with precision %d\n", device_get_nameunit(sc->sc_dev), prec); return USBD_NORMAL_COMPLETION; } switch (format) { case UA_FMT_PCM: if (prec == 8) { sc->sc_altflags |= HAS_8; } else if (prec == 16) { sc->sc_altflags |= HAS_16; } else if (prec == 24) { sc->sc_altflags |= HAS_24; } else if (prec == 32) { sc->sc_altflags |= HAS_32; } enc = AUDIO_ENCODING_SLINEAR_LE; format_str = "pcm"; break; case UA_FMT_PCM8: enc = AUDIO_ENCODING_ULINEAR_LE; sc->sc_altflags |= HAS_8U; format_str = "pcm8"; break; case UA_FMT_ALAW: enc = AUDIO_ENCODING_ALAW; sc->sc_altflags |= HAS_ALAW; format_str = "alaw"; break; case UA_FMT_MULAW: enc = AUDIO_ENCODING_ULAW; sc->sc_altflags |= HAS_MULAW; format_str = "mulaw"; break; case UA_FMT_IEEE_FLOAT: default: printf("%s: ignored setting with format %d\n", device_get_nameunit(sc->sc_dev), format); return USBD_NORMAL_COMPLETION; } #ifdef USB_DEBUG printf("%s: %s: %dch, %d/%dbit, %s,", device_get_nameunit(sc->sc_dev), dir == UE_DIR_IN ? "recording" : "playback", chan, prec, asf1d->bSubFrameSize * 8, format_str); if (asf1d->bSamFreqType == UA_SAMP_CONTNUOUS) { printf(" %d-%dHz\n", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d)); } else { int r; printf(" %d", UA_GETSAMP(asf1d, 0)); for (r = 1; r < asf1d->bSamFreqType; r++) printf(",%d", UA_GETSAMP(asf1d, r)); printf("Hz\n"); } #endif #if defined(__FreeBSD__) if (sc->uaudio_sndstat_flag != 0) { sbuf_printf(&(sc->uaudio_sndstat), "\n\t"); sbuf_printf(&(sc->uaudio_sndstat), "mode %d:(%s) %dch, %d/%dbit, %s,", id->bAlternateSetting, dir == UE_DIR_IN ? "input" : "output", chan, prec, asf1d->bSubFrameSize * 8, format_str); if (asf1d->bSamFreqType == UA_SAMP_CONTNUOUS) { sbuf_printf(&(sc->uaudio_sndstat), " %d-%dHz", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d)); } else { int r; sbuf_printf(&(sc->uaudio_sndstat), " %d", UA_GETSAMP(asf1d, 0)); for (r = 1; r < asf1d->bSamFreqType; r++) sbuf_printf(&(sc->uaudio_sndstat), ",%d", UA_GETSAMP(asf1d, r)); sbuf_printf(&(sc->uaudio_sndstat), "Hz"); } } #endif ai.alt = id->bAlternateSetting; ai.encoding = enc; ai.attributes = sed->bmAttributes; ai.idesc = id; ai.edesc = ed; ai.edesc1 = epdesc1; ai.asf1desc = asf1d; ai.sc_busy = 0; ai.ifaceh = NULL; uaudio_add_alt(sc, &ai); #ifdef USB_DEBUG if (ai.attributes & UA_SED_FREQ_CONTROL) DPRINTFN(1, ("uaudio_process_as: FREQ_CONTROL\n")); if (ai.attributes & UA_SED_PITCH_CONTROL) DPRINTFN(1, ("uaudio_process_as: PITCH_CONTROL\n")); #endif sc->sc_mode |= (dir == UE_DIR_OUT) ? AUMODE_PLAY : AUMODE_RECORD; return USBD_NORMAL_COMPLETION; } #undef offs static usbd_status uaudio_identify_as(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc) { const usb_interface_descriptor_t *id; const char *buf; int size, offs; size = UGETW(cdesc->wTotalLength); buf = (const char *)cdesc; /* Locate the AudioStreaming interface descriptor. */ offs = 0; id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOSTREAM); if (id == NULL) return USBD_INVAL; #if defined(__FreeBSD__) sc->uaudio_sndstat_flag = 0; if (sbuf_new(&(sc->uaudio_sndstat), NULL, 4096, SBUF_AUTOEXTEND) != NULL) sc->uaudio_sndstat_flag = 1; #endif /* Loop through all the alternate settings. */ while (offs <= size) { DPRINTFN(2, ("uaudio_identify: interface=%d offset=%d\n", id->bInterfaceNumber, offs)); switch (id->bNumEndpoints) { case 0: DPRINTFN(2, ("uaudio_identify: AS null alt=%d\n", id->bAlternateSetting)); sc->sc_nullalt = id->bAlternateSetting; break; case 1: #ifdef UAUDIO_MULTIPLE_ENDPOINTS case 2: #endif uaudio_process_as(sc, buf, &offs, size, id); break; default: printf("%s: ignored audio interface with %d " "endpoints\n", device_get_nameunit(sc->sc_dev), id->bNumEndpoints); break; } id = uaudio_find_iface(buf, size, &offs,UISUBCLASS_AUDIOSTREAM); if (id == NULL) break; } #if defined(__FreeBSD__) sbuf_finish(&(sc->uaudio_sndstat)); #endif if (offs > size) return USBD_INVAL; DPRINTF(("uaudio_identify_as: %d alts available\n", sc->sc_nalts)); if (sc->sc_mode == 0) { printf("%s: no usable endpoint found\n", device_get_nameunit(sc->sc_dev)); return USBD_INVAL; } return USBD_NORMAL_COMPLETION; } static usbd_status uaudio_identify_ac(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc) { struct io_terminal* iot; const usb_interface_descriptor_t *id; const struct usb_audio_control_descriptor *acdp; const usb_descriptor_t *dp; const struct usb_audio_output_terminal *pot; struct terminal_list *tml; const char *buf, *ibuf, *ibufend; int size, offs, aclen, ndps, i, j; size = UGETW(cdesc->wTotalLength); buf = (const char *)cdesc; /* Locate the AudioControl interface descriptor. */ offs = 0; id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOCONTROL); if (id == NULL) return USBD_INVAL; if (offs + sizeof *acdp > size) return USBD_INVAL; sc->sc_ac_iface = id->bInterfaceNumber; DPRINTFN(2,("uaudio_identify_ac: AC interface is %d\n", sc->sc_ac_iface)); /* A class-specific AC interface header should follow. */ ibuf = buf + offs; acdp = (const struct usb_audio_control_descriptor *)ibuf; if (acdp->bDescriptorType != UDESC_CS_INTERFACE || acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER) return USBD_INVAL; aclen = UGETW(acdp->wTotalLength); if (offs + aclen > size) return USBD_INVAL; if (!(usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_BAD_ADC) && UGETW(acdp->bcdADC) != UAUDIO_VERSION) return USBD_INVAL; sc->sc_audio_rev = UGETW(acdp->bcdADC); DPRINTFN(2,("uaudio_identify_ac: found AC header, vers=%03x, len=%d\n", sc->sc_audio_rev, aclen)); sc->sc_nullalt = -1; /* Scan through all the AC specific descriptors */ ibufend = ibuf + aclen; dp = (const usb_descriptor_t *)ibuf; ndps = 0; iot = malloc(sizeof(struct io_terminal) * 256, M_TEMP, M_NOWAIT | M_ZERO); if (iot == NULL) { printf("%s: no memory\n", __func__); return USBD_NOMEM; } for (;;) { ibuf += dp->bLength; if (ibuf >= ibufend) break; dp = (const usb_descriptor_t *)ibuf; if (ibuf + dp->bLength > ibufend) { free(iot, M_TEMP); return USBD_INVAL; } if (dp->bDescriptorType != UDESC_CS_INTERFACE) { printf("uaudio_identify_ac: skip desc type=0x%02x\n", dp->bDescriptorType); continue; } i = ((const struct usb_audio_input_terminal *)dp)->bTerminalId; iot[i].d.desc = dp; if (i > ndps) ndps = i; } ndps++; /* construct io_terminal */ for (i = 0; i < ndps; i++) { dp = iot[i].d.desc; if (dp == NULL) continue; if (dp->bDescriptorSubtype != UDESCSUB_AC_OUTPUT) continue; pot = iot[i].d.ot; tml = uaudio_io_terminaltype(UGETW(pot->wTerminalType), iot, i); if (tml != NULL) free(tml, M_TEMP); } #ifdef USB_DEBUG for (i = 0; i < 256; i++) { struct usb_audio_cluster cluster; if (iot[i].d.desc == NULL) continue; logprintf("id %d:\t", i); switch (iot[i].d.desc->bDescriptorSubtype) { case UDESCSUB_AC_INPUT: logprintf("AC_INPUT type=%s\n", uaudio_get_terminal_name (UGETW(iot[i].d.it->wTerminalType))); logprintf("\t"); cluster = uaudio_get_cluster(i, iot); uaudio_dump_cluster(&cluster); logprintf("\n"); break; case UDESCSUB_AC_OUTPUT: logprintf("AC_OUTPUT type=%s ", uaudio_get_terminal_name (UGETW(iot[i].d.ot->wTerminalType))); logprintf("src=%d\n", iot[i].d.ot->bSourceId); break; case UDESCSUB_AC_MIXER: logprintf("AC_MIXER src="); for (j = 0; j < iot[i].d.mu->bNrInPins; j++) logprintf("%d ", iot[i].d.mu->baSourceId[j]); logprintf("\n\t"); cluster = uaudio_get_cluster(i, iot); uaudio_dump_cluster(&cluster); logprintf("\n"); break; case UDESCSUB_AC_SELECTOR: logprintf("AC_SELECTOR src="); for (j = 0; j < iot[i].d.su->bNrInPins; j++) logprintf("%d ", iot[i].d.su->baSourceId[j]); logprintf("\n"); break; case UDESCSUB_AC_FEATURE: logprintf("AC_FEATURE src=%d\n", iot[i].d.fu->bSourceId); break; case UDESCSUB_AC_PROCESSING: logprintf("AC_PROCESSING src="); for (j = 0; j < iot[i].d.pu->bNrInPins; j++) logprintf("%d ", iot[i].d.pu->baSourceId[j]); logprintf("\n\t"); cluster = uaudio_get_cluster(i, iot); uaudio_dump_cluster(&cluster); logprintf("\n"); break; case UDESCSUB_AC_EXTENSION: logprintf("AC_EXTENSION src="); for (j = 0; j < iot[i].d.eu->bNrInPins; j++) logprintf("%d ", iot[i].d.eu->baSourceId[j]); logprintf("\n\t"); cluster = uaudio_get_cluster(i, iot); uaudio_dump_cluster(&cluster); logprintf("\n"); break; default: logprintf("unknown audio control (subtype=%d)\n", iot[i].d.desc->bDescriptorSubtype); } for (j = 0; j < iot[i].inputs_size; j++) { int k; logprintf("\tinput%d: ", j); tml = iot[i].inputs[j]; if (tml == NULL) { logprintf("NULL\n"); continue; } for (k = 0; k < tml->size; k++) logprintf("%s ", uaudio_get_terminal_name (tml->terminals[k])); logprintf("\n"); } logprintf("\toutput: "); tml = iot[i].output; for (j = 0; j < tml->size; j++) logprintf("%s ", uaudio_get_terminal_name(tml->terminals[j])); logprintf("\n"); } #endif for (i = 0; i < ndps; i++) { dp = iot[i].d.desc; if (dp == NULL) continue; DPRINTF(("uaudio_identify_ac: id=%d subtype=%d\n", i, dp->bDescriptorSubtype)); switch (dp->bDescriptorSubtype) { case UDESCSUB_AC_HEADER: printf("uaudio_identify_ac: unexpected AC header\n"); break; case UDESCSUB_AC_INPUT: uaudio_add_input(sc, iot, i); break; case UDESCSUB_AC_OUTPUT: uaudio_add_output(sc, iot, i); break; case UDESCSUB_AC_MIXER: uaudio_add_mixer(sc, iot, i); break; case UDESCSUB_AC_SELECTOR: uaudio_add_selector(sc, iot, i); break; case UDESCSUB_AC_FEATURE: uaudio_add_feature(sc, iot, i); break; case UDESCSUB_AC_PROCESSING: uaudio_add_processing(sc, iot, i); break; case UDESCSUB_AC_EXTENSION: uaudio_add_extension(sc, iot, i); break; default: printf("uaudio_identify_ac: bad AC desc subtype=0x%02x\n", dp->bDescriptorSubtype); break; } } /* delete io_terminal */ for (i = 0; i < 256; i++) { if (iot[i].d.desc == NULL) continue; if (iot[i].inputs != NULL) { for (j = 0; j < iot[i].inputs_size; j++) { if (iot[i].inputs[j] != NULL) free(iot[i].inputs[j], M_TEMP); } free(iot[i].inputs, M_TEMP); } if (iot[i].output != NULL) free(iot[i].output, M_TEMP); iot[i].d.desc = NULL; } free(iot, M_TEMP); return USBD_NORMAL_COMPLETION; } #if defined(__NetBSD__) || defined(__OpenBSD__) static int uaudio_query_devinfo(void *addr, mixer_devinfo_t *mi) { struct uaudio_softc *sc; struct mixerctl *mc; int n, nctls, i; sc = addr; DPRINTFN(2,("uaudio_query_devinfo: index=%d\n", mi->index)); if (sc->sc_dying) return EIO; n = mi->index; nctls = sc->sc_nctls; switch (n) { case UAC_OUTPUT: mi->type = AUDIO_MIXER_CLASS; mi->mixer_class = UAC_OUTPUT; mi->next = mi->prev = AUDIO_MIXER_LAST; strlcpy(mi->label.name, AudioCoutputs, sizeof(mi->label.name)); return 0; case UAC_INPUT: mi->type = AUDIO_MIXER_CLASS; mi->mixer_class = UAC_INPUT; mi->next = mi->prev = AUDIO_MIXER_LAST; strlcpy(mi->label.name, AudioCinputs, sizeof(mi->label.name)); return 0; case UAC_EQUAL: mi->type = AUDIO_MIXER_CLASS; mi->mixer_class = UAC_EQUAL; mi->next = mi->prev = AUDIO_MIXER_LAST; strlcpy(mi->label.name, AudioCequalization, sizeof(mi->label.name)); return 0; case UAC_RECORD: mi->type = AUDIO_MIXER_CLASS; mi->mixer_class = UAC_RECORD; mi->next = mi->prev = AUDIO_MIXER_LAST; strlcpy(mi->label.name, AudioCrecord, sizeof(mi->label.name)); return 0; default: break; } n -= UAC_NCLASSES; if (n < 0 || n >= nctls) return ENXIO; mc = &sc->sc_ctls[n]; strlcpy(mi->label.name, mc->ctlname, sizeof(mi->label.name)); mi->mixer_class = mc->class; mi->next = mi->prev = AUDIO_MIXER_LAST; /* XXX */ switch (mc->type) { case MIX_ON_OFF: mi->type = AUDIO_MIXER_ENUM; mi->un.e.num_mem = 2; strlcpy(mi->un.e.member[0].label.name, AudioNoff, sizeof(mi->un.e.member[0].label.name)); mi->un.e.member[0].ord = 0; strlcpy(mi->un.e.member[1].label.name, AudioNon, sizeof(mi->un.e.member[1].label.name)); mi->un.e.member[1].ord = 1; break; case MIX_SELECTOR: mi->type = AUDIO_MIXER_ENUM; mi->un.e.num_mem = mc->maxval - mc->minval + 1; for (i = 0; i <= mc->maxval - mc->minval; i++) { snprintf(mi->un.e.member[i].label.name, sizeof(mi->un.e.member[i].label.name), "%d", i + mc->minval); mi->un.e.member[i].ord = i + mc->minval; } break; default: mi->type = AUDIO_MIXER_VALUE; strlcpy(mi->un.v.units.name, mc->ctlunit, MAX_AUDIO_DEV_LEN); mi->un.v.num_channels = mc->nchan; mi->un.v.delta = mc->delta; break; } return 0; } static int uaudio_open(void *addr, int flags) { struct uaudio_softc *sc; sc = addr; DPRINTF(("uaudio_open: sc=%p\n", sc)); if (sc->sc_dying) return EIO; if ((flags & FWRITE) && !(sc->sc_mode & AUMODE_PLAY)) return EACCES; if ((flags & FREAD) && !(sc->sc_mode & AUMODE_RECORD)) return EACCES; return 0; } /* * Close function is called at splaudio(). */ static void uaudio_close(void *addr) { } static int uaudio_drain(void *addr) { struct uaudio_softc *sc; sc = addr; usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES); return 0; } static int uaudio_halt_out_dma(void *addr) { struct uaudio_softc *sc; sc = addr; if (sc->sc_dying) return EIO; DPRINTF(("uaudio_halt_out_dma: enter\n")); if (sc->sc_playchan.pipe != NULL) { uaudio_chan_close(sc, &sc->sc_playchan); sc->sc_playchan.pipe = NULL; uaudio_chan_free_buffers(sc, &sc->sc_playchan); sc->sc_playchan.intr = NULL; } return 0; } static int uaudio_halt_in_dma(void *addr) { struct uaudio_softc *sc; DPRINTF(("uaudio_halt_in_dma: enter\n")); sc = addr; if (sc->sc_recchan.pipe != NULL) { uaudio_chan_close(sc, &sc->sc_recchan); sc->sc_recchan.pipe = NULL; uaudio_chan_free_buffers(sc, &sc->sc_recchan); sc->sc_recchan.intr = NULL; } return 0; } static int uaudio_getdev(void *addr, struct audio_device *retp) { struct uaudio_softc *sc; DPRINTF(("uaudio_mixer_getdev:\n")); sc = addr; if (sc->sc_dying) return EIO; *retp = uaudio_device; return 0; } /* * Make sure the block size is large enough to hold all outstanding transfers. */ static int uaudio_round_blocksize(void *addr, int blk) { struct uaudio_softc *sc; int b; sc = addr; DPRINTF(("uaudio_round_blocksize: blk=%d mode=%s\n", blk, mode == AUMODE_PLAY ? "AUMODE_PLAY" : "AUMODE_RECORD")); /* chan.bytes_per_frame can be 0. */ if (mode == AUMODE_PLAY || sc->sc_recchan.bytes_per_frame <= 0) { b = param->sample_rate * UAUDIO_NFRAMES * UAUDIO_NCHANBUFS; /* * This does not make accurate value in the case * of b % USB_FRAMES_PER_SECOND != 0 */ b /= USB_FRAMES_PER_SECOND; b *= param->precision / 8 * param->channels; } else { /* * use wMaxPacketSize in bytes_per_frame. * See uaudio_set_params() and uaudio_chan_init() */ b = sc->sc_recchan.bytes_per_frame * UAUDIO_NFRAMES * UAUDIO_NCHANBUFS; } if (b <= 0) b = 1; blk = blk <= b ? b : blk / b * b; #ifdef DIAGNOSTIC if (blk <= 0) { printf("uaudio_round_blocksize: blk=%d\n", blk); blk = 512; } #endif DPRINTF(("uaudio_round_blocksize: resultant blk=%d\n", blk)); return blk; } static int uaudio_get_props(void *addr) { return AUDIO_PROP_FULLDUPLEX | AUDIO_PROP_INDEPENDENT; } #endif /* NetBSD or OpenBSD */ static int uaudio_get(struct uaudio_softc *sc, int which, int type, int wValue, int wIndex, int len) { usb_device_request_t req; uint8_t data[4]; usbd_status err; int val; #if defined(__FreeBSD__) if (sc->sc_dying) return EIO; #endif if (wValue == -1) return 0; req.bmRequestType = type; req.bRequest = which; USETW(req.wValue, wValue); USETW(req.wIndex, wIndex); USETW(req.wLength, len); DPRINTFN(2,("uaudio_get: type=0x%02x req=0x%02x wValue=0x%04x " "wIndex=0x%04x len=%d\n", type, which, wValue, wIndex, len)); #if defined(__FreeBSD__) if (sc->async != 0) err = usbd_do_request_async(sc->sc_udev, &req, data); else #endif err = usbd_do_request(sc->sc_udev, &req, data); if (err) { DPRINTF(("uaudio_get: err=%s\n", usbd_errstr(err))); return -1; } switch (len) { case 1: val = data[0]; break; case 2: val = data[0] | (data[1] << 8); break; default: DPRINTF(("uaudio_get: bad length=%d\n", len)); return -1; } DPRINTFN(2,("uaudio_get: val=%d\n", val)); return val; } static void uaudio_set(struct uaudio_softc *sc, int which, int type, int wValue, int wIndex, int len, int val) { usb_device_request_t req; uint8_t data[4]; usbd_status err; #if defined(__FreeBSD__) if (sc->sc_dying) return; #endif if (wValue == -1) return; req.bmRequestType = type; req.bRequest = which; USETW(req.wValue, wValue); USETW(req.wIndex, wIndex); USETW(req.wLength, len); switch (len) { case 1: data[0] = val; break; case 2: data[0] = val; data[1] = val >> 8; break; default: return; } DPRINTFN(2,("uaudio_set: type=0x%02x req=0x%02x wValue=0x%04x " "wIndex=0x%04x len=%d, val=%d\n", type, which, wValue, wIndex, len, val & 0xffff)); #if defined(__FreeBSD__) if (sc->async != 0) err = usbd_do_request_async(sc->sc_udev, &req, data); else #endif err = usbd_do_request(sc->sc_udev, &req, data); #ifdef USB_DEBUG if (err) DPRINTF(("uaudio_set: err=%d\n", err)); #endif } static int uaudio_signext(int type, int val) { if (!MIX_UNSIGNED(type)) { if (MIX_SIZE(type) == 2) val = (int16_t)val; else val = (int8_t)val; } return val; } #if defined(__NetBSD__) || defined(__OpenBSD__) static int uaudio_value2bsd(struct mixerctl *mc, int val) { DPRINTFN(5, ("uaudio_value2bsd: type=%03x val=%d min=%d max=%d ", mc->type, val, mc->minval, mc->maxval)); if (mc->type == MIX_ON_OFF) { val = (val != 0); } else if (mc->type == MIX_SELECTOR) { if (val < mc->minval || val > mc->maxval) val = mc->minval; } else val = ((uaudio_signext(mc->type, val) - mc->minval) * 255 + mc->mul/2) / mc->mul; DPRINTFN(5, ("val'=%d\n", val)); return val; } #endif int uaudio_bsd2value(struct mixerctl *mc, int val) { DPRINTFN(5,("uaudio_bsd2value: type=%03x val=%d min=%d max=%d ", mc->type, val, mc->minval, mc->maxval)); if (mc->type == MIX_ON_OFF) { val = (val != 0); } else if (mc->type == MIX_SELECTOR) { if (val < mc->minval || val > mc->maxval) val = mc->minval; } else val = (val + mc->delta/2) * mc->mul / 255 + mc->minval; DPRINTFN(5, ("val'=%d\n", val)); return val; } #if defined(__NetBSD__) || defined(__OpenBSD__) static int uaudio_ctl_get(struct uaudio_softc *sc, int which, struct mixerctl *mc, int chan) { int val; DPRINTFN(5,("uaudio_ctl_get: which=%d chan=%d\n", which, chan)); val = uaudio_get(sc, which, UT_READ_CLASS_INTERFACE, mc->wValue[chan], mc->wIndex, MIX_SIZE(mc->type)); return uaudio_value2bsd(mc, val); } #endif static void uaudio_ctl_set(struct uaudio_softc *sc, int which, struct mixerctl *mc, int chan, int val) { val = uaudio_bsd2value(mc, val); uaudio_set(sc, which, UT_WRITE_CLASS_INTERFACE, mc->wValue[chan], mc->wIndex, MIX_SIZE(mc->type), val); } #if defined(__NetBSD__) || defined(__OpenBSD__) static int uaudio_mixer_get_port(void *addr, mixer_ctrl_t *cp) { struct uaudio_softc *sc; struct mixerctl *mc; int i, n, vals[MIX_MAX_CHAN], val; DPRINTFN(2,("uaudio_mixer_get_port: index=%d\n", cp->dev)); sc = addr; if (sc->sc_dying) return EIO; n = cp->dev - UAC_NCLASSES; if (n < 0 || n >= sc->sc_nctls) return ENXIO; mc = &sc->sc_ctls[n]; if (mc->type == MIX_ON_OFF) { if (cp->type != AUDIO_MIXER_ENUM) return EINVAL; cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0); } else if (mc->type == MIX_SELECTOR) { if (cp->type != AUDIO_MIXER_ENUM) return EINVAL; cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0); } else { if (cp->type != AUDIO_MIXER_VALUE) return (EINVAL); if (cp->un.value.num_channels != 1 && cp->un.value.num_channels != mc->nchan) return EINVAL; for (i = 0; i < mc->nchan; i++) vals[i] = uaudio_ctl_get(sc, GET_CUR, mc, i); if (cp->un.value.num_channels == 1 && mc->nchan != 1) { for (val = 0, i = 0; i < mc->nchan; i++) val += vals[i]; vals[0] = val / mc->nchan; } for (i = 0; i < cp->un.value.num_channels; i++) cp->un.value.level[i] = vals[i]; } return 0; } static int uaudio_mixer_set_port(void *addr, mixer_ctrl_t *cp) { struct uaudio_softc *sc; struct mixerctl *mc; int i, n, vals[MIX_MAX_CHAN]; DPRINTFN(2,("uaudio_mixer_set_port: index = %d\n", cp->dev)); sc = addr; if (sc->sc_dying) return EIO; n = cp->dev - UAC_NCLASSES; if (n < 0 || n >= sc->sc_nctls) return ENXIO; mc = &sc->sc_ctls[n]; if (mc->type == MIX_ON_OFF) { if (cp->type != AUDIO_MIXER_ENUM) return EINVAL; uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord); } else if (mc->type == MIX_SELECTOR) { if (cp->type != AUDIO_MIXER_ENUM) return EINVAL; uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord); } else { if (cp->type != AUDIO_MIXER_VALUE) return EINVAL; if (cp->un.value.num_channels == 1) for (i = 0; i < mc->nchan; i++) vals[i] = cp->un.value.level[0]; else if (cp->un.value.num_channels == mc->nchan) for (i = 0; i < mc->nchan; i++) vals[i] = cp->un.value.level[i]; else return EINVAL; for (i = 0; i < mc->nchan; i++) uaudio_ctl_set(sc, SET_CUR, mc, i, vals[i]); } return 0; } static int uaudio_trigger_input(void *addr, void *start, void *end, int blksize, void (*intr)(void *), void *arg, struct audio_params *param) { struct uaudio_softc *sc; struct chan *ch; usbd_status err; int i, s; sc = addr; if (sc->sc_dying) return EIO; DPRINTFN(3,("uaudio_trigger_input: sc=%p start=%p end=%p " "blksize=%d\n", sc, start, end, blksize)); ch = &sc->sc_recchan; uaudio_chan_set_param(ch, start, end, blksize); DPRINTFN(3,("uaudio_trigger_input: sample_size=%d bytes/frame=%d " "fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame, ch->fraction)); err = uaudio_chan_alloc_buffers(sc, ch); if (err) return EIO; err = uaudio_chan_open(sc, ch); if (err) { uaudio_chan_free_buffers(sc, ch); return EIO; } ch->intr = intr; ch->arg = arg; s = splusb(); for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX -1 shouldn't be needed */ uaudio_chan_rtransfer(ch); splx(s); return 0; } static int uaudio_trigger_output(void *addr, void *start, void *end, int blksize, void (*intr)(void *), void *arg, struct audio_params *param) { struct uaudio_softc *sc; struct chan *ch; usbd_status err; int i, s; sc = addr; if (sc->sc_dying) return EIO; DPRINTFN(3,("uaudio_trigger_output: sc=%p start=%p end=%p " "blksize=%d\n", sc, start, end, blksize)); ch = &sc->sc_playchan; uaudio_chan_set_param(ch, start, end, blksize); DPRINTFN(3,("uaudio_trigger_output: sample_size=%d bytes/frame=%d " "fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame, ch->fraction)); err = uaudio_chan_alloc_buffers(sc, ch); if (err) return EIO; err = uaudio_chan_open(sc, ch); if (err) { uaudio_chan_free_buffers(sc, ch); return EIO; } ch->intr = intr; ch->arg = arg; s = splusb(); for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX */ uaudio_chan_ptransfer(ch); splx(s); return 0; } #endif /* NetBSD or OpenBSD */ /* Set up a pipe for a channel. */ static usbd_status uaudio_chan_open(struct uaudio_softc *sc, struct chan *ch) { struct as_info *as; int endpt; #if defined(__FreeBSD__) int locked; #endif usbd_status err; #if defined(__FreeBSD__) if (sc->sc_dying) return EIO; #endif as = &sc->sc_alts[ch->altidx]; endpt = as->edesc->bEndpointAddress; DPRINTF(("uaudio_chan_open: endpt=0x%02x, speed=%d, alt=%d\n", endpt, ch->sample_rate, as->alt)); #if defined(__FreeBSD__) locked = (ch->pcm_ch != NULL && mtx_owned(ch->pcm_ch->lock)) ? 1 : 0; if (locked) CHN_UNLOCK(ch->pcm_ch); #endif /* Set alternate interface corresponding to the mode. */ err = usbd_set_interface(as->ifaceh, as->alt); #if defined(__FreeBSD__) if (locked) CHN_LOCK(ch->pcm_ch); #endif if (err) return err; /* * If just one sampling rate is supported, * no need to call uaudio_set_speed(). * Roland SD-90 freezes by a SAMPLING_FREQ_CONTROL request. */ if (as->asf1desc->bSamFreqType != 1) { err = uaudio_set_speed(sc, endpt, ch->sample_rate); if (err) { DPRINTF(("uaudio_chan_open: set_speed failed err=%s\n", usbd_errstr(err))); } } ch->pipe = 0; ch->sync_pipe = 0; DPRINTF(("uaudio_chan_open: create pipe to 0x%02x\n", endpt)); err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->pipe); if (err) return err; if (as->edesc1 != NULL) { endpt = as->edesc1->bEndpointAddress; DPRINTF(("uaudio_chan_open: create sync-pipe to 0x%02x\n", endpt)); err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->sync_pipe); } return err; } static void uaudio_chan_close(struct uaudio_softc *sc, struct chan *ch) { struct as_info *as; #if defined(__FreeBSD__) int locked; if (sc->sc_dying) return ; #endif as = &sc->sc_alts[ch->altidx]; as->sc_busy = 0; #if defined(__FreeBSD__) locked = (ch->pcm_ch != NULL && mtx_owned(ch->pcm_ch->lock)) ? 1 : 0; if (locked) CHN_UNLOCK(ch->pcm_ch); #endif if (sc->sc_nullalt >= 0) { DPRINTF(("uaudio_chan_close: set null alt=%d\n", sc->sc_nullalt)); /* * The interface will be initialized later again, so an * error does not hurt. */ (void)usbd_set_interface(as->ifaceh, sc->sc_nullalt); } if (ch->pipe) { usbd_abort_pipe(ch->pipe); usbd_close_pipe(ch->pipe); } if (ch->sync_pipe) { usbd_abort_pipe(ch->sync_pipe); usbd_close_pipe(ch->sync_pipe); } #if defined(__FreeBSD__) if (locked) CHN_LOCK(ch->pcm_ch); #endif } static usbd_status uaudio_chan_alloc_buffers(struct uaudio_softc *sc, struct chan *ch) { usbd_xfer_handle xfer; void *buf; int i, size; size = (ch->bytes_per_frame + ch->sample_size) * UAUDIO_NFRAMES; for (i = 0; i < UAUDIO_NCHANBUFS; i++) { xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == 0) goto bad; ch->chanbufs[i].xfer = xfer; buf = usbd_alloc_buffer(xfer, size); if (buf == 0) { i++; goto bad; } ch->chanbufs[i].buffer = buf; ch->chanbufs[i].chan = ch; } return USBD_NORMAL_COMPLETION; bad: while (--i >= 0) /* implicit buffer free */ usbd_free_xfer(ch->chanbufs[i].xfer); return USBD_NOMEM; } static void uaudio_chan_free_buffers(struct uaudio_softc *sc, struct chan *ch) { int i; for (i = 0; i < UAUDIO_NCHANBUFS; i++) usbd_free_xfer(ch->chanbufs[i].xfer); } /* Called at splusb() */ static void uaudio_chan_ptransfer(struct chan *ch) { struct chanbuf *cb; int i, n, size, residue, total; if (ch->sc->sc_dying) return; /* Pick the next channel buffer. */ cb = &ch->chanbufs[ch->curchanbuf]; if (++ch->curchanbuf >= UAUDIO_NCHANBUFS) ch->curchanbuf = 0; /* Compute the size of each frame in the next transfer. */ residue = ch->residue; total = 0; for (i = 0; i < UAUDIO_NFRAMES; i++) { size = ch->bytes_per_frame; residue += ch->fraction; if (residue >= USB_FRAMES_PER_SECOND) { if ((ch->sc->sc_altflags & UA_NOFRAC) == 0) size += ch->sample_size; residue -= USB_FRAMES_PER_SECOND; } cb->sizes[i] = size; total += size; } ch->residue = residue; cb->size = total; /* * Transfer data from upper layer buffer to channel buffer, taking * care of wrapping the upper layer buffer. */ n = min(total, ch->end - ch->cur); memcpy(cb->buffer, ch->cur, n); ch->cur += n; if (ch->cur >= ch->end) ch->cur = ch->start; if (total > n) { total -= n; memcpy(cb->buffer + n, ch->cur, total); ch->cur += total; } #ifdef USB_DEBUG if (uaudiodebug > 8) { DPRINTF(("uaudio_chan_ptransfer: buffer=%p, residue=0.%03d\n", cb->buffer, ch->residue)); for (i = 0; i < UAUDIO_NFRAMES; i++) { DPRINTF((" [%d] length %d\n", i, cb->sizes[i])); } } #endif DPRINTFN(5,("uaudio_chan_transfer: ptransfer xfer=%p\n", cb->xfer)); /* Fill the request */ usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, UAUDIO_NFRAMES, USBD_NO_COPY, uaudio_chan_pintr); (void)usbd_transfer(cb->xfer); } static void uaudio_chan_pintr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct chanbuf *cb; struct chan *ch; u_int32_t count; int s; cb = priv; ch = cb->chan; /* Return if we are aborting. */ if (status == USBD_CANCELLED) return; usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); DPRINTFN(5,("uaudio_chan_pintr: count=%d, transferred=%d\n", count, ch->transferred)); #ifdef DIAGNOSTIC if (count != cb->size) { printf("uaudio_chan_pintr: count(%d) != size(%d)\n", count, cb->size); } #endif ch->transferred += cb->size; #if defined(__FreeBSD__) /* s = spltty(); */ s = splhigh(); chn_intr(ch->pcm_ch); splx(s); #else s = splaudio(); /* Call back to upper layer */ while (ch->transferred >= ch->blksize) { ch->transferred -= ch->blksize; DPRINTFN(5,("uaudio_chan_pintr: call %p(%p)\n", ch->intr, ch->arg)); ch->intr(ch->arg); } splx(s); #endif /* start next transfer */ uaudio_chan_ptransfer(ch); } /* Called at splusb() */ static void uaudio_chan_rtransfer(struct chan *ch) { struct chanbuf *cb; int i, size, residue, total; if (ch->sc->sc_dying) return; /* Pick the next channel buffer. */ cb = &ch->chanbufs[ch->curchanbuf]; if (++ch->curchanbuf >= UAUDIO_NCHANBUFS) ch->curchanbuf = 0; /* Compute the size of each frame in the next transfer. */ residue = ch->residue; total = 0; for (i = 0; i < UAUDIO_NFRAMES; i++) { size = ch->bytes_per_frame; cb->sizes[i] = size; cb->offsets[i] = total; total += size; } ch->residue = residue; cb->size = total; #ifdef USB_DEBUG if (uaudiodebug > 8) { DPRINTF(("uaudio_chan_rtransfer: buffer=%p, residue=0.%03d\n", cb->buffer, ch->residue)); for (i = 0; i < UAUDIO_NFRAMES; i++) { DPRINTF((" [%d] length %d\n", i, cb->sizes[i])); } } #endif DPRINTFN(5,("uaudio_chan_rtransfer: transfer xfer=%p\n", cb->xfer)); /* Fill the request */ usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, UAUDIO_NFRAMES, USBD_NO_COPY, uaudio_chan_rintr); (void)usbd_transfer(cb->xfer); } static void uaudio_chan_rintr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct chanbuf *cb = priv; struct chan *ch = cb->chan; u_int32_t count; int s, i, n, frsize; /* Return if we are aborting. */ if (status == USBD_CANCELLED) return; usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); DPRINTFN(5,("uaudio_chan_rintr: count=%d, transferred=%d\n", count, ch->transferred)); /* count < cb->size is normal for asynchronous source */ #ifdef DIAGNOSTIC if (count > cb->size) { printf("uaudio_chan_rintr: count(%d) > size(%d)\n", count, cb->size); } #endif /* * Transfer data from channel buffer to upper layer buffer, taking * care of wrapping the upper layer buffer. */ for(i = 0; i < UAUDIO_NFRAMES; i++) { frsize = cb->sizes[i]; n = min(frsize, ch->end - ch->cur); memcpy(ch->cur, cb->buffer + cb->offsets[i], n); ch->cur += n; if (ch->cur >= ch->end) ch->cur = ch->start; if (frsize > n) { memcpy(ch->cur, cb->buffer + cb->offsets[i] + n, frsize - n); ch->cur += frsize - n; } } /* Call back to upper layer */ ch->transferred += count; #if defined(__FreeBSD__) s = spltty(); chn_intr(ch->pcm_ch); splx(s); #else s = splaudio(); while (ch->transferred >= ch->blksize) { ch->transferred -= ch->blksize; DPRINTFN(5,("uaudio_chan_rintr: call %p(%p)\n", ch->intr, ch->arg)); ch->intr(ch->arg); } splx(s); #endif /* start next transfer */ uaudio_chan_rtransfer(ch); } #if defined(__NetBSD__) || defined(__OpenBSD__) static void uaudio_chan_init(struct chan *ch, int altidx, const struct audio_params *param, int maxpktsize) { int samples_per_frame, sample_size; ch->altidx = altidx; sample_size = param->precision * param->factor * param->hw_channels / 8; samples_per_frame = param->hw_sample_rate / USB_FRAMES_PER_SECOND; ch->sample_size = sample_size; ch->sample_rate = param->hw_sample_rate; if (maxpktsize == 0) { ch->fraction = param->hw_sample_rate % USB_FRAMES_PER_SECOND; ch->bytes_per_frame = samples_per_frame * sample_size; } else { ch->fraction = 0; ch->bytes_per_frame = maxpktsize; } ch->residue = 0; } static void uaudio_chan_set_param(struct chan *ch, u_char *start, u_char *end, int blksize) { ch->start = start; ch->end = end; ch->cur = start; ch->blksize = blksize; ch->transferred = 0; ch->curchanbuf = 0; } static void uaudio_get_minmax_rates(int nalts, const struct as_info *alts, const struct audio_params *p, int mode, u_long *min, u_long *max) { const struct usb_audio_streaming_type1_descriptor *a1d; int i, j; *min = ULONG_MAX; *max = 0; for (i = 0; i < nalts; i++) { a1d = alts[i].asf1desc; if (alts[i].sc_busy) continue; if (p->hw_channels != a1d->bNrChannels) continue; if (p->hw_precision != a1d->bBitResolution) continue; if (p->hw_encoding != alts[i].encoding) continue; if (mode != UE_GET_DIR(alts[i].edesc->bEndpointAddress)) continue; if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) { DPRINTFN(2,("uaudio_get_minmax_rates: cont %d-%d\n", UA_SAMP_LO(a1d), UA_SAMP_HI(a1d))); if (UA_SAMP_LO(a1d) < *min) *min = UA_SAMP_LO(a1d); if (UA_SAMP_HI(a1d) > *max) *max = UA_SAMP_HI(a1d); } else { for (j = 0; j < a1d->bSamFreqType; j++) { DPRINTFN(2,("uaudio_get_minmax_rates: disc #%d: %d\n", j, UA_GETSAMP(a1d, j))); if (UA_GETSAMP(a1d, j) < *min) *min = UA_GETSAMP(a1d, j); if (UA_GETSAMP(a1d, j) > *max) *max = UA_GETSAMP(a1d, j); } } } } static int uaudio_match_alt_sub(int nalts, const struct as_info *alts, const struct audio_params *p, int mode, u_long rate) { const struct usb_audio_streaming_type1_descriptor *a1d; int i, j; DPRINTF(("uaudio_match_alt_sub: search for %luHz %dch\n", rate, p->hw_channels)); for (i = 0; i < nalts; i++) { a1d = alts[i].asf1desc; if (alts[i].sc_busy) continue; if (p->hw_channels != a1d->bNrChannels) continue; if (p->hw_precision != a1d->bBitResolution) continue; if (p->hw_encoding != alts[i].encoding) continue; if (mode != UE_GET_DIR(alts[i].edesc->bEndpointAddress)) continue; if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) { DPRINTFN(3,("uaudio_match_alt_sub: cont %d-%d\n", UA_SAMP_LO(a1d), UA_SAMP_HI(a1d))); if (UA_SAMP_LO(a1d) <= rate && rate <= UA_SAMP_HI(a1d)) return i; } else { for (j = 0; j < a1d->bSamFreqType; j++) { DPRINTFN(3,("uaudio_match_alt_sub: disc #%d: %d\n", j, UA_GETSAMP(a1d, j))); /* XXX allow for some slack */ if (UA_GETSAMP(a1d, j) == rate) return i; } } } return -1; } static int uaudio_match_alt_chan(int nalts, const struct as_info *alts, struct audio_params *p, int mode) { int i, n; u_long min, max; u_long rate; /* Exact match */ DPRINTF(("uaudio_match_alt_chan: examine %ldHz %dch %dbit.\n", p->sample_rate, p->hw_channels, p->hw_precision)); i = uaudio_match_alt_sub(nalts, alts, p, mode, p->sample_rate); if (i >= 0) return i; uaudio_get_minmax_rates(nalts, alts, p, mode, &min, &max); DPRINTF(("uaudio_match_alt_chan: min=%lu max=%lu\n", min, max)); if (max <= 0) return -1; /* Search for biggers */ n = 2; while ((rate = p->sample_rate * n++) <= max) { i = uaudio_match_alt_sub(nalts, alts, p, mode, rate); if (i >= 0) { p->hw_sample_rate = rate; return i; } } if (p->sample_rate >= min) { i = uaudio_match_alt_sub(nalts, alts, p, mode, max); if (i >= 0) { p->hw_sample_rate = max; return i; } } else { i = uaudio_match_alt_sub(nalts, alts, p, mode, min); if (i >= 0) { p->hw_sample_rate = min; return i; } } return -1; } static int uaudio_match_alt(int nalts, const struct as_info *alts, struct audio_params *p, int mode) { int i, n; mode = mode == AUMODE_PLAY ? UE_DIR_OUT : UE_DIR_IN; i = uaudio_match_alt_chan(nalts, alts, p, mode); if (i >= 0) return i; for (n = p->channels + 1; n <= AUDIO_MAX_CHANNELS; n++) { p->hw_channels = n; i = uaudio_match_alt_chan(nalts, alts, p, mode); if (i >= 0) return i; } if (p->channels != 2) return -1; p->hw_channels = 1; return uaudio_match_alt_chan(nalts, alts, p, mode); } static int uaudio_set_params(void *addr, int setmode, int usemode, struct audio_params *play, struct audio_params *rec) { struct uaudio_softc *sc; int flags; int factor; int enc, i; int paltidx, raltidx; void (*swcode)(void *, u_char *buf, int cnt); struct audio_params *p; int mode; sc = addr; flags = sc->sc_altflags; paltidx = -1; raltidx = -1; if (sc->sc_dying) return EIO; if (((usemode & AUMODE_PLAY) && sc->sc_playchan.pipe != NULL) || ((usemode & AUMODE_RECORD) && sc->sc_recchan.pipe != NULL)) return EBUSY; if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1) sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 0; if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1) sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 0; /* Some uaudio devices are unidirectional. Don't try to find a matching mode for the unsupported direction. */ setmode &= sc->sc_mode; for (mode = AUMODE_RECORD; mode != -1; mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { if ((setmode & mode) == 0) continue; p = (mode == AUMODE_PLAY) ? play : rec; factor = 1; swcode = 0; enc = p->encoding; switch (enc) { case AUDIO_ENCODING_SLINEAR_BE: /* FALLTHROUGH */ case AUDIO_ENCODING_SLINEAR_LE: if (enc == AUDIO_ENCODING_SLINEAR_BE && p->precision == 16 && (flags & HAS_16)) { swcode = swap_bytes; enc = AUDIO_ENCODING_SLINEAR_LE; } else if (p->precision == 8) { if (flags & HAS_8) { /* No conversion */ } else if (flags & HAS_8U) { swcode = change_sign8; enc = AUDIO_ENCODING_ULINEAR_LE; } else if (flags & HAS_16) { factor = 2; p->hw_precision = 16; if (mode == AUMODE_PLAY) swcode = linear8_to_linear16_le; else swcode = linear16_to_linear8_le; } } break; case AUDIO_ENCODING_ULINEAR_BE: /* FALLTHROUGH */ case AUDIO_ENCODING_ULINEAR_LE: if (p->precision == 16) { if (enc == AUDIO_ENCODING_ULINEAR_LE) swcode = change_sign16_le; else if (mode == AUMODE_PLAY) swcode = swap_bytes_change_sign16_le; else swcode = change_sign16_swap_bytes_le; enc = AUDIO_ENCODING_SLINEAR_LE; } else if (p->precision == 8) { if (flags & HAS_8U) { /* No conversion */ } else if (flags & HAS_8) { swcode = change_sign8; enc = AUDIO_ENCODING_SLINEAR_LE; } else if (flags & HAS_16) { factor = 2; p->hw_precision = 16; enc = AUDIO_ENCODING_SLINEAR_LE; if (mode == AUMODE_PLAY) swcode = ulinear8_to_slinear16_le; else swcode = slinear16_to_ulinear8_le; } } break; case AUDIO_ENCODING_ULAW: if (flags & HAS_MULAW) break; if (flags & HAS_16) { if (mode == AUMODE_PLAY) swcode = mulaw_to_slinear16_le; else swcode = slinear16_to_mulaw_le; factor = 2; enc = AUDIO_ENCODING_SLINEAR_LE; p->hw_precision = 16; } else if (flags & HAS_8U) { if (mode == AUMODE_PLAY) swcode = mulaw_to_ulinear8; else swcode = ulinear8_to_mulaw; enc = AUDIO_ENCODING_ULINEAR_LE; } else if (flags & HAS_8) { if (mode == AUMODE_PLAY) swcode = mulaw_to_slinear8; else swcode = slinear8_to_mulaw; enc = AUDIO_ENCODING_SLINEAR_LE; } else return (EINVAL); break; case AUDIO_ENCODING_ALAW: if (flags & HAS_ALAW) break; if (mode == AUMODE_PLAY && (flags & HAS_16)) { swcode = alaw_to_slinear16_le; factor = 2; enc = AUDIO_ENCODING_SLINEAR_LE; p->hw_precision = 16; } else if (flags & HAS_8U) { if (mode == AUMODE_PLAY) swcode = alaw_to_ulinear8; else swcode = ulinear8_to_alaw; enc = AUDIO_ENCODING_ULINEAR_LE; } else if (flags & HAS_8) { if (mode == AUMODE_PLAY) swcode = alaw_to_slinear8; else swcode = slinear8_to_alaw; enc = AUDIO_ENCODING_SLINEAR_LE; } else return (EINVAL); break; default: return (EINVAL); } /* XXX do some other conversions... */ DPRINTF(("uaudio_set_params: chan=%d prec=%d enc=%d rate=%ld\n", p->channels, p->hw_precision, enc, p->sample_rate)); p->hw_encoding = enc; i = uaudio_match_alt(sc->sc_nalts, sc->sc_alts, p, mode); if (i < 0) return (EINVAL); p->sw_code = swcode; p->factor = factor; if (mode == AUMODE_PLAY) paltidx = i; else raltidx = i; } if ((setmode & AUMODE_PLAY)) { /* XXX abort transfer if currently happening? */ uaudio_chan_init(&sc->sc_playchan, paltidx, play, 0); } if ((setmode & AUMODE_RECORD)) { /* XXX abort transfer if currently happening? */ uaudio_chan_init(&sc->sc_recchan, raltidx, rec, UGETW(sc->sc_alts[raltidx].edesc->wMaxPacketSize)); } if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1) sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 1; if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1) sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 1; DPRINTF(("uaudio_set_params: use altidx=p%d/r%d, altno=p%d/r%d\n", sc->sc_playchan.altidx, sc->sc_recchan.altidx, (sc->sc_playchan.altidx >= 0) ?sc->sc_alts[sc->sc_playchan.altidx].idesc->bAlternateSetting : -1, (sc->sc_recchan.altidx >= 0) ? sc->sc_alts[sc->sc_recchan.altidx].idesc->bAlternateSetting : -1)); return 0; } #endif /* NetBSD or OpenBSD */ static usbd_status uaudio_set_speed(struct uaudio_softc *sc, int endpt, u_int speed) { usb_device_request_t req; uint8_t data[3]; DPRINTFN(5,("uaudio_set_speed: endpt=%d speed=%u\n", endpt, speed)); req.bmRequestType = UT_WRITE_CLASS_ENDPOINT; req.bRequest = SET_CUR; USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0); USETW(req.wIndex, endpt); USETW(req.wLength, 3); data[0] = speed; data[1] = speed >> 8; data[2] = speed >> 16; #if defined(__FreeBSD__) if (sc->async != 0) return usbd_do_request_async(sc->sc_udev, &req, data); #endif return usbd_do_request(sc->sc_udev, &req, data); } #if defined(__FreeBSD__) /************************************************************/ int uaudio_init_params(struct uaudio_softc *sc, struct chan *ch, int mode) { int i, j, enc; int samples_per_frame, sample_size; if ((sc->sc_playchan.pipe != NULL) || (sc->sc_recchan.pipe != NULL)) return (-1); switch(ch->format & 0x000FFFFF) { case AFMT_U8: enc = AUDIO_ENCODING_ULINEAR_LE; ch->precision = 8; break; case AFMT_S8: enc = AUDIO_ENCODING_SLINEAR_LE; ch->precision = 8; break; case AFMT_A_LAW: /* ? */ enc = AUDIO_ENCODING_ALAW; ch->precision = 8; break; case AFMT_MU_LAW: /* ? */ enc = AUDIO_ENCODING_ULAW; ch->precision = 8; break; case AFMT_S16_LE: enc = AUDIO_ENCODING_SLINEAR_LE; ch->precision = 16; break; case AFMT_S16_BE: enc = AUDIO_ENCODING_SLINEAR_BE; ch->precision = 16; break; case AFMT_U16_LE: enc = AUDIO_ENCODING_ULINEAR_LE; ch->precision = 16; break; case AFMT_U16_BE: enc = AUDIO_ENCODING_ULINEAR_BE; ch->precision = 16; break; case AFMT_S24_LE: enc = AUDIO_ENCODING_SLINEAR_LE; ch->precision = 24; break; case AFMT_S24_BE: enc = AUDIO_ENCODING_SLINEAR_BE; ch->precision = 24; break; case AFMT_U24_LE: enc = AUDIO_ENCODING_ULINEAR_LE; ch->precision = 24; break; case AFMT_U24_BE: enc = AUDIO_ENCODING_ULINEAR_BE; ch->precision = 24; break; case AFMT_S32_LE: enc = AUDIO_ENCODING_SLINEAR_LE; ch->precision = 32; break; case AFMT_S32_BE: enc = AUDIO_ENCODING_SLINEAR_BE; ch->precision = 32; break; case AFMT_U32_LE: enc = AUDIO_ENCODING_ULINEAR_LE; ch->precision = 32; break; case AFMT_U32_BE: enc = AUDIO_ENCODING_ULINEAR_BE; ch->precision = 32; break; default: enc = 0; ch->precision = 16; printf("Unknown format %x\n", ch->format); } if (ch->format & AFMT_STEREO) { ch->channels = 2; } else { ch->channels = 1; } /* for (mode = ...... */ for (i = 0; i < sc->sc_nalts; i++) { const struct usb_audio_streaming_type1_descriptor *a1d = sc->sc_alts[i].asf1desc; if (ch->channels == a1d->bNrChannels && ch->precision == a1d->bBitResolution && #if 0 enc == sc->sc_alts[i].encoding) { #else enc == sc->sc_alts[i].encoding && (mode == AUMODE_PLAY ? UE_DIR_OUT : UE_DIR_IN) == UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress)) { #endif if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) { DPRINTFN(2,("uaudio_set_params: cont %d-%d\n", UA_SAMP_LO(a1d), UA_SAMP_HI(a1d))); if (UA_SAMP_LO(a1d) <= ch->sample_rate && ch->sample_rate <= UA_SAMP_HI(a1d)) { if (mode == AUMODE_PLAY) sc->sc_playchan.altidx = i; else sc->sc_recchan.altidx = i; goto found; } } else { for (j = 0; j < a1d->bSamFreqType; j++) { DPRINTFN(2,("uaudio_set_params: disc #" "%d: %d\n", j, UA_GETSAMP(a1d, j))); /* XXX allow for some slack */ if (UA_GETSAMP(a1d, j) == ch->sample_rate) { if (mode == AUMODE_PLAY) sc->sc_playchan.altidx = i; else sc->sc_recchan.altidx = i; goto found; } } } } } /* return (EINVAL); */ if (mode == AUMODE_PLAY) printf("uaudio: This device can't play in rate=%d.\n", ch->sample_rate); else printf("uaudio: This device can't record in rate=%d.\n", ch->sample_rate); return (-1); found: #if 0 /* XXX */ p->sw_code = swcode; p->factor = factor; if (usemode == mode) sc->sc_curaltidx = i; #endif /* } */ sample_size = ch->precision * ch->channels / 8; samples_per_frame = ch->sample_rate / USB_FRAMES_PER_SECOND; ch->fraction = ch->sample_rate % USB_FRAMES_PER_SECOND; ch->sample_size = sample_size; ch->bytes_per_frame = samples_per_frame * sample_size; ch->residue = 0; ch->cur = ch->start; ch->transferred = 0; ch->curchanbuf = 0; return (0); } struct uaudio_conversion { uint8_t uaudio_fmt; uint8_t uaudio_prec; uint32_t freebsd_fmt; }; const struct uaudio_conversion const accepted_conversion[] = { {AUDIO_ENCODING_ULINEAR_LE, 8, AFMT_U8}, {AUDIO_ENCODING_ULINEAR_LE, 16, AFMT_U16_LE}, {AUDIO_ENCODING_ULINEAR_LE, 24, AFMT_U24_LE}, {AUDIO_ENCODING_ULINEAR_LE, 32, AFMT_U32_LE}, {AUDIO_ENCODING_ULINEAR_BE, 16, AFMT_U16_BE}, {AUDIO_ENCODING_ULINEAR_BE, 24, AFMT_U24_BE}, {AUDIO_ENCODING_ULINEAR_BE, 32, AFMT_U32_BE}, {AUDIO_ENCODING_SLINEAR_LE, 8, AFMT_S8}, {AUDIO_ENCODING_SLINEAR_LE, 16, AFMT_S16_LE}, {AUDIO_ENCODING_SLINEAR_LE, 24, AFMT_S24_LE}, {AUDIO_ENCODING_SLINEAR_LE, 32, AFMT_S32_LE}, {AUDIO_ENCODING_SLINEAR_BE, 16, AFMT_S16_BE}, {AUDIO_ENCODING_SLINEAR_BE, 24, AFMT_S24_BE}, {AUDIO_ENCODING_SLINEAR_BE, 32, AFMT_S32_BE}, {AUDIO_ENCODING_ALAW, 8, AFMT_A_LAW}, {AUDIO_ENCODING_ULAW, 8, AFMT_MU_LAW}, {0,0,0} }; unsigned uaudio_query_formats(device_t dev, int reqdir, unsigned maxfmt, struct pcmchan_caps *cap) { struct uaudio_softc *sc; const struct usb_audio_streaming_type1_descriptor *asf1d; const struct uaudio_conversion *iterator; unsigned fmtcount, foundcount; u_int32_t fmt; uint8_t format, numchan, subframesize, prec, dir, iscontinuous; int freq, freq_min, freq_max; char *numchannel_descr; char freq_descr[64]; int i,r; sc = device_get_softc(dev); if (sc == NULL) return 0; cap->minspeed = cap->maxspeed = 0; foundcount = fmtcount = 0; for (i = 0; i < sc->sc_nalts; i++) { dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress); if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY)) continue; asf1d = sc->sc_alts[i].asf1desc; format = sc->sc_alts[i].encoding; numchan = asf1d->bNrChannels; subframesize = asf1d->bSubFrameSize; prec = asf1d->bBitResolution; /* precision */ iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS; if (iscontinuous) snprintf(freq_descr, sizeof(freq_descr), "continous min %d max %d", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d)); else snprintf(freq_descr, sizeof(freq_descr), "fixed frequency (%d listed formats)", asf1d->bSamFreqType); if (numchan == 1) numchannel_descr = " (mono)"; else if (numchan == 2) numchannel_descr = " (stereo)"; else numchannel_descr = ""; if (bootverbose) { device_printf(dev, "uaudio_query_formats: found a native %s channel%s %s %dbit %dbytes/subframe X %d channels = %d bytes per sample\n", (dir==UE_DIR_OUT)?"playback":"record", numchannel_descr, freq_descr, prec, subframesize, numchan, subframesize*numchan); } /* * Now start rejecting the ones that don't map to FreeBSD */ if (numchan != 1 && numchan != 2) continue; for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++) if (iterator->uaudio_fmt == format && iterator->uaudio_prec == prec) break; if (iterator->uaudio_fmt == 0) continue; fmt = iterator->freebsd_fmt; if (numchan == 2) fmt |= AFMT_STEREO; foundcount++; if (fmtcount >= maxfmt) continue; cap->fmtlist[fmtcount++] = fmt; if (iscontinuous) { freq_min = UA_SAMP_LO(asf1d); freq_max = UA_SAMP_HI(asf1d); if (cap->minspeed == 0 || freq_min < cap->minspeed) cap->minspeed = freq_min; if (cap->maxspeed == 0) cap->maxspeed = cap->minspeed; if (freq_max > cap->maxspeed) cap->maxspeed = freq_max; } else { for (r = 0; r < asf1d->bSamFreqType; r++) { freq = UA_GETSAMP(asf1d, r); if (cap->minspeed == 0 || freq < cap->minspeed) cap->minspeed = freq; if (cap->maxspeed == 0) cap->maxspeed = cap->minspeed; if (freq > cap->maxspeed) cap->maxspeed = freq; } } } cap->fmtlist[fmtcount] = 0; return foundcount; } void uaudio_chan_set_param_pcm_dma_buff(device_t dev, u_char *start, u_char *end, struct pcm_channel *pc, int dir) { struct uaudio_softc *sc; struct chan *ch; sc = device_get_softc(dev); #ifndef NO_RECORDING if (dir == PCMDIR_PLAY) ch = &sc->sc_playchan; else ch = &sc->sc_recchan; #else ch = &sc->sc_playchan; #endif ch->start = start; ch->end = end; ch->pcm_ch = pc; return; } void uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize, int dir) { struct uaudio_softc *sc; struct chan *ch; sc = device_get_softc(dev); #ifndef NO_RECORDING if (dir == PCMDIR_PLAY) ch = &sc->sc_playchan; else ch = &sc->sc_recchan; #else ch = &sc->sc_playchan; #endif ch->blksize = blocksize; return; } int uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int reqdir) { const struct uaudio_conversion *iterator; struct uaudio_softc *sc; struct chan *ch; int i, r, score, hiscore, bestspeed; sc = device_get_softc(dev); #ifndef NO_RECORDING if (reqdir == PCMDIR_PLAY) ch = &sc->sc_playchan; else ch = &sc->sc_recchan; #else ch = &sc->sc_playchan; #endif /* * We are successful if we find an endpoint that matches our selected format and it * supports the requested speed. */ hiscore = 0; bestspeed = 1; for (i = 0; i < sc->sc_nalts; i++) { int dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress); int format = sc->sc_alts[i].encoding; const struct usb_audio_streaming_type1_descriptor *asf1d = sc->sc_alts[i].asf1desc; int iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS; if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY)) continue; for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++) if (iterator->uaudio_fmt != format || iterator->freebsd_fmt != (ch->format&0xfffffff)) continue; if (iscontinuous) { if (speed >= UA_SAMP_LO(asf1d) && speed <= UA_SAMP_HI(asf1d)) { ch->sample_rate = speed; return speed; } else if (speed < UA_SAMP_LO(asf1d)) { score = 0xfff * speed / UA_SAMP_LO(asf1d); if (score > hiscore) { bestspeed = UA_SAMP_LO(asf1d); hiscore = score; } } else if (speed > UA_SAMP_HI(asf1d)) { score = 0xfff * UA_SAMP_HI(asf1d) / speed; if (score > hiscore) { bestspeed = UA_SAMP_HI(asf1d); hiscore = score; } } continue; } for (r = 0; r < asf1d->bSamFreqType; r++) { if (speed == UA_GETSAMP(asf1d, r)) { ch->sample_rate = speed; return speed; } if (speed > UA_GETSAMP(asf1d, r)) score = 0xfff * UA_GETSAMP(asf1d, r) / speed; else score = 0xfff * speed / UA_GETSAMP(asf1d, r); if (score > hiscore) { bestspeed = UA_GETSAMP(asf1d, r); hiscore = score; } } } if (bestspeed != 1) { ch->sample_rate = bestspeed; return bestspeed; } return 0; } int uaudio_chan_getptr(device_t dev, int dir) { struct uaudio_softc *sc; struct chan *ch; int ptr; sc = device_get_softc(dev); #ifndef NO_RECORDING if (dir == PCMDIR_PLAY) ch = &sc->sc_playchan; else ch = &sc->sc_recchan; #else ch = &sc->sc_playchan; #endif ptr = ch->cur - ch->start; return ptr; } void uaudio_chan_set_param_format(device_t dev, u_int32_t format, int dir) { struct uaudio_softc *sc; struct chan *ch; sc = device_get_softc(dev); #ifndef NO_RECORDING if (dir == PCMDIR_PLAY) ch = &sc->sc_playchan; else ch = &sc->sc_recchan; #else ch = &sc->sc_playchan; #endif ch->format = format; return; } int uaudio_halt_out_dma(device_t dev) { struct uaudio_softc *sc; sc = device_get_softc(dev); DPRINTF(("uaudio_halt_out_dma: enter\n")); if (sc->sc_playchan.pipe != NULL) { uaudio_chan_close(sc, &sc->sc_playchan); sc->sc_playchan.pipe = 0; uaudio_chan_free_buffers(sc, &sc->sc_playchan); } return (0); } int uaudio_halt_in_dma(device_t dev) { struct uaudio_softc *sc; sc = device_get_softc(dev); if (sc->sc_dying) return (EIO); DPRINTF(("uaudio_halt_in_dma: enter\n")); if (sc->sc_recchan.pipe != NULL) { uaudio_chan_close(sc, &sc->sc_recchan); sc->sc_recchan.pipe = NULL; uaudio_chan_free_buffers(sc, &sc->sc_recchan); /* sc->sc_recchan.intr = NULL; */ } return (0); } int uaudio_trigger_input(device_t dev) { struct uaudio_softc *sc; struct chan *ch; usbd_status err; int i, s; sc = device_get_softc(dev); ch = &sc->sc_recchan; if (sc->sc_dying) return (EIO); /* uaudio_chan_set_param(ch, start, end, blksize) */ if (uaudio_init_params(sc, ch, AUMODE_RECORD)) return (EIO); err = uaudio_chan_alloc_buffers(sc, ch); if (err) return (EIO); err = uaudio_chan_open(sc, ch); if (err) { uaudio_chan_free_buffers(sc, ch); return (EIO); } /* ch->intr = intr; ch->arg = arg; */ s = splusb(); for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX -1 shouldn't be needed */ uaudio_chan_rtransfer(ch); splx(s); return (0); } int uaudio_trigger_output(device_t dev) { struct uaudio_softc *sc; struct chan *ch; usbd_status err; int i, s; sc = device_get_softc(dev); ch = &sc->sc_playchan; if (sc->sc_dying) return (EIO); if (uaudio_init_params(sc, ch, AUMODE_PLAY)) return (EIO); err = uaudio_chan_alloc_buffers(sc, ch); if (err) return (EIO); err = uaudio_chan_open(sc, ch); if (err) { uaudio_chan_free_buffers(sc, ch); return (EIO); } s = splusb(); for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX */ uaudio_chan_ptransfer(ch); splx(s); return (0); } u_int32_t uaudio_query_mix_info(device_t dev) { int i; u_int32_t mask = 0; struct uaudio_softc *sc; struct mixerctl *mc; sc = device_get_softc(dev); for (i=0; i < sc->sc_nctls; i++) { mc = &sc->sc_ctls[i]; if (mc->ctl != SOUND_MIXER_NRDEVICES) { /* Set device mask bits. See /usr/include/machine/soundcard.h */ mask |= (1 << mc->ctl); } } return mask; } u_int32_t uaudio_query_recsrc_info(device_t dev) { int i, rec_selector_id; u_int32_t mask = 0; struct uaudio_softc *sc; struct mixerctl *mc; sc = device_get_softc(dev); rec_selector_id = -1; for (i=0; i < sc->sc_nctls; i++) { mc = &sc->sc_ctls[i]; if (mc->ctl == SOUND_MIXER_NRDEVICES && mc->type == MIX_SELECTOR && mc->class == UAC_RECORD) { if (rec_selector_id == -1) { rec_selector_id = i; } else { printf("There are many selectors. Can't recognize which selector is a record source selector.\n"); return mask; } } } if (rec_selector_id == -1) return mask; mc = &sc->sc_ctls[rec_selector_id]; for (i = mc->minval; i <= mc->maxval; i++) { if (mc->slctrtype[i - 1] == SOUND_MIXER_NRDEVICES) continue; mask |= 1 << mc->slctrtype[i - 1]; } return mask; } void uaudio_mixer_set(device_t dev, unsigned type, unsigned left, unsigned right) { int i; struct uaudio_softc *sc; struct mixerctl *mc; sc = device_get_softc(dev); for (i=0; i < sc->sc_nctls; i++) { mc = &sc->sc_ctls[i]; if (mc->ctl == type) { if (mc->nchan == 2) { /* set Right */ uaudio_ctl_set(sc, SET_CUR, mc, 1, (int)(right*255)/100); } /* set Left or Mono */ uaudio_ctl_set(sc, SET_CUR, mc, 0, (int)(left*255)/100); } } return; } u_int32_t uaudio_mixer_setrecsrc(device_t dev, u_int32_t src) { int i, rec_selector_id; struct uaudio_softc *sc; struct mixerctl *mc; sc = device_get_softc(dev); rec_selector_id = -1; for (i=0; i < sc->sc_nctls; i++) { mc = &sc->sc_ctls[i]; if (mc->ctl == SOUND_MIXER_NRDEVICES && mc->type == MIX_SELECTOR && mc->class == UAC_RECORD) { if (rec_selector_id == -1) { rec_selector_id = i; } else { return src; /* Can't recognize which selector is record source selector */ } } } if (rec_selector_id == -1) return src; mc = &sc->sc_ctls[rec_selector_id]; for (i = mc->minval; i <= mc->maxval; i++) { if (src != (1 << mc->slctrtype[i - 1])) continue; uaudio_ctl_set(sc, SET_CUR, mc, 0, i); return (1 << mc->slctrtype[i - 1]); } uaudio_ctl_set(sc, SET_CUR, mc, 0, mc->minval); return (1 << mc->slctrtype[mc->minval - 1]); } static int uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) { struct snddev_info *d; - struct snddev_channel *sce; struct pcm_channel *c; struct pcm_feeder *f; - int pc, rc, vc; device_t pa_dev = device_get_parent(dev); struct uaudio_softc *sc = device_get_softc(pa_dev); if (verbose < 1) return 0; d = device_get_softc(dev); if (!d) return ENXIO; snd_mtxlock(d->lock); - if (SLIST_EMPTY(&d->channels)) { + if (CHN_EMPTY(d, channels.pcm)) { sbuf_printf(s, " (mixer only)"); snd_mtxunlock(d->lock); return 0; } - pc = rc = vc = 0; - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - if (c->direction == PCMDIR_PLAY) { - if (c->flags & CHN_F_VIRTUAL) - vc++; - else - pc++; - } else - rc++; - } - sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", - d->playcount, d->reccount, d->vchancount, + sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", + d->playcount, d->pvchancount, + d->reccount, d->rvchancount, (d->flags & SD_F_SIMPLEX)? "" : " duplex", #ifdef USING_DEVFS (device_get_unit(dev) == snd_unit)? " default" : "" #else "" #endif ); if (sc->uaudio_sndstat_flag != 0) { sbuf_cat(s, sbuf_data(&(sc->uaudio_sndstat))); } if (verbose <= 1) { snd_mtxunlock(d->lock); return 0; } - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; + CHN_FOREACH(c, d, channels.pcm) { sbuf_printf(s, "\n\t"); KASSERT(c->bufhard != NULL && c->bufsoft != NULL, ("hosed pcm channel setup")); /* it would be better to indent child channels */ sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); sbuf_printf(s, "spd %d", c->speed); if (c->speed != sndbuf_getspd(c->bufhard)) sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); sbuf_printf(s, ", fmt 0x%08x", c->format); if (c->format != sndbuf_getfmt(c->bufhard)) sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); if (c->pid != -1) sbuf_printf(s, ", pid %d", c->pid); sbuf_printf(s, "\n\t"); sbuf_printf(s, "interrupts %d, ", c->interrupts); if (c->direction == PCMDIR_REC) sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), sndbuf_getblkcnt(c->bufhard), sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), sndbuf_getblkcnt(c->bufsoft)); else sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), sndbuf_getblkcnt(c->bufhard), sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), sndbuf_getblkcnt(c->bufsoft)); sbuf_printf(s, "\n\t"); sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); sbuf_printf(s, " -> "); f = c->feeder; while (f->source != NULL) f = f->source; while (f != NULL) { sbuf_printf(s, "%s", f->class->name); if (f->desc->type == FEEDER_FMT) sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); if (f->desc->type == FEEDER_RATE) sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) sbuf_printf(s, "(0x%08x)", f->desc->out); sbuf_printf(s, " -> "); f = f->parent; } sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); } snd_mtxunlock(d->lock); return 0; } void uaudio_sndstat_register(device_t dev) { struct snddev_info *d = device_get_softc(dev); sndstat_register(dev, d->status, uaudio_sndstat_prepare_pcm); } int uaudio_get_vendor(device_t dev) { struct uaudio_softc *sc = device_get_softc(dev); if (sc == NULL) return 0; return sc->sc_vendor; } int uaudio_get_product(device_t dev) { struct uaudio_softc *sc = device_get_softc(dev); if (sc == NULL) return 0; return sc->sc_product; } int uaudio_get_release(device_t dev) { struct uaudio_softc *sc = device_get_softc(dev); if (sc == NULL) return 0; return sc->sc_release; } static int audio_attach_mi(device_t dev) { device_t child; struct sndcard_func *func; /* Attach the children. */ /* PCM Audio */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) return (ENOMEM); func->func = SCF_PCM; child = device_add_child(dev, "pcm", -1); device_set_ivars(child, func); bus_generic_attach(dev); return 0; /* XXXXX */ } DRIVER_MODULE(uaudio, uhub, uaudio_driver, uaudio_devclass, usbd_driver_load, 0); MODULE_VERSION(uaudio, 1); #endif Index: head/sys/modules/sound/sound/Makefile =================================================================== --- head/sys/modules/sound/sound/Makefile (revision 170160) +++ head/sys/modules/sound/sound/Makefile (revision 170161) @@ -1,40 +1,41 @@ # $FreeBSD$ +.PATH: ${.CURDIR}/../../../dev/sound .PATH: ${.CURDIR}/../../../dev/sound/pcm .PATH: ${.CURDIR}/../../../dev/sound/midi .PATH: ${.CURDIR}/../../../dev/sound/isa KMOD= sound SRCS= device_if.h bus_if.h isa_if.h pci_if.h opt_isa.h SRCS+= ac97_if.h channel_if.h feeder_if.h mixer_if.h SRCS+= ac97_if.c channel_if.c feeder_if.c mixer_if.c SRCS+= mpu_if.h mpufoi_if.h synth_if.h SRCS+= mpu_if.c mpufoi_if.c synth_if.c -SRCS+= ac97.c ac97_patch.c buffer.c channel.c dsp.c +SRCS+= ac97.c ac97_patch.c buffer.c channel.c clone.c dsp.c SRCS+= fake.c feeder.c feeder_fmt.c feeder_rate.c feeder_volume.c -SRCS+= mixer.c sndstat.c sound.c vchan.c +SRCS+= mixer.c sndstat.c sound.c unit.c vchan.c SRCS+= midi.c mpu401.c sequencer.c EXPORT_SYMS= YES # XXX evaluate .if ${MACHINE_ARCH} == "sparc64" # Create an empty opt_isa.h in order to keep kmod.mk from linking in an # existing one from KERNBUILDDIR which possibly has DEV_ISA defined so # sound.ko is always built without isadma support. opt_isa.h: :> ${.TARGET} .else .if !defined(KERNBUILDDIR) SRCS+= sndbuf_dma.c opt_isa.h: echo "#define DEV_ISA 1" > ${.TARGET} .else DEV_ISA!= sed -n '/DEV_ISA/p' ${KERNBUILDDIR}/opt_isa.h .if !empty(DEV_ISA) SRCS+= sndbuf_dma.c .endif .endif .endif .include