summaryrefslogtreecommitdiff
path: root/firmware/target/hosted/android/dx50/tinyalsa/pcm.c
diff options
context:
space:
mode:
authorUdo Schläpfer <rockbox-2014.10@desktopwarrior.net>2015-02-02 21:44:29 +0100
committerUdo Schläpfer <rockbox-2014.10@desktopwarrior.net>2015-02-02 21:57:55 +0100
commitdbabd0d9c34a33bc0c51243ec37f230d117db955 (patch)
tree46de348929ce739702a230a2587fdb5539585753 /firmware/target/hosted/android/dx50/tinyalsa/pcm.c
parentcef17e3d59ad93f766e8ee23b1610540a33dfe5e (diff)
downloadrockbox-dbabd0d9c34a33bc0c51243ec37f230d117db955.tar.gz
rockbox-dbabd0d9c34a33bc0c51243ec37f230d117db955.zip
iBasso DX50/DX90: Major code cleanup and reorganization.
Reorganization - Separated iBasso devices from PLATFORM_ANDROID. These are now standlone hosted targets. Most device specific code is in the firmware/target/hosted/ibasso directory. - No dependency on Android SDK, only the Android NDK is needed. 32 bit Android NDK and Android API Level 16. - Separate implementation for each device where feasible. Code cleanup - Rewrite of existing code, from simple reformat to complete reimplementation. - New backlight interface, seperating backlight from touchscreen. - Rewrite of device button handler, removing unneeded code and fixing memory leaks. - New Debug messages interface logging to Android adb logcat (DEBUGF, panicf, logf). - Rewrite of lcd device handler, removing unneeded code and fixing memory leaks. - Rewrite of audiohw device handler/pcm interface, removing unneeded code and fixing memory leaks, enabling 44.1/48kHz pthreaded playback. - Rewrite of power and powermng, proper shutdown, using batterylog results (see http://gerrit.rockbox.org/r/#/c/1047/). - Rewrite of configure (Android NDK) and device specific config. - Rewrite of the Android NDK specific Makefile. Misc - All plugins/games/demos activated. - Update tinyalsa to latest from https://github.com/tinyalsa/tinyalsa. Includes - http://gerrit.rockbox.org/r/#/c/993/ - http://gerrit.rockbox.org/r/#/c/1010/ - http://gerrit.rockbox.org/r/#/c/1035/ Does not include http://gerrit.rockbox.org/r/#/c/1007/ due to new backlight interface and new option for hold switch, touchscreen, physical button interaction. Rockbox needs the iBasso DX50/DX90 loader for startup, see http://gerrit.rockbox.org/r/#/c/1099/ The loader expects Rockbox to be installed in /mnt/sdcard/.rockbox/. If /mnt/sdcard/ is accessed as USB mass storage device, Rockbox will exit gracefully and the loader will restart Rockbox on USB disconnect. Tested on iBasso DX50. Compiled (not tested) for iBasso DX90. Compiled (not tested) for PLATFORM_ANDROID. Change-Id: I5f5e22e68f5b4cf29c28e2b40b2c265f2beb7ab7
Diffstat (limited to 'firmware/target/hosted/android/dx50/tinyalsa/pcm.c')
-rw-r--r--firmware/target/hosted/android/dx50/tinyalsa/pcm.c973
1 files changed, 0 insertions, 973 deletions
diff --git a/firmware/target/hosted/android/dx50/tinyalsa/pcm.c b/firmware/target/hosted/android/dx50/tinyalsa/pcm.c
deleted file mode 100644
index bd44dce52f..0000000000
--- a/firmware/target/hosted/android/dx50/tinyalsa/pcm.c
+++ /dev/null
@@ -1,973 +0,0 @@
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*/
28
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>
36#include <poll.h>
37
38#include <sys/ioctl.h>
39#include <sys/mman.h>
40#include <sys/time.h>
41#include <limits.h>
42
43#include <linux/ioctl.h>
44#define __force
45#define __bitwise
46#define __user
47#include <tinyalsa/asound.h>
48
49#include <tinyalsa/asoundlib.h>
50
51#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
52#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2)
53
54static inline int param_is_mask(int p)
55{
56 return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
57 (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
58}
59
60static inline int param_is_interval(int p)
61{
62 return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
63 (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
64}
65
66static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
67{
68 return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
69}
70
71static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
72{
73 return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
74}
75
76static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
77{
78 if (bit >= SNDRV_MASK_MAX)
79 return;
80 if (param_is_mask(n)) {
81 struct snd_mask *m = param_to_mask(p, n);
82 m->bits[0] = 0;
83 m->bits[1] = 0;
84 m->bits[bit >> 5] |= (1 << (bit & 31));
85 }
86}
87
88static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
89{
90 if (param_is_interval(n)) {
91 struct snd_interval *i = param_to_interval(p, n);
92 i->min = val;
93 }
94}
95
96static unsigned int param_get_min(struct snd_pcm_hw_params *p, int n)
97{
98 if (param_is_interval(n)) {
99 struct snd_interval *i = param_to_interval(p, n);
100 return i->min;
101 }
102 return 0;
103}
104
105static unsigned int param_get_max(struct snd_pcm_hw_params *p, int n)
106{
107 if (param_is_interval(n)) {
108 struct snd_interval *i = param_to_interval(p, n);
109 return i->max;
110 }
111 return 0;
112}
113
114static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
115{
116 if (param_is_interval(n)) {
117 struct snd_interval *i = param_to_interval(p, n);
118 i->min = val;
119 i->max = val;
120 i->integer = 1;
121 }
122}
123
124static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n)
125{
126 if (param_is_interval(n)) {
127 struct snd_interval *i = param_to_interval(p, n);
128 if (i->integer)
129 return i->max;
130 }
131 return 0;
132}
133
134static void param_init(struct snd_pcm_hw_params *p)
135{
136 int n;
137
138 memset(p, 0, sizeof(*p));
139 for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
140 n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
141 struct snd_mask *m = param_to_mask(p, n);
142 m->bits[0] = ~0;
143 m->bits[1] = ~0;
144 }
145 for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
146 n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
147 struct snd_interval *i = param_to_interval(p, n);
148 i->min = 0;
149 i->max = ~0;
150 }
151 p->rmask = ~0U;
152 p->cmask = 0;
153 p->info = ~0U;
154}
155
156#define PCM_ERROR_MAX 128
157
158struct pcm {
159 int fd;
160 unsigned int flags;
161 int running:1;
162 int underruns;
163 unsigned int buffer_size;
164 unsigned int boundary;
165 char error[PCM_ERROR_MAX];
166 struct pcm_config config;
167 struct snd_pcm_mmap_status *mmap_status;
168 struct snd_pcm_mmap_control *mmap_control;
169 struct snd_pcm_sync_ptr *sync_ptr;
170 void *mmap_buffer;
171 unsigned int noirq_frames_per_msec;
172};
173
174unsigned int pcm_get_buffer_size(struct pcm *pcm)
175{
176 return pcm->buffer_size;
177}
178
179const char* pcm_get_error(struct pcm *pcm)
180{
181 return pcm->error;
182}
183
184static int oops(struct pcm *pcm, int e, const char *fmt, ...)
185{
186 va_list ap;
187 int sz;
188
189 va_start(ap, fmt);
190 vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
191 va_end(ap);
192 sz = strlen(pcm->error);
193
194 if (errno)
195 snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
196 ": %s", strerror(e));
197 return -1;
198}
199
200static unsigned int pcm_format_to_alsa(enum pcm_format format)
201{
202 switch (format) {
203 case PCM_FORMAT_S32_LE:
204 return SNDRV_PCM_FORMAT_S32_LE;
205 case PCM_FORMAT_S8:
206 return SNDRV_PCM_FORMAT_S8;
207 case PCM_FORMAT_S24_LE:
208 return SNDRV_PCM_FORMAT_S24_LE;
209 default:
210 case PCM_FORMAT_S16_LE:
211 return SNDRV_PCM_FORMAT_S16_LE;
212 };
213}
214
215unsigned int pcm_format_to_bits(enum pcm_format format)
216{
217 switch (format) {
218 case PCM_FORMAT_S32_LE:
219 case PCM_FORMAT_S24_LE:
220 return 32;
221 default:
222 case PCM_FORMAT_S16_LE:
223 return 16;
224 };
225}
226
227unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes)
228{
229 return bytes / (pcm->config.channels *
230 (pcm_format_to_bits(pcm->config.format) >> 3));
231}
232
233unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames)
234{
235 return frames * pcm->config.channels *
236 (pcm_format_to_bits(pcm->config.format) >> 3);
237}
238
239static int pcm_sync_ptr(struct pcm *pcm, int flags) {
240 if (pcm->sync_ptr) {
241 pcm->sync_ptr->flags = flags;
242 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
243 return -1;
244 }
245 return 0;
246}
247
248static int pcm_hw_mmap_status(struct pcm *pcm) {
249
250 if (pcm->sync_ptr)
251 return 0;
252
253 int page_size = sysconf(_SC_PAGE_SIZE);
254 pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED,
255 pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
256 if (pcm->mmap_status == MAP_FAILED)
257 pcm->mmap_status = NULL;
258 if (!pcm->mmap_status)
259 goto mmap_error;
260
261 pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
262 MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
263 if (pcm->mmap_control == MAP_FAILED)
264 pcm->mmap_control = NULL;
265 if (!pcm->mmap_control) {
266 munmap(pcm->mmap_status, page_size);
267 pcm->mmap_status = NULL;
268 goto mmap_error;
269 }
270 pcm->mmap_control->avail_min = 1;
271
272 return 0;
273
274mmap_error:
275
276 pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr));
277 if (!pcm->sync_ptr)
278 return -ENOMEM;
279 pcm->mmap_status = &pcm->sync_ptr->s.status;
280 pcm->mmap_control = &pcm->sync_ptr->c.control;
281 pcm->mmap_control->avail_min = 1;
282 pcm_sync_ptr(pcm, 0);
283
284 return 0;
285}
286
287static void pcm_hw_munmap_status(struct pcm *pcm) {
288 if (pcm->sync_ptr) {
289 free(pcm->sync_ptr);
290 pcm->sync_ptr = NULL;
291 } else {
292 int page_size = sysconf(_SC_PAGE_SIZE);
293 if (pcm->mmap_status)
294 munmap(pcm->mmap_status, page_size);
295 if (pcm->mmap_control)
296 munmap(pcm->mmap_control, page_size);
297 }
298 pcm->mmap_status = NULL;
299 pcm->mmap_control = NULL;
300}
301
302static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
303 const char *src, unsigned int src_offset,
304 unsigned int frames)
305{
306 int size_bytes = pcm_frames_to_bytes(pcm, frames);
307 int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset);
308 int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
309
310 /* interleaved only atm */
311 memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
312 src + src_offset_bytes, size_bytes);
313 return 0;
314}
315
316static int pcm_mmap_write_areas(struct pcm *pcm, const char *src,
317 unsigned int offset, unsigned int size)
318{
319 void *pcm_areas;
320 int commit;
321 unsigned int pcm_offset, frames, count = 0;
322
323 while (size > 0) {
324 frames = size;
325 pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
326 pcm_areas_copy(pcm, pcm_offset, src, offset, frames);
327 commit = pcm_mmap_commit(pcm, pcm_offset, frames);
328 if (commit < 0) {
329 oops(pcm, commit, "failed to commit %d frames\n", frames);
330 return commit;
331 }
332
333 offset += commit;
334 count += commit;
335 size -= commit;
336 }
337 return count;
338}
339
340int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
341 struct timespec *tstamp)
342{
343 int frames;
344 int rc;
345 snd_pcm_uframes_t hw_ptr;
346
347 if (!pcm_is_ready(pcm))
348 return -1;
349
350 rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC);
351 if (rc < 0)
352 return -1;
353
354 if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
355 (pcm->mmap_status->state != PCM_STATE_DRAINING))
356 return -1;
357
358 *tstamp = pcm->mmap_status->tstamp;
359 if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
360 return -1;
361
362 hw_ptr = pcm->mmap_status->hw_ptr;
363 if (pcm->flags & PCM_IN)
364 frames = hw_ptr - pcm->mmap_control->appl_ptr;
365 else
366 frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
367
368 if (frames < 0)
369 return -1;
370
371 *avail = (unsigned int)frames;
372
373 return 0;
374}
375
376int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
377{
378 struct snd_xferi x;
379
380 if (pcm->flags & PCM_IN)
381 return -EINVAL;
382
383 x.buf = (void*)data;
384 x.frames = count / (pcm->config.channels *
385 pcm_format_to_bits(pcm->config.format) / 8);
386
387 for (;;) {
388 if (!pcm->running) {
389 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE))
390 return oops(pcm, errno, "cannot prepare channel");
391 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
392 return oops(pcm, errno, "cannot write initial data");
393 pcm->running = 1;
394 return 0;
395 }
396 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
397 pcm->running = 0;
398 if (errno == EPIPE) {
399 /* we failed to make our window -- try to restart if we are
400 * allowed to do so. Otherwise, simply allow the EPIPE error to
401 * propagate up to the app level */
402 pcm->underruns++;
403 if (pcm->flags & PCM_NORESTART)
404 return -EPIPE;
405 continue;
406 }
407 return oops(pcm, errno, "cannot write stream data");
408 }
409 return 0;
410 }
411}
412
413int pcm_read(struct pcm *pcm, void *data, unsigned int count)
414{
415 struct snd_xferi x;
416
417 if (!(pcm->flags & PCM_IN))
418 return -EINVAL;
419
420 x.buf = data;
421 x.frames = count / (pcm->config.channels *
422 pcm_format_to_bits(pcm->config.format) / 8);
423
424 for (;;) {
425 if (!pcm->running) {
426 if (pcm_start(pcm) < 0) {
427 fprintf(stderr, "start error");
428 return -errno;
429 }
430 }
431 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
432 pcm->running = 0;
433 if (errno == EPIPE) {
434 /* we failed to make our window -- try to restart */
435 pcm->underruns++;
436 continue;
437 }
438 return oops(pcm, errno, "cannot read stream data");
439 }
440 return 0;
441 }
442}
443
444static struct pcm bad_pcm = {
445 .fd = -1,
446};
447
448struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
449 unsigned int flags)
450{
451 struct snd_pcm_hw_params *params;
452 char fn[256];
453 int fd;
454
455 snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
456 flags & PCM_IN ? 'c' : 'p');
457
458 fd = open(fn, O_RDWR);
459 if (fd < 0) {
460 fprintf(stderr, "cannot open device '%s'\n", fn);
461 goto err_open;
462 }
463
464 params = calloc(1, sizeof(struct snd_pcm_hw_params));
465 if (!params)
466 goto err_calloc;
467
468 param_init(params);
469 if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
470 fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno);
471 goto err_hw_refine;
472 }
473
474 close(fd);
475
476 return (struct pcm_params *)params;
477
478err_hw_refine:
479 free(params);
480err_calloc:
481 close(fd);
482err_open:
483 return NULL;
484}
485
486void pcm_params_free(struct pcm_params *pcm_params)
487{
488 struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
489
490 if (params)
491 free(params);
492}
493
494static int pcm_param_to_alsa(enum pcm_param param)
495{
496 switch (param) {
497 case PCM_PARAM_SAMPLE_BITS:
498 return SNDRV_PCM_HW_PARAM_SAMPLE_BITS;
499 break;
500 case PCM_PARAM_FRAME_BITS:
501 return SNDRV_PCM_HW_PARAM_FRAME_BITS;
502 break;
503 case PCM_PARAM_CHANNELS:
504 return SNDRV_PCM_HW_PARAM_CHANNELS;
505 break;
506 case PCM_PARAM_RATE:
507 return SNDRV_PCM_HW_PARAM_RATE;
508 break;
509 case PCM_PARAM_PERIOD_TIME:
510 return SNDRV_PCM_HW_PARAM_PERIOD_TIME;
511 break;
512 case PCM_PARAM_PERIOD_SIZE:
513 return SNDRV_PCM_HW_PARAM_PERIOD_SIZE;
514 break;
515 case PCM_PARAM_PERIOD_BYTES:
516 return SNDRV_PCM_HW_PARAM_PERIOD_BYTES;
517 break;
518 case PCM_PARAM_PERIODS:
519 return SNDRV_PCM_HW_PARAM_PERIODS;
520 break;
521 case PCM_PARAM_BUFFER_TIME:
522 return SNDRV_PCM_HW_PARAM_BUFFER_TIME;
523 break;
524 case PCM_PARAM_BUFFER_SIZE:
525 return SNDRV_PCM_HW_PARAM_BUFFER_SIZE;
526 break;
527 case PCM_PARAM_BUFFER_BYTES:
528 return SNDRV_PCM_HW_PARAM_BUFFER_BYTES;
529 break;
530 case PCM_PARAM_TICK_TIME:
531 return SNDRV_PCM_HW_PARAM_TICK_TIME;
532 break;
533
534 default:
535 return -1;
536 }
537}
538
539unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
540 enum pcm_param param)
541{
542 struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
543 int p;
544
545 if (!params)
546 return 0;
547
548 p = pcm_param_to_alsa(param);
549 if (p < 0)
550 return 0;
551
552 return param_get_min(params, p);
553}
554
555unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
556 enum pcm_param param)
557{
558 struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
559 int p;
560
561 if (!params)
562 return 0;
563
564 p = pcm_param_to_alsa(param);
565 if (p < 0)
566 return 0;
567
568 return param_get_max(params, p);
569}
570
571int pcm_close(struct pcm *pcm)
572{
573 if (pcm == &bad_pcm)
574 return 0;
575
576 pcm_hw_munmap_status(pcm);
577
578 if (pcm->flags & PCM_MMAP) {
579 pcm_stop(pcm);
580 munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
581 }
582
583 if (pcm->fd >= 0)
584 close(pcm->fd);
585 pcm->running = 0;
586 pcm->buffer_size = 0;
587 pcm->fd = -1;
588 free(pcm);
589 return 0;
590}
591
592struct pcm *pcm_open(unsigned int card, unsigned int device,
593 unsigned int flags, struct pcm_config *config)
594{
595 struct pcm *pcm;
596 struct snd_pcm_info info;
597 struct snd_pcm_hw_params params;
598 struct snd_pcm_sw_params sparams;
599 char fn[256];
600 int rc;
601
602 pcm = calloc(1, sizeof(struct pcm));
603 if (!pcm || !config)
604 return &bad_pcm; /* TODO: could support default config here */
605
606 pcm->config = *config;
607
608 snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
609 flags & PCM_IN ? 'c' : 'p');
610
611 pcm->flags = flags;
612 pcm->fd = open(fn, O_RDWR);
613 if (pcm->fd < 0) {
614 oops(pcm, errno, "cannot open device '%s'", fn);
615 return pcm;
616 }
617
618 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
619 oops(pcm, errno, "cannot get info");
620 goto fail_close;
621 }
622
623 param_init(&params);
624 param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
625 pcm_format_to_alsa(config->format));
626 param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
627 SNDRV_PCM_SUBFORMAT_STD);
628 param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
629 param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
630 pcm_format_to_bits(config->format));
631 param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
632 pcm_format_to_bits(config->format) * config->channels);
633 param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
634 config->channels);
635 param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
636 param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
637
638 if (flags & PCM_NOIRQ) {
639
640 if (!(flags & PCM_MMAP)) {
641 oops(pcm, -EINVAL, "noirq only currently supported with mmap().");
642 goto fail;
643 }
644
645 params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
646 pcm->noirq_frames_per_msec = config->rate / 1000;
647 }
648
649 if (flags & PCM_MMAP)
650 param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
651 SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
652 else
653 param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
654 SNDRV_PCM_ACCESS_RW_INTERLEAVED);
655
656 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
657 oops(pcm, errno, "cannot set hw params");
658 goto fail_close;
659 }
660
661 /* get our refined hw_params */
662 config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
663 config->period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);
664 pcm->buffer_size = config->period_count * config->period_size;
665
666 if (flags & PCM_MMAP) {
667 pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
668 PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);
669 if (pcm->mmap_buffer == MAP_FAILED) {
670 oops(pcm, -errno, "failed to mmap buffer %d bytes\n",
671 pcm_frames_to_bytes(pcm, pcm->buffer_size));
672 goto fail_close;
673 }
674 }
675
676
677 memset(&sparams, 0, sizeof(sparams));
678 sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
679 sparams.period_step = 1;
680 sparams.avail_min = 1;
681
682 if (!config->start_threshold) {
683 if (pcm->flags & PCM_IN)
684 pcm->config.start_threshold = sparams.start_threshold = 1;
685 else
686 pcm->config.start_threshold = sparams.start_threshold =
687 config->period_count * config->period_size / 2;
688 } else
689 sparams.start_threshold = config->start_threshold;
690
691 /* pick a high stop threshold - todo: does this need further tuning */
692 if (!config->stop_threshold) {
693 if (pcm->flags & PCM_IN)
694 pcm->config.stop_threshold = sparams.stop_threshold =
695 config->period_count * config->period_size * 10;
696 else
697 pcm->config.stop_threshold = sparams.stop_threshold =
698 config->period_count * config->period_size;
699 }
700 else
701 sparams.stop_threshold = config->stop_threshold;
702
703 sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
704 sparams.silence_size = 0;
705 sparams.silence_threshold = config->silence_threshold;
706 pcm->boundary = sparams.boundary = pcm->buffer_size;
707
708 while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
709 pcm->boundary *= 2;
710
711 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
712 oops(pcm, errno, "cannot set sw params");
713 goto fail;
714 }
715
716 rc = pcm_hw_mmap_status(pcm);
717 if (rc < 0) {
718 oops(pcm, rc, "mmap status failed");
719 goto fail;
720 }
721
722 pcm->underruns = 0;
723 return pcm;
724
725fail:
726 if (flags & PCM_MMAP)
727 munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
728fail_close:
729 close(pcm->fd);
730 pcm->fd = -1;
731 return pcm;
732}
733
734int pcm_is_ready(struct pcm *pcm)
735{
736 return pcm->fd >= 0;
737}
738
739int pcm_start(struct pcm *pcm)
740{
741 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
742 return oops(pcm, errno, "cannot prepare channel");
743
744 if (pcm->flags & PCM_MMAP)
745 pcm_sync_ptr(pcm, 0);
746
747 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
748 return oops(pcm, errno, "cannot start channel");
749
750 pcm->running = 1;
751 return 0;
752}
753
754int pcm_stop(struct pcm *pcm)
755{
756 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
757 return oops(pcm, errno, "cannot stop channel");
758
759 pcm->running = 0;
760 return 0;
761}
762
763static inline int pcm_mmap_playback_avail(struct pcm *pcm)
764{
765 int avail;
766
767 avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
768
769 if (avail < 0)
770 avail += pcm->boundary;
771 else if (avail > (int)pcm->boundary)
772 avail -= pcm->boundary;
773
774 return avail;
775}
776
777static inline int pcm_mmap_capture_avail(struct pcm *pcm)
778{
779 int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr;
780 if (avail < 0)
781 avail += pcm->boundary;
782 return avail;
783}
784
785static inline int pcm_mmap_avail(struct pcm *pcm)
786{
787 pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
788 if (pcm->flags & PCM_IN)
789 return pcm_mmap_capture_avail(pcm);
790 else
791 return pcm_mmap_playback_avail(pcm);
792}
793
794static void pcm_mmap_appl_forward(struct pcm *pcm, int frames)
795{
796 unsigned int appl_ptr = pcm->mmap_control->appl_ptr;
797 appl_ptr += frames;
798
799 /* check for boundary wrap */
800 if (appl_ptr > pcm->boundary)
801 appl_ptr -= pcm->boundary;
802 pcm->mmap_control->appl_ptr = appl_ptr;
803}
804
805int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
806 unsigned int *frames)
807{
808 unsigned int continuous, copy_frames, avail;
809
810 /* return the mmap buffer */
811 *areas = pcm->mmap_buffer;
812
813 /* and the application offset in frames */
814 *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size;
815
816 avail = pcm_mmap_avail(pcm);
817 if (avail > pcm->buffer_size)
818 avail = pcm->buffer_size;
819 continuous = pcm->buffer_size - *offset;
820
821 /* we can only copy frames if the are availabale and continuos */
822 copy_frames = *frames;
823 if (copy_frames > avail)
824 copy_frames = avail;
825 if (copy_frames > continuous)
826 copy_frames = continuous;
827 *frames = copy_frames;
828
829 return 0;
830}
831
832int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames)
833{
834 (void)offset;
835 /* update the application pointer in userspace and kernel */
836 pcm_mmap_appl_forward(pcm, frames);
837 pcm_sync_ptr(pcm, 0);
838
839 return frames;
840}
841
842int pcm_avail_update(struct pcm *pcm)
843{
844 pcm_sync_ptr(pcm, 0);
845 return pcm_mmap_avail(pcm);
846}
847
848int pcm_state(struct pcm *pcm)
849{
850 int err = pcm_sync_ptr(pcm, 0);
851 if (err < 0)
852 return err;
853
854 return pcm->mmap_status->state;
855}
856
857int pcm_wait(struct pcm *pcm, int timeout)
858{
859 struct pollfd pfd;
860 int err;
861
862 pfd.fd = pcm->fd;
863 pfd.events = POLLOUT | POLLERR | POLLNVAL;
864
865 do {
866 /* let's wait for avail or timeout */
867 err = poll(&pfd, 1, timeout);
868 if (err < 0)
869 return -errno;
870
871 /* timeout ? */
872 if (err == 0)
873 return 0;
874
875 /* have we been interrupted ? */
876 if (errno == -EINTR)
877 continue;
878
879 /* check for any errors */
880 if (pfd.revents & (POLLERR | POLLNVAL)) {
881 switch (pcm_state(pcm)) {
882 case PCM_STATE_XRUN:
883 return -EPIPE;
884 case PCM_STATE_SUSPENDED:
885 return -ESTRPIPE;
886 case PCM_STATE_DISCONNECTED:
887 return -ENODEV;
888 default:
889 return -EIO;
890 }
891 }
892 /* poll again if fd not ready for IO */
893 } while (!(pfd.revents & (POLLIN | POLLOUT)));
894
895 return 1;
896}
897
898int pcm_mmap_write(struct pcm *pcm, const void *buffer, unsigned int bytes)
899{
900 int err = 0, frames, avail;
901 unsigned int offset = 0, count;
902
903 if (bytes == 0)
904 return 0;
905
906 count = pcm_bytes_to_frames(pcm, bytes);
907
908 while (count > 0) {
909
910 /* get the available space for writing new frames */
911 avail = pcm_avail_update(pcm);
912 if (avail < 0) {
913 fprintf(stderr, "cannot determine available mmap frames");
914 return err;
915 }
916
917 /* start the audio if we reach the threshold */
918 if (!pcm->running &&
919 (pcm->buffer_size - avail) >= pcm->config.start_threshold) {
920 if (pcm_start(pcm) < 0) {
921 fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n",
922 (unsigned int)pcm->mmap_status->hw_ptr,
923 (unsigned int)pcm->mmap_control->appl_ptr,
924 avail);
925 return -errno;
926 }
927 }
928
929 /* sleep until we have space to write new frames */
930 if (pcm->running &&
931 (unsigned int)avail < pcm->mmap_control->avail_min) {
932 int time = -1;
933
934 if (pcm->flags & PCM_NOIRQ)
935 time = (pcm->buffer_size - avail - pcm->mmap_control->avail_min)
936 / pcm->noirq_frames_per_msec;
937
938 err = pcm_wait(pcm, time);
939 if (err < 0) {
940 pcm->running = 0;
941 fprintf(stderr, "wait error: hw 0x%x app 0x%x avail 0x%x\n",
942 (unsigned int)pcm->mmap_status->hw_ptr,
943 (unsigned int)pcm->mmap_control->appl_ptr,
944 avail);
945 pcm->mmap_control->appl_ptr = 0;
946 return err;
947 }
948 continue;
949 }
950
951 frames = count;
952 if (frames > avail)
953 frames = avail;
954
955 if (!frames)
956 break;
957
958 /* copy frames from buffer */
959 frames = pcm_mmap_write_areas(pcm, buffer, offset, frames);
960 if (frames < 0) {
961 fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n",
962 (unsigned int)pcm->mmap_status->hw_ptr,
963 (unsigned int)pcm->mmap_control->appl_ptr,
964 avail);
965 return frames;
966 }
967
968 offset += frames;
969 count -= frames;
970 }
971
972 return 0;
973}