[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