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 = &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);//启动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的实现。

  

 

 

 

 

posted @ 2025-08-15 21:14  Action_er  阅读(50)  评论(0)    收藏  举报