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