|
@@ -39,6 +39,7 @@
|
|
#include <sound/core.h>
|
|
#include <sound/core.h>
|
|
#include <sound/control.h>
|
|
#include <sound/control.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/pcm.h>
|
|
|
|
+#include <sound/pcm_params.h>
|
|
#include <sound/info.h>
|
|
#include <sound/info.h>
|
|
#include <sound/initval.h>
|
|
#include <sound/initval.h>
|
|
|
|
|
|
@@ -305,19 +306,6 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static void params_change_substream(struct loopback_pcm *dpcm,
|
|
|
|
- struct snd_pcm_runtime *runtime)
|
|
|
|
-{
|
|
|
|
- struct snd_pcm_runtime *dst_runtime;
|
|
|
|
-
|
|
|
|
- if (dpcm == NULL || dpcm->substream == NULL)
|
|
|
|
- return;
|
|
|
|
- dst_runtime = dpcm->substream->runtime;
|
|
|
|
- if (dst_runtime == NULL)
|
|
|
|
- return;
|
|
|
|
- dst_runtime->hw = dpcm->cable->hw;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static void params_change(struct snd_pcm_substream *substream)
|
|
static void params_change(struct snd_pcm_substream *substream)
|
|
{
|
|
{
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
@@ -329,10 +317,6 @@ static void params_change(struct snd_pcm_substream *substream)
|
|
cable->hw.rate_max = runtime->rate;
|
|
cable->hw.rate_max = runtime->rate;
|
|
cable->hw.channels_min = runtime->channels;
|
|
cable->hw.channels_min = runtime->channels;
|
|
cable->hw.channels_max = runtime->channels;
|
|
cable->hw.channels_max = runtime->channels;
|
|
- params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
|
|
|
|
- runtime);
|
|
|
|
- params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE],
|
|
|
|
- runtime);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static int loopback_prepare(struct snd_pcm_substream *substream)
|
|
static int loopback_prepare(struct snd_pcm_substream *substream)
|
|
@@ -620,26 +604,29 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream)
|
|
static int rule_format(struct snd_pcm_hw_params *params,
|
|
static int rule_format(struct snd_pcm_hw_params *params,
|
|
struct snd_pcm_hw_rule *rule)
|
|
struct snd_pcm_hw_rule *rule)
|
|
{
|
|
{
|
|
|
|
+ struct loopback_pcm *dpcm = rule->private;
|
|
|
|
+ struct loopback_cable *cable = dpcm->cable;
|
|
|
|
+ struct snd_mask m;
|
|
|
|
|
|
- struct snd_pcm_hardware *hw = rule->private;
|
|
|
|
- struct snd_mask *maskp = hw_param_mask(params, rule->var);
|
|
|
|
-
|
|
|
|
- maskp->bits[0] &= (u_int32_t)hw->formats;
|
|
|
|
- maskp->bits[1] &= (u_int32_t)(hw->formats >> 32);
|
|
|
|
- memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
|
|
|
|
- if (! maskp->bits[0] && ! maskp->bits[1])
|
|
|
|
- return -EINVAL;
|
|
|
|
- return 0;
|
|
|
|
|
|
+ snd_mask_none(&m);
|
|
|
|
+ mutex_lock(&dpcm->loopback->cable_lock);
|
|
|
|
+ m.bits[0] = (u_int32_t)cable->hw.formats;
|
|
|
|
+ m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
|
|
|
|
+ mutex_unlock(&dpcm->loopback->cable_lock);
|
|
|
|
+ return snd_mask_refine(hw_param_mask(params, rule->var), &m);
|
|
}
|
|
}
|
|
|
|
|
|
static int rule_rate(struct snd_pcm_hw_params *params,
|
|
static int rule_rate(struct snd_pcm_hw_params *params,
|
|
struct snd_pcm_hw_rule *rule)
|
|
struct snd_pcm_hw_rule *rule)
|
|
{
|
|
{
|
|
- struct snd_pcm_hardware *hw = rule->private;
|
|
|
|
|
|
+ struct loopback_pcm *dpcm = rule->private;
|
|
|
|
+ struct loopback_cable *cable = dpcm->cable;
|
|
struct snd_interval t;
|
|
struct snd_interval t;
|
|
|
|
|
|
- t.min = hw->rate_min;
|
|
|
|
- t.max = hw->rate_max;
|
|
|
|
|
|
+ mutex_lock(&dpcm->loopback->cable_lock);
|
|
|
|
+ t.min = cable->hw.rate_min;
|
|
|
|
+ t.max = cable->hw.rate_max;
|
|
|
|
+ mutex_unlock(&dpcm->loopback->cable_lock);
|
|
t.openmin = t.openmax = 0;
|
|
t.openmin = t.openmax = 0;
|
|
t.integer = 0;
|
|
t.integer = 0;
|
|
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
|
|
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
|
|
@@ -648,22 +635,44 @@ static int rule_rate(struct snd_pcm_hw_params *params,
|
|
static int rule_channels(struct snd_pcm_hw_params *params,
|
|
static int rule_channels(struct snd_pcm_hw_params *params,
|
|
struct snd_pcm_hw_rule *rule)
|
|
struct snd_pcm_hw_rule *rule)
|
|
{
|
|
{
|
|
- struct snd_pcm_hardware *hw = rule->private;
|
|
|
|
|
|
+ struct loopback_pcm *dpcm = rule->private;
|
|
|
|
+ struct loopback_cable *cable = dpcm->cable;
|
|
struct snd_interval t;
|
|
struct snd_interval t;
|
|
|
|
|
|
- t.min = hw->channels_min;
|
|
|
|
- t.max = hw->channels_max;
|
|
|
|
|
|
+ mutex_lock(&dpcm->loopback->cable_lock);
|
|
|
|
+ t.min = cable->hw.channels_min;
|
|
|
|
+ t.max = cable->hw.channels_max;
|
|
|
|
+ mutex_unlock(&dpcm->loopback->cable_lock);
|
|
t.openmin = t.openmax = 0;
|
|
t.openmin = t.openmax = 0;
|
|
t.integer = 0;
|
|
t.integer = 0;
|
|
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
|
|
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;
|
|
|
|
+ int dev = get_cable_index(substream);
|
|
|
|
+ struct loopback_cable *cable;
|
|
|
|
+
|
|
|
|
+ cable = loopback->cables[substream->number][dev];
|
|
|
|
+ if (!cable)
|
|
|
|
+ return;
|
|
|
|
+ if (cable->streams[!substream->stream]) {
|
|
|
|
+ /* other stream is still alive */
|
|
|
|
+ cable->streams[substream->stream] = NULL;
|
|
|
|
+ } else {
|
|
|
|
+ /* free the cable */
|
|
|
|
+ loopback->cables[substream->number][dev] = NULL;
|
|
|
|
+ kfree(cable);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static int loopback_open(struct snd_pcm_substream *substream)
|
|
static int loopback_open(struct snd_pcm_substream *substream)
|
|
{
|
|
{
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct loopback *loopback = substream->private_data;
|
|
struct loopback *loopback = substream->private_data;
|
|
struct loopback_pcm *dpcm;
|
|
struct loopback_pcm *dpcm;
|
|
- struct loopback_cable *cable;
|
|
|
|
|
|
+ struct loopback_cable *cable = NULL;
|
|
int err = 0;
|
|
int err = 0;
|
|
int dev = get_cable_index(substream);
|
|
int dev = get_cable_index(substream);
|
|
|
|
|
|
@@ -681,7 +690,6 @@ static int loopback_open(struct snd_pcm_substream *substream)
|
|
if (!cable) {
|
|
if (!cable) {
|
|
cable = kzalloc(sizeof(*cable), GFP_KERNEL);
|
|
cable = kzalloc(sizeof(*cable), GFP_KERNEL);
|
|
if (!cable) {
|
|
if (!cable) {
|
|
- kfree(dpcm);
|
|
|
|
err = -ENOMEM;
|
|
err = -ENOMEM;
|
|
goto unlock;
|
|
goto unlock;
|
|
}
|
|
}
|
|
@@ -699,19 +707,19 @@ static int loopback_open(struct snd_pcm_substream *substream)
|
|
/* are cached -> they do not reflect the actual state */
|
|
/* are cached -> they do not reflect the actual state */
|
|
err = snd_pcm_hw_rule_add(runtime, 0,
|
|
err = snd_pcm_hw_rule_add(runtime, 0,
|
|
SNDRV_PCM_HW_PARAM_FORMAT,
|
|
SNDRV_PCM_HW_PARAM_FORMAT,
|
|
- rule_format, &runtime->hw,
|
|
|
|
|
|
+ rule_format, dpcm,
|
|
SNDRV_PCM_HW_PARAM_FORMAT, -1);
|
|
SNDRV_PCM_HW_PARAM_FORMAT, -1);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
goto unlock;
|
|
goto unlock;
|
|
err = snd_pcm_hw_rule_add(runtime, 0,
|
|
err = snd_pcm_hw_rule_add(runtime, 0,
|
|
SNDRV_PCM_HW_PARAM_RATE,
|
|
SNDRV_PCM_HW_PARAM_RATE,
|
|
- rule_rate, &runtime->hw,
|
|
|
|
|
|
+ rule_rate, dpcm,
|
|
SNDRV_PCM_HW_PARAM_RATE, -1);
|
|
SNDRV_PCM_HW_PARAM_RATE, -1);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
goto unlock;
|
|
goto unlock;
|
|
err = snd_pcm_hw_rule_add(runtime, 0,
|
|
err = snd_pcm_hw_rule_add(runtime, 0,
|
|
SNDRV_PCM_HW_PARAM_CHANNELS,
|
|
SNDRV_PCM_HW_PARAM_CHANNELS,
|
|
- rule_channels, &runtime->hw,
|
|
|
|
|
|
+ rule_channels, dpcm,
|
|
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
|
|
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
goto unlock;
|
|
goto unlock;
|
|
@@ -723,6 +731,10 @@ static int loopback_open(struct snd_pcm_substream *substream)
|
|
else
|
|
else
|
|
runtime->hw = cable->hw;
|
|
runtime->hw = cable->hw;
|
|
unlock:
|
|
unlock:
|
|
|
|
+ if (err < 0) {
|
|
|
|
+ free_cable(substream);
|
|
|
|
+ kfree(dpcm);
|
|
|
|
+ }
|
|
mutex_unlock(&loopback->cable_lock);
|
|
mutex_unlock(&loopback->cable_lock);
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
@@ -731,20 +743,10 @@ static int loopback_close(struct snd_pcm_substream *substream)
|
|
{
|
|
{
|
|
struct loopback *loopback = substream->private_data;
|
|
struct loopback *loopback = substream->private_data;
|
|
struct loopback_pcm *dpcm = substream->runtime->private_data;
|
|
struct loopback_pcm *dpcm = substream->runtime->private_data;
|
|
- struct loopback_cable *cable;
|
|
|
|
- int dev = get_cable_index(substream);
|
|
|
|
|
|
|
|
loopback_timer_stop(dpcm);
|
|
loopback_timer_stop(dpcm);
|
|
mutex_lock(&loopback->cable_lock);
|
|
mutex_lock(&loopback->cable_lock);
|
|
- cable = loopback->cables[substream->number][dev];
|
|
|
|
- if (cable->streams[!substream->stream]) {
|
|
|
|
- /* other stream is still alive */
|
|
|
|
- cable->streams[substream->stream] = NULL;
|
|
|
|
- } else {
|
|
|
|
- /* free the cable */
|
|
|
|
- loopback->cables[substream->number][dev] = NULL;
|
|
|
|
- kfree(cable);
|
|
|
|
- }
|
|
|
|
|
|
+ free_cable(substream);
|
|
mutex_unlock(&loopback->cable_lock);
|
|
mutex_unlock(&loopback->cable_lock);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|