aboutsummaryrefslogtreecommitdiffstats
path: root/loopback/loopback.c
diff options
context:
space:
mode:
Diffstat (limited to 'loopback/loopback.c')
-rw-r--r--loopback/loopback.c168
1 files changed, 163 insertions, 5 deletions
diff --git a/loopback/loopback.c b/loopback/loopback.c
index 57f3c34..6f2a5d9 100644
--- a/loopback/loopback.c
+++ b/loopback/loopback.c
@@ -18,23 +18,177 @@ MODULE_LICENSE("GPL v2");
static struct avirt_coreinfo *coreinfo;
+struct loopback_pcm {
+ struct snd_pcm_substream *substream;
+ spinlock_t lock;
+ struct timer_list timer;
+ unsigned long base_time;
+ unsigned int frac_pos; /* fractional sample position (based HZ) */
+ unsigned int frac_period_rest;
+ unsigned int frac_buffer_size; /* buffer_size * HZ */
+ unsigned int frac_period_size; /* period_size * HZ */
+ unsigned int rate;
+ int elapsed;
+};
+
+int systimer_create(struct snd_pcm_substream *substream);
+void systimer_free(struct snd_pcm_substream *substream);
+int systimer_start(struct snd_pcm_substream *substream);
+int systimer_stop(struct snd_pcm_substream *substream);
+snd_pcm_uframes_t systimer_pointer(struct snd_pcm_substream *substream);
+int systimer_prepare(struct snd_pcm_substream *substream);
+void systimer_rearm(struct loopback_pcm *dpcm);
+void systimer_update(struct loopback_pcm *dpcm);
+
+void loopback_callback(struct timer_list *tlist);
+
+/********************************
+ * Loopback Timer Functions
+ ********************************/
+
+int systimer_create(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ struct loopback_pcm *dpcm;
+ dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
+ if (!dpcm)
+ return -ENOMEM;
+
+ timer_setup(&dpcm->timer, loopback_callback, 0);
+ spin_lock_init(&dpcm->lock);
+ dpcm->substream = substream;
+
+ runtime->private_data = dpcm;
+ return 0;
+}
+
+void systimer_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ kfree(runtime->private_data);
+}
+
+int systimer_start(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+ spin_lock(&dpcm->lock);
+ dpcm->base_time = jiffies;
+ systimer_rearm(dpcm);
+ spin_unlock(&dpcm->lock);
+ return 0;
+}
+
+int systimer_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+ spin_lock(&dpcm->lock);
+ del_timer(&dpcm->timer);
+ spin_unlock(&dpcm->lock);
+ return 0;
+}
+
+snd_pcm_uframes_t systimer_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+
+ snd_pcm_uframes_t pos;
+
+ spin_lock(&dpcm->lock);
+ systimer_update(dpcm);
+ pos = dpcm->frac_pos / HZ;
+ spin_unlock(&dpcm->lock);
+ return pos;
+}
+
+int systimer_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+
+ dpcm->frac_pos = 0;
+ dpcm->rate = runtime->rate;
+ dpcm->frac_buffer_size = runtime->buffer_size * HZ;
+ dpcm->frac_period_size = runtime->period_size * HZ;
+ dpcm->frac_period_rest = dpcm->frac_period_size;
+ dpcm->elapsed = 0;
+
+ return 0;
+}
+
+void systimer_rearm(struct loopback_pcm *dpcm)
+{
+ mod_timer(&dpcm->timer, jiffies + (dpcm->frac_period_rest + dpcm->rate -
+ 1) / dpcm->rate);
+}
+
+void systimer_update(struct loopback_pcm *dpcm)
+{
+ unsigned long delta;
+
+ delta = jiffies - dpcm->base_time;
+ if (!delta)
+ return;
+ dpcm->base_time += delta;
+ delta *= dpcm->rate;
+ dpcm->frac_pos += delta;
+ while (dpcm->frac_pos >= dpcm->frac_buffer_size)
+ dpcm->frac_pos -= dpcm->frac_buffer_size;
+ while (dpcm->frac_period_rest <= delta) {
+ dpcm->elapsed++;
+ dpcm->frac_period_rest += dpcm->frac_period_size;
+ }
+ dpcm->frac_period_rest -= delta;
+}
+
+/*******
+ * Loopback Timer Callback
+ *******/
+
+void loopback_callback(struct timer_list *tlist)
+{
+ struct loopback_pcm *dpcm = from_timer(dpcm, tlist, timer);
+
+ unsigned long flags;
+ int elapsed = 0;
+
+ spin_lock_irqsave(&dpcm->lock, flags);
+
+ // Perform copy from playback to capture
+ systimer_update(dpcm);
+ systimer_rearm(dpcm);
+ elapsed = dpcm->elapsed;
+ dpcm->elapsed = 0;
+ spin_unlock_irqrestore(&dpcm->lock, flags);
+ if (elapsed)
+ coreinfo->pcm_buff_complete(dpcm->substream);
+}
+
/*******************************************************************************
* Audio Path ALSA PCM Callbacks
******************************************************************************/
static int loopback_pcm_open(struct snd_pcm_substream *substream)
{
+ int err = systimer_create(substream);
+ if (err < 0)
+ return err;
+
return 0;
}
static int loopback_pcm_close(struct snd_pcm_substream *substream)
{
+ systimer_free(substream);
return 0;
}
static snd_pcm_uframes_t
loopback_pcm_pointer(struct snd_pcm_substream *substream)
{
- return 0;
+ return systimer_pointer(substream);
}
static int loopback_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
@@ -42,16 +196,17 @@ static int loopback_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
+ return systimer_start(substream);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- return 0;
+ return systimer_stop(substream);
}
return -EINVAL;
}
static int loopback_pcm_prepare(struct snd_pcm_substream *substream)
{
- return 0;
+ return systimer_prepare(substream);
}
static struct snd_pcm_ops loopbackap_pcm_ops = {
@@ -71,10 +226,13 @@ static struct snd_pcm_hardware loopbackap_hw = {
| SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID),
.rates = SNDRV_PCM_RATE_48000,
- .rate_min = 48000,
+ .rate_min = 2000,
.rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = 32768,
.periods_min = 1,
- .periods_max = 8,
+ .periods_max = 1024,
};
static struct avirt_audiopath loopbackap_module = {