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