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