From: Ralf Jung Date: Wed, 23 Jun 2021 06:50:40 +0000 (+0200) Subject: move repository to Debian's GitLab X-Git-Url: https://git.ralfj.de/osspd.git/commitdiff_plain/HEAD?ds=sidebyside move repository to Debian's GitLab --- diff --git a/.gitignore b/.gitignore deleted file mode 100644 index b578114..0000000 --- a/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -*.[oa] -*~ -ossp-alsap -ossp-padsp -osspd -osstest diff --git a/98-osscuse.rules b/98-osscuse.rules deleted file mode 100644 index c1430fd..0000000 --- a/98-osscuse.rules +++ /dev/null @@ -1,7 +0,0 @@ -# Since these devices are not part of 'sound' subsystem the group is forced -# to audio by name -# /dev/cuse can stay mode 0660 root:root since osspd is run as root -# and drops privileges to user level when opened by user -KERNEL=="dsp", GROUP="audio" -KERNEL=="mixer", GROUP="audio" -KERNEL=="adsp", GROUP="audio" diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d511905..0000000 --- a/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/Makefile b/Makefile deleted file mode 100644 index 8111c9b..0000000 --- a/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# These can be overridden if needed -# DESTDIR is completely respected -CC := gcc -AR := ar -CFLAGS := -Wall $(CFLAGS) -XLDFLAGS := $(LDFLAGS) -LDFLAGS := -L. -lossp $(LDFLAGS) -prefix := /usr/local -DESTDIR := -UDEVDIR := /etc/udev/rules.d - -ifeq "$(origin OSSPD_CFLAGS)" "undefined" -OSSPD_CFLAGS := $(shell pkg-config --cflags fuse) -endif - -ifeq "$(origin OSSPD_LDFLAGS)" "undefined" -OSSPD_LDFLAGS := $(shell pkg-config --libs fuse) -endif - -ifeq "$(origin OSSP_PADSP_CFLAGS)" "undefined" -OSSP_PADSP_CFLAGS := $(shell pkg-config --cflags libpulse) -endif - -ifeq "$(origin OSSP_PADSP_LDFLAGS)" "undefined" -OSSP_PADSP_LDFLAGS := $(shell pkg-config --libs libpulse) -endif - -ifeq "$(origin OSSP_ALSAP_CFLAGS)" "undefined" -OSSP_ALSAP_CFLAGS := $(shell pkg-config --libs alsa) -endif - -ifeq "$(origin OSSP_ALSAP_LDFLAGS)" "undefined" -OSSP_ALSAP_LDFLAGS := $(shell pkg-config --libs alsa) -endif - -headers := ossp.h ossp-util.h ossp-slave.h - -all: osspd ossp-padsp ossp-alsap - -install: - mkdir -p $(DESTDIR)$(prefix)/sbin - install -m755 osspd ossp-padsp ossp-alsap $(DESTDIR)$(prefix)/sbin - mkdir -p $(DESTDIR)$(UDEVDIR) - install -m644 98-osscuse.rules $(DESTDIR)$(UDEVDIR) - -libossp.a: ossp.c ossp.h ossp-util.c ossp-util.h ossp-slave.c ossp-slave.h - $(CC) $(CFLAGS) -c -o ossp.o ossp.c - $(CC) $(CFLAGS) -c -o ossp-util.o ossp-util.c - $(CC) $(CFLAGS) -c -o ossp-slave.o ossp-slave.c - $(AR) rc $@ ossp.o ossp-util.o ossp-slave.o - -osspd: osspd.c libossp.a $(headers) - $(CC) $(CFLAGS) $(OSSPD_CFLAGS) -o $@ $< $(OSSPD_LDFLAGS) $(LDFLAGS) - -ossp-padsp: ossp-padsp.c libossp.a $(headers) - $(CC) $(CFLAGS) $(OSSP_PADSP_CFLAGS) -o $@ $< $(OSSP_PADSP_LDFLAGS) $(LDFLAGS) - -ossp-alsap: ossp-alsap.c libossp.a $(headers) - $(CC) $(CFLAGS) $(OSSP_ALSAP_CFLAGS) -o $@ $< $(OSSP_ALSAP_LDFLAGS) $(LDFLAGS) - -osstest: osstest.c - $(CC) $(CFLAGS) -o $@ $< $(XLDFLAGS) - -test: osstest - @./osstest - -clean: - rm -f *.o *.a osspd ossp-padsp ossp-alsap osstest diff --git a/README b/README index 6b716c7..a56aa1b 100644 --- a/README +++ b/README @@ -1,119 +1 @@ - - OSS Proxy - emulate OSS device using CUSE - - Copyright (C) 2008-2009 SUSE Linux Products GmbH - Copyright (C) 2008-2009 Tejun Heo - -1. What is it? --------------- - -Well, first, OSS refers to Open Sound System. If it still doesn't -ring a bell, think /dev/dsp, /dev/adsp and /dev/mixer. - -Currently, Linux supports two audio programming interface - ALSA and -OSS. The latter one is deprecated and has been that way for a long -time but there still are applications which still use them including -UML (usermode Linux) host sound support. - -ALSA contains OSS emulation but sadly the emulation is behind -multiplexing layer (which is in userland) which means that if your -sound card doesn't support multiple audio streams, only either one of -ALSA or OSS interface would be usable at any given moment. - -There have been also attempts to emulate OSS in userland using dynamic -library preloading - aoss and more recently padsp. This works for -many applications but it's just not easy to emulate everything using -the technique. Things like polling, signals, forking, privilege -changes make it very difficult to emulate things reliably. - -OSS Proxy uses CUSE (extension of FUSE allowing character devices to -be implemented in userspace) to implement OSS interface - /dev/dsp, -/dev/adsp and /dev/mixer. From the POV of the applications, these -devices are proper character devices and behave exactly the same way -so it can be made quite versatile. - - -2. Hmmm... So, how does the whole thing work? ---------------------------------------------- - -The OSS Proxy daemon - osspd - should be started first. Note that -osspd will fail to start if sound device number regions are already -occupied. You'll need to turn off OSS or its emulation[1]. - -On startup, osspd creates /dev/dsp, /dev/adsp and /dev/mixer using -CUSE. When an application access one of the devices, all IOs are -redirected to osspd via CUSE. Upon receiving a new DSP open request, -osspd creates a slave process which drops the root privilege and -assumes the opening process's credentials. After handshaking, osspd -forwards all relevant IOs to the slave which is responsible for -actually playing the sound. - -Currently there's only one slave implemented - ossp-padsp, which as -the name suggests forwards (again) the sound to pulseaudio. To sum -up, the whole pipe looks like the following. - - App <-> /dev/dsp <-> CUSE <-> osspd <-> ossp-padsp <-> pulseaudio - -Which is a lot of forwarding, but on modern machines, it won't be too -noticeable. - - -3. What works? --------------- - -Well, MIDI part isn't implemented and I doubt it will be in any near -future but except that everything should work. Playing, recording, -5.1ch, A-V syncing, all should work. If not, it's a bug, so please -report. - -The mixer behaves a bit differently tho. In the original OSS, -/dev/mixer is the hardware mixer, so adjusting volumes there affects -all audio streams. When using ossp, each process group gets its own -mixer and the mixer always contains only two knobs - PCM and IGAIN. -Combined with per-stream volume control of pulseaudio, this scheme -works quite well for applications with embedded volume control -although it makes standalone OSS mixer programs virtually useless[2]. - - -4. How do I use it? -------------------- - -First you need CUSE support in kernel which might land on 2.6.28 with -sufficient luck[3] and then you also need libfuse which supports -CUSE[4]. Once you have both, it should be easy. First build it by -running `make'. You can set OSSPD_CFLAGS, OSSPD_LDFLAGS, -OSSP_PADSP_CFLAGS and OSSP_PADSP_LDFLAGS if you have stuff at -non-default locations. - -After build completes, there will be two executables - `osspd' and -`ossp-padsp'. Just copy them to where other system executables live. -Specific location doesn't matter as long as both files end up in the -same directory. - -Execute `osspd'. It will create the device files and you're all set. -`osspd' uses syslog with LOG_DAEMON facility, so if something doesn't -work take a look at what osspd complains about. - - -[1] As of this writing, turning on any sound support makes the - soundcore module claim OSS device regions. Patch to make it claim - OSS device regions only when OSS support or emulation is enabled - is scheduled for 2.6.28. Even with the patch, soundcore will - claim OSS device regions if OSS support or ALSA OSS emulation is - enabled. Make sure they're turned off. - -[2] If you have a strong reason to use standalone OSS mixer program, - you can play some shell tricks to put it into the same process - group as the target audio application. e.g. To use aumix with - mpg123 - `(mpg123 asdf.mp3 > /dev/null 2>&1 & aumix)', but - seriously, just use PA or ALSA one. - -[3] For the time being, here's the git tree with all the necessary - changes. This tree is base on top of 2.6.27-rc3. - - http://git.kernel.org/?p=linux/kernel/git/tj/misc.git;a=shortlog;h=cuse - git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc.git cuse - -[4] And libfuse with the modifications can be found at... - - http://userweb.kernel.org/~tj/ossp/fuse-cuse.tar.gz +This repository moved to https://salsa.debian.org/debian/osspd diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index 9481d07..0000000 --- a/debian/changelog +++ /dev/null @@ -1,104 +0,0 @@ -osspd (1.3.2-12) unstable; urgency=low - - [ Sébastien Noel ] - * cherrypick 2 commits from upstream GIT: - + d/p/GIT-fix-adsp_se.patch - + d/p/GIT-fix-compiler-warnings.patch - * Add workaround for pulseaudio >= 13 - d/p/Hack-to-work-with-modern-PulseAudio.patch (Closes: #986662) - - [ Ralf Jung ] - * Switch to debhelper compat level 13. - - -- Ralf Jung Sat, 17 Apr 2021 14:28:09 +0200 - -osspd (1.3.2-11) unstable; urgency=medium - - * Update Standards-Version to 4.3.0. No changes needed. - * Fix cross-build with a patch by Helmut Grohne - (again). (Closes: #917827). - - -- Ralf Jung Fri, 25 Jan 2019 15:36:20 +0100 - -osspd (1.3.2-10) unstable; urgency=medium - - * Fix build on MIPS. (Closes: #916231) - - -- Ralf Jung Wed, 26 Dec 2018 19:10:03 +0100 - -osspd (1.3.2-9) unstable; urgency=medium - - * Fix using target-architecture pkg-config. Patch by - Helmut Grohne . (Closes: #882365) - * No longer depend on obsolete dh-systemd. Instead, depend - on sufficiently new debhelper. - * Update Standards-Version to 4.1.2. Changed some URLs to - https. - - -- Ralf Jung Fri, 08 Dec 2017 19:56:38 +0100 - -osspd (1.3.2-8) unstable; urgency=medium - - * Mark osspd as Multi-Arch: foreign (Closes: #861577). - * Update Standards-Version to 3.9.8. No changes needed. - * Use https URL for Vcs-Browser. - * Add Documentation to systemd service file. - * Fix a typo in the manpage. - - -- Ralf Jung Wed, 17 May 2017 19:31:18 +0200 - -osspd (1.3.2-7) unstable; urgency=medium - - * Migrate debug symbols to ddeb. - * Update Standards-Version to 3.9.6. No changes needed. - - -- Ralf Jung Mon, 28 Dec 2015 13:39:48 +0100 - -osspd (1.3.2-6) unstable; urgency=medium - - * Load cuse module via module-load.d. - * Update Standards-Version to 3.9.5. No changes needed. - - -- Ralf Jung Mon, 31 Mar 2014 17:36:52 +0200 - -osspd (1.3.2-5) unstable; urgency=low - - * Fix systemd service file. - - -- Ralf Jung Thu, 10 Oct 2013 18:29:28 +0200 - -osspd (1.3.2-4) unstable; urgency=low - - * Move backends into their own packages. - * Manage the ossp slave via update-alternatives. - * Add a systemd service file (taken from Arch). - * Add modprobe file to blacklist OSS modules - thanks, Stephen - (Closes: #712705). - - -- Ralf Jung Thu, 26 Sep 2013 22:28:08 +0200 - -osspd (1.3.2-3) unstable; urgency=low - - * Provide oss-compat, as we provide OSS, and conflict with it - since it takes the OSS major/minor numbers. - * Update Standards-Version to 3.9.4. No changes needed. - * Add "-pthreads" compiler and linker flag. Patch based on Ubuntu - patch by Logan Rosen . - * Add links to git repository. - - -- Ralf Jung Sat, 18 May 2013 18:18:14 +0200 - -osspd (1.3.2-2) unstable; urgency=low - - * 0004-Allow-to-set-slave-installation-path-during-compilat.patch: - Removed unused variables. - * Change architecture to linux-any as Hurd and FreeBSD lack epoll. - * Extend package description. - - -- Ralf Jung Tue, 02 Oct 2012 13:26:56 +0200 - -osspd (1.3.2-1) unstable; urgency=low - - * Initial release (Closes: #687711). - - -- Ralf Jung Thu, 20 Sep 2012 11:07:39 +0200 diff --git a/debian/control b/debian/control deleted file mode 100644 index ee044ca..0000000 --- a/debian/control +++ /dev/null @@ -1,61 +0,0 @@ -Source: osspd -Section: sound -Priority: optional -Maintainer: Ralf Jung -Uploaders: Sébastien Noel -Build-Depends: debhelper-compat (= 13), - libasound2-dev, - libfuse-dev, - libpulse-dev -Standards-Version: 4.3.0 -Homepage: https://sourceforge.net/projects/osspd/ -Vcs-Browser: https://git.ralfj.de/osspd.git -Vcs-Git: git://ralfj.de/osspd.git - -Package: osspd -Architecture: linux-any -Multi-Arch: foreign -Pre-Depends: ${misc:Pre-Depends} -Depends: lsb-base (>= 3.2-14), - osspd-pulseaudio | osspd-backend, - ${misc:Depends}, - ${shlibs:Depends} -Provides: oss-compat -Conflicts: oss-compat -Description: OSS Proxy Daemon: Userland OSS emulation - OSS Proxy Daemon is a Linux userland OSS sound device (/dev/[a]dsp and - /dev/mixer) implementation using CUSE. Currently it supports - forwarding OSS sound streams to PulseAudio and ALSA. - . - Actually emulating the OSS devices makes for a more robust emulation compared - to OSS wrappers using LD_PRELOAD, like aoss and padsp. It also works better - when running foreign-architecture applications or using old libc versions for - compatibility reasons. - -Package: osspd-pulseaudio -Architecture: linux-any -Provides: osspd-backend -Depends: pulseaudio, ${misc:Depends}, ${shlibs:Depends} -Recommends: osspd -Replaces: osspd (<< 1.3.2-4) -Breaks: osspd (<< 1.3.2-4) -Description: OSS Proxy Daemon: PulseAudio backend - OSS Proxy Daemon is a Linux userland OSS sound device (/dev/[a]dsp and - /dev/mixer) implementation using CUSE. Currently it supports - forwarding OSS sound streams to PulseAudio and ALSA. - . - This package contains the PulseAudio backend for osspd. - -Package: osspd-alsa -Architecture: linux-any -Provides: osspd-backend -Depends: ${misc:Depends}, ${shlibs:Depends} -Recommends: osspd -Replaces: osspd (<< 1.3.2-4) -Breaks: osspd (<< 1.3.2-4) -Description: OSS Proxy Daemon: ALSA backend (experimental) - OSS Proxy Daemon is a Linux userland OSS sound device (/dev/[a]dsp and - /dev/mixer) implementation using CUSE. Currently it supports - forwarding OSS sound streams to PulseAudio and ALSA. - . - This package contains the experimental ALSA backend for osspd. diff --git a/debian/copyright b/debian/copyright deleted file mode 100644 index f0c752d..0000000 --- a/debian/copyright +++ /dev/null @@ -1,52 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: osspd -Source: https://sourceforge.net/projects/osspd/ - -Files: * -Copyright: 2008-2010 SUSE Linux Products GmbH - 2008-2010 Tejun Heo - 2009 Maarten Lankhorst -License: GPL-2 - -Files: debian/* -Copyright: 2012-2013 Ralf Jung -License: GPL-2+ - -Files: debian/osspd.service -Copyright: 2013 Jan Alexander Steffens (heftig) - 2013 Ralf Jung -License: GPL-2 - - -License: GPL-2 - This package is free software; you can redistribute it and/or modify - it under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see - . - On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". - -License: GPL-2+ - This package is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see - . - On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". diff --git a/debian/osspd-alsa.install b/debian/osspd-alsa.install deleted file mode 100644 index 8b70aa6..0000000 --- a/debian/osspd-alsa.install +++ /dev/null @@ -1 +0,0 @@ -usr/lib/osspd/ossp-alsap diff --git a/debian/osspd-alsa.postinst b/debian/osspd-alsa.postinst deleted file mode 100644 index 64573ac..0000000 --- a/debian/osspd-alsa.postinst +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -set -e - -update-alternatives --install /usr/lib/osspd/ossp-slave \ - ossp-slave /usr/lib/osspd/ossp-alsap 60 - -#DEBHELPER# - -exit 0 diff --git a/debian/osspd-alsa.prerm b/debian/osspd-alsa.prerm deleted file mode 100644 index fee2410..0000000 --- a/debian/osspd-alsa.prerm +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -set -e - -if [ "$1" = "remove" ]; then - update-alternatives --remove ossp-slave /usr/lib/osspd/ossp-alsap -fi - -#DEBHELPER# - -exit 0 diff --git a/debian/osspd-pulseaudio.install b/debian/osspd-pulseaudio.install deleted file mode 100644 index ccb91fa..0000000 --- a/debian/osspd-pulseaudio.install +++ /dev/null @@ -1 +0,0 @@ -usr/lib/osspd/ossp-padsp diff --git a/debian/osspd-pulseaudio.postinst b/debian/osspd-pulseaudio.postinst deleted file mode 100644 index 84e2f2a..0000000 --- a/debian/osspd-pulseaudio.postinst +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -set -e - -update-alternatives --install /usr/lib/osspd/ossp-slave \ - ossp-slave /usr/lib/osspd/ossp-padsp 70 - -#DEBHELPER# - -exit 0 diff --git a/debian/osspd-pulseaudio.prerm b/debian/osspd-pulseaudio.prerm deleted file mode 100644 index 0f3c85d..0000000 --- a/debian/osspd-pulseaudio.prerm +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -set -e - -if [ "$1" = "remove" ]; then - update-alternatives --remove ossp-slave /usr/lib/osspd/ossp-padsp -fi - -#DEBHELPER# - -exit 0 diff --git a/debian/osspd.8 b/debian/osspd.8 deleted file mode 100644 index c94bc78..0000000 --- a/debian/osspd.8 +++ /dev/null @@ -1,68 +0,0 @@ -.TH OSSPD "8" "September 2012" "osspd 1.3.2" - -.SH NAME -osspd \- OSS Proxy Daemon - -.SH SYNOPSIS -\fBosspd\fR [\fIoptions\fR] - -.SH DESCRIPTION -\fBosspd\fR implements the OSS devices (/dev/[a]dsp and /dev/mixer) in userland using CUSE. -Sound data is forwarded to a DSP slave: Implementations for PulseAudio and ASLA exist, but the ALSA -slave should still be considered experimental. - -.SH OPTIONS -.TP -\fB\-\-dsp\fR=\fINAME\fR -DSP device name (default dsp) -.TP -\fB\-\-dsp\-maj\fR=\fIMAJ\fR -DSP device major number (default 14) -.TP -\fB\-\-dsp\-min\fR=\fIMIN\fR -DSP device minor number (default 3) -.TP -\fB\-\-adsp\fR=\fINAME\fR -Aux DSP device name (default adsp, blank to disable) -.TP -\fB\-\-adsp\-maj\fR=\fIMAJ\fR -Aux DSP device major number (default 14) -.TP -\fB\-\-adsp\-min\fR=\fIMIN\fR -Aux DSP device minor number (default 12) -.TP -\fB\-\-mixer\fR=\fINAME\fR -mixer device name (default mixer, blank to disable) -.TP -\fB\-\-mixer\-maj\fR=\fIMAJ\fR -mixer device major number (default 14) -.TP -\fB\-\-mixer\-min\fR=\fIMIN\fR -mixer device minor number (default 0) -.TP -\fB\-\-max\fR=\fIMAX\fR -maximum number of open streams (default 256) -.TP -\fB\-\-umax\fR=\fIMAX\fR -maximum number of open streams per UID (default \fB\-\-max\fR) -.TP -\fB\-\-exit\-on\-idle\fR -exit if idle -.TP -\fB\-\-dsp\-slave\fR=\fIPATH\fR -DSP slave (default ossp\-padsp in the same dir) -.TP -\fB\-\-log\fR=\fILEVEL\fR -log level (0..6) -.TP -\fB\-\-timestamp\fR -timestamp log messages -.TP -\fB\-v\fR -increase verbosity, can be specified multiple times -.TP -\fB\-f\fR -Run in foreground (don't daemonize) - -.SH "SEE ALSO" -A more technical documentation describing some of the inner workings can be found at \fI/usr/share/doc/osspd/README.gz\fR. diff --git a/debian/osspd.docs b/debian/osspd.docs deleted file mode 100644 index e845566..0000000 --- a/debian/osspd.docs +++ /dev/null @@ -1 +0,0 @@ -README diff --git a/debian/osspd.init b/debian/osspd.init deleted file mode 100644 index 8669c76..0000000 --- a/debian/osspd.init +++ /dev/null @@ -1,105 +0,0 @@ -#! /bin/sh -### BEGIN INIT INFO -# Provides: osspd -# Required-Start: $remote_fs $syslog -# Required-Stop: $remote_fs $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: OSS Proxy Daemon: Userland OSS emulation -# Description: Daemon providing a userland implementation of OSS devices. -# Currently it supports forwarding OSS sound streams to -# PulseAudio and ALSA. -### END INIT INFO - -# Author: Ralf Jung - -PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC="OSS Proxy Daemon" -NAME=osspd -DAEMON=/usr/sbin/$NAME -DAEMON_ARGS="--dsp-slave=/usr/lib/osspd/ossp-slave" -PIDFILE=/var/run/$NAME.pid -SCRIPTNAME=/etc/init.d/$NAME - -# Exit if the package is not installed -[ -x "$DAEMON" ] || exit 0 - -# Define LSB log_* functions. -. /lib/lsb/init-functions - -# -# Function that starts the daemon/service -# -do_start() -{ - # Return - # 0 if daemon has been started - # 1 if daemon was already running - # 2 if daemon could not be started - start-stop-daemon --start --background --quiet --pidfile $PIDFILE --make-pidfile --exec $DAEMON --test > /dev/null \ - || return 1 - # -f: run in foreground, start-stop-daemon does the forking - this is required to let start-stop-daemon handle the pidfile - start-stop-daemon --start --background --quiet --pidfile $PIDFILE --make-pidfile --exec $DAEMON -- -f \ - $DAEMON_ARGS \ - || return 2 -} - -# -# Function that stops the daemon/service -# -do_stop() -{ - # Return - # 0 if daemon has been stopped - # 1 if daemon was already stopped - # 2 if daemon could not be stopped - # other if a failure occurred - start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME - return "$?" -} - -case "$1" in - start) - log_daemon_msg "Starting $DESC" "$NAME" - do_start - case "$?" in - 0|1) log_end_msg 0 ;; - 2) log_end_msg 1 ;; - esac - ;; - stop) - log_daemon_msg "Stopping $DESC" "$NAME" - do_stop - case "$?" in - 0|1) log_end_msg 0 ;; - 2) log_end_msg 1 ;; - esac - ;; - status) - status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? - ;; - restart|force-reload) - log_daemon_msg "Restarting $DESC" "$NAME" - do_stop - case "$?" in - 0|1) - do_start - case "$?" in - 0) log_end_msg 0 ;; - 1) log_end_msg 1 ;; # Old process is still running - *) log_end_msg 1 ;; # Failed to start - esac - ;; - *) - # Failed to stop - log_end_msg 1 - ;; - esac - ;; - *) - echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 - exit 3 - ;; -esac - -: diff --git a/debian/osspd.install b/debian/osspd.install deleted file mode 100644 index ff410bf..0000000 --- a/debian/osspd.install +++ /dev/null @@ -1,2 +0,0 @@ -lib/udev/rules.d/98-osscuse.rules -usr/sbin/osspd diff --git a/debian/osspd.kmod b/debian/osspd.kmod deleted file mode 100644 index e1bacaf..0000000 --- a/debian/osspd.kmod +++ /dev/null @@ -1,2 +0,0 @@ -# Load cuse module for osspd -cuse diff --git a/debian/osspd.manpages b/debian/osspd.manpages deleted file mode 100644 index a289ff0..0000000 --- a/debian/osspd.manpages +++ /dev/null @@ -1 +0,0 @@ -debian/osspd.8 diff --git a/debian/osspd.modprobe b/debian/osspd.modprobe deleted file mode 100644 index 45d8163..0000000 --- a/debian/osspd.modprobe +++ /dev/null @@ -1,3 +0,0 @@ -blacklist snd-pcm-oss -blacklist snd-mixer-oss -blacklist snd-seq-oss diff --git a/debian/osspd.service b/debian/osspd.service deleted file mode 100644 index 5f35e2d..0000000 --- a/debian/osspd.service +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=OSS Proxy Daemon -Documentation=man:osspd - -[Service] -ExecStart=/usr/sbin/osspd -f --dsp-slave=/usr/lib/osspd/ossp-slave - -[Install] -WantedBy=multi-user.target diff --git a/debian/patches/0001-Fix-compilation-with-Werror-format-security.patch b/debian/patches/0001-Fix-compilation-with-Werror-format-security.patch deleted file mode 100644 index 085274f..0000000 --- a/debian/patches/0001-Fix-compilation-with-Werror-format-security.patch +++ /dev/null @@ -1,45 +0,0 @@ -From: Ralf Jung -Date: Sat, 15 Sep 2012 13:36:06 +0200 -Subject: Fix compilation with -Werror=format-security - -Forwarded: Committed upstream as 097dc7b6 ---- - ossp-slave.c | 4 ++-- - osspd.c | 2 +- - 2 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/ossp-slave.c b/ossp-slave.c -index 4c5cb2d..a290636 100644 ---- a/ossp-slave.c -+++ b/ossp-slave.c -@@ -89,7 +89,7 @@ void ossp_slave_init(int argc, char **argv) - } - - if (!have_uid || !have_gid || ossp_cmd_fd < 0 || ossp_notify_fd < 0) { -- fprintf(stderr, usage); -+ fputs(usage, stderr); - _exit(1); - } - -@@ -105,7 +105,7 @@ void ossp_slave_init(int argc, char **argv) - void *p; - - if (!mmap_off || !mmap_size) { -- fprintf(stderr, usage); -+ fputs(usage, stderr); - _exit(1); - } - -diff --git a/osspd.c b/osspd.c -index 37c9b35..1dbe586 100644 ---- a/osspd.c -+++ b/osspd.c -@@ -2109,7 +2109,7 @@ static int process_arg(void *data, const char *arg, int key, - - switch (key) { - case 0: -- fprintf(stderr, usage); -+ fputs(usage, stderr); - param->help = 1; - return 0; - case 1: diff --git a/debian/patches/0002-honor-CPPFLAGS.patch b/debian/patches/0002-honor-CPPFLAGS.patch deleted file mode 100644 index e073ad9..0000000 --- a/debian/patches/0002-honor-CPPFLAGS.patch +++ /dev/null @@ -1,22 +0,0 @@ -From: Ralf Jung -Date: Sat, 15 Sep 2012 13:36:23 +0200 -Subject: honor CPPFLAGS - -Forwarded: Committed upstream as 3a9a0196 ---- - Makefile | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/Makefile b/Makefile -index 8111c9b..8d8fe87 100644 ---- a/Makefile -+++ b/Makefile -@@ -2,7 +2,7 @@ - # DESTDIR is completely respected - CC := gcc - AR := ar --CFLAGS := -Wall $(CFLAGS) -+CFLAGS := -Wall $(CFLAGS) $(CPPFLAGS) - XLDFLAGS := $(LDFLAGS) - LDFLAGS := -L. -lossp $(LDFLAGS) - prefix := /usr/local diff --git a/debian/patches/0003-PA-recommends-users-not-to-be-in-the-audio-group-so-.patch b/debian/patches/0003-PA-recommends-users-not-to-be-in-the-audio-group-so-.patch deleted file mode 100644 index 4844721..0000000 --- a/debian/patches/0003-PA-recommends-users-not-to-be-in-the-audio-group-so-.patch +++ /dev/null @@ -1,26 +0,0 @@ -From: Ralf Jung -Date: Thu, 13 Sep 2012 19:13:25 +0200 -Subject: PA recommends users *not* to be in the audio group, - so allow everyone to use these devices - -Forwarded: not-needed ---- - 98-osscuse.rules | 9 ++++----- - 1 file changed, 4 insertions(+), 5 deletions(-) - -diff --git a/98-osscuse.rules b/98-osscuse.rules -index c1430fd..9b40bec 100644 ---- a/98-osscuse.rules -+++ b/98-osscuse.rules -@@ -1,7 +1,6 @@ --# Since these devices are not part of 'sound' subsystem the group is forced --# to audio by name -+# Allow everyone to use these devices - # /dev/cuse can stay mode 0660 root:root since osspd is run as root - # and drops privileges to user level when opened by user --KERNEL=="dsp", GROUP="audio" --KERNEL=="mixer", GROUP="audio" --KERNEL=="adsp", GROUP="audio" -+KERNEL=="dsp", SUBSYSTEM=="cuse", MODE="0666" -+KERNEL=="mixer", SUBSYSTEM=="cuse", MODE="0666" -+KERNEL=="adsp", SUBSYSTEM=="cuse", MODE="0666" diff --git a/debian/patches/0004-Allow-to-set-slave-installation-path-during-compilat.patch b/debian/patches/0004-Allow-to-set-slave-installation-path-during-compilat.patch deleted file mode 100644 index 59425c9..0000000 --- a/debian/patches/0004-Allow-to-set-slave-installation-path-during-compilat.patch +++ /dev/null @@ -1,85 +0,0 @@ -From: Ralf Jung -Date: Wed, 19 Sep 2012 13:12:58 +0200 -Subject: Allow to set slave installation path during compilation - -Forwarded: not-needed ---- - Makefile | 8 +++++--- - osspd.c | 12 ++---------- - 2 files changed, 7 insertions(+), 13 deletions(-) - -diff --git a/Makefile b/Makefile -index 8d8fe87..236176e 100644 ---- a/Makefile -+++ b/Makefile -@@ -8,6 +8,7 @@ LDFLAGS := -L. -lossp $(LDFLAGS) - prefix := /usr/local - DESTDIR := - UDEVDIR := /etc/udev/rules.d -+SLAVESDIR := $(prefix)/sbin - - ifeq "$(origin OSSPD_CFLAGS)" "undefined" - OSSPD_CFLAGS := $(shell pkg-config --cflags fuse) -@@ -38,8 +39,9 @@ headers := ossp.h ossp-util.h ossp-slave.h - all: osspd ossp-padsp ossp-alsap - - install: -- mkdir -p $(DESTDIR)$(prefix)/sbin -- install -m755 osspd ossp-padsp ossp-alsap $(DESTDIR)$(prefix)/sbin -+ mkdir -p $(DESTDIR)$(prefix)/sbin $(DESTDIR)$(SLAVESDIR) -+ install -m755 osspd $(DESTDIR)$(prefix)/sbin -+ install -m755 ossp-padsp ossp-alsap $(DESTDIR)$(SLAVESDIR) - mkdir -p $(DESTDIR)$(UDEVDIR) - install -m644 98-osscuse.rules $(DESTDIR)$(UDEVDIR) - -@@ -50,7 +52,7 @@ libossp.a: ossp.c ossp.h ossp-util.c ossp-util.h ossp-slave.c ossp-slave.h - $(AR) rc $@ ossp.o ossp-util.o ossp-slave.o - - osspd: osspd.c libossp.a $(headers) -- $(CC) $(CFLAGS) $(OSSPD_CFLAGS) -o $@ $< $(OSSPD_LDFLAGS) $(LDFLAGS) -+ $(CC) $(CFLAGS) $(OSSPD_CFLAGS) -DSLAVE_DEFAULT_PATH=\"$(SLAVESDIR)\" -o $@ $< $(OSSPD_LDFLAGS) $(LDFLAGS) - - ossp-padsp: ossp-padsp.c libossp.a $(headers) - $(CC) $(CFLAGS) $(OSSP_PADSP_CFLAGS) -o $@ $< $(OSSP_PADSP_LDFLAGS) $(LDFLAGS) -diff --git a/osspd.c b/osspd.c -index 1dbe586..6b63c07 100644 ---- a/osspd.c -+++ b/osspd.c -@@ -2005,7 +2005,7 @@ static const char *usage = - " --max=MAX maximum number of open streams (default 256)\n" - " --umax=MAX maximum number of open streams per UID (default --max)\n" - " --exit-on-idle exit if idle\n" --" --dsp-slave=PATH DSP slave (default ossp-padsp in the same dir)\n" -+" --dsp-slave=PATH DSP slave (default: " SLAVE_DEFAULT_PATH "/ossp-padsp)\n" - " --log=LEVEL log level (0..6)\n" - " --timestamp timestamp log messages\n" - " -v increase verbosity, can be specified multiple times\n" -@@ -2131,7 +2131,6 @@ int main(int argc, char **argv) - .max_streams = DFL_MAX_STREAMS, - }; - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -- char path_buf[PATH_MAX], *dir; - char adsp_buf[64] = "", mixer_buf[64] = ""; - struct sigaction sa; - struct stat stat_buf; -@@ -2175,19 +2174,12 @@ int main(int argc, char **argv) - if (sigaction(SIGPIPE, &sa, NULL)) - fatal_e(-errno, "failed to ignore SIGPIPE"); - -- /* determine slave path and check for availability */ -- ret = readlink("/proc/self/exe", path_buf, PATH_MAX - 1); -- if (ret < 0) -- fatal_e(-errno, "failed to determine executable path"); -- path_buf[ret] = '\0'; -- dir = dirname(path_buf); -- - if (param.dsp_slave_path) { - strncpy(dsp_slave_path, param.dsp_slave_path, PATH_MAX - 1); - dsp_slave_path[PATH_MAX - 1] = '\0'; - } else { - ret = snprintf(dsp_slave_path, PATH_MAX, "%s/%s", -- dir, "ossp-padsp"); -+ SLAVE_DEFAULT_PATH, "ossp-padsp"); - if (ret >= PATH_MAX) - fatal("dsp slave pathname too long"); - } diff --git a/debian/patches/0005-Add-pthread-compiler-and-linker-flag.patch b/debian/patches/0005-Add-pthread-compiler-and-linker-flag.patch deleted file mode 100644 index a45fb88..0000000 --- a/debian/patches/0005-Add-pthread-compiler-and-linker-flag.patch +++ /dev/null @@ -1,26 +0,0 @@ -From: Ralf Jung -Date: Sat, 11 May 2013 14:24:17 +0200 -Subject: Add -pthread compiler and linker flag - -(based on patch in Ubuntu by Logan Rosen ) -Forwarded: http://sourceforge.net/mailarchive/forum.php?thread_name=518E3901.8050206%40ralfj.de&forum_name=osspd-users ---- - Makefile | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/Makefile b/Makefile -index 236176e..11f2836 100644 ---- a/Makefile -+++ b/Makefile -@@ -2,9 +2,9 @@ - # DESTDIR is completely respected - CC := gcc - AR := ar --CFLAGS := -Wall $(CFLAGS) $(CPPFLAGS) -+CFLAGS := -Wall -pthread $(CFLAGS) $(CPPFLAGS) - XLDFLAGS := $(LDFLAGS) --LDFLAGS := -L. -lossp $(LDFLAGS) -+LDFLAGS := -L. -lossp -pthread $(LDFLAGS) - prefix := /usr/local - DESTDIR := - UDEVDIR := /etc/udev/rules.d diff --git a/debian/patches/0006-fix-build-on-MIPS.patch b/debian/patches/0006-fix-build-on-MIPS.patch deleted file mode 100644 index 9f72c3d..0000000 --- a/debian/patches/0006-fix-build-on-MIPS.patch +++ /dev/null @@ -1,20 +0,0 @@ -From: Ralf Jung -Date: Tue, 25 Dec 2018 18:51:01 +0100 -Subject: fix build on MIPS - ---- - osspd.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/osspd.c b/osspd.c -index 6b63c07..70eff98 100644 ---- a/osspd.c -+++ b/osspd.c -@@ -22,6 +22,7 @@ - #include - #include - #include -+#include - #include - #include - #include diff --git a/debian/patches/0007-cross.patch b/debian/patches/0007-cross.patch deleted file mode 100644 index 4a4b646..0000000 --- a/debian/patches/0007-cross.patch +++ /dev/null @@ -1,54 +0,0 @@ -From: Helmut Grohne -Date: Tue, 21 Nov 2017 21:20:20 +0100 -Subject: fix using target-architecture pkg-config - ---- - Makefile | 13 +++++++------ - 1 file changed, 7 insertions(+), 6 deletions(-) - -diff --git a/Makefile b/Makefile -index 11f2836..bd963ca 100644 ---- a/Makefile -+++ b/Makefile -@@ -2,6 +2,7 @@ - # DESTDIR is completely respected - CC := gcc - AR := ar -+PKG_CONFIG ?= pkg-config - CFLAGS := -Wall -pthread $(CFLAGS) $(CPPFLAGS) - XLDFLAGS := $(LDFLAGS) - LDFLAGS := -L. -lossp -pthread $(LDFLAGS) -@@ -11,27 +12,27 @@ UDEVDIR := /etc/udev/rules.d - SLAVESDIR := $(prefix)/sbin - - ifeq "$(origin OSSPD_CFLAGS)" "undefined" --OSSPD_CFLAGS := $(shell pkg-config --cflags fuse) -+OSSPD_CFLAGS := $(shell $(PKG_CONFIG) --cflags fuse) - endif - - ifeq "$(origin OSSPD_LDFLAGS)" "undefined" --OSSPD_LDFLAGS := $(shell pkg-config --libs fuse) -+OSSPD_LDFLAGS := $(shell $(PKG_CONFIG) --libs fuse) - endif - - ifeq "$(origin OSSP_PADSP_CFLAGS)" "undefined" --OSSP_PADSP_CFLAGS := $(shell pkg-config --cflags libpulse) -+OSSP_PADSP_CFLAGS := $(shell $(PKG_CONFIG) --cflags libpulse) - endif - - ifeq "$(origin OSSP_PADSP_LDFLAGS)" "undefined" --OSSP_PADSP_LDFLAGS := $(shell pkg-config --libs libpulse) -+OSSP_PADSP_LDFLAGS := $(shell $(PKG_CONFIG) --libs libpulse) - endif - - ifeq "$(origin OSSP_ALSAP_CFLAGS)" "undefined" --OSSP_ALSAP_CFLAGS := $(shell pkg-config --libs alsa) -+OSSP_ALSAP_CFLAGS := $(shell $(PKG_CONFIG) --libs alsa) - endif - - ifeq "$(origin OSSP_ALSAP_LDFLAGS)" "undefined" --OSSP_ALSAP_LDFLAGS := $(shell pkg-config --libs alsa) -+OSSP_ALSAP_LDFLAGS := $(shell $(PKG_CONFIG) --libs alsa) - endif - - headers := ossp.h ossp-util.h ossp-slave.h diff --git a/debian/patches/GIT-fix-adsp_se.patch b/debian/patches/GIT-fix-adsp_se.patch deleted file mode 100644 index 7316f9c..0000000 --- a/debian/patches/GIT-fix-adsp_se.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 4c6161d951daa98f6463904f76b3fa2ce7216194 Mon Sep 17 00:00:00 2001 -From: Tejun Heo -Date: Mon, 21 Feb 2011 11:54:06 +0100 -Subject: [PATCH] adsp_se was incorrectly created with dsp_ops. Create it with - adsp_ops. - -Reported-by: Aaron ---- - osspd.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/osspd.c b/osspd.c -index 37c9b35..df1cfc4 100644 ---- a/osspd.c -+++ b/osspd.c -@@ -2253,7 +2253,7 @@ int main(int argc, char **argv) - param.mixer_major, param.mixer_minor, - args.argc, args.argv); - if (strlen(param.adsp_name)) -- adsp_se = setup_ossp_cuse(&dsp_ops, param.adsp_name, -+ adsp_se = setup_ossp_cuse(&adsp_ops, param.adsp_name, - param.adsp_major, param.adsp_minor, - args.argc, args.argv); - diff --git a/debian/patches/GIT-fix-compiler-warnings.patch b/debian/patches/GIT-fix-compiler-warnings.patch deleted file mode 100644 index 1424b2b..0000000 --- a/debian/patches/GIT-fix-compiler-warnings.patch +++ /dev/null @@ -1,251 +0,0 @@ -From 37eb730a452f0ded2ed1c174feb438e3df041581 Mon Sep 17 00:00:00 2001 -From: Miklos Szeredi -Date: Fri, 11 Nov 2011 14:19:32 +0100 -Subject: [PATCH] fix compiler warnings - ---- - ossp-padsp.c | 3 --- - osspd.c | 75 ++++++++++++++++++++++++++++++---------------------- - 2 files changed, 44 insertions(+), 34 deletions(-) - -diff --git a/ossp-padsp.c b/ossp-padsp.c -index 1871f5b..3143960 100644 ---- a/ossp-padsp.c -+++ b/ossp-padsp.c -@@ -972,16 +972,13 @@ static void do_mmap_read(size_t bytes) - - static void stream_rw_callback(pa_stream *s, size_t length, void *userdata) - { -- int dir; - size_t size; - - if (s == stream[PLAY]) { -- dir = PLAY; - size = pa_stream_writable_size(s); - if (mmap_map[PLAY]) - do_mmap_write(size); - } else if (s == stream[REC]) { -- dir = REC; - size = pa_stream_readable_size(s); - if (mmap_map[REC]) - do_mmap_read(size); -diff --git a/osspd.c b/osspd.c -index df1cfc4..4be1ad5 100644 ---- a/osspd.c -+++ b/osspd.c -@@ -469,15 +469,6 @@ static int ioctl_prep_uarg(fuse_req_t req, void *in, size_t in_sz, void *out, - return; \ - } while (0) - --#define IOCTL_RETURN(result, outp) do { \ -- if ((outp) != NULL) \ -- fuse_reply_ioctl(req, result, (outp), sizeof(*(outp))); \ -- else \ -- fuse_reply_ioctl(req, result, NULL, 0); \ -- return; \ --} while (0) -- -- - /*************************************************************************** - * Mixer implementation - */ -@@ -709,7 +700,8 @@ static void mixer_simple_ioctl(fuse_req_t req, struct ossp_mixer *mixer, - strncpy(info.id, id, sizeof(info.id) - 1); - strncpy(info.name, name, sizeof(info.name) - 1); - info.modify_counter = mixer->modify_counter; -- IOCTL_RETURN(0, &info); -+ fuse_reply_ioctl(req, 0, &info, sizeof(info)); -+ break; - } - - case SOUND_OLD_MIXER_INFO: { -@@ -718,7 +710,8 @@ static void mixer_simple_ioctl(fuse_req_t req, struct ossp_mixer *mixer, - PREP_UARG(NULL, &info); - strncpy(info.id, id, sizeof(info.id) - 1); - strncpy(info.name, name, sizeof(info.name) - 1); -- IOCTL_RETURN(0, &info); -+ fuse_reply_ioctl(req, 0, &info, sizeof(info)); -+ break; - } - - case OSS_GETVERSION: -@@ -737,16 +730,16 @@ static void mixer_simple_ioctl(fuse_req_t req, struct ossp_mixer *mixer, - goto puti; - puti: - PREP_UARG(NULL, &i); -- IOCTL_RETURN(0, &i); -+ fuse_reply_ioctl(req, 0, &i, sizeof(i)); -+ break; - - case SOUND_MIXER_WRITE_RECSRC: -- IOCTL_RETURN(0, NULL); -+ fuse_reply_ioctl(req, 0, NULL, 0); -+ break; - - default: - *not_minep = 1; -- return; - } -- assert(0); - } - - static void mixer_do_ioctl(fuse_req_t req, struct ossp_mixer *mixer, -@@ -787,7 +780,8 @@ static void mixer_do_ioctl(fuse_req_t req, struct ossp_mixer *mixer, - break; - default: - i = 0; -- IOCTL_RETURN(0, &i); -+ fuse_reply_ioctl(req, 0, &i, sizeof(i)); -+ return; - } - - init_mixer_cmd(&mxcmd, mixer); -@@ -837,7 +831,12 @@ static void mixer_do_ioctl(fuse_req_t req, struct ossp_mixer *mixer, - finish_mixer_cmd(&mxcmd); - free(osa); - -- IOCTL_RETURN(0, out_bufsz ? &mxcmd.rvol : NULL); -+ if (out_bufsz) -+ fuse_reply_ioctl(req, 0, &mxcmd.rvol, sizeof(mxcmd.rvol)); -+ else -+ fuse_reply_ioctl(req, 0, NULL, 0); -+ -+ return; - - err: - fuse_reply_err(req, -rc); -@@ -1510,7 +1509,8 @@ static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - case OSS_GETVERSION: - i = SNDRV_OSS_VERSION; - PREP_UARG(NULL, &i); -- IOCTL_RETURN(0, &i); -+ fuse_reply_ioctl(req, 0, &i, sizeof(i)); -+ break; - - case SNDCTL_DSP_GETCAPS: - i = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | -@@ -1519,12 +1519,14 @@ static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - #endif - DSP_CAP_MULTI; - PREP_UARG(NULL, &i); -- IOCTL_RETURN(0, &i); -+ fuse_reply_ioctl(req, 0, &i, sizeof(i)); -+ break; - - case SNDCTL_DSP_NONBLOCK: - dsps->nonblock = 1; - ret = 0; -- IOCTL_RETURN(0, NULL); -+ fuse_reply_ioctl(req, 0, NULL, 0); -+ break; - - case SNDCTL_DSP_RESET: op = OSSP_DSP_RESET; goto nd; - case SNDCTL_DSP_SYNC: op = OSSP_DSP_SYNC; goto nd; -@@ -1533,7 +1535,8 @@ static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - ret = exec_simple_cmd(&dsps->os, op, NULL, NULL); - if (ret) - goto err; -- IOCTL_RETURN(0, NULL); -+ fuse_reply_ioctl(req, 0, NULL, 0); -+ break; - - case SOUND_PCM_READ_RATE: op = OSSP_DSP_GET_RATE; goto ri; - case SOUND_PCM_READ_BITS: op = OSSP_DSP_GET_FORMAT; goto ri; -@@ -1546,7 +1549,8 @@ static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - ret = exec_simple_cmd(&dsps->os, op, NULL, &i); - if (ret) - goto err; -- IOCTL_RETURN(0, &i); -+ fuse_reply_ioctl(req, 0, &i, sizeof(i)); -+ break; - - case SNDCTL_DSP_SPEED: op = OSSP_DSP_SET_RATE; goto wi; - case SNDCTL_DSP_SETFMT: op = OSSP_DSP_SET_FORMAT; goto wi; -@@ -1557,7 +1561,8 @@ static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - ret = exec_simple_cmd(&dsps->os, op, &i, &i); - if (ret) - goto err; -- IOCTL_RETURN(0, &i); -+ fuse_reply_ioctl(req, 0, &i, sizeof(i)); -+ break; - - case SNDCTL_DSP_STEREO: - PREP_UARG(NULL, &i); -@@ -1566,7 +1571,8 @@ static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - i--; - if (ret) - goto err; -- IOCTL_RETURN(0, &i); -+ fuse_reply_ioctl(req, 0, &i, sizeof(i)); -+ break; - - case SNDCTL_DSP_SETFRAGMENT: - PREP_UARG(&i, NULL); -@@ -1574,7 +1580,8 @@ static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - OSSP_DSP_SET_FRAGMENT, &i, NULL); - if (ret) - goto err; -- IOCTL_RETURN(0, NULL); -+ fuse_reply_ioctl(req, 0, NULL, 0); -+ break; - - case SNDCTL_DSP_SETTRIGGER: - PREP_UARG(&i, NULL); -@@ -1582,7 +1589,8 @@ static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - OSSP_DSP_SET_TRIGGER, &i, NULL); - if (ret) - goto err; -- IOCTL_RETURN(0, NULL); -+ fuse_reply_ioctl(req, 0, NULL, 0); -+ break; - - case SNDCTL_DSP_GETOSPACE: - case SNDCTL_DSP_GETISPACE: { -@@ -1603,7 +1611,8 @@ static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - ret = exec_simple_cmd(&dsps->os, op, NULL, &info); - if (ret) - goto err; -- IOCTL_RETURN(0, &info); -+ fuse_reply_ioctl(req, 0, &info, sizeof(info)); -+ break; - } - - case SNDCTL_DSP_GETOPTR: -@@ -1616,14 +1625,16 @@ static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - ret = exec_simple_cmd(&dsps->os, op, NULL, &info); - if (ret) - goto err; -- IOCTL_RETURN(0, &info); -+ fuse_reply_ioctl(req, 0, &info, sizeof(info)); -+ break; - } - - case SNDCTL_DSP_GETODELAY: - PREP_UARG(NULL, &i); - i = 0; - ret = exec_simple_cmd(&dsps->os, OSSP_DSP_GET_ODELAY, NULL, &i); -- IOCTL_RETURN(ret, &i); /* always copy out result, 0 on err */ -+ fuse_reply_ioctl(req, ret, &i, sizeof(i)); /* always copy out result, 0 on err */ -+ break; - - case SOUND_PCM_WRITE_FILTER: - case SOUND_PCM_READ_FILTER: -@@ -1638,14 +1649,16 @@ static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - case SNDCTL_DSP_SETSYNCRO: - case SNDCTL_DSP_SETDUPLEX: - case SNDCTL_DSP_PROFILE: -- IOCTL_RETURN(0, NULL); -+ fuse_reply_ioctl(req, 0, NULL, 0); -+ break; - - default: - warn_os(os, "unknown ioctl 0x%x", cmd); - ret = -EINVAL; - goto err; - } -- assert(0); /* control shouldn't reach here */ -+ return; -+ - err: - fuse_reply_err(req, -ret); - } diff --git a/debian/patches/Hack-to-work-with-modern-PulseAudio.patch b/debian/patches/Hack-to-work-with-modern-PulseAudio.patch deleted file mode 100644 index 25996cc..0000000 --- a/debian/patches/Hack-to-work-with-modern-PulseAudio.patch +++ /dev/null @@ -1,37 +0,0 @@ -From: "Jan Alexander Steffens (heftig)" -Date: Fri, 18 Dec 2020 23:17:36 +0000 -Subject: [PATCH] Hack to work with modern PulseAudio - ---- - ossp-padsp.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/ossp-padsp.c b/ossp-padsp.c -index c505b57..b4ac097 100644 ---- a/ossp-padsp.c -+++ b/ossp-padsp.c -@@ -22,6 +22,8 @@ - #include - #include - #include -+#include -+#include - - #include - #include -@@ -1478,9 +1480,15 @@ static void action_post(void) - int main(int argc, char **argv) - { - int rc; -+ static char runtime_dir[PATH_MAX]; - - ossp_slave_init(argc, argv); - -+ snprintf(runtime_dir, sizeof runtime_dir, "/run/user/%llu", -+ (long long unsigned) getuid()); -+ if (access(runtime_dir, R_OK | X_OK) == 0) -+ setenv("XDG_RUNTIME_DIR", runtime_dir, 0); -+ - page_size = sysconf(_SC_PAGE_SIZE); - - mainloop = pa_threaded_mainloop_new(); diff --git a/debian/patches/series b/debian/patches/series deleted file mode 100644 index 03f1d68..0000000 --- a/debian/patches/series +++ /dev/null @@ -1,10 +0,0 @@ -0001-Fix-compilation-with-Werror-format-security.patch -0002-honor-CPPFLAGS.patch -0003-PA-recommends-users-not-to-be-in-the-audio-group-so-.patch -0004-Allow-to-set-slave-installation-path-during-compilat.patch -0005-Add-pthread-compiler-and-linker-flag.patch -0006-fix-build-on-MIPS.patch -0007-cross.patch -GIT-fix-adsp_se.patch -GIT-fix-compiler-warnings.patch -Hack-to-work-with-modern-PulseAudio.patch diff --git a/debian/rules b/debian/rules deleted file mode 100755 index 2a38240..0000000 --- a/debian/rules +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/make -f - -SLAVESDIR=/usr/lib/osspd -UDEVDIR=/lib/udev/rules.d - -%: - dh $@ - -override_dh_auto_build: - dh_auto_build -- SLAVESDIR=$(SLAVESDIR) UDEVDIR=$(UDEVDIR) - -override_dh_auto_install: - dh_auto_install -- prefix=/usr SLAVESDIR=$(SLAVESDIR) UDEVDIR=$(UDEVDIR) - -override_dh_strip: - dh_strip --ddeb-migration='osspd-dbg (<< 1.3.2-7~)' - -override_dh_install: - dh_install - # adding kmod integration - install -D -m 0644 debian/osspd.kmod debian/osspd/lib/modules-load.d/osspd.conf - -# Disable tests, they require running the osspd -override_dh_auto_test: diff --git a/debian/source/format b/debian/source/format deleted file mode 100644 index 163aaf8..0000000 --- a/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/debian/watch b/debian/watch deleted file mode 100644 index 919e200..0000000 --- a/debian/watch +++ /dev/null @@ -1,2 +0,0 @@ -version=3 -https://sf.net/osspd/ossp-(.+)\.tar\.gz diff --git a/ossp-alsap.c b/ossp-alsap.c deleted file mode 100644 index 72f3bd5..0000000 --- a/ossp-alsap.c +++ /dev/null @@ -1,592 +0,0 @@ -/* - * ossp-alsap - ossp DSP slave which forwards to alsa - * - * Copyright (C) 2009 Maarten Lankhorst - * - * This file is released under the GPLv2. - * - * Why an alsa plugin as well? Just to show how much - * the alsa userspace api sucks ;-) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "ossp-slave.h" - -enum { - AFMT_FLOAT = 0x00004000, - AFMT_S32_LE = 0x00001000, - AFMT_S32_BE = 0x00002000, -}; - -static size_t page_size; - -/* alsa structures */ -static snd_pcm_t *pcm[2]; -static snd_pcm_hw_params_t *hw_params; -static snd_pcm_sw_params_t *sw_params; -static int block; - -static unsigned int byte_counter[2]; -static snd_pcm_uframes_t mmap_pos[2]; -static int stream_corked[2]; -static int stream_notify; - -static struct format { - snd_pcm_format_t format; - snd_pcm_sframes_t rate; - int channels; -} hw_format = { SND_PCM_FORMAT_U8, 8000, 1 }; - -#if 0 -/* future mmap stuff */ -static size_t mmap_raw_size, mmap_size; -static int mmap_fd[2] = { -1, -1 }; -static void *mmap_map[2]; -static uint64_t mmap_idx[2]; /* mmap pointer */ -static uint64_t mmap_last_idx[2]; /* last idx for get_ptr */ -static struct ring_buf mmap_stg[2]; /* staging ring buffer */ -static size_t mmap_lead[2]; /* lead bytes */ -static int mmap_sync[2]; /* sync with backend stream */ -#endif - -static snd_pcm_format_t fmt_oss_to_alsa(int fmt) -{ - switch (fmt) { - case AFMT_U8: return SND_PCM_FORMAT_U8; - case AFMT_A_LAW: return SND_PCM_FORMAT_A_LAW; - case AFMT_MU_LAW: return SND_PCM_FORMAT_MU_LAW; - case AFMT_S16_LE: return SND_PCM_FORMAT_S16_LE; - case AFMT_S16_BE: return SND_PCM_FORMAT_S16_BE; - case AFMT_FLOAT: return SND_PCM_FORMAT_FLOAT; - case AFMT_S32_LE: return SND_PCM_FORMAT_S32_LE; - case AFMT_S32_BE: return SND_PCM_FORMAT_S32_BE; - default: return SND_PCM_FORMAT_U8; - } -} - -static int fmt_alsa_to_oss(snd_pcm_format_t fmt) -{ - switch (fmt) { - case SND_PCM_FORMAT_U8: return AFMT_U8; - case SND_PCM_FORMAT_A_LAW: return AFMT_A_LAW; - case SND_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; - case SND_PCM_FORMAT_S16_LE: return AFMT_S16_LE; - case SND_PCM_FORMAT_S16_BE: return AFMT_S16_BE; - case SND_PCM_FORMAT_FLOAT: return AFMT_FLOAT; - case SND_PCM_FORMAT_S32_LE: return AFMT_S32_LE; - case SND_PCM_FORMAT_S32_BE: return AFMT_S32_BE; - default: return AFMT_U8; - } -} - -static void flush_streams(int drain) -{ - /* FIXME: snd_pcm_drain appears to be able to deadlock, - * always drop or check state? */ - if (drain) { - if (pcm[PLAY]) - snd_pcm_drain(pcm[PLAY]); - if (pcm[REC]) - snd_pcm_drain(pcm[REC]); - } else { - if (pcm[PLAY]) - snd_pcm_drop(pcm[PLAY]); - if (pcm[REC]) - snd_pcm_drop(pcm[REC]); - } - - /* XXX: Really needed? */ -#if 0 - if (pcm[PLAY]) { - snd_pcm_close(pcm[PLAY]); - snd_pcm_open(&pcm[PLAY], "default", - SND_PCM_STREAM_PLAYBACK, block); - } - if (pcm[REC]) { - snd_pcm_close(pcm[REC]); - snd_pcm_open(&pcm[REC], "default", - SND_PCM_STREAM_CAPTURE, block); - } -#endif -} - -static void kill_streams(void) -{ - flush_streams(0); -} - -static int trigger_streams(int play, int rec) -{ - int ret = 0; - - if (pcm[PLAY] && play >= 0) { - ret = snd_pcm_sw_params_set_start_threshold(pcm[PLAY], sw_params, - play ? 1 : -1); - if (ret >= 0) - snd_pcm_sw_params(pcm[PLAY], sw_params); - } - if (ret >= 0 && pcm[REC] && rec >= 0) { - ret = snd_pcm_sw_params_set_start_threshold(pcm[REC], sw_params, - rec ? 1 : -1); - if (ret >= 0) - snd_pcm_sw_params(pcm[REC], sw_params); - } - - return ret; -} - -static ssize_t alsap_mixer(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ -return -EBUSY; -} - -static int set_hw_params(snd_pcm_t *pcm) -{ - int ret; - unsigned rate; - - ret = snd_pcm_hw_params_any(pcm, hw_params); - if (ret >= 0) - ret = snd_pcm_hw_params_set_access(pcm, hw_params, - SND_PCM_ACCESS_RW_INTERLEAVED); - rate = hw_format.rate; - if (ret >= 0) - ret = snd_pcm_hw_params_set_rate_minmax(pcm, hw_params, - &rate, NULL, - &rate, NULL); - if (ret >= 0) - ret = snd_pcm_hw_params_set_format(pcm, hw_params, hw_format.format); - if (ret >= 0) - ret = snd_pcm_hw_params_set_channels(pcm, hw_params, - hw_format.channels); - if (ret >= 0) - ret = snd_pcm_hw_params(pcm, hw_params); - if (ret >= 0) - ret = snd_pcm_sw_params_current(pcm, sw_params); - if (ret >= 0) - ret = snd_pcm_sw_params(pcm, sw_params); - return ret; -} - -static ssize_t alsap_open(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - struct ossp_dsp_open_arg *arg = carg; - int ret; - block = arg->flags & O_NONBLOCK ? SND_PCM_NONBLOCK : 0; - int access; -// block |= SND_PCM_ASYNC; - /* Woop dee dooo.. I love handling things in SIGIO (PAIN!!) - * Probably needed for MMAP - */ - - if (!hw_params) - ret = snd_pcm_hw_params_malloc(&hw_params); - if (ret < 0) - return ret; - - if (!sw_params) - ret = snd_pcm_sw_params_malloc(&sw_params); - if (ret < 0) - return ret; - - if (pcm[PLAY]) - snd_pcm_close(pcm[PLAY]); - if (pcm[REC]) - snd_pcm_close(pcm[REC]); - pcm[REC] = pcm[PLAY] = NULL; - - access = arg->flags & O_ACCMODE; - if (access == O_WRONLY || access == O_RDWR) { - ret = snd_pcm_open(&pcm[PLAY], "default", - SND_PCM_STREAM_PLAYBACK, block); - if (ret >= 0) - ret = set_hw_params(pcm[PLAY]); - } - - if (ret >= 0 && (access == O_RDONLY || access == O_RDWR)) { - ret = snd_pcm_open(&pcm[REC], "default", - SND_PCM_STREAM_CAPTURE, block); - if (ret >= 0) - ret = set_hw_params(pcm[REC]); - } - - if (ret < 0) { - if (pcm[PLAY]) - snd_pcm_close(pcm[PLAY]); - if (pcm[REC]) - snd_pcm_close(pcm[REC]); - pcm[REC] = pcm[PLAY] = NULL; - return ret; - } - return 0; -} - -static ssize_t alsap_write(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ -// struct ossp_dsp_rw_arg *arg = carg; - int ret, insize; - - insize = snd_pcm_bytes_to_frames(pcm[PLAY], din_sz); - - if (snd_pcm_state(pcm[PLAY]) == SND_PCM_STATE_SETUP) - snd_pcm_prepare(pcm[PLAY]); - -// snd_pcm_start(pcm[PLAY]); - ret = snd_pcm_writei(pcm[PLAY], din, insize); - if (ret < 0) - ret = snd_pcm_recover(pcm[PLAY], ret, 1); - - if (ret >= 0) - return snd_pcm_frames_to_bytes(pcm[PLAY], ret); - else - return ret; -} - -static ssize_t alsap_read(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ -// struct ossp_dsp_rw_arg *arg = carg; - int ret, outsize; - - outsize = snd_pcm_bytes_to_frames(pcm[REC], *dout_szp); - - if (snd_pcm_state(pcm[REC]) == SND_PCM_STATE_SETUP) - snd_pcm_prepare(pcm[REC]); - - ret = snd_pcm_readi(pcm[REC], dout, outsize); - if (ret < 0) - ret = snd_pcm_recover(pcm[REC], ret, 1); - if (ret >= 0) - *dout_szp = ret = snd_pcm_frames_to_bytes(pcm[REC], ret); - else - *dout_szp = 0; - - return ret; -} - -static ssize_t alsap_poll(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - unsigned revents = 0; - - stream_notify |= *(int *)carg; - - if (pcm[PLAY]) - revents |= POLLOUT; - if (pcm[REC]) - revents |= POLLIN; - - *(unsigned *)rarg = revents; - return 0; -} - - -static ssize_t alsap_flush(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - flush_streams(opcode == OSSP_DSP_SYNC); - return 0; -} - -static ssize_t alsap_post(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - int ret; - - ret = trigger_streams(1, 1); - if (ret >= 0 && pcm[PLAY]) - ret = snd_pcm_start(pcm[PLAY]); - if (pcm[REC]) - ret = snd_pcm_start(pcm[REC]); - return ret; -} - -static ssize_t alsap_get_param(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, - int tfd) -{ - int v = 0; - - switch (opcode) { - case OSSP_DSP_GET_RATE: - return hw_format.rate; - - case OSSP_DSP_GET_CHANNELS: - return hw_format.channels; - - case OSSP_DSP_GET_FORMAT: { - v = fmt_alsa_to_oss(hw_format.format); - break; - } - - case OSSP_DSP_GET_BLKSIZE: { - snd_pcm_uframes_t psize; - snd_pcm_hw_params_get_period_size(hw_params, &psize, NULL); - v = psize; - break; - } - - case OSSP_DSP_GET_FORMATS: - v = AFMT_U8 | AFMT_A_LAW | AFMT_MU_LAW | AFMT_S16_LE | - AFMT_S16_BE | AFMT_FLOAT | AFMT_S32_LE | AFMT_S32_BE; - break; - - case OSSP_DSP_GET_TRIGGER: - if (!stream_corked[PLAY]) - v |= PCM_ENABLE_OUTPUT; - if (!stream_corked[REC]) - v |= PCM_ENABLE_INPUT; - break; - - default: - assert(0); - } - - *(int *)rarg = v; - - return 0; -} - -static ssize_t alsap_set_param(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, - int tfd) -{ - int v = *(int *)carg; - int ret = 0; - - /* kill the streams before changing parameters */ - kill_streams(); - - switch (opcode) { - case OSSP_DSP_SET_RATE: { - hw_format.rate = v; - break; - } - - case OSSP_DSP_SET_CHANNELS: { - hw_format.channels = v; - break; - } - - case OSSP_DSP_SET_FORMAT: { - snd_pcm_format_t format = fmt_oss_to_alsa(v); - hw_format.format = format; - break; - } - - case OSSP_DSP_SET_SUBDIVISION: - if (!v) - v = 1; -#if 0 - if (!v) { - v = user_subdivision ?: 1; - break; - } - user_frag_size = 0; - user_subdivision = v; - break; - - case OSSP_DSP_SET_FRAGMENT: - user_subdivision = 0; - user_frag_size = 1 << (v & 0xffff); - user_max_frags = (v >> 16) & 0xffff; - if (user_frag_size < 4) - user_frag_size = 4; - if (user_max_frags < 2) - user_max_frags = 2; -#else - case OSSP_DSP_SET_FRAGMENT: -#endif - break; - default: - assert(0); - } - - if (pcm[PLAY]) - ret = set_hw_params(pcm[PLAY]); - if (ret >= 0 && pcm[REC]) - ret = set_hw_params(pcm[REC]); - - if (rarg) - *(int *)rarg = v; - return 0; -} - -static ssize_t alsap_set_trigger(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, - int fd) -{ - int enable = *(int *)carg; - - stream_corked[PLAY] = !!(enable & PCM_ENABLE_OUTPUT); - stream_corked[REC] = !!(enable & PCM_ENABLE_INPUT); - - return trigger_streams(enable & PCM_ENABLE_OUTPUT, - enable & PCM_ENABLE_INPUT); -} - -static ssize_t alsap_get_space(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - int dir = (opcode == OSSP_DSP_GET_OSPACE) ? PLAY : REC; - int underrun = 0; - struct audio_buf_info info = { }; - unsigned long bufsize; - snd_pcm_uframes_t avail, fragsize; - snd_pcm_state_t state; - - if (!pcm[dir]) - return -EINVAL; - - state = snd_pcm_state(pcm[dir]); - if (state == SND_PCM_STATE_XRUN) { - snd_pcm_recover(pcm[dir], -EPIPE, 0); - underrun = 1; - } else if (state == SND_PCM_STATE_SUSPENDED) { - snd_pcm_recover(pcm[dir], -ESTRPIPE, 0); - underrun = 1; - } - - snd_pcm_hw_params_current(pcm[dir], hw_params); - snd_pcm_hw_params_get_period_size(hw_params, &fragsize, NULL); - snd_pcm_hw_params_get_buffer_size(hw_params, &bufsize); - info.fragsize = snd_pcm_frames_to_bytes(pcm[dir], fragsize); - info.fragstotal = bufsize / fragsize; - if (!underrun) { - avail = snd_pcm_avail_update(pcm[dir]); - info.fragments = avail / fragsize; - } else - info.fragments = info.fragstotal; - - info.bytes = info.fragsize * info.fragments; - - *(struct audio_buf_info *)rarg = info; - return 0; -} - -static ssize_t alsap_get_ptr(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - int dir = (opcode == OSSP_DSP_GET_OPTR) ? PLAY : REC; - struct count_info info = { }; - - if (!pcm[dir]) - return -EIO; - - snd_pcm_hw_params_current(pcm[dir], hw_params); - info.bytes = byte_counter[dir]; - snd_pcm_hw_params_get_periods(hw_params, (unsigned int *)&info.blocks, NULL); - info.ptr = mmap_pos[dir]; - - *(struct count_info *)rarg = info; - return 0; -} - -static ssize_t alsap_get_odelay(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, - int fd) -{ - snd_pcm_sframes_t delay; - - if (!pcm[PLAY]) - return -EIO; - - if (snd_pcm_delay(pcm[PLAY], &delay) < 0) - return -EIO; - - *(int *)rarg = snd_pcm_frames_to_bytes(pcm[PLAY], delay); - return 0; -} - -static ossp_action_fn_t action_fn_tbl[OSSP_NR_OPCODES] = { - [OSSP_MIXER] = alsap_mixer, - [OSSP_DSP_OPEN] = alsap_open, - [OSSP_DSP_READ] = alsap_read, - [OSSP_DSP_WRITE] = alsap_write, - [OSSP_DSP_POLL] = alsap_poll, -#if 0 - [OSSP_DSP_MMAP] = alsap_mmap, - [OSSP_DSP_MUNMAP] = alsap_munmap, -#endif - [OSSP_DSP_RESET] = alsap_flush, - [OSSP_DSP_SYNC] = alsap_flush, - [OSSP_DSP_POST] = alsap_post, - [OSSP_DSP_GET_RATE] = alsap_get_param, - [OSSP_DSP_GET_CHANNELS] = alsap_get_param, - [OSSP_DSP_GET_FORMAT] = alsap_get_param, - [OSSP_DSP_GET_BLKSIZE] = alsap_get_param, - [OSSP_DSP_GET_FORMATS] = alsap_get_param, - [OSSP_DSP_SET_RATE] = alsap_set_param, - [OSSP_DSP_SET_CHANNELS] = alsap_set_param, - [OSSP_DSP_SET_FORMAT] = alsap_set_param, - [OSSP_DSP_SET_SUBDIVISION] = alsap_set_param, - [OSSP_DSP_SET_FRAGMENT] = alsap_set_param, - [OSSP_DSP_GET_TRIGGER] = alsap_get_param, - [OSSP_DSP_SET_TRIGGER] = alsap_set_trigger, - [OSSP_DSP_GET_OSPACE] = alsap_get_space, - [OSSP_DSP_GET_ISPACE] = alsap_get_space, - [OSSP_DSP_GET_OPTR] = alsap_get_ptr, - [OSSP_DSP_GET_IPTR] = alsap_get_ptr, - [OSSP_DSP_GET_ODELAY] = alsap_get_odelay, -}; - -static int action_pre(void) -{ - return 0; -} - -static void action_post(void) -{ -} - -int main(int argc, char **argv) -{ - int rc; - - ossp_slave_init(argc, argv); - - page_size = sysconf(_SC_PAGE_SIZE); - - /* Okay, now we're open for business */ - rc = 0; - do { - rc = ossp_slave_process_command(ossp_cmd_fd, action_fn_tbl, - action_pre, action_post); - } while (rc > 0); - - return rc ? 1 : 0; -} diff --git a/ossp-padsp.c b/ossp-padsp.c deleted file mode 100644 index 1871f5b..0000000 --- a/ossp-padsp.c +++ /dev/null @@ -1,1524 +0,0 @@ -/* - * ossp-padsp - ossp DSP slave which forwards to pulseaduio - * - * Copyright (C) 2008-2010 SUSE Linux Products GmbH - * Copyright (C) 2008-2010 Tejun Heo - * - * This file is released under the GPLv2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "ossp-slave.h" - -enum { - AFMT_FLOAT = 0x00004000, - AFMT_S32_LE = 0x00001000, - AFMT_S32_BE = 0x00002000, -}; - -/* everything is in millisecs */ -struct stream_params { - size_t min_process; - size_t min_latency; - size_t dfl_process; - size_t dfl_latency; - size_t mmap_process; - size_t mmap_latency; - size_t mmap_lead; - size_t mmap_staging; -}; - -/* TODO: make this configurable */ -static struct stream_params stream_params[] = { - [ PLAY ] = { .min_process = 25, .min_latency = 100, - .dfl_process = 50, .dfl_latency = 200, - .mmap_process = 25, .mmap_latency = 50, - .mmap_lead = 25, .mmap_staging = 100 }, - [ REC ] = { .min_process = 25, .min_latency = 200, - .dfl_process = 50, .dfl_latency = 400, - .mmap_process = 25, .mmap_latency = 50, - .mmap_lead = 25, .mmap_staging = 1000 }, -}; - -static size_t page_size; -static pa_context *context; -static pa_threaded_mainloop *mainloop; -static pa_mainloop_api *mainloop_api; -static char stream_name[128]; -static int stream_enabled[2]; -static int stream_corked[2]; -static int stream_waiting; -static int stream_notify; -static pa_channel_map channel_map_stor; -static pa_channel_map *channel_map; -static pa_stream *stream[2]; -static pa_usec_t stream_ptr_timestamp[2]; -static struct ring_buf rec_buf; -static int stored_oss_vol[2][2] = { { -1, -1 }, { -1, -1 } }; -static int fail_code; - -static pa_sample_spec sample_spec = { - .format = PA_SAMPLE_U8, - .rate = 8000, - .channels = 1, -}; -static size_t sample_bps = 8000; -static size_t frame_size = 1; - -/* user visible stream parameters */ -static size_t user_frag_size; -static size_t user_subdivision; /* alternative way to determine frag_size */ -static size_t user_max_frags; /* maximum number of fragments */ -static size_t user_max_length; - -/* actual stream parameters */ -static size_t frag_size; -static size_t target_length; -static size_t max_length; -static size_t prebuf_size; - -/* mmap stuff */ -static size_t mmap_raw_size, mmap_size; -static void *mmap_map[2]; -static uint64_t mmap_idx[2]; /* mmap pointer */ -static uint64_t mmap_last_idx[2]; /* last idx for get_ptr */ -static struct ring_buf mmap_stg[2]; /* staging ring buffer */ -static size_t mmap_lead[2]; /* lead bytes */ -static int mmap_sync[2]; /* sync with backend stream */ - -static const char *dir_str[] = { - [PLAY] = "PLAY", - [REC] = "REC", -}; - -static void stream_rw_callback(pa_stream *s, size_t length, void *userdata); - -#define __pa_err pa_strerror(pa_context_errno(context)) -#define dbg1_pa(fmt, args...) dbg1(fmt" (%s)" , ##args, __pa_err) -#define dbg0_pa(fmt, args...) dbg0(fmt" (%s)" , ##args, __pa_err) -#define info_pa(fmt, args...) info(fmt" (%s)" , ##args, __pa_err) -#define warn_pa(fmt, args...) warn(fmt" (%s)" , ##args, __pa_err) -#define err_pa(fmt, args...) err(fmt" (%s)" , ##args, __pa_err) - -#define round_down(v, t) ((v) / (t) * (t)) -#define round_up(v, t) (((v) + (t) - 1) / (t) * (t)) -#define is_power2(v) !((v) & ((v) - 1)) - -static int do_mixer(int dir, int *vol); - -static int padsp_done(void) -{ - fail_code = -EIO; - mainloop_api->quit(mainloop_api, 1); - return fail_code; -} - -static int fmt_oss_to_pa(int fmt) -{ - switch (fmt) { - case AFMT_U8: return PA_SAMPLE_U8; - case AFMT_A_LAW: return PA_SAMPLE_ALAW; - case AFMT_MU_LAW: return PA_SAMPLE_ULAW; - case AFMT_S16_LE: return PA_SAMPLE_S16LE; - case AFMT_S16_BE: return PA_SAMPLE_S16BE; - case AFMT_FLOAT: return PA_SAMPLE_FLOAT32NE; - case AFMT_S32_LE: return PA_SAMPLE_S32LE; - case AFMT_S32_BE: return PA_SAMPLE_S32BE; - default: return PA_SAMPLE_U8; - } -} - -static int fmt_pa_to_oss(int fmt) -{ - switch (fmt) { - case PA_SAMPLE_U8: return AFMT_U8; - case PA_SAMPLE_ALAW: return AFMT_A_LAW; - case PA_SAMPLE_ULAW: return AFMT_MU_LAW; - case PA_SAMPLE_S16LE: return AFMT_S16_LE; - case PA_SAMPLE_S16BE: return AFMT_S16_BE; - case PA_SAMPLE_FLOAT32NE: return AFMT_FLOAT; - case PA_SAMPLE_S32LE: return AFMT_S32_LE; - case PA_SAMPLE_S32BE: return AFMT_S32_BE; - default: return AFMT_U8; - } -} - -#define EXEC_OP(op, args...) do { \ - pa_operation *_o; \ - _o = op(args); \ - if (_o) { \ - while (pa_operation_get_state(_o) != PA_OPERATION_DONE) \ - pa_threaded_mainloop_wait(mainloop); \ - pa_operation_unref(_o); \ - } } while (0) - -static void context_op_callback(pa_context *s, int success, void *userdata) -{ - *(int *)userdata = success; - pa_threaded_mainloop_signal(mainloop, 0); -} - -static void stream_op_callback(pa_stream *s, int success, void *userdata) -{ - *(int *)userdata = success; - pa_threaded_mainloop_signal(mainloop, 0); -} - -#define EXEC_CONTEXT_OP(op, args...) ({ \ - int _success; \ - EXEC_OP(op , ##args, context_op_callback, &_success); \ - if (!_success) \ - warn_pa("%s() failed", #op); \ - _success ? 0 : -EIO; }) - -#define EXEC_STREAM_OP(op, args...) ({ \ - int _success; \ - EXEC_OP(op , ##args, stream_op_callback, &_success); \ - if (!_success) \ - warn_pa("%s() failed", #op); \ - _success ? 0 : -EIO; }) - -static int mmapped(void) -{ - return mmap_map[PLAY] || mmap_map[REC]; -} - -static uint64_t get_mmap_idx(int dir) -{ - uint64_t idx; - pa_usec_t time; - - if (!stream[dir]) - return mmap_idx[dir]; - - if (pa_stream_get_time(stream[dir], &time) < 0) { - dbg1_pa("pa_stream_get_time() failed"); - return mmap_idx[dir]; - } - - /* calculate the current index from time elapsed */ - idx = ((uint64_t)time * sample_bps / 1000000); - /* round down to the nearest frame boundary */ - idx = idx / frame_size * frame_size; - - return idx; -} - -static void flush_streams(int drain) -{ - int i; - - if (!(stream[PLAY] || stream[REC])) - return; - - dbg0("FLUSH drain=%d", drain); - - /* mmapped streams run forever, can't drain */ - if (drain && !mmapped() && stream[PLAY]) - EXEC_STREAM_OP(pa_stream_drain, stream[PLAY]); - - for (i = 0; i < 2; i++) - if (stream[i]) - EXEC_STREAM_OP(pa_stream_flush, stream[i]); - - ring_consume(&rec_buf, ring_bytes(&rec_buf)); -} - -static void kill_streams(void) -{ - int dir; - - if (!(stream[PLAY] || stream[REC])) - return; - - flush_streams(1); - - dbg0("KILL"); - - for (dir = 0; dir < 2; dir++) { - if (!stream[dir]) - continue; - pa_stream_disconnect(stream[dir]); - pa_stream_unref(stream[dir]); - stream[dir] = NULL; - stream_ptr_timestamp[dir] = 0; - - ring_consume(&mmap_stg[dir], ring_bytes(&mmap_stg[dir])); - ring_resize(&mmap_stg[dir], 0); - } -} - -static int trigger_streams(int play, int rec) -{ - int ret = 0, dir, rc; - - if (play >= 0) - stream_corked[PLAY] = !play; - if (rec >= 0) - stream_corked[REC] = !rec; - - for (dir = 0; dir < 2; dir++) { - if (!stream[dir]) - continue; - - rc = EXEC_STREAM_OP(pa_stream_cork, stream[dir], - stream_corked[dir]); - if (!rc && dir == PLAY && !mmap_map[dir] && !stream_corked[dir]) - rc = EXEC_STREAM_OP(pa_stream_trigger, stream[dir]); - if (!ret) - ret = rc; - } - - return ret; -} - -static void stream_state_callback(pa_stream *s, void *userdata) -{ - pa_threaded_mainloop_signal(mainloop, 0); -} - -static void stream_underflow_callback(pa_stream *s, void *userdata) -{ - int dir = (s == stream[PLAY]) ? PLAY : REC; - - dbg0("%s stream underrun", dir_str[dir]); -} - -static void stream_overflow_callback(pa_stream *s, void *userdata) -{ - int dir = (s == stream[PLAY]) ? PLAY : REC; - - dbg0("%s stream overrun", dir_str[dir]); -} - -static size_t duration_to_bytes(size_t dur) -{ - return round_up(dur * sample_bps / 1000, frame_size); -} - -static int prepare_streams(void) -{ - const struct stream_params *sp; - size_t min_frag_size, min_target_length, tmp; - int dir, rc; - - /* nothing to do? */ - if ((!stream_enabled[PLAY] || stream[PLAY]) && - (!stream_enabled[REC] || stream[REC])) - return 0; - - /* determine sample parameters */ - sample_bps = pa_bytes_per_second(&sample_spec); - frame_size = pa_frame_size(&sample_spec); - - sp = &stream_params[PLAY]; - if (stream_enabled[REC]) - sp = &stream_params[REC]; - - min_frag_size = duration_to_bytes(sp->min_process); - min_target_length = duration_to_bytes(sp->min_latency); - - /* determine frag_size */ - if (user_frag_size % frame_size) { - warn("requested frag_size (%zu) isn't multiple of frame (%zu)", - user_frag_size, frame_size); - user_frag_size = round_up(user_frag_size, frame_size); - } - - if (user_subdivision) - user_frag_size = round_up(sample_bps / user_subdivision, - frame_size); - - if (user_frag_size) { - frag_size = user_frag_size; - if (frag_size < min_frag_size) { - dbg0("requested frag_size (%zu) is smaller than " - "minimum (%zu)", frag_size, min_frag_size); - frag_size = min_frag_size; - } - } else { - tmp = round_up(sp->dfl_process * sample_bps / 1000, frame_size); - frag_size = tmp; - /* if frame_size is power of two, make frag_size so too */ - if (is_power2(frame_size)) { - frag_size = frame_size; - while (frag_size < tmp) - frag_size <<= 1; - } - user_frag_size = frag_size; - } - - /* determine target and max length */ - if (user_max_frags) { - target_length = user_max_frags * user_frag_size; - if (target_length < min_target_length) { - dbg0("requested target_length (%zu) is smaller than " - "minimum (%zu)", target_length, min_target_length); - target_length = min_target_length; - } - } else { - tmp = round_up(sp->dfl_latency * sample_bps / 1000, frag_size); - target_length = tmp; - /* if frag_size is power of two, make target_length so - * too and align it to page_size. - */ - if (is_power2(frag_size)) { - target_length = frag_size; - while (target_length < max(tmp, page_size)) - target_length <<= 1; - } - user_max_frags = target_length / frag_size; - } - - user_max_length = user_frag_size * user_max_frags; - max_length = target_length + 2 * frag_size; - - /* If mmapped, create backend stream with fixed parameters to - * create illusion of hardware buffer with acceptable latency. - */ - if (mmapped()) { - /* set parameters for backend streams */ - frag_size = duration_to_bytes(sp->mmap_process); - target_length = duration_to_bytes(sp->mmap_latency); - max_length = target_length + frag_size; - prebuf_size = 0; - - mmap_size = round_down(mmap_raw_size, frame_size); - if (mmap_size != mmap_raw_size) - warn("mmap_raw_size (%zu) unaligned to frame_size " - "(%zu), mmap_size adjusted to %zu", - mmap_raw_size, frame_size, mmap_size); - } else { - prebuf_size = min(user_frag_size * 2, user_max_length / 2); - prebuf_size = round_down(prebuf_size, frame_size); - } - - for (dir = 0; dir < 2; dir++) { - pa_buffer_attr new_ba = { }; - char buf[128]; - pa_stream *s; - pa_stream_flags_t flags; - int vol[2]; - size_t size; - - if (!stream_enabled[dir] || stream[dir]) - continue; - - dbg0("CREATE %s %s fsz=%zu:%zu", dir_str[dir], - pa_sample_spec_snprint(buf, sizeof(buf), &sample_spec), - frag_size, frag_size * 1000 / sample_bps); - dbg0(" tlen=%zu:%zu max=%zu:%zu pre=%zu:%zu", - target_length, target_length * 1000 / sample_bps, - max_length, max_length * 1000 / sample_bps, - prebuf_size, prebuf_size * 1000 / sample_bps); - dbg0(" u_sd=%zu u_fsz=%zu:%zu u_maxf=%zu", - user_subdivision, user_frag_size, - user_frag_size * 1000 / sample_bps, user_max_frags); - - channel_map = pa_channel_map_init_auto(&channel_map_stor, - sample_spec.channels, - PA_CHANNEL_MAP_OSS); - - s = pa_stream_new(context, stream_name, &sample_spec, - channel_map); - if (!s) { - err_pa("can't create streams"); - goto fail; - } - stream[dir] = s; - - pa_stream_set_state_callback(s, stream_state_callback, NULL); - if (dir == PLAY) { - pa_stream_set_write_callback(s, - stream_rw_callback, NULL); - pa_stream_set_underflow_callback(s, - stream_underflow_callback, NULL); - } else { - pa_stream_set_read_callback(s, - stream_rw_callback, NULL); - pa_stream_set_overflow_callback(s, - stream_overflow_callback, NULL); - } - - flags = PA_STREAM_AUTO_TIMING_UPDATE | - PA_STREAM_INTERPOLATE_TIMING; - if (stream_corked[dir]) - flags |= PA_STREAM_START_CORKED; - - new_ba.maxlength = max_length; - new_ba.tlength = target_length; - new_ba.prebuf = prebuf_size; - new_ba.minreq = frag_size; - new_ba.fragsize = frag_size; - - if (dir == PLAY) { - if (pa_stream_connect_playback(s, NULL, &new_ba, flags, - NULL, NULL)) { - err_pa("failed to connect playback stream"); - goto fail; - } - } else { - if (pa_stream_connect_record(s, NULL, &new_ba, flags)) { - err_pa("failed to connect record stream"); - goto fail; - } - } - - while (pa_stream_get_state(s) == PA_STREAM_CREATING) - pa_threaded_mainloop_wait(mainloop); - if (pa_stream_get_state(s) != PA_STREAM_READY) { - err_pa("failed to connect stream (state=%d)", - pa_stream_get_state(s)); - goto fail; - } - - /* apply stored OSS volume */ - memcpy(vol, stored_oss_vol[dir], sizeof(vol)); - if (do_mixer(dir, vol)) - warn_pa("initial volume control failed"); - - /* stream is ready setup mmap stuff */ - if (!mmap_map[dir]) - continue; - - /* prep mmap staging buffer */ - size = round_up(sp->mmap_staging * sample_bps / 1000, - frag_size); - rc = ring_resize(&mmap_stg[dir], size); - if (rc) - return rc; - - mmap_idx[dir] = mmap_last_idx[dir] = get_mmap_idx(dir); - mmap_lead[dir] = round_up(sp->mmap_lead * sample_bps / 1000, - frame_size); - mmap_sync[dir] = 1; - - /* apply the current trigger settings */ - trigger_streams(-1, -1); - } - - return 0; - fail: - return padsp_done(); -} - -struct volume_ret { - int success; - pa_cvolume *cv; -}; - -static void play_volume_callback(pa_context *c, const pa_sink_input_info *i, - int eol, void *userdata) -{ - struct volume_ret *vr = userdata; - - if (i) { - *vr->cv = i->volume; - vr->success = 1; - } - pa_threaded_mainloop_signal(mainloop, 0); -} - -static void rec_volume_callback(pa_context *c, const pa_source_info *i, - int eol, void *userdata) -{ - struct volume_ret *vr = userdata; - - if (i) { - *vr->cv = i->volume; - vr->success = 1; - } - pa_threaded_mainloop_signal(mainloop, 0); -} - -static int get_volume(int dir, pa_cvolume *cv) -{ - struct volume_ret vr = { .cv = cv }; - uint32_t idx; - - if (dir == PLAY) { - idx = pa_stream_get_index(stream[PLAY]); - EXEC_OP(pa_context_get_sink_input_info, - context, idx, play_volume_callback, &vr); - } else { - idx = pa_stream_get_device_index(stream[REC]); - EXEC_OP(pa_context_get_source_info_by_index, - context, idx, rec_volume_callback, &vr); - } - if (!vr.success) { - warn_pa("failed to get %s volume", dir_str[dir]); - return -EIO; - } - return 0; -} - -static int set_volume(int dir, pa_cvolume *cv) -{ - uint32_t idx; - int rc; - - if (dir == PLAY) { - idx = pa_stream_get_index(stream[PLAY]); - rc = EXEC_CONTEXT_OP(pa_context_set_sink_input_volume, - context, idx, cv); - } else { - idx = pa_stream_get_device_index(stream[REC]); - rc = EXEC_CONTEXT_OP(pa_context_set_source_volume_by_index, - context, idx, cv); - } - return rc; -} - -static int chan_left_right(int ch) -{ - if (!channel_map || channel_map->channels <= ch) { - switch (ch) { - case 0: - return LEFT; - case 1: - return RIGHT; - default: - return -1; - } - } - - switch (channel_map->map[ch]) { - /*case PA_CHANNEL_POSITION_LEFT:*/ /* same as FRONT_LEFT */ - case PA_CHANNEL_POSITION_FRONT_LEFT: - case PA_CHANNEL_POSITION_REAR_LEFT: - case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: - case PA_CHANNEL_POSITION_SIDE_LEFT: - case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: - case PA_CHANNEL_POSITION_TOP_REAR_LEFT: - return LEFT; - /*case PA_CHANNEL_POSITION_RIGHT:*/ /* same as FRONT_RIGHT */ - case PA_CHANNEL_POSITION_FRONT_RIGHT: - case PA_CHANNEL_POSITION_REAR_RIGHT: - case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: - case PA_CHANNEL_POSITION_SIDE_RIGHT: - case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: - case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: - return RIGHT; - default: - return -1; - } -} - -static int do_mixer(int dir, int *vol) -{ - pa_cvolume cv; - unsigned lv, rv; - int i, rc; - - if (vol[0] >= 0) { - int avg; - - stored_oss_vol[dir][LEFT] = vol[LEFT]; - stored_oss_vol[dir][RIGHT] = vol[RIGHT]; - vol[LEFT] = vol[LEFT] * PA_VOLUME_NORM / 100; - vol[RIGHT] = vol[RIGHT] * PA_VOLUME_NORM / 100; - avg = (vol[LEFT] + vol[RIGHT]) / 2; - - pa_cvolume_mute(&cv, sample_spec.channels); - - for (i = 0; i < cv.channels; i++) - switch (chan_left_right(i)) { - case LEFT: cv.values[i] = vol[LEFT]; break; - case RIGHT: cv.values[i] = vol[RIGHT]; break; - default: cv.values[i] = avg; break; - } - - rc = set_volume(dir, &cv); - if (rc) - return rc; - } - - rc = get_volume(dir, &cv); - if (rc) - return rc; - - if (cv.channels == 1) - lv = rv = pa_cvolume_avg(&cv); - else { - unsigned lcnt = 0, rcnt = 0; - - for (i = 0, lv = 0, rv = 0; i < cv.channels; i++) - switch (chan_left_right(i)) { - case LEFT: lv += cv.values[i]; lcnt++; break; - case RIGHT: rv += cv.values[i]; rcnt++; break; - } - - if (lcnt) - lv /= lcnt; - if (rcnt) - rv /= rcnt; - } - - vol[LEFT] = lv * 100 / PA_VOLUME_NORM; - vol[RIGHT] = rv * 100 / PA_VOLUME_NORM; - - return 0; -} - -static ssize_t padsp_mixer(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - struct ossp_mixer_arg *arg = carg; - int i, rc[2] = { }; - - if (prepare_streams()) - return -EIO; - - for (i = 0; i < 2; i++) - if (stream[i]) - rc[i] = do_mixer(i, arg->vol[i]); - else - memset(arg->vol[i], -1, sizeof(arg->vol[i])); - - *(struct ossp_mixer_arg *)rarg = *arg; - return rc[0] ?: rc[1]; -} - -static void context_state_callback(pa_context *cxt, void *userdata) -{ - pa_threaded_mainloop_signal(mainloop, 0); -} - -static void context_subscribe_callback(pa_context *context, - pa_subscription_event_type_t type, - uint32_t idx, void *userdata) -{ - struct ossp_notify event = { .magic = OSSP_NOTIFY_MAGIC, - .opcode = OSSP_NOTIFY_VOLCHG }; - ssize_t ret; - - if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != - PA_SUBSCRIPTION_EVENT_CHANGE) - return; - - ret = write(ossp_notify_fd, &event, sizeof(event)); - if (ret != sizeof(event) && errno != EPIPE) - warn_e(-errno, "write to notify_fd failed"); -} - -static ssize_t padsp_open(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - struct ossp_dsp_open_arg *arg = carg; - char host_name[128] = "(unknown)", opener[128] = "(unknown)"; - int state; - - switch (arg->flags & O_ACCMODE) { - case O_WRONLY: - stream_enabled[PLAY] = 1; - break; - case O_RDONLY: - stream_enabled[REC] = 1; - break; - case O_RDWR: - stream_enabled[PLAY] = 1; - stream_enabled[REC] = 1; - break; - default: - assert(0); - } - - /* determine stream name */ - gethostname(host_name, sizeof(host_name) - 1); - snprintf(stream_name, sizeof(stream_name), "OSS Proxy %s/%s:%ld", - host_name, ossp_user_name, (long)arg->opener_pid); - - /* create and connect PA context */ - get_proc_self_info(arg->opener_pid, NULL, opener, sizeof(opener)); - context = pa_context_new(mainloop_api, opener); - if (!context) { - err("pa_context_new() failed"); - return -EIO; - } - - pa_context_set_state_callback(context, context_state_callback, NULL); - pa_context_set_subscribe_callback(context, context_subscribe_callback, - NULL); - - pa_context_connect(context, NULL, 0, NULL); - while (1) { - state = pa_context_get_state(context); - if (state != PA_CONTEXT_CONNECTING && - state != PA_CONTEXT_AUTHORIZING && - state != PA_CONTEXT_SETTING_NAME) - break; - - pa_threaded_mainloop_wait(mainloop); - } - - if (EXEC_CONTEXT_OP(pa_context_subscribe, context, - PA_SUBSCRIPTION_MASK_SINK_INPUT | - PA_SUBSCRIPTION_MASK_SOURCE)) - warn_pa("failed to subscribe to context events"); - - if (state != PA_CONTEXT_READY) { - err_pa("failed to connect context, state=%d", state); - return -EIO; - } - - return 0; -} - -static void mmap_fill_pstg(void) -{ - struct ring_buf *stg = &mmap_stg[PLAY]; - struct ring_buf mmap; - uint64_t new_idx = get_mmap_idx(PLAY); - size_t bytes, space, size; - void *data; - - if (new_idx <= mmap_idx[PLAY]) - return; - - bytes = new_idx - mmap_idx[PLAY]; - space = ring_space(stg); - - if (bytes > mmap_size) { - dbg0("mmap playback transfer chunk bigger than " - "mmap size (bytes=%zu mmap_size=%zu)", bytes, mmap_size); - mmap_sync[PLAY] = 1; - bytes = mmap_size; - } - - if (bytes > space) { - dbg0("mmap playback staging buffer overflow " - "(bytes=%zu space=%zu)", bytes, space); - mmap_sync[PLAY] = 1; - bytes = space; - } - - ring_manual_init(&mmap, mmap_map[PLAY], mmap_size, - new_idx % mmap_size, bytes); - - while ((data = ring_data(&mmap, &size))) { - ring_fill(stg, data, size); - ring_consume(&mmap, size); - } - - mmap_idx[PLAY] = new_idx; -} - -static void mmap_consume_rstg(void) -{ - struct ring_buf *stg = &mmap_stg[REC]; - struct ring_buf mmap; - uint64_t new_idx = get_mmap_idx(REC); - uint64_t fill_idx = mmap_idx[REC]; - size_t bytes, space; - - if (new_idx <= mmap_idx[REC]) - return; - - space = new_idx - mmap_idx[REC]; /* mmapped space to fill in */ - bytes = ring_bytes(stg); /* recorded bytes in staging */ - - if (space > bytes) { - if (!mmap_sync[REC]) - dbg0("mmap recording staging buffer underflow " - "(space=%zu bytes=%zu)", space, bytes); - mmap_sync[REC] = 1; - } - - if (space > mmap_size) { - if (!mmap_sync[REC]) - dbg0("mmap recording transfer chunk bigger than " - "mmap size (space=%zu mmap_size=%zu)", - bytes, mmap_size); - mmap_sync[REC] = 1; - space = mmap_size; - } - - /* If resync is requested, leave lead bytes in the staging - * buffer and copy everything else such that data is filled - * upto the new_idx. If there are more bytes in staging than - * available space, those will be dropped. - */ - if (mmap_sync[REC]) { - ssize_t avail = bytes - mmap_lead[REC]; - - /* make sure we always have lead bytes in staging */ - if (avail < 0) - goto skip; - - if (avail > space) { - dbg0("dropping %zu bytes from record staging buffer", - avail - space); - ring_consume(&mmap_stg[REC], avail - space); - avail = space; - } else { - dbg0("skippping %zu bytes in record mmap map", - space - avail); - space = avail; - } - - assert(new_idx >= avail); - fill_idx = new_idx - avail; - mmap_sync[REC] = 0; - } - - ring_manual_init(&mmap, mmap_map[REC], mmap_size, - fill_idx % mmap_size, 0); - - while (space) { - void *data; - size_t size, todo; - - data = ring_data(stg, &size); - assert(data); - - todo = min(size, space); - ring_fill(&mmap, data, todo); - - ring_consume(stg, todo); - space -= todo; - } - - skip: - mmap_idx[REC] = new_idx; -} - -static void do_mmap_write(size_t space) -{ - struct ring_buf *stg = &mmap_stg[PLAY]; - size_t todo; - void *data; - - space = round_down(space, frame_size); - mmap_fill_pstg(); - - while (space && (data = ring_data(stg, &todo))) { - pa_seek_mode_t mode = PA_SEEK_RELATIVE_END; - int64_t offset = 0; - - todo = min(todo, space); - - if (mmap_sync[PLAY]) { - mode = PA_SEEK_RELATIVE_ON_READ; - offset = (int64_t)mmap_lead[PLAY] - ring_bytes(stg); - dbg0("mmap resync, offset=%ld", (long)offset); - } - - if (pa_stream_write(stream[PLAY], data, todo, NULL, - offset, mode) < 0) { - err_pa("pa_stream_write() failed"); - padsp_done(); - return; - } - - mmap_sync[PLAY] = 0; - ring_consume(stg, todo); - space -= todo; - } -} - -static void do_mmap_read(size_t bytes) -{ - struct ring_buf *stg = &mmap_stg[REC]; - - bytes = round_down(bytes, frame_size); - mmap_consume_rstg(); - - while (bytes) { - const void *peek_data; - size_t size; - - if (pa_stream_peek(stream[REC], &peek_data, &size)) { - err_pa("pa_stream_peek() failed"); - padsp_done(); - return; - } - - if (!peek_data) - break; - - if (size <= ring_space(stg)) - ring_fill(stg, peek_data, size); - else { - if (!mmap_sync[REC]) - dbg0("recording staging buffer overflow, " - "requesting resync"); - mmap_sync[REC] = 1; - } - - pa_stream_drop(stream[REC]); - bytes -= size; - } -} - -static void stream_rw_callback(pa_stream *s, size_t length, void *userdata) -{ - int dir; - size_t size; - - if (s == stream[PLAY]) { - dir = PLAY; - size = pa_stream_writable_size(s); - if (mmap_map[PLAY]) - do_mmap_write(size); - } else if (s == stream[REC]) { - dir = REC; - size = pa_stream_readable_size(s); - if (mmap_map[REC]) - do_mmap_read(size); - } else { - dbg0("stream_rw_callback(): unknown stream %p PLAY/REC=%p/%p\n", - s, stream[PLAY], stream[REC]); - return; - } - - if (size < user_frag_size) - return; - if (stream_waiting) - pa_threaded_mainloop_signal(mainloop, 0); - if (stream_notify) { - struct ossp_notify event = { .magic = OSSP_NOTIFY_MAGIC, - .opcode = OSSP_NOTIFY_POLL }; - ssize_t ret; - - ret = write(ossp_notify_fd, &event, sizeof(event)); - if (ret != sizeof(event)) { - if (errno != EPIPE) - err_e(-errno, "write to notify_fd failed"); - - /* This function is run from PA mainloop and - * thus the following padsp_done() won't be - * noticed before the mainthread tries to run - * the next command. Well, that's good enough. - */ - padsp_done(); - } - stream_notify = 0; - } -} - -static ssize_t padsp_write(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - struct ossp_dsp_rw_arg *arg = carg; - size_t size; - - if (prepare_streams() || !stream[PLAY]) - return -EIO; - - stream_waiting++; - while (1) { - size = pa_stream_writable_size(stream[PLAY]); - if (arg->nonblock || size >= user_frag_size) - break; - pa_threaded_mainloop_wait(mainloop); - } - stream_waiting--; - - size = round_down(size, user_frag_size); - if (!size) - return -EAGAIN; - - size = min(size, din_sz); - - if (pa_stream_write(stream[PLAY], din, size, NULL, - 0, PA_SEEK_RELATIVE) < 0) { - err_pa("pa_stream_write() failed"); - return padsp_done(); - } - - return size; -} - -static ssize_t padsp_read(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - struct ossp_dsp_rw_arg *arg = carg; - size_t size; - void *data; - - if (prepare_streams() || !stream[REC]) - return -EIO; - again: - if (!arg->nonblock) { - stream_waiting++; - while (1) { - size = pa_stream_readable_size(stream[REC]); - if (size + ring_bytes(&rec_buf) >= user_frag_size) - break; - pa_threaded_mainloop_wait(mainloop); - } - stream_waiting--; - } - - while (ring_bytes(&rec_buf) < max(user_frag_size, *dout_szp)) { - const void *peek_data; - - if (pa_stream_peek(stream[REC], &peek_data, &size) < 0) { - err_pa("pa_stream_peek() failed"); - return padsp_done(); - } - - if (!peek_data) - break; - - if (ring_space(&rec_buf) < size) { - size_t bufsz; - - bufsz = ring_size(&rec_buf); - bufsz = max(2 * bufsz, bufsz + 2 * size); - - if (ring_resize(&rec_buf, bufsz)) { - err("failed to allocate recording buffer"); - return padsp_done(); - } - } - - ring_fill(&rec_buf, peek_data, size); - pa_stream_drop(stream[REC]); - } - - size = round_down(ring_bytes(&rec_buf), user_frag_size); - if (!size) { - if (arg->nonblock) - return -EAGAIN; - else - goto again; - } - - *dout_szp = size = min(size, *dout_szp); - - while (size) { - size_t cnt; - - data = ring_data(&rec_buf, &cnt); - assert(data); - - cnt = min(size, cnt); - memcpy(dout, data, cnt); - ring_consume(&rec_buf, cnt); - dout += cnt; - size -= cnt; - } - - return *dout_szp; -} - -static ssize_t padsp_poll(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - unsigned revents = 0; - - if (prepare_streams() < 0) - return -EIO; - - stream_notify |= *(int *)carg; - - if (stream[PLAY] && - pa_stream_writable_size(stream[PLAY]) >= user_frag_size) - revents |= POLLOUT; - if (stream[REC] && - pa_stream_readable_size(stream[REC]) >= user_frag_size) - revents |= POLLIN; - - *(unsigned *)rarg = revents; - return 0; -} - -static ssize_t padsp_mmap(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - struct ossp_dsp_mmap_arg *arg = carg; - int dir = arg->dir; - - assert(!mmap_map[dir]); - - kill_streams(); - - /* arg->size is rounded up to the nearest page boundary. - * There is no way to tell what the actual requested value is - * but assume that it was the reported buffer space if it - * falls into the same page aligned range. - */ - mmap_raw_size = arg->size; - if (user_max_length && user_max_length < mmap_raw_size && - round_up(mmap_raw_size, page_size) == - round_up(user_max_length, page_size)) { - info("MMAP adjusting raw_size %zu -> %zu", - mmap_raw_size, user_max_length); - mmap_raw_size = user_max_length; - } - - dbg0("MMAP server-addr=%p sz=%zu", ossp_mmap_addr[dir], mmap_raw_size); - - mmap_map[dir] = ossp_mmap_addr[dir]; - - /* if mmapped, only mmapped streams are enabled */ - stream_enabled[PLAY] = !!mmap_map[PLAY]; - stream_enabled[REC] = !!mmap_map[REC]; - - return 0; -} - -static ssize_t padsp_munmap(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - int dir = *(int *)carg; - - assert(mmap_map[dir]); - kill_streams(); - mmap_map[dir] = NULL; - return 0; -} - -static ssize_t padsp_flush(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - flush_streams(opcode == OSSP_DSP_SYNC); - return 0; -} - -static ssize_t padsp_post(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - return trigger_streams(1, -1); -} - -static ssize_t padsp_get_param(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, - int tfd) -{ - int v = 0; - - switch (opcode) { - case OSSP_DSP_GET_RATE: - v = sample_spec.rate; - break; - - case OSSP_DSP_GET_CHANNELS: - v = sample_spec.channels; - break; - - case OSSP_DSP_GET_FORMAT: - v = fmt_pa_to_oss(sample_spec.format); - break; - - case OSSP_DSP_GET_BLKSIZE: - if (prepare_streams() < 0) - return -EIO; - v = user_frag_size; - break; - - case OSSP_DSP_GET_FORMATS: - v = AFMT_U8 | AFMT_A_LAW | AFMT_MU_LAW | AFMT_S16_LE | - AFMT_S16_BE | AFMT_FLOAT | AFMT_S32_LE | AFMT_S32_BE; - break; - - case OSSP_DSP_GET_TRIGGER: - if (!stream_corked[PLAY]) - v |= PCM_ENABLE_OUTPUT; - if (!stream_corked[REC]) - v |= PCM_ENABLE_INPUT; - break; - - default: - assert(0); - } - - *(int *)rarg = v; - - return 0; -} - -static ssize_t padsp_set_param(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, - int tfd) -{ - pa_sample_spec new_spec = sample_spec; - int v = *(int *)carg; - - /* kill the streams before changing parameters */ - kill_streams(); - - switch (opcode) { - case OSSP_DSP_SET_RATE: - new_spec.rate = v; - if (pa_sample_spec_valid(&new_spec)) - sample_spec = new_spec; - v = sample_spec.rate; - break; - - case OSSP_DSP_SET_CHANNELS: - new_spec.channels = v; - if (pa_sample_spec_valid(&new_spec)) - sample_spec = new_spec; - v = sample_spec.channels; - break; - - case OSSP_DSP_SET_FORMAT: - new_spec.format = fmt_oss_to_pa(v); - if (pa_sample_spec_valid(&new_spec)) - sample_spec = new_spec; - v = fmt_pa_to_oss(sample_spec.format); - break; - - case OSSP_DSP_SET_SUBDIVISION: - if (!v) { - v = user_subdivision ?: 1; - break; - } - user_frag_size= 0; - user_subdivision = v; - break; - - case OSSP_DSP_SET_FRAGMENT: - user_subdivision = 0; - user_frag_size = 1 << (v & 0xffff); - user_max_frags = (v >> 16) & 0xffff; - if (user_frag_size < 4) - user_frag_size = 4; - if (user_max_frags < 2) - user_max_frags = 2; - break; - default: - assert(0); - } - - if (rarg) - *(int *)rarg = v; - return 0; -} - -static ssize_t padsp_set_trigger(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, - int fd) -{ - int enable = *(int *)carg; - - return trigger_streams(enable & PCM_ENABLE_OUTPUT, - enable & PCM_ENABLE_INPUT); -} - -static ssize_t padsp_get_space(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - int dir = (opcode == OSSP_DSP_GET_OSPACE) ? PLAY : REC; - struct audio_buf_info info = { }; - int rc; - - rc = prepare_streams(); - if (rc) - return -EIO; - - if (mmapped()) { - info.fragments = mmap_raw_size / user_frag_size; - info.fragstotal = info.fragments; - info.fragsize = user_frag_size; - info.bytes = mmap_raw_size; - } else { - size_t space; - - if (dir == PLAY) - space = pa_stream_writable_size(stream[PLAY]); - else - space = pa_stream_readable_size(stream[REC]); - - space = round_down(space, user_frag_size); - space = min(space, user_frag_size * user_max_frags); - - info.fragments = space / user_frag_size; - info.fragstotal = user_max_frags; - info.fragsize = user_frag_size; - info.bytes = space; - } - - *(struct audio_buf_info *)rarg = info; - return 0; -} - -static ssize_t padsp_get_ptr(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, int tfd) -{ - int dir = (opcode == OSSP_DSP_GET_OPTR) ? PLAY : REC; - struct count_info info = { }; - - if (prepare_streams() < 0 || !stream[dir]) - return -EIO; - - if (mmap_map[dir]) { - /* mmap operation in progress, report mmap buffer parameters */ - if (dir == PLAY) - mmap_fill_pstg(); - else - mmap_consume_rstg(); - - info.bytes = mmap_idx[dir]; - info.blocks = (mmap_idx[dir] - mmap_last_idx[dir]) / frame_size; - info.ptr = mmap_idx[dir] % mmap_size; - - mmap_last_idx[dir] = mmap_idx[dir]; - } else { - /* simulate pointers using timestamps */ - double bpus = (double)sample_bps / 1000000; - size_t bytes, delta_bytes; - pa_usec_t usec, delta; - - if (pa_stream_get_time(stream[dir], &usec) < 0) { - warn_pa("pa_stream_get_time() failed"); - return -EIO; - } - - delta = usec - stream_ptr_timestamp[dir]; - stream_ptr_timestamp[dir] = usec; - bytes = bpus * usec; - delta_bytes = bpus * delta; - - info.bytes = bytes & INT_MAX; - info.blocks = (delta_bytes + frame_size - 1) / frame_size; - info.ptr = bytes % user_max_length; - } - - *(struct count_info *)rarg = info; - return 0; -} - -static ssize_t padsp_get_odelay(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, - int fd) -{ - double bpus = (double)sample_bps / 1000000; - pa_usec_t usec; - - if (prepare_streams() < 0 || !stream[PLAY]) - return -EIO; - - if (pa_stream_get_latency(stream[PLAY], &usec, NULL) < 0) { - warn_pa("pa_stream_get_latency() failed"); - return -EIO; - } - - *(int *)rarg = bpus * usec; - return 0; -} - -static ossp_action_fn_t action_fn_tbl[OSSP_NR_OPCODES] = { - [OSSP_MIXER] = padsp_mixer, - [OSSP_DSP_OPEN] = padsp_open, - [OSSP_DSP_READ] = padsp_read, - [OSSP_DSP_WRITE] = padsp_write, - [OSSP_DSP_POLL] = padsp_poll, - [OSSP_DSP_MMAP] = padsp_mmap, - [OSSP_DSP_MUNMAP] = padsp_munmap, - [OSSP_DSP_RESET] = padsp_flush, - [OSSP_DSP_SYNC] = padsp_flush, - [OSSP_DSP_POST] = padsp_post, - [OSSP_DSP_GET_RATE] = padsp_get_param, - [OSSP_DSP_GET_CHANNELS] = padsp_get_param, - [OSSP_DSP_GET_FORMAT] = padsp_get_param, - [OSSP_DSP_GET_BLKSIZE] = padsp_get_param, - [OSSP_DSP_GET_FORMATS] = padsp_get_param, - [OSSP_DSP_SET_RATE] = padsp_set_param, - [OSSP_DSP_SET_CHANNELS] = padsp_set_param, - [OSSP_DSP_SET_FORMAT] = padsp_set_param, - [OSSP_DSP_SET_SUBDIVISION] = padsp_set_param, - [OSSP_DSP_SET_FRAGMENT] = padsp_set_param, - [OSSP_DSP_GET_TRIGGER] = padsp_get_param, - [OSSP_DSP_SET_TRIGGER] = padsp_set_trigger, - [OSSP_DSP_GET_OSPACE] = padsp_get_space, - [OSSP_DSP_GET_ISPACE] = padsp_get_space, - [OSSP_DSP_GET_OPTR] = padsp_get_ptr, - [OSSP_DSP_GET_IPTR] = padsp_get_ptr, - [OSSP_DSP_GET_ODELAY] = padsp_get_odelay, -}; - -static int action_pre(void) -{ - pa_threaded_mainloop_lock(mainloop); - if (fail_code) { - pa_threaded_mainloop_unlock(mainloop); - return fail_code; - } - return 0; -} - -static void action_post(void) -{ - pa_threaded_mainloop_unlock(mainloop); -} - -int main(int argc, char **argv) -{ - int rc; - - ossp_slave_init(argc, argv); - - page_size = sysconf(_SC_PAGE_SIZE); - - mainloop = pa_threaded_mainloop_new(); - if (!mainloop) { - err("failed to allocate mainloop"); - return 1; - } - mainloop_api = pa_threaded_mainloop_get_api(mainloop); - - if (pa_threaded_mainloop_start(mainloop)) { - err("pa_mainloop_start() failed"); - return 1; - } - - /* Okay, now we're open for business */ - rc = 0; - do { - rc = ossp_slave_process_command(ossp_cmd_fd, action_fn_tbl, - action_pre, action_post); - } while (rc > 0 && !fail_code); - if (rc) - fail_code = rc; - - pa_threaded_mainloop_lock(mainloop); - - kill_streams(); - if (context) { - pa_context_disconnect(context); - pa_context_unref(context); - } - - pa_threaded_mainloop_unlock(mainloop); - - pa_threaded_mainloop_stop(mainloop); - pa_threaded_mainloop_free(mainloop); - - return fail_code ? 1 : 0; -} diff --git a/ossp-slave.c b/ossp-slave.c deleted file mode 100644 index 4c5cb2d..0000000 --- a/ossp-slave.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * ossp-slave - OSS Proxy: Common codes for slaves - * - * Copyright (C) 2008-2010 SUSE Linux Products GmbH - * Copyright (C) 2008-2010 Tejun Heo - * - * This file is released under the GPLv2. - */ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ossp-slave.h" - -static const char *usage = -"usage: ossp-SLAVE [options]\n" -"\n" -"proxies commands from osspd to pulseaudio\n" -"\n" -"options:\n" -" -u UID uid to use\n" -" -g GID gid to use\n" -" -c CMD_FD fd to receive commands from osspd\n" -" -n NOTIFY_FD fd to send async notifications to osspd\n" -" -m MMAP_FD fd to use for mmap\n" -" -o MMAP_OFFSET mmap offset\n" -" -s MMAP_SIZE mmap size\n" -" -l LOG_LEVEL set log level\n" -" -t enable log timestamps\n"; - -char ossp_user_name[OSSP_USER_NAME_LEN]; -int ossp_cmd_fd = -1, ossp_notify_fd = -1; -void *ossp_mmap_addr[2]; - -void ossp_slave_init(int argc, char **argv) -{ - int have_uid = 0, have_gid = 0; - uid_t uid; - gid_t gid; - int mmap_fd = -1; - off_t mmap_off = 0; - size_t mmap_size = 0; - int opt; - struct passwd *pw, pw_buf; - struct sigaction sa; - char pw_sbuf[sysconf(_SC_GETPW_R_SIZE_MAX)]; - - while ((opt = getopt(argc, argv, "u:g:c:n:m:o:s:l:t")) != -1) { - switch (opt) { - case 'u': - have_uid = 1; - uid = strtol(optarg, NULL, 0); - break; - case 'g': - have_gid = 1; - gid = strtol(optarg, NULL, 0); - break; - case 'c': - ossp_cmd_fd = strtol(optarg, NULL, 0); - break; - case 'n': - ossp_notify_fd = strtol(optarg, NULL, 0); - break; - case 'm': - mmap_fd = strtol(optarg, NULL, 0); - break; - case 'o': - mmap_off = strtoull(optarg, NULL, 0); - break; - case 's': - mmap_size = strtoul(optarg, NULL, 0); - break; - case 'l': - ossp_log_level = strtol(optarg, NULL, 0); - break; - case 't': - ossp_log_timestamp = 1; - break; - } - } - - if (!have_uid || !have_gid || ossp_cmd_fd < 0 || ossp_notify_fd < 0) { - fprintf(stderr, usage); - _exit(1); - } - - snprintf(ossp_user_name, sizeof(ossp_user_name), "uid%d", uid); - if (getpwuid_r(uid, &pw_buf, pw_sbuf, sizeof(pw_sbuf), &pw) == 0) - snprintf(ossp_user_name, sizeof(ossp_user_name), "%s", - pw->pw_name); - - snprintf(ossp_log_name, sizeof(ossp_log_name), "ossp-padsp[%s:%d]", - ossp_user_name, getpid()); - - if (mmap_fd >= 0) { - void *p; - - if (!mmap_off || !mmap_size) { - fprintf(stderr, usage); - _exit(1); - } - - p = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, - mmap_fd, mmap_off); - if (p == MAP_FAILED) - fatal_e(-errno, "mmap failed"); - - ossp_mmap_addr[PLAY] = p; - ossp_mmap_addr[REC] = p + mmap_size / 2; - close(mmap_fd); - } - - /* mmap done, drop privileges */ - if (setresgid(gid, gid, gid) || setresuid(uid, uid, uid)) - fatal_e(-errno, "failed to drop privileges"); - - /* block SIGPIPE */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_IGN; - if (sigaction(SIGPIPE, &sa, NULL)) - fatal_e(-errno, "failed to ignore SIGPIPE"); -} - -int ossp_slave_process_command(int cmd_fd, - ossp_action_fn_t const *action_fn_tbl, - int (*action_pre_fn)(void), - void (*action_post_fn)(void)) -{ - static struct sized_buf carg_sbuf = { }, rarg_sbuf = { }; - static struct sized_buf din_sbuf = { }, dout_sbuf = { }; - struct ossp_cmd cmd; - int fd = -1; - char cmsg_buf[CMSG_SPACE(sizeof(fd))]; - struct iovec iov = { &cmd, sizeof(cmd) }; - struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, - .msg_control = cmsg_buf, - .msg_controllen = sizeof(cmsg_buf) }; - struct cmsghdr *cmsg; - size_t carg_size, din_size, rarg_size, dout_size; - char *carg = NULL, *din = NULL, *rarg = NULL, *dout = NULL; - struct ossp_reply reply = { .magic = OSSP_REPLY_MAGIC }; - ssize_t ret; - - ret = recvmsg(cmd_fd, &msg, 0); - if (ret == 0) - return 0; - if (ret < 0) { - ret = -errno; - err_e(ret, "failed to read command channel"); - return ret; - } - - if (ret != sizeof(cmd)) { - err("command struct size mismatch (%zu, should be %zu)", - ret, sizeof(cmd)); - return -EINVAL; - } - - if (cmd.magic != OSSP_CMD_MAGIC) { - err("illegal command magic 0x%x", cmd.magic); - return -EINVAL; - } - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; - cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS) - fd = *(int *)CMSG_DATA(cmsg); - else { - err("unknown cmsg %d:%d received (opcode %d)", - cmsg->cmsg_level, cmsg->cmsg_type, cmd.opcode); - return -EINVAL; - } - } - - if (cmd.opcode >= OSSP_NR_OPCODES) { - err("unknown opcode %d", cmd.opcode); - return -EINVAL; - } - - carg_size = ossp_arg_sizes[cmd.opcode].carg_size; - din_size = cmd.din_size; - rarg_size = ossp_arg_sizes[cmd.opcode].rarg_size; - dout_size = cmd.dout_size; - - if ((fd >= 0) != ossp_arg_sizes[cmd.opcode].has_fd) { - err("fd=%d unexpected for opcode %d", fd, cmd.opcode); - return -EINVAL; - } - - if (ensure_sbuf_size(&carg_sbuf, carg_size) || - ensure_sbuf_size(&din_sbuf, din_size) || - ensure_sbuf_size(&rarg_sbuf, rarg_size) || - ensure_sbuf_size(&dout_sbuf, dout_size)) { - err("failed to allocate command buffers"); - return -ENOMEM; - } - - if (carg_size) { - carg = carg_sbuf.buf; - ret = read_fill(cmd_fd, carg, carg_size); - if (ret < 0) - return ret; - } - if (din_size) { - din = din_sbuf.buf; - ret = read_fill(cmd_fd, din, din_size); - if (ret < 0) - return ret; - } - if (rarg_size) - rarg = rarg_sbuf.buf; - if (dout_size) - dout = dout_sbuf.buf; - - ret = -EINVAL; - if (action_fn_tbl[cmd.opcode]) { - ret = action_pre_fn(); - if (ret == 0) { - ret = action_fn_tbl[cmd.opcode](cmd.opcode, carg, - din, din_size, rarg, - dout, &dout_size, fd); - action_post_fn(); - } - } - - reply.result = ret; - if (ret >= 0) - reply.dout_size = dout_size; - else { - rarg_size = 0; - dout_size = 0; - } - - if (write_fill(cmd_fd, &reply, sizeof(reply)) < 0 || - write_fill(cmd_fd, rarg, rarg_size) < 0 || - write_fill(cmd_fd, dout, dout_size) < 0) - return -EIO; - - return 1; -} diff --git a/ossp-slave.h b/ossp-slave.h deleted file mode 100644 index 10c22cd..0000000 --- a/ossp-slave.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * ossp-slave - OSS Proxy: Common codes for slaves - * - * Copyright (C) 2008-2010 SUSE Linux Products GmbH - * Copyright (C) 2008-2010 Tejun Heo - * - * This file is released under the GPLv2. - */ - -#ifndef _OSSP_SLAVE_H -#define _OSSP_SLAVE_H - -#include "ossp.h" -#include "ossp-util.h" - -#define OSSP_USER_NAME_LEN 128 - -extern char ossp_user_name[OSSP_USER_NAME_LEN]; -extern int ossp_cmd_fd, ossp_notify_fd; -extern void *ossp_mmap_addr[2]; - -void ossp_slave_init(int argc, char **argv); -int ossp_slave_process_command(int cmd_fd, - ossp_action_fn_t const *action_fn_tbl, - int (*action_pre_fn)(void), - void (*action_post_fn)(void)); - -#endif /* _OSSP_SLAVE_H */ diff --git a/ossp-util.c b/ossp-util.c deleted file mode 100644 index 325cefd..0000000 --- a/ossp-util.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * ossp-util - OSS Proxy: Common utilities - * - * Copyright (C) 2008-2010 SUSE Linux Products GmbH - * Copyright (C) 2008-2010 Tejun Heo - * - * This file is released under the GPLv2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ossp-util.h" - -#define BIT(nr) (1UL << (nr)) -#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) -#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) -#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) -#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) - -char ossp_log_name[OSSP_LOG_NAME_LEN]; -int ossp_log_level = OSSP_LOG_DFL; -int ossp_log_timestamp; - -static const char *severity_strs[] = { - [OSSP_LOG_CRIT] = "CRIT", - [OSSP_LOG_ERR] = " ERR", - [OSSP_LOG_WARN] = "WARN", - [OSSP_LOG_INFO] = NULL, - [OSSP_LOG_DBG0] = "DBG0", - [OSSP_LOG_DBG1] = "DBG1", -}; - -static int severity_map[] = { - [OSSP_LOG_CRIT] = LOG_ERR, - [OSSP_LOG_ERR] = LOG_ERR, - [OSSP_LOG_WARN] = LOG_WARNING, - [OSSP_LOG_INFO] = LOG_INFO, - [OSSP_LOG_DBG0] = LOG_DEBUG, - [OSSP_LOG_DBG1] = LOG_DEBUG, -}; - -void log_msg(int severity, const char *fmt, ...) -{ - static int syslog_opened = 0; - char buf[1024]; - size_t len = sizeof(buf), off = 0; - va_list ap; - - if (severity > abs(ossp_log_level)) - return; - - if (ossp_log_level < 0 && !syslog_opened) - openlog(ossp_log_name, 0, LOG_DAEMON); - - assert(severity >= 0 && severity < ARRAY_SIZE(severity_strs)); - - if (ossp_log_timestamp) { - static uint64_t start; - uint64_t now; - struct timeval tv; - gettimeofday(&tv, NULL); - now = tv.tv_sec * 1000 + tv.tv_usec / 1000; - if (!start) - start = now; - - off += snprintf(buf + off, len - off, "<%08"PRIu64"> ", - now - start); - } - - if (ossp_log_level > 0) { - char sev_buf[16] = ""; - if (severity_strs[severity]) - snprintf(sev_buf, sizeof(sev_buf), " %s", - severity_strs[severity]); - off += snprintf(buf + off, len - off, "%s%s: ", - ossp_log_name, sev_buf); - } else if (severity_strs[severity]) - off += snprintf(buf + off, len - off, "%s ", - severity_strs[severity]); - - va_start(ap, fmt); - off += vsnprintf(buf + off, len - off, fmt, ap); - va_end(ap); - - off += snprintf(buf + off, len - off, "\n"); - - if (ossp_log_level > 0) - fputs(buf, stderr); - else - syslog(severity_map[severity], "%s", buf); -} - -int read_fill(int fd, void *buf, size_t size) -{ - while (size) { - ssize_t ret; - int rc; - - ret = read(fd, buf, size); - if (ret <= 0) { - if (ret == 0) - rc = -EIO; - else - rc = -errno; - err_e(rc, "failed to read_fill %zu bytes from fd %d", - size, fd); - return rc; - } - buf += ret; - size -= ret; - } - return 0; -} - -int write_fill(int fd, const void *buf, size_t size) -{ - while (size) { - ssize_t ret; - int rc; - - ret = write(fd, buf, size); - if (ret <= 0) { - if (ret == 0) - rc = -EIO; - else - rc = -errno; - err_e(rc, "failed to write_fill %zu bytes to fd %d", - size, fd); - return rc; - } - buf += ret; - size -= ret; - } - return 0; -} - -void ring_fill(struct ring_buf *ring, const void *buf, size_t size) -{ - size_t tail; - - assert(ring_space(ring) >= size); - - tail = (ring->head + ring->size - ring->bytes) % ring->size; - - if (ring->head >= tail) { - size_t todo = min(size, ring->size - ring->head); - - memcpy(ring->buf + ring->head, buf, todo); - ring->head = (ring->head + todo) % ring->size; - ring->bytes += todo; - buf += todo; - size -= todo; - } - - assert(ring->size - ring->head >= size); - memcpy(ring->buf + ring->head, buf, size); - ring->head += size; - ring->bytes += size; -} - -void *ring_data(struct ring_buf *ring, size_t *sizep) -{ - size_t tail; - - if (!ring->bytes) - return NULL; - - tail = (ring->head + ring->size - ring->bytes) % ring->size; - - *sizep = min(ring->bytes, ring->size - tail); - return ring->buf + tail; -} - -int ring_resize(struct ring_buf *ring, size_t new_size) -{ - struct ring_buf new_ring = { .size = new_size }; - void *p; - size_t size; - - if (ring_bytes(ring) > new_size) - return -ENOSPC; - - new_ring.buf = calloc(1, new_size); - if (new_size && !new_ring.buf) - return -ENOMEM; - - while ((p = ring_data(ring, &size))) { - ring_fill(&new_ring, p, size); - ring_consume(ring, size); - } - - free(ring->buf); - *ring = new_ring; - return 0; -} - -int ensure_sbuf_size(struct sized_buf *sbuf, size_t size) -{ - char *new_buf; - - if (sbuf->size >= size) - return 0; - - new_buf = realloc(sbuf->buf, size); - if (size && !new_buf) - return -ENOMEM; - - sbuf->buf = new_buf; - sbuf->size = size; - return 0; -} - -static unsigned long __ffs(unsigned long word) -{ - int num = 0; - - if (BITS_PER_LONG == 64) { - if ((word & 0xffffffff) == 0) { - num += 32; - word >>= 32; - } - } - - if ((word & 0xffff) == 0) { - num += 16; - word >>= 16; - } - if ((word & 0xff) == 0) { - num += 8; - word >>= 8; - } - if ((word & 0xf) == 0) { - num += 4; - word >>= 4; - } - if ((word & 0x3) == 0) { - num += 2; - word >>= 2; - } - if ((word & 0x1) == 0) - num += 1; - return num; -} - -#define ffz(x) __ffs(~(x)) - -unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, - unsigned long offset) -{ - const unsigned long *p = addr + BITOP_WORD(offset); - unsigned long result = offset & ~(BITS_PER_LONG-1); - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset %= BITS_PER_LONG; - if (offset) { - tmp = *(p++); - tmp |= ~0UL >> (BITS_PER_LONG - offset); - if (size < BITS_PER_LONG) - goto found_first; - if (~tmp) - goto found_middle; - size -= BITS_PER_LONG; - result += BITS_PER_LONG; - } - while (size & ~(BITS_PER_LONG-1)) { - if (~(tmp = *(p++))) - goto found_middle; - result += BITS_PER_LONG; - size -= BITS_PER_LONG; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp |= ~0UL << size; - if (tmp == ~0UL) /* Are any bits zero? */ - return result + size; /* Nope. */ -found_middle: - return result + ffz(tmp); -} - -void __set_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); - - *p |= mask; -} - -void __clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); - - *p &= ~mask; -} - -int get_proc_self_info(pid_t pid, pid_t *ppid_r, - char *cmd_buf, size_t cmd_buf_sz) - -{ - char path[64], buf[4096]; - int fd = -1; - char *cmd_start, *cmd_end, *ppid_start, *end; - ssize_t ret; - pid_t ppid; - int i, rc; - - snprintf(path, sizeof(path), "/proc/%ld/stat", (long)pid); - fd = open(path, O_RDONLY); - if (fd < 0) { - rc = -errno; - goto out; - } - - ret = read(fd, buf, sizeof(buf)); - if (ret < 0) - goto out; - if (ret == sizeof(buf)) { - rc = -EOVERFLOW; - goto out; - } - buf[ret] = '\0'; - - rc = -EINVAL; - cmd_start = strchr(buf, '('); - cmd_end = strrchr(buf, ')'); - if (!cmd_start || !cmd_end) - goto out; - cmd_start++; - - ppid_start = cmd_end; - for (i = 0; i < 3; i++) { - ppid_start = strchr(ppid_start, ' '); - if (!ppid_start) - goto out; - ppid_start++; - } - - ppid = strtoul(ppid_start, &end, 10); - if (end == ppid_start || *end != ' ') - goto out; - - if (ppid_r) - *ppid_r = ppid; - if (cmd_buf) { - size_t len = min_t(size_t, cmd_end - cmd_start, cmd_buf_sz - 1); - memcpy(cmd_buf, cmd_start, len); - cmd_buf[len] = '\0'; - } - - rc = 0; - out: - close(fd); - - return rc; -} diff --git a/ossp-util.h b/ossp-util.h deleted file mode 100644 index f48d022..0000000 --- a/ossp-util.h +++ /dev/null @@ -1,609 +0,0 @@ -/* - * ossp-util - OSS Proxy: Common utilities - * - * Copyright (C) 2008-2010 SUSE Linux Products GmbH - * Copyright (C) 2008-2010 Tejun Heo - * - * This file is released under the GPLv2. - */ - -#ifndef _OSSP_UTIL_H -#define _OSSP_UTIL_H - -#include -#include -#include -#include -#include -#include -#include "ossp.h" - -#define OSSP_LOG_NAME_LEN 128 - -enum { - OSSP_LOG_CRIT = 1, - OSSP_LOG_ERR, - OSSP_LOG_WARN, - OSSP_LOG_INFO, - OSSP_LOG_DFL = OSSP_LOG_INFO, /* default log level */ - OSSP_LOG_DBG0, - OSSP_LOG_DBG1, - OSSP_LOG_MAX = OSSP_LOG_DBG1, -}; - -extern char ossp_log_name[OSSP_LOG_NAME_LEN]; -extern int ossp_log_level; -extern int ossp_log_timestamp; - -#define BITS_PER_BYTE 8 -#define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long)) -#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) -#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) - -/* ARRAY_SIZE and min/max macros stolen from linux/kernel.h */ -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - -#define min(x, y) ({ \ - typeof(x) _min1 = (x); \ - typeof(y) _min2 = (y); \ - (void) (&_min1 == &_min2); \ - _min1 < _min2 ? _min1 : _min2; }) - -#define max(x, y) ({ \ - typeof(x) _max1 = (x); \ - typeof(y) _max2 = (y); \ - (void) (&_max1 == &_max2); \ - _max1 > _max2 ? _max1 : _max2; }) - -#define min_t(type, x, y) ({ \ - type __min1 = (x); \ - type __min2 = (y); \ - __min1 < __min2 ? __min1: __min2; }) - -#define max_t(type, x, y) ({ \ - type __max1 = (x); \ - type __max2 = (y); \ - __max1 > __max2 ? __max1: __max2; }) - -void log_msg(int severity, const char *fmt, ...) - __attribute__ ((format (printf, 2, 3))); - -#define fatal(fmt, args...) do { \ - log_msg(OSSP_LOG_CRIT, fmt , ##args); \ - _exit(1); \ -} while (0) -#define err(fmt, args...) log_msg(OSSP_LOG_ERR, fmt , ##args) -#define warn(fmt, args...) log_msg(OSSP_LOG_WARN, fmt , ##args) -#define info(fmt, args...) log_msg(OSSP_LOG_INFO, fmt , ##args) -#define dbg0(fmt, args...) log_msg(OSSP_LOG_DBG0, fmt , ##args) -#define dbg1(fmt, args...) log_msg(OSSP_LOG_DBG1, fmt , ##args) - -#define fatal_e(e, fmt, args...) \ - fatal(fmt" (%s)" , ##args, strerror(-(e))) -#define err_e(e, fmt, args...) \ - err(fmt" (%s)" , ##args, strerror(-(e))) -#define warn_e(e, fmt, args...) \ - warn(fmt" (%s)" , ##args, strerror(-(e))) -#define info_e(e, fmt, args...) \ - info(fmt" (%s)" , ##args, strerror(-(e))) -#define dbg0_e(e, fmt, args...) \ - dbg0(fmt" (%s)" , ##args, strerror(-(e))) -#define dbg1_e(e, fmt, args...) \ - dbg1(fmt" (%s)" , ##args, strerror(-(e))) - -struct ring_buf { - char *buf; - size_t size; - size_t head; - size_t bytes; -}; - -static inline size_t ring_size(struct ring_buf *ring) -{ - return ring->size; -} - -static inline size_t ring_bytes(struct ring_buf *ring) -{ - return ring->bytes; -} - -static inline size_t ring_space(struct ring_buf *ring) -{ - return ring->size - ring->bytes; -} - -static inline void ring_consume(struct ring_buf *ring, size_t size) -{ - assert(ring->bytes >= size); - ring->bytes -= size; -} - -static inline void ring_manual_init(struct ring_buf *ring, void *buf, - size_t size, size_t head, size_t bytes) -{ - ring->buf = buf; - ring->size = size; - ring->head = head; - ring->bytes = bytes; -} - -void ring_fill(struct ring_buf *ring, const void *buf, size_t size); -void *ring_data(struct ring_buf *ring, size_t *sizep); -int ring_resize(struct ring_buf *ring, size_t new_size); - -struct sized_buf { - char *buf; - size_t size; -}; - -int ensure_sbuf_size(struct sized_buf *sbuf, size_t size); - -int read_fill(int fd, void *buf, size_t size); -int write_fill(int fd, const void *buf, size_t size); - -/* - * Bitops lifted from linux asm-generic implementation. - */ -unsigned long find_next_zero_bit(const unsigned long *addr, unsigned - long size, unsigned long offset); -#define find_first_zero_bit(addr, size) find_next_zero_bit((addr), (size), 0) -extern void __set_bit(int nr, volatile unsigned long *addr); -extern void __clear_bit(int nr, volatile unsigned long *addr); - -typedef ssize_t (*ossp_action_fn_t)(enum ossp_opcode opcode, - void *carg, void *din, size_t din_sz, - void *rarg, void *dout, size_t *dout_szp, - int fd); - -int get_proc_self_info(pid_t tid, pid_t *pgrp, - char *cmd_buf, size_t cmd_buf_sz); - -/* - * Doubly linked list handling code shamelessly stolen from the Linux - * kernel 2.6.26 include/linux/list.h. - */ - -/** - * container_of - cast a member of a structure out to the containing structure - * @ptr: the pointer to the member. - * @type: the type of the container struct this is embedded in. - * @member: the name of the member within the struct. - * - */ -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) - -#define LIST_POISON1 ((void *) 0x00100100) -#define LIST_POISON2 ((void *) 0x00200200) - -/* - * Simple doubly linked list implementation. - * - * Some of the internal functions ("__xxx") are useful when - * manipulating whole lists rather than single entries, as - * sometimes we already know the next/prev entries and we can - * generate better code by using them directly rather than - * using the generic single-entry routines. - */ - -struct list_head { - struct list_head *next, *prev; -}; - -#define LIST_HEAD_INIT(name) { &(name), &(name) } - -#define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) - -static inline void INIT_LIST_HEAD(struct list_head *list) -{ - list->next = list; - list->prev = list; -} - -/* - * Insert a new entry between two known consecutive entries. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -static inline void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); -} - -/** - * list_add_tail - add a new entry - * @new: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) -{ - __list_add(new, head->prev, head); -} - -/* - * Delete a list entry by making the prev/next entries - * point to each other. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_del(struct list_head * prev, struct list_head * next) -{ - next->prev = prev; - prev->next = next; -} - -/** - * list_del - deletes entry from list. - * @entry: the element to delete from the list. - * Note: list_empty() on entry does not return true after this, the entry is - * in an undefined state. - */ -static inline void list_del(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - entry->next = LIST_POISON1; - entry->prev = LIST_POISON2; -} - -/** - * list_replace - replace old entry by new one - * @old : the element to be replaced - * @new : the new element to insert - * - * If @old was empty, it will be overwritten. - */ -static inline void list_replace(struct list_head *old, - struct list_head *new) -{ - new->next = old->next; - new->next->prev = new; - new->prev = old->prev; - new->prev->next = new; -} - -static inline void list_replace_init(struct list_head *old, - struct list_head *new) -{ - list_replace(old, new); - INIT_LIST_HEAD(old); -} - -/** - * list_del_init - deletes entry from list and reinitialize it. - * @entry: the element to delete from the list. - */ -static inline void list_del_init(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - INIT_LIST_HEAD(entry); -} - -/** - * list_move - delete from one list and add as another's head - * @list: the entry to move - * @head: the head that will precede our entry - */ -static inline void list_move(struct list_head *list, struct list_head *head) -{ - __list_del(list->prev, list->next); - list_add(list, head); -} - -/** - * list_move_tail - delete from one list and add as another's tail - * @list: the entry to move - * @head: the head that will follow our entry - */ -static inline void list_move_tail(struct list_head *list, - struct list_head *head) -{ - __list_del(list->prev, list->next); - list_add_tail(list, head); -} - -/** - * list_is_last - tests whether @list is the last entry in list @head - * @list: the entry to test - * @head: the head of the list - */ -static inline int list_is_last(const struct list_head *list, - const struct list_head *head) -{ - return list->next == head; -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -static inline int list_empty(const struct list_head *head) -{ - return head->next == head; -} - -/** - * list_empty_careful - tests whether a list is empty and not being modified - * @head: the list to test - * - * Description: - * tests whether a list is empty _and_ checks that no other CPU might be - * in the process of modifying either member (next or prev) - * - * NOTE: using list_empty_careful() without synchronization - * can only be safe if the only activity that can happen - * to the list entry is list_del_init(). Eg. it cannot be used - * if another CPU could re-list_add() it. - */ -static inline int list_empty_careful(const struct list_head *head) -{ - struct list_head *next = head->next; - return (next == head) && (next == head->prev); -} - -/** - * list_is_singular - tests whether a list has just one entry. - * @head: the list to test. - */ -static inline int list_is_singular(const struct list_head *head) -{ - return !list_empty(head) && (head->next == head->prev); -} - -static inline void __list_splice(const struct list_head *list, - struct list_head *head) -{ - struct list_head *first = list->next; - struct list_head *last = list->prev; - struct list_head *at = head->next; - - first->prev = head; - head->next = first; - - last->next = at; - at->prev = last; -} - -/** - * list_splice - join two lists - * @list: the new list to add. - * @head: the place to add it in the first list. - */ -static inline void list_splice(const struct list_head *list, - struct list_head *head) -{ - if (!list_empty(list)) - __list_splice(list, head); -} - -/** - * list_splice_init - join two lists and reinitialise the emptied list. - * @list: the new list to add. - * @head: the place to add it in the first list. - * - * The list at @list is reinitialised - */ -static inline void list_splice_init(struct list_head *list, - struct list_head *head) -{ - if (!list_empty(list)) { - __list_splice(list, head); - INIT_LIST_HEAD(list); - } -} - -/** - * list_entry - get the struct for this entry - * @ptr: the &struct list_head pointer. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_struct within the struct. - */ -#define list_entry(ptr, type, member) \ - container_of(ptr, type, member) - -/** - * list_first_entry - get the first element from a list - * @ptr: the list head to take the element from. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_struct within the struct. - * - * Note, that list is expected to be not empty. - */ -#define list_first_entry(ptr, type, member) \ - list_entry((ptr)->next, type, member) - -/** - * list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - */ -#define list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) - -/** - * list_for_each_prev - iterate over a list backwards - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - */ -#define list_for_each_prev(pos, head) \ - for (pos = (head)->prev; pos != (head); pos = pos->prev) - -/** - * list_for_each_safe - iterate over a list safe against removal of list entry - * @pos: the &struct list_head to use as a loop cursor. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. - */ -#define list_for_each_safe(pos, n, head) \ - for (pos = (head)->next, n = pos->next; pos != (head); \ - pos = n, n = pos->next) - -/** - * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry - * @pos: the &struct list_head to use as a loop cursor. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. - */ -#define list_for_each_prev_safe(pos, n, head) \ - for (pos = (head)->prev, n = pos->prev; \ - pos != (head); pos = n, n = pos->prev) - -/** - * list_for_each_entry - iterate over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry(pos, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_reverse - iterate backwards over list of given type. - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry_reverse(pos, head, member) \ - for (pos = list_entry((head)->prev, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.prev, typeof(*pos), member)) - -/** - * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() - * @pos: the type * to use as a start point - * @head: the head of the list - * @member: the name of the list_struct within the struct. - * - * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). - */ -#define list_prepare_entry(pos, head, member) \ - ((pos) ? : list_entry(head, typeof(*pos), member)) - -/** - * list_for_each_entry_continue - continue iteration over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Continue to iterate over list of given type, continuing after - * the current position. - */ -#define list_for_each_entry_continue(pos, head, member) \ - for (pos = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_continue_reverse - iterate backwards from the given point - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Start to iterate over list of given type backwards, continuing after - * the current position. - */ -#define list_for_each_entry_continue_reverse(pos, head, member) \ - for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.prev, typeof(*pos), member)) - -/** - * list_for_each_entry_from - iterate over list of given type from the current point - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate over list of given type, continuing from current position. - */ -#define list_for_each_entry_from(pos, head, member) \ - for (; &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_continue - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate over list of given type, continuing after current point, - * safe against removal of list entry. - */ -#define list_for_each_entry_safe_continue(pos, n, head, member) \ - for (pos = list_entry(pos->member.next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_from - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate over list of given type from current point, safe against - * removal of list entry. - */ -#define list_for_each_entry_safe_from(pos, n, head, member) \ - for (n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -/** - * list_for_each_entry_safe_reverse - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - * - * Iterate backwards over list of given type, safe against removal - * of list entry. - */ -#define list_for_each_entry_safe_reverse(pos, n, head, member) \ - for (pos = list_entry((head)->prev, typeof(*pos), member), \ - n = list_entry(pos->member.prev, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.prev, typeof(*n), member)) - -#endif /*_OSSP_UTIL_H*/ diff --git a/ossp.c b/ossp.c deleted file mode 100644 index 96f98fa..0000000 --- a/ossp.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * ossp - OSS Proxy: emulate OSS device using CUSE - * - * Copyright (C) 2008-2010 SUSE Linux Products GmbH - * Copyright (C) 2008-2010 Tejun Heo - * - * This file is released under the GPLv2. - */ - -#include "ossp.h" - -const struct ossp_arg_size ossp_arg_sizes[OSSP_NR_OPCODES] = { - [OSSP_MIXER] = { sizeof(struct ossp_mixer_arg), - sizeof(struct ossp_mixer_arg), 0 }, - - [OSSP_DSP_OPEN] = { sizeof(struct ossp_dsp_open_arg), 0, 0 }, - [OSSP_DSP_READ] = { sizeof(struct ossp_dsp_rw_arg), 0, 0 }, - [OSSP_DSP_WRITE] = { sizeof(struct ossp_dsp_rw_arg), 0, 0 }, - [OSSP_DSP_POLL] = { sizeof(int), sizeof(unsigned), 0 }, - [OSSP_DSP_MMAP] = { sizeof(struct ossp_dsp_mmap_arg), 0, 0 }, - [OSSP_DSP_MUNMAP] = { sizeof(int), 0, 0 }, - - [OSSP_DSP_RESET] = { 0, 0, 0 }, - [OSSP_DSP_SYNC] = { 0, 0, 0 }, - [OSSP_DSP_POST] = { 0, 0, 0 }, - [OSSP_DSP_GET_RATE] = { 0, sizeof(int), 0 }, - [OSSP_DSP_GET_CHANNELS] = { 0, sizeof(int), 0 }, - [OSSP_DSP_GET_FORMAT] = { 0, sizeof(int), 0 }, - [OSSP_DSP_GET_BLKSIZE] = { 0, sizeof(int), 0 }, - [OSSP_DSP_GET_FORMATS] = { 0, sizeof(int), 0 }, - [OSSP_DSP_SET_RATE] = { sizeof(int), sizeof(int), 0 }, - [OSSP_DSP_SET_CHANNELS] = { sizeof(int), sizeof(int), 0 }, - [OSSP_DSP_SET_FORMAT] = { sizeof(int), sizeof(int), 0 }, - [OSSP_DSP_SET_SUBDIVISION] = { sizeof(int), sizeof(int), 0 }, - [OSSP_DSP_SET_FRAGMENT] = { sizeof(int), 0, 0 }, - [OSSP_DSP_GET_TRIGGER] = { 0, sizeof(int), 0 }, - [OSSP_DSP_SET_TRIGGER] = { sizeof(int), 0, 0 }, - [OSSP_DSP_GET_OSPACE] = { 0, sizeof(struct audio_buf_info), 0 }, - [OSSP_DSP_GET_ISPACE] = { 0, sizeof(struct audio_buf_info), 0 }, - [OSSP_DSP_GET_OPTR] = { 0, sizeof(struct count_info), 0 }, - [OSSP_DSP_GET_IPTR] = { 0, sizeof(struct count_info), 0 }, - [OSSP_DSP_GET_ODELAY] = { 0, sizeof(int), 0 }, -}; - -const char *ossp_cmd_str[OSSP_NR_OPCODES] = { - [OSSP_MIXER] = "MIXER", - - [OSSP_DSP_OPEN] = "OPEN", - [OSSP_DSP_READ] = "READ", - [OSSP_DSP_WRITE] = "WRITE", - [OSSP_DSP_POLL] = "POLL", - [OSSP_DSP_MMAP] = "MMAP", - [OSSP_DSP_MUNMAP] = "MUNMAP", - - [OSSP_DSP_RESET] = "RESET", - [OSSP_DSP_SYNC] = "SYNC", - [OSSP_DSP_POST] = "POST", - - [OSSP_DSP_GET_RATE] = "GET_RATE", - [OSSP_DSP_GET_CHANNELS] = "GET_CHANNELS", - [OSSP_DSP_GET_FORMAT] = "GET_FORMAT", - [OSSP_DSP_GET_BLKSIZE] = "GET_BLKSIZE", - [OSSP_DSP_GET_FORMATS] = "GET_FORMATS", - [OSSP_DSP_SET_RATE] = "SET_RATE", - [OSSP_DSP_SET_CHANNELS] = "SET_CHANNELS", - [OSSP_DSP_SET_FORMAT] = "SET_FORMAT", - [OSSP_DSP_SET_SUBDIVISION] = "SET_BUSDIVISION", - - [OSSP_DSP_SET_FRAGMENT] = "SET_FRAGMENT", - [OSSP_DSP_GET_TRIGGER] = "GET_TRIGGER", - [OSSP_DSP_SET_TRIGGER] = "SET_TRIGGER", - [OSSP_DSP_GET_OSPACE] = "GET_OSPACE", - [OSSP_DSP_GET_ISPACE] = "GET_ISPACE", - [OSSP_DSP_GET_OPTR] = "GET_OPTR", - [OSSP_DSP_GET_IPTR] = "GET_IPTR", - [OSSP_DSP_GET_ODELAY] = "GET_ODELAY", -}; - -const char *ossp_notify_str[OSSP_NR_NOTIFY_OPCODES] = { - [OSSP_NOTIFY_POLL] = "POLL", - [OSSP_NOTIFY_OBITUARY] = "OBITUARY", - [OSSP_NOTIFY_VOLCHG] = "VOLCHG", -}; diff --git a/ossp.h b/ossp.h deleted file mode 100644 index 9d03e63..0000000 --- a/ossp.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * ossp - OSS Proxy: emulate OSS device using CUSE - * - * Copyright (C) 2008-2010 SUSE Linux Products GmbH - * Copyright (C) 2008-2010 Tejun Heo - * - * This file is released under the GPLv2. - */ - -#ifndef _OSSP_H -#define _OSSP_H - -#include -#include -#include - -#define OSSP_VERSION "1.3.2" -#define OSSP_CMD_MAGIC 0xdeadbeef -#define OSSP_REPLY_MAGIC 0xbeefdead -#define OSSP_NOTIFY_MAGIC 0xbebebebe - -#define PLAY 0 -#define REC 1 -#define LEFT 0 -#define RIGHT 1 - -enum ossp_opcode { - OSSP_MIXER, - - OSSP_DSP_OPEN, - OSSP_DSP_READ, - OSSP_DSP_WRITE, - OSSP_DSP_POLL, - OSSP_DSP_MMAP, - OSSP_DSP_MUNMAP, - - OSSP_DSP_RESET, - OSSP_DSP_SYNC, - OSSP_DSP_POST, - - OSSP_DSP_GET_RATE, - OSSP_DSP_GET_CHANNELS, - OSSP_DSP_GET_FORMAT, - OSSP_DSP_GET_BLKSIZE, - OSSP_DSP_GET_FORMATS, - OSSP_DSP_SET_RATE, - OSSP_DSP_SET_CHANNELS, - OSSP_DSP_SET_FORMAT, - OSSP_DSP_SET_SUBDIVISION, - - OSSP_DSP_SET_FRAGMENT, - OSSP_DSP_GET_TRIGGER, - OSSP_DSP_SET_TRIGGER, - OSSP_DSP_GET_OSPACE, - OSSP_DSP_GET_ISPACE, - OSSP_DSP_GET_OPTR, - OSSP_DSP_GET_IPTR, - OSSP_DSP_GET_ODELAY, - - OSSP_NR_OPCODES, -}; - -enum ossp_notify_opcode { - OSSP_NOTIFY_POLL, - OSSP_NOTIFY_OBITUARY, - OSSP_NOTIFY_VOLCHG, - - OSSP_NR_NOTIFY_OPCODES, -}; - -struct ossp_mixer_arg { - int vol[2][2]; -}; - -struct ossp_dsp_open_arg { - int flags; - pid_t opener_pid; -}; - -struct ossp_dsp_rw_arg { - unsigned nonblock:1; -}; - -struct ossp_dsp_mmap_arg { - int dir; - size_t size; -}; - -struct ossp_cmd { - unsigned magic; - enum ossp_opcode opcode; - size_t din_size; - size_t dout_size; -}; - -struct ossp_reply { - unsigned magic; - int result; - size_t dout_size; /* <= cmd.data_in_size */ -}; - -struct ossp_notify { - unsigned magic; - enum ossp_notify_opcode opcode; -}; - -struct ossp_arg_size { - ssize_t carg_size; - ssize_t rarg_size; - unsigned has_fd:1; -}; - -extern const struct ossp_arg_size ossp_arg_sizes[OSSP_NR_OPCODES]; -extern const char *ossp_cmd_str[OSSP_NR_OPCODES]; -extern const char *ossp_notify_str[OSSP_NR_NOTIFY_OPCODES]; - -#endif /* _OSSP_H */ diff --git a/osspd.c b/osspd.c deleted file mode 100644 index 37c9b35..0000000 --- a/osspd.c +++ /dev/null @@ -1,2295 +0,0 @@ -/* - * osspd - OSS Proxy Daemon: emulate OSS device using CUSE - * - * Copyright (C) 2008-2010 SUSE Linux Products GmbH - * Copyright (C) 2008-2010 Tejun Heo - * - * This file is released under the GPLv2. - */ - -#define FUSE_USE_VERSION 28 -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ossp.h" -#include "ossp-util.h" - -/* - * MMAP support needs to be updated to the new fuse MMAP API. Disable - * it for the time being. - */ -#warning mmap support disabled for now -/* #define OSSP_MMAP */ - -#define DFL_MIXER_NAME "mixer" -#define DFL_DSP_NAME "dsp" -#define DFL_ADSP_NAME "adsp" -#define STRFMT "S[%u/%d]" -#define STRID(os) os->id, os->pid - -#define dbg1_os(os, fmt, args...) dbg1(STRFMT" "fmt, STRID(os) , ##args) -#define dbg0_os(os, fmt, args...) dbg0(STRFMT" "fmt, STRID(os) , ##args) -#define warn_os(os, fmt, args...) warn(STRFMT" "fmt, STRID(os) , ##args) -#define err_os(os, fmt, args...) err(STRFMT" "fmt, STRID(os) , ##args) -#define warn_ose(os, err, fmt, args...) \ - warn_e(err, STRFMT" "fmt, STRID(os) , ##args) -#define err_ose(os, err, fmt, args...) \ - err_e(err, STRFMT" "fmt, STRID(os) , ##args) - -enum { - SNDRV_OSS_VERSION = ((3<<16)|(8<<8)|(1<<4)|(0)), /* 3.8.1a */ - DFL_MIXER_MAJOR = 14, - DFL_MIXER_MINOR = 0, - DFL_DSP_MAJOR = 14, - DFL_DSP_MINOR = 3, - DFL_ADSP_MAJOR = 14, - DFL_ADSP_MINOR = 12, - DFL_MAX_STREAMS = 128, - MIXER_PUT_DELAY = 600, /* 10 mins */ - /* DSPS_MMAP_SIZE / 2 must be multiple of SHMLBA */ - DSPS_MMAP_SIZE = 2 * (512 << 10), /* 512k for each dir */ -}; - -struct ossp_uid_cnt { - struct list_head link; - uid_t uid; - unsigned nr_os; -}; - -struct ossp_mixer { - pid_t pgrp; - struct list_head link; - struct list_head delayed_put_link; - unsigned refcnt; - /* the following two fields are protected by mixer_mutex */ - int vol[2][2]; - int modify_counter; - time_t put_expires; -}; - -struct ossp_mixer_cmd { - struct ossp_mixer *mixer; - struct ossp_mixer_arg set; - int out_dir; - int rvol; -}; - -#define for_each_vol(i, j) \ - for (i = 0, j = 0; i < 2; j += i << 1, j++, i = j >> 1, j &= 1) - -struct ossp_stream { - unsigned id; /* stream ID */ - struct list_head link; - struct list_head pgrp_link; - struct list_head notify_link; - unsigned refcnt; - pthread_mutex_t cmd_mutex; - pthread_mutex_t mmap_mutex; - struct fuse_pollhandle *ph; - - /* stream owner info */ - pid_t pid; - pid_t pgrp; - uid_t uid; - gid_t gid; - - /* slave info */ - pid_t slave_pid; - int cmd_fd; - int notify_tx; - int notify_rx; - - /* the following dead flag is set asynchronously, keep it separate. */ - int dead; - - /* stream mixer state, protected by mixer_mutex */ - int mixer_pending; - int vol[2][2]; - int vol_set[2][2]; - - off_t mmap_off; - size_t mmap_size; - - struct ossp_uid_cnt *ucnt; - struct fuse_session *se; /* associated fuse session */ - struct ossp_mixer *mixer; -}; - -struct ossp_dsp_stream { - struct ossp_stream os; - unsigned rw; - unsigned mmapped; - int nonblock; -}; - -#define os_to_dsps(_os) container_of(_os, struct ossp_dsp_stream, os) - -static unsigned max_streams; -static unsigned umax_streams; -static unsigned hashtbl_size; -static char dsp_slave_path[PATH_MAX]; - -static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_mutex_t mixer_mutex = PTHREAD_MUTEX_INITIALIZER; -static unsigned long *os_id_bitmap; -static unsigned nr_mixers; -static struct list_head *mixer_tbl; /* indexed by PGRP */ -static struct list_head *os_tbl; /* indexed by ID */ -static struct list_head *os_pgrp_tbl; /* indexed by PGRP */ -static struct list_head *os_notify_tbl; /* indexed by notify fd */ -static LIST_HEAD(uid_cnt_list); -static int notify_epfd; /* epoll used to monitor notify fds */ -static pthread_t notify_poller_thread; -static pthread_t slave_reaper_thread; -static pthread_t mixer_delayed_put_thread; -static pthread_t cuse_mixer_thread; -static pthread_t cuse_adsp_thread; -static pthread_cond_t notify_poller_kill_wait = PTHREAD_COND_INITIALIZER; -static pthread_cond_t slave_reaper_wait = PTHREAD_COND_INITIALIZER; -static LIST_HEAD(slave_corpse_list); -static LIST_HEAD(mixer_delayed_put_head); /* delayed reference */ -static pthread_cond_t mixer_delayed_put_cond = PTHREAD_COND_INITIALIZER; - -static int init_wait_fd = -1; -static int exit_on_idle; -static struct fuse_session *mixer_se; -static struct fuse_session *dsp_se; -static struct fuse_session *adsp_se; - -static void put_os(struct ossp_stream *os); - - -/*************************************************************************** - * Accessors - */ - -static struct list_head *mixer_tbl_head(pid_t pid) -{ - return &mixer_tbl[pid % hashtbl_size]; -} - -static struct list_head *os_tbl_head(uint64_t id) -{ - return &os_tbl[id % hashtbl_size]; -} - -static struct list_head *os_pgrp_tbl_head(pid_t pgrp) -{ - return &os_pgrp_tbl[pgrp % hashtbl_size]; -} - -static struct list_head *os_notify_tbl_head(int notify_rx) -{ - return &os_notify_tbl[notify_rx % hashtbl_size]; -} - -static struct ossp_mixer *find_mixer_locked(pid_t pgrp) -{ - struct ossp_mixer *mixer; - - list_for_each_entry(mixer, mixer_tbl_head(pgrp), link) - if (mixer->pgrp == pgrp) - return mixer; - return NULL; -} - -static struct ossp_mixer *find_mixer(pid_t pgrp) -{ - struct ossp_mixer *mixer; - - pthread_mutex_lock(&mutex); - mixer = find_mixer_locked(pgrp); - pthread_mutex_unlock(&mutex); - return mixer; -} - -static struct ossp_stream *find_os(unsigned id) -{ - struct ossp_stream *os, *found = NULL; - - pthread_mutex_lock(&mutex); - list_for_each_entry(os, os_tbl_head(id), link) - if (os->id == id) { - found = os; - break; - } - pthread_mutex_unlock(&mutex); - return found; -} - -static struct ossp_stream *find_os_by_notify_rx(int notify_rx) -{ - struct ossp_stream *os, *found = NULL; - - pthread_mutex_lock(&mutex); - list_for_each_entry(os, os_notify_tbl_head(notify_rx), notify_link) - if (os->notify_rx == notify_rx) { - found = os; - break; - } - pthread_mutex_unlock(&mutex); - return found; -} - - -/*************************************************************************** - * Command and ioctl helpers - */ - -static ssize_t exec_cmd_intern(struct ossp_stream *os, enum ossp_opcode opcode, - const void *carg, size_t carg_size, const void *din, size_t din_size, - void *rarg, size_t rarg_size, void *dout, size_t *dout_sizep, int fd) -{ - size_t dout_size = dout_sizep ? *dout_sizep : 0; - struct ossp_cmd cmd = { .magic = OSSP_CMD_MAGIC, .opcode = opcode, - .din_size = din_size, - .dout_size = dout_size }; - struct iovec iov = { &cmd, sizeof(cmd) }; - struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 }; - struct ossp_reply reply = { }; - char cmsg_buf[CMSG_SPACE(sizeof(fd))]; - char reason[512]; - int rc; - - if (os->dead) - return -EIO; - - dbg1_os(os, "%s carg=%zu din=%zu rarg=%zu dout=%zu", - ossp_cmd_str[opcode], carg_size, din_size, rarg_size, - dout_size); - - if (fd >= 0) { - struct cmsghdr *cmsg; - - msg.msg_control = cmsg_buf; - msg.msg_controllen = sizeof(cmsg_buf); - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); - *(int *)CMSG_DATA(cmsg) = fd; - msg.msg_controllen = cmsg->cmsg_len; - } - - if (sendmsg(os->cmd_fd, &msg, 0) <= 0) { - rc = -errno; - snprintf(reason, sizeof(reason), "command sendmsg failed: %s", - strerror(-rc)); - goto fail; - } - - if ((rc = write_fill(os->cmd_fd, carg, carg_size)) < 0 || - (rc = write_fill(os->cmd_fd, din, din_size)) < 0) { - snprintf(reason, sizeof(reason), - "can't tranfer command argument and/or data: %s", - strerror(-rc)); - goto fail; - } - if ((rc = read_fill(os->cmd_fd, &reply, sizeof(reply))) < 0) { - snprintf(reason, sizeof(reason), "can't read reply: %s", - strerror(-rc)); - goto fail; - } - - if (reply.magic != OSSP_REPLY_MAGIC) { - snprintf(reason, sizeof(reason), - "reply magic mismatch %x != %x", - reply.magic, OSSP_REPLY_MAGIC); - rc = -EINVAL; - goto fail; - } - - if (reply.result < 0) - goto out_unlock; - - if (reply.dout_size > dout_size) { - snprintf(reason, sizeof(reason), - "data out size overflow %zu > %zu", - reply.dout_size, dout_size); - rc = -EINVAL; - goto fail; - } - - dout_size = reply.dout_size; - if (dout_sizep) - *dout_sizep = dout_size; - - if ((rc = read_fill(os->cmd_fd, rarg, rarg_size)) < 0 || - (rc = read_fill(os->cmd_fd, dout, dout_size)) < 0) { - snprintf(reason, sizeof(reason), "can't read data out: %s", - strerror(-rc)); - goto fail; - } - -out_unlock: - dbg1_os(os, " completed, result=%d dout=%zu", - reply.result, dout_size); - return reply.result; - -fail: - warn_os(os, "communication with slave failed (%s)", reason); - os->dead = 1; - return rc; -} - -static ssize_t exec_cmd(struct ossp_stream *os, enum ossp_opcode opcode, - const void *carg, size_t carg_size, const void *din, size_t din_size, - void *rarg, size_t rarg_size, void *dout, size_t *dout_sizep, int fd) -{ - int is_mixer; - int i, j; - ssize_t ret, mret; - - /* mixer command is handled exlicitly below */ - is_mixer = opcode == OSSP_MIXER; - if (is_mixer) { - ret = -pthread_mutex_trylock(&os->cmd_mutex); - if (ret) - return ret; - } else { - pthread_mutex_lock(&os->cmd_mutex); - - ret = exec_cmd_intern(os, opcode, carg, carg_size, - din, din_size, rarg, rarg_size, - dout, dout_sizep, fd); - } - - /* lazy mixer handling */ - pthread_mutex_lock(&mixer_mutex); - - if (os->mixer_pending) { - struct ossp_mixer_arg marg; - repeat_mixer: - /* we have mixer command pending */ - memcpy(marg.vol, os->vol_set, sizeof(os->vol_set)); - memset(os->vol_set, -1, sizeof(os->vol_set)); - - pthread_mutex_unlock(&mixer_mutex); - mret = exec_cmd_intern(os, OSSP_MIXER, &marg, sizeof(marg), - NULL, 0, &marg, sizeof(marg), NULL, NULL, - -1); - pthread_mutex_lock(&mixer_mutex); - - /* was there mixer set request while executing mixer command? */ - for_each_vol(i, j) - if (os->vol_set[i][j] >= 0) - goto repeat_mixer; - - /* update internal mixer state */ - if (mret == 0) { - for_each_vol(i, j) { - if (marg.vol[i][j] >= 0) { - if (os->vol[i][j] != marg.vol[i][j]) - os->mixer->modify_counter++; - os->vol[i][j] = marg.vol[i][j]; - } - } - } - os->mixer_pending = 0; - } - - pthread_mutex_unlock(&os->cmd_mutex); - - /* - * mixer mutex must be released after cmd_mutex so that - * exec_mixer_cmd() can guarantee that mixer_pending flags - * will be handled immediately or when the currently - * in-progress command completes. - */ - pthread_mutex_unlock(&mixer_mutex); - - return is_mixer ? mret : ret; -} - -static ssize_t exec_simple_cmd(struct ossp_stream *os, - enum ossp_opcode opcode, void *carg, void *rarg) -{ - return exec_cmd(os, opcode, - carg, ossp_arg_sizes[opcode].carg_size, NULL, 0, - rarg, ossp_arg_sizes[opcode].rarg_size, NULL, NULL, -1); -} - -static int ioctl_prep_uarg(fuse_req_t req, void *in, size_t in_sz, void *out, - size_t out_sz, void *uarg, const void *in_buf, - size_t in_bufsz, size_t out_bufsz) -{ - struct iovec in_iov = { }, out_iov = { }; - int retry = 0; - - if (in) { - if (!in_bufsz) { - in_iov.iov_base = uarg; - in_iov.iov_len = in_sz; - retry = 1; - } else { - assert(in_bufsz == in_sz); - memcpy(in, in_buf, in_sz); - } - } - - if (out) { - if (!out_bufsz) { - out_iov.iov_base = uarg; - out_iov.iov_len = out_sz; - retry = 1; - } else - assert(out_bufsz == out_sz); - } - - if (retry) - fuse_reply_ioctl_retry(req, &in_iov, 1, &out_iov, 1); - - return retry; -} - -#define PREP_UARG(inp, outp) do { \ - if (ioctl_prep_uarg(req, (inp), sizeof(*(inp)), \ - (outp), sizeof(*(outp)), uarg, \ - in_buf, in_bufsz, out_bufsz)) \ - return; \ -} while (0) - -#define IOCTL_RETURN(result, outp) do { \ - if ((outp) != NULL) \ - fuse_reply_ioctl(req, result, (outp), sizeof(*(outp))); \ - else \ - fuse_reply_ioctl(req, result, NULL, 0); \ - return; \ -} while (0) - - -/*************************************************************************** - * Mixer implementation - */ - -static void put_mixer_real(struct ossp_mixer *mixer) -{ - if (!--mixer->refcnt) { - dbg0("DESTROY mixer(%d)", mixer->pgrp); - list_del_init(&mixer->link); - list_del_init(&mixer->delayed_put_link); - free(mixer); - nr_mixers--; - - /* - * If exit_on_idle, mixer for pgrp0 is touched during - * init and each stream has mixer attached. As mixers - * are destroyed after they have been idle for - * MIXER_PUT_DELAY seconds, we can use it for idle - * detection. Note that this might race with - * concurrent open. The race is inherent. - */ - if (exit_on_idle && !nr_mixers) { - info("idle, exiting"); - exit(0); - } - } -} - -static struct ossp_mixer *get_mixer(pid_t pgrp) -{ - struct ossp_mixer *mixer; - - pthread_mutex_lock(&mutex); - - /* is there a matching one? */ - mixer = find_mixer_locked(pgrp); - if (mixer) { - if (list_empty(&mixer->delayed_put_link)) - mixer->refcnt++; - else - list_del_init(&mixer->delayed_put_link); - goto out_unlock; - } - - /* reap delayed put list if there are too many mixers */ - while (nr_mixers > 2 * max_streams && - !list_empty(&mixer_delayed_put_head)) { - struct ossp_mixer *mixer = - list_first_entry(&mixer_delayed_put_head, - struct ossp_mixer, delayed_put_link); - - assert(mixer->refcnt == 1); - put_mixer_real(mixer); - } - - /* create a new one */ - mixer = calloc(1, sizeof(*mixer)); - if (!mixer) { - warn("failed to allocate mixer for %d", pgrp); - mixer = NULL; - goto out_unlock; - } - - mixer->pgrp = pgrp; - INIT_LIST_HEAD(&mixer->link); - INIT_LIST_HEAD(&mixer->delayed_put_link); - mixer->refcnt = 1; - memset(mixer->vol, -1, sizeof(mixer->vol)); - - list_add(&mixer->link, mixer_tbl_head(pgrp)); - nr_mixers++; - dbg0("CREATE mixer(%d)", pgrp); - -out_unlock: - pthread_mutex_unlock(&mutex); - return mixer; -} - -static void put_mixer(struct ossp_mixer *mixer) -{ - pthread_mutex_lock(&mutex); - - if (mixer) { - if (mixer->refcnt == 1) { - struct timespec ts; - - clock_gettime(CLOCK_REALTIME, &ts); - mixer->put_expires = ts.tv_sec + MIXER_PUT_DELAY; - list_add_tail(&mixer->delayed_put_link, - &mixer_delayed_put_head); - pthread_cond_signal(&mixer_delayed_put_cond); - } else - put_mixer_real(mixer); - } - - pthread_mutex_unlock(&mutex); -} - -static void *mixer_delayed_put_worker(void *arg) -{ - struct ossp_mixer *mixer; - struct timespec ts; - time_t now; - - pthread_mutex_lock(&mutex); -again: - clock_gettime(CLOCK_REALTIME, &ts); - now = ts.tv_sec; - - mixer = NULL; - while (!list_empty(&mixer_delayed_put_head)) { - mixer = list_first_entry(&mixer_delayed_put_head, - struct ossp_mixer, delayed_put_link); - - if (now <= mixer->put_expires) - break; - - assert(mixer->refcnt == 1); - put_mixer_real(mixer); - mixer = NULL; - } - - if (mixer) { - ts.tv_sec = mixer->put_expires + 1; - pthread_cond_timedwait(&mixer_delayed_put_cond, &mutex, &ts); - } else - pthread_cond_wait(&mixer_delayed_put_cond, &mutex); - - goto again; -} - -static void init_mixer_cmd(struct ossp_mixer_cmd *mxcmd, - struct ossp_mixer *mixer) -{ - memset(mxcmd, 0, sizeof(*mxcmd)); - memset(&mxcmd->set.vol, -1, sizeof(mxcmd->set.vol)); - mxcmd->mixer = mixer; - mxcmd->out_dir = -1; -} - -static int exec_mixer_cmd(struct ossp_mixer_cmd *mxcmd, struct ossp_stream *os) -{ - int i, j, rc; - - /* - * Set pending flags before trying to execute mixer command. - * Combined with lock release order in exec_cmd(), this - * guarantees that the mixer command will be executed - * immediately or when the current command completes. - */ - pthread_mutex_lock(&mixer_mutex); - os->mixer_pending = 1; - for_each_vol(i, j) - if (mxcmd->set.vol[i][j] >= 0) - os->vol_set[i][j] = mxcmd->set.vol[i][j]; - pthread_mutex_unlock(&mixer_mutex); - - rc = exec_simple_cmd(os, OSSP_MIXER, NULL, NULL); - if (rc >= 0) { - dbg0_os(os, "volume set=%d/%d:%d/%d get=%d/%d:%d/%d", - mxcmd->set.vol[PLAY][LEFT], mxcmd->set.vol[PLAY][RIGHT], - mxcmd->set.vol[REC][LEFT], mxcmd->set.vol[REC][RIGHT], - os->vol[PLAY][LEFT], os->vol[PLAY][RIGHT], - os->vol[REC][LEFT], os->vol[REC][RIGHT]); - } else if (rc != -EBUSY) - warn_ose(os, rc, "mixer command failed"); - - return rc; -} - -static void finish_mixer_cmd(struct ossp_mixer_cmd *mxcmd) -{ - struct ossp_mixer *mixer = mxcmd->mixer; - struct ossp_stream *os; - int dir = mxcmd->out_dir; - int vol[2][2] = { }; - int cnt[2][2] = { }; - int i, j; - - pthread_mutex_lock(&mixer_mutex); - - /* get volume of all streams attached to this mixer */ - pthread_mutex_lock(&mutex); - list_for_each_entry(os, os_pgrp_tbl_head(mixer->pgrp), pgrp_link) { - if (os->pgrp != mixer->pgrp) - continue; - for_each_vol(i, j) { - if (os->vol[i][j] < 0) - continue; - vol[i][j] += os->vol[i][j]; - cnt[i][j]++; - } - } - pthread_mutex_unlock(&mutex); - - /* calculate the summary volume values */ - for_each_vol(i, j) { - if (mxcmd->set.vol[i][j] >= 0) - vol[i][j] = mxcmd->set.vol[i][j]; - else if (cnt[i][j]) - vol[i][j] = vol[i][j] / cnt[i][j]; - else if (mixer->vol[i][j] >= 0) - vol[i][j] = mixer->vol[i][j]; - else - vol[i][j] = 100; - - vol[i][j] = min(max(0, vol[i][j]), 100); - } - - if (dir >= 0) - mxcmd->rvol = vol[dir][LEFT] | (vol[dir][RIGHT] << 8); - - pthread_mutex_unlock(&mixer_mutex); -} - -static void mixer_simple_ioctl(fuse_req_t req, struct ossp_mixer *mixer, - unsigned cmd, void *uarg, const void *in_buf, - size_t in_bufsz, size_t out_bufsz, - int *not_minep) -{ - const char *id = "OSS Proxy", *name = "Mixer"; - int i; - - switch (cmd) { - case SOUND_MIXER_INFO: { - struct mixer_info info = { }; - - PREP_UARG(NULL, &info); - strncpy(info.id, id, sizeof(info.id) - 1); - strncpy(info.name, name, sizeof(info.name) - 1); - info.modify_counter = mixer->modify_counter; - IOCTL_RETURN(0, &info); - } - - case SOUND_OLD_MIXER_INFO: { - struct _old_mixer_info info = { }; - - PREP_UARG(NULL, &info); - strncpy(info.id, id, sizeof(info.id) - 1); - strncpy(info.name, name, sizeof(info.name) - 1); - IOCTL_RETURN(0, &info); - } - - case OSS_GETVERSION: - i = SNDRV_OSS_VERSION; - goto puti; - case SOUND_MIXER_READ_DEVMASK: - case SOUND_MIXER_READ_STEREODEVS: - i = SOUND_MASK_PCM | SOUND_MASK_IGAIN; - goto puti; - case SOUND_MIXER_READ_CAPS: - i = SOUND_CAP_EXCL_INPUT; - goto puti; - case SOUND_MIXER_READ_RECMASK: - case SOUND_MIXER_READ_RECSRC: - i = SOUND_MASK_IGAIN; - goto puti; - puti: - PREP_UARG(NULL, &i); - IOCTL_RETURN(0, &i); - - case SOUND_MIXER_WRITE_RECSRC: - IOCTL_RETURN(0, NULL); - - default: - *not_minep = 1; - return; - } - assert(0); -} - -static void mixer_do_ioctl(fuse_req_t req, struct ossp_mixer *mixer, - unsigned cmd, void *uarg, const void *in_buf, - size_t in_bufsz, size_t out_bufsz) -{ - struct ossp_mixer_cmd mxcmd; - struct ossp_stream *os, **osa; - int not_mine = 0; - int slot = cmd & 0xff, dir; - int nr_os; - int i, rc; - - mixer_simple_ioctl(req, mixer, cmd, uarg, in_buf, in_bufsz, out_bufsz, - ¬_mine); - if (!not_mine) - return; - - rc = -ENXIO; - if (!(cmd & (SIOC_IN | SIOC_OUT))) - goto err; - - /* - * Okay, it's not one of the easy ones. Build mxcmd for - * actual volume control. - */ - if (cmd & SIOC_IN) - PREP_UARG(&i, &i); - else - PREP_UARG(NULL, &i); - - switch (slot) { - case SOUND_MIXER_PCM: - dir = PLAY; - break; - case SOUND_MIXER_IGAIN: - dir = REC; - break; - default: - i = 0; - IOCTL_RETURN(0, &i); - } - - init_mixer_cmd(&mxcmd, mixer); - - if (cmd & SIOC_IN) { - unsigned l, r; - - rc = -EINVAL; - l = i & 0xff; - r = (i >> 8) & 0xff; - if (l > 100 || r > 100) - goto err; - - mixer->vol[dir][LEFT] = mxcmd.set.vol[dir][LEFT] = l; - mixer->vol[dir][RIGHT] = mxcmd.set.vol[dir][RIGHT] = r; - } - mxcmd.out_dir = dir; - - /* - * Apply volume conrol - */ - /* acquire target streams */ - pthread_mutex_lock(&mutex); - osa = calloc(max_streams, sizeof(osa[0])); - if (!osa) { - pthread_mutex_unlock(&mutex); - rc = -ENOMEM; - goto err; - } - - nr_os = 0; - list_for_each_entry(os, os_pgrp_tbl_head(mixer->pgrp), pgrp_link) { - if (os->pgrp == mixer->pgrp) { - osa[nr_os++] = os; - os->refcnt++; - } - } - - pthread_mutex_unlock(&mutex); - - /* execute mxcmd for each stream and put it */ - for (i = 0; i < nr_os; i++) { - exec_mixer_cmd(&mxcmd, osa[i]); - put_os(osa[i]); - } - - finish_mixer_cmd(&mxcmd); - free(osa); - - IOCTL_RETURN(0, out_bufsz ? &mxcmd.rvol : NULL); - -err: - fuse_reply_err(req, -rc); -} - -static void mixer_open(fuse_req_t req, struct fuse_file_info *fi) -{ - pid_t pid = fuse_req_ctx(req)->pid, pgrp; - struct ossp_mixer *mixer; - int rc; - - rc = get_proc_self_info(pid, &pgrp, NULL, 0); - if (rc) { - err_e(rc, "get_proc_self_info(%d) failed", pid); - fuse_reply_err(req, -rc); - return; - } - - mixer = get_mixer(pgrp); - fi->fh = pgrp; - - if (mixer) - fuse_reply_open(req, fi); - else - fuse_reply_err(req, ENOMEM); -} - -static void mixer_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - struct fuse_file_info *fi, unsigned int flags, - const void *in_buf, size_t in_bufsz, size_t out_bufsz) -{ - struct ossp_mixer *mixer; - - mixer = find_mixer(fi->fh); - if (!mixer) { - fuse_reply_err(req, EBADF); - return; - } - - mixer_do_ioctl(req, mixer, signed_cmd, uarg, in_buf, in_bufsz, - out_bufsz); -} - -static void mixer_release(fuse_req_t req, struct fuse_file_info *fi) -{ - struct ossp_mixer *mixer; - - mixer = find_mixer(fi->fh); - if (mixer) { - put_mixer(mixer); - fuse_reply_err(req, 0); - } else - fuse_reply_err(req, EBADF); -} - - -/*************************************************************************** - * Stream implementation - */ - -static int alloc_os(size_t stream_size, size_t mmap_size, pid_t pid, uid_t pgrp, - uid_t uid, gid_t gid, int cmd_sock, - const int *notify, struct fuse_session *se, - struct ossp_stream **osp) -{ - struct ossp_uid_cnt *tmp_ucnt, *ucnt = NULL; - struct ossp_stream *os; - int rc; - - assert(stream_size >= sizeof(struct ossp_stream)); - os = calloc(1, stream_size); - if (!os) - return -ENOMEM; - - INIT_LIST_HEAD(&os->link); - INIT_LIST_HEAD(&os->pgrp_link); - INIT_LIST_HEAD(&os->notify_link); - os->refcnt = 1; - - rc = -pthread_mutex_init(&os->cmd_mutex, NULL); - if (rc) - goto err_free; - - rc = -pthread_mutex_init(&os->mmap_mutex, NULL); - if (rc) - goto err_destroy_cmd_mutex; - - pthread_mutex_lock(&mutex); - - list_for_each_entry(tmp_ucnt, &uid_cnt_list, link) - if (tmp_ucnt->uid == uid) { - ucnt = tmp_ucnt; - break; - } - if (!ucnt) { - rc = -ENOMEM; - ucnt = calloc(1, sizeof(*ucnt)); - if (!ucnt) - goto err_unlock; - ucnt->uid = uid; - list_add(&ucnt->link, &uid_cnt_list); - } - - rc = -EBUSY; - if (ucnt->nr_os + 1 > umax_streams) - goto err_unlock; - - /* everything looks fine, allocate id and init stream */ - rc = -EBUSY; - os->id = find_next_zero_bit(os_id_bitmap, max_streams, 0); - if (os->id >= max_streams) - goto err_unlock; - __set_bit(os->id, os_id_bitmap); - - os->cmd_fd = cmd_sock; - os->notify_tx = notify[1]; - os->notify_rx = notify[0]; - os->pid = pid; - os->pgrp = pgrp; - os->uid = uid; - os->gid = gid; - if (mmap_size) { - os->mmap_off = os->id * mmap_size; - os->mmap_size = mmap_size; - } - os->ucnt = ucnt; - os->se = se; - - memset(os->vol, -1, sizeof(os->vol)); - memset(os->vol_set, -1, sizeof(os->vol)); - - list_add(&os->link, os_tbl_head(os->id)); - list_add(&os->pgrp_link, os_pgrp_tbl_head(os->pgrp)); - - ucnt->nr_os++; - *osp = os; - pthread_mutex_unlock(&mutex); - return 0; - -err_unlock: - pthread_mutex_unlock(&mutex); - pthread_mutex_destroy(&os->mmap_mutex); -err_destroy_cmd_mutex: - pthread_mutex_destroy(&os->cmd_mutex); -err_free: - free(os); - return rc; -} - -static void shutdown_notification(struct ossp_stream *os) -{ - struct ossp_notify obituary = { .magic = OSSP_NOTIFY_MAGIC, - .opcode = OSSP_NOTIFY_OBITUARY }; - ssize_t ret; - - /* - * Shutdown notification for this stream. We politely ask - * notify_poller to shut the receive side down to avoid racing - * with it. - */ - while (os->notify_rx >= 0) { - ret = write(os->notify_tx, &obituary, sizeof(obituary)); - if (ret <= 0) { - if (ret == 0) - warn_os(os, "unexpected EOF on notify_tx"); - else if (errno != EPIPE) - warn_ose(os, -errno, - "unexpected error on notify_tx"); - close(os->notify_rx); - os->notify_rx = -1; - break; - } - - if (ret != sizeof(obituary)) - warn_os(os, "short transfer on notify_tx"); - pthread_cond_wait(¬ify_poller_kill_wait, &mutex); - } -} - -static void put_os(struct ossp_stream *os) -{ - if (!os) - return; - - pthread_mutex_lock(&mutex); - - assert(os->refcnt); - if (--os->refcnt) { - pthread_mutex_unlock(&mutex); - return; - } - - os->dead = 1; - shutdown_notification(os); - - dbg0_os(os, "DESTROY"); - - list_del_init(&os->link); - list_del_init(&os->pgrp_link); - list_del_init(&os->notify_link); - os->ucnt->nr_os--; - - pthread_mutex_unlock(&mutex); - - close(os->cmd_fd); - close(os->notify_tx); - put_mixer(os->mixer); - pthread_mutex_destroy(&os->cmd_mutex); - pthread_mutex_destroy(&os->mmap_mutex); - - pthread_mutex_lock(&mutex); - dbg1_os(os, "stream dead, requesting reaping"); - list_add_tail(&os->link, &slave_corpse_list); - pthread_cond_signal(&slave_reaper_wait); - pthread_mutex_unlock(&mutex); -} - -static void set_extra_env(pid_t pid) -{ - char procenviron[32]; - const int step = 1024; - char *data = malloc(step + 1); - int ofs = 0; - int fd; - int ret; - - if (!data) - return; - - sprintf(procenviron, "/proc/%d/environ", pid); - fd = open(procenviron, O_RDONLY); - if (fd < 0) - return; - - /* - * There should really be a 'read whole file to a newly allocated - * buffer' function. - */ - while ((ret = read(fd, data + ofs, step)) > 0) { - char *newdata; - ofs += ret; - newdata = realloc(data, ofs + step + 1); - if (!newdata) { - ret = -1; - break; - } - data = newdata; - } - if (ret == 0) { - char *ptr = data; - /* Append the extra 0 for end condition */ - data[ofs] = 0; - - while ((ret = strlen(ptr)) > 0) { - /* - * Copy all PULSE variables and DISPLAY so that - * ssh -X remotehost 'mplayer -ao oss' will work - */ - if (!strncmp(ptr, "DISPLAY=", 8) || - !strncmp(ptr, "PULSE_", 6)) - putenv(ptr); - ptr += ret + 1; - } - } - - free(data); - close(fd); -} - -static int create_os(const char *slave_path, - size_t stream_size, size_t mmap_size, - pid_t pid, pid_t pgrp, uid_t uid, gid_t gid, - struct fuse_session *se, struct ossp_stream **osp) -{ - static pthread_mutex_t create_mutex = PTHREAD_MUTEX_INITIALIZER; - int cmd_sock[2] = { -1, -1 }; - int notify_sock[2] = { -1, -1 }; - struct ossp_stream *os = NULL; - struct epoll_event ev = { }; - int i, rc; - - /* - * Only one thread can be creating a stream. This is to avoid - * leaking unwanted fds into slaves. - */ - pthread_mutex_lock(&create_mutex); - - /* prepare communication channels */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd_sock) || - socketpair(AF_UNIX, SOCK_STREAM, 0, notify_sock)) { - rc = -errno; - warn_e(rc, "failed to create slave command channel"); - goto close_all; - } - - if (fcntl(notify_sock[0], F_SETFL, O_NONBLOCK) < 0) { - rc = -errno; - warn_e(rc, "failed to set NONBLOCK on notify sock"); - goto close_all; - } - - /* - * Alloc stream which will be responsible for all server side - * resources from now on. - */ - rc = alloc_os(stream_size, mmap_size, pid, pgrp, uid, gid, cmd_sock[0], - notify_sock, se, &os); - if (rc) { - warn_e(rc, "failed to allocate stream for %d", pid); - goto close_all; - } - - rc = -ENOMEM; - os->mixer = get_mixer(pgrp); - if (!os->mixer) - goto put_os; - - /* - * Register notification. If successful, notify_poller has - * custody of notify_rx fd. - */ - pthread_mutex_lock(&mutex); - list_add(&os->notify_link, os_notify_tbl_head(os->notify_rx)); - pthread_mutex_unlock(&mutex); - - ev.events = EPOLLIN; - ev.data.fd = notify_sock[0]; - if (epoll_ctl(notify_epfd, EPOLL_CTL_ADD, notify_sock[0], &ev)) { - /* - * Without poller watching this notify sock, poller - * shutdown sequence in shutdown_notification() can't - * be used. Kill notification rx manually. - */ - rc = -errno; - warn_ose(os, rc, "failed to add notify epoll"); - close(os->notify_rx); - os->notify_rx = -1; - goto put_os; - } - - /* start slave */ - os->slave_pid = fork(); - if (os->slave_pid < 0) { - rc = -errno; - warn_ose(os, rc, "failed to fork slave"); - goto put_os; - } - - if (os->slave_pid == 0) { - /* child */ - char id_str[2][16], fd_str[3][16]; - char mmap_off_str[32], mmap_size_str[32]; - char log_str[16], slave_path_copy[PATH_MAX]; - char *argv[] = { slave_path_copy, "-u", id_str[0], - "-g", id_str[1], "-c", fd_str[0], - "-n", fd_str[1], "-m", fd_str[2], - "-o", mmap_off_str, "-s", mmap_size_str, - "-l", log_str, NULL, NULL }; - struct passwd *pwd; - - /* drop stuff we don't need */ - if (close(cmd_sock[0]) || close(notify_sock[0])) - fatal_e(-errno, "failed to close server pipe fds"); - -#ifdef OSSP_MMAP - if (!mmap_size) - close(fuse_mmap_fd(se)); -#endif - - clearenv(); - pwd = getpwuid(os->uid); - if (pwd) { - setenv("LOGNAME", pwd->pw_name, 1); - setenv("USER", pwd->pw_name, 1); - setenv("HOME", pwd->pw_dir, 1); - } - /* Set extra environment variables from the caller */ - set_extra_env(pid); - - /* prep and exec */ - slave_path_copy[sizeof(slave_path_copy) - 1] = '\0'; - strncpy(slave_path_copy, slave_path, sizeof(slave_path_copy) - 1); - if (slave_path_copy[sizeof(slave_path_copy) - 1] != '\0') { - rc = -errno; - err_ose(os, rc, "slave path too long"); - goto child_fail; - } - - snprintf(id_str[0], sizeof(id_str[0]), "%d", os->uid); - snprintf(id_str[1], sizeof(id_str[0]), "%d", os->gid); - snprintf(fd_str[0], sizeof(fd_str[0]), "%d", cmd_sock[1]); - snprintf(fd_str[1], sizeof(fd_str[1]), "%d", notify_sock[1]); - snprintf(fd_str[2], sizeof(fd_str[2]), "%d", -#ifdef OSSP_MMAP - mmap_size ? fuse_mmap_fd(se) : -#endif - -1); - snprintf(mmap_off_str, sizeof(mmap_off_str), "0x%llx", - (unsigned long long)os->mmap_off); - snprintf(mmap_size_str, sizeof(mmap_size_str), "0x%zx", - mmap_size); - snprintf(log_str, sizeof(log_str), "%d", ossp_log_level); - if (ossp_log_timestamp) - argv[ARRAY_SIZE(argv) - 2] = "-t"; - - execv(slave_path, argv); - rc = -errno; - err_ose(os, rc, "execv failed for <%d>", pid); - child_fail: - _exit(1); - } - - /* turn on CLOEXEC on all server side fds */ - if (fcntl(os->cmd_fd, F_SETFD, FD_CLOEXEC) < 0 || - fcntl(os->notify_tx, F_SETFD, FD_CLOEXEC) < 0 || - fcntl(os->notify_rx, F_SETFD, FD_CLOEXEC) < 0) { - rc = -errno; - err_ose(os, rc, "failed to set CLOEXEC on server side fds"); - goto put_os; - } - - dbg0_os(os, "CREATE slave=%d %s", os->slave_pid, slave_path); - dbg0_os(os, " client=%d cmd=%d:%d notify=%d:%d mmap=%d:0x%llx:%zu", - pid, cmd_sock[0], cmd_sock[1], notify_sock[0], notify_sock[1], -#ifdef OSSP_MMAP - os->mmap_size ? fuse_mmap_fd(se) : -#endif - -1, - (unsigned long long)os->mmap_off, os->mmap_size); - - *osp = os; - rc = 0; - goto close_client_fds; - -put_os: - put_os(os); -close_client_fds: - close(cmd_sock[1]); - pthread_mutex_unlock(&create_mutex); - return rc; - -close_all: - for (i = 0; i < 2; i++) { - close(cmd_sock[i]); - close(notify_sock[i]); - } - pthread_mutex_unlock(&create_mutex); - return rc; -} - -static void dsp_open_common(fuse_req_t req, struct fuse_file_info *fi, - struct fuse_session *se) -{ - const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req); - struct ossp_dsp_open_arg arg = { }; - struct ossp_stream *os = NULL; - struct ossp_mixer *mixer; - struct ossp_dsp_stream *dsps; - struct ossp_mixer_cmd mxcmd; - pid_t pgrp; - ssize_t ret; - - ret = get_proc_self_info(fuse_ctx->pid, &pgrp, NULL, 0); - if (ret) { - err_e(ret, "get_proc_self_info(%d) failed", fuse_ctx->pid); - goto err; - } - - ret = create_os(dsp_slave_path, sizeof(*dsps), DSPS_MMAP_SIZE, - fuse_ctx->pid, pgrp, fuse_ctx->uid, fuse_ctx->gid, - se, &os); - if (ret) - goto err; - dsps = os_to_dsps(os); - mixer = os->mixer; - - switch (fi->flags & O_ACCMODE) { - case O_WRONLY: - dsps->rw |= 1 << PLAY; - break; - case O_RDONLY: - dsps->rw |= 1 << REC; - break; - case O_RDWR: - dsps->rw |= (1 << PLAY) | (1 << REC); - break; - default: - assert(0); - } - - arg.flags = fi->flags; - arg.opener_pid = os->pid; - ret = exec_simple_cmd(&dsps->os, OSSP_DSP_OPEN, &arg, NULL); - if (ret < 0) { - put_os(os); - goto err; - } - - memcpy(os->vol, mixer->vol, sizeof(os->vol)); - if (os->vol[PLAY][0] >= 0 || os->vol[REC][0] >= 0) { - init_mixer_cmd(&mxcmd, mixer); - memcpy(mxcmd.set.vol, os->vol, sizeof(os->vol)); - exec_mixer_cmd(&mxcmd, os); - finish_mixer_cmd(&mxcmd); - } - - fi->direct_io = 1; - fi->nonseekable = 1; - fi->fh = os->id; - - fuse_reply_open(req, fi); - return; - -err: - fuse_reply_err(req, -ret); -} - -static void dsp_open(fuse_req_t req, struct fuse_file_info *fi) -{ - dsp_open_common(req, fi, dsp_se); -} - -static void adsp_open(fuse_req_t req, struct fuse_file_info *fi) -{ - dsp_open_common(req, fi, adsp_se); -} - -static void dsp_release(fuse_req_t req, struct fuse_file_info *fi) -{ - struct ossp_stream *os; - - os = find_os(fi->fh); - if (os) { - put_os(os); - fuse_reply_err(req, 0); - } else - fuse_reply_err(req, EBADF); -} - -static void dsp_read(fuse_req_t req, size_t size, off_t off, - struct fuse_file_info *fi) -{ - struct ossp_dsp_rw_arg arg = { }; - struct ossp_stream *os; - struct ossp_dsp_stream *dsps; - void *buf = NULL; - ssize_t ret; - - ret = -EBADF; - os = find_os(fi->fh); - if (!os) - goto out; - dsps = os_to_dsps(os); - - ret = -EINVAL; - if (!(dsps->rw & (1 << REC))) - goto out; - - ret = -ENXIO; - if (dsps->mmapped) - goto out; - - ret = -ENOMEM; - buf = malloc(size); - if (!buf) - goto out; - - arg.nonblock = (fi->flags & O_NONBLOCK) || dsps->nonblock; - - ret = exec_cmd(os, OSSP_DSP_READ, &arg, sizeof(arg), - NULL, 0, NULL, 0, buf, &size, -1); -out: - if (ret >= 0) - fuse_reply_buf(req, buf, size); - else - fuse_reply_err(req, -ret); - - free(buf); -} - -static void dsp_write(fuse_req_t req, const char *buf, size_t size, off_t off, - struct fuse_file_info *fi) -{ - struct ossp_dsp_rw_arg arg = { }; - struct ossp_stream *os; - struct ossp_dsp_stream *dsps; - ssize_t ret; - - ret = -EBADF; - os = find_os(fi->fh); - if (!os) - goto out; - dsps = os_to_dsps(os); - - ret = -EINVAL; - if (!(dsps->rw & (1 << PLAY))) - goto out; - - ret = -ENXIO; - if (dsps->mmapped) - goto out; - - arg.nonblock = (fi->flags & O_NONBLOCK) || dsps->nonblock; - - ret = exec_cmd(os, OSSP_DSP_WRITE, &arg, sizeof(arg), - buf, size, NULL, 0, NULL, NULL, -1); -out: - if (ret >= 0) - fuse_reply_write(req, ret); - else - fuse_reply_err(req, -ret); -} - -static void dsp_poll(fuse_req_t req, struct fuse_file_info *fi, - struct fuse_pollhandle *ph) -{ - int notify = ph != NULL; - unsigned revents = 0; - struct ossp_stream *os; - ssize_t ret; - - ret = -EBADF; - os = find_os(fi->fh); - if (!os) - goto out; - - if (ph) { - pthread_mutex_lock(&mutex); - if (os->ph) - fuse_pollhandle_destroy(os->ph); - os->ph = ph; - pthread_mutex_unlock(&mutex); - } - - ret = exec_simple_cmd(os, OSSP_DSP_POLL, ¬ify, &revents); -out: - if (ret >= 0) - fuse_reply_poll(req, revents); - else - fuse_reply_err(req, -ret); -} - -static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg, - struct fuse_file_info *fi, unsigned int flags, - const void *in_buf, size_t in_bufsz, size_t out_bufsz) -{ - /* some ioctl constants are long and has the highest bit set */ - unsigned cmd = signed_cmd; - struct ossp_stream *os; - struct ossp_dsp_stream *dsps; - enum ossp_opcode op; - ssize_t ret; - int i; - - ret = -EBADF; - os = find_os(fi->fh); - if (!os) - goto err; - dsps = os_to_dsps(os); - - /* mixer commands are allowed on DSP devices */ - if (((cmd >> 8) & 0xff) == 'M') { - mixer_do_ioctl(req, os->mixer, cmd, uarg, in_buf, in_bufsz, - out_bufsz); - return; - } - - /* and the rest */ - switch (cmd) { - case OSS_GETVERSION: - i = SNDRV_OSS_VERSION; - PREP_UARG(NULL, &i); - IOCTL_RETURN(0, &i); - - case SNDCTL_DSP_GETCAPS: - i = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | -#ifdef OSSP_MMAP - DSP_CAP_MMAP | -#endif - DSP_CAP_MULTI; - PREP_UARG(NULL, &i); - IOCTL_RETURN(0, &i); - - case SNDCTL_DSP_NONBLOCK: - dsps->nonblock = 1; - ret = 0; - IOCTL_RETURN(0, NULL); - - case SNDCTL_DSP_RESET: op = OSSP_DSP_RESET; goto nd; - case SNDCTL_DSP_SYNC: op = OSSP_DSP_SYNC; goto nd; - case SNDCTL_DSP_POST: op = OSSP_DSP_POST; goto nd; - nd: - ret = exec_simple_cmd(&dsps->os, op, NULL, NULL); - if (ret) - goto err; - IOCTL_RETURN(0, NULL); - - case SOUND_PCM_READ_RATE: op = OSSP_DSP_GET_RATE; goto ri; - case SOUND_PCM_READ_BITS: op = OSSP_DSP_GET_FORMAT; goto ri; - case SOUND_PCM_READ_CHANNELS: op = OSSP_DSP_GET_CHANNELS; goto ri; - case SNDCTL_DSP_GETBLKSIZE: op = OSSP_DSP_GET_BLKSIZE; goto ri; - case SNDCTL_DSP_GETFMTS: op = OSSP_DSP_GET_FORMATS; goto ri; - case SNDCTL_DSP_GETTRIGGER: op = OSSP_DSP_GET_TRIGGER; goto ri; - ri: - PREP_UARG(NULL, &i); - ret = exec_simple_cmd(&dsps->os, op, NULL, &i); - if (ret) - goto err; - IOCTL_RETURN(0, &i); - - case SNDCTL_DSP_SPEED: op = OSSP_DSP_SET_RATE; goto wi; - case SNDCTL_DSP_SETFMT: op = OSSP_DSP_SET_FORMAT; goto wi; - case SNDCTL_DSP_CHANNELS: op = OSSP_DSP_SET_CHANNELS; goto wi; - case SNDCTL_DSP_SUBDIVIDE: op = OSSP_DSP_SET_SUBDIVISION; goto wi; - wi: - PREP_UARG(&i, &i); - ret = exec_simple_cmd(&dsps->os, op, &i, &i); - if (ret) - goto err; - IOCTL_RETURN(0, &i); - - case SNDCTL_DSP_STEREO: - PREP_UARG(NULL, &i); - i = 2; - ret = exec_simple_cmd(&dsps->os, OSSP_DSP_SET_CHANNELS, &i, &i); - i--; - if (ret) - goto err; - IOCTL_RETURN(0, &i); - - case SNDCTL_DSP_SETFRAGMENT: - PREP_UARG(&i, NULL); - ret = exec_simple_cmd(&dsps->os, - OSSP_DSP_SET_FRAGMENT, &i, NULL); - if (ret) - goto err; - IOCTL_RETURN(0, NULL); - - case SNDCTL_DSP_SETTRIGGER: - PREP_UARG(&i, NULL); - ret = exec_simple_cmd(&dsps->os, - OSSP_DSP_SET_TRIGGER, &i, NULL); - if (ret) - goto err; - IOCTL_RETURN(0, NULL); - - case SNDCTL_DSP_GETOSPACE: - case SNDCTL_DSP_GETISPACE: { - struct audio_buf_info info; - - ret = -EINVAL; - if (cmd == SNDCTL_DSP_GETOSPACE) { - if (!(dsps->rw & (1 << PLAY))) - goto err; - op = OSSP_DSP_GET_OSPACE; - } else { - if (!(dsps->rw & (1 << REC))) - goto err; - op = OSSP_DSP_GET_ISPACE; - } - - PREP_UARG(NULL, &info); - ret = exec_simple_cmd(&dsps->os, op, NULL, &info); - if (ret) - goto err; - IOCTL_RETURN(0, &info); - } - - case SNDCTL_DSP_GETOPTR: - case SNDCTL_DSP_GETIPTR: { - struct count_info info; - - op = cmd == SNDCTL_DSP_GETOPTR ? OSSP_DSP_GET_OPTR - : OSSP_DSP_GET_IPTR; - PREP_UARG(NULL, &info); - ret = exec_simple_cmd(&dsps->os, op, NULL, &info); - if (ret) - goto err; - IOCTL_RETURN(0, &info); - } - - case SNDCTL_DSP_GETODELAY: - PREP_UARG(NULL, &i); - i = 0; - ret = exec_simple_cmd(&dsps->os, OSSP_DSP_GET_ODELAY, NULL, &i); - IOCTL_RETURN(ret, &i); /* always copy out result, 0 on err */ - - case SOUND_PCM_WRITE_FILTER: - case SOUND_PCM_READ_FILTER: - ret = -EIO; - goto err; - - case SNDCTL_DSP_MAPINBUF: - case SNDCTL_DSP_MAPOUTBUF: - ret = -EINVAL; - goto err; - - case SNDCTL_DSP_SETSYNCRO: - case SNDCTL_DSP_SETDUPLEX: - case SNDCTL_DSP_PROFILE: - IOCTL_RETURN(0, NULL); - - default: - warn_os(os, "unknown ioctl 0x%x", cmd); - ret = -EINVAL; - goto err; - } - assert(0); /* control shouldn't reach here */ -err: - fuse_reply_err(req, -ret); -} - -#ifdef OSSP_MMAP -static int dsp_mmap_dir(int prot) -{ - if (!(prot & PROT_WRITE)) - return REC; - return PLAY; -} - -static void dsp_mmap(fuse_req_t req, void *addr, size_t len, int prot, - int flags, off_t offset, struct fuse_file_info *fi, - uint64_t mh) -{ - int dir = dsp_mmap_dir(prot); - struct ossp_dsp_mmap_arg arg = { }; - struct ossp_stream *os; - struct ossp_dsp_stream *dsps; - ssize_t ret; - - os = find_os(fi->fh); - if (!os) { - fuse_reply_err(req, EBADF); - return; - } - dsps = os_to_dsps(os); - - if (!os->mmap_off || len > os->mmap_size / 2) { - fuse_reply_err(req, EINVAL); - return; - } - - pthread_mutex_lock(&os->mmap_mutex); - - ret = -EBUSY; - if (dsps->mmapped & (1 << dir)) - goto out_unlock; - - arg.dir = dir; - arg.size = len; - - ret = exec_simple_cmd(os, OSSP_DSP_MMAP, &arg, NULL); - if (ret == 0) - dsps->mmapped |= 1 << dir; - -out_unlock: - pthread_mutex_unlock(&os->mmap_mutex); - - if (ret == 0) - fuse_reply_mmap(req, os->mmap_off + dir * os->mmap_size / 2, 0); - else - fuse_reply_err(req, -ret); -} - -static void dsp_munmap(fuse_req_t req, size_t len, struct fuse_file_info *fi, - off_t offset, uint64_t mh) -{ - struct ossp_stream *os; - struct ossp_dsp_stream *dsps; - int dir, rc; - - os = find_os(fi->fh); - if (!os) - goto out; - dsps = os_to_dsps(os); - - pthread_mutex_lock(&os->mmap_mutex); - - for (dir = 0; dir < 2; dir++) - if (offset == os->mmap_off + dir * os->mmap_size / 2) - break; - if (dir == 2 || len > os->mmap_size / 2) { - warn_os(os, "invalid munmap request " - "offset=%llu len=%zu mmapped=0x%x", - (unsigned long long)offset, len, dsps->mmapped); - goto out_unlock; - } - - rc = exec_simple_cmd(os, OSSP_DSP_MUNMAP, &dir, NULL); - if (rc) - warn_ose(os, rc, "MUNMAP failed for dir=%d", dir); - - dsps->mmapped &= ~(1 << dir); - -out_unlock: - pthread_mutex_unlock(&os->mmap_mutex); -out: - fuse_reply_none(req); -} -#endif - - -/*************************************************************************** - * Notify poller - */ - -static void *notify_poller(void *arg) -{ - struct epoll_event events[1024]; - int i, nfds; - -repeat: - nfds = epoll_wait(notify_epfd, events, ARRAY_SIZE(events), -1); - for (i = 0; i < nfds; i++) { - int do_notify = 0; - struct ossp_stream *os; - struct ossp_notify notify; - ssize_t ret; - - os = find_os_by_notify_rx(events[i].data.fd); - if (!os) { - err("can't find stream for notify_rx fd %d", - events[i].data.fd); - epoll_ctl(notify_epfd, EPOLL_CTL_DEL, events[i].data.fd, - NULL); - /* we don't know what's going on, don't close the fd */ - continue; - } - - while ((ret = read(os->notify_rx, - ¬ify, sizeof(notify))) > 0) { - if (os->dead) - continue; - if (ret != sizeof(notify)) { - warn_os(os, "short read on notify_rx (%zu, " - "expected %zu), killing the stream", - ret, sizeof(notify)); - os->dead = 1; - break; - } - if (notify.magic != OSSP_NOTIFY_MAGIC) { - warn_os(os, "invalid magic on notification, " - "killing the stream"); - os->dead = 1; - break; - } - - if (notify.opcode >= OSSP_NR_NOTIFY_OPCODES) - goto unknown; - - dbg1_os(os, "NOTIFY %s", ossp_notify_str[notify.opcode]); - - switch (notify.opcode) { - case OSSP_NOTIFY_POLL: - do_notify = 1; - break; - case OSSP_NOTIFY_OBITUARY: - os->dead = 1; - break; - case OSSP_NOTIFY_VOLCHG: - pthread_mutex_lock(&mixer_mutex); - os->mixer->modify_counter++; - pthread_mutex_unlock(&mixer_mutex); - break; - default: - unknown: - warn_os(os, "unknown notification %d", - notify.opcode); - } - } - if (ret == 0) - os->dead = 1; - else if (ret < 0 && errno != EAGAIN) { - warn_ose(os, -errno, "read fail on notify fd"); - os->dead = 1; - } - - if (!do_notify && !os->dead) - continue; - - pthread_mutex_lock(&mutex); - - if (os->ph) { - fuse_lowlevel_notify_poll(os->ph); - fuse_pollhandle_destroy(os->ph); - os->ph = NULL; - } - - if (os->dead) { - dbg0_os(os, "removing %d from notify poll list", - os->notify_rx); - epoll_ctl(notify_epfd, EPOLL_CTL_DEL, os->notify_rx, - NULL); - close(os->notify_rx); - os->notify_rx = -1; - pthread_cond_broadcast(¬ify_poller_kill_wait); - } - - pthread_mutex_unlock(&mutex); - } - goto repeat; -} - - -/*************************************************************************** - * Slave corpse reaper - */ - -static void *slave_reaper(void *arg) -{ - struct ossp_stream *os; - int status; - pid_t pid; - - pthread_mutex_lock(&mutex); -repeat: - while (list_empty(&slave_corpse_list)) - pthread_cond_wait(&slave_reaper_wait, &mutex); - - os = list_first_entry(&slave_corpse_list, struct ossp_stream, link); - list_del_init(&os->link); - - pthread_mutex_unlock(&mutex); - - do { - pid = waitpid(os->slave_pid, &status, 0); - } while (pid < 0 && errno == EINTR); - - if (pid < 0) { - if (errno == ECHILD) - warn_ose(os, -errno, "slave %d already gone?", - os->slave_pid); - else - fatal_e(-errno, "waitpid(%d) failed", os->slave_pid); - } - - pthread_mutex_lock(&mutex); - - dbg1_os(os, "slave %d reaped", os->slave_pid); - __clear_bit(os->id, os_id_bitmap); - free(os); - - goto repeat; -} - - -/*************************************************************************** - * Stuff to bind and start everything - */ - -static void ossp_daemonize(void) -{ - int fd, pfd[2]; - pid_t pid; - ssize_t ret; - int err; - - fd = open("/dev/null", O_RDWR); - if (fd >= 0) { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) - close(fd); - } - - if (pipe(pfd)) - fatal_e(-errno, "failed to create pipe for init wait"); - - if (fcntl(pfd[0], F_SETFD, FD_CLOEXEC) < 0 || - fcntl(pfd[1], F_SETFD, FD_CLOEXEC) < 0) - fatal_e(-errno, "failed to set CLOEXEC on init wait pipe"); - - pid = fork(); - if (pid < 0) - fatal_e(-errno, "failed to fork for daemon"); - - if (pid == 0) { - close(pfd[0]); - init_wait_fd = pfd[1]; - - /* be evil, my child */ - chdir("/"); - setsid(); - return; - } - - /* wait for init completion and pass over success indication */ - close(pfd[1]); - - do { - ret = read(pfd[0], &err, sizeof(err)); - } while (ret < 0 && errno == EINTR); - - if (ret == sizeof(err) && err == 0) - exit(0); - - fatal("daemon init failed ret=%zd err=%d", ret, err); - exit(1); -} - -static void ossp_init_done(void *userdata) -{ - /* init complete, notify parent if it's waiting */ - if (init_wait_fd >= 0) { - ssize_t ret; - int err = 0; - - ret = write(init_wait_fd, &err, sizeof(err)); - if (ret != sizeof(err)) - fatal_e(-errno, "failed to notify init completion, " - "ret=%zd", ret); - close(init_wait_fd); - init_wait_fd = -1; - } -} - -static const struct cuse_lowlevel_ops mixer_ops = { - .open = mixer_open, - .release = mixer_release, - .ioctl = mixer_ioctl, -}; - -static const struct cuse_lowlevel_ops dsp_ops = { - .init_done = ossp_init_done, - .open = dsp_open, - .release = dsp_release, - .read = dsp_read, - .write = dsp_write, - .poll = dsp_poll, - .ioctl = dsp_ioctl, -#ifdef OSSP_MMAP - .mmap = dsp_mmap, - .munmap = dsp_munmap, -#endif -}; - -static const struct cuse_lowlevel_ops adsp_ops = { - .open = adsp_open, - .release = dsp_release, - .read = dsp_read, - .write = dsp_write, - .poll = dsp_poll, - .ioctl = dsp_ioctl, -#ifdef OSSP_MMAP - .mmap = dsp_mmap, - .munmap = dsp_munmap, -#endif -}; - -static const char *usage = -"usage: osspd [options]\n" -"\n" -"options:\n" -" --help print this help message\n" -" --dsp=NAME DSP device name (default dsp)\n" -" --dsp-maj=MAJ DSP device major number (default 14)\n" -" --dsp-min=MIN DSP device minor number (default 3)\n" -" --adsp=NAME Aux DSP device name (default adsp, blank to disable)\n" -" --adsp-maj=MAJ Aux DSP device major number (default 14)\n" -" --adsp-min=MIN Aux DSP device minor number (default 12)\n" -" --mixer=NAME mixer device name (default mixer, blank to disable)\n" -" --mixer-maj=MAJ mixer device major number (default 14)\n" -" --mixer-min=MIN mixer device minor number (default 0)\n" -" --max=MAX maximum number of open streams (default 256)\n" -" --umax=MAX maximum number of open streams per UID (default --max)\n" -" --exit-on-idle exit if idle\n" -" --dsp-slave=PATH DSP slave (default ossp-padsp in the same dir)\n" -" --log=LEVEL log level (0..6)\n" -" --timestamp timestamp log messages\n" -" -v increase verbosity, can be specified multiple times\n" -" -f Run in foreground (don't daemonize)\n" -"\n"; - -struct ossp_param { - char *dsp_name; - unsigned dsp_major; - unsigned dsp_minor; - char *adsp_name; - unsigned adsp_major; - unsigned adsp_minor; - char *mixer_name; - unsigned mixer_major; - unsigned mixer_minor; - unsigned max_streams; - unsigned umax_streams; - char *dsp_slave_path; - unsigned log_level; - int exit_on_idle; - int timestamp; - int fg; - int help; -}; - -#define OSSP_OPT(t, p) { t, offsetof(struct ossp_param, p), 1 } - -static const struct fuse_opt ossp_opts[] = { - OSSP_OPT("--dsp=%s", dsp_name), - OSSP_OPT("--dsp-maj=%u", dsp_major), - OSSP_OPT("--dsp-min=%u", dsp_minor), - OSSP_OPT("--adsp=%s", adsp_name), - OSSP_OPT("--adsp-maj=%u", adsp_major), - OSSP_OPT("--adsp-min=%u", adsp_minor), - OSSP_OPT("--mixer=%s", mixer_name), - OSSP_OPT("--mixer-maj=%u", mixer_major), - OSSP_OPT("--mixer-min=%u", mixer_minor), - OSSP_OPT("--max=%u", max_streams), - OSSP_OPT("--umax=%u", umax_streams), - OSSP_OPT("--exit-on-idle", exit_on_idle), - OSSP_OPT("--dsp-slave=%s", dsp_slave_path), - OSSP_OPT("--timestamp", timestamp), - OSSP_OPT("--log=%u", log_level), - OSSP_OPT("-f", fg), - FUSE_OPT_KEY("-h", 0), - FUSE_OPT_KEY("--help", 0), - FUSE_OPT_KEY("-v", 1), - FUSE_OPT_END -}; - -static struct fuse_session *setup_ossp_cuse(const struct cuse_lowlevel_ops *ops, - const char *name, int major, - int minor, int argc, char **argv) -{ - char name_buf[128]; - const char *bufp = name_buf; - struct cuse_info ci = { .dev_major = major, .dev_minor = minor, - .dev_info_argc = 1, .dev_info_argv = &bufp, - .flags = CUSE_UNRESTRICTED_IOCTL }; - struct fuse_session *se; - int fd; - - snprintf(name_buf, sizeof(name_buf), "DEVNAME=%s", name); - - se = cuse_lowlevel_setup(argc, argv, &ci, ops, NULL, NULL); - if (!se) { - err("failed to setup %s CUSE", name); - return NULL; - } - - fd = fuse_chan_fd(fuse_session_next_chan(se, NULL)); - if ( -#ifdef OSSP_MMAP - fd != fuse_mmap_fd(se) && -#endif - fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { - err_e(-errno, "failed to set CLOEXEC on %s CUSE fd", name); - cuse_lowlevel_teardown(se); - return NULL; - } - - return se; -} - -static void *cuse_worker(void *arg) -{ - struct fuse_session *se = arg; - int rc; - - rc = fuse_session_loop_mt(se); - cuse_lowlevel_teardown(se); - - return (void *)(unsigned long)rc; -} - -static int process_arg(void *data, const char *arg, int key, - struct fuse_args *outargs) -{ - struct ossp_param *param = data; - - switch (key) { - case 0: - fprintf(stderr, usage); - param->help = 1; - return 0; - case 1: - param->log_level++; - return 0; - } - return 1; -} - -int main(int argc, char **argv) -{ - static struct ossp_param param = { - .dsp_name = DFL_DSP_NAME, - .dsp_major = DFL_DSP_MAJOR, .dsp_minor = DFL_DSP_MINOR, - .adsp_name = DFL_ADSP_NAME, - .adsp_major = DFL_ADSP_MAJOR, .adsp_minor = DFL_ADSP_MINOR, - .mixer_name = DFL_MIXER_NAME, - .mixer_major = DFL_MIXER_MAJOR, .mixer_minor = DFL_MIXER_MINOR, - .max_streams = DFL_MAX_STREAMS, - }; - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); - char path_buf[PATH_MAX], *dir; - char adsp_buf[64] = "", mixer_buf[64] = ""; - struct sigaction sa; - struct stat stat_buf; - ssize_t ret; - unsigned u; - - snprintf(ossp_log_name, sizeof(ossp_log_name), "osspd"); - param.log_level = ossp_log_level; - - if (fuse_opt_parse(&args, ¶m, ossp_opts, process_arg)) - fatal("failed to parse arguments"); - - if (param.help) - return 0; - - max_streams = param.max_streams; - hashtbl_size = max_streams / 2 + 13; - - umax_streams = max_streams; - if (param.umax_streams) - umax_streams = param.umax_streams; - if (param.log_level > OSSP_LOG_MAX) - param.log_level = OSSP_LOG_MAX; - if (!param.fg) - param.log_level = -param.log_level; - ossp_log_level = param.log_level; - ossp_log_timestamp = param.timestamp; - - if (!param.fg) - ossp_daemonize(); - - /* daemonization already handled, prevent forking inside FUSE */ - fuse_opt_add_arg(&args, "-f"); - - info("OSS Proxy v%s (C) 2008-2010 by Tejun Heo ", - OSSP_VERSION); - - /* ignore stupid SIGPIPEs */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_IGN; - if (sigaction(SIGPIPE, &sa, NULL)) - fatal_e(-errno, "failed to ignore SIGPIPE"); - - /* determine slave path and check for availability */ - ret = readlink("/proc/self/exe", path_buf, PATH_MAX - 1); - if (ret < 0) - fatal_e(-errno, "failed to determine executable path"); - path_buf[ret] = '\0'; - dir = dirname(path_buf); - - if (param.dsp_slave_path) { - strncpy(dsp_slave_path, param.dsp_slave_path, PATH_MAX - 1); - dsp_slave_path[PATH_MAX - 1] = '\0'; - } else { - ret = snprintf(dsp_slave_path, PATH_MAX, "%s/%s", - dir, "ossp-padsp"); - if (ret >= PATH_MAX) - fatal("dsp slave pathname too long"); - } - - if (stat(dsp_slave_path, &stat_buf)) - fatal_e(-errno, "failed to stat %s", dsp_slave_path); - if (!S_ISREG(stat_buf.st_mode) || !(stat_buf.st_mode & 0444)) - fatal("%s is not executable", dsp_slave_path); - - /* allocate tables */ - os_id_bitmap = calloc(BITS_TO_LONGS(max_streams), sizeof(long)); - mixer_tbl = calloc(hashtbl_size, sizeof(mixer_tbl[0])); - os_tbl = calloc(hashtbl_size, sizeof(os_tbl[0])); - os_pgrp_tbl = calloc(hashtbl_size, sizeof(os_pgrp_tbl[0])); - os_notify_tbl = calloc(hashtbl_size, sizeof(os_notify_tbl[0])); - if (!os_id_bitmap || !mixer_tbl || !os_tbl || !os_pgrp_tbl || - !os_notify_tbl) - fatal("failed to allocate stream hash tables"); - for (u = 0; u < hashtbl_size; u++) { - INIT_LIST_HEAD(&mixer_tbl[u]); - INIT_LIST_HEAD(&os_tbl[u]); - INIT_LIST_HEAD(&os_pgrp_tbl[u]); - INIT_LIST_HEAD(&os_notify_tbl[u]); - } - __set_bit(0, os_id_bitmap); /* don't use id 0 */ - - /* create mixer delayed reference worker */ - ret = -pthread_create(&mixer_delayed_put_thread, NULL, - mixer_delayed_put_worker, NULL); - if (ret) - fatal_e(ret, "failed to create mixer delayed put worker"); - - /* if exit_on_idle, touch mixer for pgrp0 */ - exit_on_idle = param.exit_on_idle; - if (exit_on_idle) { - struct ossp_mixer *mixer; - - mixer = get_mixer(0); - if (!mixer) - fatal("failed to touch idle mixer"); - put_mixer(mixer); - } - - /* create notify epoll and kick off watcher thread */ - notify_epfd = epoll_create(max_streams); - if (notify_epfd < 0) - fatal_e(-errno, "failed to create notify epoll"); - if (fcntl(notify_epfd, F_SETFD, FD_CLOEXEC) < 0) - fatal_e(-errno, "failed to set CLOEXEC on notify epfd"); - - ret = -pthread_create(¬ify_poller_thread, NULL, notify_poller, NULL); - if (ret) - fatal_e(ret, "failed to create notify poller thread"); - - /* create reaper for slave corpses */ - ret = -pthread_create(&slave_reaper_thread, NULL, slave_reaper, NULL); - if (ret) - fatal_e(ret, "failed to create slave reaper thread"); - - /* we're set, let's setup fuse structures */ - if (strlen(param.mixer_name)) - mixer_se = setup_ossp_cuse(&mixer_ops, param.mixer_name, - param.mixer_major, param.mixer_minor, - args.argc, args.argv); - if (strlen(param.adsp_name)) - adsp_se = setup_ossp_cuse(&dsp_ops, param.adsp_name, - param.adsp_major, param.adsp_minor, - args.argc, args.argv); - - dsp_se = setup_ossp_cuse(&dsp_ops, param.dsp_name, - param.dsp_major, param.dsp_minor, - args.argc, args.argv); - if (!dsp_se) - fatal("can't create dsp, giving up"); - - if (mixer_se) - snprintf(mixer_buf, sizeof(mixer_buf), ", %s (%d:%d)", - param.mixer_name, param.mixer_major, param.mixer_minor); - if (adsp_se) - snprintf(adsp_buf, sizeof(adsp_buf), ", %s (%d:%d)", - param.adsp_name, param.adsp_major, param.adsp_minor); - - info("Creating %s (%d:%d)%s%s", param.dsp_name, param.dsp_major, - param.dsp_minor, adsp_buf, mixer_buf); - - /* start threads for mixer and adsp */ - if (mixer_se) { - ret = -pthread_create(&cuse_mixer_thread, NULL, - cuse_worker, mixer_se); - if (ret) - err_e(ret, "failed to create mixer worker"); - } - if (adsp_se) { - ret = -pthread_create(&cuse_adsp_thread, NULL, - cuse_worker, adsp_se); - if (ret) - err_e(ret, "failed to create adsp worker"); - } - - /* run CUSE for /dev/dsp in the main thread */ - ret = (ssize_t)cuse_worker(dsp_se); - if (ret < 0) - fatal("dsp worker failed"); - return 0; -} diff --git a/osstest.c b/osstest.c deleted file mode 100644 index 53c3edc..0000000 --- a/osstest.c +++ /dev/null @@ -1,266 +0,0 @@ -/* Simple oss testsuite - * - * Copyright (C) 2009 Maarten Lankhorst - * - * This file is released under the GPLv2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MIXERDEV "/dev/mixer" -#define DSPDEV "/dev/dsp" - -/* Test macros */ - -static int errors, success; -static int report_success = 1; - -#define ok(a, b, c...) do { \ - if (!(a)) { \ - fprintf(stderr, "%s@%d test failed (%s): " b "\n", __func__, __LINE__, #a, ##c); \ - ++errors; \ - } else { \ - if (report_success) \ - printf("%s@%d test succeeded (%s)\n", __func__, __LINE__, #a); \ - ++success; \ - } } while (0) - -static int mixerfd, dspfd; - -static int reopen(int blocking) -{ - close(dspfd); - if (!blocking) - blocking = O_NDELAY; - else - blocking = 0; - dspfd = open(DSPDEV, O_RDWR|blocking); - return dspfd; -} - -static void test_ro(int fd) -{ - int ret; - char buf[1024]; - struct audio_buf_info abi; - memset(buf, 0, sizeof(buf)); - - ret = read(fd, buf, sizeof(buf)); - ok(ret >= 0, "%s", strerror(errno)); - - ret = write(fd, buf, sizeof(buf)); - ok(ret < 0, "read %d bytes", ret); - - ret = ioctl(fd, SNDCTL_DSP_GETISPACE, &abi); - ok(ret >= 0, "%s", strerror(errno)); - - ret = ioctl(fd, SNDCTL_DSP_GETOSPACE, &abi); - ok(ret < 0, "%s", strerror(errno)); - if (ret < 0) - ok(errno == EINVAL, "Invalid errno: %s", strerror(errno)); -} - -static void test_wo(int fd) -{ - int ret; - char buf[1024]; - struct audio_buf_info abi; - memset(buf, 0, sizeof(buf)); - - ret = read(fd, buf, sizeof(buf)); - ok(ret < 0, "read %d bytes", ret); - - ret = write(fd, buf, sizeof(buf)); - ok(ret >= 0, "%s", strerror(errno)); - - ret = ioctl(fd, SNDCTL_DSP_GETISPACE, &abi); - ok(ret < 0, "%s", strerror(errno)); - if (ret < 0) - ok(errno == EINVAL, "Invalid errno: %s", strerror(errno)); - - ret = ioctl(fd, SNDCTL_DSP_GETOSPACE, &abi); - ok(ret >= 0, "%s", strerror(errno)); -} - -static void test_rw(int fd) -{ - int ret; - char buf[1024]; - struct audio_buf_info abi; - memset(buf, 0, sizeof(buf)); - - ret = read(fd, buf, sizeof(buf)); - ok(ret >= 0, "%s", strerror(errno)); - - ret = write(fd, buf, sizeof(buf)); - ok(ret >= 0, "%s", strerror(errno)); - - ret = ioctl(fd, SNDCTL_DSP_GETISPACE, &abi); - ok(ret >= 0, "%s", strerror(errno)); - - ret = ioctl(fd, SNDCTL_DSP_GETOSPACE, &abi); - ok(ret >= 0, "%s", strerror(errno)); -} - -static void test_open(void) -{ - int ro_fd, rw_fd, wo_fd; - - mixerfd = open(MIXERDEV, O_RDONLY|O_NDELAY); - ok(mixerfd >= 0, "%s", strerror(errno)); - - - /* In order to make this work it has to be serialized - * alsa's kernel emulation can only have device open once - * so do some specific smokescreen tests here - * and then open dsp for testing - */ - ro_fd = open(DSPDEV, O_RDONLY); - ok(ro_fd >= 0, "%s", strerror(errno)); - - if (ro_fd >= 0) - test_ro(ro_fd); - - close(ro_fd); - - wo_fd = open(DSPDEV, O_WRONLY); - ok(wo_fd >= 0, "%s", strerror(errno)); - - if (wo_fd >= 0) - test_wo(wo_fd); - - close(wo_fd); - - rw_fd = open(DSPDEV, O_RDWR); - ok(rw_fd >= 0, "%s", strerror(errno)); - - if (rw_fd >= 0) - test_rw(rw_fd); - - dspfd = rw_fd; -} - -static void test_mixer(void) -{ - int ret; - struct mixer_info info; - memset(&info, 0, sizeof(info)); - - ret = ioctl(mixerfd, SOUND_MIXER_INFO, &info); - ok(ret >= 0, "%s", strerror(errno)); - if (ret >= 0) { - printf("Mixer id: %s\n", info.id); - printf("Name: %s\n", info.name); - } -} - -static void test_trigger(int fd) -{ - int ret, i; - - ret = ioctl(fd, SNDCTL_DSP_GETTRIGGER, &i); - ok(ret == 0, "Returned error %s", strerror(errno)); - ok(i == (PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT), "i is set to %d", i); - - i = 0; - ret = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &i); - ok(ret == 0, "Returned error %s", strerror(errno)); - ok(i == 0, "Wrong i returned"); - - i = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT; - ret = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &i); - ok(ret == 0, "Returned error %s", strerror(errno)); - ok(i == (PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT), "i has value %d", i); - - ret = ioctl(fd, SNDCTL_DSP_POST, NULL); - ok(ret == 0, "Returned error %s", strerror(errno)); -} - -static void test_mmap(int fd) -{ - char *area; - int ret; - char buf[24]; - - area = mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - ok(area != MAP_FAILED, "Failed to map: %s\n", strerror(errno)); - - if (area == MAP_FAILED) - return; - - ret = write(fd, &buf, sizeof(buf)); - ok(ret == -1, "write after mmap returned %i\n", ret); - if (ret == -1) - ok(errno == ENXIO, "Error returned is %s\n", strerror(errno)); - - munmap(area, 8192); -} - -static void test_notify(int fd) -{ - struct audio_buf_info bi; - char *bytes = NULL; - int ret, written; - struct pollfd pfd = { fd, POLLOUT }; - int rounds = 20; - - ioctl(fd, SNDCTL_DSP_GETOSPACE, &bi); - - bytes = calloc(1, bi.fragsize); - written = 0; - ok(0, "Fragsize: %i, bytes: %i\n", bi.fragsize, bi.bytes); - while (written + bi.fragsize - 1 < bi.bytes) - { - ret = write(fd, bytes, bi.fragsize); - ok(ret == bi.fragsize, "Returned: %i instead of %i\n", - ret, bi.fragsize); - if (ret > 0) - written += ret; - }; - - while (rounds--) - { - ret = poll(&pfd, 1, -1); - ok(ret > 0, "Poll returned %i\n", ret); - if (ret < 0) - break; - ret = write(fd, bytes, bi.fragsize); - if (ret < 0) ret = -errno; - ok(ret == bi.fragsize, "Returned: %i instead of %i\n", - ret, bi.fragsize); - } -} - -int main() -{ - test_open(); - if (mixerfd >= 0) - test_mixer(); - - if (reopen(1) >= 0) - test_trigger(dspfd); - - if (reopen(0) >= 0) - test_notify(dspfd); - - if (reopen(1) >= 0) - test_mmap(dspfd); - - close(mixerfd); - close(dspfd); - printf("Tests: %d errors %d success\n", errors, success); - return errors > 127 ? 127 : errors; -} -