return 0;
}
-static int snd_pcm_plugin_action(snd_pcm_t *pcm, int channel, int action)
+static int snd_pcm_plugin_action(snd_pcm_t *pcm, int channel, int action,
+ unsigned long data)
{
snd_pcm_plugin_t *plugin;
int err;
plugin = pcm->plugin_first[channel];
while (plugin) {
if (plugin->action) {
- if ((err = plugin->action(plugin, action))<0)
+ if ((err = plugin->action(plugin, action, data))<0)
return err;
}
plugin = plugin->next;
err = snd_pcm_channel_params(pcm, &hwparams);
if (err < 0)
return err;
- err = snd_pcm_plugin_action(pcm, hwparams.channel, INIT);
+ err = snd_pcm_plugin_action(pcm, hwparams.channel, INIT, (long)&hwparams);
if (err < 0)
return err;
return 0;
{
int err;
- if ((err = snd_pcm_plugin_action(pcm, channel, PREPARE))<0)
+ if ((err = snd_pcm_plugin_action(pcm, channel, PREPARE, 0))<0)
return err;
return snd_pcm_channel_prepare(pcm, channel);
}
{
int err;
- if ((err = snd_pcm_plugin_action(pcm, SND_PCM_CHANNEL_PLAYBACK, DRAIN))<0)
+ if ((err = snd_pcm_plugin_action(pcm, SND_PCM_CHANNEL_PLAYBACK, DRAIN, 0))<0)
return err;
return snd_pcm_playback_drain(pcm);
}
int err;
pdprintf("flush\n");
- if ((err = snd_pcm_plugin_action(pcm, channel, FLUSH))<0)
+ if ((err = snd_pcm_plugin_action(pcm, channel, FLUSH, 0))<0)
return err;
return snd_pcm_channel_flush(pcm, channel);
}
snd_pcm_mmap_control_t *control;
char *buffer;
int frag;
+ int start_mode, stop_mode;
+ int frags, frags_used;
+ int frags_min, frags_max;
+ unsigned int lastblock;
};
+static int playback_ok(struct mmap_private_data *data)
+{
+ snd_pcm_mmap_control_t *control = data->control;
+ int delta = control->status.block;
+
+ if (delta < data->lastblock) {
+ delta += (~0 - data->lastblock) + 1;
+ } else {
+ delta -= data->lastblock;
+ }
+ data->frags_used -= delta;
+ if (data->frags_used < 0) {
+ /* correction for rollover */
+ data->frag += -data->frags_used;
+ data->frag %= data->frags;
+ data->frags_used = 0;
+ }
+ data->lastblock += delta;
+ return data->frags_used <= data->frags_max &&
+ (data->frags - data->frags_used) >= data->frags_min;
+}
+
static int poll_playback(snd_pcm_t *pcm)
{
int err;
return err < 0 ? err : 0;
}
-static int query_playback(struct mmap_private_data *data,
- snd_pcm_mmap_control_t *control,
- int not_use_poll)
+static int query_playback(struct mmap_private_data *data, int not_use_poll)
{
+ snd_pcm_mmap_control_t *control = data->control;
int err;
switch (control->status.status) {
case SND_PCM_STATUS_PREPARED:
- err = snd_pcm_channel_go(data->pcm, data->channel);
- if (err < 0)
- return err;
+ if (data->start_mode == SND_PCM_START_GO)
+ return -EAGAIN;
+ if ((data->start_mode == SND_PCM_START_DATA &&
+ playback_ok(data)) ||
+ (data->start_mode == SND_PCM_START_FULL &&
+ data->frags_used == data->frags)) {
+ err = snd_pcm_channel_go(data->pcm, data->channel);
+ if (err < 0)
+ return err;
+ }
break;
case SND_PCM_STATUS_RUNNING:
if (!not_use_poll) {
return 0;
}
+static int capture_ok(struct mmap_private_data *data)
+{
+ snd_pcm_mmap_control_t *control = data->control;
+ int delta = control->status.block;
+
+ if (delta < data->lastblock) {
+ delta += (~0 - data->lastblock) + 1;
+ } else {
+ delta -= data->lastblock;
+ }
+ data->frags_used += delta;
+ if (data->frags_used > data->frags) {
+ /* correction for rollover */
+ data->frag += data->frags_used - data->frags;
+ data->frag %= data->frags;
+ data->frags_used = data->frags;
+ }
+ data->lastblock += delta;
+ return data->frags_used >= data->frags_min;
+}
+
static int poll_capture(snd_pcm_t *pcm)
{
int err;
return err < 0 ? err : 0;
}
-static int query_capture(struct mmap_private_data *data,
- snd_pcm_mmap_control_t *control,
- int not_use_poll)
+static int query_capture(struct mmap_private_data *data, int not_use_poll)
{
+ snd_pcm_mmap_control_t *control = data->control;
int err;
switch (control->status.status) {
case SND_PCM_STATUS_PREPARED:
+ if (data->start_mode != SND_PCM_START_DATA)
+ return -EAGAIN;
err = snd_pcm_channel_go(data->pcm, data->channel);
if (err < 0)
return err;
break;
case SND_PCM_STATUS_RUNNING:
if (!not_use_poll) {
- control->status.expblock = control->status.block + 1;
+ control->status.expblock = control->status.block + data->frags_min;
err = poll_capture(data->pcm);
if (err < 0)
return err;
*buffer = data->buffer + control->fragments[data->frag].addr;
if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
/* wait until the block is not free */
- while (control->fragments[data->frag].data) {
- err = query_playback(data, control, 0);
+ while (!playback_ok(data)) {
+ err = query_playback(data, 0);
if (err < 0)
return err;
}
return -EINVAL;
}
if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
- while (control->fragments[data->frag].data) {
- err = query_playback(data, control, 0);
+ while (!playback_ok(data)) {
+ err = query_playback(data, 0);
if (err < 0)
return err;
}
memcpy(addr, dst_ptr, dst_size);
control->fragments[data->frag++].data = 1;
data->frag %= control->status.frags;
+ data->frags_used++;
} else {
int frag;
for (voice = 0; voice < control->status.voices; voice++) {
- frag = data->frag + (voice * (control->status.frags / control->status.voices));
+ frag = data->frag + (voice * data->frags);
while (control->fragments[frag].data) {
- err = query_playback(data, control, 1);
+ err = query_playback(data, 1);
if (err < 0)
return err;
}
dst_ptr += control->status.frag_size;
}
data->frag++;
- data->frag %= control->status.frags;
+ data->frag %= data->frags;
+ data->frags_used++;
}
return dst_size;
} else if (data->channel == SND_PCM_CHANNEL_CAPTURE) {
- while (!control->fragments[data->frag].data) {
- err = query_capture(data, control, 0);
+ while (!capture_ok(data)) {
+ err = query_capture(data, 0);
if (err < 0)
return err;
}
memcpy(dst_ptr, addr, dst_size);
control->fragments[data->frag++].data = 0;
data->frag %= control->status.frags;
+ data->frags_used--;
} else {
+ int frag;
+
for (voice = 0; voice < control->status.voices; voice++) {
+ frag = data->frag + (voice * data->frags);
while (!control->fragments[data->frag].data) {
- err = query_capture(data, control, 1);
+ err = query_capture(data, 1);
if (err < 0)
return err;
}
- addr = data->buffer + control->fragments[data->frag].addr;
+ addr = data->buffer + control->fragments[frag].addr;
if (dst_ptr != addr)
memcpy(dst_ptr, addr, control->status.frag_size);
- control->fragments[data->frag++].data = 0;
- data->frag %= control->status.frags;
+ control->fragments[frag].data = 0;
dst_ptr += control->status.frag_size;
}
+ data->frag++;
+ data->frag %= data->frags;
+ data->frags_used--;
}
return dst_size;
} else {
}
}
-static int mmap_action(snd_pcm_plugin_t *plugin, snd_pcm_plugin_action_t action)
+static int mmap_action(snd_pcm_plugin_t *plugin,
+ snd_pcm_plugin_action_t action,
+ unsigned long udata)
{
struct mmap_private_data *data;
return -EINVAL;
data = (struct mmap_private_data *)snd_pcm_plugin_extra_data(plugin);
if (action == INIT) {
+ snd_pcm_channel_params_t *params;
+ snd_pcm_channel_setup_t setup;
+ int result, frags;
+
if (data->control)
snd_pcm_munmap(data->pcm, data->channel);
- return snd_pcm_mmap(data->pcm, data->channel, &data->control, (void **)&data->buffer);
+ result = snd_pcm_mmap(data->pcm, data->channel, &data->control, (void **)&data->buffer);
+ if (result < 0)
+ return result;
+ params = (snd_pcm_channel_params_t *)udata;
+ data->start_mode = params->start_mode;
+ data->stop_mode = params->stop_mode;
+ memset(&setup, 0, sizeof(setup));
+ setup.channel = data->channel;
+ if ((result = snd_pcm_channel_setup(data->pcm, &setup)) < 0)
+ return result;
+ data->frags = setup.buf.block.frags;
+ data->frags_min = setup.buf.block.frags_min;
+ data->frags_max = setup.buf.block.frags_max;
+ if (data->frags_min < 0)
+ data->frags_min = 0;
+ if (data->frags_min >= setup.buf.block.frags)
+ data->frags_min = setup.buf.block.frags - 1;
+ if (data->frags_max < 0)
+ data->frags_max = setup.buf.block.frags + data->frags_max;
+ if (data->frags_max < data->frags_min)
+ data->frags_max = data->frags_min;
+ if (data->frags_max < 1)
+ data->frags_max = 1;
+ if (data->frags_max > setup.buf.block.frags)
+ data->frags_max = setup.buf.block.frags;
+ return 0;
} else if (action == PREPARE) {
data->frag = 0;
+ data->lastblock = 0;
} else if (action == DRAIN && data->channel == SND_PCM_CHANNEL_PLAYBACK) {
data->frag = 0;
+ data->lastblock = 0;
} else if (action == FLUSH) {
data->frag = 0;
+ data->lastblock = 0;
}
return 0; /* silenty ignore other actions */
}