Home | History | Annotate | Download | only in alsa_plugin
      1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  */
      5 
      6 #include <alsa/asoundlib.h>
      7 #include <alsa/pcm_external.h>
      8 #include <cras_client.h>
      9 #include <sys/socket.h>
     10 
     11 /* Holds configuration for the alsa plugin.
     12  *  io - ALSA ioplug object.
     13  *  fd - Wakes users with polled io.
     14  *  stream_playing - Indicates if the stream is playing/capturing.
     15  *  hw_ptr - Current read or write position.
     16  *  channels - Number of channels.
     17  *  stream_id - CRAS ID of the playing/capturing stream.
     18  *  bytes_per_frame - number of bytes in an audio frame.
     19  *  direction - input or output.
     20  *  areas - ALSA areas used to read from/write to.
     21  *  client - CRAS client object.
     22  *  capture_sample_index - The sample tracked for capture latency calculation.
     23  *  playback_sample_index - The sample tracked for playback latency calculation.
     24  *  capture_sample_time - The time when capture_sample_index was captured.
     25  *  playback_sample_time - The time when playback_sample_index was captured.
     26  */
     27 struct snd_pcm_cras {
     28 	snd_pcm_ioplug_t io;
     29 	int fd;
     30 	int stream_playing;
     31 	unsigned int hw_ptr;
     32 	unsigned int channels;
     33 	cras_stream_id_t stream_id;
     34 	size_t bytes_per_frame;
     35 	enum CRAS_STREAM_DIRECTION direction;
     36 	snd_pcm_channel_area_t *areas;
     37 	struct cras_client *client;
     38 	int capture_sample_index;
     39 	int playback_sample_index;
     40 	struct timespec capture_sample_time;
     41 	struct timespec playback_sample_time;
     42 };
     43 
     44 /* Frees all resources allocated during use. */
     45 static void snd_pcm_cras_free(struct snd_pcm_cras *pcm_cras)
     46 {
     47 	if (pcm_cras == NULL)
     48 		return;
     49 	assert(!pcm_cras->stream_playing);
     50 	if (pcm_cras->fd >= 0)
     51 		close(pcm_cras->fd);
     52 	if (pcm_cras->io.poll_fd >= 0)
     53 		close(pcm_cras->io.poll_fd);
     54 	cras_client_destroy(pcm_cras->client);
     55 	free(pcm_cras->areas);
     56 	free(pcm_cras);
     57 }
     58 
     59 /* Stops a playing or capturing CRAS plugin. */
     60 static int snd_pcm_cras_stop(snd_pcm_ioplug_t *io)
     61 {
     62 	struct snd_pcm_cras *pcm_cras = io->private_data;
     63 
     64 	if (pcm_cras->stream_playing) {
     65 		cras_client_rm_stream(pcm_cras->client, pcm_cras->stream_id);
     66 		cras_client_stop(pcm_cras->client);
     67 		pcm_cras->stream_playing = 0;
     68 	}
     69 	return 0;
     70 }
     71 
     72 /* Close a CRAS plugin opened with snd_pcm_cras_open. */
     73 static int snd_pcm_cras_close(snd_pcm_ioplug_t *io)
     74 {
     75 	struct snd_pcm_cras *pcm_cras = io->private_data;
     76 
     77 	if (pcm_cras->stream_playing)
     78 		snd_pcm_cras_stop(io);
     79 	snd_pcm_cras_free(pcm_cras);
     80 	return 0;
     81 }
     82 
     83 /* Poll callback used to wait for data ready (playback) or space available
     84  * (capture). */
     85 static int snd_pcm_cras_poll_revents(snd_pcm_ioplug_t *io,
     86 				     struct pollfd *pfds,
     87 				     unsigned int nfds,
     88 				     unsigned short *revents)
     89 {
     90 	static char buf[1];
     91 	int rc;
     92 
     93 	if (pfds == NULL || nfds != 1 || revents == NULL)
     94 		return -EINVAL;
     95 	rc = read(pfds[0].fd, buf, 1);
     96 	if (rc < 0 && errno != EWOULDBLOCK && errno != EAGAIN) {
     97 		fprintf(stderr, "%s read failed %d\n", __func__, errno);
     98 		return errno;
     99 	}
    100 	*revents = pfds[0].revents & ~(POLLIN | POLLOUT);
    101 	if (pfds[0].revents & POLLIN)
    102 		*revents |= (io->stream == SND_PCM_STREAM_PLAYBACK) ? POLLOUT
    103 								    : POLLIN;
    104 	return 0;
    105 }
    106 
    107 /* Callback to return the location of the write (playback) or read (capture)
    108  * pointer. */
    109 static snd_pcm_sframes_t snd_pcm_cras_pointer(snd_pcm_ioplug_t *io)
    110 {
    111 	struct snd_pcm_cras *pcm_cras = io->private_data;
    112 	return pcm_cras->hw_ptr;
    113 }
    114 
    115 /* Main callback for processing audio.  This is called by CRAS when more samples
    116  * are needed (playback) or ready (capture).  Copies bytes between ALSA and CRAS
    117  * buffers. */
    118 static int pcm_cras_process_cb(struct cras_client *client,
    119 			       cras_stream_id_t stream_id,
    120 			       uint8_t *capture_samples,
    121 			       uint8_t *playback_samples,
    122 			       unsigned int nframes,
    123 			       const struct timespec *capture_ts,
    124 			       const struct timespec *playback_ts,
    125 			       void *arg)
    126 {
    127 	snd_pcm_ioplug_t *io;
    128 	struct snd_pcm_cras *pcm_cras;
    129 	const snd_pcm_channel_area_t *areas;
    130 	snd_pcm_uframes_t copied_frames;
    131 	char dummy_byte;
    132 	size_t chan, frame_bytes, sample_bytes;
    133 	int rc;
    134 	uint8_t *samples;
    135 	const struct timespec *sample_time;
    136 
    137 	samples = capture_samples ? : playback_samples;
    138 	sample_time = capture_ts ? : playback_ts;
    139 
    140 	io = (snd_pcm_ioplug_t *)arg;
    141 	pcm_cras = (struct snd_pcm_cras *)io->private_data;
    142 	frame_bytes = pcm_cras->bytes_per_frame;
    143 	sample_bytes = snd_pcm_format_physical_width(io->format) / 8;
    144 
    145 	if (io->stream == SND_PCM_STREAM_PLAYBACK) {
    146 		if (io->state != SND_PCM_STATE_RUNNING &&
    147 		    io->state != SND_PCM_STATE_DRAINING) {
    148 			memset(samples, 0, nframes * frame_bytes);
    149 			return nframes;
    150 		}
    151 		/* Only take one period of data at a time. */
    152 		if (nframes > io->period_size)
    153 			nframes = io->period_size;
    154 
    155 		/* Keep track of the first transmitted sample index and the time
    156 		 * it will be played. */
    157 		pcm_cras->playback_sample_index = io->hw_ptr;
    158 		pcm_cras->playback_sample_time = *sample_time;
    159 	} else {
    160 		/* Keep track of the first read sample index and the time it
    161 		 * was captured. */
    162 		pcm_cras->capture_sample_index = io->hw_ptr;
    163 		pcm_cras->capture_sample_time = *sample_time;
    164 	}
    165 
    166 	/* CRAS always takes interleaved samples. */
    167 	for (chan = 0; chan < io->channels; chan++) {
    168 		pcm_cras->areas[chan].addr = samples + chan * sample_bytes;
    169 		pcm_cras->areas[chan].first = 0;
    170 		pcm_cras->areas[chan].step =
    171 			snd_pcm_format_physical_width(io->format) *
    172 			io->channels;
    173 	}
    174 
    175 	areas = snd_pcm_ioplug_mmap_areas(io);
    176 
    177 	copied_frames = 0;
    178 	while (copied_frames < nframes) {
    179 		snd_pcm_uframes_t frames = nframes - copied_frames;
    180 		snd_pcm_uframes_t remain = io->buffer_size - pcm_cras->hw_ptr;
    181 
    182 		if (frames > remain)
    183 			frames = remain;
    184 
    185 		for (chan = 0; chan < io->channels; chan++)
    186 			if (io->stream == SND_PCM_STREAM_PLAYBACK)
    187 				snd_pcm_area_copy(&pcm_cras->areas[chan],
    188 						  copied_frames,
    189 						  &areas[chan],
    190 						  pcm_cras->hw_ptr,
    191 						  frames,
    192 						  io->format);
    193 			else
    194 				snd_pcm_area_copy(&areas[chan],
    195 						  pcm_cras->hw_ptr,
    196 						  &pcm_cras->areas[chan],
    197 						  copied_frames,
    198 						  frames,
    199 						  io->format);
    200 
    201 		pcm_cras->hw_ptr += frames;
    202 		pcm_cras->hw_ptr %= io->buffer_size;
    203 		copied_frames += frames;
    204 	}
    205 
    206 	rc = write(pcm_cras->fd, &dummy_byte, 1); /* Wake up polling clients. */
    207 	if (rc < 0 && errno != EWOULDBLOCK && errno != EAGAIN)
    208 		fprintf(stderr, "%s write failed %d\n", __func__, errno);
    209 
    210 	return nframes;
    211 }
    212 
    213 /* Callback from CRAS for stream errors. */
    214 static int pcm_cras_error_cb(struct cras_client *client,
    215 			     cras_stream_id_t stream_id,
    216 			     int err,
    217 			     void *arg)
    218 {
    219 	fprintf(stderr, "Stream error %d\n", err);
    220 	return 0;
    221 }
    222 
    223 /* ALSA calls this automatically when the stream enters the
    224  * SND_PCM_STATE_PREPARED state. */
    225 static int snd_pcm_cras_prepare(snd_pcm_ioplug_t *io)
    226 {
    227 	struct snd_pcm_cras *pcm_cras = io->private_data;
    228 
    229 	return cras_client_connect(pcm_cras->client);
    230 }
    231 
    232 /* Called when an ALSA stream is started. */
    233 static int snd_pcm_cras_start(snd_pcm_ioplug_t *io)
    234 {
    235 	struct snd_pcm_cras *pcm_cras = io->private_data;
    236 	struct cras_stream_params *params;
    237 	struct cras_audio_format *audio_format;
    238 	int rc;
    239 
    240 	audio_format = cras_audio_format_create(io->format, io->rate,
    241 						io->channels);
    242 	if (audio_format == NULL)
    243 		return -ENOMEM;
    244 
    245 	params = cras_client_unified_params_create(
    246 			pcm_cras->direction,
    247 			io->period_size,
    248 			0,
    249 			0,
    250 			io,
    251 			pcm_cras_process_cb,
    252 			pcm_cras_error_cb,
    253 			audio_format);
    254 	if (params == NULL) {
    255 		rc = -ENOMEM;
    256 		goto error_out;
    257 	}
    258 
    259 	rc = cras_client_run_thread(pcm_cras->client);
    260 	if (rc < 0)
    261 		goto error_out;
    262 
    263 	pcm_cras->bytes_per_frame =
    264 		cras_client_format_bytes_per_frame(audio_format);
    265 
    266 	rc = cras_client_add_stream(pcm_cras->client,
    267 				    &pcm_cras->stream_id,
    268 				    params);
    269 	if (rc < 0) {
    270 		fprintf(stderr, "CRAS add failed\n");
    271 		goto error_out;
    272 	}
    273 	pcm_cras->stream_playing = 1;
    274 
    275 error_out:
    276 	cras_audio_format_destroy(audio_format);
    277 	cras_client_stream_params_destroy(params);
    278 	return rc;
    279 }
    280 
    281 static snd_pcm_ioplug_callback_t cras_pcm_callback = {
    282 	.close = snd_pcm_cras_close,
    283 	.start = snd_pcm_cras_start,
    284 	.stop = snd_pcm_cras_stop,
    285 	.pointer = snd_pcm_cras_pointer,
    286 	.prepare = snd_pcm_cras_prepare,
    287 	.poll_revents = snd_pcm_cras_poll_revents,
    288 };
    289 
    290 /* Set constraints for hw_params.  This lists the handled formats, sample rates,
    291  * access patters, and buffer/period sizes.  These are enforce in
    292  * snd_pcm_set_params(). */
    293 static int set_hw_constraints(struct snd_pcm_cras *pcm_cras)
    294 {
    295 	static const unsigned int access_list[] = {
    296 		SND_PCM_ACCESS_MMAP_INTERLEAVED,
    297 		SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
    298 		SND_PCM_ACCESS_RW_INTERLEAVED,
    299 		SND_PCM_ACCESS_RW_NONINTERLEAVED
    300 	};
    301 	static const unsigned int format_list[] = {
    302 		SND_PCM_FORMAT_U8,
    303 		SND_PCM_FORMAT_S16_LE,
    304 		SND_PCM_FORMAT_S24_LE,
    305 		SND_PCM_FORMAT_S32_LE,
    306 		SND_PCM_FORMAT_S24_3LE,
    307 	};
    308 	int rc;
    309 
    310 	rc = snd_pcm_ioplug_set_param_list(&pcm_cras->io,
    311 					   SND_PCM_IOPLUG_HW_ACCESS,
    312 					   ARRAY_SIZE(access_list),
    313 					   access_list);
    314 	if (rc < 0)
    315 		return rc;
    316 	rc = snd_pcm_ioplug_set_param_list(&pcm_cras->io,
    317 					   SND_PCM_IOPLUG_HW_FORMAT,
    318 					   ARRAY_SIZE(format_list),
    319 					   format_list);
    320 	if (rc < 0)
    321 		return rc;
    322 	rc = snd_pcm_ioplug_set_param_minmax(&pcm_cras->io,
    323 					     SND_PCM_IOPLUG_HW_CHANNELS,
    324 					     1,
    325 					     pcm_cras->channels);
    326 	if (rc < 0)
    327 		return rc;
    328 	rc = snd_pcm_ioplug_set_param_minmax(&pcm_cras->io,
    329 					    SND_PCM_IOPLUG_HW_RATE,
    330 					    8000,
    331 					    48000);
    332 	if (rc < 0)
    333 		return rc;
    334 	rc = snd_pcm_ioplug_set_param_minmax(&pcm_cras->io,
    335 					     SND_PCM_IOPLUG_HW_BUFFER_BYTES,
    336 					     64,
    337 					     2 * 1024 * 1024);
    338 	if (rc < 0)
    339 		return rc;
    340 	rc = snd_pcm_ioplug_set_param_minmax(&pcm_cras->io,
    341 					     SND_PCM_IOPLUG_HW_PERIOD_BYTES,
    342 					     64,
    343 					     2 * 1024 * 1024);
    344 	if (rc < 0)
    345 		return rc;
    346 	rc = snd_pcm_ioplug_set_param_minmax(&pcm_cras->io,
    347 					     SND_PCM_IOPLUG_HW_PERIODS,
    348 					     1,
    349 					     2048);
    350 	return rc;
    351 }
    352 
    353 /* Called by snd_pcm_open().  Creates a CRAS client and an ioplug plugin. */
    354 static int snd_pcm_cras_open(snd_pcm_t **pcmp, const char *name,
    355 			     snd_pcm_stream_t stream, int mode)
    356 {
    357 	struct snd_pcm_cras *pcm_cras;
    358 	int rc;
    359 	int fd[2];
    360 
    361 	assert(pcmp);
    362 	pcm_cras = calloc(1, sizeof(*pcm_cras));
    363 	if (!pcm_cras)
    364 		return -ENOMEM;
    365 
    366 	pcm_cras->fd = -1;
    367 	pcm_cras->io.poll_fd = -1;
    368 	pcm_cras->channels = 2;
    369 	pcm_cras->direction = (stream == SND_PCM_STREAM_PLAYBACK)
    370 				? CRAS_STREAM_OUTPUT : CRAS_STREAM_INPUT;
    371 
    372 	rc = cras_client_create(&pcm_cras->client);
    373 	if (rc != 0 || pcm_cras->client == NULL) {
    374 		fprintf(stderr, "Couldn't create CRAS client\n");
    375 		free(pcm_cras);
    376 		return rc;
    377 	}
    378 
    379 	pcm_cras->areas = calloc(pcm_cras->channels,
    380 				 sizeof(snd_pcm_channel_area_t));
    381 	if (pcm_cras->areas == NULL) {
    382 		snd_pcm_cras_free(pcm_cras);
    383 		return -ENOMEM;
    384 	}
    385 
    386 	socketpair(AF_LOCAL, SOCK_STREAM, 0, fd);
    387 
    388 	cras_make_fd_nonblocking(fd[0]);
    389 	cras_make_fd_nonblocking(fd[1]);
    390 
    391 	pcm_cras->fd = fd[0];
    392 
    393 	pcm_cras->io.version = SND_PCM_IOPLUG_VERSION;
    394 	pcm_cras->io.name = "ALSA to CRAS Plugin";
    395 	pcm_cras->io.callback = &cras_pcm_callback;
    396 	pcm_cras->io.private_data = pcm_cras;
    397 	pcm_cras->io.poll_fd = fd[1];
    398 	pcm_cras->io.poll_events = POLLIN;
    399 	pcm_cras->io.mmap_rw = 1;
    400 
    401 	rc = snd_pcm_ioplug_create(&pcm_cras->io, name, stream, mode);
    402 	if (rc < 0) {
    403 		snd_pcm_cras_free(pcm_cras);
    404 		return rc;
    405 	}
    406 
    407 	rc = set_hw_constraints(pcm_cras);
    408 	if (rc < 0) {
    409 		snd_pcm_ioplug_delete(&pcm_cras->io);
    410 		return rc;
    411 	}
    412 
    413 	*pcmp = pcm_cras->io.pcm;
    414 
    415 	return 0;
    416 }
    417 
    418 
    419 SND_PCM_PLUGIN_DEFINE_FUNC(cras)
    420 {
    421 	return snd_pcm_cras_open(pcmp, name, stream, mode);
    422 }
    423 
    424 SND_PCM_PLUGIN_SYMBOL(cras);
    425