From 2623e4bb76820ff4b8310afa67a86806624445da Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 19 Sep 2021 21:00:11 +0200 Subject: [PATCH] ucm: add initial doxygen documenation This is an attempt to explain the UCM in a more verbose way. BugLink: https://github.com/alsa-project/alsa-ucm-conf/issues/103 Signed-off-by: Jaroslav Kysela --- doc/index.doxygen | 85 +++---- src/ucm/Makefile.am | 2 +- src/ucm/ucm_confdoc.h | 531 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 576 insertions(+), 42 deletions(-) create mode 100644 src/ucm/ucm_confdoc.h diff --git a/doc/index.doxygen b/doc/index.doxygen index cde3b94b..fc2d6596 100644 --- a/doc/index.doxygen +++ b/doc/index.doxygen @@ -1,57 +1,60 @@ -/*! \mainpage Index Preamble and License +/*! \mainpage Index, Preamble and License \author Jaroslav Kysela \author Abramo Bagnara \author Takashi Iwai \author Frank van de Pol -

Preface

-

The Advanced Linux Sound Architecture (\e ALSA) comes with a kernel +Preface +------- + +The Advanced Linux Sound Architecture (\e ALSA) comes with a kernel API and a library API. This document describes the library API and how -it interfaces with the kernel API.

+it interfaces with the kernel API. -

Documentation License

+### Documentation License -

This documentation is free; you can redistribute it without -any restrictions. Modifications or derived work must retain -the copyright and list all authors.

+ This documentation is free; you can redistribute it without + any restrictions. Modifications or derived work must retain + the copyright and list all authors. -

This documentation is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

+ This documentation is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +API usage +--------- -

API usage

-

Application programmers should use the library API rather than the +Application programmers should use the library API rather than the kernel API. The library offers 100% of the functionality of the kernel API, but adds major improvements in usability, making the application code simpler and better looking. In addition, future fixes or compatibility code -may be placed in the library code instead of the kernel driver.

- -

API links

- -
    -
  • Page \ref control explains the primitive controls API. -
  • Page \ref control_plugins explains the design of primitive control plugins. -
  • Page \ref hcontrol explains the high-level primitive controls API. -
  • Page \ref mixer explains the mixer controls API. -
  • Page \ref pcm explains the design of the PCM (digital audio) API. -
  • Page \ref pcm_plugins explains the design of PCM (digital audio) plugins. -
  • Page \ref pcm_external_plugins explains the external PCM plugin SDK. -
  • Page \ref ctl_external_plugins explains the external control plugin SDK. -
  • Page \ref rawmidi explains the design of the RawMidi API. -
  • Page \ref timer explains the design of the Timer API. -
  • Page \ref seq explains the design of the Sequencer API. -
  • Page \ref ucm explains the use case API. -
  • Page \ref topology explains the DSP topology API. -
- -

Configuration

- -
    -
  • Page \ref conf explains the syntax of library configuration files. -
  • Page \ref confarg explains the run-time argument syntax. -
  • Page \ref conffunc explains run-time function definitions and their usage. -
  • Page \ref confhooks explains run-time hook definitions and their usage. -
+may be placed in the library code instead of the kernel driver. + +API links +--------- + +- Page \subpage control explains the primitive controls API. +- Page \subpage control_plugins explains the design of primitive control plugins. +- Page \subpage hcontrol explains the high-level primitive controls API. +- Page \subpage mixer explains the mixer controls API. +- Page \subpage pcm explains the design of the PCM (digital audio) API. +- Page \subpage pcm_plugins explains the design of PCM (digital audio) plugins. +- Page \subpage pcm_external_plugins explains the external PCM plugin SDK. +- Page \subpage ctl_external_plugins explains the external control plugin SDK. +- Page \subpage rawmidi explains the design of the RawMidi API. +- Page \subpage timer explains the design of the Timer API. +- Page \subpage seq explains the design of the Sequencer API. +- Page \subpage ucm explains the use case API. +- Page \subpage topology explains the DSP topology API. + +Configuration +------------- + +- Page \subpage conf explains the syntax of library configuration. +- Page \subpage confarg explains the run-time argument syntax. +- Page \subpage conffunc explains run-time function definitions and their usage. +- Page \subpage confhooks explains run-time hook definitions and their usage. +- Page \subpage ucm_conf explains the UCM configuration and their usage. */ diff --git a/src/ucm/Makefile.am b/src/ucm/Makefile.am index 1f1d8b1f..7108968f 100644 --- a/src/ucm/Makefile.am +++ b/src/ucm/Makefile.am @@ -3,7 +3,7 @@ EXTRA_LTLIBRARIES = libucm.la libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c ucm_include.c \ ucm_regex.c ucm_exec.c main.c -noinst_HEADERS = ucm_local.h +noinst_HEADERS = ucm_local.h ucm_confdoc.h all: libucm.la diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h new file mode 100644 index 00000000..7241f5e8 --- /dev/null +++ b/src/ucm/ucm_confdoc.h @@ -0,0 +1,531 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Support for the verb/device/modifier core logic and API, + * command line tool and file parser was kindly sponsored by + * Texas Instruments Inc. + * Support for multiple active modifiers and devices, + * transition sequences, multiple client access and user defined use + * cases was kindly sponsored by Wolfson Microelectronics PLC. + * + * Copyright (C) 2021 Red Hat Inc. + * Authors: Jaroslav Kysela + */ + +/** + * \defgroup ucm_conf Use Case Configuration + * The ALSA Use Case Configuration. + * See \ref Usecase_conf page for more details. + * \{ + */ + +/*! \page Usecase_conf ALSA Use Case Configuration + +The use case configuration files use \ref conf syntax to define the +static configuration tree. This tree is evaluated (modified) at runtime +according the conditions and dynamic variables in the configuration tree. +The result is parsed and exported to the applications using \ref ucm API. + +### Configuration directory and main filename lookup + +The lookup paths are describen in *ucm.conf* file. The configuration +structure looks like: + +~~~{.html} +UseCasePath.path1 { + Directory "conf.virt.d" + File "${OpenName}.conf" +} +UseCasePath.path2 { + Directory "external" + File "${OpenName}.conf" +} +~~~ + +### UCM main configuration file + +Each sound card has a master sound card file that lists all the supported +use case verbs for that sound card. i.e.: + +~~~{.html} +# Example master file for blah sound card +# By Joe Blogs + +# Use Case name for user interface +Comment "Nice Abstracted Soundcard" + +# The file is divided into Use case sections. One section per use case verb. + +SectionUseCase."Voice Call" { + File "voice_call_blah" + Comment "Make a voice phone call." +} + +SectionUseCase."HiFi" { + File "hifi_blah" + Comment "Play and record HiFi quality Music." +} + +# Define Value defaults + +ValueDefaults { + PlaybackChannels 4 + CaptureChannels 4 +} + +# Define boot / initialization sequence +# This sequence is skipped, when the soundcard was already configured by system +# (alsactl configuration was already created). The purpose is to not alter +# ALSA card controls which may be modified by user after initial settings. + +BootSequence [ + cset "name='My control' on" +] + +# Define fixed boot sequence +# This sequence is always executed on boot (hotplug). + +FixedBootSequence [ + cset "name='Something to toggle' toggle" +] +~~~ + +### UCM verb configuration file + +The verb configuration file defines devices, modifier and initialization sequences. +It is something like a sound profile. + +~~~{.html} +# Example Use case verb section for Voice call blah +# By Joe Blogs + +# verb global section + +SectionVerb { + + # enable and disable sequences are compulsory + EnableSequence [ + cset "name='Master Playback Switch',index=2 0,0" + cset "name='Master Playback Volume',index=2 25,25" + msleep 50 + cset "name='Master Playback Switch',index=2 1,1" + cset "name='Master Playback Volume',index=2 50,50" + ] + + DisableSequence [ + cset "name='Master Playback Switch',index=2 0,0" + cset "name='Master Playback Volume',index=2 25,25" + msleep 50 + cset "name='Master Playback Switch',index=2 1,1" + cset "name='Master Playback Volume',index=2 50,50" + ] + + # Optional transition verb + TransitionSequence."ToCaseName" [ + msleep 1 + ] + + # Optional TQ and device values + Value { + TQ HiFi + PlaybackChannels 6 + } +} + +# Each device is described in new section. N devices are allowed + +SectionDevice."Headphones" { + + SupportedDevice [ + "x" + "y" + ] + + # or (not both) + + ConflictingDevice [ + "x" + "y" + ] + + EnableSequence [ + ... + ] + + DisableSequence [ + ... + ] + + TransitionSequence."ToDevice" [ + ... + ] + + Value { + PlaybackVolume "name='Master Playback Volume',index=2" + PlaybackSwitch "name='Master Playback Switch',index=2" + PlaybackPCM "hw:${CardId},4" + } +} + +# Each modifier is described in new section. N modifiers are allowed + +SectionModifier."Capture Voice" { + Comment "Record voice call" + + SupportedDevice [ + "x" + "y" + ] + + # or (not both) + + ConflictingDevice [ + "x" + "y" + ] + + EnableSequence [ + ... + ] + + DisableSequence [ + ... + ] + + TransitionSequence."ToModifierName" [ + ... + ] + + # Optional TQ and ALSA PCMs + Value { + TQ Voice + CapturePCM "hw:${CardId},11" + PlaybackVolume "name='Master Playback Volume',index=2" + PlaybackSwitch "name='Master Playback Switch',index=2" + } +} +~~~ + +### Sequence commands + +Command name | Description +---------------|---------------------------------------------- +cdev ARG | ALSA control device name for snd_ctl_open() +cset ARG | ALSA control set - snd_ctl_ascii_elem_id_parse() + snd_ctl_ascii_value_parse() +cset-new ARG | Create new ALSA user control element - snd_ctl_ascii_elem_id_parse() + description +ctl-remove ARG | Remove ALSA user control element - snd_ctl_ascii_elem_id_parse() +sysw ARG | write to sysfs tree +usleep ARG | sleep for specified amount of microseconds +msleep ARG | sleep for specified amount of milliseconds +exec ARG | execute a specific command (without shell - *man execv*) +shell ARG | execute a specific command (using shell - *man system*) +cfg-save ARG | save LibraryConfig to a file + +~~~{.html} +# Examples +cset "name='PCM Playback Volue',index=2 99" +cset-new "name='Bool2' type=bool,count=2 1,0" +cset-new "name='Enum' type=enum,labels='L1;L2;L3' 'L2'" +ctl-remove "name='Bool2'" +sysw "-/class/sound/ctl-led/speaker/card${CardNumber}/attach:Speaker Channel Switch" +usleep 10 +exec "/bin/echo hello" +shell "set" +cfg-save "/tmp/test.conf:+pcm" +~~~ + +### Naming (devices, verbs) + +See the SND_USE_CASE_VERB constains like #SND_USE_CASE_VERB_HIFI for the full list of known verbs. + +See the SND_USE_CASE_DEV constants like #SND_USE_CASE_DEV_SPEAKER for the full list of known devices. +If multiple devices with the same name exists, the number suffixes should +be added to these names like HDMI1,HDMI2,HDMI3 etc. No number gaps are +allowed. The names with numbers must be continuous. It is allowed to put +a whitespace between name and index (like 'Line 1') for the better +readability. The device names 'Line 1' and 'Line1' are equal for +this purpose. + +If EnableSequence/DisableSequence controls independent paths in the hardware +it is also recommended to split playback and capture UCM devices and use +the number suffixes. Example use case: Use the integrated microphone +in the laptop instead the microphone in headphones. + +The preference of the devices is determined by the priority value (higher value = higher priority). + +See the SND_USE_CASE_MOD constants like #SND_USE_CASE_MOD_ECHO_REF for the full list of known modifiers. + +### Boot (alsactl) + +The *FixedBootSequence* is executed at each boot. The *BootSequence* is executed only +if the card's configuration is missing. The purpose is to let the users modify the +configuration like volumes or switches. The alsactl ensures the persistency (store +the state of the controls to the /var tree and loads the previous state in the next +boot). + +### Device volume + +It is expected that the applications handle the volume settings. It is not recommended +to set the fixed values for the volume settings to the Enable / Disable sequences for +verbs or devices, if the device exports the hardware volume (MixerElem or Volume/Switch +values). The default volume settings should be set in the *BootSequence*. + +### Dynamic configuration tree + +The evaluation order may look a bit different from the user perspective. +At first, the standard alsa-lib configuration tree is parsed. All other +layers on top are working with this tree. It may shift / move the configuration +blocks from the configuration files as they are placed to the tree internally. + +~~~{.html} +Example configuration | Parsed static tree | Identical static tree +----------------------------+-------------------------+------------------------------- +If.1 { | If { | If.1.True.Define.VAR "A" + True.Define.VAR "A" | 1.True.Define.VAR "A" | If.2.True.Define.VAR "C" +} | 2.True.Define.VAR "C" | Define.VAR "B" +Define.VAR "B" | } | +If.2 { | Define.VAR "B" | + True.Define.VAR "C" | | +} | | +~~~ + +Even if one or both conditions are evaluated as true, the variable _VAR_ will +be evaluated always as **B** because the first _If_ block was before the non-nested +_Define_ . The second _If_ block was appended to the first _If_ block (before +_Define_ in the configuration tree) in the text configuration parser. + + +### Syntax + +Unless described, the syntax version 4 is used. + +~~~ +Syntax 4 +~~~ + + +### Include + +There are two ways to include other configuration files. + +#### Static include + +The static include is inherited from the standard alsa-lib configuration +syntax. It can be placed anywhere in the configuration files. The search +path is composed from the root alsa configuration path (usually +_/usr/share/alsa_) and _ucm2_ directory. + +~~~{.html} + # include file using the search path + # include file using the absolute path +~~~ + +#### Lazy include + +The lazy include is evaluated at runtime. The root path is the ucm2 +tree. The absolute include appends the ucm2 absolute path to the +specified path. The relative include is relative to the file which +contains the _Include_ configuration block. + +~~~{.html} +Include.id1.File "/some/path/file.conf" # absolute include (in the ucm2 tree) +Include.id2.File "subdir/file.conf" # relative include to the current configuration directory (UseCasePath) +~~~ + +### Configuration tree evaluation + +The evaluation of the static configuration tree is proceed in +the specific order (see table bellow). When the dynamic configuration +tree changes, the evaluation sequence is restarted to evaluate +all possible changes (new *Define* or *Include* or *If* blocks). + +Evaluation order | Configuration block | Evaluation restart +------------------:|---------------------|-------------------- +1 | Define | No +2 | Include | Yes +3 | If | Yes + + +### Substitutions + +The dynamic tree identifiers and assigned values in the configuration tree are +substituted. The substitutes strings are in the table bellow. + +Substituted string | Value +---------------------|--------------------- +${OpenName} | Original UCM card name (passed to snd_use_case_mgr_open()) +${ConfLibDir} | Library top-level configuration directory (e.g. /usr/share/alsa) +${ConfTopDir} | Top-level UCM configuration directory (e.g. /usr/share/alsa/ucm2) +${ConfDir} | Card's UCM configuration directory (e.g. /usr/share/alsa/ucm2/conf.d/USB-Audio) +${ConfName} | Configuration name (e.g. USB-Audio.conf) +${CardNumber} | Real ALSA card number (or empty string for the virtual UCM card) +${CardId} | ALSA card identifier (see snd_ctl_card_info_get_id()) +${CardDriver} | ALSA card driver (see snd_ctl_card_info_get_driver()) +${CardName} | ALSA card name (see snd_ctl_card_info_get_name()) +${CardLongName} | ALSA card long name (see snd_ctl_card_info_get_longname()) +${CardComponents} | ALSA card components (see snd_ctl_card_info_get_components()) +${env:} | Environment variable +${sys:} | Contents of sysfs file +${var:} | UCM parser variable (set using a _Define_ block) +${eval:} | Evaluate expression like *($var+2)/3* [**Syntax 5**] +${find-card:} | Find a card - see _Find card substitution_ section +${find-device:} | Find a device - see _Find device substitution_ section + +#### Find card substitution + +This substitutions finds the ALSA card and returns the appropriate identifier or +the card number (see return argument). + +Usage example: + +~~~{.html} +${find-card:field=name,regex='^acp$',return=number} +~~~ + +Arguments: + +Argument | Description +---------------------|----------------------- +return | return value type (id, number), id is the default +field | field for the lookup (id, driver, name, longname, mixername, components) +regex | regex string for the field match + +#### Find device substitution + +Usage example: + +~~~{.html} +${find-device:type=pcm,field=name,regex='DMIC'} +~~~ + +Arguments: + +Argument | Description +---------------------|----------------------- +type | device type (pcm) +stream | stream type (playback, capture), playback is default +field | field for the lookup (id, name, subname) +regex | regex string for the field match + + +### Variable defines + +The variables can be defined and altered with the *Define* or *DefineRegex* blocks. +The *Define* block looks like: + +~~~{.html} +Define { + variable1 "a" + variable2 "b" +} +~~~ + +The *DefineRegex* allows substring extraction like: + +~~~{.html} +DefineRegex.rval { + Regex "(hello)|(regex)" + String "hello, it's my regex" +} +~~~ + +The result will be stored to variables *rval1* as *hello* and *rval2* as *regex* (every matched +substrings are stored to a separate variable with the sequence number postfix. + +Variables can be substituted using the `${var:rval1}` reference for example. + +### Conditions + +The configuration tree evaluation supports the conditions - *If* blocks. Each *If* blocks +must define a *Condition* block and *True* or *False* blocks or both. The *True* or *False* +blocks will be merged to the parent tree (where the *If* block is defined) when +the *Condition* is evaluated. + +Example: + +~~~{.html} +If.uniqueid { + Condition { + Type String + Haystack "abcd" + Needle "a" + } + True { + Define.a a + define.b b + } +} +~~~ + +#### True (Type AlwaysTrue) + +Execute only *True* block. It may be used to change the evaluation order as +explained in the *Configuration Tree* paragraph. + +#### String is empty (Type String) + +Field | Description +---------------------|----------------------- +Empty | string + +#### Strings are equal (Type String) + +Field | Description +---------------------|----------------------- +String1 | string +String2 | substring in string + +#### Substring is present (Type String) + +Field | Description +---------------------|----------------------- +Haystack | string +Needle | substring in string + +#### Regex match (Type RegexMatch) + +Field | Description +---------------------|----------------------- +String | string +Regex | regex expression (extended posix, ignore case) + +#### ALSA control element exists (Type ControlExists) + +Field | Description +---------------------|----------------------- +Device | ALSA control device (see snd_ctl_open()) +Control | control in ASCII (parsed using snd_ctl_ascii_elem_id_parse()) +ControlEnum | value for the enum control (optional) + +Example: + +~~~{.html} +If.fmic { + Condition { + Type ControlExists + Control "name='Front Mic Playback Switch'" + } + True { + ... + } +} +~~~ + + +*/ + +/** + \} + */ -- 2.47.1