]> code.delx.au - pulseaudio/blob - src/utils/padsp.c
pactl: Fix a copy-paster error
[pulseaudio] / src / utils / padsp.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006 Lennart Poettering
5 Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #ifdef _FILE_OFFSET_BITS
28 #undef _FILE_OFFSET_BITS
29 #endif
30
31 #ifndef _LARGEFILE64_SOURCE
32 #define _LARGEFILE64_SOURCE 1
33 #endif
34
35 #include <sys/soundcard.h>
36 #include <sys/ioctl.h>
37 #include <pthread.h>
38 #include <unistd.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <dlfcn.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <string.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <signal.h>
48
49 #ifdef __linux__
50 #include <linux/sockios.h>
51 #endif
52
53 #include <pulse/pulseaudio.h>
54 #include <pulse/gccmacro.h>
55 #include <pulsecore/llist.h>
56 #include <pulsecore/core-util.h>
57
58 /* On some systems SIOCINQ isn't defined, but FIONREAD is just an alias */
59 #if !defined(SIOCINQ) && defined(FIONREAD)
60 # define SIOCINQ FIONREAD
61 #endif
62
63 /* make sure gcc doesn't redefine open and friends as macros */
64 #undef open
65 #undef open64
66
67 typedef enum {
68 FD_INFO_MIXER,
69 FD_INFO_STREAM,
70 } fd_info_type_t;
71
72 typedef struct fd_info fd_info;
73
74 struct fd_info {
75 pthread_mutex_t mutex;
76 int ref;
77 int unusable;
78
79 fd_info_type_t type;
80 int app_fd, thread_fd;
81
82 pa_sample_spec sample_spec;
83 size_t fragment_size;
84 unsigned n_fragments;
85
86 pa_threaded_mainloop *mainloop;
87 pa_context *context;
88 pa_stream *play_stream;
89 pa_stream *rec_stream;
90 int play_precork;
91 int rec_precork;
92
93 pa_io_event *io_event;
94 pa_io_event_flags_t io_flags;
95
96 void *buf;
97 size_t rec_offset;
98
99 int operation_success;
100
101 pa_cvolume sink_volume, source_volume;
102 uint32_t sink_index, source_index;
103 int volume_modify_count;
104
105 int optr_n_blocks;
106
107 PA_LLIST_FIELDS(fd_info);
108 };
109
110 static int dsp_drain(fd_info *i);
111 static void fd_info_remove_from_list(fd_info *i);
112
113 static pthread_mutex_t fd_infos_mutex = PTHREAD_MUTEX_INITIALIZER;
114 static pthread_mutex_t func_mutex = PTHREAD_MUTEX_INITIALIZER;
115
116 static PA_LLIST_HEAD(fd_info, fd_infos) = NULL;
117
118 static int (*_ioctl)(int, int, void*) = NULL;
119 static int (*_close)(int) = NULL;
120 static int (*_open)(const char *, int, mode_t) = NULL;
121 static int (*___open_2)(const char *, int) = NULL;
122 static FILE* (*_fopen)(const char *path, const char *mode) = NULL;
123 static int (*_stat)(const char *, struct stat *) = NULL;
124 #ifdef _STAT_VER
125 static int (*___xstat)(int, const char *, struct stat *) = NULL;
126 #endif
127 #ifdef HAVE_OPEN64
128 static int (*_open64)(const char *, int, mode_t) = NULL;
129 static int (*___open64_2)(const char *, int) = NULL;
130 static FILE* (*_fopen64)(const char *path, const char *mode) = NULL;
131 static int (*_stat64)(const char *, struct stat64 *) = NULL;
132 #ifdef _STAT_VER
133 static int (*___xstat64)(int, const char *, struct stat64 *) = NULL;
134 #endif
135 #endif
136 static int (*_fclose)(FILE *f) = NULL;
137 static int (*_access)(const char *, int) = NULL;
138
139 /* dlsym() violates ISO C, so confide the breakage into this function to
140 * avoid warnings. */
141 typedef void (*fnptr)(void);
142 static inline fnptr dlsym_fn(void *handle, const char *symbol) {
143 return (fnptr) (long) dlsym(handle, symbol);
144 }
145
146 #define LOAD_IOCTL_FUNC() \
147 do { \
148 pthread_mutex_lock(&func_mutex); \
149 if (!_ioctl) \
150 _ioctl = (int (*)(int, int, void*)) dlsym_fn(RTLD_NEXT, "ioctl"); \
151 pthread_mutex_unlock(&func_mutex); \
152 } while(0)
153
154 #define LOAD_OPEN_FUNC() \
155 do { \
156 pthread_mutex_lock(&func_mutex); \
157 if (!_open) \
158 _open = (int (*)(const char *, int, mode_t)) dlsym_fn(RTLD_NEXT, "open"); \
159 pthread_mutex_unlock(&func_mutex); \
160 } while(0)
161
162 #define LOAD___OPEN_2_FUNC() \
163 do { \
164 pthread_mutex_lock(&func_mutex); \
165 if (!___open_2) \
166 ___open_2 = (int (*)(const char *, int)) dlsym_fn(RTLD_NEXT, "__open_2"); \
167 pthread_mutex_unlock(&func_mutex); \
168 } while(0)
169
170 #define LOAD_OPEN64_FUNC() \
171 do { \
172 pthread_mutex_lock(&func_mutex); \
173 if (!_open64) \
174 _open64 = (int (*)(const char *, int, mode_t)) dlsym_fn(RTLD_NEXT, "open64"); \
175 pthread_mutex_unlock(&func_mutex); \
176 } while(0)
177
178 #define LOAD___OPEN64_2_FUNC() \
179 do { \
180 pthread_mutex_lock(&func_mutex); \
181 if (!___open64_2) \
182 ___open64_2 = (int (*)(const char *, int)) dlsym_fn(RTLD_NEXT, "__open64_2"); \
183 pthread_mutex_unlock(&func_mutex); \
184 } while(0)
185
186 #define LOAD_CLOSE_FUNC() \
187 do { \
188 pthread_mutex_lock(&func_mutex); \
189 if (!_close) \
190 _close = (int (*)(int)) dlsym_fn(RTLD_NEXT, "close"); \
191 pthread_mutex_unlock(&func_mutex); \
192 } while(0)
193
194 #define LOAD_ACCESS_FUNC() \
195 do { \
196 pthread_mutex_lock(&func_mutex); \
197 if (!_access) \
198 _access = (int (*)(const char*, int)) dlsym_fn(RTLD_NEXT, "access"); \
199 pthread_mutex_unlock(&func_mutex); \
200 } while(0)
201
202 #define LOAD_STAT_FUNC() \
203 do { \
204 pthread_mutex_lock(&func_mutex); \
205 if (!_stat) \
206 _stat = (int (*)(const char *, struct stat *)) dlsym_fn(RTLD_NEXT, "stat"); \
207 pthread_mutex_unlock(&func_mutex); \
208 } while(0)
209
210 #define LOAD_STAT64_FUNC() \
211 do { \
212 pthread_mutex_lock(&func_mutex); \
213 if (!_stat64) \
214 _stat64 = (int (*)(const char *, struct stat64 *)) dlsym_fn(RTLD_NEXT, "stat64"); \
215 pthread_mutex_unlock(&func_mutex); \
216 } while(0)
217
218 #define LOAD_XSTAT_FUNC() \
219 do { \
220 pthread_mutex_lock(&func_mutex); \
221 if (!___xstat) \
222 ___xstat = (int (*)(int, const char *, struct stat *)) dlsym_fn(RTLD_NEXT, "__xstat"); \
223 pthread_mutex_unlock(&func_mutex); \
224 } while(0)
225
226 #define LOAD_XSTAT64_FUNC() \
227 do { \
228 pthread_mutex_lock(&func_mutex); \
229 if (!___xstat64) \
230 ___xstat64 = (int (*)(int, const char *, struct stat64 *)) dlsym_fn(RTLD_NEXT, "__xstat64"); \
231 pthread_mutex_unlock(&func_mutex); \
232 } while(0)
233
234 #define LOAD_FOPEN_FUNC() \
235 do { \
236 pthread_mutex_lock(&func_mutex); \
237 if (!_fopen) \
238 _fopen = (FILE* (*)(const char *, const char*)) dlsym_fn(RTLD_NEXT, "fopen"); \
239 pthread_mutex_unlock(&func_mutex); \
240 } while(0)
241
242 #define LOAD_FOPEN64_FUNC() \
243 do { \
244 pthread_mutex_lock(&func_mutex); \
245 if (!_fopen64) \
246 _fopen64 = (FILE* (*)(const char *, const char*)) dlsym_fn(RTLD_NEXT, "fopen64"); \
247 pthread_mutex_unlock(&func_mutex); \
248 } while(0)
249
250 #define LOAD_FCLOSE_FUNC() \
251 do { \
252 pthread_mutex_lock(&func_mutex); \
253 if (!_fclose) \
254 _fclose = (int (*)(FILE *)) dlsym_fn(RTLD_NEXT, "fclose"); \
255 pthread_mutex_unlock(&func_mutex); \
256 } while(0)
257
258 #define CONTEXT_CHECK_DEAD_GOTO(i, label) do { \
259 if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY) { \
260 debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
261 goto label; \
262 } \
263 } while(0)
264
265 #define PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, label) do { \
266 if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY || \
267 !(i)->play_stream || pa_stream_get_state((i)->play_stream) != PA_STREAM_READY) { \
268 debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
269 goto label; \
270 } \
271 } while(0)
272
273 #define RECORD_STREAM_CHECK_DEAD_GOTO(i, label) do { \
274 if (!(i)->context || pa_context_get_state((i)->context) != PA_CONTEXT_READY || \
275 !(i)->rec_stream || pa_stream_get_state((i)->rec_stream) != PA_STREAM_READY) { \
276 debug(DEBUG_LEVEL_NORMAL, __FILE__": Not connected: %s\n", (i)->context ? pa_strerror(pa_context_errno((i)->context)) : "NULL"); \
277 goto label; \
278 } \
279 } while(0)
280
281 static void debug(int level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
282
283 #define DEBUG_LEVEL_ALWAYS 0
284 #define DEBUG_LEVEL_NORMAL 1
285 #define DEBUG_LEVEL_VERBOSE 2
286
287 static void debug(int level, const char *format, ...) {
288 va_list ap;
289 const char *dlevel_s;
290 int dlevel;
291
292 dlevel_s = getenv("PADSP_DEBUG");
293 if (!dlevel_s)
294 return;
295
296 dlevel = atoi(dlevel_s);
297
298 if (dlevel < level)
299 return;
300
301 va_start(ap, format);
302 vfprintf(stderr, format, ap);
303 va_end(ap);
304 }
305
306 static int padsp_disabled(void) {
307 static int *sym;
308 static int sym_resolved = 0;
309
310 /* If the current process has a symbol __padsp_disabled__ we use
311 * it to detect whether we should enable our stuff or not. A
312 * program needs to be compiled with -rdynamic for this to work!
313 * The symbol must be an int containing a three bit bitmask: bit 1
314 * -> disable /dev/dsp emulation, bit 2 -> disable /dev/sndstat
315 * emulation, bit 3 -> disable /dev/mixer emulation. Hence a value
316 * of 7 disables padsp entirely. */
317
318 pthread_mutex_lock(&func_mutex);
319 if (!sym_resolved) {
320 sym = (int*) dlsym(RTLD_DEFAULT, "__padsp_disabled__");
321 sym_resolved = 1;
322 }
323 pthread_mutex_unlock(&func_mutex);
324
325 if (!sym)
326 return 0;
327
328 return *sym;
329 }
330
331 static int dsp_cloak_enable(void) {
332 if (padsp_disabled() & 1)
333 return 0;
334
335 if (getenv("PADSP_NO_DSP") || getenv("PULSE_INTERNAL"))
336 return 0;
337
338 return 1;
339 }
340
341 static int sndstat_cloak_enable(void) {
342 if (padsp_disabled() & 2)
343 return 0;
344
345 if (getenv("PADSP_NO_SNDSTAT") || getenv("PULSE_INTERNAL"))
346 return 0;
347
348 return 1;
349 }
350
351 static int mixer_cloak_enable(void) {
352 if (padsp_disabled() & 4)
353 return 0;
354
355 if (getenv("PADSP_NO_MIXER") || getenv("PULSE_INTERNAL"))
356 return 0;
357
358 return 1;
359 }
360 static pthread_key_t recursion_key;
361
362 static void recursion_key_alloc(void) {
363 pthread_key_create(&recursion_key, NULL);
364 }
365
366 static int function_enter(void) {
367 /* Avoid recursive calls */
368 static pthread_once_t recursion_key_once = PTHREAD_ONCE_INIT;
369 pthread_once(&recursion_key_once, recursion_key_alloc);
370
371 if (pthread_getspecific(recursion_key))
372 return 0;
373
374 pthread_setspecific(recursion_key, (void*) 1);
375 return 1;
376 }
377
378 static void function_exit(void) {
379 pthread_setspecific(recursion_key, NULL);
380 }
381
382 static void fd_info_free(fd_info *i) {
383 assert(i);
384
385 debug(DEBUG_LEVEL_NORMAL, __FILE__": freeing fd info (fd=%i)\n", i->app_fd);
386
387 dsp_drain(i);
388
389 if (i->mainloop)
390 pa_threaded_mainloop_stop(i->mainloop);
391
392 if (i->play_stream) {
393 pa_stream_disconnect(i->play_stream);
394 pa_stream_unref(i->play_stream);
395 }
396
397 if (i->rec_stream) {
398 pa_stream_disconnect(i->rec_stream);
399 pa_stream_unref(i->rec_stream);
400 }
401
402 if (i->context) {
403 pa_context_disconnect(i->context);
404 pa_context_unref(i->context);
405 }
406
407 if (i->mainloop)
408 pa_threaded_mainloop_free(i->mainloop);
409
410 if (i->app_fd >= 0) {
411 LOAD_CLOSE_FUNC();
412 _close(i->app_fd);
413 }
414
415 if (i->thread_fd >= 0) {
416 LOAD_CLOSE_FUNC();
417 _close(i->thread_fd);
418 }
419
420 free(i->buf);
421
422 pthread_mutex_destroy(&i->mutex);
423 free(i);
424 }
425
426 static fd_info *fd_info_ref(fd_info *i) {
427 assert(i);
428
429 pthread_mutex_lock(&i->mutex);
430 assert(i->ref >= 1);
431 i->ref++;
432
433 debug(DEBUG_LEVEL_VERBOSE, __FILE__": ref++, now %i\n", i->ref);
434 pthread_mutex_unlock(&i->mutex);
435
436 return i;
437 }
438
439 static void fd_info_unref(fd_info *i) {
440 int r;
441 pthread_mutex_lock(&i->mutex);
442 assert(i->ref >= 1);
443 r = --i->ref;
444 debug(DEBUG_LEVEL_VERBOSE, __FILE__": ref--, now %i\n", i->ref);
445 pthread_mutex_unlock(&i->mutex);
446
447 if (r <= 0)
448 fd_info_free(i);
449 }
450
451 static void context_state_cb(pa_context *c, void *userdata) {
452 fd_info *i = userdata;
453 assert(c);
454
455 switch (pa_context_get_state(c)) {
456 case PA_CONTEXT_READY:
457 case PA_CONTEXT_TERMINATED:
458 case PA_CONTEXT_FAILED:
459 pa_threaded_mainloop_signal(i->mainloop, 0);
460 break;
461
462 case PA_CONTEXT_UNCONNECTED:
463 case PA_CONTEXT_CONNECTING:
464 case PA_CONTEXT_AUTHORIZING:
465 case PA_CONTEXT_SETTING_NAME:
466 break;
467 }
468 }
469
470 static void reset_params(fd_info *i) {
471 assert(i);
472
473 i->sample_spec.format = PA_SAMPLE_U8;
474 i->sample_spec.channels = 1;
475 i->sample_spec.rate = 8000;
476 i->fragment_size = 0;
477 i->n_fragments = 0;
478 }
479
480 static const char *client_name(char *buf, size_t n) {
481 char *p;
482 const char *e;
483
484 if ((e = getenv("PADSP_CLIENT_NAME")))
485 return e;
486
487 if ((p = pa_get_binary_name_malloc())) {
488 snprintf(buf, n, "OSS Emulation[%s]", p);
489 pa_xfree(p);
490 } else
491 snprintf(buf, n, "OSS");
492
493 return buf;
494 }
495
496 static const char *stream_name(void) {
497 const char *e;
498
499 if ((e = getenv("PADSP_STREAM_NAME")))
500 return e;
501
502 return "Audio Stream";
503 }
504
505 static void atfork_prepare(void) {
506 fd_info *i;
507
508 debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_prepare() enter\n");
509
510 function_enter();
511
512 pthread_mutex_lock(&fd_infos_mutex);
513
514 for (i = fd_infos; i; i = i->next) {
515 pthread_mutex_lock(&i->mutex);
516 pa_threaded_mainloop_lock(i->mainloop);
517 }
518
519 pthread_mutex_lock(&func_mutex);
520
521 debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_prepare() exit\n");
522 }
523
524 static void atfork_parent(void) {
525 fd_info *i;
526
527 debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_parent() enter\n");
528
529 pthread_mutex_unlock(&func_mutex);
530
531 for (i = fd_infos; i; i = i->next) {
532 pa_threaded_mainloop_unlock(i->mainloop);
533 pthread_mutex_unlock(&i->mutex);
534 }
535
536 pthread_mutex_unlock(&fd_infos_mutex);
537
538 function_exit();
539
540 debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_parent() exit\n");
541 }
542
543 static void atfork_child(void) {
544 fd_info *i;
545
546 debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_child() enter\n");
547
548 /* We do only the bare minimum to get all fds closed */
549 pthread_mutex_init(&func_mutex, NULL);
550 pthread_mutex_init(&fd_infos_mutex, NULL);
551
552 for (i = fd_infos; i; i = i->next) {
553 pthread_mutex_init(&i->mutex, NULL);
554
555 if (i->context) {
556 pa_context_disconnect(i->context);
557 pa_context_unref(i->context);
558 i->context = NULL;
559 }
560
561 if (i->play_stream) {
562 pa_stream_unref(i->play_stream);
563 i->play_stream = NULL;
564 }
565
566 if (i->rec_stream) {
567 pa_stream_unref(i->rec_stream);
568 i->rec_stream = NULL;
569 }
570
571 if (i->app_fd >= 0) {
572 LOAD_CLOSE_FUNC();
573 _close(i->app_fd);
574 i->app_fd = -1;
575 }
576
577 if (i->thread_fd >= 0) {
578 LOAD_CLOSE_FUNC();
579 _close(i->thread_fd);
580 i->thread_fd = -1;
581 }
582
583 i->unusable = 1;
584 }
585
586 function_exit();
587
588 debug(DEBUG_LEVEL_NORMAL, __FILE__": atfork_child() exit\n");
589 }
590
591 static void install_atfork(void) {
592 pthread_atfork(atfork_prepare, atfork_parent, atfork_child);
593 }
594
595 static void stream_success_cb(pa_stream *s, int success, void *userdata) {
596 fd_info *i = userdata;
597
598 assert(s);
599 assert(i);
600
601 i->operation_success = success;
602 pa_threaded_mainloop_signal(i->mainloop, 0);
603 }
604
605 static void context_success_cb(pa_context *c, int success, void *userdata) {
606 fd_info *i = userdata;
607
608 assert(c);
609 assert(i);
610
611 i->operation_success = success;
612 pa_threaded_mainloop_signal(i->mainloop, 0);
613 }
614
615 static fd_info* fd_info_new(fd_info_type_t type, int *_errno) {
616 fd_info *i;
617 int sfds[2] = { -1, -1 };
618 char name[64];
619 static pthread_once_t install_atfork_once = PTHREAD_ONCE_INIT;
620
621 debug(DEBUG_LEVEL_NORMAL, __FILE__": fd_info_new()\n");
622
623 signal(SIGPIPE, SIG_IGN); /* Yes, ugly as hell */
624
625 pthread_once(&install_atfork_once, install_atfork);
626
627 if (!(i = malloc(sizeof(fd_info)))) {
628 *_errno = ENOMEM;
629 goto fail;
630 }
631
632 i->app_fd = i->thread_fd = -1;
633 i->type = type;
634
635 i->mainloop = NULL;
636 i->context = NULL;
637 i->play_stream = NULL;
638 i->rec_stream = NULL;
639 i->play_precork = 0;
640 i->rec_precork = 0;
641 i->io_event = NULL;
642 i->io_flags = 0;
643 pthread_mutex_init(&i->mutex, NULL);
644 i->ref = 1;
645 i->buf = NULL;
646 i->rec_offset = 0;
647 i->unusable = 0;
648 pa_cvolume_reset(&i->sink_volume, 2);
649 pa_cvolume_reset(&i->source_volume, 2);
650 i->volume_modify_count = 0;
651 i->sink_index = (uint32_t) -1;
652 i->source_index = (uint32_t) -1;
653 i->optr_n_blocks = 0;
654 PA_LLIST_INIT(fd_info, i);
655
656 reset_params(i);
657
658 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfds) < 0) {
659 *_errno = errno;
660 debug(DEBUG_LEVEL_NORMAL, __FILE__": socket() failed: %s\n", strerror(errno));
661 goto fail;
662 }
663
664 i->app_fd = sfds[0];
665 i->thread_fd = sfds[1];
666
667 if (!(i->mainloop = pa_threaded_mainloop_new())) {
668 *_errno = EIO;
669 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_threaded_mainloop_new() failed\n");
670 goto fail;
671 }
672
673 if (!(i->context = pa_context_new(pa_threaded_mainloop_get_api(i->mainloop), client_name(name, sizeof(name))))) {
674 *_errno = EIO;
675 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_context_new() failed\n");
676 goto fail;
677 }
678
679 pa_context_set_state_callback(i->context, context_state_cb, i);
680
681 if (pa_context_connect(i->context, NULL, 0, NULL) < 0) {
682 *_errno = ECONNREFUSED;
683 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
684 goto fail;
685 }
686
687 pa_threaded_mainloop_lock(i->mainloop);
688
689 if (pa_threaded_mainloop_start(i->mainloop) < 0) {
690 *_errno = EIO;
691 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_threaded_mainloop_start() failed\n");
692 goto unlock_and_fail;
693 }
694
695 /* Wait until the context is ready */
696 pa_threaded_mainloop_wait(i->mainloop);
697
698 if (pa_context_get_state(i->context) != PA_CONTEXT_READY) {
699 *_errno = ECONNREFUSED;
700 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
701 goto unlock_and_fail;
702 }
703
704 pa_threaded_mainloop_unlock(i->mainloop);
705 return i;
706
707 unlock_and_fail:
708
709 pa_threaded_mainloop_unlock(i->mainloop);
710
711 fail:
712
713 if (i)
714 fd_info_unref(i);
715
716 return NULL;
717 }
718
719 static void fd_info_add_to_list(fd_info *i) {
720 assert(i);
721
722 pthread_mutex_lock(&fd_infos_mutex);
723 PA_LLIST_PREPEND(fd_info, fd_infos, i);
724 pthread_mutex_unlock(&fd_infos_mutex);
725
726 fd_info_ref(i);
727 }
728
729 static void fd_info_remove_from_list(fd_info *i) {
730 assert(i);
731
732 pthread_mutex_lock(&fd_infos_mutex);
733 PA_LLIST_REMOVE(fd_info, fd_infos, i);
734 pthread_mutex_unlock(&fd_infos_mutex);
735
736 fd_info_unref(i);
737 }
738
739 static fd_info* fd_info_find(int fd) {
740 fd_info *i;
741
742 pthread_mutex_lock(&fd_infos_mutex);
743
744 for (i = fd_infos; i; i = i->next)
745 if (i->app_fd == fd && !i->unusable) {
746 fd_info_ref(i);
747 break;
748 }
749
750 pthread_mutex_unlock(&fd_infos_mutex);
751
752 return i;
753 }
754
755 static void fix_metrics(fd_info *i) {
756 size_t fs;
757 char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
758
759 fs = pa_frame_size(&i->sample_spec);
760
761 /* Don't fix things more than necessary */
762 if ((i->fragment_size % fs) == 0 &&
763 i->n_fragments >= 2 &&
764 i->fragment_size > 0)
765 return;
766
767 i->fragment_size = (i->fragment_size/fs)*fs;
768
769 /* Number of fragments set? */
770 if (i->n_fragments < 2) {
771 if (i->fragment_size > 0) {
772 i->n_fragments = (unsigned) (pa_bytes_per_second(&i->sample_spec) / 2 / i->fragment_size);
773 if (i->n_fragments < 2)
774 i->n_fragments = 2;
775 } else
776 i->n_fragments = 12;
777 }
778
779 /* Fragment size set? */
780 if (i->fragment_size <= 0) {
781 i->fragment_size = pa_bytes_per_second(&i->sample_spec) / 2 / i->n_fragments;
782 if (i->fragment_size < 1024)
783 i->fragment_size = 1024;
784 }
785
786 debug(DEBUG_LEVEL_NORMAL, __FILE__": sample spec: %s\n", pa_sample_spec_snprint(t, sizeof(t), &i->sample_spec));
787 debug(DEBUG_LEVEL_NORMAL, __FILE__": fixated metrics to %i fragments, %li bytes each.\n", i->n_fragments, (long)i->fragment_size);
788 }
789
790 static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
791 fd_info *i = userdata;
792 assert(s);
793
794 if (i->io_event) {
795 pa_mainloop_api *api;
796 size_t n;
797
798 api = pa_threaded_mainloop_get_api(i->mainloop);
799
800 if (s == i->play_stream) {
801 n = pa_stream_writable_size(i->play_stream);
802 if (n == (size_t)-1) {
803 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_writable_size(): %s\n",
804 pa_strerror(pa_context_errno(i->context)));
805 }
806
807 if (n >= i->fragment_size)
808 i->io_flags |= PA_IO_EVENT_INPUT;
809 else
810 i->io_flags &= ~PA_IO_EVENT_INPUT;
811 }
812
813 if (s == i->rec_stream) {
814 n = pa_stream_readable_size(i->rec_stream);
815 if (n == (size_t)-1) {
816 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_readable_size(): %s\n",
817 pa_strerror(pa_context_errno(i->context)));
818 }
819
820 if (n >= i->fragment_size)
821 i->io_flags |= PA_IO_EVENT_OUTPUT;
822 else
823 i->io_flags &= ~PA_IO_EVENT_OUTPUT;
824 }
825
826 api->io_enable(i->io_event, i->io_flags);
827 }
828 }
829
830 static void stream_latency_update_cb(pa_stream *s, void *userdata) {
831 fd_info *i = userdata;
832 assert(s);
833
834 pa_threaded_mainloop_signal(i->mainloop, 0);
835 }
836
837 static void fd_info_shutdown(fd_info *i) {
838 assert(i);
839
840 if (i->io_event) {
841 pa_mainloop_api *api;
842 api = pa_threaded_mainloop_get_api(i->mainloop);
843 api->io_free(i->io_event);
844 i->io_event = NULL;
845 i->io_flags = 0;
846 }
847
848 if (i->thread_fd >= 0) {
849 close(i->thread_fd);
850 i->thread_fd = -1;
851 }
852 }
853
854 static int fd_info_copy_data(fd_info *i, int force) {
855 size_t n;
856
857 if (!i->play_stream && !i->rec_stream)
858 return -1;
859
860 if ((i->play_stream) && (pa_stream_get_state(i->play_stream) == PA_STREAM_READY)) {
861 n = pa_stream_writable_size(i->play_stream);
862
863 if (n == (size_t)-1) {
864 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_writable_size(): %s\n",
865 pa_strerror(pa_context_errno(i->context)));
866 return -1;
867 }
868
869 while (n >= i->fragment_size || force) {
870 ssize_t r;
871
872 if (!i->buf) {
873 if (!(i->buf = malloc(i->fragment_size))) {
874 debug(DEBUG_LEVEL_NORMAL, __FILE__": malloc() failed.\n");
875 return -1;
876 }
877 }
878
879 if ((r = read(i->thread_fd, i->buf, i->fragment_size)) <= 0) {
880
881 if (errno == EAGAIN)
882 break;
883
884 debug(DEBUG_LEVEL_NORMAL, __FILE__": read(): %s\n", r == 0 ? "EOF" : strerror(errno));
885 return -1;
886 }
887
888 if (pa_stream_write(i->play_stream, i->buf, (size_t) r, free, 0LL, PA_SEEK_RELATIVE) < 0) {
889 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_write(): %s\n", pa_strerror(pa_context_errno(i->context)));
890 return -1;
891 }
892
893 i->buf = NULL;
894
895 assert(n >= (size_t) r);
896 n -= (size_t) r;
897 }
898
899 if (n >= i->fragment_size)
900 i->io_flags |= PA_IO_EVENT_INPUT;
901 else
902 i->io_flags &= ~PA_IO_EVENT_INPUT;
903 }
904
905 if ((i->rec_stream) && (pa_stream_get_state(i->rec_stream) == PA_STREAM_READY)) {
906 n = pa_stream_readable_size(i->rec_stream);
907
908 if (n == (size_t)-1) {
909 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_readable_size(): %s\n",
910 pa_strerror(pa_context_errno(i->context)));
911 return -1;
912 }
913
914 while (n >= i->fragment_size || force) {
915 ssize_t r;
916 const void *data;
917 const char *buf;
918 size_t len;
919
920 if (pa_stream_peek(i->rec_stream, &data, &len) < 0) {
921 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_peek(): %s\n", pa_strerror(pa_context_errno(i->context)));
922 return -1;
923 }
924
925 if (len <= 0)
926 break;
927
928 if (!data) {
929 /* Maybe we should generate silence here, but I'm lazy and
930 * I'll just skip any holes in the stream. */
931 if (pa_stream_drop(i->rec_stream) < 0) {
932 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drop(): %s\n", pa_strerror(pa_context_errno(i->context)));
933 return -1;
934 }
935
936 assert(n >= len);
937 n -= len;
938 continue;
939 }
940
941 buf = (const char*)data + i->rec_offset;
942
943 if ((r = write(i->thread_fd, buf, len - i->rec_offset)) <= 0) {
944
945 if (errno == EAGAIN)
946 break;
947
948 debug(DEBUG_LEVEL_NORMAL, __FILE__": write(): %s\n", strerror(errno));
949 return -1;
950 }
951
952 assert((size_t)r <= len - i->rec_offset);
953 i->rec_offset += (size_t) r;
954
955 if (i->rec_offset == len) {
956 if (pa_stream_drop(i->rec_stream) < 0) {
957 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drop(): %s\n", pa_strerror(pa_context_errno(i->context)));
958 return -1;
959 }
960 i->rec_offset = 0;
961 }
962
963 assert(n >= (size_t) r);
964 n -= (size_t) r;
965 }
966
967 if (n >= i->fragment_size)
968 i->io_flags |= PA_IO_EVENT_OUTPUT;
969 else
970 i->io_flags &= ~PA_IO_EVENT_OUTPUT;
971 }
972
973 if (i->io_event) {
974 pa_mainloop_api *api;
975
976 api = pa_threaded_mainloop_get_api(i->mainloop);
977 api->io_enable(i->io_event, i->io_flags);
978 }
979
980 /* So, we emptied the socket now, let's tell dsp_empty_socket()
981 * about this */
982 pa_threaded_mainloop_signal(i->mainloop, 0);
983
984 return 0;
985 }
986
987 static void stream_state_cb(pa_stream *s, void * userdata) {
988 fd_info *i = userdata;
989 assert(s);
990
991 switch (pa_stream_get_state(s)) {
992
993 case PA_STREAM_READY:
994 debug(DEBUG_LEVEL_NORMAL, __FILE__": stream established.\n");
995 break;
996
997 case PA_STREAM_FAILED:
998 if (s == i->play_stream) {
999 debug(DEBUG_LEVEL_NORMAL,
1000 __FILE__": pa_stream_connect_playback() failed: %s\n",
1001 pa_strerror(pa_context_errno(i->context)));
1002 pa_stream_unref(i->play_stream);
1003 i->play_stream = NULL;
1004 } else if (s == i->rec_stream) {
1005 debug(DEBUG_LEVEL_NORMAL,
1006 __FILE__": pa_stream_connect_record() failed: %s\n",
1007 pa_strerror(pa_context_errno(i->context)));
1008 pa_stream_unref(i->rec_stream);
1009 i->rec_stream = NULL;
1010 }
1011 fd_info_shutdown(i);
1012 break;
1013
1014 case PA_STREAM_TERMINATED:
1015 case PA_STREAM_UNCONNECTED:
1016 case PA_STREAM_CREATING:
1017 break;
1018 }
1019 }
1020
1021 static int create_playback_stream(fd_info *i) {
1022 pa_buffer_attr attr;
1023 int n, flags;
1024
1025 assert(i);
1026
1027 fix_metrics(i);
1028
1029 if (!(i->play_stream = pa_stream_new(i->context, stream_name(), &i->sample_spec, NULL))) {
1030 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
1031 goto fail;
1032 }
1033
1034 pa_stream_set_state_callback(i->play_stream, stream_state_cb, i);
1035 pa_stream_set_write_callback(i->play_stream, stream_request_cb, i);
1036 pa_stream_set_latency_update_callback(i->play_stream, stream_latency_update_cb, i);
1037
1038 memset(&attr, 0, sizeof(attr));
1039 attr.maxlength = (uint32_t) (i->fragment_size * (i->n_fragments+1));
1040 attr.tlength = (uint32_t) (i->fragment_size * i->n_fragments);
1041 attr.prebuf = (uint32_t) i->fragment_size;
1042 attr.minreq = (uint32_t) i->fragment_size;
1043
1044 flags = PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_EARLY_REQUESTS;
1045 if (i->play_precork) {
1046 flags |= PA_STREAM_START_CORKED;
1047 debug(DEBUG_LEVEL_NORMAL, __FILE__": creating stream corked\n");
1048 }
1049 if (pa_stream_connect_playback(i->play_stream, NULL, &attr, flags, NULL, NULL) < 0) {
1050 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
1051 goto fail;
1052 }
1053
1054 n = (int) i->fragment_size;
1055 setsockopt(i->app_fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n));
1056 n = (int) i->fragment_size;
1057 setsockopt(i->thread_fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
1058
1059 return 0;
1060
1061 fail:
1062 return -1;
1063 }
1064
1065 static int create_record_stream(fd_info *i) {
1066 pa_buffer_attr attr;
1067 int n, flags;
1068
1069 assert(i);
1070
1071 fix_metrics(i);
1072
1073 if (!(i->rec_stream = pa_stream_new(i->context, stream_name(), &i->sample_spec, NULL))) {
1074 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
1075 goto fail;
1076 }
1077
1078 pa_stream_set_state_callback(i->rec_stream, stream_state_cb, i);
1079 pa_stream_set_read_callback(i->rec_stream, stream_request_cb, i);
1080 pa_stream_set_latency_update_callback(i->rec_stream, stream_latency_update_cb, i);
1081
1082 memset(&attr, 0, sizeof(attr));
1083 attr.maxlength = (uint32_t) (i->fragment_size * (i->n_fragments+1));
1084 attr.fragsize = (uint32_t) i->fragment_size;
1085
1086 flags = PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE;
1087 if (i->rec_precork) {
1088 flags |= PA_STREAM_START_CORKED;
1089 debug(DEBUG_LEVEL_NORMAL, __FILE__": creating stream corked\n");
1090 }
1091 if (pa_stream_connect_record(i->rec_stream, NULL, &attr, flags) < 0) {
1092 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(i->context)));
1093 goto fail;
1094 }
1095
1096 n = (int) i->fragment_size;
1097 setsockopt(i->app_fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
1098 n = (int) i->fragment_size;
1099 setsockopt(i->thread_fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n));
1100
1101 return 0;
1102
1103 fail:
1104 return -1;
1105 }
1106
1107 static void free_streams(fd_info *i) {
1108 assert(i);
1109
1110 if (i->play_stream) {
1111 pa_stream_disconnect(i->play_stream);
1112 pa_stream_unref(i->play_stream);
1113 i->play_stream = NULL;
1114 i->io_flags |= PA_IO_EVENT_INPUT;
1115 }
1116
1117 if (i->rec_stream) {
1118 pa_stream_disconnect(i->rec_stream);
1119 pa_stream_unref(i->rec_stream);
1120 i->rec_stream = NULL;
1121 i->io_flags |= PA_IO_EVENT_OUTPUT;
1122 }
1123
1124 if (i->io_event) {
1125 pa_mainloop_api *api;
1126
1127 api = pa_threaded_mainloop_get_api(i->mainloop);
1128 api->io_enable(i->io_event, i->io_flags);
1129 }
1130 }
1131
1132 static void io_event_cb(pa_mainloop_api *api, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
1133 fd_info *i = userdata;
1134
1135 pa_threaded_mainloop_signal(i->mainloop, 0);
1136
1137 if (flags & PA_IO_EVENT_INPUT) {
1138
1139 if (!i->play_stream) {
1140 if (create_playback_stream(i) < 0)
1141 goto fail;
1142 } else {
1143 if (fd_info_copy_data(i, 0) < 0)
1144 goto fail;
1145 }
1146
1147 } else if (flags & PA_IO_EVENT_OUTPUT) {
1148
1149 if (!i->rec_stream) {
1150 if (create_record_stream(i) < 0)
1151 goto fail;
1152 } else {
1153 if (fd_info_copy_data(i, 0) < 0)
1154 goto fail;
1155 }
1156
1157 } else if (flags & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR))
1158 goto fail;
1159
1160 return;
1161
1162 fail:
1163 /* We can't do anything better than removing the event source */
1164 fd_info_shutdown(i);
1165 }
1166
1167 static int dsp_open(int flags, int *_errno) {
1168 fd_info *i;
1169 pa_mainloop_api *api;
1170 int ret;
1171 int f;
1172
1173 debug(DEBUG_LEVEL_NORMAL, __FILE__": dsp_open()\n");
1174
1175 if (!(i = fd_info_new(FD_INFO_STREAM, _errno)))
1176 return -1;
1177
1178 if ((flags & O_NONBLOCK) == O_NONBLOCK) {
1179 if ((f = fcntl(i->app_fd, F_GETFL)) >= 0)
1180 fcntl(i->app_fd, F_SETFL, f|O_NONBLOCK);
1181 }
1182 if ((f = fcntl(i->thread_fd, F_GETFL)) >= 0)
1183 fcntl(i->thread_fd, F_SETFL, f|O_NONBLOCK);
1184
1185 fcntl(i->app_fd, F_SETFD, FD_CLOEXEC);
1186 fcntl(i->thread_fd, F_SETFD, FD_CLOEXEC);
1187
1188 pa_threaded_mainloop_lock(i->mainloop);
1189 api = pa_threaded_mainloop_get_api(i->mainloop);
1190
1191 switch (flags & O_ACCMODE) {
1192 case O_RDONLY:
1193 i->io_flags = PA_IO_EVENT_OUTPUT;
1194 shutdown(i->thread_fd, SHUT_RD);
1195 shutdown(i->app_fd, SHUT_WR);
1196 break;
1197 case O_WRONLY:
1198 i->io_flags = PA_IO_EVENT_INPUT;
1199 shutdown(i->thread_fd, SHUT_WR);
1200 shutdown(i->app_fd, SHUT_RD);
1201 break;
1202 case O_RDWR:
1203 i->io_flags = PA_IO_EVENT_INPUT | PA_IO_EVENT_OUTPUT;
1204 break;
1205 default:
1206 return -1;
1207 }
1208
1209 if (!(i->io_event = api->io_new(api, i->thread_fd, i->io_flags, io_event_cb, i)))
1210 goto fail;
1211
1212 pa_threaded_mainloop_unlock(i->mainloop);
1213
1214 debug(DEBUG_LEVEL_NORMAL, __FILE__": dsp_open() succeeded, fd=%i\n", i->app_fd);
1215
1216 fd_info_add_to_list(i);
1217 ret = i->app_fd;
1218 fd_info_unref(i);
1219
1220 return ret;
1221
1222 fail:
1223 pa_threaded_mainloop_unlock(i->mainloop);
1224
1225 if (i)
1226 fd_info_unref(i);
1227
1228 *_errno = EIO;
1229
1230 debug(DEBUG_LEVEL_NORMAL, __FILE__": dsp_open() failed\n");
1231
1232 return -1;
1233 }
1234
1235 static void sink_info_cb(pa_context *context, const pa_sink_info *si, int eol, void *userdata) {
1236 fd_info *i = userdata;
1237
1238 if (eol < 0) {
1239 i->operation_success = 0;
1240 pa_threaded_mainloop_signal(i->mainloop, 0);
1241 return;
1242 }
1243
1244 if (eol)
1245 return;
1246
1247 if (!pa_cvolume_equal(&i->sink_volume, &si->volume))
1248 i->volume_modify_count++;
1249
1250 i->sink_volume = si->volume;
1251 i->sink_index = si->index;
1252
1253 i->operation_success = 1;
1254 pa_threaded_mainloop_signal(i->mainloop, 0);
1255 }
1256
1257 static void source_info_cb(pa_context *context, const pa_source_info *si, int eol, void *userdata) {
1258 fd_info *i = userdata;
1259
1260 if (eol < 0) {
1261 i->operation_success = 0;
1262 pa_threaded_mainloop_signal(i->mainloop, 0);
1263 return;
1264 }
1265
1266 if (eol)
1267 return;
1268
1269 if (!pa_cvolume_equal(&i->source_volume, &si->volume))
1270 i->volume_modify_count++;
1271
1272 i->source_volume = si->volume;
1273 i->source_index = si->index;
1274
1275 i->operation_success = 1;
1276 pa_threaded_mainloop_signal(i->mainloop, 0);
1277 }
1278
1279 static void subscribe_cb(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
1280 fd_info *i = userdata;
1281 pa_operation *o = NULL;
1282
1283 if (i->sink_index != idx)
1284 return;
1285
1286 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
1287 return;
1288
1289 if (!(o = pa_context_get_sink_info_by_index(i->context, i->sink_index, sink_info_cb, i))) {
1290 debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
1291 return;
1292 }
1293
1294 pa_operation_unref(o);
1295 }
1296
1297 static int mixer_open(int flags, int *_errno) {
1298 fd_info *i;
1299 pa_operation *o = NULL;
1300 int ret;
1301
1302 debug(DEBUG_LEVEL_NORMAL, __FILE__": mixer_open()\n");
1303
1304 if (!(i = fd_info_new(FD_INFO_MIXER, _errno)))
1305 return -1;
1306
1307 pa_threaded_mainloop_lock(i->mainloop);
1308
1309 pa_context_set_subscribe_callback(i->context, subscribe_cb, i);
1310
1311 if (!(o = pa_context_subscribe(i->context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, context_success_cb, i))) {
1312 debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to subscribe to events: %s", pa_strerror(pa_context_errno(i->context)));
1313 *_errno = EIO;
1314 goto fail;
1315 }
1316
1317 i->operation_success = 0;
1318 while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
1319 pa_threaded_mainloop_wait(i->mainloop);
1320 CONTEXT_CHECK_DEAD_GOTO(i, fail);
1321 }
1322
1323 pa_operation_unref(o);
1324 o = NULL;
1325
1326 if (!i->operation_success) {
1327 debug(DEBUG_LEVEL_NORMAL, __FILE__":Failed to subscribe to events: %s", pa_strerror(pa_context_errno(i->context)));
1328 *_errno = EIO;
1329 goto fail;
1330 }
1331
1332 /* Get sink info */
1333
1334 if (!(o = pa_context_get_sink_info_by_name(i->context, NULL, sink_info_cb, i))) {
1335 debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
1336 *_errno = EIO;
1337 goto fail;
1338 }
1339
1340 i->operation_success = 0;
1341 while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
1342 pa_threaded_mainloop_wait(i->mainloop);
1343 CONTEXT_CHECK_DEAD_GOTO(i, fail);
1344 }
1345
1346 pa_operation_unref(o);
1347 o = NULL;
1348
1349 if (!i->operation_success) {
1350 debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get sink info: %s", pa_strerror(pa_context_errno(i->context)));
1351 *_errno = EIO;
1352 goto fail;
1353 }
1354
1355 /* Get source info */
1356
1357 if (!(o = pa_context_get_source_info_by_name(i->context, NULL, source_info_cb, i))) {
1358 debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get source info: %s", pa_strerror(pa_context_errno(i->context)));
1359 *_errno = EIO;
1360 goto fail;
1361 }
1362
1363 i->operation_success = 0;
1364 while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
1365 pa_threaded_mainloop_wait(i->mainloop);
1366 CONTEXT_CHECK_DEAD_GOTO(i, fail);
1367 }
1368
1369 pa_operation_unref(o);
1370 o = NULL;
1371
1372 if (!i->operation_success) {
1373 debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to get source info: %s", pa_strerror(pa_context_errno(i->context)));
1374 *_errno = EIO;
1375 goto fail;
1376 }
1377
1378 pa_threaded_mainloop_unlock(i->mainloop);
1379
1380 debug(DEBUG_LEVEL_NORMAL, __FILE__": mixer_open() succeeded, fd=%i\n", i->app_fd);
1381
1382 fd_info_add_to_list(i);
1383 ret = i->app_fd;
1384 fd_info_unref(i);
1385
1386 return ret;
1387
1388 fail:
1389 if (o)
1390 pa_operation_unref(o);
1391
1392 pa_threaded_mainloop_unlock(i->mainloop);
1393
1394 if (i)
1395 fd_info_unref(i);
1396
1397 *_errno = EIO;
1398
1399 debug(DEBUG_LEVEL_NORMAL, __FILE__": mixer_open() failed\n");
1400
1401 return -1;
1402 }
1403
1404 static int sndstat_open(int flags, int *_errno) {
1405 static const char sndstat[] =
1406 "Sound Driver:3.8.1a-980706 (PulseAudio Virtual OSS)\n"
1407 "Kernel: POSIX\n"
1408 "Config options: 0\n"
1409 "\n"
1410 "Installed drivers:\n"
1411 "Type 255: PulseAudio Virtual OSS\n"
1412 "\n"
1413 "Card config:\n"
1414 "PulseAudio Virtual OSS\n"
1415 "\n"
1416 "Audio devices:\n"
1417 "0: PulseAudio Virtual OSS\n"
1418 "\n"
1419 "Synth devices: NOT ENABLED IN CONFIG\n"
1420 "\n"
1421 "Midi devices:\n"
1422 "\n"
1423 "Timers:\n"
1424 "\n"
1425 "Mixers:\n"
1426 "0: PulseAudio Virtual OSS\n";
1427
1428 char *fn;
1429 mode_t u;
1430 int fd = -1;
1431 int e;
1432
1433 fn = pa_sprintf_malloc("%s" PA_PATH_SEP "padsp-sndstat-XXXXXX", pa_get_temp_dir());
1434
1435 debug(DEBUG_LEVEL_NORMAL, __FILE__": sndstat_open()\n");
1436
1437 if (flags != O_RDONLY
1438 #ifdef O_LARGEFILE
1439 && flags != (O_RDONLY|O_LARGEFILE)
1440 #endif
1441 ) {
1442 *_errno = EACCES;
1443 debug(DEBUG_LEVEL_NORMAL, __FILE__": bad access!\n");
1444 goto fail;
1445 }
1446
1447 u = umask(0077);
1448 fd = mkstemp(fn);
1449 e = errno;
1450 umask(u);
1451
1452 if (fd < 0) {
1453 *_errno = e;
1454 debug(DEBUG_LEVEL_NORMAL, __FILE__": mkstemp() failed: %s\n", strerror(errno));
1455 goto fail;
1456 }
1457
1458 unlink(fn);
1459 pa_xfree(fn);
1460 fn = NULL;
1461
1462 if (write(fd, sndstat, sizeof(sndstat) -1) != sizeof(sndstat)-1) {
1463 *_errno = errno;
1464 debug(DEBUG_LEVEL_NORMAL, __FILE__": write() failed: %s\n", strerror(errno));
1465 goto fail;
1466 }
1467
1468 if (lseek(fd, SEEK_SET, 0) < 0) {
1469 *_errno = errno;
1470 debug(DEBUG_LEVEL_NORMAL, __FILE__": lseek() failed: %s\n", strerror(errno));
1471 goto fail;
1472 }
1473
1474 return fd;
1475
1476 fail:
1477 pa_xfree(fn);
1478 if (fd >= 0)
1479 close(fd);
1480 return -1;
1481 }
1482
1483 static int real_open(const char *filename, int flags, mode_t mode) {
1484 int r, _errno = 0;
1485
1486 debug(DEBUG_LEVEL_VERBOSE, __FILE__": open(%s)\n", filename?filename:"NULL");
1487
1488 if (!function_enter()) {
1489 LOAD_OPEN_FUNC();
1490 return _open(filename, flags, mode);
1491 }
1492
1493 if (filename && dsp_cloak_enable() && (pa_streq(filename, "/dev/dsp") || pa_streq(filename, "/dev/adsp") || pa_streq(filename, "/dev/audio")))
1494 r = dsp_open(flags, &_errno);
1495 else if (filename && mixer_cloak_enable() && pa_streq(filename, "/dev/mixer"))
1496 r = mixer_open(flags, &_errno);
1497 else if (filename && sndstat_cloak_enable() && pa_streq(filename, "/dev/sndstat"))
1498 r = sndstat_open(flags, &_errno);
1499 else {
1500 function_exit();
1501 LOAD_OPEN_FUNC();
1502 return _open(filename, flags, mode);
1503 }
1504
1505 function_exit();
1506
1507 if (_errno)
1508 errno = _errno;
1509
1510 return r;
1511 }
1512
1513 int open(const char *filename, int flags, ...) {
1514 va_list args;
1515 mode_t mode = 0;
1516
1517 if (flags & O_CREAT) {
1518 va_start(args, flags);
1519 if (sizeof(mode_t) < sizeof(int))
1520 mode = (mode_t) va_arg(args, int);
1521 else
1522 mode = va_arg(args, mode_t);
1523 va_end(args);
1524 }
1525
1526 return real_open(filename, flags, mode);
1527 }
1528
1529 static bool is_audio_device_node(const char *path) {
1530 return
1531 pa_streq(path, "/dev/dsp") ||
1532 pa_streq(path, "/dev/adsp") ||
1533 pa_streq(path, "/dev/audio") ||
1534 pa_streq(path, "/dev/sndstat") ||
1535 pa_streq(path, "/dev/mixer");
1536 }
1537
1538 int __open_2(const char *filename, int flags) {
1539 debug(DEBUG_LEVEL_VERBOSE, __FILE__": __open_2(%s)\n", filename?filename:"NULL");
1540
1541 if ((flags & O_CREAT) ||
1542 !filename ||
1543 !is_audio_device_node(filename)) {
1544 LOAD___OPEN_2_FUNC();
1545 return ___open_2(filename, flags);
1546 }
1547 return real_open(filename, flags, 0);
1548 }
1549
1550 static int mixer_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {
1551 int ret = -1;
1552
1553 switch (request) {
1554 case SOUND_MIXER_READ_DEVMASK :
1555 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_DEVMASK\n");
1556
1557 *(int*) argp = SOUND_MASK_PCM | SOUND_MASK_IGAIN;
1558 break;
1559
1560 case SOUND_MIXER_READ_RECMASK :
1561 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_RECMASK\n");
1562
1563 *(int*) argp = SOUND_MASK_IGAIN;
1564 break;
1565
1566 case SOUND_MIXER_READ_STEREODEVS:
1567 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_STEREODEVS\n");
1568
1569 pa_threaded_mainloop_lock(i->mainloop);
1570 *(int*) argp = 0;
1571 if (i->sink_volume.channels > 1)
1572 *(int*) argp |= SOUND_MASK_PCM;
1573 if (i->source_volume.channels > 1)
1574 *(int*) argp |= SOUND_MASK_IGAIN;
1575 pa_threaded_mainloop_unlock(i->mainloop);
1576
1577 break;
1578
1579 case SOUND_MIXER_READ_RECSRC:
1580 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_RECSRC\n");
1581
1582 *(int*) argp = SOUND_MASK_IGAIN;
1583 break;
1584
1585 case SOUND_MIXER_WRITE_RECSRC:
1586 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_WRITE_RECSRC\n");
1587 break;
1588
1589 case SOUND_MIXER_READ_CAPS:
1590 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_CAPS\n");
1591
1592 *(int*) argp = 0;
1593 break;
1594
1595 case SOUND_MIXER_READ_PCM:
1596 case SOUND_MIXER_READ_IGAIN: {
1597 pa_cvolume *v;
1598
1599 if (request == SOUND_MIXER_READ_PCM)
1600 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_PCM\n");
1601 else
1602 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_READ_IGAIN\n");
1603
1604 pa_threaded_mainloop_lock(i->mainloop);
1605
1606 if (request == SOUND_MIXER_READ_PCM)
1607 v = &i->sink_volume;
1608 else
1609 v = &i->source_volume;
1610
1611 *(int*) argp =
1612 ((v->values[0]*100/PA_VOLUME_NORM)) |
1613 ((v->values[v->channels > 1 ? 1 : 0]*100/PA_VOLUME_NORM) << 8);
1614
1615 pa_threaded_mainloop_unlock(i->mainloop);
1616
1617 break;
1618 }
1619
1620 case SOUND_MIXER_WRITE_PCM:
1621 case SOUND_MIXER_WRITE_IGAIN: {
1622 pa_cvolume v, *pv;
1623
1624 if (request == SOUND_MIXER_WRITE_PCM)
1625 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_WRITE_PCM\n");
1626 else
1627 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_WRITE_IGAIN\n");
1628
1629 pa_threaded_mainloop_lock(i->mainloop);
1630
1631 if (request == SOUND_MIXER_WRITE_PCM) {
1632 v = i->sink_volume;
1633 pv = &i->sink_volume;
1634 } else {
1635 v = i->source_volume;
1636 pv = &i->source_volume;
1637 }
1638
1639 pv->values[0] = ((*(int*) argp & 0xFF)*PA_VOLUME_NORM)/100;
1640 pv->values[1] = ((*(int*) argp >> 8)*PA_VOLUME_NORM)/100;
1641
1642 if (!pa_cvolume_equal(pv, &v)) {
1643 pa_operation *o;
1644
1645 if (request == SOUND_MIXER_WRITE_PCM)
1646 o = pa_context_set_sink_volume_by_index(i->context, i->sink_index, pv, context_success_cb, i);
1647 else
1648 o = pa_context_set_source_volume_by_index(i->context, i->source_index, pv, context_success_cb, i);
1649
1650 if (!o)
1651 debug(DEBUG_LEVEL_NORMAL, __FILE__":Failed set volume: %s", pa_strerror(pa_context_errno(i->context)));
1652 else {
1653
1654 i->operation_success = 0;
1655 while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
1656 CONTEXT_CHECK_DEAD_GOTO(i, exit_loop);
1657
1658 pa_threaded_mainloop_wait(i->mainloop);
1659 }
1660 exit_loop:
1661
1662 if (!i->operation_success)
1663 debug(DEBUG_LEVEL_NORMAL, __FILE__": Failed to set volume: %s\n", pa_strerror(pa_context_errno(i->context)));
1664
1665 pa_operation_unref(o);
1666 }
1667
1668 /* We don't wait for completion here */
1669 i->volume_modify_count++;
1670 }
1671
1672 pa_threaded_mainloop_unlock(i->mainloop);
1673
1674 break;
1675 }
1676
1677 case SOUND_MIXER_INFO: {
1678 mixer_info *mi = argp;
1679
1680 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_MIXER_INFO\n");
1681
1682 memset(mi, 0, sizeof(mixer_info));
1683 strncpy(mi->id, "PULSEAUDIO", sizeof(mi->id));
1684 strncpy(mi->name, "PulseAudio Virtual OSS", sizeof(mi->name));
1685 pa_threaded_mainloop_lock(i->mainloop);
1686 mi->modify_counter = i->volume_modify_count;
1687 pa_threaded_mainloop_unlock(i->mainloop);
1688 break;
1689 }
1690
1691 default:
1692 debug(DEBUG_LEVEL_NORMAL, __FILE__": unknown ioctl 0x%08lx\n", request);
1693
1694 *_errno = EINVAL;
1695 goto fail;
1696 }
1697
1698 ret = 0;
1699
1700 fail:
1701
1702 return ret;
1703 }
1704
1705 static int map_format(int *fmt, pa_sample_spec *ss) {
1706
1707 switch (*fmt) {
1708 case AFMT_MU_LAW:
1709 ss->format = PA_SAMPLE_ULAW;
1710 break;
1711
1712 case AFMT_A_LAW:
1713 ss->format = PA_SAMPLE_ALAW;
1714 break;
1715
1716 case AFMT_S8:
1717 *fmt = AFMT_U8;
1718 /* fall through */
1719 case AFMT_U8:
1720 ss->format = PA_SAMPLE_U8;
1721 break;
1722
1723 case AFMT_U16_BE:
1724 *fmt = AFMT_S16_BE;
1725 /* fall through */
1726 case AFMT_S16_BE:
1727 ss->format = PA_SAMPLE_S16BE;
1728 break;
1729
1730 case AFMT_U16_LE:
1731 *fmt = AFMT_S16_LE;
1732 /* fall through */
1733 case AFMT_S16_LE:
1734 ss->format = PA_SAMPLE_S16LE;
1735 break;
1736
1737 default:
1738 ss->format = PA_SAMPLE_S16NE;
1739 *fmt = AFMT_S16_NE;
1740 break;
1741 }
1742
1743 return 0;
1744 }
1745
1746 static int map_format_back(pa_sample_format_t format) {
1747 switch (format) {
1748 case PA_SAMPLE_S16LE: return AFMT_S16_LE;
1749 case PA_SAMPLE_S16BE: return AFMT_S16_BE;
1750 case PA_SAMPLE_ULAW: return AFMT_MU_LAW;
1751 case PA_SAMPLE_ALAW: return AFMT_A_LAW;
1752 case PA_SAMPLE_U8: return AFMT_U8;
1753 default:
1754 abort();
1755 }
1756 }
1757
1758 static int dsp_flush_fd(int fd) {
1759 #ifdef SIOCINQ
1760 int l;
1761
1762 if (ioctl(fd, SIOCINQ, &l) < 0) {
1763 debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ: %s\n", strerror(errno));
1764 return -1;
1765 }
1766
1767 while (l > 0) {
1768 char buf[1024];
1769 size_t k;
1770
1771 k = (size_t) l > sizeof(buf) ? sizeof(buf) : (size_t) l;
1772 if (read(fd, buf, k) < 0)
1773 debug(DEBUG_LEVEL_NORMAL, __FILE__": read(): %s\n", strerror(errno));
1774 l -= k;
1775 }
1776
1777 return 0;
1778 #else
1779 # warning "Your platform does not support SIOCINQ, something might not work as intended."
1780 return 0;
1781 #endif
1782 }
1783
1784 static int dsp_flush_socket(fd_info *i) {
1785 int res = 0;
1786
1787 if ((i->thread_fd < 0) && (i->app_fd < 0))
1788 return -1;
1789
1790 if (i->thread_fd >= 0)
1791 res = dsp_flush_fd(i->thread_fd);
1792
1793 if (res < 0)
1794 return res;
1795
1796 if (i->app_fd >= 0)
1797 res = dsp_flush_fd(i->app_fd);
1798
1799 if (res < 0)
1800 return res;
1801
1802 return 0;
1803 }
1804
1805 static int dsp_empty_socket(fd_info *i) {
1806 #ifdef SIOCINQ
1807 int ret = -1;
1808
1809 /* Empty the socket */
1810 for (;;) {
1811 int l;
1812
1813 if (i->thread_fd < 0)
1814 break;
1815
1816 if (ioctl(i->thread_fd, SIOCINQ, &l) < 0) {
1817 debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ: %s\n", strerror(errno));
1818 break;
1819 }
1820
1821 if (!l) {
1822 ret = 0;
1823 break;
1824 }
1825
1826 pa_threaded_mainloop_wait(i->mainloop);
1827 }
1828
1829 return ret;
1830 #else
1831 # warning "Your platform does not support SIOCINQ, something might not work as intended."
1832 return 0;
1833 #endif
1834 }
1835
1836 static int dsp_drain(fd_info *i) {
1837 pa_operation *o = NULL;
1838 int r = -1;
1839
1840 if (!i->mainloop)
1841 return 0;
1842
1843 debug(DEBUG_LEVEL_NORMAL, __FILE__": Draining.\n");
1844
1845 pa_threaded_mainloop_lock(i->mainloop);
1846
1847 if (dsp_empty_socket(i) < 0)
1848 goto fail;
1849
1850 if (!i->play_stream)
1851 goto fail;
1852
1853 debug(DEBUG_LEVEL_NORMAL, __FILE__": Really draining.\n");
1854
1855 if (!(o = pa_stream_drain(i->play_stream, stream_success_cb, i))) {
1856 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drain(): %s\n", pa_strerror(pa_context_errno(i->context)));
1857 goto fail;
1858 }
1859
1860 i->operation_success = 0;
1861 while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
1862 PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
1863
1864 pa_threaded_mainloop_wait(i->mainloop);
1865 }
1866
1867 if (!i->operation_success) {
1868 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_drain() 2: %s\n", pa_strerror(pa_context_errno(i->context)));
1869 goto fail;
1870 }
1871
1872 r = 0;
1873
1874 fail:
1875
1876 if (o)
1877 pa_operation_unref(o);
1878
1879 pa_threaded_mainloop_unlock(i->mainloop);
1880
1881 return r;
1882 }
1883
1884 static int dsp_trigger(fd_info *i) {
1885 pa_operation *o = NULL;
1886 int r = -1;
1887
1888 if (!i->play_stream)
1889 return 0;
1890
1891 pa_threaded_mainloop_lock(i->mainloop);
1892
1893 if (dsp_empty_socket(i) < 0)
1894 goto fail;
1895
1896 debug(DEBUG_LEVEL_NORMAL, __FILE__": Triggering.\n");
1897
1898 if (!(o = pa_stream_trigger(i->play_stream, stream_success_cb, i))) {
1899 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_trigger(): %s\n", pa_strerror(pa_context_errno(i->context)));
1900 goto fail;
1901 }
1902
1903 i->operation_success = 0;
1904 while (!pa_operation_get_state(o) != PA_OPERATION_DONE) {
1905 PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
1906
1907 pa_threaded_mainloop_wait(i->mainloop);
1908 }
1909
1910 if (!i->operation_success) {
1911 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_trigger(): %s\n", pa_strerror(pa_context_errno(i->context)));
1912 goto fail;
1913 }
1914
1915 r = 0;
1916
1917 fail:
1918
1919 if (o)
1920 pa_operation_unref(o);
1921
1922 pa_threaded_mainloop_unlock(i->mainloop);
1923
1924 return r;
1925 }
1926
1927 static int dsp_cork(fd_info *i, pa_stream *s, int b) {
1928 pa_operation *o = NULL;
1929 int r = -1;
1930
1931 pa_threaded_mainloop_lock(i->mainloop);
1932
1933 if (!(o = pa_stream_cork(s, b, stream_success_cb, i))) {
1934 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_cork(): %s\n", pa_strerror(pa_context_errno(i->context)));
1935 goto fail;
1936 }
1937
1938 i->operation_success = 0;
1939 while (!pa_operation_get_state(o) != PA_OPERATION_DONE) {
1940 if (s == i->play_stream)
1941 PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, fail);
1942 else if (s == i->rec_stream)
1943 RECORD_STREAM_CHECK_DEAD_GOTO(i, fail);
1944
1945 pa_threaded_mainloop_wait(i->mainloop);
1946 }
1947
1948 if (!i->operation_success) {
1949 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_cork(): %s\n", pa_strerror(pa_context_errno(i->context)));
1950 goto fail;
1951 }
1952
1953 r = 0;
1954
1955 fail:
1956
1957 if (o)
1958 pa_operation_unref(o);
1959
1960 pa_threaded_mainloop_unlock(i->mainloop);
1961
1962 return r;
1963 }
1964
1965 static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno) {
1966 int ret = -1;
1967
1968 if (i->thread_fd == -1) {
1969 /*
1970 * We've encountered some fatal error and are just waiting
1971 * for a close.
1972 */
1973 debug(DEBUG_LEVEL_NORMAL, __FILE__": got ioctl 0x%08lx in fatal error state\n", request);
1974 *_errno = EIO;
1975 return -1;
1976 }
1977
1978 switch (request) {
1979 case SNDCTL_DSP_SETFMT: {
1980 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETFMT: %i\n", *(int*) argp);
1981
1982 pa_threaded_mainloop_lock(i->mainloop);
1983
1984 if (*(int*) argp == AFMT_QUERY)
1985 *(int*) argp = map_format_back(i->sample_spec.format);
1986 else {
1987 map_format((int*) argp, &i->sample_spec);
1988 free_streams(i);
1989 }
1990
1991 pa_threaded_mainloop_unlock(i->mainloop);
1992 break;
1993 }
1994
1995 case SNDCTL_DSP_SPEED: {
1996 pa_sample_spec ss;
1997 int valid;
1998 char t[256];
1999
2000 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SPEED: %i\n", *(int*) argp);
2001
2002 pa_threaded_mainloop_lock(i->mainloop);
2003
2004 ss = i->sample_spec;
2005 ss.rate = *(int*) argp;
2006
2007 if ((valid = pa_sample_spec_valid(&ss))) {
2008 i->sample_spec = ss;
2009 free_streams(i);
2010 }
2011
2012 debug(DEBUG_LEVEL_NORMAL, __FILE__": ss: %s\n", pa_sample_spec_snprint(t, sizeof(t), &i->sample_spec));
2013
2014 pa_threaded_mainloop_unlock(i->mainloop);
2015
2016 if (!valid) {
2017 *_errno = EINVAL;
2018 goto fail;
2019 }
2020
2021 break;
2022 }
2023
2024 case SNDCTL_DSP_STEREO:
2025 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_STEREO: %i\n", *(int*) argp);
2026
2027 pa_threaded_mainloop_lock(i->mainloop);
2028
2029 i->sample_spec.channels = *(int*) argp ? 2 : 1;
2030 free_streams(i);
2031
2032 pa_threaded_mainloop_unlock(i->mainloop);
2033 return 0;
2034
2035 case SNDCTL_DSP_CHANNELS: {
2036 pa_sample_spec ss;
2037 int valid;
2038
2039 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_CHANNELS: %i\n", *(int*) argp);
2040
2041 pa_threaded_mainloop_lock(i->mainloop);
2042
2043 ss = i->sample_spec;
2044 ss.channels = *(int*) argp;
2045
2046 if ((valid = pa_sample_spec_valid(&ss))) {
2047 i->sample_spec = ss;
2048 free_streams(i);
2049 }
2050
2051 pa_threaded_mainloop_unlock(i->mainloop);
2052
2053 if (!valid) {
2054 *_errno = EINVAL;
2055 goto fail;
2056 }
2057
2058 break;
2059 }
2060
2061 case SNDCTL_DSP_GETBLKSIZE:
2062 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETBLKSIZE\n");
2063
2064 pa_threaded_mainloop_lock(i->mainloop);
2065
2066 fix_metrics(i);
2067 *(int*) argp = i->fragment_size;
2068
2069 pa_threaded_mainloop_unlock(i->mainloop);
2070
2071 break;
2072
2073 case SNDCTL_DSP_SETFRAGMENT:
2074 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETFRAGMENT: 0x%08x\n", *(int*) argp);
2075
2076 pa_threaded_mainloop_lock(i->mainloop);
2077
2078 i->fragment_size = 1 << ((*(int*) argp) & 31);
2079 i->n_fragments = (*(int*) argp) >> 16;
2080
2081 /* 0x7FFF means that we can set whatever we like */
2082 if (i->n_fragments == 0x7FFF)
2083 i->n_fragments = 12;
2084
2085 free_streams(i);
2086
2087 pa_threaded_mainloop_unlock(i->mainloop);
2088
2089 break;
2090
2091 case SNDCTL_DSP_GETCAPS:
2092 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_CAPS\n");
2093
2094 *(int*) argp = DSP_CAP_DUPLEX | DSP_CAP_TRIGGER
2095 #ifdef DSP_CAP_MULTI
2096 | DSP_CAP_MULTI
2097 #endif
2098 ;
2099 break;
2100
2101 case SNDCTL_DSP_GETODELAY: {
2102 int l;
2103
2104 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETODELAY\n");
2105
2106 pa_threaded_mainloop_lock(i->mainloop);
2107
2108 *(int*) argp = 0;
2109
2110 for (;;) {
2111 pa_usec_t usec;
2112
2113 PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, exit_loop);
2114
2115 if (pa_stream_get_latency(i->play_stream, &usec, NULL) >= 0) {
2116 *(int*) argp = pa_usec_to_bytes(usec, &i->sample_spec);
2117 break;
2118 }
2119
2120 if (pa_context_errno(i->context) != PA_ERR_NODATA) {
2121 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_get_latency(): %s\n", pa_strerror(pa_context_errno(i->context)));
2122 break;
2123 }
2124
2125 pa_threaded_mainloop_wait(i->mainloop);
2126 }
2127
2128 exit_loop:
2129
2130 #ifdef SIOCINQ
2131 if (ioctl(i->thread_fd, SIOCINQ, &l) < 0)
2132 debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ failed: %s\n", strerror(errno));
2133 else
2134 *(int*) argp += l;
2135 #else
2136 # warning "Your platform does not support SIOCINQ, something might not work as intended."
2137 #endif
2138
2139 pa_threaded_mainloop_unlock(i->mainloop);
2140
2141 debug(DEBUG_LEVEL_NORMAL, __FILE__": ODELAY: %i\n", *(int*) argp);
2142
2143 break;
2144 }
2145
2146 case SNDCTL_DSP_RESET: {
2147 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_RESET\n");
2148
2149 pa_threaded_mainloop_lock(i->mainloop);
2150
2151 free_streams(i);
2152 dsp_flush_socket(i);
2153
2154 i->optr_n_blocks = 0;
2155
2156 pa_threaded_mainloop_unlock(i->mainloop);
2157 break;
2158 }
2159
2160 case SNDCTL_DSP_GETFMTS: {
2161 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETFMTS\n");
2162
2163 *(int*) argp = AFMT_MU_LAW|AFMT_A_LAW|AFMT_U8|AFMT_S16_LE|AFMT_S16_BE;
2164 break;
2165 }
2166
2167 case SNDCTL_DSP_POST:
2168 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_POST\n");
2169
2170 if (dsp_trigger(i) < 0)
2171 *_errno = EIO;
2172 break;
2173
2174 case SNDCTL_DSP_GETTRIGGER:
2175 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETTRIGGER\n");
2176
2177 *(int*) argp = 0;
2178 if (!i->play_precork)
2179 *(int*) argp |= PCM_ENABLE_OUTPUT;
2180 if (!i->rec_precork)
2181 *(int*) argp |= PCM_ENABLE_INPUT;
2182
2183 break;
2184
2185 case SNDCTL_DSP_SETTRIGGER:
2186 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETTRIGGER: 0x%08x\n", *(int*) argp);
2187
2188 if (!i->io_event) {
2189 *_errno = EIO;
2190 break;
2191 }
2192
2193 i->play_precork = !((*(int*) argp) & PCM_ENABLE_OUTPUT);
2194
2195 if (i->play_stream) {
2196 if (dsp_cork(i, i->play_stream, !((*(int*) argp) & PCM_ENABLE_OUTPUT)) < 0)
2197 *_errno = EIO;
2198 if (dsp_trigger(i) < 0)
2199 *_errno = EIO;
2200 }
2201
2202 i->rec_precork = !((*(int*) argp) & PCM_ENABLE_INPUT);
2203
2204 if (i->rec_stream) {
2205 if (dsp_cork(i, i->rec_stream, !((*(int*) argp) & PCM_ENABLE_INPUT)) < 0)
2206 *_errno = EIO;
2207 }
2208
2209 break;
2210
2211 case SNDCTL_DSP_SYNC:
2212 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SYNC\n");
2213
2214 if (dsp_drain(i) < 0)
2215 *_errno = EIO;
2216
2217 break;
2218
2219 case SNDCTL_DSP_GETOSPACE:
2220 case SNDCTL_DSP_GETISPACE: {
2221 audio_buf_info *bi = (audio_buf_info*) argp;
2222 int l = 0;
2223 size_t k = 0;
2224
2225 if (request == SNDCTL_DSP_GETOSPACE)
2226 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETOSPACE\n");
2227 else
2228 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETISPACE\n");
2229
2230 pa_threaded_mainloop_lock(i->mainloop);
2231
2232 fix_metrics(i);
2233
2234 if (request == SNDCTL_DSP_GETOSPACE) {
2235 if (i->play_stream) {
2236 if ((k = pa_stream_writable_size(i->play_stream)) == (size_t) -1)
2237 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_writable_size(): %s\n", pa_strerror(pa_context_errno(i->context)));
2238 } else
2239 k = i->fragment_size * i->n_fragments;
2240
2241 #ifdef SIOCINQ
2242 if (ioctl(i->thread_fd, SIOCINQ, &l) < 0) {
2243 debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ failed: %s\n", strerror(errno));
2244 l = 0;
2245 }
2246 #else
2247 # warning "Your platform does not dsp_flush_fd, something might not work as intended."
2248 #endif
2249
2250 bi->bytes = k > (size_t) l ? k - l : 0;
2251 } else {
2252 if (i->rec_stream) {
2253 if ((k = pa_stream_readable_size(i->rec_stream)) == (size_t) -1)
2254 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_readable_size(): %s\n", pa_strerror(pa_context_errno(i->context)));
2255 } else
2256 k = 0;
2257
2258 #ifdef SIOCINQ
2259 if (ioctl(i->app_fd, SIOCINQ, &l) < 0) {
2260 debug(DEBUG_LEVEL_NORMAL, __FILE__": SIOCINQ failed: %s\n", strerror(errno));
2261 l = 0;
2262 }
2263 #else
2264 # warning "Your platform does not dsp_flush_fd, something might not work as intended."
2265 #endif
2266 bi->bytes = k + l;
2267 }
2268
2269 bi->fragsize = i->fragment_size;
2270 bi->fragstotal = i->n_fragments;
2271 bi->fragments = bi->bytes / bi->fragsize;
2272
2273 pa_threaded_mainloop_unlock(i->mainloop);
2274
2275 debug(DEBUG_LEVEL_NORMAL, __FILE__": fragsize=%i, fragstotal=%i, bytes=%i, fragments=%i\n", bi->fragsize, bi->fragstotal, bi->bytes, bi->fragments);
2276
2277 break;
2278 }
2279
2280 case SOUND_PCM_READ_RATE:
2281 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_RATE\n");
2282
2283 pa_threaded_mainloop_lock(i->mainloop);
2284 *(int*) argp = i->sample_spec.rate;
2285 pa_threaded_mainloop_unlock(i->mainloop);
2286 break;
2287
2288 case SOUND_PCM_READ_CHANNELS:
2289 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_CHANNELS\n");
2290
2291 pa_threaded_mainloop_lock(i->mainloop);
2292 *(int*) argp = i->sample_spec.channels;
2293 pa_threaded_mainloop_unlock(i->mainloop);
2294 break;
2295
2296 case SOUND_PCM_READ_BITS:
2297 debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_BITS\n");
2298
2299 pa_threaded_mainloop_lock(i->mainloop);
2300 *(int*) argp = pa_sample_size(&i->sample_spec)*8;
2301 pa_threaded_mainloop_unlock(i->mainloop);
2302 break;
2303
2304 case SNDCTL_DSP_GETOPTR: {
2305 count_info *info;
2306
2307 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_GETOPTR\n");
2308
2309 info = (count_info*) argp;
2310 memset(info, 0, sizeof(*info));
2311
2312 pa_threaded_mainloop_lock(i->mainloop);
2313
2314 for (;;) {
2315 pa_usec_t usec;
2316
2317 PLAYBACK_STREAM_CHECK_DEAD_GOTO(i, exit_loop2);
2318
2319 if (pa_stream_get_time(i->play_stream, &usec) >= 0) {
2320 size_t k = pa_usec_to_bytes(usec, &i->sample_spec);
2321 int m;
2322
2323 info->bytes = (int) k;
2324 m = k / i->fragment_size;
2325 info->blocks = m - i->optr_n_blocks;
2326 i->optr_n_blocks = m;
2327
2328 break;
2329 }
2330
2331 if (pa_context_errno(i->context) != PA_ERR_NODATA) {
2332 debug(DEBUG_LEVEL_NORMAL, __FILE__": pa_stream_get_latency(): %s\n", pa_strerror(pa_context_errno(i->context)));
2333 break;
2334 }
2335
2336 pa_threaded_mainloop_wait(i->mainloop);
2337 }
2338
2339 exit_loop2:
2340
2341 pa_threaded_mainloop_unlock(i->mainloop);
2342
2343 debug(DEBUG_LEVEL_NORMAL, __FILE__": GETOPTR bytes=%i, blocks=%i, ptr=%i\n", info->bytes, info->blocks, info->ptr);
2344
2345 break;
2346 }
2347
2348 case SNDCTL_DSP_GETIPTR:
2349 debug(DEBUG_LEVEL_NORMAL, __FILE__": invalid ioctl SNDCTL_DSP_GETIPTR\n");
2350 goto inval;
2351
2352 case SNDCTL_DSP_SETDUPLEX:
2353 debug(DEBUG_LEVEL_NORMAL, __FILE__": SNDCTL_DSP_SETDUPLEX\n");
2354 /* this is a no-op */
2355 break;
2356
2357 default:
2358 /* Mixer ioctls are valid on /dev/dsp as well */
2359 return mixer_ioctl(i, request, argp, _errno);
2360
2361 inval:
2362 *_errno = EINVAL;
2363 goto fail;
2364 }
2365
2366 ret = 0;
2367
2368 fail:
2369
2370 return ret;
2371 }
2372
2373 #ifdef sun
2374 int ioctl(int fd, int request, ...) {
2375 #else
2376 int ioctl(int fd, unsigned long request, ...) {
2377 #endif
2378 fd_info *i;
2379 va_list args;
2380 void *argp;
2381 int r, _errno = 0;
2382
2383 debug(DEBUG_LEVEL_VERBOSE, __FILE__": ioctl()\n");
2384
2385 va_start(args, request);
2386 argp = va_arg(args, void *);
2387 va_end(args);
2388
2389 if (!function_enter()) {
2390 LOAD_IOCTL_FUNC();
2391 return _ioctl(fd, request, argp);
2392 }
2393
2394 if (!(i = fd_info_find(fd))) {
2395 function_exit();
2396 LOAD_IOCTL_FUNC();
2397 return _ioctl(fd, request, argp);
2398 }
2399
2400 if (i->type == FD_INFO_MIXER)
2401 r = mixer_ioctl(i, request, argp, &_errno);
2402 else
2403 r = dsp_ioctl(i, request, argp, &_errno);
2404
2405 fd_info_unref(i);
2406
2407 if (_errno)
2408 errno = _errno;
2409
2410 function_exit();
2411
2412 return r;
2413 }
2414
2415 int close(int fd) {
2416 fd_info *i;
2417
2418 debug(DEBUG_LEVEL_VERBOSE, __FILE__": close()\n");
2419
2420 if (!function_enter()) {
2421 LOAD_CLOSE_FUNC();
2422 return _close(fd);
2423 }
2424
2425 if (!(i = fd_info_find(fd))) {
2426 function_exit();
2427 LOAD_CLOSE_FUNC();
2428 return _close(fd);
2429 }
2430
2431 fd_info_remove_from_list(i);
2432 fd_info_unref(i);
2433
2434 function_exit();
2435
2436 return 0;
2437 }
2438
2439 int access(const char *pathname, int mode) {
2440
2441 debug(DEBUG_LEVEL_VERBOSE, __FILE__": access(%s)\n", pathname?pathname:"NULL");
2442
2443 if (!pathname ||
2444 !is_audio_device_node(pathname)) {
2445 LOAD_ACCESS_FUNC();
2446 return _access(pathname, mode);
2447 }
2448
2449 if (mode & X_OK) {
2450 debug(DEBUG_LEVEL_NORMAL, __FILE__": access(%s, %x) = EACCESS\n", pathname, mode);
2451 errno = EACCES;
2452 return -1;
2453 }
2454
2455 debug(DEBUG_LEVEL_NORMAL, __FILE__": access(%s, %x) = OK\n", pathname, mode);
2456
2457 return 0;
2458 }
2459
2460 int stat(const char *pathname, struct stat *buf) {
2461 #ifdef HAVE_OPEN64
2462 struct stat64 parent;
2463 #else
2464 struct stat parent;
2465 #endif
2466 int ret;
2467
2468 if (!pathname ||
2469 !buf ||
2470 !is_audio_device_node(pathname)) {
2471 debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat(%s)\n", pathname?pathname:"NULL");
2472 LOAD_STAT_FUNC();
2473 return _stat(pathname, buf);
2474 }
2475
2476 debug(DEBUG_LEVEL_NORMAL, __FILE__": stat(%s)\n", pathname);
2477
2478 #ifdef _STAT_VER
2479 #ifdef HAVE_OPEN64
2480 ret = __xstat64(_STAT_VER, "/dev", &parent);
2481 #else
2482 ret = __xstat(_STAT_VER, "/dev", &parent);
2483 #endif
2484 #else
2485 #ifdef HAVE_OPEN64
2486 ret = stat64("/dev", &parent);
2487 #else
2488 ret = stat("/dev", &parent);
2489 #endif
2490 #endif
2491
2492 if (ret) {
2493 debug(DEBUG_LEVEL_NORMAL, __FILE__": unable to stat \"/dev\"\n");
2494 return -1;
2495 }
2496
2497 buf->st_dev = parent.st_dev;
2498 buf->st_ino = 0xDEADBEEF; /* FIXME: Can we do this in a safe way? */
2499 buf->st_mode = S_IFCHR | S_IRUSR | S_IWUSR;
2500 buf->st_nlink = 1;
2501 buf->st_uid = getuid();
2502 buf->st_gid = getgid();
2503 buf->st_rdev = 0x0E03; /* FIXME: Linux specific */
2504 buf->st_size = 0;
2505 buf->st_atime = 1181557705;
2506 buf->st_mtime = 1181557705;
2507 buf->st_ctime = 1181557705;
2508 buf->st_blksize = 1;
2509 buf->st_blocks = 0;
2510
2511 return 0;
2512 }
2513
2514 #ifdef HAVE_OPEN64
2515
2516 int stat64(const char *pathname, struct stat64 *buf) {
2517 struct stat oldbuf;
2518 int ret;
2519
2520 debug(DEBUG_LEVEL_VERBOSE, __FILE__": stat64(%s)\n", pathname?pathname:"NULL");
2521
2522 if (!pathname ||
2523 !buf ||
2524 !is_audio_device_node(pathname)) {
2525 LOAD_STAT64_FUNC();
2526 return _stat64(pathname, buf);
2527 }
2528
2529 ret = stat(pathname, &oldbuf);
2530 if (ret)
2531 return ret;
2532
2533 buf->st_dev = oldbuf.st_dev;
2534 buf->st_ino = oldbuf.st_ino;
2535 buf->st_mode = oldbuf.st_mode;
2536 buf->st_nlink = oldbuf.st_nlink;
2537 buf->st_uid = oldbuf.st_uid;
2538 buf->st_gid = oldbuf.st_gid;
2539 buf->st_rdev = oldbuf.st_rdev;
2540 buf->st_size = oldbuf.st_size;
2541 buf->st_atime = oldbuf.st_atime;
2542 buf->st_mtime = oldbuf.st_mtime;
2543 buf->st_ctime = oldbuf.st_ctime;
2544 buf->st_blksize = oldbuf.st_blksize;
2545 buf->st_blocks = oldbuf.st_blocks;
2546
2547 return 0;
2548 }
2549
2550 int open64(const char *filename, int flags, ...) {
2551 va_list args;
2552 mode_t mode = 0;
2553
2554 debug(DEBUG_LEVEL_VERBOSE, __FILE__": open64(%s)\n", filename?filename:"NULL");
2555
2556 if (flags & O_CREAT) {
2557 va_start(args, flags);
2558 if (sizeof(mode_t) < sizeof(int))
2559 mode = va_arg(args, int);
2560 else
2561 mode = va_arg(args, mode_t);
2562 va_end(args);
2563 }
2564
2565 if (!filename ||
2566 !is_audio_device_node(filename)) {
2567 LOAD_OPEN64_FUNC();
2568 return _open64(filename, flags, mode);
2569 }
2570
2571 return real_open(filename, flags, mode);
2572 }
2573
2574 int __open64_2(const char *filename, int flags) {
2575 debug(DEBUG_LEVEL_VERBOSE, __FILE__": __open64_2(%s)\n", filename?filename:"NULL");
2576
2577 if ((flags & O_CREAT) ||
2578 !filename ||
2579 !is_audio_device_node(filename)) {
2580 LOAD___OPEN64_2_FUNC();
2581 return ___open64_2(filename, flags);
2582 }
2583
2584 return real_open(filename, flags, 0);
2585 }
2586
2587 #endif
2588
2589 #ifdef _STAT_VER
2590
2591 int __xstat(int ver, const char *pathname, struct stat *buf) {
2592 debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat(%s)\n", pathname?pathname:"NULL");
2593
2594 if (!pathname ||
2595 !buf ||
2596 !is_audio_device_node(pathname)) {
2597 LOAD_XSTAT_FUNC();
2598 return ___xstat(ver, pathname, buf);
2599 }
2600
2601 if (ver != _STAT_VER) {
2602 errno = EINVAL;
2603 return -1;
2604 }
2605
2606 return stat(pathname, buf);
2607 }
2608
2609 #ifdef HAVE_OPEN64
2610
2611 int __xstat64(int ver, const char *pathname, struct stat64 *buf) {
2612 debug(DEBUG_LEVEL_VERBOSE, __FILE__": __xstat64(%s)\n", pathname?pathname:"NULL");
2613
2614 if (!pathname ||
2615 !buf ||
2616 !is_audio_device_node(pathname)) {
2617 LOAD_XSTAT64_FUNC();
2618 return ___xstat64(ver, pathname, buf);
2619 }
2620
2621 if (ver != _STAT_VER) {
2622 errno = EINVAL;
2623 return -1;
2624 }
2625
2626 return stat64(pathname, buf);
2627 }
2628
2629 #endif
2630
2631 #endif
2632
2633 FILE* fopen(const char *filename, const char *mode) {
2634 FILE *f = NULL;
2635 int fd;
2636 mode_t m;
2637
2638 debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen(%s)\n", filename?filename:"NULL");
2639
2640 if (!filename ||
2641 !mode ||
2642 !is_audio_device_node(filename)) {
2643 LOAD_FOPEN_FUNC();
2644 return _fopen(filename, mode);
2645 }
2646
2647 switch (mode[0]) {
2648 case 'r':
2649 m = O_RDONLY;
2650 break;
2651 case 'w':
2652 case 'a':
2653 m = O_WRONLY;
2654 break;
2655 default:
2656 errno = EINVAL;
2657 return NULL;
2658 }
2659
2660 if ((((mode[1] == 'b') || (mode[1] == 't')) && (mode[2] == '+')) || (mode[1] == '+'))
2661 m = O_RDWR;
2662
2663 if ((fd = real_open(filename, m, 0)) < 0)
2664 return NULL;
2665
2666 if (!(f = fdopen(fd, mode))) {
2667 close(fd);
2668 return NULL;
2669 }
2670
2671 return f;
2672 }
2673
2674 #ifdef HAVE_OPEN64
2675
2676 FILE *fopen64(const char *filename, const char *mode) {
2677
2678 debug(DEBUG_LEVEL_VERBOSE, __FILE__": fopen64(%s)\n", filename?filename:"NULL");
2679
2680 if (!filename ||
2681 !mode ||
2682 !is_audio_device_node(filename)) {
2683 LOAD_FOPEN64_FUNC();
2684 return _fopen64(filename, mode);
2685 }
2686
2687 return fopen(filename, mode);
2688 }
2689
2690 #endif
2691
2692 int fclose(FILE *f) {
2693 fd_info *i;
2694
2695 debug(DEBUG_LEVEL_VERBOSE, __FILE__": fclose()\n");
2696
2697 if (!function_enter()) {
2698 LOAD_FCLOSE_FUNC();
2699 return _fclose(f);
2700 }
2701
2702 if (!(i = fd_info_find(fileno(f)))) {
2703 function_exit();
2704 LOAD_FCLOSE_FUNC();
2705 return _fclose(f);
2706 }
2707
2708 fd_info_remove_from_list(i);
2709
2710 /* Dirty trick to avoid that the fd is not freed twice, once by us
2711 * and once by the real fclose() */
2712 i->app_fd = -1;
2713
2714 fd_info_unref(i);
2715
2716 function_exit();
2717
2718 LOAD_FCLOSE_FUNC();
2719 return _fclose(f);
2720 }