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"
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)
55 SNDRV_OSS_VERSION
= ((3<<16)|(8<<8)|(1<<4)|(0)), /* 3.8.1a */
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 */
69 struct list_head link
;
76 struct list_head link
;
77 struct list_head delayed_put_link
;
79 /* the following two fields are protected by mixer_mutex */
85 struct ossp_mixer_cmd
{
86 struct ossp_mixer
*mixer
;
87 struct ossp_mixer_arg set
;
92 #define for_each_vol(i, j) \
93 for (i = 0, j = 0; i < 2; j += i << 1, j++, i = j >> 1, j &= 1)
96 unsigned id
; /* stream ID */
97 struct list_head link
;
98 struct list_head pgrp_link
;
99 struct list_head notify_link
;
101 pthread_mutex_t cmd_mutex
;
102 pthread_mutex_t mmap_mutex
;
103 struct fuse_pollhandle
*ph
;
105 /* stream owner info */
117 /* the following dead flag is set asynchronously, keep it separate. */
120 /* stream mixer state, protected by mixer_mutex */
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
;
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 /***************************************************************************
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
)
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
);
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
)
234 pthread_mutex_unlock(&mutex
);
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
) {
248 pthread_mutex_unlock(&mutex
);
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
))];
275 dbg1_os(os
, "%s carg=%zu din=%zu rarg=%zu dout=%zu",
276 ossp_cmd_str
[opcode
], carg_size
, din_size
, rarg_size
,
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) {
294 snprintf(reason
, sizeof(reason
), "command sendmsg failed: %s",
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",
306 if ((rc
= read_fill(os
->cmd_fd
, &reply
, sizeof(reply
))) < 0) {
307 snprintf(reason
, sizeof(reason
), "can't read reply: %s",
312 if (reply
.magic
!= OSSP_REPLY_MAGIC
) {
313 snprintf(reason
, sizeof(reason
),
314 "reply magic mismatch %x != %x",
315 reply
.magic
, OSSP_REPLY_MAGIC
);
320 if (reply
.result
< 0)
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
);
331 dout_size
= reply
.dout_size
;
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",
343 dbg1_os(os
, " completed, result=%d dout=%zu",
344 reply
.result
, dout_size
);
348 warn_os(os
, "communication with slave failed (%s)", reason
);
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
)
361 /* mixer command is handled exlicitly below */
362 is_mixer
= opcode
== OSSP_MIXER
;
364 ret
= -pthread_mutex_trylock(&os
->cmd_mutex
);
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
;
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
,
389 pthread_mutex_lock(&mixer_mutex
);
391 /* was there mixer set request while executing mixer command? */
393 if (os
->vol_set
[i
][j
] >= 0)
396 /* update internal mixer state */
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
= { };
439 in_iov
.iov_base
= uarg
;
440 in_iov
.iov_len
= in_sz
;
443 assert(in_bufsz
== in_sz
);
444 memcpy(in
, in_buf
, in_sz
);
450 out_iov
.iov_base
= uarg
;
451 out_iov
.iov_len
= out_sz
;
454 assert(out_bufsz
== out_sz
);
458 fuse_reply_ioctl_retry(req
, &in_iov
, 1, &out_iov
, 1);
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)) \
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
);
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");
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
);
507 if (list_empty(&mixer
->delayed_put_link
))
510 list_del_init(&mixer
->delayed_put_link
);
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
));
528 warn("failed to allocate mixer for %d", pgrp
);
534 INIT_LIST_HEAD(&mixer
->link
);
535 INIT_LIST_HEAD(&mixer
->delayed_put_link
);
537 memset(mixer
->vol
, -1, sizeof(mixer
->vol
));
539 list_add(&mixer
->link
, mixer_tbl_head(pgrp
));
541 dbg0("CREATE mixer(%d)", pgrp
);
544 pthread_mutex_unlock(&mutex
);
548 static void put_mixer(struct ossp_mixer
*mixer
)
550 pthread_mutex_lock(&mutex
);
553 if (mixer
->refcnt
== 1) {
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
);
562 put_mixer_real(mixer
);
565 pthread_mutex_unlock(&mutex
);
568 static void *mixer_delayed_put_worker(void *arg
)
570 struct ossp_mixer
*mixer
;
574 pthread_mutex_lock(&mutex
);
576 clock_gettime(CLOCK_REALTIME
, &ts
);
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
)
587 assert(mixer
->refcnt
== 1);
588 put_mixer_real(mixer
);
593 ts
.tv_sec
= mixer
->put_expires
+ 1;
594 pthread_cond_timedwait(&mixer_delayed_put_cond
, &mutex
, &ts
);
596 pthread_cond_wait(&mixer_delayed_put_cond
, &mutex
);
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
;
610 static int exec_mixer_cmd(struct ossp_mixer_cmd
*mxcmd
, struct ossp_stream
*os
)
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;
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
);
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");
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
;
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
)
657 if (os
->vol
[i
][j
] < 0)
659 vol
[i
][j
] += os
->vol
[i
][j
];
663 pthread_mutex_unlock(&mutex
);
665 /* calculate the summary volume values */
667 if (mxcmd
->set
.vol
[i
][j
] >= 0)
668 vol
[i
][j
] = mxcmd
->set
.vol
[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
];
676 vol
[i
][j
] = min(max(0, vol
[i
][j
]), 100);
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
,
690 const char *id
= "OSS Proxy", *name
= "Mixer";
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
));
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
));
716 i
= SNDRV_OSS_VERSION
;
718 case SOUND_MIXER_READ_DEVMASK
:
719 case SOUND_MIXER_READ_STEREODEVS
:
720 i
= SOUND_MASK_PCM
| SOUND_MASK_IGAIN
;
722 case SOUND_MIXER_READ_CAPS
:
723 i
= SOUND_CAP_EXCL_INPUT
;
725 case SOUND_MIXER_READ_RECMASK
:
726 case SOUND_MIXER_READ_RECSRC
:
727 i
= SOUND_MASK_IGAIN
;
731 fuse_reply_ioctl(req
, 0, &i
, sizeof(i
));
734 case SOUND_MIXER_WRITE_RECSRC
:
735 fuse_reply_ioctl(req
, 0, NULL
, 0);
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
;
750 int slot
= cmd
& 0xff, dir
;
754 mixer_simple_ioctl(req
, mixer
, cmd
, uarg
, in_buf
, in_bufsz
, out_bufsz
,
760 if (!(cmd
& (SIOC_IN
| SIOC_OUT
)))
764 * Okay, it's not one of the easy ones. Build mxcmd for
765 * actual volume control.
773 case SOUND_MIXER_PCM
:
776 case SOUND_MIXER_IGAIN
:
781 fuse_reply_ioctl(req
, 0, &i
, sizeof(i
));
785 init_mixer_cmd(&mxcmd
, mixer
);
793 if (l
> 100 || r
> 100)
796 mixer
->vol
[dir
][LEFT
] = mxcmd
.set
.vol
[dir
][LEFT
] = l
;
797 mixer
->vol
[dir
][RIGHT
] = mxcmd
.set
.vol
[dir
][RIGHT
] = r
;
802 * Apply volume conrol
804 /* acquire target streams */
805 pthread_mutex_lock(&mutex
);
806 osa
= calloc(max_streams
, sizeof(osa
[0]));
808 pthread_mutex_unlock(&mutex
);
814 list_for_each_entry(os
, os_pgrp_tbl_head(mixer
->pgrp
), pgrp_link
) {
815 if (os
->pgrp
== mixer
->pgrp
) {
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
]);
829 finish_mixer_cmd(&mxcmd
);
833 fuse_reply_ioctl(req
, 0, &mxcmd
.rvol
, sizeof(mxcmd
.rvol
));
835 fuse_reply_ioctl(req
, 0, NULL
, 0);
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
;
849 rc
= get_proc_self_info(pid
, &pgrp
, NULL
, 0);
851 err_e(rc
, "get_proc_self_info(%d) failed", pid
);
852 fuse_reply_err(req
, -rc
);
856 mixer
= get_mixer(pgrp
);
860 fuse_reply_open(req
, fi
);
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
);
873 fuse_reply_err(req
, EBADF
);
877 mixer_do_ioctl(req
, mixer
, signed_cmd
, uarg
, in_buf
, in_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
);
888 fuse_reply_err(req
, 0);
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
)
911 sprintf(shmname
, "/ossp.%i", getpid());
912 fd
= shm_open(shmname
, O_RDWR
| O_CREAT
| O_EXCL
| O_TRUNC
,
917 warn_ose(os
, rc
, "failed to open shared memory");
920 rc
= shm_unlink(shmname
);
924 warn_ose(os
, rc
, "failed to unlink shared memory");
927 rc
= ftruncate(fd
, mmap_size
+ 2*sizeof(struct ossp_transfer
));
931 warn_ose(os
, rc
, "failed to set shared memory size");
934 rc
= fcntl(fd
, F_SETFD
, 0); /* reset cloexec */
938 warn_ose(os
, rc
, "failed to reset close-on-exec for shared memory");
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
) {
946 warn_ose(os
, rc
, "failed to mmap shared memory");
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) {
957 munmap(p
, mmap_size
+ 2 * sizeof(struct ossp_transfer
));
958 warn_ose(os
, rc
, "failed to init shared semaphore");
963 os
->mmap_size
= mmap_size
;
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
;
980 assert(stream_size
>= sizeof(struct ossp_stream
));
981 os
= calloc(1, stream_size
);
985 INIT_LIST_HEAD(&os
->link
);
986 INIT_LIST_HEAD(&os
->pgrp_link
);
987 INIT_LIST_HEAD(&os
->notify_link
);
990 rc
= -pthread_mutex_init(&os
->cmd_mutex
, NULL
);
994 rc
= -pthread_mutex_init(&os
->mmap_mutex
, NULL
);
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
) {
1007 ucnt
= calloc(1, sizeof(*ucnt
));
1011 list_add(&ucnt
->link
, &uid_cnt_list
);
1015 if (ucnt
->nr_os
+ 1 > umax_streams
)
1018 /* everything looks fine, allocate id and init stream */
1020 os
->id
= find_next_zero_bit(os_id_bitmap
, max_streams
, 0);
1021 if (os
->id
>= max_streams
)
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];
1035 rc
= os_create_shared_memory(os
, mmap_size
);
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
));
1047 pthread_mutex_unlock(&mutex
);
1051 pthread_mutex_unlock(&mutex
);
1052 pthread_mutex_destroy(&os
->mmap_mutex
);
1053 err_destroy_cmd_mutex
:
1054 pthread_mutex_destroy(&os
->cmd_mutex
);
1060 static void shutdown_notification(struct ossp_stream
*os
)
1062 struct ossp_notify obituary
= { .magic
= OSSP_NOTIFY_MAGIC
,
1063 .opcode
= OSSP_NOTIFY_OBITUARY
};
1067 * Shutdown notification for this stream. We politely ask
1068 * notify_poller to shut the receive side down to avoid racing
1071 while (os
->notify_rx
>= 0) {
1072 ret
= write(os
->notify_tx
, &obituary
, sizeof(obituary
));
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
);
1084 if (ret
!= sizeof(obituary
))
1085 warn_os(os
, "short transfer on notify_tx");
1086 pthread_cond_wait(¬ify_poller_kill_wait
, &mutex
);
1090 static void put_os(struct ossp_stream
*os
)
1095 pthread_mutex_lock(&mutex
);
1099 pthread_mutex_unlock(&mutex
);
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
);
1113 pthread_mutex_unlock(&mutex
);
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);
1140 sprintf(procenviron
, "/proc/%d/environ", pid
);
1141 fd
= open(procenviron
, O_RDONLY
);
1146 * There should really be a 'read whole file to a newly allocated
1149 while ((ret
= read(fd
, data
+ ofs
, step
)) > 0) {
1152 newdata
= realloc(data
, ofs
+ step
+ 1);
1161 /* Append the extra 0 for end condition */
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))
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
= { };
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
)) {
1202 warn_e(rc
, "failed to create slave command channel");
1206 if (fcntl(notify_sock
[0], F_SETFL
, O_NONBLOCK
) < 0) {
1208 warn_e(rc
, "failed to set NONBLOCK on notify sock");
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
);
1219 warn_e(rc
, "failed to allocate stream for %d", pid
);
1224 os
->mixer
= get_mixer(pgrp
);
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.
1245 warn_ose(os
, rc
, "failed to add notify epoll");
1246 close(os
->notify_rx
);
1252 os
->slave_pid
= fork();
1253 if (os
->slave_pid
< 0) {
1255 warn_ose(os
, rc
, "failed to fork slave");
1259 if (os
->slave_pid
== 0) {
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
};
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");
1276 pwd
= getpwuid(os
->uid
);
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 */
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') {
1290 err_ose(os
, rc
, "slave path too long");
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",
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
);
1307 err_ose(os
, rc
, "execv failed for <%d>", pid
);
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) {
1317 err_ose(os
, rc
, "failed to set CLOEXEC on server side fds");
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
);
1331 goto close_client_fds
;
1337 pthread_mutex_unlock(&create_mutex
);
1341 for (i
= 0; i
< 2; i
++) {
1343 close(notify_sock
[i
]);
1345 pthread_mutex_unlock(&create_mutex
);
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
;
1361 ret
= get_proc_self_info(fuse_ctx
->pid
, &pgrp
, NULL
, 0);
1363 err_e(ret
, "get_proc_self_info(%d) failed", fuse_ctx
->pid
);
1367 ret
= create_os(dsp_slave_path
, sizeof(*dsps
), DSPS_MMAP_SIZE
,
1368 fuse_ctx
->pid
, pgrp
, fuse_ctx
->uid
, fuse_ctx
->gid
,
1372 dsps
= os_to_dsps(os
);
1375 switch (fi
->flags
& O_ACCMODE
) {
1377 dsps
->rw
|= 1 << PLAY
;
1380 dsps
->rw
|= 1 << REC
;
1383 dsps
->rw
|= (1 << PLAY
) | (1 << REC
);
1389 arg
.flags
= fi
->flags
;
1390 arg
.opener_pid
= os
->pid
;
1391 ret
= exec_simple_cmd(&dsps
->os
, OSSP_DSP_OPEN
, &arg
, NULL
);
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
);
1406 fi
->nonseekable
= 1;
1409 fuse_reply_open(req
, fi
);
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
);
1433 fuse_reply_err(req
, 0);
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
;
1448 os
= find_os(fi
->fh
);
1451 dsps
= os_to_dsps(os
);
1454 if (!(dsps
->rw
& (1 << REC
)))
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);
1472 fuse_reply_buf(req
, buf
, size
);
1474 fuse_reply_err(req
, -ret
);
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
;
1488 os
= find_os(fi
->fh
);
1491 dsps
= os_to_dsps(os
);
1494 if (!(dsps
->rw
& (1 << PLAY
)))
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);
1507 fuse_reply_write(req
, ret
);
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
;
1521 os
= find_os(fi
->fh
);
1526 pthread_mutex_lock(&mutex
);
1528 fuse_pollhandle_destroy(os
->ph
);
1530 pthread_mutex_unlock(&mutex
);
1533 ret
= exec_simple_cmd(os
, OSSP_DSP_POLL
, ¬ify
, &revents
);
1536 fuse_reply_poll(req
, revents
);
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
;
1554 os
= find_os(fi
->fh
);
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
,
1568 case OSS_GETVERSION
:
1569 i
= SNDRV_OSS_VERSION
;
1570 PREP_UARG(NULL
, &i
);
1571 fuse_reply_ioctl(req
, 0, &i
, sizeof(i
));
1574 case SNDCTL_DSP_GETCAPS
:
1575 i
= DSP_CAP_DUPLEX
| DSP_CAP_REALTIME
| DSP_CAP_TRIGGER
|
1580 PREP_UARG(NULL
, &i
);
1581 fuse_reply_ioctl(req
, 0, &i
, sizeof(i
));
1584 case SNDCTL_DSP_NONBLOCK
:
1587 fuse_reply_ioctl(req
, 0, NULL
, 0);
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
);
1597 fuse_reply_ioctl(req
, 0, NULL
, 0);
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
);
1611 fuse_reply_ioctl(req
, 0, &i
, sizeof(i
));
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
;
1620 ret
= exec_simple_cmd(&dsps
->os
, op
, &i
, &i
);
1623 fuse_reply_ioctl(req
, 0, &i
, sizeof(i
));
1626 case SNDCTL_DSP_STEREO
:
1627 PREP_UARG(NULL
, &i
);
1629 ret
= exec_simple_cmd(&dsps
->os
, OSSP_DSP_SET_CHANNELS
, &i
, &i
);
1633 fuse_reply_ioctl(req
, 0, &i
, sizeof(i
));
1636 case SNDCTL_DSP_SETFRAGMENT
:
1637 PREP_UARG(&i
, NULL
);
1638 ret
= exec_simple_cmd(&dsps
->os
,
1639 OSSP_DSP_SET_FRAGMENT
, &i
, NULL
);
1642 fuse_reply_ioctl(req
, 0, NULL
, 0);
1645 case SNDCTL_DSP_SETTRIGGER
:
1646 PREP_UARG(&i
, NULL
);
1647 ret
= exec_simple_cmd(&dsps
->os
,
1648 OSSP_DSP_SET_TRIGGER
, &i
, NULL
);
1651 fuse_reply_ioctl(req
, 0, NULL
, 0);
1654 case SNDCTL_DSP_GETOSPACE
:
1655 case SNDCTL_DSP_GETISPACE
: {
1656 struct audio_buf_info info
;
1659 if (cmd
== SNDCTL_DSP_GETOSPACE
) {
1660 if (!(dsps
->rw
& (1 << PLAY
)))
1662 op
= OSSP_DSP_GET_OSPACE
;
1664 if (!(dsps
->rw
& (1 << REC
)))
1666 op
= OSSP_DSP_GET_ISPACE
;
1669 PREP_UARG(NULL
, &info
);
1670 ret
= exec_simple_cmd(&dsps
->os
, op
, NULL
, &info
);
1673 fuse_reply_ioctl(req
, 0, &info
, sizeof(info
));
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
);
1687 fuse_reply_ioctl(req
, 0, &info
, sizeof(info
));
1691 case SNDCTL_DSP_GETODELAY
:
1692 PREP_UARG(NULL
, &i
);
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 */
1698 case SOUND_PCM_WRITE_FILTER
:
1699 case SOUND_PCM_READ_FILTER
:
1703 case SNDCTL_DSP_MAPINBUF
:
1704 case SNDCTL_DSP_MAPOUTBUF
:
1708 case SNDCTL_DSP_SETSYNCRO
:
1709 case SNDCTL_DSP_SETDUPLEX
:
1710 case SNDCTL_DSP_PROFILE
:
1711 fuse_reply_ioctl(req
, 0, NULL
, 0);
1715 warn_os(os
, "unknown ioctl 0x%x", cmd
);
1722 fuse_reply_err(req
, -ret
);
1726 static int dsp_mmap_dir(int prot
)
1728 if (!(prot
& PROT_WRITE
))
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
;
1752 os
= find_os(fi
->fh
);
1754 fuse_reply_err(req
, EBADF
);
1757 dsps
= os_to_dsps(os
);
1759 if (len
> os
->mmap_size
/ 2) {
1760 fuse_reply_err(req
, EINVAL
);
1764 pthread_mutex_lock(&os
->mmap_mutex
);
1767 if (dsps
->mmapped
& (1 << dir
))
1773 ret
= exec_simple_cmd(os
, OSSP_DSP_MMAP
, &arg
, NULL
);
1775 dsps
->mmapped
|= 1 << dir
;
1778 pthread_mutex_unlock(&os
->mmap_mutex
);
1781 fuse_reply_mmap(req
, dsp_dir_to_mapid(os
, dir
), os
->mmap_size
/ 2);
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
;
1793 os
= find_os(fi
->fh
);
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
);
1808 rc
= exec_simple_cmd(os
, OSSP_DSP_MUNMAP
, &dir
, NULL
);
1810 warn_ose(os
, rc
, "MUNMAP failed for dir=%d", dir
);
1812 dsps
->mmapped
&= ~(1 << dir
);
1815 pthread_mutex_unlock(&os
->mmap_mutex
);
1817 fuse_reply_err(req
, -rc
);
1820 static void fill_mmap_buffer(struct ossp_stream
*os
)
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
);
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
);
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
);
1854 dest
.buf
[0].mem
= os
->mmap_addr
[PLAY
] + offset
;
1856 res
= fuse_buf_copy(&dest
, bufv
, 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
);
1869 sem_post(&os
->mmap_transfer
[PLAY
].sem
);
1873 static void store_mmap_buffer(struct ossp_stream
*os
)
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);
1888 warn_ose(os
, rc
, "store_mmap_buffer: failure");
1890 sem_post(&os
->mmap_transfer
[REC
].sem
);
1895 /***************************************************************************
1899 static void *notify_poller(void *arg
)
1901 struct epoll_event events
[1024];
1905 nfds
= epoll_wait(notify_epfd
, events
, ARRAY_SIZE(events
), -1);
1906 for (i
= 0; i
< nfds
; i
++) {
1908 struct ossp_stream
*os
;
1909 struct ossp_notify notify
;
1912 os
= find_os_by_notify_rx(events
[i
].data
.fd
);
1914 err("can't find stream for notify_rx fd %d",
1916 epoll_ctl(notify_epfd
, EPOLL_CTL_DEL
, events
[i
].data
.fd
,
1918 /* we don't know what's going on, don't close the fd */
1922 while ((ret
= read(os
->notify_rx
,
1923 ¬ify
, sizeof(notify
))) > 0) {
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
));
1933 if (notify
.magic
!= OSSP_NOTIFY_MAGIC
) {
1934 warn_os(os
, "invalid magic on notification, "
1935 "killing the stream");
1940 if (notify
.opcode
>= OSSP_NR_NOTIFY_OPCODES
)
1943 dbg1_os(os
, "NOTIFY %s", ossp_notify_str
[notify
.opcode
]);
1945 switch (notify
.opcode
) {
1946 case OSSP_NOTIFY_POLL
:
1949 case OSSP_NOTIFY_OBITUARY
:
1952 case OSSP_NOTIFY_VOLCHG
:
1953 pthread_mutex_lock(&mixer_mutex
);
1954 os
->mixer
->modify_counter
++;
1955 pthread_mutex_unlock(&mixer_mutex
);
1958 case OSSP_NOTIFY_FILL
:
1959 fill_mmap_buffer(os
);
1961 case OSSP_NOTIFY_STORE
:
1962 store_mmap_buffer(os
);
1967 warn_os(os
, "unknown notification %d",
1973 else if (ret
< 0 && errno
!= EAGAIN
) {
1974 warn_ose(os
, -errno
, "read fail on notify fd");
1978 if (!do_notify
&& !os
->dead
)
1981 pthread_mutex_lock(&mutex
);
1984 fuse_lowlevel_notify_poll(os
->ph
);
1985 fuse_pollhandle_destroy(os
->ph
);
1990 dbg0_os(os
, "removing %d from notify poll list",
1992 epoll_ctl(notify_epfd
, EPOLL_CTL_DEL
, os
->notify_rx
,
1994 close(os
->notify_rx
);
1996 pthread_cond_broadcast(¬ify_poller_kill_wait
);
1999 pthread_mutex_unlock(&mutex
);
2008 /***************************************************************************
2009 * Slave corpse reaper
2012 static void *slave_reaper(void *arg
)
2014 struct ossp_stream
*os
;
2018 pthread_mutex_lock(&mutex
);
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
);
2029 pid
= waitpid(os
->slave_pid
, &status
, 0);
2030 } while (pid
< 0 && errno
== EINTR
);
2033 if (errno
== ECHILD
)
2034 warn_ose(os
, -errno
, "slave %d already gone?",
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
));
2061 /***************************************************************************
2062 * Stuff to bind and start everything
2065 static void ossp_daemonize(void)
2072 fd
= open("/dev/null", O_RDWR
);
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");
2090 fatal_e(-errno
, "failed to fork for daemon");
2094 init_wait_fd
= pfd
[1];
2096 /* be evil, my child */
2102 /* wait for init completion and pass over success indication */
2106 ret
= read(pfd
[0], &err
, sizeof(err
));
2107 } while (ret
< 0 && errno
== EINTR
);
2109 if (ret
== sizeof(err
) && err
== 0)
2112 fatal("daemon init failed ret=%zd err=%d", ret
, err
);
2116 static void ossp_init_done(void *userdata
)
2118 /* init complete, notify parent if it's waiting */
2119 if (init_wait_fd
>= 0) {
2123 ret
= write(init_wait_fd
, &err
, sizeof(err
));
2124 if (ret
!= sizeof(err
))
2125 fatal_e(-errno
, "failed to notify init completion, "
2127 close(init_wait_fd
);
2132 static const struct cuse_lowlevel_ops mixer_ops
= {
2134 .release
= mixer_release
,
2135 .ioctl
= mixer_ioctl
,
2138 static const struct cuse_lowlevel_ops dsp_ops
= {
2139 .init_done
= ossp_init_done
,
2141 .release
= dsp_release
,
2148 .munmap
= dsp_munmap
,
2149 .retrieve_reply
= dsp_retrieve_reply
,
2153 static const struct cuse_lowlevel_ops adsp_ops
= {
2155 .release
= dsp_release
,
2162 .munmap
= dsp_munmap
,
2163 .retrieve_reply
= dsp_retrieve_reply
,
2167 static const char *usage
=
2168 "usage: osspd [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"
2196 unsigned adsp_major
;
2197 unsigned adsp_minor
;
2199 unsigned mixer_major
;
2200 unsigned mixer_minor
;
2201 unsigned max_streams
;
2202 unsigned umax_streams
;
2203 char *dsp_slave_path
;
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
),
2230 FUSE_OPT_KEY("-h", 0),
2231 FUSE_OPT_KEY("--help", 0),
2232 FUSE_OPT_KEY("-v", 1),
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
)
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
;
2248 snprintf(name_buf
, sizeof(name_buf
), "DEVNAME=%s", name
);
2250 se
= cuse_lowlevel_setup(argc
, argv
, &ci
, ops
, NULL
, NULL
);
2252 err("failed to setup %s CUSE", name
);
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
);
2266 static void *cuse_worker(void *arg
)
2268 struct fuse_session
*se
= arg
;
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
;
2284 fprintf(stderr
, usage
);
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
;
2313 snprintf(ossp_log_name
, sizeof(ossp_log_name
), "osspd");
2314 param
.log_level
= ossp_log_level
;
2316 if (fuse_opt_parse(&args
, ¶m
, ossp_opts
, process_arg
))
2317 fatal("failed to parse arguments");
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
;
2331 param
.log_level
= -param
.log_level
;
2332 ossp_log_level
= param
.log_level
;
2333 ossp_log_timestamp
= param
.timestamp
;
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>",
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);
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';
2361 ret
= snprintf(dsp_slave_path
, PATH_MAX
, "%s/%s",
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
||
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
);
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
;
2398 struct ossp_mixer
*mixer
;
2400 mixer
= get_mixer(0);
2402 fatal("failed to touch idle 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(¬ify_poller_thread
, NULL
, notify_poller
, NULL
);
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
);
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
);
2436 fatal("can't create dsp, giving up");
2439 snprintf(mixer_buf
, sizeof(mixer_buf
), ", %s (%d:%d)",
2440 param
.mixer_name
, param
.mixer_major
, param
.mixer_minor
);
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 */
2450 ret
= -pthread_create(&cuse_mixer_thread
, NULL
,
2451 cuse_worker
, mixer_se
);
2453 err_e(ret
, "failed to create mixer worker");
2456 ret
= -pthread_create(&cuse_adsp_thread
, NULL
,
2457 cuse_worker
, adsp_se
);
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
);
2465 fatal("dsp worker failed");