提交 599a2005 authored 作者: Giovanni Maruzzelli's avatar Giovanni Maruzzelli

skypopen: adding osscuse directory, lot of news to come in some days ;)

上级 c7aefe93
# 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"
差异被折叠。
# 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
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
/*
* 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;
}
/*
* 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 */
/*
* 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;
}
/*
* 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",
};
/*
* 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 */
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论