in gadget/function/u_audio.c [978:1213]
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
const char *card_name)
{
struct snd_uac_chip *uac;
struct snd_card *card;
struct snd_pcm *pcm;
struct snd_kcontrol *kctl;
struct uac_params *params;
int p_chmask, c_chmask;
int i, err;
if (!g_audio)
return -EINVAL;
uac = kzalloc(sizeof(*uac), GFP_KERNEL);
if (!uac)
return -ENOMEM;
g_audio->uac = uac;
uac->audio_dev = g_audio;
params = &g_audio->params;
p_chmask = params->p_chmask;
c_chmask = params->c_chmask;
if (c_chmask) {
struct uac_rtd_params *prm = &uac->c_prm;
spin_lock_init(&prm->lock);
uac->c_prm.uac = uac;
prm->max_psize = g_audio->out_ep_maxpsize;
prm->reqs = kcalloc(params->req_number,
sizeof(struct usb_request *),
GFP_KERNEL);
if (!prm->reqs) {
err = -ENOMEM;
goto fail;
}
prm->rbuf = kcalloc(params->req_number, prm->max_psize,
GFP_KERNEL);
if (!prm->rbuf) {
prm->max_psize = 0;
err = -ENOMEM;
goto fail;
}
}
if (p_chmask) {
struct uac_rtd_params *prm = &uac->p_prm;
spin_lock_init(&prm->lock);
uac->p_prm.uac = uac;
prm->max_psize = g_audio->in_ep_maxpsize;
prm->reqs = kcalloc(params->req_number,
sizeof(struct usb_request *),
GFP_KERNEL);
if (!prm->reqs) {
err = -ENOMEM;
goto fail;
}
prm->rbuf = kcalloc(params->req_number, prm->max_psize,
GFP_KERNEL);
if (!prm->rbuf) {
prm->max_psize = 0;
err = -ENOMEM;
goto fail;
}
}
/* Choose any slot, with no id */
err = snd_card_new(&g_audio->gadget->dev,
-1, NULL, THIS_MODULE, 0, &card);
if (err < 0)
goto fail;
uac->card = card;
/*
* Create first PCM device
* Create a substream only for non-zero channel streams
*/
err = snd_pcm_new(uac->card, pcm_name, 0,
p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm);
if (err < 0)
goto snd_fail;
strscpy(pcm->name, pcm_name, sizeof(pcm->name));
pcm->private_data = uac;
uac->pcm = pcm;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
/*
* 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));
if (c_chmask && g_audio->in_ep_fback) {
kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL],
&uac->c_prm);
if (!kctl) {
err = -ENOMEM;
goto snd_fail;
}
kctl->id.device = pcm->device;
kctl->id.subdevice = 0;
err = snd_ctl_add(card, kctl);
if (err < 0)
goto snd_fail;
}
if (p_chmask) {
kctl = snd_ctl_new1(&u_audio_controls[UAC_P_PITCH_CTRL],
&uac->p_prm);
if (!kctl) {
err = -ENOMEM;
goto snd_fail;
}
kctl->id.device = pcm->device;
kctl->id.subdevice = 0;
err = snd_ctl_add(card, kctl);
if (err < 0)
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 = 0;
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 = 0;
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->shortname, card_name, sizeof(card->shortname));
sprintf(card->longname, "%s %i", card_name, card->dev->id);
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
NULL, 0, BUFF_SIZE_MAX);
err = snd_card_register(card);
if (!err)
return 0;
snd_fail:
snd_card_free(card);
fail:
kfree(uac->p_prm.reqs);
kfree(uac->c_prm.reqs);
kfree(uac->p_prm.rbuf);
kfree(uac->c_prm.rbuf);
kfree(uac);
return err;
}