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