/*
* PCM - Direct Stream Mixing
- * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
*
*
* This library is free software; you can redistribute it and/or modify
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <grp.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
-#include <sys/poll.h>
+#include <poll.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h>
return 0;
}
+#define SND_PCM_DIRECT_MAGIC (0xa15ad300 + sizeof(snd_pcm_direct_share_t))
+
/*
* global shared memory area
*/
int snd_pcm_direct_shm_create_or_connect(snd_pcm_direct_t *dmix)
{
struct shmid_ds buf;
- int tmpid, err;
+ int tmpid, err, first_instance = 0;
retryget:
dmix->shmid = shmget(dmix->ipc_key, sizeof(snd_pcm_direct_share_t),
- IPC_CREAT | dmix->ipc_perm);
+ dmix->ipc_perm);
+ if (dmix->shmid < 0 && errno == ENOENT) {
+ if ((dmix->shmid = shmget(dmix->ipc_key, sizeof(snd_pcm_direct_share_t),
+ IPC_CREAT | IPC_EXCL | dmix->ipc_perm)) != -1)
+ first_instance = 1;
+ else if (errno == EEXIST)
+ goto retryget;
+ }
err = -errno;
- if (dmix->shmid < 0){
+ if (dmix->shmid < 0) {
if (errno == EINVAL)
if ((tmpid = shmget(dmix->ipc_key, 0, dmix->ipc_perm)) != -1)
if (!shmctl(tmpid, IPC_STAT, &buf))
}
dmix->shmptr = shmat(dmix->shmid, 0, 0);
if (dmix->shmptr == (void *) -1) {
+ err = -errno;
snd_pcm_direct_shm_discard(dmix);
- return -errno;
+ return err;
}
mlock(dmix->shmptr, sizeof(snd_pcm_direct_share_t));
if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) {
+ err = -errno;
snd_pcm_direct_shm_discard(dmix);
- return -errno;
+ return err;
}
- if (buf.shm_nattch == 1) { /* we're the first user, clear the segment */
+ if (first_instance) { /* we're the first user, clear the segment */
memset(dmix->shmptr, 0, sizeof(snd_pcm_direct_share_t));
if (dmix->ipc_gid >= 0) {
buf.shm_perm.gid = dmix->ipc_gid;
shmctl(dmix->shmid, IPC_SET, &buf);
}
+ dmix->shmptr->magic = SND_PCM_DIRECT_MAGIC;
return 1;
+ } else {
+ if (dmix->shmptr->magic != SND_PCM_DIRECT_MAGIC) {
+ snd_pcm_direct_shm_discard(dmix);
+ return -EINVAL;
+ }
}
return 0;
}
}
/* empty the timer read queue */
-void snd_pcm_direct_clear_timer_queue(snd_pcm_direct_t *dmix)
+int snd_pcm_direct_clear_timer_queue(snd_pcm_direct_t *dmix)
{
+ int changed = 0;
if (dmix->timer_need_poll) {
while (poll(&dmix->timer_fd, 1, 0) > 0) {
+ changed++;
/* we don't need the value */
if (dmix->tread) {
snd_timer_tread_t rbuf[4];
snd_timer_tread_t rbuf[4];
int len;
while ((len = snd_timer_read(dmix->timer, rbuf,
- sizeof(rbuf))) > 0 &&
+ sizeof(rbuf))) > 0
+ && (++changed) &&
len != sizeof(rbuf[0]))
;
} else {
snd_timer_read_t rbuf;
while (snd_timer_read(dmix->timer, &rbuf, sizeof(rbuf)) > 0)
- ;
+ changed++;
}
}
+ return changed;
}
int snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix)
{
snd_timer_stop(dmix->timer);
- snd_pcm_direct_clear_timer_queue(dmix);
return 0;
}
+/*
+ * Recover slave on XRUN.
+ * Even if direct plugins disable xrun detection, there might be an xrun
+ * raised directly by some drivers.
+ * The first client recovers slave pcm.
+ * Each client needs to execute sw xrun handling afterwards
+ */
+int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
+{
+ int ret;
+ int semerr;
+
+ semerr = snd_pcm_direct_semaphore_down(direct,
+ DIRECT_IPC_SEM_CLIENT);
+ if (semerr < 0) {
+ SNDERR("SEMDOWN FAILED with err %d", semerr);
+ return semerr;
+ }
+
+ if (snd_pcm_state(direct->spcm) != SND_PCM_STATE_XRUN) {
+ /* ignore... someone else already did recovery */
+ semerr = snd_pcm_direct_semaphore_up(direct,
+ DIRECT_IPC_SEM_CLIENT);
+ if (semerr < 0) {
+ SNDERR("SEMUP FAILED with err %d", semerr);
+ return semerr;
+ }
+ return 0;
+ }
+
+ ret = snd_pcm_prepare(direct->spcm);
+ if (ret < 0) {
+ SNDERR("recover: unable to prepare slave");
+ semerr = snd_pcm_direct_semaphore_up(direct,
+ DIRECT_IPC_SEM_CLIENT);
+ if (semerr < 0) {
+ SNDERR("SEMUP FAILED with err %d", semerr);
+ return semerr;
+ }
+ return ret;
+ }
+
+ if (direct->type == SND_PCM_TYPE_DSHARE) {
+ const snd_pcm_channel_area_t *dst_areas;
+ dst_areas = snd_pcm_mmap_areas(direct->spcm);
+ snd_pcm_areas_silence(dst_areas, 0, direct->spcm->channels,
+ direct->spcm->buffer_size,
+ direct->spcm->format);
+ }
+
+ ret = snd_pcm_start(direct->spcm);
+ if (ret < 0) {
+ SNDERR("recover: unable to start slave");
+ semerr = snd_pcm_direct_semaphore_up(direct,
+ DIRECT_IPC_SEM_CLIENT);
+ if (semerr < 0) {
+ SNDERR("SEMUP FAILED with err %d", semerr);
+ return semerr;
+ }
+ return ret;
+ }
+ direct->shmptr->s.recoveries++;
+ semerr = snd_pcm_direct_semaphore_up(direct,
+ DIRECT_IPC_SEM_CLIENT);
+ if (semerr < 0) {
+ SNDERR("SEMUP FAILED with err %d", semerr);
+ return semerr;
+ }
+ return 0;
+}
+
+/*
+ * enter xrun state, if slave xrun occurred
+ * @return: 0 - no xrun >0: xrun happened
+ */
+int snd_pcm_direct_client_chk_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm)
+{
+ if (direct->shmptr->s.recoveries != direct->recoveries) {
+ /* no matter how many xruns we missed -
+ * so don't increment but just update to actual counter
+ */
+ direct->recoveries = direct->shmptr->s.recoveries;
+ pcm->fast_ops->drop(pcm);
+ /* trigger_tstamp update is missing in drop callbacks */
+ gettimestamp(&direct->trigger_tstamp, pcm->tstamp_type);
+ /* no timer clear:
+ * if slave already entered xrun again the event is lost.
+ * snd_pcm_direct_clear_timer_queue(direct);
+ */
+ direct->state = SND_PCM_STATE_XRUN;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * This is the only operation guaranteed to be called before entering poll().
+ * Direct plugins use fd of snd_timer to poll on, these timers do NOT check
+ * state of substream in kernel by intention.
+ * Only the enter to xrun might be notified once (SND_TIMER_EVENT_MSTOP).
+ * If xrun event was not correctly handled or was ignored it will never be
+ * evaluated again afterwards.
+ * This will result in snd_pcm_wait() always returning timeout.
+ * In contrast poll() on pcm hardware checks ALSA state and will immediately
+ * return POLLERR on XRUN.
+ *
+ * To prevent timeout and applications endlessly spinning without xrun
+ * detected we add a state check here which may trigger the xrun sequence.
+ *
+ * return count of filled descriptors or negative error code
+ */
+int snd_pcm_direct_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds,
+ unsigned int space)
+{
+ if (pcm->poll_fd < 0) {
+ SNDMSG("poll_fd < 0");
+ return -EIO;
+ }
+ if (space >= 1 && pfds) {
+ pfds->fd = pcm->poll_fd;
+ pfds->events = pcm->poll_events | POLLERR | POLLNVAL;
+ } else {
+ return 0;
+ }
+
+ /* this will also evaluate slave state and enter xrun if necessary */
+ /* using __snd_pcm_state() since this function is called inside lock */
+ switch (__snd_pcm_state(pcm)) {
+ case SND_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ break;
+ }
+ return 1;
+}
+
int snd_pcm_direct_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{
snd_pcm_direct_t *dmix = pcm->private_data;
int empty = 0;
assert(pfds && nfds == 1 && revents);
+
+timer_changed:
events = pfds[0].revents;
if (events & POLLIN) {
snd_pcm_uframes_t avail;
- snd_pcm_avail_update(pcm);
+ __snd_pcm_avail_update(pcm);
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
events |= POLLOUT;
events &= ~POLLIN;
}
switch (snd_pcm_state(dmix->spcm)) {
case SND_PCM_STATE_XRUN:
+ /* recover slave and update client state to xrun
+ * before returning POLLERR
+ */
+ snd_pcm_direct_slave_recover(dmix);
+ snd_pcm_direct_client_chk_xrun(dmix, pcm);
+ /* fallthrough */
case SND_PCM_STATE_SUSPENDED:
+ case SND_PCM_STATE_SETUP:
events |= POLLERR;
break;
default:
if (empty) {
- snd_pcm_direct_clear_timer_queue(dmix);
+ /* here we have a race condition:
+ * if period event arrived after the avail_update call
+ * above we might clear this event with the following
+ * clear_timer_queue.
+ * There is no way to do this in atomic manner, so we
+ * need to recheck avail_update if we successfully
+ * cleared a poll event.
+ */
+ if (snd_pcm_direct_clear_timer_queue(dmix))
+ goto timer_changed;
events &= ~(POLLOUT|POLLIN);
/* additional check */
- switch (snd_pcm_state(pcm)) {
+ switch (__snd_pcm_state(pcm)) {
case SND_PCM_STATE_XRUN:
case SND_PCM_STATE_SUSPENDED:
+ case SND_PCM_STATE_SETUP:
events |= POLLERR;
break;
default:
int snd_pcm_direct_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
{
- // snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_direct_t *dmix = pcm->private_data;
+
+ if (dmix->spcm && !dmix->shmptr->use_server)
+ return snd_pcm_info(dmix->spcm, info);
memset(info, 0, sizeof(*info));
info->stream = pcm->stream;
info->card = -1;
/* FIXME: fill this with something more useful: we know the hardware name */
if (pcm->name) {
- strncpy((char *)info->id, pcm->name, sizeof(info->id));
- strncpy((char *)info->name, pcm->name, sizeof(info->name));
- strncpy((char *)info->subname, pcm->name, sizeof(info->subname));
+ snd_strlcpy((char *)info->id, pcm->name, sizeof(info->id));
+ snd_strlcpy((char *)info->name, pcm->name, sizeof(info->name));
+ snd_strlcpy((char *)info->subname, pcm->name, sizeof(info->subname));
}
info->subdevices_count = 1;
return 0;
return hw_param_interval_refine_one(params, var, &t);
}
+/* this code is used 'as-is' from the alsa kernel code */
+static int snd_interval_step(struct snd_interval *i, unsigned int min,
+ unsigned int step)
+{
+ unsigned int n;
+ int changed = 0;
+ n = (i->min - min) % step;
+ if (n != 0 || i->openmin) {
+ i->min += step - n;
+ changed = 1;
+ }
+ n = (i->max - min) % step;
+ if (n != 0 || i->openmax) {
+ i->max -= n;
+ changed = 1;
+ }
+ if (snd_interval_checkempty(i)) {
+ i->empty = 1;
+ return -EINVAL;
+ }
+ return changed;
+}
+
#undef REFINE_DEBUG
int snd_pcm_direct_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_direct_t *dshare = pcm->private_data;
- static snd_mask_t access = { .bits = {
+ static const snd_mask_t access = { .bits = {
(1<<SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) |
(1<<SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) |
(1<<SNDRV_PCM_ACCESS_RW_INTERLEAVED) |
&dshare->shmptr->hw.rate);
if (err < 0)
return err;
- err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE,
- &dshare->shmptr->hw.period_size);
- if (err < 0)
- return err;
- err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME,
- &dshare->shmptr->hw.period_time);
- if (err < 0)
- return err;
+
if (dshare->max_periods < 0) {
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE,
+ &dshare->shmptr->hw.period_size);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME,
+ &dshare->shmptr->hw.period_time);
+ if (err < 0)
+ return err;
err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_SIZE,
&dshare->shmptr->hw.buffer_size);
if (err < 0)
} else if (params->rmask & ((1<<SND_PCM_HW_PARAM_PERIODS)|
(1<<SND_PCM_HW_PARAM_BUFFER_BYTES)|
(1<<SND_PCM_HW_PARAM_BUFFER_SIZE)|
- (1<<SND_PCM_HW_PARAM_BUFFER_TIME))) {
+ (1<<SND_PCM_HW_PARAM_BUFFER_TIME)|
+ (1<<SND_PCM_HW_PARAM_PERIOD_TIME)|
+ (1<<SND_PCM_HW_PARAM_PERIOD_SIZE)|
+ (1<<SND_PCM_HW_PARAM_PERIOD_BYTES))) {
+ snd_interval_t period_size = dshare->shmptr->hw.period_size;
+ snd_interval_t period_time = dshare->shmptr->hw.period_time;
int changed;
unsigned int max_periods = dshare->max_periods;
if (max_periods < 2)
max_periods = dshare->slave_buffer_size / dshare->slave_period_size;
+
+ /* make sure buffer size does not exceed slave buffer size */
+ err = hw_param_interval_refine_minmax(params, SND_PCM_HW_PARAM_BUFFER_SIZE,
+ 2 * dshare->slave_period_size, dshare->slave_buffer_size);
+ if (err < 0)
+ return err;
+ if (dshare->var_periodsize) {
+ /* more tolerant settings... */
+ if (dshare->shmptr->hw.buffer_size.max / 2 > period_size.max)
+ period_size.max = dshare->shmptr->hw.buffer_size.max / 2;
+ if (dshare->shmptr->hw.buffer_time.max / 2 > period_time.max)
+ period_time.max = dshare->shmptr->hw.buffer_time.max / 2;
+ }
+
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE,
+ &period_size);
+ if (err < 0)
+ return err;
+ err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME,
+ &period_time);
+ if (err < 0)
+ return err;
do {
changed = 0;
err = hw_param_interval_refine_minmax(params, SND_PCM_HW_PARAM_PERIODS,
if (err < 0)
return err;
changed |= err;
+ err = snd_interval_step(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE),
+ 0, dshare->slave_period_size);
+ if (err < 0)
+ return err;
+ changed |= err;
+ if (err)
+ params->rmask |= (1 << SND_PCM_HW_PARAM_PERIOD_SIZE);
} while (changed);
}
+ dshare->timer_ticks = hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE)->max / dshare->slave_period_size;
params->info = dshare->shmptr->s.info;
#ifdef REFINE_DEBUG
snd_output_puts(log, "DMIX REFINE (end):\n");
return 0;
}
-int snd_pcm_direct_resume(snd_pcm_t *pcm)
+snd_pcm_chmap_query_t **snd_pcm_direct_query_chmaps(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ return snd_pcm_query_chmaps(dmix->spcm);
+}
+
+snd_pcm_chmap_t *snd_pcm_direct_get_chmap(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ return snd_pcm_get_chmap(dmix->spcm);
+}
+
+int snd_pcm_direct_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ return snd_pcm_set_chmap(dmix->spcm, map);
+}
+
+int snd_pcm_direct_prepare(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dmix = pcm->private_data;
int err;
-
- snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
- err = snd_pcm_resume(dmix->spcm);
- if (err == -ENOSYS) {
- /* FIXME: error handling? */
- snd_pcm_prepare(dmix->spcm);
+
+ switch (snd_pcm_state(dmix->spcm)) {
+ case SND_PCM_STATE_SETUP:
+ case SND_PCM_STATE_XRUN:
+ case SND_PCM_STATE_SUSPENDED:
+ err = snd_pcm_prepare(dmix->spcm);
+ if (err < 0)
+ return err;
snd_pcm_start(dmix->spcm);
- err = 0;
+ break;
+ case SND_PCM_STATE_OPEN:
+ case SND_PCM_STATE_DISCONNECTED:
+ return -EBADFD;
+ default:
+ break;
+ }
+ snd_pcm_direct_check_interleave(dmix, pcm);
+ dmix->state = SND_PCM_STATE_PREPARED;
+ dmix->appl_ptr = dmix->last_appl_ptr = 0;
+ dmix->hw_ptr = 0;
+ return snd_pcm_direct_set_timer_params(dmix);
+}
+
+int snd_pcm_direct_resume(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_t *spcm = dmix->spcm;
+
+ snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
+ /* some buggy drivers require the device resumed before prepared;
+ * when a device has RESUME flag and is in SUSPENDED state, resume
+ * here but immediately drop to bring it to a sane active state.
+ */
+ if ((spcm->info & SND_PCM_INFO_RESUME) &&
+ snd_pcm_state(spcm) == SND_PCM_STATE_SUSPENDED) {
+ snd_pcm_resume(spcm);
+ snd_pcm_drop(spcm);
+ snd_pcm_direct_timer_stop(dmix);
+ snd_pcm_direct_clear_timer_queue(dmix);
+ snd_pcm_areas_silence(snd_pcm_mmap_areas(spcm), 0,
+ spcm->channels, spcm->buffer_size,
+ spcm->format);
+ snd_pcm_prepare(spcm);
+ snd_pcm_start(spcm);
}
snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
- return err;
+ return -ENOSYS;
}
+#define COPY_SLAVE(field) (dmix->shmptr->s.field = spcm->field)
+
+/* copy the slave setting */
+static void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm)
+{
+ spcm->info &= ~SND_PCM_INFO_PAUSE;
+
+ COPY_SLAVE(access);
+ COPY_SLAVE(format);
+ COPY_SLAVE(subformat);
+ COPY_SLAVE(channels);
+ COPY_SLAVE(rate);
+ COPY_SLAVE(period_size);
+ COPY_SLAVE(period_time);
+ COPY_SLAVE(periods);
+ COPY_SLAVE(tstamp_mode);
+ COPY_SLAVE(tstamp_type);
+ COPY_SLAVE(period_step);
+ COPY_SLAVE(avail_min);
+ COPY_SLAVE(start_threshold);
+ COPY_SLAVE(stop_threshold);
+ COPY_SLAVE(silence_threshold);
+ COPY_SLAVE(silence_size);
+ COPY_SLAVE(boundary);
+ COPY_SLAVE(info);
+ COPY_SLAVE(msbits);
+ COPY_SLAVE(rate_num);
+ COPY_SLAVE(rate_den);
+ COPY_SLAVE(hw_flags);
+ COPY_SLAVE(fifo_size);
+ COPY_SLAVE(buffer_size);
+ COPY_SLAVE(buffer_time);
+ COPY_SLAVE(sample_bits);
+ COPY_SLAVE(frame_bits);
+
+ dmix->shmptr->s.info &= ~SND_PCM_INFO_RESUME;
+}
+
+#undef COPY_SLAVE
+
/*
* this function initializes hardware and starts playback operation with
* no stop threshold (it operates all time without xrun checking)
*/
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;
+ snd_pcm_hw_params_t hw_params = {0};
+ snd_pcm_sw_params_t sw_params = {0};
int ret, buffer_is_not_initialized;
snd_pcm_uframes_t boundary;
struct pollfd fd;
int loops = 10;
- snd_pcm_hw_params_alloca(&hw_params);
- snd_pcm_sw_params_alloca(&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);
+ 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);
+ 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);
+ 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 (params->format == SND_PCM_FORMAT_UNKNOWN)
+ ret = -EINVAL;
+ else
+ ret = snd_pcm_hw_params_set_format(spcm, &hw_params,
+ params->format);
if (ret < 0) {
+ static const snd_pcm_format_t dmix_formats[] = {
+ SND_PCM_FORMAT_S32,
+ SND_PCM_FORMAT_S32 ^ SND_PCM_FORMAT_S32_LE ^
+ SND_PCM_FORMAT_S32_BE,
+ SND_PCM_FORMAT_S16,
+ SND_PCM_FORMAT_S16 ^ SND_PCM_FORMAT_S16_LE ^
+ SND_PCM_FORMAT_S16_BE,
+ SND_PCM_FORMAT_S24_LE,
+ SND_PCM_FORMAT_S24_3LE,
+ SND_PCM_FORMAT_U8,
+ };
snd_pcm_format_t format;
- if (dmix->type == SND_PCM_TYPE_DMIX) {
- switch (params->format) {
- case SND_PCM_FORMAT_S32_LE:
- case SND_PCM_FORMAT_S32_BE:
- case SND_PCM_FORMAT_S16_LE:
- case SND_PCM_FORMAT_S16_BE:
- case SND_PCM_FORMAT_S24_3LE:
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dmix_formats); ++i) {
+ format = dmix_formats[i];
+ ret = snd_pcm_hw_params_set_format(spcm, &hw_params,
+ format);
+ if (ret >= 0)
break;
- default:
- SNDERR("invalid format");
- return -EINVAL;
- }
}
- format = params->format;
- ret = snd_pcm_hw_params_set_format(spcm, hw_params, format);
+ if (ret < 0 && dmix->type != SND_PCM_TYPE_DMIX) {
+ /* TODO: try to choose a good format */
+ ret = INTERNAL(snd_pcm_hw_params_set_format_first)(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);
+ ret = INTERNAL(snd_pcm_hw_params_set_channels_near)(spcm, &hw_params,
+ (unsigned int *)¶ms->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;
- }
+ SNDERR("requested count of channels is not available");
+ return ret;
}
- ret = INTERNAL(snd_pcm_hw_params_set_rate_near)(spcm, hw_params, (unsigned int *)¶ms->rate, 0);
+ ret = INTERNAL(snd_pcm_hw_params_set_rate_near)(spcm, &hw_params,
+ (unsigned int *)¶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, (unsigned int *)¶ms->buffer_time, 0);
+ ret = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(spcm,
+ &hw_params, (unsigned int *)¶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, (snd_pcm_uframes_t *)¶ms->buffer_size);
+ ret = INTERNAL(snd_pcm_hw_params_set_buffer_size_near)(spcm,
+ &hw_params, (snd_pcm_uframes_t *)¶ms->buffer_size);
if (ret < 0) {
SNDERR("unable to set buffer size");
return ret;
}
if (params->period_time > 0) {
- ret = INTERNAL(snd_pcm_hw_params_set_period_time_near)(spcm, hw_params, (unsigned int *)¶ms->period_time, 0);
+ ret = INTERNAL(snd_pcm_hw_params_set_period_time_near)(spcm,
+ &hw_params, (unsigned int *)¶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, (snd_pcm_uframes_t *)¶ms->period_size, 0);
+ ret = INTERNAL(snd_pcm_hw_params_set_period_size_near)(spcm,
+ &hw_params, (snd_pcm_uframes_t *)¶ms->period_size,
+ 0);
if (ret < 0) {
SNDERR("unable to set period_size");
return ret;
if (buffer_is_not_initialized && params->periods > 0) {
unsigned int periods = params->periods;
- ret = INTERNAL(snd_pcm_hw_params_set_periods_near)(spcm, hw_params, ¶ms->periods, 0);
+ 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;
}
}
- ret = snd_pcm_hw_params(spcm, hw_params);
+ ret = snd_pcm_hw_params(spcm, &hw_params);
if (ret < 0) {
SNDERR("unable to install hw params");
return ret;
}
/* store some hw_params values to shared info */
- dmix->shmptr->hw.format = snd_mask_value(hw_param_mask(hw_params, SND_PCM_HW_PARAM_FORMAT));
- dmix->shmptr->hw.rate = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_RATE);
- dmix->shmptr->hw.buffer_size = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_BUFFER_SIZE);
- dmix->shmptr->hw.buffer_time = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_BUFFER_TIME);
- dmix->shmptr->hw.period_size = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_PERIOD_SIZE);
- dmix->shmptr->hw.period_time = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_PERIOD_TIME);
- dmix->shmptr->hw.periods = *hw_param_interval(hw_params, SND_PCM_HW_PARAM_PERIODS);
-
-
- ret = snd_pcm_sw_params_current(spcm, sw_params);
+ dmix->shmptr->hw.format =
+ snd_mask_value(hw_param_mask(&hw_params,
+ SND_PCM_HW_PARAM_FORMAT));
+ dmix->shmptr->hw.rate =
+ *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_RATE);
+ dmix->shmptr->hw.buffer_size =
+ *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_BUFFER_SIZE);
+ dmix->shmptr->hw.buffer_time =
+ *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_BUFFER_TIME);
+ dmix->shmptr->hw.period_size =
+ *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIOD_SIZE);
+ dmix->shmptr->hw.period_time =
+ *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIOD_TIME);
+ dmix->shmptr->hw.periods =
+ *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIODS);
+
+
+ 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);
+ 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);
+ ret = snd_pcm_sw_params_set_stop_threshold(spcm, &sw_params, boundary);
if (ret < 0) {
SNDERR("unable to set stop threshold");
return ret;
}
- if (dmix->type != SND_PCM_TYPE_DMIX)
+ /* set timestamp mode to MMAP
+ * the slave timestamp is copied appropriately in dsnoop/dmix/dshare
+ * based on the tstamp_mode of each client
+ */
+ ret = snd_pcm_sw_params_set_tstamp_mode(spcm, &sw_params,
+ SND_PCM_TSTAMP_ENABLE);
+ if (ret < 0) {
+ SNDERR("unable to tstamp mode MMAP");
+ return ret;
+ }
+
+ if (dmix->type != SND_PCM_TYPE_DMIX &&
+ dmix->type != SND_PCM_TYPE_DSHARE)
goto __skip_silencing;
- ret = snd_pcm_sw_params_set_silence_threshold(spcm, sw_params, 0);
+ 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);
+ 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;
__skip_silencing:
- ret = snd_pcm_sw_params(spcm, sw_params);
+ 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;
if (dmix->type == SND_PCM_TYPE_DSHARE) {
const snd_pcm_channel_area_t *dst_areas;
dst_areas = snd_pcm_mmap_areas(spcm);
- snd_pcm_areas_silence(dst_areas, 0, spcm->channels, spcm->buffer_size, spcm->format);
+ snd_pcm_areas_silence(dst_areas, 0, spcm->channels,
+ spcm->buffer_size, spcm->format);
}
ret = snd_pcm_start(spcm);
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.period_size = spcm->period_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.info = spcm->info & ~SND_PCM_INFO_PAUSE;
- dmix->shmptr->s.msbits = spcm->msbits;
+ save_slave_setting(dmix, spcm);
/* Currently, we assume that each dmix client has the same
* hw_params setting.
dmix->slave_boundary = spcm->boundary;
spcm->donot_close = 1;
+
+ {
+ int ver = 0;
+ ioctl(spcm->poll_fd, SNDRV_PCM_IOCTL_PVERSION, &ver);
+ if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 8))
+ dmix->shmptr->use_server = 1;
+ }
+
return 0;
}
int snd_pcm_direct_initialize_poll_fd(snd_pcm_direct_t *dmix)
{
int ret;
- snd_pcm_info_t *info;
+ snd_pcm_info_t info = {0};
char name[128];
int capture = dmix->type == SND_PCM_TYPE_DSNOOP ? 1 : 0;
dmix->tread = 1;
dmix->timer_need_poll = 0;
- snd_pcm_info_alloca(&info);
- ret = snd_pcm_info(dmix->spcm, info);
+ dmix->timer_ticks = 1;
+ 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 + capture);
- ret = snd_timer_open(&dmix->timer, name, SND_TIMER_OPEN_NONBLOCK | SND_TIMER_OPEN_TREAD);
+ (int)SND_TIMER_CLASS_PCM,
+ snd_pcm_info_get_card(&info),
+ snd_pcm_info_get_device(&info),
+ snd_pcm_info_get_subdevice(&info) * 2 + capture);
+ ret = snd_timer_open(&dmix->timer, name,
+ SND_TIMER_OPEN_NONBLOCK | SND_TIMER_OPEN_TREAD);
if (ret < 0) {
- dmix->tread = 1;
- ret = snd_timer_open(&dmix->timer, name, SND_TIMER_OPEN_NONBLOCK);
+ dmix->tread = 0;
+ ret = snd_timer_open(&dmix->timer, name,
+ SND_TIMER_OPEN_NONBLOCK);
if (ret < 0) {
SNDERR("unable to open timer '%s'", name);
return ret;
}
if (snd_timer_poll_descriptors_count(dmix->timer) != 1) {
- SNDERR("unable to use timer with fd more than one!!!", name);
+ SNDERR("unable to use timer '%s' with more than one fd!", name);
return ret;
}
snd_timer_poll_descriptors(dmix->timer, &dmix->timer_fd, 1);
dmix->poll_fd = dmix->timer_fd.fd;
- dmix->timer_event_suspend = 1<<SND_TIMER_EVENT_MSUSPEND;
- dmix->timer_event_resume = 1<<SND_TIMER_EVENT_MRESUME;
+ dmix->timer_events = (1<<SND_TIMER_EVENT_MSUSPEND) |
+ (1<<SND_TIMER_EVENT_MRESUME) |
+ (1<<SND_TIMER_EVENT_MSTOP) |
+ (1<<SND_TIMER_EVENT_STOP);
/*
* Some hacks for older kernel drivers
* suspend/resume events.
*/
if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 5)) {
- dmix->timer_event_suspend = 1<<SND_TIMER_EVENT_MPAUSE;
- dmix->timer_event_resume = 1<<SND_TIMER_EVENT_MCONTINUE;
+ dmix->timer_events &= ~((1<<SND_TIMER_EVENT_MSUSPEND) |
+ (1<<SND_TIMER_EVENT_MRESUME));
+ dmix->timer_events |= (1<<SND_TIMER_EVENT_MPAUSE) |
+ (1<<SND_TIMER_EVENT_MCONTINUE);
}
+ /* In older versions, use SND_TIMER_EVENT_START too.
+ */
+ if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 6))
+ dmix->timer_events |= 1<<SND_TIMER_EVENT_START;
}
return 0;
}
return (snd_pcm_uframes_t)bsize;
}
+#define COPY_SLAVE(field) (spcm->field = dmix->shmptr->s.field)
+
+/* copy the slave setting */
+static void copy_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm)
+{
+ COPY_SLAVE(access);
+ COPY_SLAVE(format);
+ COPY_SLAVE(subformat);
+ COPY_SLAVE(channels);
+ COPY_SLAVE(rate);
+ COPY_SLAVE(period_size);
+ COPY_SLAVE(period_time);
+ COPY_SLAVE(periods);
+ COPY_SLAVE(tstamp_mode);
+ COPY_SLAVE(tstamp_type);
+ COPY_SLAVE(period_step);
+ COPY_SLAVE(avail_min);
+ COPY_SLAVE(start_threshold);
+ COPY_SLAVE(stop_threshold);
+ COPY_SLAVE(silence_threshold);
+ COPY_SLAVE(silence_size);
+ COPY_SLAVE(boundary);
+ COPY_SLAVE(info);
+ COPY_SLAVE(msbits);
+ COPY_SLAVE(rate_num);
+ COPY_SLAVE(rate_den);
+ COPY_SLAVE(hw_flags);
+ COPY_SLAVE(fifo_size);
+ COPY_SLAVE(buffer_size);
+ COPY_SLAVE(buffer_time);
+ COPY_SLAVE(sample_bits);
+ COPY_SLAVE(frame_bits);
+
+ spcm->info &= ~SND_PCM_INFO_PAUSE;
+ spcm->boundary = recalc_boundary_size(dmix->shmptr->s.boundary, spcm->buffer_size);
+}
+
+#undef COPY_SLAVE
+
+
/*
* open a slave PCM as secondary client (dup'ed fd)
*/
int ret;
snd_pcm_t *spcm;
- ret = snd_pcm_hw_open_fd(spcmp, client_name, dmix->hw_fd, 0, 0);
+ ret = snd_pcm_hw_open_fd(spcmp, client_name, dmix->hw_fd, 0);
if (ret < 0) {
SNDERR("unable to open hardware");
return ret;
spcm = *spcmp;
spcm->donot_close = 1;
spcm->setup = 1;
- /* we copy the slave setting */
- 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 = recalc_boundary_size(dmix->shmptr->s.boundary, spcm->buffer_size);
- spcm->info = dmix->shmptr->s.info;
+
+ copy_slave_setting(dmix, spcm);
+
+ /* Use the slave setting as SPCM, so far */
+ dmix->slave_buffer_size = spcm->buffer_size;
+ dmix->slave_period_size = dmix->shmptr->s.period_size;
+ dmix->slave_boundary = spcm->boundary;
+ dmix->recoveries = dmix->shmptr->s.recoveries;
+
+ ret = snd_pcm_mmap(spcm);
+ if (ret < 0) {
+ SNDERR("unable to mmap channels");
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * open a slave PCM as secondary client (dup'ed fd)
+ */
+int snd_pcm_direct_initialize_secondary_slave(snd_pcm_direct_t *dmix,
+ snd_pcm_t *spcm,
+ struct slave_params *params ATTRIBUTE_UNUSED)
+{
+ int ret;
+
+ spcm->donot_close = 1;
+ spcm->setup = 1;
+
+ copy_slave_setting(dmix, spcm);
/* Use the slave setting as SPCM, so far */
dmix->slave_buffer_size = spcm->buffer_size;
int snd_pcm_direct_set_timer_params(snd_pcm_direct_t *dmix)
{
- snd_timer_params_t *params;
+ snd_timer_params_t params = {0};
unsigned int filter;
int ret;
- snd_timer_params_alloca(¶ms);
- snd_timer_params_set_auto_start(params, 1);
+ snd_timer_params_set_auto_start(¶ms, 1);
if (dmix->type != SND_PCM_TYPE_DSNOOP)
- snd_timer_params_set_early_event(params, 1);
- snd_timer_params_set_ticks(params, 1);
+ snd_timer_params_set_early_event(¶ms, 1);
+ snd_timer_params_set_ticks(¶ms, dmix->timer_ticks);
if (dmix->tread) {
filter = (1<<SND_TIMER_EVENT_TICK) |
- dmix->timer_event_suspend |
- dmix->timer_event_resume;
- snd_timer_params_set_filter(params, filter);
+ dmix->timer_events;
+ INTERNAL(snd_timer_params_set_filter)(¶ms, filter);
}
- ret = snd_timer_params(dmix->timer, params);
+ ret = snd_timer_params(dmix->timer, ¶ms);
if (ret < 0) {
SNDERR("unable to set timer parameters");
- return ret;
+ return ret;
}
return 0;
}
const snd_pcm_channel_area_t *dst_areas;
const snd_pcm_channel_area_t *src_areas;
- bits = snd_pcm_format_physical_width(dmix->type);
+ bits = snd_pcm_format_physical_width(pcm->format);
if ((bits % 8) != 0)
interleaved = 0;
channels = dmix->channels;
return -EINVAL;
}
if (schannel < 0 || schannel >= params->channels) {
- SNDERR("invalid slave channel number %d in binding to %d",
+ SNDERR("invalid slave channel number %ld in binding to %ld",
schannel, cchannel);
free(bindings);
return -EINVAL;
}
bindings[cchannel] = schannel;
}
- if (dmix->type == SND_PCM_TYPE_DSNOOP)
+ if (dmix->type == SND_PCM_TYPE_DSNOOP ||
+ ! dmix->bindings)
goto __skip_same_dst;
for (chn = 0; chn < count; chn++) {
for (chn1 = 0; chn1 < count; chn1++) {
int hop)
{
snd_config_iterator_t i, next;
+ snd_config_t *pcm_conf, *pcm_conf2;
int err;
long card = 0, device = 0, subdevice = 0;
const char *str;
if (snd_config_get_string(sconf, &str) >= 0) {
- snd_config_t *pcm_conf;
if (hop > SND_CONF_MAX_HOPS) {
SNDERR("Too many definition levels (looped?)");
return -EINVAL;
return err;
}
+#if 0 /* for debug purposes */
+ {
+ snd_output_t *out;
+ snd_output_stdio_attach(&out, stderr, 0);
+ snd_config_save(sconf, out);
+ snd_output_close(out);
+ }
+#endif
+
+ if (snd_config_search(sconf, "slave", &pcm_conf) >= 0) {
+ if (snd_config_search(pcm_conf, "pcm", &pcm_conf) >= 0) {
+ return _snd_pcm_direct_get_slave_ipc_offset(root,
+ pcm_conf,
+ direction,
+ hop + 1);
+ } else {
+ if (snd_config_get_string(pcm_conf, &str) >= 0 &&
+ snd_config_search_definition(root, "pcm_slave",
+ str, &pcm_conf) >= 0) {
+ if (snd_config_search(pcm_conf, "pcm",
+ &pcm_conf2) >= 0) {
+ err =
+ _snd_pcm_direct_get_slave_ipc_offset(
+ root, pcm_conf2, direction, hop + 1);
+ snd_config_delete(pcm_conf);
+ return err;
+ }
+ snd_config_delete(pcm_conf);
+ }
+ }
+ }
+
snd_config_for_each(i, next, sconf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id, *str;
rec->ipc_key = 0;
rec->ipc_perm = 0600;
rec->ipc_gid = -1;
- rec->slowptr = 0;
+ rec->slowptr = 1;
rec->max_periods = 0;
+ rec->var_periodsize = 0;
+ rec->direct_memory_access = 1;
+ rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_AUTO;
/* read defaults */
if (snd_config_search(root, "defaults.pcm.dmix_max_periods", &n) >= 0) {
continue;
}
if (strcmp(id, "ipc_perm") == 0) {
- char *perm;
- char *endp;
- err = snd_config_get_ascii(n, &perm);
+ long perm;
+ err = snd_config_get_integer(n, &perm);
if (err < 0) {
- SNDERR("The field ipc_perm must be a valid file permission");
+ SNDERR("Invalid type for %s", id);
return err;
}
- if (isdigit(*perm) == 0) {
+ if ((perm & ~0777) != 0) {
SNDERR("The field ipc_perm must be a valid file permission");
- free(perm);
return -EINVAL;
}
- rec->ipc_perm = strtol(perm, &endp, 8);
- free(perm);
+ rec->ipc_perm = perm;
+ continue;
+ }
+ if (strcmp(id, "hw_ptr_alignment") == 0) {
+ const char *str;
+ err = snd_config_get_string(n, &str);
+ if (err < 0) {
+ SNDERR("Invalid type for %s", id);
+ return -EINVAL;
+ }
+ if (strcmp(str, "no") == 0)
+ rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_NO;
+ else if (strcmp(str, "roundup") == 0)
+ rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_ROUNDUP;
+ else if (strcmp(str, "rounddown") == 0)
+ rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_ROUNDDOWN;
+ else if (strcmp(str, "auto") == 0)
+ rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_AUTO;
+ else {
+ SNDERR("The field hw_ptr_alignment is invalid : %s", str);
+ return -EINVAL;
+ }
+
continue;
}
if (strcmp(id, "ipc_gid") == 0) {
continue;
}
if (isdigit(*group) == 0) {
- struct group *grp = getgrnam(group);
- if (grp == NULL) {
+ long clen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ size_t len = (clen == -1) ? 1024 : (size_t)clen;
+ struct group grp, *pgrp;
+ char *buffer = (char *)malloc(len);
+ if (buffer == NULL)
+ return -ENOMEM;
+ int st = getgrnam_r(group, &grp, buffer, len, &pgrp);
+ if (st != 0 || !pgrp) {
SNDERR("The field ipc_gid must be a valid group (create group %s)", group);
- free(group);
+ free(buffer);
return -EINVAL;
}
- rec->ipc_gid = grp->gr_gid;
+ rec->ipc_gid = pgrp->gr_gid;
+ free(buffer);
} else {
rec->ipc_gid = strtol(group, &endp, 10);
}
rec->max_periods = val;
continue;
}
+ if (strcmp(id, "var_periodsize") == 0) {
+ err = snd_config_get_bool(n);
+ if (err < 0)
+ return err;
+ rec->var_periodsize = err;
+ continue;
+ }
+ if (strcmp(id, "direct_memory_access") == 0) {
+ err = snd_config_get_bool(n);
+ if (err < 0)
+ return err;
+ rec->direct_memory_access = err;
+ continue;
+ }
SNDERR("Unknown field %s", id);
return -EINVAL;
}
- if (! rec->slave) {
+ if (!rec->slave) {
SNDERR("slave is not defined");
return -EINVAL;
}
}
if (ipc_key_add_uid)
rec->ipc_key += getuid();
- err = snd_pcm_direct_get_slave_ipc_offset(root, rec->slave, stream);
+ err = snd_pcm_direct_get_slave_ipc_offset(root, conf, stream);
if (err < 0)
return err;
rec->ipc_key += err;
return 0;
}
+
+void snd_pcm_direct_reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix)
+{
+
+ if (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_ROUNDUP ||
+ (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO &&
+ pcm->buffer_size <= pcm->period_size * 2))
+ dmix->slave_appl_ptr =
+ ((dmix->slave_appl_ptr + dmix->slave_period_size - 1) /
+ dmix->slave_period_size) * dmix->slave_period_size;
+ else if (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_ROUNDDOWN ||
+ (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO &&
+ (dmix->slave_period_size * SEC_TO_MS) /
+ pcm->rate < LOW_LATENCY_PERIOD_TIME))
+ dmix->slave_appl_ptr = dmix->slave_hw_ptr =
+ ((dmix->slave_hw_ptr / dmix->slave_period_size) *
+ dmix->slave_period_size);
+}