|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \file pcm/pcm_share.c
|
|
Packit Service |
db8eaa |
* \ingroup PCM_Plugins
|
|
Packit Service |
db8eaa |
* \brief PCM Share Plugin Interface
|
|
Packit Service |
db8eaa |
* \author Abramo Bagnara <abramo@alsa-project.org>
|
|
Packit Service |
db8eaa |
* \date 2000-2001
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* PCM - Share
|
|
Packit Service |
db8eaa |
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* This library is free software; you can redistribute it and/or modify
|
|
Packit Service |
db8eaa |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit Service |
db8eaa |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit Service |
db8eaa |
* the License, or (at your option) any later version.
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
db8eaa |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
db8eaa |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
db8eaa |
* GNU Lesser General Public License for more details.
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit Service |
db8eaa |
* License along with this library; if not, write to the Free Software
|
|
Packit Service |
db8eaa |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#include <stdio.h>
|
|
Packit Service |
db8eaa |
#include <stdlib.h>
|
|
Packit Service |
db8eaa |
#include <limits.h>
|
|
Packit Service |
db8eaa |
#include <unistd.h>
|
|
Packit Service |
db8eaa |
#include <string.h>
|
|
Packit Service |
db8eaa |
#include <signal.h>
|
|
Packit Service |
db8eaa |
#include <math.h>
|
|
Packit Service |
db8eaa |
#include <sys/socket.h>
|
|
Packit Service |
db8eaa |
#include <poll.h>
|
|
Packit Service |
db8eaa |
#include <pthread.h>
|
|
Packit Service |
db8eaa |
#include "pcm_local.h"
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#ifndef PIC
|
|
Packit Service |
db8eaa |
/* entry for static linking */
|
|
Packit Service |
db8eaa |
const char *_snd_module_pcm_share = "";
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#ifndef DOC_HIDDEN
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static LIST_HEAD(snd_pcm_share_slaves);
|
|
Packit Service |
db8eaa |
static pthread_mutex_t snd_pcm_share_slaves_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#ifdef MUTEX_DEBUG
|
|
Packit Service |
db8eaa |
#define Pthread_mutex_lock(mutex) \
|
|
Packit Service |
db8eaa |
char *snd_pcm_share_slaves_mutex_holder;
|
|
Packit Service |
db8eaa |
do { \
|
|
Packit Service |
db8eaa |
int err = pthread_mutex_trylock(mutex); \
|
|
Packit Service |
db8eaa |
if (err < 0) { \
|
|
Packit Service |
db8eaa |
fprintf(stderr, "lock " #mutex " is busy (%s): waiting in " __func__ "\n", *(mutex##_holder)); \
|
|
Packit Service |
db8eaa |
pthread_mutex_lock(mutex); \
|
|
Packit Service |
db8eaa |
fprintf(stderr, "... got\n"); \
|
|
Packit Service |
db8eaa |
} \
|
|
Packit Service |
db8eaa |
*(mutex##_holder) = __func__; \
|
|
Packit Service |
db8eaa |
} while (0)
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#define Pthread_mutex_unlock(mutex) \
|
|
Packit Service |
db8eaa |
do { \
|
|
Packit Service |
db8eaa |
*(mutex##_holder) = 0; \
|
|
Packit Service |
db8eaa |
pthread_mutex_unlock(mutex); \
|
|
Packit Service |
db8eaa |
} while (0)
|
|
Packit Service |
db8eaa |
#else
|
|
Packit Service |
db8eaa |
#define Pthread_mutex_lock(mutex) pthread_mutex_lock(mutex)
|
|
Packit Service |
db8eaa |
#define Pthread_mutex_unlock(mutex) pthread_mutex_unlock(mutex)
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
typedef struct {
|
|
Packit Service |
db8eaa |
struct list_head clients;
|
|
Packit Service |
db8eaa |
struct list_head list;
|
|
Packit Service |
db8eaa |
snd_pcm_t *pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_format_t format;
|
|
Packit Service |
db8eaa |
int rate;
|
|
Packit Service |
db8eaa |
unsigned int channels;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t period_time;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t buffer_time;
|
|
Packit Service |
db8eaa |
unsigned int open_count;
|
|
Packit Service |
db8eaa |
unsigned int setup_count;
|
|
Packit Service |
db8eaa |
unsigned int prepared_count;
|
|
Packit Service |
db8eaa |
unsigned int running_count;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t safety_threshold;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t silence_frames;
|
|
Packit Service |
db8eaa |
snd_pcm_sw_params_t sw_params;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t hw_ptr;
|
|
Packit Service |
db8eaa |
int poll[2];
|
|
Packit Service |
db8eaa |
int polling;
|
|
Packit Service |
db8eaa |
pthread_t thread;
|
|
Packit Service |
db8eaa |
pthread_mutex_t mutex;
|
|
Packit Service |
db8eaa |
#ifdef MUTEX_DEBUG
|
|
Packit Service |
db8eaa |
char *mutex_holder;
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
pthread_cond_t poll_cond;
|
|
Packit Service |
db8eaa |
} snd_pcm_share_slave_t;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
typedef struct {
|
|
Packit Service |
db8eaa |
struct list_head list;
|
|
Packit Service |
db8eaa |
snd_pcm_t *pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave;
|
|
Packit Service |
db8eaa |
unsigned int channels;
|
|
Packit Service |
db8eaa |
unsigned int *slave_channels;
|
|
Packit Service |
db8eaa |
int drain_silenced;
|
|
Packit Service |
db8eaa |
snd_htimestamp_t trigger_tstamp;
|
|
Packit Service |
db8eaa |
snd_pcm_state_t state;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t hw_ptr;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t appl_ptr;
|
|
Packit Service |
db8eaa |
int ready;
|
|
Packit Service |
db8eaa |
int client_socket;
|
|
Packit Service |
db8eaa |
int slave_socket;
|
|
Packit Service |
db8eaa |
} snd_pcm_share_t;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#endif /* DOC_HIDDEN */
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static void _snd_pcm_share_stop(snd_pcm_t *pcm, snd_pcm_state_t state);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_uframes_t snd_pcm_share_slave_avail(snd_pcm_share_slave_t *slave)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t avail;
|
|
Packit Service |
db8eaa |
snd_pcm_t *pcm = slave->pcm;
|
|
Packit Service |
db8eaa |
avail = slave->hw_ptr - *pcm->appl.ptr;
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
|
Packit Service |
db8eaa |
avail += pcm->buffer_size;
|
|
Packit Service |
db8eaa |
if (avail < 0)
|
|
Packit Service |
db8eaa |
avail += pcm->boundary;
|
|
Packit Service |
db8eaa |
else if ((snd_pcm_uframes_t) avail >= pcm->boundary)
|
|
Packit Service |
db8eaa |
avail -= pcm->boundary;
|
|
Packit Service |
db8eaa |
return avail;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* Warning: take the mutex before to call this */
|
|
Packit Service |
db8eaa |
/* Return number of frames to mmap_commit the slave */
|
|
Packit Service |
db8eaa |
static snd_pcm_uframes_t _snd_pcm_share_slave_forward(snd_pcm_share_slave_t *slave)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
struct list_head *i;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t buffer_size;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t frames, safety_frames;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t min_frames, max_frames;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t avail, slave_avail;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t slave_hw_avail;
|
|
Packit Service |
db8eaa |
slave_avail = snd_pcm_share_slave_avail(slave);
|
|
Packit Service |
db8eaa |
buffer_size = slave->pcm->buffer_size;
|
|
Packit Service |
db8eaa |
min_frames = slave_avail;
|
|
Packit Service |
db8eaa |
max_frames = 0;
|
|
Packit Service |
db8eaa |
list_for_each(i, &slave->clients) {
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list);
|
|
Packit Service |
db8eaa |
snd_pcm_t *pcm = share->pcm;
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
avail = snd_pcm_mmap_avail(pcm);
|
|
Packit Service |
db8eaa |
frames = slave_avail - avail;
|
|
Packit Service |
db8eaa |
if (frames > max_frames)
|
|
Packit Service |
db8eaa |
max_frames = frames;
|
|
Packit Service |
db8eaa |
if (share->state != SND_PCM_STATE_RUNNING)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
if (frames < min_frames)
|
|
Packit Service |
db8eaa |
min_frames = frames;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (max_frames == 0)
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
frames = min_frames;
|
|
Packit Service |
db8eaa |
/* Slave xrun prevention */
|
|
Packit Service |
db8eaa |
slave_hw_avail = buffer_size - slave_avail;
|
|
Packit Service |
db8eaa |
safety_frames = slave->safety_threshold - slave_hw_avail;
|
|
Packit Service |
db8eaa |
if (safety_frames > 0 &&
|
|
Packit Service |
db8eaa |
frames < safety_frames) {
|
|
Packit Service |
db8eaa |
/* Avoid to pass over the last */
|
|
Packit Service |
db8eaa |
if (max_frames < safety_frames)
|
|
Packit Service |
db8eaa |
frames = max_frames;
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
frames = safety_frames;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (frames < 0)
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
return frames;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
- stop PCM on xrun
|
|
Packit Service |
db8eaa |
- update poll status
|
|
Packit Service |
db8eaa |
- draining silencing
|
|
Packit Service |
db8eaa |
- return distance in frames to next event
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
static snd_pcm_uframes_t _snd_pcm_share_missing(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_t *spcm = slave->pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t buffer_size = spcm->buffer_size;
|
|
Packit Service |
db8eaa |
int ready = 1, running = 0;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t avail = 0, slave_avail;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t hw_avail;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t missing = INT_MAX;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t ready_missing;
|
|
Packit Service |
db8eaa |
// printf("state=%s hw_ptr=%ld appl_ptr=%ld slave appl_ptr=%ld safety=%ld silence=%ld\n", snd_pcm_state_name(share->state), slave->hw_ptr, share->appl_ptr, *slave->pcm->appl_ptr, slave->safety_threshold, slave->silence_frames);
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
/* Fall through */
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
return INT_MAX;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
share->hw_ptr = slave->hw_ptr;
|
|
Packit Service |
db8eaa |
avail = snd_pcm_mmap_avail(pcm);
|
|
Packit Service |
db8eaa |
if (avail >= pcm->stop_threshold) {
|
|
Packit Service |
db8eaa |
_snd_pcm_share_stop(pcm, share->state == SND_PCM_STATE_DRAINING ? SND_PCM_STATE_SETUP : SND_PCM_STATE_XRUN);
|
|
Packit Service |
db8eaa |
goto update_poll;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
hw_avail = buffer_size - avail;
|
|
Packit Service |
db8eaa |
slave_avail = snd_pcm_share_slave_avail(slave);
|
|
Packit Service |
db8eaa |
if (avail < slave_avail) {
|
|
Packit Service |
db8eaa |
/* Some frames need still to be transferred */
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t slave_hw_avail = buffer_size - slave_avail;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t safety_missing = slave_hw_avail - slave->safety_threshold;
|
|
Packit Service |
db8eaa |
if (safety_missing < 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t err;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t frames = slave_avail - avail;
|
|
Packit Service |
db8eaa |
if (-safety_missing <= frames) {
|
|
Packit Service |
db8eaa |
frames = -safety_missing;
|
|
Packit Service |
db8eaa |
missing = 1;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
err = snd_pcm_mmap_commit(spcm, snd_pcm_mmap_offset(spcm), frames);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
SYSMSG("snd_pcm_mmap_commit error");
|
|
Packit Service |
db8eaa |
return INT_MAX;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (err != frames)
|
|
Packit Service |
db8eaa |
SYSMSG("commit returns %ld for size %ld", err, frames);
|
|
Packit Service |
db8eaa |
slave_avail -= err;
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
if (safety_missing == 0)
|
|
Packit Service |
db8eaa |
missing = 1;
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
missing = safety_missing;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
Packit Service |
db8eaa |
if (hw_avail <= 0) {
|
|
Packit Service |
db8eaa |
_snd_pcm_share_stop(pcm, SND_PCM_STATE_SETUP);
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if ((snd_pcm_uframes_t)hw_avail < missing)
|
|
Packit Service |
db8eaa |
missing = hw_avail;
|
|
Packit Service |
db8eaa |
running = 1;
|
|
Packit Service |
db8eaa |
ready = 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
if (avail >= pcm->stop_threshold) {
|
|
Packit Service |
db8eaa |
_snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t xrun_missing = pcm->stop_threshold - avail;
|
|
Packit Service |
db8eaa |
if (missing > xrun_missing)
|
|
Packit Service |
db8eaa |
missing = xrun_missing;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
ready_missing = pcm->avail_min - avail;
|
|
Packit Service |
db8eaa |
if (ready_missing > 0) {
|
|
Packit Service |
db8eaa |
ready = 0;
|
|
Packit Service |
db8eaa |
if (missing > (snd_pcm_uframes_t)ready_missing)
|
|
Packit Service |
db8eaa |
missing = ready_missing;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
running = 1;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
SNDERR("invalid shared PCM state %d", share->state);
|
|
Packit Service |
db8eaa |
return INT_MAX;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
update_poll:
|
|
Packit Service |
db8eaa |
if (ready != share->ready) {
|
|
Packit Service |
db8eaa |
char buf[1];
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
Packit Service |
db8eaa |
if (ready)
|
|
Packit Service |
db8eaa |
read(share->slave_socket, buf, 1);
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
write(share->client_socket, buf, 1);
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
if (ready)
|
|
Packit Service |
db8eaa |
write(share->slave_socket, buf, 1);
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
read(share->client_socket, buf, 1);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
share->ready = ready;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (!running)
|
|
Packit Service |
db8eaa |
return INT_MAX;
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
|
|
Packit Service |
db8eaa |
share->state == SND_PCM_STATE_DRAINING &&
|
|
Packit Service |
db8eaa |
!share->drain_silenced) {
|
|
Packit Service |
db8eaa |
/* drain silencing */
|
|
Packit Service |
db8eaa |
if (avail >= slave->silence_frames) {
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t offset = share->appl_ptr % buffer_size;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t xfer = 0;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t size = slave->silence_frames;
|
|
Packit Service |
db8eaa |
while (xfer < size) {
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t frames = size - xfer;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t cont = buffer_size - offset;
|
|
Packit Service |
db8eaa |
if (cont < frames)
|
|
Packit Service |
db8eaa |
frames = cont;
|
|
Packit Service |
db8eaa |
snd_pcm_areas_silence(pcm->running_areas, offset, pcm->channels, frames, pcm->format);
|
|
Packit Service |
db8eaa |
offset += frames;
|
|
Packit Service |
db8eaa |
if (offset >= buffer_size)
|
|
Packit Service |
db8eaa |
offset = 0;
|
|
Packit Service |
db8eaa |
xfer += frames;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
share->drain_silenced = 1;
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t silence_missing;
|
|
Packit Service |
db8eaa |
silence_missing = slave->silence_frames - avail;
|
|
Packit Service |
db8eaa |
if (silence_missing < missing)
|
|
Packit Service |
db8eaa |
missing = silence_missing;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
// printf("missing=%d\n", missing);
|
|
Packit Service |
db8eaa |
return missing;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_uframes_t _snd_pcm_share_slave_missing(snd_pcm_share_slave_t *slave)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t missing = INT_MAX;
|
|
Packit Service |
db8eaa |
struct list_head *i;
|
|
Packit Service |
db8eaa |
/* snd_pcm_sframes_t avail = */ snd_pcm_avail_update(slave->pcm);
|
|
Packit Service |
db8eaa |
slave->hw_ptr = *slave->pcm->hw.ptr;
|
|
Packit Service |
db8eaa |
list_for_each(i, &slave->clients) {
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list);
|
|
Packit Service |
db8eaa |
snd_pcm_t *pcm = share->pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t m = _snd_pcm_share_missing(pcm);
|
|
Packit Service |
db8eaa |
if (m < missing)
|
|
Packit Service |
db8eaa |
missing = m;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return missing;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static void *snd_pcm_share_thread(void *data)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = data;
|
|
Packit Service |
db8eaa |
snd_pcm_t *spcm = slave->pcm;
|
|
Packit Service |
db8eaa |
struct pollfd pfd[2];
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
pfd[0].fd = slave->poll[0];
|
|
Packit Service |
db8eaa |
pfd[0].events = POLLIN;
|
|
Packit Service |
db8eaa |
err = snd_pcm_poll_descriptors(spcm, &pfd[1], 1);
|
|
Packit Service |
db8eaa |
if (err != 1) {
|
|
Packit Service |
db8eaa |
SNDERR("invalid poll descriptors %d", err);
|
|
Packit Service |
db8eaa |
return NULL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
err = pipe(slave->poll);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
SYSERR("can't create a pipe");
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return NULL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
while (slave->open_count > 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t missing;
|
|
Packit Service |
db8eaa |
// printf("begin min_missing\n");
|
|
Packit Service |
db8eaa |
missing = _snd_pcm_share_slave_missing(slave);
|
|
Packit Service |
db8eaa |
// printf("min_missing=%ld\n", missing);
|
|
Packit Service |
db8eaa |
if (missing < INT_MAX) {
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t hw_ptr;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t avail_min;
|
|
Packit Service |
db8eaa |
hw_ptr = slave->hw_ptr + missing;
|
|
Packit Service |
db8eaa |
hw_ptr += spcm->period_size - 1;
|
|
Packit Service |
db8eaa |
if (hw_ptr >= spcm->boundary)
|
|
Packit Service |
db8eaa |
hw_ptr -= spcm->boundary;
|
|
Packit Service |
db8eaa |
hw_ptr -= hw_ptr % spcm->period_size;
|
|
Packit Service |
db8eaa |
avail_min = hw_ptr - *spcm->appl.ptr;
|
|
Packit Service |
db8eaa |
if (spcm->stream == SND_PCM_STREAM_PLAYBACK)
|
|
Packit Service |
db8eaa |
avail_min += spcm->buffer_size;
|
|
Packit Service |
db8eaa |
if (avail_min < 0)
|
|
Packit Service |
db8eaa |
avail_min += spcm->boundary;
|
|
Packit Service |
db8eaa |
// printf("avail_min=%d\n", avail_min);
|
|
Packit Service |
db8eaa |
if ((snd_pcm_uframes_t)avail_min != spcm->avail_min) {
|
|
Packit Service |
db8eaa |
snd_pcm_sw_params_set_avail_min(spcm, &slave->sw_params, avail_min);
|
|
Packit Service |
db8eaa |
err = snd_pcm_sw_params(spcm, &slave->sw_params);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
SYSERR("snd_pcm_sw_params error");
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return NULL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
slave->polling = 1;
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
err = poll(pfd, 2, -1);
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
if (pfd[0].revents & POLLIN) {
|
|
Packit Service |
db8eaa |
char buf[1];
|
|
Packit Service |
db8eaa |
read(pfd[0].fd, buf, 1);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
slave->polling = 0;
|
|
Packit Service |
db8eaa |
pthread_cond_wait(&slave->poll_cond, &slave->mutex);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return NULL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static void _snd_pcm_share_update(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_t *spcm = slave->pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t missing;
|
|
Packit Service |
db8eaa |
/* snd_pcm_sframes_t avail = */ snd_pcm_avail_update(spcm);
|
|
Packit Service |
db8eaa |
slave->hw_ptr = *slave->pcm->hw.ptr;
|
|
Packit Service |
db8eaa |
missing = _snd_pcm_share_missing(pcm);
|
|
Packit Service |
db8eaa |
// printf("missing %ld\n", missing);
|
|
Packit Service |
db8eaa |
if (!slave->polling) {
|
|
Packit Service |
db8eaa |
pthread_cond_signal(&slave->poll_cond);
|
|
Packit Service |
db8eaa |
return;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (missing < INT_MAX) {
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t hw_ptr;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t avail_min;
|
|
Packit Service |
db8eaa |
hw_ptr = slave->hw_ptr + missing;
|
|
Packit Service |
db8eaa |
hw_ptr += spcm->period_size - 1;
|
|
Packit Service |
db8eaa |
if (hw_ptr >= spcm->boundary)
|
|
Packit Service |
db8eaa |
hw_ptr -= spcm->boundary;
|
|
Packit Service |
db8eaa |
hw_ptr -= hw_ptr % spcm->period_size;
|
|
Packit Service |
db8eaa |
avail_min = hw_ptr - *spcm->appl.ptr;
|
|
Packit Service |
db8eaa |
if (spcm->stream == SND_PCM_STREAM_PLAYBACK)
|
|
Packit Service |
db8eaa |
avail_min += spcm->buffer_size;
|
|
Packit Service |
db8eaa |
if (avail_min < 0)
|
|
Packit Service |
db8eaa |
avail_min += spcm->boundary;
|
|
Packit Service |
db8eaa |
if ((snd_pcm_uframes_t)avail_min < spcm->avail_min) {
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
snd_pcm_sw_params_set_avail_min(spcm, &slave->sw_params, avail_min);
|
|
Packit Service |
db8eaa |
err = snd_pcm_sw_params(spcm, &slave->sw_params);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
SYSERR("snd_pcm_sw_params error");
|
|
Packit Service |
db8eaa |
return;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_async(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int sig ATTRIBUTE_UNUSED, pid_t pid ATTRIBUTE_UNUSED)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return -ENOSYS;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
return snd_pcm_info(share->slave->pcm, info);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_t access_mask;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_any(&access_mask);
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
|
|
Packit Service |
db8eaa |
&access_mask);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS,
|
|
Packit Service |
db8eaa |
share->channels, 0);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
if (slave->format != SND_PCM_FORMAT_UNKNOWN) {
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_params_set_format(params, slave->format);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (slave->rate >= 0) {
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_RATE,
|
|
Packit Service |
db8eaa |
slave->rate, 0);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (slave->period_time >= 0) {
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_PERIOD_TIME,
|
|
Packit Service |
db8eaa |
slave->period_time, 0);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (slave->buffer_time >= 0) {
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_BUFFER_TIME,
|
|
Packit Service |
db8eaa |
slave->buffer_time, 0);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
params->info |= SND_PCM_INFO_DOUBLE;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
|
|
Packit Service |
db8eaa |
_snd_pcm_hw_params_any(sparams);
|
|
Packit Service |
db8eaa |
_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
|
|
Packit Service |
db8eaa |
&saccess_mask);
|
|
Packit Service |
db8eaa |
_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
|
|
Packit Service |
db8eaa |
slave->channels, 0);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
|
|
Packit Service |
db8eaa |
snd_pcm_hw_params_t *sparams)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_SUBFORMAT |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_RATE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIOD_SIZE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIOD_TIME |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_BUFFER_SIZE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_BUFFER_TIME |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIODS);
|
|
Packit Service |
db8eaa |
const snd_pcm_access_mask_t *access_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
|
|
Packit Service |
db8eaa |
if (!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
|
|
Packit Service |
db8eaa |
!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) &&
|
|
Packit Service |
db8eaa |
!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_t saccess_mask;
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_any(&saccess_mask);
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_reset(&saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
|
|
Packit Service |
db8eaa |
&saccess_mask);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_params_refine(sparams, links, params);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
|
|
Packit Service |
db8eaa |
snd_pcm_hw_params_t *sparams)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_SUBFORMAT |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_RATE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIOD_SIZE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIOD_TIME |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_BUFFER_SIZE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_BUFFER_TIME |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIODS);
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_t access_mask;
|
|
Packit Service |
db8eaa |
const snd_pcm_access_mask_t *saccess_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS);
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_any(&access_mask);
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
|
|
Packit Service |
db8eaa |
if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
|
|
Packit Service |
db8eaa |
if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX) &&
|
|
Packit Service |
db8eaa |
!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_COMPLEX);
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
|
|
Packit Service |
db8eaa |
&access_mask);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_params_refine(params, links, sparams);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
return snd_pcm_hw_refine(share->slave->pcm, params);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_hw_params_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
return _snd_pcm_hw_params_internal(share->slave->pcm, params);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return snd_pcm_hw_refine_slave(pcm, params,
|
|
Packit Service |
db8eaa |
snd_pcm_share_hw_refine_cprepare,
|
|
Packit Service |
db8eaa |
snd_pcm_share_hw_refine_cchange,
|
|
Packit Service |
db8eaa |
snd_pcm_share_hw_refine_sprepare,
|
|
Packit Service |
db8eaa |
snd_pcm_share_hw_refine_schange,
|
|
Packit Service |
db8eaa |
snd_pcm_share_hw_refine_slave);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_t *spcm = slave->pcm;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
if (slave->setup_count) {
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_params_set_format(params, spcm->format);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_params_set_subformat(params, spcm->subformat);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set_minmax(params, SND_PCM_HW_PARAM_RATE,
|
|
Packit Service |
db8eaa |
spcm->rate, 0,
|
|
Packit Service |
db8eaa |
spcm->rate, 1);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set_minmax(params, SND_PCM_HW_PARAM_PERIOD_TIME,
|
|
Packit Service |
db8eaa |
spcm->period_time, 0,
|
|
Packit Service |
db8eaa |
spcm->period_time, 1);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_BUFFER_SIZE,
|
|
Packit Service |
db8eaa |
spcm->buffer_size, 0);
|
|
Packit Service |
db8eaa |
_err:
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("slave is already running with incompatible setup");
|
|
Packit Service |
db8eaa |
err = -EBUSY;
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
err = snd_pcm_hw_params_slave(pcm, params,
|
|
Packit Service |
db8eaa |
snd_pcm_share_hw_refine_cchange,
|
|
Packit Service |
db8eaa |
snd_pcm_share_hw_refine_sprepare,
|
|
Packit Service |
db8eaa |
snd_pcm_share_hw_refine_schange,
|
|
Packit Service |
db8eaa |
snd_pcm_share_hw_params_slave);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
snd_pcm_sw_params_current(slave->pcm, &slave->sw_params);
|
|
Packit Service |
db8eaa |
/* >= 30 ms */
|
|
Packit Service |
db8eaa |
slave->safety_threshold = slave->pcm->rate * 30 / 1000;
|
|
Packit Service |
db8eaa |
slave->safety_threshold += slave->pcm->period_size - 1;
|
|
Packit Service |
db8eaa |
slave->safety_threshold -= slave->safety_threshold % slave->pcm->period_size;
|
|
Packit Service |
db8eaa |
slave->silence_frames = slave->safety_threshold;
|
|
Packit Service |
db8eaa |
if (slave->pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
|
Packit Service |
db8eaa |
snd_pcm_areas_silence(slave->pcm->running_areas, 0, slave->pcm->channels, slave->pcm->buffer_size, slave->pcm->format);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
share->state = SND_PCM_STATE_SETUP;
|
|
Packit Service |
db8eaa |
slave->setup_count++;
|
|
Packit Service |
db8eaa |
_end:
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_hw_free(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
slave->setup_count--;
|
|
Packit Service |
db8eaa |
if (slave->setup_count == 0)
|
|
Packit Service |
db8eaa |
err = snd_pcm_hw_free(slave->pcm);
|
|
Packit Service |
db8eaa |
share->state = SND_PCM_STATE_OPEN;
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t *params ATTRIBUTE_UNUSED)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t sd = 0, d = 0;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
Packit Service |
db8eaa |
status->avail = snd_pcm_mmap_playback_avail(pcm);
|
|
Packit Service |
db8eaa |
if (share->state != SND_PCM_STATE_RUNNING &&
|
|
Packit Service |
db8eaa |
share->state != SND_PCM_STATE_DRAINING)
|
|
Packit Service |
db8eaa |
goto _notrunning;
|
|
Packit Service |
db8eaa |
d = pcm->buffer_size - status->avail;
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
status->avail = snd_pcm_mmap_capture_avail(pcm);
|
|
Packit Service |
db8eaa |
if (share->state != SND_PCM_STATE_RUNNING)
|
|
Packit Service |
db8eaa |
goto _notrunning;
|
|
Packit Service |
db8eaa |
d = status->avail;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
err = snd_pcm_delay(slave->pcm, &sd);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
_notrunning:
|
|
Packit Service |
db8eaa |
status->delay = sd + d;
|
|
Packit Service |
db8eaa |
status->state = share->state;
|
|
Packit Service |
01c0b7 |
status->appl_ptr = *pcm->appl.ptr;
|
|
Packit Service |
01c0b7 |
status->hw_ptr = *pcm->hw.ptr;
|
|
Packit Service |
db8eaa |
status->trigger_tstamp = share->trigger_tstamp;
|
|
Packit Service |
db8eaa |
_end:
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_state_t snd_pcm_share_state(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
return share->state;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int _snd_pcm_share_hwsync(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
return -EPIPE;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return snd_pcm_hwsync(slave->pcm);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_hwsync(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
err = _snd_pcm_share_hwsync(pcm);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int _snd_pcm_share_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
return -EPIPE;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
/* Fall through */
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return snd_pcm_delay(slave->pcm, delayp);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
err = _snd_pcm_share_delay(pcm, delayp);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_share_avail_update(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t avail;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
if (share->state == SND_PCM_STATE_RUNNING) {
|
|
Packit Service |
db8eaa |
avail = snd_pcm_avail_update(slave->pcm);
|
|
Packit Service |
db8eaa |
if (avail < 0) {
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return avail;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
share->hw_ptr = *slave->pcm->hw.ptr;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
avail = snd_pcm_mmap_avail(pcm);
|
|
Packit Service |
db8eaa |
if ((snd_pcm_uframes_t)avail > pcm->buffer_size)
|
|
Packit Service |
db8eaa |
return -EPIPE;
|
|
Packit Service |
db8eaa |
return avail;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail,
|
|
Packit Service |
db8eaa |
snd_htimestamp_t *tstamp)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
err = snd_pcm_htimestamp(slave->pcm, avail, tstamp);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* Call it with mutex held */
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t _snd_pcm_share_mmap_commit(snd_pcm_t *pcm,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t size)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_t *spcm = slave->pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t ret;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t frames;
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
|
|
Packit Service |
db8eaa |
share->state == SND_PCM_STATE_RUNNING) {
|
|
Packit Service |
db8eaa |
frames = *spcm->appl.ptr - share->appl_ptr;
|
|
Packit Service |
db8eaa |
if (frames > (snd_pcm_sframes_t)pcm->buffer_size)
|
|
Packit Service |
db8eaa |
frames -= pcm->boundary;
|
|
Packit Service |
db8eaa |
else if (frames < -(snd_pcm_sframes_t)pcm->buffer_size)
|
|
Packit Service |
db8eaa |
frames += pcm->boundary;
|
|
Packit Service |
db8eaa |
if (frames > 0) {
|
|
Packit Service |
db8eaa |
/* Latecomer PCM */
|
|
Packit Service |
db8eaa |
ret = snd_pcm_rewind(spcm, frames);
|
|
Packit Service |
db8eaa |
if (ret < 0)
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_pcm_mmap_appl_forward(pcm, size);
|
|
Packit Service |
db8eaa |
if (share->state == SND_PCM_STATE_RUNNING) {
|
|
Packit Service |
db8eaa |
frames = _snd_pcm_share_slave_forward(slave);
|
|
Packit Service |
db8eaa |
if (frames > 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t err;
|
|
Packit Service |
db8eaa |
err = snd_pcm_mmap_commit(spcm, snd_pcm_mmap_offset(spcm), frames);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
SYSMSG("snd_pcm_mmap_commit error");
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (err != frames) {
|
|
Packit Service |
db8eaa |
SYSMSG("commit returns %ld for size %ld", err, frames);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
_snd_pcm_share_update(pcm);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return size;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_share_mmap_commit(snd_pcm_t *pcm,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t offset,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t size)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t ret;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
ret = _snd_pcm_share_mmap_commit(pcm, offset, size);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_prepare(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_OPEN:
|
|
Packit Service |
db8eaa |
err = -EBADFD;
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
err = -EBUSY;
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_PREPARED:
|
|
Packit Service |
db8eaa |
err = 0;
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
default: /* nothing todo */
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (slave->prepared_count == 0) {
|
|
Packit Service |
db8eaa |
err = snd_pcm_prepare(slave->pcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
slave->prepared_count++;
|
|
Packit Service |
db8eaa |
share->hw_ptr = 0;
|
|
Packit Service |
db8eaa |
share->appl_ptr = 0;
|
|
Packit Service |
db8eaa |
share->state = SND_PCM_STATE_PREPARED;
|
|
Packit Service |
db8eaa |
_end:
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_reset(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
/* FIXME? */
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
snd_pcm_areas_silence(pcm->running_areas, 0, pcm->channels, pcm->buffer_size, pcm->format);
|
|
Packit Service |
db8eaa |
share->hw_ptr = *slave->pcm->hw.ptr;
|
|
Packit Service |
db8eaa |
share->appl_ptr = share->hw_ptr;
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_start(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_t *spcm = slave->pcm;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
if (share->state != SND_PCM_STATE_PREPARED)
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
share->state = SND_PCM_STATE_RUNNING;
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t hw_avail = snd_pcm_mmap_playback_hw_avail(pcm);
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t xfer = 0;
|
|
Packit Service |
db8eaa |
if (hw_avail == 0) {
|
|
Packit Service |
db8eaa |
err = -EPIPE;
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (slave->running_count) {
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t sd;
|
|
Packit Service |
db8eaa |
err = snd_pcm_delay(spcm, &sd);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
err = snd_pcm_rewind(spcm, sd);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
assert(share->hw_ptr == 0);
|
|
Packit Service |
db8eaa |
share->hw_ptr = *spcm->hw.ptr;
|
|
Packit Service |
db8eaa |
share->appl_ptr = *spcm->appl.ptr;
|
|
Packit Service |
db8eaa |
while (xfer < hw_avail) {
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t frames = hw_avail - xfer;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t offset = snd_pcm_mmap_offset(pcm);
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t cont = pcm->buffer_size - offset;
|
|
Packit Service |
db8eaa |
if (cont < frames)
|
|
Packit Service |
db8eaa |
frames = cont;
|
|
Packit Service |
db8eaa |
if (pcm->stopped_areas != NULL)
|
|
Packit Service |
db8eaa |
snd_pcm_areas_copy(pcm->running_areas, offset,
|
|
Packit Service |
db8eaa |
pcm->stopped_areas, xfer,
|
|
Packit Service |
db8eaa |
pcm->channels, frames,
|
|
Packit Service |
db8eaa |
pcm->format);
|
|
Packit Service |
db8eaa |
xfer += frames;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_pcm_mmap_appl_forward(pcm, hw_avail);
|
|
Packit Service |
db8eaa |
if (slave->running_count == 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t res;
|
|
Packit Service |
db8eaa |
res = snd_pcm_mmap_commit(spcm, snd_pcm_mmap_offset(spcm), hw_avail);
|
|
Packit Service |
db8eaa |
if (res < 0) {
|
|
Packit Service |
db8eaa |
err = res;
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
assert((snd_pcm_uframes_t)res == hw_avail);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (slave->running_count == 0) {
|
|
Packit Service |
db8eaa |
err = snd_pcm_start(spcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
slave->running_count++;
|
|
Packit Service |
db8eaa |
_snd_pcm_share_update(pcm);
|
|
Packit Service |
db8eaa |
gettimestamp(&share->trigger_tstamp, pcm->tstamp_type);
|
|
Packit Service |
db8eaa |
_end:
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return -ENOSYS;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return -ENXIO;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
unsigned int channel = info->channel;
|
|
Packit Service |
db8eaa |
int c = share->slave_channels[channel];
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
info->channel = c;
|
|
Packit Service |
db8eaa |
err = snd_pcm_channel_info(slave->pcm, info);
|
|
Packit Service |
db8eaa |
info->channel = channel;
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t _snd_pcm_share_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t n;
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_PREPARED:
|
|
Packit Service |
db8eaa |
if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
if (pcm->stream != SND_PCM_STREAM_CAPTURE)
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
return -EPIPE;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
n = snd_pcm_mmap_hw_avail(pcm);
|
|
Packit Service |
db8eaa |
assert(n >= 0);
|
|
Packit Service |
db8eaa |
if ((snd_pcm_uframes_t)n > frames)
|
|
Packit Service |
db8eaa |
frames = n;
|
|
Packit Service |
db8eaa |
if (share->state == SND_PCM_STATE_RUNNING && frames > 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t ret = snd_pcm_rewind(slave->pcm, frames);
|
|
Packit Service |
db8eaa |
if (ret < 0)
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
frames = ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_pcm_mmap_appl_backward(pcm, frames);
|
|
Packit Service |
db8eaa |
_snd_pcm_share_update(pcm);
|
|
Packit Service |
db8eaa |
return n;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_share_rewindable(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t ret;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
ret = snd_pcm_rewindable(slave->pcm);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_share_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t ret;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
ret = _snd_pcm_share_rewind(pcm, frames);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t _snd_pcm_share_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t n;
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_PREPARED:
|
|
Packit Service |
db8eaa |
if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
if (pcm->stream != SND_PCM_STREAM_CAPTURE)
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
return -EPIPE;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
n = snd_pcm_mmap_avail(pcm);
|
|
Packit Service |
db8eaa |
if ((snd_pcm_uframes_t)n > frames)
|
|
Packit Service |
db8eaa |
frames = n;
|
|
Packit Service |
db8eaa |
if (share->state == SND_PCM_STATE_RUNNING && frames > 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t ret = INTERNAL(snd_pcm_forward)(slave->pcm, frames);
|
|
Packit Service |
db8eaa |
if (ret < 0)
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
frames = ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_pcm_mmap_appl_forward(pcm, frames);
|
|
Packit Service |
db8eaa |
_snd_pcm_share_update(pcm);
|
|
Packit Service |
db8eaa |
return n;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_share_forwardable(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t ret;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
ret = snd_pcm_forwardable(slave->pcm);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_share_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t ret;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
ret = _snd_pcm_share_forward(pcm, frames);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* Warning: take the mutex before to call this */
|
|
Packit Service |
db8eaa |
static void _snd_pcm_share_stop(snd_pcm_t *pcm, snd_pcm_state_t state)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
#if 0
|
|
Packit Service |
db8eaa |
if (!pcm->mmap_channels) {
|
|
Packit Service |
db8eaa |
/* PCM closing already begun in the main thread */
|
|
Packit Service |
db8eaa |
return;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
gettimestamp(&share->trigger_tstamp, pcm->tstamp_type);
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
|
|
Packit Service |
db8eaa |
snd_pcm_areas_copy(pcm->stopped_areas, 0,
|
|
Packit Service |
db8eaa |
pcm->running_areas, 0,
|
|
Packit Service |
db8eaa |
pcm->channels, pcm->buffer_size,
|
|
Packit Service |
db8eaa |
pcm->format);
|
|
Packit Service |
db8eaa |
} else if (slave->running_count > 1) {
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t delay;
|
|
Packit Service |
db8eaa |
snd_pcm_areas_silence(pcm->running_areas, 0, pcm->channels,
|
|
Packit Service |
db8eaa |
pcm->buffer_size, pcm->format);
|
|
Packit Service |
db8eaa |
err = snd_pcm_delay(slave->pcm, &delay);
|
|
Packit Service |
db8eaa |
if (err >= 0 && delay > 0)
|
|
Packit Service |
db8eaa |
snd_pcm_rewind(slave->pcm, delay);
|
|
Packit Service |
db8eaa |
share->drain_silenced = 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
share->state = state;
|
|
Packit Service |
db8eaa |
slave->prepared_count--;
|
|
Packit Service |
db8eaa |
slave->running_count--;
|
|
Packit Service |
db8eaa |
if (slave->running_count == 0) {
|
|
Packit Service |
db8eaa |
int err = snd_pcm_drop(slave->pcm);
|
|
Packit Service |
db8eaa |
assert(err >= 0);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_drain(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_OPEN:
|
|
Packit Service |
db8eaa |
err = -EBADFD;
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_PREPARED:
|
|
Packit Service |
db8eaa |
share->state = SND_PCM_STATE_SETUP;
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_SETUP:
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
share->state = SND_PCM_STATE_SETUP;
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
share->state = SND_PCM_STATE_DRAINING;
|
|
Packit Service |
db8eaa |
_snd_pcm_share_update(pcm);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
if (!(pcm->mode & SND_PCM_NONBLOCK))
|
|
Packit Service |
db8eaa |
snd_pcm_wait(pcm, -1);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
assert(0);
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
_snd_pcm_share_stop(pcm, SND_PCM_STATE_DRAINING);
|
|
Packit Service |
db8eaa |
_snd_pcm_share_update(pcm);
|
|
Packit Service |
db8eaa |
/* Fall through */
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
if (snd_pcm_mmap_capture_avail(pcm) <= 0)
|
|
Packit Service |
db8eaa |
share->state = SND_PCM_STATE_SETUP;
|
|
Packit Service |
db8eaa |
else
|
|
Packit Service |
db8eaa |
share->state = SND_PCM_STATE_DRAINING;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
assert(0);
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
_end:
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_drop(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
switch (share->state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_OPEN:
|
|
Packit Service |
db8eaa |
err = -EBADFD;
|
|
Packit Service |
db8eaa |
goto _end;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_SETUP:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
|
|
Packit Service |
db8eaa |
share->state = SND_PCM_STATE_SETUP;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
/* Fall through */
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
_snd_pcm_share_stop(pcm, SND_PCM_STATE_SETUP);
|
|
Packit Service |
db8eaa |
_snd_pcm_share_update(pcm);
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_PREPARED:
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
share->state = SND_PCM_STATE_SETUP;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
assert(0);
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
share->appl_ptr = share->hw_ptr = 0;
|
|
Packit Service |
db8eaa |
_end:
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_close(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&snd_pcm_share_slaves_mutex);
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
slave->open_count--;
|
|
Packit Service |
db8eaa |
if (slave->open_count == 0) {
|
|
Packit Service |
db8eaa |
pthread_cond_signal(&slave->poll_cond);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
err = pthread_join(slave->thread, 0);
|
|
Packit Service |
db8eaa |
assert(err == 0);
|
|
Packit Service |
db8eaa |
err = snd_pcm_close(slave->pcm);
|
|
Packit Service |
db8eaa |
pthread_mutex_destroy(&slave->mutex);
|
|
Packit Service |
db8eaa |
pthread_cond_destroy(&slave->poll_cond);
|
|
Packit Service |
db8eaa |
list_del(&slave->list);
|
|
Packit Service |
db8eaa |
free(slave);
|
|
Packit Service |
db8eaa |
list_del(&share->list);
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
list_del(&share->list);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&snd_pcm_share_slaves_mutex);
|
|
Packit Service |
db8eaa |
close(share->client_socket);
|
|
Packit Service |
db8eaa |
close(share->slave_socket);
|
|
Packit Service |
db8eaa |
free(share->slave_channels);
|
|
Packit Service |
db8eaa |
free(share);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_share_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static void snd_pcm_share_dump(snd_pcm_t *pcm, snd_output_t *out)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = share->slave;
|
|
Packit Service |
db8eaa |
unsigned int k;
|
|
Packit Service |
db8eaa |
snd_output_printf(out, "Share PCM\n");
|
|
Packit Service |
db8eaa |
snd_output_printf(out, " Channel bindings:\n");
|
|
Packit Service |
db8eaa |
for (k = 0; k < share->channels; ++k)
|
|
Packit Service |
db8eaa |
snd_output_printf(out, " %d: %d\n", k, share->slave_channels[k]);
|
|
Packit Service |
db8eaa |
if (pcm->setup) {
|
|
Packit Service |
db8eaa |
snd_output_printf(out, "Its setup is:\n");
|
|
Packit Service |
db8eaa |
snd_pcm_dump_setup(pcm, out);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_output_printf(out, "Slave: ");
|
|
Packit Service |
db8eaa |
snd_pcm_dump(slave->pcm, out);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static const snd_pcm_ops_t snd_pcm_share_ops = {
|
|
Packit Service |
db8eaa |
.close = snd_pcm_share_close,
|
|
Packit Service |
db8eaa |
.info = snd_pcm_share_info,
|
|
Packit Service |
db8eaa |
.hw_refine = snd_pcm_share_hw_refine,
|
|
Packit Service |
db8eaa |
.hw_params = snd_pcm_share_hw_params,
|
|
Packit Service |
db8eaa |
.hw_free = snd_pcm_share_hw_free,
|
|
Packit Service |
db8eaa |
.sw_params = snd_pcm_share_sw_params,
|
|
Packit Service |
db8eaa |
.channel_info = snd_pcm_share_channel_info,
|
|
Packit Service |
db8eaa |
.dump = snd_pcm_share_dump,
|
|
Packit Service |
db8eaa |
.nonblock = snd_pcm_share_nonblock,
|
|
Packit Service |
db8eaa |
.async = snd_pcm_share_async,
|
|
Packit Service |
db8eaa |
.mmap = snd_pcm_share_mmap,
|
|
Packit Service |
db8eaa |
.munmap = snd_pcm_share_munmap,
|
|
Packit Service |
db8eaa |
};
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static const snd_pcm_fast_ops_t snd_pcm_share_fast_ops = {
|
|
Packit Service |
db8eaa |
.status = snd_pcm_share_status,
|
|
Packit Service |
db8eaa |
.state = snd_pcm_share_state,
|
|
Packit Service |
db8eaa |
.hwsync = snd_pcm_share_hwsync,
|
|
Packit Service |
db8eaa |
.delay = snd_pcm_share_delay,
|
|
Packit Service |
db8eaa |
.prepare = snd_pcm_share_prepare,
|
|
Packit Service |
db8eaa |
.reset = snd_pcm_share_reset,
|
|
Packit Service |
db8eaa |
.start = snd_pcm_share_start,
|
|
Packit Service |
db8eaa |
.drop = snd_pcm_share_drop,
|
|
Packit Service |
db8eaa |
.drain = snd_pcm_share_drain,
|
|
Packit Service |
db8eaa |
.pause = snd_pcm_share_pause,
|
|
Packit Service |
db8eaa |
.writei = snd_pcm_mmap_writei,
|
|
Packit Service |
db8eaa |
.writen = snd_pcm_mmap_writen,
|
|
Packit Service |
db8eaa |
.readi = snd_pcm_mmap_readi,
|
|
Packit Service |
db8eaa |
.readn = snd_pcm_mmap_readn,
|
|
Packit Service |
db8eaa |
.rewindable = snd_pcm_share_rewindable,
|
|
Packit Service |
db8eaa |
.rewind = snd_pcm_share_rewind,
|
|
Packit Service |
db8eaa |
.forwardable = snd_pcm_share_forwardable,
|
|
Packit Service |
db8eaa |
.forward = snd_pcm_share_forward,
|
|
Packit Service |
db8eaa |
.resume = snd_pcm_share_resume,
|
|
Packit Service |
db8eaa |
.avail_update = snd_pcm_share_avail_update,
|
|
Packit Service |
db8eaa |
.htimestamp = snd_pcm_share_htimestamp,
|
|
Packit Service |
db8eaa |
.mmap_commit = snd_pcm_share_mmap_commit,
|
|
Packit Service |
db8eaa |
};
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Creates a new Share PCM
|
|
Packit Service |
db8eaa |
* \param pcmp Returns created PCM handle
|
|
Packit Service |
db8eaa |
* \param name Name of PCM
|
|
Packit Service |
db8eaa |
* \param sname Slave name
|
|
Packit Service |
db8eaa |
* \param sformat Slave format
|
|
Packit Service |
db8eaa |
* \param srate Slave rate
|
|
Packit Service |
db8eaa |
* \param schannels Slave channels
|
|
Packit Service |
db8eaa |
* \param speriod_time Slave period time
|
|
Packit Service |
db8eaa |
* \param sbuffer_time Slave buffer time
|
|
Packit Service |
db8eaa |
* \param channels Count of channels
|
|
Packit Service |
db8eaa |
* \param channels_map Map of channels
|
|
Packit Service |
db8eaa |
* \param stream Direction
|
|
Packit Service |
db8eaa |
* \param mode PCM mode
|
|
Packit Service |
db8eaa |
* \retval zero on success otherwise a negative error code
|
|
Packit Service |
db8eaa |
* \warning Using of this function might be dangerous in the sense
|
|
Packit Service |
db8eaa |
* of compatibility reasons. The prototype might be freely
|
|
Packit Service |
db8eaa |
* changed in future.
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int snd_pcm_share_open(snd_pcm_t **pcmp, const char *name, const char *sname,
|
|
Packit Service |
db8eaa |
snd_pcm_format_t sformat, int srate,
|
|
Packit Service |
db8eaa |
unsigned int schannels,
|
|
Packit Service |
db8eaa |
int speriod_time, int sbuffer_time,
|
|
Packit Service |
db8eaa |
unsigned int channels, unsigned int *channels_map,
|
|
Packit Service |
db8eaa |
snd_pcm_stream_t stream, int mode)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_t *pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *share;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
struct list_head *i;
|
|
Packit Service |
db8eaa |
char slave_map[32] = { 0 };
|
|
Packit Service |
db8eaa |
unsigned int k;
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *slave = NULL;
|
|
Packit Service |
db8eaa |
int sd[2];
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
assert(pcmp);
|
|
Packit Service |
db8eaa |
assert(channels > 0 && sname && channels_map);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (k = 0; k < channels; ++k) {
|
|
Packit Service |
db8eaa |
if (channels_map[k] >= sizeof(slave_map) / sizeof(slave_map[0])) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid slave channel (%d) in binding", channels_map[k]);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (slave_map[channels_map[k]]) {
|
|
Packit Service |
db8eaa |
SNDERR("Repeated slave channel (%d) in binding", channels_map[k]);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
slave_map[channels_map[k]] = 1;
|
|
Packit Service |
db8eaa |
assert((unsigned)channels_map[k] < schannels);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
share = calloc(1, sizeof(snd_pcm_share_t));
|
|
Packit Service |
db8eaa |
if (!share)
|
|
Packit Service |
db8eaa |
return -ENOMEM;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
share->channels = channels;
|
|
Packit Service |
db8eaa |
share->slave_channels = calloc(channels, sizeof(*share->slave_channels));
|
|
Packit Service |
db8eaa |
if (!share->slave_channels) {
|
|
Packit Service |
db8eaa |
free(share);
|
|
Packit Service |
db8eaa |
return -ENOMEM;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
memcpy(share->slave_channels, channels_map, channels * sizeof(*share->slave_channels));
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
err = snd_pcm_new(&pcm, SND_PCM_TYPE_SHARE, name, stream, mode);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
free(share->slave_channels);
|
|
Packit Service |
db8eaa |
free(share);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
err = socketpair(AF_LOCAL, SOCK_STREAM, 0, sd);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_free(pcm);
|
|
Packit Service |
db8eaa |
free(share->slave_channels);
|
|
Packit Service |
db8eaa |
free(share);
|
|
Packit Service |
db8eaa |
return -errno;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (stream == SND_PCM_STREAM_PLAYBACK) {
|
|
Packit Service |
db8eaa |
int bufsize = 1;
|
|
Packit Service |
db8eaa |
err = setsockopt(sd[0], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
|
|
Packit Service |
db8eaa |
if (err >= 0) {
|
|
Packit Service |
db8eaa |
struct pollfd pfd;
|
|
Packit Service |
db8eaa |
pfd.fd = sd[0];
|
|
Packit Service |
db8eaa |
pfd.events = POLLOUT;
|
|
Packit Service |
db8eaa |
while ((err = poll(&pfd, 1, 0)) == 1) {
|
|
Packit Service |
db8eaa |
char buf[1];
|
|
Packit Service |
db8eaa |
err = write(sd[0], buf, 1);
|
|
Packit Service |
db8eaa |
assert(err != 0);
|
|
Packit Service |
db8eaa |
if (err != 1)
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
err = -errno;
|
|
Packit Service |
db8eaa |
close(sd[0]);
|
|
Packit Service |
db8eaa |
close(sd[1]);
|
|
Packit Service |
db8eaa |
snd_pcm_free(pcm);
|
|
Packit Service |
db8eaa |
free(share->slave_channels);
|
|
Packit Service |
db8eaa |
free(share);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&snd_pcm_share_slaves_mutex);
|
|
Packit Service |
db8eaa |
list_for_each(i, &snd_pcm_share_slaves) {
|
|
Packit Service |
db8eaa |
snd_pcm_share_slave_t *s = list_entry(i, snd_pcm_share_slave_t, list);
|
|
Packit Service |
db8eaa |
if (s->pcm->name && strcmp(s->pcm->name, sname) == 0) {
|
|
Packit Service |
db8eaa |
slave = s;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (!slave) {
|
|
Packit Service |
db8eaa |
snd_pcm_t *spcm;
|
|
Packit Service |
db8eaa |
err = snd_pcm_open(&spcm, sname, stream, mode);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&snd_pcm_share_slaves_mutex);
|
|
Packit Service |
db8eaa |
close(sd[0]);
|
|
Packit Service |
db8eaa |
close(sd[1]);
|
|
Packit Service |
db8eaa |
snd_pcm_free(pcm);
|
|
Packit Service |
db8eaa |
free(share->slave_channels);
|
|
Packit Service |
db8eaa |
free(share);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
/* FIXME: bellow is a real ugly hack to get things working */
|
|
Packit Service |
db8eaa |
/* there is a memory leak somewhere, but I'm unable to trace it --jk */
|
|
Packit Service |
db8eaa |
slave = calloc(1, sizeof(snd_pcm_share_slave_t) * 8);
|
|
Packit Service |
db8eaa |
if (!slave) {
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&snd_pcm_share_slaves_mutex);
|
|
Packit Service |
db8eaa |
snd_pcm_close(spcm);
|
|
Packit Service |
db8eaa |
close(sd[0]);
|
|
Packit Service |
db8eaa |
close(sd[1]);
|
|
Packit Service |
db8eaa |
snd_pcm_free(pcm);
|
|
Packit Service |
db8eaa |
free(share->slave_channels);
|
|
Packit Service |
db8eaa |
free(share);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
INIT_LIST_HEAD(&slave->clients);
|
|
Packit Service |
db8eaa |
slave->pcm = spcm;
|
|
Packit Service |
db8eaa |
slave->channels = schannels;
|
|
Packit Service |
db8eaa |
slave->format = sformat;
|
|
Packit Service |
db8eaa |
slave->rate = srate;
|
|
Packit Service |
db8eaa |
slave->period_time = speriod_time;
|
|
Packit Service |
db8eaa |
slave->buffer_time = sbuffer_time;
|
|
Packit Service |
db8eaa |
pthread_mutex_init(&slave->mutex, NULL);
|
|
Packit Service |
db8eaa |
pthread_cond_init(&slave->poll_cond, NULL);
|
|
Packit Service |
db8eaa |
list_add_tail(&slave->list, &snd_pcm_share_slaves);
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
err = pthread_create(&slave->thread, NULL, snd_pcm_share_thread, slave);
|
|
Packit Service |
db8eaa |
assert(err == 0);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&snd_pcm_share_slaves_mutex);
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
Pthread_mutex_lock(&slave->mutex);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&snd_pcm_share_slaves_mutex);
|
|
Packit Service |
db8eaa |
list_for_each(i, &slave->clients) {
|
|
Packit Service |
db8eaa |
snd_pcm_share_t *sh = list_entry(i, snd_pcm_share_t, list);
|
|
Packit Service |
db8eaa |
for (k = 0; k < sh->channels; ++k) {
|
|
Packit Service |
db8eaa |
if (slave_map[sh->slave_channels[k]]) {
|
|
Packit Service |
db8eaa |
SNDERR("Slave channel %d is already in use", sh->slave_channels[k]);
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
close(sd[0]);
|
|
Packit Service |
db8eaa |
close(sd[1]);
|
|
Packit Service |
db8eaa |
snd_pcm_free(pcm);
|
|
Packit Service |
db8eaa |
free(share->slave_channels);
|
|
Packit Service |
db8eaa |
free(share);
|
|
Packit Service |
db8eaa |
return -EBUSY;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
share->slave = slave;
|
|
Packit Service |
db8eaa |
share->pcm = pcm;
|
|
Packit Service |
db8eaa |
share->client_socket = sd[0];
|
|
Packit Service |
db8eaa |
share->slave_socket = sd[1];
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
pcm->mmap_rw = 1;
|
|
Packit Service |
db8eaa |
pcm->ops = &snd_pcm_share_ops;
|
|
Packit Service |
db8eaa |
pcm->fast_ops = &snd_pcm_share_fast_ops;
|
|
Packit Service |
db8eaa |
pcm->private_data = share;
|
|
Packit Service |
db8eaa |
pcm->poll_fd = share->client_socket;
|
|
Packit Service |
db8eaa |
pcm->poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
|
|
Packit Service |
db8eaa |
pcm->tstamp_type = slave->pcm->tstamp_type;
|
|
Packit Service |
db8eaa |
snd_pcm_set_hw_ptr(pcm, &share->hw_ptr, -1, 0);
|
|
Packit Service |
db8eaa |
snd_pcm_set_appl_ptr(pcm, &share->appl_ptr, -1, 0);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
slave->open_count++;
|
|
Packit Service |
db8eaa |
list_add_tail(&share->list, &slave->clients);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
Pthread_mutex_unlock(&slave->mutex);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
*pcmp = pcm;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/*! \page pcm_plugins
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
\section pcm_plugins_share Plugin: Share
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
This plugin allows sharing of multiple channels with more clients. The access
|
|
Packit Service |
db8eaa |
to each channel is exlusive (samples are not mixed together). It means, if
|
|
Packit Service |
db8eaa |
the channel zero is used with first client, the channel cannot be used with
|
|
Packit Service |
db8eaa |
second one. If you are looking for a mixing plugin, use the
|
|
Packit Service |
db8eaa |
\ref pcm_plugins_dmix "dmix plugin".
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
The difference from \ref pcm_plugins_dshare "dshare plugin" is that
|
|
Packit Service |
db8eaa |
share plugin requires the server program "aserver", while dshare plugin
|
|
Packit Service |
db8eaa |
doesn't need the explicit server but access to the shared buffer.
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
\code
|
|
Packit Service |
db8eaa |
pcm.name {
|
|
Packit Service |
db8eaa |
type share # Share PCM
|
|
Packit Service |
db8eaa |
slave STR # Slave name
|
|
Packit Service |
db8eaa |
# or
|
|
Packit Service |
db8eaa |
slave { # Slave definition
|
|
Packit Service |
db8eaa |
pcm STR # Slave PCM name
|
|
Packit Service |
db8eaa |
[format STR] # Slave format
|
|
Packit Service |
db8eaa |
[channels INT] # Slave channels
|
|
Packit Service |
db8eaa |
[rate INT] # Slave rate
|
|
Packit Service |
db8eaa |
[period_time INT] # Slave period time in us
|
|
Packit Service |
db8eaa |
[buffer_time INT] # Slave buffer time in us
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
bindings {
|
|
Packit Service |
db8eaa |
N INT # Slave channel INT for client channel N
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
\endcode
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
\subsection pcm_plugins_share_funcref Function reference
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_pcm_share_open()
|
|
Packit Service |
db8eaa |
_snd_pcm_share_open()
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Creates a new Share PCM
|
|
Packit Service |
db8eaa |
* \param pcmp Returns created PCM handle
|
|
Packit Service |
db8eaa |
* \param name Name of PCM
|
|
Packit Service |
db8eaa |
* \param root Root configuration node
|
|
Packit Service |
db8eaa |
* \param conf Configuration node with Share PCM description
|
|
Packit Service |
db8eaa |
* \param stream Stream type
|
|
Packit Service |
db8eaa |
* \param mode Stream mode
|
|
Packit Service |
db8eaa |
* \retval zero on success otherwise a negative error code
|
|
Packit Service |
db8eaa |
* \warning Using of this function might be dangerous in the sense
|
|
Packit Service |
db8eaa |
* of compatibility reasons. The prototype might be freely
|
|
Packit Service |
db8eaa |
* changed in future.
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int _snd_pcm_share_open(snd_pcm_t **pcmp, const char *name,
|
|
Packit Service |
db8eaa |
snd_config_t *root, snd_config_t *conf,
|
|
Packit Service |
db8eaa |
snd_pcm_stream_t stream, int mode)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_config_iterator_t i, next;
|
|
Packit Service |
db8eaa |
const char *sname = NULL;
|
|
Packit Service |
db8eaa |
snd_config_t *bindings = NULL;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
snd_config_t *slave = NULL, *sconf;
|
|
Packit Service |
db8eaa |
unsigned int *channels_map = NULL;
|
|
Packit Service |
db8eaa |
unsigned int channels = 0;
|
|
Packit Service |
db8eaa |
snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
|
|
Packit Service |
db8eaa |
int schannels = -1;
|
|
Packit Service |
db8eaa |
int srate = -1;
|
|
Packit Service |
db8eaa |
int speriod_time= -1, sbuffer_time = -1;
|
|
Packit Service |
db8eaa |
unsigned int schannel_max = 0;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_config_for_each(i, next, conf) {
|
|
Packit Service |
db8eaa |
snd_config_t *n = snd_config_iterator_entry(i);
|
|
Packit Service |
db8eaa |
const char *id;
|
|
Packit Service |
db8eaa |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
if (snd_pcm_conf_generic_id(id))
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
if (strcmp(id, "slave") == 0) {
|
|
Packit Service |
db8eaa |
slave = n;
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (strcmp(id, "bindings") == 0) {
|
|
Packit Service |
db8eaa |
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid type for %s", id);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
bindings = n;
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
SNDERR("Unknown field %s", id);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (!slave) {
|
|
Packit Service |
db8eaa |
SNDERR("slave is not defined");
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
err = snd_pcm_slave_conf(root, slave, &sconf, 5,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_CHANNELS, 0, &schannels,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_RATE, 0, &srate,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_PERIOD_TIME, 0, &speriod_time,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_BUFFER_TIME, 0, &sbuffer_time);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* FIXME: nothing strictly forces to have named definition */
|
|
Packit Service |
db8eaa |
err = snd_config_get_string(sconf, &sname);
|
|
Packit Service |
db8eaa |
sname = err >= 0 && sname ? strdup(sname) : NULL;
|
|
Packit Service |
db8eaa |
snd_config_delete(sconf);
|
|
Packit Service |
db8eaa |
if (sname == NULL) {
|
|
Packit Service |
db8eaa |
SNDERR("slave.pcm is not a string");
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (!bindings) {
|
|
Packit Service |
db8eaa |
SNDERR("bindings is not defined");
|
|
Packit Service |
db8eaa |
err = -EINVAL;
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_config_for_each(i, next, bindings) {
|
|
Packit Service |
db8eaa |
long cchannel = -1;
|
|
Packit Service |
db8eaa |
snd_config_t *n = snd_config_iterator_entry(i);
|
|
Packit Service |
db8eaa |
const char *id;
|
|
Packit Service |
db8eaa |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
err = safe_strtol(id, &cchannel);
|
|
Packit Service |
db8eaa |
if (err < 0 || cchannel < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid client channel in binding: %s", id);
|
|
Packit Service |
db8eaa |
err = -EINVAL;
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if ((unsigned)cchannel >= channels)
|
|
Packit Service |
db8eaa |
channels = cchannel + 1;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (channels == 0) {
|
|
Packit Service |
db8eaa |
SNDERR("No bindings defined");
|
|
Packit Service |
db8eaa |
err = -EINVAL;
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
channels_map = calloc(channels, sizeof(*channels_map));
|
|
Packit Service |
db8eaa |
if (! channels_map) {
|
|
Packit Service |
db8eaa |
err = -ENOMEM;
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_config_for_each(i, next, bindings) {
|
|
Packit Service |
db8eaa |
snd_config_t *n = snd_config_iterator_entry(i);
|
|
Packit Service |
db8eaa |
const char *id;
|
|
Packit Service |
db8eaa |
long cchannel;
|
|
Packit Service |
db8eaa |
long schannel = -1;
|
|
Packit Service |
db8eaa |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
cchannel = atoi(id);
|
|
Packit Service |
db8eaa |
err = snd_config_get_integer(n, &schannel);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
assert(schannel >= 0);
|
|
Packit Service |
db8eaa |
assert(schannels <= 0 || schannel < schannels);
|
|
Packit Service |
db8eaa |
channels_map[cchannel] = schannel;
|
|
Packit Service |
db8eaa |
if ((unsigned)schannel > schannel_max)
|
|
Packit Service |
db8eaa |
schannel_max = schannel;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (schannels <= 0)
|
|
Packit Service |
db8eaa |
schannels = schannel_max + 1;
|
|
Packit Service |
db8eaa |
err = snd_pcm_share_open(pcmp, name, sname, sformat, srate,
|
|
Packit Service |
db8eaa |
(unsigned int) schannels,
|
|
Packit Service |
db8eaa |
speriod_time, sbuffer_time,
|
|
Packit Service |
db8eaa |
channels, channels_map, stream, mode);
|
|
Packit Service |
db8eaa |
_free:
|
|
Packit Service |
db8eaa |
free(channels_map);
|
|
Packit Service |
db8eaa |
free((char *)sname);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
#ifndef DOC_HIDDEN
|
|
Packit Service |
db8eaa |
SND_DLSYM_BUILD_VERSION(_snd_pcm_share_open, SND_PCM_DLSYM_VERSION);
|
|
Packit Service |
db8eaa |
#endif
|