/* 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.
 */

#include <alsa/asoundlib.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <sys/param.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <syslog.h>
#include <time.h>

#include "audio_thread.h"
#include "cras_alsa_helpers.h"
#include "cras_alsa_io.h"
#include "cras_alsa_jack.h"
#include "cras_alsa_mixer.h"
#include "cras_alsa_ucm.h"
#include "cras_audio_area.h"
#include "cras_config.h"
#include "cras_utf8.h"
#include "cras_iodev.h"
#include "cras_iodev_list.h"
#include "cras_messages.h"
#include "cras_ramp.h"
#include "cras_rclient.h"
#include "cras_shm.h"
#include "cras_system_state.h"
#include "cras_types.h"
#include "cras_util.h"
#include "cras_volume_curve.h"
#include "sfh.h"
#include "softvol_curve.h"
#include "utlist.h"

#define MAX_ALSA_DEV_NAME_LENGTH 9 /* Alsa names "hw:XX,YY" + 1 for null. */
#define HOTWORD_DEV "Wake on Voice"
#define DEFAULT "(default)"
#define HDMI "HDMI"
#define INTERNAL_MICROPHONE "Internal Mic"
#define INTERNAL_SPEAKER "Speaker"
#define KEYBOARD_MIC "Keyboard Mic"
#define USB "USB"

/*
 * For USB, pad the output buffer.  This avoids a situation where there isn't a
 * complete URB's worth of audio ready to be transmitted when it is requested.
 * The URB interval does track directly to the audio clock, making it hard to
 * predict the exact interval.
 */
#define USB_EXTRA_BUFFER_FRAMES 768

/*
 * When snd_pcm_avail returns a value that is greater than buffer size,
 * we know there is an underrun. If the number of underrun samples
 * (avail - buffer_size) is greater than SEVERE_UNDERRUN_MS * rate,
 * it is a severe underrun. Main thread should disable and then enable
 * device to recover it from underrun.
 */
#define SEVERE_UNDERRUN_MS 5000

/*
 * This extends cras_ionode to include alsa-specific information.
 * Members:
 *    mixer_output - From cras_alsa_mixer.
 *    volume_curve - Volume curve for this node.
 *    jack - The jack associated with the node.
 */
struct alsa_output_node {
	struct cras_ionode base;
	struct mixer_control *mixer_output;
	struct cras_volume_curve *volume_curve;
	const struct cras_alsa_jack *jack;
};

struct alsa_input_node {
	struct cras_ionode base;
	struct mixer_control* mixer_input;
	const struct cras_alsa_jack *jack;
	int8_t *channel_layout;
};

/*
 * Child of cras_iodev, alsa_io handles ALSA interaction for sound devices.
 * base - The cras_iodev structure "base class".
 * dev - String that names this device (e.g. "hw:0,0").
 * dev_name - value from snd_pcm_info_get_name
 * dev_id - value from snd_pcm_info_get_id
 * device_index - ALSA index of device, Y in "hw:X:Y".
 * next_ionode_index - The index we will give to the next ionode. Each ionode
 *     have a unique index within the iodev.
 * card_type - the type of the card this iodev belongs.
 * is_first - true if this is the first iodev on the card.
 * fully_specified - true if this device and it's nodes were fully specified.
 *     That is, don't automatically create nodes for it.
 * enable_htimestamp - True when the device's htimestamp is used.
 * handle - Handle to the opened ALSA device.
 * num_underruns - Number of times we have run out of data (playback only).
 * num_severe_underruns - Number of times we have run out of data badly.
                          Unlike num_underruns which records for the duration
                          where device is opened, num_severe_underruns records
                          since device is created. When severe underrun occurs
                          a possible action is to close/open device.
 * alsa_stream - Playback or capture type.
 * mixer - Alsa mixer used to control volume and mute of the device.
 * config - Card config for this alsa device.
 * jack_list - List of alsa jack controls for this device.
 * ucm - CRAS use case manager, if configuration is found.
 * mmap_offset - offset returned from mmap_begin.
 * dsp_name_default - the default dsp name for the device. It can be overridden
 *     by the jack specific dsp name.
 * poll_fd - Descriptor used to block until data is ready.
 * dma_period_set_microsecs - If non-zero, the value to apply to the dma_period.
 * is_free_running - true if device is playing zeros in the buffer without
 *                   user filling meaningful data. The device buffer is filled
 *                   with zeros. In this state, appl_ptr remains the same
 *                   while hw_ptr keeps running ahead.
 * filled_zeros_for_draining - The number of zeros filled for draining.
 * severe_underrun_frames - The threshold for severe underrun.
 * default_volume_curve - Default volume curve that converts from an index
 *                        to dBFS.
 */
struct alsa_io {
	struct cras_iodev base;
	char *dev;
	char *dev_name;
	char *dev_id;
	uint32_t device_index;
	uint32_t next_ionode_index;
	enum CRAS_ALSA_CARD_TYPE card_type;
	int is_first;
	int fully_specified;
	int enable_htimestamp;
	snd_pcm_t *handle;
	unsigned int num_underruns;
	unsigned int num_severe_underruns;
	snd_pcm_stream_t alsa_stream;
	struct cras_alsa_mixer *mixer;
	const struct cras_card_config *config;
	struct cras_alsa_jack_list *jack_list;
	struct cras_use_case_mgr *ucm;
	snd_pcm_uframes_t mmap_offset;
	const char *dsp_name_default;
	int poll_fd;
	unsigned int dma_period_set_microsecs;
	int is_free_running;
	unsigned int filled_zeros_for_draining;
	snd_pcm_uframes_t severe_underrun_frames;
	struct cras_volume_curve *default_volume_curve;
};

static void init_device_settings(struct alsa_io *aio);

static int alsa_iodev_set_active_node(struct cras_iodev *iodev,
				      struct cras_ionode *ionode,
				      unsigned dev_enabled);

/*
 * Defines the default values of nodes.
 */
static const struct {
	const char *name;
	enum CRAS_NODE_TYPE type;
	enum CRAS_NODE_POSITION position;
} node_defaults[] = {
	{
		.name = DEFAULT,
		.type = CRAS_NODE_TYPE_UNKNOWN,
		.position = NODE_POSITION_INTERNAL,
	},
	{
		.name = INTERNAL_SPEAKER,
		.type = CRAS_NODE_TYPE_INTERNAL_SPEAKER,
		.position = NODE_POSITION_INTERNAL,
	},
	{
		.name = INTERNAL_MICROPHONE,
		.type = CRAS_NODE_TYPE_MIC,
		.position = NODE_POSITION_INTERNAL,
	},
	{
		.name = KEYBOARD_MIC,
		.type = CRAS_NODE_TYPE_MIC,
		.position = NODE_POSITION_KEYBOARD,
	},
	{
		.name = HDMI,
		.type = CRAS_NODE_TYPE_HDMI,
		.position = NODE_POSITION_EXTERNAL,
	},
	{
		.name = "IEC958",
		.type = CRAS_NODE_TYPE_HDMI,
		.position = NODE_POSITION_EXTERNAL,
	},
	{
		.name = "Headphone",
		.type = CRAS_NODE_TYPE_HEADPHONE,
		.position = NODE_POSITION_EXTERNAL,
	},
	{
		.name = "Front Headphone",
		.type = CRAS_NODE_TYPE_HEADPHONE,
		.position = NODE_POSITION_EXTERNAL,
	},
	{
		.name = "Front Mic",
		.type = CRAS_NODE_TYPE_MIC,
		.position = NODE_POSITION_FRONT,
	},
	{
		.name = "Rear Mic",
		.type = CRAS_NODE_TYPE_MIC,
		.position = NODE_POSITION_REAR,
	},
	{
		.name = "Mic",
		.type = CRAS_NODE_TYPE_MIC,
		.position = NODE_POSITION_EXTERNAL,
	},
	{
		.name = HOTWORD_DEV,
		.type = CRAS_NODE_TYPE_HOTWORD,
		.position = NODE_POSITION_INTERNAL,
	},
	{
		.name = "Haptic",
		.type = CRAS_NODE_TYPE_HAPTIC,
		.position = NODE_POSITION_INTERNAL,
	},
	{
		.name = "Rumbler",
		.type = CRAS_NODE_TYPE_HAPTIC,
		.position = NODE_POSITION_INTERNAL,
	},
	{
		.name = "Line Out",
		.type = CRAS_NODE_TYPE_LINEOUT,
		.position = NODE_POSITION_EXTERNAL,
	},
};

/*
 * iodev callbacks.
 */

static int frames_queued(const struct cras_iodev *iodev,
			 struct timespec *tstamp)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	int rc;
	snd_pcm_uframes_t frames;

	rc = cras_alsa_get_avail_frames(aio->handle,
					aio->base.buffer_size,
					aio->severe_underrun_frames,
					iodev->info.name,
					&frames, tstamp,
					&aio->num_underruns);
	if (rc < 0) {
		if (rc == -EPIPE)
			aio->num_severe_underruns++;
		return rc;
	}
	if (!aio->enable_htimestamp)
		clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
	if (iodev->direction == CRAS_STREAM_INPUT)
		return (int)frames;

	/* For output, return number of frames that are used. */
	return iodev->buffer_size - frames;
}

static int delay_frames(const struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	snd_pcm_sframes_t delay;
	int rc;

	rc = cras_alsa_get_delay_frames(aio->handle,
					iodev->buffer_size,
					&delay);
	if (rc < 0)
		return rc;

	return (int)delay;
}

static int close_dev(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;

	/* Removes audio thread callback from main thread. */
	if (aio->poll_fd >= 0)
		audio_thread_rm_callback_sync(
				cras_iodev_list_get_audio_thread(),
				aio->poll_fd);
	if (!aio->handle)
		return 0;
	cras_alsa_pcm_close(aio->handle);
	aio->handle = NULL;
	aio->is_free_running = 0;
	aio->filled_zeros_for_draining = 0;
	cras_iodev_free_format(&aio->base);
	cras_iodev_free_audio_area(&aio->base);
	return 0;
}

static int dummy_hotword_cb(void *arg)
{
	/* Only need this once. */
	struct alsa_io *aio = (struct alsa_io *)arg;
	audio_thread_rm_callback(aio->poll_fd);
	aio->poll_fd = -1;
	return 0;
}

static int open_dev(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	snd_pcm_t *handle;
	int period_wakeup;
	int rc;

	/* This is called after the first stream added so configure for it.
	 * format must be set before opening the device.
	 */
	if (iodev->format == NULL)
		return -EINVAL;
	aio->num_underruns = 0;
	aio->is_free_running = 0;
	aio->filled_zeros_for_draining = 0;
	aio->severe_underrun_frames =
			SEVERE_UNDERRUN_MS * iodev->format->frame_rate / 1000;

	cras_iodev_init_audio_area(iodev, iodev->format->num_channels);

	syslog(LOG_DEBUG, "Configure alsa device %s rate %zuHz, %zu channels",
	       aio->dev, iodev->format->frame_rate,
	       iodev->format->num_channels);
	handle = 0; /* Avoid unused warning. */
	rc = cras_alsa_pcm_open(&handle, aio->dev, aio->alsa_stream);
	if (rc < 0)
		return rc;

	/* If it's a wake on voice device, period_wakeups are required. */
	period_wakeup = (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD);

	rc = cras_alsa_set_hwparams(handle, iodev->format,
				    &iodev->buffer_size, period_wakeup,
				    aio->dma_period_set_microsecs);
	if (rc < 0) {
		cras_alsa_pcm_close(handle);
		return rc;
	}

	/* Set channel map to device */
	rc = cras_alsa_set_channel_map(handle,
				       iodev->format);
	if (rc < 0) {
		cras_alsa_pcm_close(handle);
		return rc;
	}

	/* Configure software params. */
	rc = cras_alsa_set_swparams(handle, &aio->enable_htimestamp);
	if (rc < 0) {
		cras_alsa_pcm_close(handle);
		return rc;
	}

	/* Assign pcm handle then initialize device settings. */
	aio->handle = handle;
	init_device_settings(aio);

	aio->poll_fd = -1;
	if (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD) {
		struct pollfd *ufds;
		int count, i;

		count = snd_pcm_poll_descriptors_count(handle);
		if (count <= 0) {
			syslog(LOG_ERR, "Invalid poll descriptors count\n");
			return count;
		}

		ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * count);
		if (ufds == NULL)
			return -ENOMEM;

		rc = snd_pcm_poll_descriptors(handle, ufds, count);
		if (rc < 0) {
			syslog(LOG_ERR,
			       "Getting hotword poll descriptors: %s\n",
			       snd_strerror(rc));
			free(ufds);
			return rc;
		}

		for (i = 0; i < count; i++) {
			if (ufds[i].events & POLLIN) {
				aio->poll_fd = ufds[i].fd;
				break;
			}
		}
		free(ufds);

		if (aio->poll_fd >= 0)
			audio_thread_add_callback(aio->poll_fd,
						  dummy_hotword_cb,
						  aio);
	}

	/* Capture starts right away, playback will wait for samples. */
	if (aio->alsa_stream == SND_PCM_STREAM_CAPTURE)
		cras_alsa_pcm_start(aio->handle);

	return 0;
}

/*
 * Check if ALSA device is opened by checking if handle is valid.
 * Note that to fully open a cras_iodev, ALSA device is opened first, then there
 * are some device init settings to be done in init_device_settings.
 * Therefore, when setting volume/mute/gain in init_device_settings,
 * cras_iodev is not in CRAS_IODEV_STATE_OPEN yet. We need to check if handle
 * is valid when setting those properties, instead of checking
 * cras_iodev_is_open.
 */
static int has_handle(const struct alsa_io *aio)
{
	return !!aio->handle;
}

static int start(const struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	snd_pcm_t *handle = aio->handle;
	int rc;

	if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING)
		return 0;

	if (snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
		rc = cras_alsa_attempt_resume(handle);
		if (rc < 0) {
			syslog(LOG_ERR, "Resume error: %s", snd_strerror(rc));
			return rc;
		}
		cras_iodev_reset_rate_estimator(iodev);
	} else {
		rc = cras_alsa_pcm_start(handle);
		if (rc < 0) {
			syslog(LOG_ERR, "Start error: %s", snd_strerror(rc));
			return rc;
		}
	}

	return 0;
}

static int get_buffer(struct cras_iodev *iodev,
		      struct cras_audio_area **area,
		      unsigned *frames)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	snd_pcm_uframes_t nframes = *frames;
	uint8_t *dst = NULL;
	size_t format_bytes;
	int rc;

	aio->mmap_offset = 0;
	format_bytes = cras_get_format_bytes(iodev->format);

	rc = cras_alsa_mmap_begin(aio->handle,
				  format_bytes,
				  &dst,
				  &aio->mmap_offset,
				  &nframes,
				  &aio->num_underruns);

	iodev->area->frames = nframes;
	cras_audio_area_config_buf_pointers(iodev->area, iodev->format, dst);

	*area = iodev->area;
	*frames = nframes;

	return rc;
}

static int put_buffer(struct cras_iodev *iodev, unsigned nwritten)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;

	return cras_alsa_mmap_commit(aio->handle,
				     aio->mmap_offset,
				     nwritten,
				     &aio->num_underruns);
}

static int flush_buffer(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	snd_pcm_uframes_t nframes;

	if (iodev->direction == CRAS_STREAM_INPUT) {
		nframes = snd_pcm_forwardable(aio->handle);
		return snd_pcm_forward(aio->handle, nframes);
	}
	return 0;
}

/*
 * Gets the first plugged node in list. This is used as the
 * default node to set as active.
 */
static struct cras_ionode *first_plugged_node(struct cras_iodev *iodev)
{
	struct cras_ionode *n;

	/* When this is called at iodev creation, none of the nodes
	 * are selected. Just pick the first plugged one and let Chrome
	 * choose it later. */
	DL_FOREACH(iodev->nodes, n) {
		if (n->plugged)
			return n;
	}
	return iodev->nodes;
}

static void update_active_node(struct cras_iodev *iodev, unsigned node_idx,
			       unsigned dev_enabled)
{
	struct cras_ionode *n;

	/* If a node exists for node_idx, set it as active. */
	DL_FOREACH(iodev->nodes, n) {
		if (n->idx == node_idx) {
			alsa_iodev_set_active_node(iodev, n, dev_enabled);
			return;
		}
	}

	alsa_iodev_set_active_node(iodev, first_plugged_node(iodev),
				   dev_enabled);
}

static int update_channel_layout(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	snd_pcm_t *handle = NULL;
	snd_pcm_uframes_t buf_size = 0;
	int err = 0;

	/* If the capture channel map is specified in UCM, prefer it over
	 * what ALSA provides. */
	if (aio->ucm && (iodev->direction == CRAS_STREAM_INPUT)) {
		struct alsa_input_node *input =
			(struct alsa_input_node *)iodev->active_node;

		if (input->channel_layout) {
			memcpy(iodev->format->channel_layout,
			       input->channel_layout,
			       CRAS_CH_MAX * sizeof(*input->channel_layout));
			return 0;
		}
	}

	err = cras_alsa_pcm_open(&handle, aio->dev, aio->alsa_stream);
	if (err < 0) {
		syslog(LOG_ERR, "snd_pcm_open_failed: %s", snd_strerror(err));
		return err;
	}

	/* Sets frame rate and channel count to alsa device before
	 * we test channel mapping. */
	err = cras_alsa_set_hwparams(handle, iodev->format, &buf_size, 0,
				     aio->dma_period_set_microsecs);
	if (err < 0) {
		cras_alsa_pcm_close(handle);
		return err;
	}

	err = cras_alsa_get_channel_map(handle, iodev->format);

	cras_alsa_pcm_close(handle);
	return err;
}

static int set_hotword_model(struct cras_iodev *iodev, const char *model_name)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	if (!aio->ucm)
		return -EINVAL;

	return ucm_set_hotword_model(aio->ucm, model_name);
}

static char *get_hotword_models(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	if (!aio->ucm)
		return NULL;

	return ucm_get_hotword_models(aio->ucm);
}

/*
 * Alsa helper functions.
 */

static struct alsa_output_node *get_active_output(const struct alsa_io *aio)
{
	return (struct alsa_output_node *)aio->base.active_node;
}

static struct alsa_input_node *get_active_input(const struct alsa_io *aio)
{
	return (struct alsa_input_node *)aio->base.active_node;
}

/*
 * Gets the curve for the active output node. If the node doesn't have volume
 * curve specified, return the default volume curve of the parent iodev.
 */
static const struct cras_volume_curve *get_curve_for_output_node(
		const struct alsa_io *aio,
		const struct alsa_output_node *node)
{
	if (node && node->volume_curve)
		return node->volume_curve;
	return aio->default_volume_curve;
}

/*
 * Gets the curve for the active output.
 */
static const struct cras_volume_curve *get_curve_for_active_output(
		const struct alsa_io *aio)
{
	struct alsa_output_node *node = get_active_output(aio);
	return get_curve_for_output_node(aio, node);
}

/*
 * Informs the system of the volume limits for this device.
 */
static void set_alsa_volume_limits(struct alsa_io *aio)
{
	const struct cras_volume_curve *curve;

	/* Only set the limits if the dev is active. */
	if (!has_handle(aio))
		return;

	curve = get_curve_for_active_output(aio);
	cras_system_set_volume_limits(
			curve->get_dBFS(curve, 1), /* min */
			curve->get_dBFS(curve, CRAS_MAX_SYSTEM_VOLUME));
}

/*
 * Sets the alsa mute control for this iodev.
 */
static void set_alsa_mute_control(const struct alsa_io *aio, int muted)
{
	struct alsa_output_node *aout;

	if (!has_handle(aio))
		return;

	aout = get_active_output(aio);
	cras_alsa_mixer_set_mute(
		aio->mixer,
		muted,
		aout ? aout->mixer_output : NULL);
}

/*
 * Sets the volume of the playback device to the specified level. Receives a
 * volume index from the system settings, ranging from 0 to 100, converts it to
 * dB using the volume curve, and sends the dB value to alsa.
 */
static void set_alsa_volume(struct cras_iodev *iodev)
{
	const struct alsa_io *aio = (const struct alsa_io *)iodev;
	const struct cras_volume_curve *curve;
	size_t volume;
	struct alsa_output_node *aout;

	assert(aio);
	if (aio->mixer == NULL)
		return;

	/* Only set the volume if the dev is active. */
	if (!has_handle(aio))
		return;

	volume = cras_system_get_volume();
	curve = get_curve_for_active_output(aio);
	if (curve == NULL)
		return;
	aout = get_active_output(aio);
	if (aout)
		volume = cras_iodev_adjust_node_volume(&aout->base, volume);

	/* Samples get scaled for devices using software volume, set alsa
	 * volume to 100. */
	if (cras_iodev_software_volume_needed(iodev))
		volume = 100;

	cras_alsa_mixer_set_dBFS(
		aio->mixer,
		curve->get_dBFS(curve, volume),
		aout ? aout->mixer_output : NULL);
}

static void set_alsa_mute(struct cras_iodev *iodev)
{
	/* Mute for zero. */
	const struct alsa_io *aio = (const struct alsa_io *)iodev;
	set_alsa_mute_control(aio, cras_system_get_mute());
}

/*
 * Sets the capture gain to the current system input gain level, given in dBFS.
 * Set mute based on the system mute state.  This gain can be positive or
 * negative and might be adjusted often if an app is running an AGC.
 */
static void set_alsa_capture_gain(struct cras_iodev *iodev)
{
	const struct alsa_io *aio = (const struct alsa_io *)iodev;
	struct alsa_input_node *ain;
	long gain;

	assert(aio);
	if (aio->mixer == NULL)
		return;

	/* Only set the volume if the dev is active. */
	if (!has_handle(aio))
		return;
	gain = cras_iodev_adjust_active_node_gain(
				iodev, cras_system_get_capture_gain());

	/* Set hardware gain to 0dB if software gain is needed. */
	if (cras_iodev_software_volume_needed(iodev))
		gain = 0;

	ain = get_active_input(aio);

	cras_alsa_mixer_set_capture_dBFS(
			aio->mixer,
			gain,
			ain ? ain->mixer_input : NULL);
	cras_alsa_mixer_set_capture_mute(aio->mixer,
					 cras_system_get_capture_mute(),
					 ain ? ain->mixer_input : NULL);
}

/*
 * Swaps the left and right channels of the given node.
 */
static int set_alsa_node_swapped(struct cras_iodev *iodev,
				 struct cras_ionode *node, int enable)
{
	const struct alsa_io *aio = (const struct alsa_io *)iodev;
	assert(aio);
	return ucm_enable_swap_mode(aio->ucm, node->name, enable);
}

/*
 * Initializes the device settings according to system volume, mute, gain
 * settings.
 * Updates system capture gain limits based on current active device/node.
 */
static void init_device_settings(struct alsa_io *aio)
{
	/* Register for volume/mute callback and set initial volume/mute for
	 * the device. */
	if (aio->base.direction == CRAS_STREAM_OUTPUT) {
		set_alsa_volume_limits(aio);
		set_alsa_volume(&aio->base);
		set_alsa_mute(&aio->base);
	} else {
		struct mixer_control *mixer_input = NULL;
		struct alsa_input_node *ain = get_active_input(aio);
		long min_capture_gain, max_capture_gain;

		if (ain)
			mixer_input = ain->mixer_input;

		if (cras_iodev_software_volume_needed(&aio->base)) {
			min_capture_gain = DEFAULT_MIN_CAPTURE_GAIN;
			max_capture_gain = cras_iodev_maximum_software_gain(
					&aio->base);
		} else {
			min_capture_gain =
				cras_alsa_mixer_get_minimum_capture_gain(
						aio->mixer, mixer_input);
			max_capture_gain =
				cras_alsa_mixer_get_maximum_capture_gain(
						aio->mixer, mixer_input);
		}
		cras_system_set_capture_gain_limits(min_capture_gain,
						    max_capture_gain);
		set_alsa_capture_gain(&aio->base);
	}
}

/*
 * Functions run in the main server context.
 */

/*
 * Frees resources used by the alsa iodev.
 * Args:
 *    iodev - the iodev to free the resources from.
 */
static void free_alsa_iodev_resources(struct alsa_io *aio)
{
	struct cras_ionode *node;
	struct alsa_output_node *aout;

	free(aio->base.supported_rates);
	free(aio->base.supported_channel_counts);
	free(aio->base.supported_formats);

	DL_FOREACH(aio->base.nodes, node) {
		if (aio->base.direction == CRAS_STREAM_OUTPUT) {
			aout = (struct alsa_output_node *)node;
			cras_volume_curve_destroy(aout->volume_curve);
		}
		cras_iodev_rm_node(&aio->base, node);
		free(node->softvol_scalers);
		free(node);
	}

	free((void *)aio->dsp_name_default);
	cras_iodev_free_resources(&aio->base);
	free(aio->dev);
	if (aio->dev_id)
		free(aio->dev_id);
	if (aio->dev_name)
		free(aio->dev_name);
}

/*
 * Returns true if this is the first internal device.
 */
static int first_internal_device(struct alsa_io *aio)
{
	return aio->is_first && aio->card_type == ALSA_CARD_TYPE_INTERNAL;
}

/*
 * Returns true if there is already a node created with the given name.
 */
static int has_node(struct alsa_io *aio, const char *name)
{
	struct cras_ionode *node;

	DL_FOREACH(aio->base.nodes, node)
		if (!strcmp(node->name, name))
			return 1;

	return 0;
}

/*
 * Returns true if string s ends with the given suffix.
 */
int endswith(const char *s, const char *suffix)
{
	size_t n = strlen(s);
	size_t m = strlen(suffix);
	return n >= m && !strcmp(s + (n - m), suffix);
}

/*
 * Drop the node name and replace it with node type.
 */
static void drop_node_name(struct cras_ionode *node)
{
	if (node->type == CRAS_NODE_TYPE_USB)
		strcpy(node->name, USB);
	else if (node->type == CRAS_NODE_TYPE_HDMI)
		strcpy(node->name, HDMI);
	else {
		/* Only HDMI or USB node might have invalid name to drop */
		syslog(LOG_ERR, "Unexpectedly drop node name for "
		       "node: %s, type: %d", node->name, node->type);
		strcpy(node->name, DEFAULT);
	}
}

/*
 * Sets the initial plugged state and type of a node based on its
 * name. Chrome will assign priority to nodes base on node type.
 */
static void set_node_initial_state(struct cras_ionode *node,
				   enum CRAS_ALSA_CARD_TYPE card_type)
{

	unsigned i;

	node->volume = 100;
	node->type = CRAS_NODE_TYPE_UNKNOWN;
	/* Go through the known names */
	for (i = 0; i < ARRAY_SIZE(node_defaults); i++)
		if (!strncmp(node->name, node_defaults[i].name,
			     strlen(node_defaults[i].name))) {
			node->position = node_defaults[i].position;
			node->plugged = (node->position
					!= NODE_POSITION_EXTERNAL);
			node->type = node_defaults[i].type;
			if (node->plugged)
				gettimeofday(&node->plugged_time, NULL);
			break;
		}

	/* If we didn't find a matching name above, but the node is a jack node,
	 * set its type to headphone/mic. This matches node names like "DAISY-I2S Mic
	 * Jack".
	 * If HDMI is in the node name, set its type to HDMI. This matches node names
	 * like "Rockchip HDMI Jack".
	 */
	if (i == ARRAY_SIZE(node_defaults)) {
		if (endswith(node->name, "Jack")) {
			if (node->dev->direction == CRAS_STREAM_OUTPUT)
				node->type = CRAS_NODE_TYPE_HEADPHONE;
			else
				node->type = CRAS_NODE_TYPE_MIC;
		}
		if (strstr(node->name, HDMI) &&
		    node->dev->direction == CRAS_STREAM_OUTPUT)
			node->type = CRAS_NODE_TYPE_HDMI;
	}

	/* Regardless of the node name of a USB headset (it can be "Speaker"),
	 * set it's type to usb.
	 */
	if (card_type == ALSA_CARD_TYPE_USB) {
		node->type = CRAS_NODE_TYPE_USB;
		node->position = NODE_POSITION_EXTERNAL;
	}

	if (!is_utf8_string(node->name))
		drop_node_name(node);
}

static int get_ucm_flag_integer(struct alsa_io *aio,
				const char *flag_name,
				int *result)
{
	char *value;
	int i;

	if (!aio->ucm)
		return -1;

	value = ucm_get_flag(aio->ucm, flag_name);
	if (!value)
		return -1;

	i = atoi(value);
	free(value);
	*result = i;
	return 0;
}

static int auto_unplug_input_node(struct alsa_io *aio)
{
	int result;
	if (get_ucm_flag_integer(aio, "AutoUnplugInputNode", &result))
		return 0;
	return result;
}

static int auto_unplug_output_node(struct alsa_io *aio)
{
	int result;
	if (get_ucm_flag_integer(aio, "AutoUnplugOutputNode", &result))
		return 0;
	return result;
}

static int no_create_default_input_node(struct alsa_io *aio)
{
	int result;
	if (get_ucm_flag_integer(aio, "NoCreateDefaultInputNode", &result))
		return 0;
	return result;
}

static int no_create_default_output_node(struct alsa_io *aio)
{
	int result;
	if (get_ucm_flag_integer(aio, "NoCreateDefaultOutputNode", &result))
		return 0;
	return result;
}

static void set_output_node_software_volume_needed(
	struct alsa_output_node *output, struct alsa_io *aio)
{

	struct cras_alsa_mixer *mixer = aio->mixer;
	long range = 0;

	if (aio->ucm && ucm_get_disable_software_volume(aio->ucm)) {
		output->base.software_volume_needed = 0;
		syslog(LOG_DEBUG, "Disable software volume for %s from ucm.",
		       output->base.name);
		return;
	}

	/* Use software volume for HDMI output and nodes without volume mixer
	 * control. */
	if ((output->base.type == CRAS_NODE_TYPE_HDMI) ||
	    (!cras_alsa_mixer_has_main_volume(mixer) &&
	     !cras_alsa_mixer_has_volume(output->mixer_output)))
		output->base.software_volume_needed = 1;

	/* Use software volume if the usb device's volume range is smaller
	 * than 40dB */
	if (output->base.type == CRAS_NODE_TYPE_USB) {
		range += cras_alsa_mixer_get_dB_range(mixer);
		range += cras_alsa_mixer_get_output_dB_range(
				output->mixer_output);
		if (range < 4000)
			output->base.software_volume_needed = 1;
	}
	if (output->base.software_volume_needed)
		syslog(LOG_DEBUG, "Use software volume for node: %s",
		       output->base.name);
}

static void set_input_node_software_volume_needed(
	struct alsa_input_node *input, struct alsa_io *aio)
{
	long max_software_gain;
	int rc;

	input->base.software_volume_needed = 0;
	input->base.max_software_gain = 0;

	/* Enable software gain only if max software gain is specified in UCM.*/
	if (!aio->ucm)
		return;

	rc = ucm_get_max_software_gain(aio->ucm, input->base.name,
	                               &max_software_gain);
	if (rc)
		return;

	input->base.software_volume_needed = 1;
	input->base.max_software_gain = max_software_gain;
	syslog(LOG_INFO,
	       "Use software gain for %s with max %ld because it is specified"
	       " in UCM", input->base.name, max_software_gain);
}

static void set_input_default_node_gain(struct alsa_input_node *input,
					struct alsa_io *aio)
{
	long default_node_gain;
	int rc;

	if (!aio->ucm)
		return;

	rc = ucm_get_default_node_gain(aio->ucm, input->base.name,
					 &default_node_gain);
	if (rc)
		return;

	input->base.capture_gain = default_node_gain;
}

static void check_auto_unplug_output_node(struct alsa_io *aio,
					  struct cras_ionode *node,
					  int plugged)
{
	struct cras_ionode *tmp;

	if (!auto_unplug_output_node(aio))
		return;

	/* Auto unplug internal speaker if any output node has been created */
	if (!strcmp(node->name, INTERNAL_SPEAKER) && plugged) {
		DL_FOREACH(aio->base.nodes, tmp)
			if (tmp->plugged && (tmp != node))
				cras_iodev_set_node_attr(node,
							 IONODE_ATTR_PLUGGED,
							 0);
	} else {
		DL_FOREACH(aio->base.nodes, tmp) {
			if (!strcmp(tmp->name, INTERNAL_SPEAKER))
				cras_iodev_set_node_attr(tmp,
							 IONODE_ATTR_PLUGGED,
							 !plugged);
		}
	}
}

/*
 * Callback for listing mixer outputs. The mixer will call this once for each
 * output associated with this device. Most commonly this is used to tell the
 * device it has Headphones and Speakers.
 */
static struct alsa_output_node *new_output(struct alsa_io *aio,
					   struct mixer_control *cras_output,
					   const char *name)
{
	struct alsa_output_node *output;
	syslog(LOG_DEBUG, "New output node for '%s'", name);
	if (aio == NULL) {
		syslog(LOG_ERR, "Invalid aio when listing outputs.");
		return NULL;
	}
	output = (struct alsa_output_node *)calloc(1, sizeof(*output));
	if (output == NULL) {
		syslog(LOG_ERR, "Out of memory when listing outputs.");
		return NULL;
	}
	output->base.dev = &aio->base;
	output->base.idx = aio->next_ionode_index++;
	output->base.stable_id = SuperFastHash(name,
					       strlen(name),
					       aio->base.info.stable_id);
	output->base.stable_id_new = SuperFastHash(name,
						   strlen(name),
						   aio->base.info.stable_id_new
						   );
	output->mixer_output = cras_output;

	/* Volume curve. */
	output->volume_curve = cras_card_config_get_volume_curve_for_control(
			aio->config,
			name ? name
			     : cras_alsa_mixer_get_control_name(cras_output));

	strncpy(output->base.name, name, sizeof(output->base.name) - 1);
	set_node_initial_state(&output->base, aio->card_type);
	set_output_node_software_volume_needed(output, aio);

	cras_iodev_add_node(&aio->base, &output->base);

	check_auto_unplug_output_node(aio, &output->base, output->base.plugged);
	return output;
}

static void new_output_by_mixer_control(struct mixer_control *cras_output,
				        void *callback_arg)
{
	struct alsa_io *aio = (struct alsa_io *)callback_arg;
	char node_name[CRAS_IODEV_NAME_BUFFER_SIZE];
	const char *ctl_name;

	ctl_name = cras_alsa_mixer_get_control_name(cras_output);
	if (!ctl_name)
	        return;

	if (aio->card_type == ALSA_CARD_TYPE_USB) {
		snprintf(node_name, sizeof(node_name), "%s: %s",
			aio->base.info.name, ctl_name);
		new_output(aio, cras_output, node_name);
	} else {
		new_output(aio, cras_output, ctl_name);
	}
}

static void check_auto_unplug_input_node(struct alsa_io *aio,
					 struct cras_ionode *node,
					 int plugged)
{
	struct cras_ionode *tmp;
	if (!auto_unplug_input_node(aio))
		return;

	/* Auto unplug internal mic if any input node has already
	 * been created */
	if (!strcmp(node->name, INTERNAL_MICROPHONE) && plugged) {
		DL_FOREACH(aio->base.nodes, tmp)
			if (tmp->plugged && (tmp != node))
				cras_iodev_set_node_attr(node,
							 IONODE_ATTR_PLUGGED,
							 0);
	} else {
		DL_FOREACH(aio->base.nodes, tmp)
			if (!strcmp(tmp->name, INTERNAL_MICROPHONE))
				cras_iodev_set_node_attr(tmp,
							 IONODE_ATTR_PLUGGED,
							 !plugged);
	}
}

static struct alsa_input_node *new_input(struct alsa_io *aio,
		struct mixer_control *cras_input, const char *name)
{
	struct alsa_input_node *input;
	char *mic_positions;
	int err;

	input = (struct alsa_input_node *)calloc(1, sizeof(*input));
	if (input == NULL) {
		syslog(LOG_ERR, "Out of memory when listing inputs.");
		return NULL;
	}
	input->base.dev = &aio->base;
	input->base.idx = aio->next_ionode_index++;
	input->base.stable_id = SuperFastHash(name,
					      strlen(name),
					      aio->base.info.stable_id);
	input->base.stable_id_new = SuperFastHash(name,
						  strlen(name),
						  aio->base.info.stable_id_new);
	input->mixer_input = cras_input;
	strncpy(input->base.name, name, sizeof(input->base.name) - 1);
	set_node_initial_state(&input->base, aio->card_type);
	set_input_node_software_volume_needed(input, aio);
	set_input_default_node_gain(input, aio);

	if (aio->ucm) {
		/* Check mic positions only for internal mic. */
		if ((input->base.type == CRAS_NODE_TYPE_MIC) &&
		    (input->base.position == NODE_POSITION_INTERNAL)) {
			mic_positions = ucm_get_mic_positions(aio->ucm);
			if (mic_positions) {
				strncpy(input->base.mic_positions,
					mic_positions,
					sizeof(input->base.mic_positions) - 1);
				free(mic_positions);
			}
		}

		/* Check if channel map is specified in UCM. */
		input->channel_layout = (int8_t *)malloc(
				CRAS_CH_MAX * sizeof(*input->channel_layout));
		err = ucm_get_capture_chmap_for_dev(aio->ucm, name,
						    input->channel_layout);
		if (err) {
			free(input->channel_layout);
			input->channel_layout = 0;
		}
	}

	cras_iodev_add_node(&aio->base, &input->base);
	check_auto_unplug_input_node(aio, &input->base,
				     input->base.plugged);
	return input;
}

static void new_input_by_mixer_control(struct mixer_control *cras_input,
				       void *callback_arg)
{
	struct alsa_io *aio = (struct alsa_io *)callback_arg;
	char node_name[CRAS_IODEV_NAME_BUFFER_SIZE];
	const char *ctl_name = cras_alsa_mixer_get_control_name(cras_input);

	if (aio->card_type == ALSA_CARD_TYPE_USB) {
		snprintf(node_name , sizeof(node_name), "%s: %s",
			 aio->base.info.name, ctl_name);
		new_input(aio, cras_input, node_name);
	} else {
		new_input(aio, cras_input, ctl_name);
	}
}

/*
 * Finds the output node associated with the jack. Returns NULL if not found.
 */
static struct alsa_output_node *get_output_node_from_jack(
		struct alsa_io *aio, const struct cras_alsa_jack *jack)
{
	struct mixer_control *mixer_output;
	struct cras_ionode *node = NULL;
	struct alsa_output_node *aout = NULL;

	/* Search by jack first. */
	DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, aout,
				   jack, jack);
	if (aout)
		return aout;

	/* Search by mixer control next. */
	mixer_output = cras_alsa_jack_get_mixer_output(jack);
	if (mixer_output == NULL)
		return NULL;

	DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, aout,
				   mixer_output, mixer_output);
	return aout;
}

static struct alsa_input_node *get_input_node_from_jack(
		struct alsa_io *aio, const struct cras_alsa_jack *jack)
{
	struct mixer_control *mixer_input;
	struct cras_ionode *node = NULL;
	struct alsa_input_node *ain = NULL;

	mixer_input = cras_alsa_jack_get_mixer_input(jack);
	if (mixer_input == NULL) {
		DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, ain,
					   jack, jack);
		return ain;
	}

	DL_SEARCH_SCALAR_WITH_CAST(aio->base.nodes, node, ain,
				   mixer_input, mixer_input);
	return ain;
}

/*
 * Returns the dsp name specified in the ucm config. If there is a dsp
 * name specified for the jack of the active node, use that. Otherwise
 * use the default dsp name for the alsa_io device.
 */
static const char *get_active_dsp_name(struct alsa_io *aio)
{
	struct cras_ionode *node = aio->base.active_node;
	const struct cras_alsa_jack *jack;

	if (node == NULL)
		return NULL;

	if (aio->base.direction == CRAS_STREAM_OUTPUT)
		jack = ((struct alsa_output_node *) node)->jack;
	else
		jack = ((struct alsa_input_node *) node)->jack;

	return cras_alsa_jack_get_dsp_name(jack) ? : aio->dsp_name_default;
}

/*
 * Creates volume curve for the node associated with given jack.
 */
static struct cras_volume_curve *create_volume_curve_for_jack(
		const struct cras_card_config *config,
		const struct cras_alsa_jack *jack)
{
	struct cras_volume_curve *curve;
	const char *name;

	/* Use jack's UCM device name as key to get volume curve. */
	name = cras_alsa_jack_get_ucm_device(jack);
	curve = cras_card_config_get_volume_curve_for_control(config, name);
	if (curve)
		return curve;

	/* Use alsa jack's name as key to get volume curve. */
	name = cras_alsa_jack_get_name(jack);
	curve = cras_card_config_get_volume_curve_for_control(config, name);
	if (curve)
		return curve;

	return NULL;
}

/*
 * Callback that is called when an output jack is plugged or unplugged.
 */
static void jack_output_plug_event(const struct cras_alsa_jack *jack,
				    int plugged,
				    void *arg)
{
	struct alsa_io *aio;
	struct alsa_output_node *node;
	const char *jack_name;

	if (arg == NULL)
		return;

	aio = (struct alsa_io *)arg;
	node = get_output_node_from_jack(aio, jack);
	jack_name = cras_alsa_jack_get_name(jack);
	if (!strcmp(jack_name, "Speaker Phantom Jack"))
		jack_name = INTERNAL_SPEAKER;

	/* If there isn't a node for this jack, create one. */
	if (node == NULL) {
		if (aio->fully_specified) {
			/* When fully specified, can't have new nodes. */
			syslog(LOG_ERR, "No matching output node for jack %s!",
			       jack_name);
			return;
		}
		node = new_output(aio, NULL, jack_name);
		if (node == NULL)
			return;

		cras_alsa_jack_update_node_type(jack, &(node->base.type));
	}

	if (!node->jack) {
		if (aio->fully_specified)
			syslog(LOG_ERR,
			       "Jack '%s' was found to match output node '%s'."
			       " Please fix your UCM configuration to match.",
			       jack_name, node->base.name);

		/* If we already have the node, associate with the jack. */
		node->jack = jack;
		if (node->volume_curve == NULL)
			node->volume_curve = create_volume_curve_for_jack(
					aio->config, jack);
	}

	syslog(LOG_DEBUG, "%s plugged: %d, %s", jack_name, plugged,
	       cras_alsa_mixer_get_control_name(node->mixer_output));

	cras_alsa_jack_update_monitor_name(jack, node->base.name,
					   sizeof(node->base.name));
	/* The name got from jack might be an invalid UTF8 string. */
	if (!is_utf8_string(node->base.name))
		drop_node_name(&node->base);

	cras_iodev_set_node_attr(&node->base, IONODE_ATTR_PLUGGED, plugged);

	check_auto_unplug_output_node(aio, &node->base, plugged);
}

/*
 * Callback that is called when an input jack is plugged or unplugged.
 */
static void jack_input_plug_event(const struct cras_alsa_jack *jack,
				  int plugged,
				  void *arg)
{
	struct alsa_io *aio;
	struct alsa_input_node *node;
	struct mixer_control *cras_input;
	const char *jack_name;

	if (arg == NULL)
		return;
	aio = (struct alsa_io *)arg;
	node = get_input_node_from_jack(aio, jack);
	jack_name = cras_alsa_jack_get_name(jack);

	/* If there isn't a node for this jack, create one. */
	if (node == NULL) {
		if (aio->fully_specified) {
			/* When fully specified, can't have new nodes. */
			syslog(LOG_ERR, "No matching input node for jack %s!",
			       jack_name);
			return;
		}
		cras_input = cras_alsa_jack_get_mixer_input(jack);
		node = new_input(aio, cras_input, jack_name);
		if (node == NULL)
			return;
	}

	syslog(LOG_DEBUG, "%s plugged: %d, %s", jack_name, plugged,
	       cras_alsa_mixer_get_control_name(node->mixer_input));

	/* If we already have the node, associate with the jack. */
	if (!node->jack) {
		if (aio->fully_specified)
			syslog(LOG_ERR,
			       "Jack '%s' was found to match input node '%s'."
			       " Please fix your UCM configuration to match.",
			       jack_name, node->base.name);
		node->jack = jack;
	}

	cras_iodev_set_node_attr(&node->base, IONODE_ATTR_PLUGGED, plugged);

	check_auto_unplug_input_node(aio, &node->base, plugged);
}

/*
 * Sets the name of the given iodev, using the name and index of the card
 * combined with the device index and direction.
 */
static void set_iodev_name(struct cras_iodev *dev,
			   const char *card_name,
			   const char *dev_name,
			   size_t card_index,
			   size_t device_index,
			   enum CRAS_ALSA_CARD_TYPE card_type,
			   size_t usb_vid,
			   size_t usb_pid,
			   char *usb_serial_number)
{
	snprintf(dev->info.name,
		 sizeof(dev->info.name),
		 "%s: %s:%zu,%zu",
		 card_name,
		 dev_name,
		 card_index,
		 device_index);
	dev->info.name[ARRAY_SIZE(dev->info.name) - 1] = '\0';
	syslog(LOG_DEBUG, "Add device name=%s", dev->info.name);

	dev->info.stable_id = SuperFastHash(card_name,
					    strlen(card_name),
					    strlen(card_name));
	dev->info.stable_id = SuperFastHash(dev_name,
					    strlen(dev_name),
					    dev->info.stable_id);

	switch (card_type) {
	case ALSA_CARD_TYPE_INTERNAL:
		dev->info.stable_id = SuperFastHash((const char *)&device_index,
						    sizeof(device_index),
						    dev->info.stable_id);
		dev->info.stable_id_new = dev->info.stable_id;
		break;
	case ALSA_CARD_TYPE_USB:
		dev->info.stable_id = SuperFastHash((const char *)&usb_vid,
						    sizeof(usb_vid),
						    dev->info.stable_id);
		dev->info.stable_id = SuperFastHash((const char *)&usb_pid,
						    sizeof(usb_pid),
						    dev->info.stable_id);
		dev->info.stable_id_new =
			SuperFastHash(usb_serial_number,
				      strlen(usb_serial_number),
				      dev->info.stable_id);
		break;
	default:
		dev->info.stable_id_new = dev->info.stable_id;
		break;
	}
	syslog(LOG_DEBUG, "Stable ID=%08x, New Stable ID=%08x",
	       dev->info.stable_id, dev->info.stable_id_new);
}

static int get_fixed_rate(struct alsa_io *aio)
{
	const char *name;

	if (aio->base.direction == CRAS_STREAM_OUTPUT) {
		struct alsa_output_node *active = get_active_output(aio);
		if (!active)
			return -ENOENT;
		name = active->base.name;
	} else {
		struct alsa_input_node *active = get_active_input(aio);
		if (!active)
			return -ENOENT;
		name = active->base.name;
	}

	return ucm_get_sample_rate_for_dev(aio->ucm, name, aio->base.direction);
}

/*
 * Updates the supported sample rates and channel counts.
 */
static int update_supported_formats(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	int err;
	int fixed_rate;

	free(iodev->supported_rates);
	iodev->supported_rates = NULL;
	free(iodev->supported_channel_counts);
	iodev->supported_channel_counts = NULL;
	free(iodev->supported_formats);
	iodev->supported_formats = NULL;

	err = cras_alsa_fill_properties(aio->dev, aio->alsa_stream,
					&iodev->supported_rates,
					&iodev->supported_channel_counts,
					&iodev->supported_formats);
	if (err)
		return err;

	if (aio->ucm) {
		/* Allow UCM to override supplied rates. */
		fixed_rate = get_fixed_rate(aio);
		if (fixed_rate > 0) {
			free(iodev->supported_rates);
			iodev->supported_rates = (size_t*)malloc(
					2 * sizeof(iodev->supported_rates[0]));
			iodev->supported_rates[0] = fixed_rate;
			iodev->supported_rates[1] = 0;
		}
	}
	return 0;
}

/*
 * Builds software volume scalers for output nodes in the device.
 */
static void build_softvol_scalers(struct alsa_io *aio)
{
	struct cras_ionode *ionode;

	DL_FOREACH(aio->base.nodes, ionode) {
		struct alsa_output_node *aout;
		const struct cras_volume_curve *curve;

		aout = (struct alsa_output_node *)ionode;
		curve = get_curve_for_output_node(aio, aout);

		ionode->softvol_scalers = softvol_build_from_curve(curve);
	}
}

static void enable_active_ucm(struct alsa_io *aio, int plugged)
{
	const struct cras_alsa_jack *jack;
	const char *name;

	if (aio->base.direction == CRAS_STREAM_OUTPUT) {
		struct alsa_output_node *active = get_active_output(aio);
		if (!active)
			return;
		name = active->base.name;
		jack = active->jack;
	} else {
		struct alsa_input_node *active = get_active_input(aio);
		if (!active)
			return;
		name = active->base.name;
		jack = active->jack;
	}

	if (jack)
		cras_alsa_jack_enable_ucm(jack, plugged);
	else if (aio->ucm)
		ucm_set_enabled(aio->ucm, name, plugged);
}

static int fill_whole_buffer_with_zeros(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	int rc;
	uint8_t *dst = NULL;
	size_t format_bytes;

	/* Fill whole buffer with zeros. */
	rc = cras_alsa_mmap_get_whole_buffer(
			aio->handle, &dst, &aio->num_underruns);

	if (rc < 0) {
		syslog(LOG_ERR, "Failed to get whole buffer: %s",
		       snd_strerror(rc));
		return rc;
	}

	format_bytes = cras_get_format_bytes(iodev->format);
	memset(dst, 0, iodev->buffer_size * format_bytes);

	return 0;
}

static int adjust_appl_ptr(struct cras_iodev *odev)
{
	struct alsa_io *aio = (struct alsa_io *)odev;

	/* Move appl_ptr to min_buffer_level + min_cb_level frames ahead of
	 * hw_ptr when resuming from free run or adjusting appl_ptr from
	 * underrun. */
	return cras_alsa_resume_appl_ptr(
			aio->handle,
			odev->min_buffer_level + odev->min_cb_level);
}

static int alsa_output_underrun(struct cras_iodev *odev)
{
	int rc;
	/* Fill whole buffer with zeros. This avoids samples left in buffer causing
	 * noise when device plays them. */
	rc = fill_whole_buffer_with_zeros(odev);
	if (rc)
		return rc;
	/* Adjust appl_ptr to leave underrun. */
	return adjust_appl_ptr(odev);
}

static int possibly_enter_free_run(struct cras_iodev *odev)
{
	struct alsa_io *aio = (struct alsa_io *)odev;
	int rc;
	unsigned int hw_level, fr_to_write;
	unsigned int target_hw_level = odev->min_cb_level * 2;
	struct timespec hw_tstamp;

	if (aio->is_free_running)
		return 0;

	/* Check if all valid samples are played.
	 * If all valid samples are played, fill whole buffer with zeros. */
	rc = cras_iodev_frames_queued(odev, &hw_tstamp);
	if (rc < 0)
		return rc;
	hw_level = rc;

	if (hw_level < aio->filled_zeros_for_draining || hw_level == 0) {
		rc = fill_whole_buffer_with_zeros(odev);
		if (rc < 0)
			return rc;
		aio->is_free_running = 1;
		return 0;
	}

	/* Fill some zeros to drain valid samples. */
	fr_to_write = cras_iodev_buffer_avail(odev, hw_level);

	if (hw_level <= target_hw_level) {
		fr_to_write = MIN(target_hw_level - hw_level, fr_to_write);
		rc = cras_iodev_fill_odev_zeros(odev, fr_to_write);
		if (rc)
			return rc;
		aio->filled_zeros_for_draining += fr_to_write;
	}

	return 0;
}

static int leave_free_run(struct cras_iodev *odev)
{
	struct alsa_io *aio = (struct alsa_io *)odev;
	int rc;

	if (!aio->is_free_running)
		return 0;

	rc = adjust_appl_ptr(odev);
	if (rc) {
		syslog(LOG_ERR, "device %s failed to leave free run, rc = %d",
		       odev->info.name, rc);
		return rc;
	}
	aio->is_free_running = 0;
	aio->filled_zeros_for_draining = 0;

	return 0;
}

/*
 * Free run state is the optimization of no_stream playback on alsa_io.
 * The whole buffer will be filled with zeros. Device can play these zeros
 * indefinitely. When there is new meaningful sample, appl_ptr should be
 * resumed to some distance ahead of hw_ptr.
 */
static int no_stream(struct cras_iodev *odev, int enable)
{
	if (enable)
		return possibly_enter_free_run(odev);
	else
		return leave_free_run(odev);
}

static int output_should_wake(const struct cras_iodev *odev)
{
	struct alsa_io *aio = (struct alsa_io *)odev;
	if (aio->is_free_running)
		return 0;
	else
		return ((cras_iodev_state(odev) ==
					CRAS_IODEV_STATE_NO_STREAM_RUN) ||
		        (cras_iodev_state(odev) ==
					CRAS_IODEV_STATE_NORMAL_RUN));
}

static unsigned int get_num_underruns(const struct cras_iodev *iodev)
{
	const struct alsa_io *aio = (const struct alsa_io *)iodev;
	return aio->num_underruns;
}

static unsigned int get_num_severe_underruns(const struct cras_iodev *iodev)
{
	const struct alsa_io *aio = (const struct alsa_io *)iodev;
	return aio->num_severe_underruns;
}

static void set_default_hotword_model(struct cras_iodev *iodev)
{
	const char *default_model = "en_us";
	cras_node_id_t node_id;

	if (!iodev->active_node ||
	     iodev->active_node->type != CRAS_NODE_TYPE_HOTWORD)
		return;

	node_id = cras_make_node_id(iodev->info.idx, iodev->active_node->idx);
	/* This is a no-op if the default_model is not supported */
	cras_iodev_list_set_hotword_model(node_id, default_model);
}

/*
 * Exported Interface.
 */

struct cras_iodev *alsa_iodev_create(size_t card_index,
				     const char *card_name,
				     size_t device_index,
				     const char *dev_name,
				     const char *dev_id,
				     enum CRAS_ALSA_CARD_TYPE card_type,
				     int is_first,
				     struct cras_alsa_mixer *mixer,
				     const struct cras_card_config *config,
				     struct cras_use_case_mgr *ucm,
				     snd_hctl_t *hctl,
				     enum CRAS_STREAM_DIRECTION direction,
				     size_t usb_vid,
				     size_t usb_pid,
				     char *usb_serial_number)
{
	struct alsa_io *aio;
	struct cras_iodev *iodev;

	if (direction != CRAS_STREAM_INPUT && direction != CRAS_STREAM_OUTPUT)
		return NULL;

	aio = (struct alsa_io *)calloc(1, sizeof(*aio));
	if (!aio)
		return NULL;
	iodev = &aio->base;
	iodev->direction = direction;

	aio->device_index = device_index;
	aio->card_type = card_type;
	aio->is_first = is_first;
	aio->handle = NULL;
	aio->num_severe_underruns = 0;
	if (dev_name) {
		aio->dev_name = strdup(dev_name);
		if (!aio->dev_name)
			goto cleanup_iodev;
	}
	if (dev_id) {
		aio->dev_id = strdup(dev_id);
		if (!aio->dev_id)
			goto cleanup_iodev;
	}
	aio->is_free_running = 0;
	aio->filled_zeros_for_draining = 0;
	aio->dev = (char *)malloc(MAX_ALSA_DEV_NAME_LENGTH);
	if (aio->dev == NULL)
		goto cleanup_iodev;
	snprintf(aio->dev,
		 MAX_ALSA_DEV_NAME_LENGTH,
		 "hw:%zu,%zu",
		 card_index,
		 device_index);

	if (direction == CRAS_STREAM_INPUT) {
		aio->alsa_stream = SND_PCM_STREAM_CAPTURE;
		aio->base.set_capture_gain = set_alsa_capture_gain;
		aio->base.set_capture_mute = set_alsa_capture_gain;
	} else {
		aio->alsa_stream = SND_PCM_STREAM_PLAYBACK;
		aio->base.set_volume = set_alsa_volume;
		aio->base.set_mute = set_alsa_mute;
		aio->base.output_underrun = alsa_output_underrun;
	}
	iodev->open_dev = open_dev;
	iodev->close_dev = close_dev;
	iodev->update_supported_formats = update_supported_formats;
	iodev->frames_queued = frames_queued;
	iodev->delay_frames = delay_frames;
	iodev->get_buffer = get_buffer;
	iodev->put_buffer = put_buffer;
	iodev->flush_buffer = flush_buffer;
	iodev->start = start;
	iodev->update_active_node = update_active_node;
	iodev->update_channel_layout = update_channel_layout;
	iodev->set_hotword_model = set_hotword_model;
	iodev->get_hotword_models = get_hotword_models;
	iodev->no_stream = no_stream;
	iodev->output_should_wake = output_should_wake;
	iodev->get_num_underruns = get_num_underruns;
	iodev->get_num_severe_underruns = get_num_severe_underruns;
	iodev->set_swap_mode_for_node = cras_iodev_dsp_set_swap_mode_for_node;

	if (card_type == ALSA_CARD_TYPE_USB)
		iodev->min_buffer_level = USB_EXTRA_BUFFER_FRAMES;

	iodev->ramp = cras_ramp_create();
	if (iodev->ramp == NULL)
		goto cleanup_iodev;

	aio->mixer = mixer;
	aio->config = config;
	if (direction == CRAS_STREAM_OUTPUT) {
		aio->default_volume_curve =
				cras_card_config_get_volume_curve_for_control(
						config, "Default");
		if (aio->default_volume_curve == NULL)
			aio->default_volume_curve =
					cras_volume_curve_create_default();
	}
	aio->ucm = ucm;
	if (ucm) {
		unsigned int level;

		aio->dsp_name_default = ucm_get_dsp_name_default(ucm,
								 direction);
		/* Set callback for swap mode if it is supported
		 * in ucm modifier. */
		if (ucm_swap_mode_exists(ucm))
			aio->base.set_swap_mode_for_node =
				set_alsa_node_swapped;

		level = ucm_get_min_buffer_level(ucm);
		if (level && direction == CRAS_STREAM_OUTPUT)
			iodev->min_buffer_level = level;

		aio->enable_htimestamp =
			ucm_get_enable_htimestamp_flag(ucm);
	}

	set_iodev_name(iodev, card_name, dev_name, card_index, device_index,
		       card_type, usb_vid, usb_pid, usb_serial_number);

	aio->jack_list =
		cras_alsa_jack_list_create(
			card_index,
			card_name,
			device_index,
			is_first,
			mixer,
			ucm,
			hctl,
			direction,
			direction == CRAS_STREAM_OUTPUT ?
				     jack_output_plug_event :
				     jack_input_plug_event,
			aio);
	if (!aio->jack_list)
		goto cleanup_iodev;

	/* HDMI outputs don't have volume adjustment, do it in software. */
	if (direction == CRAS_STREAM_OUTPUT && strstr(dev_name, HDMI))
		iodev->software_volume_needed = 1;

	/* Add this now so that cleanup of the iodev (in case of error or card
	 * card removal will function as expected. */
	if (direction == CRAS_STREAM_OUTPUT)
		cras_iodev_list_add_output(&aio->base);
	else
		cras_iodev_list_add_input(&aio->base);
	return &aio->base;

cleanup_iodev:
	free_alsa_iodev_resources(aio);
	free(aio);
	return NULL;
}

int alsa_iodev_legacy_complete_init(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	const char *dev_name;
	const char *dev_id;
	enum CRAS_STREAM_DIRECTION direction;
	int err;
	int is_first;
	struct cras_alsa_mixer *mixer;

	if (!aio)
		return -EINVAL;
	direction = iodev->direction;
	dev_name = aio->dev_name;
	dev_id = aio->dev_id;
	is_first = aio->is_first;
	mixer = aio->mixer;

	/* Create output nodes for mixer controls, such as Headphone
	 * and Speaker, only for the first device. */
	if (direction == CRAS_STREAM_OUTPUT && is_first)
		cras_alsa_mixer_list_outputs(mixer,
				new_output_by_mixer_control, aio);
	else if (direction == CRAS_STREAM_INPUT && is_first)
		cras_alsa_mixer_list_inputs(mixer,
				new_input_by_mixer_control, aio);

	err = cras_alsa_jack_list_find_jacks_by_name_matching(aio->jack_list);
	if (err)
		return err;

	/* Create nodes for jacks that aren't associated with an
	 * already existing node. Get an initial read of the jacks for
	 * this device. */
	cras_alsa_jack_list_report(aio->jack_list);

	/* Make a default node if there is still no node for this
	 * device, or we still don't have the "Speaker"/"Internal Mic"
	 * node for the first internal device. Note that the default
	 * node creation can be supressed by UCM flags for platforms
	 * which really don't have an internal device. */
	if ((direction == CRAS_STREAM_OUTPUT) &&
			!no_create_default_output_node(aio)) {
		if (first_internal_device(aio) &&
		    !has_node(aio, INTERNAL_SPEAKER) &&
		    !has_node(aio, HDMI)) {
			if (strstr(aio->base.info.name, HDMI))
				new_output(aio, NULL, HDMI);
			else
				new_output(aio, NULL, INTERNAL_SPEAKER);
		} else if (!aio->base.nodes) {
			new_output(aio, NULL, DEFAULT);
		}
	} else if ((direction == CRAS_STREAM_INPUT) &&
			!no_create_default_input_node(aio)) {
		if (first_internal_device(aio) &&
		    !has_node(aio, INTERNAL_MICROPHONE))
			new_input(aio, NULL, INTERNAL_MICROPHONE);
		else if (strstr(dev_name, KEYBOARD_MIC))
			new_input(aio, NULL, KEYBOARD_MIC);
		else if (dev_id && strstr(dev_id, HOTWORD_DEV))
			new_input(aio, NULL, HOTWORD_DEV);
		else if (!aio->base.nodes)
			new_input(aio, NULL, DEFAULT);
	}

	/* Build software volume scalers. */
	if (direction == CRAS_STREAM_OUTPUT)
		build_softvol_scalers(aio);

	/* Set the active node as the best node we have now. */
	alsa_iodev_set_active_node(&aio->base,
				   first_plugged_node(&aio->base),
				   0);

	/* Set plugged for the first USB device per card when it appears. */
	if (aio->card_type == ALSA_CARD_TYPE_USB && is_first)
		cras_iodev_set_node_attr(iodev->active_node,
					 IONODE_ATTR_PLUGGED, 1);

	set_default_hotword_model(iodev);

	return 0;
}

int alsa_iodev_ucm_add_nodes_and_jacks(struct cras_iodev *iodev,
				       struct ucm_section *section)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	struct mixer_control *control;
	struct alsa_input_node *input_node = NULL;
	struct cras_alsa_jack *jack;
	struct alsa_output_node *output_node = NULL;
	int rc;

	if (!aio || !section)
		return -EINVAL;
	if ((uint32_t)section->dev_idx != aio->device_index)
		return -EINVAL;

	/* This iodev is fully specified. Avoid automatic node creation. */
	aio->fully_specified = 1;

	/* Check here in case the DmaPeriodMicrosecs flag has only been
	 * specified on one of many device entries with the same PCM. */
	if (!aio->dma_period_set_microsecs)
		aio->dma_period_set_microsecs =
			ucm_get_dma_period_for_dev(aio->ucm, section->name);

	/* Create a node matching this section. If there is a matching
	 * control use that, otherwise make a node without a control. */
	control = cras_alsa_mixer_get_control_for_section(aio->mixer, section);
	if (iodev->direction == CRAS_STREAM_OUTPUT) {
		output_node = new_output(aio, control, section->name);
		if (!output_node)
			return -ENOMEM;
	} else if (iodev->direction == CRAS_STREAM_INPUT) {
		input_node = new_input(aio, control, section->name);
		if (!input_node)
			return -ENOMEM;
	}

	/* Find any jack controls for this device. */
	rc = cras_alsa_jack_list_add_jack_for_section(
					aio->jack_list, section, &jack);
	if (rc)
		return rc;

	/* Associated the jack with the node. */
	if (jack) {
		if (output_node) {
			output_node->jack = jack;
			if (!output_node->volume_curve)
				output_node->volume_curve =
					create_volume_curve_for_jack(
						aio->config, jack);
		} else if (input_node) {
			input_node->jack = jack;
		}
	}
	return 0;
}

void alsa_iodev_ucm_complete_init(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;

	if (!iodev)
		return;

	/* Get an initial read of the jacks for this device. */
	cras_alsa_jack_list_report(aio->jack_list);

	/* Build software volume scaler. */
	if (iodev->direction == CRAS_STREAM_OUTPUT)
		build_softvol_scalers(aio);

	/* Set the active node as the best node we have now. */
	alsa_iodev_set_active_node(&aio->base,
				   first_plugged_node(&aio->base),
				   0);

	/* Set plugged for the first USB device per card when it appears. */
	if (aio->card_type == ALSA_CARD_TYPE_USB && aio->is_first)
		cras_iodev_set_node_attr(iodev->active_node,
					 IONODE_ATTR_PLUGGED, 1);

	set_default_hotword_model(iodev);
}

void alsa_iodev_destroy(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	int rc;

	cras_alsa_jack_list_destroy(aio->jack_list);
	if (iodev->direction == CRAS_STREAM_INPUT)
		rc = cras_iodev_list_rm_input(iodev);
	else
		rc = cras_iodev_list_rm_output(iodev);

	if (rc == -EBUSY) {
		syslog(LOG_ERR, "Failed to remove iodev %s", iodev->info.name);
		return;
	}

	/* Free resources when device successfully removed. */
	free_alsa_iodev_resources(aio);
	cras_volume_curve_destroy(aio->default_volume_curve);
	free(iodev);
}

unsigned alsa_iodev_index(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	return aio->device_index;
}

int alsa_iodev_has_hctl_jacks(struct cras_iodev *iodev)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;
	return cras_alsa_jack_list_has_hctl_jacks(aio->jack_list);
}

static void alsa_iodev_unmute_node(struct alsa_io *aio,
				   struct cras_ionode *ionode)
{
	struct alsa_output_node *active = (struct alsa_output_node *)ionode;
	struct mixer_control *mixer = active->mixer_output;
	struct alsa_output_node *output;
	struct cras_ionode *node;

	/* If this node is associated with mixer output, unmute the
	 * active mixer output and mute all others, otherwise just set
	 * the node as active and set the volume curve. */
	if (mixer) {
		set_alsa_mute_control(aio, 1);
		/* Unmute the active mixer output, mute all others. */
		DL_FOREACH(aio->base.nodes, node) {
			output = (struct alsa_output_node *)node;
			if (output->mixer_output)
				cras_alsa_mixer_set_output_active_state(
					output->mixer_output, node == ionode);
		}
	}
}

static int alsa_iodev_set_active_node(struct cras_iodev *iodev,
				      struct cras_ionode *ionode,
				      unsigned dev_enabled)
{
	struct alsa_io *aio = (struct alsa_io *)iodev;

	if (iodev->active_node == ionode) {
		enable_active_ucm(aio, dev_enabled);
		init_device_settings(aio);
		return 0;
	}

	/* Disable jack ucm before switching node. */
	enable_active_ucm(aio, 0);
	if (dev_enabled && (iodev->direction == CRAS_STREAM_OUTPUT))
		alsa_iodev_unmute_node(aio, ionode);

	cras_iodev_set_active_node(iodev, ionode);
	aio->base.dsp_name = get_active_dsp_name(aio);
	cras_iodev_update_dsp(iodev);
	enable_active_ucm(aio, dev_enabled);
	/* Setting the volume will also unmute if the system isn't muted. */
	init_device_settings(aio);
	return 0;
}