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

函数usb_control_msg调用了usb_internal_control_msg之后就去一边儿睡大觉了,脏活儿累活儿,全部留给usb_internal_control_msg去做了。

drivers/usb/core/message.c

static int usb_internal_control_msg(struct usb_device *usb_dev,
				    unsigned int pipe,
				    struct usb_ctrlrequest *cmd,
				    void *data, int len, int timeout)
{
	struct urb *urb;
	int retv;
	int length;

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

	usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,
			     len, usb_api_blocking_completion, NULL);

	retv = usb_start_wait_urb(urb, timeout, &length);
	if (retv < 0)
		return retv;
	else
		return length;
}
这个函数粗看过去,可以概括为一个中心,三个基本点,以一个struct urb结构体为中心,以usb_alloc_urb、usb_fill_control_urb、usb_start_wait_urb三个函数为基本点。

一个中心:struct urb结构体,就是咱们前面多次提到又多次飘过,只闻其名不见其形的传说中的urb,全称usb request block,站在咱们的角度看,usb通信靠的就是它这张脸。
第一个基本点:usb_alloc_urb函数,创建一个urb,struct urb结构体只能使用它来创建,它是urb在usb世界里的独家代理。
第二个基本点:usb_fill_control_urb函数,初始化一个控制urb,urb被创建之后,使用之前必须要正确的初始化。
第三个基本点:usb_start_wait_urb函数,将urb提交给咱们的usb core,以便分配给特定的主机控制器驱动进行处理,然后默默的等待处理结果,或者超时。
struct urb {
	/* private: usb core and host controller only fields in the urb */
	struct kref kref;		/* reference count of the URB */
	void *hcpriv;			/* private data for host controller */
	atomic_t use_count;		/* concurrent submissions counter */
	atomic_t reject;		/* submissions will fail */
	int unlinked;			/* unlink error code */

	/* public: documented fields in the urb that can be used by drivers */
	struct list_head urb_list;	/* list head for use by the urb's
					 * current owner */
	struct list_head anchor_list;	/* the URB may be anchored */
	struct usb_anchor *anchor;
	struct usb_device *dev;		/* (in) pointer to associated device */
	struct usb_host_endpoint *ep;	/* (internal) pointer to endpoint */
	unsigned int pipe;		/* (in) pipe information */
	unsigned int stream_id;		/* (in) stream ID */
	int status;			/* (return) non-ISO status */
	unsigned int transfer_flags;	/* (in) URB_SHORT_NOT_OK | ...*/
	void *transfer_buffer;		/* (in) associated data buffer */
	dma_addr_t transfer_dma;	/* (in) dma addr for transfer_buffer */
	struct scatterlist *sg;		/* (in) scatter gather buffer list */
	int num_mapped_sgs;		/* (internal) mapped sg entries */
	int num_sgs;			/* (in) number of entries in the sg list */
	u32 transfer_buffer_length;	/* (in) data buffer length */
	u32 actual_length;		/* (return) actual transfer length */
	unsigned char *setup_packet;	/* (in) setup packet (control only) */
	dma_addr_t setup_dma;		/* (in) dma addr for setup_packet */
	int start_frame;		/* (modify) start frame (ISO) */
	int number_of_packets;		/* (in) number of ISO packets */
	int interval;			/* (modify) transfer interval
					 * (INT/ISO) */
	int error_count;		/* (return) number of ISO errors */
	void *context;			/* (in) context for completion */
	usb_complete_t complete;	/* (in) completion routine */
	struct usb_iso_packet_descriptor iso_frame_desc[0];
					/* (in) ISO ONLY */
};
kref,urb的引用计数。甭看它是隐藏在urb内部的一个不起眼的小角色,但小角色做大事情,它决定了一个urb的生死存亡。一个urb有用没用,是继续委以重任还是无情销毁都要看它的脸色。那第一个问题就来了,为什么urb的生死要掌握在这个小小的引用计数手里边儿?
很早很早很早很早以前就说过,主机与设备之间通过管道来传输数据,管道的一端是主机上的一个缓冲区,另一端是设备上的端点。管道之中流动的数据,在主机控制器和设备看来是一个个packets,在咱们看来就是urb。因而,端点之中就有那么一个队列,叫urb队列。不过,这并不代表一个urb只能发配给一个端点,它可能通过不同的管道发配给不同的端点,那么这样一来,我们如何知道这个urb正在被多少个端点使用,如何判断这个urb的生命已经over?如果没有任何一个端点在使用它,而我们又无法判断这种情况,它就会永远的飘荡在usb的世界里。我们需要寻求某种办法在这种情况下给它们一个好的归宿,这就是引用计数。每多一个使用者,它的这个引用计数就加1,每减少一个使用者,引用计数就减一,如果连最后一个使用者都释放了这个urb,宣称不再使用它了,那它的生命周期就走到了尽头,会自动的销毁。
接下来就是第二个问题,如何来表示这个神奇的引用计数?其实它是一个struct kref结构体,在include/linux/kref.h里定义
struct kref {
	atomic_t refcount;
};

这个结构与struct urb相比简约到极致了,简直就是迎着咱们的口味来的。不过别看它简单,内核里就是使用它来判断一个对象还有没有用的。它里边儿只包括了一个原子变量,为什么是原子变量?既然都使用引用计数了,那就说明可能同时有多个地方在使用这个对象,总要考虑一下它们同时修改这个计数的可能性吧,也就是俗称的并发访问,那怎么办?加个锁?就这么一个整数值专门加个锁未免也忒大材小用了些,所以就使用了原子变量。围绕这个结构,内核里还定义了几个专门操作引用计数的函数,它们在lib/kref.c里定义

/**
 * kref_init - initialize object.
 * @kref: object in question.
 */
static inline void kref_init(struct kref *kref)
{
	atomic_set(&kref->refcount, 1);
}

/**
 * kref_get - increment refcount for object.
 * @kref: object.
 */
static inline void kref_get(struct kref *kref)
{
	/* If refcount was 0 before incrementing then we have a race
	 * condition when this kref is freeing by some other thread right now.
	 * In this case one should use kref_get_unless_zero()
	 */
	WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2);
}

/**
 * kref_sub - subtract a number of refcounts for object.
 * @kref: object.
 * @count: Number of recounts to subtract.
 * @release: pointer to the function that will clean up the object when the
 *	     last reference to the object is released.
 *	     This pointer is required, and it is not acceptable to pass kfree
 *	     in as this function.  If the caller does pass kfree to this
 *	     function, you will be publicly mocked mercilessly by the kref
 *	     maintainer, and anyone else who happens to notice it.  You have
 *	     been warned.
 *
 * Subtract @count from the refcount, and if 0, call release().
 * Return 1 if the object was removed, otherwise return 0.  Beware, if this
 * function returns 0, you still can not count on the kref from remaining in
 * memory.  Only use the return value if you want to see if the kref is now
 * gone, not present.
 */
static inline int kref_sub(struct kref *kref, unsigned int count,
	     void (*release)(struct kref *kref))
{
	WARN_ON(release == NULL);

	if (atomic_sub_and_test((int) count, &kref->refcount)) {
		release(kref);
		return 1;
	}
	return 0;
}

/**
 * kref_put - decrement refcount for object.
 * @kref: object.
 * @release: pointer to the function that will clean up the object when the
 *	     last reference to the object is released.
 *	     This pointer is required, and it is not acceptable to pass kfree
 *	     in as this function.  If the caller does pass kfree to this
 *	     function, you will be publicly mocked mercilessly by the kref
 *	     maintainer, and anyone else who happens to notice it.  You have
 *	     been warned.
 *
 * Decrement the refcount, and if 0, call release().
 * Return 1 if the object was removed, otherwise return 0.  Beware, if this
 * function returns 0, you still can not count on the kref from remaining in
 * memory.  Only use the return value if you want to see if the kref is now
 * gone, not present.
 */
static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
{
	return kref_sub(kref, 1, release);
}

整个kref.c文件就定义了这么三个函数,kref_init初始化,kref_get将引用计数加1,kref_put调用kref_sub将引用计数减一并判断是不是为0,为0的话就调用参数里release函数指针指向的函数把对象销毁掉。它们对独苗儿refcount的操作都是通过原子变量特有的操作函数,其实这句话可以当选当日最大废话,原子变量当然要使用专门的操作函数了,编译器还能做些优化,否则直接使用一般的变量就可以了干吗还要使用原子变量,不是没事找事儿么,再说如果你直接像对待一般整型值一样对待它,编译器也会看不过去你的行为,直接给你个error的。友情提醒一下,kref_init初始化时,是把refcount的值初始化为1了的,不是0。还有一点要说的是kref_put参数里的那个函数指针,你不能传递一个NULL过去,否则这个引用计数就只是计数,而背离了最初的目的,要记住我们需要在这个计数减为0的时候将嵌入这个引用计数struct kref结构体的对象给销毁掉,所以这个函数指针也不能为kfree,因为这样的话就只是把这个struct kref结构体给销毁了,而不是整个对象。
第三个问题,如何使用struct kref结构来为我们的对象计数?当然我们需要把这样一个结构嵌入到你希望计数的对象里边,不然你根本就无法对对象在它整个生命周期里的使用情况作出判断,难道还真能以为Linus是linux里的太阳,像太阳神阿波罗一样掐指一算就知道谁谁在哪儿待过,现在在哪儿。但是我们应该是几乎见不到内核里边儿直接使用上面那几个函数来给对象计数的,而是每种对象又定义了自己专用的引用计数函数,比如咱们的urb,在drivers/usb/core/urb.c里定义
void usb_init_urb(struct urb *urb)
{
	if (urb) {
		memset(urb, 0, sizeof(*urb));
		kref_init(&urb->kref);
		INIT_LIST_HEAD(&urb->anchor_list);
	}
}
void usb_free_urb(struct urb *urb)
{
	if (urb)
		kref_put(&urb->kref, urb_destroy);
}
struct urb *usb_get_urb(struct urb *urb)
{
	if (urb)
		kref_get(&urb->kref);
	return urb;
}
usb_init_urb、usb_get_urb、usb_free_urb这三个函数分别调用了前面看到的struct kref结构的三个操作函数来进行引用计数的初始化、加1、减一。什么叫封装?这就叫封装。usb_init_urb和usb_get_urb都没什么好说的,比较感兴趣的是usb_free_urb里给kref_put传递的那个函数urb_destroy,它也在urb.c里定义
static void urb_destroy(struct kref *kref)
{
	struct urb *urb = to_urb(kref);

	if (urb->transfer_flags & URB_FREE_BUFFER)
		kfree(urb->transfer_buffer);

	kfree(urb);
}
这个urb_destroy首先调用了to_urb,实际上就是一个container_of来获得引用计数关联的那个urb,然后使用kfree将它销毁。
到此,世界如此美丽,引用计数如此简单,不是么?
lock,一把自旋锁。韦唯早就唱了,每个urb都有一把自旋锁。
hcpriv,走到今天,你应该明白这个urb最终还是要提交给主机控制器驱动的,这个字段就是urb里主机控制器驱动的自留地,咱们就不插手了。
use_count,这里又是一个使用计数,不过此计数非彼计数,它与上面那个用来追踪urb生命周期的kref一点儿血缘关系也没有,连远亲都不是。那它是用来做什么的,凭什么在臃肿的struct urb不断喊着要瘦身的时候还仍有一席之地?
先了解下使用urb来完成一次完整的usb通信都要经历哪些阶段,首先,驱动程序发现自己有与usb设备通信的需要,于是创建一个urb,并指定它的目的地是设备上的哪个端点,然后提交给usb core,usb core将它修修补补的做些美化之后再移交给主机控制器的驱动程序HCD,HCD会去解析这个urb,了解它的目的是什么,并与usb设备进行相应的交流,在交流结束,urb的目的达到之后,HCD再把这个urb的所有权移交回驱动程序。
这里的use_count就是在usb core将urb移交给HCD,办理移交手续的时候,插上了那么一脚,每当走到这一步,它的值就会加1。什么时候减1?在HCD重新将urb的所有权移交回驱动程序的时候。这样说吧,只要HCD拥有这个urb的所有权,那么该urb的use_count就不会为0。这么一说,似乎use_count也有点追踪urb生命周期的味道了,当它的值大于0时,就表示当前有HCD正在处理它,和上面的kref概念上有部分的重叠,不过,显然它们之间是有区别的,没区别的话这儿干吗要用两个计数,不是没事儿找抽么。上面的那个kref实现方式是内核里统一的引用计数机制,当计数减为0时,urb对象就被urb_destroy给销毁了。这里的use_count只是用来统计当前这个urb是不是正在被哪个HCD处理,即使它的值为0,也只是说明没有HCD在使用它而已,并不代表就得把它给销毁掉。比方说,HCD利用完了urb,把它还给了驱动,这时驱动还可以对这个urb检修检修,再提交给哪个HCD去使用。
下面的问题就是既然它不会平白无故的多出来,那它究竟是用来干啥的?还要从刚提到的那几个阶段说起。urb驱动也创建了,提交也提交了,HCD正处理着那,可驱动反悔了,它不想再继续这次通信了,想将这个urb给终止掉,善解任意的usb core当然会给驱动提供这样的接口来满足这样的需要。不过这个需要还被写代码的哥们儿细分为两种,一种是驱动只想通过usb core告诉HCD一声,说这个urb我想终止掉,您就别费心再处理了,然后它不想在那里等着HCD的处理,想忙别的事去,这就是俗称的异步,对应的是usb_unlink_urb函数。当然对应的还有种同步的,驱动会在那里苦苦等候着HCD的处理结果,等待着urb被终止,对应的是usb_kill_urb函数。而HCD将这次通信终止后,同样会将urb的所有权移交回驱动。那么驱动通过什么判断HCD已经终止了这次通信?就是通过这里的use_count,驱动会在usb_kill_urb里面一直等待着这个值变为0。
reject,拒绝,拒绝什么?在目前版本的内核里,只有usb_kill_urb函数有特权对它进行修改,那么,显然reject就与上面说的urb终止有关了。那就看看drivers/usb/core/urb.c里定义的这个函数
void usb_kill_urb(struct urb *urb)
{
	might_sleep();
	if (!(urb && urb->dev && urb->ep))
		return;
	atomic_inc(&urb->reject);

	usb_hcd_unlink_urb(urb, -ENOENT);
	wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);

	atomic_dec(&urb->reject);
}
3行,因为usb_kill_urb函数要一直等候着HCD将urb终止掉,它必须是可以休眠的,不然就太可恶了,就像那些脚踩多只船的,占着本来就稀有的ppmm资源,让大批男同志们找不到另一半来关爱。所以说usb_kill_urb不能用在中断上下文,必须能够休眠将自己占的资源给让出来。
写代码的哥们儿也都是忧国忧民的主儿,也深刻体会到广大男同胞们的无奈,于是提供了might_sleep函数,用它来判断一下这个函数是不是处在能够休眠的情况,如果不是,就会打印出一大堆的堆栈信息,比如你在中断上下文调用了这个函数时。不过,它也就是基于调试的目的用一用,方便日后找错,并不能强制哪个函数改变自己的上下文。
4行,这里就是判断一下urb,urb要去的那个设备,还有那个设备在的总线有没有,如果哪个不存在,就还是返回吧。
6行,将reject加1。加1有什么用?其实目前版本的内核里只有两个地方用到了这个值进行判断。第一个地方是在usb core将urb提交给HCD,正在办移交手续的时候,如果reject大于0,就不再接着移交了,也就是说这个urb被HCD给拒绝了。这是为了防止这边儿正在终止这个urb,那边儿某个地方却又妄想将这个urb重新提交给HCD。
8行,这里告诉HCD驱动要终止这个urb了,usb_hcd_unlink_urb函数也只是告诉HCD一声,然后不管HCD怎么处理就返回了。
9行,上面的usb_hcd_unlink_urb是返回了,但并不代表HCD已经将urb给终止了,HCD可能没那么快,所以这里usb_kill_urb要休息休息,等人通知它。这里使用了wait_event宏来实现休眠,usb_kill_urb_queue是在/drivers/usb/core/hcd.h里定义的一个等待队列,专门给usb_kill_urb休息用的。需要注意的是这里的唤醒条件,use_count必须等于0,终于看到use_count实战的地方了。
那在哪里唤醒正在睡大觉的usb_kill_urb?这牵扯到了第二个使用reject来做判断的地方。在HCD将urb的所有权返还给驱动的时候,会对reject进行判断,如果reject大于0,就调用wake_up唤醒在usb_kill_urb_queue上休息的usb_kill_urb。也好理解,HCD都要将urb的所有权返回给驱动了,那当然就是已经处理完了,放在这里就是已经将这个urb终止了,usb_kill_urb等的就是这一天的到来,当然就要醒过来继续往下走了。
11行,将reject刚才增加的那个1给减掉。urb都已经终止了,也没人再会去拒绝它了,reject还是开始什么样儿结束的时候就什么样吧。
索性将usb_unlink_urb函数也贴出来看看它们之间有什么区别吧
int usb_unlink_urb(struct urb *urb)
{
	if (!urb)
		return -EINVAL;
	if (!urb->dev)
		return -ENODEV;
	if (!urb->ep)
		return -EIDRM;
	return usb_hcd_unlink_urb(urb, -ECONNRESET);
}
看usb_unlink_urb这儿就简单多了,只是把自己的意愿告诉HCD,然后就非常洒脱的返回了。
struct urb结构里的前边儿这几个,只是usb core和主机控制器驱动需要关心的,实际的驱动里根本用不着也管不着,它们就是usb和HCD的后花园,想种点什么不种点什么都由写这块儿代码的哥们儿决定,他们在里面怎么为所欲为都不关你写驱动的啥事。usb在linux里起起伏伏这么多年,前边儿的这些内容早就变过多少次,说不定你今天还看到谁谁,到接下来的哪天就看不到了,不过,变化的是形式,不变的是道理。驱动要做的只是创建一个urb,然后初始化,再把它提交给usb core就可以了,使用不使用引用计数,加不加锁之类的一点都不用去操心。感谢David Brownell,感谢Alan Stern,感谢……,没有他们就没有usb在linux里的今天。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值