Linux那些事儿 之 戏说USB(29)驱动的生命线(一)

现在开始就沿着usb_generic_driver的成名之路走一走,设备的生命线你可以想当然的认为是从你的usb设备连接到hub的某个端口时开始,驱动的生命线就必须得回溯到usb子系统的初始化函数usb_init了。

drivers/usb/core/usb.c

	retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
	if (!retval)
		goto out;

在usb子系统初始化的时候就调用driver.c里的usb_register_device_driver函数将usb_generic_driver注册给系统了,现在看看usb_register_device_driver函数。

drivers/usb/core/driver.c

int usb_register_device_driver(struct usb_device_driver *new_udriver,
		struct module *owner)
{
	int retval = 0;

	if (usb_disabled())
		return -ENODEV;

	new_udriver->drvwrap.for_devices = 1;
	new_udriver->drvwrap.driver.name = new_udriver->name;
	new_udriver->drvwrap.driver.bus = &usb_bus_type;
	new_udriver->drvwrap.driver.probe = usb_probe_device;
	new_udriver->drvwrap.driver.remove = usb_unbind_device;
	new_udriver->drvwrap.driver.owner = owner;

	retval = driver_register(&new_udriver->drvwrap.driver);

	if (!retval)
		pr_info("%s: registered new device driver %s\n",
			usbcore_name, new_udriver->name);
	else
		printk(KERN_ERR "%s: error %d registering device "
			"	driver %s\n",
			usbcore_name, retval, new_udriver->name);

	return retval;
}

6行,判断一下usb子系统是不是在你启动内核的时候就被禁止了,一般来说,你不至于无聊到那种地步和她过不去吧。usb_disabled在usb.c里定义

drivers/usb/core/usb.c

int usb_disabled(void)
{
	return nousb;
}
如果你不是存心和我过不去的话,是应该知道nosub表示什么意思的。
9行,看到没,for_devices就是在这儿被初始化为1的,有了它,match里的那个is_usb_device_driver把门儿的才有章可循有凭可依。
下面就是充实了下usb_generic_driver里嵌入的那个struct device_driver结构体,usb_generic_driver就是通过它和设备模型搭上关系的。name就是usb_generic_driver的名字,即usb,所属的总线类型同样被设置为usb_bus_type,然后是指定probe函数和remove函数。
16行,调用设备模型的函数driver_register将usb_generic_driver添加到usb总线的那条驱动链表里,然后它就可以接受usb设备的讨好、献殷勤、表白。

usb_generic_driver和usb设备匹配成功后,就会调用12行指定的probe函数usb_probe_device(),现在看看driver.c里定义的这个函数

drivers/usb/core/driver.c

static int usb_probe_device(struct device *dev)
{
	struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
	struct usb_device *udev = to_usb_device(dev);
	int error = 0;

	dev_dbg(dev, "%s\n", __func__);

	/* TODO: Add real matching code */

	/* The device should always appear to be in use
	 * unless the driver supports autosuspend.
	 */
	if (!udriver->supports_autosuspend)
		error = usb_autoresume_device(udev);

	if (!error)
		error = udriver->probe(udev);
	return error;
}

3行,to_usb_device_driver是include/linux/usb.h里定义的一个宏,和前面遇到的那个to_usb_device有异曲同工之妙,

include/linux/usb.h

#define	to_usb_device_driver(d) container_of(d, struct usb_device_driver, \
		drvwrap.driver)
4行,说曹操曹操就到,前面刚提到to_usb_device它这就来了。
14行,supports_autosuspend这两个前面都提到过那么一下,现在将那两个片断给回顾一下。每个struct usb_driver或struct usb_device_driver里都有一个supports_autosuspend。提到supports_autosuspend时说如果它为0就不再允许绑定到这个驱动的接口autosuspend。
所有的usb设备都是绑定到usb_generic_driver上面的,usb_generic_driver的supports_autosuspend字段又是为1的,也就是说允许设备autosuspend。
18行,飘过了上面有关电源管理的那行之后,这里要调用usb_generic_driver自己私有的probe函数generic_probe()对你的设备进行进一步的审查,它在generic.c里定义
static int generic_probe(struct usb_device *udev)
{
	int err, c;

	/* Choose and set the configuration.  This registers the interfaces
	 * with the driver core and lets interface drivers bind to them.
	 */
	if (udev->authorized == 0)
		dev_err(&udev->dev, "Device is not authorized for usage\n");
	else {
		c = usb_choose_configuration(udev);
		if (c >= 0) {
			err = usb_set_configuration(udev, c);
			if (err && err != -ENODEV) {
				dev_err(&udev->dev, "can't set config #%d, error %d\n",
					c, err);
				/* This need not be fatal.  The user can try to
				 * set other configurations. */
			}
		}
	}
	/* USB device state == configured ... usable */
	usb_notify_add_device(udev);

	return 0;
}
这函数说简单也简单,说复杂也复杂,简单的是外表,复杂的是内心。用一句话去概括它的中心思想,就是从设备可能的众多配置中选择一个合适的,然后去配置设备,从而让设备进入期待已久的Configured状态。概括了中心思想,再去看看细节。先看看是怎么选择一个配置的,调用的是generic.c里的usb_choose_configuration函数。
int usb_choose_configuration(struct usb_device *udev)
{
	int i;
	int num_configs;
	int insufficient_power = 0;
	struct usb_host_config *c, *best;

	if (usb_device_is_owned(udev))
		return 0;

	best = NULL;
	c = udev->config;
	num_configs = udev->descriptor.bNumConfigurations;
	for (i = 0; i < num_configs; (i++, c++)) {
		struct usb_interface_descriptor	*desc = NULL;

		/* It's possible that a config has no interfaces! */
		if (c->desc.bNumInterfaces > 0)
			desc = &c->intf_cache[0]->altsetting->desc;

		/*
		 * HP's USB bus-powered keyboard has only one configuration
		 * and it claims to be self-powered; other devices may have
		 * similar errors in their descriptors.  If the next test
		 * were allowed to execute, such configurations would always
		 * be rejected and the devices would not work as expected.
		 * In the meantime, we run the risk of selecting a config
		 * that requires external power at a time when that power
		 * isn't available.  It seems to be the lesser of two evils.
		 *
		 * Bugzilla #6448 reports a device that appears to crash
		 * when it receives a GET_DEVICE_STATUS request!  We don't
		 * have any other way to tell whether a device is self-powered,
		 * but since we don't use that information anywhere but here,
		 * the call has been removed.
		 *
		 * Maybe the GET_DEVICE_STATUS call and the test below can
		 * be reinstated when device firmwares become more reliable.
		 * Don't hold your breath.
		 */
#if 0
		/* Rule out self-powered configs for a bus-powered device */
		if (bus_powered && (c->desc.bmAttributes &
					USB_CONFIG_ATT_SELFPOWER))
			continue;
#endif

		/*
		 * The next test may not be as effective as it should be.
		 * Some hubs have errors in their descriptor, claiming
		 * to be self-powered when they are really bus-powered.
		 * We will overestimate the amount of current such hubs
		 * make available for each port.
		 *
		 * This is a fairly benign sort of failure.  It won't
		 * cause us to reject configurations that we should have
		 * accepted.
		 */

		/* Rule out configs that draw too much bus current */
		if (usb_get_max_power(udev, c) > udev->bus_mA) {
			insufficient_power++;
			continue;
		}

		/* When the first config's first interface is one of Microsoft's
		 * pet nonstandard Ethernet-over-USB protocols, ignore it unless
		 * this kernel has enabled the necessary host side driver.
		 * But: Don't ignore it if it's the only config.
		 */
		if (i == 0 && num_configs > 1 && desc &&
				(is_rndis(desc) || is_activesync(desc))) {
#if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE)
			continue;
#else
			best = c;
#endif
		}

		/* From the remaining configs, choose the first one whose
		 * first interface is for a non-vendor-specific class.
		 * Reason: Linux is more likely to have a class driver
		 * than a vendor-specific driver. */
		else if (udev->descriptor.bDeviceClass !=
						USB_CLASS_VENDOR_SPEC &&
				(desc && desc->bInterfaceClass !=
						USB_CLASS_VENDOR_SPEC)) {
			best = c;
			break;
		}

		/* If all the remaining configs are vendor-specific,
		 * choose the first one. */
		else if (!best)
			best = c;
	}

	if (insufficient_power > 0)
		dev_info(&udev->dev, "rejected %d configuration%s "
			"due to insufficient available bus power\n",
			insufficient_power, plural(insufficient_power));

	if (best) {
		i = best->desc.bConfigurationValue;
		dev_dbg(&udev->dev,
			"configuration #%d chosen from %d choice%s\n",
			i, num_configs, plural(num_configs));
	} else {
		i = -1;
		dev_warn(&udev->dev,
			"no configuration chosen from %d choice%s\n",
			num_configs, plural(num_configs));
	}
	return i;
}
设备各个配置的详细信息在设备自身的漫漫人生旅途中就已经获取存放在相关的几个成员里了,怎么从中挑选一个让人满意的?显然谁都会说去一个一个的浏览每个配置,看看有没有称心如意的,于是就有了14行的for循环。
刚看到这个for循环就有点傻眼了,有点不大相信,居然注释要远远多于代码!。
这么一个for循环,咱们把它分成三大段,21~46这一大段你什么都可以不看,就是不能不看那个#if 0,因为它就意味着你可以华丽丽的飘过这么一大段了。
第二段是60~64行,这一段牵扯到人世间最让人无可奈何的一对矛盾,索取与给予。一个配置索取的电流比hub所能给予的还要大,显然它不会是一个让人满意的配置。
第三段是71~96行,关于这段只说一个原则,linux更care那些标准的东西,比如USB_CLASS_VIDEO、USB_CLASS_AUDIO等等这样子的设备和接口就更讨人喜欢一些,所以就会优先选择非USB_CLASS_VENDOR_SPEC的接口。
for循环之后,残余的那些部分都是调试用的,输出一些调试信息,不需要去关心,不过里面出现了个有趣的函数plural,它是一个在generic.c开头儿定义的内联函数
static inline const char *plural(int n)
{
	return (n == 1 ? "" : "s");
}
参数n为1返回一个空字符串,否则返回一个‘s’,瞄一下使用了这个函数的那几个打印语句,就明白它是用来打印一个英语名词的单复数的,复数的话就加上个s。呵呵,那些不规律的名词单复数形式咋办?凉拌!

不管你疑惑也好,满意也好,choose_configuration就是这样按照自己的标准挑选了一个比较合自己心意的配置,接下来当然就是要用这个配置去配置设备以便让它迈进Configured状态了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值