Added undo callbacks for snd_pcm_plugin based plugins.
- helpers when mmap_commit proceed only a partial transfer
Fixes to avail_update implementation in pcm_hw.c.
f = *frames;
avail = snd_pcm_mmap_avail(pcm);
if (avail > pcm->buffer_size)
- return -EPIPE;
+ avail = pcm->buffer_size;
if (f > avail)
f = avail;
if (f > cont)
snd_pcm_uframes_t offset,
snd_pcm_uframes_t frames)
{
- int res;
- snd_pcm_uframes_t appl_ptr;
-
assert(pcm);
assert(offset == *pcm->appl_ptr % pcm->buffer_size);
assert(frames <= snd_pcm_mmap_avail(pcm));
- appl_ptr = *pcm->appl_ptr;
- res = pcm->fast_ops->mmap_commit(pcm->fast_op_arg, offset, frames);
- if (res < 0) {
- snd_pcm_sframes_t diff;
-
- if (appl_ptr == *pcm->appl_ptr)
- return res;
- diff = *pcm->appl_ptr - appl_ptr;
- if (diff < 0)
- diff += pcm->boundary;
- assert(diff >= 0 && (snd_pcm_uframes_t)diff < pcm->boundary);
- return diff;
- }
- return frames;
+ return pcm->fast_ops->mmap_commit(pcm->fast_op_arg, offset, frames);
}
#ifndef DOC_HIDDEN
return -ENOMEM;
}
adpcm->sformat = sformat;
+ snd_pcm_plugin_init(&adpcm->plug);
adpcm->plug.read = snd_pcm_adpcm_read_areas;
adpcm->plug.write = snd_pcm_adpcm_write_areas;
adpcm->plug.init = snd_pcm_adpcm_init;
if (!alaw) {
return -ENOMEM;
}
+ snd_pcm_plugin_init(&alaw->plug);
alaw->sformat = sformat;
alaw->plug.read = snd_pcm_alaw_read_areas;
alaw->plug.write = snd_pcm_alaw_write_areas;
+ alaw->plug.undo_read = snd_pcm_plugin_undo_read_generic;
+ alaw->plug.undo_write = snd_pcm_plugin_undo_write_generic;
alaw->plug.slave = slave;
alaw->plug.close_slave = close_slave;
if (!copy) {
return -ENOMEM;
}
+ snd_pcm_plugin_init(©->plug);
copy->plug.read = snd_pcm_copy_read_areas;
copy->plug.write = snd_pcm_copy_write_areas;
+ copy->plug.undo_read = snd_pcm_plugin_undo_read_generic;
+ copy->plug.undo_write = snd_pcm_plugin_undo_write_generic;
copy->plug.slave = slave;
copy->plug.close_slave = close_slave;
static void snd_pcm_file_add_frames(snd_pcm_t *pcm,
const snd_pcm_channel_area_t *areas,
- snd_pcm_uframes_t offset, snd_pcm_uframes_t frames)
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t frames)
{
snd_pcm_file_t *file = pcm->private_data;
while (frames > 0) {
return n;
}
-static int snd_pcm_file_mmap_commit(snd_pcm_t *pcm,
- snd_pcm_uframes_t offset,
- snd_pcm_uframes_t size)
+static snd_pcm_sframes_t snd_pcm_file_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
{
snd_pcm_file_t *file = pcm->private_data;
snd_pcm_uframes_t ofs;
snd_pcm_uframes_t siz = size;
const snd_pcm_channel_area_t *areas;
+ snd_pcm_sframes_t result;
+
snd_pcm_mmap_begin(file->slave, &areas, &ofs, &siz);
assert(ofs == offset && siz == size);
- snd_pcm_mmap_commit(file->slave, ofs, siz);
- snd_pcm_file_add_frames(pcm, areas, ofs, siz);
- return 0;
+ result = snd_pcm_mmap_commit(file->slave, ofs, siz);
+ if (result > 0)
+ snd_pcm_file_add_frames(pcm, areas, ofs, result);
+ return result;
}
static snd_pcm_sframes_t snd_pcm_file_avail_update(snd_pcm_t *pcm)
return snd_pcm_readn(h->slave, bufs, size);
}
-static int snd_pcm_hooks_mmap_commit(snd_pcm_t *pcm,
- snd_pcm_uframes_t offset,
- snd_pcm_uframes_t size)
+static snd_pcm_sframes_t snd_pcm_hooks_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
{
snd_pcm_hooks_t *h = pcm->private_data;
return snd_pcm_mmap_commit(h->slave, offset, size);
#define UPDATE_SHADOW_PTR(hw) \
do { if (hw->shadow_appl_ptr && !hw->avail_update_flag) \
hw->appl_ptr = hw->mmap_control->appl_ptr; } while (0)
+#define FAST_PCM_STATE(hw) \
+ ((enum sndrv_pcm_state) (hw)->mmap_status->state)
#endif /* DOC_HIDDEN */
return 0;
}
-static int snd_pcm_hw_mmap_commit(snd_pcm_t *pcm,
- snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
- snd_pcm_uframes_t size)
+static snd_pcm_sframes_t snd_pcm_hw_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t size)
{
snd_pcm_hw_t *hw = pcm->private_data;
+
if (hw->mmap_shm) {
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
- snd_pcm_sframes_t res;
+ snd_pcm_sframes_t result = 0, res;
do {
res = snd_pcm_write_mmap(pcm, size);
if (res < 0)
- return res;
+ return result > 0 ? result : res;
size -= res;
+ result += res;
} while (size > 0);
- return 0;
+ return result;
} else {
snd_pcm_hw_t *hw = pcm->private_data;
assert(hw->shadow_appl_ptr);
}
}
snd_pcm_mmap_appl_forward(pcm, size);
- return 0;
+ return size;
}
static snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
return err;
}
}
- if (avail >= pcm->stop_threshold) {
- /* SNDRV_PCM_IOCTL_XRUN ioctl has been implemented since PCM kernel API 2.0.1 */
- if (SNDRV_PROTOCOL_VERSION(2, 0, 1) <= hw->version) {
- if (ioctl(hw->fd, SND_PCM_IOCTL_XRUN) < 0)
- return -errno;
+ switch (FAST_PCM_STATE(hw)) {
+ case SNDRV_PCM_STATE_RUNNING:
+ if (avail >= pcm->stop_threshold) {
+ /* SNDRV_PCM_IOCTL_XRUN ioctl has been implemented since PCM kernel API 2.0.1 */
+ if (SNDRV_PROTOCOL_VERSION(2, 0, 1) <= hw->version) {
+ if (ioctl(hw->fd, SND_PCM_IOCTL_XRUN) < 0)
+ return -errno;
+ }
+ /* everything is ok, state == SND_PCM_STATE_XRUN at the moment */
+ return -EPIPE;
}
- /* everything is ok, state == SND_PCM_STATE_XRUN at the moment */
+ case SNDRV_PCM_STATE_XRUN:
return -EPIPE;
+ default:
+ break;
}
return avail;
}
ladspa = calloc(1, sizeof(snd_pcm_ladspa_t));
if (!ladspa)
return -ENOMEM;
+ snd_pcm_plugin_init(&ladspa->plug);
ladspa->plug.init = snd_pcm_ladspa_init;
ladspa->plug.read = snd_pcm_ladspa_read_areas;
ladspa->plug.write = snd_pcm_ladspa_write_areas;
+ ladspa->plug.undo_read = snd_pcm_plugin_undo_read_generic;
+ ladspa->plug.undo_write = snd_pcm_plugin_undo_write_generic;
ladspa->plug.slave = slave;
ladspa->plug.close_slave = close_slave;
if (!lfloat) {
return -ENOMEM;
}
+ snd_pcm_plugin_init(&lfloat->plug);
lfloat->sformat = sformat;
lfloat->plug.read = snd_pcm_lfloat_read_areas;
lfloat->plug.write = snd_pcm_lfloat_write_areas;
+ lfloat->plug.undo_read = snd_pcm_plugin_undo_read_generic;
+ lfloat->plug.undo_write = snd_pcm_plugin_undo_write_generic;
lfloat->plug.slave = slave;
lfloat->plug.close_slave = close_slave;
if (!linear) {
return -ENOMEM;
}
+ snd_pcm_plugin_init(&linear->plug);
linear->sformat = sformat;
linear->plug.read = snd_pcm_linear_read_areas;
linear->plug.write = snd_pcm_linear_write_areas;
+ linear->plug.undo_read = snd_pcm_plugin_undo_read_generic;
+ linear->plug.undo_write = snd_pcm_plugin_undo_write_generic;
linear->plug.slave = slave;
linear->plug.close_slave = close_slave;
snd_pcm_sframes_t (*readi)(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size);
snd_pcm_sframes_t (*readn)(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size);
snd_pcm_sframes_t (*avail_update)(snd_pcm_t *pcm);
- int (*mmap_commit)(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t size);
+ snd_pcm_sframes_t (*mmap_commit)(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t size);
} snd_pcm_fast_ops_t;
struct _snd_pcm {
return snd_pcm_resume(meter->slave);
}
-static int snd_pcm_meter_mmap_commit(snd_pcm_t *pcm,
- snd_pcm_uframes_t offset,
- snd_pcm_uframes_t size)
+static snd_pcm_sframes_t snd_pcm_meter_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
{
snd_pcm_meter_t *meter = pcm->private_data;
snd_pcm_uframes_t old_rptr = *pcm->appl_ptr;
if (result <= 0)
return result;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
- snd_pcm_meter_add_frames(pcm, snd_pcm_mmap_areas(pcm), old_rptr, size);
+ snd_pcm_meter_add_frames(pcm, snd_pcm_mmap_areas(pcm), old_rptr, result);
meter->rptr = *pcm->appl_ptr;
}
- return 0;
+ return result;
}
static snd_pcm_sframes_t snd_pcm_meter_avail_update(snd_pcm_t *pcm)
snd_pcm_uframes_t size)
{
snd_pcm_uframes_t xfer = 0;
- int err;
assert(snd_pcm_mmap_playback_avail(pcm) >= size);
while (size > 0) {
const snd_pcm_channel_area_t *pcm_areas;
snd_pcm_uframes_t pcm_offset;
snd_pcm_uframes_t frames = size;
+ snd_pcm_sframes_t result;
+
snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
snd_pcm_areas_copy(pcm_areas, pcm_offset,
areas, offset,
pcm->channels,
frames, pcm->format);
- err = snd_pcm_mmap_commit(pcm, pcm_offset, frames);
- if (err < 0)
- return xfer > 0 ? xfer : err;
- offset += frames;
- xfer += frames;
- size -= frames;
+ result = snd_pcm_mmap_commit(pcm, pcm_offset, frames);
+ if (result < 0)
+ return xfer > 0 ? xfer : result;
+ offset += result;
+ xfer += result;
+ size -= result;
}
return xfer;
}
snd_pcm_uframes_t size)
{
snd_pcm_uframes_t xfer = 0;
- int err;
assert(snd_pcm_mmap_capture_avail(pcm) >= size);
while (size > 0) {
const snd_pcm_channel_area_t *pcm_areas;
snd_pcm_uframes_t pcm_offset;
snd_pcm_uframes_t frames = size;
+ snd_pcm_sframes_t result;
+
snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
snd_pcm_areas_copy(areas, offset,
pcm_areas, pcm_offset,
pcm->channels,
frames, pcm->format);
- err = snd_pcm_mmap_commit(pcm, pcm_offset, frames);
- if (err < 0)
- return xfer > 0 ? xfer : err;
- offset += frames;
- xfer += frames;
- size -= frames;
+ result = snd_pcm_mmap_commit(pcm, pcm_offset, frames);
+ if (result < 0)
+ return xfer > 0 ? xfer : result;
+ offset += result;
+ xfer += result;
+ size -= result;
}
return xfer;
}
if (!mulaw) {
return -ENOMEM;
}
+ snd_pcm_plugin_init(&mulaw->plug);
mulaw->sformat = sformat;
mulaw->plug.read = snd_pcm_mulaw_read_areas;
mulaw->plug.write = snd_pcm_mulaw_write_areas;
+ mulaw->plug.undo_read = snd_pcm_plugin_undo_read_generic;
+ mulaw->plug.undo_write = snd_pcm_plugin_undo_write_generic;
mulaw->plug.slave = slave;
mulaw->plug.close_slave = close_slave;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave_i = multi->slaves[i].pcm;
snd_pcm_uframes_t f = pos[i] - frames;
- if (f > 0)
- snd_pcm_mmap_commit(slave_i, snd_pcm_mmap_offset(slave_i), f);
+ snd_pcm_sframes_t result;
+ if (f > 0) {
+ result = snd_pcm_mmap_commit(slave_i, snd_pcm_mmap_offset(slave_i), f);
+ if (result < 0)
+ return result;
+ if ((snd_pcm_uframes_t)result != f)
+ return -EIO;
+ }
}
return frames;
}
return err;
}
-static int snd_pcm_multi_mmap_commit(snd_pcm_t *pcm,
- snd_pcm_uframes_t offset,
- snd_pcm_uframes_t size)
+static snd_pcm_sframes_t snd_pcm_multi_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave;
unsigned int i;
- int err;
+ snd_pcm_sframes_t result;
for (i = 0; i < multi->slaves_count; ++i) {
slave = multi->slaves[i].pcm;
- err = snd_pcm_mmap_commit(slave, offset, size);
- if (err < 0)
- return err;
+ result = snd_pcm_mmap_commit(slave, offset, size);
+ if (result < 0)
+ return result;
+ if ((snd_pcm_uframes_t)result != size)
+ return -EIO;
}
return 0;
}
return snd_pcm_read_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas);
}
-static int snd_pcm_null_mmap_commit(snd_pcm_t *pcm,
- snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
- snd_pcm_uframes_t size)
+static snd_pcm_sframes_t snd_pcm_null_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t size)
{
snd_pcm_sframes_t res;
res = snd_pcm_null_fwd(pcm, size);
if (res < 0)
return res;
- assert((snd_pcm_uframes_t)res == size);
- return 0;
+ return res;
}
static snd_pcm_sframes_t snd_pcm_null_avail_update(snd_pcm_t *pcm)
#ifndef DOC_HIDDEN
+static snd_pcm_sframes_t
+snd_pcm_plugin_undo_read(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
+ const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
+{
+ return -EIO;
+}
+
+static snd_pcm_sframes_t
+snd_pcm_plugin_undo_write(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
+ const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
+{
+ return -EIO;
+}
+
+snd_pcm_sframes_t
+snd_pcm_plugin_undo_read_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
+ const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t slave_undo_size)
+{
+ return slave_undo_size;
+}
+
+snd_pcm_sframes_t
+snd_pcm_plugin_undo_write_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
+ const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t slave_undo_size)
+{
+ return slave_undo_size;
+}
+
+void snd_pcm_plugin_init(snd_pcm_plugin_t *plugin)
+{
+ memset(plugin, 0, sizeof(snd_pcm_plugin_t));
+ plugin->undo_read = snd_pcm_plugin_undo_read;
+ plugin->undo_write = snd_pcm_plugin_undo_write;
+}
+
int snd_pcm_plugin_close(snd_pcm_t *pcm)
{
snd_pcm_plugin_t *plugin = pcm->private_data;
snd_pcm_plugin_t *plugin = pcm->private_data;
snd_pcm_t *slave = plugin->slave;
snd_pcm_uframes_t xfer = 0;
- snd_pcm_sframes_t err;
+ snd_pcm_sframes_t result;
while (size > 0) {
snd_pcm_uframes_t frames = size;
assert(slave_frames <= snd_pcm_mmap_playback_avail(slave));
snd_atomic_write_begin(&plugin->watom);
snd_pcm_mmap_appl_forward(pcm, frames);
- err = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
+ result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
snd_atomic_write_end(&plugin->watom);
- if (err < 0)
- return xfer > 0 ? xfer : err;
- offset += frames;
- xfer += frames;
- size -= frames;
+ if (result < 0)
+ return xfer > 0 ? xfer : result;
+ offset += result;
+ xfer += result;
+ size -= result;
}
return xfer;
}
snd_pcm_plugin_t *plugin = pcm->private_data;
snd_pcm_t *slave = plugin->slave;
snd_pcm_uframes_t xfer = 0;
- snd_pcm_sframes_t err;
+ snd_pcm_sframes_t result;
while (size > 0) {
snd_pcm_uframes_t frames = size;
assert(slave_frames <= snd_pcm_mmap_capture_avail(slave));
snd_atomic_write_begin(&plugin->watom);
snd_pcm_mmap_appl_forward(pcm, frames);
- err = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
+ result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
snd_atomic_write_end(&plugin->watom);
- if (err < 0)
- return xfer > 0 ? xfer : err;
- offset += frames;
- xfer += frames;
- size -= frames;
+ if (result < 0)
+ return xfer > 0 ? xfer : result;
+ offset += result;
+ xfer += result;
+ size -= result;
}
return xfer;
}
snd_pcm_plugin_read_areas);
}
-int snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm,
- snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
- snd_pcm_uframes_t size)
+snd_pcm_sframes_t snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t size)
{
snd_pcm_plugin_t *plugin = pcm->private_data;
snd_pcm_t *slave = plugin->slave;
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t appl_offset;
snd_pcm_sframes_t slave_size;
+ snd_pcm_sframes_t xfer;
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
snd_atomic_write_begin(&plugin->watom);
slave_size = snd_pcm_avail_update(slave);
if (slave_size < 0)
return slave_size;
- if ((snd_pcm_uframes_t)slave_size < size)
- return -EIO;
areas = snd_pcm_mmap_areas(pcm);
appl_offset = snd_pcm_mmap_offset(pcm);
+ xfer = 0;
while (size > 0 && slave_size > 0) {
snd_pcm_uframes_t frames = size;
snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
const snd_pcm_channel_area_t *slave_areas;
snd_pcm_uframes_t slave_offset;
snd_pcm_uframes_t slave_frames = ULONG_MAX;
+ snd_pcm_sframes_t result;
+ int err;
- snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
+ err = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
+ if (err < 0)
+ return xfer > 0 ? xfer : err;
if (frames > cont)
frames = cont;
frames = plugin->write(pcm, areas, appl_offset, frames,
slave_areas, slave_offset, &slave_frames);
snd_atomic_write_begin(&plugin->watom);
snd_pcm_mmap_appl_forward(pcm, frames);
- snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
+ result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
snd_atomic_write_end(&plugin->watom);
+ if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
+ snd_pcm_sframes_t res;
+
+ res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
+ if (res < 0)
+ return xfer > 0 ? xfer : res;
+ frames -= res;
+ }
+ if (result <= 0)
+ return xfer > 0 ? xfer : result;
if (frames == cont)
appl_offset = 0;
else
- appl_offset += frames;
+ appl_offset += result;
size -= frames;
- slave_size -= slave_frames;
+ slave_size -= frames;
+ xfer += frames;
}
assert(size == 0);
- return 0;
+ return xfer;
}
snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t xfer, hw_offset, size;
- xfer = snd_pcm_mmap_capture_avail(pcm);
- size = pcm->buffer_size - xfer;
+ size = snd_pcm_mmap_capture_avail(pcm);
+ size = pcm->buffer_size - size;
areas = snd_pcm_mmap_areas(pcm);
hw_offset = snd_pcm_mmap_hw_offset(pcm);
+ xfer = 0;
while (size > 0 && slave_size > 0) {
- snd_pcm_uframes_t frames = size;
+ snd_pcm_uframes_t frames = size;
snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
const snd_pcm_channel_area_t *slave_areas;
snd_pcm_uframes_t slave_offset;
snd_pcm_uframes_t slave_frames = ULONG_MAX;
- snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
+ snd_pcm_sframes_t result;
+ int err;
+
+ err = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
+ if (err < 0)
+ return xfer > 0 ? xfer : err;
if (frames > cont)
frames = cont;
frames = plugin->read(pcm, areas, hw_offset, frames,
slave_areas, slave_offset, &slave_frames);
snd_atomic_write_begin(&plugin->watom);
snd_pcm_mmap_hw_forward(pcm, frames);
- snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
+ result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
snd_atomic_write_end(&plugin->watom);
- xfer += frames;
+ if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
+ snd_pcm_sframes_t res;
+
+ res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result);
+ if (res < 0)
+ return xfer > 0 ? xfer : res;
+ frames -= res;
+ }
+ if (result <= 0)
+ return xfer > 0 ? xfer : result;
if (frames == cont)
hw_offset = 0;
else
hw_offset += frames;
size -= frames;
slave_size -= slave_frames;
+ xfer += frames;
}
return xfer;
}
snd_pcm_uframes_t slave_offset,
snd_pcm_uframes_t *slave_sizep);
+typedef snd_pcm_sframes_t (*snd_pcm_slave_xfer_areas_undo_func_t)
+ (snd_pcm_t *pcm,
+ const snd_pcm_channel_area_t *res_areas, /* result areas */
+ snd_pcm_uframes_t res_offset, /* offset of result areas */
+ snd_pcm_uframes_t res_size, /* size of result areas */
+ snd_pcm_uframes_t slave_undo_size);
+
typedef struct {
snd_pcm_t *slave;
int close_slave;
snd_pcm_slave_xfer_areas_func_t read;
snd_pcm_slave_xfer_areas_func_t write;
+ snd_pcm_slave_xfer_areas_undo_func_t undo_read;
+ snd_pcm_slave_xfer_areas_undo_func_t undo_write;
snd_pcm_sframes_t (*client_frames)(snd_pcm_t *pcm, snd_pcm_sframes_t frames);
snd_pcm_sframes_t (*slave_frames)(snd_pcm_t *pcm, snd_pcm_sframes_t frames);
int (*init)(snd_pcm_t *pcm);
snd_atomic_write_t watom;
} snd_pcm_plugin_t;
+void snd_pcm_plugin_init(snd_pcm_plugin_t *plugin);
int snd_pcm_plugin_close(snd_pcm_t *pcm);
int snd_pcm_plugin_card(snd_pcm_t *pcm);
int snd_pcm_plugin_nonblock(snd_pcm_t *pcm, int nonblock);
snd_pcm_sframes_t snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size);
snd_pcm_sframes_t snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size);
snd_pcm_sframes_t snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size);
-int snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t size);
+snd_pcm_sframes_t snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t size);
snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm);
int snd_pcm_plugin_mmap_status(snd_pcm_t *pcm);
int snd_pcm_plugin_mmap_control(snd_pcm_t *pcm);
extern snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops;
+snd_pcm_sframes_t snd_pcm_plugin_undo_read_generic
+ (snd_pcm_t *pcm,
+ const snd_pcm_channel_area_t *res_areas, /* result areas */
+ snd_pcm_uframes_t res_offset, /* offset of result areas */
+ snd_pcm_uframes_t res_size, /* size of result areas */
+ snd_pcm_uframes_t slave_undo_size);
+
+snd_pcm_sframes_t snd_pcm_plugin_undo_write_generic
+ (snd_pcm_t *pcm,
+ const snd_pcm_channel_area_t *res_areas, /* result areas */
+ snd_pcm_uframes_t res_offset, /* offset of result areas */
+ snd_pcm_uframes_t res_size, /* size of result areas */
+ snd_pcm_uframes_t slave_undo_size);
+
int snd_pcm_linear_get_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format);
int snd_pcm_linear_put_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format);
int snd_pcm_linear_convert_index(snd_pcm_format_t src_format, snd_pcm_format_t dst_format);
if (!rate) {
return -ENOMEM;
}
+ snd_pcm_plugin_init(&rate->plug);
rate->srate = srate;
rate->sformat = sformat;
rate->plug.read = snd_pcm_rate_read_areas;
if (!route) {
return -ENOMEM;
}
+ snd_pcm_plugin_init(&route->plug);
route->sformat = sformat;
route->schannels = schannels;
route->plug.read = snd_pcm_route_read_areas;
route->plug.write = snd_pcm_route_write_areas;
+ route->plug.undo_read = snd_pcm_plugin_undo_read_generic;
+ route->plug.undo_write = snd_pcm_plugin_undo_write_generic;
route->plug.slave = slave;
route->plug.close_slave = close_slave;
}
/* Call it with mutex held */
-static int _snd_pcm_share_mmap_commit(snd_pcm_t *pcm,
- snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
- snd_pcm_uframes_t size)
+static snd_pcm_sframes_t _snd_pcm_share_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t size)
{
snd_pcm_share_t *share = pcm->private_data;
snd_pcm_share_slave_t *slave = share->slave;
}
_snd_pcm_share_update(pcm);
}
- return 0;
+ return size;
}
-static int snd_pcm_share_mmap_commit(snd_pcm_t *pcm,
- snd_pcm_uframes_t offset,
- snd_pcm_uframes_t size)
+static snd_pcm_sframes_t snd_pcm_share_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
{
snd_pcm_share_t *share = pcm->private_data;
snd_pcm_share_slave_t *slave = share->slave;
xfer += frames;
}
snd_pcm_mmap_appl_forward(pcm, hw_avail);
- if (slave->running_count == 0)
- snd_pcm_mmap_commit(spcm, snd_pcm_mmap_offset(spcm), hw_avail);
+ if (slave->running_count == 0) {
+ snd_pcm_sframes_t res;
+ res = snd_pcm_mmap_commit(spcm, snd_pcm_mmap_offset(spcm), hw_avail);
+ if (res < 0) {
+ err = res;
+ goto _end;
+ }
+ assert((snd_pcm_uframes_t)res == hw_avail);
+ }
}
if (slave->running_count == 0) {
err = snd_pcm_start(spcm);
}
#endif
-static int snd_pcm_shm_action(snd_pcm_t *pcm)
+static long snd_pcm_shm_action(snd_pcm_t *pcm)
{
snd_pcm_shm_t *shm = pcm->private_data;
int err;
return ctrl->result;
}
-static int snd_pcm_shm_action_fd(snd_pcm_t *pcm, int *fd)
+static long snd_pcm_shm_action_fd(snd_pcm_t *pcm, int *fd)
{
snd_pcm_shm_t *shm = pcm->private_data;
int err;
return snd_pcm_shm_action(pcm);
}
-static int snd_pcm_shm_mmap_commit(snd_pcm_t *pcm,
- snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
- snd_pcm_uframes_t size)
+static snd_pcm_sframes_t snd_pcm_shm_mmap_commit(snd_pcm_t *pcm,
+ snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
+ snd_pcm_uframes_t size)
{
snd_pcm_shm_t *shm = pcm->private_data;
volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;