Linux那些事儿 之 戏说USB(23)设备的生命线(六)

drivers/usb/core/urb.c

int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
	static int			pipetypes[4] = {
		PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
	};
	int				xfertype, max;
	struct usb_device		*dev;
	struct usb_host_endpoint	*ep;
	int				is_out;
	unsigned int			allowed;

	if (!urb || !urb->complete)
		return -EINVAL;
	if (urb->hcpriv) {
		WARN_ONCE(1, "URB %p submitted while active\n", urb);
		return -EBUSY;
	}

	dev = urb->dev;
	if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
		return -ENODEV;

	/* For now, get the endpoint from the pipe.  Eventually drivers
	 * will be required to set urb->ep directly and we will eliminate
	 * urb->pipe.
	 */
	ep = usb_pipe_endpoint(dev, urb->pipe);
	if (!ep)
		return -ENOENT;

	urb->ep = ep;
	urb->status = -EINPROGRESS;
	urb->actual_length = 0;

	/* Lots of sanity checks, so HCDs can rely on clean data
	 * and don't need to duplicate tests
	 */
	xfertype = usb_endpoint_type(&ep->desc);
	if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
		struct usb_ctrlrequest *setup =
				(struct usb_ctrlrequest *) urb->setup_packet;

		if (!setup)
			return -ENOEXEC;
		is_out = !(setup->bRequestType & USB_DIR_IN) ||
				!setup->wLength;
	} else {
		is_out = usb_endpoint_dir_out(&ep->desc);
	}

	/* Clear the internal flags and cache the direction for later use */
	urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |
			URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |
			URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
			URB_DMA_SG_COMBINED);
	urb->transfer_flags |= (is_out ? URB_DIR_OUT : URB_DIR_IN);

	if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
			dev->state < USB_STATE_CONFIGURED)
		return -ENODEV;

	max = usb_endpoint_maxp(&ep->desc);
	if (max <= 0) {
		dev_dbg(&dev->dev,
			"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
			usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
			__func__, max);
		return -EMSGSIZE;
	}

	/* periodic transfers limit size per frame/uframe,
	 * but drivers only control those sizes for ISO.
	 * while we're checking, initialize return status.
	 */
	if (xfertype == USB_ENDPOINT_XFER_ISOC) {
		int	n, len;

		/* SuperSpeed isoc endpoints have up to 16 bursts of up to
		 * 3 packets each
		 */
		if (dev->speed == USB_SPEED_SUPER) {
			int     burst = 1 + ep->ss_ep_comp.bMaxBurst;
			int     mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes);
			max *= burst;
			max *= mult;
		}

		/* "high bandwidth" mode, 1-3 packets/uframe? */
		if (dev->speed == USB_SPEED_HIGH) {
			int	mult = 1 + ((max >> 11) & 0x03);
			max &= 0x07ff;
			max *= mult;
		}

		if (urb->number_of_packets <= 0)
			return -EINVAL;
		for (n = 0; n < urb->number_of_packets; n++) {
			len = urb->iso_frame_desc[n].length;
			if (len < 0 || len > max)
				return -EMSGSIZE;
			urb->iso_frame_desc[n].status = -EXDEV;
			urb->iso_frame_desc[n].actual_length = 0;
		}
	} else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint &&
			dev->speed != USB_SPEED_WIRELESS) {
		struct scatterlist *sg;
		int i;

		for_each_sg(urb->sg, sg, urb->num_sgs - 1, i)
			if (sg->length % max)
				return -EINVAL;
	}

	/* the I/O buffer must be mapped/unmapped, except when length=0 */
	if (urb->transfer_buffer_length > INT_MAX)
		return -EMSGSIZE;

	/*
	 * stuff that drivers shouldn't do, but which shouldn't
	 * cause problems in HCDs if they get it wrong.
	 */

	/* Check that the pipe's type matches the endpoint's type */
	if (usb_pipetype(urb->pipe) != pipetypes[xfertype])
		dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
			usb_pipetype(urb->pipe), pipetypes[xfertype]);

	/* Check against a simple/standard policy */
	allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK |
			URB_FREE_BUFFER);
	switch (xfertype) {
	case USB_ENDPOINT_XFER_BULK:
	case USB_ENDPOINT_XFER_INT:
		if (is_out)
			allowed |= URB_ZERO_PACKET;
		/* FALLTHROUGH */
	case USB_ENDPOINT_XFER_CONTROL:
		allowed |= URB_NO_FSBR;	/* only affects UHCI */
		/* FALLTHROUGH */
	default:			/* all non-iso endpoints */
		if (!is_out)
			allowed |= URB_SHORT_NOT_OK;
		break;
	case USB_ENDPOINT_XFER_ISOC:
		allowed |= URB_ISO_ASAP;
		break;
	}
	allowed &= urb->transfer_flags;

	/* warn if submitter gave bogus flags */
	if (allowed != urb->transfer_flags)
		dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n",
			urb->transfer_flags, allowed);

	/*
	 * Force periodic transfer intervals to be legal values that are
	 * a power of two (so HCDs don't need to).
	 *
	 * FIXME want bus->{intr,iso}_sched_horizon values here.  Each HC
	 * supports different values... this uses EHCI/UHCI defaults (and
	 * EHCI can use smaller non-default values).
	 */
	switch (xfertype) {
	case USB_ENDPOINT_XFER_ISOC:
	case USB_ENDPOINT_XFER_INT:
		/* too small? */
		switch (dev->speed) {
		case USB_SPEED_WIRELESS:
			if ((urb->interval < 6)
				&& (xfertype == USB_ENDPOINT_XFER_INT))
				return -EINVAL;
		default:
			if (urb->interval <= 0)
				return -EINVAL;
			break;
		}
		/* too big? */
		switch (dev->speed) {
		case USB_SPEED_SUPER:	/* units are 125us */
			/* Handle up to 2^(16-1) microframes */
			if (urb->interval > (1 << 15))
				return -EINVAL;
			max = 1 << 15;
			break;
		case USB_SPEED_WIRELESS:
			if (urb->interval > 16)
				return -EINVAL;
			break;
		case USB_SPEED_HIGH:	/* units are microframes */
			/* NOTE usb handles 2^15 */
			if (urb->interval > (1024 * 8))
				urb->interval = 1024 * 8;
			max = 1024 * 8;
			break;
		case USB_SPEED_FULL:	/* units are frames/msec */
		case USB_SPEED_LOW:
			if (xfertype == USB_ENDPOINT_XFER_INT) {
				if (urb->interval > 255)
					return -EINVAL;
				/* NOTE ohci only handles up to 32 */
				max = 128;
			} else {
				if (urb->interval > 1024)
					urb->interval = 1024;
				/* NOTE usb and ohci handle up to 2^15 */
				max = 1024;
			}
			break;
		default:
			return -EINVAL;
		}
		if (dev->speed != USB_SPEED_WIRELESS) {
			/* Round down to a power of 2, no more than max */
			urb->interval = min(max, 1 << ilog2(urb->interval));
		}
	}

	return usb_hcd_submit_urb(urb, mem_flags);
}
看到这个函数之后我深刻的体会到,世界上只有一个地方有真乐:乐土。不过这个函数长归长,目标却很简单,就是对urb做些前期处理后扔给HCD。

12行,一些有关存在性的判断,某主义的哲学告诉我们:存在是检验真理的唯一标准。所以这个函数的开头儿就要履行一下常规的检验,urb为空,都没有初始化是不可以提交给core的,core很生气,后果很严重,hcpriv本来说好了留给HCD用的,你得保证送过去的时候它还是贞洁的,自己不能偷偷先用了,HCD很生气,后果也会很严重,complete,每个urb结束了都必须的调用一次complete代表的函数,这是真理,你必须得让它存在。

20行,上边儿是对urb本身的检验,这里是对urb的目的地usb设备的检验。设备所属于的那条总线不存在,或者设备本身不存在,你urb还嚷嚷着要过去要过去,也太苏维埃了吧?或者设备甚至还没进入USB_STATE_DEFAULT状态,管道的另一端还都是堵着的怎么过去,早先强调多少回了,要想让设备回应你,它起码得达到Default状态。

32行,常规检查都做完了,core和HCD已经认同了这个urb,就将它的状态设置为-EINPROGRESS,表示从现在开始urb的控制权就在core和HCD手里边儿了,驱动那里是看不到这个状态的。

33行,这时还没开始传输,实际传输的数据长度当然为0了,这里初始化这么一下,也是为了防止以后哪里出错返回了,驱动里好检查。

48行,这几行获得管道的类型还有方向。

62行,获得端点的wMaxPacketSize,看看include/uapi/linux/usb/ch9.h里定义的这个函数
static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd)
{
	return __le16_to_cpu(epd->wMaxPacketSize);
}
这个函数不管从理论上还是实际上都是很简单的。

75行,如果是等时传输要做一些特别的处理。81到93这几行涉及到高速、高带宽端点(high speed,high bandwidth endpoint)。前面提到interval的时候,说过每一帧或微帧最多只能有一次等时传输,完成一次等时transaction,那时这么说主要是因为还没遇到高速高带宽的等时端点。高速高带宽等时端点每个微帧可以进行2到3次等时transaction,它和一般等时端点的主要区别也就在这儿,没必要专门为它搞个描述符类型,端点描述符wMaxPacketSize字段的bit 11~12就是用来指定可以额外有几次等时transaction的,00表示没有额外的transaction,01表示额外有1次,10表示额外有两次,11被保留。wMaxPacketSize字段的前10位就是实际的端点每次能够处理的最大字节数。所以这几行意思就是如果是高速等时端点,获得它允许的额外的等时transaction次数,和每次能够处理的最大字节数,再将它们相乘就得出了该等时端点每个微帧的所能传输的最大字节数。

95行,number_of_packets不大于0就表示这个等时urb没有指定任何一次等时传输,这就怪哉了,咋混过来的,可以直接返回了。

97~103行对等时urb里指定的各次等时传输分别做处理。如果它们预期传输的数据长度比上面算出来的max还要大,对不起,要求太过分了,返回吧。然后将它们实际传输的数据长度先置为0,状态都先初始化为-EXDEV,表示这次等时传输仅仅部分完成了,因为走到这里时传输都还没开始那。

163行,xfertype是上面计算出来的管道类型,那下面的各个case肯定是针对四种传输类型的了。不过经过实地考察,我们可以发现,这里只case了等时传输和中断传输两种周期性的传输类型,因为是关于interval的处理,所以就没控制传输和批量传输什么事儿了。

173行,这里保证等时和中断urb的interval必须是大于0的,不然主机那边儿看不懂你这是表示什么意思,究竟要不要去访问你,搞个负数和0含含糊糊的,日里万鸡的主机没功夫去猜你心思。

178行,这里的switch根据目的设备的速度去case,速度有三种,case也有三个。我们前面已经说过,不同的速度,urb->interval可以取不同的范围,不过你可能会发现那时说的最大值要比这里的限制要大一些,这是因为协议归协议,实现归实现,比如,对于UHCI来说,中断传输的interval不能比128更大,而协议规定的最大值为255。那么现在的问题是,max又是做什么用的?要注意urb->interval的单位是帧或者微帧,max只是为了调整它的值为2的次幂。

218行,将urb扔给HCD,然后就进入HCD的片儿区了。

本来usb_submit_urb函数到此应该结束了,但是它对于写驱动的来说太重要了,驱动里做的所有铺垫就是为了使用usb_submit_urb提交一个合适的urb给设备,然后满怀期待的等待着设备回馈你需要的信息,再然后才有你接下来的处理,不然你的usb驱动只是一纸空谈毫无用处,所以有必要多说一些。

首先还是要再次强调一下,在调用usb_submit_urb提交你的urb之前,一定必须不得不要对它正确的初始化,对于控制/中断/批量传输,core都提供了usb_fill_control_urb的几个孪生兄弟供你初始化使用,对于等时传输要自己手工一步一步小心翼翼的对urb的相关元素逐个赋值。下层基础决定上层建筑,你的urb决定了你的整个usb驱动能否顺利运转。

第二,对于驱动来说,usb_submit_urb是异步的,也就是说不用等传输完全完成就返回了,只要usb_submit_urb的返回值表示为0,就表示已经提交成功了,你的urb已经被core和HCD认可了,接下来core和HCD怎么处理就是它们的事了,驱动该干吗干吗去。比如你东拼西凑拿够了开发商要的款子,他给你个证明,俗称合同,然后你就就不用管也管不着他们怎么去盖,用什么材料去盖,你能做的只是等着收房子。

只要你提交成功了,不管是中间出了差错还是顺利完成,你指定的结束处理函数总是会调用,只有到这个时候,你才能够重新拿到urb的控制权,检查它是不是出错了,需要不需要释放或者是重新提交。你只要付了款子,房子盖好了不管怎样总是会有你的,你可以去测量,去检查,去验收,运气好了就OK,运气不好就自己受着吧,或者去大马路边儿上挂横幅“黑心开发商还我血汗钱”。过程是不同的,道理是一样的。

那么,第三就是,什么时候需要在结束处理函数里重新提交这个urb?其实,我更想问的是对于中断/等时传输,是怎么实现让主机按一定周期去访问端点的?端点的描述符里已经指定了这个间隔时间是没错儿,urb里也有interval描述了这个间隔周期,更没错儿,可是咱们的urb一次只完成一次传输,即使等时传输也只完成有限次的传输,然后就在结束处理函数里返回了,urb的控制权就完全属于驱动了,接下来的周期访问是怎么做到的?难道脱离urb主机自己就去智能化的自动的与端点通信了?OK,即使是这样了,那通信的数据又在哪里,你又怎么去得到这些数据?

事实上,你第一次提交一个中断或等时的urb的时候,HCD会根据interval判断一下自己是否能够满足你的需要,如果不能够安排足够的带宽来完成这种周期性的传输,它是不可能会批准你的请求的,如果它估量一下觉得自己可以满足,就会为你保留足够的带宽。但是这并不是就表明万事大吉了,HCD是为你保留带宽了,可是驱动得保证在对应端点要处理的那个urb队列里总是有urb,不能是空的,否则这个保留的带宽就会被cancel掉。那么问题就变成,对于中断/等时传输,如何保证对应端点的urb队列里总是会有urb?这就回到最开始的问题了,驱动需要在结束处理函数里重新初始化和提交刚刚完成的urb,友情提醒一下,这个时候你是不能够修改interval的值的,否则等待你的只能是错误。中断传输的例子可以去看看触摸屏驱动,等时传输的可以去看看摄像头驱动,看看它们在结束处理函数里都做了些什么,你就悟道了。

第四个要说的是,对于控制/批量/中断传输,实际上很多时候你可以不用创建urb,不用对它初始化,不用调用usb_submit_urb来提交,core将这个过程分别封装在了usb_control_msg、usb_bulk_msg和usb_interrupt_msg这三个函数里,不同的是它们的实现是同步的,会去等待传输的完全结束。咱们就是从usb_control_msg走过来的,所以这里只看看另外两个,它们都定义在drivers/usb/core/message.c里
int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
		      void *data, int len, int *actual_length, int timeout)
{
	return usb_bulk_msg(usb_dev, pipe, data, len, actual_length, timeout);
}
usb_interrupt_msg够酷,也够省事儿,全部都借助usb_bulk_msg去完成了。
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
		 void *data, int len, int *actual_length, int timeout)
{
	struct urb *urb;
	struct usb_host_endpoint *ep;

	ep = usb_pipe_endpoint(usb_dev, pipe);
	if (!ep || len < 0)
		return -EINVAL;

	urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!urb)
		return -ENOMEM;

	if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
			USB_ENDPOINT_XFER_INT) {
		pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
		usb_fill_int_urb(urb, usb_dev, pipe, data, len,
				usb_api_blocking_completion, NULL,
				ep->desc.bInterval);
	} else
		usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
				usb_api_blocking_completion, NULL);

	return usb_start_wait_urb(urb, timeout, actual_length);
}
都坚持走到这里了,看懂这个函数还是很easy的,首先根据指定的pipe获得struct usb_host_endpoint结构体,接着调用usb_alloc_urb创建urb。因为这个函数可能是从usb_interrupt_msg那里调用过来的,所以接下来要根据端点描述符的bmAttributes字段获取传输的类型,判断究竟是中断传输还是批量传输,是中断传输的话还要修改pipe的类型,防止万一谁谁直接调用usb_bulk_msg来完成中断传输,虽说很少人会穷极无聊到这种地步,预防一下总归是没错的。不管是中断传输还是批量传输,都要调用usb_fill_xxx_urb来初始化,最后,和usb_control_msg一样,调用usb_start_wait_urb函数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
作者用通俗幽默的语言简述了linux的相关知识点,很容易掌握的,建议大家好好学哦 我是U盘 说的是2.6.10的内核 我是Sysfs 说的是2.6.10的内核 戏说USB 说的是2.6.22的内核 我是Hub/UHCI/EHCI 说的是2.6.22.1的内核 其中我是U盘属于基础性的.这一阶段会遇到一些问题.比如urb提交之后究竟怎么处理的?用户空间究竟是如何访问U盘的?DMA究竟怎么回事. 这之后可以开始看Hub.这一阶段你会明白一个usb设备,比如U盘,连接到hub上之后都发生了什么. 然后是usb core(也就是戏说USB),这一阶段是个承上启下的角色,承的是U盘/HUB,启的是UHCI/EHCI,主要描述一个usb设备连接到hub上之后,在HUB和UHCI/EHCI之间也就是usb core里发生的故事. 再然后可以开始看UHCI/EHCI.这一阶段会找到上一阶段的部分问题的答案.但同时在PCI方面可能会遇到另外一些问题.比如PCI设备究竟如何被枚举的? 再然后是PCI总线驱动.这一阶段会找到上一阶段问题的答案. 再然后是scsi disk的驱动.这一阶段会找到第一阶段的部分问题的答案.即U盘是如何以一种磁盘的方式被访问的. 再然后是scsi core以及block层驱动.到了这一阶段基本上就能回答我是U盘中所有的困惑了.(其中Block层由于过于复杂和规模庞大,主要是举例和个别函数分析,主要是辅助另外几个模块进行分析.) 以上每一阶段代码跳跃性会逐渐增强,即开始会讲的细,之后会讲的粗.但整体上,各个阶段是相互关联的.  ==================================================================================== 此外, 我是Sysfs是贯穿所有故事的,sysfs提供了诸多的接口,各个子系统都会用到.可以把它当作参考书. 这个系列可以伴随着相应的系列一起看,对理解其它各部分都有帮助.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值