From 404cd090b279b329c86514984dc74439dedf2e90 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 22 Sep 2010 14:31:15 +0200 Subject: ucm: Introduce "Value {}" section, more implementation work - new "Value {}" section is introduced for read-only values describing the PCM and control/mixer IDs (or any other things) - more complete implementation for API functions Signed-off-by: Jaroslav Kysela --- include/use-case.h | 45 +- src/ucm/main.c | 1765 +++++++++++++++++++-------------------------------- src/ucm/parser.c | 242 ++++--- src/ucm/ucm_local.h | 40 +- src/ucm/utils.c | 34 +- 5 files changed, 823 insertions(+), 1303 deletions(-) diff --git a/include/use-case.h b/include/use-case.h index 6b9b6455..fdbcaca0 100644 --- a/include/use-case.h +++ b/include/use-case.h @@ -179,9 +179,10 @@ char *snd_use_case_identifier(const char *fmt, ...); /** * \brief Free a string list * \param list The string list to free + * \param items Count of strings * \return Zero if success, otherwise a negative error code */ -int snd_use_case_free_list(const char *list[]); +int snd_use_case_free_list(const char *list[], int items); /** * \brief Obtain a list of entries @@ -192,14 +193,14 @@ int snd_use_case_free_list(const char *list[]); * * Defined identifiers: * NULL - get card list - * (in pair verb+comment) + * (in pair cardname+comment) * _verbs - get verb list * (in pair verb+comment) * _devices[/] - get list of supported devices * (in pair device+comment) * _modifiers[/]- get list of supported modifiers * (in pair modifier+comment) - * _tqs[/] - get list of QoS identifiers + * TQ[/] - get list of TQ identifiers * _enadevs - get list of enabled devices * _enamods - get list of enabled modifiers * @@ -222,20 +223,19 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, * Known identifiers: * NULL - return current card * _verb - return current verb - * _tq - return current Tone Quality - * _tq/ - return Tone Quality for given modifier - * _pcm_/_pdevice[/] - full PCM playback device name - * _pcm_/_cdevice[/] - full PCM capture device name - * _ctl_/_pctl_[/] - playback control device name - * _ctl_/_pctlvol[/] - playback control volume ID string - * _ctl_/_pctlsw[/] - playback control switch ID string - * _ctl_/_cctl[/] - capture control device name - * _ctl_/_cctlvol[/] - capture control volume ID string - * _ctl_/_cctlsw[/] - capture control switch ID string - * _mixer_/_pname[/] - name of playback mixer - * _mixer_/_pid[/] - mixer playback ID - * _mixer_/_cname[/] - name of capture mixer - * _mixer_/_cid[/] - mixer capture ID + * TQ[/] - Tone Quality [for given modifier] + * PlaybackPCM[/] - full PCM playback device name + * CapturePCM[/] - full PCM capture device name + * PlaybackCTL[/] - playback control device name + * PlaybackVolume[/] - playback control volume ID string + * PlaybackSwitch[/] - playback control switch ID string + * CaptureCTL[/] - capture control device name + * CaptureVolume[/] - capture control volume ID string + * CaptureSwitch[/] - capture control switch ID string + * PlaybackMixer[/] - name of playback mixer + * PlaybackMixerID[/] - mixer playback ID + * CaptureMixer[/] - name of capture mixer + * CaptureMixerID[/] - mixer capture ID */ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, const char *identifier, @@ -251,8 +251,8 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, * _devstatus/ - return status for given device * _modstatus/ - return status for given modifier */ -int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, - const char *identifier); +long snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, + const char *identifier); /** * \brief Set new @@ -310,13 +310,6 @@ int snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr); */ int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr); -/** - * \brief Dump current sound card use case control settings - * \param card_name Sound card name - * \return zero if success, otherwise a negative error code - */ -int snd_use_case_dump(const char *card_name); - /** * \} */ diff --git a/src/ucm/main.c b/src/ucm/main.c index a8df44c2..e036c4dd 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -31,8 +31,93 @@ */ #include "ucm_local.h" +#include #include +/* + * misc + */ + +static int check_identifier(const char *identifier, const char *prefix) +{ + int len; + + if (strcmp(identifier, prefix) == 0) + return 1; + len = strlen(prefix); + if (memcmp(identifier, prefix, len) == 0 && identifier[len] == '/') + return 1; + return 0; +} + +static int list_count(struct list_head *list) +{ + struct list_head *pos; + int count = 0; + + list_for_each(pos, list) { + count += 1; + } + return count; +} + +static int alloc_str_list(struct list_head *list, int mult, char **result[]) +{ + char **res; + int cnt; + + cnt = list_count(list) * mult; + if (cnt == 0) + return cnt; + res = calloc(mult, cnt * sizeof(char *)); + if (res == NULL) + return -ENOMEM; + *result = res; + return cnt; +} + +/** + * \brief Create an identifier + * \param fmt Format (sprintf like) + * \param ... Optional arguments for sprintf like format + * \return Allocated string identifier or NULL on error + */ +char *snd_use_case_identifier(const char *fmt, ...) +{ + char *str, *res; + int size = strlen(fmt) + 512; + va_list args; + + str = malloc(size); + if (str == NULL) + return NULL; + va_start(args, fmt); + vsnprintf(str, size, fmt, args); + va_end(args); + str[size-1] = '\0'; + res = realloc(str, strlen(str) + 1); + if (res) + return res; + return str; +} + +/** + * \brief Free a string list + * \param list The string list to free + * \param items Count of strings + * \return Zero if success, otherwise a negative error code + */ +int snd_use_case_free_list(const char *list[], int items) +{ + int i; + if (list == NULL) + return 0; + for (i = 0; i < items; i++) + free((void *)list[i]); + free(list); + return 0; +} + /** * \brief Execute the sequence * \param uc_mgr Use case manager @@ -85,15 +170,13 @@ static int import_master_config(snd_use_case_mgr_t *uc_mgr) /** * \brief Universal find - string in a list - * \param uc_mgr Use case manager * \param list List of structures * \param offset Offset of list structure * \param soffset Offset of string structure * \param match String to match * \return structure on success, otherwise a NULL (not found) */ -static void *find0(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, - struct list_head *list, +static void *find0(struct list_head *list, unsigned long offset, unsigned long soffset, const char *match) @@ -110,10 +193,111 @@ static void *find0(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, return NULL; } -#define find(uc_mgr, list, type, member, value, match) \ - find0(uc_mgr, list, \ - (unsigned long)(&((type *)0)->member), \ - (unsigned long)(&((type *)0)->value), match) +#define find(list, type, member, value, match) \ + find0(list, (unsigned long)(&((type *)0)->member), \ + (unsigned long)(&((type *)0)->value), match) + +/** + * \brief Universal string list + * \param list List of structures + * \param result Result list + * \param offset Offset of list structure + * \param s1offset Offset of string structure + * \return count of items on success, otherwise a negative error code + */ +static int get_list0(struct list_head *list, + const char **result[], + unsigned long offset, + unsigned long s1offset) +{ + char **res; + int cnt; + struct list_head *pos; + char *ptr, *str1; + + cnt = alloc_str_list(list, 1, &res); + if (cnt <= 0) + return cnt; + *result = (const char **)res; + list_for_each(pos, list) { + ptr = list_entry_offset(pos, char, offset); + str1 = *((char **)(ptr + s1offset)); + if (str1 != NULL) { + *res = strdup(str1); + if (*res == NULL) + goto __fail; + } else { + *res = NULL; + } + res++; + } + return cnt; + __fail: + snd_use_case_free_list((const char **)res, cnt); + return -ENOMEM; +} + +#define get_list(list, result, type, member, s1) \ + get_list0(list, result, \ + (unsigned long)(&((type *)0)->member), \ + (unsigned long)(&((type *)0)->s1)) + +/** + * \brief Universal string list - pair of strings + * \param list List of structures + * \param result Result list + * \param offset Offset of list structure + * \param s1offset Offset of string structure + * \param s1offset Offset of string structure + * \return count of items on success, otherwise a negative error code + */ +static int get_list20(struct list_head *list, + const char **result[], + unsigned long offset, + unsigned long s1offset, + unsigned long s2offset) +{ + char **res; + int cnt; + struct list_head *pos; + char *ptr, *str1, *str2; + + cnt = alloc_str_list(list, 2, &res); + if (cnt <= 0) + return cnt; + *result = (const char **)res; + list_for_each(pos, list) { + ptr = list_entry_offset(pos, char, offset); + str1 = *((char **)(ptr + s1offset)); + if (str1 != NULL) { + *res = strdup(str1); + if (*res == NULL) + goto __fail; + } else { + *res = NULL; + } + res++; + str2 = *((char **)(ptr + s2offset)); + if (str2 != NULL) { + *res = strdup(str2); + if (*res == NULL) + goto __fail; + } else { + *res = NULL; + } + res++; + } + return cnt; + __fail: + snd_use_case_free_list((const char **)res, cnt); + return -ENOMEM; +} + +#define get_list2(list, result, type, member, s1, s2) \ + get_list20(list, result, \ + (unsigned long)(&((type *)0)->member), \ + (unsigned long)(&((type *)0)->s1), \ + (unsigned long)(&((type *)0)->s2)) /** * \brief Find verb @@ -122,17 +306,33 @@ static void *find0(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, * \return structure on success, otherwise a NULL (not found) */ static inline struct use_case_verb *find_verb(snd_use_case_mgr_t *uc_mgr, - const char *_name) + const char *verb_name) { - return find(uc_mgr, &uc_mgr->verb_list, + return find(&uc_mgr->verb_list, struct use_case_verb, list, name, - _name); + verb_name); +} + +/** + * \brief Find modifier + * \param verb Use case verb + * \param modifier_name modifier to find + * \return structure on success, otherwise a NULL (not found) + */ +static inline struct use_case_modifier * + find_modifier(struct use_case_verb *verb, + const char *modifier_name) +{ + return find(&verb->modifier_list, + struct use_case_modifier, list, name, + modifier_name); } /** * \brief Set verb * \param uc_mgr Use case manager * \param verb verb to set + * \param enable nonzero = enable, zero = disable * \return zero on success, otherwise a negative error code */ static int set_verb(snd_use_case_mgr_t *uc_mgr, @@ -153,6 +353,62 @@ static int set_verb(snd_use_case_mgr_t *uc_mgr, return err; } +/** + * \brief Set modifier + * \param uc_mgr Use case manager + * \param modifier modifier to set + * \param enable nonzero = enable, zero = disable + * \return zero on success, otherwise a negative error code + */ +static int set_modifier(snd_use_case_mgr_t *uc_mgr, + struct use_case_modifier *modifier, + int enable) +{ + struct list_head *seq; + int err; + + if (enable) { + seq = &modifier->enable_list; + } else { + seq = &modifier->disable_list; + } + err = execute_sequence(uc_mgr, seq); + if (enable && err >= 0) { + list_add_tail(&modifier->active_list, &uc_mgr->active_modifiers); + } else if (!enable) { + list_del(&modifier->active_list); + } + return err; +} + +/** + * \brief Set device + * \param uc_mgr Use case manager + * \param device device to set + * \param enable nonzero = enable, zero = disable + * \return zero on success, otherwise a negative error code + */ +static int set_device(snd_use_case_mgr_t *uc_mgr, + struct use_case_device *device, + int enable) +{ + struct list_head *seq; + int err; + + if (enable) { + seq = &device->enable_list; + } else { + seq = &device->disable_list; + } + err = execute_sequence(uc_mgr, seq); + if (enable && err >= 0) { + list_add_tail(&device->active_list, &uc_mgr->active_devices); + } else if (!enable) { + list_del(&device->active_list); + } + return err; +} + /** * \brief Init sound card use case manager. * \param uc_mgr Returned use case manager pointer @@ -249,7 +505,7 @@ int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr) list_for_each_safe(pos, npos, &uc_mgr->active_modifiers) { modifier = list_entry(pos, struct use_case_modifier, active_list); - err = disable_modifier(uc_mgr, modifier); + err = set_modifier(uc_mgr, modifier, 0); if (err < 0) uc_error("Unable to disable modifier %s", modifier->name); } @@ -258,13 +514,13 @@ int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr) list_for_each_safe(pos, npos, &uc_mgr->active_devices) { device = list_entry(pos, struct use_case_device, active_list); - err = disable_device(uc_mgr, device); + err = set_device(uc_mgr, device, 0); if (err < 0) uc_error("Unable to disable device %s", device->name); } INIT_LIST_HEAD(&uc_mgr->active_devices); - err = disable_verb(uc_mgr, uc_mgr->active_verb); + err = set_verb(uc_mgr, uc_mgr->active_verb, 0); if (err < 0) { uc_error("Unable to disable verb %s", uc_mgr->active_verb->name); return err; @@ -277,1172 +533,469 @@ int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr) return err; } -#if 0 -static int enable_use_case_verb(snd_use_case_mgr_t *uc_mgr, int verb_id, - snd_ctl_elem_list_t *list, snd_ctl_t *handle) -{ - struct use_case_verb *verb; - int ret; - - if (verb_id >= uc_mgr->num_verbs) { - uc_error("error: invalid verb id %d", verb_id); - return -EINVAL; - } - verb = &uc_mgr->verb[verb_id]; - - uc_dbg("verb %s", verb->name); - ret = exec_sequence(verb->enable, uc_mgr, list, handle); - if (ret < 0) { - uc_error("error: could not enable verb %s", verb->name); - return ret; - } - uc_mgr->card.current_verb = verb_id; - - return 0; -} - -static int disable_use_case_verb(snd_use_case_mgr_t *uc_mgr, int verb_id, - snd_ctl_elem_list_t *list, snd_ctl_t *handle) -{ - struct use_case_verb *verb; - int ret; - - if (verb_id >= uc_mgr->num_verbs) { - uc_error("error: invalid verb id %d", verb_id); - return -EINVAL; - } - verb = &uc_mgr->verb[verb_id]; - - /* we set the invalid verb at open() but we should still - * check that this succeeded */ - if (verb == NULL) - return 0; - - uc_dbg("verb %s", verb->name); - ret = exec_sequence(verb->disable, uc_mgr, list, handle); - if (ret < 0) { - uc_error("error: could not disable verb %s", verb->name); - return ret; - } - - return 0; -} - -static int enable_use_case_device(snd_use_case_mgr_t *uc_mgr, - int device_id, snd_ctl_elem_list_t *list, snd_ctl_t *handle) -{ - struct use_case_verb *verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - struct use_case_device *device = &verb->device[device_id]; - int ret; - - if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) - return -EINVAL; - - uc_dbg("device %s", device->name); - ret = exec_sequence(device->enable, uc_mgr, list, handle); - if (ret < 0) { - uc_error("error: could not enable device %s", device->name); - return ret; - } - - set_device_status(uc_mgr, device_id, 1); - return 0; -} - -static int disable_use_case_device(snd_use_case_mgr_t *uc_mgr, - int device_id, snd_ctl_elem_list_t *list, snd_ctl_t *handle) -{ - struct use_case_verb *verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - struct use_case_device *device = &verb->device[device_id]; - int ret; - - if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) - return -EINVAL; - - uc_dbg("device %s", device->name); - ret = exec_sequence(device->disable, uc_mgr, list, handle); - if (ret < 0) { - uc_error("error: could not disable device %s", device->name); - return ret; - } - - set_device_status(uc_mgr, device_id, 0); - return 0; -} - -static int enable_use_case_modifier(snd_use_case_mgr_t *uc_mgr, - int modifier_id, snd_ctl_elem_list_t *list, snd_ctl_t *handle) -{ - struct use_case_verb *verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - struct use_case_modifier *modifier = &verb->modifier[modifier_id]; - int ret; - - if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) - return -EINVAL; - - uc_dbg("modifier %s", modifier->name); - ret = exec_sequence(modifier->enable, uc_mgr, list, handle); - if (ret < 0) { - uc_error("error: could not enable modifier %s", modifier->name); - return ret; - } - - set_modifier_status(uc_mgr, modifier_id, 1); - return 0; -} - -static int disable_use_case_modifier(snd_use_case_mgr_t *uc_mgr, - int modifier_id, snd_ctl_elem_list_t *list, snd_ctl_t *handle) -{ - struct use_case_verb *verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - struct use_case_modifier *modifier = &verb->modifier[modifier_id]; - int ret; - - if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) - return -EINVAL; - - uc_dbg("modifier %s", modifier->name); - ret = exec_sequence(modifier->disable, uc_mgr, list, handle); - if (ret < 0) { - uc_error("error: could not disable modifier %s", modifier->name); - return ret; - } - - set_modifier_status(uc_mgr, modifier_id, 0); - return 0; -} - -/* - * Tear down current use case verb, device and modifier. - */ -static int dismantle_use_case(snd_use_case_mgr_t *uc_mgr, - snd_ctl_elem_list_t *list, snd_ctl_t *handle) -{ - struct use_case_verb *verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - int ret, i; - - /* No active verb */ - if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) - return 0; - - /* disable all modifiers that are active */ - for (i = 0; i < verb->num_modifiers; i++) { - if (get_modifier_status(uc_mgr,i)) { - ret = disable_use_case_modifier(uc_mgr, i, list, handle); - if (ret < 0) - return ret; - } - } - - /* disable all devices that are active */ - for (i = 0; i < verb->num_devices; i++) { - if (get_device_status(uc_mgr,i)) { - ret = disable_use_case_device(uc_mgr, i, list, handle); - if (ret < 0) - return ret; - } - } - - /* disable verb */ - ret = disable_use_case_verb(uc_mgr, uc_mgr->card.current_verb, list, handle); - if (ret < 0) - return ret; - - return 0; -} - - /** - * \brief Dump sound card controls in format required for sequencer. - * \param card_name The name of the sound card to be dumped - * \return zero on success, otherwise a negative error code - */ -int snd_use_case_dump(const char *card_name) -{ - snd_ctl_t *handle; - snd_ctl_card_info_t *info; - snd_ctl_elem_list_t *list; - int ret, i, count, idx; - char ctl_name[8]; - - snd_ctl_card_info_alloca(&info); - snd_ctl_elem_list_alloca(&list); - - idx = snd_card_get_index(card_name); - if (idx < 0) - return idx; - sprintf(ctl_name, "hw:%d", idx); - - /* open and load snd card */ - ret = snd_ctl_open(&handle, ctl_name, SND_CTL_READONLY); - if (ret < 0) { - uc_error("error: could not open controls for %s: %s", - card_name, snd_strerror(ret)); - return ret; - } - - ret = snd_ctl_card_info(handle, info); - if (ret < 0) { - uc_error("error: could not get control info for %s:%s", - card_name, snd_strerror(ret)); - goto close; - } - - ret = snd_ctl_elem_list(handle, list); - if (ret < 0) { - uc_error("error: cannot determine controls for %s: %s", - card_name, snd_strerror(ret)); - goto close; - } - - count = snd_ctl_elem_list_get_count(list); - if (count < 0) { - ret = 0; - goto close; - } - - snd_ctl_elem_list_set_offset(list, 0); - if (snd_ctl_elem_list_alloc_space(list, count) < 0) { - uc_error("error: not enough memory for control elements"); - ret = -ENOMEM; - goto close; - } - if ((ret = snd_ctl_elem_list(handle, list)) < 0) { - uc_error("error: cannot determine controls: %s", - snd_strerror(ret)); - goto free; - } - - /* iterate through each kcontrol and add to use - * case manager control list */ - for (i = 0; i < count; ++i) { - snd_ctl_elem_id_t *id; - snd_ctl_elem_id_alloca(&id); - snd_ctl_elem_list_get_id(list, i, id); - - /* dump to stdout in friendly format */ - ret = dump_control(handle, id); - if (ret < 0) { - uc_error("error: control dump failed: %s", - snd_strerror(ret)); - goto free; - } - } -free: - snd_ctl_elem_list_free_space(list); -close: - snd_ctl_close(handle); - return ret; -} - -/** - * \brief List supported use case verbs for given soundcard - * \param uc_mgr use case manager - * \param verb returned list of supported use case verb id and names - * \return number of use case verbs if success, otherwise a negative error code - */ -int snd_use_case_get_verb_list(snd_use_case_mgr_t *uc_mgr, - const char **verb[]) -{ - int ret; - - pthread_mutex_lock(&uc_mgr->mutex); - - *verb = uc_mgr->verb_list; - ret = uc_mgr->num_verbs; - - pthread_mutex_unlock(&uc_mgr->mutex); - return ret; -} - -/** - * \brief List supported use case devices for given verb - * \param uc_mgr use case manager - * \param verb verb id. - * \param device returned list of supported use case device id and names - * \return number of use case devices if success, otherwise a negative error code - */ -int snd_use_case_get_device_list(snd_use_case_mgr_t *uc_mgr, - const char *verb_name, const char **device[]) -{ - struct use_case_verb *verb = NULL; - int i, ret = -EINVAL; - - pthread_mutex_lock(&uc_mgr->mutex); - - /* find verb name */ - for (i = 0; i < uc_mgr->num_verbs; i++) { - verb = &uc_mgr->verb[i]; - if (!strcmp(uc_mgr->verb[i].name, verb_name)) - goto found; - } - - uc_error("error: use case verb %s not found", verb_name); - goto out; - -found: - *device = verb->device_list; - ret = verb->num_devices; -out: - pthread_mutex_unlock(&uc_mgr->mutex); - return ret; -} - /** - * \brief List supported use case verb modifiers for given verb - * \param uc_mgr use case manager - * \param verb verb id. - * \param mod returned list of supported use case modifier id and names - * \return number of use case modifiers if success, otherwise a negative error code + * \brief Get list of cards in pair cardname+comment + * \param list Returned list + * \return Number of list entries if success, otherwise a negative error code */ -int snd_use_case_get_mod_list(snd_use_case_mgr_t *uc_mgr, - const char *verb_name, const char **mod[]) -{ - struct use_case_verb *verb = NULL; - int i, ret = -EINVAL; - - pthread_mutex_lock(&uc_mgr->mutex); - - /* find verb name */ - for (i = 0; i num_verbs; i++) { - verb = &uc_mgr->verb[i]; - if (!strcmp(uc_mgr->verb[i].name, verb_name)) - goto found; - } - - uc_error("error: use case verb %s not found", verb_name); - goto out; - -found: - *mod = verb->modifier_list; - ret = verb->num_modifiers; -out: - pthread_mutex_unlock(&uc_mgr->mutex); - return ret; -} - -static struct sequence_element *get_transition_sequence( - struct transition_sequence *trans_list, const char *name) -{ - struct transition_sequence *trans = trans_list; - - while (trans) { - if (trans->name && !strcmp(trans->name, name)) - return trans->transition; - - trans = trans->next; - } - - return NULL; -} - -static int exec_transition_sequence(snd_use_case_mgr_t *uc_mgr, - struct sequence_element *trans_sequence) +static int get_card_list(const char **list[]) { - int ret; - - ret = exec_sequence(trans_sequence, uc_mgr, uc_mgr->list, - uc_mgr->handle); - if (ret < 0) - uc_error("error: could not exec transition sequence"); - - return ret; -} - -static int handle_transition_verb(snd_use_case_mgr_t *uc_mgr, - int new_verb_id) -{ - struct use_case_verb *old_verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - struct use_case_verb *new_verb; - static struct sequence_element *trans_sequence; - - if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) - return -EINVAL; - - if (new_verb_id >= uc_mgr->num_verbs) { - uc_error("error: invalid new_verb id %d", new_verb_id); - return -EINVAL; - } - - new_verb = &uc_mgr->verb[new_verb_id]; - - uc_dbg("new verb %s", new_verb->name); - - trans_sequence = get_transition_sequence(old_verb->transition_list, - new_verb->name); - if (trans_sequence != NULL) { - int ret, i; - - uc_dbg("find transition sequence %s->%s", - old_verb->name, new_verb->name); - - /* disable all modifiers that are active */ - for (i = 0; i < old_verb->num_modifiers; i++) { - if (get_modifier_status(uc_mgr,i)) { - ret = disable_use_case_modifier(uc_mgr, i, - uc_mgr->list, uc_mgr->handle); - if (ret < 0) - return ret; - } - } - - /* disable all devices that are active */ - for (i = 0; i < old_verb->num_devices; i++) { - if (get_device_status(uc_mgr,i)) { - ret = disable_use_case_device(uc_mgr, i, - uc_mgr->list, uc_mgr->handle); - if (ret < 0) - return ret; - } - } - - ret = exec_transition_sequence(uc_mgr, trans_sequence); - if (ret) - return ret; - - uc_mgr->card.current_verb = new_verb_id; - - return 0; - } - - return-EINVAL; + return -ENXIO; /* Not Yet Implemented */ } /** - * \brief Set new use case verb for sound card - * \param uc_mgr use case manager - * \param verb verb id - * \return zero if success, otherwise a negative error code + * \brief Get list of verbs in pair verbname+comment + * \param list Returned list + * \param verbname For verb (NULL = current) + * \return Number of list entries if success, otherwise a negative error code */ -int snd_use_case_set_verb(snd_use_case_mgr_t *uc_mgr, - const char *verb_name) +static int get_verb_list(snd_use_case_mgr_t *uc_mgr, const char **list[]) { - int i = 0, ret = -EINVAL, inactive = 0; - - pthread_mutex_lock(&uc_mgr->mutex); - - uc_dbg("uc_mgr %p, verb_name %s", uc_mgr, verb_name); - - /* check for "Inactive" */ - if (!strcmp(verb_name, SND_USE_CASE_VERB_INACTIVE)) { - inactive = 1; - goto found; - } - - /* find verb name */ - for (i = 0; i num_verbs; i++) { - if (!strcmp(uc_mgr->verb[i].name, verb_name)) - goto found; - } - - uc_error("error: use case verb %s not found", verb_name); - goto out; -found: - /* use case verb found - check that we actually changing the verb */ - if (i == uc_mgr->card.current_verb) { - uc_dbg("current verb ID %d", i); - ret = 0; - goto out; - } - - if (handle_transition_verb(uc_mgr, i) == 0) - goto out; - - /* - * Dismantle the old use cases by running it's verb, device and modifier - * disable sequences - */ - ret = dismantle_use_case(uc_mgr, uc_mgr->list, uc_mgr->handle); - if (ret < 0) { - uc_error("error: failed to dismantle current use case: %s", - uc_mgr->verb[i].name); - goto out; - } - - /* we don't need to initialise new verb if inactive */ - if (inactive) - goto out; - - /* Initialise the new use case verb */ - ret = enable_use_case_verb(uc_mgr, i, uc_mgr->list, uc_mgr->handle); - if (ret < 0) - uc_error("error: failed to initialise new use case: %s", - verb_name); -out: - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -static int config_use_case_device(snd_use_case_mgr_t *uc_mgr, - const char *device_name, int enable) -{ - struct use_case_verb *verb; - int ret, i; - - pthread_mutex_lock(&uc_mgr->mutex); - - if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) { - uc_error("error: no valid use case verb set\n"); - ret = -EINVAL; - goto out; - } - - verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - - uc_dbg("current verb %s", verb->name); - uc_dbg("uc_mgr %p device_name %s", uc_mgr, device_name); - - /* find device name and index */ - for (i = 0; i num_devices; i++) { - uc_dbg("verb->num_devices %s", verb->device[i].name); - if (!strcmp(verb->device[i].name, device_name)) - goto found; - } - - uc_error("error: use case device %s not found", device_name); - ret = -EINVAL; - goto out; - -found: - if (enable) { - /* Initialise the new use case device */ - ret = enable_use_case_device(uc_mgr, i, uc_mgr->list, - uc_mgr->handle); - if (ret < 0) - goto out; - } else { - /* disable the old device */ - ret = disable_use_case_device(uc_mgr, i, uc_mgr->list, - uc_mgr->handle); - if (ret < 0) - goto out; - } - -out: - pthread_mutex_unlock(&uc_mgr->mutex); - return ret; + return get_list2(&uc_mgr->verb_list, list, + struct use_case_verb, list, + name, comment); } /** - * \brief Enable use case device - * \param uc_mgr Use case manager - * \param device the device to be enabled - * \return 0 = successful negative = error - */ -int snd_use_case_enable_device(snd_use_case_mgr_t *uc_mgr, - const char *device) -{ - return config_use_case_device(uc_mgr, device, 1); + * \brief Get list of devices in pair devicename+comment + * \param list Returned list + * \param verbname For verb (NULL = current) + * \return Number of list entries if success, otherwise a negative error code + */ +static int get_device_list(snd_use_case_mgr_t *uc_mgr, const char **list[], + char *verbname) +{ + struct use_case_verb *verb; + + if (verbname) { + verb = find_verb(uc_mgr, verbname); + } else { + verb = uc_mgr->active_verb; + } + if (verb == NULL) + return -ENOENT; + return get_list2(&verb->device_list, list, + struct use_case_device, list, + name, comment); + return 0; } /** - * \brief Disable use case device - * \param uc_mgr Use case manager - * \param device the device to be disabled - * \return 0 = successful negative = error - */ -int snd_use_case_disable_device(snd_use_case_mgr_t *uc_mgr, - const char *device) -{ - return config_use_case_device(uc_mgr, device, 0); -} - -static struct use_case_device *get_device(snd_use_case_mgr_t *uc_mgr, - const char *name, int *id) -{ - struct use_case_verb *verb; - int i; - - if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) - return NULL; - - verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - - uc_dbg("current verb %s", verb->name); - - for (i = 0; i < verb->num_devices; i++) { - uc_dbg("device %s", verb->device[i].name); - - if (!strcmp(verb->device[i].name, name)) { - if (id) - *id = i; - return &verb->device[i]; - } - } - - return NULL; + * \brief Get list of modifiers in pair devicename+comment + * \param list Returned list + * \param verbname For verb (NULL = current) + * \return Number of list entries if success, otherwise a negative error code + */ +static int get_modifier_list(snd_use_case_mgr_t *uc_mgr, const char **list[], + char *verbname) +{ + struct use_case_verb *verb; + + if (verbname) { + verb = find_verb(uc_mgr, verbname); + } else { + verb = uc_mgr->active_verb; + } + if (verb == NULL) + return -ENOENT; + return get_list2(&verb->modifier_list, list, + struct use_case_modifier, list, + name, comment); + return 0; +} + +struct myvalue { + struct list_head list; + char *value; +}; + +static int add_values(struct list_head *list, + const char *identifier, + struct list_head *source) +{ + struct ucm_value *v; + struct myvalue *val; + struct list_head *pos, *pos1; + int match; + + list_for_each(pos, source) { + v = list_entry(pos, struct ucm_value, list); + if (check_identifier(identifier, v->name)) { + match = 0; + list_for_each(pos1, list) { + val = list_entry(pos1, struct myvalue, list); + if (strcmp(val->value, v->data) == 0) { + match = 1; + break; + } + } + if (!match) { + val = malloc(sizeof(struct myvalue)); + if (val == NULL) + return -ENOMEM; + list_add_tail(&val->list, list); + } + } + } + return 0; } /** - * \brief Disable old_device and then enable new_device. - * If from_device is not enabled just return. - * Check transition sequence firstly. - * \param uc_mgr Use case manager - * \param old the device to be closed - * \param new the device to be opened - * \return 0 = successful negative = error - */ -int snd_use_case_switch_device(snd_use_case_mgr_t *uc_mgr, - const char *old, const char *new) -{ - static struct sequence_element *trans_sequence; - struct use_case_device *old_device; - struct use_case_device *new_device; - int ret = 0, old_id, new_id; - - uc_dbg("old %s, new %s", old, new); - - pthread_mutex_lock(&uc_mgr->mutex); - - old_device = get_device(uc_mgr, old, &old_id); - if (!old_device) { - uc_error("error: device %s not found", old); - ret = -EINVAL; - goto out; - } - - if (!get_device_status(uc_mgr, old_id)) { - uc_error("error: device %s not enabled", old); - goto out; - } - - new_device = get_device(uc_mgr, new, &new_id); - if (!new_device) { - uc_error("error: device %s not found", new); - ret = -EINVAL; - goto out; - } - - trans_sequence = get_transition_sequence(old_device->transition_list, new); - if (trans_sequence != NULL) { - - uc_dbg("find transition sequece %s->%s", old, new); - - ret = exec_transition_sequence(uc_mgr, trans_sequence); - if (ret) - goto out; - - set_device_status(uc_mgr, old_id, 0); - set_device_status(uc_mgr, new_id, 1); - } else { - /* use lock in config_use_case_device */ - pthread_mutex_unlock(&uc_mgr->mutex); - - config_use_case_device(uc_mgr, old, 0); - config_use_case_device(uc_mgr, new, 1); - - return 0; - } -out: - pthread_mutex_unlock(&uc_mgr->mutex); - return ret; -} - -/* - * Check to make sure that the modifier actually supports any of the - * active devices. - */ -static int is_modifier_valid(snd_use_case_mgr_t *uc_mgr, - struct use_case_verb *verb, struct use_case_modifier *modifier) -{ - struct dev_list *dev_list; - int dev; - - /* check modifier list against each enabled device */ - for (dev = 0; dev < verb->num_devices; dev++) { - if (!get_device_status(uc_mgr, dev)) - continue; - - dev_list = modifier->dev_list; - uc_dbg("checking device %s for %s", verb->device[dev].name, - dev_list->name ? dev_list->name : ""); - - while (dev_list) { - uc_dbg("device supports %s", dev_list->name); - if (!strcmp(dev_list->name, verb->device[dev].name)) - return 1; - dev_list = dev_list->next; - } - } - return 0; -} - -static int config_use_case_mod(snd_use_case_mgr_t *uc_mgr, - const char *modifier_name, int enable) -{ - struct use_case_verb *verb; - int ret, i; - - pthread_mutex_lock(&uc_mgr->mutex); - - if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) { - ret = -EINVAL; - goto out; - } - - verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - - uc_dbg("current verb %s", verb->name); - uc_dbg("uc_mgr %p modifier_name %s", uc_mgr, modifier_name); - - /* find modifier name */ - for (i = 0; i num_modifiers; i++) { - uc_dbg("verb->num_modifiers %d %s", i, verb->modifier[i].name); - if (!strcmp(verb->modifier[i].name, modifier_name) && - is_modifier_valid(uc_mgr, verb, &verb->modifier[i])) - goto found; - } - - uc_error("error: use case modifier %s not found or invalid", - modifier_name); - ret = -EINVAL; - goto out; - -found: - if (enable) { - /* Initialise the new use case device */ - ret = enable_use_case_modifier(uc_mgr, i, uc_mgr->list, - uc_mgr->handle); - if (ret < 0) - goto out; - } else { - /* disable the old device */ - ret = disable_use_case_modifier(uc_mgr, i, uc_mgr->list, - uc_mgr->handle); - if (ret < 0) - goto out; - } - -out: - pthread_mutex_unlock(&uc_mgr->mutex); - return ret; + * \brief Get list of values + * \param list Returned list + * \param verbname For verb (NULL = current) + * \return Number of list entries if success, otherwise a negative error code + */ +static int get_value_list(snd_use_case_mgr_t *uc_mgr, + const char *identifier, + const char **list[], + char *verbname) +{ + struct list_head mylist, *pos, *npos; + struct myvalue *val; + struct use_case_verb *verb; + struct use_case_device *dev; + struct use_case_modifier *mod; + char **res; + int err; + + if (verbname) { + verb = find_verb(uc_mgr, verbname); + } else { + verb = uc_mgr->active_verb; + } + if (verb == NULL) + return -ENOENT; + INIT_LIST_HEAD(&mylist); + err = add_values(&mylist, identifier, &verb->value_list); + if (err < 0) + goto __fail; + list_for_each(pos, &verb->device_list) { + dev = list_entry(pos, struct use_case_device, list); + err = add_values(&mylist, identifier, &dev->value_list); + if (err < 0) + goto __fail; + } + list_for_each(pos, &verb->modifier_list) { + mod = list_entry(pos, struct use_case_modifier, list); + err = add_values(&mylist, identifier, &mod->value_list); + if (err < 0) + goto __fail; + } + err = alloc_str_list(&mylist, 1, &res); + *list = (const char **)res; + if (err >= 0) { + list_for_each(pos, &mylist) { + val = list_entry(pos, struct myvalue, list); + *res = strdup(val->value); + if (*res == NULL) { + snd_use_case_free_list((const char **)res, err); + err = -ENOMEM; + goto __fail; + } + res++; + } + } + __fail: + list_for_each_safe(pos, npos, &mylist) { + val = list_entry(pos, struct myvalue, list); + list_del(&val->list); + free(val); + } + return err; } /** - * \brief Enable use case modifier - * \param uc_mgr Use case manager - * \param modifier the modifier to be enabled - * \return 0 = successful negative = error + * \brief Get list of enabled devices + * \param list Returned list + * \param verbname For verb (NULL = current) + * \return Number of list entries if success, otherwise a negative error code */ -int snd_use_case_enable_modifier(snd_use_case_mgr_t *uc_mgr, - const char *modifier) +static int get_enabled_device_list(snd_use_case_mgr_t *uc_mgr, + const char **list[]) { - return config_use_case_mod(uc_mgr, modifier, 1); + if (uc_mgr->active_verb == NULL) + return -EINVAL; + return get_list(&uc_mgr->active_devices, list, + struct use_case_device, active_list, + name); } /** - * \brief Disable use case modifier - * \param uc_mgr Use case manager - * \param modifier the modifier to be disabled - * \return 0 = successful negative = error + * \brief Get list of enabled modifiers + * \param list Returned list + * \param verbname For verb (NULL = current) + * \return Number of list entries if success, otherwise a negative error code */ -int snd_use_case_disable_modifier(snd_use_case_mgr_t *uc_mgr, - const char *modifier) -{ - return config_use_case_mod(uc_mgr, modifier, 0); -} - -static struct use_case_modifier *get_modifier(snd_use_case_mgr_t *uc_mgr, - const char *name, int *mod_id) +static int get_enabled_modifier_list(snd_use_case_mgr_t *uc_mgr, + const char **list[]) { - struct use_case_verb *verb; - int i; - - if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) - return NULL; - - verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - - uc_dbg("current verb %s", verb->name); - - uc_dbg("uc_mgr %p modifier_name %s", uc_mgr, name); - - for (i = 0; i < verb->num_modifiers; i++) { - uc_dbg("verb->num_devices %s", verb->modifier[i].name); - - if (!strcmp(verb->modifier[i].name, name)) { - if (mod_id) - *mod_id = i; - return &verb->modifier[i]; - } - } - - return NULL; + if (uc_mgr->active_verb == NULL) + return -EINVAL; + return get_list(&uc_mgr->active_modifiers, list, + struct use_case_modifier, active_list, + name); } /** - * \brief Disable old_modifier and then enable new_modifier. - * If old_modifier is not enabled just return. - * Check transition sequence firstly. - * \param uc_mgr Use case manager - * \param old the modifier to be closed - * \param new the modifier to be opened - * \return 0 = successful negative = error + * \brief Obtain a list of entries + * \param uc_mgr Use case manager (may be NULL - card list) + * \param identifier (may be NULL - card list) + * \param list Returned allocated list + * \return Number of list entries if success, otherwise a negative error code */ -int snd_use_case_switch_modifier(snd_use_case_mgr_t *uc_mgr, - const char *old, const char *new) +int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, + const char *identifier, + const char **list[]) { - struct use_case_modifier *old_modifier; - struct use_case_modifier *new_modifier; - static struct sequence_element *trans_sequence; - int ret = 0, old_id, new_id - - uc_dbg("old %s, new %s", old, new); + char *str, *str1; + int err; + if (uc_mgr == NULL || identifier == NULL) + return get_card_list(list); pthread_mutex_lock(&uc_mgr->mutex); - - old_modifier = get_modifier(uc_mgr, old, &old_id); - if (!old_modifier) { - uc_error("error: modifier %s not found", old); - ret = -EINVAL; - goto out; - } - - if (!get_modifier_status(uc_mgr, old_id)) { - uc_error("error: modifier %s not enabled", old); - ret = -EINVAL; - goto out; - } - - new_modifier = get_modifier(uc_mgr, new, &new_id); - if (!new_modifier) { - uc_error("error: modifier %s not found", new); - ret = -EINVAL; - goto out; - } - - trans_sequence = get_transition_sequence( - old_modifier->transition_list, new); - if (trans_sequence != NULL) { - uc_dbg("find transition sequence %s->%s", old, new); - - ret = exec_transition_sequence(uc_mgr, trans_sequence); - if (ret) - goto out; - - set_device_status(uc_mgr, old_id, 0); - set_device_status(uc_mgr, new_id, 1); - } else { - /* use lock in config_use_case_mod*/ - pthread_mutex_unlock(&uc_mgr->mutex); - - config_use_case_mod(uc_mgr, old, 0); - config_use_case_mod(uc_mgr, new, 1); - - return 0; - } -out: + if (strcmp(identifier, "_verbs") == 0) + err = get_verb_list(uc_mgr, list); + else if (strcmp(identifier, "_enadevs")) + err = get_enabled_device_list(uc_mgr, list); + else if (strcmp(identifier, "_enamods")) + err = get_enabled_modifier_list(uc_mgr, list); + else { + str1 = strchr(identifier, '/'); + if (str1) { + str = strdup(str1 + 1); + if (str == NULL) { + err = -ENOMEM; + goto __end; + } + } else { + str = NULL; + } + if (check_identifier(identifier, "_devices")) + err = get_device_list(uc_mgr, list, str); + else if (check_identifier(identifier, "_modifiers")) + err = get_modifier_list(uc_mgr, list, str); + else + err = get_value_list(uc_mgr, identifier, list, str); + if (str) + free(str); + } + __end: pthread_mutex_unlock(&uc_mgr->mutex); - return ret; + return err; } -/** - * \brief Get current use case verb from sound card - * \param uc_mgr use case manager - * \return Verb Name if success, otherwise NULL - */ -const char *snd_use_case_get_verb(snd_use_case_mgr_t *uc_mgr) +static int get_value1(const char **value, struct list_head *value_list, + const char *identifier) { - const char *ret = NULL; - - pthread_mutex_lock(&uc_mgr->mutex); - - if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) - ret = uc_mgr->verb_list[uc_mgr->card.current_verb]; - - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; + struct ucm_value *val; + struct list_head *pos; + + list_for_each(pos, value_list) { + val = list_entry(pos, struct ucm_value, list); + if (check_identifier(identifier, val->name)) { + *value = strdup(val->data); + if (*value == NULL) + return -ENOMEM; + return 0; + } + } + return 0; } /** - * \brief Get device status for current use case verb - * \param uc_mgr Use case manager - * \param device_name The device we are interested in. - * \return - 1 = enabled, 0 = disabled, negative = error + * \brief Get value + * \param list Returned list + * \param verbname For verb (NULL = current) + * \return Number of list entries if success, otherwise a negative error code */ -int snd_use_case_get_device_status(snd_use_case_mgr_t *uc_mgr, - const char *device_name) +static int get_value(snd_use_case_mgr_t *uc_mgr, + const char *identifier, + const char **value, + const char *modifier) { - struct use_case_device *device; - int ret = -EINVAL, dev_id; - - pthread_mutex_lock(&uc_mgr->mutex); - - device = get_device(uc_mgr, device_name, &dev_id); - if (device == NULL) { - uc_error("error: use case device %s not found", device_name); - goto out; - } - - ret = get_device_status(uc_mgr, dev_id); -out: - pthread_mutex_unlock(&uc_mgr->mutex); + struct use_case_modifier *mod; - return ret; + if (uc_mgr->active_verb == NULL) + return -ENOENT; + if (modifier == NULL) + return get_value1(value, &uc_mgr->active_verb->value_list, + identifier); + mod = find_modifier(uc_mgr->active_verb, modifier); + if (mod == NULL) + return -EINVAL; + return get_value1(value, &mod->value_list, identifier); } /** - * \brief Get modifier status for current use case verb + * \brief Get current - string * \param uc_mgr Use case manager - * \param device_name The device we are interested in. - * \return - 1 = enabled, 0 = disabled, negative = error - */ -int snd_use_case_get_modifier_status(snd_use_case_mgr_t *uc_mgr, - const char *modifier_name) -{ - struct use_case_modifier *modifier; - int ret = -EINVAL, mod_id; - - pthread_mutex_lock(&uc_mgr->mutex); - - modifier = get_modifier(uc_mgr, modifier_name, &mod_id); - if (modifier == NULL) { - uc_error("error: use case modifier %s not found", modifier_name); - goto out; - } - - ret = get_modifier_status(uc_mgr, mod_id); -out: - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get current use case verb QoS - * \param uc_mgr use case manager - * \return QoS level - */ -enum snd_use_case_qos - snd_use_case_get_verb_qos(snd_use_case_mgr_t *uc_mgr) -{ - struct use_case_verb *verb; - enum snd_use_case_qos ret = SND_USE_CASE_QOS_UNKNOWN; - - pthread_mutex_lock(&uc_mgr->mutex); - - if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) { - verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - ret = verb->qos; - } - - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get current use case modifier QoS - * \param uc_mgr use case manager - * \return QoS level - */ -enum snd_use_case_qos - snd_use_case_get_mod_qos(snd_use_case_mgr_t *uc_mgr, - const char *modifier_name) -{ - struct use_case_modifier *modifier; - enum snd_use_case_qos ret = SND_USE_CASE_QOS_UNKNOWN; - - pthread_mutex_lock(&uc_mgr->mutex); - - modifier = get_modifier(uc_mgr, modifier_name, NULL); - if (modifier != NULL) - ret = modifier->qos; - else - uc_error("error: use case modifier %s not found", modifier_name); - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get current use case verb playback PCM - * \param uc_mgr use case manager - * \return PCM number if success, otherwise negative - */ -int snd_use_case_get_verb_playback_pcm(snd_use_case_mgr_t *uc_mgr) + * \param identifier + * \param value Value pointer + * \return Zero if success, otherwise a negative error code + * + * Note: String is dynamically allocated, use free() to + * deallocate this string. + */ +int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, + const char *identifier, + const char **value) { - struct use_case_verb *verb; - int ret = -EINVAL; + char *str, *str1; + int err; pthread_mutex_lock(&uc_mgr->mutex); - - if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) { - verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - ret = verb->playback_pcm; - } - + if (identifier == NULL) { + *value = strdup(uc_mgr->card_name); + if (*value == NULL) { + err = -ENOMEM; + goto __end; + } + err = 0; + } else if (strcmp(identifier, "_verb") == 0) { + if (uc_mgr->active_verb == NULL) + return -ENOENT; + *value = strdup(uc_mgr->active_verb->name); + if (*value == NULL) { + err = -ENOMEM; + goto __end; + } + err = 0; + } else { + str1 = strchr(identifier, '/'); + if (str1) { + str = strdup(str1 + 1); + if (str == NULL) { + err = -ENOMEM; + goto __end; + } + } else { + str = NULL; + } + err = get_value(uc_mgr, identifier, value, str); + if (str) + free(str); + } + __end: pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; + return err; } -/** - * \brief Get current use case verb playback PCM - * \param uc_mgr use case manager - * \return PCM number if success, otherwise negative - */ -int snd_use_case_get_verb_capture_pcm(snd_use_case_mgr_t *uc_mgr) +long device_status(snd_use_case_mgr_t *uc_mgr, + const char *device_name) { - struct use_case_verb *verb; - int ret = -EINVAL; - - pthread_mutex_lock(&uc_mgr->mutex); - - if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) { - verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - ret = verb->capture_pcm; - } - - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; + struct use_case_device *dev; + struct list_head *pos; + + list_for_each(pos, &uc_mgr->active_devices) { + dev = list_entry(pos, struct use_case_device, active_list); + if (strcmp(dev->name, device_name) == 0) + return 1; + } + return 0; } -/** - * \brief Get current use case modifier playback PCM - * \param uc_mgr use case manager - * \return PCM number if success, otherwise negative - */ -int snd_use_case_get_mod_playback_pcm(snd_use_case_mgr_t *uc_mgr, - const char *modifier_name) +long modifier_status(snd_use_case_mgr_t *uc_mgr, + const char *modifier_name) { - struct use_case_modifier *modifier; - int ret = -EINVAL; - - pthread_mutex_lock(&uc_mgr->mutex); - - modifier = get_modifier(uc_mgr, modifier_name, NULL); - if (modifier == NULL) - uc_error("error: use case modifier %s not found", - modifier_name); - else - ret = modifier->playback_pcm; - - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; + struct use_case_modifier *mod; + struct list_head *pos; + + list_for_each(pos, &uc_mgr->active_modifiers) { + mod = list_entry(pos, struct use_case_modifier, active_list); + if (strcmp(mod->name, modifier_name) == 0) + return 1; + } + return 0; } -/** - * \brief Get current use case modifier playback PCM - * \param uc_mgr use case manager - * \return PCM number if success, otherwise negative - */ -int snd_use_case_get_mod_capture_pcm(snd_use_case_mgr_t *uc_mgr, - const char *modifier_name) -{ - struct use_case_modifier *modifier; - int ret = -EINVAL; - - pthread_mutex_lock(&uc_mgr->mutex); - - modifier = get_modifier(uc_mgr, modifier_name, NULL); - if (modifier == NULL) - uc_error("error: use case modifier %s not found", - modifier_name); - else - ret = modifier->capture_pcm; - - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} /** - * \brief Get volume/mute control name depending on use case device. - * \param uc_mgr use case manager - * \param type the control type we are looking for - * \param device_name The use case device we are interested in. - * \return control name if success, otherwise NULL - * - * Get the control id for common volume and mute controls that are aliased - * in the named use case device. + * \brief Get current - integer + * \param uc_mgr Use case manager + * \param identifier + * \return Value if success, otherwise a negative error code */ -const char *snd_use_case_get_device_ctl_elem_name(snd_use_case_mgr_t *uc_mgr, - enum snd_use_case_control_alias type, const char *device_name) +long snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, + const char *identifier) { - struct use_case_device *device; - const char *kcontrol_name = NULL; + char *str, *str1; + long err; pthread_mutex_lock(&uc_mgr->mutex); - - device = get_device(uc_mgr, device_name, NULL); - if (!device) { - uc_error("error: device %s not found", device_name); - goto out; - } - - switch (type) { - case SND_USE_CASE_ALIAS_PLAYBACK_VOLUME: - kcontrol_name = device->playback_volume_id; - break; - case SND_USE_CASE_ALIAS_CAPTURE_VOLUME: - kcontrol_name = device->capture_volume_id; - break; - case SND_USE_CASE_ALIAS_PLAYBACK_SWITCH: - kcontrol_name = device->playback_switch_id; - break; - case SND_USE_CASE_ALIAS_CAPTURE_SWITCH: - kcontrol_name = device->capture_switch_id; - break; - default: - uc_error("error: invalid control alias %d", type); - break; - } - -out: + if (0) { + /* nothing here - prepared for fixed identifiers */ + } else { + str1 = strchr(identifier, '/'); + if (str1) { + str = strdup(str1 + 1); + if (str == NULL) { + err = -ENOMEM; + goto __end; + } + } else { + str = NULL; + } + if (check_identifier(identifier, "_devstatus")) + err = device_status(uc_mgr, str); + else if (check_identifier(identifier, "_modstatus")) + err = modifier_status(uc_mgr, str); + else + err = -EINVAL; + if (str) + free(str); + } + __end: pthread_mutex_unlock(&uc_mgr->mutex); - - return kcontrol_name; + return err; } /** - * \brief Get volume/mute control IDs depending on use case modifier. - * \param uc_mgr use case manager - * \param type the control type we are looking for - * \param modifier_name The use case modifier we are interested in. - * \return ID if success, otherwise a negative error code - * - * Get the control id for common volume and mute controls that are aliased - * in the named use case device. + * \brief Set new + * \param uc_mgr Use case manager + * \param identifier + * \param value Value + * \return Zero if success, otherwise a negative error code */ -const char *snd_use_case_get_modifier_ctl_elem_name(snd_use_case_mgr_t *uc_mgr, - enum snd_use_case_control_alias type, const char *modifier_name) +int snd_use_case_set(snd_use_case_mgr_t *uc_mgr, + const char *identifier, + const char *value) { - struct use_case_modifier *modifier; - const char *kcontrol_name = NULL; + char *str, *str1; + int err; pthread_mutex_lock(&uc_mgr->mutex); - - modifier = get_modifier(uc_mgr, modifier_name, NULL); - if (!modifier) { - uc_error("error: modifier %s not found", modifier_name); - goto out; - } - - switch (type) { - case SND_USE_CASE_ALIAS_PLAYBACK_VOLUME: - kcontrol_name = modifier->playback_volume_id; - break; - case SND_USE_CASE_ALIAS_CAPTURE_VOLUME: - kcontrol_name = modifier->capture_volume_id; - break; - case SND_USE_CASE_ALIAS_PLAYBACK_SWITCH: - kcontrol_name = modifier->playback_switch_id; - break; - case SND_USE_CASE_ALIAS_CAPTURE_SWITCH: - kcontrol_name = modifier->capture_switch_id; - break; - default: - uc_error("error: invalid control alias %d", type); - break; - } - -out: + if (strcmp(identifier, "_verb") == 0) + err = set_verb_user(uc_mgr, value); + else if (strcmp(identifier, "_enadev") == 0) + err = set_device_user(uc_mgr, value, 1); + else if (strcmp(identifier, "_disdev") == 0) + err = set_device_user(uc_mgr, value, 0); + else if (strcmp(identifier, "_enamod") == 0) + err = set_modifier_user(uc_mgr, value, 1); + else if (strcmp(identifier, "_dismod") == 0) + err = set_modifier_user(uc_mgr, value, 0); + else { + str1 = strchr(identifier, '/'); + if (str1) { + str = strdup(str1 + 1); + if (str == NULL) { + err = -ENOMEM; + goto __end; + } + } else { + str = NULL; + } + if (check_identifier(identifier, "_swdev")) + err = switch_device(uc_mgr, str, value); + else if (check_identifier(identifier, "_swmod")) + err = switch_modifier(uc_mgr, str, value); + else + err = -EINVAL; + if (str) + free(str); + } + __end: pthread_mutex_unlock(&uc_mgr->mutex); - - return kcontrol_name; + return err; } -#endif diff --git a/src/ucm/parser.c b/src/ucm/parser.c index bb047515..e540c20d 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -219,6 +219,88 @@ static int parse_sequence(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, return 0; } +/* + * Parse values. + * + * Parse values describing PCM, control/mixer settings and stream parameters. + * + * Value { + * TQ Voice + * CapturePCM "hw:1" + * PlaybackVolume "name='Master Playback Volume',index=2" + * PlaybackSwitch "name='Master Playback Switch',index=2" + * } + */ +static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, + struct list_head *base, + snd_config_t *cfg) +{ + struct ucm_value *curr; + snd_config_iterator_t i, next, j, next2; + snd_config_t *n, *n2; + long l; + long long ll; + double d; + snd_config_type_t type; + int err; + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + snd_config_for_each(j, next2, n) { + const char *id; + n2 = snd_config_iterator_entry(i); + err = snd_config_get_id(n2, &id); + if (err < 0) + continue; + + /* alloc new value */ + curr = calloc(1, sizeof(struct ucm_value)); + if (curr == NULL) + return -ENOMEM; + list_add_tail(&curr->list, base); + curr->name = strdup(id); + if (curr->name == NULL) + return -ENOMEM; + type = snd_config_get_type(n2); + switch (type) { + case SND_CONFIG_TYPE_INTEGER: + curr->data = malloc(16); + if (curr->data == NULL) + return -ENOMEM; + snd_config_get_integer(n2, &l); + sprintf(curr->data, "%li", l); + break; + case SND_CONFIG_TYPE_INTEGER64: + curr->data = malloc(32); + if (curr->data == NULL) + return -ENOMEM; + snd_config_get_integer64(n2, &ll); + sprintf(curr->data, "%lli", ll); + break; + case SND_CONFIG_TYPE_REAL: + curr->data = malloc(64); + if (curr->data == NULL) + return -ENOMEM; + snd_config_get_real(n2, &d); + sprintf(curr->data, "%-16g", d); + break; + case SND_CONFIG_TYPE_STRING: + err = parse_string(n2, &curr->data); + if (err < 0) { + uc_error("error: unable to parse a string for id '%s'!", id); + return err; + } + break; + default: + uc_error("error: invalid type %i in Value compound", type); + return -EINVAL; + } + } + } + + return 0; +} + /* * Parse Modifier Use cases * @@ -240,10 +322,12 @@ static int parse_sequence(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, * ] * * # Optional TQ and ALSA PCMs - * TQ Voice - * CapturePCM "hw:1" - * MasterPlaybackVolume "name='Master Playback Volume',index=2" - * MasterPlaybackSwitch "name='Master Playback Switch',index=2" + * Value { + * TQ Voice + * CapturePCM "hw:1" + * PlaybackVolume "name='Master Playback Volume',index=2" + * PlaybackSwitch "name='Master Playback Switch',index=2" + * } * * } */ @@ -267,6 +351,7 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, INIT_LIST_HEAD(&modifier->disable_list); INIT_LIST_HEAD(&modifier->transition_list); INIT_LIST_HEAD(&modifier->dev_list); + INIT_LIST_HEAD(&modifier->value_list); list_add_tail(&modifier->list, &verb->modifier_list); err = snd_config_get_id(cfg, &id); if (err < 0) @@ -335,64 +420,10 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, continue; } - if (strcmp(id, "TQ") == 0) { - err = parse_string(n, &modifier->tq); + if (strcmp(id, "Value") == 0) { + err = parse_value(uc_mgr, &modifier->value_list, n); if (err < 0) { - uc_error("error: failed to parse TQ"); - return err; - } - continue; - } - - if (strcmp(id, "CapturePCM") == 0) { - err = parse_string(n, &modifier->capture_pcm); - if (err < 0) { - uc_error("error: failed to get Capture PCM ID"); - return err; - } - continue; - } - - if (strcmp(id, "PlaybackPCM") == 0) { - err = parse_string(n, &modifier->playback_pcm); - if (err < 0) { - uc_error("error: failed to get Playback PCM ID"); - return err; - } - continue; - } - - if (strcmp(id, "MasterPlaybackVolume") == 0) { - err = parse_string(n, &modifier->playback_volume_id); - if (err < 0) { - uc_error("error: failed to get MasterPlaybackVolume"); - return err; - } - continue; - } - - if (strcmp(id, "MasterPlaybackSwitch") == 0) { - err = parse_string(n, &modifier->playback_switch_id); - if (err < 0) { - uc_error("error: failed to get MasterPlaybackSwitch"); - return err; - } - continue; - } - - if (strcmp(id, "MasterCaptureVolume") == 0) { - err = parse_string(n, &modifier->capture_volume_id); - if (err < 0) { - uc_error("error: failed to get MasterCaptureVolume"); - return err; - } - continue; - } - - if (strcmp(id, "MasterCaptureSwitch") == 0) { - err = parse_string(n, &modifier->capture_switch_id); - if (err < 0) { - uc_error("error: failed to get MasterCaptureSwitch"); + uc_error("error: failed to parse Value"); return err; } continue; @@ -422,9 +453,10 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, * ... * ] * - * MasterPlaybackVolume "name='Master Playback Volume',index=2" - * MasterPlaybackSwitch "name='Master Playback Switch',index=2" - * + * Value { + * PlaybackVolume "name='Master Playback Volume',index=2" + * PlaybackSwitch "name='Master Playback Switch',index=2" + * } * } */ static int parse_device_index(snd_use_case_mgr_t *uc_mgr, @@ -446,17 +478,16 @@ static int parse_device_index(snd_use_case_mgr_t *uc_mgr, INIT_LIST_HEAD(&device->enable_list); INIT_LIST_HEAD(&device->disable_list); INIT_LIST_HEAD(&device->transition_list); + INIT_LIST_HEAD(&device->value_list); list_add_tail(&device->list, &verb->device_list); - device->name = strdup(name); - if (device->name == NULL) - return -ENOMEM; if (snd_config_get_id(cfg, &id) < 0) return -EINVAL; - err = safe_strtol(id, &device->idx); - if (err < 0) { - uc_error("Invalid device index '%s'", id); - return -EINVAL; - } + device->name = malloc(strlen(name) + strlen(id) + 2); + if (device->name == NULL) + return -ENOMEM; + strcpy(device->name, name); + strcat(device->name, "."); + strcat(device->name, id); snd_config_for_each(i, next, cfg) { const char *id; @@ -506,37 +537,10 @@ static int parse_device_index(snd_use_case_mgr_t *uc_mgr, continue; } - if (strcmp(id, "MasterPlaybackVolume") == 0) { - err = parse_string(n, &device->playback_volume_id); - if (err < 0) { - uc_error("error: failed to get MasterPlaybackVolume"); - return err; - } - continue; - } - - if (strcmp(id, "MasterPlaybackSwitch") == 0) { - err = parse_string(n, &device->playback_switch_id); - if (err < 0) { - uc_error("error: failed to get MasterPlaybackSwitch"); - return err; - } - continue; - } - - if (strcmp(id, "MasterCaptureVolume") == 0) { - err = parse_string(n, &device->capture_volume_id); + if (strcmp(id, "Value") == 0) { + err = parse_value(uc_mgr, &device->value_list, n); if (err < 0) { - uc_error("error: failed to get MasterCaptureVolume"); - return err; - } - continue; - } - - if (strcmp(id, "MasterCaptureSwitch") == 0) { - err = parse_string(n, &device->capture_switch_id); - if (err < 0) { - uc_error("error: failed to get MasterCaptureSwitch"); + uc_error("error: failed to parse Value"); return err; } continue; @@ -577,7 +581,7 @@ static int parse_device(snd_use_case_mgr_t *uc_mgr, * # 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" + * 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" @@ -592,10 +596,11 @@ static int parse_device(snd_use_case_mgr_t *uc_mgr, * ] * * # Optional TQ and ALSA PCMs - * TQ HiFi - * CapturePCM 0 - * PlaybackPCM 0 - * + * Value { + * TQ HiFi + * CapturePCM 0 + * PlaybackPCM 0 + * } * } */ static int parse_verb(snd_use_case_mgr_t *uc_mgr, @@ -643,25 +648,9 @@ static int parse_verb(snd_use_case_mgr_t *uc_mgr, continue; } - if (strcmp(id, "TQ") == 0) { - uc_dbg("Parse TQ"); - err = parse_string(n, &verb->tq); - if (err < 0) - return err; - continue; - } - - if (strcmp(id, "CapturePCM") == 0) { - uc_dbg("Parse CapturePCM"); - err = parse_string(n, &verb->capture_pcm); - if (err < 0) - return err; - continue; - } - - if (strcmp(id, "PlaybackPCM") == 0) { - uc_dbg("Parse PlaybackPCM"); - err = parse_string(n, &verb->playback_pcm); + if (strcmp(id, "Value") == 0) { + uc_dbg("Parse Value"); + err = parse_value(uc_mgr, &verb->value_list, n); if (err < 0) return err; continue; @@ -703,6 +692,7 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, INIT_LIST_HEAD(&verb->transition_list); INIT_LIST_HEAD(&verb->device_list); INIT_LIST_HEAD(&verb->modifier_list); + INIT_LIST_HEAD(&verb->value_list); list_add_tail(&verb->list, &uc_mgr->verb_list); verb->name = strdup(use_case_name); if (verb->name == NULL) diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index ffef8f00..a3c1dcb4 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -50,6 +50,12 @@ #define SEQUENCE_ELEMENT_TYPE_SLEEP 2 #define SEQUENCE_ELEMENT_TYPE_EXEC 3 +struct ucm_value { + struct list_head list; + char *name; + char *data; +}; + struct sequence_element { struct list_head list; unsigned int type; @@ -99,20 +105,8 @@ struct use_case_modifier { /* list of supported devices per modifier */ struct list_head dev_list; - /* ALSA PCM devices associated with any modifier PCM streams */ - char *capture_pcm; - char *playback_pcm; - - /* Any modifier stream TQ */ - char *tq; - - /* aliased controls */ - char *playback_ctl; - char *playback_volume_id; - char *playback_switch_id; - char *capture_ctl; - char *capture_volume_id; - char *capture_switch_id; + /* values */ + struct list_head value_list; }; /* @@ -127,7 +121,6 @@ struct use_case_device { char *name; char *comment; - long idx; /* index for similar devices i.e. 2 headphone jacks */ /* device enable and disable sequences */ struct list_head enable_list; @@ -136,13 +129,8 @@ struct use_case_device { /* device transition list */ struct list_head transition_list; - /* aliased controls */ - char *playback_ctl; - char *playback_volume_id; - char *playback_switch_id; - char *capture_ctl; - char *capture_volume_id; - char *capture_switch_id; + /* value list */ + struct list_head value_list; }; /* @@ -164,16 +152,14 @@ struct use_case_verb { /* verb transition list */ struct list_head transition_list; - /* verb PCMs and TQ */ - char *tq; - char *capture_pcm; - char *playback_pcm; - /* hardware devices that can be used with this use case */ struct list_head device_list; /* modifiers that can be used with this use case */ struct list_head modifier_list; + + /* value list */ + struct list_head value_list; }; /* diff --git a/src/ucm/utils.c b/src/ucm/utils.c index 98e3cf65..83926da3 100644 --- a/src/ucm/utils.c +++ b/src/ucm/utils.c @@ -80,6 +80,19 @@ int uc_mgr_config_load(const char *file, snd_config_t **cfg) return 0; } +void uc_mgr_free_value(struct list_head *base) +{ + struct list_head *pos, *npos; + struct ucm_value *val; + + list_for_each_safe(pos, npos, base) { + val = list_entry(pos, struct ucm_value, list); + free(val->name); + free(val->data); + list_del(pos); + } +} + void uc_mgr_free_dev_list(struct list_head *base) { struct list_head *pos, *npos; @@ -147,15 +160,7 @@ void uc_mgr_free_modifier(struct list_head *base) uc_mgr_free_sequence(&mod->disable_list); uc_mgr_free_transition(&mod->transition_list); uc_mgr_free_dev_list(&mod->dev_list); - free(mod->capture_pcm); - free(mod->playback_pcm); - free(mod->tq); - free(mod->playback_ctl); - free(mod->playback_volume_id); - free(mod->playback_switch_id); - free(mod->capture_ctl); - free(mod->capture_volume_id); - free(mod->capture_switch_id); + uc_mgr_free_value(&mod->value_list); free(mod); list_del(pos); } @@ -173,12 +178,7 @@ void uc_mgr_free_device(struct list_head *base) uc_mgr_free_sequence(&dev->enable_list); uc_mgr_free_sequence(&dev->disable_list); uc_mgr_free_transition(&dev->transition_list); - free(dev->playback_ctl); - free(dev->playback_volume_id); - free(dev->playback_switch_id); - free(dev->capture_ctl); - free(dev->capture_volume_id); - free(dev->capture_switch_id); + uc_mgr_free_value(&dev->value_list); free(dev); list_del(pos); } @@ -196,9 +196,7 @@ void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr) uc_mgr_free_sequence(&verb->enable_list); uc_mgr_free_sequence(&verb->disable_list); uc_mgr_free_transition(&verb->transition_list); - free(verb->tq); - free(verb->capture_pcm); - free(verb->playback_pcm); + uc_mgr_free_value(&verb->value_list); uc_mgr_free_device(&verb->device_list); uc_mgr_free_modifier(&verb->modifier_list); free(verb); -- cgit v1.2.1