]> git.alsa-project.org Git - alsa-lib.git/commitdiff
ucm: add initial doxygen documenation
authorJaroslav Kysela <perex@perex.cz>
Sun, 19 Sep 2021 19:00:11 +0000 (21:00 +0200)
committerJaroslav Kysela <perex@perex.cz>
Wed, 1 Dec 2021 09:33:44 +0000 (10:33 +0100)
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 <perex@perex.cz>
doc/index.doxygen
src/ucm/Makefile.am
src/ucm/ucm_confdoc.h [new file with mode: 0644]

index cde3b94b6eca0182dd83383c4c87e60006bf64b1..fc2d6596811487bc3660bac8ec2d341dba127022 100644 (file)
@@ -1,57 +1,60 @@
-/*! \mainpage Index Preamble and License
+/*! \mainpage Index, Preamble and License
 
 \author Jaroslav Kysela <perex@perex.cz>
 \author Abramo Bagnara <abramo@alsa-project.org>
 \author Takashi Iwai <tiwai@suse.de>
 \author Frank van de Pol <fvdpol@coil.demon.nl>
 
-<H2>Preface</H2>
-<P>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.</P>
+it interfaces with the kernel API.
 
-<H2>Documentation License</H2>
+### Documentation License
 
-<P>This documentation is free; you can redistribute it without
-any restrictions. Modifications or derived work must retain
-the copyright and list all authors.</P>
+    This documentation is free; you can redistribute it without
+    any restrictions. Modifications or derived work must retain
+    the copyright and list all authors.
  
-<P>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.</P>
+    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
+---------
 
-<H2>API usage</H2>
-<P>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.</P>
-
-<H2>API links</H2>
-
-<UL>
-  <LI>Page \ref control explains the primitive controls API.
-  <LI>Page \ref control_plugins explains the design of primitive control plugins.
-  <LI>Page \ref hcontrol explains the high-level primitive controls API.
-  <LI>Page \ref mixer explains the mixer controls API.
-  <LI>Page \ref pcm explains the design of the PCM (digital audio) API.
-  <LI>Page \ref pcm_plugins explains the design of PCM (digital audio) plugins.
-  <LI>Page \ref pcm_external_plugins explains the external PCM plugin SDK.
-  <LI>Page \ref ctl_external_plugins explains the external control plugin SDK.
-  <LI>Page \ref rawmidi explains the design of the RawMidi API.
-  <LI>Page \ref timer explains the design of the Timer API.
-  <LI>Page \ref seq explains the design of the Sequencer API.
-  <LI>Page \ref ucm explains the use case API.
-  <LI>Page \ref topology explains the DSP topology API.
-</UL>
-
-<H2>Configuration</H2>
-
-<UL>
-  <LI>Page \ref conf explains the syntax of library configuration files.
-  <LI>Page \ref confarg explains the run-time argument syntax.
-  <LI>Page \ref conffunc explains run-time function definitions and their usage.
-  <LI>Page \ref confhooks explains run-time hook definitions and their usage.
-</UL>
+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.
 
 */
index 1f1d8b1f46056ec22f01269d4036c6b66313034b..7108968f1988368fbd7cb049d2cd45f91eddc609 100644 (file)
@@ -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 (file)
index 0000000..7241f5e
--- /dev/null
@@ -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 <perex@perex.cz>
+ */
+
+/**
+ *  \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 <joe@bloggs.org>
+
+# 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 <joe@blogs.com>
+
+# 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}
+<some/path/file.conf>        # include file using the search path
+</absolute/path/file.conf>   # 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:<str>}         | Environment variable <str>
+${sys:<str>}         | Contents of sysfs file <str>
+${var:<str>}         | UCM parser variable (set using a _Define_ block)
+${eval:<str>}        | Evaluate expression like *($var+2)/3* [**Syntax 5**]
+${find-card:<str>}   | Find a card - see _Find card substitution_ section
+${find-device:<str>} | 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 {
+    ...
+  }
+}
+~~~
+
+
+*/
+
+/**
+ \}
+ */