summaryrefslogtreecommitdiff
path: root/cras/src/server/cras_alsa_card.c
diff options
context:
space:
mode:
Diffstat (limited to 'cras/src/server/cras_alsa_card.c')
-rw-r--r--cras/src/server/cras_alsa_card.c641
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;
-}