CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
orangepi-xunlong

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: orangepi-xunlong/orangepi-build
Path: blob/next/external/cache/sources/tinyalsa_hub/pcm.c
Views: 3959
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 <sound/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
54
static 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
60
static 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
66
static 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
71
static 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
76
static 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
88
static 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
96
static 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
105
static 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
114
static 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
124
static 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
134
static 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
158
struct pcm {
159
int fd;
160
unsigned int flags;
161
int running:1;
162
int prepared:1;
163
int underruns;
164
unsigned int buffer_size;
165
unsigned int boundary;
166
char error[PCM_ERROR_MAX];
167
struct pcm_config config;
168
struct snd_pcm_mmap_status *mmap_status;
169
struct snd_pcm_mmap_control *mmap_control;
170
struct snd_pcm_sync_ptr *sync_ptr;
171
void *mmap_buffer;
172
unsigned int noirq_frames_per_msec;
173
};
174
175
unsigned int pcm_get_buffer_size(struct pcm *pcm)
176
{
177
return pcm->buffer_size;
178
}
179
180
const char* pcm_get_error(struct pcm *pcm)
181
{
182
return pcm->error;
183
}
184
185
static int oops(struct pcm *pcm, int e, const char *fmt, ...)
186
{
187
va_list ap;
188
int sz;
189
190
va_start(ap, fmt);
191
vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
192
va_end(ap);
193
sz = strlen(pcm->error);
194
195
if (errno)
196
snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
197
": %s", strerror(e));
198
return -1;
199
}
200
201
static unsigned int pcm_format_to_alsa(enum pcm_format format)
202
{
203
switch (format) {
204
case PCM_FORMAT_S32_LE:
205
return SNDRV_PCM_FORMAT_S32_LE;
206
case PCM_FORMAT_S8:
207
return SNDRV_PCM_FORMAT_S8;
208
case PCM_FORMAT_S24_LE:
209
return SNDRV_PCM_FORMAT_S24_LE;
210
default:
211
case PCM_FORMAT_S16_LE:
212
return SNDRV_PCM_FORMAT_S16_LE;
213
};
214
}
215
216
unsigned int pcm_format_to_bits(enum pcm_format format)
217
{
218
switch (format) {
219
case PCM_FORMAT_S32_LE:
220
case PCM_FORMAT_S24_LE:
221
return 32;
222
default:
223
case PCM_FORMAT_S16_LE:
224
return 16;
225
};
226
}
227
228
unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes)
229
{
230
return bytes / (pcm->config.channels *
231
(pcm_format_to_bits(pcm->config.format) >> 3));
232
}
233
234
unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames)
235
{
236
return frames * pcm->config.channels *
237
(pcm_format_to_bits(pcm->config.format) >> 3);
238
}
239
240
static int pcm_sync_ptr(struct pcm *pcm, int flags) {
241
if (pcm->sync_ptr) {
242
pcm->sync_ptr->flags = flags;
243
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
244
return -1;
245
}
246
return 0;
247
}
248
249
static int pcm_hw_mmap_status(struct pcm *pcm) {
250
251
if (pcm->sync_ptr)
252
return 0;
253
254
int page_size = sysconf(_SC_PAGE_SIZE);
255
pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED,
256
pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
257
if (pcm->mmap_status == MAP_FAILED)
258
pcm->mmap_status = NULL;
259
if (!pcm->mmap_status)
260
goto mmap_error;
261
262
pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
263
MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
264
if (pcm->mmap_control == MAP_FAILED)
265
pcm->mmap_control = NULL;
266
if (!pcm->mmap_control) {
267
munmap(pcm->mmap_status, page_size);
268
pcm->mmap_status = NULL;
269
goto mmap_error;
270
}
271
pcm->mmap_control->avail_min = 1;
272
273
return 0;
274
275
mmap_error:
276
277
pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr));
278
if (!pcm->sync_ptr)
279
return -ENOMEM;
280
pcm->mmap_status = &pcm->sync_ptr->s.status;
281
pcm->mmap_control = &pcm->sync_ptr->c.control;
282
pcm->mmap_control->avail_min = 1;
283
pcm_sync_ptr(pcm, 0);
284
285
return 0;
286
}
287
288
static void pcm_hw_munmap_status(struct pcm *pcm) {
289
if (pcm->sync_ptr) {
290
free(pcm->sync_ptr);
291
pcm->sync_ptr = NULL;
292
} else {
293
int page_size = sysconf(_SC_PAGE_SIZE);
294
if (pcm->mmap_status)
295
munmap(pcm->mmap_status, page_size);
296
if (pcm->mmap_control)
297
munmap(pcm->mmap_control, page_size);
298
}
299
pcm->mmap_status = NULL;
300
pcm->mmap_control = NULL;
301
}
302
303
static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
304
char *buf, unsigned int src_offset,
305
unsigned int frames)
306
{
307
int size_bytes = pcm_frames_to_bytes(pcm, frames);
308
int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset);
309
int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
310
311
/* interleaved only atm */
312
if (pcm->flags & PCM_IN)
313
memcpy(buf + src_offset_bytes,
314
(char*)pcm->mmap_buffer + pcm_offset_bytes,
315
size_bytes);
316
else
317
memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
318
buf + src_offset_bytes,
319
size_bytes);
320
return 0;
321
}
322
323
static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf,
324
unsigned int offset, unsigned int size)
325
{
326
void *pcm_areas;
327
int commit;
328
unsigned int pcm_offset, frames, count = 0;
329
330
while (size > 0) {
331
frames = size;
332
pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
333
pcm_areas_copy(pcm, pcm_offset, buf, offset, frames);
334
commit = pcm_mmap_commit(pcm, pcm_offset, frames);
335
if (commit < 0) {
336
oops(pcm, commit, "failed to commit %d frames\n", frames);
337
return commit;
338
}
339
340
offset += commit;
341
count += commit;
342
size -= commit;
343
}
344
return count;
345
}
346
347
int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
348
struct timespec *tstamp)
349
{
350
int frames;
351
int rc;
352
snd_pcm_uframes_t hw_ptr;
353
354
if (!pcm_is_ready(pcm))
355
return -1;
356
357
rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC);
358
if (rc < 0)
359
return -1;
360
361
if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
362
(pcm->mmap_status->state != PCM_STATE_DRAINING))
363
return -1;
364
365
*tstamp = pcm->mmap_status->tstamp;
366
if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
367
return -1;
368
369
hw_ptr = pcm->mmap_status->hw_ptr;
370
if (pcm->flags & PCM_IN)
371
frames = hw_ptr - pcm->mmap_control->appl_ptr;
372
else
373
frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
374
375
if (frames < 0)
376
return -1;
377
378
*avail = (unsigned int)frames;
379
380
return 0;
381
}
382
383
int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
384
{
385
struct snd_xferi x;
386
387
if (pcm->flags & PCM_IN)
388
return -EINVAL;
389
390
x.buf = (void*)data;
391
x.frames = count / (pcm->config.channels *
392
pcm_format_to_bits(pcm->config.format) / 8);
393
394
for (;;) {
395
if (!pcm->running) {
396
int prepare_error = pcm_prepare(pcm);
397
if (prepare_error)
398
return prepare_error;
399
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
400
printf("[%s]-->line:%d,errno:%d\n", __func__, __LINE__, errno);
401
return oops(pcm, errno, "cannot write initial data");
402
}
403
pcm->running = 1;
404
return 0;
405
}
406
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
407
pcm->prepared = 0;
408
pcm->running = 0;
409
printf("[%s]-->line:%d,errno:%d\n", __func__, __LINE__, errno);
410
if (errno == EPIPE) {
411
/* we failed to make our window -- try to restart if we are
412
* allowed to do so. Otherwise, simply allow the EPIPE error to
413
* propagate up to the app level */
414
pcm->underruns++;
415
if (pcm->flags & PCM_NORESTART)
416
return -EPIPE;
417
continue;
418
}
419
return oops(pcm, errno, "cannot write stream data");
420
}
421
return 0;
422
}
423
}
424
425
int pcm_read(struct pcm *pcm, void *data, unsigned int count)
426
{
427
struct snd_xferi x;
428
429
if (!(pcm->flags & PCM_IN))
430
return -EINVAL;
431
432
x.buf = data;
433
x.frames = count / (pcm->config.channels *
434
pcm_format_to_bits(pcm->config.format) / 8);
435
436
for (;;) {
437
if (!pcm->running) {
438
if (pcm_start(pcm) < 0) {
439
fprintf(stderr, "start error");
440
return -errno;
441
}
442
}
443
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
444
pcm->prepared = 0;
445
pcm->running = 0;
446
printf("[%s]-->line:%d,errno:%d\n", __func__, __LINE__, errno);
447
if (errno == EPIPE) {
448
/* we failed to make our window -- try to restart */
449
pcm->underruns++;
450
continue;
451
}
452
return oops(pcm, errno, "cannot read stream data");
453
}
454
return 0;
455
}
456
}
457
458
static struct pcm bad_pcm = {
459
.fd = -1,
460
};
461
462
struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
463
unsigned int flags)
464
{
465
struct snd_pcm_hw_params *params;
466
char fn[256];
467
int fd;
468
469
snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
470
flags & PCM_IN ? 'c' : 'p');
471
472
fd = open(fn, O_RDWR);
473
if (fd < 0) {
474
fprintf(stderr, "cannot open device '%s'\n", fn);
475
goto err_open;
476
}
477
478
params = calloc(1, sizeof(struct snd_pcm_hw_params));
479
if (!params)
480
goto err_calloc;
481
482
param_init(params);
483
if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
484
fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno);
485
goto err_hw_refine;
486
}
487
488
close(fd);
489
490
return (struct pcm_params *)params;
491
492
err_hw_refine:
493
free(params);
494
err_calloc:
495
close(fd);
496
err_open:
497
return NULL;
498
}
499
500
void pcm_params_free(struct pcm_params *pcm_params)
501
{
502
struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
503
504
if (params)
505
free(params);
506
}
507
508
static int pcm_param_to_alsa(enum pcm_param param)
509
{
510
switch (param) {
511
case PCM_PARAM_ACCESS:
512
return SNDRV_PCM_HW_PARAM_ACCESS;
513
case PCM_PARAM_FORMAT:
514
return SNDRV_PCM_HW_PARAM_FORMAT;
515
case PCM_PARAM_SUBFORMAT:
516
return SNDRV_PCM_HW_PARAM_SUBFORMAT;
517
case PCM_PARAM_SAMPLE_BITS:
518
return SNDRV_PCM_HW_PARAM_SAMPLE_BITS;
519
break;
520
case PCM_PARAM_FRAME_BITS:
521
return SNDRV_PCM_HW_PARAM_FRAME_BITS;
522
break;
523
case PCM_PARAM_CHANNELS:
524
return SNDRV_PCM_HW_PARAM_CHANNELS;
525
break;
526
case PCM_PARAM_RATE:
527
return SNDRV_PCM_HW_PARAM_RATE;
528
break;
529
case PCM_PARAM_PERIOD_TIME:
530
return SNDRV_PCM_HW_PARAM_PERIOD_TIME;
531
break;
532
case PCM_PARAM_PERIOD_SIZE:
533
return SNDRV_PCM_HW_PARAM_PERIOD_SIZE;
534
break;
535
case PCM_PARAM_PERIOD_BYTES:
536
return SNDRV_PCM_HW_PARAM_PERIOD_BYTES;
537
break;
538
case PCM_PARAM_PERIODS:
539
return SNDRV_PCM_HW_PARAM_PERIODS;
540
break;
541
case PCM_PARAM_BUFFER_TIME:
542
return SNDRV_PCM_HW_PARAM_BUFFER_TIME;
543
break;
544
case PCM_PARAM_BUFFER_SIZE:
545
return SNDRV_PCM_HW_PARAM_BUFFER_SIZE;
546
break;
547
case PCM_PARAM_BUFFER_BYTES:
548
return SNDRV_PCM_HW_PARAM_BUFFER_BYTES;
549
break;
550
case PCM_PARAM_TICK_TIME:
551
return SNDRV_PCM_HW_PARAM_TICK_TIME;
552
break;
553
554
default:
555
return -1;
556
}
557
}
558
559
struct pcm_mask *pcm_params_get_mask(struct pcm_params *pcm_params,
560
enum pcm_param param)
561
{
562
int p;
563
struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
564
if (params == NULL) {
565
return NULL;
566
}
567
568
p = pcm_param_to_alsa(param);
569
if (p < 0 || !param_is_mask(p)) {
570
return NULL;
571
}
572
573
return (struct pcm_mask *)param_to_mask(params, p);
574
}
575
576
unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
577
enum pcm_param param)
578
{
579
struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
580
int p;
581
582
if (!params)
583
return 0;
584
585
p = pcm_param_to_alsa(param);
586
if (p < 0)
587
return 0;
588
589
return param_get_min(params, p);
590
}
591
592
unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
593
enum pcm_param param)
594
{
595
struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
596
int p;
597
598
if (!params)
599
return 0;
600
601
p = pcm_param_to_alsa(param);
602
if (p < 0)
603
return 0;
604
605
return param_get_max(params, p);
606
}
607
608
int pcm_close(struct pcm *pcm)
609
{
610
if (pcm == &bad_pcm)
611
return 0;
612
613
pcm_hw_munmap_status(pcm);
614
615
if (pcm->flags & PCM_MMAP) {
616
pcm_stop(pcm);
617
munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
618
}
619
620
if (pcm->fd >= 0)
621
close(pcm->fd);
622
pcm->prepared = 0;
623
pcm->running = 0;
624
pcm->buffer_size = 0;
625
pcm->fd = -1;
626
free(pcm);
627
return 0;
628
}
629
630
struct pcm *pcm_open(unsigned int card, unsigned int device,
631
unsigned int flags, struct pcm_config *config)
632
{
633
struct pcm *pcm;
634
struct snd_pcm_info info;
635
struct snd_pcm_hw_params params;
636
struct snd_pcm_sw_params sparams;
637
char fn[256];
638
int rc;
639
640
pcm = calloc(1, sizeof(struct pcm));
641
if (!pcm || !config)
642
return &bad_pcm; /* TODO: could support default config here */
643
644
pcm->config = *config;
645
646
snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
647
flags & PCM_IN ? 'c' : 'p');
648
649
pcm->flags = flags;
650
pcm->fd = open(fn, O_RDWR);
651
if (pcm->fd < 0) {
652
oops(pcm, errno, "cannot open device '%s'", fn);
653
return pcm;
654
}
655
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
656
oops(pcm, errno, "cannot get info");
657
goto fail_close;
658
}
659
660
param_init(&params);
661
param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
662
pcm_format_to_alsa(config->format));
663
param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
664
SNDRV_PCM_SUBFORMAT_STD);
665
param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
666
pcm_format_to_bits(config->format));
667
param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
668
pcm_format_to_bits(config->format) * config->channels);
669
param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
670
config->channels);
671
param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
672
param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
673
674
if (flags & PCM_NOIRQ) {
675
676
if (!(flags & PCM_MMAP)) {
677
oops(pcm, -EINVAL, "noirq only currently supported with mmap().");
678
goto fail;
679
}
680
681
params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
682
pcm->noirq_frames_per_msec = config->rate / 1000;
683
}
684
685
if (flags & PCM_MMAP)
686
param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
687
SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
688
else
689
param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
690
SNDRV_PCM_ACCESS_RW_INTERLEAVED);
691
692
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
693
oops(pcm, errno, "cannot set hw params");
694
goto fail_close;
695
}
696
697
/* get our refined hw_params */
698
config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
699
config->period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);
700
pcm->buffer_size = config->period_count * config->period_size;
701
702
if (flags & PCM_MMAP) {
703
pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
704
PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);
705
if (pcm->mmap_buffer == MAP_FAILED) {
706
oops(pcm, -errno, "failed to mmap buffer %d bytes\n",
707
pcm_frames_to_bytes(pcm, pcm->buffer_size));
708
goto fail_close;
709
}
710
}
711
712
713
memset(&sparams, 0, sizeof(sparams));
714
sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
715
sparams.period_step = 1;
716
sparams.avail_min = 1;
717
718
if (!config->start_threshold) {
719
if (pcm->flags & PCM_IN)
720
pcm->config.start_threshold = sparams.start_threshold = 1;
721
else
722
pcm->config.start_threshold = sparams.start_threshold =
723
config->period_count * config->period_size / 2;
724
} else
725
sparams.start_threshold = config->start_threshold;
726
727
/* pick a high stop threshold - todo: does this need further tuning */
728
if (!config->stop_threshold) {
729
if (pcm->flags & PCM_IN)
730
pcm->config.stop_threshold = sparams.stop_threshold =
731
config->period_count * config->period_size * 10;
732
else
733
pcm->config.stop_threshold = sparams.stop_threshold =
734
config->period_count * config->period_size;
735
}
736
else
737
sparams.stop_threshold = config->stop_threshold;
738
739
sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
740
sparams.silence_size = 0;
741
sparams.silence_threshold = config->silence_threshold;
742
pcm->boundary = sparams.boundary = pcm->buffer_size;
743
744
while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
745
pcm->boundary *= 2;
746
747
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
748
oops(pcm, errno, "cannot set sw params");
749
goto fail;
750
}
751
752
rc = pcm_hw_mmap_status(pcm);
753
if (rc < 0) {
754
oops(pcm, rc, "mmap status failed");
755
goto fail;
756
}
757
758
#ifdef SNDRV_PCM_IOCTL_TTSTAMP
759
if (pcm->flags & PCM_MONOTONIC) {
760
int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
761
rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
762
if (rc < 0) {
763
oops(pcm, rc, "cannot set timestamp type");
764
goto fail;
765
}
766
}
767
#endif
768
769
pcm->underruns = 0;
770
return pcm;
771
772
fail:
773
if (flags & PCM_MMAP)
774
munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
775
fail_close:
776
close(pcm->fd);
777
pcm->fd = -1;
778
return pcm;
779
}
780
781
int pcm_is_ready(struct pcm *pcm)
782
{
783
return pcm->fd >= 0;
784
}
785
786
int pcm_prepare(struct pcm *pcm)
787
{
788
if (pcm->prepared)
789
return 0;
790
791
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
792
return oops(pcm, errno, "cannot prepare channel");
793
794
pcm->prepared = 1;
795
return 0;
796
}
797
798
int pcm_start(struct pcm *pcm)
799
{
800
int prepare_error = pcm_prepare(pcm);
801
if (prepare_error)
802
return prepare_error;
803
804
if (pcm->flags & PCM_MMAP)
805
pcm_sync_ptr(pcm, 0);
806
807
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
808
return oops(pcm, errno, "cannot start channel");
809
810
pcm->running = 1;
811
return 0;
812
}
813
814
int pcm_stop(struct pcm *pcm)
815
{
816
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
817
return oops(pcm, errno, "cannot stop channel");
818
819
pcm->prepared = 0;
820
pcm->running = 0;
821
return 0;
822
}
823
824
static inline int pcm_mmap_playback_avail(struct pcm *pcm)
825
{
826
int avail;
827
828
avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
829
830
if (avail < 0)
831
avail += pcm->boundary;
832
else if (avail > (int)pcm->boundary)
833
avail -= pcm->boundary;
834
835
return avail;
836
}
837
838
static inline int pcm_mmap_capture_avail(struct pcm *pcm)
839
{
840
int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr;
841
if (avail < 0)
842
avail += pcm->boundary;
843
return avail;
844
}
845
846
static inline int pcm_mmap_avail(struct pcm *pcm)
847
{
848
pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
849
if (pcm->flags & PCM_IN)
850
return pcm_mmap_capture_avail(pcm);
851
else
852
return pcm_mmap_playback_avail(pcm);
853
}
854
855
static void pcm_mmap_appl_forward(struct pcm *pcm, int frames)
856
{
857
unsigned int appl_ptr = pcm->mmap_control->appl_ptr;
858
appl_ptr += frames;
859
860
/* check for boundary wrap */
861
if (appl_ptr > pcm->boundary)
862
appl_ptr -= pcm->boundary;
863
pcm->mmap_control->appl_ptr = appl_ptr;
864
}
865
866
int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
867
unsigned int *frames)
868
{
869
unsigned int continuous, copy_frames, avail;
870
871
/* return the mmap buffer */
872
*areas = pcm->mmap_buffer;
873
874
/* and the application offset in frames */
875
*offset = pcm->mmap_control->appl_ptr % pcm->buffer_size;
876
877
avail = pcm_mmap_avail(pcm);
878
if (avail > pcm->buffer_size)
879
avail = pcm->buffer_size;
880
continuous = pcm->buffer_size - *offset;
881
882
/* we can only copy frames if the are availabale and continuos */
883
copy_frames = *frames;
884
if (copy_frames > avail)
885
copy_frames = avail;
886
if (copy_frames > continuous)
887
copy_frames = continuous;
888
*frames = copy_frames;
889
890
return 0;
891
}
892
893
int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames)
894
{
895
/* update the application pointer in userspace and kernel */
896
pcm_mmap_appl_forward(pcm, frames);
897
pcm_sync_ptr(pcm, 0);
898
899
return frames;
900
}
901
902
int pcm_avail_update(struct pcm *pcm)
903
{
904
pcm_sync_ptr(pcm, 0);
905
return pcm_mmap_avail(pcm);
906
}
907
908
int pcm_state(struct pcm *pcm)
909
{
910
int err = pcm_sync_ptr(pcm, 0);
911
if (err < 0)
912
return err;
913
914
return pcm->mmap_status->state;
915
}
916
917
int pcm_wait(struct pcm *pcm, int timeout)
918
{
919
struct pollfd pfd;
920
int err;
921
922
pfd.fd = pcm->fd;
923
pfd.events = POLLOUT | POLLERR | POLLNVAL;
924
925
do {
926
/* let's wait for avail or timeout */
927
err = poll(&pfd, 1, timeout);
928
if (err < 0)
929
return -errno;
930
931
/* timeout ? */
932
if (err == 0)
933
return 0;
934
935
/* have we been interrupted ? */
936
if (errno == -EINTR)
937
continue;
938
939
/* check for any errors */
940
if (pfd.revents & (POLLERR | POLLNVAL)) {
941
switch (pcm_state(pcm)) {
942
case PCM_STATE_XRUN:
943
return -EPIPE;
944
case PCM_STATE_SUSPENDED:
945
return -ESTRPIPE;
946
case PCM_STATE_DISCONNECTED:
947
return -ENODEV;
948
default:
949
return -EIO;
950
}
951
}
952
/* poll again if fd not ready for IO */
953
} while (!(pfd.revents & (POLLIN | POLLOUT)));
954
955
return 1;
956
}
957
958
int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes)
959
{
960
int err = 0, frames, avail;
961
unsigned int offset = 0, count;
962
963
if (bytes == 0)
964
return 0;
965
966
count = pcm_bytes_to_frames(pcm, bytes);
967
968
while (count > 0) {
969
970
/* get the available space for writing new frames */
971
avail = pcm_avail_update(pcm);
972
if (avail < 0) {
973
fprintf(stderr, "cannot determine available mmap frames");
974
return err;
975
}
976
977
/* start the audio if we reach the threshold */
978
if (!pcm->running &&
979
(pcm->buffer_size - avail) >= pcm->config.start_threshold) {
980
if (pcm_start(pcm) < 0) {
981
fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n",
982
(unsigned int)pcm->mmap_status->hw_ptr,
983
(unsigned int)pcm->mmap_control->appl_ptr,
984
avail);
985
return -errno;
986
}
987
}
988
989
/* sleep until we have space to write new frames */
990
if (pcm->running &&
991
(unsigned int)avail < pcm->mmap_control->avail_min) {
992
int time = -1;
993
994
if (pcm->flags & PCM_NOIRQ)
995
time = (pcm->buffer_size - avail - pcm->mmap_control->avail_min)
996
/ pcm->noirq_frames_per_msec;
997
998
err = pcm_wait(pcm, time);
999
if (err < 0) {
1000
pcm->prepared = 0;
1001
pcm->running = 0;
1002
fprintf(stderr, "wait error: hw 0x%x app 0x%x avail 0x%x\n",
1003
(unsigned int)pcm->mmap_status->hw_ptr,
1004
(unsigned int)pcm->mmap_control->appl_ptr,
1005
avail);
1006
pcm->mmap_control->appl_ptr = 0;
1007
return err;
1008
}
1009
continue;
1010
}
1011
1012
frames = count;
1013
if (frames > avail)
1014
frames = avail;
1015
1016
if (!frames)
1017
break;
1018
1019
/* copy frames from buffer */
1020
frames = pcm_mmap_transfer_areas(pcm, (void *)buffer, offset, frames);
1021
if (frames < 0) {
1022
fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n",
1023
(unsigned int)pcm->mmap_status->hw_ptr,
1024
(unsigned int)pcm->mmap_control->appl_ptr,
1025
avail);
1026
return frames;
1027
}
1028
1029
offset += frames;
1030
count -= frames;
1031
}
1032
1033
return 0;
1034
}
1035
1036
int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count)
1037
{
1038
if ((~pcm->flags) & (PCM_OUT | PCM_MMAP))
1039
return -ENOSYS;
1040
1041
return pcm_mmap_transfer(pcm, (void *)data, count);
1042
}
1043
1044
int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count)
1045
{
1046
if ((~pcm->flags) & (PCM_IN | PCM_MMAP))
1047
return -ENOSYS;
1048
1049
return pcm_mmap_transfer(pcm, data, count);
1050
}
1051
1052