--- /dev/null
+/*
+ * Atomic read/write
+ * Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <time.h>
+#include <sched.h>
+#include "atomic.h"
+
+void snd_atomic_read_wait(snd_atomic_read_t *t)
+{
+ volatile const snd_atomic_write_t *w = t->write;
+ unsigned int loops = 0;
+ struct timespec ts;
+ while (w->begin != w->end) {
+ if (loops < MAX_SPIN_COUNT) {
+ sched_yield();
+ loops++;
+ continue;
+ }
+ loops = 0;
+ ts.tv_sec = 0;
+ ts.tv_nsec = SPIN_SLEEP_DURATION;
+ nanosleep(&ts, NULL);
+ }
+}
+
--- /dev/null
+/*
+ * Atomic read/write
+ * Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <asm/system.h>
+
+/* Max number of times we must spin on a spinlock calling sched_yield().
+ After MAX_SPIN_COUNT iterations, we put the calling thread to sleep. */
+
+#ifndef MAX_SPIN_COUNT
+#define MAX_SPIN_COUNT 50
+#endif
+
+/* Duration of sleep (in nanoseconds) when we can't acquire a spinlock
+ after MAX_SPIN_COUNT iterations of sched_yield().
+ This MUST BE > 2ms.
+ (Otherwise the kernel does busy-waiting for realtime threads,
+ giving other threads no chance to run.) */
+
+#ifndef SPIN_SLEEP_DURATION
+#define SPIN_SLEEP_DURATION 2000001
+#endif
+
+typedef struct {
+ unsigned int begin, end;
+} snd_atomic_write_t;
+
+typedef struct {
+ volatile const snd_atomic_write_t *write;
+ unsigned int end;
+} snd_atomic_read_t;
+
+void snd_atomic_read_wait(snd_atomic_read_t *t);
+
+static inline void snd_atomic_write_init(snd_atomic_write_t *w)
+{
+ w->begin = 0;
+ w->end = 0;
+}
+
+static inline void snd_atomic_write_begin(snd_atomic_write_t *w)
+{
+ w->begin++;
+ wmb();
+}
+
+static inline void snd_atomic_write_end(snd_atomic_write_t *w)
+{
+ wmb();
+ w->end++;
+}
+
+static inline void snd_atomic_read_init(snd_atomic_read_t *r, snd_atomic_write_t *w)
+{
+ r->write = w;
+}
+
+static inline void snd_atomic_read_begin(snd_atomic_read_t *r)
+{
+ r->end = r->write->end;
+ rmb();
+}
+
+static inline int snd_atomic_read_ok(snd_atomic_read_t *r)
+{
+ rmb();
+ return r->end == r->write->begin;
+}
+
return snd_pcm_channel_info_shm(pcm, info, plugin->shmid);
}
-int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
-{
- snd_pcm_plugin_t *plugin = pcm->private_data;
- int err = snd_pcm_status(plugin->slave, status);
- if (err < 0)
- return err;
- status->avail = snd_pcm_mmap_avail(pcm);
- if (plugin->client_frames)
- status->avail_max = plugin->client_frames(pcm, status->avail_max);
- return 0;
-}
-
snd_pcm_state_t snd_pcm_plugin_state(snd_pcm_t *pcm)
{
snd_pcm_plugin_t *plugin = pcm->private_data;
int snd_pcm_plugin_prepare(snd_pcm_t *pcm)
{
snd_pcm_plugin_t *plugin = pcm->private_data;
- int err = snd_pcm_prepare(plugin->slave);
- if (err < 0)
+ int err;
+ snd_atomic_write_begin(&plugin->watom);
+ err = snd_pcm_prepare(plugin->slave);
+ if (err < 0) {
+ snd_atomic_write_end(&plugin->watom);
return err;
+ }
plugin->hw_ptr = 0;
plugin->appl_ptr = 0;
+ snd_atomic_write_end(&plugin->watom);
if (plugin->init) {
err = plugin->init(pcm);
if (err < 0)
int snd_pcm_plugin_reset(snd_pcm_t *pcm)
{
snd_pcm_plugin_t *plugin = pcm->private_data;
- int err = snd_pcm_reset(plugin->slave);
- if (err < 0)
+ int err;
+ snd_atomic_write_begin(&plugin->watom);
+ err = snd_pcm_reset(plugin->slave);
+ if (err < 0) {
+ snd_atomic_write_end(&plugin->watom);
return err;
+ }
plugin->hw_ptr = 0;
plugin->appl_ptr = 0;
+ snd_atomic_write_end(&plugin->watom);
if (plugin->init) {
err = plugin->init(pcm);
if (err < 0)
/* FIXME: rate plugin */
if (plugin->slave_frames)
frames = plugin->slave_frames(pcm, frames);
+ snd_atomic_write_begin(&plugin->watom);
err = snd_pcm_rewind(plugin->slave, frames);
if (err < 0) {
- if (n <= 0)
+ if (n <= 0) {
+ snd_atomic_write_end(&plugin->watom);
return err;
+ }
goto _end;
}
if (plugin->client_frames)
err = plugin->client_frames(pcm, err);
snd_pcm_mmap_hw_backward(pcm, err);
n += err;
- }
+ } else
+ snd_atomic_write_begin(&plugin->watom);
_end:
snd_pcm_mmap_appl_backward(pcm, n);
+ snd_atomic_write_end(&plugin->watom);
return n;
}
frames = plugin->write(pcm, areas, offset, frames,
slave_areas, slave_offset, &slave_frames);
assert(slave_frames <= snd_pcm_mmap_playback_avail(slave));
+ snd_atomic_write_begin(&plugin->watom);
snd_pcm_mmap_appl_forward(pcm, frames);
snd_pcm_mmap_hw_forward(pcm, frames);
snd_pcm_mmap_forward(slave, slave_frames);
+ snd_atomic_write_end(&plugin->watom);
offset += frames;
size -= frames;
if (slave_frames == slave_cont)
frames = plugin->read(pcm, areas, offset, frames,
slave_areas, slave_offset, &slave_frames);
assert(slave_frames <= snd_pcm_mmap_capture_avail(slave));
+ snd_atomic_write_begin(&plugin->watom);
snd_pcm_mmap_appl_forward(pcm, frames);
snd_pcm_mmap_hw_forward(pcm, frames);
snd_pcm_mmap_forward(slave, slave_frames);
+ snd_atomic_write_end(&plugin->watom);
offset += frames;
size -= frames;
if (slave_frames == slave_cont)
snd_pcm_uframes_t xfer, offset;
snd_pcm_uframes_t slave_offset, slave_size;
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
+ snd_atomic_write_begin(&plugin->watom);
snd_pcm_mmap_appl_forward(pcm, size);
+ snd_atomic_write_end(&plugin->watom);
return size;
}
slave_size = snd_pcm_avail_update(slave);
slave_frames = slave_cont;
frames = plugin->write(pcm, areas, offset, frames,
slave_areas, slave_offset, &slave_frames);
+ snd_atomic_write_begin(&plugin->watom);
snd_pcm_mmap_appl_forward(pcm, frames);
snd_pcm_mmap_hw_forward(pcm, frames);
snd_pcm_mmap_forward(slave, slave_frames);
+ snd_atomic_write_end(&plugin->watom);
xfer += frames;
if (frames == cont)
offset = 0;
slave_frames = slave_cont;
frames = plugin->read(pcm, areas, offset, frames,
slave_areas, slave_offset, &slave_frames);
+ snd_atomic_write_begin(&plugin->watom);
snd_pcm_mmap_hw_forward(pcm, frames);
snd_pcm_mmap_forward(slave, slave_frames);
+ snd_atomic_write_end(&plugin->watom);
xfer += frames;
if (frames == cont)
offset = 0;
return _snd_pcm_poll_descriptor(plugin->slave);
}
+int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
+{
+ snd_pcm_plugin_t *plugin = pcm->private_data;
+ snd_pcm_sframes_t err;
+ snd_atomic_read_t ratom;
+ snd_atomic_read_init(&ratom, &plugin->watom);
+ _again:
+ snd_atomic_read_begin(&ratom);
+ err = snd_pcm_status(plugin->slave, status);
+ if (err < 0) {
+ snd_atomic_read_ok(&ratom);
+ return err;
+ }
+ status->appl_ptr = plugin->appl_ptr;
+ status->hw_ptr = plugin->hw_ptr;
+ err = snd_pcm_plugin_avail_update(pcm);
+ if (err < 0)
+ status->avail = pcm->buffer_size;
+ else
+ status->avail = err;
+ snd_pcm_plugin_delay(pcm, &status->delay);
+ if (!snd_atomic_read_ok(&ratom)) {
+ snd_atomic_read_wait(&ratom);
+ goto _again;
+ }
+ if (plugin->client_frames)
+ status->avail_max = plugin->client_frames(pcm, status->avail_max);
+ return 0;
+}
+
int snd_pcm_plugin_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_plugin_t *plugin = pcm->private_data;