[alsa-devel] [PATCH 1/2] ucm: Automatically load the best config file based on the card long name
Pierre-Louis Bossart
pierre-louis.bossart at linux.intel.com
Wed Dec 21 15:46:45 CET 2016
On 12/21/16 7:09 AM, mengdong.lin at linux.intel.com wrote:
> From: Mengdong Lin <mengdong.lin at linux.intel.com>
>
> Intel DSP platform drivers are used by many different devices. For user
> space to differentiate them, ASoC machine driver may include the DMI info
> (vendor, product and board) in card long name. Possible card long names
> are:
> broadwell-rt286-Dell Inc.-XPS 13 9343-0310JH
> broadwell-rt286-Intel Corp.-Broadwell Client platform-Wilson Beach SDS
> bytcr-rt5640-ASUSTeK COMPUTER INC.-T100TA-T100TA
> bytcr-rt5651-Circuitco-Minnowboard Max D0 PLATFORM-MinnowBoard MAX
> ...
>
> And user space can define configuration files including fields separated
> by '.' as below:
> broadwell-rt286
> broadwell-rt286.Dell.XPS
> bytcr-rt5640
> bytcr-rt5640.ASUS.T100
> bytcr-rt5651.MinnowboardMax
> ...
It's weird to use a different naming convention in driver and userspace.
Now the '.' is a separator?
> When being asked to load configuration file of a card, UCM will try to
> find the card long name from the local machine, and then scan all
> available configuration file names, search every field of config file
> name in the card long name. The more characters match, the higher score
> the file has. Finally, the file with the highest score will be loaded to
> configure the sound card.
There are cases where we absolutely don't want to let users use a
shorter name. T100 is terrible for example, it would work for T100TA,
T100TAF, T00HA which are completely different platforms.
While the partial matching is elegant, I would err on the side of
simplicity and keep the filename as reported by the driver. that way
people just copy/paste the longname name and there are fewer
opportunities for screwups. Yes the file names would be a tad long but
we are not using DOS, are we?
>
> Signed-off-by: Mengdong Lin <mengdong.lin at linux.intel.com>
>
> diff --git a/src/ucm/parser.c b/src/ucm/parser.c
> index c98373a..ff75da7 100644
> --- a/src/ucm/parser.c
> +++ b/src/ucm/parser.c
> @@ -55,6 +55,9 @@ static const char * const component_dir[] = {
> NULL, /* terminator */
> };
>
> +static int filename_filter(const struct dirent *dirent);
> +static int is_component_directory(const char *dir);
> +
> static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
> struct list_head *base,
> snd_config_t *cfg);
> @@ -1328,6 +1331,210 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
> return 0;
> }
>
> +/* trim SPACE (0x20) from the string */
> +static void trim_space(char *str)
> +{
> + int i, j = 0;
> +
> + for (i = 0; str[i] && i < MAX_FILE; i++) {
> + if (str[i] != 0x20)
> + str[j++] = str[i];
> + }
> +
> + str[j] = '\0';
> +}
> +
> +/* find and store the card long name if the card is in this machine */
> +static int get_card_long_name(snd_use_case_mgr_t *mgr)
> +{
> + const char *card_name = mgr->card_name;
> + snd_ctl_t *handle;
> + int card, err, dev, idx;
> + snd_ctl_card_info_t *info;
> + const char *_name, *_long_name;
> +
> + snd_ctl_card_info_alloca(&info);
> +
> + card = -1;
> + if (snd_card_next(&card) < 0 || card < 0) {
> + uc_error("no soundcards found...");
> + return -1;
> + }
> +
> + while (card >= 0) {
> + char name[32];
> +
> + sprintf(name, "hw:%d", card);
> + err = snd_ctl_open(&handle, name, 0);
> + if (err < 0) {
> + uc_error("control open (%i): %s", card,
> + snd_strerror(err));
> + goto next_card;
> + }
> +
> + err = snd_ctl_card_info(handle, info);
> + if (err < 0) {
> + uc_error("control hardware info (%i): %s", card,
> + snd_strerror(err));
> + snd_ctl_close(handle);
> + goto next_card;
> + }
> +
> + _name = snd_ctl_card_info_get_name(info);
> + if (!strncmp(card_name, _name, 32)) {
> + _long_name = snd_ctl_card_info_get_longname(info);
> + strncpy(mgr->card_long_name, _long_name,
> + MAX_CARD_LONG_NAME);
> + snd_ctl_close(handle);
> + return 0;
> + }
> +
> + snd_ctl_close(handle);
> +next_card:
> + if (snd_card_next(&card) < 0) {
> + uc_error("snd_card_next");
> + break;
> + }
> + }
> +
> + return -1;
> +}
> +
> +/* This function will find the best device-specific configuration file based
> + * on the sound card long name.
> + * Different devices may share the same sound driver and thus the same sound
> + * card name (short name), but they may still need different device-specific
> + * configurations. For user space to differentiate them, kernel drivers may
> + * include the DMI info (vendor, product and board) in the card long name.
> + * And user space can define configuration file names appending DMI keywords
> + * to the card name, like:
> + * bytcr-rt5640.ASUS.T100
> + * bytcr-rt5651.MinnowBoard
> + *
> + * When being asked to load the configuration file for a card, this function
> + * will try to find the card long name from the local machine, and then scan
> + * all available configuration file names, search every field of the config
> + * file name in the card long name. The more characters match, the higher
> + * score the file has. Finally, the file with the highest score will be loaded.
> + */
> +static void find_best_config_file(snd_use_case_mgr_t *mgr)
> +{
> + const char *card_name = mgr->card_name;
> + char name[MAX_FILE];
> + char *env = getenv(ALSA_CONFIG_UCM_VAR);
> + struct dirent **namelist;
> + char card_long_name[MAX_CARD_LONG_NAME];
> + char *lpos, *lpos_next, *lname_end;
> + char *d_name, *dpos, *dpos_next, *d_name_end;
> + int d_name_len, field_len, long_name_len;
> + int i, cnt, err;
> + int score, score_max = 0, d_index = -1;
> + int is_first_field;
> +
> + if (get_card_long_name(mgr) < 0) {
> + /* cannot get long name, use card name as the conf file name */
> + strcpy(mgr->conf_file_name, mgr->card_name);
> + return;
> + }
> +
> + strncpy(card_long_name, mgr->card_long_name, MAX_CARD_LONG_NAME);
> + trim_space(card_long_name); /* trim SPACE for matching */
> + lname_end = card_long_name + strlen(card_long_name);
> +
> + /* get list of card directories under /usr/share/alsa/ucm */
> + snprintf(name, sizeof(name)-1,
> + "%s", env ? env : ALSA_USE_CASE_DIR);
> + name[MAX_FILE-1] = '\0';
> +
> + err = scandir(name, &namelist, filename_filter, alphasort);
> + if (err < 0) {
> + err = -errno;
> + uc_error("error: could not scan directory %s: %s",
> + name, strerror(-err));
> + return;
> + }
> + cnt = err;
> +
> + /* scan the card directory names */
> + for (i = 0; i < cnt; i++) {
> +
> + /* Skip the directories for component devices */
> + if (is_component_directory(namelist[i]->d_name))
> + continue;
> +
> + score = 0;
> + lpos = card_long_name;
> +
> + d_name = namelist[i]->d_name;
> + d_name_len = strlen(d_name);
> + d_name_end = d_name + d_name_len;
> + is_first_field = 1;
> + dpos = d_name;
> +
> + /* scan fields in the card directory name, separated by '.' */
> + while (1) {
> + dpos_next = strchr(dpos, '.');
> + if (dpos_next == dpos) /* start with '.' */
> + goto next_field;
> +
> + if (!dpos_next) /* last field */
> + field_len = d_name_end - dpos;
> + else
> + field_len = dpos_next - dpos;
> +
> + memcpy(name, dpos, field_len);
> + name[field_len] = '\0';
> +
> + /* search the field in the given card long name */
> + lpos_next = strstr(lpos, name);
> + if (!lpos_next) {
> + /* first field is the card name, must match */
> + if (is_first_field)
> + break;
> + goto next_field;
> + } else {
> + /* The more characters match,
> + * the higher the score is.
> + */
> + score += field_len;
> + if (lpos_next + field_len >= lname_end)
> + break; /* reach end of long name */
> +
> + /* Skip the matched field,
> + * for searching next field.
> + */
> + lpos = lpos_next + field_len;
> + }
> +
> +next_field:
> + if (!dpos_next)
> + break; /* no more fields */
> +
> + dpos = dpos_next + 1; /* skip the separator '.' */
> + is_first_field = 0;
> + if (dpos >= d_name_end)
> + break;
> + }
> +
> + if (score > score_max) {
> + score_max = score;
> + d_index = i;
> + } else if (score == 0 && score_max > 0) {
> + /* passed the card directories that can match,
> + * since directories are in alphabetical order.
> + */
> + break;
> + }
> + }
> +
> + if (d_index >= 0)
> + strncpy(mgr->conf_file_name, namelist[d_index]->d_name,
> + MAX_CARD_LONG_NAME);
> + else
> + strncpy(mgr->conf_file_name, mgr->card_name,
> + MAX_CARD_LONG_NAME);
> +}
> +
> static int load_master_config(const char *card_name, snd_config_t **cfg)
> {
> char filename[MAX_FILE];
> @@ -1355,7 +1562,9 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
> snd_config_t *cfg;
> int err;
>
> - err = load_master_config(uc_mgr->card_name, &cfg);
> + find_best_config_file(uc_mgr);
> +
> + err = load_master_config(uc_mgr->conf_file_name, &cfg);
> if (err < 0)
> return err;
> err = parse_master_file(uc_mgr, cfg);
> diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h
> index 6d3302f..299a5b9 100644
> --- a/src/ucm/ucm_local.h
> +++ b/src/ucm/ucm_local.h
> @@ -41,6 +41,7 @@
> #include "use-case.h"
>
> #define MAX_FILE 256
> +#define MAX_CARD_LONG_NAME 80
> #define ALSA_USE_CASE_DIR ALSA_CONFIG_DIR "/ucm"
>
> #define SEQUENCE_ELEMENT_TYPE_CDEV 1
> @@ -190,6 +191,8 @@ struct use_case_verb {
> */
> struct snd_use_case_mgr {
> char *card_name;
> + char card_long_name[MAX_CARD_LONG_NAME];
> + char conf_file_name[MAX_CARD_LONG_NAME];
> char *comment;
>
> /* use case verb, devices and modifier configs parsed from files */
>
More information about the Alsa-devel
mailing list