diff --git a/src/common/droid-config.c b/src/common/droid-config.c index 9832e37..52067df 100644 --- a/src/common/droid-config.c +++ b/src/common/droid-config.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2018 Jolla Ltd. + * Copyright (C) 2013-2022 Jolla Ltd. * * Contact: Juho Hämäläinen * @@ -26,6 +26,7 @@ #include "droid/version.h" #include "droid/droid-config.h" #include "droid/sllist.h" +#include "config-parser-xml.h" #include #include @@ -36,52 +37,28 @@ #include #endif -#include -#include -#include #include - -#include -#include -#include -#include -#include -#include -#include -#include #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#define ODM_AUDIO_POLICY_CONFIG_XML_FILE "/odm/etc/audio_policy_configuration.xml" -#define VENDOR_AUDIO_AUDIO_POLICY_CONFIG_XML_FILE "/vendor/etc/audio/audio_policy_configuration.xml" -#define VENDOR_AUDIO_POLICY_CONFIG_XML_FILE "/vendor/etc/audio_policy_configuration.xml" -#define SYSTEM_AUDIO_POLICY_CONFIG_XML_FILE "/system/etc/audio_policy_configuration.xml" +#define ODM_AUDIO_POLICY_CONFIG_XML_FILE "/odm/etc/audio_policy_configuration.xml" +#define VENDOR_AUDIO_AUDIO_POLICY_CONFIG_XML_FILE "/vendor/etc/audio/audio_policy_configuration.xml" +#define VENDOR_AUDIO_POLICY_CONFIG_XML_FILE "/vendor/etc/audio_policy_configuration.xml" +#define SYSTEM_AUDIO_POLICY_CONFIG_XML_FILE "/system/etc/audio_policy_configuration.xml" -pa_droid_config_audio *pa_droid_config_load(pa_modargs *ma) { - pa_droid_config_audio *config = NULL; +dm_config_device *dm_config_load(pa_modargs *ma) { + dm_config_device *config = NULL; const char *manual_config; const char *config_location[] = { ODM_AUDIO_POLICY_CONFIG_XML_FILE, VENDOR_AUDIO_AUDIO_POLICY_CONFIG_XML_FILE, VENDOR_AUDIO_POLICY_CONFIG_XML_FILE, - AUDIO_POLICY_VENDOR_CONFIG_FILE, SYSTEM_AUDIO_POLICY_CONFIG_XML_FILE, - AUDIO_POLICY_CONFIG_FILE, NULL}; pa_assert(ma); @@ -106,92 +83,210 @@ pa_droid_config_audio *pa_droid_config_load(pa_modargs *ma) { return config; } -pa_droid_config_audio *pa_droid_config_dup(const pa_droid_config_audio *config) { - pa_droid_config_audio *config_copy; - pa_droid_config_hw_module *module, *module_copy; - pa_droid_config_device *device, *device_copy; +static dm_config_profile *config_profile_dup(const dm_config_profile *profile) { + dm_config_profile *copy = pa_xnew0(dm_config_profile, 1); + + copy->name = pa_xstrdup(profile->name); + copy->format = profile->format; + memcpy(copy->sampling_rates, + profile->sampling_rates, + sizeof(profile->sampling_rates)); + memcpy(copy->channel_masks, + profile->channel_masks, + sizeof(profile->channel_masks)); + + return copy; +} + +static dm_config_port *config_port_dup(const dm_config_port *port, dm_config_module *module) { + dm_config_port *copy = pa_xnew0(dm_config_port, 1); + const dm_list_entry *i; + + copy->module = module; + copy->port_type = port->port_type; + copy->name = pa_xstrdup(port->name); + copy->role = port->role; + copy->profiles = dm_list_new(); + + DM_LIST_FOREACH(i, port->profiles) + dm_list_push_back(copy->profiles, config_profile_dup(i->data)); + + if (port->port_type == DM_CONFIG_TYPE_DEVICE_PORT) { + copy->type = port->type; + copy->address = pa_xstrdup(port->address); + } + + if (port->port_type == DM_CONFIG_TYPE_MIX_PORT) { + copy->flags = port->flags; + copy->max_open_count = port->max_open_count; + copy->max_active_count = port->max_active_count; + } + + return copy; +} + +static dm_config_route *config_route_dup(const dm_config_route *route, dm_list *ports) { + dm_config_route *copy = pa_xnew0(dm_config_route, 1); + dm_config_port *port_copy, *port; + void *state, *state2; + + copy->type = route->type; + copy->sources = dm_list_new(); + + DM_LIST_FOREACH_DATA(port, route->sources, state) { + DM_LIST_FOREACH_DATA(port_copy, ports, state2) { + if (dm_config_port_equal(port, port_copy)) { + dm_list_push_back(copy->sources, port_copy); + break; + } + } + } + + DM_LIST_FOREACH_DATA(port_copy, ports, state) { + if (dm_config_port_equal(port_copy, route->sink)) { + copy->sink = port_copy; + break; + } + } + + return copy; +} + +static dm_config_module *config_module_dup(const dm_config_module *module) { + dm_config_module *copy = pa_xnew0(dm_config_module, 1); + dm_config_port *device_port, *attached_device, *mix_port; + dm_config_route *route; + void *state, *state2; + + copy = pa_xnew0(dm_config_module, 1); + copy->name = pa_xstrdup(module->name); + copy->version_major = module->version_major; + copy->version_minor = module->version_minor; + copy->attached_devices = dm_list_new(); + copy->default_output_device = NULL; + copy->mix_ports = dm_list_new(); + copy->device_ports = dm_list_new(); + copy->ports = dm_list_new(); + copy->routes = dm_list_new(); + + DM_LIST_FOREACH_DATA(device_port, module->device_ports, state) { + dm_config_port *device_port_copy = config_port_dup(device_port, copy); + dm_list_push_back(copy->device_ports, device_port_copy); + dm_list_push_back(copy->ports, device_port_copy); + if (module->default_output_device == device_port) + copy->default_output_device = device_port_copy; + DM_LIST_FOREACH_DATA(attached_device, module->attached_devices, state2) { + if (attached_device == device_port) { + dm_list_push_back(copy->attached_devices, device_port_copy); + break; + } + } + } + + DM_LIST_FOREACH_DATA(mix_port, module->mix_ports, state) { + dm_config_port *mix_port_copy = config_port_dup(mix_port, copy); + dm_list_push_back(copy->mix_ports, mix_port_copy); + dm_list_push_back(copy->ports, mix_port_copy); + } + + DM_LIST_FOREACH_DATA(route, module->routes, state) + dm_list_push_back(copy->routes, config_route_dup(route, copy->ports)); + + return copy; +} + +dm_config_device *dm_config_dup(const dm_config_device *config) { + dm_config_device *copy; + dm_config_module *module; + void *state; pa_assert(config); - config_copy = pa_xnew0(pa_droid_config_audio, 1); + copy = pa_xnew0(dm_config_device, 1); + copy->global_config = dm_list_new(); + copy->modules = dm_list_new(); - if (config->global_config) - config_copy->global_config = pa_xmemdup(config->global_config, sizeof(*config->global_config)); + if (config->global_config) { + dm_config_global *global, *global_copy; - SLLIST_FOREACH(module, config->hw_modules) { - module_copy = pa_droid_config_hw_module_new(config_copy, module->name); - if (module->global_config) - module_copy->global_config = pa_xmemdup(module->global_config, sizeof(*module->global_config)); - - SLLIST_FOREACH(device, module->outputs) { - device_copy = pa_xmemdup(device, sizeof(*device)); - device_copy->module = module_copy; - device_copy->name = pa_xstrdup(device->name); - SLLIST_APPEND(pa_droid_config_device, module_copy->outputs, device_copy); + DM_LIST_FOREACH_DATA(global, config->global_config, state) { + global_copy = pa_xnew0(dm_config_global, 1); + global_copy->key = pa_xstrdup(global->key); + global_copy->value = pa_xstrdup(global->value); + dm_list_push_back(copy->global_config, global_copy); } - - SLLIST_FOREACH(device, module->inputs) { - device_copy = pa_xmemdup(device, sizeof(*device)); - device_copy->module = module_copy; - device_copy->name = pa_xstrdup(device->name); - SLLIST_APPEND(pa_droid_config_device, module_copy->inputs, device_copy); - } - - SLLIST_APPEND(pa_droid_config_hw_module, config_copy->hw_modules, module_copy); } - return config_copy; + DM_LIST_FOREACH_DATA(module, config->modules, state) + dm_list_push_back(copy->modules, config_module_dup(module)); + + return copy; } -pa_droid_config_audio *pa_parse_droid_audio_config(const char *filename) { - const char *suffix; - - pa_assert(filename); - - if ((suffix = rindex(filename, '.'))) { - if (strlen(suffix) == 4 && pa_streq(suffix, ".xml")) - return pa_parse_droid_audio_config_xml(filename); - else if (strlen(suffix) == 5 && pa_streq(suffix, ".conf")) - return pa_parse_droid_audio_config_legacy(filename); - } - - return NULL; +dm_config_device *pa_parse_droid_audio_config(const char *filename) { + return pa_parse_droid_audio_config_xml(filename); } -void pa_droid_config_free(pa_droid_config_audio *config) { - pa_droid_config_hw_module *module; - pa_droid_config_device *device; +static void config_global_free(void *data) { + dm_config_global *global = data; + pa_xfree(global->key); + pa_xfree(global->value); + pa_xfree(global); +} + +static void config_profile_free(void *data) { + dm_config_profile *profile = data; + + pa_xfree(profile->name); + pa_xfree(profile); +} + +static void config_port_free(void *data) { + dm_config_port *port = data; + + pa_xfree(port->name); + pa_xfree(port->address); + dm_list_free(port->profiles, config_profile_free); + pa_xfree(port); +} + +static void config_route_free(void *data) { + dm_config_route *route = data; + + dm_list_free(route->sources, NULL); + pa_xfree(route); +} + +static void config_module_free(void *data) { + dm_config_module *module = data; + + pa_xfree(module->name); + dm_list_free(module->attached_devices, NULL); + dm_list_free(module->ports, config_port_free); + dm_list_free(module->device_ports, NULL); + dm_list_free(module->mix_ports, NULL); + dm_list_free(module->routes, config_route_free); + pa_xfree(module); +} + +void dm_config_free(dm_config_device *config) { if (!config) return; - while (config->hw_modules) { - SLLIST_STEAL_FIRST(module, config->hw_modules); - - while (module->outputs) { - SLLIST_STEAL_FIRST(device, module->outputs); - pa_droid_config_device_free(device); - } - - while (module->inputs) { - SLLIST_STEAL_FIRST(device, module->inputs); - pa_droid_config_device_free(device); - } - - pa_droid_config_hw_module_free(module); - } - - pa_xfree(config->global_config); + dm_list_free(config->global_config, config_global_free); + dm_list_free(config->modules, config_module_free); pa_xfree(config); } -const pa_droid_config_hw_module *pa_droid_config_find_module(const pa_droid_config_audio *config, const char* module_id) { - pa_droid_config_hw_module *module; +dm_config_module *dm_config_find_module(dm_config_device *config, const char* module_id) { + dm_config_module *module; + void *state; pa_assert(config); pa_assert(module_id); - SLLIST_FOREACH(module, config->hw_modules) { + DM_LIST_FOREACH_DATA(module, config->modules, state) { if (pa_streq(module_id, module->name)) return module; } @@ -199,71 +294,73 @@ const pa_droid_config_hw_module *pa_droid_config_find_module(const pa_droid_conf return NULL; } -static const pa_droid_config_device *find_device(const pa_droid_config_hw_module *module, bool output, const char* device_name) { - pa_droid_config_device *device; +dm_config_port *dm_config_find_port(dm_config_module *module, const char* name) { + dm_config_port *port; + void *state; pa_assert(module); - pa_assert(device_name); + pa_assert(name); - SLLIST_FOREACH(device, output ? module->outputs : module->inputs) { - if (pa_streq(device_name, device->name)) - return device; + DM_LIST_FOREACH_DATA(port, module->ports, state) { + if (pa_streq(name, port->name)) + return port; } return NULL; } -const pa_droid_config_device *pa_droid_config_find_output(const pa_droid_config_hw_module *module, const char* output_name) { - return find_device(module, true, output_name); -} - -const pa_droid_config_device *pa_droid_config_find_input(const pa_droid_config_hw_module *module, const char* input_name) { - return find_device(module, false, input_name); -} - -pa_droid_config_hw_module *pa_droid_config_hw_module_new(const pa_droid_config_audio *config, const char *name) { - pa_droid_config_hw_module *hw_module; - - pa_assert(config); - pa_assert(name); - - hw_module = pa_xnew0(pa_droid_config_hw_module, 1); - hw_module->config = config; - hw_module->name = pa_xstrndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN); - - return hw_module; -} - -void pa_droid_config_hw_module_free(pa_droid_config_hw_module *hw_module) { - if (!hw_module) - return; - - pa_xfree(hw_module->name); - pa_xfree(hw_module->global_config); - pa_xfree(hw_module); -} - -pa_droid_config_device *pa_droid_config_device_new(const pa_droid_config_hw_module *module, - pa_direction_t direction, - const char *name) { - pa_droid_config_device *device; - +dm_config_port *dm_config_default_output_device(dm_config_module *module) { pa_assert(module); - pa_assert(direction == PA_DIRECTION_OUTPUT || direction == PA_DIRECTION_INPUT); - pa_assert(name); - device = pa_xnew0(pa_droid_config_device, 1); - device->module = module; - device->direction = direction; - device->name = pa_replace(name, " ", "_"); - - return device; + if (module->default_output_device) + return module->default_output_device; + else { + pa_log("Module %s doesn't have default output device.", module->name); + return 0; + } } -void pa_droid_config_device_free(pa_droid_config_device *device) { - if (!device) - return; +char *dm_config_escape_string(const char *string) { + if (!string) + return NULL; - pa_xfree(device->name); - pa_xfree(device); + /* Just replace whitespace with underscores for now. */ + + return pa_replace(string, " ", "_"); +} + +dm_config_port *dm_config_find_device_port(dm_config_port *port, audio_devices_t device) { + dm_config_port *device_port; + void *state; + + pa_assert(port); + + DM_LIST_FOREACH_DATA(device_port, port->module->device_ports, state) { + if (device_port->type == device) + return device_port; + } + + return NULL; +} + +bool dm_config_port_equal(const dm_config_port *a, const dm_config_port *b) { + if ((!a && b) || (a && !b)) + return false; + + else if (!a && !b) + return true; + + return (pa_streq(a->name, b->name) && a->type == b->type); +} + +dm_config_port *dm_config_find_mix_port(dm_config_module *module, const char *name) { + dm_config_port *mix_port = NULL; + void *state; + + DM_LIST_FOREACH_DATA(mix_port, module->mix_ports, state) { + if (pa_streq(mix_port->name, name)) + return mix_port; + } + + return NULL; } diff --git a/src/common/include/droid/droid-config.h b/src/common/include/droid/droid-config.h index 3e7a645..2b6e11c 100644 --- a/src/common/include/droid/droid-config.h +++ b/src/common/include/droid/droid-config.h @@ -2,7 +2,7 @@ #define foodroidconfigfoo /* - * Copyright (C) 2018 Jolla Ltd. + * Copyright (C) 2018-2022 Jolla Ltd. * * Contact: Juho Hämäläinen * @@ -30,75 +30,106 @@ #include #include +#include #include -typedef struct pa_droid_config_audio pa_droid_config_audio; -typedef struct pa_droid_config_hw_module pa_droid_config_hw_module; +#define AUDIO_MAX_SAMPLING_RATES (32) +#define AUDIO_MAX_CHANNEL_MASKS (32) -#define AUDIO_MAX_SAMPLING_RATES (32) +typedef struct dm_config_global dm_config_global; +typedef struct dm_config_port dm_config_port; +typedef struct dm_config_route dm_config_route; +typedef struct dm_config_module dm_config_module; +typedef struct dm_config_device dm_config_device; +typedef struct dm_config_profile dm_config_profile; -typedef struct pa_droid_config_global { - uint32_t audio_hal_version; - audio_devices_t attached_output_devices; - audio_devices_t default_output_device; - audio_devices_t attached_input_devices; -} pa_droid_config_global; - -typedef struct pa_droid_config_device { - const pa_droid_config_hw_module *module; - - char *name; - uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; /* (uint32_t) -1 -> dynamic */ - audio_channel_mask_t channel_masks; /* 0 -> dynamic */ - audio_format_t formats; /* 0 -> dynamic */ - audio_devices_t devices; - /* Instead of using audio_output_flags_t and audio_input_flags_t - * unify the flags as uint32_t so that we can have single struct for both - * output and input configurations. - * audio_input_flags_t was introduced in APIs 2 & 3, depending on adaptation, - * so having input flags as uint32_t is simpler from input implementation - * point of view as well. */ - uint32_t flags; - pa_direction_t direction; - - struct pa_droid_config_device *next; -} pa_droid_config_device; - -struct pa_droid_config_hw_module { - const pa_droid_config_audio *config; - - char *name; - /* If global config is not defined for module, use root global config. */ - pa_droid_config_global *global_config; - pa_droid_config_device *outputs; - pa_droid_config_device *inputs; - - struct pa_droid_config_hw_module *next; +struct dm_config_global { + char *key; + char *value; }; -struct pa_droid_config_audio { - pa_droid_config_global *global_config; - pa_droid_config_hw_module *hw_modules; +struct dm_config_profile { + char *name; + audio_format_t format; /* 0 -> dynamic TODO check that this is still true */ + uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; /* sampling_rates[0] == 0 -> dynamic, otherwise 0 terminates list */ + audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]; /* channel_masks[0] == 0 -> dynamic */ }; +typedef enum dm_config_role { + DM_CONFIG_ROLE_SINK, + DM_CONFIG_ROLE_SOURCE, +} dm_config_role_t; + +typedef enum dm_config_type { + DM_CONFIG_TYPE_MIX, + DM_CONFIG_TYPE_DEVICE_PORT, + DM_CONFIG_TYPE_MIX_PORT, +} dm_config_type_t; + +struct dm_config_port { + dm_config_module *module; + + /* common values */ + + dm_config_type_t port_type; /* either mixPort or devicePort */ + char *name; + dm_config_role_t role; + dm_list *profiles; /* dm_config_profile* */ + + /* devicePort specific values */ + + audio_devices_t type; + char *address; + + /* mixPort specific values */ + + uint32_t flags; /* audio_output_flag_t or audio_input_flag_t */ + int max_open_count; /* 0 == not defined */ + int max_active_count; /* 0 == not defined */ +}; + +struct dm_config_route { + dm_config_type_t type; + dm_config_port *sink; + dm_list *sources; /* dm_config_port* */ +}; + +struct dm_config_module { + dm_config_device *config; + + char *name; + int version_major; + int version_minor; + + dm_list *attached_devices; /* dm_config_port* owned by device_ports list below */ + dm_config_port *default_output_device; /* owned by device_ports list below */ + dm_list *ports; /* dm_config_port* - for convenience port types are filtered to two lists below: */ + dm_list *mix_ports; /* dm_config_port* */ + dm_list *device_ports; /* dm_config_port* */ + dm_list *routes; /* dm_config_route* */ +}; + +struct dm_config_device { + dm_list *global_config; /* dm_config_global* */ + dm_list *modules; /* dm_config_module* */ +}; + + /* Config parser */ -pa_droid_config_audio *pa_droid_config_load(pa_modargs *ma); -pa_droid_config_audio *pa_droid_config_dup(const pa_droid_config_audio *config); -void pa_droid_config_free(pa_droid_config_audio *config); -pa_droid_config_audio *pa_parse_droid_audio_config_legacy(const char *filename); -pa_droid_config_audio *pa_parse_droid_audio_config_xml(const char *filename); +dm_config_device *dm_config_load(pa_modargs *ma); +dm_config_device *dm_config_dup(const dm_config_device *config); +void dm_config_free(dm_config_device *config); /* autodetect config type from filename and parse */ -pa_droid_config_audio *pa_parse_droid_audio_config(const char *filename); +dm_config_device *pa_parse_droid_audio_config(const char *filename); -const pa_droid_config_hw_module *pa_droid_config_find_module(const pa_droid_config_audio *config, const char* module_id); -const pa_droid_config_device *pa_droid_config_find_output(const pa_droid_config_hw_module *module, const char* output_name); -const pa_droid_config_device *pa_droid_config_find_input(const pa_droid_config_hw_module *module, const char* input_name); +dm_config_module *dm_config_find_module(dm_config_device *config, const char* module_id); +dm_config_port *dm_config_find_port(dm_config_module *module, const char* name); +dm_config_port *dm_config_default_output_device(dm_config_module *module); +dm_config_port *dm_config_find_device_port(dm_config_port *port, audio_devices_t device); +char *dm_config_escape_string(const char *string); -pa_droid_config_hw_module *pa_droid_config_hw_module_new(const pa_droid_config_audio *config, const char *name); -void pa_droid_config_hw_module_free(pa_droid_config_hw_module *hw_module); -pa_droid_config_device *pa_droid_config_device_new(const pa_droid_config_hw_module *module, - pa_direction_t direction, - const char *name); -void pa_droid_config_device_free(pa_droid_config_device *device); +bool dm_config_port_equal(const dm_config_port *a, const dm_config_port *b); + +dm_config_port *dm_config_find_mix_port(dm_config_module *module, const char *name); #endif