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

首先当然是你将usb设备连接在hub的某个端口上,hub检测到有设备连接了进来,它会为设备分配一个struct usb_device结构的对象并初始化,并调用设备模型提供的接口将设备添加到usb总线的设备列表里,然后usb总线会遍历驱动列表里的每个驱动,调用自己的match函数看它们和你的设备或接口是否匹配。

hub检测到自己的某个端口有设备连接了进来后,它会调用core里的usb_alloc_dev函数为struct usb_device结构的对象申请内存,这个函数在drivers/usb/core/usb.c文件里定义
struct usb_device *usb_alloc_dev(struct usb_device *parent,
				 struct usb_bus *bus, unsigned port1)
{
	struct usb_device *dev;
	struct usb_hcd *usb_hcd = bus_to_hcd(bus);
	unsigned root_hub = 0;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return NULL;

	if (!usb_get_hcd(usb_hcd)) {
		kfree(dev);
		return NULL;
	}
	/* Root hubs aren't true devices, so don't allocate HCD resources */
	if (usb_hcd->driver->alloc_dev && parent &&
		!usb_hcd->driver->alloc_dev(usb_hcd, dev)) {
		usb_put_hcd(bus_to_hcd(bus));
		kfree(dev);
		return NULL;
	}

	device_initialize(&dev->dev);
	dev->dev.bus = &usb_bus_type;
	dev->dev.type = &usb_device_type;
	dev->dev.groups = usb_device_groups;
	dev->dev.dma_mask = bus->controller->dma_mask;
	set_dev_node(&dev->dev, dev_to_node(bus->controller));
	dev->state = USB_STATE_ATTACHED;
	dev->lpm_disable_count = 1;
	atomic_set(&dev->urbnum, 0);

	INIT_LIST_HEAD(&dev->ep0.urb_list);
	dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
	dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
	/* ep0 maxpacket comes later, from device descriptor */
	usb_enable_endpoint(dev, &dev->ep0, false);
	dev->can_submit = 1;

	/* Save readable and stable topology id, distinguishing devices
	 * by location for diagnostics, tools, driver model, etc.  The
	 * string is a path along hub ports, from the root.  Each device's
	 * dev->devpath will be stable until USB is re-cabled, and hubs
	 * are often labeled with these port numbers.  The name isn't
	 * as stable:  bus->busnum changes easily from modprobe order,
	 * cardbus or pci hotplugging, and so on.
	 */
	if (unlikely(!parent)) {
		dev->devpath[0] = '0';
		dev->route = 0;

		dev->dev.parent = bus->controller;
		dev_set_name(&dev->dev, "usb%d", bus->busnum);
		root_hub = 1;
	} else {
		/* match any labeling on the hubs; it's one-based */
		if (parent->devpath[0] == '0') {
			snprintf(dev->devpath, sizeof dev->devpath,
				"%d", port1);
			/* Root ports are not counted in route string */
			dev->route = 0;
		} else {
			snprintf(dev->devpath, sizeof dev->devpath,
				"%s.%d", parent->devpath, port1);
			/* Route string assumes hubs have less than 16 ports */
			if (port1 < 15)
				dev->route = parent->route +
					(port1 << ((parent->level - 1)*4));
			else
				dev->route = parent->route +
					(15 << ((parent->level - 1)*4));
		}

		dev->dev.parent = &parent->dev;
		dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);

		/* hub driver sets up TT records */
	}

	dev->portnum = port1;
	dev->bus = bus;
	dev->parent = parent;
	INIT_LIST_HEAD(&dev->filelist);

#ifdef	CONFIG_PM
	pm_runtime_set_autosuspend_delay(&dev->dev,
			usb_autosuspend_delay * 1000);
	dev->connect_time = jiffies;
	dev->active_duration = -jiffies;
#endif
	if (root_hub)	/* Root hub always ok [and always wired] */
		dev->authorized = 1;
	else {
		dev->authorized = usb_hcd->authorized_default;
		dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0;
	}
	return dev;
}
usb_alloc_dev函数就相当于usb设备的构造函数,参数里边儿,parent是设备连接的那个hub,bus是设备连接的那条总线,port1就是设备连接在hub上的那个端口。

8行,为一个struct usb_device结构的对象申请内存并初始化为0。kzalloc直接取代了kmalloc/memset,一个函数起到了两个函数的作用。
然后是判断内存申请成功了没。通过这么几行,咱们应该记住两个凡是,凡是你想用kmalloc/memset组合申请内存的时候,就使用kzalloc代替吧,凡是申请内存的,不要忘了判断是不是申请成功了。
12行,这里的两个函数是hcd,主机控制器驱动里的,具体咱不讲,只要知道usb的世界里一个主机控制器对应着一条usb总线,主机控制器驱动用struct usb_hcd 结构表示,一条总线用struct usb_bus结构表示,函数 bus_to_hcd 是为了获得总线对应的主机控制器驱动,也就是 struct usb_hcd 结构对象,函数 usb_get_hcd 只是将得到的这个usb_hcd 结构对象的引用计数加1,为什么?因为总线上多了一个设备,设备在主机控制器的数据结构就得在,当然得为它增加引用计数。如果这俩函数没有很好的完成自己的任务,那整个usb_alloc_dev函数也就没有继续执行下去的必要了,将之前为struct usb_device结构对象申请的内存释放掉就稍息去吧。
24行,device_initialize是设备模型里的函数,这里就是将struct usb_device结构里嵌入的那个struct device结构体初始化掉。
25行,将设备所在的总线类型设置为usb_bus_type。usb_bus_type咱们很早很早就遇到它了,usb子系统的初始化函数usb_init 里就把它给注册掉了,还记得聊到模型的时候说过的那个著名的三角关系不,这里就是把设备和总线这条边儿给搭上了。如果您忘了,那就回头看看吧。
26行,将设备的设备类型初始化为usb_device_type,这是咱们上节第二次遇到usb_device_match函数,走设备那条路,使用is_usb_device判断是不是usb设备时留下的疑问,就是在这儿把设备的类型给初始化成usb_device_type了。
28行,这个就是与DMA传输相关的了,设备能不能进行dma传输,得看主机控制器的脸色,主机控制器不支持的话设备自作多情那也没有用。所以这里dma_mask被设置为host controller的dma_mask。
30行,将usb设备的状态设置为Attached,表示设备已经连接到usb接口上了,是hub检测到设备时的初始状态。咱们前面说了,USB设备从生到死都要按照那么几个状态,这里随着设备生命线的逐渐深入,咱们会看到设备的状态也在逐渐的变化。
34行,端点0实在是太太太特殊了,这个咱们是一而再再而三的感叹,struct usb_device里直接就有这么一个成员ep0,这行就将ep0的urb_list给初始化掉。因为接下来遇到的那些主要角色的成员前面集中都说过了,咱们就不再说它们是嘛意思了,忘了的话可以到前面看看。
35行,36行,分别初始化了端点0的描述符长度和描述符类型。
49行,这里平白无故的多出了个unlikely,不知道什么意思?先看看它们在include/linux/compiler.h的定义
# define likely(x)	__builtin_expect(!!(x), 1)
# define unlikely(x)	__builtin_expect(!!(x), 0)
unlikely不是一个人在奋斗,还有个likely。定义里那个怪怪的__builtin_expect是GCC里内建的一个函数,具体是做嘛用的可以看看GCC的手册
long __builtin_expect (long exp, long c)
You may use __builtin_expect to provide the compiler with branch prediction information. In general, you should prefer to use actual profile feedback for this (‘-fprofile-arcs’), as programmers are notoriously bad at predicting how their programs actually perform. However, there are applications in which this data is hard to collect.
The return value is the value of exp, which should be an integral expression. The value of c must be a compile-time constant. The semantics of the built-in are that it is expected that exp == c. For example:
if (__builtin_expect (x, 0))
foo ();
would indicate that we do not expect to call foo, since we expect x to be zero. Since
you are limited to integral expressions for exp, you should use constructions such
as
if (__builtin_expect (ptr != NULL, 1))
error ();
when testing pointer or floating-point values.

大致意思就是由于大部分写代码的在分支预测方面做的比较的糟糕,所以GCC提供了这个内建的函数来帮助处理分支预测,优化程序,它的第一个参数exp为一个整型的表达式,返回值也是这个exp,它的第二个参数c的值必须是一个编译期的常量,那这个内建函数的意思就是exp的预期值为c,编译器可以根据这个信息适当的重排条件语句块的顺序,将符合这个条件的分支放在合适的地方。具体到unlikely(x)就是告诉编译器条件x发生的可能性不大,那么这个条件块儿里语句的目标码可能就会被放在一个比较远的为止,以保证经常执行的目标码更紧凑。likely则相反。在一个月高风黑的夜晚,你向你从穿开裆裤就开始暗恋的mm表白,她回复你
if(unlikely(你以后会有房子,不是按揭的,会有车子,不是奥拓))
咱们明天可以去领证儿
你觉得你是应该高兴还是不应该高兴?她这明摆着就是告你别做梦了,你有房子车子的可能性太小了,往远处排吧,她要先照顾有房有车的。
使用的时候还是很简单的,就是,if语句你照用,只是如果你觉得if条件为1的可能性非常大的时候,可以在条件表达式外面包装一个likely(),如果可能性非常小,则用unlikely()包装。那么这里272行的意思就很明显了,就是说写内核的哥们儿觉得你的usb设备直接连接到root hub上的可能性比较小,因为parent指的就是你的设备连接的那个hub。
49~79行整个的代码就是首先判断你的设备是不是直接连到root hub上的,如果是,将dev->devpath[0]赋值为‘0’,以示特殊,然后父设备设为controller,同时把dev->bus_id[]设置为像usb1/usb2/usb3/usb4这样的字符串。如果你的设备不是直接连到root hub上的,有两种情况,如果你的设备连接的那个hub是直接连到root hub上的,则dev->devpath就等于端口号,否则dev->devpath就等于在父hub的devpath基础上加一个‘.’再加一个端口号,最后把bus_id[]设置成1-/2-/3-/4-这样的字符串后面连接上devpath。
81~83行,没什么说的,轻轻的飘过。
84行,初始化一个队列,usbfs用的。
86~91行,电源管理的,仍然飘过。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值