• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. */
snd_pcm_cras_free(struct snd_pcm_cras * pcm_cras)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. */
snd_pcm_cras_stop(snd_pcm_ioplug_t * io)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. */
snd_pcm_cras_close(snd_pcm_ioplug_t * io)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). */
snd_pcm_cras_poll_revents(snd_pcm_ioplug_t * io,struct pollfd * pfds,unsigned int nfds,unsigned short * revents)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. */
snd_pcm_cras_pointer(snd_pcm_ioplug_t * io)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. */
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)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. */
pcm_cras_error_cb(struct cras_client * client,cras_stream_id_t stream_id,int err,void * arg)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. */
snd_pcm_cras_prepare(snd_pcm_ioplug_t * io)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. */
snd_pcm_cras_start(snd_pcm_ioplug_t * io)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(). */
set_hw_constraints(struct snd_pcm_cras * pcm_cras)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. */
snd_pcm_cras_open(snd_pcm_t ** pcmp,const char * name,snd_pcm_stream_t stream,int mode)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 
SND_PCM_PLUGIN_DEFINE_FUNC(cras)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