]> git.alsa-project.org Git - alsa-lib.git/commitdiff
Add detailed documentation for external control plugin SDK
authorTakashi Iwai <tiwai@suse.de>
Mon, 13 Jun 2005 13:19:03 +0000 (13:19 +0000)
committerTakashi Iwai <tiwai@suse.de>
Mon, 13 Jun 2005 13:19:03 +0000 (13:19 +0000)
Added the detailed documentation for external control plugin SDK.

doc/doxygen.cfg
doc/index.doxygen
include/control_external.h
src/control/control_ext.c

index 61fae9cbcf7a92f8b925716b2f73b94b1cf934af..5458f24157840cf2b2c1bd8208f22f23498d44cf 100644 (file)
@@ -26,6 +26,7 @@ INPUT            = index.doxygen \
                   ../include/pcm_external.h \
                   ../include/pcm_extplug.h \
                   ../include/pcm_ioplug.h \
+                  ../include/control_external.h \
                   ../include/conv.h \
                   ../include/instr.h \
                   ../src/error.c \
index 0edd98afdcc0669014b896434e7ed775d429b6b5..e33681e6e7b48c7fb3b18897b97930788a94acc6 100644 (file)
@@ -36,6 +36,7 @@ may be placed in the library code instead of the kernel driver.</P>
   <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.
index c36380596eff759818012eacc1ae93ac1958ee3b..7c066cf9e3df745f7aa5b4618086201e1ddddcec 100644 (file)
@@ -33,7 +33,7 @@ extern "C" {
 #endif
 
 /**
- *  \defgroup CtlPlugin_SDK External control plugin SDK
+ *  \defgroup CtlPlugin_SDK External Control Plugin SDK
  *  \{
  */
 
@@ -129,47 +129,128 @@ struct snd_ctl_ext {
 
 /** Callback table of ext */
 struct snd_ctl_ext_callback {
-       void (*close)(snd_ctl_ext_t *ext); /* opt */
-       void (*subscribe_events)(snd_ctl_ext_t *ext, int subscribe); /* opt */
-       int (*elem_count)(snd_ctl_ext_t *ext); /* req */
-       int (*elem_list)(snd_ctl_ext_t *ext, unsigned int offset, snd_ctl_elem_id_t *id); /* req */
-       snd_ctl_ext_key_t (*find_elem)(snd_ctl_ext_t *ext, const snd_ctl_elem_id_t *id); /* req */
-       void (*free_key)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key); /* opt */
+       /**
+        * close the control handle; optional
+        */
+       void (*close)(snd_ctl_ext_t *ext);
+       /**
+        * return the total number of elements; required
+        */
+       int (*elem_count)(snd_ctl_ext_t *ext);
+       /**
+        * return the element id of the given offset (array index); required
+        */
+       int (*elem_list)(snd_ctl_ext_t *ext, unsigned int offset, snd_ctl_elem_id_t *id);
+       /**
+        * convert the element id to a search key; required
+        */
+       snd_ctl_ext_key_t (*find_elem)(snd_ctl_ext_t *ext, const snd_ctl_elem_id_t *id);
+       /**
+        * the destructor of the key; optional
+        */
+       void (*free_key)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key);
+       /**
+        * get the attribute of the element; required
+        */
        int (*get_attribute)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
-                            int *type, unsigned int *acc, unsigned int *count); /* req */
+                            int *type, unsigned int *acc, unsigned int *count);
+       /**
+        * get the element information of integer type
+        */
        int (*get_integer_info)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
                                long *imin, long *imax, long *istep);
+       /**
+        * get the element information of integer64 type
+        */
        int (*get_integer64_info)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
                                  int64_t *imin, int64_t *imax, int64_t *istep);
+       /**
+        * get the element information of enumerated type
+        */
        int (*get_enumerated_info)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items);
+       /**
+        * get the name of the enumerated item
+        */
        int (*get_enumerated_name)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int item,
                                   char *name, size_t name_max_len);
+       /**
+        * read the current values of integer type
+        */
        int (*read_integer)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value);
+       /**
+        * read the current values of integer64 type
+        */
        int (*read_integer64)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, int64_t *value);
+       /**
+        * read the current values of enumerated type
+        */
        int (*read_enumerated)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items);
+       /**
+        * read the current values of bytes type
+        */
        int (*read_bytes)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned char *data,
                          size_t max_bytes);
+       /**
+        * read the current values of iec958 type
+        */
        int (*read_iec958)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, snd_aes_iec958_t *iec958);
+       /**
+        * update the current values of integer type with the given values
+        */
        int (*write_integer)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value);
+       /**
+        * update the current values of integer64 type with the given values
+        */
        int (*write_integer64)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, int64_t *value);
+       /**
+        * update the current values of enumerated type with the given values
+        */
        int (*write_enumerated)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items);
+       /**
+        * update the current values of bytes type with the given values
+        */
        int (*write_bytes)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned char *data,
                           size_t max_bytes);
+       /**
+        * update the current values of iec958 type with the given values
+        */
        int (*write_iec958)(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, snd_aes_iec958_t *iec958);
+       /**
+        * subscribe/unsubscribe the event notification; optional
+        */
+       void (*subscribe_events)(snd_ctl_ext_t *ext, int subscribe);
+       /**
+        * read a pending notification event; optional
+        */
        int (*read_event)(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id, unsigned int *event_mask);
+       /**
+        * return the number of poll descriptors; optional
+        */
        int (*poll_descriptors_count)(snd_ctl_ext_t *ext);
+       /**
+        * fill the poll descriptors; optional
+        */
        int (*poll_descriptors)(snd_ctl_ext_t *ext, struct pollfd *pfds, unsigned int space);
+       /**
+        * mangle the revents of poll descriptors
+        */
        int (*poll_revents)(snd_ctl_ext_t *ext, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
 };
 
-enum snd_ctl_ext_access_t {
+/**
+ * The access type bits stored in get_attribute callback
+ */
+typedef enum snd_ctl_ext_access {
        SND_CTL_EXT_ACCESS_READ = (1<<0),
        SND_CTL_EXT_ACCESS_WRITE = (1<<1),
        SND_CTL_EXT_ACCESS_READWRITE = (3<<0),
        SND_CTL_EXT_ACCESS_VOLATILE = (1<<2),
        SND_CTL_EXT_ACCESS_INACTIVE = (1<<8),
-};
+} snd_ctl_ext_access_t;
 
+/**
+ * find_elem callback returns this if no matching control element is found
+ */
 #define SND_CTL_EXT_KEY_NOT_FOUND      (snd_ctl_ext_key_t)(-1)
 
 int snd_ctl_ext_create(snd_ctl_ext_t *ext, const char *name, int mode);
index 69d856724612576b25adb1e7686baadfd7e75f0e..426685e3a4f38836a2e89a6b8e566cb2a480b177 100644 (file)
@@ -1,3 +1,10 @@
+/**
+ * \file control/control_ext.c
+ * \ingroup CtlPlugin_SDK
+ * \brief External Control Plugin SDK
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 2005
+ */
 /*
  *  Control Interface - External Control Plugin SDK
  *
@@ -413,6 +420,206 @@ static snd_ctl_ops_t snd_ctl_ext_ops = {
        .poll_revents = snd_ctl_ext_poll_revents,
 };
 
+/*
+ * Exported functions
+ */
+
+/*! \page ctl_external_plugins External Control Plugin SDK
+
+\section ctl_externals External Control Plugins
+
+The external plugins are implemented in a shared object file located
+at /usr/lib/alsa-lib (the exact location depends on the build option
+and asoundrc configuration).  It has to be the file like
+libasound_module_ctl_MYPLUGIN.so, where MYPLUGIN corresponds to your
+own plugin name.
+
+The entry point of the plugin is defined via
+#SND_CTL_PLUGIN_DEFINE_FUNC() macro.  This macro defines the function
+with a proper name to be referred from alsa-lib.  The function takes
+the following 5 arguments:
+\code
+int (snd_ctl_t **phandle, const char *name, snd_config_t *root,
+       snd_config_t *conf, int mode)
+\endcode
+The first argument, phandle, is the pointer to store the resultant control
+handle.  The arguments name, root and mode are the parameters
+to be passed to the plugin constructor.  The conf is the configuration
+tree for the plugin.  The arguments above are defined in the macro
+itself, so don't use variables with the same names to shadow
+parameters.
+
+After parsing the configuration parameters in the given conf tree,
+usually you will call the external plugin API function
+#snd_ctl_ext_create().
+The control handle must be filled *phandle in return.
+Then this function must return either a value 0 when succeeded, or a
+negative value as the error code. 
+
+Finally, add #SND_CTL_PLUGIN_SYMBOL() with the name of your
+plugin as the argument at the end.  This defines the proper versioned
+symbol as the reference.
+
+The typical code would look like below:
+\code
+struct myctl_info {
+       snd_ctl_ext_t ext;
+       int my_own_data;
+       ...
+};
+
+SND_CTL_PLUGIN_DEFINE_FUNC(myctl)
+{
+       snd_config_iterator_t i, next;
+       struct myctl_info *myctl;
+       int err;
+
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id;
+               if (snd_config_get_id(n, &id) < 0)
+                       continue;
+               if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
+                       continue;
+               if (strcmp(id, "my_own_parameter") == 0) {
+                       ....
+                       continue;
+               }
+               SNDERR("Unknown field %s", id);
+               return -EINVAL;
+       }
+
+       myctl = calloc(1, sizeof(*myctl));
+       if (myctl == NULL)
+               return -ENOMEM;
+
+       myctl->ext.version = SND_CTL_EXT_VERSION;
+       myctl->ext.card_idx = 0;
+       strcpy(myctl->ext.id, "Myctl");
+       strcpy(myctl->ext.name, "My Control");
+       strcpy(myctl->ext.longname, "My External Control for Foobar");
+       strcpy(myctl->ext.mixername, "My Control");
+       myctl->ext.callback = &my_own_callback;
+       myctl->ext.private_data = myctl;
+       ....
+
+       err = snd_pcm_extplug_create(&myctl->ext, name, mode);
+       if (err < 0) {
+               myctl_free(myctl);
+               return err;
+       }
+
+       *phandle = myctl->ext.handle;
+       return 0;
+}
+
+SND_CTL_PLUGIN_SYMBOL(myctl);
+\endcode
+
+Read the codes in alsa-plugins package for the real examples.
+
+
+\section ctl_ext_impl Implementation of External Control Plugins
+
+The following fields have to be filled in external control record before calling
+#snd_ctl_ext_create() : version, card_idx, id, name, longname, mixername, poll_fd and callback.
+Otherfields are optional and should be initialized with zero.
+
+The constant #SND_CTL_EXT_VERSION must be passed to the version
+field for the version check in alsa-lib.  The card_idx field specifies the card
+index of this control.  [FIXME: solve confliction of card index in alsa-lib?]
+
+The id, name, longname and mixername fields are the strings shown in the card_info
+inqurirys.  They are the char arrays, so you have to <i>copy</i> strings to these
+fields.
+
+The callback field contains the  table of callback functions for this plugin (defined as
+#snd_ctl_ext_callback_t).
+The poll_fd can be used to specify the poll file descriptor for this control.
+Set -1 if not available.  Alternatively, you can define poll_descriptors_count and
+poll_descriptors callbacks in the callback table for handling the poll descriptor(s)
+dynamically after the creation of plugin instance.
+
+The driver can set an arbitrary value (pointer) to private_data
+field to refer its own data in the callbacks.
+
+The rest fields are filled by #snd_ctl_ext_create().  The handle field
+is the resultant PCM handle.  The others are the current status of the
+PCM.
+
+\section ctl_ext_impl Callback Functions of External Control Plugins
+
+The callback functions in #snd_ctl_ext_callback_t define the real
+behavior of the driver.  There are many callbacks but many of them are optional. 
+
+The close callback is called when the PCM is closed.  If the plugin
+allocates private resources, this is the place to release them
+again.  This callback is optional.
+
+The elem_count and elem_list callbacks are mandatory.  The elem_count returns the
+total number of control elements.  The elem_list returns the control element ID
+of the corresponding element offset (the offset is from 0 to elem_count - 1).
+The id field is initialized to all zero in prior to elem_list callback.  The callback
+has to fill the necessary field (typically iface, name and index) in return via the
+standard control API functions like #snd_ctl_elem_id_set_ifarce,
+#snd_ctl_elem_id_set_name and #snd_ctl_elem_id_set_index, etc.  The callbacks should
+return 0 if successful, or a negative error code.
+
+The find_elem callback is used to convert the given control element ID to the
+certain key value for the faster access to get, read and write callbacks.
+The key type is alias of unsigned long, so you can assign some static number
+(e.g. index of the array) to this value of the corresponding element, or
+assign the pointer (cast to #snd_ctl_ext_key_t).  When no key is defined or found,
+return #SND_CTL_EXT_KEY_NOT_FOUND.  This callback is (very likely) required
+if you use get, read and write callbacks as follows.
+If you need to create a record dynamically (e.g. via malloc) at each find_elem call,
+the allocated record can be released with the optional free_key callback.
+
+The get_attribute is a mandatory callback, which returns the attribute of the 
+control element given via a key value (converted with find_elem callback).
+It must fill the control element type (#snd_ctl_elem_type_t), the access type
+(#snd_ctl_ext_access_t), and the count (element array size).  The callback returns
+0 if successful, or a negative error code, as usual.
+
+The get_integer_info, get_integetr64_info and get_enumerated_info callbacks are called
+to return the information of the given control element for each element type.
+For integer and integer64 types, the callbacks need to fill the minimal (imin),
+maximal (imax) and the step (istep) values of the control.  For the enumerated type,
+the number of enum items must be filled.  Additionally, the enum control has to define
+get_enumerated_name callback to store the name of the enumerated item of the given control
+element.  All functions return 0 if successful, or a negative error code.
+
+For reading the current values of a control element, read_integer, read_integer64,
+read_enumerated, read_bytes and read_iec958 callbacks are called depending on the
+element type.  These callbacks have to fill the current values of the element in return.
+Note that a control element can be an array.  If it contains more than one values
+(i.e. the count value in get_attribute callback is more than 1), <i>all</i> values
+must be filled on the given value pointer as an array.  Also, note that the boolean type
+is handled as integer here (although boolean type doesn't need to define the corresponding
+info callback since it's obvious).  These callbacks return 0 if successful, or
+a negative error code.
+
+For writing the current values, write_integer, write_integer64, write_bytes, and
+write_iec958 callbacks are called as well as for read.  The callbacks should check the
+current values and compare with the given values.  If they are identical, the callbacks
+should do nothing and return 0.  If they differ, update the current values and return 1,
+instead.  For any errors, return a negative error code.
+
+The subscribe_events callback is called when the application subscribes or cancels
+the event notifications (e.g. through mixer API).  The current value of event
+subscription is kept in the subscribed field.
+The read_event callback is called for reading a pending notification event.
+The callback needs to fill the event_mask value, a bit-field defined as SND_CTL_EVENT_MASK_XXX.
+If no event is pending, return -EAGAIN.  These two callbacks are optional.
+
+The poll_descriptors_count and poll_descriptors callbacks are used to return
+the poll descriptor(s) via callbacks.  As already mentioned, if the callback cannot
+set the static poll_fd, you can define these callbacks to return dynamically.
+Also, when multiple poll descriptors are required, use these callbacks.
+The poll_revents callback is used for handle poll revents.
+
+*/
+
 /**
  * \brief Create an external control plugin instance
  * \param ext the plugin handle