UAC
1 UAC 就是USB audio class,可以通过usb传输audio data
UAC作为usb的function,向usb注册function
code位于:linux-5.15.11\drivers\usb\gadget\function\f_uac2.c
1 static struct usb_function *afunc_alloc(struct usb_function_instance *fi) 2 { 3 struct f_uac2 *uac2; 4 struct f_uac2_opts *opts; 5 6 uac2 = kzalloc(sizeof(*uac2), GFP_KERNEL); 7 if (uac2 == NULL) 8 return ERR_PTR(-ENOMEM); 9 10 opts = container_of(fi, struct f_uac2_opts, func_inst); 11 mutex_lock(&opts->lock); 12 ++opts->refcnt; 13 mutex_unlock(&opts->lock); 14 15 uac2->g_audio.func.name = "uac2_func"; 16 uac2->g_audio.func.bind = afunc_bind; 17 uac2->g_audio.func.unbind = afunc_unbind; 18 uac2->g_audio.func.set_alt = afunc_set_alt; 19 uac2->g_audio.func.get_alt = afunc_get_alt; 20 uac2->g_audio.func.disable = afunc_disable; 21 uac2->g_audio.func.setup = afunc_setup; 22 uac2->g_audio.func.free_func = afunc_free; 23 24 return &uac2->g_audio.func; 25 } 26 27 DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
2 afunc_bind 函数
将UAC注册到usb控制器时就会call这个函数,然后call函数g_audio_setup
static int
afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
{
struct f_uac2 *uac2 = func_to_uac2(fn);
struct g_audio *agdev = func_to_g_audio(fn);
struct usb_composite_dev *cdev = cfg->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &gadget->dev;
struct f_uac2_opts *uac2_opts = g_audio_to_uac2_opts(agdev);
struct usb_string *us;
int ret;
ret = afunc_validate_opts(agdev, dev);
if (ret)
return ret;
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
if (IS_ERR(us))
return PTR_ERR(us);
if (FUOUT_EN(uac2_opts)) {
out_feature_unit_desc = build_fu_desc(uac2_opts->c_chmask);
if (!out_feature_unit_desc)
return -ENOMEM;
}
if (FUIN_EN(uac2_opts)) {
in_feature_unit_desc = build_fu_desc(uac2_opts->p_chmask);
if (!in_feature_unit_desc) {
ret = -ENOMEM;
goto err_free_fu;
}
}
iad_desc.iFunction = us[STR_ASSOC].id;
std_ac_if_desc.iInterface = us[STR_IF_CTRL].id;
in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id;
out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id;
usb_out_it_desc.iTerminal = us[STR_USB_IT].id;
io_in_it_desc.iTerminal = us[STR_IO_IT].id;
usb_in_ot_desc.iTerminal = us[STR_USB_OT].id;
io_out_ot_desc.iTerminal = us[STR_IO_OT].id;
std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id;
std_as_out_if1_desc.iInterface = us[STR_AS_OUT_ALT1].id;
std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id;
std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id;
if (FUOUT_EN(uac2_opts)) {
u8 *i_feature = (u8 *)out_feature_unit_desc +
out_feature_unit_desc->bLength - 1;
*i_feature = us[STR_FU_OUT].id;
}
if (FUIN_EN(uac2_opts)) {
u8 *i_feature = (u8 *)in_feature_unit_desc +
in_feature_unit_desc->bLength - 1;
*i_feature = us[STR_FU_IN].id;
}
/* Initialize the configurable parameters */
usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
usb_out_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
io_in_it_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
io_in_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
as_out_hdr_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
as_out_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
as_in_hdr_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
as_in_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
as_out_fmt1_desc.bSubslotSize = uac2_opts->c_ssize;
as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8;
as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize;
as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8;
if (FUOUT_EN(uac2_opts)) {
__le32 *bma = (__le32 *)&out_feature_unit_desc->bmaControls[0];
u32 control = 0;
if (uac2_opts->c_mute_present)
control |= CONTROL_RDWR << FU_MUTE_CTRL;
if (uac2_opts->c_volume_present)
control |= CONTROL_RDWR << FU_VOL_CTRL;
*bma = cpu_to_le32(control);
}
if (FUIN_EN(uac2_opts)) {
__le32 *bma = (__le32 *)&in_feature_unit_desc->bmaControls[0];
u32 control = 0;
if (uac2_opts->p_mute_present)
control |= CONTROL_RDWR << FU_MUTE_CTRL;
if (uac2_opts->p_volume_present)
control |= CONTROL_RDWR << FU_VOL_CTRL;
*bma = cpu_to_le32(control);
}
snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
goto err_free_fu;
}
iad_desc.bFirstInterface = ret;
std_ac_if_desc.bInterfaceNumber = ret;
uac2->ac_intf = ret;
uac2->ac_alt = 0;
if (EPOUT_EN(uac2_opts)) {
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
goto err_free_fu;
}
std_as_out_if0_desc.bInterfaceNumber = ret;
std_as_out_if1_desc.bInterfaceNumber = ret;
uac2->as_out_intf = ret;
uac2->as_out_alt = 0;
if (EPOUT_FBACK_IN_EN(uac2_opts)) {
fs_epout_desc.bmAttributes =
USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
hs_epout_desc.bmAttributes =
USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
ss_epout_desc.bmAttributes =
USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
std_as_out_if1_desc.bNumEndpoints++;
} else {
fs_epout_desc.bmAttributes =
USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
hs_epout_desc.bmAttributes =
USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
ss_epout_desc.bmAttributes =
USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
}
}
if (EPIN_EN(uac2_opts)) {
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
goto err_free_fu;
}
std_as_in_if0_desc.bInterfaceNumber = ret;
std_as_in_if1_desc.bInterfaceNumber = ret;
uac2->as_in_intf = ret;
uac2->as_in_alt = 0;
}
if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts)) {
uac2->int_ep = usb_ep_autoconfig(gadget, &fs_ep_int_desc);
if (!uac2->int_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
ret = -ENODEV;
goto err_free_fu;
}
std_ac_if_desc.bNumEndpoints = 1;
}
/* Calculate wMaxPacketSize according to audio bandwidth */
ret = set_ep_max_packet_size(uac2_opts, &fs_epin_desc, USB_SPEED_FULL,
true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
ret = set_ep_max_packet_size(uac2_opts, &fs_epout_desc, USB_SPEED_FULL,
false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
ret = set_ep_max_packet_size(uac2_opts, &hs_epin_desc, USB_SPEED_HIGH,
true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
ret = set_ep_max_packet_size(uac2_opts, &hs_epout_desc, USB_SPEED_HIGH,
false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
ret = set_ep_max_packet_size(uac2_opts, &ss_epin_desc, USB_SPEED_SUPER,
true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
ret = set_ep_max_packet_size(uac2_opts, &ss_epout_desc, USB_SPEED_SUPER,
false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
if (EPOUT_EN(uac2_opts)) {
agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
if (!agdev->out_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
ret = -ENODEV;
goto err_free_fu;
}
if (EPOUT_FBACK_IN_EN(uac2_opts)) {
agdev->in_ep_fback = usb_ep_autoconfig(gadget,
&fs_epin_fback_desc);
if (!agdev->in_ep_fback) {
dev_err(dev, "%s:%d Error!\n",
__func__, __LINE__);
ret = -ENODEV;
goto err_free_fu;
}
}
}
if (EPIN_EN(uac2_opts)) {
agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
if (!agdev->in_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
ret = -ENODEV;
goto err_free_fu;
}
}
agdev->in_ep_maxpsize = max_t(u16,
le16_to_cpu(fs_epin_desc.wMaxPacketSize),
le16_to_cpu(hs_epin_desc.wMaxPacketSize));
agdev->out_ep_maxpsize = max_t(u16,
le16_to_cpu(fs_epout_desc.wMaxPacketSize),
le16_to_cpu(hs_epout_desc.wMaxPacketSize));
agdev->in_ep_maxpsize = max_t(u16, agdev->in_ep_maxpsize,
le16_to_cpu(ss_epin_desc.wMaxPacketSize));
agdev->out_ep_maxpsize = max_t(u16, agdev->out_ep_maxpsize,
le16_to_cpu(ss_epout_desc.wMaxPacketSize));
ss_epin_desc_comp.wBytesPerInterval = ss_epin_desc.wMaxPacketSize;
ss_epout_desc_comp.wBytesPerInterval = ss_epout_desc.wMaxPacketSize;
// HS and SS endpoint addresses are copied from autoconfigured FS descriptors
hs_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress;
hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
hs_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress;
hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
ss_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
ss_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress;
ss_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
ss_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress;
setup_descriptor(uac2_opts);
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, ss_audio_desc,
ss_audio_desc);
if (ret)
goto err_free_fu;
agdev->gadget = gadget;
agdev->params.p_chmask = uac2_opts->p_chmask;
agdev->params.p_srate = uac2_opts->p_srate;
agdev->params.p_ssize = uac2_opts->p_ssize;
if (FUIN_EN(uac2_opts)) {
agdev->params.p_fu.id = USB_IN_FU_ID;
agdev->params.p_fu.mute_present = uac2_opts->p_mute_present;
agdev->params.p_fu.volume_present = uac2_opts->p_volume_present;
agdev->params.p_fu.volume_min = uac2_opts->p_volume_min;
agdev->params.p_fu.volume_max = uac2_opts->p_volume_max;
agdev->params.p_fu.volume_res = uac2_opts->p_volume_res;
}
agdev->params.c_chmask = uac2_opts->c_chmask;
agdev->params.c_srate = uac2_opts->c_srate;
agdev->params.c_ssize = uac2_opts->c_ssize;
if (FUOUT_EN(uac2_opts)) {
agdev->params.c_fu.id = USB_OUT_FU_ID;
agdev->params.c_fu.mute_present = uac2_opts->c_mute_present;
agdev->params.c_fu.volume_present = uac2_opts->c_volume_present;
agdev->params.c_fu.volume_min = uac2_opts->c_volume_min;
agdev->params.c_fu.volume_max = uac2_opts->c_volume_max;
agdev->params.c_fu.volume_res = uac2_opts->c_volume_res;
}
agdev->params.req_number = uac2_opts->req_number;
agdev->params.fb_max = uac2_opts->fb_max;
if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts))
agdev->notify = afunc_notify;
ret = g_audio_setup(agdev, "UAC2 PCM", "UAC2_Gadget");
if (ret)
goto err_free_descs;
return 0;
err_free_descs:
usb_free_all_descriptors(fn);
agdev->gadget = NULL;
err_free_fu:
kfree(out_feature_unit_desc);
out_feature_unit_desc = NULL;
kfree(in_feature_unit_desc);
in_feature_unit_desc = NULL;
return ret;
}
2.1 g_audio_setup
code位于:linux-5.15.11\drivers\usb\gadget\function\u_audio.c
这个函数主要作用:
(1)创建pcm device,包括playback 和capture
(2)设置pcm device ops
(3)call 函数snd_pcm_set_managed_buffer_all 设置dma分配的参数substream->managed_buffer_alloc = 1,然后在snd_pcm_hw_params时分配DMA
(4)注册声卡
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;
}
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->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;
}
3 afunc_set_alt函数
static int
afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
{
struct usb_composite_dev *cdev = fn->config->cdev;
struct f_uac2 *uac2 = func_to_uac2(fn);
struct g_audio *agdev = func_to_g_audio(fn);
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &gadget->dev;
int ret = 0;
/* No i/f has more than 2 alt settings */
if (alt > 1) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL;
}
if (intf == uac2->ac_intf) {
/* Control I/f has only 1 AltSetting - 0 */
if (alt) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL;
}
/* restart interrupt endpoint */
if (uac2->int_ep) {
usb_ep_disable(uac2->int_ep);
config_ep_by_speed(gadget, &agdev->func, uac2->int_ep);
usb_ep_enable(uac2->int_ep);
}
return 0;
}
if (intf == uac2->as_out_intf) {
uac2->as_out_alt = alt;
if (alt)
ret = u_audio_start_capture(&uac2->g_audio);//启动capture
else
u_audio_stop_capture(&uac2->g_audio);
} else if (intf == uac2->as_in_intf) {
uac2->as_in_alt = alt;
if (alt)
ret = u_audio_start_playback(&uac2->g_audio);//启动playback
else
u_audio_stop_playback(&uac2->g_audio);
} else {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL;
}
return ret;
}
3.1 u_audio_start_capture
真正开始capture在回调函数u_audio_iso_complete
int u_audio_start_capture(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
struct usb_gadget *gadget = audio_dev->gadget;
struct device *dev = &gadget->dev;
struct usb_request *req, *req_fback;
struct usb_ep *ep, *ep_fback;
struct uac_rtd_params *prm;
struct uac_params *params = &audio_dev->params;
int req_len, i;
ep = audio_dev->out_ep;
prm = &uac->c_prm;
config_ep_by_speed(gadget, &audio_dev->func, ep);
req_len = ep->maxpacket;
prm->ep_enabled = true;
usb_ep_enable(ep);
for (i = 0; i < params->req_number; i++) {
if (!prm->reqs[i]) {
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (req == NULL)
return -ENOMEM;
prm->reqs[i] = req;
req->zero = 0;
req->context = prm;
req->length = req_len;
req->complete = u_audio_iso_complete;//赋值函数指针,usb有数据传过来时会call这个函数
req->buf = prm->rbuf + i * ep->maxpacket;
}
if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC))//加入队列,usb控制器会以此处理queue中device请求
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
}
ep_fback = audio_dev->in_ep_fback;
if (!ep_fback)
return 0;
/* Setup feedback endpoint */
config_ep_by_speed(gadget, &audio_dev->func, ep_fback);
prm->fb_ep_enabled = true;
usb_ep_enable(ep_fback);
req_len = ep_fback->maxpacket;
req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC);
if (req_fback == NULL)
return -ENOMEM;
prm->req_fback = req_fback;
req_fback->zero = 0;
req_fback->context = prm;
req_fback->length = req_len;
req_fback->complete = u_audio_iso_fback_complete;
req_fback->buf = kzalloc(req_len, GFP_ATOMIC);
if (!req_fback->buf)
return -ENOMEM;
/*
* Configure the feedback endpoint's reported frequency.
* Always start with original frequency since its deviation can't
* be meauserd at start of playback
*/
prm->pitch = 1000000;
u_audio_set_fback_frequency(audio_dev->gadget->speed, ep,
params->c_srate, prm->pitch,
req_fback->buf);
if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC))
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return 0;
}
3.2 u_audio_start_playback
真正开始playback在回调函数u_audio_iso_complete
int u_audio_start_playback(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
struct usb_gadget *gadget = audio_dev->gadget;
struct device *dev = &gadget->dev;
struct usb_request *req;
struct usb_ep *ep;
struct uac_rtd_params *prm;
struct uac_params *params = &audio_dev->params;
unsigned int factor;
const struct usb_endpoint_descriptor *ep_desc;
int req_len, i;
ep = audio_dev->in_ep;
prm = &uac->p_prm;
config_ep_by_speed(gadget, &audio_dev->func, ep);
ep_desc = ep->desc;
/* pre-calculate the playback endpoint's interval */
if (gadget->speed == USB_SPEED_FULL)
factor = 1000;
else
factor = 8000;
/* pre-compute some values for iso_complete() */
uac->p_framesize = params->p_ssize *
num_channels(params->p_chmask);
uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
uac->p_pktsize = min_t(unsigned int,
uac->p_framesize *
(params->p_srate / uac->p_interval),
ep->maxpacket);
if (uac->p_pktsize < ep->maxpacket)
uac->p_pktsize_residue = uac->p_framesize *
(params->p_srate % uac->p_interval);
else
uac->p_pktsize_residue = 0;
req_len = uac->p_pktsize;
uac->p_residue = 0;
prm->ep_enabled = true;
usb_ep_enable(ep);
for (i = 0; i < params->req_number; i++) {
if (!prm->reqs[i]) {
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (req == NULL)
return -ENOMEM;
prm->reqs[i] = req;
req->zero = 0;
req->context = prm;
req->length = req_len;
req->complete = u_audio_iso_complete;//赋值函数指针,usb需要传输数据时会call这个函数
req->buf = prm->rbuf + i * ep->maxpacket;
}
if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC))//加入queue,等待usb core处理
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
}
return 0;
}
3.3 u_audio_iso_complete
static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
{
unsigned int pending;
unsigned int hw_ptr;
int status = req->status;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
struct uac_rtd_params *prm = req->context;
struct snd_uac_chip *uac = prm->uac;
/* i/f shutting down */
if (!prm->ep_enabled) {
usb_ep_free_request(ep, req);
return;
}
if (req->status == -ESHUTDOWN)
return;
/*
* We can't really do much about bad xfers.
* Afterall, the ISOCH xfers could fail legitimately.
*/
if (status)
pr_debug("%s: iso_complete status(%d) %d/%d\n",
__func__, status, req->actual, req->length);
substream = prm->ss;
/* Do nothing if ALSA isn't active */
if (!substream)
goto exit;
snd_pcm_stream_lock(substream);
runtime = substream->runtime;
if (!runtime || !snd_pcm_running(substream)) {
snd_pcm_stream_unlock(substream);
goto exit;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/*
* For each IN packet, take the quotient of the current data
* rate and the endpoint's interval as the base packet size.
* If there is a residue from this division, add it to the
* residue accumulator.
*/
req->length = uac->p_pktsize;
uac->p_residue += uac->p_pktsize_residue;
/*
* Whenever there are more bytes in the accumulator than we
* need to add one more sample frame, increase this packet's
* size and decrease the accumulator.
*/
if (uac->p_residue / uac->p_interval >= uac->p_framesize) {
req->length += uac->p_framesize;
uac->p_residue -= uac->p_framesize *
uac->p_interval;
}
req->actual = req->length;
}
hw_ptr = prm->hw_ptr;
/* Pack USB load in ALSA ring buffer */
pending = runtime->dma_bytes - hw_ptr;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {//playback把DMA audio data 放到usb buf中
if (unlikely(pending < req->actual)) {
memcpy(req->buf, runtime->dma_area + hw_ptr, pending);
memcpy(req->buf + pending, runtime->dma_area,
req->actual - pending);
} else {
memcpy(req->buf, runtime->dma_area + hw_ptr,
req->actual);
}
} else {
if (unlikely(pending < req->actual)) { //capture,把usb buf audio data放到dma中
memcpy(runtime->dma_area + hw_ptr, req->buf, pending);
memcpy(runtime->dma_area, req->buf + pending,
req->actual - pending);
} else {
memcpy(runtime->dma_area + hw_ptr, req->buf,
req->actual);
}
}
/* update hw_ptr after data is copied to memory */
prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes;
hw_ptr = prm->hw_ptr;
snd_pcm_stream_unlock(substream);
if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
snd_pcm_period_elapsed(substream);//更新ptr
exit:
if (usb_ep_queue(ep, req, GFP_ATOMIC))
dev_err(uac->card->dev, "%d Error!\n", __LINE__);
}
总结,以上UAC code主要时usb作为device mode时注册UAC声卡,和alsa交互code的实现。
行胜于言,自强不息。

浙公网安备 33010602011771号