Initial framework for dsnoop and dshare plugins.
SND_PCM_TYPE_DMIX,
/** Jack Audio Connection Kit plugin */
SND_PCM_TYPE_JACK,
- SND_PCM_TYPE_LAST = SND_PCM_TYPE_JACK
+ /** Direct Snooping plugin */
+ SND_PCM_TYPE_DSNOOP,
+ /** Direct Sharing plugin */
+ SND_PCM_TYPE_DSHARE,
+ SND_PCM_TYPE_LAST = SND_PCM_TYPE_DSNOOP
};
/** PCM type */
pcm_rate.c pcm_plug.c pcm_misc.c pcm_mmap.c pcm_multi.c \
pcm_shm.c pcm_file.c pcm_null.c pcm_share.c \
pcm_meter.c pcm_hooks.c pcm_lfloat.c pcm_ladspa.c \
- pcm_dmix.c pcm_symbols.c
+ pcm_direct.c pcm_dmix.c pcm_dsnoop.c pcm_dshare.c \
+ pcm_symbols.c
noinst_HEADERS = pcm_local.h pcm_plugin.h mask.h mask_inline.h \
interval.h interval_inline.h plugin_ops.h ladspa.h \
- pcm_dmix_i386.h
+ pcm_direct.h pcm_dmix_i386.h
alsadir = $(datadir)/alsa
static char *build_in_pcms[] = {
"adpcm", "alaw", "copy", "dmix", "file", "hooks", "hw", "ladspa", "lfloat",
"linear", "meter", "mulaw", "multi", "null", "plug", "rate", "route", "share",
- "shm", NULL
+ "shm", "dsnoop", "dshare", NULL
};
static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
--- /dev/null
+/**
+ * \file pcm/pcm_direct.c
+ * \ingroup PCM_Plugins
+ * \brief PCM Direct Stream Mixing (dmix) Plugin Interface
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \date 2003
+ */
+/*
+ * PCM - Direct Stream Mixing
+ * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/shm.h>
+#include <sys/sem.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/mman.h>
+#include "pcm_direct.h"
+
+/*
+ *
+ */
+
+
+/*
+ * FIXME:
+ * add possibility to use futexes here
+ */
+
+int snd_pcm_direct_semaphore_create_or_connect(snd_pcm_direct_t *dmix)
+{
+ dmix->semid = semget(dmix->ipc_key, DIRECT_IPC_SEMS, IPC_CREAT | 0666);
+ if (dmix->semid < 0)
+ return -errno;
+ return 0;
+}
+
+int snd_pcm_direct_semaphore_discard(snd_pcm_direct_t *dmix)
+{
+ if (dmix->semid < 0)
+ return -EINVAL;
+ if (semctl(dmix->semid, 0, IPC_RMID, NULL) < 0)
+ return -errno;
+ dmix->semid = -1;
+ return 0;
+}
+
+int snd_pcm_direct_semaphore_down(snd_pcm_direct_t *dmix, int sem_num)
+{
+ struct sembuf op[2] = { { 0, 0, SEM_UNDO }, { 0, 1, SEM_UNDO | IPC_NOWAIT } };
+ assert(dmix->semid >= 0);
+ op[0].sem_num = sem_num;
+ op[1].sem_num = sem_num;
+ if (semop(dmix->semid, op, 2) < 0)
+ return -errno;
+ return 0;
+}
+
+int snd_pcm_direct_semaphore_up(snd_pcm_direct_t *dmix, int sem_num)
+{
+ struct sembuf op = { 0, -1, SEM_UNDO | IPC_NOWAIT };
+ assert(dmix->semid >= 0);
+ op.sem_num = sem_num;
+ if (semop(dmix->semid, &op, 1) < 0)
+ return -errno;
+ return 0;
+}
+
+/*
+ * global shared memory area
+ */
+
+int snd_pcm_direct_shm_create_or_connect(snd_pcm_direct_t *dmix)
+{
+ static int snd_pcm_direct_shm_discard(snd_pcm_direct_t *dmix);
+ struct shmid_ds buf;
+ int ret = 0;
+
+ dmix->shmid = shmget(dmix->ipc_key, sizeof(snd_pcm_direct_share_t), IPC_CREAT | 0666);
+ if (dmix->shmid < 0)
+ return -errno;
+ dmix->shmptr = shmat(dmix->shmid, 0, 0);
+ if (dmix->shmptr == (void *) -1) {
+ snd_pcm_direct_shm_discard(dmix);
+ return -errno;
+ }
+ mlock(dmix->shmptr, sizeof(snd_pcm_direct_share_t));
+ if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) {
+ snd_pcm_direct_shm_discard(dmix);
+ return -errno;
+ }
+ if (buf.shm_nattch == 1) { /* we're the first user, clear the segment */
+ memset(dmix->shmptr, 0, sizeof(snd_pcm_direct_share_t));
+ ret = 1;
+ }
+ return ret;
+}
+
+int snd_pcm_direct_shm_discard(snd_pcm_direct_t *dmix)
+{
+ struct shmid_ds buf;
+ int ret = 0;
+
+ if (dmix->shmid < 0)
+ return -EINVAL;
+ if (dmix->shmptr != (void *) -1 && shmdt(dmix->shmptr) < 0)
+ return -errno;
+ dmix->shmptr = (void *) -1;
+ if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0)
+ return -errno;
+ if (buf.shm_nattch == 0) { /* we're the last user, destroy the segment */
+ if (shmctl(dmix->shmid, IPC_RMID, NULL) < 0)
+ return -errno;
+ ret = 1;
+ }
+ dmix->shmid = -1;
+ return ret;
+}
+
+/*
+ * server side
+ */
+
+static int get_tmp_name(char *filename, size_t size)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ snprintf(filename, size, "/tmp/alsa-dmix-%i-%li-%li", getpid(), tv.tv_sec, tv.tv_usec);
+ filename[size-1] = '\0';
+ return 0;
+}
+
+static int make_local_socket(const char *filename, int server)
+{
+ size_t l = strlen(filename);
+ size_t size = offsetof(struct sockaddr_un, sun_path) + l;
+ struct sockaddr_un *addr = alloca(size);
+ int sock;
+
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0) {
+ int result = -errno;
+ SYSERR("socket failed");
+ return result;
+ }
+
+ if (server)
+ unlink(filename);
+ addr->sun_family = AF_LOCAL;
+ memcpy(addr->sun_path, filename, l);
+
+ if (server) {
+ if (bind(sock, (struct sockaddr *) addr, size) < 0) {
+ int result = -errno;
+ SYSERR("bind failed");
+ return result;
+ }
+ } else {
+ if (connect(sock, (struct sockaddr *) addr, size) < 0) {
+ SYSERR("connect failed");
+ return -errno;
+ }
+ }
+ return sock;
+}
+
+#if 0
+#define server_printf(fmt, args...) printf(fmt, ##args)
+#else
+#define server_printf(fmt, args...) /* nothing */
+#endif
+
+static void server_job(snd_pcm_direct_t *dmix)
+{
+ int ret, sck, i;
+ int max = 128, current = 0;
+ struct pollfd pfds[max + 1];
+
+ /* close all files to free resources */
+ i = sysconf(_SC_OPEN_MAX);
+ while (--i >= 3) {
+ if (i != dmix->server_fd && i != dmix->hw_fd)
+ close(i);
+ }
+
+ /* detach from parent */
+ setsid();
+
+ pfds[0].fd = dmix->server_fd;
+ pfds[0].events = POLLIN | POLLERR | POLLHUP;
+
+ server_printf("DIRECT SERVER STARTED\n");
+ while (1) {
+ ret = poll(pfds, current + 1, 500);
+ server_printf("DIRECT SERVER: poll ret = %i, revents[0] = 0x%x\n", ret, pfds[0].revents);
+ if (ret < 0) /* some error */
+ break;
+ if (ret == 0 || pfds[0].revents & (POLLERR | POLLHUP)) { /* timeout or error? */
+ struct shmid_ds buf;
+ snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
+ if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) {
+ snd_pcm_direct_shm_discard(dmix);
+ continue;
+ }
+ server_printf("DIRECT SERVER: nattch = %i\n", (int)buf.shm_nattch);
+ if (buf.shm_nattch == 1) /* server is the last user, exit */
+ break;
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
+ continue;
+ }
+ if (pfds[0].revents & POLLIN) {
+ ret--;
+ sck = accept(dmix->server_fd, 0, 0);
+ if (sck >= 0) {
+ server_printf("DIRECT SERVER: new connection %i\n", sck);
+ if (current == max) {
+ close(sck);
+ } else {
+ unsigned char buf = 'A';
+ pfds[current+1].fd = sck;
+ pfds[current+1].events = POLLIN | POLLERR | POLLHUP;
+ snd_send_fd(sck, &buf, 1, dmix->hw_fd);
+ server_printf("DIRECT SERVER: fd sent ok\n");
+ current++;
+ }
+ }
+ }
+ for (i = 0; i < current && ret > 0; i++) {
+ struct pollfd *pfd = &pfds[i+1];
+ unsigned char cmd;
+ server_printf("client %i revents = 0x%x\n", pfd->fd, pfd->revents);
+ if (pfd->revents & (POLLERR | POLLHUP)) {
+ ret--;
+ close(pfd->fd);
+ pfd->fd = -1;
+ continue;
+ }
+ if (!(pfd->revents & POLLIN))
+ continue;
+ ret--;
+ if (read(pfd->fd, &cmd, 1) == 1)
+ cmd = 0 /*process command */;
+ }
+ for (i = 0; i < current; i++) {
+ if (pfds[i+1].fd < 0) {
+ if (i + 1 != max)
+ memcpy(&pfds[i+1], &pfds[i+2], sizeof(struct pollfd) * (max - i - 1));
+ current--;
+ }
+ }
+ }
+ close(dmix->server_fd);
+ close(dmix->hw_fd);
+ snd_pcm_direct_shm_discard(dmix);
+ snd_pcm_direct_semaphore_discard(dmix);
+ server_printf("DIRECT SERVER EXIT\n");
+ exit(EXIT_SUCCESS);
+}
+
+int snd_pcm_direct_server_create(snd_pcm_direct_t *dmix)
+{
+ int ret;
+
+ dmix->server_fd = -1;
+
+ ret = get_tmp_name(dmix->shmptr->socket_name, sizeof(dmix->shmptr->socket_name));
+ if (ret < 0)
+ return ret;
+
+ ret = make_local_socket(dmix->shmptr->socket_name, 1);
+ if (ret < 0)
+ return ret;
+ dmix->server_fd = ret;
+
+ ret = listen(dmix->server_fd, 4);
+ if (ret < 0) {
+ close(dmix->server_fd);
+ return ret;
+ }
+
+ ret = fork();
+ if (ret < 0) {
+ close(dmix->server_fd);
+ return ret;
+ } else if (ret == 0) {
+ server_job(dmix);
+ }
+ dmix->server_pid = ret;
+ dmix->server = 1;
+ return 0;
+}
+
+int snd_pcm_direct_server_discard(snd_pcm_direct_t *dmix)
+{
+ if (dmix->server) {
+ //kill(dmix->server_pid, SIGTERM);
+ //waitpid(dmix->server_pid, NULL, 0);
+ dmix->server_pid = (pid_t)-1;
+ }
+ if (dmix->server_fd > 0) {
+ close(dmix->server_fd);
+ dmix->server_fd = -1;
+ }
+ dmix->server = 0;
+ return 0;
+}
+
+/*
+ * client side
+ */
+
+int snd_pcm_direct_client_connect(snd_pcm_direct_t *dmix)
+{
+ int ret;
+ unsigned char buf;
+
+ ret = make_local_socket(dmix->shmptr->socket_name, 0);
+ if (ret < 0)
+ return ret;
+ dmix->comm_fd = ret;
+
+ ret = snd_receive_fd(dmix->comm_fd, &buf, 1, &dmix->hw_fd);
+ if (ret < 1 || buf != 'A') {
+ close(dmix->comm_fd);
+ dmix->comm_fd = -1;
+ return ret;
+ }
+
+ dmix->client = 1;
+ return 0;
+}
+
+int snd_pcm_direct_client_discard(snd_pcm_direct_t *dmix)
+{
+ if (dmix->client) {
+ close(dmix->comm_fd);
+ dmix->comm_fd = -1;
+ }
+ return 0;
+}
+
+/*
+ * this function initializes hardware and starts playback operation with
+ * no stop threshold (it operates all time without xrun checking)
+ * also, the driver silences the unused ring buffer areas for us
+ */
+int snd_pcm_direct_initialize_slave(snd_pcm_direct_t *dmix, snd_pcm_t *spcm, struct slave_params *params)
+{
+ snd_pcm_hw_params_t *hw_params;
+ snd_pcm_sw_params_t *sw_params;
+ int ret, buffer_is_not_initialized;
+ snd_pcm_uframes_t boundary;
+ struct pollfd fd;
+ int loops = 10;
+
+ hw_params = &dmix->shmptr->hw_params;
+ sw_params = &dmix->shmptr->sw_params;
+
+ __again:
+ if (loops-- <= 0) {
+ SNDERR("unable to find a valid configuration for slave");
+ return -EINVAL;
+ }
+ ret = snd_pcm_hw_params_any(spcm, hw_params);
+ if (ret < 0) {
+ SNDERR("snd_pcm_hw_params_any failed");
+ return ret;
+ }
+ ret = snd_pcm_hw_params_set_access(spcm, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED);
+ if (ret < 0) {
+ ret = snd_pcm_hw_params_set_access(spcm, hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
+ if (ret < 0) {
+ SNDERR("slave plugin does not support mmap interleaved or mmap noninterleaved access");
+ return ret;
+ }
+ }
+ ret = snd_pcm_hw_params_set_format(spcm, hw_params, params->format);
+ if (ret < 0) {
+ snd_pcm_format_t format;
+ switch (params->format) {
+ case SND_PCM_FORMAT_S32: format = SND_PCM_FORMAT_S16; break;
+ case SND_PCM_FORMAT_S16: format = SND_PCM_FORMAT_S32; break;
+ default:
+ SNDERR("invalid format");
+ return -EINVAL;
+ }
+ ret = snd_pcm_hw_params_set_format(spcm, hw_params, format);
+ if (ret < 0) {
+ SNDERR("requested or auto-format is not available");
+ return ret;
+ }
+ params->format = format;
+ }
+ ret = snd_pcm_hw_params_set_channels(spcm, hw_params, params->channels);
+ if (ret < 0) {
+ unsigned int min, max;
+ ret = INTERNAL(snd_pcm_hw_params_get_channels_min)(hw_params, &min);
+ if (ret < 0) {
+ SNDERR("cannot obtain minimal count of channels");
+ return ret;
+ }
+ ret = INTERNAL(snd_pcm_hw_params_get_channels_min)(hw_params, &max);
+ if (ret < 0) {
+ SNDERR("cannot obtain maximal count of channels");
+ return ret;
+ }
+ if (min == max) {
+ ret = snd_pcm_hw_params_set_channels(spcm, hw_params, min);
+ if (ret >= 0)
+ params->channels = min;
+ }
+ if (ret < 0) {
+ SNDERR("requested count of channels is not available");
+ return ret;
+ }
+ }
+ ret = INTERNAL(snd_pcm_hw_params_set_rate_near)(spcm, hw_params, ¶ms->rate, 0);
+ if (ret < 0) {
+ SNDERR("requested rate is not available");
+ return ret;
+ }
+
+ buffer_is_not_initialized = 0;
+ if (params->buffer_time > 0) {
+ ret = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(spcm, hw_params, ¶ms->buffer_time, 0);
+ if (ret < 0) {
+ SNDERR("unable to set buffer time");
+ return ret;
+ }
+ } else if (params->buffer_size > 0) {
+ ret = INTERNAL(snd_pcm_hw_params_set_buffer_size_near)(spcm, hw_params, ¶ms->buffer_size);
+ if (ret < 0) {
+ SNDERR("unable to set buffer size");
+ return ret;
+ }
+ } else {
+ buffer_is_not_initialized = 1;
+ }
+
+ if (params->period_time > 0) {
+ ret = INTERNAL(snd_pcm_hw_params_set_period_time_near)(spcm, hw_params, ¶ms->period_time, 0);
+ if (ret < 0) {
+ SNDERR("unable to set period_time");
+ return ret;
+ }
+ } else if (params->period_size > 0) {
+ ret = INTERNAL(snd_pcm_hw_params_set_period_size_near)(spcm, hw_params, ¶ms->period_size, 0);
+ if (ret < 0) {
+ SNDERR("unable to set period_size");
+ return ret;
+ }
+ }
+
+ if (buffer_is_not_initialized && params->periods > 0) {
+ int periods = params->periods;
+ ret = INTERNAL(snd_pcm_hw_params_set_periods_near)(spcm, hw_params, ¶ms->periods, 0);
+ if (ret < 0) {
+ SNDERR("unable to set requested periods");
+ return ret;
+ }
+ if (params->periods == 1) {
+ params->periods = periods;
+ if (params->period_time > 0) {
+ params->period_time /= 2;
+ goto __again;
+ } else if (params->period_size > 0) {
+ params->period_size /= 2;
+ goto __again;
+ }
+ SNDERR("unable to use stream with periods == 1");
+ return ret;
+ }
+ }
+
+ ret = snd_pcm_hw_params(spcm, hw_params);
+ if (ret < 0) {
+ SNDERR("unable to install hw params");
+ return ret;
+ }
+
+ ret = snd_pcm_sw_params_current(spcm, sw_params);
+ if (ret < 0) {
+ SNDERR("unable to get current sw_params");
+ return ret;
+ }
+
+ ret = snd_pcm_sw_params_get_boundary(sw_params, &boundary);
+ if (ret < 0) {
+ SNDERR("unable to get boundary");
+ return ret;
+ }
+ ret = snd_pcm_sw_params_set_stop_threshold(spcm, sw_params, boundary);
+ if (ret < 0) {
+ SNDERR("unable to set stop threshold");
+ return ret;
+ }
+ ret = snd_pcm_sw_params_set_silence_threshold(spcm, sw_params, 0);
+ if (ret < 0) {
+ SNDERR("unable to set silence threshold");
+ return ret;
+ }
+ ret = snd_pcm_sw_params_set_silence_size(spcm, sw_params, boundary);
+ if (ret < 0) {
+ SNDERR("unable to set silence threshold (please upgrade to 0.9.0rc8+ driver)");
+ return ret;
+ }
+
+ ret = snd_pcm_sw_params(spcm, sw_params);
+ if (ret < 0) {
+ SNDERR("unable to install sw params (please upgrade to 0.9.0rc8+ driver)");
+ return ret;
+ }
+
+ ret = snd_pcm_start(spcm);
+ if (ret < 0) {
+ SNDERR("unable to start PCM stream");
+ return ret;
+ }
+
+ if (snd_pcm_poll_descriptors_count(spcm) != 1) {
+ SNDERR("unable to use hardware pcm with fd more than one!!!");
+ return ret;
+ }
+ snd_pcm_poll_descriptors(spcm, &fd, 1);
+ dmix->hw_fd = fd.fd;
+
+ dmix->shmptr->s.boundary = spcm->boundary;
+ dmix->shmptr->s.buffer_size = spcm->buffer_size;
+ dmix->shmptr->s.sample_bits = spcm->sample_bits;
+ dmix->shmptr->s.channels = spcm->channels;
+ dmix->shmptr->s.rate = spcm->rate;
+ dmix->shmptr->s.format = spcm->format;
+ dmix->shmptr->s.boundary = spcm->boundary;
+
+ spcm->donot_close = 1;
+ return 0;
+}
+
+/*
+ * the trick is used here; we cannot use effectively the hardware handle because
+ * we cannot drive multiple accesses to appl_ptr; so we use slave timer of given
+ * PCM hardware handle; it's not this easy and cheap?
+ */
+int snd_pcm_direct_initialize_poll_fd(snd_pcm_direct_t *dmix)
+{
+ int ret;
+ snd_pcm_info_t *info;
+ snd_timer_params_t *params;
+ char name[128];
+ struct pollfd fd;
+
+ snd_pcm_info_alloca(&info);
+ snd_timer_params_alloca(¶ms);
+ ret = snd_pcm_info(dmix->spcm, info);
+ if (ret < 0) {
+ SNDERR("unable to info for slave pcm");
+ return ret;
+ }
+ sprintf(name, "hw:CLASS=%i,SCLASS=0,CARD=%i,DEV=%i,SUBDEV=%i",
+ (int)SND_TIMER_CLASS_PCM,
+ snd_pcm_info_get_card(info),
+ snd_pcm_info_get_device(info),
+ snd_pcm_info_get_subdevice(info) * 2); /* it's a bit trick to distict playback and capture */
+ ret = snd_timer_open(&dmix->timer, name, SND_TIMER_OPEN_NONBLOCK);
+ if (ret < 0) {
+ SNDERR("unable to open timer '%s'", name);
+ return ret;
+ }
+ snd_timer_params_set_auto_start(params, 1);
+ snd_timer_params_set_ticks(params, 1);
+ ret = snd_timer_params(dmix->timer, params);
+ if (ret < 0) {
+ SNDERR("unable to set timer parameters", name);
+ return ret;
+ }
+ if (snd_timer_poll_descriptors_count(dmix->timer) != 1) {
+ SNDERR("unable to use timer with fd more than one!!!", name);
+ return ret;
+ }
+ snd_timer_poll_descriptors(dmix->timer, &fd, 1);
+ dmix->poll_fd = fd.fd;
+ return 0;
+}
+
+/*
+ * ring buffer operation
+ */
+int snd_pcm_direct_check_interleave(snd_pcm_direct_t *dmix, snd_pcm_t *pcm)
+{
+ unsigned int chn, channels;
+ int interleaved = 1;
+ const snd_pcm_channel_area_t *dst_areas;
+ const snd_pcm_channel_area_t *src_areas;
+
+ channels = dmix->u.dmix.channels;
+ dst_areas = snd_pcm_mmap_areas(dmix->spcm);
+ src_areas = snd_pcm_mmap_areas(pcm);
+ for (chn = 1; chn < channels; chn++) {
+ if (dst_areas[chn-1].addr != dst_areas[chn].addr) {
+ interleaved = 0;
+ break;
+ }
+ if (src_areas[chn-1].addr != src_areas[chn].addr) {
+ interleaved = 0;
+ break;
+ }
+ }
+ for (chn = 0; chn < channels; chn++) {
+ if (dmix->u.dmix.bindings && dmix->u.dmix.bindings[chn] != chn) {
+ interleaved = 0;
+ break;
+ }
+ if (dst_areas[chn].first != sizeof(signed short) * chn * 8 ||
+ dst_areas[chn].step != channels * sizeof(signed short) * 8) {
+ interleaved = 0;
+ break;
+ }
+ if (src_areas[chn].first != sizeof(signed short) * chn * 8 ||
+ src_areas[chn].step != channels * sizeof(signed short) * 8) {
+ interleaved = 0;
+ break;
+ }
+ }
+ return dmix->interleaved = interleaved;
+}
--- /dev/null
+/**
+ * \file pcm/pcm_dmix.c
+ * \ingroup PCM_Plugins
+ * \brief PCM Direct Stream Mixing (dmix) Plugin Interface
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \date 2003
+ */
+/*
+ * PCM - Direct Stream Mixing
+ * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "pcm_local.h"
+
+#define DIRECT_IPC_SEMS 1
+#define DIRECT_IPC_SEM_CLIENT 0
+
+typedef void (mix_areas1_t)(unsigned int size,
+ volatile signed short *dst, signed short *src,
+ volatile signed int *sum, unsigned int dst_step,
+ unsigned int src_step, unsigned int sum_step);
+
+typedef void (mix_areas2_t)(unsigned int size,
+ volatile signed int *dst, signed int *src,
+ volatile signed int *sum, unsigned int dst_step,
+ unsigned int src_step, unsigned int sum_step);
+
+struct slave_params {
+ snd_pcm_format_t format;
+ int rate;
+ int channels;
+ int period_time;
+ int buffer_time;
+ snd_pcm_sframes_t period_size;
+ snd_pcm_sframes_t buffer_size;
+ int periods;
+};
+
+typedef struct {
+ char socket_name[256]; /* name of communication socket */
+ snd_pcm_type_t type; /* PCM type (currently only hw) */
+ snd_pcm_hw_params_t hw_params;
+ snd_pcm_sw_params_t sw_params;
+ struct {
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t boundary;
+ snd_pcm_uframes_t channels;
+ unsigned int sample_bits;
+ unsigned int rate;
+ snd_pcm_format_t format;
+ } s;
+} snd_pcm_direct_share_t;
+
+typedef struct {
+ key_t ipc_key; /* IPC key for semaphore and memory */
+ int semid; /* IPC global semaphore identification */
+ int shmid; /* IPC global shared memory identification */
+ snd_pcm_direct_share_t *shmptr; /* pointer to shared memory area */
+ snd_pcm_t *spcm; /* slave PCM handle */
+ snd_pcm_uframes_t appl_ptr;
+ snd_pcm_uframes_t hw_ptr;
+ snd_pcm_uframes_t avail_max;
+ snd_pcm_uframes_t slave_appl_ptr;
+ snd_pcm_uframes_t slave_hw_ptr;
+ snd_pcm_state_t state;
+ snd_htimestamp_t trigger_tstamp;
+ int server, client;
+ int comm_fd; /* communication file descriptor (socket) */
+ int hw_fd; /* hardware file descriptor */
+ int poll_fd;
+ int server_fd;
+ pid_t server_pid;
+ snd_timer_t *timer; /* timer used as poll_fd */
+ int interleaved; /* we have interleaved buffer */
+ union {
+ struct {
+ int shmid_sum; /* IPC global sum ring buffer memory identification */
+ signed int *sum_buffer; /* shared sum buffer */
+ mix_areas1_t *mix_areas1;
+ mix_areas2_t *mix_areas2;
+ unsigned int channels; /* client's channels */
+ unsigned int *bindings;
+ } dmix;
+ struct {
+ } dsnoop;
+ struct {
+ } dshare;
+ } u;
+} snd_pcm_direct_t;
+
+int snd_pcm_direct_semaphore_create_or_connect(snd_pcm_direct_t *dmix);
+int snd_pcm_direct_semaphore_discard(snd_pcm_direct_t *dmix);
+int snd_pcm_direct_semaphore_down(snd_pcm_direct_t *dmix, int sem_num);
+int snd_pcm_direct_semaphore_up(snd_pcm_direct_t *dmix, int sem_num);
+int snd_pcm_direct_shm_create_or_connect(snd_pcm_direct_t *dmix);
+int snd_pcm_direct_shm_discard(snd_pcm_direct_t *dmix);
+int snd_pcm_direct_server_create(snd_pcm_direct_t *dmix);
+int snd_pcm_direct_server_discard(snd_pcm_direct_t *dmix);
+int snd_pcm_direct_client_connect(snd_pcm_direct_t *dmix);
+int snd_pcm_direct_client_discard(snd_pcm_direct_t *dmix);
+int snd_pcm_direct_initialize_slave(snd_pcm_direct_t *dmix, snd_pcm_t *spcm, struct slave_params *params);
+int snd_pcm_direct_initialize_poll_fd(snd_pcm_direct_t *dmix);
+int snd_pcm_direct_check_interleave(snd_pcm_direct_t *dmix, snd_pcm_t *pcm);
+
+int snd_timer_async(snd_timer_t *timer, int sig, pid_t pid);
+struct timespec snd_pcm_hw_fast_tstamp(snd_pcm_t *pcm);
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
-#include "pcm_local.h"
-#include "../control/control_local.h"
+#include "pcm_direct.h"
#ifndef PIC
/* entry for static linking */
/*
*
*/
-
-
-/*
- * FIXME:
- * add possibility to use futexes here
- */
-#define DMIX_IPC_SEMS 1
-#define DMIX_IPC_SEM_CLIENT 0
-
-/*
- *
- */
-
-int snd_timer_async(snd_timer_t *timer, int sig, pid_t pid);
-struct timespec snd_pcm_hw_fast_tstamp(snd_pcm_t *pcm);
-
-typedef void (mix_areas1_t)(unsigned int size,
- volatile signed short *dst, signed short *src,
- volatile signed int *sum, unsigned int dst_step,
- unsigned int src_step, unsigned int sum_step);
-
-typedef void (mix_areas2_t)(unsigned int size,
- volatile signed int *dst, signed int *src,
- volatile signed int *sum, unsigned int dst_step,
- unsigned int src_step, unsigned int sum_step);
-
-/*
- *
- */
-
-struct slave_params {
- snd_pcm_format_t format;
- int rate;
- int channels;
- int period_time;
- int buffer_time;
- snd_pcm_sframes_t period_size;
- snd_pcm_sframes_t buffer_size;
- int periods;
-};
-
-typedef struct {
- char socket_name[256]; /* name of communication socket */
- snd_pcm_type_t type; /* PCM type (currently only hw) */
- snd_pcm_hw_params_t hw_params;
- snd_pcm_sw_params_t sw_params;
- struct {
- snd_pcm_uframes_t buffer_size;
- snd_pcm_uframes_t boundary;
- snd_pcm_uframes_t channels;
- unsigned int sample_bits;
- unsigned int rate;
- snd_pcm_format_t format;
- } s;
-} snd_pcm_dmix_share_t;
-
-typedef struct {
- key_t ipc_key; /* IPC key for semaphore and memory */
- int semid; /* IPC global semaphore identification */
- int shmid; /* IPC global shared memory identification */
- int shmid_sum; /* IPC global sum ring buffer memory identification */
- snd_pcm_dmix_share_t *shmptr; /* pointer to shared memory area */
- signed int *sum_buffer; /* shared sum buffer */
- snd_pcm_t *spcm; /* slave PCM handle */
- snd_pcm_uframes_t appl_ptr;
- snd_pcm_uframes_t hw_ptr;
- snd_pcm_uframes_t avail_max;
- snd_pcm_uframes_t slave_appl_ptr;
- snd_pcm_uframes_t slave_hw_ptr;
- snd_pcm_state_t state;
- snd_htimestamp_t trigger_tstamp;
- int server, client;
- int comm_fd; /* communication file descriptor (socket) */
- int hw_fd; /* hardware file descriptor */
- int poll_fd;
- int server_fd;
- pid_t server_pid;
- snd_timer_t *timer; /* timer used as poll_fd */
- int interleaved; /* we have interleaved buffer */
- mix_areas1_t *mix_areas1;
- mix_areas2_t *mix_areas2;
- unsigned int channels; /* client's channels */
- unsigned int *bindings;
-} snd_pcm_dmix_t;
-
-/*
- * global semaphore and shared memory area
- */
-
-#if 0
-struct sembuf {
- unsigned short sem_num;
- short sem_op;
- short sem_flg;
-};
-#endif
-
-static int semaphore_create_or_connect(snd_pcm_dmix_t *dmix)
-{
- dmix->semid = semget(dmix->ipc_key, DMIX_IPC_SEMS, IPC_CREAT | 0666);
- if (dmix->semid < 0)
- return -errno;
- return 0;
-}
-
-static int semaphore_discard(snd_pcm_dmix_t *dmix)
-{
- if (dmix->semid < 0)
- return -EINVAL;
- if (semctl(dmix->semid, 0, IPC_RMID, NULL) < 0)
- return -errno;
- dmix->semid = -1;
- return 0;
-}
-
-static int semaphore_down(snd_pcm_dmix_t *dmix, int sem_num)
-{
- struct sembuf op[2] = { { 0, 0, SEM_UNDO }, { 0, 1, SEM_UNDO | IPC_NOWAIT } };
- assert(dmix->semid >= 0);
- op[0].sem_num = sem_num;
- op[1].sem_num = sem_num;
- if (semop(dmix->semid, op, 2) < 0)
- return -errno;
- return 0;
-}
-
-static int semaphore_up(snd_pcm_dmix_t *dmix, int sem_num)
-{
- struct sembuf op = { 0, -1, SEM_UNDO | IPC_NOWAIT };
- assert(dmix->semid >= 0);
- op.sem_num = sem_num;
- if (semop(dmix->semid, &op, 1) < 0)
- return -errno;
- return 0;
-}
-
-/*
- * global shared memory area
- */
-
-static int shm_create_or_connect(snd_pcm_dmix_t *dmix)
-{
- static int shm_discard(snd_pcm_dmix_t *dmix);
- struct shmid_ds buf;
- int ret = 0;
-
- dmix->shmid = shmget(dmix->ipc_key, sizeof(snd_pcm_dmix_share_t), IPC_CREAT | 0666);
- if (dmix->shmid < 0)
- return -errno;
- dmix->shmptr = shmat(dmix->shmid, 0, 0);
- if (dmix->shmptr == (void *) -1) {
- shm_discard(dmix);
- return -errno;
- }
- mlock(dmix->shmptr, sizeof(snd_pcm_dmix_share_t));
- if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) {
- shm_discard(dmix);
- return -errno;
- }
- if (buf.shm_nattch == 1) { /* we're the first user, clear the segment */
- memset(dmix->shmptr, 0, sizeof(snd_pcm_dmix_share_t));
- ret = 1;
- }
- return ret;
-}
-
-static int shm_discard(snd_pcm_dmix_t *dmix)
-{
- struct shmid_ds buf;
- int ret = 0;
-
- if (dmix->shmid < 0)
- return -EINVAL;
- if (dmix->shmptr != (void *) -1 && shmdt(dmix->shmptr) < 0)
- return -errno;
- dmix->shmptr = (void *) -1;
- if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0)
- return -errno;
- if (buf.shm_nattch == 0) { /* we're the last user, destroy the segment */
- if (shmctl(dmix->shmid, IPC_RMID, NULL) < 0)
- return -errno;
- ret = 1;
- }
- dmix->shmid = -1;
- return ret;
-}
/*
* sum ring buffer shared memory area
*/
-
-static int shm_sum_create_or_connect(snd_pcm_dmix_t *dmix)
+static int shm_sum_create_or_connect(snd_pcm_direct_t *dmix)
{
- static int shm_sum_discard(snd_pcm_dmix_t *dmix);
+ static int shm_sum_discard(snd_pcm_direct_t *dmix);
size_t size;
size = dmix->shmptr->s.channels *
dmix->shmptr->s.buffer_size *
sizeof(signed int);
- dmix->shmid_sum = shmget(dmix->ipc_key + 1, size, IPC_CREAT | 0666);
- if (dmix->shmid_sum < 0)
+ dmix->u.dmix.shmid_sum = shmget(dmix->ipc_key + 1, size, IPC_CREAT | 0666);
+ if (dmix->u.dmix.shmid_sum < 0)
return -errno;
- dmix->sum_buffer = shmat(dmix->shmid_sum, 0, 0);
- if (dmix->sum_buffer == (void *) -1) {
+ dmix->u.dmix.sum_buffer = shmat(dmix->u.dmix.shmid_sum, 0, 0);
+ if (dmix->u.dmix.sum_buffer == (void *) -1) {
shm_sum_discard(dmix);
return -errno;
}
- mlock(dmix->sum_buffer, size);
+ mlock(dmix->u.dmix.sum_buffer, size);
return 0;
}
-static int shm_sum_discard(snd_pcm_dmix_t *dmix)
+static int shm_sum_discard(snd_pcm_direct_t *dmix)
{
struct shmid_ds buf;
int ret = 0;
- if (dmix->shmid_sum < 0)
+ if (dmix->u.dmix.shmid_sum < 0)
return -EINVAL;
- if (dmix->sum_buffer != (void *) -1 && shmdt(dmix->sum_buffer) < 0)
+ if (dmix->u.dmix.sum_buffer != (void *) -1 && shmdt(dmix->u.dmix.sum_buffer) < 0)
return -errno;
- dmix->sum_buffer = (void *) -1;
- if (shmctl(dmix->shmid_sum, IPC_STAT, &buf) < 0)
+ dmix->u.dmix.sum_buffer = (void *) -1;
+ if (shmctl(dmix->u.dmix.shmid_sum, IPC_STAT, &buf) < 0)
return -errno;
if (buf.shm_nattch == 0) { /* we're the last user, destroy the segment */
- if (shmctl(dmix->shmid_sum, IPC_RMID, NULL) < 0)
+ if (shmctl(dmix->u.dmix.shmid_sum, IPC_RMID, NULL) < 0)
return -errno;
ret = 1;
}
- dmix->shmid_sum = -1;
+ dmix->u.dmix.shmid_sum = -1;
return ret;
}
-/*
- * server side
- */
-
-static int get_tmp_name(char *filename, size_t size)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
- snprintf(filename, size, "/tmp/alsa-dmix-%i-%li-%li", getpid(), tv.tv_sec, tv.tv_usec);
- filename[size-1] = '\0';
- return 0;
-}
-
-static int make_local_socket(const char *filename, int server)
-{
- size_t l = strlen(filename);
- size_t size = offsetof(struct sockaddr_un, sun_path) + l;
- struct sockaddr_un *addr = alloca(size);
- int sock;
-
- sock = socket(PF_LOCAL, SOCK_STREAM, 0);
- if (sock < 0) {
- int result = -errno;
- SYSERR("socket failed");
- return result;
- }
-
- if (server)
- unlink(filename);
- addr->sun_family = AF_LOCAL;
- memcpy(addr->sun_path, filename, l);
-
- if (server) {
- if (bind(sock, (struct sockaddr *) addr, size) < 0) {
- int result = -errno;
- SYSERR("bind failed");
- return result;
- }
- } else {
- if (connect(sock, (struct sockaddr *) addr, size) < 0) {
- SYSERR("connect failed");
- return -errno;
- }
- }
- return sock;
-}
-
-#if 0
-#define server_printf(fmt, args...) printf(fmt, ##args)
-#else
-#define server_printf(fmt, args...) /* nothing */
-#endif
-
-static void server_job(snd_pcm_dmix_t *dmix)
-{
- int ret, sck, i;
- int max = 128, current = 0;
- struct pollfd pfds[max + 1];
-
- /* close all files to free resources */
- i = sysconf(_SC_OPEN_MAX);
- while (--i >= 3) {
- if (i != dmix->server_fd && i != dmix->hw_fd)
- close(i);
- }
-
- /* detach from parent */
- setsid();
-
- pfds[0].fd = dmix->server_fd;
- pfds[0].events = POLLIN | POLLERR | POLLHUP;
-
- server_printf("DMIX SERVER STARTED\n");
- while (1) {
- ret = poll(pfds, current + 1, 500);
- server_printf("DMIX SERVER: poll ret = %i, revents[0] = 0x%x\n", ret, pfds[0].revents);
- if (ret < 0) /* some error */
- break;
- if (ret == 0 || pfds[0].revents & (POLLERR | POLLHUP)) { /* timeout or error? */
- struct shmid_ds buf;
- semaphore_down(dmix, DMIX_IPC_SEM_CLIENT);
- if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) {
- shm_discard(dmix);
- continue;
- }
- server_printf("DMIX SERVER: nattch = %i\n", (int)buf.shm_nattch);
- if (buf.shm_nattch == 1) /* server is the last user, exit */
- break;
- semaphore_up(dmix, DMIX_IPC_SEM_CLIENT);
- continue;
- }
- if (pfds[0].revents & POLLIN) {
- ret--;
- sck = accept(dmix->server_fd, 0, 0);
- if (sck >= 0) {
- server_printf("DMIX SERVER: new connection %i\n", sck);
- if (current == max) {
- close(sck);
- } else {
- unsigned char buf = 'A';
- pfds[current+1].fd = sck;
- pfds[current+1].events = POLLIN | POLLERR | POLLHUP;
- snd_send_fd(sck, &buf, 1, dmix->hw_fd);
- server_printf("DMIX SERVER: fd sent ok\n");
- current++;
- }
- }
- }
- for (i = 0; i < current && ret > 0; i++) {
- struct pollfd *pfd = &pfds[i+1];
- unsigned char cmd;
- server_printf("client %i revents = 0x%x\n", pfd->fd, pfd->revents);
- if (pfd->revents & (POLLERR | POLLHUP)) {
- ret--;
- close(pfd->fd);
- pfd->fd = -1;
- continue;
- }
- if (!(pfd->revents & POLLIN))
- continue;
- ret--;
- if (read(pfd->fd, &cmd, 1) == 1)
- cmd = 0 /*process command */;
- }
- for (i = 0; i < current; i++) {
- if (pfds[i+1].fd < 0) {
- if (i + 1 != max)
- memcpy(&pfds[i+1], &pfds[i+2], sizeof(struct pollfd) * (max - i - 1));
- current--;
- }
- }
- }
- close(dmix->server_fd);
- close(dmix->hw_fd);
- shm_discard(dmix);
- semaphore_discard(dmix);
- server_printf("DMIX SERVER EXIT\n");
- exit(EXIT_SUCCESS);
-}
-
-static int server_create(snd_pcm_dmix_t *dmix)
-{
- int ret;
-
- dmix->server_fd = -1;
-
- ret = get_tmp_name(dmix->shmptr->socket_name, sizeof(dmix->shmptr->socket_name));
- if (ret < 0)
- return ret;
-
- ret = make_local_socket(dmix->shmptr->socket_name, 1);
- if (ret < 0)
- return ret;
- dmix->server_fd = ret;
-
- ret = listen(dmix->server_fd, 4);
- if (ret < 0) {
- close(dmix->server_fd);
- return ret;
- }
-
- ret = fork();
- if (ret < 0) {
- close(dmix->server_fd);
- return ret;
- } else if (ret == 0) {
- server_job(dmix);
- }
- dmix->server_pid = ret;
- dmix->server = 1;
- return 0;
-}
-
-static int server_discard(snd_pcm_dmix_t *dmix)
-{
- if (dmix->server) {
- //kill(dmix->server_pid, SIGTERM);
- //waitpid(dmix->server_pid, NULL, 0);
- dmix->server_pid = (pid_t)-1;
- }
- if (dmix->server_fd > 0) {
- close(dmix->server_fd);
- dmix->server_fd = -1;
- }
- dmix->server = 0;
- return 0;
-}
-
-/*
- * client side
- */
-
-static int client_connect(snd_pcm_dmix_t *dmix)
-{
- int ret;
- unsigned char buf;
-
- ret = make_local_socket(dmix->shmptr->socket_name, 0);
- if (ret < 0)
- return ret;
- dmix->comm_fd = ret;
-
- ret = snd_receive_fd(dmix->comm_fd, &buf, 1, &dmix->hw_fd);
- if (ret < 0 || buf != 'A') {
- close(dmix->comm_fd);
- dmix->comm_fd = -1;
- return ret;
- }
-
- dmix->client = 1;
- return 0;
-}
-
-static int client_discard(snd_pcm_dmix_t *dmix)
-{
- if (dmix->client) {
- close(dmix->comm_fd);
- dmix->comm_fd = -1;
- }
- return 0;
-}
-
-/*
- * ring buffer operation
- */
-
-static int check_interleave(snd_pcm_dmix_t *dmix, snd_pcm_t *pcm)
-{
- unsigned int chn, channels;
- int interleaved = 1;
- const snd_pcm_channel_area_t *dst_areas;
- const snd_pcm_channel_area_t *src_areas;
-
- channels = dmix->channels;
- dst_areas = snd_pcm_mmap_areas(dmix->spcm);
- src_areas = snd_pcm_mmap_areas(pcm);
- for (chn = 1; chn < channels; chn++) {
- if (dst_areas[chn-1].addr != dst_areas[chn].addr) {
- interleaved = 0;
- break;
- }
- if (src_areas[chn-1].addr != src_areas[chn].addr) {
- interleaved = 0;
- break;
- }
- }
- for (chn = 0; chn < channels; chn++) {
- if (dmix->bindings && dmix->bindings[chn] != chn) {
- interleaved = 0;
- break;
- }
- if (dst_areas[chn].first != sizeof(signed short) * chn * 8 ||
- dst_areas[chn].step != channels * sizeof(signed short) * 8) {
- interleaved = 0;
- break;
- }
- if (src_areas[chn].first != sizeof(signed short) * chn * 8 ||
- src_areas[chn].step != channels * sizeof(signed short) * 8) {
- interleaved = 0;
- break;
- }
- }
- return dmix->interleaved = interleaved;
-}
-
/*
* the main function of this plugin: mixing
* FIXME: optimize it for different architectures
#undef MIX_AREAS2
#undef LOCK_PREFIX
-static void mix_select_callbacks(snd_pcm_dmix_t *dmix)
+static void mix_select_callbacks(snd_pcm_direct_t *dmix)
{
FILE *in;
char line[255];
int smp = 0, mmx = 0;
/* safe settings for all i386 CPUs */
- dmix->mix_areas1 = mix_areas1_smp;
+ dmix->u.dmix.mix_areas1 = mix_areas1_smp;
/* try to determine, if we have a MMX capable CPU */
in = fopen("/proc/cpuinfo", "r");
if (in == NULL)
fclose(in);
// printf("MMX: %i, SMP: %i\n", mmx, smp);
if (mmx) {
- dmix->mix_areas1 = smp > 1 ? mix_areas1_smp_mmx : mix_areas1_mmx;
+ dmix->u.dmix.mix_areas1 = smp > 1 ? mix_areas1_smp_mmx : mix_areas1_mmx;
} else {
- dmix->mix_areas1 = smp > 1 ? mix_areas1_smp : mix_areas1;
+ dmix->u.dmix.mix_areas1 = smp > 1 ? mix_areas1_smp : mix_areas1;
}
- dmix->mix_areas2 = smp > 1 ? mix_areas2_smp : mix_areas2;
+ dmix->u.dmix.mix_areas2 = smp > 1 ? mix_areas2_smp : mix_areas2;
}
#endif
}
}
-static void mix_select_callbacks(snd_pcm_dmix_t *dmix)
+static void mix_select_callbacks(snd_pcm_direct_t *dmix)
{
- dmix->mix_areas1 = mix_areas1;
- dmix->mix_areas2 = mix_areas2;
+ dmix->u.dmix.mix_areas1 = mix_areas1;
+ dmix->u.dmix.mix_areas2 = mix_areas2;
}
#endif
-static void mix_areas(snd_pcm_dmix_t *dmix,
+static void mix_areas(snd_pcm_direct_t *dmix,
const snd_pcm_channel_area_t *src_areas,
const snd_pcm_channel_area_t *dst_areas,
snd_pcm_uframes_t src_ofs,
unsigned int src_step, dst_step;
unsigned int chn, dchn, channels;
- channels = dmix->channels;
+ channels = dmix->u.dmix.channels;
if (dmix->shmptr->s.format == SND_PCM_FORMAT_S16) {
signed short *src;
volatile signed short *dst;
* process all areas in one loop
* it optimizes the memory accesses for this case
*/
- dmix->mix_areas1(size * channels,
+ dmix->u.dmix.mix_areas1(size * channels,
((signed short *)dst_areas[0].addr) + (dst_ofs * channels),
((signed short *)src_areas[0].addr) + (src_ofs * channels),
- dmix->sum_buffer + (dst_ofs * channels),
+ dmix->u.dmix.sum_buffer + (dst_ofs * channels),
sizeof(signed short),
sizeof(signed short),
sizeof(signed int));
return;
}
for (chn = 0; chn < channels; chn++) {
- dchn = dmix->bindings ? dmix->bindings[chn] : chn;
+ dchn = dmix->u.dmix.bindings ? dmix->u.dmix.bindings[chn] : chn;
if (dchn >= dmix->shmptr->s.channels)
continue;
src_step = src_areas[chn].step / 8;
dst_step = dst_areas[dchn].step / 8;
src = (signed short *)(((char *)src_areas[chn].addr + src_areas[chn].first / 8) + (src_ofs * src_step));
dst = (signed short *)(((char *)dst_areas[dchn].addr + dst_areas[dchn].first / 8) + (dst_ofs * dst_step));
- sum = dmix->sum_buffer + channels * dst_ofs + chn;
- dmix->mix_areas1(size, dst, src, sum, dst_step, src_step, channels * sizeof(signed int));
+ sum = dmix->u.dmix.sum_buffer + channels * dst_ofs + chn;
+ dmix->u.dmix.mix_areas1(size, dst, src, sum, dst_step, src_step, channels * sizeof(signed int));
}
} else {
signed int *src;
* process all areas in one loop
* it optimizes the memory accesses for this case
*/
- dmix->mix_areas2(size * channels,
+ dmix->u.dmix.mix_areas2(size * channels,
((signed int *)dst_areas[0].addr) + (dst_ofs * channels),
((signed int *)src_areas[0].addr) + (src_ofs * channels),
- dmix->sum_buffer + (dst_ofs * channels),
+ dmix->u.dmix.sum_buffer + (dst_ofs * channels),
sizeof(signed int),
sizeof(signed int),
sizeof(signed int));
return;
}
for (chn = 0; chn < channels; chn++) {
- dchn = dmix->bindings ? dmix->bindings[chn] : chn;
+ dchn = dmix->u.dmix.bindings ? dmix->u.dmix.bindings[chn] : chn;
if (dchn >= dmix->shmptr->s.channels)
continue;
src_step = src_areas[chn].step / 8;
dst_step = dst_areas[dchn].step / 8;
src = (signed int *)(((char *)src_areas[chn].addr + src_areas[chn].first / 8) + (src_ofs * src_step));
dst = (signed int *)(((char *)dst_areas[dchn].addr + dst_areas[dchn].first / 8) + (dst_ofs * dst_step));
- sum = dmix->sum_buffer + channels * dst_ofs + chn;
- dmix->mix_areas2(size, dst, src, sum, dst_step, src_step, channels * sizeof(signed int));
+ sum = dmix->u.dmix.sum_buffer + channels * dst_ofs + chn;
+ dmix->u.dmix.mix_areas2(size, dst, src, sum, dst_step, src_step, channels * sizeof(signed int));
}
}
}
*/
static void snd_pcm_dmix_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t size)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
snd_pcm_uframes_t appl_ptr, slave_appl_ptr, transfer;
const snd_pcm_channel_area_t *src_areas, *dst_areas;
*/
static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail;
snd_pcm_sframes_t diff;
static int snd_pcm_dmix_async(snd_pcm_t *pcm, int sig, pid_t pid)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
return snd_timer_async(dmix->timer, sig, pid);
}
static int snd_pcm_dmix_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
unsigned short events;
static snd_timer_read_t rbuf[5]; /* can be overwriten by multiple plugins, we don't need the value */
static int snd_pcm_dmix_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
{
- // snd_pcm_dmix_t *dmix = pcm->private_data;
+ // snd_pcm_direct_t *dmix = pcm->private_data;
memset(info, 0, sizeof(*info));
info->stream = pcm->stream;
static int snd_pcm_dmix_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
snd_pcm_hw_params_t *hw_params = &dmix->shmptr->hw_params;
static snd_mask_t access = { .bits = {
(1<<SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) |
snd_output_puts(log, "DMIX REFINE (begin):\n");
snd_pcm_hw_params_dump(params, log);
#endif
-\
if (params->rmask & (1<<SND_PCM_HW_PARAM_ACCESS)) {
if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS))) {
SNDERR("dmix access mask empty?");
SNDERR("dmix channels mask empty?");
return -EINVAL;
}
- err = snd_interval_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS), dmix->channels);
+ err = snd_interval_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS), dmix->u.dmix.channels);
if (err < 0)
return err;
}
static int snd_pcm_dmix_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
memset(status, 0, sizeof(*status));
status->state = dmix->state;
static snd_pcm_state_t snd_pcm_dmix_state(snd_pcm_t *pcm)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
return dmix->state;
}
static int snd_pcm_dmix_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
int err;
switch(dmix->state) {
static int snd_pcm_dmix_hwsync(snd_pcm_t *pcm)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
switch(dmix->state) {
case SNDRV_PCM_STATE_DRAINING:
static int snd_pcm_dmix_prepare(snd_pcm_t *pcm)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
- check_interleave(dmix, pcm);
+ snd_pcm_direct_check_interleave(dmix, pcm);
// assert(pcm->boundary == dmix->shmptr->s.boundary); /* for sure */
dmix->state = SND_PCM_STATE_PREPARED;
dmix->appl_ptr = 0;
static int snd_pcm_dmix_reset(snd_pcm_t *pcm)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
dmix->hw_ptr %= pcm->period_size;
dmix->appl_ptr = dmix->hw_ptr;
dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
static int snd_pcm_dmix_start(snd_pcm_t *pcm)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
snd_pcm_sframes_t avail;
struct timeval tv;
int err;
static int snd_pcm_dmix_drop(snd_pcm_t *pcm)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
if (dmix->state == SND_PCM_STATE_OPEN)
return -EBADFD;
snd_timer_stop(dmix->timer);
static int snd_pcm_dmix_drain(snd_pcm_t *pcm)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
snd_pcm_uframes_t stop_threshold;
int err;
static int snd_pcm_dmix_pause(snd_pcm_t *pcm, int enable)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
if (enable) {
if (dmix->state != SND_PCM_STATE_RUNNING)
return -EBADFD;
static int snd_pcm_dmix_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
- // snd_pcm_dmix_t *dmix = pcm->private_data;
+ // snd_pcm_direct_t *dmix = pcm->private_data;
// FIXME
return 0;
}
static int snd_pcm_dmix_close(snd_pcm_t *pcm)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
if (dmix->timer)
snd_timer_close(dmix->timer);
- semaphore_down(dmix, DMIX_IPC_SEM_CLIENT);
+ snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
snd_pcm_close(dmix->spcm);
if (dmix->server)
- server_discard(dmix);
+ snd_pcm_direct_server_discard(dmix);
if (dmix->client)
- client_discard(dmix);
+ snd_pcm_direct_client_discard(dmix);
shm_sum_discard(dmix);
- if (shm_discard(dmix) > 0) {
- if (semaphore_discard(dmix) < 0)
- semaphore_up(dmix, DMIX_IPC_SEM_CLIENT);
+ if (snd_pcm_direct_shm_discard(dmix) > 0) {
+ if (snd_pcm_direct_semaphore_discard(dmix) < 0)
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
} else {
- semaphore_up(dmix, DMIX_IPC_SEM_CLIENT);
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
}
- if (dmix->bindings)
- free(dmix->bindings);
+ if (dmix->u.dmix.bindings)
+ free(dmix->u.dmix.bindings);
pcm->private_data = NULL;
free(dmix);
return 0;
snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
snd_pcm_uframes_t size)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
int err;
snd_pcm_mmap_appl_forward(pcm, size);
static snd_pcm_sframes_t snd_pcm_dmix_avail_update(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
int err;
if (dmix->state == SND_PCM_STATE_RUNNING) {
static void snd_pcm_dmix_dump(snd_pcm_t *pcm, snd_output_t *out)
{
- snd_pcm_dmix_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
snd_output_printf(out, "Direct Stream Mixing PCM\n");
if (pcm->setup) {
mmap_commit: snd_pcm_dmix_mmap_commit,
};
-/*
- * this function initializes hardware and starts playback operation with
- * no stop threshold (it operates all time without xrun checking)
- * also, the driver silences the unused ring buffer areas for us
- */
-static int snd_pcm_dmix_initialize_slave(snd_pcm_dmix_t *dmix, snd_pcm_t *spcm, struct slave_params *params)
-{
- snd_pcm_hw_params_t *hw_params;
- snd_pcm_sw_params_t *sw_params;
- int ret, buffer_is_not_initialized;
- snd_pcm_uframes_t boundary;
- struct pollfd fd;
- int loops = 10;
-
- hw_params = &dmix->shmptr->hw_params;
- sw_params = &dmix->shmptr->sw_params;
-
- __again:
- if (loops-- <= 0) {
- SNDERR("unable to find a valid configuration for slave");
- return -EINVAL;
- }
- ret = snd_pcm_hw_params_any(spcm, hw_params);
- if (ret < 0) {
- SNDERR("snd_pcm_hw_params_any failed");
- return ret;
- }
- ret = snd_pcm_hw_params_set_access(spcm, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED);
- if (ret < 0) {
- ret = snd_pcm_hw_params_set_access(spcm, hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
- if (ret < 0) {
- SNDERR("slave plugin does not support mmap interleaved or mmap noninterleaved access");
- return ret;
- }
- }
- ret = snd_pcm_hw_params_set_format(spcm, hw_params, params->format);
- if (ret < 0) {
- snd_pcm_format_t format;
- switch (params->format) {
- case SND_PCM_FORMAT_S32: format = SND_PCM_FORMAT_S16; break;
- case SND_PCM_FORMAT_S16: format = SND_PCM_FORMAT_S32; break;
- default:
- SNDERR("invalid format");
- return -EINVAL;
- }
- ret = snd_pcm_hw_params_set_format(spcm, hw_params, format);
- if (ret < 0) {
- SNDERR("requested or auto-format is not available");
- return ret;
- }
- params->format = format;
- }
- ret = snd_pcm_hw_params_set_channels(spcm, hw_params, params->channels);
- if (ret < 0) {
- unsigned int min, max;
- ret = INTERNAL(snd_pcm_hw_params_get_channels_min)(hw_params, &min);
- if (ret < 0) {
- SNDERR("cannot obtain minimal count of channels");
- return ret;
- }
- ret = INTERNAL(snd_pcm_hw_params_get_channels_min)(hw_params, &max);
- if (ret < 0) {
- SNDERR("cannot obtain maximal count of channels");
- return ret;
- }
- if (min == max) {
- ret = snd_pcm_hw_params_set_channels(spcm, hw_params, min);
- if (ret >= 0)
- params->channels = min;
- }
- if (ret < 0) {
- SNDERR("requested count of channels is not available");
- return ret;
- }
- }
- ret = INTERNAL(snd_pcm_hw_params_set_rate_near)(spcm, hw_params, ¶ms->rate, 0);
- if (ret < 0) {
- SNDERR("requested rate is not available");
- return ret;
- }
-
- buffer_is_not_initialized = 0;
- if (params->buffer_time > 0) {
- ret = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(spcm, hw_params, ¶ms->buffer_time, 0);
- if (ret < 0) {
- SNDERR("unable to set buffer time");
- return ret;
- }
- } else if (params->buffer_size > 0) {
- ret = INTERNAL(snd_pcm_hw_params_set_buffer_size_near)(spcm, hw_params, ¶ms->buffer_size);
- if (ret < 0) {
- SNDERR("unable to set buffer size");
- return ret;
- }
- } else {
- buffer_is_not_initialized = 1;
- }
-
- if (params->period_time > 0) {
- ret = INTERNAL(snd_pcm_hw_params_set_period_time_near)(spcm, hw_params, ¶ms->period_time, 0);
- if (ret < 0) {
- SNDERR("unable to set period_time");
- return ret;
- }
- } else if (params->period_size > 0) {
- ret = INTERNAL(snd_pcm_hw_params_set_period_size_near)(spcm, hw_params, ¶ms->period_size, 0);
- if (ret < 0) {
- SNDERR("unable to set period_size");
- return ret;
- }
- }
-
- if (buffer_is_not_initialized && params->periods > 0) {
- int periods = params->periods;
- ret = INTERNAL(snd_pcm_hw_params_set_periods_near)(spcm, hw_params, ¶ms->periods, 0);
- if (ret < 0) {
- SNDERR("unable to set requested periods");
- return ret;
- }
- if (params->periods == 1) {
- params->periods = periods;
- if (params->period_time > 0) {
- params->period_time /= 2;
- goto __again;
- } else if (params->period_size > 0) {
- params->period_size /= 2;
- goto __again;
- }
- SNDERR("unable to use stream with periods == 1");
- return ret;
- }
- }
-
- ret = snd_pcm_hw_params(spcm, hw_params);
- if (ret < 0) {
- SNDERR("unable to install hw params");
- return ret;
- }
-
- ret = snd_pcm_sw_params_current(spcm, sw_params);
- if (ret < 0) {
- SNDERR("unable to get current sw_params");
- return ret;
- }
-
- ret = snd_pcm_sw_params_get_boundary(sw_params, &boundary);
- if (ret < 0) {
- SNDERR("unable to get boundary");
- return ret;
- }
- ret = snd_pcm_sw_params_set_stop_threshold(spcm, sw_params, boundary);
- if (ret < 0) {
- SNDERR("unable to set stop threshold");
- return ret;
- }
- ret = snd_pcm_sw_params_set_silence_threshold(spcm, sw_params, 0);
- if (ret < 0) {
- SNDERR("unable to set silence threshold");
- return ret;
- }
- ret = snd_pcm_sw_params_set_silence_size(spcm, sw_params, boundary);
- if (ret < 0) {
- SNDERR("unable to set silence threshold (please upgrade to 0.9.0rc8+ driver)");
- return ret;
- }
-
- ret = snd_pcm_sw_params(spcm, sw_params);
- if (ret < 0) {
- SNDERR("unable to install sw params (please upgrade to 0.9.0rc8+ driver)");
- return ret;
- }
-
- ret = snd_pcm_start(spcm);
- if (ret < 0) {
- SNDERR("unable to start PCM stream");
- return ret;
- }
-
- if (snd_pcm_poll_descriptors_count(spcm) != 1) {
- SNDERR("unable to use hardware pcm with fd more than one!!!");
- return ret;
- }
- snd_pcm_poll_descriptors(spcm, &fd, 1);
- dmix->hw_fd = fd.fd;
-
- dmix->shmptr->s.boundary = spcm->boundary;
- dmix->shmptr->s.buffer_size = spcm->buffer_size;
- dmix->shmptr->s.sample_bits = spcm->sample_bits;
- dmix->shmptr->s.channels = spcm->channels;
- dmix->shmptr->s.rate = spcm->rate;
- dmix->shmptr->s.format = spcm->format;
- dmix->shmptr->s.boundary = spcm->boundary;
-
- spcm->donot_close = 1;
- return 0;
-}
-
-/*
- * the trick is used here; we cannot use effectively the hardware handle because
- * we cannot drive multiple accesses to appl_ptr; so we use slave timer of given
- * PCM hardware handle; it's not this easy and cheap?
- */
-static int snd_pcm_dmix_initialize_poll_fd(snd_pcm_dmix_t *dmix)
-{
- int ret;
- snd_pcm_info_t *info;
- snd_timer_params_t *params;
- char name[128];
- struct pollfd fd;
-
- snd_pcm_info_alloca(&info);
- snd_timer_params_alloca(¶ms);
- ret = snd_pcm_info(dmix->spcm, info);
- if (ret < 0) {
- SNDERR("unable to info for slave pcm");
- return ret;
- }
- sprintf(name, "hw:CLASS=%i,SCLASS=0,CARD=%i,DEV=%i,SUBDEV=%i",
- (int)SND_TIMER_CLASS_PCM,
- snd_pcm_info_get_card(info),
- snd_pcm_info_get_device(info),
- snd_pcm_info_get_subdevice(info) * 2); /* it's a bit trick to distict playback and capture */
- ret = snd_timer_open(&dmix->timer, name, SND_TIMER_OPEN_NONBLOCK);
- if (ret < 0) {
- SNDERR("unable to open timer '%s'", name);
- return ret;
- }
- snd_timer_params_set_auto_start(params, 1);
- snd_timer_params_set_ticks(params, 1);
- ret = snd_timer_params(dmix->timer, params);
- if (ret < 0) {
- SNDERR("unable to set timer parameters", name);
- return ret;
- }
- if (snd_timer_poll_descriptors_count(dmix->timer) != 1) {
- SNDERR("unable to use timer with fd more than one!!!", name);
- return ret;
- }
- snd_timer_poll_descriptors(dmix->timer, &fd, 1);
- dmix->poll_fd = fd.fd;
- return 0;
-}
-
/*
* parse the channel map
* id == client channel
* value == slave's channel
*/
-static int parse_bindings(snd_pcm_dmix_t *dmix, snd_config_t *cfg)
+static int parse_bindings(snd_pcm_direct_t *dmix, snd_config_t *cfg)
{
snd_config_iterator_t i, next;
unsigned int chn, chn1, count = 0;
int err;
- dmix->channels = UINT_MAX;
+ dmix->u.dmix.channels = UINT_MAX;
if (cfg == NULL)
return 0;
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("client channel out of range");
return -EINVAL;
}
- dmix->bindings = malloc(count * sizeof(unsigned int));
+ dmix->u.dmix.bindings = malloc(count * sizeof(unsigned int));
for (chn = 0; chn < count; chn++)
- dmix->bindings[chn] = UINT_MAX; /* don't route */
+ dmix->u.dmix.bindings[chn] = UINT_MAX; /* don't route */
snd_config_for_each(i, next, cfg) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
SNDERR("unable to get slave channel (should be integer type) in binding: %s\n", id);
return -EINVAL;
}
- dmix->bindings[cchannel] = schannel;
+ dmix->u.dmix.bindings[cchannel] = schannel;
}
for (chn = 0; chn < count; chn++) {
for (chn1 = 0; chn1 < count; chn1++) {
if (chn == chn1)
continue;
- if (dmix->bindings[chn] == dmix->bindings[chn1]) {
- SNDERR("unable to route channels %d,%d to same destination %d", chn, chn1, dmix->bindings[chn]);
+ if (dmix->u.dmix.bindings[chn] == dmix->u.dmix.bindings[chn1]) {
+ SNDERR("unable to route channels %d,%d to same destination %d", chn, chn1, dmix->u.dmix.bindings[chn]);
return -EINVAL;
}
}
}
- dmix->channels = count;
+ dmix->u.dmix.channels = count;
return 0;
}
snd_pcm_stream_t stream, int mode)
{
snd_pcm_t *pcm = NULL, *spcm = NULL;
- snd_pcm_dmix_t *dmix = NULL;
+ snd_pcm_direct_t *dmix = NULL;
int ret, first_instance;
assert(pcmp);
return -EINVAL;
}
- dmix = calloc(1, sizeof(snd_pcm_dmix_t));
+ dmix = calloc(1, sizeof(snd_pcm_direct_t));
if (!dmix) {
ret = -ENOMEM;
goto _err;
if (ret < 0)
goto _err;
- ret = semaphore_create_or_connect(dmix);
+ ret = snd_pcm_direct_semaphore_create_or_connect(dmix);
if (ret < 0) {
SNDERR("unable to create IPC semaphore");
goto _err;
}
- ret = semaphore_down(dmix, DMIX_IPC_SEM_CLIENT);
+ ret = snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
if (ret < 0) {
- semaphore_discard(dmix);
+ snd_pcm_direct_semaphore_discard(dmix);
goto _err;
}
- first_instance = ret = shm_create_or_connect(dmix);
+ first_instance = ret = snd_pcm_direct_shm_create_or_connect(dmix);
if (ret < 0) {
SNDERR("unable to create IPC shm instance");
goto _err;
goto _err;
}
- ret = snd_pcm_dmix_initialize_slave(dmix, spcm, params);
+ ret = snd_pcm_direct_initialize_slave(dmix, spcm, params);
if (ret < 0) {
SNDERR("unable to initialize slave");
goto _err;
dmix->spcm = spcm;
- ret = server_create(dmix);
+ ret = snd_pcm_direct_server_create(dmix);
if (ret < 0) {
SNDERR("unable to create server");
goto _err;
dmix->shmptr->type = spcm->type;
} else {
- ret = client_connect(dmix);
+ ret = snd_pcm_direct_client_connect(dmix);
if (ret < 0) {
SNDERR("unable to connect client");
return ret;
goto _err;
}
- ret = snd_pcm_dmix_initialize_poll_fd(dmix);
+ ret = snd_pcm_direct_initialize_poll_fd(dmix);
if (ret < 0) {
SNDERR("unable to initialize poll_fd");
goto _err;
snd_pcm_set_hw_ptr(pcm, &dmix->hw_ptr, -1, 0);
snd_pcm_set_appl_ptr(pcm, &dmix->appl_ptr, -1, 0);
- if (dmix->channels == UINT_MAX)
- dmix->channels = dmix->shmptr->s.channels;
+ if (dmix->u.dmix.channels == UINT_MAX)
+ dmix->u.dmix.channels = dmix->shmptr->s.channels;
- semaphore_up(dmix, DMIX_IPC_SEM_CLIENT);
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
*pcmp = pcm;
return 0;
if (dmix->timer)
snd_timer_close(dmix->timer);
if (dmix->server)
- server_discard(dmix);
+ snd_pcm_direct_server_discard(dmix);
if (dmix->client)
- client_discard(dmix);
+ snd_pcm_direct_client_discard(dmix);
if (spcm)
snd_pcm_close(spcm);
- if (dmix->shmid_sum >= 0)
+ if (dmix->u.dmix.shmid_sum >= 0)
shm_sum_discard(dmix);
if (dmix->shmid >= 0) {
- if (shm_discard(dmix) > 0) {
+ if (snd_pcm_direct_shm_discard(dmix) > 0) {
if (dmix->semid >= 0) {
- if (semaphore_discard(dmix) < 0)
- semaphore_up(dmix, DMIX_IPC_SEM_CLIENT);
+ if (snd_pcm_direct_semaphore_discard(dmix) < 0)
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
}
}
}
- if (dmix->bindings)
- free(dmix->bindings);
+ if (dmix->u.dmix.bindings)
+ free(dmix->u.dmix.bindings);
free(dmix);
}
if (pcm)
--- /dev/null
+/**
+ * \file pcm/pcm_dshare.c
+ * \ingroup PCM_Plugins
+ * \brief PCM Direct Sharing of Channels Plugin Interface
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \date 2003
+ */
+/*
+ * PCM - Direct Sharing of Channels
+ * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/shm.h>
+#include <sys/sem.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/mman.h>
+#include "pcm_direct.h"
+
+#ifndef PIC
+/* entry for static linking */
+const char *_snd_module_pcm_dshare = "";
+#endif
+
+static void share_areas(snd_pcm_direct_t *dmix,
+ const snd_pcm_channel_area_t *src_areas,
+ const snd_pcm_channel_area_t *dst_areas,
+ snd_pcm_uframes_t src_ofs,
+ snd_pcm_uframes_t dst_ofs,
+ snd_pcm_uframes_t size)
+{
+ if (dmix->interleaved) {
+ } else {
+ }
+}
+
+/*
+ * synchronize shm ring buffer with hardware
+ */
+static void snd_pcm_dshare_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t size)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_uframes_t appl_ptr, slave_appl_ptr, transfer;
+ const snd_pcm_channel_area_t *src_areas, *dst_areas;
+
+ /* get the start of update area */
+ appl_ptr = dmix->appl_ptr - size;
+ if (appl_ptr > pcm->boundary)
+ appl_ptr += pcm->boundary;
+ appl_ptr %= pcm->buffer_size;
+ /* add sample areas here */
+ src_areas = snd_pcm_mmap_areas(pcm);
+ dst_areas = snd_pcm_mmap_areas(dmix->spcm);
+ slave_appl_ptr = dmix->slave_appl_ptr % dmix->shmptr->s.buffer_size;
+ dmix->slave_appl_ptr += size;
+ dmix->slave_appl_ptr %= dmix->shmptr->s.boundary;
+ while (size > 0) {
+ transfer = appl_ptr + size > pcm->buffer_size ? pcm->buffer_size - appl_ptr : size;
+ transfer = slave_appl_ptr + transfer > dmix->shmptr->s.buffer_size ? dmix->shmptr->s.buffer_size - slave_appl_ptr : transfer;
+ size -= transfer;
+ share_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer);
+ slave_appl_ptr += transfer;
+ slave_appl_ptr %= dmix->shmptr->s.buffer_size;
+ appl_ptr += transfer;
+ appl_ptr %= pcm->buffer_size;
+ }
+}
+
+/*
+ * synchronize hardware pointer (hw_ptr) with ours
+ */
+static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail;
+ snd_pcm_sframes_t diff;
+
+ old_slave_hw_ptr = dmix->slave_hw_ptr;
+ slave_hw_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
+ diff = slave_hw_ptr - old_slave_hw_ptr;
+ if (diff == 0) /* fast path */
+ return 0;
+ if (diff < 0) {
+ slave_hw_ptr += dmix->shmptr->s.boundary;
+ diff = slave_hw_ptr - old_slave_hw_ptr;
+ }
+ dmix->hw_ptr += diff;
+ dmix->hw_ptr %= pcm->boundary;
+ // printf("sync ptr diff = %li\n", diff);
+ if (pcm->stop_threshold >= pcm->boundary) /* don't care */
+ return 0;
+ if ((avail = snd_pcm_mmap_playback_avail(pcm)) >= pcm->stop_threshold) {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ dmix->trigger_tstamp.tv_sec = tv.tv_sec;
+ dmix->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
+ dmix->state = SND_PCM_STATE_XRUN;
+ dmix->avail_max = avail;
+ return -EPIPE;
+ }
+ if (avail > dmix->avail_max)
+ dmix->avail_max = avail;
+ return 0;
+}
+
+/*
+ * plugin implementation
+ */
+
+static int snd_pcm_dshare_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
+{
+ /* value is cached for us in pcm->mode (SND_PCM_NONBLOCK flag) */
+ return 0;
+}
+
+static int snd_pcm_dshare_async(snd_pcm_t *pcm, int sig, pid_t pid)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ return snd_timer_async(dmix->timer, sig, pid);
+}
+
+static int snd_pcm_dshare_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ unsigned short events;
+ static snd_timer_read_t rbuf[5]; /* can be overwriten by multiple plugins, we don't need the value */
+
+ assert(pfds && nfds == 1 && revents);
+ events = pfds[0].revents;
+ if (events & POLLIN) {
+ events |= POLLOUT;
+ events &= ~POLLIN;
+ /* empty the timer read queue */
+ while (snd_timer_read(dmix->timer, &rbuf, sizeof(rbuf)) == sizeof(rbuf)) ;
+ }
+ *revents = events;
+ return 0;
+}
+
+static int snd_pcm_dshare_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
+{
+ // snd_pcm_direct_t *dmix = pcm->private_data;
+
+ memset(info, 0, sizeof(*info));
+ info->stream = pcm->stream;
+ info->card = -1;
+ /* FIXME: fill this with something more useful: we know the hardware name */
+ strncpy(info->id, pcm->name, sizeof(info->id));
+ strncpy(info->name, pcm->name, sizeof(info->name));
+ strncpy(info->subname, pcm->name, sizeof(info->subname));
+ info->subdevices_count = 1;
+ return 0;
+}
+
+static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_param_t var)
+{
+ return ¶ms->masks[var - SND_PCM_HW_PARAM_FIRST_MASK];
+}
+
+static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_param_t var)
+{
+ return ¶ms->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL];
+}
+
+static int hw_param_interval_refine_one(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_param_t var,
+ snd_pcm_hw_params_t *src)
+{
+ snd_interval_t *i;
+
+ if (!(params->rmask & (1<<var))) /* nothing to do? */
+ return 0;
+ i = hw_param_interval(params, var);
+ if (snd_interval_empty(i)) {
+ SNDERR("dmix interval %i empty?", (int)var);
+ return -EINVAL;
+ }
+ if (snd_interval_refine(i, hw_param_interval(src, var)))
+ params->cmask |= 1<<var;
+ return 0;
+}
+
+#undef REFINE_DEBUG
+
+static int snd_pcm_dshare_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_hw_params_t *hw_params = &dmix->shmptr->hw_params;
+ static snd_mask_t access = { .bits = {
+ (1<<SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) |
+ (1<<SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) |
+ (1<<SNDRV_PCM_ACCESS_RW_INTERLEAVED) |
+ (1<<SNDRV_PCM_ACCESS_RW_NONINTERLEAVED),
+ 0, 0, 0 } };
+ int err;
+
+#ifdef REFINE_DEBUG
+ snd_output_t *log;
+ snd_output_stdio_attach(&log, stderr, 0);
+ snd_output_puts(log, "DMIX REFINE (begin):\n");
+ snd_pcm_hw_params_dump(params, log);
+#endif
+\
+ if (params->rmask & (1<<SND_PCM_HW_PARAM_ACCESS)) {
+ if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS))) {
+ SNDERR("dmix access mask empty?");
+ return -EINVAL;
+ }
+ if (snd_mask_refine(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS), &access))
+ params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
+ }
+ if (params->rmask & (1<<SND_PCM_HW_PARAM_FORMAT)) {
+ if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT))) {
+ SNDERR("dmix format mask empty?");
+ return -EINVAL;
+ }
+ if (snd_mask_refine_set(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT),
+ snd_mask_value(hw_param_mask(hw_params, SND_PCM_HW_PARAM_FORMAT))))
+ params->cmask |= 1<<SND_PCM_HW_PARAM_FORMAT;
+ }
+ //snd_mask_none(hw_param_mask(params, SND_PCM_HW_PARAM_SUBFORMAT));
+ if (params->rmask & (1<<SND_PCM_HW_PARAM_CHANNELS)) {
+ if (snd_interval_empty(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS))) {
+ SNDERR("dmix channels mask empty?");
+ return -EINVAL;
+ }
+ err = snd_interval_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS), dmix->u.dmix.channels);
+ if (err < 0)
+ return err;
+ }
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_RATE, hw_params);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_SIZE, hw_params);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_TIME, hw_params);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE, hw_params);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME, hw_params);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIODS, hw_params);
+ if (err < 0)
+ return err;
+#ifdef REFINE_DEBUG
+ snd_output_puts(log, "DMIX REFINE (end):\n");
+ snd_pcm_hw_params_dump(params, log);
+ snd_output_close(log);
+#endif
+ return 0;
+}
+
+static int snd_pcm_dshare_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED)
+{
+ /* values are cached in the pcm structure */
+
+ return 0;
+}
+
+static int snd_pcm_dshare_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+ /* values are cached in the pcm structure */
+ return 0;
+}
+
+static int snd_pcm_dshare_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params ATTRIBUTE_UNUSED)
+{
+ /* values are cached in the pcm structure */
+ return 0;
+}
+
+static int snd_pcm_dshare_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
+{
+ return snd_pcm_channel_info_shm(pcm, info, -1);
+}
+
+static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+
+ memset(status, 0, sizeof(*status));
+ status->state = dmix->state;
+ status->trigger_tstamp = dmix->trigger_tstamp;
+ status->tstamp = snd_pcm_hw_fast_tstamp(dmix->spcm);
+ status->avail = snd_pcm_mmap_playback_avail(pcm);
+ status->avail_max = status->avail > dmix->avail_max ? status->avail : dmix->avail_max;
+ dmix->avail_max = 0;
+ return 0;
+}
+
+static snd_pcm_state_t snd_pcm_dshare_state(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ return dmix->state;
+}
+
+static int snd_pcm_dshare_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ int err;
+
+ switch(dmix->state) {
+ case SNDRV_PCM_STATE_DRAINING:
+ case SNDRV_PCM_STATE_RUNNING:
+ err = snd_pcm_dshare_sync_ptr(pcm);
+ if (err < 0)
+ return err;
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ *delayp = snd_pcm_mmap_playback_hw_avail(pcm);
+ return 0;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ return -EBADFD;
+ }
+}
+
+static int snd_pcm_dshare_hwsync(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+
+ switch(dmix->state) {
+ case SNDRV_PCM_STATE_DRAINING:
+ case SNDRV_PCM_STATE_RUNNING:
+ return snd_pcm_dshare_sync_ptr(pcm);
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ return 0;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ return -EBADFD;
+ }
+}
+
+static int snd_pcm_dshare_prepare(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+
+ snd_pcm_direct_check_interleave(dmix, pcm);
+ // assert(pcm->boundary == dmix->shmptr->s.boundary); /* for sure */
+ dmix->state = SND_PCM_STATE_PREPARED;
+ dmix->appl_ptr = 0;
+ dmix->hw_ptr = 0;
+ return 0;
+}
+
+static int snd_pcm_dshare_reset(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ dmix->hw_ptr %= pcm->period_size;
+ dmix->appl_ptr = dmix->hw_ptr;
+ dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
+ return 0;
+}
+
+static int snd_pcm_dshare_start(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_sframes_t avail;
+ struct timeval tv;
+ int err;
+
+ if (dmix->state != SND_PCM_STATE_PREPARED)
+ return -EBADFD;
+ err = snd_timer_start(dmix->timer);
+ if (err < 0)
+ return err;
+ dmix->state = SND_PCM_STATE_RUNNING;
+ dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
+ avail = snd_pcm_mmap_playback_hw_avail(pcm);
+ if (avail < 0)
+ return 0;
+ if (avail > (snd_pcm_sframes_t)pcm->buffer_size)
+ avail = pcm->buffer_size;
+ snd_pcm_dshare_sync_area(pcm, avail);
+ gettimeofday(&tv, 0);
+ dmix->trigger_tstamp.tv_sec = tv.tv_sec;
+ dmix->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
+ return 0;
+}
+
+static int snd_pcm_dshare_drop(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ if (dmix->state == SND_PCM_STATE_OPEN)
+ return -EBADFD;
+ snd_timer_stop(dmix->timer);
+ dmix->state = SND_PCM_STATE_SETUP;
+ return 0;
+}
+
+static int snd_pcm_dshare_drain(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_uframes_t stop_threshold;
+ int err;
+
+ if (dmix->state == SND_PCM_STATE_OPEN)
+ return -EBADFD;
+ stop_threshold = pcm->stop_threshold;
+ if (pcm->stop_threshold > pcm->buffer_size)
+ pcm->stop_threshold = pcm->buffer_size;
+ while (dmix->state == SND_PCM_STATE_RUNNING) {
+ err = snd_pcm_dshare_sync_ptr(pcm);
+ if (err < 0)
+ break;
+ if (pcm->mode & SND_PCM_NONBLOCK)
+ return -EAGAIN;
+ snd_pcm_wait(pcm, -1);
+ }
+ pcm->stop_threshold = stop_threshold;
+ return snd_pcm_dshare_drop(pcm);
+}
+
+static int snd_pcm_dshare_pause(snd_pcm_t *pcm, int enable)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ if (enable) {
+ if (dmix->state != SND_PCM_STATE_RUNNING)
+ return -EBADFD;
+ dmix->state = SND_PCM_STATE_PAUSED;
+ snd_timer_stop(dmix->timer);
+ } else {
+ if (dmix->state != SND_PCM_STATE_PAUSED)
+ return -EBADFD;
+ dmix->state = SND_PCM_STATE_RUNNING;
+ snd_timer_start(dmix->timer);
+ }
+ return 0;
+}
+
+static snd_pcm_sframes_t snd_pcm_dshare_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
+{
+ /* FIXME: substract samples from the mix ring buffer, too? */
+ snd_pcm_mmap_appl_backward(pcm, frames);
+ return frames;
+}
+
+static snd_pcm_sframes_t snd_pcm_dshare_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
+{
+ snd_pcm_sframes_t avail;
+
+ avail = snd_pcm_mmap_avail(pcm);
+ if (avail < 0)
+ return 0;
+ if (frames > (snd_pcm_uframes_t)avail)
+ frames = avail;
+ snd_pcm_mmap_appl_forward(pcm, frames);
+ return frames;
+}
+
+static int snd_pcm_dshare_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+ // snd_pcm_direct_t *dmix = pcm->private_data;
+ // FIXME
+ return 0;
+}
+
+static snd_pcm_sframes_t snd_pcm_dshare_readi(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
+{
+ return -ENODEV;
+}
+
+static snd_pcm_sframes_t snd_pcm_dshare_readn(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
+{
+ return -ENODEV;
+}
+
+static int snd_pcm_dshare_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static int snd_pcm_dshare_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static int snd_pcm_dshare_close(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+
+ if (dmix->timer)
+ snd_timer_close(dmix->timer);
+ snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
+ snd_pcm_close(dmix->spcm);
+ if (dmix->server)
+ snd_pcm_direct_server_discard(dmix);
+ if (dmix->client)
+ snd_pcm_direct_client_discard(dmix);
+ if (snd_pcm_direct_shm_discard(dmix) > 0) {
+ if (snd_pcm_direct_semaphore_discard(dmix) < 0)
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
+ } else {
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
+ }
+ if (dmix->u.dmix.bindings)
+ free(dmix->u.dmix.bindings);
+ pcm->private_data = NULL;
+ free(dmix);
+ return 0;
+}
+
+static snd_pcm_sframes_t snd_pcm_dshare_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t size)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ int err;
+
+ snd_pcm_mmap_appl_forward(pcm, size);
+ if (dmix->state == SND_PCM_STATE_RUNNING) {
+ err = snd_pcm_dshare_sync_ptr(pcm);
+ if (err < 0)
+ return err;
+ /* ok, we commit the changes after the validation of area */
+ /* it's intended, although the result might be crappy */
+ snd_pcm_dshare_sync_area(pcm, size);
+ }
+ return size;
+}
+
+static snd_pcm_sframes_t snd_pcm_dshare_avail_update(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ int err;
+
+ if (dmix->state == SND_PCM_STATE_RUNNING) {
+ err = snd_pcm_dshare_sync_ptr(pcm);
+ if (err < 0)
+ return err;
+ }
+ return snd_pcm_mmap_playback_avail(pcm);
+}
+
+static void snd_pcm_dshare_dump(snd_pcm_t *pcm, snd_output_t *out)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+
+ snd_output_printf(out, "Direct Stream Mixing PCM\n");
+ if (pcm->setup) {
+ snd_output_printf(out, "\nIts setup is:\n");
+ snd_pcm_dump_setup(pcm, out);
+ }
+ if (dmix->spcm)
+ snd_pcm_dump(dmix->spcm, out);
+}
+
+static snd_pcm_ops_t snd_pcm_dshare_ops = {
+ close: snd_pcm_dshare_close,
+ info: snd_pcm_dshare_info,
+ hw_refine: snd_pcm_dshare_hw_refine,
+ hw_params: snd_pcm_dshare_hw_params,
+ hw_free: snd_pcm_dshare_hw_free,
+ sw_params: snd_pcm_dshare_sw_params,
+ channel_info: snd_pcm_dshare_channel_info,
+ dump: snd_pcm_dshare_dump,
+ nonblock: snd_pcm_dshare_nonblock,
+ async: snd_pcm_dshare_async,
+ poll_revents: snd_pcm_dshare_poll_revents,
+ mmap: snd_pcm_dshare_mmap,
+ munmap: snd_pcm_dshare_munmap,
+};
+
+static snd_pcm_fast_ops_t snd_pcm_dshare_fast_ops = {
+ status: snd_pcm_dshare_status,
+ state: snd_pcm_dshare_state,
+ hwsync: snd_pcm_dshare_hwsync,
+ delay: snd_pcm_dshare_delay,
+ prepare: snd_pcm_dshare_prepare,
+ reset: snd_pcm_dshare_reset,
+ start: snd_pcm_dshare_start,
+ drop: snd_pcm_dshare_drop,
+ drain: snd_pcm_dshare_drain,
+ pause: snd_pcm_dshare_pause,
+ rewind: snd_pcm_dshare_rewind,
+ forward: snd_pcm_dshare_forward,
+ resume: snd_pcm_dshare_resume,
+ writei: snd_pcm_mmap_writei,
+ writen: snd_pcm_mmap_writen,
+ readi: snd_pcm_dshare_readi,
+ readn: snd_pcm_dshare_readn,
+ avail_update: snd_pcm_dshare_avail_update,
+ mmap_commit: snd_pcm_dshare_mmap_commit,
+};
+
+/*
+ * parse the channel map
+ * id == client channel
+ * value == slave's channel
+ */
+static int parse_bindings(snd_pcm_direct_t *dmix, snd_config_t *cfg)
+{
+ snd_config_iterator_t i, next;
+ unsigned int chn, chn1, count = 0;
+ int err;
+
+ dmix->u.dmix.channels = UINT_MAX;
+ if (cfg == NULL)
+ return 0;
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+ SNDERR("invalid type for bindings");
+ return -EINVAL;
+ }
+ snd_config_for_each(i, next, cfg) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id;
+ long cchannel;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ err = safe_strtol(id, &cchannel);
+ if (err < 0 || cchannel < 0) {
+ SNDERR("invalid client channel in binding: %s\n", id);
+ return -EINVAL;
+ }
+ if ((unsigned)cchannel > count)
+ count = cchannel + 1;
+ }
+ if (count == 0)
+ return 0;
+ if (count > 1024) {
+ SNDERR("client channel out of range");
+ return -EINVAL;
+ }
+ dmix->u.dmix.bindings = malloc(count * sizeof(unsigned int));
+ for (chn = 0; chn < count; chn++)
+ dmix->u.dmix.bindings[chn] = UINT_MAX; /* don't route */
+ snd_config_for_each(i, next, cfg) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id;
+ long cchannel, schannel;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ safe_strtol(id, &cchannel);
+ if (snd_config_get_integer(n, &schannel) < 0) {
+ SNDERR("unable to get slave channel (should be integer type) in binding: %s\n", id);
+ return -EINVAL;
+ }
+ dmix->u.dmix.bindings[cchannel] = schannel;
+ }
+ for (chn = 0; chn < count; chn++) {
+ for (chn1 = 0; chn1 < count; chn1++) {
+ if (chn == chn1)
+ continue;
+ if (dmix->u.dmix.bindings[chn] == dmix->u.dmix.bindings[chn1]) {
+ SNDERR("unable to route channels %d,%d to same destination %d", chn, chn1, dmix->u.dmix.bindings[chn]);
+ return -EINVAL;
+ }
+ }
+ }
+ dmix->u.dmix.channels = count;
+ return 0;
+}
+
+/**
+ * \brief Creates a new dmix PCM
+ * \param pcmp Returns created PCM handle
+ * \param name Name of PCM
+ * \param ipc_key IPC key for semaphore and shared memory
+ * \param params Parameters for slave
+ * \param root Configuration root
+ * \param sconf Slave configuration
+ * \param stream PCM Direction (stream)
+ * \param mode PCM Mode
+ * \retval zero on success otherwise a negative error code
+ * \warning Using of this function might be dangerous in the sense
+ * of compatibility reasons. The prototype might be freely
+ * changed in future.
+ */
+int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name,
+ key_t ipc_key, struct slave_params *params,
+ snd_config_t *bindings,
+ snd_config_t *root, snd_config_t *sconf,
+ snd_pcm_stream_t stream, int mode)
+{
+ snd_pcm_t *pcm = NULL, *spcm = NULL;
+ snd_pcm_direct_t *dmix = NULL;
+ int ret, first_instance;
+
+ assert(pcmp);
+
+ if (stream != SND_PCM_STREAM_PLAYBACK) {
+ SNDERR("The dmix plugin supports only playback stream");
+ return -EINVAL;
+ }
+
+ dmix = calloc(1, sizeof(snd_pcm_direct_t));
+ if (!dmix) {
+ ret = -ENOMEM;
+ goto _err;
+ }
+
+ ret = parse_bindings(dmix, bindings);
+ if (ret < 0)
+ goto _err;
+
+ dmix->ipc_key = ipc_key;
+ dmix->semid = -1;
+ dmix->shmid = -1;
+
+ ret = snd_pcm_new(&pcm, SND_PCM_TYPE_DSHARE, name, stream, mode);
+ if (ret < 0)
+ goto _err;
+
+ ret = snd_pcm_direct_semaphore_create_or_connect(dmix);
+ if (ret < 0) {
+ SNDERR("unable to create IPC semaphore");
+ goto _err;
+ }
+
+ ret = snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
+ if (ret < 0) {
+ snd_pcm_direct_semaphore_discard(dmix);
+ goto _err;
+ }
+
+ first_instance = ret = snd_pcm_direct_shm_create_or_connect(dmix);
+ if (ret < 0) {
+ SNDERR("unable to create IPC shm instance");
+ goto _err;
+ }
+
+ pcm->ops = &snd_pcm_dshare_ops;
+ pcm->fast_ops = &snd_pcm_dshare_fast_ops;
+ pcm->private_data = dmix;
+ dmix->state = SND_PCM_STATE_OPEN;
+
+ if (first_instance) {
+ ret = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
+ if (ret < 0) {
+ SNDERR("unable to open slave");
+ goto _err;
+ }
+
+ if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
+ SNDERR("dmix plugin can be only connected to hw plugin");
+ goto _err;
+ }
+
+ ret = snd_pcm_direct_initialize_slave(dmix, spcm, params);
+ if (ret < 0) {
+ SNDERR("unable to initialize slave");
+ goto _err;
+ }
+
+ dmix->spcm = spcm;
+
+ ret = snd_pcm_direct_server_create(dmix);
+ if (ret < 0) {
+ SNDERR("unable to create server");
+ goto _err;
+ }
+
+ dmix->shmptr->type = spcm->type;
+ } else {
+ ret = snd_pcm_direct_client_connect(dmix);
+ if (ret < 0) {
+ SNDERR("unable to connect client");
+ return ret;
+ }
+
+ ret = snd_pcm_hw_open_fd(&spcm, "dmix_client", dmix->hw_fd, 0);
+ if (ret < 0) {
+ SNDERR("unable to open hardware");
+ goto _err;
+ }
+
+ spcm->donot_close = 1;
+ spcm->setup = 1;
+ spcm->buffer_size = dmix->shmptr->s.buffer_size;
+ spcm->sample_bits = dmix->shmptr->s.sample_bits;
+ spcm->channels = dmix->shmptr->s.channels;
+ spcm->format = dmix->shmptr->s.format;
+ spcm->boundary = dmix->shmptr->s.boundary;
+ ret = snd_pcm_mmap(spcm);
+ if (ret < 0) {
+ SNDERR("unable to mmap channels");
+ goto _err;
+ }
+ dmix->spcm = spcm;
+ }
+
+ ret = snd_pcm_direct_initialize_poll_fd(dmix);
+ if (ret < 0) {
+ SNDERR("unable to initialize poll_fd");
+ goto _err;
+ }
+
+ pcm->poll_fd = dmix->poll_fd;
+ pcm->poll_events = POLLIN; /* it's different than other plugins */
+
+ pcm->mmap_rw = 1;
+ snd_pcm_set_hw_ptr(pcm, &dmix->hw_ptr, -1, 0);
+ snd_pcm_set_appl_ptr(pcm, &dmix->appl_ptr, -1, 0);
+
+ if (dmix->u.dmix.channels == UINT_MAX)
+ dmix->u.dmix.channels = dmix->shmptr->s.channels;
+
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
+
+ *pcmp = pcm;
+ return 0;
+
+ _err:
+ if (dmix) {
+ if (dmix->timer)
+ snd_timer_close(dmix->timer);
+ if (dmix->server)
+ snd_pcm_direct_server_discard(dmix);
+ if (dmix->client)
+ snd_pcm_direct_client_discard(dmix);
+ if (spcm)
+ snd_pcm_close(spcm);
+ if (dmix->shmid >= 0) {
+ if (snd_pcm_direct_shm_discard(dmix) > 0) {
+ if (dmix->semid >= 0) {
+ if (snd_pcm_direct_semaphore_discard(dmix) < 0)
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
+ }
+ }
+ }
+ if (dmix->u.dmix.bindings)
+ free(dmix->u.dmix.bindings);
+ free(dmix);
+ }
+ if (pcm)
+ snd_pcm_free(pcm);
+ return ret;
+}
+
+/*! \page pcm_plugins
+
+\section pcm_plugins_dshare Plugin: dshare
+
+This plugin provides sharing channels.
+
+\code
+pcm.name {
+ type dshare # Direct sharing
+ ipc_key INT # unique IPC key
+ ipc_key_add_uid BOOL # add current uid to unique IPC key
+ slave STR
+ # or
+ slave { # Slave definition
+ pcm STR # slave PCM name
+ # or
+ pcm { } # slave PCM definition
+ format STR # format definition
+ rate INT # rate definition
+ channels INT
+ period_time INT # in usec
+ # or
+ period_size INT # in bytes
+ buffer_time INT # in usec
+ # or
+ buffer_size INT # in bytes
+ periods INT # when buffer_size or buffer_time is not specified
+ }
+ bindings { # note: this is client independent!!!
+ N INT # maps slave channel to client channel N
+ }
+}
+\endcode
+
+\subsection pcm_plugins_hw_funcref Function reference
+
+<UL>
+ <LI>snd_pcm_dshare_open()
+ <LI>_snd_pcm_dshare_open()
+</UL>
+
+*/
+
+/**
+ * \brief Creates a new dmix PCM
+ * \param pcmp Returns created PCM handle
+ * \param name Name of PCM
+ * \param root Root configuration node
+ * \param conf Configuration node with dmix PCM description
+ * \param stream PCM Stream
+ * \param mode PCM Mode
+ * \warning Using of this function might be dangerous in the sense
+ * of compatibility reasons. The prototype might be freely
+ * changed in future.
+ */
+int _snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name,
+ snd_config_t *root, snd_config_t *conf,
+ snd_pcm_stream_t stream, int mode)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *slave = NULL, *bindings = NULL, *sconf;
+ struct slave_params params;
+ int bsize, psize, ipc_key_add_uid = 0;
+ key_t ipc_key = 0;
+ int err;
+ snd_config_for_each(i, next, conf) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ if (snd_pcm_conf_generic_id(id))
+ continue;
+ if (strcmp(id, "ipc_key") == 0) {
+ long key;
+ err = snd_config_get_integer(n, &key);
+ if (err < 0) {
+ SNDERR("The field ipc_key must be an integer type");
+ return err;
+ }
+ ipc_key = key;
+ continue;
+ }
+ if (strcmp(id, "ipc_key_add_uid") == 0) {
+ char *tmp;
+ err = snd_config_get_ascii(n, &tmp);
+ if (err < 0) {
+ SNDERR("The field ipc_key_add_uid must be a boolean type");
+ return err;
+ }
+ err = snd_config_get_bool_ascii(tmp);
+ free(tmp);
+ if (err < 0) {
+ SNDERR("The field ipc_key_add_uid must be a boolean type");
+ return err;
+ }
+ ipc_key_add_uid = err;
+ continue;
+ }
+ if (strcmp(id, "slave") == 0) {
+ slave = n;
+ continue;
+ }
+ if (strcmp(id, "bindings") == 0) {
+ bindings = n;
+ continue;
+ }
+ SNDERR("Unknown field %s", id);
+ return -EINVAL;
+ }
+ if (!slave) {
+ SNDERR("slave is not defined");
+ return -EINVAL;
+ }
+ if (ipc_key_add_uid)
+ ipc_key += getuid();
+ if (!ipc_key) {
+ SNDERR("Unique IPC key is not defined");
+ return -EINVAL;
+ }
+ /* the default settings, it might be invalid for some hardware */
+ params.format = SND_PCM_FORMAT_S16;
+ params.rate = 48000;
+ params.channels = 2;
+ params.period_time = 125000; /* 0.125 seconds */
+ params.buffer_time = -1;
+ bsize = psize = -1;
+ params.periods = 3;
+ err = snd_pcm_slave_conf(root, slave, &sconf, 8,
+ SND_PCM_HW_PARAM_FORMAT, 0, ¶ms.format,
+ SND_PCM_HW_PARAM_RATE, 0, ¶ms.rate,
+ SND_PCM_HW_PARAM_CHANNELS, 0, ¶ms.channels,
+ SND_PCM_HW_PARAM_PERIOD_TIME, 0, ¶ms.period_time,
+ SND_PCM_HW_PARAM_BUFFER_TIME, 0, ¶ms.buffer_time,
+ SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &bsize,
+ SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &psize,
+ SND_PCM_HW_PARAM_PERIODS, 0, ¶ms.periods);
+ if (err < 0)
+ return err;
+
+ /* sorry, limited features */
+ if (params.format != SND_PCM_FORMAT_S16 &&
+ params.format != SND_PCM_FORMAT_S32) {
+ SNDERR("invalid format, specify s16 or s32");
+ snd_config_delete(sconf);
+ return -EINVAL;
+ }
+
+ params.period_size = psize;
+ params.buffer_size = bsize;
+ err = snd_pcm_dshare_open(pcmp, name, ipc_key, ¶ms, bindings, root, sconf, stream, mode);
+ if (err < 0)
+ snd_config_delete(sconf);
+ return err;
+}
+#ifndef DOC_HIDDEN
+SND_DLSYM_BUILD_VERSION(_snd_pcm_dshare_open, SND_PCM_DLSYM_VERSION);
+#endif
--- /dev/null
+/**
+ * \file pcm/pcm_snoop.c
+ * \ingroup PCM_Plugins
+ * \brief PCM Capture Stream Snooping (dsnoop) Plugin Interface
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \date 2003
+ */
+/*
+ * PCM - Capture Stream Snooping
+ * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/shm.h>
+#include <sys/sem.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/mman.h>
+#include "pcm_direct.h"
+
+#ifndef PIC
+/* entry for static linking */
+const char *_snd_module_pcm_dsnoop = "";
+#endif
+
+/*
+ *
+ */
+
+static void snoop_areas(snd_pcm_direct_t *dmix,
+ const snd_pcm_channel_area_t *src_areas,
+ const snd_pcm_channel_area_t *dst_areas,
+ snd_pcm_uframes_t src_ofs,
+ snd_pcm_uframes_t dst_ofs,
+ snd_pcm_uframes_t size)
+{
+ if (dmix->interleaved) {
+ } else {
+ }
+}
+
+/*
+ * synchronize shm ring buffer with hardware
+ */
+static void snd_pcm_dsnoop_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t size)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_uframes_t appl_ptr, slave_appl_ptr, transfer;
+ const snd_pcm_channel_area_t *src_areas, *dst_areas;
+
+ /* get the start of update area */
+ appl_ptr = dmix->appl_ptr - size;
+ if (appl_ptr > pcm->boundary)
+ appl_ptr += pcm->boundary;
+ appl_ptr %= pcm->buffer_size;
+ /* add sample areas here */
+ src_areas = snd_pcm_mmap_areas(pcm);
+ dst_areas = snd_pcm_mmap_areas(dmix->spcm);
+ slave_appl_ptr = dmix->slave_appl_ptr % dmix->shmptr->s.buffer_size;
+ dmix->slave_appl_ptr += size;
+ dmix->slave_appl_ptr %= dmix->shmptr->s.boundary;
+ while (size > 0) {
+ transfer = appl_ptr + size > pcm->buffer_size ? pcm->buffer_size - appl_ptr : size;
+ transfer = slave_appl_ptr + transfer > dmix->shmptr->s.buffer_size ? dmix->shmptr->s.buffer_size - slave_appl_ptr : transfer;
+ size -= transfer;
+ snoop_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer);
+ slave_appl_ptr += transfer;
+ slave_appl_ptr %= dmix->shmptr->s.buffer_size;
+ appl_ptr += transfer;
+ appl_ptr %= pcm->buffer_size;
+ }
+}
+
+/*
+ * synchronize hardware pointer (hw_ptr) with ours
+ */
+static int snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail;
+ snd_pcm_sframes_t diff;
+
+ old_slave_hw_ptr = dmix->slave_hw_ptr;
+ slave_hw_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
+ diff = slave_hw_ptr - old_slave_hw_ptr;
+ if (diff == 0) /* fast path */
+ return 0;
+ if (diff < 0) {
+ slave_hw_ptr += dmix->shmptr->s.boundary;
+ diff = slave_hw_ptr - old_slave_hw_ptr;
+ }
+ dmix->hw_ptr += diff;
+ dmix->hw_ptr %= pcm->boundary;
+ // printf("sync ptr diff = %li\n", diff);
+ if (pcm->stop_threshold >= pcm->boundary) /* don't care */
+ return 0;
+ if ((avail = snd_pcm_mmap_playback_avail(pcm)) >= pcm->stop_threshold) {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ dmix->trigger_tstamp.tv_sec = tv.tv_sec;
+ dmix->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
+ dmix->state = SND_PCM_STATE_XRUN;
+ dmix->avail_max = avail;
+ return -EPIPE;
+ }
+ if (avail > dmix->avail_max)
+ dmix->avail_max = avail;
+ return 0;
+}
+
+/*
+ * plugin implementation
+ */
+
+static int snd_pcm_dsnoop_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
+{
+ /* value is cached for us in pcm->mode (SND_PCM_NONBLOCK flag) */
+ return 0;
+}
+
+static int snd_pcm_dsnoop_async(snd_pcm_t *pcm, int sig, pid_t pid)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ return snd_timer_async(dmix->timer, sig, pid);
+}
+
+static int snd_pcm_dsnoop_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ unsigned short events;
+ static snd_timer_read_t rbuf[5]; /* can be overwriten by multiple plugins, we don't need the value */
+
+ assert(pfds && nfds == 1 && revents);
+ events = pfds[0].revents;
+ if (events & POLLIN) {
+ events |= POLLOUT;
+ events &= ~POLLIN;
+ /* empty the timer read queue */
+ while (snd_timer_read(dmix->timer, &rbuf, sizeof(rbuf)) == sizeof(rbuf)) ;
+ }
+ *revents = events;
+ return 0;
+}
+
+static int snd_pcm_dsnoop_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
+{
+ // snd_pcm_direct_t *dmix = pcm->private_data;
+
+ memset(info, 0, sizeof(*info));
+ info->stream = pcm->stream;
+ info->card = -1;
+ /* FIXME: fill this with something more useful: we know the hardware name */
+ strncpy(info->id, pcm->name, sizeof(info->id));
+ strncpy(info->name, pcm->name, sizeof(info->name));
+ strncpy(info->subname, pcm->name, sizeof(info->subname));
+ info->subdevices_count = 1;
+ return 0;
+}
+
+static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_param_t var)
+{
+ return ¶ms->masks[var - SND_PCM_HW_PARAM_FIRST_MASK];
+}
+
+static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_param_t var)
+{
+ return ¶ms->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL];
+}
+
+static int hw_param_interval_refine_one(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_param_t var,
+ snd_pcm_hw_params_t *src)
+{
+ snd_interval_t *i;
+
+ if (!(params->rmask & (1<<var))) /* nothing to do? */
+ return 0;
+ i = hw_param_interval(params, var);
+ if (snd_interval_empty(i)) {
+ SNDERR("dmix interval %i empty?", (int)var);
+ return -EINVAL;
+ }
+ if (snd_interval_refine(i, hw_param_interval(src, var)))
+ params->cmask |= 1<<var;
+ return 0;
+}
+
+#undef REFINE_DEBUG
+
+static int snd_pcm_dsnoop_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_hw_params_t *hw_params = &dmix->shmptr->hw_params;
+ static snd_mask_t access = { .bits = {
+ (1<<SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) |
+ (1<<SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) |
+ (1<<SNDRV_PCM_ACCESS_RW_INTERLEAVED) |
+ (1<<SNDRV_PCM_ACCESS_RW_NONINTERLEAVED),
+ 0, 0, 0 } };
+ int err;
+
+#ifdef REFINE_DEBUG
+ snd_output_t *log;
+ snd_output_stdio_attach(&log, stderr, 0);
+ snd_output_puts(log, "DMIX REFINE (begin):\n");
+ snd_pcm_hw_params_dump(params, log);
+#endif
+ if (params->rmask & (1<<SND_PCM_HW_PARAM_ACCESS)) {
+ if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS))) {
+ SNDERR("dmix access mask empty?");
+ return -EINVAL;
+ }
+ if (snd_mask_refine(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS), &access))
+ params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
+ }
+ if (params->rmask & (1<<SND_PCM_HW_PARAM_FORMAT)) {
+ if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT))) {
+ SNDERR("dmix format mask empty?");
+ return -EINVAL;
+ }
+ if (snd_mask_refine_set(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT),
+ snd_mask_value(hw_param_mask(hw_params, SND_PCM_HW_PARAM_FORMAT))))
+ params->cmask |= 1<<SND_PCM_HW_PARAM_FORMAT;
+ }
+ //snd_mask_none(hw_param_mask(params, SND_PCM_HW_PARAM_SUBFORMAT));
+ if (params->rmask & (1<<SND_PCM_HW_PARAM_CHANNELS)) {
+ if (snd_interval_empty(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS))) {
+ SNDERR("dmix channels mask empty?");
+ return -EINVAL;
+ }
+ err = snd_interval_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS), dmix->u.dmix.channels);
+ if (err < 0)
+ return err;
+ }
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_RATE, hw_params);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_SIZE, hw_params);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_TIME, hw_params);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE, hw_params);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME, hw_params);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIODS, hw_params);
+ if (err < 0)
+ return err;
+#ifdef REFINE_DEBUG
+ snd_output_puts(log, "DMIX REFINE (end):\n");
+ snd_pcm_hw_params_dump(params, log);
+ snd_output_close(log);
+#endif
+ return 0;
+}
+
+static int snd_pcm_dsnoop_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED)
+{
+ /* values are cached in the pcm structure */
+
+ return 0;
+}
+
+static int snd_pcm_dsnoop_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+ /* values are cached in the pcm structure */
+ return 0;
+}
+
+static int snd_pcm_dsnoop_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params ATTRIBUTE_UNUSED)
+{
+ /* values are cached in the pcm structure */
+ return 0;
+}
+
+static int snd_pcm_dsnoop_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
+{
+ return snd_pcm_channel_info_shm(pcm, info, -1);
+}
+
+static int snd_pcm_dsnoop_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+
+ memset(status, 0, sizeof(*status));
+ status->state = dmix->state;
+ status->trigger_tstamp = dmix->trigger_tstamp;
+ status->tstamp = snd_pcm_hw_fast_tstamp(dmix->spcm);
+ status->avail = snd_pcm_mmap_playback_avail(pcm);
+ status->avail_max = status->avail > dmix->avail_max ? status->avail : dmix->avail_max;
+ dmix->avail_max = 0;
+ return 0;
+}
+
+static snd_pcm_state_t snd_pcm_dsnoop_state(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ return dmix->state;
+}
+
+static int snd_pcm_dsnoop_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ int err;
+
+ switch(dmix->state) {
+ case SNDRV_PCM_STATE_DRAINING:
+ case SNDRV_PCM_STATE_RUNNING:
+ err = snd_pcm_dsnoop_sync_ptr(pcm);
+ if (err < 0)
+ return err;
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ *delayp = snd_pcm_mmap_playback_hw_avail(pcm);
+ return 0;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ return -EBADFD;
+ }
+}
+
+static int snd_pcm_dsnoop_hwsync(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+
+ switch(dmix->state) {
+ case SNDRV_PCM_STATE_DRAINING:
+ case SNDRV_PCM_STATE_RUNNING:
+ return snd_pcm_dsnoop_sync_ptr(pcm);
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ return 0;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ return -EBADFD;
+ }
+}
+
+static int snd_pcm_dsnoop_prepare(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+
+ snd_pcm_direct_check_interleave(dmix, pcm);
+ // assert(pcm->boundary == dmix->shmptr->s.boundary); /* for sure */
+ dmix->state = SND_PCM_STATE_PREPARED;
+ dmix->appl_ptr = 0;
+ dmix->hw_ptr = 0;
+ return 0;
+}
+
+static int snd_pcm_dsnoop_reset(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ dmix->hw_ptr %= pcm->period_size;
+ dmix->appl_ptr = dmix->hw_ptr;
+ dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
+ return 0;
+}
+
+static int snd_pcm_dsnoop_start(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_sframes_t avail;
+ struct timeval tv;
+ int err;
+
+ if (dmix->state != SND_PCM_STATE_PREPARED)
+ return -EBADFD;
+ err = snd_timer_start(dmix->timer);
+ if (err < 0)
+ return err;
+ dmix->state = SND_PCM_STATE_RUNNING;
+ dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
+ avail = snd_pcm_mmap_playback_hw_avail(pcm);
+ if (avail < 0)
+ return 0;
+ if (avail > (snd_pcm_sframes_t)pcm->buffer_size)
+ avail = pcm->buffer_size;
+ snd_pcm_dsnoop_sync_area(pcm, avail);
+ gettimeofday(&tv, 0);
+ dmix->trigger_tstamp.tv_sec = tv.tv_sec;
+ dmix->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L;
+ return 0;
+}
+
+static int snd_pcm_dsnoop_drop(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ if (dmix->state == SND_PCM_STATE_OPEN)
+ return -EBADFD;
+ snd_timer_stop(dmix->timer);
+ dmix->state = SND_PCM_STATE_SETUP;
+ return 0;
+}
+
+static int snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_uframes_t stop_threshold;
+ int err;
+
+ if (dmix->state == SND_PCM_STATE_OPEN)
+ return -EBADFD;
+ stop_threshold = pcm->stop_threshold;
+ if (pcm->stop_threshold > pcm->buffer_size)
+ pcm->stop_threshold = pcm->buffer_size;
+ while (dmix->state == SND_PCM_STATE_RUNNING) {
+ err = snd_pcm_dsnoop_sync_ptr(pcm);
+ if (err < 0)
+ break;
+ if (pcm->mode & SND_PCM_NONBLOCK)
+ return -EAGAIN;
+ snd_pcm_wait(pcm, -1);
+ }
+ pcm->stop_threshold = stop_threshold;
+ return snd_pcm_dsnoop_drop(pcm);
+}
+
+static int snd_pcm_dsnoop_pause(snd_pcm_t *pcm, int enable)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ if (enable) {
+ if (dmix->state != SND_PCM_STATE_RUNNING)
+ return -EBADFD;
+ dmix->state = SND_PCM_STATE_PAUSED;
+ snd_timer_stop(dmix->timer);
+ } else {
+ if (dmix->state != SND_PCM_STATE_PAUSED)
+ return -EBADFD;
+ dmix->state = SND_PCM_STATE_RUNNING;
+ snd_timer_start(dmix->timer);
+ }
+ return 0;
+}
+
+static snd_pcm_sframes_t snd_pcm_dsnoop_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
+{
+ /* FIXME: substract samples from the mix ring buffer, too? */
+ snd_pcm_mmap_appl_backward(pcm, frames);
+ return frames;
+}
+
+static snd_pcm_sframes_t snd_pcm_dsnoop_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
+{
+ snd_pcm_sframes_t avail;
+
+ avail = snd_pcm_mmap_avail(pcm);
+ if (avail < 0)
+ return 0;
+ if (frames > (snd_pcm_uframes_t)avail)
+ frames = avail;
+ snd_pcm_mmap_appl_forward(pcm, frames);
+ return frames;
+}
+
+static int snd_pcm_dsnoop_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+ // snd_pcm_direct_t *dmix = pcm->private_data;
+ // FIXME
+ return 0;
+}
+
+static snd_pcm_sframes_t snd_pcm_dsnoop_readi(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
+{
+ return -ENODEV;
+}
+
+static snd_pcm_sframes_t snd_pcm_dsnoop_readn(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
+{
+ return -ENODEV;
+}
+
+static int snd_pcm_dsnoop_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static int snd_pcm_dsnoop_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static int snd_pcm_dsnoop_close(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+
+ if (dmix->timer)
+ snd_timer_close(dmix->timer);
+ snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
+ snd_pcm_close(dmix->spcm);
+ if (dmix->server)
+ snd_pcm_direct_server_discard(dmix);
+ if (dmix->client)
+ snd_pcm_direct_client_discard(dmix);
+ if (snd_pcm_direct_shm_discard(dmix) > 0) {
+ if (snd_pcm_direct_semaphore_discard(dmix) < 0)
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
+ } else {
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
+ }
+ if (dmix->u.dmix.bindings)
+ free(dmix->u.dmix.bindings);
+ pcm->private_data = NULL;
+ free(dmix);
+ return 0;
+}
+
+static snd_pcm_sframes_t snd_pcm_dsnoop_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t size)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ int err;
+
+ snd_pcm_mmap_appl_forward(pcm, size);
+ if (dmix->state == SND_PCM_STATE_RUNNING) {
+ err = snd_pcm_dsnoop_sync_ptr(pcm);
+ if (err < 0)
+ return err;
+ /* ok, we commit the changes after the validation of area */
+ /* it's intended, although the result might be crappy */
+ snd_pcm_dsnoop_sync_area(pcm, size);
+ }
+ return size;
+}
+
+static snd_pcm_sframes_t snd_pcm_dsnoop_avail_update(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ int err;
+
+ if (dmix->state == SND_PCM_STATE_RUNNING) {
+ err = snd_pcm_dsnoop_sync_ptr(pcm);
+ if (err < 0)
+ return err;
+ }
+ return snd_pcm_mmap_playback_avail(pcm);
+}
+
+static void snd_pcm_dsnoop_dump(snd_pcm_t *pcm, snd_output_t *out)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+
+ snd_output_printf(out, "Direct Stream Mixing PCM\n");
+ if (pcm->setup) {
+ snd_output_printf(out, "\nIts setup is:\n");
+ snd_pcm_dump_setup(pcm, out);
+ }
+ if (dmix->spcm)
+ snd_pcm_dump(dmix->spcm, out);
+}
+
+static snd_pcm_ops_t snd_pcm_dsnoop_ops = {
+ close: snd_pcm_dsnoop_close,
+ info: snd_pcm_dsnoop_info,
+ hw_refine: snd_pcm_dsnoop_hw_refine,
+ hw_params: snd_pcm_dsnoop_hw_params,
+ hw_free: snd_pcm_dsnoop_hw_free,
+ sw_params: snd_pcm_dsnoop_sw_params,
+ channel_info: snd_pcm_dsnoop_channel_info,
+ dump: snd_pcm_dsnoop_dump,
+ nonblock: snd_pcm_dsnoop_nonblock,
+ async: snd_pcm_dsnoop_async,
+ poll_revents: snd_pcm_dsnoop_poll_revents,
+ mmap: snd_pcm_dsnoop_mmap,
+ munmap: snd_pcm_dsnoop_munmap,
+};
+
+static snd_pcm_fast_ops_t snd_pcm_dsnoop_fast_ops = {
+ status: snd_pcm_dsnoop_status,
+ state: snd_pcm_dsnoop_state,
+ hwsync: snd_pcm_dsnoop_hwsync,
+ delay: snd_pcm_dsnoop_delay,
+ prepare: snd_pcm_dsnoop_prepare,
+ reset: snd_pcm_dsnoop_reset,
+ start: snd_pcm_dsnoop_start,
+ drop: snd_pcm_dsnoop_drop,
+ drain: snd_pcm_dsnoop_drain,
+ pause: snd_pcm_dsnoop_pause,
+ rewind: snd_pcm_dsnoop_rewind,
+ forward: snd_pcm_dsnoop_forward,
+ resume: snd_pcm_dsnoop_resume,
+ writei: snd_pcm_mmap_writei,
+ writen: snd_pcm_mmap_writen,
+ readi: snd_pcm_dsnoop_readi,
+ readn: snd_pcm_dsnoop_readn,
+ avail_update: snd_pcm_dsnoop_avail_update,
+ mmap_commit: snd_pcm_dsnoop_mmap_commit,
+};
+
+/*
+ * parse the channel map
+ * id == client channel
+ * value == slave's channel
+ */
+static int parse_bindings(snd_pcm_direct_t *dmix, snd_config_t *cfg)
+{
+ snd_config_iterator_t i, next;
+ unsigned int chn, chn1, count = 0;
+ int err;
+
+ dmix->u.dmix.channels = UINT_MAX;
+ if (cfg == NULL)
+ return 0;
+ if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+ SNDERR("invalid type for bindings");
+ return -EINVAL;
+ }
+ snd_config_for_each(i, next, cfg) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id;
+ long cchannel;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ err = safe_strtol(id, &cchannel);
+ if (err < 0 || cchannel < 0) {
+ SNDERR("invalid client channel in binding: %s\n", id);
+ return -EINVAL;
+ }
+ if ((unsigned)cchannel > count)
+ count = cchannel + 1;
+ }
+ if (count == 0)
+ return 0;
+ if (count > 1024) {
+ SNDERR("client channel out of range");
+ return -EINVAL;
+ }
+ dmix->u.dmix.bindings = malloc(count * sizeof(unsigned int));
+ for (chn = 0; chn < count; chn++)
+ dmix->u.dmix.bindings[chn] = UINT_MAX; /* don't route */
+ snd_config_for_each(i, next, cfg) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id;
+ long cchannel, schannel;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ safe_strtol(id, &cchannel);
+ if (snd_config_get_integer(n, &schannel) < 0) {
+ SNDERR("unable to get slave channel (should be integer type) in binding: %s\n", id);
+ return -EINVAL;
+ }
+ dmix->u.dmix.bindings[cchannel] = schannel;
+ }
+ for (chn = 0; chn < count; chn++) {
+ for (chn1 = 0; chn1 < count; chn1++) {
+ if (chn == chn1)
+ continue;
+ if (dmix->u.dmix.bindings[chn] == dmix->u.dmix.bindings[chn1]) {
+ SNDERR("unable to route channels %d,%d to same destination %d", chn, chn1, dmix->u.dmix.bindings[chn]);
+ return -EINVAL;
+ }
+ }
+ }
+ dmix->u.dmix.channels = count;
+ return 0;
+}
+
+/**
+ * \brief Creates a new dmix PCM
+ * \param pcmp Returns created PCM handle
+ * \param name Name of PCM
+ * \param ipc_key IPC key for semaphore and shared memory
+ * \param params Parameters for slave
+ * \param root Configuration root
+ * \param sconf Slave configuration
+ * \param stream PCM Direction (stream)
+ * \param mode PCM Mode
+ * \retval zero on success otherwise a negative error code
+ * \warning Using of this function might be dangerous in the sense
+ * of compatibility reasons. The prototype might be freely
+ * changed in future.
+ */
+int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
+ key_t ipc_key, struct slave_params *params,
+ snd_config_t *bindings,
+ snd_config_t *root, snd_config_t *sconf,
+ snd_pcm_stream_t stream, int mode)
+{
+ snd_pcm_t *pcm = NULL, *spcm = NULL;
+ snd_pcm_direct_t *dmix = NULL;
+ int ret, first_instance;
+
+ assert(pcmp);
+
+ if (stream != SND_PCM_STREAM_PLAYBACK) {
+ SNDERR("The dmix plugin supports only playback stream");
+ return -EINVAL;
+ }
+
+ dmix = calloc(1, sizeof(snd_pcm_direct_t));
+ if (!dmix) {
+ ret = -ENOMEM;
+ goto _err;
+ }
+
+ ret = parse_bindings(dmix, bindings);
+ if (ret < 0)
+ goto _err;
+
+ dmix->ipc_key = ipc_key;
+ dmix->semid = -1;
+ dmix->shmid = -1;
+
+ ret = snd_pcm_new(&pcm, SND_PCM_TYPE_DSNOOP, name, stream, mode);
+ if (ret < 0)
+ goto _err;
+
+ ret = snd_pcm_direct_semaphore_create_or_connect(dmix);
+ if (ret < 0) {
+ SNDERR("unable to create IPC semaphore");
+ goto _err;
+ }
+
+ ret = snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
+ if (ret < 0) {
+ snd_pcm_direct_semaphore_discard(dmix);
+ goto _err;
+ }
+
+ first_instance = ret = snd_pcm_direct_shm_create_or_connect(dmix);
+ if (ret < 0) {
+ SNDERR("unable to create IPC shm instance");
+ goto _err;
+ }
+
+ pcm->ops = &snd_pcm_dsnoop_ops;
+ pcm->fast_ops = &snd_pcm_dsnoop_fast_ops;
+ pcm->private_data = dmix;
+ dmix->state = SND_PCM_STATE_OPEN;
+
+ if (first_instance) {
+ ret = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
+ if (ret < 0) {
+ SNDERR("unable to open slave");
+ goto _err;
+ }
+
+ if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
+ SNDERR("dmix plugin can be only connected to hw plugin");
+ goto _err;
+ }
+
+ ret = snd_pcm_direct_initialize_slave(dmix, spcm, params);
+ if (ret < 0) {
+ SNDERR("unable to initialize slave");
+ goto _err;
+ }
+
+ dmix->spcm = spcm;
+
+ ret = snd_pcm_direct_server_create(dmix);
+ if (ret < 0) {
+ SNDERR("unable to create server");
+ goto _err;
+ }
+
+ dmix->shmptr->type = spcm->type;
+ } else {
+ ret = snd_pcm_direct_client_connect(dmix);
+ if (ret < 0) {
+ SNDERR("unable to connect client");
+ return ret;
+ }
+
+ ret = snd_pcm_hw_open_fd(&spcm, "dmix_client", dmix->hw_fd, 0);
+ if (ret < 0) {
+ SNDERR("unable to open hardware");
+ goto _err;
+ }
+
+ spcm->donot_close = 1;
+ spcm->setup = 1;
+ spcm->buffer_size = dmix->shmptr->s.buffer_size;
+ spcm->sample_bits = dmix->shmptr->s.sample_bits;
+ spcm->channels = dmix->shmptr->s.channels;
+ spcm->format = dmix->shmptr->s.format;
+ spcm->boundary = dmix->shmptr->s.boundary;
+ ret = snd_pcm_mmap(spcm);
+ if (ret < 0) {
+ SNDERR("unable to mmap channels");
+ goto _err;
+ }
+ dmix->spcm = spcm;
+ }
+
+ ret = snd_pcm_direct_initialize_poll_fd(dmix);
+ if (ret < 0) {
+ SNDERR("unable to initialize poll_fd");
+ goto _err;
+ }
+
+ pcm->poll_fd = dmix->poll_fd;
+ pcm->poll_events = POLLIN; /* it's different than other plugins */
+
+ pcm->mmap_rw = 1;
+ snd_pcm_set_hw_ptr(pcm, &dmix->hw_ptr, -1, 0);
+ snd_pcm_set_appl_ptr(pcm, &dmix->appl_ptr, -1, 0);
+
+ if (dmix->u.dmix.channels == UINT_MAX)
+ dmix->u.dmix.channels = dmix->shmptr->s.channels;
+
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
+
+ *pcmp = pcm;
+ return 0;
+
+ _err:
+ if (dmix) {
+ if (dmix->timer)
+ snd_timer_close(dmix->timer);
+ if (dmix->server)
+ snd_pcm_direct_server_discard(dmix);
+ if (dmix->client)
+ snd_pcm_direct_client_discard(dmix);
+ if (spcm)
+ snd_pcm_close(spcm);
+ if (dmix->shmid >= 0) {
+ if (snd_pcm_direct_shm_discard(dmix) > 0) {
+ if (dmix->semid >= 0) {
+ if (snd_pcm_direct_semaphore_discard(dmix) < 0)
+ snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
+ }
+ }
+ }
+ if (dmix->u.dmix.bindings)
+ free(dmix->u.dmix.bindings);
+ free(dmix);
+ }
+ if (pcm)
+ snd_pcm_free(pcm);
+ return ret;
+}
+
+/*! \page pcm_plugins
+
+\section pcm_plugins_snoop Plugin: dsnoop
+
+This plugin splits one capture stream to more.
+
+\code
+pcm.name {
+ type dsnoop # Direct snoop
+ ipc_key INT # unique IPC key
+ ipc_key_add_uid BOOL # add current uid to unique IPC key
+ slave STR
+ # or
+ slave { # Slave definition
+ pcm STR # slave PCM name
+ # or
+ pcm { } # slave PCM definition
+ format STR # format definition
+ rate INT # rate definition
+ channels INT
+ period_time INT # in usec
+ # or
+ period_size INT # in bytes
+ buffer_time INT # in usec
+ # or
+ buffer_size INT # in bytes
+ periods INT # when buffer_size or buffer_time is not specified
+ }
+ bindings { # note: this is client independent!!!
+ N INT # maps slave channel to client channel N
+ }
+}
+\endcode
+
+\subsection pcm_plugins_hw_funcref Function reference
+
+<UL>
+ <LI>snd_pcm_dsnoop_open()
+ <LI>_snd_pcm_dsnoop_open()
+</UL>
+
+*/
+
+/**
+ * \brief Creates a new dmix PCM
+ * \param pcmp Returns created PCM handle
+ * \param name Name of PCM
+ * \param root Root configuration node
+ * \param conf Configuration node with dmix PCM description
+ * \param stream PCM Stream
+ * \param mode PCM Mode
+ * \warning Using of this function might be dangerous in the sense
+ * of compatibility reasons. The prototype might be freely
+ * changed in future.
+ */
+int _snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
+ snd_config_t *root, snd_config_t *conf,
+ snd_pcm_stream_t stream, int mode)
+{
+ snd_config_iterator_t i, next;
+ snd_config_t *slave = NULL, *bindings = NULL, *sconf;
+ struct slave_params params;
+ int bsize, psize, ipc_key_add_uid = 0;
+ key_t ipc_key = 0;
+ int err;
+ snd_config_for_each(i, next, conf) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ if (snd_pcm_conf_generic_id(id))
+ continue;
+ if (strcmp(id, "ipc_key") == 0) {
+ long key;
+ err = snd_config_get_integer(n, &key);
+ if (err < 0) {
+ SNDERR("The field ipc_key must be an integer type");
+ return err;
+ }
+ ipc_key = key;
+ continue;
+ }
+ if (strcmp(id, "ipc_key_add_uid") == 0) {
+ char *tmp;
+ err = snd_config_get_ascii(n, &tmp);
+ if (err < 0) {
+ SNDERR("The field ipc_key_add_uid must be a boolean type");
+ return err;
+ }
+ err = snd_config_get_bool_ascii(tmp);
+ free(tmp);
+ if (err < 0) {
+ SNDERR("The field ipc_key_add_uid must be a boolean type");
+ return err;
+ }
+ ipc_key_add_uid = err;
+ continue;
+ }
+ if (strcmp(id, "slave") == 0) {
+ slave = n;
+ continue;
+ }
+ if (strcmp(id, "bindings") == 0) {
+ bindings = n;
+ continue;
+ }
+ SNDERR("Unknown field %s", id);
+ return -EINVAL;
+ }
+ if (!slave) {
+ SNDERR("slave is not defined");
+ return -EINVAL;
+ }
+ if (ipc_key_add_uid)
+ ipc_key += getuid();
+ if (!ipc_key) {
+ SNDERR("Unique IPC key is not defined");
+ return -EINVAL;
+ }
+ /* the default settings, it might be invalid for some hardware */
+ params.format = SND_PCM_FORMAT_S16;
+ params.rate = 48000;
+ params.channels = 2;
+ params.period_time = 125000; /* 0.125 seconds */
+ params.buffer_time = -1;
+ bsize = psize = -1;
+ params.periods = 3;
+ err = snd_pcm_slave_conf(root, slave, &sconf, 8,
+ SND_PCM_HW_PARAM_FORMAT, 0, ¶ms.format,
+ SND_PCM_HW_PARAM_RATE, 0, ¶ms.rate,
+ SND_PCM_HW_PARAM_CHANNELS, 0, ¶ms.channels,
+ SND_PCM_HW_PARAM_PERIOD_TIME, 0, ¶ms.period_time,
+ SND_PCM_HW_PARAM_BUFFER_TIME, 0, ¶ms.buffer_time,
+ SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &bsize,
+ SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &psize,
+ SND_PCM_HW_PARAM_PERIODS, 0, ¶ms.periods);
+ if (err < 0)
+ return err;
+
+ /* sorry, limited features */
+ if (params.format != SND_PCM_FORMAT_S16 &&
+ params.format != SND_PCM_FORMAT_S32) {
+ SNDERR("invalid format, specify s16 or s32");
+ snd_config_delete(sconf);
+ return -EINVAL;
+ }
+
+ params.period_size = psize;
+ params.buffer_size = bsize;
+ err = snd_pcm_dsnoop_open(pcmp, name, ipc_key, ¶ms, bindings, root, sconf, stream, mode);
+ if (err < 0)
+ snd_config_delete(sconf);
+ return err;
+}
+#ifndef DOC_HIDDEN
+SND_DLSYM_BUILD_VERSION(_snd_pcm_dsnoop_open, SND_PCM_DLSYM_VERSION);
+#endif
extern const char *_snd_module_pcm_lfloat;
extern const char *_snd_module_pcm_ladspa;
extern const char *_snd_module_pcm_dmix;
+extern const char *_snd_module_pcm_dsnoop;
+extern const char *_snd_module_pcm_dshare;
static const char **snd_pcm_open_objects[] = {
&_snd_module_pcm_adpcm,
&_snd_module_pcm_shm,
&_snd_module_pcm_lfloat,
&_snd_module_pcm_ladspa,
- &_snd_module_pcm_dmix
+ &_snd_module_pcm_dmix,
+ &_snd_module_pcm_dsnoop,
+ &_snd_module_pcm_dshare
};
void *snd_pcm_open_symbols(void)