Implement mmap support with STORE and RETRIEVE notifications
[ossp.git] / osspd.c
blobc1b9152796eea7d5740368ad9e64f3ae0e7a1370
1 /*
2 * osspd - OSS Proxy Daemon: emulate OSS device using CUSE
4 * Copyright (C) 2008-2010 SUSE Linux Products GmbH
5 * Copyright (C) 2008-2010 Tejun Heo <tj@kernel.org>
7 * This file is released under the GPLv2.
8 */
10 #define FUSE_USE_VERSION 28
11 #define _GNU_SOURCE
13 #include <assert.h>
14 #include <cuse_lowlevel.h>
15 #include <fcntl.h>
16 #include <fuse_opt.h>
17 #include <libgen.h>
18 #include <limits.h>
19 #include <pthread.h>
20 #include <pwd.h>
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <signal.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <sys/epoll.h>
28 #include <sys/socket.h>
29 #include <sys/soundcard.h>
30 #include <sys/time.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
34 #include "ossp.h"
35 #include "ossp-util.h"
37 #define OSSP_MMAP
39 #define DFL_MIXER_NAME "mixer"
40 #define DFL_DSP_NAME "dsp"
41 #define DFL_ADSP_NAME "adsp"
42 #define STRFMT "S[%u/%d]"
43 #define STRID(os) os->id, os->pid
45 #define dbg1_os(os, fmt, args...) dbg1(STRFMT" "fmt, STRID(os) , ##args)
46 #define dbg0_os(os, fmt, args...) dbg0(STRFMT" "fmt, STRID(os) , ##args)
47 #define warn_os(os, fmt, args...) warn(STRFMT" "fmt, STRID(os) , ##args)
48 #define err_os(os, fmt, args...) err(STRFMT" "fmt, STRID(os) , ##args)
49 #define warn_ose(os, err, fmt, args...) \
50 warn_e(err, STRFMT" "fmt, STRID(os) , ##args)
51 #define err_ose(os, err, fmt, args...) \
52 err_e(err, STRFMT" "fmt, STRID(os) , ##args)
54 enum {
55 SNDRV_OSS_VERSION = ((3<<16)|(8<<8)|(1<<4)|(0)), /* 3.8.1a */
56 DFL_MIXER_MAJOR = 14,
57 DFL_MIXER_MINOR = 0,
58 DFL_DSP_MAJOR = 14,
59 DFL_DSP_MINOR = 3,
60 DFL_ADSP_MAJOR = 14,
61 DFL_ADSP_MINOR = 12,
62 DFL_MAX_STREAMS = 128,
63 MIXER_PUT_DELAY = 600, /* 10 mins */
64 /* DSPS_MMAP_SIZE / 2 must be multiple of SHMLBA */
65 DSPS_MMAP_SIZE = 2 * (512 << 10), /* 512k for each dir */
68 struct ossp_uid_cnt {
69 struct list_head link;
70 uid_t uid;
71 unsigned nr_os;
74 struct ossp_mixer {
75 pid_t pgrp;
76 struct list_head link;
77 struct list_head delayed_put_link;
78 unsigned refcnt;
79 /* the following two fields are protected by mixer_mutex */
80 int vol[2][2];
81 int modify_counter;
82 time_t put_expires;
85 struct ossp_mixer_cmd {
86 struct ossp_mixer *mixer;
87 struct ossp_mixer_arg set;
88 int out_dir;
89 int rvol;
92 #define for_each_vol(i, j) \
93 for (i = 0, j = 0; i < 2; j += i << 1, j++, i = j >> 1, j &= 1)
95 struct ossp_stream {
96 unsigned id; /* stream ID */
97 struct list_head link;
98 struct list_head pgrp_link;
99 struct list_head notify_link;
100 unsigned refcnt;
101 pthread_mutex_t cmd_mutex;
102 pthread_mutex_t mmap_mutex;
103 struct fuse_pollhandle *ph;
105 /* stream owner info */
106 pid_t pid;
107 pid_t pgrp;
108 uid_t uid;
109 gid_t gid;
111 /* slave info */
112 pid_t slave_pid;
113 int cmd_fd;
114 int notify_tx;
115 int notify_rx;
117 /* the following dead flag is set asynchronously, keep it separate. */
118 int dead;
120 /* stream mixer state, protected by mixer_mutex */
121 int mixer_pending;
122 int vol[2][2];
123 int vol_set[2][2];
125 int mmap_fd;
126 size_t mmap_size;
127 void *mmap;
128 void *mmap_addr[2];
129 struct ossp_transfer *mmap_transfer;
131 struct ossp_uid_cnt *ucnt;
132 struct fuse_session *se; /* associated fuse session */
133 struct ossp_mixer *mixer;
136 struct ossp_dsp_stream {
137 struct ossp_stream os;
138 unsigned rw;
139 unsigned mmapped;
140 int nonblock;
143 #define os_to_dsps(_os) container_of(_os, struct ossp_dsp_stream, os)
145 static unsigned max_streams;
146 static unsigned umax_streams;
147 static unsigned hashtbl_size;
148 static char dsp_slave_path[PATH_MAX];
150 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
151 static pthread_mutex_t mixer_mutex = PTHREAD_MUTEX_INITIALIZER;
152 static unsigned long *os_id_bitmap;
153 static unsigned nr_mixers;
154 static struct list_head *mixer_tbl; /* indexed by PGRP */
155 static struct list_head *os_tbl; /* indexed by ID */
156 static struct list_head *os_pgrp_tbl; /* indexed by PGRP */
157 static struct list_head *os_notify_tbl; /* indexed by notify fd */
158 static LIST_HEAD(uid_cnt_list);
159 static int notify_epfd; /* epoll used to monitor notify fds */
160 static pthread_t notify_poller_thread;
161 static pthread_t slave_reaper_thread;
162 static pthread_t mixer_delayed_put_thread;
163 static pthread_t cuse_mixer_thread;
164 static pthread_t cuse_adsp_thread;
165 static pthread_cond_t notify_poller_kill_wait = PTHREAD_COND_INITIALIZER;
166 static pthread_cond_t slave_reaper_wait = PTHREAD_COND_INITIALIZER;
167 static LIST_HEAD(slave_corpse_list);
168 static LIST_HEAD(mixer_delayed_put_head); /* delayed reference */
169 static pthread_cond_t mixer_delayed_put_cond = PTHREAD_COND_INITIALIZER;
171 static int init_wait_fd = -1;
172 static int exit_on_idle;
173 static struct fuse_session *mixer_se;
174 static struct fuse_session *dsp_se;
175 static struct fuse_session *adsp_se;
177 static void put_os(struct ossp_stream *os);
180 /***************************************************************************
181 * Accessors
184 static struct list_head *mixer_tbl_head(pid_t pid)
186 return &mixer_tbl[pid % hashtbl_size];
189 static struct list_head *os_tbl_head(uint64_t id)
191 return &os_tbl[id % hashtbl_size];
194 static struct list_head *os_pgrp_tbl_head(pid_t pgrp)
196 return &os_pgrp_tbl[pgrp % hashtbl_size];
199 static struct list_head *os_notify_tbl_head(int notify_rx)
201 return &os_notify_tbl[notify_rx % hashtbl_size];
204 static struct ossp_mixer *find_mixer_locked(pid_t pgrp)
206 struct ossp_mixer *mixer;
208 list_for_each_entry(mixer, mixer_tbl_head(pgrp), link)
209 if (mixer->pgrp == pgrp)
210 return mixer;
211 return NULL;
214 static struct ossp_mixer *find_mixer(pid_t pgrp)
216 struct ossp_mixer *mixer;
218 pthread_mutex_lock(&mutex);
219 mixer = find_mixer_locked(pgrp);
220 pthread_mutex_unlock(&mutex);
221 return mixer;
224 static struct ossp_stream *find_os(unsigned id)
226 struct ossp_stream *os, *found = NULL;
228 pthread_mutex_lock(&mutex);
229 list_for_each_entry(os, os_tbl_head(id), link)
230 if (os->id == id) {
231 found = os;
232 break;
234 pthread_mutex_unlock(&mutex);
235 return found;
238 static struct ossp_stream *find_os_by_notify_rx(int notify_rx)
240 struct ossp_stream *os, *found = NULL;
242 pthread_mutex_lock(&mutex);
243 list_for_each_entry(os, os_notify_tbl_head(notify_rx), notify_link)
244 if (os->notify_rx == notify_rx) {
245 found = os;
246 break;
248 pthread_mutex_unlock(&mutex);
249 return found;
253 /***************************************************************************
254 * Command and ioctl helpers
257 static ssize_t exec_cmd_intern(struct ossp_stream *os, enum ossp_opcode opcode,
258 const void *carg, size_t carg_size, const void *din, size_t din_size,
259 void *rarg, size_t rarg_size, void *dout, size_t *dout_sizep, int fd)
261 size_t dout_size = dout_sizep ? *dout_sizep : 0;
262 struct ossp_cmd cmd = { .magic = OSSP_CMD_MAGIC, .opcode = opcode,
263 .din_size = din_size,
264 .dout_size = dout_size };
265 struct iovec iov = { &cmd, sizeof(cmd) };
266 struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
267 struct ossp_reply reply = { };
268 char cmsg_buf[CMSG_SPACE(sizeof(fd))];
269 char reason[512];
270 int rc;
272 if (os->dead)
273 return -EIO;
275 dbg1_os(os, "%s carg=%zu din=%zu rarg=%zu dout=%zu",
276 ossp_cmd_str[opcode], carg_size, din_size, rarg_size,
277 dout_size);
279 if (fd >= 0) {
280 struct cmsghdr *cmsg;
282 msg.msg_control = cmsg_buf;
283 msg.msg_controllen = sizeof(cmsg_buf);
284 cmsg = CMSG_FIRSTHDR(&msg);
285 cmsg->cmsg_level = SOL_SOCKET;
286 cmsg->cmsg_type = SCM_RIGHTS;
287 cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
288 *(int *)CMSG_DATA(cmsg) = fd;
289 msg.msg_controllen = cmsg->cmsg_len;
292 if (sendmsg(os->cmd_fd, &msg, 0) <= 0) {
293 rc = -errno;
294 snprintf(reason, sizeof(reason), "command sendmsg failed: %s",
295 strerror(-rc));
296 goto fail;
299 if ((rc = write_fill(os->cmd_fd, carg, carg_size)) < 0 ||
300 (rc = write_fill(os->cmd_fd, din, din_size)) < 0) {
301 snprintf(reason, sizeof(reason),
302 "can't tranfer command argument and/or data: %s",
303 strerror(-rc));
304 goto fail;
306 if ((rc = read_fill(os->cmd_fd, &reply, sizeof(reply))) < 0) {
307 snprintf(reason, sizeof(reason), "can't read reply: %s",
308 strerror(-rc));
309 goto fail;
312 if (reply.magic != OSSP_REPLY_MAGIC) {
313 snprintf(reason, sizeof(reason),
314 "reply magic mismatch %x != %x",
315 reply.magic, OSSP_REPLY_MAGIC);
316 rc = -EINVAL;
317 goto fail;
320 if (reply.result < 0)
321 goto out_unlock;
323 if (reply.dout_size > dout_size) {
324 snprintf(reason, sizeof(reason),
325 "data out size overflow %zu > %zu",
326 reply.dout_size, dout_size);
327 rc = -EINVAL;
328 goto fail;
331 dout_size = reply.dout_size;
332 if (dout_sizep)
333 *dout_sizep = dout_size;
335 if ((rc = read_fill(os->cmd_fd, rarg, rarg_size)) < 0 ||
336 (rc = read_fill(os->cmd_fd, dout, dout_size)) < 0) {
337 snprintf(reason, sizeof(reason), "can't read data out: %s",
338 strerror(-rc));
339 goto fail;
342 out_unlock:
343 dbg1_os(os, " completed, result=%d dout=%zu",
344 reply.result, dout_size);
345 return reply.result;
347 fail:
348 warn_os(os, "communication with slave failed (%s)", reason);
349 os->dead = 1;
350 return rc;
353 static ssize_t exec_cmd(struct ossp_stream *os, enum ossp_opcode opcode,
354 const void *carg, size_t carg_size, const void *din, size_t din_size,
355 void *rarg, size_t rarg_size, void *dout, size_t *dout_sizep, int fd)
357 int is_mixer;
358 int i, j;
359 ssize_t ret, mret;
361 /* mixer command is handled exlicitly below */
362 is_mixer = opcode == OSSP_MIXER;
363 if (is_mixer) {
364 ret = -pthread_mutex_trylock(&os->cmd_mutex);
365 if (ret)
366 return ret;
367 } else {
368 pthread_mutex_lock(&os->cmd_mutex);
370 ret = exec_cmd_intern(os, opcode, carg, carg_size,
371 din, din_size, rarg, rarg_size,
372 dout, dout_sizep, fd);
375 /* lazy mixer handling */
376 pthread_mutex_lock(&mixer_mutex);
378 if (os->mixer_pending) {
379 struct ossp_mixer_arg marg;
380 repeat_mixer:
381 /* we have mixer command pending */
382 memcpy(marg.vol, os->vol_set, sizeof(os->vol_set));
383 memset(os->vol_set, -1, sizeof(os->vol_set));
385 pthread_mutex_unlock(&mixer_mutex);
386 mret = exec_cmd_intern(os, OSSP_MIXER, &marg, sizeof(marg),
387 NULL, 0, &marg, sizeof(marg), NULL, NULL,
388 -1);
389 pthread_mutex_lock(&mixer_mutex);
391 /* was there mixer set request while executing mixer command? */
392 for_each_vol(i, j)
393 if (os->vol_set[i][j] >= 0)
394 goto repeat_mixer;
396 /* update internal mixer state */
397 if (mret == 0) {
398 for_each_vol(i, j) {
399 if (marg.vol[i][j] >= 0) {
400 if (os->vol[i][j] != marg.vol[i][j])
401 os->mixer->modify_counter++;
402 os->vol[i][j] = marg.vol[i][j];
406 os->mixer_pending = 0;
409 pthread_mutex_unlock(&os->cmd_mutex);
412 * mixer mutex must be released after cmd_mutex so that
413 * exec_mixer_cmd() can guarantee that mixer_pending flags
414 * will be handled immediately or when the currently
415 * in-progress command completes.
417 pthread_mutex_unlock(&mixer_mutex);
419 return is_mixer ? mret : ret;
422 static ssize_t exec_simple_cmd(struct ossp_stream *os,
423 enum ossp_opcode opcode, void *carg, void *rarg)
425 return exec_cmd(os, opcode,
426 carg, ossp_arg_sizes[opcode].carg_size, NULL, 0,
427 rarg, ossp_arg_sizes[opcode].rarg_size, NULL, NULL, -1);
430 static int ioctl_prep_uarg(fuse_req_t req, void *in, size_t in_sz, void *out,
431 size_t out_sz, void *uarg, const void *in_buf,
432 size_t in_bufsz, size_t out_bufsz)
434 struct iovec in_iov = { }, out_iov = { };
435 int retry = 0;
437 if (in) {
438 if (!in_bufsz) {
439 in_iov.iov_base = uarg;
440 in_iov.iov_len = in_sz;
441 retry = 1;
442 } else {
443 assert(in_bufsz == in_sz);
444 memcpy(in, in_buf, in_sz);
448 if (out) {
449 if (!out_bufsz) {
450 out_iov.iov_base = uarg;
451 out_iov.iov_len = out_sz;
452 retry = 1;
453 } else
454 assert(out_bufsz == out_sz);
457 if (retry)
458 fuse_reply_ioctl_retry(req, &in_iov, 1, &out_iov, 1);
460 return retry;
463 #define PREP_UARG(inp, outp) do { \
464 if (ioctl_prep_uarg(req, (inp), sizeof(*(inp)), \
465 (outp), sizeof(*(outp)), uarg, \
466 in_buf, in_bufsz, out_bufsz)) \
467 return; \
468 } while (0)
470 /***************************************************************************
471 * Mixer implementation
474 static void put_mixer_real(struct ossp_mixer *mixer)
476 if (!--mixer->refcnt) {
477 dbg0("DESTROY mixer(%d)", mixer->pgrp);
478 list_del_init(&mixer->link);
479 list_del_init(&mixer->delayed_put_link);
480 free(mixer);
481 nr_mixers--;
484 * If exit_on_idle, mixer for pgrp0 is touched during
485 * init and each stream has mixer attached. As mixers
486 * are destroyed after they have been idle for
487 * MIXER_PUT_DELAY seconds, we can use it for idle
488 * detection. Note that this might race with
489 * concurrent open. The race is inherent.
491 if (exit_on_idle && !nr_mixers) {
492 info("idle, exiting");
493 exit(0);
498 static struct ossp_mixer *get_mixer(pid_t pgrp)
500 struct ossp_mixer *mixer;
502 pthread_mutex_lock(&mutex);
504 /* is there a matching one? */
505 mixer = find_mixer_locked(pgrp);
506 if (mixer) {
507 if (list_empty(&mixer->delayed_put_link))
508 mixer->refcnt++;
509 else
510 list_del_init(&mixer->delayed_put_link);
511 goto out_unlock;
514 /* reap delayed put list if there are too many mixers */
515 while (nr_mixers > 2 * max_streams &&
516 !list_empty(&mixer_delayed_put_head)) {
517 struct ossp_mixer *mixer =
518 list_first_entry(&mixer_delayed_put_head,
519 struct ossp_mixer, delayed_put_link);
521 assert(mixer->refcnt == 1);
522 put_mixer_real(mixer);
525 /* create a new one */
526 mixer = calloc(1, sizeof(*mixer));
527 if (!mixer) {
528 warn("failed to allocate mixer for %d", pgrp);
529 mixer = NULL;
530 goto out_unlock;
533 mixer->pgrp = pgrp;
534 INIT_LIST_HEAD(&mixer->link);
535 INIT_LIST_HEAD(&mixer->delayed_put_link);
536 mixer->refcnt = 1;
537 memset(mixer->vol, -1, sizeof(mixer->vol));
539 list_add(&mixer->link, mixer_tbl_head(pgrp));
540 nr_mixers++;
541 dbg0("CREATE mixer(%d)", pgrp);
543 out_unlock:
544 pthread_mutex_unlock(&mutex);
545 return mixer;
548 static void put_mixer(struct ossp_mixer *mixer)
550 pthread_mutex_lock(&mutex);
552 if (mixer) {
553 if (mixer->refcnt == 1) {
554 struct timespec ts;
556 clock_gettime(CLOCK_REALTIME, &ts);
557 mixer->put_expires = ts.tv_sec + MIXER_PUT_DELAY;
558 list_add_tail(&mixer->delayed_put_link,
559 &mixer_delayed_put_head);
560 pthread_cond_signal(&mixer_delayed_put_cond);
561 } else
562 put_mixer_real(mixer);
565 pthread_mutex_unlock(&mutex);
568 static void *mixer_delayed_put_worker(void *arg)
570 struct ossp_mixer *mixer;
571 struct timespec ts;
572 time_t now;
574 pthread_mutex_lock(&mutex);
575 again:
576 clock_gettime(CLOCK_REALTIME, &ts);
577 now = ts.tv_sec;
579 mixer = NULL;
580 while (!list_empty(&mixer_delayed_put_head)) {
581 mixer = list_first_entry(&mixer_delayed_put_head,
582 struct ossp_mixer, delayed_put_link);
584 if (now <= mixer->put_expires)
585 break;
587 assert(mixer->refcnt == 1);
588 put_mixer_real(mixer);
589 mixer = NULL;
592 if (mixer) {
593 ts.tv_sec = mixer->put_expires + 1;
594 pthread_cond_timedwait(&mixer_delayed_put_cond, &mutex, &ts);
595 } else
596 pthread_cond_wait(&mixer_delayed_put_cond, &mutex);
598 goto again;
601 static void init_mixer_cmd(struct ossp_mixer_cmd *mxcmd,
602 struct ossp_mixer *mixer)
604 memset(mxcmd, 0, sizeof(*mxcmd));
605 memset(&mxcmd->set.vol, -1, sizeof(mxcmd->set.vol));
606 mxcmd->mixer = mixer;
607 mxcmd->out_dir = -1;
610 static int exec_mixer_cmd(struct ossp_mixer_cmd *mxcmd, struct ossp_stream *os)
612 int i, j, rc;
615 * Set pending flags before trying to execute mixer command.
616 * Combined with lock release order in exec_cmd(), this
617 * guarantees that the mixer command will be executed
618 * immediately or when the current command completes.
620 pthread_mutex_lock(&mixer_mutex);
621 os->mixer_pending = 1;
622 for_each_vol(i, j)
623 if (mxcmd->set.vol[i][j] >= 0)
624 os->vol_set[i][j] = mxcmd->set.vol[i][j];
625 pthread_mutex_unlock(&mixer_mutex);
627 rc = exec_simple_cmd(os, OSSP_MIXER, NULL, NULL);
628 if (rc >= 0) {
629 dbg0_os(os, "volume set=%d/%d:%d/%d get=%d/%d:%d/%d",
630 mxcmd->set.vol[PLAY][LEFT], mxcmd->set.vol[PLAY][RIGHT],
631 mxcmd->set.vol[REC][LEFT], mxcmd->set.vol[REC][RIGHT],
632 os->vol[PLAY][LEFT], os->vol[PLAY][RIGHT],
633 os->vol[REC][LEFT], os->vol[REC][RIGHT]);
634 } else if (rc != -EBUSY)
635 warn_ose(os, rc, "mixer command failed");
637 return rc;
640 static void finish_mixer_cmd(struct ossp_mixer_cmd *mxcmd)
642 struct ossp_mixer *mixer = mxcmd->mixer;
643 struct ossp_stream *os;
644 int dir = mxcmd->out_dir;
645 int vol[2][2] = { };
646 int cnt[2][2] = { };
647 int i, j;
649 pthread_mutex_lock(&mixer_mutex);
651 /* get volume of all streams attached to this mixer */
652 pthread_mutex_lock(&mutex);
653 list_for_each_entry(os, os_pgrp_tbl_head(mixer->pgrp), pgrp_link) {
654 if (os->pgrp != mixer->pgrp)
655 continue;
656 for_each_vol(i, j) {
657 if (os->vol[i][j] < 0)
658 continue;
659 vol[i][j] += os->vol[i][j];
660 cnt[i][j]++;
663 pthread_mutex_unlock(&mutex);
665 /* calculate the summary volume values */
666 for_each_vol(i, j) {
667 if (mxcmd->set.vol[i][j] >= 0)
668 vol[i][j] = mxcmd->set.vol[i][j];
669 else if (cnt[i][j])
670 vol[i][j] = vol[i][j] / cnt[i][j];
671 else if (mixer->vol[i][j] >= 0)
672 vol[i][j] = mixer->vol[i][j];
673 else
674 vol[i][j] = 100;
676 vol[i][j] = min(max(0, vol[i][j]), 100);
679 if (dir >= 0)
680 mxcmd->rvol = vol[dir][LEFT] | (vol[dir][RIGHT] << 8);
682 pthread_mutex_unlock(&mixer_mutex);
685 static void mixer_simple_ioctl(fuse_req_t req, struct ossp_mixer *mixer,
686 unsigned cmd, void *uarg, const void *in_buf,
687 size_t in_bufsz, size_t out_bufsz,
688 int *not_minep)
690 const char *id = "OSS Proxy", *name = "Mixer";
691 int i;
693 switch (cmd) {
694 case SOUND_MIXER_INFO: {
695 struct mixer_info info = { };
697 PREP_UARG(NULL, &info);
698 strncpy(info.id, id, sizeof(info.id) - 1);
699 strncpy(info.name, name, sizeof(info.name) - 1);
700 info.modify_counter = mixer->modify_counter;
701 fuse_reply_ioctl(req, 0, &info, sizeof(info));
702 break;
705 case SOUND_OLD_MIXER_INFO: {
706 struct _old_mixer_info info = { };
708 PREP_UARG(NULL, &info);
709 strncpy(info.id, id, sizeof(info.id) - 1);
710 strncpy(info.name, name, sizeof(info.name) - 1);
711 fuse_reply_ioctl(req, 0, &info, sizeof(info));
712 break;
715 case OSS_GETVERSION:
716 i = SNDRV_OSS_VERSION;
717 goto puti;
718 case SOUND_MIXER_READ_DEVMASK:
719 case SOUND_MIXER_READ_STEREODEVS:
720 i = SOUND_MASK_PCM | SOUND_MASK_IGAIN;
721 goto puti;
722 case SOUND_MIXER_READ_CAPS:
723 i = SOUND_CAP_EXCL_INPUT;
724 goto puti;
725 case SOUND_MIXER_READ_RECMASK:
726 case SOUND_MIXER_READ_RECSRC:
727 i = SOUND_MASK_IGAIN;
728 goto puti;
729 puti:
730 PREP_UARG(NULL, &i);
731 fuse_reply_ioctl(req, 0, &i, sizeof(i));
732 break;
734 case SOUND_MIXER_WRITE_RECSRC:
735 fuse_reply_ioctl(req, 0, NULL, 0);
736 break;
738 default:
739 *not_minep = 1;
743 static void mixer_do_ioctl(fuse_req_t req, struct ossp_mixer *mixer,
744 unsigned cmd, void *uarg, const void *in_buf,
745 size_t in_bufsz, size_t out_bufsz)
747 struct ossp_mixer_cmd mxcmd;
748 struct ossp_stream *os, **osa;
749 int not_mine = 0;
750 int slot = cmd & 0xff, dir;
751 int nr_os;
752 int i, rc;
754 mixer_simple_ioctl(req, mixer, cmd, uarg, in_buf, in_bufsz, out_bufsz,
755 &not_mine);
756 if (!not_mine)
757 return;
759 rc = -ENXIO;
760 if (!(cmd & (SIOC_IN | SIOC_OUT)))
761 goto err;
764 * Okay, it's not one of the easy ones. Build mxcmd for
765 * actual volume control.
767 if (cmd & SIOC_IN)
768 PREP_UARG(&i, &i);
769 else
770 PREP_UARG(NULL, &i);
772 switch (slot) {
773 case SOUND_MIXER_PCM:
774 dir = PLAY;
775 break;
776 case SOUND_MIXER_IGAIN:
777 dir = REC;
778 break;
779 default:
780 i = 0;
781 fuse_reply_ioctl(req, 0, &i, sizeof(i));
782 return;
785 init_mixer_cmd(&mxcmd, mixer);
787 if (cmd & SIOC_IN) {
788 unsigned l, r;
790 rc = -EINVAL;
791 l = i & 0xff;
792 r = (i >> 8) & 0xff;
793 if (l > 100 || r > 100)
794 goto err;
796 mixer->vol[dir][LEFT] = mxcmd.set.vol[dir][LEFT] = l;
797 mixer->vol[dir][RIGHT] = mxcmd.set.vol[dir][RIGHT] = r;
799 mxcmd.out_dir = dir;
802 * Apply volume conrol
804 /* acquire target streams */
805 pthread_mutex_lock(&mutex);
806 osa = calloc(max_streams, sizeof(osa[0]));
807 if (!osa) {
808 pthread_mutex_unlock(&mutex);
809 rc = -ENOMEM;
810 goto err;
813 nr_os = 0;
814 list_for_each_entry(os, os_pgrp_tbl_head(mixer->pgrp), pgrp_link) {
815 if (os->pgrp == mixer->pgrp) {
816 osa[nr_os++] = os;
817 os->refcnt++;
821 pthread_mutex_unlock(&mutex);
823 /* execute mxcmd for each stream and put it */
824 for (i = 0; i < nr_os; i++) {
825 exec_mixer_cmd(&mxcmd, osa[i]);
826 put_os(osa[i]);
829 finish_mixer_cmd(&mxcmd);
830 free(osa);
832 if (out_bufsz)
833 fuse_reply_ioctl(req, 0, &mxcmd.rvol, sizeof(mxcmd.rvol));
834 else
835 fuse_reply_ioctl(req, 0, NULL, 0);
837 return;
839 err:
840 fuse_reply_err(req, -rc);
843 static void mixer_open(fuse_req_t req, struct fuse_file_info *fi)
845 pid_t pid = fuse_req_ctx(req)->pid, pgrp;
846 struct ossp_mixer *mixer;
847 int rc;
849 rc = get_proc_self_info(pid, &pgrp, NULL, 0);
850 if (rc) {
851 err_e(rc, "get_proc_self_info(%d) failed", pid);
852 fuse_reply_err(req, -rc);
853 return;
856 mixer = get_mixer(pgrp);
857 fi->fh = pgrp;
859 if (mixer)
860 fuse_reply_open(req, fi);
861 else
862 fuse_reply_err(req, ENOMEM);
865 static void mixer_ioctl(fuse_req_t req, int signed_cmd, void *uarg,
866 struct fuse_file_info *fi, unsigned int flags,
867 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
869 struct ossp_mixer *mixer;
871 mixer = find_mixer(fi->fh);
872 if (!mixer) {
873 fuse_reply_err(req, EBADF);
874 return;
877 mixer_do_ioctl(req, mixer, signed_cmd, uarg, in_buf, in_bufsz,
878 out_bufsz);
881 static void mixer_release(fuse_req_t req, struct fuse_file_info *fi)
883 struct ossp_mixer *mixer;
885 mixer = find_mixer(fi->fh);
886 if (mixer) {
887 put_mixer(mixer);
888 fuse_reply_err(req, 0);
889 } else
890 fuse_reply_err(req, EBADF);
894 /***************************************************************************
895 * Stream implementation
898 static int os_create_shared_memory(struct ossp_stream *os, size_t mmap_size)
900 int rc;
902 os->mmap_fd = -1;
903 os->mmap_size = 0;
905 #ifdef OSSP_MMAP
906 if (mmap_size) {
907 char shmname[32];
908 int fd;
909 void *p;
911 sprintf(shmname, "/ossp.%i", getpid());
912 fd = shm_open(shmname, O_RDWR | O_CREAT | O_EXCL | O_TRUNC,
913 0600);
915 if (fd == -1) {
916 rc = -errno;
917 warn_ose(os, rc, "failed to open shared memory");
918 return rc;
920 rc = shm_unlink(shmname);
921 if (rc == -1) {
922 rc = -errno;
923 close(fd);
924 warn_ose(os, rc, "failed to unlink shared memory");
925 return rc;
927 rc = ftruncate(fd, mmap_size + 2*sizeof(struct ossp_transfer));
928 if (rc == -1) {
929 rc = -errno;
930 close(fd);
931 warn_ose(os, rc, "failed to set shared memory size");
932 return rc;
934 rc = fcntl(fd, F_SETFD, 0); /* reset cloexec */
935 if (rc == -1) {
936 rc = -errno;
937 close(fd);
938 warn_ose(os, rc, "failed to reset close-on-exec for shared memory");
939 return rc;
941 p = mmap(NULL, mmap_size + 2 * sizeof(struct ossp_transfer),
942 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
943 if (p == MAP_FAILED) {
944 rc = -errno;
945 close(fd);
946 warn_ose(os, rc, "failed to mmap shared memory");
947 return rc;
949 os->mmap_addr[PLAY] = p;
950 os->mmap_addr[REC] = p + mmap_size / 2;
951 os->mmap_transfer = p + mmap_size;
953 if (sem_init(&os->mmap_transfer[PLAY].sem, 1, 0) == -1 ||
954 sem_init(&os->mmap_transfer[REC].sem, 1, 0) == -1) {
955 rc = -errno;
956 close(fd);
957 munmap(p, mmap_size + 2 * sizeof(struct ossp_transfer));
958 warn_ose(os, rc, "failed to init shared semaphore");
959 return rc;
962 os->mmap = p;
963 os->mmap_size = mmap_size;
964 os->mmap_fd = fd;
965 #endif
968 return 0;
971 static int alloc_os(size_t stream_size, size_t mmap_size, pid_t pid, uid_t pgrp,
972 uid_t uid, gid_t gid, int cmd_sock,
973 const int *notify, struct fuse_session *se,
974 struct ossp_stream **osp)
976 struct ossp_uid_cnt *tmp_ucnt, *ucnt = NULL;
977 struct ossp_stream *os;
978 int rc;
980 assert(stream_size >= sizeof(struct ossp_stream));
981 os = calloc(1, stream_size);
982 if (!os)
983 return -ENOMEM;
985 INIT_LIST_HEAD(&os->link);
986 INIT_LIST_HEAD(&os->pgrp_link);
987 INIT_LIST_HEAD(&os->notify_link);
988 os->refcnt = 1;
990 rc = -pthread_mutex_init(&os->cmd_mutex, NULL);
991 if (rc)
992 goto err_free;
994 rc = -pthread_mutex_init(&os->mmap_mutex, NULL);
995 if (rc)
996 goto err_destroy_cmd_mutex;
998 pthread_mutex_lock(&mutex);
1000 list_for_each_entry(tmp_ucnt, &uid_cnt_list, link)
1001 if (tmp_ucnt->uid == uid) {
1002 ucnt = tmp_ucnt;
1003 break;
1005 if (!ucnt) {
1006 rc = -ENOMEM;
1007 ucnt = calloc(1, sizeof(*ucnt));
1008 if (!ucnt)
1009 goto err_unlock;
1010 ucnt->uid = uid;
1011 list_add(&ucnt->link, &uid_cnt_list);
1014 rc = -EBUSY;
1015 if (ucnt->nr_os + 1 > umax_streams)
1016 goto err_unlock;
1018 /* everything looks fine, allocate id and init stream */
1019 rc = -EBUSY;
1020 os->id = find_next_zero_bit(os_id_bitmap, max_streams, 0);
1021 if (os->id >= max_streams)
1022 goto err_unlock;
1023 __set_bit(os->id, os_id_bitmap);
1025 os->cmd_fd = cmd_sock;
1026 os->notify_tx = notify[1];
1027 os->notify_rx = notify[0];
1028 os->pid = pid;
1029 os->pgrp = pgrp;
1030 os->uid = uid;
1031 os->gid = gid;
1032 os->ucnt = ucnt;
1033 os->se = se;
1035 rc = os_create_shared_memory(os, mmap_size);
1036 if (rc)
1037 goto err_unlock;
1039 memset(os->vol, -1, sizeof(os->vol));
1040 memset(os->vol_set, -1, sizeof(os->vol));
1042 list_add(&os->link, os_tbl_head(os->id));
1043 list_add(&os->pgrp_link, os_pgrp_tbl_head(os->pgrp));
1045 ucnt->nr_os++;
1046 *osp = os;
1047 pthread_mutex_unlock(&mutex);
1048 return 0;
1050 err_unlock:
1051 pthread_mutex_unlock(&mutex);
1052 pthread_mutex_destroy(&os->mmap_mutex);
1053 err_destroy_cmd_mutex:
1054 pthread_mutex_destroy(&os->cmd_mutex);
1055 err_free:
1056 free(os);
1057 return rc;
1060 static void shutdown_notification(struct ossp_stream *os)
1062 struct ossp_notify obituary = { .magic = OSSP_NOTIFY_MAGIC,
1063 .opcode = OSSP_NOTIFY_OBITUARY };
1064 ssize_t ret;
1067 * Shutdown notification for this stream. We politely ask
1068 * notify_poller to shut the receive side down to avoid racing
1069 * with it.
1071 while (os->notify_rx >= 0) {
1072 ret = write(os->notify_tx, &obituary, sizeof(obituary));
1073 if (ret <= 0) {
1074 if (ret == 0)
1075 warn_os(os, "unexpected EOF on notify_tx");
1076 else if (errno != EPIPE)
1077 warn_ose(os, -errno,
1078 "unexpected error on notify_tx");
1079 close(os->notify_rx);
1080 os->notify_rx = -1;
1081 break;
1084 if (ret != sizeof(obituary))
1085 warn_os(os, "short transfer on notify_tx");
1086 pthread_cond_wait(&notify_poller_kill_wait, &mutex);
1090 static void put_os(struct ossp_stream *os)
1092 if (!os)
1093 return;
1095 pthread_mutex_lock(&mutex);
1097 assert(os->refcnt);
1098 if (--os->refcnt) {
1099 pthread_mutex_unlock(&mutex);
1100 return;
1103 os->dead = 1;
1104 shutdown_notification(os);
1106 dbg0_os(os, "DESTROY");
1108 list_del_init(&os->link);
1109 list_del_init(&os->pgrp_link);
1110 list_del_init(&os->notify_link);
1111 os->ucnt->nr_os--;
1113 pthread_mutex_unlock(&mutex);
1115 close(os->cmd_fd);
1116 close(os->notify_tx);
1117 put_mixer(os->mixer);
1118 pthread_mutex_destroy(&os->cmd_mutex);
1119 pthread_mutex_destroy(&os->mmap_mutex);
1121 pthread_mutex_lock(&mutex);
1122 dbg1_os(os, "stream dead, requesting reaping");
1123 list_add_tail(&os->link, &slave_corpse_list);
1124 pthread_cond_signal(&slave_reaper_wait);
1125 pthread_mutex_unlock(&mutex);
1128 static void set_extra_env(pid_t pid)
1130 char procenviron[32];
1131 const int step = 1024;
1132 char *data = malloc(step + 1);
1133 int ofs = 0;
1134 int fd;
1135 int ret;
1137 if (!data)
1138 return;
1140 sprintf(procenviron, "/proc/%d/environ", pid);
1141 fd = open(procenviron, O_RDONLY);
1142 if (fd < 0)
1143 return;
1146 * There should really be a 'read whole file to a newly allocated
1147 * buffer' function.
1149 while ((ret = read(fd, data + ofs, step)) > 0) {
1150 char *newdata;
1151 ofs += ret;
1152 newdata = realloc(data, ofs + step + 1);
1153 if (!newdata) {
1154 ret = -1;
1155 break;
1157 data = newdata;
1159 if (ret == 0) {
1160 char *ptr = data;
1161 /* Append the extra 0 for end condition */
1162 data[ofs] = 0;
1164 while ((ret = strlen(ptr)) > 0) {
1166 * Copy all PULSE variables and DISPLAY so that
1167 * ssh -X remotehost 'mplayer -ao oss' will work
1169 if (!strncmp(ptr, "DISPLAY=", 8) ||
1170 !strncmp(ptr, "PULSE_", 6))
1171 putenv(ptr);
1172 ptr += ret + 1;
1176 free(data);
1177 close(fd);
1180 static int create_os(const char *slave_path,
1181 size_t stream_size, size_t mmap_size,
1182 pid_t pid, pid_t pgrp, uid_t uid, gid_t gid,
1183 struct fuse_session *se, struct ossp_stream **osp)
1185 static pthread_mutex_t create_mutex = PTHREAD_MUTEX_INITIALIZER;
1186 int cmd_sock[2] = { -1, -1 };
1187 int notify_sock[2] = { -1, -1 };
1188 struct ossp_stream *os = NULL;
1189 struct epoll_event ev = { };
1190 int i, rc;
1193 * Only one thread can be creating a stream. This is to avoid
1194 * leaking unwanted fds into slaves.
1196 pthread_mutex_lock(&create_mutex);
1198 /* prepare communication channels */
1199 if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd_sock) ||
1200 socketpair(AF_UNIX, SOCK_STREAM, 0, notify_sock)) {
1201 rc = -errno;
1202 warn_e(rc, "failed to create slave command channel");
1203 goto close_all;
1206 if (fcntl(notify_sock[0], F_SETFL, O_NONBLOCK) < 0) {
1207 rc = -errno;
1208 warn_e(rc, "failed to set NONBLOCK on notify sock");
1209 goto close_all;
1213 * Alloc stream which will be responsible for all server side
1214 * resources from now on.
1216 rc = alloc_os(stream_size, mmap_size, pid, pgrp, uid, gid, cmd_sock[0],
1217 notify_sock, se, &os);
1218 if (rc) {
1219 warn_e(rc, "failed to allocate stream for %d", pid);
1220 goto close_all;
1223 rc = -ENOMEM;
1224 os->mixer = get_mixer(pgrp);
1225 if (!os->mixer)
1226 goto put_os;
1229 * Register notification. If successful, notify_poller has
1230 * custody of notify_rx fd.
1232 pthread_mutex_lock(&mutex);
1233 list_add(&os->notify_link, os_notify_tbl_head(os->notify_rx));
1234 pthread_mutex_unlock(&mutex);
1236 ev.events = EPOLLIN;
1237 ev.data.fd = notify_sock[0];
1238 if (epoll_ctl(notify_epfd, EPOLL_CTL_ADD, notify_sock[0], &ev)) {
1240 * Without poller watching this notify sock, poller
1241 * shutdown sequence in shutdown_notification() can't
1242 * be used. Kill notification rx manually.
1244 rc = -errno;
1245 warn_ose(os, rc, "failed to add notify epoll");
1246 close(os->notify_rx);
1247 os->notify_rx = -1;
1248 goto put_os;
1251 /* start slave */
1252 os->slave_pid = fork();
1253 if (os->slave_pid < 0) {
1254 rc = -errno;
1255 warn_ose(os, rc, "failed to fork slave");
1256 goto put_os;
1259 if (os->slave_pid == 0) {
1260 /* child */
1261 char id_str[2][16], fd_str[3][16];
1262 char mmap_size_str[32];
1263 char log_str[16], slave_path_copy[PATH_MAX];
1264 char *argv[] = { slave_path_copy, "-u", id_str[0],
1265 "-g", id_str[1], "-c", fd_str[0],
1266 "-n", fd_str[1], "-m", fd_str[2],
1267 "-s", mmap_size_str,
1268 "-l", log_str, NULL, NULL };
1269 struct passwd *pwd;
1271 /* drop stuff we don't need */
1272 if (close(cmd_sock[0]) || close(notify_sock[0]))
1273 fatal_e(-errno, "failed to close server pipe fds");
1275 clearenv();
1276 pwd = getpwuid(os->uid);
1277 if (pwd) {
1278 setenv("LOGNAME", pwd->pw_name, 1);
1279 setenv("USER", pwd->pw_name, 1);
1280 setenv("HOME", pwd->pw_dir, 1);
1282 /* Set extra environment variables from the caller */
1283 set_extra_env(pid);
1285 /* prep and exec */
1286 slave_path_copy[sizeof(slave_path_copy) - 1] = '\0';
1287 strncpy(slave_path_copy, slave_path, sizeof(slave_path_copy) - 1);
1288 if (slave_path_copy[sizeof(slave_path_copy) - 1] != '\0') {
1289 rc = -errno;
1290 err_ose(os, rc, "slave path too long");
1291 goto child_fail;
1294 snprintf(id_str[0], sizeof(id_str[0]), "%d", os->uid);
1295 snprintf(id_str[1], sizeof(id_str[0]), "%d", os->gid);
1296 snprintf(fd_str[0], sizeof(fd_str[0]), "%d", cmd_sock[1]);
1297 snprintf(fd_str[1], sizeof(fd_str[1]), "%d", notify_sock[1]);
1298 snprintf(fd_str[2], sizeof(fd_str[2]), "%d", os->mmap_fd);
1299 snprintf(mmap_size_str, sizeof(mmap_size_str), "0x%zx",
1300 os->mmap_size);
1301 snprintf(log_str, sizeof(log_str), "%d", ossp_log_level);
1302 if (ossp_log_timestamp)
1303 argv[ARRAY_SIZE(argv) - 2] = "-t";
1305 execv(slave_path, argv);
1306 rc = -errno;
1307 err_ose(os, rc, "execv failed for <%d>", pid);
1308 child_fail:
1309 _exit(1);
1312 /* turn on CLOEXEC on all server side fds */
1313 if (fcntl(os->cmd_fd, F_SETFD, FD_CLOEXEC) < 0 ||
1314 fcntl(os->notify_tx, F_SETFD, FD_CLOEXEC) < 0 ||
1315 fcntl(os->notify_rx, F_SETFD, FD_CLOEXEC) < 0) {
1316 rc = -errno;
1317 err_ose(os, rc, "failed to set CLOEXEC on server side fds");
1318 goto put_os;
1321 dbg0_os(os, "CREATE slave=%d %s", os->slave_pid, slave_path);
1322 dbg0_os(os, " client=%d cmd=%d:%d notify=%d:%d mmap=%d:%zu",
1323 pid, cmd_sock[0], cmd_sock[1], notify_sock[0], notify_sock[1],
1324 os->mmap_fd, os->mmap_size);
1326 close(os->mmap_fd);
1327 os->mmap_fd = -1;
1329 *osp = os;
1330 rc = 0;
1331 goto close_client_fds;
1333 put_os:
1334 put_os(os);
1335 close_client_fds:
1336 close(cmd_sock[1]);
1337 pthread_mutex_unlock(&create_mutex);
1338 return rc;
1340 close_all:
1341 for (i = 0; i < 2; i++) {
1342 close(cmd_sock[i]);
1343 close(notify_sock[i]);
1345 pthread_mutex_unlock(&create_mutex);
1346 return rc;
1349 static void dsp_open_common(fuse_req_t req, struct fuse_file_info *fi,
1350 struct fuse_session *se)
1352 const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1353 struct ossp_dsp_open_arg arg = { };
1354 struct ossp_stream *os = NULL;
1355 struct ossp_mixer *mixer;
1356 struct ossp_dsp_stream *dsps;
1357 struct ossp_mixer_cmd mxcmd;
1358 pid_t pgrp;
1359 ssize_t ret;
1361 ret = get_proc_self_info(fuse_ctx->pid, &pgrp, NULL, 0);
1362 if (ret) {
1363 err_e(ret, "get_proc_self_info(%d) failed", fuse_ctx->pid);
1364 goto err;
1367 ret = create_os(dsp_slave_path, sizeof(*dsps), DSPS_MMAP_SIZE,
1368 fuse_ctx->pid, pgrp, fuse_ctx->uid, fuse_ctx->gid,
1369 se, &os);
1370 if (ret)
1371 goto err;
1372 dsps = os_to_dsps(os);
1373 mixer = os->mixer;
1375 switch (fi->flags & O_ACCMODE) {
1376 case O_WRONLY:
1377 dsps->rw |= 1 << PLAY;
1378 break;
1379 case O_RDONLY:
1380 dsps->rw |= 1 << REC;
1381 break;
1382 case O_RDWR:
1383 dsps->rw |= (1 << PLAY) | (1 << REC);
1384 break;
1385 default:
1386 assert(0);
1389 arg.flags = fi->flags;
1390 arg.opener_pid = os->pid;
1391 ret = exec_simple_cmd(&dsps->os, OSSP_DSP_OPEN, &arg, NULL);
1392 if (ret < 0) {
1393 put_os(os);
1394 goto err;
1397 memcpy(os->vol, mixer->vol, sizeof(os->vol));
1398 if (os->vol[PLAY][0] >= 0 || os->vol[REC][0] >= 0) {
1399 init_mixer_cmd(&mxcmd, mixer);
1400 memcpy(mxcmd.set.vol, os->vol, sizeof(os->vol));
1401 exec_mixer_cmd(&mxcmd, os);
1402 finish_mixer_cmd(&mxcmd);
1405 fi->direct_io = 1;
1406 fi->nonseekable = 1;
1407 fi->fh = os->id;
1409 fuse_reply_open(req, fi);
1410 return;
1412 err:
1413 fuse_reply_err(req, -ret);
1416 static void dsp_open(fuse_req_t req, struct fuse_file_info *fi)
1418 dsp_open_common(req, fi, dsp_se);
1421 static void adsp_open(fuse_req_t req, struct fuse_file_info *fi)
1423 dsp_open_common(req, fi, adsp_se);
1426 static void dsp_release(fuse_req_t req, struct fuse_file_info *fi)
1428 struct ossp_stream *os;
1430 os = find_os(fi->fh);
1431 if (os) {
1432 put_os(os);
1433 fuse_reply_err(req, 0);
1434 } else
1435 fuse_reply_err(req, EBADF);
1438 static void dsp_read(fuse_req_t req, size_t size, off_t off,
1439 struct fuse_file_info *fi)
1441 struct ossp_dsp_rw_arg arg = { };
1442 struct ossp_stream *os;
1443 struct ossp_dsp_stream *dsps;
1444 void *buf = NULL;
1445 ssize_t ret;
1447 ret = -EBADF;
1448 os = find_os(fi->fh);
1449 if (!os)
1450 goto out;
1451 dsps = os_to_dsps(os);
1453 ret = -EINVAL;
1454 if (!(dsps->rw & (1 << REC)))
1455 goto out;
1457 ret = -ENXIO;
1458 if (dsps->mmapped)
1459 goto out;
1461 ret = -ENOMEM;
1462 buf = malloc(size);
1463 if (!buf)
1464 goto out;
1466 arg.nonblock = (fi->flags & O_NONBLOCK) || dsps->nonblock;
1468 ret = exec_cmd(os, OSSP_DSP_READ, &arg, sizeof(arg),
1469 NULL, 0, NULL, 0, buf, &size, -1);
1470 out:
1471 if (ret >= 0)
1472 fuse_reply_buf(req, buf, size);
1473 else
1474 fuse_reply_err(req, -ret);
1476 free(buf);
1479 static void dsp_write(fuse_req_t req, const char *buf, size_t size, off_t off,
1480 struct fuse_file_info *fi)
1482 struct ossp_dsp_rw_arg arg = { };
1483 struct ossp_stream *os;
1484 struct ossp_dsp_stream *dsps;
1485 ssize_t ret;
1487 ret = -EBADF;
1488 os = find_os(fi->fh);
1489 if (!os)
1490 goto out;
1491 dsps = os_to_dsps(os);
1493 ret = -EINVAL;
1494 if (!(dsps->rw & (1 << PLAY)))
1495 goto out;
1497 ret = -ENXIO;
1498 if (dsps->mmapped)
1499 goto out;
1501 arg.nonblock = (fi->flags & O_NONBLOCK) || dsps->nonblock;
1503 ret = exec_cmd(os, OSSP_DSP_WRITE, &arg, sizeof(arg),
1504 buf, size, NULL, 0, NULL, NULL, -1);
1505 out:
1506 if (ret >= 0)
1507 fuse_reply_write(req, ret);
1508 else
1509 fuse_reply_err(req, -ret);
1512 static void dsp_poll(fuse_req_t req, struct fuse_file_info *fi,
1513 struct fuse_pollhandle *ph)
1515 int notify = ph != NULL;
1516 unsigned revents = 0;
1517 struct ossp_stream *os;
1518 ssize_t ret;
1520 ret = -EBADF;
1521 os = find_os(fi->fh);
1522 if (!os)
1523 goto out;
1525 if (ph) {
1526 pthread_mutex_lock(&mutex);
1527 if (os->ph)
1528 fuse_pollhandle_destroy(os->ph);
1529 os->ph = ph;
1530 pthread_mutex_unlock(&mutex);
1533 ret = exec_simple_cmd(os, OSSP_DSP_POLL, &notify, &revents);
1534 out:
1535 if (ret >= 0)
1536 fuse_reply_poll(req, revents);
1537 else
1538 fuse_reply_err(req, -ret);
1541 static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg,
1542 struct fuse_file_info *fi, unsigned int flags,
1543 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
1545 /* some ioctl constants are long and has the highest bit set */
1546 unsigned cmd = signed_cmd;
1547 struct ossp_stream *os;
1548 struct ossp_dsp_stream *dsps;
1549 enum ossp_opcode op;
1550 ssize_t ret;
1551 int i;
1553 ret = -EBADF;
1554 os = find_os(fi->fh);
1555 if (!os)
1556 goto err;
1557 dsps = os_to_dsps(os);
1559 /* mixer commands are allowed on DSP devices */
1560 if (((cmd >> 8) & 0xff) == 'M') {
1561 mixer_do_ioctl(req, os->mixer, cmd, uarg, in_buf, in_bufsz,
1562 out_bufsz);
1563 return;
1566 /* and the rest */
1567 switch (cmd) {
1568 case OSS_GETVERSION:
1569 i = SNDRV_OSS_VERSION;
1570 PREP_UARG(NULL, &i);
1571 fuse_reply_ioctl(req, 0, &i, sizeof(i));
1572 break;
1574 case SNDCTL_DSP_GETCAPS:
1575 i = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER |
1576 #ifdef OSSP_MMAP
1577 DSP_CAP_MMAP |
1578 #endif
1579 DSP_CAP_MULTI;
1580 PREP_UARG(NULL, &i);
1581 fuse_reply_ioctl(req, 0, &i, sizeof(i));
1582 break;
1584 case SNDCTL_DSP_NONBLOCK:
1585 dsps->nonblock = 1;
1586 ret = 0;
1587 fuse_reply_ioctl(req, 0, NULL, 0);
1588 break;
1590 case SNDCTL_DSP_RESET: op = OSSP_DSP_RESET; goto nd;
1591 case SNDCTL_DSP_SYNC: op = OSSP_DSP_SYNC; goto nd;
1592 case SNDCTL_DSP_POST: op = OSSP_DSP_POST; goto nd;
1594 ret = exec_simple_cmd(&dsps->os, op, NULL, NULL);
1595 if (ret)
1596 goto err;
1597 fuse_reply_ioctl(req, 0, NULL, 0);
1598 break;
1600 case SOUND_PCM_READ_RATE: op = OSSP_DSP_GET_RATE; goto ri;
1601 case SOUND_PCM_READ_BITS: op = OSSP_DSP_GET_FORMAT; goto ri;
1602 case SOUND_PCM_READ_CHANNELS: op = OSSP_DSP_GET_CHANNELS; goto ri;
1603 case SNDCTL_DSP_GETBLKSIZE: op = OSSP_DSP_GET_BLKSIZE; goto ri;
1604 case SNDCTL_DSP_GETFMTS: op = OSSP_DSP_GET_FORMATS; goto ri;
1605 case SNDCTL_DSP_GETTRIGGER: op = OSSP_DSP_GET_TRIGGER; goto ri;
1607 PREP_UARG(NULL, &i);
1608 ret = exec_simple_cmd(&dsps->os, op, NULL, &i);
1609 if (ret)
1610 goto err;
1611 fuse_reply_ioctl(req, 0, &i, sizeof(i));
1612 break;
1614 case SNDCTL_DSP_SPEED: op = OSSP_DSP_SET_RATE; goto wi;
1615 case SNDCTL_DSP_SETFMT: op = OSSP_DSP_SET_FORMAT; goto wi;
1616 case SNDCTL_DSP_CHANNELS: op = OSSP_DSP_SET_CHANNELS; goto wi;
1617 case SNDCTL_DSP_SUBDIVIDE: op = OSSP_DSP_SET_SUBDIVISION; goto wi;
1619 PREP_UARG(&i, &i);
1620 ret = exec_simple_cmd(&dsps->os, op, &i, &i);
1621 if (ret)
1622 goto err;
1623 fuse_reply_ioctl(req, 0, &i, sizeof(i));
1624 break;
1626 case SNDCTL_DSP_STEREO:
1627 PREP_UARG(NULL, &i);
1628 i = 2;
1629 ret = exec_simple_cmd(&dsps->os, OSSP_DSP_SET_CHANNELS, &i, &i);
1630 i--;
1631 if (ret)
1632 goto err;
1633 fuse_reply_ioctl(req, 0, &i, sizeof(i));
1634 break;
1636 case SNDCTL_DSP_SETFRAGMENT:
1637 PREP_UARG(&i, NULL);
1638 ret = exec_simple_cmd(&dsps->os,
1639 OSSP_DSP_SET_FRAGMENT, &i, NULL);
1640 if (ret)
1641 goto err;
1642 fuse_reply_ioctl(req, 0, NULL, 0);
1643 break;
1645 case SNDCTL_DSP_SETTRIGGER:
1646 PREP_UARG(&i, NULL);
1647 ret = exec_simple_cmd(&dsps->os,
1648 OSSP_DSP_SET_TRIGGER, &i, NULL);
1649 if (ret)
1650 goto err;
1651 fuse_reply_ioctl(req, 0, NULL, 0);
1652 break;
1654 case SNDCTL_DSP_GETOSPACE:
1655 case SNDCTL_DSP_GETISPACE: {
1656 struct audio_buf_info info;
1658 ret = -EINVAL;
1659 if (cmd == SNDCTL_DSP_GETOSPACE) {
1660 if (!(dsps->rw & (1 << PLAY)))
1661 goto err;
1662 op = OSSP_DSP_GET_OSPACE;
1663 } else {
1664 if (!(dsps->rw & (1 << REC)))
1665 goto err;
1666 op = OSSP_DSP_GET_ISPACE;
1669 PREP_UARG(NULL, &info);
1670 ret = exec_simple_cmd(&dsps->os, op, NULL, &info);
1671 if (ret)
1672 goto err;
1673 fuse_reply_ioctl(req, 0, &info, sizeof(info));
1674 break;
1677 case SNDCTL_DSP_GETOPTR:
1678 case SNDCTL_DSP_GETIPTR: {
1679 struct count_info info;
1681 op = cmd == SNDCTL_DSP_GETOPTR ? OSSP_DSP_GET_OPTR
1682 : OSSP_DSP_GET_IPTR;
1683 PREP_UARG(NULL, &info);
1684 ret = exec_simple_cmd(&dsps->os, op, NULL, &info);
1685 if (ret)
1686 goto err;
1687 fuse_reply_ioctl(req, 0, &info, sizeof(info));
1688 break;
1691 case SNDCTL_DSP_GETODELAY:
1692 PREP_UARG(NULL, &i);
1693 i = 0;
1694 ret = exec_simple_cmd(&dsps->os, OSSP_DSP_GET_ODELAY, NULL, &i);
1695 fuse_reply_ioctl(req, ret, &i, sizeof(i)); /* always copy out result, 0 on err */
1696 break;
1698 case SOUND_PCM_WRITE_FILTER:
1699 case SOUND_PCM_READ_FILTER:
1700 ret = -EIO;
1701 goto err;
1703 case SNDCTL_DSP_MAPINBUF:
1704 case SNDCTL_DSP_MAPOUTBUF:
1705 ret = -EINVAL;
1706 goto err;
1708 case SNDCTL_DSP_SETSYNCRO:
1709 case SNDCTL_DSP_SETDUPLEX:
1710 case SNDCTL_DSP_PROFILE:
1711 fuse_reply_ioctl(req, 0, NULL, 0);
1712 break;
1714 default:
1715 warn_os(os, "unknown ioctl 0x%x", cmd);
1716 ret = -EINVAL;
1717 goto err;
1719 return;
1721 err:
1722 fuse_reply_err(req, -ret);
1725 #ifdef OSSP_MMAP
1726 static int dsp_mmap_dir(int prot)
1728 if (!(prot & PROT_WRITE))
1729 return REC;
1730 return PLAY;
1733 static int dsp_dir_to_mapid(struct ossp_stream *os, int dir)
1735 return os->id * 2 + dir + 1;
1738 static int dsp_mapid_to_dir(int map_id)
1740 return (map_id - 1) & 1;
1743 static void dsp_mmap(fuse_req_t req, uint64_t addr, size_t len, int prot,
1744 int flags, off_t offset, struct fuse_file_info *fi)
1746 int dir = dsp_mmap_dir(prot);
1747 struct ossp_dsp_mmap_arg arg = { };
1748 struct ossp_stream *os;
1749 struct ossp_dsp_stream *dsps;
1750 ssize_t ret;
1752 os = find_os(fi->fh);
1753 if (!os) {
1754 fuse_reply_err(req, EBADF);
1755 return;
1757 dsps = os_to_dsps(os);
1759 if (len > os->mmap_size / 2) {
1760 fuse_reply_err(req, EINVAL);
1761 return;
1764 pthread_mutex_lock(&os->mmap_mutex);
1766 ret = -EBUSY;
1767 if (dsps->mmapped & (1 << dir))
1768 goto out_unlock;
1770 arg.dir = dir;
1771 arg.size = len;
1773 ret = exec_simple_cmd(os, OSSP_DSP_MMAP, &arg, NULL);
1774 if (ret == 0)
1775 dsps->mmapped |= 1 << dir;
1777 out_unlock:
1778 pthread_mutex_unlock(&os->mmap_mutex);
1780 if (ret == 0)
1781 fuse_reply_mmap(req, dsp_dir_to_mapid(os, dir), os->mmap_size / 2);
1782 else
1783 fuse_reply_err(req, -ret);
1786 static void dsp_munmap(fuse_req_t req, uint64_t map_id, size_t length,
1787 struct fuse_file_info *fi)
1789 struct ossp_stream *os;
1790 struct ossp_dsp_stream *dsps;
1791 int dir, rc;
1793 os = find_os(fi->fh);
1794 if (!os)
1795 goto out;
1796 dsps = os_to_dsps(os);
1798 pthread_mutex_lock(&os->mmap_mutex);
1800 dir = dsp_mapid_to_dir(map_id);
1801 if (dsp_dir_to_mapid(os, dir) != map_id ||
1802 (dir != PLAY && dir != REC) || length != os->mmap_size / 2) {
1803 warn_os(os, "invalid munmap request map_id=%llu len=%zu mmapped=0x%x",
1804 (unsigned long long) map_id, length, dsps->mmapped);
1805 goto out_unlock;
1808 rc = exec_simple_cmd(os, OSSP_DSP_MUNMAP, &dir, NULL);
1809 if (rc)
1810 warn_ose(os, rc, "MUNMAP failed for dir=%d", dir);
1812 dsps->mmapped &= ~(1 << dir);
1814 out_unlock:
1815 pthread_mutex_unlock(&os->mmap_mutex);
1816 out:
1817 fuse_reply_err(req, -rc);
1820 static void fill_mmap_buffer(struct ossp_stream *os)
1822 int rc;
1823 struct fuse_chan *ch = fuse_session_next_chan(os->se, NULL);
1824 unsigned mapid = dsp_dir_to_mapid(os, PLAY);
1825 size_t pos = os->mmap_transfer[PLAY].pos;
1826 size_t bytes = os->mmap_transfer[PLAY].bytes;
1828 dbg1_os(os, "fill mmap buffer pos=%zu bytes=%zu", pos, bytes);
1830 rc = fuse_lowlevel_notify_retrieve(ch, mapid, bytes, pos, os);
1831 if (rc)
1832 sem_post(&os->mmap_transfer[PLAY].sem);
1835 static void dsp_retrieve_reply(fuse_req_t req, void *cookie, uint64_t map_id,
1836 off_t offset, struct fuse_bufvec *bufv)
1838 struct ossp_stream *os = cookie;
1839 size_t size = fuse_buf_size(bufv);
1840 struct fuse_bufvec dest = FUSE_BUFVEC_INIT(size);
1841 ssize_t res;
1843 if (dsp_mapid_to_dir(map_id) != PLAY ||
1844 dsp_dir_to_mapid(os, PLAY) != map_id ||
1845 offset != os->mmap_transfer[PLAY].pos ||
1846 size > os->mmap_transfer[PLAY].bytes) {
1847 warn_os(os, "invalid retrieve reply map_id=%llu offset=%llu, size=%zu",
1848 (unsigned long long) map_id,
1849 (unsigned long long) offset, size);
1850 sem_post(&os->mmap_transfer[PLAY].sem);
1851 return;
1854 dest.buf[0].mem = os->mmap_addr[PLAY] + offset;
1856 res = fuse_buf_copy(&dest, bufv, 0);
1857 if (res < 0)
1858 warn_ose(os, res, "dsp_retrieve_reply: buffer copy");
1859 else if ((size_t) res < size) {
1860 warn_os(os, "dsp_retrieve_reply: short buffer copy");
1863 if (size < os->mmap_transfer[PLAY].bytes) {
1864 os->mmap_transfer[PLAY].bytes -= size;
1865 os->mmap_transfer[PLAY].pos += size;
1867 fill_mmap_buffer(os);
1868 } else {
1869 sem_post(&os->mmap_transfer[PLAY].sem);
1873 static void store_mmap_buffer(struct ossp_stream *os)
1875 int rc;
1876 struct fuse_chan *ch = fuse_session_next_chan(os->se, NULL);
1877 unsigned mapid = dsp_dir_to_mapid(os, REC);
1878 size_t pos = os->mmap_transfer[REC].pos;
1879 size_t bytes = os->mmap_transfer[REC].bytes;
1880 struct fuse_bufvec bufv = FUSE_BUFVEC_INIT(bytes);
1882 dbg1_os(os, "store mmap buffer pos=%zu bytes=%zu", pos, bytes);
1884 bufv.buf[0].mem = os->mmap_addr[REC] + pos;
1886 rc = fuse_lowlevel_notify_store(ch, mapid, pos, &bufv, 0);
1887 if (rc < 0)
1888 warn_ose(os, rc, "store_mmap_buffer: failure");
1890 sem_post(&os->mmap_transfer[REC].sem);
1892 #endif
1895 /***************************************************************************
1896 * Notify poller
1899 static void *notify_poller(void *arg)
1901 struct epoll_event events[1024];
1902 int i, nfds;
1904 repeat:
1905 nfds = epoll_wait(notify_epfd, events, ARRAY_SIZE(events), -1);
1906 for (i = 0; i < nfds; i++) {
1907 int do_notify = 0;
1908 struct ossp_stream *os;
1909 struct ossp_notify notify;
1910 ssize_t ret;
1912 os = find_os_by_notify_rx(events[i].data.fd);
1913 if (!os) {
1914 err("can't find stream for notify_rx fd %d",
1915 events[i].data.fd);
1916 epoll_ctl(notify_epfd, EPOLL_CTL_DEL, events[i].data.fd,
1917 NULL);
1918 /* we don't know what's going on, don't close the fd */
1919 continue;
1922 while ((ret = read(os->notify_rx,
1923 &notify, sizeof(notify))) > 0) {
1924 if (os->dead)
1925 continue;
1926 if (ret != sizeof(notify)) {
1927 warn_os(os, "short read on notify_rx (%zu, "
1928 "expected %zu), killing the stream",
1929 ret, sizeof(notify));
1930 os->dead = 1;
1931 break;
1933 if (notify.magic != OSSP_NOTIFY_MAGIC) {
1934 warn_os(os, "invalid magic on notification, "
1935 "killing the stream");
1936 os->dead = 1;
1937 break;
1940 if (notify.opcode >= OSSP_NR_NOTIFY_OPCODES)
1941 goto unknown;
1943 dbg1_os(os, "NOTIFY %s", ossp_notify_str[notify.opcode]);
1945 switch (notify.opcode) {
1946 case OSSP_NOTIFY_POLL:
1947 do_notify = 1;
1948 break;
1949 case OSSP_NOTIFY_OBITUARY:
1950 os->dead = 1;
1951 break;
1952 case OSSP_NOTIFY_VOLCHG:
1953 pthread_mutex_lock(&mixer_mutex);
1954 os->mixer->modify_counter++;
1955 pthread_mutex_unlock(&mixer_mutex);
1956 break;
1957 #ifdef OSSP_MMAP
1958 case OSSP_NOTIFY_FILL:
1959 fill_mmap_buffer(os);
1960 break;
1961 case OSSP_NOTIFY_STORE:
1962 store_mmap_buffer(os);
1963 break;
1964 #endif
1965 default:
1966 unknown:
1967 warn_os(os, "unknown notification %d",
1968 notify.opcode);
1971 if (ret == 0)
1972 os->dead = 1;
1973 else if (ret < 0 && errno != EAGAIN) {
1974 warn_ose(os, -errno, "read fail on notify fd");
1975 os->dead = 1;
1978 if (!do_notify && !os->dead)
1979 continue;
1981 pthread_mutex_lock(&mutex);
1983 if (os->ph) {
1984 fuse_lowlevel_notify_poll(os->ph);
1985 fuse_pollhandle_destroy(os->ph);
1986 os->ph = NULL;
1989 if (os->dead) {
1990 dbg0_os(os, "removing %d from notify poll list",
1991 os->notify_rx);
1992 epoll_ctl(notify_epfd, EPOLL_CTL_DEL, os->notify_rx,
1993 NULL);
1994 close(os->notify_rx);
1995 os->notify_rx = -1;
1996 pthread_cond_broadcast(&notify_poller_kill_wait);
1999 pthread_mutex_unlock(&mutex);
2001 goto repeat;
2003 /* not reachable */
2004 return NULL;
2008 /***************************************************************************
2009 * Slave corpse reaper
2012 static void *slave_reaper(void *arg)
2014 struct ossp_stream *os;
2015 int status;
2016 pid_t pid;
2018 pthread_mutex_lock(&mutex);
2019 repeat:
2020 while (list_empty(&slave_corpse_list))
2021 pthread_cond_wait(&slave_reaper_wait, &mutex);
2023 os = list_first_entry(&slave_corpse_list, struct ossp_stream, link);
2024 list_del_init(&os->link);
2026 pthread_mutex_unlock(&mutex);
2028 do {
2029 pid = waitpid(os->slave_pid, &status, 0);
2030 } while (pid < 0 && errno == EINTR);
2032 if (pid < 0) {
2033 if (errno == ECHILD)
2034 warn_ose(os, -errno, "slave %d already gone?",
2035 os->slave_pid);
2036 else
2037 fatal_e(-errno, "waitpid(%d) failed", os->slave_pid);
2040 pthread_mutex_lock(&mutex);
2042 dbg1_os(os, "slave %d reaped", os->slave_pid);
2043 __clear_bit(os->id, os_id_bitmap);
2044 if (os->mmap_size) {
2045 sem_destroy(&os->mmap_transfer[PLAY].sem);
2046 sem_destroy(&os->mmap_transfer[REC].sem);
2048 munmap(os->mmap, os->mmap_size + 2 * sizeof(struct ossp_transfer));
2049 close(os->mmap_fd);
2052 free(os);
2054 goto repeat;
2056 /* not reachable */
2057 return NULL;
2061 /***************************************************************************
2062 * Stuff to bind and start everything
2065 static void ossp_daemonize(void)
2067 int fd, pfd[2];
2068 pid_t pid;
2069 ssize_t ret;
2070 int err;
2072 fd = open("/dev/null", O_RDWR);
2073 if (fd >= 0) {
2074 dup2(fd, 0);
2075 dup2(fd, 1);
2076 dup2(fd, 2);
2077 if (fd > 2)
2078 close(fd);
2081 if (pipe(pfd))
2082 fatal_e(-errno, "failed to create pipe for init wait");
2084 if (fcntl(pfd[0], F_SETFD, FD_CLOEXEC) < 0 ||
2085 fcntl(pfd[1], F_SETFD, FD_CLOEXEC) < 0)
2086 fatal_e(-errno, "failed to set CLOEXEC on init wait pipe");
2088 pid = fork();
2089 if (pid < 0)
2090 fatal_e(-errno, "failed to fork for daemon");
2092 if (pid == 0) {
2093 close(pfd[0]);
2094 init_wait_fd = pfd[1];
2096 /* be evil, my child */
2097 chdir("/");
2098 setsid();
2099 return;
2102 /* wait for init completion and pass over success indication */
2103 close(pfd[1]);
2105 do {
2106 ret = read(pfd[0], &err, sizeof(err));
2107 } while (ret < 0 && errno == EINTR);
2109 if (ret == sizeof(err) && err == 0)
2110 exit(0);
2112 fatal("daemon init failed ret=%zd err=%d", ret, err);
2113 exit(1);
2116 static void ossp_init_done(void *userdata)
2118 /* init complete, notify parent if it's waiting */
2119 if (init_wait_fd >= 0) {
2120 ssize_t ret;
2121 int err = 0;
2123 ret = write(init_wait_fd, &err, sizeof(err));
2124 if (ret != sizeof(err))
2125 fatal_e(-errno, "failed to notify init completion, "
2126 "ret=%zd", ret);
2127 close(init_wait_fd);
2128 init_wait_fd = -1;
2132 static const struct cuse_lowlevel_ops mixer_ops = {
2133 .open = mixer_open,
2134 .release = mixer_release,
2135 .ioctl = mixer_ioctl,
2138 static const struct cuse_lowlevel_ops dsp_ops = {
2139 .init_done = ossp_init_done,
2140 .open = dsp_open,
2141 .release = dsp_release,
2142 .read = dsp_read,
2143 .write = dsp_write,
2144 .poll = dsp_poll,
2145 .ioctl = dsp_ioctl,
2146 #ifdef OSSP_MMAP
2147 .mmap = dsp_mmap,
2148 .munmap = dsp_munmap,
2149 .retrieve_reply = dsp_retrieve_reply,
2150 #endif
2153 static const struct cuse_lowlevel_ops adsp_ops = {
2154 .open = adsp_open,
2155 .release = dsp_release,
2156 .read = dsp_read,
2157 .write = dsp_write,
2158 .poll = dsp_poll,
2159 .ioctl = dsp_ioctl,
2160 #ifdef OSSP_MMAP
2161 .mmap = dsp_mmap,
2162 .munmap = dsp_munmap,
2163 .retrieve_reply = dsp_retrieve_reply,
2164 #endif
2167 static const char *usage =
2168 "usage: osspd [options]\n"
2169 "\n"
2170 "options:\n"
2171 " --help print this help message\n"
2172 " --dsp=NAME DSP device name (default dsp)\n"
2173 " --dsp-maj=MAJ DSP device major number (default 14)\n"
2174 " --dsp-min=MIN DSP device minor number (default 3)\n"
2175 " --adsp=NAME Aux DSP device name (default adsp, blank to disable)\n"
2176 " --adsp-maj=MAJ Aux DSP device major number (default 14)\n"
2177 " --adsp-min=MIN Aux DSP device minor number (default 12)\n"
2178 " --mixer=NAME mixer device name (default mixer, blank to disable)\n"
2179 " --mixer-maj=MAJ mixer device major number (default 14)\n"
2180 " --mixer-min=MIN mixer device minor number (default 0)\n"
2181 " --max=MAX maximum number of open streams (default 256)\n"
2182 " --umax=MAX maximum number of open streams per UID (default --max)\n"
2183 " --exit-on-idle exit if idle\n"
2184 " --dsp-slave=PATH DSP slave (default ossp-padsp in the same dir)\n"
2185 " --log=LEVEL log level (0..6)\n"
2186 " --timestamp timestamp log messages\n"
2187 " -v increase verbosity, can be specified multiple times\n"
2188 " -f Run in foreground (don't daemonize)\n"
2189 "\n";
2191 struct ossp_param {
2192 char *dsp_name;
2193 unsigned dsp_major;
2194 unsigned dsp_minor;
2195 char *adsp_name;
2196 unsigned adsp_major;
2197 unsigned adsp_minor;
2198 char *mixer_name;
2199 unsigned mixer_major;
2200 unsigned mixer_minor;
2201 unsigned max_streams;
2202 unsigned umax_streams;
2203 char *dsp_slave_path;
2204 unsigned log_level;
2205 int exit_on_idle;
2206 int timestamp;
2207 int fg;
2208 int help;
2211 #define OSSP_OPT(t, p) { t, offsetof(struct ossp_param, p), 1 }
2213 static const struct fuse_opt ossp_opts[] = {
2214 OSSP_OPT("--dsp=%s", dsp_name),
2215 OSSP_OPT("--dsp-maj=%u", dsp_major),
2216 OSSP_OPT("--dsp-min=%u", dsp_minor),
2217 OSSP_OPT("--adsp=%s", adsp_name),
2218 OSSP_OPT("--adsp-maj=%u", adsp_major),
2219 OSSP_OPT("--adsp-min=%u", adsp_minor),
2220 OSSP_OPT("--mixer=%s", mixer_name),
2221 OSSP_OPT("--mixer-maj=%u", mixer_major),
2222 OSSP_OPT("--mixer-min=%u", mixer_minor),
2223 OSSP_OPT("--max=%u", max_streams),
2224 OSSP_OPT("--umax=%u", umax_streams),
2225 OSSP_OPT("--exit-on-idle", exit_on_idle),
2226 OSSP_OPT("--dsp-slave=%s", dsp_slave_path),
2227 OSSP_OPT("--timestamp", timestamp),
2228 OSSP_OPT("--log=%u", log_level),
2229 OSSP_OPT("-f", fg),
2230 FUSE_OPT_KEY("-h", 0),
2231 FUSE_OPT_KEY("--help", 0),
2232 FUSE_OPT_KEY("-v", 1),
2233 FUSE_OPT_END
2236 static struct fuse_session *setup_ossp_cuse(const struct cuse_lowlevel_ops *ops,
2237 const char *name, int major,
2238 int minor, int argc, char **argv)
2240 char name_buf[128];
2241 const char *bufp = name_buf;
2242 struct cuse_info ci = { .dev_major = major, .dev_minor = minor,
2243 .dev_info_argc = 1, .dev_info_argv = &bufp,
2244 .flags = CUSE_UNRESTRICTED_IOCTL };
2245 struct fuse_session *se;
2246 int fd;
2248 snprintf(name_buf, sizeof(name_buf), "DEVNAME=%s", name);
2250 se = cuse_lowlevel_setup(argc, argv, &ci, ops, NULL, NULL);
2251 if (!se) {
2252 err("failed to setup %s CUSE", name);
2253 return NULL;
2256 fd = fuse_chan_fd(fuse_session_next_chan(se, NULL));
2257 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
2258 err_e(-errno, "failed to set CLOEXEC on %s CUSE fd", name);
2259 cuse_lowlevel_teardown(se);
2260 return NULL;
2263 return se;
2266 static void *cuse_worker(void *arg)
2268 struct fuse_session *se = arg;
2269 int rc;
2271 rc = fuse_session_loop_mt(se);
2272 cuse_lowlevel_teardown(se);
2274 return (void *)(unsigned long)rc;
2277 static int process_arg(void *data, const char *arg, int key,
2278 struct fuse_args *outargs)
2280 struct ossp_param *param = data;
2282 switch (key) {
2283 case 0:
2284 fprintf(stderr, usage);
2285 param->help = 1;
2286 return 0;
2287 case 1:
2288 param->log_level++;
2289 return 0;
2291 return 1;
2294 int main(int argc, char **argv)
2296 static struct ossp_param param = {
2297 .dsp_name = DFL_DSP_NAME,
2298 .dsp_major = DFL_DSP_MAJOR, .dsp_minor = DFL_DSP_MINOR,
2299 .adsp_name = DFL_ADSP_NAME,
2300 .adsp_major = DFL_ADSP_MAJOR, .adsp_minor = DFL_ADSP_MINOR,
2301 .mixer_name = DFL_MIXER_NAME,
2302 .mixer_major = DFL_MIXER_MAJOR, .mixer_minor = DFL_MIXER_MINOR,
2303 .max_streams = DFL_MAX_STREAMS,
2305 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
2306 char path_buf[PATH_MAX], *dir;
2307 char adsp_buf[64] = "", mixer_buf[64] = "";
2308 struct sigaction sa;
2309 struct stat stat_buf;
2310 ssize_t ret;
2311 unsigned u;
2313 snprintf(ossp_log_name, sizeof(ossp_log_name), "osspd");
2314 param.log_level = ossp_log_level;
2316 if (fuse_opt_parse(&args, &param, ossp_opts, process_arg))
2317 fatal("failed to parse arguments");
2319 if (param.help)
2320 return 0;
2322 max_streams = param.max_streams;
2323 hashtbl_size = max_streams / 2 + 13;
2325 umax_streams = max_streams;
2326 if (param.umax_streams)
2327 umax_streams = param.umax_streams;
2328 if (param.log_level > OSSP_LOG_MAX)
2329 param.log_level = OSSP_LOG_MAX;
2330 if (!param.fg)
2331 param.log_level = -param.log_level;
2332 ossp_log_level = param.log_level;
2333 ossp_log_timestamp = param.timestamp;
2335 if (!param.fg)
2336 ossp_daemonize();
2338 /* daemonization already handled, prevent forking inside FUSE */
2339 fuse_opt_add_arg(&args, "-f");
2341 info("OSS Proxy v%s (C) 2008-2010 by Tejun Heo <teheo@suse.de>",
2342 OSSP_VERSION);
2344 /* ignore stupid SIGPIPEs */
2345 memset(&sa, 0, sizeof(sa));
2346 sa.sa_handler = SIG_IGN;
2347 if (sigaction(SIGPIPE, &sa, NULL))
2348 fatal_e(-errno, "failed to ignore SIGPIPE");
2350 /* determine slave path and check for availability */
2351 ret = readlink("/proc/self/exe", path_buf, PATH_MAX - 1);
2352 if (ret < 0)
2353 fatal_e(-errno, "failed to determine executable path");
2354 path_buf[ret] = '\0';
2355 dir = dirname(path_buf);
2357 if (param.dsp_slave_path) {
2358 strncpy(dsp_slave_path, param.dsp_slave_path, PATH_MAX - 1);
2359 dsp_slave_path[PATH_MAX - 1] = '\0';
2360 } else {
2361 ret = snprintf(dsp_slave_path, PATH_MAX, "%s/%s",
2362 dir, "ossp-padsp");
2363 if (ret >= PATH_MAX)
2364 fatal("dsp slave pathname too long");
2367 if (stat(dsp_slave_path, &stat_buf))
2368 fatal_e(-errno, "failed to stat %s", dsp_slave_path);
2369 if (!S_ISREG(stat_buf.st_mode) || !(stat_buf.st_mode & 0444))
2370 fatal("%s is not executable", dsp_slave_path);
2372 /* allocate tables */
2373 os_id_bitmap = calloc(BITS_TO_LONGS(max_streams), sizeof(long));
2374 mixer_tbl = calloc(hashtbl_size, sizeof(mixer_tbl[0]));
2375 os_tbl = calloc(hashtbl_size, sizeof(os_tbl[0]));
2376 os_pgrp_tbl = calloc(hashtbl_size, sizeof(os_pgrp_tbl[0]));
2377 os_notify_tbl = calloc(hashtbl_size, sizeof(os_notify_tbl[0]));
2378 if (!os_id_bitmap || !mixer_tbl || !os_tbl || !os_pgrp_tbl ||
2379 !os_notify_tbl)
2380 fatal("failed to allocate stream hash tables");
2381 for (u = 0; u < hashtbl_size; u++) {
2382 INIT_LIST_HEAD(&mixer_tbl[u]);
2383 INIT_LIST_HEAD(&os_tbl[u]);
2384 INIT_LIST_HEAD(&os_pgrp_tbl[u]);
2385 INIT_LIST_HEAD(&os_notify_tbl[u]);
2387 __set_bit(0, os_id_bitmap); /* don't use id 0 */
2389 /* create mixer delayed reference worker */
2390 ret = -pthread_create(&mixer_delayed_put_thread, NULL,
2391 mixer_delayed_put_worker, NULL);
2392 if (ret)
2393 fatal_e(ret, "failed to create mixer delayed put worker");
2395 /* if exit_on_idle, touch mixer for pgrp0 */
2396 exit_on_idle = param.exit_on_idle;
2397 if (exit_on_idle) {
2398 struct ossp_mixer *mixer;
2400 mixer = get_mixer(0);
2401 if (!mixer)
2402 fatal("failed to touch idle mixer");
2403 put_mixer(mixer);
2406 /* create notify epoll and kick off watcher thread */
2407 notify_epfd = epoll_create(max_streams);
2408 if (notify_epfd < 0)
2409 fatal_e(-errno, "failed to create notify epoll");
2410 if (fcntl(notify_epfd, F_SETFD, FD_CLOEXEC) < 0)
2411 fatal_e(-errno, "failed to set CLOEXEC on notify epfd");
2413 ret = -pthread_create(&notify_poller_thread, NULL, notify_poller, NULL);
2414 if (ret)
2415 fatal_e(ret, "failed to create notify poller thread");
2417 /* create reaper for slave corpses */
2418 ret = -pthread_create(&slave_reaper_thread, NULL, slave_reaper, NULL);
2419 if (ret)
2420 fatal_e(ret, "failed to create slave reaper thread");
2422 /* we're set, let's setup fuse structures */
2423 if (strlen(param.mixer_name))
2424 mixer_se = setup_ossp_cuse(&mixer_ops, param.mixer_name,
2425 param.mixer_major, param.mixer_minor,
2426 args.argc, args.argv);
2427 if (strlen(param.adsp_name))
2428 adsp_se = setup_ossp_cuse(&adsp_ops, param.adsp_name,
2429 param.adsp_major, param.adsp_minor,
2430 args.argc, args.argv);
2432 dsp_se = setup_ossp_cuse(&dsp_ops, param.dsp_name,
2433 param.dsp_major, param.dsp_minor,
2434 args.argc, args.argv);
2435 if (!dsp_se)
2436 fatal("can't create dsp, giving up");
2438 if (mixer_se)
2439 snprintf(mixer_buf, sizeof(mixer_buf), ", %s (%d:%d)",
2440 param.mixer_name, param.mixer_major, param.mixer_minor);
2441 if (adsp_se)
2442 snprintf(adsp_buf, sizeof(adsp_buf), ", %s (%d:%d)",
2443 param.adsp_name, param.adsp_major, param.adsp_minor);
2445 info("Creating %s (%d:%d)%s%s", param.dsp_name, param.dsp_major,
2446 param.dsp_minor, adsp_buf, mixer_buf);
2448 /* start threads for mixer and adsp */
2449 if (mixer_se) {
2450 ret = -pthread_create(&cuse_mixer_thread, NULL,
2451 cuse_worker, mixer_se);
2452 if (ret)
2453 err_e(ret, "failed to create mixer worker");
2455 if (adsp_se) {
2456 ret = -pthread_create(&cuse_adsp_thread, NULL,
2457 cuse_worker, adsp_se);
2458 if (ret)
2459 err_e(ret, "failed to create adsp worker");
2462 /* run CUSE for /dev/dsp in the main thread */
2463 ret = (ssize_t)cuse_worker(dsp_se);
2464 if (ret < 0)
2465 fatal("dsp worker failed");
2466 return 0;