* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Support for the verb/device/modifier core logic and API,
* command line tool and file parser was kindly sponsored by
/*
* Parse safe ID
*/
-int parse_is_name_safe(char *name)
+int parse_is_name_safe(const char *name)
{
if (strchr(name, '.')) {
- uc_error("char '.' now allowed in '%s'", name);
+ uc_error("char '.' not allowed in '%s'", name);
return 0;
}
return 1;
err = snd_config_get_id(n, id);
if (err < 0)
return err;
- return parse_is_name_safe((char *)(*id));
+ if (!parse_is_name_safe((char *)(*id)))
+ return -EINVAL;
+ return 0;
}
/*
n = snd_config_iterator_entry(i);
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
- uc_error("compound type expected for %s", id);
+ uc_error("compound type expected for %s, is %d", id, snd_config_get_type(cfg));
return -EINVAL;
}
return 0;
}
+static int strip_legacy_dev_index(char *name)
+{
+ char *dot = strchr(name, '.');
+ if (!dot)
+ return 0;
+ if (dot[1] != '0' || dot[2] != '\0') {
+ uc_error("device name %s contains a '.',"
+ " and is not legacy foo.0 format", name);
+ return -EINVAL;
+ }
+ *dot = '\0';
+ return 0;
+}
/*
- * Parse transition
+ * Parse device list
*/
-static int parse_supported_device(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
- struct list_head *dlist,
- snd_config_t *cfg)
+static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
+ struct dev_list *dev_list,
+ enum dev_list_type type,
+ snd_config_t *cfg)
{
- struct dev_list *sdev;
+ struct dev_list_node *sdev;
const char *id;
snd_config_iterator_t i, next;
snd_config_t *n;
int err;
+ if (dev_list->type != DEVLIST_NONE) {
+ uc_error("error: multiple supported or"
+ " conflicting device lists");
+ return -EEXIST;
+ }
+
if (snd_config_get_id(cfg, &id) < 0)
return -EINVAL;
if (snd_config_get_id(n, &id) < 0)
return -EINVAL;
- sdev = calloc(1, sizeof(struct dev_list));
+ sdev = calloc(1, sizeof(struct dev_list_node));
if (sdev == NULL)
return -ENOMEM;
err = parse_string(n, &sdev->name);
free(sdev);
return err;
}
- list_add(&sdev->list, dlist);
+ err = strip_legacy_dev_index(sdev->name);
+ if (err < 0) {
+ free(sdev->name);
+ free(sdev);
+ return err;
+ }
+ list_add(&sdev->list, &dev_list->list);
}
+
+ dev_list->type = type;
+
return 0;
}
continue;
}
+ if (strcmp(cmd, "msleep") == 0) {
+ curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
+ err = snd_config_get_integer(n, &curr->data.sleep);
+ if (err < 0) {
+ uc_error("error: msleep requires integer!");
+ return err;
+ }
+ curr->data.sleep *= 1000L;
+ continue;
+ }
+
if (strcmp(cmd, "exec") == 0) {
curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
err = parse_string(n, &curr->data.exec);
* Parse Modifier Use cases
*
* # Each modifier is described in new section. N modifiers are allowed
- * SectionModifier."Capture Voice".0 {
+ * SectionModifier."Capture Voice" {
*
* Comment "Record voice call"
+ *
* SupportedDevice [
- * "x" # all x device instances
- * "y.0" # device y instance 0 only
+ * "x"
+ * "y"
+ * ]
+ *
+ * ConflictingDevice [
+ * "x"
+ * "y"
* ]
*
* EnableSequence [
* }
*
* }
+ *
+ * SupportedDevice and ConflictingDevice cannot be specified together.
+ * Both are optional.
*/
static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
snd_config_t *cfg,
{
struct use_case_verb *verb = data1;
struct use_case_modifier *modifier;
- char *name = data2;
- const char *id;
+ const char *name;
snd_config_iterator_t i, next;
snd_config_t *n;
int err;
- if (!parse_is_name_safe(name))
- return -EINVAL;
+ if (data2) {
+ name = data2;
+ if (!parse_is_name_safe(name))
+ return -EINVAL;
+ }
+ else {
+ if (parse_get_safe_id(cfg, &name) < 0)
+ return -EINVAL;
+ }
+
/* allocate modifier */
modifier = calloc(1, sizeof(*modifier));
if (modifier == NULL)
INIT_LIST_HEAD(&modifier->enable_list);
INIT_LIST_HEAD(&modifier->disable_list);
INIT_LIST_HEAD(&modifier->transition_list);
- INIT_LIST_HEAD(&modifier->dev_list);
+ INIT_LIST_HEAD(&modifier->dev_list.list);
INIT_LIST_HEAD(&modifier->value_list);
list_add_tail(&modifier->list, &verb->modifier_list);
- err = parse_get_safe_id(cfg, &id);
- if (err < 0)
- return err;
- modifier->name = malloc(strlen(name) + strlen(id) + 2);
- if (modifier->name == NULL)
- return -ENOMEM;
- strcpy(modifier->name, name);
- strcat(modifier->name, ".");
- strcat(modifier->name, id);
+ modifier->name = strdup(name);
snd_config_for_each(i, next, cfg) {
const char *id;
}
if (strcmp(id, "SupportedDevice") == 0) {
- err = parse_supported_device(uc_mgr, &modifier->dev_list, n);
+ err = parse_device_list(uc_mgr, &modifier->dev_list,
+ DEVLIST_SUPPORTED, n);
if (err < 0) {
uc_error("error: failed to parse supported"
" device list");
}
}
+ if (strcmp(id, "ConflictingDevice") == 0) {
+ err = parse_device_list(uc_mgr, &modifier->dev_list,
+ DEVLIST_CONFLICTING, n);
+ if (err < 0) {
+ uc_error("error: failed to parse conflicting"
+ " device list");
+ return err;
+ }
+ }
+
if (strcmp(id, "EnableSequence") == 0) {
err = parse_sequence(uc_mgr, &modifier->enable_list, n);
if (err < 0) {
}
}
- if (list_empty(&modifier->dev_list)) {
- uc_error("error: %s: modifier missing supported device sequence", modifier->name);
- return -EINVAL;
- }
-
return 0;
}
* Parse Device Use Cases
*
*# Each device is described in new section. N devices are allowed
- *SectionDevice."Headphones".0 {
+ *SectionDevice."Headphones" {
* Comment "Headphones connected to 3.5mm jack"
*
+ * upportedDevice [
+ * "x"
+ * "y"
+ * ]
+ *
+ * ConflictingDevice [
+ * "x"
+ * "y"
+ * ]
+ *
* EnableSequence [
* ....
* ]
* }
* }
*/
-static int parse_device_index(snd_use_case_mgr_t *uc_mgr,
- snd_config_t *cfg,
- void *data1,
- void *data2)
+static int parse_device(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *cfg,
+ void *data1,
+ void *data2)
{
struct use_case_verb *verb = data1;
- char *name = data2;
+ const char *name;
struct use_case_device *device;
- const char *id;
snd_config_iterator_t i, next;
snd_config_t *n;
int err;
-
- if (!parse_is_name_safe(name))
- return -EINVAL;
+
+ if (data2) {
+ name = data2;
+ if (!parse_is_name_safe(name))
+ return -EINVAL;
+ }
+ else {
+ if (parse_get_safe_id(cfg, &name) < 0)
+ return -EINVAL;
+ }
+
device = calloc(1, sizeof(*device));
if (device == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&device->enable_list);
INIT_LIST_HEAD(&device->disable_list);
INIT_LIST_HEAD(&device->transition_list);
+ INIT_LIST_HEAD(&device->dev_list.list);
INIT_LIST_HEAD(&device->value_list);
list_add_tail(&device->list, &verb->device_list);
- if (parse_get_safe_id(cfg, &id) < 0)
- 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);
+ device->name = strdup(name);
snd_config_for_each(i, next, cfg) {
const char *id;
continue;
}
+ if (strcmp(id, "SupportedDevice") == 0) {
+ err = parse_device_list(uc_mgr, &device->dev_list,
+ DEVLIST_SUPPORTED, n);
+ if (err < 0) {
+ uc_error("error: failed to parse supported"
+ " device list");
+ return err;
+ }
+ }
+
+ if (strcmp(id, "ConflictingDevice") == 0) {
+ err = parse_device_list(uc_mgr, &device->dev_list,
+ DEVLIST_CONFLICTING, n);
+ if (err < 0) {
+ uc_error("error: failed to parse conflicting"
+ " device list");
+ return err;
+ }
+ }
+
if (strcmp(id, "EnableSequence") == 0) {
uc_dbg("EnableSequence");
err = parse_sequence(uc_mgr, &device->enable_list, n);
return 0;
}
-static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
- snd_config_t *cfg,
- void *data1,
- void *data2 ATTRIBUTE_UNUSED)
+static int parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *cfg,
+ int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
+ void *data1)
{
- const char *id;
+ const char *id, *idchild;
+ int child_ctr = 0, legacy_format = 1;
+ snd_config_iterator_t i, next;
+ snd_config_t *child;
int err;
err = snd_config_get_id(cfg, &id);
if (err < 0)
return err;
- return parse_compound(uc_mgr, cfg, parse_device_index,
- data1, (void *)id);
+
+ snd_config_for_each(i, next, cfg) {
+ child_ctr++;
+ if (child_ctr > 1) {
+ break;
+ }
+
+ child = snd_config_iterator_entry(i);
+
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+ legacy_format = 0;
+ break;
+ }
+
+ if (snd_config_get_id(child, &idchild) < 0)
+ return -EINVAL;
+
+ if (strcmp(idchild, "0")) {
+ legacy_format = 0;
+ break;
+ }
+ }
+ if (child_ctr != 1) {
+ legacy_format = 0;
+ }
+
+ if (legacy_format)
+ return parse_compound(uc_mgr, cfg, fcn, data1, (void *)id);
+ else
+ return fcn(uc_mgr, cfg, data1, NULL);
}
-static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
+static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
snd_config_t *cfg,
void *data1,
void *data2 ATTRIBUTE_UNUSED)
{
- const char *id;
- int err;
+ return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1);
+}
- err = snd_config_get_id(cfg, &id);
- if (err < 0)
- return err;
- return parse_compound(uc_mgr, cfg, parse_modifier,
- data1, (void *)id);
+static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
+ snd_config_t *cfg,
+ void *data1,
+ void *data2 ATTRIBUTE_UNUSED)
+{
+ return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1);
}
/*