summaryrefslogtreecommitdiff
path: root/cras/src/alsa_plugin/pcm_cras.c
diff options
context:
space:
mode:
Diffstat (limited to 'cras/src/alsa_plugin/pcm_cras.c')
-rw-r--r--cras/src/alsa_plugin/pcm_cras.c405
1 files changed, 0 insertions, 405 deletions
diff --git a/cras/src/alsa_plugin/pcm_cras.c b/cras/src/alsa_plugin/pcm_cras.c
deleted file mode 100644
index 7bc960bc..00000000
--- a/cras/src/alsa_plugin/pcm_cras.c
+++ /dev/null
@@ -1,405 +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.
- */
-
-#include <alsa/asoundlib.h>
-#include <alsa/pcm_external.h>
-#include <cras_client.h>
-#include <sys/socket.h>
-
-/* Holds configuration for the alsa plugin.
- * io - ALSA ioplug object.
- * fd - Wakes users with polled io.
- * stream_playing - Indicates if the stream is playing/capturing.
- * hw_ptr - Current read or write position.
- * channels - Number of channels.
- * stream_id - CRAS ID of the playing/capturing stream.
- * bytes_per_frame - number of bytes in an audio frame.
- * direction - input or output.
- * areas - ALSA areas used to read from/write to.
- * client - CRAS client object.
- * capture_sample_index - The sample tracked for capture latency calculation.
- * playback_sample_index - The sample tracked for playback latency calculation.
- * capture_sample_time - The time when capture_sample_index was captured.
- * playback_sample_time - The time when playback_sample_index was captured.
- */
-struct snd_pcm_cras {
- snd_pcm_ioplug_t io;
- int fd;
- int stream_playing;
- unsigned int hw_ptr;
- unsigned int channels;
- cras_stream_id_t stream_id;
- size_t bytes_per_frame;
- enum CRAS_STREAM_DIRECTION direction;
- snd_pcm_channel_area_t *areas;
- struct cras_client *client;
- int capture_sample_index;
- int playback_sample_index;
- struct timespec capture_sample_time;
- struct timespec playback_sample_time;
-};
-
-/* Frees all resources allocated during use. */
-static void snd_pcm_cras_free(struct snd_pcm_cras *pcm_cras)
-{
- if (pcm_cras == NULL)
- return;
- assert(!pcm_cras->stream_playing);
- if (pcm_cras->fd >= 0)
- close(pcm_cras->fd);
- if (pcm_cras->io.poll_fd >= 0)
- close(pcm_cras->io.poll_fd);
- cras_client_destroy(pcm_cras->client);
- free(pcm_cras->areas);
- free(pcm_cras);
-}
-
-/* Stops a playing or capturing CRAS plugin. */
-static int snd_pcm_cras_stop(snd_pcm_ioplug_t *io)
-{
- struct snd_pcm_cras *pcm_cras = io->private_data;
-
- if (pcm_cras->stream_playing) {
- cras_client_rm_stream(pcm_cras->client, pcm_cras->stream_id);
- cras_client_stop(pcm_cras->client);
- pcm_cras->stream_playing = 0;
- }
- return 0;
-}
-
-/* Close a CRAS plugin opened with snd_pcm_cras_open. */
-static int snd_pcm_cras_close(snd_pcm_ioplug_t *io)
-{
- struct snd_pcm_cras *pcm_cras = io->private_data;
-
- if (pcm_cras->stream_playing)
- snd_pcm_cras_stop(io);
- snd_pcm_cras_free(pcm_cras);
- return 0;
-}
-
-/* Poll callback used to wait for data ready (playback) or space available
- * (capture). */
-static int snd_pcm_cras_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfds,
- unsigned int nfds, unsigned short *revents)
-{
- static char buf[1];
- int rc;
-
- if (pfds == NULL || nfds != 1 || revents == NULL)
- return -EINVAL;
- rc = read(pfds[0].fd, buf, 1);
- if (rc < 0 && errno != EWOULDBLOCK && errno != EAGAIN) {
- fprintf(stderr, "%s read failed %d\n", __func__, errno);
- return errno;
- }
- *revents = pfds[0].revents & ~(POLLIN | POLLOUT);
- if (pfds[0].revents & POLLIN)
- *revents |= (io->stream == SND_PCM_STREAM_PLAYBACK) ? POLLOUT :
- POLLIN;
- return 0;
-}
-
-/* Callback to return the location of the write (playback) or read (capture)
- * pointer. */
-static snd_pcm_sframes_t snd_pcm_cras_pointer(snd_pcm_ioplug_t *io)
-{
- struct snd_pcm_cras *pcm_cras = io->private_data;
- return pcm_cras->hw_ptr;
-}
-
-/* Main callback for processing audio. This is called by CRAS when more samples
- * are needed (playback) or ready (capture). Copies bytes between ALSA and CRAS
- * buffers. */
-static int pcm_cras_process_cb(struct cras_client *client,
- cras_stream_id_t stream_id,
- uint8_t *capture_samples,
- uint8_t *playback_samples, unsigned int nframes,
- const struct timespec *capture_ts,
- const struct timespec *playback_ts, void *arg)
-{
- snd_pcm_ioplug_t *io;
- struct snd_pcm_cras *pcm_cras;
- const snd_pcm_channel_area_t *areas;
- snd_pcm_uframes_t copied_frames;
- char empty_byte;
- size_t chan, frame_bytes, sample_bytes;
- int rc;
- uint8_t *samples;
- const struct timespec *sample_time;
-
- samples = capture_samples ?: playback_samples;
- sample_time = capture_ts ?: playback_ts;
-
- io = (snd_pcm_ioplug_t *)arg;
- pcm_cras = (struct snd_pcm_cras *)io->private_data;
- frame_bytes = pcm_cras->bytes_per_frame;
- sample_bytes = snd_pcm_format_physical_width(io->format) / 8;
-
- if (io->stream == SND_PCM_STREAM_PLAYBACK) {
- if (io->state != SND_PCM_STATE_RUNNING &&
- io->state != SND_PCM_STATE_DRAINING) {
- memset(samples, 0, nframes * frame_bytes);
- return nframes;
- }
- /* Only take one period of data at a time. */
- if (nframes > io->period_size)
- nframes = io->period_size;
-
- /* Keep track of the first transmitted sample index and the time
- * it will be played. */
- pcm_cras->playback_sample_index = io->hw_ptr;
- pcm_cras->playback_sample_time = *sample_time;
- } else {
- /* Keep track of the first read sample index and the time it
- * was captured. */
- pcm_cras->capture_sample_index = io->hw_ptr;
- pcm_cras->capture_sample_time = *sample_time;
- }
-
- /* CRAS always takes interleaved samples. */
- for (chan = 0; chan < io->channels; chan++) {
- pcm_cras->areas[chan].addr = samples + chan * sample_bytes;
- pcm_cras->areas[chan].first = 0;
- pcm_cras->areas[chan].step =
- snd_pcm_format_physical_width(io->format) *
- io->channels;
- }
-
- areas = snd_pcm_ioplug_mmap_areas(io);
-
- copied_frames = 0;
- while (copied_frames < nframes) {
- snd_pcm_uframes_t frames = nframes - copied_frames;
- snd_pcm_uframes_t remain = io->buffer_size - pcm_cras->hw_ptr;
-
- if (frames > remain)
- frames = remain;
-
- for (chan = 0; chan < io->channels; chan++)
- if (io->stream == SND_PCM_STREAM_PLAYBACK)
- snd_pcm_area_copy(&pcm_cras->areas[chan],
- copied_frames, &areas[chan],
- pcm_cras->hw_ptr, frames,
- io->format);
- else
- snd_pcm_area_copy(&areas[chan],
- pcm_cras->hw_ptr,
- &pcm_cras->areas[chan],
- copied_frames, frames,
- io->format);
-
- pcm_cras->hw_ptr += frames;
- pcm_cras->hw_ptr %= io->buffer_size;
- copied_frames += frames;
- }
-
- rc = write(pcm_cras->fd, &empty_byte, 1); /* Wake up polling clients. */
- if (rc < 0 && errno != EWOULDBLOCK && errno != EAGAIN)
- fprintf(stderr, "%s write failed %d\n", __func__, errno);
-
- return nframes;
-}
-
-/* Callback from CRAS for stream errors. */
-static int pcm_cras_error_cb(struct cras_client *client,
- cras_stream_id_t stream_id, int err, void *arg)
-{
- fprintf(stderr, "Stream error %d\n", err);
- return 0;
-}
-
-/* ALSA calls this automatically when the stream enters the
- * SND_PCM_STATE_PREPARED state. */
-static int snd_pcm_cras_prepare(snd_pcm_ioplug_t *io)
-{
- struct snd_pcm_cras *pcm_cras = io->private_data;
-
- return cras_client_connect(pcm_cras->client);
-}
-
-/* Called when an ALSA stream is started. */
-static int snd_pcm_cras_start(snd_pcm_ioplug_t *io)
-{
- struct snd_pcm_cras *pcm_cras = io->private_data;
- struct cras_stream_params *params;
- struct cras_audio_format *audio_format;
- int rc;
-
- audio_format =
- cras_audio_format_create(io->format, io->rate, io->channels);
- if (audio_format == NULL)
- return -ENOMEM;
-
- params = cras_client_unified_params_create(
- pcm_cras->direction, io->period_size, 0, 0, io,
- pcm_cras_process_cb, pcm_cras_error_cb, audio_format);
- if (params == NULL) {
- rc = -ENOMEM;
- goto error_out;
- }
-
- cras_client_stream_params_set_client_type(params, CRAS_CLIENT_TYPE_PCM);
-
- rc = cras_client_run_thread(pcm_cras->client);
- if (rc < 0)
- goto error_out;
-
- pcm_cras->bytes_per_frame =
- cras_client_format_bytes_per_frame(audio_format);
-
- rc = cras_client_add_stream(pcm_cras->client, &pcm_cras->stream_id,
- params);
- if (rc < 0) {
- fprintf(stderr, "CRAS add failed\n");
- goto error_out;
- }
- pcm_cras->stream_playing = 1;
-
-error_out:
- cras_audio_format_destroy(audio_format);
- cras_client_stream_params_destroy(params);
- return rc;
-}
-
-static snd_pcm_ioplug_callback_t cras_pcm_callback = {
- .close = snd_pcm_cras_close,
- .start = snd_pcm_cras_start,
- .stop = snd_pcm_cras_stop,
- .pointer = snd_pcm_cras_pointer,
- .prepare = snd_pcm_cras_prepare,
- .poll_revents = snd_pcm_cras_poll_revents,
-};
-
-/* Set constraints for hw_params. This lists the handled formats, sample rates,
- * access patters, and buffer/period sizes. These are enforce in
- * snd_pcm_set_params(). */
-static int set_hw_constraints(struct snd_pcm_cras *pcm_cras)
-{
- // clang-format off
- static const unsigned int access_list[] = {
- SND_PCM_ACCESS_MMAP_INTERLEAVED,
- SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
- SND_PCM_ACCESS_RW_INTERLEAVED,
- SND_PCM_ACCESS_RW_NONINTERLEAVED
- };
- static const unsigned int format_list[] = {
- SND_PCM_FORMAT_U8,
- SND_PCM_FORMAT_S16_LE,
- SND_PCM_FORMAT_S24_LE,
- SND_PCM_FORMAT_S32_LE,
- SND_PCM_FORMAT_S24_3LE,
- };
- // clang-format on
- int rc;
-
- rc = snd_pcm_ioplug_set_param_list(&pcm_cras->io,
- SND_PCM_IOPLUG_HW_ACCESS,
- ARRAY_SIZE(access_list),
- access_list);
- if (rc < 0)
- return rc;
- rc = snd_pcm_ioplug_set_param_list(&pcm_cras->io,
- SND_PCM_IOPLUG_HW_FORMAT,
- ARRAY_SIZE(format_list),
- format_list);
- if (rc < 0)
- return rc;
- rc = snd_pcm_ioplug_set_param_minmax(&pcm_cras->io,
- SND_PCM_IOPLUG_HW_CHANNELS, 1,
- pcm_cras->channels);
- if (rc < 0)
- return rc;
- rc = snd_pcm_ioplug_set_param_minmax(
- &pcm_cras->io, SND_PCM_IOPLUG_HW_RATE, 8000, 48000);
- if (rc < 0)
- return rc;
- rc = snd_pcm_ioplug_set_param_minmax(&pcm_cras->io,
- SND_PCM_IOPLUG_HW_BUFFER_BYTES, 64,
- 2 * 1024 * 1024);
- if (rc < 0)
- return rc;
- rc = snd_pcm_ioplug_set_param_minmax(&pcm_cras->io,
- SND_PCM_IOPLUG_HW_PERIOD_BYTES, 64,
- 2 * 1024 * 1024);
- if (rc < 0)
- return rc;
- rc = snd_pcm_ioplug_set_param_minmax(
- &pcm_cras->io, SND_PCM_IOPLUG_HW_PERIODS, 1, 2048);
- return rc;
-}
-
-/* Called by snd_pcm_open(). Creates a CRAS client and an ioplug plugin. */
-static int snd_pcm_cras_open(snd_pcm_t **pcmp, const char *name,
- snd_pcm_stream_t stream, int mode)
-{
- struct snd_pcm_cras *pcm_cras;
- int rc;
- int fd[2];
-
- assert(pcmp);
- pcm_cras = calloc(1, sizeof(*pcm_cras));
- if (!pcm_cras)
- return -ENOMEM;
-
- pcm_cras->fd = -1;
- pcm_cras->io.poll_fd = -1;
- pcm_cras->channels = 2;
- pcm_cras->direction = (stream == SND_PCM_STREAM_PLAYBACK) ?
- CRAS_STREAM_OUTPUT :
- CRAS_STREAM_INPUT;
-
- rc = cras_client_create(&pcm_cras->client);
- if (rc != 0 || pcm_cras->client == NULL) {
- fprintf(stderr, "Couldn't create CRAS client\n");
- free(pcm_cras);
- return rc;
- }
-
- pcm_cras->areas =
- calloc(pcm_cras->channels, sizeof(snd_pcm_channel_area_t));
- if (pcm_cras->areas == NULL) {
- snd_pcm_cras_free(pcm_cras);
- return -ENOMEM;
- }
-
- socketpair(AF_LOCAL, SOCK_STREAM, 0, fd);
-
- cras_make_fd_nonblocking(fd[0]);
- cras_make_fd_nonblocking(fd[1]);
-
- pcm_cras->fd = fd[0];
-
- pcm_cras->io.version = SND_PCM_IOPLUG_VERSION;
- pcm_cras->io.name = "ALSA to CRAS Plugin";
- pcm_cras->io.callback = &cras_pcm_callback;
- pcm_cras->io.private_data = pcm_cras;
- pcm_cras->io.poll_fd = fd[1];
- pcm_cras->io.poll_events = POLLIN;
- pcm_cras->io.mmap_rw = 1;
-
- rc = snd_pcm_ioplug_create(&pcm_cras->io, name, stream, mode);
- if (rc < 0) {
- snd_pcm_cras_free(pcm_cras);
- return rc;
- }
-
- rc = set_hw_constraints(pcm_cras);
- if (rc < 0) {
- snd_pcm_ioplug_delete(&pcm_cras->io);
- return rc;
- }
-
- *pcmp = pcm_cras->io.pcm;
-
- return 0;
-}
-
-SND_PCM_PLUGIN_DEFINE_FUNC(cras)
-{
- return snd_pcm_cras_open(pcmp, name, stream, mode);
-}
-
-SND_PCM_PLUGIN_SYMBOL(cras);