@@ -36,12 +36,14 @@
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/stringify.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
+#include <sound/timer.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("A loopback soundcard");
@@ -49,12 +51,15 @@ MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}");
#define MAX_PCM_SUBSTREAMS 8
+#define TIMER_SRC_JIFFIES -1
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
static int pcm_notify[SNDRV_CARDS];
+static char *timer_source[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] =
+ __stringify(TIMER_SRC_JIFFIES)};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for loopback soundcard.");
@@ -67,6 +72,12 @@ MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver.");
module_param_array(pcm_notify, int, NULL, 0444);
MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes.");
+/* This parameter has to be kept read only because switch between jiffies and
+ * sound timer is not supported.
+ */
+module_param_array(timer_source, charp, NULL, 0444);
+MODULE_PARM_DESC(timer_source, "Sound card number of timer to be used (>=0). -2 jiffies timer [default].");
+
#define NO_PITCH 100000
#define CABLE_VALID_PLAYBACK BIT(SNDRV_PCM_STREAM_PLAYBACK)
@@ -116,6 +127,13 @@ struct loopback_cable {
unsigned int pause;
/* timer specific */
struct loopback_ops *ops;
+ /* When loopback.timer_source > TIMER_SRC_JIFFIES */
+ struct {
+ int owner;
+ struct snd_timer_id id;
+ struct tasklet_struct event_tasklet;
+ struct snd_timer_instance *instance;
+ } snd_timer;
};
struct loopback_setup {
@@ -136,6 +154,8 @@ struct loopback {
struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2];
struct snd_pcm *pcm[2];
struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2];
+ char *timer_source_name;
+ int timer_source;
};
struct loopback_pcm {
@@ -159,6 +179,7 @@ struct loopback_pcm {
unsigned int period_size_frac; /* period size in jiffies ticks */
unsigned int last_drift;
unsigned long last_jiffies;
+ /* When loopback.timer_source <= TIMER_SRC_JIFFIES */
struct timer_list timer;
};
@@ -227,6 +248,31 @@ static int loopback_jiffies_timer_start(struct loopback_pcm *dpcm)
}
/* call in cable->lock */
+static int loopback_snd_timer_start(struct loopback_pcm *dpcm)
+{
+ int err;
+
+ /* Loopback device has to use same period as timer card. Therefore
+ * wake up for each snd_pcm_period_elapsed() call of timer card.
+ */
+ err = snd_timer_start(dpcm->cable->snd_timer.instance, 1);
+ if (err < 0) {
+ /* do not report error if trying to start but already
+ * running. For example called by opposit substream
+ * of the same cable
+ */
+ if (err == -EBUSY)
+ return 0;
+
+ pcm_err(dpcm->substream->pcm, "snd_timer_start(card %d) failed with %d",
+ dpcm->cable->snd_timer.id.card, err);
+ return err;
+ }
+
+ return 0;
+}
+
+/* call in cable->lock */
static inline int loopback_jiffies_timer_stop(struct loopback_pcm *dpcm)
{
del_timer(&dpcm->timer);
@@ -235,6 +281,24 @@ static inline int loopback_jiffies_timer_stop(struct loopback_pcm *dpcm)
return 0;
}
+/* call in cable->lock */
+static int loopback_snd_timer_stop(struct loopback_pcm *dpcm)
+{
+ int err;
+
+ /* only stop if both devices (playback and capture) are not running */
+ if (dpcm->cable->running)
+ return 0;
+ err = snd_timer_stop(dpcm->cable->snd_timer.instance);
+ if (err < 0) {
+ pcm_err(dpcm->substream->pcm, "snd_timer_stop(card %d) failed with %d",
+ dpcm->cable->snd_timer.id.card, err);
+ return err;
+ }
+
+ return 0;
+}
+
static inline int loopback_jiffies_timer_stop_sync(struct loopback_pcm *dpcm)
{
del_timer_sync(&dpcm->timer);
@@ -242,6 +306,29 @@ static inline int loopback_jiffies_timer_stop_sync(struct loopback_pcm *dpcm)
return 0;
}
+/* call in loopback->cable_lock */
+static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm)
+{
+ int err;
+
+ /* wait till drain tasklet has finished if requested */
+ tasklet_kill(&dpcm->cable->snd_timer.event_tasklet);
+
+ /* will only be called from free_cable() when other stream was
+ * already closed. Other stream cannot be reopened as long as
+ * loopback->cable_lock is locked. Therefore no need to lock
+ * cable->lock;
+ */
+ err = snd_timer_close(dpcm->cable->snd_timer.instance);
+ if (err < 0) {
+ pcm_err(dpcm->substream->pcm, "snd_timer_close(card %d) = %d",
+ dpcm->cable->snd_timer.id.card, err);
+ }
+ memset(&dpcm->cable->snd_timer, 0, sizeof(dpcm->cable->snd_timer));
+
+ return err;
+}
+
static int loopback_check_format(struct loopback_cable *cable, int stream)
{
struct snd_pcm_runtime *runtime, *cruntime;
@@ -368,6 +455,13 @@ static void params_change(struct snd_pcm_substream *substream)
cable->hw.rate_max = runtime->rate;
cable->hw.channels_min = runtime->channels;
cable->hw.channels_max = runtime->channels;
+
+ if (dpcm->loopback->timer_source > TIMER_SRC_JIFFIES) {
+ cable->hw.period_bytes_min =
+ frames_to_bytes(runtime, runtime->period_size);
+ cable->hw.period_bytes_max = cable->hw.period_bytes_min;
+ }
+
}
static int loopback_prepare(struct snd_pcm_substream *substream)
@@ -591,6 +685,160 @@ static void loopback_jiffies_timer_function(struct timer_list *t)
spin_unlock_irqrestore(&dpcm->cable->lock, flags);
}
+/* call in cable->lock */
+static int loopback_snd_timer_check_resolution(struct snd_pcm_runtime *runtime,
+ unsigned long resolution)
+{
+ if (resolution != runtime->timer_resolution) {
+ struct loopback_pcm *dpcm = runtime->private_data;
+ struct loopback_cable *cable = dpcm->cable;
+ /* Worst case estimation of possible values for resolution
+ * resolution <= (512 * 1024) frames / 8kHz in nsec
+ * resolution <= 65.536.000.000 nsec
+ *
+ * period_size <= 65.536.000.000 nsec / 1000nsec/usec * 192kHz +
+ * 500.000
+ * period_size <= 12.582.912.000.000 <64bit
+ * / 1.000.000 usec/sec
+ */
+ const snd_pcm_uframes_t period_size_usec = resolution / 1000 *
+ runtime->rate;
+ /* round to nearest sample rate */
+ const snd_pcm_uframes_t period_size = (period_size_usec + 500 *
+ 1000) / (1000 * 1000);
+
+ pcm_err(dpcm->substream->pcm, "Period size (%lu frames) of loopback device is not corresponding to timer resolution (%lu nsec = %lu frames) of card %d. Use period size of %lu frames for loopback device.",
+ runtime->period_size, resolution,
+ period_size, cable->snd_timer.id.card, period_size);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void loopback_snd_timer_period_elapsed
+ (struct loopback_cable * const cable, const int event,
+ const unsigned long resolution)
+{
+ struct loopback_pcm *dpcm_play =
+ cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ struct loopback_pcm *dpcm_capt =
+ cable->streams[SNDRV_PCM_STREAM_CAPTURE];
+ struct snd_pcm_runtime *valid_runtime;
+ unsigned int running, elapsed_bytes;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cable->lock, flags);
+ running = cable->running ^ cable->pause;
+ /* no need to do anything if no stream is running */
+ if (!running) {
+ spin_unlock_irqrestore(&cable->lock, flags);
+ return;
+ }
+
+ if (event == SNDRV_TIMER_EVENT_MSTOP) {
+ if (!dpcm_play || !dpcm_play->substream ||
+ !dpcm_play->substream->runtime ||
+ !dpcm_play->substream->runtime->status ||
+ dpcm_play->substream->runtime->status->state !=
+ SNDRV_PCM_STATE_DRAINING) {
+ spin_unlock_irqrestore(&cable->lock, flags);
+ return;
+ }
+ }
+
+ valid_runtime = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
+ dpcm_play->substream->runtime :
+ dpcm_capt->substream->runtime;
+
+ /* resolution is only valid for SNDRV_TIMER_EVENT_TICK events */
+ if (event == SNDRV_TIMER_EVENT_TICK) {
+ /* The hardware rules guarantee that playback and capture period
+ * are the same. Therefore only one device has to be checked
+ * here.
+ */
+ if (loopback_snd_timer_check_resolution(valid_runtime,
+ resolution) < 0) {
+ spin_unlock_irqrestore(&cable->lock, flags);
+ if (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK))
+ snd_pcm_stop_xrun(dpcm_play->substream);
+ if (cable->running & (1 << SNDRV_PCM_STREAM_CAPTURE))
+ snd_pcm_stop_xrun(dpcm_capt->substream);
+ return;
+ }
+ }
+
+ elapsed_bytes = frames_to_bytes(valid_runtime,
+ valid_runtime->period_size);
+ /* The same timer interrupt is used for playback and capture device */
+ if ((running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) &&
+ (running & (1 << SNDRV_PCM_STREAM_CAPTURE))) {
+ copy_play_buf(dpcm_play, dpcm_capt, elapsed_bytes);
+ bytepos_finish(dpcm_play, elapsed_bytes);
+ bytepos_finish(dpcm_capt, elapsed_bytes);
+ } else if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
+ bytepos_finish(dpcm_play, elapsed_bytes);
+ } else if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
+ clear_capture_buf(dpcm_capt, elapsed_bytes);
+ bytepos_finish(dpcm_capt, elapsed_bytes);
+ }
+ spin_unlock_irqrestore(&cable->lock, flags);
+
+ if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK))
+ snd_pcm_period_elapsed(dpcm_play->substream);
+ if (running & (1 << SNDRV_PCM_STREAM_CAPTURE))
+ snd_pcm_period_elapsed(dpcm_capt->substream);
+}
+
+static void loopback_snd_timer_function(struct snd_timer_instance *timeri,
+ unsigned long resolution,
+ unsigned long ticks)
+{
+ struct loopback_cable *cable = timeri->callback_data;
+
+ loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_TICK,
+ resolution);
+}
+
+static void loopback_snd_timer_tasklet(unsigned long arg)
+{
+ struct snd_timer_instance *timeri = (struct snd_timer_instance *)arg;
+ struct loopback_cable *cable = timeri->callback_data;
+
+ loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_MSTOP, 0);
+}
+
+static void loopback_snd_timer_event(struct snd_timer_instance * const timeri,
+ const int event,
+ struct timespec * const tstamp,
+ const unsigned long resolution)
+{
+ /* Do not lock cable->lock here because timer->lock is already hold.
+ * There are other functions which first lock cable->lock and than
+ * timer->lock e.g.
+ * loopback_trigger()
+ * spin_lock(&cable->lock)
+ * loopback_snd_timer_start()
+ * snd_timer_start()
+ * spin_lock(&timer->lock)
+ * Therefore when using the oposit order of locks here it could result
+ * in a deadlock.
+ */
+
+ if (event == SNDRV_TIMER_EVENT_MSTOP) {
+ struct loopback_cable *cable = timeri->callback_data;
+
+ /* sound card of the timer was stopped. Therefore there will not
+ * be any further timer callbacks. Due to this forward audio
+ * data from here if in draining state. When still in running
+ * state the streaming will be aborted by the usual timeout. It
+ * should not be aborted here because may be the timer sound
+ * card does only a recovery and the timer is back soon.
+ * This tasklet triggers loopback_snd_timer_tasklet()
+ */
+ tasklet_schedule(&cable->snd_timer.event_tasklet);
+ }
+}
+
static void loopback_jiffies_timer_dpcm_info(struct loopback_pcm *dpcm,
struct snd_info_buffer *buffer)
{
@@ -603,6 +851,18 @@ static void loopback_jiffies_timer_dpcm_info(struct loopback_pcm *dpcm,
snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires);
}
+static void loopback_snd_timer_dpcm_info(struct loopback_pcm *dpcm,
+ struct snd_info_buffer *buffer)
+{
+ snd_iprintf(buffer, " sound timer:\thw:%d,%d,%d\n",
+ dpcm->cable->snd_timer.id.card,
+ dpcm->cable->snd_timer.id.device,
+ dpcm->cable->snd_timer.id.subdevice);
+ snd_iprintf(buffer, " owner:\t\t%s\n",
+ (dpcm->cable->snd_timer.owner == SNDRV_PCM_STREAM_CAPTURE) ?
+ "capture" : "playback");
+}
+
static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -721,6 +981,23 @@ static int rule_channels(struct snd_pcm_hw_params *params,
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
+static int rule_period_bytes(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
+ struct snd_interval t;
+
+ mutex_lock(&dpcm->loopback->cable_lock);
+ t.min = cable->hw.period_bytes_min;
+ t.max = cable->hw.period_bytes_max;
+ mutex_unlock(&dpcm->loopback->cable_lock);
+ t.openmin = 0;
+ t.openmax = 0;
+ t.integer = 0;
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
static void free_cable(struct snd_pcm_substream *substream)
{
struct loopback *loopback = substream->private_data;
@@ -765,6 +1042,112 @@ static struct loopback_ops loopback_jiffies_timer_ops = {
.dpcm_info = loopback_jiffies_timer_dpcm_info,
};
+static int loopback_timer_source_by_name(const char * const name,
+ int * const number)
+{
+ return kstrtoint(name, 10, number);
+}
+
+static int loopback_snd_card_by_name(const char * const name)
+{
+ int number;
+
+ if (loopback_timer_source_by_name(name, &number) >= 0)
+ return number;
+
+ for (number = 0; number < ARRAY_SIZE(snd_cards); number++) {
+ const struct snd_card * const card = snd_cards[number];
+
+ if (card && strcmp(card->id, name) == 0)
+ return number;
+ }
+
+ return -EINVAL;
+}
+
+/* call in loopback->cable_lock */
+static int loopback_snd_timer_open(struct loopback_pcm *dpcm)
+{
+ int err = 0;
+ unsigned long flags;
+ struct snd_timer_id tid = {
+ .dev_class = SNDRV_TIMER_CLASS_PCM,
+ .dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION,
+ .card = loopback_snd_card_by_name
+ (dpcm->loopback->timer_source_name),
+ .device = 0,
+ .subdevice = 0,
+ };
+ struct snd_timer_instance *timer = NULL;
+
+ spin_lock_irqsave(&dpcm->cable->lock, flags);
+ dpcm->cable->snd_timer.owner = dpcm->substream->stream;
+ dpcm->cable->snd_timer.id = tid;
+
+ /* check if timer was already opened. It is only opened once
+ * per playback and capture subdevice (aka cable).
+ */
+ if (dpcm->cable->snd_timer.instance)
+ goto unlock;
+
+ /* snd_timer_close() and snd_timer_open() should not be called with
+ * locked spinlock because both functions can block on a mutex. The
+ * mutex loopback->cable_lock is kept locked. Therefore snd_timer_open()
+ * cannot be called a second time by the other device of the same cable.
+ * Therefore the following issue cannot happen:
+ * [proc1] Call loopback_timer_open() ->
+ * Unlock cable->lock for snd_timer_close/open() call
+ * [proc2] Call loopback_timer_open() -> snd_timer_open(),
+ * snd_timer_start()
+ * [proc1] Call snd_timer_open() and overwrite running timer
+ * instance
+ */
+ spin_unlock_irqrestore(&dpcm->cable->lock, flags);
+ err = snd_timer_open(&timer, dpcm->loopback->card->id,
+ &dpcm->cable->snd_timer.id,
+ current->pid);
+ if (err < 0) {
+ pcm_err(dpcm->substream->pcm, "Opening sound timer of card %d failed with %d",
+ dpcm->cable->snd_timer.id.card, err);
+ return err;
+ }
+ spin_lock_irqsave(&dpcm->cable->lock, flags);
+
+ /* The callback has to be called from another tasklet. If
+ * SNDRV_TIMER_IFLG_FAST is specified it will be called from the
+ * snd_pcm_period_elapsed() call of the selected sound card.
+ * snd_pcm_period_elapsed() helds snd_pcm_stream_lock_irqsave().
+ * Due to our callback loopback_snd_timer_function() also calls
+ * snd_pcm_period_elapsed() which calls snd_pcm_stream_lock_irqsave().
+ * This would end up in a dead lock.
+ */
+ timer->flags |= SNDRV_TIMER_IFLG_AUTO;
+ timer->callback = loopback_snd_timer_function;
+ timer->callback_data = (void *)dpcm->cable;
+ timer->ccallback = loopback_snd_timer_event;
+ dpcm->cable->snd_timer.instance = timer;
+
+ /* initialise a tasklet used for draining */
+ tasklet_init(&dpcm->cable->snd_timer.event_tasklet,
+ loopback_snd_timer_tasklet, (unsigned long)timer);
+
+unlock:
+ spin_unlock_irqrestore(&dpcm->cable->lock, flags);
+
+ return err;
+}
+
+/* stop_sync() is not required for sound timer because it does not need to be
+ * restarted in loopback_prepare() on Xrun recovery
+ */
+static struct loopback_ops loopback_snd_timer_ops = {
+ .open = loopback_snd_timer_open,
+ .start = loopback_snd_timer_start,
+ .stop = loopback_snd_timer_stop,
+ .close_cable = loopback_snd_timer_close_cable,
+ .dpcm_info = loopback_snd_timer_dpcm_info,
+};
+
static int loopback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -793,7 +1176,10 @@ static int loopback_open(struct snd_pcm_substream *substream)
}
spin_lock_init(&cable->lock);
cable->hw = loopback_pcm_hardware;
- cable->ops = &loopback_jiffies_timer_ops;
+ if (loopback->timer_source <= TIMER_SRC_JIFFIES)
+ cable->ops = &loopback_jiffies_timer_ops;
+ else
+ cable->ops = &loopback_snd_timer_ops;
loopback->cables[substream->number][dev] = cable;
}
dpcm->cable = cable;
@@ -829,6 +1215,19 @@ static int loopback_open(struct snd_pcm_substream *substream)
if (err < 0)
goto unlock;
+ /* In case of sound timer the period time of both devices of the same
+ * loop has to be the same. This rule only takes effect if a sound timer
+ * was chosen via the kernel parameter.
+ */
+ if (loopback->timer_source > TIMER_SRC_JIFFIES) {
+ err = snd_pcm_hw_rule_add(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ rule_period_bytes, dpcm,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1);
+ if (err < 0)
+ goto unlock;
+ }
+
/* loopback_runtime_free() has not to be called if kfree(dpcm) was
* already called here. Otherwise it will end up with a double free.
*/
@@ -1250,6 +1649,15 @@ static int loopback_probe(struct platform_device *devptr)
pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
loopback->card = card;
+ loopback->timer_source_name = timer_source[dev];
+ /* If it was not a number a sound card name was specified. Therefore a
+ * the sound timer of this card should be used. The right card name will
+ * be converted in loopback_snd_timer_open()
+ */
+ if (loopback_timer_source_by_name(loopback->timer_source_name,
+ &loopback->timer_source) < 0)
+ loopback->timer_source = 0;
+
mutex_init(&loopback->cable_lock);
err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]);