当前位置: 首页 > news >正文

UAC

1 UAC 就是USB audio class,可以通过usb传输audio data

UAC作为usb的function,向usb注册function

 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 descriptorshs_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

这个函数主要作用:

(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 = &params->p_fu;direction = "Playback";} else {prm = &uac->c_prm;fu = &params->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);//启动captureelseu_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);//启动playbackelseu_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;elsefactor = 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);elseuac->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);//更新ptrexit: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的实现。

  

 

 

 

 

http://www.sczhlp.com/news/12667/

相关文章:

  • 鸽巢
  • yny组合计数
  • unixODBC编程(八)使用滚动游标
  • unixODBC编程(九)分片查询长数据
  • 题解 P3380【模板】树套树(莫队套分块)
  • unixODBC编程(四)插入数据
  • 考研题:假设二叉树采用二叉链存储结构,设计一个算法,计算该二叉树中结点的总数。
  • 2.11 rt-thread实操 esp8266 +webserver
  • unxiODBC编程(五)错误处理
  • Kotlin基础
  • JAVA学习(8月15号)
  • 硬币购物
  • Docker知识点2 - Charon
  • unixODBC编程(二)连接数据库
  • 河南萌新联赛2025第(五)场题解:I J(LCA)
  • Ruby 和 Tesseract OCR 实现验证码识别
  • Kotlin 和 Tesseract OCR 实现验证码识别
  • ReasonRank:从关键词匹配到逻辑推理,排序准确性大幅超越传统方法
  • 【ARM Cache 及 MMU 系列文章 6.2 -- ARMv8v9 如何读取 Cache 内部数据并对其进行解析?】
  • ARM - SME 指令
  • 细思极恐怖如斯
  • 牛客2025多校 R5
  • HTML第三次作业 - 详解
  • 高效神经组合优化求解器解决最小最大异构容量车辆路径问题
  • 低成本电阻网络兼容FPGA_DPHY的简要概括
  • 2025 暑假集训 Day10
  • 8gu-JVM
  • JWT 这点小秘密,你们肯定知道!
  • tarjan学习笔记
  • 用stm32f407zgt6说明stm系列芯片命名规则