Linux 设备驱动之 USB hub 驱动
Linux 设备驱动之 USB hub 驱动
------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:
------------------------------------------
一:前言
继 UHCI 的驱动之后,我们对 USB Control 的运作有了一定的了解.在接下来的分析中,我们
对 USB 设备的驱动做一个全面的分析,我们先从 HUB 的驱动说起.关于 HUB, spec 上
有详细的定义,基于这部份的代码位于 下,也就是说,这部份代码
是位于 core 下,和具体设备是无关的,因为各厂商的 hub 都是按照 spec 的要求来设计的.
二:UHCI 驱动中的 root hub
记得在分析 UHCI 驱动的时候,曾详细分析过 root hub 的初始化操作.为了分析方便,将代码
片段列出如下:
usb_add_hcd() à usb_alloc_dev():
struct usb_device *usb_alloc_dev(struct usb_device *parent,
struct usb_bus *bus, unsigned port1)
{
……
……
//usb_device,内嵌有 struct device 结构,对这个结构进行初始化
device_initialize(&dev->dev);
dev-> = &usb_bus_type;
dev-> = &usb_device_type;
……
……
}
一看到前面对 dev 的赋值,根据我们对设备模型的理解,一旦这个 device 进行注册,就会发生
driver 和 device 的匹配过程了.
不过,现在还不是分析这个过程的时候,我们先来看一下,USB 子系统中的两种驱动.
三:USB 子系统中的两种驱动
中 , 我们可以找到两种 register driver 的方式 , 分别为
usb_register_driver()和 usb_register_device_driver().分别来分析一下这两个接口.
usb_register_device_driver()接口的代码如下:
int usb_register_device_driver(struct usb_device_driver *new_udriver,
struct module *owner)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
new_udriver->_devices = 1;
new_udriver-> = (char *) new_udriver->name;
new_udriver-> = &usb_bus_type;
new_udriver-> = usb_probe_device;
new_udriver-> = usb_unbind_device;
new_udriver-> = owner;
retval = driver_register(&new_udriver->);
if (!retval) {
pr_info(“%s: registered new device driver %s\n”,
usbcore_name, new_udriver->name);
usbfs_update_special();
} else {
printk(KERN_ERR “%s: error %d registering device “
“ driver %s\n”,
usbcore_name, retval, new_udriver->name);
}
return retval;
}
首先,通过 usb_disabled()来判断一下 usb 是否被禁用,如果被禁用,当然就不必执行下面的流
程了,直接退出即可.
从上面的代码,很明显可以看到, struct usb_device_driver 对 struct device_driver 进行了一次封
装,我们注意一下这里的赋值操作:new_udriver->_devices = 1.等等.这些在后面都
是用派上用场的.
usb_register_driver()的代码如下:
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
const char *mod_name)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
new_driver->_devices = 0;
new_driver-> = (char *) new_driver->name;
new_driver-> = &usb_bus_type;
new_driver-> = usb_probe_interface;
new_driver-> = usb_unbind_interface;
new_driver-> = owner;
new_driver->_name = mod_name;
spin_lock_init(&new_driver->);
INIT_LIST_HEAD(&new_driver->);
retval = driver_register(&new_driver->);
if (!retval) {
pr_info(“%s: registered new interface driver %s\n”,
usbcore_name, new_driver->name);
usbfs_update_special();
usb_create_newid_file(new_driver);
} else {
printk(KERN_ERR “%s: error %d registering interface “
“ driver %s\n”,
usbcore_name, retval, new_driver->name);
}
return retval;
}
很明显,在这里接口里,将 new_driver->_devices 设为了 0.而且两个接口的 porbe()
函数也不一样.
其 实 , 对 于 usb_register_driver() 可 以 看 作 是 usb 设 备 中 的 接 口 驱 动 , 而
usb_register_device_driver()是一个单纯的 USB 设备驱动.
四: hub 的驱动分析
: usb_bus_type->match()的匹配过程
usb_bus_type->match()用来判断驱动和设备是否匹配,它的代码如下:
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* 整理 by */
//usb device 的情况
if (is_usb_device(dev)) {
/* interface drivers never match devices */
if (!is_usb_device_driver(drv))
return 0;
/* TODO: Add real matching code */
return 1;
}
//interface 的情况
else {
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
/*整理 by */
if (is_usb_device_driver(drv))
return 0;
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
id = usb_match_id(intf, usb_drv->id_table);
if (id)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
这里的 match 会区分上面所说的两种驱动,即设备的驱动和接口的驱动.
is_usb_device()的代码如下:
static inline int is_usb_device(const struct device *dev)
{
return dev->type == &usb_device_type;
}
很明显,对于 root hub 来说,这个判断是肯定会满足的.
static inline int is_usb_device_driver(struct device_driver *drv)
{
return container_of(drv, struct usbdrv_wrap, driver)->
for_devices;
}
回 忆 一 下 , 我 们 在 分 析 usb_register_device_driver() 的 时 候 , 不 是 将
new_udriver->_devices 置为了 1 么?所以对于 usb_register_device_driver()注册的
驱动来说,这里也是会满足的.
因此,对应 root hub 的情况,从第一个 if 就会匹配到 usb_register_device_driver()注册的驱动.
对于接口的驱动,我们等遇到的时候再来进行分析.
:root hub 的驱动入口
既然我们知道,root hub 会匹配到 usb_bus_type->match()的驱动,那这个驱动到底是什么呢?我
们从 usb 子系统的初始化开始说起.
在 中.有这样的一段代码:
subsys_initcall(usb_init);
对于 subsys_initcall()我们已经不陌生了,在很多地方都会遇到它.在系统初始化的时候,会调
用到它对应的函数.在这里,即为 usb_init().
在 usb_init()中,有这样的代码片段:
static int __init usb_init(void)
{
……
……
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
if (!retval)
goto out;
……
}
在这里终于看到 usb_register_device_driver()了. usb_generic_driver 会匹配到所有 usb 设备.定
义如下:
struct usb_device_driver usb_generic_driver = {
.name = “usb”,
.probe = generic_probe,
.disconnect = generic_disconnect,
#ifdefCONFIG_PM
.suspend = generic_suspend,
.resume = generic_resume,
#endif
.supports_autosuspend = 1,
};
现在是到分析 probe()的时候了.我们这里说的并不是 usb_generic_driver 中的 probe,而是封装
在 struct usb_device_driver 中的 driver 对应的 probe 函数.
在 上 面 的 分 析 , usb_register_device_driver() 将 封 装 的 driver 的 probe() 函 数 设 置 为 了
usb_probe_device().代码如下:
static int usb_probe_device(struct device *dev)
{
struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
struct usb_device *udev;
int error = -ENODEV;
dev_dbg(dev, “%s\n”, __FUNCTION__);
//再次判断 dev 是否是 usb device
if (!is_usb_device(dev)) /* Sanity check */
return error;
udev = to_usb_device(dev);
/* TODO: Add real matching code */
/* The device should always appear to be in use
* unless the driver suports autosuspend.
*/
//pm_usage_cnt: autosuspend 计数.如果此计数为 1,则不允许 autosuspend
udev->pm_usage_cnt = !(udriver->supports_autosuspend);
error = udriver->probe(udev);
return error;
}
首 先 , 可 以 通 过 container_of() 将 封 装 的 struct device, struct device_driver 转 换 为 struct
usb_device 和 struct usb_device_driver.
然后,再执行一次安全检查,判断 dev 是否是属于一个 usb device.
在这里,我们首次接触到了 hub suspend.如果不支持 suspend(udriver->supports_autosuspend
为 0),则 udev->pm_usage_cnt 被设为 1,也就是说,它不允许设备 suspend.否则,将其初始化为 0.
最后,正如你所看到的,流程转入到了 usb_device_driver->probe().
对应到 root hub,流程会转入到 generic_probe().代码如下:
static int generic_probe(struct usb_device *udev)
{
int err, c;
/* put device-specific files into sysfs */
usb_create_sysfs_dev_files(udev);
/* Choose and set the 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) {
dev_err(&udev->dev, “can’t set config #%d, error %d\n”,
c, err);
/* This need not be user can try to
* set other configurations. */
}
}
}
/* USB device state == configured ... usable */
usb_notify_add_device(udev);
return 0;
}
usb_create_sysfs_dev_files()是在 sysfs 中显示几个属性文件,不进行详细分析,有兴趣的可以
结合之前分析的>来看下代码.
usb_notify_add_device()是有关 notify 链表的操作,这里也不做详细分析.
至于 udev->authorized,在 root hub 的初始化中,是会将其初始化为 1 的.后面的逻辑就更简单
了.为 root hub 选择一个配置然后再设定这个配置.
还记得我们在分析 root hub 的时候,在 usb_new_device()中,会将设备的所有配置都取出来,然
后将它们放到了 usb_device-> config.现在这些信息终于会派上用场了.不太熟悉的,可以看下
本站之前有关 usb 控制器驱动的文档.
spec 上规定,对于 hub 设备,只能有一个 config,一个 interface,一个 endpoint.实际上,在
这里,对 hub 的选择约束不大,反正就一个配置,不管怎么样,选择和设定都是这个配置.
不 过 , 为 了 方 便 以 后 的 分 析 , 我 们 还 是 跟 进 去 看 下 usb_choose_configuration() 和
usb_set_configuration()的实现.
实际上,经过这两个函数之后,设备的 probe()过程也就会结束了.
:usb_choose_configuration()函数分析
usb_choose_configuration()的代码如下:
//为 usb device 选择一个合适的配置
int usb_choose_configuration(struct usb_device *udev)
{
int i;
int num_configs;
int insufficient_power = 0;
struct usb_host_config *c, *best;
best = NULL;
//config 数组
c = udev->config;
//config 项数
num_configs = udev->;
//遍历所有配置项
for (i = 0; i
struct usb_interface_descriptor *desc = NULL;
/* It’s possible that a config has no interfaces! */
//配置项的接口数目
//取配置项的第一个接口
if (c-> > 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 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 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-> &
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 won’t
* cause us to reject configurations that we should have
* accepted.
*/
/* Rule out configs that draw too much bus current */
//电源不足.配置描述符中的电力是所需电力的 1/2
if (c-> * 2 > 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.
*/
if (i == 0 && 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. */
//选择一个不是 USB_CLASS_VENDOR_SPEC 的配置
else if (udev-> !=
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));
//如果选择好了配置,返回配置的序号,否则,返回-1
if (best) {
i = best->;
dev_info(&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;
}
Linux 按照自己的喜好选择好了配置之后,返回配置的序号.不过对于 HUB 来说,它有且仅有
一个配置.
:usb_set_configuration()函数分析
既然已经选好配置了,那就告诉设备选好的配置,这个过程是在 usb_set_configuration()中完成
的.它的代码如下:
int usb_set_configuration(struct usb_device *dev, int configuration)
{
int i, ret;
struct usb_host_config *cp = NULL;
struct usb_interface **new_interfaces = NULL;
int n, nintf;
if (dev->authorized == 0 || configuration == -1)
configuration = 0;
else {
for (i = 0; i ; i++) {
if (dev-> ==
configuration) {
cp = &dev->config;
break;
}
}
}
if ((!cp && configuration != 0))
return -EINVAL;
/* The USB spec says configuration 0 means unconfigured.
* But if a device includes a configuration numbered 0,
* we will accept it as a correctly configured state.
* Use -1 if you really want to unconfigure the device.
*/
if (cp && configuration == 0)
dev_warn(&dev->dev, “config 0 descriptor??\n”);
首先,根据选择好的配置号找到相应的配置,在这里要注意了, dev->config[]数组中的配置并
不是按照配置的序号来存放的,而是按照遍历到顺序来排序的.因为有些设备在发送配置描述
符 的 时 候 , 并 不 是 按 照 配 置 序 号 来 发 送 的 , 例 如 , 配 置 2 可 能 在 第 一 次
GET_CONFIGURATION 就被发送了,而配置 1 可能是在第二次 GET_CONFIGURATION 才
能发送.
取得配置描述信息之后,要对它进行有效性判断,注意一下本段代码的最后几行代码:
spec 上规定,0 号配置是无效配置,但是可能有些厂商的设备并末按照这一约定,所以在 linux
中,遇到这种情况只是打印出警告信息,然后尝试使用这一配置.
/* Allocate memory for new interfaces before doing anything else,
* so that if we run out then nothing will have changed. */
n = nintf = 0;
if (cp) {
//接口总数
nintf = cp->;
//interface 指针数组,
new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),
GFP_KERNEL);
if (!new_interfaces) {
dev_err(&dev->dev, “Out of memory\n”);
return -ENOMEM;
}
for (; n
new_interfaces[n] = kzalloc(
sizeof(struct usb_interface),
GFP_KERNEL);
if (!new_interfaces[n]) {
dev_err(&dev->dev, “Out of memory\n”);
ret = -ENOMEM;
free_interfaces:
while (--n >= 0)
kfree(new_interfaces[n]);
kfree(new_interfaces);
return ret;
}
}
//如果总电源小于所需电流,打印警告信息
i = dev->bus_mA - cp-> * 2;
if (i
dev_warn(&dev->dev, “new config #%d exceeds power “
“limit by %dmA\n”,
configuration, -i);
}
在这里,注要是为 new_interfaces 分配空间,要这意的是, new_interfaces 是一个二级指针,它的
最终指向是 struct usb_interface 结构.特别的,如果总电流数要小于配置所需电流,则打印出警
告消息.实际上,这种情况在 usb_choose_configuration()中已经进行了过滤.
/* Wake up the device so we can send it the Set-Config request */
//要对设备进行配置了,先唤醒它
ret = usb_autoresume_device(dev);
if (ret)
goto free_interfaces;
/* if it’s already configured, clear out old state first.
* getting rid of old interfaces means unbinding their drivers.
*/
//不是处于 ADDRESS 状态,先清除设备的状态
if (dev->state != USB_STATE_ADDRESS)
usb_disable_device(dev, 1); /* Skip ep0 */
//发送控制消息,选取配置
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT);
if (ret
/* All the old state is gone, so what else can we do?
* The device is probably useless now anyway.
*/
cp = NULL;
}
//dev->actconfig 存放的是当前设备选取的配置
dev->actconfig = cp;
if (!cp) {
usb_set_device_state(dev, USB_STATE_ADDRESS);
usb_autosuspend_device(dev);
goto free_interfaces;
}
//将状态设为 CONFIGURED
usb_set_device_state(dev, USB_STATE_CONFIGURED);
接下来,就要对设备进行配置了,首先,将设备唤醒.回忆一下我们在分析 UHCI 驱动时,列出来
的设备状态图.只有在 ADDRESS 状态才能转入到 CONFIG 状态.(SUSPEND 状态除外). 所
以,如果设备当前不是处于 ADDRESS 状态,就需要将设备的状态初始化.usb_disable_device()
函数是个比较重要的操作,在接下来再对它进行详细分析.
接着,发送 SET_CONFIGURATION 的 Control 消息给设备,用来选择配置
最后,将 dev->actconfig 指向选定的配置,将设备状态设为 CONFIG
/* Initialize the new interface structures and the
* hc/hcd/usbcore interface/endpoint state.
*/
//遍历所有的接口
for (i = 0; i
struct usb_interface_cache *intfc;
struct usb_interface *intf;
struct usb_host_interface *alt;
cp->interface = intf = new_interfaces;
intfc = cp->intf_cache;
intf->altsetting = intfc->altsetting;
intf->num_altsetting = intfc->num_altsetting;
//是否关联的接口描述符,定义在 minor usb spec 中
intf->intf_assoc = find_iad(dev, cp, i);
kref_get(&intfc->ref);
//选择 0 号设置
alt = usb_altnum_to_altsetting(intf, 0);
/* No altsetting 0?We’ll assume the first altsetting.
* We could use a GetInterface call, but if a device is
* so non-compliant that it doesn’t have altsetting 0
* then I wouldn’t trust its reply anyway.
*/
//如果 0 号设置不存在,选排在第一个设置
if (!alt)
alt = &intf->altsetting[0];
//当前的配置
intf->cur_altsetting = alt;
usb_enable_interface(dev, intf);
intf-> = &dev->dev;
intf-> = NULL;
intf-> = &usb_bus_type;
intf-> = &usb_if_device_type;
intf->_mask = dev->_mask;
device_initialize(&intf->dev);
mark_quiesced(intf);
sprintf(&intf->_id[0], “%d-%s:%d.%d”,
dev->bus->busnum, dev->devpath,
configuration, alt->);
}
kfree(new_interfaces);
if (cp->string == NULL)
cp->string = usb_cache_string(dev, cp->);
之前初始化的 new_interfaces 在这里终于要派上用场了.初始化各接口,从上面的初始化过程
中,我们可以看出:
Intf->altsetting,表示接口的各种设置
Intf->num_altsetting:表示接口的设置数目
Intf->intf_assoc:接口的关联接口(定义于 minor usb spec)
Intf->cur_altsetting:接口的当前设置.
结合之前在 UHCI 中的分析,我们总结一下:
Usb_dev->config,其实是一个数组,存放设备的配置.usb_dev->config[m]-> interface[n]表示第
m 个配置的第 n 个接口的 intercace 结构.(m,bsp; dev->bus->busnum, dev->devpath,
configuration, alt->);
dev 指的是这个接口所属的 usb_dev,结合我们之前在 UHCI 中关于 usb 设备命名方式的描述.
可得出它的命令方式如下:
USB 总线号-设备路径:配置号.接口号.
例如,在我的虚拟机上:
[root@localhost devices]# pwd
/sys/bus/usb/devices
[root@localhost devices]# ls
1-0:
[root@localhost devices]#
可以得知,系统只有一个 usb control.
1-0:: 表示 , 第一个 usb cont 意思上看来 , 它是标记接口为停止状态 . 它的” 反函数” 是
mark_active().两个函数如下示:
static inline void mark_active(struct usb_interface *f)
{
f->is_active = 1;
f-> = PM_EVENT_ON;
}
static inline void mark_quiesced(struct usb_interface *f)
{
f->is_active = 0;
f-> = PM_EVENT_SUSPEND;
}
从代码看来,它只是对接口的活动标志(is_active)进行了设置.
/* Now that all the interfaces are set up, register them
* to trigger binding of drivers to ()
* routines may install different altsettings and may
* claim() any interfaces not yet class drivers
* need that: CDC, audio, video, etc.
*/
//注册每一个接口?
for (i = 0; i
struct usb_interface *intf = cp->interface;
dev_dbg(&dev->dev,
“adding %s (config #%d, interface %d)\n”,
intf->_id, configuration,
intf->cur_altsetting->);
ret = device_add(&intf->dev);
if (ret != 0) {
dev_err(&dev->dev, “device_add(%s) --> %d\n”,
intf->_id, ret);
continue;
}
usb_create_sysfs_intf_files(intf);
}
//使设备 suspend
usb_autosuspend_device(dev);
return 0;
}
最后,注册 intf 内嵌的 device 结构.设备配置完成了,为了省电,可以将设备置为 SUSPEND 状
态.
这个函数中还有几个比较重要的子函数,依次分析如下:
1: usb_disable_device()函数.
顾名思义,这个函数是将设备 disable 掉.代码如下:
void usb_disable_device(struct usb_device *dev, int skip_ep0)
{
int i;
dev_dbg(&dev->dev, “%s nuking %s URBs\n”, __FUNCTION__,
skip_ep0 ? “non-ep0” : “all”);
for (i = skip_ep0; i
usb_disable_endpoint(dev, i);
usb_disable_endpoint(dev, i + USB_DIR_IN);
}
dev->toggle[0] = dev->toggle[1] = 0;
/* getting rid of interfaces will disconnect
* any drivers bound to them (a key side effect)
*/
if (dev->actconfig) {
for (i = 0; i actconfig->; i++) {
struct usb_interface *interface;
/* remove this interface if it has been registered */
interface = dev->actconfig->interface;
if (!device_is_registered(&interface->dev))
continue;
dev_dbg(&dev->dev, “unregistering interface %s\n”,
interface->_id);
usb_remove_sysfs_intf_files(interface);
device_del(&interface->dev);
}
/* Now that the interfaces are unbound, nobody should
* try to access them.
*/
for (i = 0; i actconfig->; i++) {
put_device(&dev->actconfig->interface->dev);
dev->actconfig->interface = NULL;
}
dev->actconfig = NULL;
if (dev->state == USB_STATE_CONFIGURED)
usb_set_device_state(dev, USB_STATE_ADDRESS);
}
}
第二个参数是 skip_ep0.是表示是否跳过 ep0.为 1 表示跳过,为 0 表示清除掉设备中的所有
endpoint.
这个函数可以分为两个部份,一部份是对 usb_dev 中的 endpoint 进行操作,一方面是释放
usb_dev 的选定配置项.
对于第一部份:
从代码中可能看到,如果 skip_ep0 为 1,那就是从 1 开始循环,所以,就跳过了 ep0.另外,一个端
点号对应了两个端点,一个 IN,一个 端点比 OUT 端点要大 USB_DIR_IN.
另外,既然设备都已经被禁用了,那 toggle 也应该回归原位了.因些将两个方向的 toggle 都设
为 0. usb_disable_endpoint()是一个很有意思的处理.它的代码如下:
void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
{
unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
struct usb_host_endpoint *ep;
if (!dev)
return;
//在 dev->ep_out 和 dev->ep_in 删除 endpoint
if (usb_endpoint_out(epaddr)) {
ep = dev->ep_out[epnum];
dev->ep_out[epnum] = NULL;
} else {
ep = dev->ep_in[epnum];
dev->ep_in[epnum] = NULL;
}
//禁用掉此 ep.包括删除 ep 上提交的 urb 和 ep 上的 QH
if (ep) {
ep->enabled = 0;
usb_hcd_flush_endpoint(dev, ep);
usb_hcd_disable_endpoint(dev, ep);
}
}
在 dev->ep_in[]/dev->ep_out[]中删除 endpoint,这点很好理解.比较难以理解的是后面的两个
操作,即 usb_hcd_flush_endpoint()和 usb_hcd_disable_endpoint().根据之前分析的 UHCI 的驱
动,我们得知,对于每个 endpoint 都有一个传输的 qh,这个 qh 上又挂上了要传输的 urb.因此,
这两个函数一个是删除 urb,一个是删除 qh.
usb_hcd_flush_endpoint()的代码如下:
void usb_hcd_flush_endpoint(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
struct usb_hcd *hcd;
struct urb *urb;
if (!ep)
return;
might_sleep();
hcd = bus_to_hcd(udev->bus);
/* No more submits can occur */
//在提交 urb 时,将 urb 加到 ep->urb_list 上的时候要持锁
//因此,这里持锁的话,无法发生中断和提交 urb
spin_lock_irq(&hcd_urb_list_lock);
rescan:
//将挂在 ep->urb_list 上的所有 urb unlink.注意这里 unlink 一般只会设置 urb->unlinked 的
//值,不会将 urb 从 ep->urb_list 上删除.只有在 UHCI 的中断处理的时候,才会调用
//uhci_giveback_urb()将其从 ep->urb_list 中删除
list_for_each_entry (urb, &ep->urb_list, urb_list) {
int is_in;
if (urb->unlinked)
continue;
usb_get_urb (urb);
is_in = usb_urb_dir_in(urb);
spin_unlock(&hcd_urb_list_lock);
/* kick hcd */
unlink1(hcd, urb, -ESHUTDOWN);
dev_dbg (hcd->,
“shutdown urb %p ep%d%s%s\n”,
urb, usb_endpoint_num(&ep->desc),
is_in ? “in” : “out”,
({char *s;
switch (usb_endpoint_type(&ep->desc)) {
case USB_ENDPOINT_XFER_CONTROL:
s = ““; break;
case USB_ENDPOINT_XFER_BULK:
s = “-bulk”; break;
case USB_ENDPOINT_XFER_INT:
s = “-intr”; break;
default:
s = “-iso”; break;
};
s;
}));
usb_put_urb (urb);
/* list contents may have changed */
//在这里解开锁了,对应 ep->urb_list 上又可以提交 urb.
//这里释放释的话,主要是为了能够产生中断
spin_lock(&hcd_urb_list_lock);
goto rescan;
}
spin_unlock_irq(&hcd_urb_list_lock);
/* Wait until the endpoint queue is completely empty */
//等待 urb 被调度完
while (!list_empty (&ep->urb_list)) {
spin_lock_irq(&hcd_urb_list_lock);
/* The list may have changed while we acquired the spinlock */
urb = NULL;
if (!list_empty (&ep->urb_list)) {
urb = list_entry (ep->, struct urb,
urb_list);
usb_get_urb (urb);
}
spin_unlock_irq(&hcd_urb_list_lock);
if (urb) {
usb_kill_urb (urb);
usb_put_urb (urb);
}
}
}
仔细体会这里的代码,为什么在前一个循环中,要使用 goto rescan 重新开始这个循环呢?这是
因为在后面已经将自旋锁释放了,因此,就会有这样的可能,在函数中操作的 urb,可能已经被
调度完释放了.因此,再对这个 urb 操作就会产生错误.所以,需要重新开始这个循环.
那后一个循环又是干什么的呢?后一个循环就是等待 urb 被调度完.有人就会有这样的疑问了,
这里一边等待,然后 endpoint 一边还提交 urb,那这个函数岂不是要耗掉很长时间?
在这里 ,不要忘记了前面的操作 ,在调这个函数之前 , usb_disable_endpoint()已经将这个
endpoint 禁用了,也就是说该 endpoint 不会产生新的 urb.因为,在后一个循环中,只需要等待那
些被 unlink 的 urb 调度完即可.在 usb_kill_urb()中,会一直等待,直到这个 urb 被调度完成为止.
可能有人又会有这样的疑问:
Usb_kill_urb()中也有 unlink urb 的操作,为什么这里要分做两个循环呢?
另外的一个函数是 usb_hcd_disable_endpoint().代码如下:
void usb_hcd_disable_endpoint(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
struct usb_hcd *hcd;
might_sleep();
hcd = bus_to_hcd(udev->bus);
if (hcd->driver->endpoint_disable)
hcd->driver->endpoint_disable(hcd, ep);
}
从上面的代码可以看到,操作转向了 hcd->driver 的 endpoint_disable()接口.
以 UHCI 为例.在 UHCI 中,对应的接口为:
static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,
struct usb_host_endpoint *hep)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
struct uhci_qh *qh;
spin_lock_irq(&uhci->lock);
qh = (struct uhci_qh *) hep->hcpriv;
if (qh == NULL)
goto done;
while (qh->state != QH_STATE_IDLE) {
++uhci->num_waiting;
spin_unlock_irq(&uhci->lock);
wait_event_interruptible(uhci->waitqh,
qh->state == QH_STATE_IDLE);
spin_lock_irq(&uhci->lock);
--uhci->num_waiting;
}
uhci_free_qh(uhci, qh);
done:
spin_unlock_irq(&uhci->lock);
}
这个函数没啥好说的,就是在 uhci->waitqh 上等待队列状态变为 QH_STATE_IDLE.来回忆一
下,qh 在什么情况下才会变为 QH_STATE_IDLE 呢? 是在 qh 没有待传输的 urb 的时候.
然后,将 qh 释放.
现在我们来接着看 usb_disable_device()的第二个部份.
第二部份主要是针对 dev->actconfig 进行的操作, dev->actconfig 存放的是设备当前的配置,
现 在 要 将 设 备 设 回 Address 状 态 . 就 些 东 西 当 然 是 用 不 了 上 的 了 . 释 放
dev->actconfig->interface[]中的各元素,注意不要将 dev->actconfig->interface[]所指向的信息
释放了,它都是指向 dev->config[]-> intf_cache[]中的东西,这些东西一释放,usb device 在 Get_
Configure 所获得的信息就会部丢失了.
就这样, usb_disable_device()函数也走到了尾声.
2: usb_cache_string()函数
这个函数我们在分析 UHCI 的时候已经接触过,但末做详细的分析.
首先了解一下这个函数的作用,有时候,为了形象的说明,会提供一个字符串形式的说明.例如,
对于配置描述符来说,它的 iConfiguration 就表示一个字符串索引,然后用 Get_String 就可以
取得这个索引所对应的字串了.不过,事情并不是这么简单.因为字符串对应不同的编码,所以
这里还会对应有编码的处理.来看具体的代码:
char *usb_cache_string(struct usb_device *udev, int index)
{
char *buf;
char *smallbuf = NULL;
int len;
if (index
return NULL;
//不知道字符到底有多长,就按最长 256 字节处理
buf = kmalloc(256, GFP_KERNEL);
if (buf) {
len = usb_string(udev, index, buf, 256);
//取到字符了,分配合适的长度
if (len > 0) {
smallbuf = kmalloc(++len, GFP_KERNEL);
if (!smallbuf)
return buf;
//将字符 copy 过去
memcpy(smallbuf, buf, len);
}
//释放旧空间
kfree(buf);
}
return smallbuf;
}
这个函数没啥好说的,流程转入到 usb_string 中.代码如下:
int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
{
unsigned char *tbuf;
int err;
unsigned int u, idx;
if (dev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
if (size
return -EINVAL;
buf[0] = 0;
tbuf = kmalloc(256, GFP_KERNEL);
if (!tbuf)
return -ENOMEM;
/* get langid for strings if it’s not yet known */
//先取得设备支持的编码 ID
if (!dev->have_langid) {
//以 0 号序号和编码 0,Get_String 就可得到设备所支持的编码列表
err = usb_string_sub(dev, 0, 0, tbuf);
//如果发生了错误,或者是取得的数据超短(最短为 4 字节)
if (err
dev_err(&dev->dev,
“string descriptor 0 read error: %d\n”,
err);
goto errout;
} else if (err
dev_err(&dev->dev, “string descriptor 0 too short\n”);
err = -EINVAL;
goto errout;
}
//取设备支持的第一个编码
else {
dev->have_langid = 1;
dev->string_langid = tbuf[2] | (tbuf[3]
/* always use the first langid listed */
dev_dbg(&dev->dev, “default language 0x%04x\n”,
dev->string_langid);
}
}
//以编码 ID 和序号 Index 作为参数 Get_String 取得序号对应的字串
err = usb_string_sub(dev, dev->string_langid, index, tbuf);
if (err
goto errout;
//空一个字符来用来存放结束符
size--; /* leave room for trailing NULL char in output buffer */
//两字节一组,(Unicode 编码的)
for (idx = 0, u = 2; u
if (idx >= size)
break;
//如果高字节有值,说明它不是 ISO-8859-1 编码的,将它置为?
//否则,就将低位的值存放到 buf 中
if (tbuf[u+1]) /* high byte */
buf[idx++] = ‘?’;/* non ISO-8859-1 character */
else
buf[idx++] = tbuf;
}
//在最后一位赋 0,字串结尾
buf[idx] = 0;
//返回字串的长度,(算上了最后的结尾字符)
err = idx;
//如果该描述符不是 STRING 描述符,打印出错误提示
if (tbuf[1] != USB_DT_STRING)
dev_dbg(&dev->dev,
“wrong descriptor type %02x for string %d (\”%s\”)\n”,
tbuf[1], index, buf);
errout:
//释放空间,返回长度
kfree(tbuf);
return err;
}
结合代码中的注释,就很容易理解这一函数了,在此不对这一函数做详细分析.
跟踪进 usb_string_sub().代码如下:
static int usb_string_sub(struct usb_device *dev, unsigned int langid,
unsigned int index, unsigned char *buf)
{
int rc;
/* Try to read the string descriptor by asking for the maximum
* possible number of bytes */
//如果设备不需要 Fixup 就发出 Get_String
if (dev->quirks & USB_QUIRK_STRING_FETCH_255)
rc = -EIO;
else
rc = usb_get_string(dev, langid, index, buf, 255);
/* If that failed try to read the descriptor length, then
* ask for just that many bytes */
//如果 Get_String 失败或者取得长度有问题.就先取字符描述符的头部
//再以实际的长度和参数,再次 Get_String
if (rc
rc = usb_get_string(dev, langid, index, buf, 2);
if (rc == 2)
rc = usb_get_string(dev, langid, index, buf, buf[0]);
}
//如果成功
if (rc >= 2) {
//如果前两个字节为空.则需要找到数据的有效起始位置
if (!buf[0] && !buf[1])
usb_try_string_workarounds(buf, &rc);
/* There might be extra junk at the end of the descriptor */
//整调一下描述符的长度
if (buf[0]
rc = buf[0];
//将 rc 置为了一个偶数.
rc = rc - (rc & 1); /* force a multiple of two */
}
//长度最终小于 2.返回错误值
if (rc
rc = (rc
return rc;
}
在这个地方,有个错误处理,可能有的设备你一次用 255 的长度去取它对字符串会返回一个错
误,所以,在用 255 长度返回错误的时候,先以 2 为长度取它的描述符头度,再以描述符的实际
长度去取字符串描述符串.
另外,在描述符的前两个字节都为空的情况下,就需要计算它的有效长度.在代码中,这一工作
是由 usb_try_string_workarounds()完成的.
static void usb_try_string_workarounds(unsigned char *buf, int *length)
{
int newlength, oldlength = *length;
//前两个字节是描述符头部,所以从 2 开始循环.
//Unicode 编码用两个字节来表示一个字符. 所以每次循环完了之后要+2
for (newlength = 2; newlength + 1
//低字节是不可打印字符,或者高字节不为空(不是 ISO-8859-1), 就退出循环.
if (!isprint(buf[newlength]) || buf[newlength + 1])
break;
//修正字符串描述符的实际长度.
//如果 newlength 等于 2.说明字符中描述符没有带字串
if (newlength > 2) {
buf[0] = newlength;
*length = newlength;
}
}
这个函数涉及到编码方面的东东,建议参阅 fudan_abc 的>,上面的较详细的描述.
至此, usb_cache_string()完分析完了.
到这里,usb device driver 的 probe 过程也就完成了.
五:hub 接口驱动分析
:接口驱动架构
是时候来分析接口驱动的架构了.
我们在上面看到了接口设备的注册.在开篇的时候分析了接口驱动的注册.我们首先来分析接
口驱备和接口驱动的匹配.
代码还是在 usb_bus_type->match().只不过是对应另外的一种情况了.将相关代码列出:
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
……
if (is_usb_device(dev)) {
……
}
//interface 的情况
else {
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
/* device drivers never match interfaces */
if (is_usb_device_driver(drv))
return 0;
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
id = usb_match_id(intf, usb_drv->id_table);
if (id)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
经过前面的分析,因为在注册接口设备的时候,是将 type 设为 usb_if_device_type,因此,这个函
数第一个 if 是不会满足的.
首先 , 将 struct device 和 struct device_driver 转换为被封装的 struct usb_interface 和 struct
usb_driver. 紧 接 着 , 我 们 看 到 了 两 个 匹 配 , 一 个 是 usb_match_id(). 另 外 一 个 是
usb_match_dynamic_id().后者只有在前者没有匹配成功的情况下才能调用.我们也可以看到,
struct usb_driver 中一个 struct usb_device_id 类型的数组(id_table 字段)和一个 dynids 链表.哪
id 和 dyname_id 有什么区别呢?
一般来说 ,id_table 是 usb 接口驱动静态定义的可以和此驱动匹配设备的一些参数 .而
dyname_id 是可以动态调整的.
你可以通过写/sys/bus/usb/drivers/XXX/new_id 来设置驱动的 dyname_id.其中,XXX 表示驱动
的名称.
这两个函数的逻辑都差不多,我们以 usb_match_id()为例进行分析.代码如下:
const struct usb_device_id *usb_match_id(struct usb_interface *interface,
const struct usb_device_id *id)
{
/* proc_connectinfo in may call us with id == NULL. */
//如果 driver 中没有定义符合条件的 id_talbe.则不能静态匹配所有设备
if (id == NULL)
return NULL;
/* It is important to check that id->driver_info is nonzero,
since an entry that is all zeroes except for a nonzero
id->driver_info is the way to create an entry that
indicates that the driver want to examine every
device and interface. */
//如果 id_talbe 中,有定义的匹配项,则调用 usb_match_one_id()进行匹配测试
//如果成功,则返回匹配成功的 id_talbe 项数.否则,继续下一项
for (; id->idVendor || id->idProduct || id->bDeviceClass ||
id->bInterfaceClass || id->driver_info; id++) {
if (usb_match_one_id(interface, id))
return id;
}
//如果没有匹配成功,则返回 NULL
return NULL;
}
Id_talbe 所属的结构如下示:
struct usb_device_id {
/* which fields to match against? */
__u16 match_flags;
/* Used for product specific matches; range is inclusive */
__u16 idVendor;
__u16 idProduct;
__u16 bcdDevice_lo;
__u16 bcdDevice_hi;
/* Used for device class matches */
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
/* Used for interface class matches */
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
/* not matched against */
kernel_ulong_tdriver_info;
};
match_flags 是一个匹配标志,表示要匹配哪一项.而后面的成员,例如 idVendor, idProduct.表
示具体要匹配项的值.
usb_match_one_id()的代码如下:
int usb_match_one_id(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_host_interface *intf;
struct usb_device *dev;
/* proc_connectinfo in may call us with id == NULL. */
//再次判断 id 是否为空
if (id == NULL)
return 0;
//接口听当前匹配
intf = interface->cur_altsetting;
//转换为所属的 usb_dev
dev = interface_to_usbdev(interface);
//关于设备参数的匹配.只有在设备参数符合的情况下,才会进行接口
//参数的匹配.
if (!usb_match_device(dev, id))
return 0;
//下面是关于接口参数的匹配
/* The interface class, subclass, and protocol should never be
* checked for a match if the device class is Vendor Specific,
* unless the match record specifies the Vendor ID. */
// 当 设 备 是 厂 商 自 定 义 类 型 时 (Vendor Specific). 除 非 定 义 了
//USB_DEVICE_ID_MATCH_VENDOR
//否则是不需要匹配的
if (dev-> == USB_CLASS_VENDOR_SPEC &&
!(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
(id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS |
USB_DEVICE_ID_MATCH_INT_PROTOCOL)))
return 0;
//如果要匹配 interface class
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
(id->bInterfaceClass != intf->))
return 0;
//如果要匹配 interface subclass
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
(id->bInterfaceSubClass != intf->))
return 0;
//如果要匹配 interface protocol
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
(id->bInterfaceProtocol != intf->))
return 0;
return 1;
}
这个函数逻辑比较简单.它先检测所属设备参数的匹配项.再检查接口参数的匹配项.由于这
个函数比较简单,就不对它多费舌去解释了.
简单的说一下关于 Vendor Specific class 的设备,这个类型在 USB Class Codes 被定义如下:
This base class is defined for vendors to use as they class codes can be used in both
Device and Interface Descriptors.
*) {=true; =*; =‘Click here to open
new window\nCTRL+Mouse wheel to zoom in/out’;}”
onmouseover=“if(>*) {=true; =*;
=‘hand’; =‘Click here to open new window\nCTRL+Mouse wheel to zoom
in/out’;}” onclick=“if(!) {return true;} else
{(‘
onmousewheel=“return imgzoom(this);” alt=““ />
也就是说,这个类型是被厂商自定义的.它主要是靠 SubClass 和 protocol 来区分设备的.这些
都是非标准定义的.
如果被这个 match 匹配成功的话,就会转入 probe()了,由于 bus 中没有 probe()函数,因此,流程
转入到了与之匹配的驱动的 probe 函数.
记得在开篇的时候,分析过接口驱动,它的 probe 函数被设置为了 usb_cache_string().代码如下:
static int usb_probe_interface(struct device *dev)
{
struct usb_driver *driver = to_usb_driver(dev->driver);
struct usb_interface *intf;
struct usb_device *udev;
const struct usb_device_id *id;
int error = -ENODEV;
dev_dbg(dev, “%s\n”, __FUNCTION__);
//如果是一个 usb 设备,退出
if (is_usb_device(dev)) /* Sanity check */
return error;
intf = to_usb_interface(dev);
udev = interface_to_usbdev(intf);
//如果所属设备的 authorized 为 0.错误,退出
if (udev->authorized == 0) {
dev_err(&intf->dev, “Device is not authorized for usage\n”);
return -ENODEV;
}
//再确认一下设备和驱动是否匹配
id = usb_match_id(intf, driver->id_table);
if (!id)
id = usb_match_dynamic_id(intf, driver);
//如果匹配
if (id) {
dev_dbg(dev, “%s - got id\n”, __FUNCTION__);
//使设备 resume
error = usb_autoresume_device(udev);
if (error)
return error;
/* Interface “power state” doesn’t correspond to any hardware
* state use it to record when it’s bound to
* a driver that may start I/0:it’s not frozen/quiesced.
*/
//将接口标志为 active
mark_active(intf);
//intf->condition:接口的状态
//USB_INTERFACE_BINDING:正在绑定
intf->condition = USB_INTERFACE_BINDING;
/* The interface should always appear to be in use
* unless the driver suports autosuspend.
*/
//intf->pm_usage_cnt:如果为 1,接口不会 autosuspend
intf->pm_usage_cnt = !(driver->supports_autosuspend);
//调用挂装结构的 probe
error = driver->probe(intf, id);
//如果发生了错误,将接口标记成不活动的,接口状态设为 USB_INTERFACE_UNBOUND
if (error) {
mark_quiesced(intf);
intf->needs_remote_wakeup = 0;
intf->condition = USB_INTERFACE_UNBOUND;
}
//如果成功,将接口状态设为 USB_INTERFACE_BOUND
else
intf->condition = USB_INTERFACE_BOUND;
//使设备 suspend
usb_autosuspend_device(udev);
}
return error;
}
至此为至,我们遇到了大量的 usb_autoresume_device()/usb_autosuspend_device().先将它们放
到一边,等以后再来详细分析他们的操作.
还记得,在 usb_set_configuration()中,为每一个接口调用了 mark_quiesced().所以在这个 probe
里,将正确匹配的接口调用 mark_active(),将其标记为 Active.
另 外 , 我 们 来 看 一 下 ,intf-> condition 的 几 种 情 况 , 在 代 码 中 , condition 属 于 enum
usb_interface_condition 结构.如下示:
enum usb_interface_condition {
USB_INTERFACE_UNBOUND = 0,
USB_INTERFACE_BINDING,
USB_INTERFACE_BOUND,
USB_INTERFACE_UNBINDING,
};
分别表示,没有绑定,正在绑定,已经绑定,正在解除绑定.
从上面的代码中可以看出.最后的流程会回溯到 usb_driver 的 probe.
:hub 的接口驱动
经过上面的分析,我们知道,接口驱动和接口的匹配主要是根据接口的一些信息来判断的.那
对于 hub 的接口,它的标志信息是什么呢?
在 spec 上 ,规定了 hub 的设备 class 和接口 class 都为 0x9.也就是代码中定义的
USB_CLASS_HUB.
同时,我们注意到,在 usb_init()àusb_hub_init()中:
int usb_hub_init(void)
{
if (usb_register(&hub_driver)
printk(KERN_ERR “%s: can’t register hub driver\n”,
usbcore_name);
return -1;
}
khubd_task = kthread_run(hub_thread, NULL, “khubd”);
if (!IS_ERR(khubd_task))
return 0;
/* Fall through if kernel_thread failed */
usb_deregister(&hub_driver);
printk(KERN_ERR “%s: can’t start khubd\n”, usbcore_name);
return -1;
}
这个函数注册了 hub_driver 对应的接口驱动,还启动了一个内核线程.在这里,我们注意到,
hub_driver 的定义如下:
static struct usb_driver hub_driver = {
.name = “hub”,
.probe = hub_probe,
.disconnect = hub_disconnect,
.suspend =hub_suspend,
.resume = hub_resume,
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
.ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
Hub_id_talbe 被定义为:
static struct usb_device_id hub_id_table [] = {
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_CLASS_HUB},
{ } /* Terminating entry */
};
看到了没,这些信息就是 hub 的接口 class 信息.
也就是说, hub_driver 就是为们要找的 hub 接口的驱动.
根据上面的分析,流程最终会到 hub_driver->probe 中,对应的接口为 hub_probe().
:接口驱动的 probe 过程
Hub_probe()的代码如下:
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_host_interface *desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *hdev;
struct usb_hub *hub;
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
#ifdefCONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
dev_warn(&intf->dev, “ignoring external hub\n”);
return -ENODEV;
}
#endif
/* Some hubs have a subclass of 1, which AFAICT according to the */
/*specs is not defined, but it works */
//spec 上规定接口的 subclass 为 0.但为 1 时,也能工作
if ((desc-> != 0) &&
(desc-> != 1)) {
descriptor_error:
dev_err (&intf->dev, “bad descriptor, ignoring hub\n”);
return -EIO;
}
/* Multiple endpoints? What kind of mutant ninja-hub is this? */
//spec 上规定 hub interface 的 endpoint 数目为 1,这里的数目没有包括 ep0
if (desc-> != 1)
goto descriptor_error;
//端点描述符
endpoint = &desc->endpoint[0].desc;
/* If it’s not an interrupt in endpoint, we’d better punt! */
//不是 IN 方向的中断传输端点,有错误
if (!usb_endpoint_is_int_in(endpoint))
goto descriptor_error;
/* We found a hub */
dev_info (&intf->dev, “USB hub found\n”);
//初始化一个 struct usb_hub 结构
hub = kzalloc(sizeof(*hub), GFP_KERNEL);
if (!hub) {
dev_dbg (&intf->dev, “couldn’t kmalloc hub struct\n”);
return -ENOMEM;
}
//初始化引用计数
kref_init(&hub->kref);
//初始化 event_list
INIT_LIST_HEAD(&hub->event_list);
//hub->intfdev:指向接口封装的 dev
hub->intfdev = &intf->dev;
//hub->hdev:hub 所属的 usb_dev
hub->hdev = hdev;
//初始化一个延时工作队列,用来管理 hub LED 的
INIT_DELAYED_WORK(&hub->leds, led_work);
//增加 intf->dev 的引用计数
usb_get_intf(intf);
//intf->dev 的私有结构指和 hub
usb_set_intfdata (intf, hub);
//接口需要远程唤醒
intf->needs_remote_wakeup = 1;
//如果是一个高速设备,增加 highspeed_hubs 计数
if (hdev->speed == USB_SPEED_HIGH)
highspeed_hubs++;
//配置 hub
if (hub_configure(hub, endpoint) >= 0)
return 0;
hub_disconnect (intf);
return -ENODEV;
}
这个函数前一部份进行一些有效性检查,后半部份分配并初始化一个 struct usb_hub 结构.然
后流程就转入了 hub_configure().
hub_configure()函数是一个很重要的操作,它的代码如下:
static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev;
u16 hubstatus, hubchange;
u16 wHubCharacteristics;
unsigned int pipe;
int maxp, ret;
char *message;
//为 buffer 分配空间,DMA 分配,其物理地址存放在 hub->buffer_dma 中
//buffer 是一个指向数组的指针
hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,
&hub->buffer_dma);
if (!hub->buffer) {
message = “can’t allocate hub irq buffer”;
ret = -ENOMEM;
goto fail;
}
//为 status 分配空间
hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
if (!hub->status) {
message = “can’t kmalloc hub status buffer”;
ret = -ENOMEM;
goto fail;
}
mutex_init(&hub->status_mutex);
//为 hub->descriptor 分配空间
hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
if (!hub->descriptor) {
message = “can’t kmalloc hub descriptor”;
ret = -ENOMEM;
goto fail;
}
/* Request the entire hub descriptor.
* hub->descriptor can handle USB_MAXCHILDREN ports,
* but the hub can/will return fewer bytes here.
*/
//取得 hub 描述符
ret = get_hub_descriptor(hdev, hub->descriptor,
sizeof(*hub->descriptor));
//如果 Get_Descriptor 失败或者 hub 端口超多
if (ret
message = “can’t read hub descriptor”;
goto fail;
}
//协议上规定 hub 最多有 255 个接口,但 Linux 认为 31 个接口已经够多了
else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {
message = “hub has too many ports!”;
ret = -ENODEV;
goto fail;
}
//hdev->maxchild: hub 的端口数目
hdev->maxchild = hub->descriptor->bNbrPorts;
dev_info (hub_dev, “%d port%s detected\n”, hdev->maxchild,
(hdev->maxchild == 1) ? ““ : “s”);
//wHubCharacteristics 字段
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
//是否是一个复合设备
if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
int i;
char portstr [USB_MAXCHILDREN + 1];
for (i = 0; i maxchild; i++)
//找到数组中的所在项和数组项中的位置
portstr = hub->descriptor->DeviceRemovable
[((i + 1) / 8)] & (1
? ‘F’ : ‘R’;
//以\0 结尾
portstr[hdev->maxchild] = 0;
dev_dbg(hub_dev, “compound device; port removable status: %s\n”, portstr);
} else
dev_dbg(hub_dev, “standalone hub\n”);
//电源开关模式
switch (wHubCharacteristics & HUB_CHAR_LPSM) {
//ganged 电源开关,所有连接端口同时开机
case 0x00:
dev_dbg(hub_dev, “ganged power switching\n”);
break;
//端口有单独的电源开关
case 0x01:
dev_dbg(hub_dev, “individual port power switching\n”);
break;
//1X:保留, 表示没有电源开关
case 0x02:
case 0x03:
dev_dbg(hub_dev, “no power switching (usb )\n”);
break;
}
//过电流保护模式
switch (wHubCharacteristics & HUB_CHAR_OCPM) {
//全部电流保护
case 0x00:
dev_dbg(hub_dev, “global over-current protection\n”);
break;
//个别连接端口电源保护
case 0x08:
dev_dbg(hub_dev, “individual port over-current protection\n”);
break;
//没有过电流保护
case 0x10:
case 0x18:
dev_dbg(hub_dev, “no over-current protection\n”);
break;
}
//初始化 TT 相关的东西
spin_lock_init (&hub->);
INIT_LIST_HEAD (&hub->_list);
INIT_WORK (&hub->, hub_tt_kevent);
//设备描述符的 bDeviceProtocol 字段
switch (hdev->) {
//HUB 是一个低速/全速设备
case 0:
break;
//只有一个 TT
case 1:
dev_dbg(hub_dev, “Single TT\n”);
hub-> = hdev;
break;
//多个 TT
case 2:
//为接口选取 1 号设置
ret = usb_set_interface(hdev, 0, 1);
if (ret == 0) {
dev_dbg(hub_dev, “TT per port\n”);
hub-> = 1;
} else
dev_err(hub_dev, “Using single TT (err %d)\n”,
ret);
hub-> = hdev;
break;
default:
dev_dbg(hub_dev, “Unrecognized hub protocol %d\n”,
hdev->);
break;
}
/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
//TT think time,
switch (wHubCharacteristics & HUB_CHAR_TTTT) {
case HUB_TTTT_8_BITS:
if (hdev-> != 0) {
hub->_time = 666;
dev_dbg(hub_dev, “TT requires at most %d “
“FS bit times (%d ns)\n”,
8, hub->_time);
}
break;
case HUB_TTTT_16_BITS:
hub->_time = 666 * 2;
dev_dbg(hub_dev, “TT requires at most %d “
“FS bit times (%d ns)\n”,
16, hub->_time);
break;
case HUB_TTTT_24_BITS:
hub->_time = 666 * 3;
dev_dbg(hub_dev, “TT requires at most %d “
“FS bit times (%d ns)\n”,
24, hub->_time);
break;
case HUB_TTTT_32_BITS:
hub->_time = 666 * 4;
dev_dbg(hub_dev, “TT requires at most %d “
“FS bit times (%d ns)\n”,
32, hub->_time);
break;
}
/* probe() zeroes hub->indicator[] */
//是否支持连接端口 LED
if (wHubCharacteristics & HUB_CHAR_PORTIND) {
hub->has_indicators = 1;
dev_dbg(hub_dev, “Port indicators are supported\n”);
}
//bPwrOn2PwrGood 字段表示连接端口从开机到准备好的时间
dev_dbg(hub_dev, “power on to power good time: %dms\n”,
hub->descriptor->bPwrOn2PwrGood * 2);
/* power budgeting mostly matters with bus-powered hubs,
* and battery-powered root hubs (may provide just 8 mA).
*/
//Get_Status 设备
ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
//Get_Status 失败
if (ret
message = “can’t get hub status”;
goto fail;
}
//设备的 status 包含两个部份:bit 0 表示使用自身电源 bit 1 是远程唤醒字段
le16_to_cpus(&hubstatus);
//如果是 root hub
if (hdev == hdev->bus->root_hub) {
//如果电流没有限制
if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)
hub->mA_per_port = 500;
//有限制的情况下
else {
hub->mA_per_port = hdev->bus_mA;
hub->limited_power = 1;
}
}
//如果使用总线电流
else if ((hubstatus & (1
dev_dbg(hub_dev, “hub controller current requirement: %dmA\n”,
hub->descriptor->bHubContrCurrent);
hub->limited_power = 1;
if (hdev->maxchild > 0) {
int remaining = hdev->bus_mA -
hub->descriptor->bHubContrCurrent;
if (remaining maxchild * 100)
dev_warn(hub_dev,
“insufficient power available “
“to use all downstream ports\n”);
hub->mA_per_port = 100; /* */
}
}
// 如果使用本身电流
else {/* Self-powered external hub */
/* FIXME: What about battery-powered external hubs that
* provide less current per port? */
hub->mA_per_port = 500;
}
if (hub->mA_per_port
dev_dbg(hub_dev, “%umA bus power budget for each child\n”,
hub->mA_per_port);
//Get hub status
//hub 状态位和改变位
ret = hub_hub_status(hub, &hubstatus, &hubchange);
if (ret
message = “can’t get hub status”;
goto fail;
}
/* local power status reports aren’t always correct */
//自身供电
if (hdev->actconfig-> & USB_CONFIG_ATT_SELFPOWER)
dev_dbg(hub_dev, “local power source is %s\n”,
(hubstatus & HUB_STATUS_LOCAL_POWER)
? “lost (inactive)” : “good”);
//不支持过电流保护
if ((wHubCharacteristics & HUB_CHAR_OCPM) == 0)
dev_dbg(hub_dev, “%sover-current condition exists\n”,
(hubstatus & HUB_STATUS_OVERCURRENT) ? ““ : “no “);
/* set up the interrupt endpoint
* We use the EP’s maxpacket size instead of (PORTS+1+7)/8
* bytes as [] says because some hubs are known
* to send more data (and thus cause overflow). For root hubs,
* maxpktsize is defined in ’s fake endpoint descriptors
* to be big enough for at least USB_MAXCHILDREN ports. */
//设备中断控制传输的 urb
//通道和最大包长度
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
if (maxp > sizeof(*hub->buffer))
maxp = sizeof(*hub->buffer);
//分配 urb
hub->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!hub->urb) {
message = “couldn’t allocate interrupt urb”;
ret = -ENOMEM;
goto fail;
}
//填充 urb
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
hub->urb->transfer_dma = hub->buffer_dma;
hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* maybe cycle the hub leds */
if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
//驱动 hub
hub_power_on(hub);
//激活 hub
hub_activate(hub);
return 0;
fail:
dev_err (hub_dev, “config failed, %s (err %d)\n”,
message, ret);
/* hub_disconnect() frees urb and descriptor */
return ret;
}
这个函数先初始化 struct usb_hub 中的几个指针,为之分配空间,然后,取得 hub 的描述符,再根
据取得的描述符信息再初始化和显示一些调试信息.其中的一些成员赋值等我们用到的时候
再来进行分析.这个函数的后面关于 urb 部份和后面调用的两个子函数才是我们要分析的重
点.
在这里,顺带提一下 HUB 的指示灯问题.
Hub 描述符的 wHubCharacteristics 的 bit7 来描述设备是否支持显示灯.为 1 表示在下游的连
接端口上支持显示灯,为 0 则不支持.
如果 Hub 支持指示灯,则将 hub->has_indicators 置为 1.另外,HUB 的指示灯是否起作用,还由
一个参数决定,在代码中,大家也看到,这个参数是 blinkenlights.这个参数定义如下:
static int blinkenlights = 0;
module_param (blinkenlights, bool, S_IRUGO);
MODULE_PARM_DESC (blinkenlights, “true to cycle leds on hubs”);
这是一个可调的模块参数.如果要显示灯起作用,必须要将其置为 1 才可以.我们可以用下面
两种方法来设置这个参数:
1:如果模块没有编译进 kernel,可以在插入模块的时候,加上这个参数:
modprobe usbcore blinkenlights=1
2:如果模块已经编进 kernel,那在 kernel 的启动参数上加上如下参数:
=1
在 spec 中,对不同情况下的灯颜色都做了定义.
在代码中,灯的显示交给了一延迟的工作队列进行处理,初始化如下所示:
INIT_DELAYED_WORK(&hub->leds, led_work); (在 hub_probe()函数中)
在这里,并不打算详细分析这一部份,有兴趣的可以跟踪下去看下.
那函数后面初始化的 URB 是用来干什么的呢?我们将这个 URB 的初始化部份单独列出如
下:
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
if (maxp > sizeof(*hub->buffer))
maxp = sizeof(*hub->buffer);
我们从此可以看出,这个 URB 是作用于 ep0 之外的另一个端点,而且传输数据的长度最大为
sizeof(*hub->buffer)
Hub->buffer 被定义如下:
struct usb_hub {
……
char (*buffer)[8];
……
}
由此可以看出 buffer 是指向一个 8 元素字符数组的指针,sizeof(*hub->buffer)等于 0.
关于传输的数据长度,代码中有一段注释,这段注释说,spec 上规定的长度是(PORTS+1+7)/8.
而 linux 中,对每个 hub 上挂有认为最多的端口进行处理,因些,就是(31+1+7)/8 = 5
为什么这里要是 8 呢?
因为 spec 上规定,ep0 的最大发包长度,可能为 .所以选择比 5 要大的最
小值 8.
另外,我们注意到,URB 完成之后,所要调用的函数是 hub_irq().如下所示:
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
UHChub_power_on().代码如下:
static void hub_power_on(struct usb_hub *hub)
{
int port1;
//从连接端口从开机到准备好的时间
unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
//wHubCharacteristics 字段
u16 wHubCharacteristics =
le16_to_cpu(hub->descriptor->wHubCharacteristics);
/* Enable power on each hubs have reserved values
* of LPSM (> 2) in their descriptors, even though they are
* USB hubs do not implement port-power switching
* but only emulate all cases, the ports won’t work
* unless we send these messages to the hub.
*/
//开关模式
if ((wHubCharacteristics & HUB_CHAR_LPSM)
dev_dbg(hub->intfdev, “enabling power on all ports\n”);
else
dev_dbg(hub->intfdev, “trying to enable port power on “
“non-switchable hub\n”);
//给每一个端口发送 PORT_POWER 的 Set_Feature 消息
for (port1 = 1; port1 descriptor->bNbrPorts; port1++)
set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
/* Wait at least 100 msec for power to become stable */
//等待端口可以正常工作,至少 100 ms
msleep(max(pgood_delay, (unsigned) 100));
}
这里就是给每个接口发送 PORT_POWER 的 Set_Feature 消息,告之可起来工作了,然后等待
端口可以工作.
另外要分析的函数是 hub_activate().代码如下:
static void hub_activate(struct usb_hub *hub)
{
int status;
hub->quiescing = 0;
hub->activating = 1;
//提交 urb
status = usb_submit_urb(hub->urb, GFP_NOIO);
if (status
dev_err(hub->intfdev, “activate --> %d\n”, status);
if (hub->has_indicators && blinkenlights)
schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
/* scan all ports ASAP */
kick_khubd(hub);
}
首先,是在 hub 的两个字段进行赋值操作,hub-> quiescing 和 hub->activating 表示分别表示
hub 处理暂停和活跃状态.注意,在这里,不要和接口的 mark_active()设置的 intf->is_active 相
混淆.
然后,将 hub->urb 提交.开始调度 Led 的工作队列.
最后,流程转入 kick_khubd().代码如下:
static void kick_khubd(struct usb_hub *hub)
{
unsigned long flags;
/* Suppress autosuspend until khubd runs */
//pm_usage_cnt 设置为 1,防止 autosuspend
to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
spin_lock_irqsave(&hub_event_lock, flags);
if (!hub->disconnected && list_empty(&hub->event_list)) {
list_add_tail(&hub->event_list, &hub_event_list);
wake_up(&khubd_wait);
}
spin_unlock_irqrestore(&hub_event_lock, flags);
}
在这个函数中,先将接口的 pm_usage_cnt 置 1,此后,该接口就不能 SUSPEND.
本文档完。