usb: gadget: u_audio: add bi-directional volume and mute support
USB Audio Class 1/2 have ability to change device's volume and mute by USB Host through class-specific control requests. Device also can notify Host about volume/mute change on its side through optional interrupt endpoint. This patch adds Volume and Mute ALSA controls which can be used by user to send and receive notifications to/from the USB Host about Volume and Mute change. These params come from f_uac* so volume and mute controls will be created only if the function support and enable each explicitly Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com> Signed-off-by: Pavel Hofman <pavel.hofman@ivitera.com> Link: https://lore.kernel.org/r/20210712125529.76070-3-pavel.hofman@ivitera.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
b48f8939b9
commit
02de698ca8
2 changed files with 380 additions and 11 deletions
|
@ -12,11 +12,14 @@
|
||||||
* Jaswinder Singh (jaswinder.singh@linaro.org)
|
* Jaswinder Singh (jaswinder.singh@linaro.org)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
#include <sound/control.h>
|
#include <sound/control.h>
|
||||||
|
#include <sound/tlv.h>
|
||||||
|
#include <linux/usb/audio.h>
|
||||||
|
|
||||||
#include "u_audio.h"
|
#include "u_audio.h"
|
||||||
|
|
||||||
|
@ -24,6 +27,12 @@
|
||||||
#define PRD_SIZE_MAX PAGE_SIZE
|
#define PRD_SIZE_MAX PAGE_SIZE
|
||||||
#define MIN_PERIODS 4
|
#define MIN_PERIODS 4
|
||||||
|
|
||||||
|
enum {
|
||||||
|
UAC_FBACK_CTRL,
|
||||||
|
UAC_MUTE_CTRL,
|
||||||
|
UAC_VOLUME_CTRL,
|
||||||
|
};
|
||||||
|
|
||||||
/* Runtime data params for one stream */
|
/* Runtime data params for one stream */
|
||||||
struct uac_rtd_params {
|
struct uac_rtd_params {
|
||||||
struct snd_uac_chip *uac; /* parent chip */
|
struct snd_uac_chip *uac; /* parent chip */
|
||||||
|
@ -43,6 +52,17 @@ struct uac_rtd_params {
|
||||||
|
|
||||||
struct usb_request *req_fback; /* Feedback endpoint request */
|
struct usb_request *req_fback; /* Feedback endpoint request */
|
||||||
bool fb_ep_enabled; /* if the ep is enabled */
|
bool fb_ep_enabled; /* if the ep is enabled */
|
||||||
|
|
||||||
|
/* Volume/Mute controls and their state */
|
||||||
|
int fu_id; /* Feature Unit ID */
|
||||||
|
struct snd_kcontrol *snd_kctl_volume;
|
||||||
|
struct snd_kcontrol *snd_kctl_mute;
|
||||||
|
s16 volume_min, volume_max, volume_res;
|
||||||
|
s16 volume;
|
||||||
|
int mute;
|
||||||
|
|
||||||
|
spinlock_t lock; /* lock for control transfers */
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct snd_uac_chip {
|
struct snd_uac_chip {
|
||||||
|
@ -597,6 +617,103 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
|
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
|
||||||
|
|
||||||
|
int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val)
|
||||||
|
{
|
||||||
|
struct snd_uac_chip *uac = audio_dev->uac;
|
||||||
|
struct uac_rtd_params *prm;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (playback)
|
||||||
|
prm = &uac->p_prm;
|
||||||
|
else
|
||||||
|
prm = &uac->c_prm;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&prm->lock, flags);
|
||||||
|
*val = prm->volume;
|
||||||
|
spin_unlock_irqrestore(&prm->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(u_audio_get_volume);
|
||||||
|
|
||||||
|
int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val)
|
||||||
|
{
|
||||||
|
struct snd_uac_chip *uac = audio_dev->uac;
|
||||||
|
struct uac_rtd_params *prm;
|
||||||
|
unsigned long flags;
|
||||||
|
int change = 0;
|
||||||
|
|
||||||
|
if (playback)
|
||||||
|
prm = &uac->p_prm;
|
||||||
|
else
|
||||||
|
prm = &uac->c_prm;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&prm->lock, flags);
|
||||||
|
val = clamp(val, prm->volume_min, prm->volume_max);
|
||||||
|
if (prm->volume != val) {
|
||||||
|
prm->volume = val;
|
||||||
|
change = 1;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&prm->lock, flags);
|
||||||
|
|
||||||
|
if (change)
|
||||||
|
snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||||
|
&prm->snd_kctl_volume->id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(u_audio_set_volume);
|
||||||
|
|
||||||
|
int u_audio_get_mute(struct g_audio *audio_dev, int playback, int *val)
|
||||||
|
{
|
||||||
|
struct snd_uac_chip *uac = audio_dev->uac;
|
||||||
|
struct uac_rtd_params *prm;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (playback)
|
||||||
|
prm = &uac->p_prm;
|
||||||
|
else
|
||||||
|
prm = &uac->c_prm;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&prm->lock, flags);
|
||||||
|
*val = prm->mute;
|
||||||
|
spin_unlock_irqrestore(&prm->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(u_audio_get_mute);
|
||||||
|
|
||||||
|
int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val)
|
||||||
|
{
|
||||||
|
struct snd_uac_chip *uac = audio_dev->uac;
|
||||||
|
struct uac_rtd_params *prm;
|
||||||
|
unsigned long flags;
|
||||||
|
int change = 0;
|
||||||
|
int mute;
|
||||||
|
|
||||||
|
if (playback)
|
||||||
|
prm = &uac->p_prm;
|
||||||
|
else
|
||||||
|
prm = &uac->c_prm;
|
||||||
|
|
||||||
|
mute = val ? 1 : 0;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&prm->lock, flags);
|
||||||
|
if (prm->mute != mute) {
|
||||||
|
prm->mute = mute;
|
||||||
|
change = 1;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&prm->lock, flags);
|
||||||
|
|
||||||
|
if (change)
|
||||||
|
snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||||
|
&prm->snd_kctl_mute->id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(u_audio_set_mute);
|
||||||
|
|
||||||
|
|
||||||
static int u_audio_pitch_info(struct snd_kcontrol *kcontrol,
|
static int u_audio_pitch_info(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_ctl_elem_info *uinfo)
|
struct snd_ctl_elem_info *uinfo)
|
||||||
{
|
{
|
||||||
|
@ -656,14 +773,158 @@ static int u_audio_pitch_put(struct snd_kcontrol *kcontrol,
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct snd_kcontrol_new u_audio_controls[] = {
|
static int u_audio_mute_info(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_info *uinfo)
|
||||||
{
|
{
|
||||||
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||||
|
uinfo->count = 1;
|
||||||
|
uinfo->value.integer.min = 0;
|
||||||
|
uinfo->value.integer.max = 1;
|
||||||
|
uinfo->value.integer.step = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int u_audio_mute_get(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&prm->lock, flags);
|
||||||
|
ucontrol->value.integer.value[0] = !prm->mute;
|
||||||
|
spin_unlock_irqrestore(&prm->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int u_audio_mute_put(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||||
|
struct snd_uac_chip *uac = prm->uac;
|
||||||
|
struct g_audio *audio_dev = uac->audio_dev;
|
||||||
|
unsigned int val;
|
||||||
|
unsigned long flags;
|
||||||
|
int change = 0;
|
||||||
|
|
||||||
|
val = !ucontrol->value.integer.value[0];
|
||||||
|
|
||||||
|
spin_lock_irqsave(&prm->lock, flags);
|
||||||
|
if (val != prm->mute) {
|
||||||
|
prm->mute = val;
|
||||||
|
change = 1;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&prm->lock, flags);
|
||||||
|
|
||||||
|
if (change && audio_dev->notify)
|
||||||
|
audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_MUTE);
|
||||||
|
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TLV callback for mixer volume controls
|
||||||
|
*/
|
||||||
|
static int u_audio_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
||||||
|
unsigned int size, unsigned int __user *_tlv)
|
||||||
|
{
|
||||||
|
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||||
|
DECLARE_TLV_DB_MINMAX(scale, 0, 0);
|
||||||
|
|
||||||
|
if (size < sizeof(scale))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* UAC volume resolution is 1/256 dB, TLV is 1/100 dB */
|
||||||
|
scale[2] = (prm->volume_min * 100) / 256;
|
||||||
|
scale[3] = (prm->volume_max * 100) / 256;
|
||||||
|
if (copy_to_user(_tlv, scale, sizeof(scale)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int u_audio_volume_info(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_info *uinfo)
|
||||||
|
{
|
||||||
|
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||||
|
|
||||||
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||||
|
uinfo->count = 1;
|
||||||
|
uinfo->value.integer.min = 0;
|
||||||
|
uinfo->value.integer.max =
|
||||||
|
(prm->volume_max - prm->volume_min + prm->volume_res - 1)
|
||||||
|
/ prm->volume_res;
|
||||||
|
uinfo->value.integer.step = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int u_audio_volume_get(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&prm->lock, flags);
|
||||||
|
ucontrol->value.integer.value[0] =
|
||||||
|
(prm->volume - prm->volume_min) / prm->volume_res;
|
||||||
|
spin_unlock_irqrestore(&prm->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int u_audio_volume_put(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
|
||||||
|
struct snd_uac_chip *uac = prm->uac;
|
||||||
|
struct g_audio *audio_dev = uac->audio_dev;
|
||||||
|
unsigned int val;
|
||||||
|
s16 volume;
|
||||||
|
unsigned long flags;
|
||||||
|
int change = 0;
|
||||||
|
|
||||||
|
val = ucontrol->value.integer.value[0];
|
||||||
|
|
||||||
|
spin_lock_irqsave(&prm->lock, flags);
|
||||||
|
volume = (val * prm->volume_res) + prm->volume_min;
|
||||||
|
volume = clamp(volume, prm->volume_min, prm->volume_max);
|
||||||
|
if (volume != prm->volume) {
|
||||||
|
prm->volume = volume;
|
||||||
|
change = 1;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&prm->lock, flags);
|
||||||
|
|
||||||
|
if (change && audio_dev->notify)
|
||||||
|
audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_VOLUME);
|
||||||
|
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct snd_kcontrol_new u_audio_controls[] = {
|
||||||
|
[UAC_FBACK_CTRL] {
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||||
.name = "Capture Pitch 1000000",
|
.name = "Capture Pitch 1000000",
|
||||||
.info = u_audio_pitch_info,
|
.info = u_audio_pitch_info,
|
||||||
.get = u_audio_pitch_get,
|
.get = u_audio_pitch_get,
|
||||||
.put = u_audio_pitch_put,
|
.put = u_audio_pitch_put,
|
||||||
},
|
},
|
||||||
|
[UAC_MUTE_CTRL] {
|
||||||
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
|
.name = "", /* will be filled later */
|
||||||
|
.info = u_audio_mute_info,
|
||||||
|
.get = u_audio_mute_get,
|
||||||
|
.put = u_audio_mute_put,
|
||||||
|
},
|
||||||
|
[UAC_VOLUME_CTRL] {
|
||||||
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
|
.name = "", /* will be filled later */
|
||||||
|
.info = u_audio_volume_info,
|
||||||
|
.get = u_audio_volume_get,
|
||||||
|
.put = u_audio_volume_put,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||||
|
@ -675,7 +936,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||||
struct snd_kcontrol *kctl;
|
struct snd_kcontrol *kctl;
|
||||||
struct uac_params *params;
|
struct uac_params *params;
|
||||||
int p_chmask, c_chmask;
|
int p_chmask, c_chmask;
|
||||||
int err;
|
int i, err;
|
||||||
|
|
||||||
if (!g_audio)
|
if (!g_audio)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -693,6 +954,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||||
if (c_chmask) {
|
if (c_chmask) {
|
||||||
struct uac_rtd_params *prm = &uac->c_prm;
|
struct uac_rtd_params *prm = &uac->c_prm;
|
||||||
|
|
||||||
|
spin_lock_init(&prm->lock);
|
||||||
uac->c_prm.uac = uac;
|
uac->c_prm.uac = uac;
|
||||||
prm->max_psize = g_audio->out_ep_maxpsize;
|
prm->max_psize = g_audio->out_ep_maxpsize;
|
||||||
|
|
||||||
|
@ -716,6 +978,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||||
if (p_chmask) {
|
if (p_chmask) {
|
||||||
struct uac_rtd_params *prm = &uac->p_prm;
|
struct uac_rtd_params *prm = &uac->p_prm;
|
||||||
|
|
||||||
|
spin_lock_init(&prm->lock);
|
||||||
uac->p_prm.uac = uac;
|
uac->p_prm.uac = uac;
|
||||||
prm->max_psize = g_audio->in_ep_maxpsize;
|
prm->max_psize = g_audio->in_ep_maxpsize;
|
||||||
|
|
||||||
|
@ -760,10 +1023,18 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
|
||||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
|
||||||
|
|
||||||
if (c_chmask && g_audio->in_ep_fback) {
|
/*
|
||||||
|
* Create mixer and controls
|
||||||
|
* Create only if it's required on USB side
|
||||||
|
*/
|
||||||
|
if ((c_chmask && g_audio->in_ep_fback)
|
||||||
|
|| (p_chmask && params->p_fu.id)
|
||||||
|
|| (c_chmask && params->c_fu.id))
|
||||||
strscpy(card->mixername, card_name, sizeof(card->driver));
|
strscpy(card->mixername, card_name, sizeof(card->driver));
|
||||||
|
|
||||||
kctl = snd_ctl_new1(&u_audio_controls[0], &uac->c_prm);
|
if (c_chmask && g_audio->in_ep_fback) {
|
||||||
|
kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL],
|
||||||
|
&uac->c_prm);
|
||||||
if (!kctl) {
|
if (!kctl) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto snd_fail;
|
goto snd_fail;
|
||||||
|
@ -777,6 +1048,82 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||||
goto snd_fail;
|
goto snd_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) {
|
||||||
|
struct uac_rtd_params *prm;
|
||||||
|
struct uac_fu_params *fu;
|
||||||
|
char ctrl_name[24];
|
||||||
|
char *direction;
|
||||||
|
|
||||||
|
if (!pcm->streams[i].substream_count)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (i == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
|
prm = &uac->p_prm;
|
||||||
|
fu = ¶ms->p_fu;
|
||||||
|
direction = "Playback";
|
||||||
|
} else {
|
||||||
|
prm = &uac->c_prm;
|
||||||
|
fu = ¶ms->c_fu;
|
||||||
|
direction = "Capture";
|
||||||
|
}
|
||||||
|
|
||||||
|
prm->fu_id = fu->id;
|
||||||
|
|
||||||
|
if (fu->mute_present) {
|
||||||
|
snprintf(ctrl_name, sizeof(ctrl_name),
|
||||||
|
"PCM %s Switch", direction);
|
||||||
|
|
||||||
|
u_audio_controls[UAC_MUTE_CTRL].name = ctrl_name;
|
||||||
|
|
||||||
|
kctl = snd_ctl_new1(&u_audio_controls[UAC_MUTE_CTRL],
|
||||||
|
prm);
|
||||||
|
if (!kctl) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto snd_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
kctl->id.device = pcm->device;
|
||||||
|
kctl->id.subdevice = i;
|
||||||
|
|
||||||
|
err = snd_ctl_add(card, kctl);
|
||||||
|
if (err < 0)
|
||||||
|
goto snd_fail;
|
||||||
|
prm->snd_kctl_mute = kctl;
|
||||||
|
prm->mute = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fu->volume_present) {
|
||||||
|
snprintf(ctrl_name, sizeof(ctrl_name),
|
||||||
|
"PCM %s Volume", direction);
|
||||||
|
|
||||||
|
u_audio_controls[UAC_VOLUME_CTRL].name = ctrl_name;
|
||||||
|
|
||||||
|
kctl = snd_ctl_new1(&u_audio_controls[UAC_VOLUME_CTRL],
|
||||||
|
prm);
|
||||||
|
if (!kctl) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto snd_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
kctl->id.device = pcm->device;
|
||||||
|
kctl->id.subdevice = i;
|
||||||
|
|
||||||
|
|
||||||
|
kctl->tlv.c = u_audio_volume_tlv;
|
||||||
|
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ |
|
||||||
|
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
|
||||||
|
|
||||||
|
err = snd_ctl_add(card, kctl);
|
||||||
|
if (err < 0)
|
||||||
|
goto snd_fail;
|
||||||
|
prm->snd_kctl_volume = kctl;
|
||||||
|
prm->volume = fu->volume_max;
|
||||||
|
prm->volume_max = fu->volume_max;
|
||||||
|
prm->volume_min = fu->volume_min;
|
||||||
|
prm->volume_res = fu->volume_res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strscpy(card->driver, card_name, sizeof(card->driver));
|
strscpy(card->driver, card_name, sizeof(card->driver));
|
||||||
strscpy(card->shortname, card_name, sizeof(card->shortname));
|
strscpy(card->shortname, card_name, sizeof(card->shortname));
|
||||||
sprintf(card->longname, "%s %i", card_name, card->dev->id);
|
sprintf(card->longname, "%s %i", card_name, card->dev->id);
|
||||||
|
|
|
@ -19,16 +19,30 @@
|
||||||
*/
|
*/
|
||||||
#define FBACK_SLOW_MAX 250
|
#define FBACK_SLOW_MAX 250
|
||||||
|
|
||||||
|
/* Feature Unit parameters */
|
||||||
|
struct uac_fu_params {
|
||||||
|
int id; /* Feature Unit ID */
|
||||||
|
|
||||||
|
bool mute_present; /* mute control enable */
|
||||||
|
|
||||||
|
bool volume_present; /* volume control enable */
|
||||||
|
s16 volume_min; /* min volume in 1/256 dB */
|
||||||
|
s16 volume_max; /* max volume in 1/256 dB */
|
||||||
|
s16 volume_res; /* volume resolution in 1/256 dB */
|
||||||
|
};
|
||||||
|
|
||||||
struct uac_params {
|
struct uac_params {
|
||||||
/* playback */
|
/* playback */
|
||||||
int p_chmask; /* channel mask */
|
int p_chmask; /* channel mask */
|
||||||
int p_srate; /* rate in Hz */
|
int p_srate; /* rate in Hz */
|
||||||
int p_ssize; /* sample size */
|
int p_ssize; /* sample size */
|
||||||
|
struct uac_fu_params p_fu; /* Feature Unit parameters */
|
||||||
|
|
||||||
/* capture */
|
/* capture */
|
||||||
int c_chmask; /* channel mask */
|
int c_chmask; /* channel mask */
|
||||||
int c_srate; /* rate in Hz */
|
int c_srate; /* rate in Hz */
|
||||||
int c_ssize; /* sample size */
|
int c_ssize; /* sample size */
|
||||||
|
struct uac_fu_params c_fu; /* Feature Unit parameters */
|
||||||
|
|
||||||
int req_number; /* number of preallocated requests */
|
int req_number; /* number of preallocated requests */
|
||||||
int fb_max; /* upper frequency drift feedback limit per-mil */
|
int fb_max; /* upper frequency drift feedback limit per-mil */
|
||||||
|
@ -49,6 +63,9 @@ struct g_audio {
|
||||||
/* Max packet size for all out_ep possible speeds */
|
/* Max packet size for all out_ep possible speeds */
|
||||||
unsigned int out_ep_maxpsize;
|
unsigned int out_ep_maxpsize;
|
||||||
|
|
||||||
|
/* Notify UAC driver about control change */
|
||||||
|
int (*notify)(struct g_audio *g_audio, int unit_id, int cs);
|
||||||
|
|
||||||
/* The ALSA Sound Card it represents on the USB-Client side */
|
/* The ALSA Sound Card it represents on the USB-Client side */
|
||||||
struct snd_uac_chip *uac;
|
struct snd_uac_chip *uac;
|
||||||
|
|
||||||
|
@ -94,4 +111,9 @@ void u_audio_stop_capture(struct g_audio *g_audio);
|
||||||
int u_audio_start_playback(struct g_audio *g_audio);
|
int u_audio_start_playback(struct g_audio *g_audio);
|
||||||
void u_audio_stop_playback(struct g_audio *g_audio);
|
void u_audio_stop_playback(struct g_audio *g_audio);
|
||||||
|
|
||||||
|
int u_audio_get_volume(struct g_audio *g_audio, int playback, s16 *val);
|
||||||
|
int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val);
|
||||||
|
int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val);
|
||||||
|
int u_audio_set_mute(struct g_audio *g_audio, int playback, int val);
|
||||||
|
|
||||||
#endif /* __U_AUDIO_H */
|
#endif /* __U_AUDIO_H */
|
||||||
|
|
Loading…
Add table
Reference in a new issue