@@ -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,66 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
return 0;
}
+/* find the card in the local machine and store the card long name */
+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;
+ 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;
+ }
+
+ /* Find the local card by comparing the given name with the
+ * card short name and long name. The given card name may be
+ * either a short name or long name, because users may open
+ * the card by either of the two names.
+ */
+ _name = snd_ctl_card_info_get_name(info);
+ _long_name = snd_ctl_card_info_get_longname(info);
+ if (!strcmp(card_name, _name)
+ || !strcmp(card_name, _long_name)) {
+ strcpy(mgr->card_long_name, _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;
+}
static int load_master_config(const char *card_name, snd_config_t **cfg)
{
char filename[MAX_FILE];
@@ -1355,15 +1418,43 @@ static int load_master_config(const char *card_name, snd_config_t **cfg)
return 0;
}
-/* load master use case file for sound card */
+/* load master use case file for sound card
+ *
+ * The same ASoC machine driver can be shared by many different devices.
+ * For user space to differentiate them and get the best device-specific
+ * configuration, ASoC machine drivers may use the DMI info
+ * (vendor-product-version-board) as the card long name. And user space can
+ * define configuration files like longname\longname.conf for a specific device.
+ *
+ * This function will try to find the card in the local machine and get its
+ * long name, then load the file longname\longname.conf to get the best
+ * device-specific configuration. If the card is not found in the local
+ * machine or the device-specific file is not available, fall back to load
+ * the default configuration file name\name.conf.
+ */
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);
- if (err < 0)
- return err;
+ err = get_card_long_name(uc_mgr);
+ if (err == 0) /* load file that maches the card long name */
+ err = load_master_config(uc_mgr->card_long_name, &cfg);
+
+ if (err == 0) {
+ /* got device-specific file that matches the card long name */
+ strcpy(uc_mgr->conf_file_name, uc_mgr->card_long_name);
+ } else {
+ /* Fall back to the file that maches the given card name,
+ * either short name or long name (users may open a card by
+ * its name or long name).
+ */
+ err = load_master_config(uc_mgr->card_name, &cfg);
+ if (err < 0)
+ return err;
+ strcpy(uc_mgr->conf_file_name, uc_mgr->card_name);
+ }
+
err = parse_master_file(uc_mgr, cfg);
snd_config_delete(cfg);
if (err < 0)
@@ -191,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 */