diff options
Diffstat (limited to 'cras/src/server/cras_alsa_card.c')
-rw-r--r-- | cras/src/server/cras_alsa_card.c | 641 |
1 files changed, 0 insertions, 641 deletions
diff --git a/cras/src/server/cras_alsa_card.c b/cras/src/server/cras_alsa_card.c deleted file mode 100644 index 362e6a68..00000000 --- a/cras/src/server/cras_alsa_card.c +++ /dev/null @@ -1,641 +0,0 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE /* For asprintf */ -#endif - -#include <alsa/asoundlib.h> -#include <syslog.h> - -#include "cras_alsa_card.h" -#include "cras_alsa_io.h" -#include "cras_alsa_mixer.h" -#include "cras_alsa_ucm.h" -#include "cras_device_blocklist.h" -#include "cras_card_config.h" -#include "cras_config.h" -#include "cras_iodev.h" -#include "cras_iodev_list.h" -#include "cras_system_state.h" -#include "cras_types.h" -#include "cras_util.h" -#include "utlist.h" - -#define MAX_ALSA_CARDS 32 /* Alsa limit on number of cards. */ -#define MAX_ALSA_CARD_NAME_LENGTH 6 /* Alsa card name "hw:XX" + 1 for null. */ -#define MAX_ALSA_PCM_NAME_LENGTH 9 /* Alsa pcm name "hw:XX,YY" + 1 for null. */ -#define MAX_COUPLED_OUTPUT_SIZE 4 - -struct iodev_list_node { - struct cras_iodev *iodev; - enum CRAS_STREAM_DIRECTION direction; - struct iodev_list_node *prev, *next; -}; - -/* Keeps an fd that is registered with system state. A list of fds must be - * kept so that they can be removed when the card is destroyed. */ -struct hctl_poll_fd { - int fd; - struct hctl_poll_fd *prev, *next; -}; - -/* Holds information about each sound card on the system. - * name - of the form hw:XX. - * card_index - 0 based index, value of "XX" in the name. - * iodevs - Input and output devices for this card. - * mixer - Controls the mixer controls for this card. - * ucm - CRAS use case manager if available. - * hctl - ALSA high-level control interface. - * hctl_poll_fds - List of fds registered with cras_system_state. - * config - Config info for this card, can be NULL if none found. - */ -struct cras_alsa_card { - char name[MAX_ALSA_CARD_NAME_LENGTH]; - size_t card_index; - struct iodev_list_node *iodevs; - struct cras_alsa_mixer *mixer; - struct cras_use_case_mgr *ucm; - snd_hctl_t *hctl; - struct hctl_poll_fd *hctl_poll_fds; - struct cras_card_config *config; -}; - -/* Creates an iodev for the given device. - * Args: - * alsa_card - the alsa_card the device will be added to. - * info - Information about the card type and priority. - * card_name - The name of the card. - * dev_name - The name of the device. - * dev_id - The id string of the device. - * device_index - 0 based index, value of "YY" in "hw:XX,YY". - * direction - Input or output. - * Returns: - * Pointer to the created iodev, or NULL on error. - * other negative error code otherwise. - */ -struct cras_iodev *create_iodev_for_device( - struct cras_alsa_card *alsa_card, struct cras_alsa_card_info *info, - const char *card_name, const char *dev_name, const char *dev_id, - unsigned device_index, enum CRAS_STREAM_DIRECTION direction) -{ - struct iodev_list_node *new_dev; - struct iodev_list_node *node; - int first = 1; - char pcm_name[MAX_ALSA_PCM_NAME_LENGTH]; - - /* Find whether this is the first device in this direction, and - * avoid duplicate device indexes. */ - DL_FOREACH (alsa_card->iodevs, node) { - if (node->direction != direction) - continue; - first = 0; - if (alsa_iodev_index(node->iodev) == device_index) { - syslog(LOG_DEBUG, - "Skipping duplicate device for %s:%s:%s [%u]", - card_name, dev_name, dev_id, device_index); - return node->iodev; - } - } - - new_dev = calloc(1, sizeof(*new_dev)); - if (new_dev == NULL) - return NULL; - - /* Append device index to card namem, ex: 'hw:0', for the PCM name of - * target iodev. */ - snprintf(pcm_name, MAX_ALSA_PCM_NAME_LENGTH, "%s,%u", alsa_card->name, - device_index); - - new_dev->direction = direction; - new_dev->iodev = - alsa_iodev_create(info->card_index, card_name, device_index, - pcm_name, dev_name, dev_id, info->card_type, - first, alsa_card->mixer, alsa_card->config, - alsa_card->ucm, alsa_card->hctl, direction, - info->usb_vendor_id, info->usb_product_id, - info->usb_serial_number); - if (new_dev->iodev == NULL) { - syslog(LOG_ERR, "Couldn't create alsa_iodev for %s", pcm_name); - free(new_dev); - return NULL; - } - - syslog(LOG_DEBUG, "New %s device %s", - direction == CRAS_STREAM_OUTPUT ? "playback" : "capture", - pcm_name); - - DL_APPEND(alsa_card->iodevs, new_dev); - return new_dev->iodev; -} - -/* Returns non-zero if this card has hctl jacks. - */ -static int card_has_hctl_jack(struct cras_alsa_card *alsa_card) -{ - struct iodev_list_node *node; - - /* Find the first device that has an hctl jack. */ - DL_FOREACH (alsa_card->iodevs, node) { - if (alsa_iodev_has_hctl_jacks(node->iodev)) - return 1; - } - return 0; -} - -/* Check if a device should be ignored for this card. Returns non-zero if the - * device is in the blocklist and should be ignored. - */ -static int should_ignore_dev(struct cras_alsa_card_info *info, - struct cras_device_blocklist *blocklist, - size_t device_index) -{ - if (info->card_type == ALSA_CARD_TYPE_USB) - return cras_device_blocklist_check( - blocklist, info->usb_vendor_id, info->usb_product_id, - info->usb_desc_checksum, device_index); - return 0; -} - -/* Filters an array of mixer control names. Keep a name if it is - * specified in the ucm config. */ -static struct mixer_name *filter_controls(struct cras_use_case_mgr *ucm, - struct mixer_name *controls) -{ - struct mixer_name *control; - DL_FOREACH (controls, control) { - char *dev = ucm_get_dev_for_mixer(ucm, control->name, - CRAS_STREAM_OUTPUT); - if (!dev) - DL_DELETE(controls, control); - else - free(dev); - } - return controls; -} - -/* Handles notifications from alsa controls. Called by main thread when a poll - * fd provided by alsa signals there is an event available. */ -static void alsa_control_event_pending(void *arg, int revent) -{ - struct cras_alsa_card *card; - - card = (struct cras_alsa_card *)arg; - if (card == NULL) { - syslog(LOG_ERR, "Invalid card from control event."); - return; - } - - /* handle_events will trigger the callback registered with each control - * that has changed. */ - snd_hctl_handle_events(card->hctl); -} - -static int -add_controls_and_iodevs_by_matching(struct cras_alsa_card_info *info, - struct cras_device_blocklist *blocklist, - struct cras_alsa_card *alsa_card, - const char *card_name, snd_ctl_t *handle) -{ - struct mixer_name *coupled_controls = NULL; - int dev_idx; - snd_pcm_info_t *dev_info; - struct mixer_name *extra_controls = NULL; - int rc = 0; - - snd_pcm_info_alloca(&dev_info); - - if (alsa_card->ucm) { - char *extra_main_volume; - - /* Filter the extra output mixer names */ - extra_controls = filter_controls( - alsa_card->ucm, - mixer_name_add(extra_controls, "IEC958", - CRAS_STREAM_OUTPUT, MIXER_NAME_VOLUME)); - - /* Get the extra main volume control. */ - extra_main_volume = - ucm_get_flag(alsa_card->ucm, "ExtraMainVolume"); - if (extra_main_volume) { - extra_controls = mixer_name_add(extra_controls, - extra_main_volume, - CRAS_STREAM_OUTPUT, - MIXER_NAME_MAIN_VOLUME); - free(extra_main_volume); - } - mixer_name_dump(extra_controls, "extra controls"); - - /* Check if coupled controls has been specified for speaker. */ - coupled_controls = - ucm_get_coupled_mixer_names(alsa_card->ucm, "Speaker"); - mixer_name_dump(coupled_controls, "coupled controls"); - } - - /* Add controls to mixer by name matching. */ - rc = cras_alsa_mixer_add_controls_by_name_matching( - alsa_card->mixer, extra_controls, coupled_controls); - if (rc) { - syslog(LOG_ERR, "Fail adding controls to mixer for %s.", - alsa_card->name); - goto error; - } - - /* Go through every device. */ - dev_idx = -1; - while (1) { - rc = snd_ctl_pcm_next_device(handle, &dev_idx); - if (rc < 0) - goto error; - if (dev_idx < 0) - break; - - snd_pcm_info_set_device(dev_info, dev_idx); - snd_pcm_info_set_subdevice(dev_info, 0); - - /* Check for playback devices. */ - snd_pcm_info_set_stream(dev_info, SND_PCM_STREAM_PLAYBACK); - if (snd_ctl_pcm_info(handle, dev_info) == 0 && - !should_ignore_dev(info, blocklist, dev_idx)) { - struct cras_iodev *iodev = create_iodev_for_device( - alsa_card, info, card_name, - snd_pcm_info_get_name(dev_info), - snd_pcm_info_get_id(dev_info), dev_idx, - CRAS_STREAM_OUTPUT); - if (iodev) { - rc = alsa_iodev_legacy_complete_init(iodev); - if (rc < 0) - goto error; - } - } - - /* Check for capture devices. */ - snd_pcm_info_set_stream(dev_info, SND_PCM_STREAM_CAPTURE); - if (snd_ctl_pcm_info(handle, dev_info) == 0) { - struct cras_iodev *iodev = create_iodev_for_device( - alsa_card, info, card_name, - snd_pcm_info_get_name(dev_info), - snd_pcm_info_get_id(dev_info), dev_idx, - CRAS_STREAM_INPUT); - if (iodev) { - rc = alsa_iodev_legacy_complete_init(iodev); - if (rc < 0) - goto error; - } - } - } -error: - mixer_name_free(coupled_controls); - mixer_name_free(extra_controls); - return rc; -} - -static int add_controls_and_iodevs_with_ucm(struct cras_alsa_card_info *info, - struct cras_alsa_card *alsa_card, - const char *card_name, - snd_ctl_t *handle) -{ - snd_pcm_info_t *dev_info; - struct mixer_name *main_volume_control_names; - struct iodev_list_node *node; - int rc = 0; - struct ucm_section *section; - struct ucm_section *ucm_sections; - - snd_pcm_info_alloca(&dev_info); - - main_volume_control_names = ucm_get_main_volume_names(alsa_card->ucm); - if (main_volume_control_names) { - rc = cras_alsa_mixer_add_main_volume_control_by_name( - alsa_card->mixer, main_volume_control_names); - if (rc) { - syslog(LOG_ERR, - "Failed adding main volume controls to" - " mixer for '%s'.'", - card_name); - goto cleanup_names; - } - } - - /* Get info on the devices specified in the UCM config. */ - ucm_sections = ucm_get_sections(alsa_card->ucm); - if (!ucm_sections) { - syslog(LOG_ERR, - "Could not retrieve any UCM SectionDevice" - " info for '%s'.", - card_name); - rc = -ENOENT; - goto cleanup_names; - } - - /* Create all of the controls first. */ - DL_FOREACH (ucm_sections, section) { - rc = cras_alsa_mixer_add_controls_in_section(alsa_card->mixer, - section); - if (rc) { - syslog(LOG_ERR, - "Failed adding controls to" - " mixer for '%s:%s'", - card_name, section->name); - goto cleanup; - } - } - - /* Create all of the devices. */ - DL_FOREACH (ucm_sections, section) { - /* If a UCM section specifies certain device as dependency - * then don't create an alsa iodev for it, just append it - * as node later. */ - if (section->dependent_dev_idx != -1) - continue; - snd_pcm_info_set_device(dev_info, section->dev_idx); - snd_pcm_info_set_subdevice(dev_info, 0); - if (section->dir == CRAS_STREAM_OUTPUT) - snd_pcm_info_set_stream(dev_info, - SND_PCM_STREAM_PLAYBACK); - else if (section->dir == CRAS_STREAM_INPUT) - snd_pcm_info_set_stream(dev_info, - SND_PCM_STREAM_CAPTURE); - else { - syslog(LOG_ERR, "Unexpected direction: %d", - section->dir); - rc = -EINVAL; - goto cleanup; - } - - if (snd_ctl_pcm_info(handle, dev_info)) { - syslog(LOG_ERR, "Could not get info for device: %s", - section->name); - continue; - } - - create_iodev_for_device(alsa_card, info, card_name, - snd_pcm_info_get_name(dev_info), - snd_pcm_info_get_id(dev_info), - section->dev_idx, section->dir); - } - - /* Setup jacks and controls for the devices. If a SectionDevice is - * dependent on another SectionDevice, it'll be added as a node to - * a existing ALSA iodev. */ - DL_FOREACH (ucm_sections, section) { - DL_FOREACH (alsa_card->iodevs, node) { - if (node->direction != section->dir) - continue; - if (alsa_iodev_index(node->iodev) == section->dev_idx) - break; - if (alsa_iodev_index(node->iodev) == - section->dependent_dev_idx) - break; - } - if (node) { - rc = alsa_iodev_ucm_add_nodes_and_jacks(node->iodev, - section); - if (rc < 0) - goto cleanup; - } - } - - DL_FOREACH (alsa_card->iodevs, node) { - alsa_iodev_ucm_complete_init(node->iodev); - } - -cleanup: - ucm_section_free_list(ucm_sections); -cleanup_names: - mixer_name_free(main_volume_control_names); - return rc; -} - -static void configure_echo_reference_dev(struct cras_alsa_card *alsa_card) -{ - struct iodev_list_node *dev_node, *echo_ref_node; - const char *echo_ref_name; - - if (!alsa_card->ucm) - return; - - DL_FOREACH (alsa_card->iodevs, dev_node) { - if (!dev_node->iodev->nodes) - continue; - - echo_ref_name = ucm_get_echo_reference_dev_name_for_dev( - alsa_card->ucm, dev_node->iodev->nodes->name); - if (!echo_ref_name) - continue; - DL_FOREACH (alsa_card->iodevs, echo_ref_node) { - if (echo_ref_node->iodev->nodes == NULL) - continue; - if (!strcmp(echo_ref_name, - echo_ref_node->iodev->nodes->name)) - break; - } - if (echo_ref_node) - dev_node->iodev->echo_reference_dev = - echo_ref_node->iodev; - else - syslog(LOG_ERR, - "Echo ref dev %s doesn't exist on card %s", - echo_ref_name, alsa_card->name); - free((void *)echo_ref_name); - } -} - -/* - * Exported Interface. - */ - -struct cras_alsa_card *cras_alsa_card_create( - struct cras_alsa_card_info *info, const char *device_config_dir, - struct cras_device_blocklist *blocklist, const char *ucm_suffix) -{ - snd_ctl_t *handle = NULL; - int rc, n; - snd_ctl_card_info_t *card_info; - const char *card_name; - struct cras_alsa_card *alsa_card; - - if (info->card_index >= MAX_ALSA_CARDS) { - syslog(LOG_ERR, "Invalid alsa card index %u", info->card_index); - return NULL; - } - - snd_ctl_card_info_alloca(&card_info); - - alsa_card = calloc(1, sizeof(*alsa_card)); - if (alsa_card == NULL) - return NULL; - alsa_card->card_index = info->card_index; - - snprintf(alsa_card->name, MAX_ALSA_CARD_NAME_LENGTH, "hw:%u", - info->card_index); - - rc = snd_ctl_open(&handle, alsa_card->name, 0); - if (rc < 0) { - syslog(LOG_ERR, "Fail opening control %s.", alsa_card->name); - goto error_bail; - } - - rc = snd_ctl_card_info(handle, card_info); - if (rc < 0) { - syslog(LOG_ERR, "Error getting card info."); - goto error_bail; - } - - card_name = snd_ctl_card_info_get_name(card_info); - if (card_name == NULL) { - syslog(LOG_ERR, "Error getting card name."); - goto error_bail; - } - - if (info->card_type != ALSA_CARD_TYPE_INTERNAL || - cras_system_check_ignore_ucm_suffix(card_name)) - ucm_suffix = NULL; - - /* Read config file for this card if it exists. */ - alsa_card->config = - cras_card_config_create(device_config_dir, card_name); - if (alsa_card->config == NULL) - syslog(LOG_DEBUG, "No config file for %s", alsa_card->name); - - /* Create a use case manager if a configuration is available. */ - if (ucm_suffix) { - char *ucm_name; - if (asprintf(&ucm_name, "%s.%s", card_name, ucm_suffix) == -1) { - syslog(LOG_ERR, "Error creating ucm name"); - goto error_bail; - } - alsa_card->ucm = ucm_create(ucm_name); - syslog(LOG_INFO, "Card %s (%s) has UCM: %s", alsa_card->name, - ucm_name, alsa_card->ucm ? "yes" : "no"); - free(ucm_name); - } else { - alsa_card->ucm = ucm_create(card_name); - syslog(LOG_INFO, "Card %s (%s) has UCM: %s", alsa_card->name, - card_name, alsa_card->ucm ? "yes" : "no"); - } - - if (info->card_type == ALSA_CARD_TYPE_INTERNAL && !alsa_card->ucm) - syslog(LOG_ERR, "No ucm config on internal card %s", card_name); - - rc = snd_hctl_open(&alsa_card->hctl, alsa_card->name, SND_CTL_NONBLOCK); - if (rc < 0) { - syslog(LOG_DEBUG, "failed to get hctl for %s", alsa_card->name); - alsa_card->hctl = NULL; - } else { - rc = snd_hctl_nonblock(alsa_card->hctl, 1); - if (rc < 0) { - syslog(LOG_ERR, "failed to nonblock hctl for %s", - alsa_card->name); - goto error_bail; - } - - rc = snd_hctl_load(alsa_card->hctl); - if (rc < 0) { - syslog(LOG_ERR, "failed to load hctl for %s", - alsa_card->name); - goto error_bail; - } - } - - /* Create one mixer per card. */ - alsa_card->mixer = cras_alsa_mixer_create(alsa_card->name); - - if (alsa_card->mixer == NULL) { - syslog(LOG_ERR, "Fail opening mixer for %s.", alsa_card->name); - goto error_bail; - } - - if (alsa_card->ucm && ucm_has_fully_specified_ucm_flag(alsa_card->ucm)) - rc = add_controls_and_iodevs_with_ucm(info, alsa_card, - card_name, handle); - else - rc = add_controls_and_iodevs_by_matching( - info, blocklist, alsa_card, card_name, handle); - if (rc) - goto error_bail; - - configure_echo_reference_dev(alsa_card); - - n = alsa_card->hctl ? snd_hctl_poll_descriptors_count(alsa_card->hctl) : - 0; - if (n != 0 && card_has_hctl_jack(alsa_card)) { - struct hctl_poll_fd *registered_fd; - struct pollfd *pollfds; - int i; - - pollfds = malloc(n * sizeof(*pollfds)); - if (pollfds == NULL) { - rc = -ENOMEM; - goto error_bail; - } - - n = snd_hctl_poll_descriptors(alsa_card->hctl, pollfds, n); - for (i = 0; i < n; i++) { - registered_fd = calloc(1, sizeof(*registered_fd)); - if (registered_fd == NULL) { - free(pollfds); - rc = -ENOMEM; - goto error_bail; - } - registered_fd->fd = pollfds[i].fd; - DL_APPEND(alsa_card->hctl_poll_fds, registered_fd); - rc = cras_system_add_select_fd( - registered_fd->fd, alsa_control_event_pending, - alsa_card, POLLIN); - if (rc < 0) { - DL_DELETE(alsa_card->hctl_poll_fds, - registered_fd); - free(pollfds); - goto error_bail; - } - } - free(pollfds); - } - - snd_ctl_close(handle); - return alsa_card; - -error_bail: - if (handle != NULL) - snd_ctl_close(handle); - cras_alsa_card_destroy(alsa_card); - return NULL; -} - -void cras_alsa_card_destroy(struct cras_alsa_card *alsa_card) -{ - struct iodev_list_node *curr; - struct hctl_poll_fd *poll_fd; - - if (alsa_card == NULL) - return; - - DL_FOREACH (alsa_card->iodevs, curr) { - alsa_iodev_destroy(curr->iodev); - DL_DELETE(alsa_card->iodevs, curr); - free(curr); - } - DL_FOREACH (alsa_card->hctl_poll_fds, poll_fd) { - cras_system_rm_select_fd(poll_fd->fd); - DL_DELETE(alsa_card->hctl_poll_fds, poll_fd); - free(poll_fd); - } - if (alsa_card->hctl) - snd_hctl_close(alsa_card->hctl); - if (alsa_card->ucm) - ucm_destroy(alsa_card->ucm); - if (alsa_card->mixer) - cras_alsa_mixer_destroy(alsa_card->mixer); - if (alsa_card->config) - cras_card_config_destroy(alsa_card->config); - free(alsa_card); -} - -size_t cras_alsa_card_get_index(const struct cras_alsa_card *alsa_card) -{ - assert(alsa_card); - return alsa_card->card_index; -} |