usb声卡驱动(七):prepare和端点管理

usb声卡驱动(七)

前面记录了pcm的打开关闭,还有硬件参数和软件参数的设置

现在就可以进行播放了。

在播放之前,需要通过ioctl,传递SNDRV_PCM_IOCTL_PREPARE来调用prepare,alsa内部的prepare的逻辑实现,不做过多的探究。

当应用传递命令SNDRV_PCM_IOCTL_PREPARE时,usb声卡驱动,将会调用pcm的prepare回调。见《usb声卡驱动(四)》prepare

在前面的介绍中,open,hw_params回调已经将部分设置,初始化好了。剩下的就交给prepare回调了,看看prepare的回调的具体内容:

static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
{
	

    //各种判断,无需赘述
	

    //同步端点,数据端点,等待完全停止
	snd_usb_endpoint_sync_pending_stop(subs->sync_endpoint);
	snd_usb_endpoint_sync_pending_stop(subs->data_endpoint);

    //设置当前的音频格式,即切换到正确的接口,并创建对应的endpoint封装对象
	//以后usb声卡的直接操作这个endpoint封装对象,类型为:snd_usb_endpoint
	ret = set_format(subs, subs->cur_audiofmt);

    //初始化采样率,内部通过usb的快捷方法,snd_usb_ctl_msg进行设置
	ret = snd_usb_init_sample_rate(subs->stream->chip,
				       subs->cur_audiofmt->iface,
				       alts,
				       subs->cur_audiofmt,
				       subs->cur_rate);
	
    //需要配置subs里面创建的snd_usb_endpoint对象。
	//每次硬件参数更改,也需要重新配置
	if (subs->need_setup_ep) {
		ret = configure_endpoint(subs);
		if (ret < 0)
			goto unlock;
		subs->need_setup_ep = false;
	}


	/* for playback, submit the URBs now; otherwise, the first hwptr_done
	 * updates for all URBs would happen at the same time when starting */
     //如果是播放,现在开始启动端点
	 //启动端点,先启动数据端点,然后是同步端点。
	 //端点的启动,即提交urb给HCD。当没有数据时,发送的是静默数据。
	if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
		ret = start_endpoints(subs, true);

}

上面的函数,做了如下的事情:

  1. 切换这个substream对应的usb接口和设置。并创建usb声卡需要操作的usb端点封装对象snd_usb_endpoint。它在set_format函数中完成
  2. 对上面的封装对象进行初始化。初始中,最主要的就是创建每个period使用的urb。它在configure_endpoint函数中完成
  3. 如果是播放端点,则直接开始。此时,应用向驱动写入数据,即可开始播放
  4. 顺便初始化了采样率,pitch

总体来说prepare只有两部分:

  1. 声卡内部对象的创建和初始化,这个对象就是usb端点的封装对象
  2. 硬件的一些初始化,如采样率,pitch等。

usb声卡的端点管理

1. 端点的创建

在继续prepare之前,需要先对usb声卡内部的一个数据结构进行介绍。它就是usb端点的封装对象snd_usb_endpoint如下:

struct snd_usb_endpoint {
	struct snd_usb_audio *chip;

	int use_count;
	int ep_num;		/* the referenced endpoint number */
	int type;		/* SND_USB_ENDPOINT_TYPE_* */
	unsigned long flags;

	void (*prepare_data_urb) (struct snd_usb_substream *subs,
				  struct urb *urb);
	void (*retire_data_urb) (struct snd_usb_substream *subs,
				 struct urb *urb);

	struct snd_usb_substream *data_subs;
	struct snd_usb_endpoint *sync_master;
	struct snd_usb_endpoint *sync_slave;

	struct snd_urb_ctx urb[MAX_URBS];

	struct snd_usb_packet_info {
		uint32_t packet_size[MAX_PACKS_HS];
		int packets;
	} next_packet[MAX_URBS];
	int next_packet_read_pos, next_packet_write_pos;
	struct list_head ready_playback_urbs;

	unsigned int nurbs;		/* # urbs */
	unsigned long active_mask;	/* bitmask of active urbs */
	unsigned long unlink_mask;	/* bitmask of unlinked urbs */
	char *syncbuf;			/* sync buffer for all sync URBs */
	dma_addr_t sync_dma;		/* DMA address of syncbuf */

	unsigned int pipe;		/* the data i/o pipe */
	unsigned int freqn;		/* nominal sampling rate in fs/fps in Q16.16 format */
	unsigned int freqm;		/* momentary sampling rate in fs/fps in Q16.16 format */
	int	   freqshift;		/* how much to shift the feedback value to get Q16.16 */
	unsigned int freqmax;		/* maximum sampling rate, used for buffer management */
	unsigned int phase;		/* phase accumulator */
	unsigned int maxpacksize;	/* max packet size in bytes */
	unsigned int maxframesize;      /* max packet size in frames */
	unsigned int max_urb_frames;	/* max URB size in frames */
	unsigned int curpacksize;	/* current packet size in bytes (for capture) */
	unsigned int curframesize;      /* current packet size in frames (for capture) */
	unsigned int syncmaxsize;	/* sync endpoint packet size */
	unsigned int fill_max:1;	/* fill max packet size always */
	unsigned int udh01_fb_quirk:1;	/* corrupted feedback data */
	unsigned int datainterval;      /* log_2 of data packet interval */
	unsigned int syncinterval;	/* P for adaptive mode, 0 otherwise */
	unsigned char silence_value;
	unsigned int stride;
	int iface, altsetting;
	int skip_packets;		/* quirks for devices to ignore the first n packets
					   in a stream */

	spinlock_t lock;
	struct list_head list;
};
  1. chip:chip对象指针
  2. use_count:这个端点的使用者的个数
  3. ep_num,type:usb endpoint数和类型
  4. flags:用于表示各种状态,如:EP_FLAG_STOPPING和EP_FLAG_RUNNING分别表示正在停止和正在运行
  5. prepare_data_urb:在正式提交urb之前,这个回调函数会被回调。用于urb的一些特殊处理
  6. retire_data_urb:在使用完成之后,这个回调会被回调
  7. data_subs:对应的substream
  8. sync_master:同步用的端点,数据端点赋值此值为同接口下的同步端点
  9. sync_slave:同步用的端点,同步端点赋值此值为同接口下的数据端点
  10. urb[]:urb的封装对象数组,个数为nurbs
  11. next_packet[]:为隐式fb端点而设计,在此不表
  12. next_packet_read_pos, next_packet_write_pos:为隐式fb端点设计,在此不表
  13. ready_playback_urbs:为隐式fb端点设计,在此不表
  14. nurbs:每个period需要提交的urb个数
  15. active_mask:激活的urb的bitmask,即每提交一个urb就会将对应的位置位
  16. unlink_mask:unlink的urb的bitmask,即每unlink一个urb对应的位就会被置位
  17. syncbuf:同步端点使用的buffer指针
  18. sync_dma:同步buffer的dma指针
  19. pipe: 数据io管道
  20. freqn:Q16.16格式的标准采样速率
  21. freqm:Q16.16格式的瞬时采样速率
  22. freqshift:反馈值需要移动freqshift才能得到Q16.16
  23. freqmax:最大采样值
  24. phase:phase累加器
  25. maxpacksize:包最大大小
  26. maxframesize:每帧中的包的最大值
  27. max_urb_frames: 每帧中的urb最大值
  28. curpacksize:当前包大小,单位字节
  29. curframesize:当前包大小,单位帧
  30. syncmaxsize:同步端点packet大小
  31. fill_max:是否要使用最大的包大小
  32. udh01_fb_quirk:反馈数据是否损坏
  33. datainterval:数据包间隔
  34. syncinterval:正值表示自适应模式。否则为0
  35. silence_value:静态数据的值
  36. stride:每帧字节数
  37. iface, altsetting:接口相关:iface,接口。altsetting可选设置
  38. skip_packets:忽略,前面的skip_packets个数字的包

snd_usb_endpoint对象的创建,使用snd_usb_add_endpoint函数。如下:

struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
					      struct usb_host_interface *alts,
					      int ep_num, int direction, int type)
{
	//...
	mutex_lock(&chip->mutex);

	//寻找空闲的snd_usb_endpoint对象,如果找到,就返回
	list_for_each_entry(ep, &chip->ep_list, list) {
		if (ep->ep_num == ep_num &&
		    ep->iface == alts->desc.bInterfaceNumber &&
		    ep->altsetting == alts->desc.bAlternateSetting) {
			usb_audio_dbg(ep->chip,
				      "Re-using EP %x in iface %d,%d @%pK\n",
					ep_num, ep->iface, ep->altsetting, ep);
			goto __exit_unlock;
		}
	}

	usb_audio_dbg(chip, "Creating new %s %s endpoint #%x\n",
		    is_playback ? "playback" : "capture",
		    type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync",
		    ep_num);

	//如果没有找到,则重新创建
	//然后各种初始化
	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
	if (!ep)
		goto __exit_unlock;

	ep->chip = chip;
	spin_lock_init(&ep->lock);
	ep->type = type;
	ep->ep_num = ep_num;
	ep->iface = alts->desc.bInterfaceNumber;
	ep->altsetting = alts->desc.bAlternateSetting;
	INIT_LIST_HEAD(&ep->ready_playback_urbs);
	ep_num &= USB_ENDPOINT_NUMBER_MASK;

	if (is_playback)
		ep->pipe = usb_sndisocpipe(chip->dev, ep_num);
	else
		ep->pipe = usb_rcvisocpipe(chip->dev, ep_num);

	if (type == SND_USB_ENDPOINT_TYPE_SYNC) {
		if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
		    get_endpoint(alts, 1)->bRefresh >= 1 &&
		    get_endpoint(alts, 1)->bRefresh <= 9)
			ep->syncinterval = get_endpoint(alts, 1)->bRefresh;
		else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL)
			ep->syncinterval = 1;
		else if (get_endpoint(alts, 1)->bInterval >= 1 &&
			 get_endpoint(alts, 1)->bInterval <= 16)
			ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
		else
			ep->syncinterval = 3;

		ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);

		if (chip->usb_id == USB_ID(0x0644, 0x8038) /* TEAC UD-H01 */ &&
		    ep->syncmaxsize == 4)
			ep->udh01_fb_quirk = 1;
	}

	//接着放入chip对象的ep_list链表中
	list_add_tail(&ep->list, &chip->ep_list);

__exit_unlock:
	mutex_unlock(&chip->mutex);

	return ep;
}


从prepare知道,上面函数的触发就在prepare回调中。

2. 端点的配置

上面的函数,处理逻辑非常简单。一旦创建了snd_usb_endpoint之后,就可以开始使用它了。

使用这个snd_usb_endpoint之前,需要先对其进行配置。看看它的配置函数。

int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
				snd_pcm_format_t pcm_format,
				unsigned int channels,
				unsigned int period_bytes,
				unsigned int period_frames,
				unsigned int buffer_periods,
				unsigned int rate,
				struct audioformat *fmt,
				struct snd_usb_endpoint *sync_ep)
{
	int err;

	//如果正在使用,则不能够对其进行配置
	if (ep->use_count != 0) {
		usb_audio_warn(ep->chip,
			 "Unable to change format on ep #%x: already in use\n",
			 ep->ep_num);
		return -EBUSY;
	}

	/* release old buffers, if any */
	//因为需要在这个阶段,分配urb,所以需要将以前的urb都释放掉
	release_urbs(ep, 0);

	//又是各种赋值。无需赘言

	//下面根据端点的类型,分别调用数据端点或同步端点的配置函数。
	switch (ep->type) {
	case  SND_USB_ENDPOINT_TYPE_DATA:
		err = data_ep_set_params(ep, pcm_format, channels,
					 period_bytes, period_frames,
					 buffer_periods, fmt, sync_ep);
		break;
	case  SND_USB_ENDPOINT_TYPE_SYNC:
		err = sync_ep_set_params(ep);
		break;
	default:
		err = -EINVAL;
	}

	usb_audio_dbg(ep->chip,
		"Setting params for ep #%x (type %d, %d urbs), ret=%d\n",
		ep->ep_num, ep->type, ep->nurbs, err);

	return err;
}


上面函数,分别调用数据端点或同步端点的配置函数,如下:

static int data_ep_set_params(struct snd_usb_endpoint *ep,
			      snd_pcm_format_t pcm_format,
			      unsigned int channels,
			      unsigned int period_bytes,
			      unsigned int frames_per_period,
			      unsigned int periods_per_buffer,
			      struct audioformat *fmt,
			      struct snd_usb_endpoint *sync_ep)
{
	
	//...
	//各种各样对,nurbs的计算,计算的过程,暂且不表。大概步骤如下:
	/*
	1. 根据采样率和usb的传输间隔,计算出每次传输包的大小x
	2. period_size 除以x,得 n
	3. 然后根据各种最大值,求得period_size里面需要m个urb
	4. n除以m,即为nurbs的值
*/
	
	//根据nurbs,分配好urb
	/* allocate and initialize data urbs */
	for (i = 0; i < ep->nurbs; i++) {
		struct snd_urb_ctx *u = &ep->urb[i];
		u->index = i;
		u->ep = ep;
		u->packets = urb_packs;
		u->buffer_size = maxsize * u->packets;

		if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
			u->packets++; /* for transfer delimiter */
		u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
		if (!u->urb)
			goto out_of_memory;

		u->urb->transfer_buffer =
			usb_alloc_coherent(ep->chip->dev, u->buffer_size,
					   GFP_KERNEL, &u->urb->transfer_dma);
		if (!u->urb->transfer_buffer)
			goto out_of_memory;
		u->urb->pipe = ep->pipe;
		u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
		u->urb->interval = 1 << ep->datainterval;
		u->urb->context = u;
		u->urb->complete = snd_complete_urb;
		INIT_LIST_HEAD(&u->ready_list);
	}

	return 0;

out_of_memory:
	release_urbs(ep, 0);
	return -ENOMEM;
}

接下来看看,同步端点的配置,如下:

static int sync_ep_set_params(struct snd_usb_endpoint *ep)
{
	int i;

	ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4,
					 GFP_KERNEL, &ep->sync_dma);
	if (!ep->syncbuf)
		return -ENOMEM;

	for (i = 0; i < SYNC_URBS; i++) {
		struct snd_urb_ctx *u = &ep->urb[i];
		u->index = i;
		u->ep = ep;
		u->packets = 1;
		u->urb = usb_alloc_urb(1, GFP_KERNEL);
		if (!u->urb)
			goto out_of_memory;
		u->urb->transfer_buffer = ep->syncbuf + i * 4;
		u->urb->transfer_dma = ep->sync_dma + i * 4;
		u->urb->transfer_buffer_length = 4;
		u->urb->pipe = ep->pipe;
		u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
		u->urb->number_of_packets = 1;
		u->urb->interval = 1 << ep->syncinterval;
		u->urb->context = u;
		u->urb->complete = snd_complete_urb;
	}

	ep->nurbs = SYNC_URBS;

	return 0;

out_of_memory:
	release_urbs(ep, 0);
	return -ENOMEM;
}

同步端点的配置函数,跟数据端点的函数功能类似:分配必要的urb。不同的是,同步端点使用的urb数是固定的。

上面配置逻辑的触发,在每次硬件参数更改时,都会被触发。

3. 端点开始和停止

要进行数据的传输。就需要

  1. 将应用写入缓冲区的数据复制到urb中,然后提交到HCD。
  2. 然后在urb的complete中,继续重复1的步骤。

第一步的开始,使用snd_usb_endpoint_start触发。如下:

nt snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep)
{
	int err;
	unsigned int i;

	//处在关机状态下,则返回错误值
	if (ep->chip->shutdown)
		return -EBADFD;

	//如果已经开始了,则不用再次开始
	if (++ep->use_count != 1)
		return 0;

	//如果还有激活的urb,则直接unlink掉
	deactivate_urbs(ep, false);
	if (can_sleep)
		wait_clear_urbs(ep);

	ep->active_mask = 0;
	ep->unlink_mask = 0;
	ep->phase = 0;

	snd_usb_endpoint_start_quirk(ep);

	/*
	 * If this endpoint has a data endpoint as implicit feedback source,
	 * don't start the urbs here. Instead, mark them all as available,
	 * wait for the record urbs to return and queue the playback urbs
	 * from that context.
	 */

	//设置flag表示,其处于running状态
	set_bit(EP_FLAG_RUNNING, &ep->flags);

	//如果这个端点,是作为隐式FB端点,则将urb全部放入ready_playback_urbs队列,等待后面被触发
	//所有隐式FB端点不做讨论
	if (snd_usb_endpoint_implicit_feedback_sink(ep)) {
		for (i = 0; i < ep->nurbs; i++) {
			struct snd_urb_ctx *ctx = ep->urb + i;
			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
		}

		return 0;
	}

	//否则,提交nurbs个urb以满足,硬件的需求
	for (i = 0; i < ep->nurbs; i++) {
		struct urb *urb = ep->urb[i].urb;

		if (snd_BUG_ON(!urb))
			goto __error;

		//在提交之前,需要将缓冲区的数据,复制到urb中
		//prepare_xxbound_urb就是做这个用处
		if (usb_pipeout(ep->pipe)) {
			prepare_outbound_urb(ep, urb->context);
		} else {
			prepare_inbound_urb(ep, urb->context);
		}

		//提交
		err = usb_submit_urb(urb, GFP_ATOMIC);
		if (err < 0) {
			usb_audio_err(ep->chip,
				"cannot submit urb %d, error %d: %s\n",
				i, err, usb_error_string(err));
			goto __error;
		}
		set_bit(i, &ep->active_mask);
	}

	return 0;

__error:
	clear_bit(EP_FLAG_RUNNING, &ep->flags);
	ep->use_count--;
	deactivate_urbs(ep, false);
	return -EPIPE;
}

上面的函数,只有两件事:

  1. 复制数据
  2. 提交

复制数据,在prepare_xxbound_urb中完成。如下:

static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
				 struct snd_urb_ctx *ctx)
{
	//...

	switch (ep->type) {
	case SND_USB_ENDPOINT_TYPE_DATA:
		//如果有prepare_data_urb回调,则进入回调,否则用静默数据填充
		if (ep->prepare_data_urb) {
			ep->prepare_data_urb(ep->data_subs, urb);
		} else {
			/* no data provider, so send silence */
			unsigned int offs = 0;
			for (i = 0; i < ctx->packets; ++i) {
				int counts;

				if (ctx->packet_size[i])
					counts = ctx->packet_size[i];
				else
					counts = snd_usb_endpoint_next_packet_size(ep);

				urb->iso_frame_desc[i].offset = offs * ep->stride;
				urb->iso_frame_desc[i].length = counts * ep->stride;
				offs += counts;
			}

			urb->number_of_packets = ctx->packets;
			urb->transfer_buffer_length = offs * ep->stride;
			memset(urb->transfer_buffer, ep->silence_value,
			       offs * ep->stride);
		}
		break;

	case SND_USB_ENDPOINT_TYPE_SYNC:
		//填充同步端点的数据
		if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
			/*
			 * fill the length and offset of each urb descriptor.
			 * the fixed 12.13 frequency is passed as 16.16 through the pipe.
			 */
			urb->iso_frame_desc[0].length = 4;
			urb->iso_frame_desc[0].offset = 0;
			cp[0] = ep->freqn;
			cp[1] = ep->freqn >> 8;
			cp[2] = ep->freqn >> 16;
			cp[3] = ep->freqn >> 24;
		} else {
			/*
			 * fill the length and offset of each urb descriptor.
			 * the fixed 10.14 frequency is passed through the pipe.
			 */
			urb->iso_frame_desc[0].length = 3;
			urb->iso_frame_desc[0].offset = 0;
			cp[0] = ep->freqn >> 2;
			cp[1] = ep->freqn >> 10;
			cp[2] = ep->freqn >> 18;
		}

		break;
	}
}

在上面函数中,主要就是,数据的填充,而使用者,可以注册prepare_data_urb回调来填充自己的数据。如果没有,则默认填充静默数据。

当urb的控制权回到本驱动时,complete回调会被触发。如下:

static void snd_complete_urb(struct urb *urb)
{
	//必须做的urb的状态判断
	//...

    
	if (usb_pipeout(ep->pipe)) {
		//在再次复用urb之前,需要将urb的控制权,交给使用者,进行最后一步的处理
		//retire_xxbound_urb函数就是做这个用法的
		retire_outbound_urb(ep, ctx);
		/* can be stopped during retire callback */
		if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
			goto exit_clear;

		//隐式FB端点,暂且不表
		if (snd_usb_endpoint_implicit_feedback_sink(ep)) {
			unsigned long flags;

			spin_lock_irqsave(&ep->lock, flags);
			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
			spin_unlock_irqrestore(&ep->lock, flags);
			queue_pending_output_urbs(ep);

			goto exit_clear;
		}

		//在使用之前,需要赋值相应的数据,因此调用preapre函数
		prepare_outbound_urb(ep, ctx);
	} else {
		retire_inbound_urb(ep, ctx);
		/* can be stopped during retire callback */
		if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
			goto exit_clear;

		prepare_inbound_urb(ep, ctx);
	}

	//再次提交
	err = usb_submit_urb(urb, GFP_ATOMIC);
	if (err == 0)
		return;

	usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err);
	//snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);

exit_clear:
	clear_bit(ctx->index, &ep->active_mask);
}

complete函数主要做如下几件事情:

  1. 将urb的使用权,交给使用者,通过retire_xxbound_urb函数
  2. 再次使用时,调用prepare_xxbound_urb函数,复制数据
  3. 再次提交这个urb。

循环往复,这样端点就开始运行起来了。

接下来看看端点的停止。

端点停止,调用的函数,名字和开始函数类似,为:snd_usb_endpoint_stop

void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep)
{
	if (!ep)
		return;

	if (snd_BUG_ON(ep->use_count == 0))
		return;

	if (--ep->use_count == 0) {
		deactivate_urbs(ep, false);
		ep->data_subs = NULL;
		ep->sync_slave = NULL;
		ep->retire_data_urb = NULL;
		ep->prepare_data_urb = NULL;
		set_bit(EP_FLAG_STOPPING, &ep->flags);
	}
}

该函数的逻辑如下:

  1. 使用者减一
  2. 当使用者为0时,unlink所有的urb,并设置状态位:EP_FLAG_STOPPING

至此,usb端点的管理,告一段落。这个笔记里面主要记录的是他们的操作流程。而不是具体的数据分析。比如端点内部的interva怎么计算,每个周期需要多少urb。都不涉及。

想来这些问题,只需稍稍在纸上面动动笔,就能够解其中的奥秘了。

继续prepare

上面prepare回调中,有一个关键的函数,set_format。它会触发对应的端点对象的创建。

static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
{
	//...

	/* close the old interface */
	//关掉以前的interface
	if (subs->interface >= 0 && subs->interface != fmt->iface) {
		err = usb_set_interface(subs->dev, subs->interface, 0);
		if (err < 0) {
			dev_err(&dev->dev,
				"%d:%d: return to setting 0 failed (%d)\n",
				fmt->iface, fmt->altsetting, err);
			return -EIO;
		}
		subs->interface = -1;
		subs->altset_idx = 0;
	}

	/* set interface */
	//切换到期望的接口
	if (subs->interface != fmt->iface ||
	    subs->altset_idx != fmt->altset_idx) {

		err = snd_usb_select_mode_quirk(subs, fmt);
		if (err < 0)
			return -EIO;

		err = usb_set_interface(dev, fmt->iface, fmt->altsetting);
		if (err < 0) {
			dev_err(&dev->dev,
				"%d:%d: usb_set_interface failed (%d)\n",
				fmt->iface, fmt->altsetting, err);
			return -EIO;
		}
		dev_dbg(&dev->dev, "setting usb interface %d:%d\n",
			fmt->iface, fmt->altsetting);
		subs->interface = fmt->iface;
		subs->altset_idx = fmt->altset_idx;

		snd_usb_set_interface_quirk(dev);
	}

	//创建usb端点的封装对象。snd_usb_endpoint。
	//详见端点操作
	subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
						   alts, fmt->endpoint, subs->direction,
						   SND_USB_ENDPOINT_TYPE_DATA);

	if (!subs->data_endpoint)
		return -EINVAL;

	//创建同步端点使用的封装对象snd_usb_endpoint
	err = set_sync_endpoint(subs, fmt, dev, alts, altsd);
	if (err < 0)
		return err;

    //初始化pitch,内部调用snd_usb_ctl_msg函数完成
	err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt);
	if (err < 0)
		return err;

	subs->cur_audiofmt = fmt;

	return 0;
}

在上面的函数中,主要有两件事:

  1. 切换到对应的interface
  2. 然后分别创建数据端点,和同步端点的封装对象。他们分别保存在substream的data_endpoint和sync_endpoint中
  3. 顺便初始化pitch

看看set_sync_endpoint函数:

static int set_sync_endpoint(struct snd_usb_substream *subs,
			     struct audioformat *fmt,
			     struct usb_device *dev,
			     struct usb_host_interface *alts,
			     struct usb_interface_descriptor *altsd)
{


	//前面是各种判断,因为有些设备可能并没有按照要求来

	//接下来调用snd_usb_add_endpoint创建端点封装对象
	//详见端点操作
	subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
						   alts, ep, !subs->direction,
						   implicit_fb ?
							SND_USB_ENDPOINT_TYPE_DATA :
							SND_USB_ENDPOINT_TYPE_SYNC);
	if (!subs->sync_endpoint)
		return -EINVAL;

	subs->data_endpoint->sync_master = subs->sync_endpoint;

	return 0;
}

同样上面的函数,非常容易看懂。接下来回到configure_endpoint函数中。如下:

static int configure_endpoint(struct snd_usb_substream *subs)
{
	int ret;

	//在设置端点之前,需要停止端点
	stop_endpoints(subs, true);
	//调用snd_usb_endpoint_set_params,该函数会分配使用的urb
	//关于端点的操作,见前面的端点操作详情
	ret = snd_usb_endpoint_set_params(subs->data_endpoint,
					  subs->pcm_format,
					  subs->channels,
					  subs->period_bytes,
					  subs->period_frames,
					  subs->buffer_periods,
					  subs->cur_rate,
					  subs->cur_audiofmt,
					  subs->sync_endpoint);
	if (ret < 0)
		return ret;

	//如果存在同步端点,则继续配置同步端点
	if (subs->sync_endpoint)
		ret = configure_sync_endpoint(subs);

	return ret;
}

继续看configure_sync_endpoint

static int configure_sync_endpoint(struct snd_usb_substream *subs)
{
	

	//先判断同步端点的类型,正常的同步端点会进入if里面执行
	if (subs->sync_endpoint->type != SND_USB_ENDPOINT_TYPE_DATA ||
	    !subs->stream)
		return snd_usb_endpoint_set_params(subs->sync_endpoint,
						   subs->pcm_format,
						   subs->channels,
						   subs->period_bytes,
						   0, 0,
						   subs->cur_rate,
						   subs->cur_audiofmt,
						   NULL);



	//如果该端点是,隐式的FB端点,首先则找到最匹配的格式,然后再重新配置
	//因为对隐式端点不是特别清楚,因此,在此不表。
	//等有机会,回来填上这些空缺。


	return ret;
}

至此。prepare已经OK

为了本笔记,不至于太长,下一篇继续,pcm的开始和停止操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值