1 /* pcm.c
2 **
3 ** Copyright 2011, The Android Open Source Project
4 **
5 ** Redistribution and use in source and binary forms, with or without
6 ** modification, are permitted provided that the following conditions are met:
7 ** * Redistributions of source code must retain the above copyright
8 ** notice, this list of conditions and the following disclaimer.
9 ** * Redistributions in binary form must reproduce the above copyright
10 ** notice, this list of conditions and the following disclaimer in the
11 ** documentation and/or other materials provided with the distribution.
12 ** * Neither the name of The Android Open Source Project nor the names of
13 ** its contributors may be used to endorse or promote products derived
14 ** from this software without specific prior written permission.
15 **
16 ** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26 ** DAMAGE.
27 */
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 #include <stdarg.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <unistd.h>
37 #include <sys/ioctl.h>
38 #include <sys/mman.h>
39 #include <sys/time.h>
41 #include <linux/ioctl.h>
42 #define __force
43 #define __bitwise
44 #define __user
45 #include <sound/asound.h>
47 #include <tinyalsa/asoundlib.h>
49 #define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
51 static inline int param_is_mask(int p)
52 {
53 return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
54 (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
55 }
57 static inline int param_is_interval(int p)
58 {
59 return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
60 (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
61 }
63 static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
64 {
65 return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
66 }
68 static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
69 {
70 return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
71 }
73 static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
74 {
75 if (bit >= SNDRV_MASK_MAX)
76 return;
77 if (param_is_mask(n)) {
78 struct snd_mask *m = param_to_mask(p, n);
79 m->bits[0] = 0;
80 m->bits[1] = 0;
81 m->bits[bit >> 5] |= (1 << (bit & 31));
82 }
83 }
85 static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
86 {
87 if (param_is_interval(n)) {
88 struct snd_interval *i = param_to_interval(p, n);
89 i->min = val;
90 }
91 }
93 static void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned int val)
94 {
95 if (param_is_interval(n)) {
96 struct snd_interval *i = param_to_interval(p, n);
97 i->max = val;
98 }
99 }
101 static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
102 {
103 if (param_is_interval(n)) {
104 struct snd_interval *i = param_to_interval(p, n);
105 i->min = val;
106 i->max = val;
107 i->integer = 1;
108 }
109 }
111 static void param_init(struct snd_pcm_hw_params *p)
112 {
113 int n;
115 memset(p, 0, sizeof(*p));
116 for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
117 n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
118 struct snd_mask *m = param_to_mask(p, n);
119 m->bits[0] = ~0;
120 m->bits[1] = ~0;
121 }
122 for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
123 n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
124 struct snd_interval *i = param_to_interval(p, n);
125 i->min = 0;
126 i->max = ~0;
127 }
128 }
130 #define PCM_ERROR_MAX 128
132 struct pcm {
133 int fd;
134 unsigned int flags;
135 int running:1;
136 int underruns;
137 unsigned int buffer_size;
138 char error[PCM_ERROR_MAX];
139 struct pcm_config config;
140 struct snd_pcm_mmap_status *mmap_status;
141 struct snd_pcm_mmap_control *mmap_control;
142 struct snd_pcm_sync_ptr *sync_ptr;
143 };
145 unsigned int pcm_get_buffer_size(struct pcm *pcm)
146 {
147 return pcm->buffer_size;
148 }
150 const char* pcm_get_error(struct pcm *pcm)
151 {
152 return pcm->error;
153 }
155 static int oops(struct pcm *pcm, int e, const char *fmt, ...)
156 {
157 va_list ap;
158 int sz;
160 va_start(ap, fmt);
161 vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
162 va_end(ap);
163 sz = strlen(pcm->error);
165 if (errno)
166 snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
167 ": %s", strerror(e));
168 return -1;
169 }
171 static unsigned int pcm_format_to_alsa(enum pcm_format format)
172 {
173 switch (format) {
174 case PCM_FORMAT_S32_LE:
175 return SNDRV_PCM_FORMAT_S32_LE;
176 default:
177 case PCM_FORMAT_S16_LE:
178 return SNDRV_PCM_FORMAT_S16_LE;
179 };
180 }
182 static unsigned int pcm_format_to_bits(enum pcm_format format)
183 {
184 switch (format) {
185 case PCM_FORMAT_S32_LE:
186 return 32;
187 default:
188 case PCM_FORMAT_S16_LE:
189 return 16;
190 };
191 }
193 static int pcm_sync_ptr(struct pcm *pcm, int flags) {
194 if (pcm->sync_ptr) {
195 pcm->sync_ptr->flags = flags;
196 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
197 return -1;
198 }
199 return 0;
200 }
202 static int pcm_hw_mmap_status(struct pcm *pcm) {
204 if (pcm->sync_ptr)
205 return 0;
207 int page_size = sysconf(_SC_PAGE_SIZE);
208 pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED,
209 pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
210 if (pcm->mmap_status == MAP_FAILED)
211 pcm->mmap_status = NULL;
212 if (!pcm->mmap_status)
213 goto mmap_error;
215 pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
216 MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
217 if (pcm->mmap_control == MAP_FAILED)
218 pcm->mmap_control = NULL;
219 if (!pcm->mmap_control) {
220 munmap(pcm->mmap_status, page_size);
221 pcm->mmap_status = NULL;
222 goto mmap_error;
223 }
224 pcm->mmap_control->avail_min = 1;
226 return 0;
228 mmap_error:
230 pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr));
231 if (!pcm->sync_ptr)
232 return -ENOMEM;
233 pcm->mmap_status = &pcm->sync_ptr->s.status;
234 pcm->mmap_control = &pcm->sync_ptr->c.control;
235 pcm->mmap_control->avail_min = 1;
236 pcm_sync_ptr(pcm, 0);
238 return 0;
239 }
241 static void pcm_hw_munmap_status(struct pcm *pcm) {
242 if (pcm->sync_ptr) {
243 free(pcm->sync_ptr);
244 pcm->sync_ptr = NULL;
245 } else {
246 int page_size = sysconf(_SC_PAGE_SIZE);
247 if (pcm->mmap_status)
248 munmap(pcm->mmap_status, page_size);
249 if (pcm->mmap_control)
250 munmap(pcm->mmap_control, page_size);
251 }
252 pcm->mmap_status = NULL;
253 pcm->mmap_control = NULL;
254 }
256 int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
257 struct timespec *tstamp)
258 {
259 int frames;
260 int rc;
261 snd_pcm_uframes_t hw_ptr;
263 if (!pcm_is_ready(pcm))
264 return -1;
266 rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC);
267 if (rc < 0)
268 return -1;
270 *tstamp = pcm->mmap_status->tstamp;
271 if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
272 return -1;
274 hw_ptr = pcm->mmap_status->hw_ptr;
275 if (pcm->flags & PCM_IN)
276 frames = hw_ptr - pcm->mmap_control->appl_ptr;
277 else
278 frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
280 if (frames < 0)
281 return -1;
283 *avail = (unsigned int)frames;
285 return 0;
286 }
288 int pcm_write(struct pcm *pcm, void *data, unsigned int count)
289 {
290 struct snd_xferi x;
292 if (pcm->flags & PCM_IN)
293 return -EINVAL;
295 x.buf = data;
296 x.frames = count / (pcm->config.channels *
297 pcm_format_to_bits(pcm->config.format) / 8);
299 for (;;) {
300 if (!pcm->running) {
301 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE))
302 return oops(pcm, errno, "cannot prepare channel");
303 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
304 return oops(pcm, errno, "cannot write initial data");
305 pcm->running = 1;
306 return 0;
307 }
308 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
309 pcm->running = 0;
310 if (errno == EPIPE) {
311 /* we failed to make our window -- try to restart */
312 pcm->underruns++;
313 continue;
314 }
315 return oops(pcm, errno, "cannot write stream data");
316 }
317 return 0;
318 }
319 }
321 int pcm_read(struct pcm *pcm, void *data, unsigned int count)
322 {
323 struct snd_xferi x;
325 if (!(pcm->flags & PCM_IN))
326 return -EINVAL;
328 x.buf = data;
329 x.frames = count / (pcm->config.channels *
330 pcm_format_to_bits(pcm->config.format) / 8);
332 for (;;) {
333 if (!pcm->running) {
334 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE))
335 return oops(pcm, errno, "cannot prepare channel");
336 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START))
337 return oops(pcm, errno, "cannot start channel");
338 pcm->running = 1;
339 }
340 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
341 pcm->running = 0;
342 if (errno == EPIPE) {
343 /* we failed to make our window -- try to restart */
344 pcm->underruns++;
345 continue;
346 }
347 return oops(pcm, errno, "cannot read stream data");
348 }
349 return 0;
350 }
351 }
353 static struct pcm bad_pcm = {
354 .fd = -1,
355 };
357 int pcm_close(struct pcm *pcm)
358 {
359 if (pcm == &bad_pcm)
360 return 0;
362 pcm_hw_munmap_status(pcm);
364 if (pcm->fd >= 0)
365 close(pcm->fd);
366 pcm->running = 0;
367 pcm->buffer_size = 0;
368 pcm->fd = -1;
369 free(pcm);
370 return 0;
371 }
373 struct pcm *pcm_open(unsigned int card, unsigned int device,
374 unsigned int flags, struct pcm_config *config)
375 {
376 struct pcm *pcm;
377 struct snd_pcm_info info;
378 struct snd_pcm_hw_params params;
379 struct snd_pcm_sw_params sparams;
380 char fn[256];
381 int rc;
383 pcm = calloc(1, sizeof(struct pcm));
384 if (!pcm || !config)
385 return &bad_pcm; /* TODO: could support default config here */
387 pcm->config = *config;
389 snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
390 flags & PCM_IN ? 'c' : 'p');
392 pcm->flags = flags;
393 pcm->fd = open(fn, O_RDWR);
394 if (pcm->fd < 0) {
395 oops(pcm, errno, "cannot open device '%s'", fn);
396 return pcm;
397 }
399 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
400 oops(pcm, errno, "cannot get info");
401 goto fail;
402 }
404 param_init(¶ms);
405 param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS,
406 SNDRV_PCM_ACCESS_RW_INTERLEAVED);
407 param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT,
408 pcm_format_to_alsa(config->format));
409 param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT,
410 SNDRV_PCM_SUBFORMAT_STD);
411 param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
412 param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
413 pcm_format_to_bits(config->format));
414 param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS,
415 pcm_format_to_bits(config->format) * config->channels);
416 param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS,
417 config->channels);
418 param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
419 param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, config->rate);
421 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) {
422 oops(pcm, errno, "cannot set hw params");
423 goto fail;
424 }
426 memset(&sparams, 0, sizeof(sparams));
427 sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
428 sparams.period_step = 1;
429 sparams.avail_min = 1;
431 if (!config->start_threshold)
432 sparams.start_threshold = config->period_count * config->period_size;
433 else
434 sparams.start_threshold = config->start_threshold;
436 if (!config->stop_threshold)
437 sparams.stop_threshold = config->period_count * config->period_size;
438 else
439 sparams.stop_threshold = config->stop_threshold;
441 sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
442 sparams.silence_size = 0;
443 sparams.silence_threshold = config->silence_threshold;
446 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
447 oops(pcm, errno, "cannot set sw params");
448 goto fail;
449 }
451 rc = pcm_hw_mmap_status(pcm);
452 if (rc < 0) {
453 oops(pcm, rc, "mmap status failed");
454 goto fail;
455 }
457 pcm->buffer_size = config->period_count * config->period_size;
458 pcm->underruns = 0;
459 return pcm;
461 fail:
462 close(pcm->fd);
463 pcm->fd = -1;
464 return pcm;
465 }
467 int pcm_is_ready(struct pcm *pcm)
468 {
469 return pcm->fd >= 0;
470 }
472 int pcm_start(struct pcm *pcm)
473 {
474 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
475 return oops(pcm, errno, "cannot prepare channel");
476 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
477 return oops(pcm, errno, "cannot start channel");
479 return 0;
480 }
482 int pcm_stop(struct pcm *pcm)
483 {
484 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
485 return oops(pcm, errno, "cannot stop channel");
487 return 0;
488 }