]> code.delx.au - pulseaudio/blob - src/utils/padsp.c
implement a /dev/mixer interface
[pulseaudio] / src / utils / padsp.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
6 polypaudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 polypaudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #ifdef _FILE_OFFSET_BITS
27 #undef _FILE_OFFSET_BITS
28 #endif
29
30 #ifndef _LARGEFILE64_SOURCE
31 #define _LARGEFILE64_SOURCE 1
32 #endif
33
34 #include <sys/soundcard.h>
35 #include <sys/ioctl.h>
36 #include <pthread.h>
37 #include <unistd.h>
38 #include <sys/socket.h>
39 #include <dlfcn.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <string.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <signal.h>
46
47 #include <linux/sockios.h>
48
49 #include <polyp/polypaudio.h>
50 #include <polypcore/llist.h>
51 #include <polypcore/gccmacro.h>
52
53 typedef enum {
54 FD_INFO_MIXER,
55 FD_INFO_PLAYBACK
56 } fd_info_type_t;
57
58 typedef struct fd_info fd_info;
59
60 struct fd_info {
61 pthread_mutex_t mutex;
62 int ref;
63 int unusable;
64
65 fd_info_type_t type;
66 int app_fd, thread_fd;
67
68 pa_sample_spec sample_spec;
69 size_t fragment_size;
70 unsigned n_fragments;
71
72 pa_threaded_mainloop *mainloop;
73 pa_context *context;
74 pa_stream *stream;
75
76 pa_io_event *io_event;
77
78 void *buf;
79
80 int operation_success;
81
82 pa_cvolume volume;
83 uint32_t sink_index;
84 int volume_modify_count;
85
86 PA_LLIST_FIELDS(fd_info);
87 };
88
89 static int dsp_drain(fd_info *i);
90 static void fd_info_remove_from_list(fd_info *i);
91
92 static pthread_mutex_t fd_infos_mutex = PTHREAD_MUTEX_INITIALIZER;
93 static pthread_mutex_t func_mutex = PTHREAD_MUTEX_INITIALIZER;
94
95 static PA_LLIST_HEAD(fd_info, fd_infos) = NULL;
96
97 static int (*_ioctl)(int, int, void*) = NULL;
98 static int (*_close)(int) = NULL;
99 static int (*_open)(const char *, int, mode_t) = NULL;
100 static FILE* (*_fopen)(const char *path, const char *mode) = NULL;
101 static int (*_open64)(const char *, int, mode_t) = NULL;
102 static FILE* (*_fopen64)(const char *path, const char *mode) = NULL;
103 static int (*_fclose)(FILE *f) = NULL;
104
105 #define LOAD_IOCTL_FUNC() \
106 do { \
107 pthread_mutex_lock(&func_mutex); \
108 if (!_ioctl) \
109 _ioctl = (int (*)(int, int, void*)) dlsym(RTLD_NEXT, "ioctl"); \
110 pthread_mutex_unlock(&func_mutex); \
111 } while(0)
112
113 #define LOAD_OPEN_FUNC() \
114 do { \
115 pthread_mutex_lock(&func_mutex); \
116 if (!_open) \
117 _open = (int (*)(const char *, int, mode_t)) dlsym(RTLD_NEXT, "open"); \
118 pthread_mutex_unlock(&func_mutex); \
119 } while(0)
120
121 #define LOAD_OPEN64_FUNC() \
122 do { \
123 pthread_mutex_lock(&func_mutex); \
124 if (!_open64) \
125 _open64 = (int (*)(const char *, int, mode_t)) dlsym(RTLD_NEXT, "open64"); \
126 pthread_mutex_unlock(&func_mutex); \
127 } while(0)
128
129 #define LOAD_CLOSE_FUNC() \
130 do { \
131 pthread_mutex_lock(&func_mutex); \
132 if (!_close) \
133 _close = (int (*)(int)) dlsym(RTLD_NEXT, "close"); \
134 pthread_mutex_unlock(&func_mutex); \
135 } while(0)
136
137 #define LOAD_FOPEN_FUNC() \
138 do { \
139 pthread_mutex_lock(&func_mutex); \
140 if (!_fopen) \
141 _fopen = (FILE* (*)(const char *, const char*)) dlsym(RTLD_NEXT, "fopen"); \
142 pthread_mutex_unlock(&func_mutex); \
143 } while(0)
144
145 #define LOAD_FOPEN64_FUNC() \
146 do { \
147 pthread_mutex_lock(&func_mutex); \
148 if (!_fopen64) \
149 _fopen64 = (FILE* (*)(const char *, const char*)) dlsym(RTLD_NEXT, "fopen64"); \
150 pthread_mutex_unlock(&func_mutex); \
151 } while(0)
152
153 #define LOAD_FCLOSE_FUNC() \
154 do { \
155 pthread_mutex_lock(&func_mutex); \
156 if (!_fclose) \
157 _fclose = (int (*)(FILE *)) dlsym(RTLD_NEXT, "fclose"); \
158 pthread_mutex_unlock(&func_mutex); \
159 } while(0)
160
161 #define CONTEXT_CHECK_DEAD_GOTO(i, label) do { \
162 if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY) { \
163 debug(__FILE__": Not connected: %s", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
164 goto label; \
165 } \
166 } while(0);
167
168 #define STREAM_CHECK_DEAD_GOTO(i, label) do { \
169 if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY || \
170 !(i)->stream || pa_stream_get_state((i)->stream) != PA_STREAM_READY) { \
171 debug(__FILE__": Not connected: %s", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
172 goto label; \
173 } \
174 } while(0);
175
176 static void debug(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2);
177
178 static void debug(const char *format, ...) {
179 va_list ap;
180 if (getenv("PADSP_DEBUG")) {
181 va_start(ap, format);
182 vfprintf(stderr, format, ap);
183 va_end(ap);
184 }
185 }
186
187 static pthread_key_t recursion_key;
188
189 static void recursion_key_alloc(void) {
190 pthread_key_create(&recursion_key, NULL);
191 }
192
193 static int function_enter(void) {
194 /* Avoid recursive calls */
195 static pthread_once_t recursion_key_once = PTHREAD_ONCE_INIT;
196 pthread_once(&recursion_key_once, recursion_key_alloc);
197
198 if (pthread_getspecific(recursion_key))
199 return 0;
200
201 pthread_setspecific(recursion_key, (void*) 1);
202 return 1;
203 }
204
205 static void function_exit(void) {
206 pthread_setspecific(recursion_key, NULL);
207 }
208
209 static void fd_info_free(fd_info *i) {
210 assert(i);
211
212 debug(__FILE__": freeing fd info (fd=%i)\n", i->app_fd);
213
214 dsp_drain(i);
215
216 if (i->mainloop)
217 pa_threaded_mainloop_stop(i->mainloop);
218
219 if (i->stream) {
220 pa_stream_disconnect(i->stream);
221 pa_stream_unref(i->stream);
222 }
223
224 if (i->context) {
225 pa_context_disconnect(i->context);
226 pa_context_unref(i->context);
227 }
228
229 if (i->mainloop)
230 pa_threaded_mainloop_free(i->mainloop);
231
232 if (i->app_fd >= 0) {
233 LOAD_CLOSE_FUNC();
234 _close(i->app_fd);
235 }
236
237 if (i->thread_fd >= 0) {
238 LOAD_CLOSE_FUNC();
239 _close(i->thread_fd);
240 }
241
242 free(i->buf);
243
244 pthread_mutex_destroy(&i->mutex);
245 free(i);
246 }
247
248 static fd_info *fd_info_ref(fd_info *i) {
249 assert(i);
250
251 pthread_mutex_lock(&i->mutex);
252 assert(i->ref >= 1);
253 i->ref++;
254
255 /* debug(__FILE__": ref++, now %i\n", i->ref); */
256 pthread_mutex_unlock(&i->mutex);
257
258 return i;
259 }
260
261 static void fd_info_unref(fd_info *i) {
262 int r;
263 pthread_mutex_lock(&i->mutex);
264 assert(i->ref >= 1);
265 r = --i->ref;
266 /* debug(__FILE__": ref--, now %i\n", i->ref); */
267 pthread_mutex_unlock(&i->mutex);
268
269 if (r <= 0)
270 fd_info_free(i);
271 }
272
273 static void context_state_cb(pa_context *c, void *userdata) {
274 fd_info *i = userdata;
275 assert(c);
276
277 switch (pa_context_get_state(c)) {
278 case PA_CONTEXT_READY:
279 case PA_CONTEXT_TERMINATED:
280 case PA_CONTEXT_FAILED:
281 pa_threaded_mainloop_signal(i->mainloop, 0);
282 break;
283
284 case PA_CONTEXT_UNCONNECTED:
285 case PA_CONTEXT_CONNECTING:
286 case PA_CONTEXT_AUTHORIZING:
287 case PA_CONTEXT_SETTING_NAME:
288 break;
289 }
290 }
291
292 static void reset_params(fd_info *i) {
293 assert(i);
294
295 i->sample_spec.format = PA_SAMPLE_ULAW;
296 i->sample_spec.channels = 1;
297 i->sample_spec.rate = 8000;
298 i->fragment_size = 1024;
299 i->n_fragments = 0;
300 }
301
302 static char *client_name(char *buf, size_t n) {
303 char p[PATH_MAX];
304
305 if (pa_get_binary_name(p, sizeof(p)))
306 snprintf(buf, n, "oss[%s]", pa_path_get_filename(p));
307 else
308 snprintf(buf, n, "oss");
309
310 return buf;
311 }
312
313 static void atfork_prepare(void) {
314 fd_info *i;
315
316 debug(__FILE__": atfork_prepare() enter\n");
317
318 function_enter();
319
320 pthread_mutex_lock(&fd_infos_mutex);
321
322 for (i = fd_infos; i; i = i->next) {
323 pthread_mutex_lock(&i->mutex);
324 pa_threaded_mainloop_lock(i->mainloop);
325 }
326
327 pthread_mutex_lock(&func_mutex);
328
329
330 debug(__FILE__": atfork_prepare() exit\n");
331 }
332
333 static void atfork_parent(void) {
334 fd_info *i;
335
336 debug(__FILE__": atfork_parent() enter\n");
337
338 pthread_mutex_unlock(&func_mutex);
339
340 for (i = fd_infos; i; i = i->next) {
341 pa_threaded_mainloop_unlock(i->mainloop);
342 pthread_mutex_unlock(&i->mutex);
343 }
344
345 pthread_mutex_unlock(&fd_infos_mutex);
346
347 function_exit();
348
349 debug(__FILE__": atfork_parent() exit\n");
350 }
351
352 static void atfork_child(void) {
353 fd_info *i;
354
355 debug(__FILE__": atfork_child() enter\n");
356
357 /* We do only the bare minimum to get all fds closed */
358 pthread_mutex_init(&func_mutex, NULL);
359 pthread_mutex_init(&fd_infos_mutex, NULL);
360
361 for (i = fd_infos; i; i = i->next) {
362 pthread_mutex_init(&i->mutex, NULL);
363
364 if (i->context) {
365 pa_context_disconnect(i->context);
366 pa_context_unref(i->context);
367 i->context = NULL;
368 }
369
370 if (i->stream) {
371 pa_stream_unref(i->stream);
372 i->stream = NULL;
373 }
374
375 if (i->app_fd >= 0) {
376 close(i->app_fd);
377 i->app_fd = -1;
378 }
379
380 if (i->thread_fd >= 0) {
381 close(i->thread_fd);
382 i->thread_fd = -1;
383 }
384
385 i->unusable = 1;
386 }
387
388 function_exit();
389
390 debug(__FILE__": atfork_child() exit\n");
391 }
392
393 static void install_atfork(void) {
394 pthread_atfork(atfork_prepare, atfork_parent, atfork_child);
395 }
396
397 static void stream_success_cb(pa_stream *s, int success, void *userdata) {
398 fd_info *i = userdata;
399
400 assert(s);
401 assert(i);
402
403 i->operation_success = success;
404 pa_threaded_mainloop_signal(i->mainloop, 0);
405 }
406
407 static void context_success_cb(pa_context *c, int success, void *userdata) {
408 fd_info *i = userdata;
409
410 assert(c);
411 assert(i);
412
413 i->operation_success = success;
414 pa_threaded_mainloop_signal(i->mainloop, 0);
415 }
416
417 static fd_info* fd_info_new(fd_info_type_t type, int *_errno) {
418 fd_info *i;
419 int sfds[2] = { -1, -1 };
420 char name[64];
421 static pthread_once_t install_atfork_once = PTHREAD_ONCE_INIT;
422
423 debug(__FILE__": fd_info_new()\n");
424
425 signal(SIGPIPE, SIG_IGN); /* Yes, ugly as hell */
426
427 pthread_once(&install_atfork_once, install_atfork);
428
429 if (!(i = malloc(sizeof(fd_info)))) {
430 *_errno = ENOMEM;
431 goto fail;
432 }
433
434 i->app_fd = i->thread_fd = -1;
435 i->type = type;
436
437 i->mainloop = NULL;
438 i->context = NULL;
439 i->stream = NULL;
440 i->io_event = NULL;
441 pthread_mutex_init(&i->mutex, NULL);
442 i->ref = 1;
443 i->buf = NULL;
444 i->unusable = 0;
445 pa_cvolume_reset(&i->volume, 2);
446 i->volume_modify_count = 0;
447 i->sink_index = (uint32_t) -1;
448 PA_LLIST_INIT(fd_info, i);
449
450 reset_params(i);
451
452 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfds) < 0) {
453 *_errno = errno;
454 debug(__FILE__": socket() failed: %s\n", strerror(errno));
455 goto fail;
456 }
457
458 i->app_fd = sfds[0];
459 i->thread_fd = sfds[1];
460
461 if (!(i->mainloop = pa_threaded_mainloop_new())) {
462 *_errno = EIO;
463 debug(__FILE__": pa_threaded_mainloop_new() failed\n");
464 goto fail;
465 }
466
467 if (!(i->context = pa_context_new(pa_threaded_mainloop_get_api(i->mainloop), client_name(name, sizeof(name))))) {
468 *_errno = EIO;
469 debug(__FILE__": pa_context_new() failed\n");
470 goto fail;
471 }
472
473 pa_context_set_state_callback(i->context, context_state_cb, i);
474
475 if (pa_context_connect(i->context, NULL, 0, NULL) < 0) {
476 *_errno = ECONNREFUSED;
477 debug(__FILE__": pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
478 goto fail;
479 }
480
481 pa_threaded_mainloop_lock(i->mainloop);
482
483 if (pa_threaded_mainloop_start(i->mainloop) < 0) {
484 *_errno = EIO;
485 debug(__FILE__": pa_threaded_mainloop_start() failed\n");
486 goto unlock_and_fail;
487 }
488
489 /* Wait until the context is ready */
490 pa_threaded_mainloop_wait(i->mainloop);
491
492 if (pa_context_get_state(i->context) != PA_CONTEXT_READY) {
493 *_errno = ECONNREFUSED;
494 debug(__FILE__": pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
495 goto unlock_and_fail;
496 }
497
498 pa_threaded_mainloop_unlock(i->mainloop);
499 return i;
500
501 unlock_and_fail:
502
503 pa_threaded_mainloop_unlock(i->mainloop);
504
505 fail:
506
507 if (i)
508 fd_info_unref(i);
509
510 return NULL;
511 }
512
513 static void fd_info_add_to_list(fd_info *i) {
514 assert(i);
515
516 pthread_mutex_lock(&fd_infos_mutex);
517 PA_LLIST_PREPEND(fd_info, fd_infos, i);
518 pthread_mutex_unlock(&fd_infos_mutex);
519
520 fd_info_ref(i);
521 }
522
523 static void fd_info_remove_from_list(fd_info *i) {
524 assert(i);
525
526 pthread_mutex_lock(&fd_infos_mutex);
527 PA_LLIST_REMOVE(fd_info, fd_infos, i);
528 pthread_mutex_unlock(&fd_infos_mutex);
529
530 fd_info_unref(i);
531 }
532
533 static fd_info* fd_info_find(int fd) {
534 fd_info *i;
535
536 pthread_mutex_lock(&fd_infos_mutex);
537
538 for (i = fd_infos; i; i = i->next)
539 if (i->app_fd == fd && !i->unusable) {
540 fd_info_ref(i);
541 break;
542 }
543
544 pthread_mutex_unlock(&fd_infos_mutex);
545
546 return i;
547 }
548
549 static void fix_metrics(fd_info *i) {
550 size_t fs;
551 char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
552
553 fs = pa_frame_size(&i->sample_spec);
554 i->fragment_size = (i->fragment_size/fs)*fs;
555
556 if (i->n_fragments < 2)
557 i->n_fragments = 12;
558
559 if (i->fragment_size <= 0)
560 if ((i->fragment_size = pa_bytes_per_second(&i->sample_spec) / 2 / i->n_fragments) <= 0)
561 i->fragment_size = 1024;
562
563 debug(__FILE__": sample spec: %s\n", pa_sample_spec_snprint(t, sizeof(t), &i->sample_spec));
564 debug(__FILE__": fixated metrics to %i fragments, %li bytes each.\n", i->n_fragments, (long)i->fragment_size);
565 }
566
567 static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
568 fd_info *i = userdata;
569 assert(s);
570
571 if (i->io_event) {
572 pa_mainloop_api *api;
573 api = pa_threaded_mainloop_get_api(i->mainloop);
574 api->io_enable(i->io_event, PA_IO_EVENT_INPUT);
575 }
576 }
577
578 static void stream_latency_update_cb(pa_stream *s, void *userdata) {
579 fd_info *i = userdata;
580 assert(s);
581
582 pa_threaded_mainloop_signal(i->mainloop, 0);
583 }
584
585 static void fd_info_shutdown(fd_info *i) {
586 assert(i);
587
588 if (i->io_event) {
589 pa_mainloop_api *api;
590 api = pa_threaded_mainloop_get_api(i->mainloop);
591 api->io_free(i->io_event);
592 i->io_event = NULL;
593 }
594
595 if (i->thread_fd >= 0) {
596 close(i->thread_fd);
597 i->thread_fd = -1;
598 }
599 }
600
601 static int fd_info_copy_data(fd_info *i, int force) {
602 size_t n;
603
604 if (!i->stream)
605 return -1;
606
607 if ((n = pa_stream_writable_size(i->stream)) == (size_t) -1) {
608 debug(__FILE__": pa_stream_writable_size(): %s\n", pa_strerror(pa_context_errno(i->context)));
609 return -1;
610 }
611
612 while (n >= i->fragment_size || force) {
613 ssize_t r;
614
615 if (!i->buf) {
616 if (!(i->buf = malloc(i->fragment_size))) {
617 debug(__FILE__": malloc() failed.\n");
618 return -1;
619 }
620 }
621
622 if ((r = read(i->thread_fd, i->buf, i->fragment_size)) <= 0) {
623
624 if (errno == EAGAIN)
625 break;
626
627 debug(__FILE__": read(): %s\n", r == 0 ? "EOF" : strerror(errno));
628 return -1;
629 }
630
631 if (pa_stream_write(i->stream, i->buf, r, free, 0, PA_SEEK_RELATIVE) < 0) {
632 debug(__FILE__": pa_stream_write(): %s\n", pa_strerror(pa_context_errno(i->context)));
633 return -1;
634 }
635
636 i->buf = NULL;
637
638 assert(n >= (size_t) r);
639 n -= r;
640 }
641
642 if (i->io_event) {
643 pa_mainloop_api *api;
644 api = pa_threaded_mainloop_get_api(i->mainloop);
645 api->io_enable(i->io_event, n >= i->fragment_size ? PA_IO_EVENT_INPUT : 0);
646 }
647
648 return 0;
649 }
650
651 static void stream_state_cb(pa_stream *s, void * userdata) {
652 fd_info *i = userdata;
653 assert(s);
654
655 switch (pa_stream_get_state(s)) {
656
657 case PA_STREAM_READY:
658 debug(__FILE__": stream established.\n");
659 break;
660
661 case PA_STREAM_FAILED:
662 debug(__FILE__": pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
663 fd_info_shutdown(i);
664 break;
665
666 case PA_STREAM_TERMINATED:
667 case PA_STREAM_UNCONNECTED:
668 case PA_STREAM_CREATING:
669 break;
670 }
671 }
672
673 static int create_stream(fd_info *i) {
674 pa_buffer_attr attr;
675 int n;
676
677 assert(i);
678
679 fix_metrics(i);
680
681 if (!(i->stream = pa_stream_new(i->context, "Audio Stream", &i->sample_spec, NULL))) {
682 debug(__FILE__": pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
683 goto fail;
684 }
685
686 pa_stream_set_state_callback(i->stream, stream_state_cb, i);
687 pa_stream_set_write_callback(i->stream, stream_request_cb, i);
688 pa_stream_set_latency_update_callback(i->stream, stream_latency_update_cb, i);
689
690 memset(&attr, 0, sizeof(attr));
691 attr.maxlength = i->fragment_size * (i->n_fragments+1);
692 attr.tlength = i->fragment_size * i->n_fragments;
693 attr.prebuf = i->fragment_size;
694 attr.minreq = i->fragment_size;
695
696 if (pa_stream_connect_playback(i->stream, NULL, &attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0) {
697 debug(__FILE__": pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
698 goto fail;
699 }
700
701 n = i->fragment_size;
702 setsockopt(i->app_fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n));
703 n = i->fragment_size;
704 setsockopt(i->thread_fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
705
706 return 0;
707
708 fail:
709 return -1;
710 }
711
712 static void free_stream(fd_info *i) {
713 assert(i);
714
715 if (i->stream) {
716 pa_stream_disconnect(i->stream);
717 pa_stream_unref(i->stream);
718 i->stream = NULL;
719 }
720 }
721
722 static void io_event_cb(pa_mainloop_api *api, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
723 fd_info *i = userdata;
724
725 pa_threaded_mainloop_signal(i->mainloop, 0);
726
727 if (flags & PA_IO_EVENT_INPUT) {
728
729 if (!i->stream) {
730 api->io_enable(e, 0);
731
732 if (create_stream(i) < 0)
733 goto fail;
734
735 } else {
736 if (fd_info_copy_data(i, 0) < 0)
737 goto fail;
738 }
739
740 } else if (flags & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR))
741 goto fail;
742
743 return;
744
745 fail:
746 /* We can't do anything better than removing the event source */
747 fd_info_shutdown(i);
748 }
749
750 static int dsp_open(int flags, int *_errno) {
751 fd_info *i;
752 pa_mainloop_api *api;
753 int ret;
754 int f;
755
756 if ((flags != O_WRONLY) && (flags != (O_WRONLY|O_NONBLOCK))) {
757 *_errno = EACCES;
758 return -1;
759 }
760
761 if (!(i = fd_info_new(FD_INFO_PLAYBACK, _errno)))
762 return -1;
763
764 shutdown(i->thread_fd, SHUT_WR);
765 shutdown(i->app_fd, SHUT_RD);
766
767 if ((flags & O_NONBLOCK) == O_NONBLOCK) {
768 if ((f = fcntl(i->app_fd, F_GETFL)) >= 0)
769 fcntl(i->app_fd, F_SETFL, f|O_NONBLOCK);
770 }
771 if ((f = fcntl(i->thread_fd, F_GETFL)) >= 0)
772 fcntl(i->thread_fd, F_SETFL, f|O_NONBLOCK);
773
774 fcntl(i->app_fd, F_SETFD, FD_CLOEXEC);
775 fcntl(i->thread_fd, F_SETFD, FD_CLOEXEC);
776
777 pa_threaded_mainloop_lock(i->mainloop);
778 api = pa_threaded_mainloop_get_api(i->mainloop);
779 if (!(i->io_event = api->io_new(api, i->thread_fd, PA_IO_EVENT_INPUT, io_event_cb, i)))
780 goto fail;
781
782 pa_threaded_mainloop_unlock(i->mainloop);
783
784 debug(__FILE__": dsp_open() succeeded, fd=%i\n", i->app_fd);
785
786 fd_info_add_to_list(i);
787 ret = i->app_fd;
788 fd_info_unref(i);
789
790 return ret;
791
792 fail:
793 pa_threaded_mainloop_unlock(i->mainloop);
794
795 if (i)
796 fd_info_unref(i);
797
798 *_errno = EIO;
799
800 debug(__FILE__": dsp_open() failed\n");
801
802 return -1;
803 }
804
805 static void sink_info_cb(pa_context *context, const pa_sink_info *si, int eol, void *userdata) {
806 fd_info *i = userdata;
807
808 if (!si && eol < 0) {
809 i->operation_success = 0;
810 pa_threaded_mainloop_signal(i->mainloop, 0);
811 return;
812 }
813
814 if (eol)
815 return;
816
817 if (!pa_cvolume_equal(&i->volume, &si->volume))
818 i->volume_modify_count++;
819
820 i->volume = si->volume;
821 i->sink_index = si->index;
822
823 i->operation_success = 1;
824 pa_threaded_mainloop_signal(i->mainloop, 0);
825 }
826
827 static void subscribe_cb(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
828 fd_info *i = userdata;
829 pa_operation *o = NULL;
830
831 if (i->sink_index != idx)
832 return;
833
834 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
835 return;
836
837 if (!(o = pa_context_get_sink_info_by_index(i->context, i->sink_index, sink_info_cb, i))) {
838 debug(__FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
839 return;
840 }
841
842 pa_operation_unref(o);
843 }
844
845 static int mixer_open(int flags, int *_errno) {
846 fd_info *i;
847 pa_operation *o;
848 int ret;
849
850 if (!(i = fd_info_new(FD_INFO_MIXER, _errno)))
851 return -1;
852
853 pa_threaded_mainloop_lock(i->mainloop);
854
855 pa_context_set_subscribe_callback(i->context, subscribe_cb, i);
856
857 if (!(o = pa_context_subscribe(i->context, PA_SUBSCRIPTION_MASK_SINK, context_success_cb, i))) {
858 debug(__FILE__": Failed to subscribe to events: %s", pa_strerror(pa_context_errno(i->context)));
859 *_errno = EIO;
860 goto fail;
861 }
862
863 i->operation_success = 0;
864 while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
865 pa_threaded_mainloop_wait(i->mainloop);
866 CONTEXT_CHECK_DEAD_GOTO(i, fail);
867 }
868
869 if (!i->operation_success) {
870 debug(__FILE__":Failed to subscribe to events: %s", pa_strerror(pa_context_errno(i->context)));
871 *_errno = EIO;
872 goto fail;
873 }
874
875 /* Get sink info */
876
877 pa_operation_unref(o);
878 if (!(o = pa_context_get_sink_info_by_name(i->context, NULL, sink_info_cb, i))) {
879 debug(__FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
880 *_errno = EIO;
881 goto fail;
882 }
883
884 i->operation_success = 0;
885 while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
886 pa_threaded_mainloop_wait(i->mainloop);
887 CONTEXT_CHECK_DEAD_GOTO(i, fail);
888 }
889
890 if (!i->operation_success) {
891 debug(__FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
892 *_errno = EIO;
893 goto fail;
894 }
895
896 pa_threaded_mainloop_unlock(i->mainloop);
897
898 debug(__FILE__": mixer_open() succeeded, fd=%i\n", i->app_fd);
899
900 fd_info_add_to_list(i);
901 ret = i->app_fd;
902 fd_info_unref(i);
903
904 return ret;
905
906 fail:
907 pa_threaded_mainloop_unlock(i->mainloop);
908
909 if (i)
910 fd_info_unref(i);
911
912 *_errno = EIO;
913
914 debug(__FILE__": mixer_open() failed\n");
915
916 return -1;
917 }
918
919 static int sndstat_open(int flags, int *_errno) {
920 static const char sndstat[] =
921 "Sound Driver:3.8.1a-980706 (Polypaudio Virtual OSS)\n"
922 "Kernel: POSIX\n"
923 "Config options: 0\n"
924 "\n"
925 "Installed drivers:\n"
926 "Type 255: Polypaudio Virtual OSS\n"
927 "\n"
928 "Card config:\n"
929 "Polypaudio Virtual OSS\n"
930 "\n"
931 "Audio devices:\n"
932 "0: Polypaudio Virtual OSS\n"
933 "\n"
934 "Synth devices: NOT ENABLED IN CONFIG\n"
935 "\n"
936 "Midi devices:\n"
937 "\n"
938 "Timers:\n"
939 "\n"
940 "\Mixers:\n"
941 "0: Polypaudio Virtual OSS\n";
942
943 char fn[] = "/tmp/padsp-sndstat-XXXXXX";
944 mode_t u;
945 int fd = -1;
946 int e;
947
948 debug(__FILE__": sndstat_open()\n");
949
950 if (flags != O_RDONLY && flags != (O_RDONLY|O_LARGEFILE)) {
951 *_errno = EACCES;
952 debug(__FILE__": bad access!\n");
953 goto fail;
954 }
955
956 u = umask(0077);
957 fd = mkstemp(fn);
958 e = errno;
959 umask(u);
960
961 if (fd < 0) {
962 *_errno = e;
963 debug(__FILE__": mkstemp() failed: %s\n", strerror(errno));
964 goto fail;
965 }
966
967 unlink(fn);
968
969 if (write(fd, sndstat, sizeof(sndstat) -1) != sizeof(sndstat)-1) {
970 *_errno = errno;
971 debug(__FILE__": write() failed: %s\n", strerror(errno));
972 goto fail;
973 }
974
975 if (lseek(fd, SEEK_SET, 0) < 0) {
976 *_errno = errno;
977 debug(__FILE__": lseek() failed: %s\n", strerror(errno));
978 goto fail;
979 }
980
981 return fd;
982
983 fail:
984 if (fd >= 0)
985 close(fd);
986 return -1;
987 }
988
989 int open(const char *filename, int flags, ...) {
990 va_list args;
991 mode_t mode = 0;
992 int r, _errno = 0;
993
994 debug(__FILE__": open(%s)\n", filename);
995
996 va_start(args, flags);
997 if (flags & O_CREAT)
998 mode = va_arg(args, mode_t);
999 va_end(args);
1000
1001 if (!function_enter()) {
1002 LOAD_OPEN_FUNC();
1003 return _open(filename, flags, mode);
1004 }
1005
1006 if (strcmp(filename, "/dev/dsp") == 0 || strcmp(filename, "/dev/adsp") == 0) {
1007 r = dsp_open(flags, &_errno);
1008 } else if (strcmp(filename, "/dev/mixer") == 0) {
1009 r = mixer_open(flags, &_errno);
1010 } else if (strcmp(filename, "/dev/sndstat") == 0) {
1011 r = sndstat_open(flags, &_errno);
1012 } else {
1013 function_exit();
1014 LOAD_OPEN_FUNC();
1015 return _open(filename, flags, mode);
1016 }
1017
1018 function_exit();
1019
1020 if (_errno)
1021 errno = _errno;
1022
1023 return r;
1024 }
1025
1026 static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {
1027 int ret = -1;
1028
1029 switch (request) {
1030 case SOUND_MIXER_READ_DEVMASK :
1031 debug(__FILE__": SOUND_MIXER_READ_DEVMASK\n");
1032
1033 *(int*) argp = SOUND_MASK_PCM;
1034 break;
1035
1036 case SOUND_MIXER_READ_RECMASK :
1037 debug(__FILE__": SOUND_MIXER_READ_RECMASK\n");
1038
1039 *(int*) argp = 0;
1040 break;
1041
1042 case SOUND_MIXER_READ_STEREODEVS:
1043 debug(__FILE__": SOUND_MIXER_READ_STEREODEVS\n");
1044
1045 pa_threaded_mainloop_lock(i->mainloop);
1046 *(int*) argp = i->volume.channels > 1 ? SOUND_MASK_PCM : 0;
1047 pa_threaded_mainloop_unlock(i->mainloop);
1048
1049 break;
1050
1051 case SOUND_MIXER_READ_RECSRC:
1052 debug(__FILE__": SOUND_MIXER_READ_RECSRC\n");
1053
1054 *(int*) argp = 0;
1055 break;
1056
1057 case SOUND_MIXER_CAPS:
1058 debug(__FILE__": SOUND_MIXER_CAPS\n");
1059
1060 *(int*) argp = 0;
1061 break;
1062
1063 case SOUND_MIXER_READ_PCM:
1064
1065 debug(__FILE__": SOUND_MIXER_READ_PCM\n");
1066
1067 pa_threaded_mainloop_lock(i->mainloop);
1068
1069 *(int*) argp =
1070 ((i->volume.values[0]*100/PA_VOLUME_NORM) << 8) |
1071 ((i->volume.values[i->volume.channels > 1 ? 1 : 0]*100/PA_VOLUME_NORM));
1072
1073 pa_threaded_mainloop_unlock(i->mainloop);
1074
1075 break;
1076
1077 case SOUND_MIXER_WRITE_PCM: {
1078 pa_cvolume v;
1079
1080 debug(__FILE__": SOUND_MIXER_WRITE_PCM\n");
1081
1082 pa_threaded_mainloop_lock(i->mainloop);
1083
1084 v = i->volume;
1085
1086 i->volume.values[0] = ((*(int*) argp >> 8)*PA_VOLUME_NORM)/100;
1087 i->volume.values[1] = ((*(int*) argp & 0xFF)*PA_VOLUME_NORM)/100;
1088
1089 if (!pa_cvolume_equal(&i->volume, &v)) {
1090 pa_operation *o;
1091
1092 if (!(o = pa_context_set_sink_volume_by_index(i->context, i->sink_index, &i->volume, NULL, NULL)))
1093 debug(__FILE__":Failed set volume: %s", pa_strerror(pa_context_errno(i->context)));
1094 else
1095 pa_operation_unref(o);
1096
1097 /* We don't wait for completion here */
1098 i->volume_modify_count++;
1099 }
1100
1101 pa_threaded_mainloop_unlock(i->mainloop);
1102
1103 break;
1104 }
1105
1106 case SOUND_MIXER_INFO: {
1107 mixer_info *mi = argp;
1108
1109 memset(mi, 0, sizeof(mixer_info));
1110 strncpy(mi->id, "POLYPAUDIO", sizeof(mi->id));
1111 strncpy(mi->name, "Polypaudio Virtual OSS", sizeof(mi->name));
1112 pa_threaded_mainloop_lock(i->mainloop);
1113 mi->modify_counter = i->volume_modify_count;
1114 pa_threaded_mainloop_unlock(i->mainloop);
1115 break;
1116 }
1117
1118 default:
1119 debug(__FILE__": unknwon ioctl 0x%08lx\n", request);
1120
1121 *_errno = EINVAL;
1122 goto fail;
1123 }
1124
1125 ret = 0;
1126
1127 fail:
1128
1129 return ret;
1130 }
1131
1132 static int map_format(int *fmt, pa_sample_spec *ss) {
1133
1134 switch (*fmt) {
1135 case AFMT_MU_LAW:
1136 ss->format = PA_SAMPLE_ULAW;
1137 break;
1138
1139 case AFMT_A_LAW:
1140 ss->format = PA_SAMPLE_ALAW;
1141 break;
1142
1143 case AFMT_S8:
1144 *fmt = AFMT_U8;
1145 /* fall through */
1146 case AFMT_U8:
1147 ss->format = PA_SAMPLE_U8;
1148 break;
1149
1150 case AFMT_U16_BE:
1151 *fmt = AFMT_S16_BE;
1152 /* fall through */
1153 case AFMT_S16_BE:
1154 ss->format = PA_SAMPLE_S16BE;
1155 break;
1156
1157 case AFMT_U16_LE:
1158 *fmt = AFMT_S16_LE;
1159 /* fall through */
1160 case AFMT_S16_LE:
1161 ss->format = PA_SAMPLE_S16LE;
1162 break;
1163
1164 default:
1165 ss->format = PA_SAMPLE_S16NE;
1166 *fmt = AFMT_S16_NE;
1167 break;
1168 }
1169
1170 return 0;
1171 }
1172
1173 static int map_format_back(pa_sample_format_t format) {
1174 switch (format) {
1175 case PA_SAMPLE_S16LE: return AFMT_S16_LE;
1176 case PA_SAMPLE_S16BE: return AFMT_S16_BE;
1177 case PA_SAMPLE_ULAW: return AFMT_MU_LAW;
1178 case PA_SAMPLE_ALAW: return AFMT_A_LAW;
1179 case PA_SAMPLE_U8: return AFMT_U8;
1180 default:
1181 abort();
1182 }
1183 }
1184
1185 static int dsp_flush_socket(fd_info *i) {
1186 int l;
1187
1188 if (i->thread_fd < 0)
1189 return -1;
1190
1191 if (ioctl(i->thread_fd, SIOCINQ, &l) < 0) {
1192 debug(__FILE__": SIOCINQ: %s\n", strerror(errno));
1193 return -1;
1194 }
1195
1196 while (l > 0) {
1197 char buf[1024];
1198 size_t k;
1199
1200 k = (size_t) l > sizeof(buf) ? sizeof(buf) : (size_t) l;
1201 if (read(i->thread_fd, buf, k) < 0)
1202 debug(__FILE__": read(): %s\n", strerror(errno));
1203 l -= k;
1204 }
1205
1206 return 0;
1207 }
1208
1209 static int dsp_empty_socket(fd_info *i) {
1210 int ret = -1;
1211
1212 /* Empty the socket */
1213 for (;;) {
1214 int l;
1215
1216 if (i->thread_fd < 0)
1217 break;
1218
1219 if (ioctl(i->thread_fd, SIOCINQ, &l) < 0) {
1220 debug(__FILE__": SIOCINQ: %s\n", strerror(errno));
1221 break;
1222 }
1223
1224 if (!l) {
1225 ret = 0;
1226 break;
1227 }
1228
1229 pa_threaded_mainloop_wait(i->mainloop);
1230 }
1231
1232 return ret;
1233 }
1234
1235 static int dsp_drain(fd_info *i) {
1236 pa_operation *o = NULL;
1237 int r = -1;
1238
1239 if (!i->mainloop)
1240 return 0;
1241
1242 debug(__FILE__": Draining.\n");
1243
1244 pa_threaded_mainloop_lock(i->mainloop);
1245
1246 if (dsp_empty_socket(i) < 0)
1247 goto fail;
1248
1249 if (!i->stream)
1250 goto fail;
1251
1252 debug(__FILE__": Really draining.\n");
1253
1254 if (!(o = pa_stream_drain(i->stream, stream_success_cb, i))) {
1255 debug(__FILE__": pa_stream_drain(): %s\n", pa_strerror(pa_context_errno(i->context)));
1256 goto fail;
1257 }
1258
1259 i->operation_success = 0;
1260 while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
1261 STREAM_CHECK_DEAD_GOTO(i, fail);
1262
1263 pa_threaded_mainloop_wait(i->mainloop);
1264 }
1265
1266 if (!i->operation_success) {
1267 debug(__FILE__": pa_stream_drain() 2: %s\n", pa_strerror(pa_context_errno(i->context)));
1268 goto fail;
1269 }
1270
1271 r = 0;
1272
1273 fail:
1274
1275 if (o)
1276 pa_operation_unref(o);
1277
1278 pa_threaded_mainloop_unlock(i->mainloop);
1279
1280 return 0;
1281 }
1282
1283 static int dsp_trigger(fd_info *i) {
1284 pa_operation *o = NULL;
1285 int r = -1;
1286
1287 if (!i->stream)
1288 return 0;
1289
1290 pa_threaded_mainloop_lock(i->mainloop);
1291
1292 if (dsp_empty_socket(i) < 0)
1293 goto fail;
1294
1295 debug(__FILE__": Triggering.\n");
1296
1297 if (!(o = pa_stream_trigger(i->stream, stream_success_cb, i))) {
1298 debug(__FILE__": pa_stream_trigger(): %s\n", pa_strerror(pa_context_errno(i->context)));
1299 goto fail;
1300 }
1301
1302 i->operation_success = 0;
1303 while (!pa_operation_get_state(o) != PA_OPERATION_DONE) {
1304 STREAM_CHECK_DEAD_GOTO(i, fail);
1305
1306 pa_threaded_mainloop_wait(i->mainloop);
1307 }
1308
1309 if (!i->operation_success) {
1310 debug(__FILE__": pa_stream_trigger(): %s\n", pa_strerror(pa_context_errno(i->context)));
1311 goto fail;
1312 }
1313
1314 r = 0;
1315
1316 fail:
1317
1318 if (o)
1319 pa_operation_unref(o);
1320
1321 pa_threaded_mainloop_unlock(i->mainloop);
1322
1323 return 0;
1324 }
1325
1326 static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {
1327 int ret = -1;
1328
1329 switch (request) {
1330 case SNDCTL_DSP_SETFMT: {
1331 debug(__FILE__": SNDCTL_DSP_SETFMT: %i\n", *(int*) argp);
1332
1333 pa_threaded_mainloop_lock(i->mainloop);
1334
1335 if (*(int*) argp == AFMT_QUERY)
1336 *(int*) argp = map_format_back(i->sample_spec.format);
1337 else {
1338 map_format((int*) argp, &i->sample_spec);
1339 free_stream(i);
1340 }
1341
1342 pa_threaded_mainloop_unlock(i->mainloop);
1343 break;
1344 }
1345
1346 case SNDCTL_DSP_SPEED: {
1347 pa_sample_spec ss;
1348 int valid;
1349 char t[256];
1350
1351 debug(__FILE__": SNDCTL_DSP_SPEED: %i\n", *(int*) argp);
1352
1353 pa_threaded_mainloop_lock(i->mainloop);
1354
1355 ss = i->sample_spec;
1356 ss.rate = *(int*) argp;
1357
1358 if ((valid = pa_sample_spec_valid(&ss))) {
1359 i->sample_spec = ss;
1360 free_stream(i);
1361 }
1362
1363 debug(__FILE__": ss: %s\n", pa_sample_spec_snprint(t, sizeof(t), &i->sample_spec));
1364
1365 pa_threaded_mainloop_unlock(i->mainloop);
1366
1367 if (!valid) {
1368 *_errno = EINVAL;
1369 goto fail;
1370 }
1371
1372 break;
1373 }
1374
1375 case SNDCTL_DSP_STEREO:
1376 debug(__FILE__": SNDCTL_DSP_STEREO: %i\n", *(int*) argp);
1377
1378 pa_threaded_mainloop_lock(i->mainloop);
1379
1380 i->sample_spec.channels = *(int*) argp ? 2 : 1;
1381 free_stream(i);
1382
1383 pa_threaded_mainloop_unlock(i->mainloop);
1384 return 0;
1385
1386 case SNDCTL_DSP_CHANNELS: {
1387 pa_sample_spec ss;
1388 int valid;
1389
1390 debug(__FILE__": SNDCTL_DSP_CHANNELS: %i\n", *(int*) argp);
1391
1392 pa_threaded_mainloop_lock(i->mainloop);
1393
1394 ss = i->sample_spec;
1395 ss.channels = *(int*) argp;
1396
1397 if ((valid = pa_sample_spec_valid(&ss))) {
1398 i->sample_spec = ss;
1399 free_stream(i);
1400 }
1401
1402 pa_threaded_mainloop_unlock(i->mainloop);
1403
1404 if (!valid) {
1405 *_errno = EINVAL;
1406 goto fail;
1407 }
1408
1409 break;
1410 }
1411
1412 case SNDCTL_DSP_GETBLKSIZE:
1413 debug(__FILE__": SNDCTL_DSP_GETBLKSIZE\n");
1414
1415 pa_threaded_mainloop_lock(i->mainloop);
1416
1417 fix_metrics(i);
1418 *(int*) argp = i->fragment_size;
1419
1420 pa_threaded_mainloop_unlock(i->mainloop);
1421
1422 break;
1423
1424 case SNDCTL_DSP_SETFRAGMENT:
1425 debug(__FILE__": SNDCTL_DSP_SETFRAGMENT: 0x%8x\n", *(int*) argp);
1426
1427 pa_threaded_mainloop_lock(i->mainloop);
1428
1429 i->fragment_size = 1 << (*(int*) argp);
1430 i->n_fragments = (*(int*) argp) >> 16;
1431
1432 free_stream(i);
1433
1434 pa_threaded_mainloop_unlock(i->mainloop);
1435
1436 break;
1437
1438 case SNDCTL_DSP_GETCAPS:
1439 debug(__FILE__": SNDCTL_DSP_CAPS\n");
1440
1441 *(int*) argp = DSP_CAP_MULTI;
1442 break;
1443
1444 case SNDCTL_DSP_GETODELAY: {
1445 int l;
1446
1447 debug(__FILE__": SNDCTL_DSP_GETODELAY\n");
1448
1449 pa_threaded_mainloop_lock(i->mainloop);
1450
1451 *(int*) argp = 0;
1452
1453 for (;;) {
1454 pa_usec_t usec;
1455
1456 STREAM_CHECK_DEAD_GOTO(i, exit_loop);
1457
1458 if (pa_stream_get_latency(i->stream, &usec, NULL) >= 0) {
1459 *(int*) argp = pa_usec_to_bytes(usec, &i->sample_spec);
1460 break;
1461 }
1462
1463 if (pa_context_errno(i->context) != PA_ERR_NODATA) {
1464 debug(__FILE__": pa_stream_get_latency(): %s\n", pa_strerror(pa_context_errno(i->context)));
1465 break;
1466 }
1467
1468 pa_threaded_mainloop_wait(i->mainloop);
1469 }
1470
1471 exit_loop:
1472
1473 if (ioctl(i->thread_fd, SIOCINQ, &l) < 0)
1474 debug(__FILE__": SIOCINQ failed: %s\n", strerror(errno));
1475 else
1476 *(int*) argp += l;
1477
1478 pa_threaded_mainloop_unlock(i->mainloop);
1479
1480 debug(__FILE__": ODELAY: %i\n", *(int*) argp);
1481
1482 break;
1483 }
1484
1485 case SNDCTL_DSP_RESET: {
1486 debug(__FILE__": SNDCTL_DSP_RESET\n");
1487
1488 pa_threaded_mainloop_lock(i->mainloop);
1489
1490 free_stream(i);
1491 dsp_flush_socket(i);
1492 reset_params(i);
1493
1494 pa_threaded_mainloop_unlock(i->mainloop);
1495 break;
1496 }
1497
1498 case SNDCTL_DSP_GETFMTS: {
1499 debug(__FILE__": SNDCTL_DSP_GETFMTS\n");
1500
1501 *(int*) argp = AFMT_MU_LAW|AFMT_A_LAW|AFMT_U8|AFMT_S16_LE|AFMT_S16_BE;
1502 break;
1503 }
1504
1505 case SNDCTL_DSP_POST:
1506 debug(__FILE__": SNDCTL_DSP_POST\n");
1507
1508 if (dsp_trigger(i) < 0)
1509 *_errno = EIO;
1510 break;
1511
1512 case SNDCTL_DSP_SYNC:
1513 debug(__FILE__": SNDCTL_DSP_SYNC\n");
1514
1515 if (dsp_drain(i) < 0)
1516 *_errno = EIO;
1517
1518 break;
1519
1520 case SNDCTL_DSP_GETOSPACE: {
1521 audio_buf_info *bi = (audio_buf_info*) argp;
1522 int l;
1523 size_t k = 0;
1524
1525 debug(__FILE__": SNDCTL_DSP_GETOSPACE\n");
1526
1527 pa_threaded_mainloop_lock(i->mainloop);
1528
1529 fix_metrics(i);
1530
1531 if (i->stream) {
1532 if ((k = pa_stream_writable_size(i->stream)) == (size_t) -1)
1533 debug(__FILE__": pa_stream_writable_size(): %s\n", pa_strerror(pa_context_errno(i->context)));
1534 } else
1535 k = i->fragment_size * i->n_fragments;
1536
1537 if (ioctl(i->thread_fd, SIOCINQ, &l) < 0) {
1538 debug(__FILE__": SIOCINQ failed: %s\n", strerror(errno));
1539 l = 0;
1540 }
1541
1542 bi->fragsize = i->fragment_size;
1543 bi->fragstotal = i->n_fragments;
1544 bi->bytes = k > (size_t) l ? k - l : 0;
1545 bi->fragments = bi->bytes / bi->fragsize;
1546
1547 pa_threaded_mainloop_unlock(i->mainloop);
1548
1549 debug(__FILE__": fragsize=%i, fragstotal=%i, bytes=%i, fragments=%i\n", bi->fragsize, bi->fragstotal, bi->bytes, bi->fragments);
1550
1551 break;
1552 }
1553
1554 default:
1555 debug(__FILE__": unknwon ioctl 0x%08lx\n", request);
1556
1557 *_errno = EINVAL;
1558 goto fail;
1559 }
1560
1561 ret = 0;
1562
1563 fail:
1564
1565 return ret;
1566 }
1567
1568 int ioctl(int fd, unsigned long request, ...) {
1569 fd_info *i;
1570 va_list args;
1571 void *argp;
1572 int r, _errno = 0;
1573
1574 debug(__FILE__": ioctl()\n");
1575
1576 va_start(args, request);
1577 argp = va_arg(args, void *);
1578 va_end(args);
1579
1580 if (!function_enter()) {
1581 LOAD_IOCTL_FUNC();
1582 return _ioctl(fd, request, argp);
1583 }
1584
1585 if (!(i = fd_info_find(fd))) {
1586 function_exit();
1587 LOAD_IOCTL_FUNC();
1588 return _ioctl(fd, request, argp);
1589 }
1590
1591 if (i->type == FD_INFO_MIXER)
1592 r = mixer_ioctl(i, request, argp, &_errno);
1593 else
1594 r = dsp_ioctl(i, request, argp, &_errno);
1595
1596 fd_info_unref(i);
1597
1598 if (_errno)
1599 errno = _errno;
1600
1601 function_exit();
1602
1603 return r;
1604 }
1605
1606 int close(int fd) {
1607 fd_info *i;
1608
1609 debug(__FILE__": close()\n");
1610
1611 if (!function_enter()) {
1612 LOAD_CLOSE_FUNC();
1613 return _close(fd);
1614 }
1615
1616 if (!(i = fd_info_find(fd))) {
1617 function_exit();
1618 LOAD_CLOSE_FUNC();
1619 return _close(fd);
1620 }
1621
1622 fd_info_remove_from_list(i);
1623 fd_info_unref(i);
1624
1625 function_exit();
1626
1627 return 0;
1628 }
1629
1630 int open64(const char *filename, int flags, ...) {
1631 va_list args;
1632 mode_t mode = 0;
1633
1634 debug(__FILE__": open64(%s)\n", filename);
1635
1636 va_start(args, flags);
1637 if (flags & O_CREAT)
1638 mode = va_arg(args, mode_t);
1639 va_end(args);
1640
1641 if (strcmp(filename, "/dev/dsp") != 0 &&
1642 strcmp(filename, "/dev/adsp") != 0 &&
1643 strcmp(filename, "/dev/sndstat") != 0 &&
1644 strcmp(filename, "/dev/mixer") != 0) {
1645 LOAD_OPEN64_FUNC();
1646 return _open64(filename, flags, mode);
1647 }
1648
1649 return open(filename, flags, mode);
1650 }
1651
1652 FILE* fopen(const char *filename, const char *mode) {
1653 FILE *f = NULL;
1654 int fd;
1655 mode_t m;
1656
1657 debug(__FILE__": fopen(%s)\n", filename);
1658
1659 if (strcmp(filename, "/dev/dsp") == 0 ||
1660 strcmp(filename, "/dev/adsp") == 0) {
1661
1662 if (strcmp(mode, "wb") != 0) {
1663 errno = EACCES;
1664 return NULL;
1665 }
1666
1667 m = O_WRONLY;
1668 } else if (strcmp(filename, "/dev/sndstat") == 0) {
1669
1670 if (strcmp(mode, "r") != 0) {
1671 errno = EACCES;
1672 return NULL;
1673 }
1674
1675 m = O_RDONLY;
1676 } else if (strcmp(filename, "/dev/mixer") != 0)
1677 m = O_RDWR;
1678 else {
1679 LOAD_FOPEN_FUNC();
1680 return _fopen(filename, mode);
1681 }
1682
1683 if ((fd = open(filename, m)) < 0)
1684 return NULL;
1685
1686 if (!(f = fdopen(fd, mode))) {
1687 close(fd);
1688 return NULL;
1689 }
1690
1691 return f;
1692 }
1693
1694 FILE *fopen64(const char *filename, const char *mode) {
1695
1696 debug(__FILE__": fopen64(%s)\n", filename);
1697
1698 if (strcmp(filename, "/dev/dsp") != 0 &&
1699 strcmp(filename, "/dev/adsp") != 0 &&
1700 strcmp(filename, "/dev/sndstat") != 0 &&
1701 strcmp(filename, "/dev/mixer") != 0) {
1702 LOAD_FOPEN64_FUNC();
1703 return _fopen64(filename, mode);
1704 }
1705
1706 return fopen(filename, mode);
1707 }
1708
1709 int fclose(FILE *f) {
1710 fd_info *i;
1711
1712 debug(__FILE__": fclose()\n");
1713
1714 if (!function_enter()) {
1715 LOAD_FCLOSE_FUNC();
1716 return _fclose(f);
1717 }
1718
1719 if (!(i = fd_info_find(fileno(f)))) {
1720 function_exit();
1721 LOAD_FCLOSE_FUNC();
1722 return _fclose(f);
1723 }
1724
1725 fd_info_remove_from_list(i);
1726
1727 /* Dirty trick to avoid that the fd is not freed twice, once by us
1728 * and once by the real fclose() */
1729 i->app_fd = -1;
1730
1731 fd_info_unref(i);
1732
1733 function_exit();
1734
1735 LOAD_FCLOSE_FUNC();
1736 return _fclose(f);
1737 }