skypopen: adding osscuse directory, lot of news to come in some days ;)
This commit is contained in:
parent
c7aefe934f
commit
599a200509
|
@ -0,0 +1,7 @@
|
|||
# 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"
|
|
@ -0,0 +1,339 @@
|
|||
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.
|
|
@ -0,0 +1,69 @@
|
|||
# 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
|
|
@ -0,0 +1,119 @@
|
|||
|
||||
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
|
|
@ -0,0 +1,613 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 */
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -0,0 +1,609 @@
|
|||
/*
|
||||
* 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*/
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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",
|
||||
};
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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 */
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue