+++ /dev/null
-# 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"
+++ /dev/null
- 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.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- 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.
-
- <signature of Ty Coon>, 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.
+++ /dev/null
-# 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
-all: osspd 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
+++ /dev/null
-
- OSS Proxy - emulate OSS device using CUSE
-
- Copyright (C) 2008-2009 SUSE Linux Products GmbH
- Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
-
-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
+++ /dev/null
-/*
- * ossp-alsap - ossp DSP slave which forwards to alsa
- *
- * Copyright (C) 2009 Maarten Lankhorst <m.b.lankhorst@gmail.com>
- *
- * This file is released under the GPLv2.
- *
- * Why an alsa plugin as well? Just to show how much
- * the alsa userspace api sucks ;-)
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <libgen.h>
-#include <limits.h>
-#include <poll.h>
-#include <pthread.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <alsa/asoundlib.h>
-#include <sys/soundcard.h>
-
-#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;
-}
-
-#define GIOVANNI
-#ifdef GIOVANNI
-
-#define GIOVA_SLEEP 40000
-#define GIOVA_BLK 3840
-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)
-{
- usleep((GIOVA_SLEEP/GIOVA_BLK)* din_sz);
- return din_sz;
-}
-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)
-{
- usleep((GIOVA_SLEEP/GIOVA_BLK)* *dout_szp);
- return *dout_szp;
-}
-#else// GIOVANNI
-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;
-}
-#endif// GIOVANNI
-
-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;
-}
+++ /dev/null
-/*
- * ossp-slave - OSS Proxy: Common codes for slaves
- *
- * Copyright (C) 2008-2010 SUSE Linux Products GmbH
- * Copyright (C) 2008-2010 Tejun Heo <tj@kernel.org>
- *
- * This file is released under the GPLv2.
- */
-
-#define _GNU_SOURCE
-
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <pwd.h>
-#include <signal.h>
-
-#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;
-}
+++ /dev/null
-/*
- * ossp-slave - OSS Proxy: Common codes for slaves
- *
- * Copyright (C) 2008-2010 SUSE Linux Products GmbH
- * Copyright (C) 2008-2010 Tejun Heo <tj@kernel.org>
- *
- * 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 */
+++ /dev/null
-/*
- * ossp-util - OSS Proxy: Common utilities
- *
- * Copyright (C) 2008-2010 SUSE Linux Products GmbH
- * Copyright (C) 2008-2010 Tejun Heo <tj@kernel.org>
- *
- * This file is released under the GPLv2.
- */
-
-#include <ctype.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <syslog.h>
-#include <unistd.h>
-#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;
-}
+++ /dev/null
-/*
- * ossp-util - OSS Proxy: Common utilities
- *
- * Copyright (C) 2008-2010 SUSE Linux Products GmbH
- * Copyright (C) 2008-2010 Tejun Heo <tj@kernel.org>
- *
- * This file is released under the GPLv2.
- */
-
-#ifndef _OSSP_UTIL_H
-#define _OSSP_UTIL_H
-
-#include <assert.h>
-#include <stddef.h>
-#include <string.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <unistd.h>
-#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*/
+++ /dev/null
-/*
- * ossp - OSS Proxy: emulate OSS device using CUSE
- *
- * Copyright (C) 2008-2010 SUSE Linux Products GmbH
- * Copyright (C) 2008-2010 Tejun Heo <tj@kernel.org>
- *
- * 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",
-};
+++ /dev/null
-/*
- * ossp - OSS Proxy: emulate OSS device using CUSE
- *
- * Copyright (C) 2008-2010 SUSE Linux Products GmbH
- * Copyright (C) 2008-2010 Tejun Heo <tj@kernel.org>
- *
- * This file is released under the GPLv2.
- */
-
-#ifndef _OSSP_H
-#define _OSSP_H
-
-#include <sys/types.h>
-#include <inttypes.h>
-#include <sys/soundcard.h>
-
-#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 */
+++ /dev/null
-/*
- * osspd - OSS Proxy Daemon: emulate OSS device using CUSE
- *
- * Copyright (C) 2008-2010 SUSE Linux Products GmbH
- * Copyright (C) 2008-2010 Tejun Heo <tj@kernel.org>
- *
- * This file is released under the GPLv2.
- */
-#undef GIOVANNI
-
-#define FUSE_USE_VERSION 28
-#define _GNU_SOURCE
-
-#include <assert.h>
-#include <cuse_lowlevel.h>
-#include <fcntl.h>
-#include <fuse_opt.h>
-#include <libgen.h>
-#include <limits.h>
-#include <pthread.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/epoll.h>
-#include <sys/socket.h>
-#include <sys/soundcard.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#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, "opcode %s=%d carg=%zu din=%zu rarg=%zu dout=%zu",
- //ossp_cmd_str[opcode], opcode, carg_size, din_size, rarg_size,
- //dout_size);
-#ifndef GIOVANNI
-memset(dout, 255, dout_size);
-memset(din, 255, din_size);
-
-#define GIOVA_BLK 3840
-#define GIOVA_SLEEP 40000
-switch(opcode){
-
- case 1: //OPEN
- reply.result = 0;
- break;
- case 2: //READ
- usleep((GIOVA_SLEEP/GIOVA_BLK)* *dout_sizep);
- reply.result = *dout_sizep;
- break;
- case 3: //WRITE
- usleep((GIOVA_SLEEP/GIOVA_BLK)* din_size);
- reply.result = din_size;
- break;
- case 9: //POST
- reply.result = -32;
- break;
- case 13: //GET_BLKSIZE
- reply.result = 0;
- *(int *)rarg = GIOVA_BLK;
- break;
- case 14: //GET_FORMATS
- reply.result = 0;
- *(int *)rarg = 28731;
- break;
- case 15: //SET_RATE
- reply.result = 0;
- *(int *)rarg = *(int *) carg;
- break;
- case 16: //SET_CHANNELS
- reply.result = 0;
- *(int *)rarg = *(int *) carg;
- break;
- case 17: //SET_FORMAT
- reply.result = 0;
- *(int *)rarg = *(int *) carg;
- break;
- case 19: //SET_FRAGMENT
- reply.result = 0;
- break;
- default:
- reply.result = 0;
- break;
-}
-#endif // GIOVANNI
-
-#ifdef GIOVANNI
- 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;
- }
-
-#endif // GIOVANNI
-
-out_unlock:
- //dbg1_os(os, " completed, result=%d dout=%zu",
- //reply.result, dout_size);
-
-//if(rarg)
- //dbg1_os(os, " 2 %s=%d completed, result=%d dout=%zu carg=%d rarg=%d", ossp_cmd_str[opcode], opcode,
- //reply.result, dout_size, carg ? *(int *) carg : 666, *(int *)rarg);
- 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);
-}
-
-#ifndef GIOVANNI
-int contapid = 13000;
-#endif// GIOVANNI
-
-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);
-
-#ifndef GIOVANNI
- os->slave_pid = contapid;
- contapid++;
- if(contapid > 30000)
- contapid=13000;
-#endif //GIOVANNI
-
-//#ifdef GIOVANNI
- 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);
- }
-//#endif //GIOVANNI
-
- /* 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 <teheo@suse.de>",
- 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");
-
-//#ifdef GIOVANNI
- /* 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);
-
-//#endif// GIOVANNI
- /* 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");
-
-#ifdef GIOVANNI
- /* 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);
-
-#endif// GIOVANNI
- 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");
-
-#ifdef GIOVANNI
- 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);
-
-#endif// GIOVANNI
- info("Creating %s (%d:%d)%s%s", param.dsp_name, param.dsp_major,
- param.dsp_minor, adsp_buf, mixer_buf);
-
-#ifdef GIOVANNI
- /* 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");
- }
-#endif// GIOVANNI
-
- /* 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;
-}