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.
10 #define FUSE_USE_VERSION 28
14 #include <cuse_lowlevel.h>
27 #include <sys/epoll.h>
28 #include <sys/socket.h>
29 #include <sys/soundcard.h>
35 #include "ossp-util.h"
38 * MMAP support needs to be updated to the new fuse MMAP API. Disable
39 * it for the time being.
41 #warning mmap support disabled for now
42 /* #define OSSP_MMAP */
44 #define DFL_MIXER_NAME "mixer"
45 #define DFL_DSP_NAME "dsp"
46 #define DFL_ADSP_NAME "adsp"
47 #define STRFMT "S[%u/%d]"
48 #define STRID(os) os->id, os->pid
50 #define dbg1_os(os, fmt, args...) dbg1(STRFMT" "fmt, STRID(os) , ##args)
51 #define dbg0_os(os, fmt, args...) dbg0(STRFMT" "fmt, STRID(os) , ##args)
52 #define warn_os(os, fmt, args...) warn(STRFMT" "fmt, STRID(os) , ##args)
53 #define err_os(os, fmt, args...) err(STRFMT" "fmt, STRID(os) , ##args)
54 #define warn_ose(os, err, fmt, args...) \
55 warn_e(err, STRFMT" "fmt, STRID(os) , ##args)
56 #define err_ose(os, err, fmt, args...) \
57 err_e(err, STRFMT" "fmt, STRID(os) , ##args)
60 SNDRV_OSS_VERSION = ((3<<16)|(8<<8)|(1<<4)|(0)), /* 3.8.1a */
67 DFL_MAX_STREAMS = 128,
68 MIXER_PUT_DELAY = 600, /* 10 mins */
69 /* DSPS_MMAP_SIZE / 2 must be multiple of SHMLBA */
70 DSPS_MMAP_SIZE = 2 * (512 << 10), /* 512k for each dir */
74 struct list_head link;
81 struct list_head link;
82 struct list_head delayed_put_link;
84 /* the following two fields are protected by mixer_mutex */
90 struct ossp_mixer_cmd {
91 struct ossp_mixer *mixer;
92 struct ossp_mixer_arg set;
97 #define for_each_vol(i, j) \
98 for (i = 0, j = 0; i < 2; j += i << 1, j++, i = j >> 1, j &= 1)
101 unsigned id; /* stream ID */
102 struct list_head link;
103 struct list_head pgrp_link;
104 struct list_head notify_link;
106 pthread_mutex_t cmd_mutex;
107 pthread_mutex_t mmap_mutex;
108 struct fuse_pollhandle *ph;
110 /* stream owner info */
122 /* the following dead flag is set asynchronously, keep it separate. */
125 /* stream mixer state, protected by mixer_mutex */
133 struct ossp_uid_cnt *ucnt;
134 struct fuse_session *se; /* associated fuse session */
135 struct ossp_mixer *mixer;
138 struct ossp_dsp_stream {
139 struct ossp_stream os;
145 #define os_to_dsps(_os) container_of(_os, struct ossp_dsp_stream, os)
147 static unsigned max_streams;
148 static unsigned umax_streams;
149 static unsigned hashtbl_size;
150 static char dsp_slave_path[PATH_MAX];
152 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
153 static pthread_mutex_t mixer_mutex = PTHREAD_MUTEX_INITIALIZER;
154 static unsigned long *os_id_bitmap;
155 static unsigned nr_mixers;
156 static struct list_head *mixer_tbl; /* indexed by PGRP */
157 static struct list_head *os_tbl; /* indexed by ID */
158 static struct list_head *os_pgrp_tbl; /* indexed by PGRP */
159 static struct list_head *os_notify_tbl; /* indexed by notify fd */
160 static LIST_HEAD(uid_cnt_list);
161 static int notify_epfd; /* epoll used to monitor notify fds */
162 static pthread_t notify_poller_thread;
163 static pthread_t slave_reaper_thread;
164 static pthread_t mixer_delayed_put_thread;
165 static pthread_t cuse_mixer_thread;
166 static pthread_t cuse_adsp_thread;
167 static pthread_cond_t notify_poller_kill_wait = PTHREAD_COND_INITIALIZER;
168 static pthread_cond_t slave_reaper_wait = PTHREAD_COND_INITIALIZER;
169 static LIST_HEAD(slave_corpse_list);
170 static LIST_HEAD(mixer_delayed_put_head); /* delayed reference */
171 static pthread_cond_t mixer_delayed_put_cond = PTHREAD_COND_INITIALIZER;
173 static int init_wait_fd = -1;
174 static int exit_on_idle;
175 static struct fuse_session *mixer_se;
176 static struct fuse_session *dsp_se;
177 static struct fuse_session *adsp_se;
179 static void put_os(struct ossp_stream *os);
182 /***************************************************************************
186 static struct list_head *mixer_tbl_head(pid_t pid)
188 return &mixer_tbl[pid % hashtbl_size];
191 static struct list_head *os_tbl_head(uint64_t id)
193 return &os_tbl[id % hashtbl_size];
196 static struct list_head *os_pgrp_tbl_head(pid_t pgrp)
198 return &os_pgrp_tbl[pgrp % hashtbl_size];
201 static struct list_head *os_notify_tbl_head(int notify_rx)
203 return &os_notify_tbl[notify_rx % hashtbl_size];
206 static struct ossp_mixer *find_mixer_locked(pid_t pgrp)
208 struct ossp_mixer *mixer;
210 list_for_each_entry(mixer, mixer_tbl_head(pgrp), link)
211 if (mixer->pgrp == pgrp)
216 static struct ossp_mixer *find_mixer(pid_t pgrp)
218 struct ossp_mixer *mixer;
220 pthread_mutex_lock(&mutex);
221 mixer = find_mixer_locked(pgrp);
222 pthread_mutex_unlock(&mutex);
226 static struct ossp_stream *find_os(unsigned id)
228 struct ossp_stream *os, *found = NULL;
230 pthread_mutex_lock(&mutex);
231 list_for_each_entry(os, os_tbl_head(id), link)
236 pthread_mutex_unlock(&mutex);
240 static struct ossp_stream *find_os_by_notify_rx(int notify_rx)
242 struct ossp_stream *os, *found = NULL;
244 pthread_mutex_lock(&mutex);
245 list_for_each_entry(os, os_notify_tbl_head(notify_rx), notify_link)
246 if (os->notify_rx == notify_rx) {
250 pthread_mutex_unlock(&mutex);
255 /***************************************************************************
256 * Command and ioctl helpers
259 static ssize_t exec_cmd_intern(struct ossp_stream *os, enum ossp_opcode opcode,
260 const void *carg, size_t carg_size, const void *din, size_t din_size,
261 void *rarg, size_t rarg_size, void *dout, size_t *dout_sizep, int fd)
263 size_t dout_size = dout_sizep ? *dout_sizep : 0;
264 struct ossp_cmd cmd = { .magic = OSSP_CMD_MAGIC, .opcode = opcode,
265 .din_size = din_size,
266 .dout_size = dout_size };
267 struct iovec iov = { &cmd, sizeof(cmd) };
268 struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };
269 struct ossp_reply reply = { };
270 char cmsg_buf[CMSG_SPACE(sizeof(fd))];
277 dbg1_os(os, "%s carg=%zu din=%zu rarg=%zu dout=%zu",
278 ossp_cmd_str[opcode], carg_size, din_size, rarg_size,
282 struct cmsghdr *cmsg;
284 msg.msg_control = cmsg_buf;
285 msg.msg_controllen = sizeof(cmsg_buf);
286 cmsg = CMSG_FIRSTHDR(&msg);
287 cmsg->cmsg_level = SOL_SOCKET;
288 cmsg->cmsg_type = SCM_RIGHTS;
289 cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
290 *(int *)CMSG_DATA(cmsg) = fd;
291 msg.msg_controllen = cmsg->cmsg_len;
294 if (sendmsg(os->cmd_fd, &msg, 0) <= 0) {
296 snprintf(reason, sizeof(reason), "command sendmsg failed: %s",
301 if ((rc = write_fill(os->cmd_fd, carg, carg_size)) < 0 ||
302 (rc = write_fill(os->cmd_fd, din, din_size)) < 0) {
303 snprintf(reason, sizeof(reason),
304 "can't tranfer command argument and/or data: %s",
308 if ((rc = read_fill(os->cmd_fd, &reply, sizeof(reply))) < 0) {
309 snprintf(reason, sizeof(reason), "can't read reply: %s",
314 if (reply.magic != OSSP_REPLY_MAGIC) {
315 snprintf(reason, sizeof(reason),
316 "reply magic mismatch %x != %x",
317 reply.magic, OSSP_REPLY_MAGIC);
322 if (reply.result < 0)
325 if (reply.dout_size > dout_size) {
326 snprintf(reason, sizeof(reason),
327 "data out size overflow %zu > %zu",
328 reply.dout_size, dout_size);
333 dout_size = reply.dout_size;
335 *dout_sizep = dout_size;
337 if ((rc = read_fill(os->cmd_fd, rarg, rarg_size)) < 0 ||
338 (rc = read_fill(os->cmd_fd, dout, dout_size)) < 0) {
339 snprintf(reason, sizeof(reason), "can't read data out: %s",
345 dbg1_os(os, " completed, result=%d dout=%zu",
346 reply.result, dout_size);
350 warn_os(os, "communication with slave failed (%s)", reason);
355 static ssize_t exec_cmd(struct ossp_stream *os, enum ossp_opcode opcode,
356 const void *carg, size_t carg_size, const void *din, size_t din_size,
357 void *rarg, size_t rarg_size, void *dout, size_t *dout_sizep, int fd)
363 /* mixer command is handled exlicitly below */
364 is_mixer = opcode == OSSP_MIXER;
366 ret = -pthread_mutex_trylock(&os->cmd_mutex);
370 pthread_mutex_lock(&os->cmd_mutex);
372 ret = exec_cmd_intern(os, opcode, carg, carg_size,
373 din, din_size, rarg, rarg_size,
374 dout, dout_sizep, fd);
377 /* lazy mixer handling */
378 pthread_mutex_lock(&mixer_mutex);
380 if (os->mixer_pending) {
381 struct ossp_mixer_arg marg;
383 /* we have mixer command pending */
384 memcpy(marg.vol, os->vol_set, sizeof(os->vol_set));
385 memset(os->vol_set, -1, sizeof(os->vol_set));
387 pthread_mutex_unlock(&mixer_mutex);
388 mret = exec_cmd_intern(os, OSSP_MIXER, &marg, sizeof(marg),
389 NULL, 0, &marg, sizeof(marg), NULL, NULL,
391 pthread_mutex_lock(&mixer_mutex);
393 /* was there mixer set request while executing mixer command? */
395 if (os->vol_set[i][j] >= 0)
398 /* update internal mixer state */
401 if (marg.vol[i][j] >= 0) {
402 if (os->vol[i][j] != marg.vol[i][j])
403 os->mixer->modify_counter++;
404 os->vol[i][j] = marg.vol[i][j];
408 os->mixer_pending = 0;
411 pthread_mutex_unlock(&os->cmd_mutex);
414 * mixer mutex must be released after cmd_mutex so that
415 * exec_mixer_cmd() can guarantee that mixer_pending flags
416 * will be handled immediately or when the currently
417 * in-progress command completes.
419 pthread_mutex_unlock(&mixer_mutex);
421 return is_mixer ? mret : ret;
424 static ssize_t exec_simple_cmd(struct ossp_stream *os,
425 enum ossp_opcode opcode, void *carg, void *rarg)
427 return exec_cmd(os, opcode,
428 carg, ossp_arg_sizes[opcode].carg_size, NULL, 0,
429 rarg, ossp_arg_sizes[opcode].rarg_size, NULL, NULL, -1);
432 static int ioctl_prep_uarg(fuse_req_t req, void *in, size_t in_sz, void *out,
433 size_t out_sz, void *uarg, const void *in_buf,
434 size_t in_bufsz, size_t out_bufsz)
436 struct iovec in_iov = { }, out_iov = { };
441 in_iov.iov_base = uarg;
442 in_iov.iov_len = in_sz;
445 assert(in_bufsz == in_sz);
446 memcpy(in, in_buf, in_sz);
452 out_iov.iov_base = uarg;
453 out_iov.iov_len = out_sz;
456 assert(out_bufsz == out_sz);
460 fuse_reply_ioctl_retry(req, &in_iov, 1, &out_iov, 1);
465 #define PREP_UARG(inp, outp) do { \
466 if (ioctl_prep_uarg(req, (inp), sizeof(*(inp)), \
467 (outp), sizeof(*(outp)), uarg, \
468 in_buf, in_bufsz, out_bufsz)) \
472 #define IOCTL_RETURN(result, outp) do { \
473 if ((outp) != NULL) \
474 fuse_reply_ioctl(req, result, (outp), sizeof(*(outp))); \
476 fuse_reply_ioctl(req, result, NULL, 0); \
481 /***************************************************************************
482 * Mixer implementation
485 static void put_mixer_real(struct ossp_mixer *mixer)
487 if (!--mixer->refcnt) {
488 dbg0("DESTROY mixer(%d)", mixer->pgrp);
489 list_del_init(&mixer->link);
490 list_del_init(&mixer->delayed_put_link);
495 * If exit_on_idle, mixer for pgrp0 is touched during
496 * init and each stream has mixer attached. As mixers
497 * are destroyed after they have been idle for
498 * MIXER_PUT_DELAY seconds, we can use it for idle
499 * detection. Note that this might race with
500 * concurrent open. The race is inherent.
502 if (exit_on_idle && !nr_mixers) {
503 info("idle, exiting");
509 static struct ossp_mixer *get_mixer(pid_t pgrp)
511 struct ossp_mixer *mixer;
513 pthread_mutex_lock(&mutex);
515 /* is there a matching one? */
516 mixer = find_mixer_locked(pgrp);
518 if (list_empty(&mixer->delayed_put_link))
521 list_del_init(&mixer->delayed_put_link);
525 /* reap delayed put list if there are too many mixers */
526 while (nr_mixers > 2 * max_streams &&
527 !list_empty(&mixer_delayed_put_head)) {
528 struct ossp_mixer *mixer =
529 list_first_entry(&mixer_delayed_put_head,
530 struct ossp_mixer, delayed_put_link);
532 assert(mixer->refcnt == 1);
533 put_mixer_real(mixer);
536 /* create a new one */
537 mixer = calloc(1, sizeof(*mixer));
539 warn("failed to allocate mixer for %d", pgrp);
545 INIT_LIST_HEAD(&mixer->link);
546 INIT_LIST_HEAD(&mixer->delayed_put_link);
548 memset(mixer->vol, -1, sizeof(mixer->vol));
550 list_add(&mixer->link, mixer_tbl_head(pgrp));
552 dbg0("CREATE mixer(%d)", pgrp);
555 pthread_mutex_unlock(&mutex);
559 static void put_mixer(struct ossp_mixer *mixer)
561 pthread_mutex_lock(&mutex);
564 if (mixer->refcnt == 1) {
567 clock_gettime(CLOCK_REALTIME, &ts);
568 mixer->put_expires = ts.tv_sec + MIXER_PUT_DELAY;
569 list_add_tail(&mixer->delayed_put_link,
570 &mixer_delayed_put_head);
571 pthread_cond_signal(&mixer_delayed_put_cond);
573 put_mixer_real(mixer);
576 pthread_mutex_unlock(&mutex);
579 static void *mixer_delayed_put_worker(void *arg)
581 struct ossp_mixer *mixer;
585 pthread_mutex_lock(&mutex);
587 clock_gettime(CLOCK_REALTIME, &ts);
591 while (!list_empty(&mixer_delayed_put_head)) {
592 mixer = list_first_entry(&mixer_delayed_put_head,
593 struct ossp_mixer, delayed_put_link);
595 if (now <= mixer->put_expires)
598 assert(mixer->refcnt == 1);
599 put_mixer_real(mixer);
604 ts.tv_sec = mixer->put_expires + 1;
605 pthread_cond_timedwait(&mixer_delayed_put_cond, &mutex, &ts);
607 pthread_cond_wait(&mixer_delayed_put_cond, &mutex);
612 static void init_mixer_cmd(struct ossp_mixer_cmd *mxcmd,
613 struct ossp_mixer *mixer)
615 memset(mxcmd, 0, sizeof(*mxcmd));
616 memset(&mxcmd->set.vol, -1, sizeof(mxcmd->set.vol));
617 mxcmd->mixer = mixer;
621 static int exec_mixer_cmd(struct ossp_mixer_cmd *mxcmd, struct ossp_stream *os)
626 * Set pending flags before trying to execute mixer command.
627 * Combined with lock release order in exec_cmd(), this
628 * guarantees that the mixer command will be executed
629 * immediately or when the current command completes.
631 pthread_mutex_lock(&mixer_mutex);
632 os->mixer_pending = 1;
634 if (mxcmd->set.vol[i][j] >= 0)
635 os->vol_set[i][j] = mxcmd->set.vol[i][j];
636 pthread_mutex_unlock(&mixer_mutex);
638 rc = exec_simple_cmd(os, OSSP_MIXER, NULL, NULL);
640 dbg0_os(os, "volume set=%d/%d:%d/%d get=%d/%d:%d/%d",
641 mxcmd->set.vol[PLAY][LEFT], mxcmd->set.vol[PLAY][RIGHT],
642 mxcmd->set.vol[REC][LEFT], mxcmd->set.vol[REC][RIGHT],
643 os->vol[PLAY][LEFT], os->vol[PLAY][RIGHT],
644 os->vol[REC][LEFT], os->vol[REC][RIGHT]);
645 } else if (rc != -EBUSY)
646 warn_ose(os, rc, "mixer command failed");
651 static void finish_mixer_cmd(struct ossp_mixer_cmd *mxcmd)
653 struct ossp_mixer *mixer = mxcmd->mixer;
654 struct ossp_stream *os;
655 int dir = mxcmd->out_dir;
660 pthread_mutex_lock(&mixer_mutex);
662 /* get volume of all streams attached to this mixer */
663 pthread_mutex_lock(&mutex);
664 list_for_each_entry(os, os_pgrp_tbl_head(mixer->pgrp), pgrp_link) {
665 if (os->pgrp != mixer->pgrp)
668 if (os->vol[i][j] < 0)
670 vol[i][j] += os->vol[i][j];
674 pthread_mutex_unlock(&mutex);
676 /* calculate the summary volume values */
678 if (mxcmd->set.vol[i][j] >= 0)
679 vol[i][j] = mxcmd->set.vol[i][j];
681 vol[i][j] = vol[i][j] / cnt[i][j];
682 else if (mixer->vol[i][j] >= 0)
683 vol[i][j] = mixer->vol[i][j];
687 vol[i][j] = min(max(0, vol[i][j]), 100);
691 mxcmd->rvol = vol[dir][LEFT] | (vol[dir][RIGHT] << 8);
693 pthread_mutex_unlock(&mixer_mutex);
696 static void mixer_simple_ioctl(fuse_req_t req, struct ossp_mixer *mixer,
697 unsigned cmd, void *uarg, const void *in_buf,
698 size_t in_bufsz, size_t out_bufsz,
701 const char *id = "OSS Proxy", *name = "Mixer";
705 case SOUND_MIXER_INFO: {
706 struct 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 info.modify_counter = mixer->modify_counter;
712 IOCTL_RETURN(0, &info);
715 case SOUND_OLD_MIXER_INFO: {
716 struct _old_mixer_info info = { };
718 PREP_UARG(NULL, &info);
719 strncpy(info.id, id, sizeof(info.id) - 1);
720 strncpy(info.name, name, sizeof(info.name) - 1);
721 IOCTL_RETURN(0, &info);
725 i = SNDRV_OSS_VERSION;
727 case SOUND_MIXER_READ_DEVMASK:
728 case SOUND_MIXER_READ_STEREODEVS:
729 i = SOUND_MASK_PCM | SOUND_MASK_IGAIN;
731 case SOUND_MIXER_READ_CAPS:
732 i = SOUND_CAP_EXCL_INPUT;
734 case SOUND_MIXER_READ_RECMASK:
735 case SOUND_MIXER_READ_RECSRC:
736 i = SOUND_MASK_IGAIN;
742 case SOUND_MIXER_WRITE_RECSRC:
743 IOCTL_RETURN(0, NULL);
752 static void mixer_do_ioctl(fuse_req_t req, struct ossp_mixer *mixer,
753 unsigned cmd, void *uarg, const void *in_buf,
754 size_t in_bufsz, size_t out_bufsz)
756 struct ossp_mixer_cmd mxcmd;
757 struct ossp_stream *os, **osa;
759 int slot = cmd & 0xff, dir;
763 mixer_simple_ioctl(req, mixer, cmd, uarg, in_buf, in_bufsz, out_bufsz,
769 if (!(cmd & (SIOC_IN | SIOC_OUT)))
773 * Okay, it's not one of the easy ones. Build mxcmd for
774 * actual volume control.
782 case SOUND_MIXER_PCM:
785 case SOUND_MIXER_IGAIN:
793 init_mixer_cmd(&mxcmd, mixer);
801 if (l > 100 || r > 100)
804 mixer->vol[dir][LEFT] = mxcmd.set.vol[dir][LEFT] = l;
805 mixer->vol[dir][RIGHT] = mxcmd.set.vol[dir][RIGHT] = r;
810 * Apply volume conrol
812 /* acquire target streams */
813 pthread_mutex_lock(&mutex);
814 osa = calloc(max_streams, sizeof(osa[0]));
816 pthread_mutex_unlock(&mutex);
822 list_for_each_entry(os, os_pgrp_tbl_head(mixer->pgrp), pgrp_link) {
823 if (os->pgrp == mixer->pgrp) {
829 pthread_mutex_unlock(&mutex);
831 /* execute mxcmd for each stream and put it */
832 for (i = 0; i < nr_os; i++) {
833 exec_mixer_cmd(&mxcmd, osa[i]);
837 finish_mixer_cmd(&mxcmd);
840 IOCTL_RETURN(0, out_bufsz ? &mxcmd.rvol : NULL);
843 fuse_reply_err(req, -rc);
846 static void mixer_open(fuse_req_t req, struct fuse_file_info *fi)
848 pid_t pid = fuse_req_ctx(req)->pid, pgrp;
849 struct ossp_mixer *mixer;
852 rc = get_proc_self_info(pid, &pgrp, NULL, 0);
854 err_e(rc, "get_proc_self_info(%d) failed", pid);
855 fuse_reply_err(req, -rc);
859 mixer = get_mixer(pgrp);
863 fuse_reply_open(req, fi);
865 fuse_reply_err(req, ENOMEM);
868 static void mixer_ioctl(fuse_req_t req, int signed_cmd, void *uarg,
869 struct fuse_file_info *fi, unsigned int flags,
870 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
872 struct ossp_mixer *mixer;
874 mixer = find_mixer(fi->fh);
876 fuse_reply_err(req, EBADF);
880 mixer_do_ioctl(req, mixer, signed_cmd, uarg, in_buf, in_bufsz,
884 static void mixer_release(fuse_req_t req, struct fuse_file_info *fi)
886 struct ossp_mixer *mixer;
888 mixer = find_mixer(fi->fh);
891 fuse_reply_err(req, 0);
893 fuse_reply_err(req, EBADF);
897 /***************************************************************************
898 * Stream implementation
901 static int alloc_os(size_t stream_size, size_t mmap_size, pid_t pid, uid_t pgrp,
902 uid_t uid, gid_t gid, int cmd_sock,
903 const int *notify, struct fuse_session *se,
904 struct ossp_stream **osp)
906 struct ossp_uid_cnt *tmp_ucnt, *ucnt = NULL;
907 struct ossp_stream *os;
910 assert(stream_size >= sizeof(struct ossp_stream));
911 os = calloc(1, stream_size);
915 INIT_LIST_HEAD(&os->link);
916 INIT_LIST_HEAD(&os->pgrp_link);
917 INIT_LIST_HEAD(&os->notify_link);
920 rc = -pthread_mutex_init(&os->cmd_mutex, NULL);
924 rc = -pthread_mutex_init(&os->mmap_mutex, NULL);
926 goto err_destroy_cmd_mutex;
928 pthread_mutex_lock(&mutex);
930 list_for_each_entry(tmp_ucnt, &uid_cnt_list, link)
931 if (tmp_ucnt->uid == uid) {
937 ucnt = calloc(1, sizeof(*ucnt));
941 list_add(&ucnt->link, &uid_cnt_list);
945 if (ucnt->nr_os + 1 > umax_streams)
948 /* everything looks fine, allocate id and init stream */
950 os->id = find_next_zero_bit(os_id_bitmap, max_streams, 0);
951 if (os->id >= max_streams)
953 __set_bit(os->id, os_id_bitmap);
955 os->cmd_fd = cmd_sock;
956 os->notify_tx = notify[1];
957 os->notify_rx = notify[0];
963 os->mmap_off = os->id * mmap_size;
964 os->mmap_size = mmap_size;
969 memset(os->vol, -1, sizeof(os->vol));
970 memset(os->vol_set, -1, sizeof(os->vol));
972 list_add(&os->link, os_tbl_head(os->id));
973 list_add(&os->pgrp_link, os_pgrp_tbl_head(os->pgrp));
977 pthread_mutex_unlock(&mutex);
981 pthread_mutex_unlock(&mutex);
982 pthread_mutex_destroy(&os->mmap_mutex);
983 err_destroy_cmd_mutex:
984 pthread_mutex_destroy(&os->cmd_mutex);
990 static void shutdown_notification(struct ossp_stream *os)
992 struct ossp_notify obituary = { .magic = OSSP_NOTIFY_MAGIC,
993 .opcode = OSSP_NOTIFY_OBITUARY };
997 * Shutdown notification for this stream. We politely ask
998 * notify_poller to shut the receive side down to avoid racing
1001 while (os->notify_rx >= 0) {
1002 ret = write(os->notify_tx, &obituary, sizeof(obituary));
1005 warn_os(os, "unexpected EOF on notify_tx");
1006 else if (errno != EPIPE)
1007 warn_ose(os, -errno,
1008 "unexpected error on notify_tx");
1009 close(os->notify_rx);
1014 if (ret != sizeof(obituary))
1015 warn_os(os, "short transfer on notify_tx");
1016 pthread_cond_wait(¬ify_poller_kill_wait, &mutex);
1020 static void put_os(struct ossp_stream *os)
1025 pthread_mutex_lock(&mutex);
1029 pthread_mutex_unlock(&mutex);
1034 shutdown_notification(os);
1036 dbg0_os(os, "DESTROY");
1038 list_del_init(&os->link);
1039 list_del_init(&os->pgrp_link);
1040 list_del_init(&os->notify_link);
1043 pthread_mutex_unlock(&mutex);
1046 close(os->notify_tx);
1047 put_mixer(os->mixer);
1048 pthread_mutex_destroy(&os->cmd_mutex);
1049 pthread_mutex_destroy(&os->mmap_mutex);
1051 pthread_mutex_lock(&mutex);
1052 dbg1_os(os, "stream dead, requesting reaping");
1053 list_add_tail(&os->link, &slave_corpse_list);
1054 pthread_cond_signal(&slave_reaper_wait);
1055 pthread_mutex_unlock(&mutex);
1058 static void set_extra_env(pid_t pid)
1060 char procenviron[32];
1061 const int step = 1024;
1062 char *data = malloc(step + 1);
1070 sprintf(procenviron, "/proc/%d/environ", pid);
1071 fd = open(procenviron, O_RDONLY);
1076 * There should really be a 'read whole file to a newly allocated
1079 while ((ret = read(fd, data + ofs, step)) > 0) {
1082 newdata = realloc(data, ofs + step + 1);
1091 /* Append the extra 0 for end condition */
1094 while ((ret = strlen(ptr)) > 0) {
1096 * Copy all PULSE variables and DISPLAY so that
1097 * ssh -X remotehost 'mplayer -ao oss' will work
1099 if (!strncmp(ptr, "DISPLAY=", 8) ||
1100 !strncmp(ptr, "PULSE_", 6))
1110 static int create_os(const char *slave_path,
1111 size_t stream_size, size_t mmap_size,
1112 pid_t pid, pid_t pgrp, uid_t uid, gid_t gid,
1113 struct fuse_session *se, struct ossp_stream **osp)
1115 static pthread_mutex_t create_mutex = PTHREAD_MUTEX_INITIALIZER;
1116 int cmd_sock[2] = { -1, -1 };
1117 int notify_sock[2] = { -1, -1 };
1118 struct ossp_stream *os = NULL;
1119 struct epoll_event ev = { };
1123 * Only one thread can be creating a stream. This is to avoid
1124 * leaking unwanted fds into slaves.
1126 pthread_mutex_lock(&create_mutex);
1128 /* prepare communication channels */
1129 if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd_sock) ||
1130 socketpair(AF_UNIX, SOCK_STREAM, 0, notify_sock)) {
1132 warn_e(rc, "failed to create slave command channel");
1136 if (fcntl(notify_sock[0], F_SETFL, O_NONBLOCK) < 0) {
1138 warn_e(rc, "failed to set NONBLOCK on notify sock");
1143 * Alloc stream which will be responsible for all server side
1144 * resources from now on.
1146 rc = alloc_os(stream_size, mmap_size, pid, pgrp, uid, gid, cmd_sock[0],
1147 notify_sock, se, &os);
1149 warn_e(rc, "failed to allocate stream for %d", pid);
1154 os->mixer = get_mixer(pgrp);
1159 * Register notification. If successful, notify_poller has
1160 * custody of notify_rx fd.
1162 pthread_mutex_lock(&mutex);
1163 list_add(&os->notify_link, os_notify_tbl_head(os->notify_rx));
1164 pthread_mutex_unlock(&mutex);
1166 ev.events = EPOLLIN;
1167 ev.data.fd = notify_sock[0];
1168 if (epoll_ctl(notify_epfd, EPOLL_CTL_ADD, notify_sock[0], &ev)) {
1170 * Without poller watching this notify sock, poller
1171 * shutdown sequence in shutdown_notification() can't
1172 * be used. Kill notification rx manually.
1175 warn_ose(os, rc, "failed to add notify epoll");
1176 close(os->notify_rx);
1182 os->slave_pid = fork();
1183 if (os->slave_pid < 0) {
1185 warn_ose(os, rc, "failed to fork slave");
1189 if (os->slave_pid == 0) {
1191 char id_str[2][16], fd_str[3][16];
1192 char mmap_off_str[32], mmap_size_str[32];
1193 char log_str[16], slave_path_copy[PATH_MAX];
1194 char *argv[] = { slave_path_copy, "-u", id_str[0],
1195 "-g", id_str[1], "-c", fd_str[0],
1196 "-n", fd_str[1], "-m", fd_str[2],
1197 "-o", mmap_off_str, "-s", mmap_size_str,
1198 "-l", log_str, NULL, NULL };
1201 /* drop stuff we don't need */
1202 if (close(cmd_sock[0]) || close(notify_sock[0]))
1203 fatal_e(-errno, "failed to close server pipe fds");
1207 close(fuse_mmap_fd(se));
1211 pwd = getpwuid(os->uid);
1213 setenv("LOGNAME", pwd->pw_name, 1);
1214 setenv("USER", pwd->pw_name, 1);
1215 setenv("HOME", pwd->pw_dir, 1);
1217 /* Set extra environment variables from the caller */
1221 slave_path_copy[sizeof(slave_path_copy) - 1] = '\0';
1222 strncpy(slave_path_copy, slave_path, sizeof(slave_path_copy) - 1);
1223 if (slave_path_copy[sizeof(slave_path_copy) - 1] != '\0') {
1225 err_ose(os, rc, "slave path too long");
1229 snprintf(id_str[0], sizeof(id_str[0]), "%d", os->uid);
1230 snprintf(id_str[1], sizeof(id_str[0]), "%d", os->gid);
1231 snprintf(fd_str[0], sizeof(fd_str[0]), "%d", cmd_sock[1]);
1232 snprintf(fd_str[1], sizeof(fd_str[1]), "%d", notify_sock[1]);
1233 snprintf(fd_str[2], sizeof(fd_str[2]), "%d",
1235 mmap_size ? fuse_mmap_fd(se) :
1238 snprintf(mmap_off_str, sizeof(mmap_off_str), "0x%llx",
1239 (unsigned long long)os->mmap_off);
1240 snprintf(mmap_size_str, sizeof(mmap_size_str), "0x%zx",
1242 snprintf(log_str, sizeof(log_str), "%d", ossp_log_level);
1243 if (ossp_log_timestamp)
1244 argv[ARRAY_SIZE(argv) - 2] = "-t";
1246 execv(slave_path, argv);
1248 err_ose(os, rc, "execv failed for <%d>", pid);
1253 /* turn on CLOEXEC on all server side fds */
1254 if (fcntl(os->cmd_fd, F_SETFD, FD_CLOEXEC) < 0 ||
1255 fcntl(os->notify_tx, F_SETFD, FD_CLOEXEC) < 0 ||
1256 fcntl(os->notify_rx, F_SETFD, FD_CLOEXEC) < 0) {
1258 err_ose(os, rc, "failed to set CLOEXEC on server side fds");
1262 dbg0_os(os, "CREATE slave=%d %s", os->slave_pid, slave_path);
1263 dbg0_os(os, " client=%d cmd=%d:%d notify=%d:%d mmap=%d:0x%llx:%zu",
1264 pid, cmd_sock[0], cmd_sock[1], notify_sock[0], notify_sock[1],
1266 os->mmap_size ? fuse_mmap_fd(se) :
1269 (unsigned long long)os->mmap_off, os->mmap_size);
1273 goto close_client_fds;
1279 pthread_mutex_unlock(&create_mutex);
1283 for (i = 0; i < 2; i++) {
1285 close(notify_sock[i]);
1287 pthread_mutex_unlock(&create_mutex);
1291 static void dsp_open_common(fuse_req_t req, struct fuse_file_info *fi,
1292 struct fuse_session *se)
1294 const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1295 struct ossp_dsp_open_arg arg = { };
1296 struct ossp_stream *os = NULL;
1297 struct ossp_mixer *mixer;
1298 struct ossp_dsp_stream *dsps;
1299 struct ossp_mixer_cmd mxcmd;
1303 ret = get_proc_self_info(fuse_ctx->pid, &pgrp, NULL, 0);
1305 err_e(ret, "get_proc_self_info(%d) failed", fuse_ctx->pid);
1309 ret = create_os(dsp_slave_path, sizeof(*dsps), DSPS_MMAP_SIZE,
1310 fuse_ctx->pid, pgrp, fuse_ctx->uid, fuse_ctx->gid,
1314 dsps = os_to_dsps(os);
1317 switch (fi->flags & O_ACCMODE) {
1319 dsps->rw |= 1 << PLAY;
1322 dsps->rw |= 1 << REC;
1325 dsps->rw |= (1 << PLAY) | (1 << REC);
1331 arg.flags = fi->flags;
1332 arg.opener_pid = os->pid;
1333 ret = exec_simple_cmd(&dsps->os, OSSP_DSP_OPEN, &arg, NULL);
1339 memcpy(os->vol, mixer->vol, sizeof(os->vol));
1340 if (os->vol[PLAY][0] >= 0 || os->vol[REC][0] >= 0) {
1341 init_mixer_cmd(&mxcmd, mixer);
1342 memcpy(mxcmd.set.vol, os->vol, sizeof(os->vol));
1343 exec_mixer_cmd(&mxcmd, os);
1344 finish_mixer_cmd(&mxcmd);
1348 fi->nonseekable = 1;
1351 fuse_reply_open(req, fi);
1355 fuse_reply_err(req, -ret);
1358 static void dsp_open(fuse_req_t req, struct fuse_file_info *fi)
1360 dsp_open_common(req, fi, dsp_se);
1363 static void adsp_open(fuse_req_t req, struct fuse_file_info *fi)
1365 dsp_open_common(req, fi, adsp_se);
1368 static void dsp_release(fuse_req_t req, struct fuse_file_info *fi)
1370 struct ossp_stream *os;
1372 os = find_os(fi->fh);
1375 fuse_reply_err(req, 0);
1377 fuse_reply_err(req, EBADF);
1380 static void dsp_read(fuse_req_t req, size_t size, off_t off,
1381 struct fuse_file_info *fi)
1383 struct ossp_dsp_rw_arg arg = { };
1384 struct ossp_stream *os;
1385 struct ossp_dsp_stream *dsps;
1390 os = find_os(fi->fh);
1393 dsps = os_to_dsps(os);
1396 if (!(dsps->rw & (1 << REC)))
1408 arg.nonblock = (fi->flags & O_NONBLOCK) || dsps->nonblock;
1410 ret = exec_cmd(os, OSSP_DSP_READ, &arg, sizeof(arg),
1411 NULL, 0, NULL, 0, buf, &size, -1);
1414 fuse_reply_buf(req, buf, size);
1416 fuse_reply_err(req, -ret);
1421 static void dsp_write(fuse_req_t req, const char *buf, size_t size, off_t off,
1422 struct fuse_file_info *fi)
1424 struct ossp_dsp_rw_arg arg = { };
1425 struct ossp_stream *os;
1426 struct ossp_dsp_stream *dsps;
1430 os = find_os(fi->fh);
1433 dsps = os_to_dsps(os);
1436 if (!(dsps->rw & (1 << PLAY)))
1443 arg.nonblock = (fi->flags & O_NONBLOCK) || dsps->nonblock;
1445 ret = exec_cmd(os, OSSP_DSP_WRITE, &arg, sizeof(arg),
1446 buf, size, NULL, 0, NULL, NULL, -1);
1449 fuse_reply_write(req, ret);
1451 fuse_reply_err(req, -ret);
1454 static void dsp_poll(fuse_req_t req, struct fuse_file_info *fi,
1455 struct fuse_pollhandle *ph)
1457 int notify = ph != NULL;
1458 unsigned revents = 0;
1459 struct ossp_stream *os;
1463 os = find_os(fi->fh);
1468 pthread_mutex_lock(&mutex);
1470 fuse_pollhandle_destroy(os->ph);
1472 pthread_mutex_unlock(&mutex);
1475 ret = exec_simple_cmd(os, OSSP_DSP_POLL, ¬ify, &revents);
1478 fuse_reply_poll(req, revents);
1480 fuse_reply_err(req, -ret);
1483 static void dsp_ioctl(fuse_req_t req, int signed_cmd, void *uarg,
1484 struct fuse_file_info *fi, unsigned int flags,
1485 const void *in_buf, size_t in_bufsz, size_t out_bufsz)
1487 /* some ioctl constants are long and has the highest bit set */
1488 unsigned cmd = signed_cmd;
1489 struct ossp_stream *os;
1490 struct ossp_dsp_stream *dsps;
1491 enum ossp_opcode op;
1496 os = find_os(fi->fh);
1499 dsps = os_to_dsps(os);
1501 /* mixer commands are allowed on DSP devices */
1502 if (((cmd >> 8) & 0xff) == 'M') {
1503 mixer_do_ioctl(req, os->mixer, cmd, uarg, in_buf, in_bufsz,
1510 case OSS_GETVERSION:
1511 i = SNDRV_OSS_VERSION;
1512 PREP_UARG(NULL, &i);
1513 IOCTL_RETURN(0, &i);
1515 case SNDCTL_DSP_GETCAPS:
1516 i = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER |
1521 PREP_UARG(NULL, &i);
1522 IOCTL_RETURN(0, &i);
1524 case SNDCTL_DSP_NONBLOCK:
1527 IOCTL_RETURN(0, NULL);
1529 case SNDCTL_DSP_RESET: op = OSSP_DSP_RESET; goto nd;
1530 case SNDCTL_DSP_SYNC: op = OSSP_DSP_SYNC; goto nd;
1531 case SNDCTL_DSP_POST: op = OSSP_DSP_POST; goto nd;
1533 ret = exec_simple_cmd(&dsps->os, op, NULL, NULL);
1536 IOCTL_RETURN(0, NULL);
1538 case SOUND_PCM_READ_RATE: op = OSSP_DSP_GET_RATE; goto ri;
1539 case SOUND_PCM_READ_BITS: op = OSSP_DSP_GET_FORMAT; goto ri;
1540 case SOUND_PCM_READ_CHANNELS: op = OSSP_DSP_GET_CHANNELS; goto ri;
1541 case SNDCTL_DSP_GETBLKSIZE: op = OSSP_DSP_GET_BLKSIZE; goto ri;
1542 case SNDCTL_DSP_GETFMTS: op = OSSP_DSP_GET_FORMATS; goto ri;
1543 case SNDCTL_DSP_GETTRIGGER: op = OSSP_DSP_GET_TRIGGER; goto ri;
1545 PREP_UARG(NULL, &i);
1546 ret = exec_simple_cmd(&dsps->os, op, NULL, &i);
1549 IOCTL_RETURN(0, &i);
1551 case SNDCTL_DSP_SPEED: op = OSSP_DSP_SET_RATE; goto wi;
1552 case SNDCTL_DSP_SETFMT: op = OSSP_DSP_SET_FORMAT; goto wi;
1553 case SNDCTL_DSP_CHANNELS: op = OSSP_DSP_SET_CHANNELS; goto wi;
1554 case SNDCTL_DSP_SUBDIVIDE: op = OSSP_DSP_SET_SUBDIVISION; goto wi;
1557 ret = exec_simple_cmd(&dsps->os, op, &i, &i);
1560 IOCTL_RETURN(0, &i);
1562 case SNDCTL_DSP_STEREO:
1563 PREP_UARG(NULL, &i);
1565 ret = exec_simple_cmd(&dsps->os, OSSP_DSP_SET_CHANNELS, &i, &i);
1569 IOCTL_RETURN(0, &i);
1571 case SNDCTL_DSP_SETFRAGMENT:
1572 PREP_UARG(&i, NULL);
1573 ret = exec_simple_cmd(&dsps->os,
1574 OSSP_DSP_SET_FRAGMENT, &i, NULL);
1577 IOCTL_RETURN(0, NULL);
1579 case SNDCTL_DSP_SETTRIGGER:
1580 PREP_UARG(&i, NULL);
1581 ret = exec_simple_cmd(&dsps->os,
1582 OSSP_DSP_SET_TRIGGER, &i, NULL);
1585 IOCTL_RETURN(0, NULL);
1587 case SNDCTL_DSP_GETOSPACE:
1588 case SNDCTL_DSP_GETISPACE: {
1589 struct audio_buf_info info;
1592 if (cmd == SNDCTL_DSP_GETOSPACE) {
1593 if (!(dsps->rw & (1 << PLAY)))
1595 op = OSSP_DSP_GET_OSPACE;
1597 if (!(dsps->rw & (1 << REC)))
1599 op = OSSP_DSP_GET_ISPACE;
1602 PREP_UARG(NULL, &info);
1603 ret = exec_simple_cmd(&dsps->os, op, NULL, &info);
1606 IOCTL_RETURN(0, &info);
1609 case SNDCTL_DSP_GETOPTR:
1610 case SNDCTL_DSP_GETIPTR: {
1611 struct count_info info;
1613 op = cmd == SNDCTL_DSP_GETOPTR ? OSSP_DSP_GET_OPTR
1614 : OSSP_DSP_GET_IPTR;
1615 PREP_UARG(NULL, &info);
1616 ret = exec_simple_cmd(&dsps->os, op, NULL, &info);
1619 IOCTL_RETURN(0, &info);
1622 case SNDCTL_DSP_GETODELAY:
1623 PREP_UARG(NULL, &i);
1625 ret = exec_simple_cmd(&dsps->os, OSSP_DSP_GET_ODELAY, NULL, &i);
1626 IOCTL_RETURN(ret, &i); /* always copy out result, 0 on err */
1628 case SOUND_PCM_WRITE_FILTER:
1629 case SOUND_PCM_READ_FILTER:
1633 case SNDCTL_DSP_MAPINBUF:
1634 case SNDCTL_DSP_MAPOUTBUF:
1638 case SNDCTL_DSP_SETSYNCRO:
1639 case SNDCTL_DSP_SETDUPLEX:
1640 case SNDCTL_DSP_PROFILE:
1641 IOCTL_RETURN(0, NULL);
1644 warn_os(os, "unknown ioctl 0x%x", cmd);
1648 assert(0); /* control shouldn't reach here */
1650 fuse_reply_err(req, -ret);
1654 static int dsp_mmap_dir(int prot)
1656 if (!(prot & PROT_WRITE))
1661 static void dsp_mmap(fuse_req_t req, void *addr, size_t len, int prot,
1662 int flags, off_t offset, struct fuse_file_info *fi,
1665 int dir = dsp_mmap_dir(prot);
1666 struct ossp_dsp_mmap_arg arg = { };
1667 struct ossp_stream *os;
1668 struct ossp_dsp_stream *dsps;
1671 os = find_os(fi->fh);
1673 fuse_reply_err(req, EBADF);
1676 dsps = os_to_dsps(os);
1678 if (!os->mmap_off || len > os->mmap_size / 2) {
1679 fuse_reply_err(req, EINVAL);
1683 pthread_mutex_lock(&os->mmap_mutex);
1686 if (dsps->mmapped & (1 << dir))
1692 ret = exec_simple_cmd(os, OSSP_DSP_MMAP, &arg, NULL);
1694 dsps->mmapped |= 1 << dir;
1697 pthread_mutex_unlock(&os->mmap_mutex);
1700 fuse_reply_mmap(req, os->mmap_off + dir * os->mmap_size / 2, 0);
1702 fuse_reply_err(req, -ret);
1705 static void dsp_munmap(fuse_req_t req, size_t len, struct fuse_file_info *fi,
1706 off_t offset, uint64_t mh)
1708 struct ossp_stream *os;
1709 struct ossp_dsp_stream *dsps;
1712 os = find_os(fi->fh);
1715 dsps = os_to_dsps(os);
1717 pthread_mutex_lock(&os->mmap_mutex);
1719 for (dir = 0; dir < 2; dir++)
1720 if (offset == os->mmap_off + dir * os->mmap_size / 2)
1722 if (dir == 2 || len > os->mmap_size / 2) {
1723 warn_os(os, "invalid munmap request "
1724 "offset=%llu len=%zu mmapped=0x%x",
1725 (unsigned long long)offset, len, dsps->mmapped);
1729 rc = exec_simple_cmd(os, OSSP_DSP_MUNMAP, &dir, NULL);
1731 warn_ose(os, rc, "MUNMAP failed for dir=%d", dir);
1733 dsps->mmapped &= ~(1 << dir);
1736 pthread_mutex_unlock(&os->mmap_mutex);
1738 fuse_reply_none(req);
1743 /***************************************************************************
1747 static void *notify_poller(void *arg)
1749 struct epoll_event events[1024];
1753 nfds = epoll_wait(notify_epfd, events, ARRAY_SIZE(events), -1);
1754 for (i = 0; i < nfds; i++) {
1756 struct ossp_stream *os;
1757 struct ossp_notify notify;
1760 os = find_os_by_notify_rx(events[i].data.fd);
1762 err("can't find stream for notify_rx fd %d",
1764 epoll_ctl(notify_epfd, EPOLL_CTL_DEL, events[i].data.fd,
1766 /* we don't know what's going on, don't close the fd */
1770 while ((ret = read(os->notify_rx,
1771 ¬ify, sizeof(notify))) > 0) {
1774 if (ret != sizeof(notify)) {
1775 warn_os(os, "short read on notify_rx (%zu, "
1776 "expected %zu), killing the stream",
1777 ret, sizeof(notify));
1781 if (notify.magic != OSSP_NOTIFY_MAGIC) {
1782 warn_os(os, "invalid magic on notification, "
1783 "killing the stream");
1788 if (notify.opcode >= OSSP_NR_NOTIFY_OPCODES)
1791 dbg1_os(os, "NOTIFY %s", ossp_notify_str[notify.opcode]);
1793 switch (notify.opcode) {
1794 case OSSP_NOTIFY_POLL:
1797 case OSSP_NOTIFY_OBITUARY:
1800 case OSSP_NOTIFY_VOLCHG:
1801 pthread_mutex_lock(&mixer_mutex);
1802 os->mixer->modify_counter++;
1803 pthread_mutex_unlock(&mixer_mutex);
1807 warn_os(os, "unknown notification %d",
1813 else if (ret < 0 && errno != EAGAIN) {
1814 warn_ose(os, -errno, "read fail on notify fd");
1818 if (!do_notify && !os->dead)
1821 pthread_mutex_lock(&mutex);
1824 fuse_lowlevel_notify_poll(os->ph);
1825 fuse_pollhandle_destroy(os->ph);
1830 dbg0_os(os, "removing %d from notify poll list",
1832 epoll_ctl(notify_epfd, EPOLL_CTL_DEL, os->notify_rx,
1834 close(os->notify_rx);
1836 pthread_cond_broadcast(¬ify_poller_kill_wait);
1839 pthread_mutex_unlock(&mutex);
1845 /***************************************************************************
1846 * Slave corpse reaper
1849 static void *slave_reaper(void *arg)
1851 struct ossp_stream *os;
1855 pthread_mutex_lock(&mutex);
1857 while (list_empty(&slave_corpse_list))
1858 pthread_cond_wait(&slave_reaper_wait, &mutex);
1860 os = list_first_entry(&slave_corpse_list, struct ossp_stream, link);
1861 list_del_init(&os->link);
1863 pthread_mutex_unlock(&mutex);
1866 pid = waitpid(os->slave_pid, &status, 0);
1867 } while (pid < 0 && errno == EINTR);
1870 if (errno == ECHILD)
1871 warn_ose(os, -errno, "slave %d already gone?",
1874 fatal_e(-errno, "waitpid(%d) failed", os->slave_pid);
1877 pthread_mutex_lock(&mutex);
1879 dbg1_os(os, "slave %d reaped", os->slave_pid);
1880 __clear_bit(os->id, os_id_bitmap);
1887 /***************************************************************************
1888 * Stuff to bind and start everything
1891 static void ossp_daemonize(void)
1898 fd = open("/dev/null", O_RDWR);
1908 fatal_e(-errno, "failed to create pipe for init wait");
1910 if (fcntl(pfd[0], F_SETFD, FD_CLOEXEC) < 0 ||
1911 fcntl(pfd[1], F_SETFD, FD_CLOEXEC) < 0)
1912 fatal_e(-errno, "failed to set CLOEXEC on init wait pipe");
1916 fatal_e(-errno, "failed to fork for daemon");
1920 init_wait_fd = pfd[1];
1922 /* be evil, my child */
1928 /* wait for init completion and pass over success indication */
1932 ret = read(pfd[0], &err, sizeof(err));
1933 } while (ret < 0 && errno == EINTR);
1935 if (ret == sizeof(err) && err == 0)
1938 fatal("daemon init failed ret=%zd err=%d", ret, err);
1942 static void ossp_init_done(void *userdata)
1944 /* init complete, notify parent if it's waiting */
1945 if (init_wait_fd >= 0) {
1949 ret = write(init_wait_fd, &err, sizeof(err));
1950 if (ret != sizeof(err))
1951 fatal_e(-errno, "failed to notify init completion, "
1953 close(init_wait_fd);
1958 static const struct cuse_lowlevel_ops mixer_ops = {
1960 .release = mixer_release,
1961 .ioctl = mixer_ioctl,
1964 static const struct cuse_lowlevel_ops dsp_ops = {
1965 .init_done = ossp_init_done,
1967 .release = dsp_release,
1974 .munmap = dsp_munmap,
1978 static const struct cuse_lowlevel_ops adsp_ops = {
1980 .release = dsp_release,
1987 .munmap = dsp_munmap,
1991 static const char *usage =
1992 "usage: osspd [options]\n"
1995 " --help print this help message\n"
1996 " --dsp=NAME DSP device name (default dsp)\n"
1997 " --dsp-maj=MAJ DSP device major number (default 14)\n"
1998 " --dsp-min=MIN DSP device minor number (default 3)\n"
1999 " --adsp=NAME Aux DSP device name (default adsp, blank to disable)\n"
2000 " --adsp-maj=MAJ Aux DSP device major number (default 14)\n"
2001 " --adsp-min=MIN Aux DSP device minor number (default 12)\n"
2002 " --mixer=NAME mixer device name (default mixer, blank to disable)\n"
2003 " --mixer-maj=MAJ mixer device major number (default 14)\n"
2004 " --mixer-min=MIN mixer device minor number (default 0)\n"
2005 " --max=MAX maximum number of open streams (default 256)\n"
2006 " --umax=MAX maximum number of open streams per UID (default --max)\n"
2007 " --exit-on-idle exit if idle\n"
2008 " --dsp-slave=PATH DSP slave (default ossp-padsp in the same dir)\n"
2009 " --log=LEVEL log level (0..6)\n"
2010 " --timestamp timestamp log messages\n"
2011 " -v increase verbosity, can be specified multiple times\n"
2012 " -f Run in foreground (don't daemonize)\n"
2020 unsigned adsp_major;
2021 unsigned adsp_minor;
2023 unsigned mixer_major;
2024 unsigned mixer_minor;
2025 unsigned max_streams;
2026 unsigned umax_streams;
2027 char *dsp_slave_path;
2035 #define OSSP_OPT(t, p) { t, offsetof(struct ossp_param, p), 1 }
2037 static const struct fuse_opt ossp_opts[] = {
2038 OSSP_OPT("--dsp=%s", dsp_name),
2039 OSSP_OPT("--dsp-maj=%u", dsp_major),
2040 OSSP_OPT("--dsp-min=%u", dsp_minor),
2041 OSSP_OPT("--adsp=%s", adsp_name),
2042 OSSP_OPT("--adsp-maj=%u", adsp_major),
2043 OSSP_OPT("--adsp-min=%u", adsp_minor),
2044 OSSP_OPT("--mixer=%s", mixer_name),
2045 OSSP_OPT("--mixer-maj=%u", mixer_major),
2046 OSSP_OPT("--mixer-min=%u", mixer_minor),
2047 OSSP_OPT("--max=%u", max_streams),
2048 OSSP_OPT("--umax=%u", umax_streams),
2049 OSSP_OPT("--exit-on-idle", exit_on_idle),
2050 OSSP_OPT("--dsp-slave=%s", dsp_slave_path),
2051 OSSP_OPT("--timestamp", timestamp),
2052 OSSP_OPT("--log=%u", log_level),
2054 FUSE_OPT_KEY("-h", 0),
2055 FUSE_OPT_KEY("--help", 0),
2056 FUSE_OPT_KEY("-v", 1),
2060 static struct fuse_session *setup_ossp_cuse(const struct cuse_lowlevel_ops *ops,
2061 const char *name, int major,
2062 int minor, int argc, char **argv)
2065 const char *bufp = name_buf;
2066 struct cuse_info ci = { .dev_major = major, .dev_minor = minor,
2067 .dev_info_argc = 1, .dev_info_argv = &bufp,
2068 .flags = CUSE_UNRESTRICTED_IOCTL };
2069 struct fuse_session *se;
2072 snprintf(name_buf, sizeof(name_buf), "DEVNAME=%s", name);
2074 se = cuse_lowlevel_setup(argc, argv, &ci, ops, NULL, NULL);
2076 err("failed to setup %s CUSE", name);
2080 fd = fuse_chan_fd(fuse_session_next_chan(se, NULL));
2083 fd != fuse_mmap_fd(se) &&
2085 fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
2086 err_e(-errno, "failed to set CLOEXEC on %s CUSE fd", name);
2087 cuse_lowlevel_teardown(se);
2094 static void *cuse_worker(void *arg)
2096 struct fuse_session *se = arg;
2099 rc = fuse_session_loop_mt(se);
2100 cuse_lowlevel_teardown(se);
2102 return (void *)(unsigned long)rc;
2105 static int process_arg(void *data, const char *arg, int key,
2106 struct fuse_args *outargs)
2108 struct ossp_param *param = data;
2112 fprintf(stderr, usage);
2122 int main(int argc, char **argv)
2124 static struct ossp_param param = {
2125 .dsp_name = DFL_DSP_NAME,
2126 .dsp_major = DFL_DSP_MAJOR, .dsp_minor = DFL_DSP_MINOR,
2127 .adsp_name = DFL_ADSP_NAME,
2128 .adsp_major = DFL_ADSP_MAJOR, .adsp_minor = DFL_ADSP_MINOR,
2129 .mixer_name = DFL_MIXER_NAME,
2130 .mixer_major = DFL_MIXER_MAJOR, .mixer_minor = DFL_MIXER_MINOR,
2131 .max_streams = DFL_MAX_STREAMS,
2133 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
2134 char path_buf[PATH_MAX], *dir;
2135 char adsp_buf[64] = "", mixer_buf[64] = "";
2136 struct sigaction sa;
2137 struct stat stat_buf;
2141 snprintf(ossp_log_name, sizeof(ossp_log_name), "osspd");
2142 param.log_level = ossp_log_level;
2144 if (fuse_opt_parse(&args, ¶m, ossp_opts, process_arg))
2145 fatal("failed to parse arguments");
2150 max_streams = param.max_streams;
2151 hashtbl_size = max_streams / 2 + 13;
2153 umax_streams = max_streams;
2154 if (param.umax_streams)
2155 umax_streams = param.umax_streams;
2156 if (param.log_level > OSSP_LOG_MAX)
2157 param.log_level = OSSP_LOG_MAX;
2159 param.log_level = -param.log_level;
2160 ossp_log_level = param.log_level;
2161 ossp_log_timestamp = param.timestamp;
2166 /* daemonization already handled, prevent forking inside FUSE */
2167 fuse_opt_add_arg(&args, "-f");
2169 info("OSS Proxy v%s (C) 2008-2010 by Tejun Heo <teheo@suse.de>",
2172 /* ignore stupid SIGPIPEs */
2173 memset(&sa, 0, sizeof(sa));
2174 sa.sa_handler = SIG_IGN;
2175 if (sigaction(SIGPIPE, &sa, NULL))
2176 fatal_e(-errno, "failed to ignore SIGPIPE");
2178 /* determine slave path and check for availability */
2179 ret = readlink("/proc/self/exe", path_buf, PATH_MAX - 1);
2181 fatal_e(-errno, "failed to determine executable path");
2182 path_buf[ret] = '\0';
2183 dir = dirname(path_buf);
2185 if (param.dsp_slave_path) {
2186 strncpy(dsp_slave_path, param.dsp_slave_path, PATH_MAX - 1);
2187 dsp_slave_path[PATH_MAX - 1] = '\0';
2189 ret = snprintf(dsp_slave_path, PATH_MAX, "%s/%s",
2191 if (ret >= PATH_MAX)
2192 fatal("dsp slave pathname too long");
2195 if (stat(dsp_slave_path, &stat_buf))
2196 fatal_e(-errno, "failed to stat %s", dsp_slave_path);
2197 if (!S_ISREG(stat_buf.st_mode) || !(stat_buf.st_mode & 0444))
2198 fatal("%s is not executable", dsp_slave_path);
2200 /* allocate tables */
2201 os_id_bitmap = calloc(BITS_TO_LONGS(max_streams), sizeof(long));
2202 mixer_tbl = calloc(hashtbl_size, sizeof(mixer_tbl[0]));
2203 os_tbl = calloc(hashtbl_size, sizeof(os_tbl[0]));
2204 os_pgrp_tbl = calloc(hashtbl_size, sizeof(os_pgrp_tbl[0]));
2205 os_notify_tbl = calloc(hashtbl_size, sizeof(os_notify_tbl[0]));
2206 if (!os_id_bitmap || !mixer_tbl || !os_tbl || !os_pgrp_tbl ||
2208 fatal("failed to allocate stream hash tables");
2209 for (u = 0; u < hashtbl_size; u++) {
2210 INIT_LIST_HEAD(&mixer_tbl[u]);
2211 INIT_LIST_HEAD(&os_tbl[u]);
2212 INIT_LIST_HEAD(&os_pgrp_tbl[u]);
2213 INIT_LIST_HEAD(&os_notify_tbl[u]);
2215 __set_bit(0, os_id_bitmap); /* don't use id 0 */
2217 /* create mixer delayed reference worker */
2218 ret = -pthread_create(&mixer_delayed_put_thread, NULL,
2219 mixer_delayed_put_worker, NULL);
2221 fatal_e(ret, "failed to create mixer delayed put worker");
2223 /* if exit_on_idle, touch mixer for pgrp0 */
2224 exit_on_idle = param.exit_on_idle;
2226 struct ossp_mixer *mixer;
2228 mixer = get_mixer(0);
2230 fatal("failed to touch idle mixer");
2234 /* create notify epoll and kick off watcher thread */
2235 notify_epfd = epoll_create(max_streams);
2236 if (notify_epfd < 0)
2237 fatal_e(-errno, "failed to create notify epoll");
2238 if (fcntl(notify_epfd, F_SETFD, FD_CLOEXEC) < 0)
2239 fatal_e(-errno, "failed to set CLOEXEC on notify epfd");
2241 ret = -pthread_create(¬ify_poller_thread, NULL, notify_poller, NULL);
2243 fatal_e(ret, "failed to create notify poller thread");
2245 /* create reaper for slave corpses */
2246 ret = -pthread_create(&slave_reaper_thread, NULL, slave_reaper, NULL);
2248 fatal_e(ret, "failed to create slave reaper thread");
2250 /* we're set, let's setup fuse structures */
2251 if (strlen(param.mixer_name))
2252 mixer_se = setup_ossp_cuse(&mixer_ops, param.mixer_name,
2253 param.mixer_major, param.mixer_minor,
2254 args.argc, args.argv);
2255 if (strlen(param.adsp_name))
2256 adsp_se = setup_ossp_cuse(&dsp_ops, param.adsp_name,
2257 param.adsp_major, param.adsp_minor,
2258 args.argc, args.argv);
2260 dsp_se = setup_ossp_cuse(&dsp_ops, param.dsp_name,
2261 param.dsp_major, param.dsp_minor,
2262 args.argc, args.argv);
2264 fatal("can't create dsp, giving up");
2267 snprintf(mixer_buf, sizeof(mixer_buf), ", %s (%d:%d)",
2268 param.mixer_name, param.mixer_major, param.mixer_minor);
2270 snprintf(adsp_buf, sizeof(adsp_buf), ", %s (%d:%d)",
2271 param.adsp_name, param.adsp_major, param.adsp_minor);
2273 info("Creating %s (%d:%d)%s%s", param.dsp_name, param.dsp_major,
2274 param.dsp_minor, adsp_buf, mixer_buf);
2276 /* start threads for mixer and adsp */
2278 ret = -pthread_create(&cuse_mixer_thread, NULL,
2279 cuse_worker, mixer_se);
2281 err_e(ret, "failed to create mixer worker");
2284 ret = -pthread_create(&cuse_adsp_thread, NULL,
2285 cuse_worker, adsp_se);
2287 err_e(ret, "failed to create adsp worker");
2290 /* run CUSE for /dev/dsp in the main thread */
2291 ret = (ssize_t)cuse_worker(dsp_se);
2293 fatal("dsp worker failed");